Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Social plugin: can't find fonts on Windows #7085

Closed
4 tasks done
Aazih opened this issue Apr 24, 2024 · 17 comments · Fixed by #7117
Closed
4 tasks done

Social plugin: can't find fonts on Windows #7085

Aazih opened this issue Apr 24, 2024 · 17 comments · Fixed by #7117
Labels
bug Issue reports a bug resolved Issue is resolved, yet unreleased if open

Comments

@Aazih
Copy link

Aazih commented Apr 24, 2024

Context

I am attempting to setup social cards for the first time in Windows. Have searched for solutions but it appears that loading fonts is not working as expected

Bug description

Enable social plugin in mkdocs.yml via adding the following lines:

`plugins:

  • info
  • social
    `
    Results in the following error:

INFO - Building documentation... INFO - Cleaning site directory INFO - Documentation built in 0.28 seconds INFO - [23:48:15] Watching paths for changes: 'docs', 'mkdocs.yml' INFO - [23:48:15] Serving on http://127.0.0.1:8000/ INFO - [23:48:16] Browser connected: http://127.0.0.1:8000/ INFO - [23:48:21] Detected file changes INFO - Building documentation... Traceback (most recent call last): File "C:\Programming\materialtest\venv\Lib\site-packages\mkdocs\livereload\__init__.py", line 193, in _build_loop func() File "C:\Programming\materialtest\venv\Lib\site-packages\mkdocs\commands\serve.py", line 67, in builder build(config, live_server=None if is_clean else server, dirty=is_dirty) File "C:\Programming\materialtest\venv\Lib\site-packages\mkdocs\commands\build.py", line 277, in build config = config.plugins.on_config(config) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Programming\materialtest\venv\Lib\site-packages\mkdocs\plugins.py", line 527, in on_config return self.run_event('config', config) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Programming\materialtest\venv\Lib\site-packages\mkdocs\plugins.py", line 507, in run_event result = method(item, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^ File "C:\Programming\materialtest\venv\Lib\site-packages\material\plugins\social\plugin.py", line 160, in on_config self.font = self._load_font(config) ^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Programming\materialtest\venv\Lib\site-packages\material\plugins\social\plugin.py", line 462, in _load_font font[style] = self._resolve_font(name, style) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Programming\materialtest\venv\Lib\site-packages\material\plugins\social\plugin.py", line 476, in _resolve_font self._fetch_font_from_google_fonts(family) File "C:\Programming\materialtest\venv\Lib\site-packages\material\plugins\social\plugin.py", line 537, in _fetch_font_from_google_fonts font = ImageFont.truetype(temp.name) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Programming\materialtest\venv\Lib\site-packages\PIL\ImageFont.py", line 807, in truetype return freetype(font) ^^^^^^^^^^^^^^ File "C:\Programming\materialtest\venv\Lib\site-packages\PIL\ImageFont.py", line 804, in freetype return FreeTypeFont(font, size, index, encoding, layout_engine) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Programming\materialtest\venv\Lib\site-packages\PIL\ImageFont.py", line 244, in __init__ self.font = core.getfont( ^^^^^^^^^^^^^ OSError: cannot open resource

Related links

Reproduction

9.5.18-social-plugin-windows.zip

Steps to reproduce

  1. Add the following lines to minimal reproduction mkdocs.yml `plugins:
  • info
  • social
    `
  1. Run mkdocs serve

Browser

No response

Before submitting

@squidfunk squidfunk added the needs investigation Issue must be investigated by the maintainers label Apr 24, 2024
@squidfunk
Copy link
Owner

squidfunk commented Apr 24, 2024

Thanks for reporting. I'm not a Windows user, so any help is appreciated. Does your project have a .cache folder? Did the social plugin download the font files? It should look like this:

.cache/plugin/social/fonts
└── Roboto
    ├── Black Italic.ttf
    ├── Black.ttf
    ├── Bold Italic.ttf
    ├── Bold.ttf
    ├── Italic.ttf
    ├── Light Italic.ttf
    ├── Light.ttf
    ├── Medium Italic.ttf
    ├── Medium.ttf
    ├── Regular.ttf
    ├── Thin Italic.ttf
    └── Thin.ttf

It the font files are not there, the download or writing the files did not work. To be clear, the social plugin will download the files automatically, but if that failed for whatever reason or the files cannot be written because of permissions, it would explain the problem. If there is a .cache folder, please try deleting it and running the plugin again.

@squidfunk squidfunk changed the title Activating social plugin in windows causes crash when running mkdocs serve Social plugin: can't find fonts on Windows Apr 24, 2024
@alexvoss
Copy link
Sponsor Collaborator

alexvoss commented Apr 24, 2024

I am trying to reproduce the issue but am afraid I have not found a way yet to install the libcairo dependency on my Windows laptop. @Aazih, how did you install it? What version of Windows are you on? Should I bite the bullet and finally (up/down)grade to 11? I am using the GTK3+ runtime linked to from the troubleshooting guide. Even selected the installation into the lib directory, checked it is on the path and ran the diagnosis script. All seems fine. Edit: Ok, so the problems I was having seem to trace back to this known problem with loading DLLs. I might be able do get to the actual question soon...

@alexvoss
Copy link
Sponsor Collaborator

Ok, I managed to run the replication project and get an OSError as well, though without the stack trace for some reason. The .cache directory has the plugin/social subdirectories. There is a manifest.json but it contains an empty dict. No log messages about downloading fonts are generated. Will see if I can set up a development environment debug this.

@Aazih
Copy link
Author

Aazih commented Apr 24, 2024

Thanks for reporting. I'm not a Windows user, so any help is appreciated. Does your project have a .cache folder? Did the social plugin download the font files? It should look like this:

.cache/plugin/social/fonts
└── Roboto
    ├── Black Italic.ttf
    ├── Black.ttf
    ├── Bold Italic.ttf
    ├── Bold.ttf
    ├── Italic.ttf
    ├── Light Italic.ttf
    ├── Light.ttf
    ├── Medium Italic.ttf
    ├── Medium.ttf
    ├── Regular.ttf
    ├── Thin Italic.ttf
    └── Thin.ttf

It the font files are not there, the download or writing the files did not work. To be clear, the social plugin will download the files automatically, but if that failed for whatever reason or the files cannot be written because of permissions, it would explain the problem. If there is a .cache folder, please try deleting it and running the plugin again.

Initially:
image
So there is nothing in social, but windows has the ugly \ slash.

Deleting the .cache folder and doing serve again with plugins enabled.
image
Issue recreated. .cache, plugin, and social folders are created but nothing below that.

@Aazih
Copy link
Author

Aazih commented Apr 24, 2024

I am trying to reproduce the issue but am afraid I have not found a way yet to install the libcairo dependency on my Windows laptop. @Aazih, how did you install it? What version of Windows are you on? Should I bite the bullet and finally (up/down)grade to 11? I am using the GTK3+ runtime linked to from the troubleshooting guide. Even selected the installation into the lib directory, checked it is on the path and ran the diagnosis script. All seems fine. Edit: Ok, so the problems I was having seem to trace back to this known problem with loading DLLs. I might be able do get to the actual question soon...

Thank you. Just FYI I followed the following steps to get cairo up and running.

Social Card Steps

  1. Install Msys2
  2. Within Msys2 run pacman -S mingw-w64-x86_64-gtk4
  3. Add to Windows PATH C:\msys64\mingw64\bin\
  4. Within python virtual environment pip install "mkdocs-material[imaging]"

@alexvoss
Copy link
Sponsor Collaborator

Ok, so this issue occurs in both the public and the insiders version and traces to a call to ImageFont.truetype(temp.name). The argument is a string that seems to point to the correct (temporary) file.

I copied the file to another file while the mkdocs serve was paused in the debugger. Loaded the font I copied in a separate Python session and all was fine. When I tried to open the original file, with the temporary file still open in the process in the debugger, I got the OSError when using ImageFont.truetype.

Is it possible that Windows does not allow a temporary file to be opened again?

I wrote a small test script to see what would happen:

from tempfile import NamedTemporaryFile

with NamedTemporaryFile(mode='w') as file:
    file.write("HELLO")
    file.flush()
    fd = open(file.name, 'r')

Promptly got a

PermissionError: [Errno 13] Permission denied: 'C:\\Users\\ALEXVO~1\\AppData\\Local\\Temp\\tmpmhpwb1bt'

That is not the same as an OSError, so still looking at this.

@alexvoss
Copy link
Sponsor Collaborator

@Aazih thanks for the info about installing using MSYS2. I tried that in October but ran into various problems. Would be good if we could say this has become a reliable way of installing it.

@alexvoss
Copy link
Sponsor Collaborator

alexvoss commented Apr 24, 2024

Ok, it does look like this is a Windows-specific issue with NamedTemporaryFile: https://stackoverflow.com/questions/6416782/what-is-namedtemporaryfile-useful-for-on-windows

@squidfunk, this is in

font = ImageFont.truetype(temp.name)
in the public version. I tried to seek the open temporary file back to 0 and pass the file object to ImageFont.truetype() instead of the filename. That did the trick but the next problem is that copy_file also wants to open the file and fails - with a PermissionError. We'd need to do the same trick with that one, I guess? Let me know if you want me to try to implement this. It would save two file open operations and I don't see why it should not work on Unix-type operating systems.

@squidfunk
Copy link
Owner

Wow, thanks for gathering all the intel! I'm a little tight on time right now, but as I read we could just exchange the approach using NamedTemporaryFile to use a regular file to download and write the font and then just move it, assuming moving doesn't require any extra permissions. Or does that lead to the same problem with seek? If yes, we can seek back to 0.

@squidfunk
Copy link
Owner

Or wait no, let's go for seek for now, if that works 😅 Saving file open/close sys calls is probably a better idea.

@squidfunk squidfunk added bug Issue reports a bug and removed needs investigation Issue must be investigated by the maintainers labels Apr 25, 2024
@kamilkrzyskow
Copy link
Collaborator

kamilkrzyskow commented Apr 27, 2024

I used cairosvg from my PATH in Windows 10 and I believe Python 3.12 in March, had no issues @alexvoss

Even selected the installation into the lib directory, checked it is on the path and ran the diagnosis script
So I don't think I selected anything non-default like install to lib, in the GTK-Installer, and install to bin

Python 3.8 no longer searches for DLLs in PATH

I'm interested in the source, this claim is based on, and what caused it to change 🤔
CairoFFI still uses find_library:

And find_library on Windows still searches in PATH:

I can see you've resolved the issue, and did great work on the debug process Alex, but maybe you could check your ctypes/util.py in the Python interpreter directory to see how your find_library does look like?
I want to know if something is missing from the troubleshooting guide 🤔


As for the issue at hand can't we set delete=False like the StackOverFlow answer suggests, or is our issue at the temp.flush()?
Also the ImageFont.truetype should accept a file-like object, so perhaps the BytesIO(res.content) could work?
All untested ideas, food for thought.

@squidfunk
Copy link
Owner

@kamilkrzyskow @alexvoss happy for PRs on this, as I'm unable to reproduce it.

Also the ImageFont.truetype should accept a file-like object, so perhaps the BytesIO(res.content) could work?

Sounds really promising!

@alexvoss
Copy link
Sponsor Collaborator

Also the ImageFont.truetype should accept a file-like object, so perhaps the BytesIO(res.content) could work?

Aha, BytesIO might be what I was looking for. Hope it is possible toread the data more than once without copying to a buffer. We might be able to get rid of the whole NamedTemporaryFile thing. Will try a bit later on. Am still struggling with Windows as a development environment.

Thanks for the notes on DLLs. I will look at that as well later on and open a new issue if don't find I just made a silly mistake. First time in a long time I am using Windows for anything other than playing Minecraft with my daughter, so apologies in advance.

alexvoss added a commit to alexvoss/mkdocs-material-fork that referenced this issue Apr 27, 2024
@alexvoss
Copy link
Sponsor Collaborator

I created a pull request with code based on @kamilkrzyskow's suggestion of using ByteIO, which works just fine. I tested against the Material for MkDocs documentation as well as the reproduction posted by @Aazih.

Seems a nice rare case where fixing a problem that affects only one OS led to a reduction in code size as well as, presumably, a speedup since we are writing to only one file now. Will do the equivalent for the Insider Edition tomorrow - unless @squidfunk beats me to it or anyone spots a problem.

@squidfunk squidfunk linked a pull request Apr 28, 2024 that will close this issue
squidfunk pushed a commit that referenced this issue Apr 29, 2024
#7117)

* fix: social plugin fonts on Windows (squidfunk #7085)

* fix: managed to edit in material instead of src

* added resource mgmt for ByteIO, comments

* formatted comment

* Fix for Social plugin crashes when font autoloading is disabled (#7118)
@squidfunk
Copy link
Owner

Keeping open until released.

@squidfunk squidfunk reopened this Apr 29, 2024
@squidfunk squidfunk added the resolved Issue is resolved, yet unreleased if open label Apr 29, 2024
@squidfunk
Copy link
Owner

Released as part of 9.5.20.

@vedranmiletic
Copy link
Contributor

@alexvoss @Aazih tangentially related, on the topic of MSYS2: it doesn't actually require full GTK, installing just Cairo is enough. I posted a proposed documentation update in #7163.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Issue reports a bug resolved Issue is resolved, yet unreleased if open
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants