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: crash when all fonts are prefixed with variant #6928

Closed
4 tasks done
mitches-got-glitches opened this issue Mar 18, 2024 · 8 comments
Closed
4 tasks done
Labels
bug Issue reports a bug resolved Issue is resolved, yet unreleased if open

Comments

@mitches-got-glitches
Copy link

Context

I'm writing a personal blog with the Blog plugin and I'm trying to generate socials cards with the socials plugin.

Running mkdocs-material[imaging]==9.5.14 with imaging dependencies installed.

Bug description

I get the following traceback when trying to build social cards using the Nunito Sans Google font:

Traceback (most recent call last):
  File "/home/mitch/.pyenv/versions/3.11.4/bin/mkdocs", line 8, in <module>
    sys.exit(cli())
             ^^^^^
  File "/home/mitch/.pyenv/versions/3.11.4/lib/python3.11/site-packages/click/core.py", line 1157, in __call__
    return self.main(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/mitch/.pyenv/versions/3.11.4/lib/python3.11/site-packages/click/core.py", line 1078, in main
    rv = self.invoke(ctx)
         ^^^^^^^^^^^^^^^^
  File "/home/mitch/.pyenv/versions/3.11.4/lib/python3.11/site-packages/click/core.py", line 1688, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/mitch/.pyenv/versions/3.11.4/lib/python3.11/site-packages/click/core.py", line 1434, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/mitch/.pyenv/versions/3.11.4/lib/python3.11/site-packages/click/core.py", line 783, in invoke
    return __callback(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/mitch/.pyenv/versions/3.11.4/lib/python3.11/site-packages/mkdocs/__main__.py", line 286, in build_command
    build.build(cfg, dirty=not clean)
  File "/home/mitch/.pyenv/versions/3.11.4/lib/python3.11/site-packages/mkdocs/commands/build.py", line 354, in build
    config.plugins.on_post_build(config=config)
  File "/home/mitch/.pyenv/versions/3.11.4/lib/python3.11/site-packages/mkdocs/plugins.py", line 542, in on_post_build
    return self.run_event('post_build', config=config)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/mitch/.pyenv/versions/3.11.4/lib/python3.11/site-packages/mkdocs/plugins.py", line 509, in run_event
    result = method(**kwargs)
             ^^^^^^^^^^^^^^^^
  File "/home/mitch/.pyenv/versions/3.11.4/lib/python3.11/site-packages/material/plugins/social/plugin.py", line 211, in on_post_build
    promise.result()
  File "/home/mitch/.pyenv/versions/3.11.4/lib/python3.11/concurrent/futures/_base.py", line 449, in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
  File "/home/mitch/.pyenv/versions/3.11.4/lib/python3.11/concurrent/futures/_base.py", line 401, in __get_result
    raise self._exception
  File "/home/mitch/.pyenv/versions/3.11.4/lib/python3.11/concurrent/futures/thread.py", line 58, in run
    result = self.fn(*self.args, **self.kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/mitch/.pyenv/versions/3.11.4/lib/python3.11/site-packages/material/plugins/social/plugin.py", line 218, in _cache_image
    image = render_function()
            ^^^^^^^^^^^^^^^^^
  File "/home/mitch/.pyenv/versions/3.11.4/lib/python3.11/site-packages/material/plugins/social/plugin.py", line 198, in <lambda>
    render_function = lambda: self._render_card(site_name, title, description)
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/mitch/.pyenv/versions/3.11.4/lib/python3.11/site-packages/material/plugins/social/plugin.py", line 238, in _render_card
    font = self._get_font("Bold", 36)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/mitch/.pyenv/versions/3.11.4/lib/python3.11/site-packages/material/plugins/social/plugin.py", line 226, in _get_font
    return ImageFont.truetype(self.font[kind], size)
                              ~~~~~~~~~^^^^^^
  File "/home/mitch/.pyenv/versions/3.11.4/lib/python3.11/site-packages/material/plugins/social/plugin.py", line 464, in <lambda>
    return defaultdict(lambda: font["Regular"], font)
                               ~~~~^^^^^^^^^^^
KeyError: 'Regular'

The custom font I am using is called Nunito Sans, specified in mkdocs.yml like so:

theme:
  font:
    text: Nunito Sans

Building the socials cards works with the default font, and with another custom font (Lato). I think it has something do with how this particular font is downloaded. Rather than having unpacked .tff files under .cache/plugin/social, most of them are instead stored in .cache/plugin/social/static/. See below for the difference in the cache file tree:

image

Therefore the selection of the "Regular" key isn't working because the only two files at the top level are:

  • NunitoSans-Italic-VariableFont_YTLC,opsz,wdth,wght.tff
  • NunitoSans-VariableFont_YTLC,opsz,wdth,wght.tff

I'm no expert in fonts but it seems to be a difference in the way static and variable fonts are handled that's causing this bug.

Strongly linked to #5517 and also #5201. There must be a difference in the way Nunito Sans and Open Sans are configured - maybe you have some insights @croesus as the author of the fix to #5517?

Related links

Reproduction

9.5.14-socials-breaks-with-font.zip

Steps to reproduce

Using the makefile on Linux...

  1. make all (installs imaging dependencies and requirements into a virtual environment
  2. Remove - info from the plugins in mkdocs.yml
  3. Activate venv with source .venv/bin/activate
  4. make docs

Browser

No response

Before submitting

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

Thanks for reporting. I can reproduce the problem. It does not work with the community edition, because the font management implementation is very basic. We've already made font management more resilient in Insiders as part of the latest rewrite, but there still seems to be a problem with this particular font: it seems to have another level of variants. If you run this on Insiders, you get the following files that the plugin has trouble resolving correctly:

.cache/plugin/social/fonts
└── Nunito Sans
    ├── 10pt Black Italic.ttf
    ├── 10pt Black.ttf
    ├── 10pt Bold Italic.ttf
    ├── 10pt Bold.ttf
    ├── 10pt Condensed Black Italic.ttf
    ├── 10pt Condensed Black.ttf
    ├── 10pt Condensed Bold Italic.ttf
    ├── 10pt Condensed Bold.ttf
    ├── 10pt Condensed ExtraBold Italic.ttf
    ├── 10pt Condensed ExtraBold.ttf
    ├── 10pt Condensed ExtraLight Italic.ttf
    ├── 10pt Condensed ExtraLight.ttf
    ├── 10pt Condensed Italic.ttf
    ├── 10pt Condensed Light Italic.ttf
    ├── 10pt Condensed Light.ttf
    ├── 10pt Condensed Medium Italic.ttf
    ├── 10pt Condensed Medium.ttf
    ├── 10pt Condensed Regular.ttf
    ├── 10pt Condensed SemiBold Italic.ttf
    ├── 10pt Condensed SemiBold.ttf
    ├── 10pt Expanded Black Italic.ttf
    ├── 10pt Expanded Black.ttf
    ├── 10pt Expanded Bold Italic.ttf
    ├── 10pt Expanded Bold.ttf
    ├── 10pt Expanded ExtraBold Italic.ttf
    ├── 10pt Expanded ExtraBold.ttf
    ├── 10pt Expanded ExtraLight Italic.ttf
    ├── 10pt Expanded ExtraLight.ttf
    ├── 10pt Expanded Italic.ttf
    ├── 10pt Expanded Light Italic.ttf
    ├── 10pt Expanded Light.ttf
    ├── 10pt Expanded Medium Italic.ttf
    ├── 10pt Expanded Medium.ttf
    ├── 10pt Expanded Regular.ttf
    ├── 10pt Expanded SemiBold Italic.ttf
    ├── 10pt Expanded SemiBold.ttf
    ├── 10pt ExtraBold Italic.ttf
    ├── 10pt ExtraBold.ttf
    ├── 10pt ExtraLight Italic.ttf
    ├── 10pt ExtraLight.ttf
    ├── 10pt Italic.ttf
    ├── 10pt Light Italic.ttf
    ├── 10pt Light.ttf
    ├── 10pt Medium Italic.ttf
    ├── 10pt Medium.ttf
    ├── 10pt Regular.ttf
    ├── 10pt SemiBold Italic.ttf
    ├── 10pt SemiBold.ttf
    ├── 10pt SemiCondensed Black Italic.ttf
    ├── 10pt SemiCondensed Black.ttf
    ├── 10pt SemiCondensed Bold Italic.ttf
    ├── 10pt SemiCondensed Bold.ttf
    ├── 10pt SemiCondensed ExtraBold Italic.ttf
    ├── 10pt SemiCondensed ExtraBold.ttf
    ├── 10pt SemiCondensed ExtraLight Italic.ttf
    ├── 10pt SemiCondensed ExtraLight.ttf
    ├── 10pt SemiCondensed Italic.ttf
    ├── 10pt SemiCondensed Light Italic.ttf
    ├── 10pt SemiCondensed Light.ttf
    ├── 10pt SemiCondensed Medium Italic.ttf
    ├── 10pt SemiCondensed Medium.ttf
    ├── 10pt SemiCondensed Regular.ttf
    ├── 10pt SemiCondensed SemiBold Italic.ttf
    ├── 10pt SemiCondensed SemiBold.ttf
    ├── 10pt SemiExpanded Black Italic.ttf
    ├── 10pt SemiExpanded Black.ttf
    ├── 10pt SemiExpanded Bold Italic.ttf
    ├── 10pt SemiExpanded Bold.ttf
    ├── 10pt SemiExpanded ExtraBold Italic.ttf
    ├── 10pt SemiExpanded ExtraBold.ttf
    ├── 10pt SemiExpanded ExtraLight Italic.ttf
    ├── 10pt SemiExpanded ExtraLight.ttf
    ├── 10pt SemiExpanded Italic.ttf
    ├── 10pt SemiExpanded Light Italic.ttf
    ├── 10pt SemiExpanded Light.ttf
    ├── 10pt SemiExpanded Medium Italic.ttf
    ├── 10pt SemiExpanded Medium.ttf
    ├── 10pt SemiExpanded Regular.ttf
    ├── 10pt SemiExpanded SemiBold Italic.ttf
    ├── 10pt SemiExpanded SemiBold.ttf
    ├── 12pt ExtraLight Italic.ttf
    ├── 12pt ExtraLight.ttf
    ├── 7pt Black Italic.ttf
    ├── 7pt Black.ttf
    ├── 7pt Bold Italic.ttf
    ├── 7pt Bold.ttf
    ├── 7pt Condensed Black Italic.ttf
    ├── 7pt Condensed Black.ttf
    ├── 7pt Condensed Bold Italic.ttf
    ├── 7pt Condensed Bold.ttf
    ├── 7pt Condensed ExtraBold Italic.ttf
    ├── 7pt Condensed ExtraBold.ttf
    ├── 7pt Condensed ExtraLight Italic.ttf
    ├── 7pt Condensed ExtraLight.ttf
    ├── 7pt Condensed Italic.ttf
    ├── 7pt Condensed Light Italic.ttf
    ├── 7pt Condensed Light.ttf
    ├── 7pt Condensed Medium Italic.ttf
    ├── 7pt Condensed Medium.ttf
    ├── 7pt Condensed Regular.ttf
    ├── 7pt Condensed SemiBold Italic.ttf
    ├── 7pt Condensed SemiBold.ttf
    ├── 7pt Expanded Black Italic.ttf
    ├── 7pt Expanded Black.ttf
    ├── 7pt Expanded Bold Italic.ttf
    ├── 7pt Expanded Bold.ttf
    ├── 7pt Expanded ExtraBold Italic.ttf
    ├── 7pt Expanded ExtraBold.ttf
    ├── 7pt Expanded ExtraLight Italic.ttf
    ├── 7pt Expanded ExtraLight.ttf
    ├── 7pt Expanded Italic.ttf
    ├── 7pt Expanded Light Italic.ttf
    ├── 7pt Expanded Light.ttf
    ├── 7pt Expanded Medium Italic.ttf
    ├── 7pt Expanded Medium.ttf
    ├── 7pt Expanded Regular.ttf
    ├── 7pt Expanded SemiBold Italic.ttf
    ├── 7pt Expanded SemiBold.ttf
    ├── 7pt ExtraBold Italic.ttf
    ├── 7pt ExtraBold.ttf
    ├── 7pt ExtraLight Italic.ttf
    ├── 7pt ExtraLight.ttf
    ├── 7pt Italic.ttf
    ├── 7pt Light Italic.ttf
    ├── 7pt Light.ttf
    ├── 7pt Medium Italic.ttf
    ├── 7pt Medium.ttf
    ├── 7pt Regular.ttf
    ├── 7pt SemiBold Italic.ttf
    ├── 7pt SemiBold.ttf
    ├── 7pt SemiCondensed Black Italic.ttf
    ├── 7pt SemiCondensed Black.ttf
    ├── 7pt SemiCondensed Bold Italic.ttf
    ├── 7pt SemiCondensed Bold.ttf
    ├── 7pt SemiCondensed ExtraBold Italic.ttf
    ├── 7pt SemiCondensed ExtraBold.ttf
    ├── 7pt SemiCondensed ExtraLight Italic.ttf
    ├── 7pt SemiCondensed ExtraLight.ttf
    ├── 7pt SemiCondensed Italic.ttf
    ├── 7pt SemiCondensed Light Italic.ttf
    ├── 7pt SemiCondensed Light.ttf
    ├── 7pt SemiCondensed Medium Italic.ttf
    ├── 7pt SemiCondensed Medium.ttf
    ├── 7pt SemiCondensed Regular.ttf
    ├── 7pt SemiCondensed SemiBold Italic.ttf
    ├── 7pt SemiCondensed SemiBold.ttf
    ├── 7pt SemiExpanded Black Italic.ttf
    ├── 7pt SemiExpanded Black.ttf
    ├── 7pt SemiExpanded Bold Italic.ttf
    ├── 7pt SemiExpanded Bold.ttf
    ├── 7pt SemiExpanded ExtraBold Italic.ttf
    ├── 7pt SemiExpanded ExtraBold.ttf
    ├── 7pt SemiExpanded ExtraLight Italic.ttf
    ├── 7pt SemiExpanded ExtraLight.ttf
    ├── 7pt SemiExpanded Italic.ttf
    ├── 7pt SemiExpanded Light Italic.ttf
    ├── 7pt SemiExpanded Light.ttf
    ├── 7pt SemiExpanded Medium Italic.ttf
    ├── 7pt SemiExpanded Medium.ttf
    ├── 7pt SemiExpanded Regular.ttf
    ├── 7pt SemiExpanded SemiBold Italic.ttf
    └── 7pt SemiExpanded SemiBold.ttf

The plugin tries to resolve Regular.ttf (by default) but can't, since all regular variants are prefixed with *pt. In order to allow the author to fix this problem (as the plugin itself cannot decide), we should add another option to all default layouts that we might call font_variant. Currently, the following is used to render a layer (taken from the default layout):

layers:
  ...
  # Page description
  - size: { width: 832, height: 64 }
    offset: { x: 64, y: 512 }
    typography:
      content: *page_description
      align: start
      color: *color
      line:
        amount: 2
        height: 1.5
      font:
        family: *font_family
        style: Regular
        # style: "{{ layout.font_variant }} Regular"

Now, the author would be able to set the variant in mkdocs.yml:

plugins:
  - social:
      cards_layout_options:
        font_variant: 7pt

This should fix the problem. Please understand that this fix will only be part of Insiders and not of the community edition, because the entire layout system has been rewritten and backporting this fix is quite impossible without releasing the entire code base right now. If you or somebody else wants to craft a PR to fix this in the community edition, we're happy to consider merging it. However, the code base of the social plugin is going to be entirely replaced in the near future when the funding goal is hit, so I'm not sure it's worth the effort.

@squidfunk squidfunk added bug Issue reports a bug and removed needs investigation Issue must be investigated by the maintainers labels Mar 19, 2024
@squidfunk
Copy link
Owner

squidfunk commented Mar 19, 2024

A simple mitigation is to use another font for generating social cards – social cards by default use the default font you specify via theme.font.text, but it can be overridden with the font_family layout option:

plugins:
  - social:
      cards_layout_options:
        font_family: Roboto

@squidfunk squidfunk changed the title Socials plugin breaks with Nunito Sans variable font that installs in .cache/plugin/social/static/ Social plugin: crash when all fonts are prefixed with variant Mar 19, 2024
@squidfunk
Copy link
Owner

Oh, and thanks for the excellent issue and reproduction! One can easily see that a lot of time went into this – this made isolating the problem and finding a fix straight forward 🚀

@mitches-got-glitches
Copy link
Author

mitches-got-glitches commented Mar 19, 2024 via email

@mitches-got-glitches
Copy link
Author

Oh, and thanks for the excellent issue and reproduction! One can easily see that a lot of time went into this – this made isolating the problem and finding a fix straight forward 🚀

No problem, thanks! Great guide to put it together and the zipping tool is pretty handy.

@squidfunk
Copy link
Owner

squidfunk commented Mar 20, 2024

Fixed in 0d48a6b (Insiders). There's now a new layout property called font.variant which can be used in custom layouts. Additionally, all included layouts contain a new exposed option called font_variant:

plugins:
  - social:
      cards_layout_options:
        font_variant: 7pt

Since theme.font.text is set to Nunito Sans, this will now download the font and instruct the social plugin to use the following font files:

.cache/plugin/social/fonts
└── Nunito Sans
    ├── 7pt Black Italic.ttf
    ├── 7pt Black.ttf
    ├── 7pt Bold Italic.ttf
    ├── 7pt Bold.ttf
    ├── 7pt Italic.ttf
    ├── 7pt Light Italic.ttf
    ├── 7pt Light.ttf
    ├── 7pt Medium Italic.ttf
    ├── 7pt Medium.ttf
    ├── 7pt Regular.ttf
    ├── 7pt SemiBold Italic.ttf
    └── 7pt SemiBold.ttf

The layouts that we ship with Material for MkDocs will now correctly pick 7pt Bold.ttf and 7pt Regular.ttf for rendering. You can now easily change to another style, e.g. SemiCondensed, like so:

plugins:
  - social:
      cards_layout_options:
        font_variant: 10pt SemiCondensed

Additionally, resilience was improved in that if the font variant/style cannot be found, the font will not only try to look for Regular.ttf, but for the shortest possible file that contains "Regular", which is "7pt Regular.ttf". When debug is set to true, a warning will be printed that this font was used as a fallback.

This is still not perfect, but I think we should've considerably improved the font handling logic, making it more resilient and hopefully easier to work with. I'm really looking forward to hitting the funding goals to release all of this work into the larger community ☺️

We still need to add documentation for this.

@squidfunk squidfunk added the resolved Issue is resolved, yet unreleased if open label Mar 20, 2024
@squidfunk
Copy link
Owner

squidfunk commented Mar 20, 2024

Documentation added in 8addc95. We will improve the social card guide in the future and better explain how custom layouts can be built, also adding tutorials and other resources, but for the time being this feature is documented. Inspecting the default layouts is a great idea to learn how to build custom layouts.

@squidfunk
Copy link
Owner

Released as part of 9.5.15+insiders-4.53.3.

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

No branches or pull requests

2 participants