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

Add support for CBDT and COLR fonts #4955

Merged
merged 13 commits into from Oct 12, 2020
Merged

Conversation

nulano
Copy link
Contributor

@nulano nulano commented Oct 7, 2020

Proper fix for #891, partial support for #3346

Changes proposed in this pull request:

  • properly detect glyph bitmap mode instead of guessing
  • use FT_Convert_Bitmap instead of a custom solution
  • add support for Google CBDT (if FreeType is compiled with libpng) and Microsoft COLR fonts

Apple SBIX and W3C SVG fonts are not yet supported by FreeType.

Example:

from PIL import Image, ImageDraw, ImageFont

def test(font, out_name):
    fnt = ImageFont.truetype(font, size=109, layout_engine=ImageFont.LAYOUT_RAQM)
    im = Image.new("RGBA", (600, 600), (100, 100, 100, 100))
    draw = ImageDraw.Draw(im)
    draw.text((0, 32), "a\u263A", fill="#faa2", embedded_color=True, font=fnt)
    draw.text((0, 132), "a\u263A", fill="#faa8", embedded_color=True, font=fnt)
    draw.text((0, 232), "a\u263A", fill="#faa", embedded_color=True, font=fnt)
    draw.text((0, 332), "\U0001F3AC\U0001F44B\U0001F3FB\U0001F44B\U0001F3FF", fill="white", embedded_color=True, font=fnt)
    draw.text((0, 432), "a\u263A", fill="#faa2", font=fnt)
    im.show()
    im.save(f"testemoji_{out_name}.png")

test(r"C:\Users\Nulano\Downloads\NotoColorEmoji.ttf", "cbdt")
test("seguiemj.ttf", "colr")

NotoColorEmoji.ttf (CBDT):
testemoji_cbdt

seguiemj.ttf (COLR):
testemoji_colr

Tests/test_imagefont_bitmap.py Outdated Show resolved Hide resolved
@nulano
Copy link
Contributor Author

nulano commented Oct 7, 2020

Big-endian s390 is failing, but I'm not sure I can fix it without seeing the output image.

@hugovk
Copy link
Member

hugovk commented Oct 7, 2020

This increases the size of the sdist by about 20% from 58M to 69M.

Are all these fonts necessary, or can we still be confident in testing with existing fonts (I guess they're not CBDT or COLR), or just one of the smaller additions?

@nulano
Copy link
Contributor Author

nulano commented Oct 7, 2020

This increases the size of the sdist by about 20% from 58M to 69M.

Are all these fonts necessary, or can we still be confident in testing with existing fonts (I guess they're not CBDT or COLR), or just one of the smaller additions?

DejaVuSans-24-*.ttf (6MB total) can be replaced by a different font or a cut-down version with just a few glyphs for testing.
Edit: replaced by stripped down versions (keeping ASCII range only) with ~60KB each.

The biggest chunk is the 9MB CBDT font NotoColorEmoji.ttf; CBDT fonts are essentially just a collection of bitmaps (uncompressed or PNG), so any font will be large. If the license allows it, stripping out all except the tested bitmaps might be possible.
Edit: FontForge can't open it so I'm not sure how I would strip it down. It might be easier to recompile it from source keeping only the one glyph that is tested.

BungeeColor-Regular_colr_Windows.ttf (COLR font) is just 73.6 KB.

@radarhere
Copy link
Member

Big-endian s390 is failing, but I'm not sure I can fix it without seeing the output image.

Here you go - generated with this commit forked from your branch.
standard_embedded_fail

@nulano
Copy link
Contributor Author

nulano commented Oct 7, 2020

Big-endian s390 is failing, but I'm not sure I can fix it without seeing the output image.

Here you go - generated with this commit forked from your branch.
standard_embedded_fail

Ah, I had the right idea, but forgot long long is 8 bytes...

Thanks for the help.

@nulano
Copy link
Contributor Author

nulano commented Oct 7, 2020

This increases the size of the sdist by about 20% from 58M to 69M.

Are all these fonts necessary, or can we still be confident in testing with existing fonts (I guess they're not CBDT or COLR), or just one of the smaller additions?

I've stripped down the DejaVuSans-24-*.ttf fonts to ASCII and used an older version of NotoColorEmoji.ttf (5.1MB). Is this sufficient?
I can't find any other CBDT fonts or a simple way to edit them.

@hugovk
Copy link
Member

hugovk commented Oct 11, 2020

Looks like we also need the setuptools workaround for 3.8 (like #4958) for Windows/GHA:

RuntimeWarning: The _imaging extension was built for another version of Pillow or PIL:
Core version: None
Pillow version: 8.0.0.dev0
Traceback (most recent call last):
  File "selftest.py", line 6, in <module>
    from PIL import Image, features
  File "<frozen zipimport>", line 259, in load_module
  File "C:\hostedtoolcache\windows\Python\3.8.6\x86\lib\site-packages\pillow-8.0.0.dev0-py3.8-win32.egg\PIL\Image.py", line 97, in <module>
ImportError: The _imaging extension was built for another version of Pillow or PIL:
Core version: None
Pillow version: 8.0.0.dev0
Error: Process completed with exit code 1.

https://github.com/python-pillow/Pillow/pull/4955/checks?check_run_id=1239105264

@nulano
Copy link
Contributor Author

nulano commented Oct 11, 2020

#4958 already includes 3.8, this branch just needs to be rebased or merge master (I think re-running the checks should suffice)

Tests/test_imagefont.py Outdated Show resolved Hide resolved
Tests/test_imagefont.py Outdated Show resolved Hide resolved
Tests/test_imagefont.py Outdated Show resolved Hide resolved
if parse_version(features.version_module("freetype2")) < parse_version(
"2.10.0"
):
pytest.skip("Freetype 2.10.0 or newer required")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's quite a few Freetype version check and skips, let's put them into a helper, for example:

def skip_unless_freetype(required_version, message):

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went with the following to let the check be reusable for other features:

def skip_unless_feature_version(feature, version_required, reason=None):

Tests/test_imagefont_bitmap.py Outdated Show resolved Hide resolved
docs/reference/ImageDraw.rst Outdated Show resolved Hide resolved
@nulano
Copy link
Contributor Author

nulano commented Oct 11, 2020

Removing test_imagefont_bitmap.test_similar (in favor of the new test test_imagefont.test_bitmap_font) also allows removing 0.9MB DejaVuSans-bitmap.ttf.

@hugovk hugovk merged commit 43c3f4d into python-pillow:master Oct 12, 2020
@hugovk
Copy link
Member

hugovk commented Oct 12, 2020

Thank you! Please can you add release notes for this, plus for any of your other PRs since the last release?

@NightMachinery
Copy link

Apple font is fine on my system, FYI.
image

Is there a way to use one font for emojis and another for the normal text?

@nulano
Copy link
Contributor Author

nulano commented Mar 14, 2021

Apple font is fine on my system, FYI.

Yep, figured that out a few weeks after this PR (#5155).

Is there a way to use one font for emojis and another for the normal text?

Not automatically, see #4808. If you are fine with splitting up the text yourself, you can use the approach outlined here: #3346 (comment).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants