-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
Perform font fallback #6926
base: main
Are you sure you want to change the base?
Perform font fallback #6926
Conversation
src/_imagingft.c
Outdated
switch (anchor[1]) { | ||
case 'a': // ascender | ||
y_anchor = PIXEL(self->face->size->metrics.ascender); | ||
y_anchor = PIXEL(family->faces[0]->size->metrics.ascender); | ||
break; | ||
case 't': // top | ||
y_anchor = y_max; | ||
break; | ||
case 'm': // middle (ascender + descender) / 2 | ||
y_anchor = PIXEL( | ||
(self->face->size->metrics.ascender + | ||
self->face->size->metrics.descender) / | ||
(family->faces[0]->size->metrics.ascender + | ||
family->faces[0]->size->metrics.descender) / | ||
2); | ||
break; | ||
case 's': // horizontal baseline |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think here you would need to get the ascenders and descenders of every font used on this line, not just the first one, and use the greatest value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps. I don't think looking just at the fonts used in a given line would be good - that could create an inconsistent layout for the text_multiline
functions.
This should use either the first font's metrics or the greatest value across all fonts in a family object, and it should be consistent with family.getmetrics()
(not yet implemented).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've just tested what MS Edge (i.e. Chromium) does - it uses the metrics of all fonts up to the last one that is in use for a given line, even if it causes uneven line spacing.
For example, with three fonts, a paragraph that only uses the second font will have line spacing computed from the first two fonts. If one word in the paragraph then uses the third font, that line will include the third font's line spacing, but the other lines are unaffected.
This is incompatible with how Pillow currently calculates line spacing for multiline text (see #6469 (comment)), so I think I'll just use the maximum over all fonts (i.e. assume that if a user creates a FontFamily
object they actually want to use all of the fonts), and leave fixing it for #1646 (+document the limitation).
} | ||
} | ||
/* prefer first font's missing glyph if no font support this codepoint */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/* prefer first font's missing glyph if no font support this codepoint */ | |
/* prefer first font's missing glyph if no font supports this codepoint */ |
@@ -209,6 +222,177 @@ getfont(PyObject *self_, PyObject *args, PyObject *kw) { | |||
return (PyObject *)self; | |||
} | |||
|
|||
static PyObject * | |||
getfamily(PyObject *self_, PyObject *args, PyObject *kw) { | |||
/* create a font family object from a list of file names and a sizes (in pixels) */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/* create a font family object from a list of file names and a sizes (in pixels) */ | |
/* create a font family object from a list of file names and sizes (in pixels) */ |
(*glyph_info)[i].x_offset = 0; | ||
(*glyph_info)[i].y_offset = 0; | ||
|
||
/* This has been broken and had no effect for many years now... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you expand on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How to download the module or the build with below module? AttributeError: module 'PIL.ImageFont' has no attribute 'FreeTypeFontFamily' |
You can use a source install from the branch https://github.com/nulano/Pillow/archive/refs/heads/font-fallback.zip using the installation instructions: https://pillow.readthedocs.io/en/stable/installation.html#building-from-source The previous Windows dev build has expired so I've re-run the CI for this branch here: https://github.com/nulano/Pillow/actions/runs/5568846705 |
Thank you. |
If I want to use below method to draw text for each char. is it slower than fontfamily? Iterate all chacters in a string, |
Hi. Is there any progress? |
@Mitchell-kw-Lee Looks like this is still a WIP, are you able to test the branch and report results? That may help … |
This needs a pretty large rebase before it can cleanly merge with the main branch. Currently, I see #6926 (comment) as the biggest unresolved issue with this PR. I think I have an idea that could work, but I've not had time to work on this. However, if you are able to test this PR as is (even though it is made for an older version of Pillow) and report whether it works / causes issues, it could perhaps be helpful. |
@nulano @aclark4life Errr, actually have no idea that the test process. Zero knowledge of git cowork test and manual installation stuff. And there is running environment that may impact own work. |
As I wrote above, this branch has a non-trivial merge conflict I haven't had time to resolve, even though I would like to get to it at some point. If you'd like (keep in mind this is based on Pillow from a year ago), I can re-run the CI to generate new Windows wheels for easier installation. If you are on Linux/macOS, you'll need to figure out source installation (hint: https://pillow.readthedocs.io/en/stable/installation.html#building-from-source) |
@nulano Ah? You are going to give me aa package that can be installed by a command 'python -m pip install pillow==TESTVERSION --upgrade --user' or so? Then lets do it. I'll test it by applying piilow code in my code. And I can do feedback.(I'm running on Windows 11 64bit BUT Python 3.11.7- 32BIT, beware please.) |
I've rerun the CI: https://github.com/nulano/Pillow/actions/runs/7765749734 Example usage is at the top of this page (click on |
@nulano okay, after fix version conflict, I have got the installation on my system now. BUT will comback early next week. Give me sometime. |
@nulano When I call the FreeTypeFontFamily I got the error <class 'AttributeError'>'FreeTypeFontFamily' object has no attribute 'getsize'. (Fortunately, there is no 'No FreeTypeFontFamily method found' stuff. |
FreeTypeFontFamily doesn't have |
@radarhere I do not catch. Or you don't catch the whole situation. |
The error above came from the line |
Cannot see download link for windows. Does this wheel 10.3.0 support fontfamily? |
fontfamily (font1 (include some chars)+font2(include most chars)) cannot show some characters font_file0: ballpen.zip. font_file1: tianshiyanti2.0.ttf.zip str = 'をも資资儲储議议歷历權权個个TextInAaBbCcDdEeFfGgHhIi family PILLOW' image = Image.new('RGB', (image_width=1000, image_height=25), color='white')
draw = ImageDraw.Draw(image)
font0 = ImageFont.truetype(font_file0, size=20)
font1 = ImageFont.truetype(font_file1, size=20)
font_family = ImageFont.FreeTypeFontFamily(font0, font1)
draw.text((0, 0), textstr, fill='black', font=font_family) |
If you scroll down on that page, you will see dist-windows-x86, dist-windows-ARM64 and dist-windows-AMD64. Yes, those wheels would support |
I can download from your above link, but I don't see such link on list. How to find above links? What is difference between [dist-windows-x86] and [dist-windows-AMD64]? (https://github.com/nulano/Pillow/actions/runs/8583137967/artifacts/1391023653). |
No, except multiline text seems to work now.
Go to https://github.com/nulano/Pillow/actions/runs/8583137967 and scroll down: |
Saw it. Thank you. There is still below problem from yesterday. font_file0: ballpen.zip. font_file1: tianshiyanti2.0.ttf.zip str = 'をも資资儲储議议歷历權权個个TextInAaBbCcDdEeFfGgHhIi family PILLOW' image = Image.new('RGB', (image_width=1000, image_height=25), color='white')
draw = ImageDraw.Draw(image)
font0 = ImageFont.truetype(font_file0, size=20)
font1 = ImageFont.truetype(font_file1, size=20)
font_family = ImageFont.FreeTypeFontFamily(font0, font1)
draw.text((0, 0), textstr, fill='black', font=font_family) |
Same as for regular text: draw.text((0, 0), "line1\nline2", font=family, ...)
I'll take a look at it when I have the time. |
I wonder whether there is any function to import html to pillow canvas, like rendering a web page. |
This is off-topic for this discussion of multiple fonts, but no, Pillow doesn't have any functionality for rendering HTML to an image. |
Would it be possible to get a |
Sure, I didn't bother for a WIP PR when I wasn't sure it would be useful, but it's quite simple (literally just copy-pasted from You should be able to download wheels from this workflow in a few hours: https://github.com/nulano/Pillow/actions/runs/8774721560 |
Hi, the builds are expired. Could you please re run? I'm fairly new to this, so I don't know if there's another way to get the build |
…-missing glyph in a single cluster
… instead of last font's missing glyph
for more information, see https://pre-commit.ci
I've rebased the PR and triggered a build, you should see the wheels here in a few hours: https://github.com/nulano/Pillow/actions/runs/10547427520 |
Fixes #4808.
Add a new type,
ImageFont.FreeTypeFontFamily(font1, font2, ..., layout_engine=layout_engine)
, that can be used withImageDraw.text*(...)
functions performing font fallback. Font fallback is done per cluster with Raqm layout (similar to Chromium) and per codepoint with basic layout.This PR is far from complete, several TODOs:
ImageFont.truetype(...)
, perhapsImageFont.truetype_family(...)
?I would like to get some feedback, both on the API and the implementation, before working on the TODOs above.
A dev build for Windows is available from the artifact here: https://github.com/nulano/Pillow/actions/runs/8583137967
A few examples (click to expand):
All examples use this helper block:
Combining Latin, symbols, and an emoji:
Combining Arabic, Greek, Latin, and a symbol:
Combining characters are treated as part of a single cluster (with Raqm layout):
Raqm layout:
Basic layout:
The string
s
contains: