Skip to content

Commit

Permalink
Use a new strategy to retrieve font content
Browse files Browse the repository at this point in the history
This way seems to work everywhere, including strange Windows computers.

Fix #2079.
  • Loading branch information
liZe committed Apr 24, 2024
1 parent e901ff7 commit 3a208fe
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 34 deletions.
24 changes: 8 additions & 16 deletions weasyprint/draw.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
from .layout.background import BackgroundLayer
from .matrix import Matrix
from .stacking import StackingContext
from .text.ffi import ffi, harfbuzz, pango, units_from_double, units_to_double
from .text.ffi import ffi, pango, units_from_double, units_to_double
from .text.fonts import get_hb_face_blob_data, get_pango_font_hb_face
from .text.line_break import get_last_word_end

SIDES = ('top', 'right', 'bottom', 'left')
Expand Down Expand Up @@ -1232,14 +1233,9 @@ def draw_first_line(stream, textbox, text_overflow, block_ellipsis, matrix):
previous_utf8_position = utf8_position

if font.svg:
hb_font = pango.pango_font_get_hb_font(pango_font)
hb_face = harfbuzz.hb_font_get_face(hb_font)
hb_blob = ffi.gc(
harfbuzz.hb_ot_color_glyph_reference_svg(hb_face, glyph),
harfbuzz.hb_blob_destroy)
hb_data = harfbuzz.hb_blob_get_data(hb_blob, stream.length)
if hb_data != ffi.NULL:
svg_data = ffi.unpack(hb_data, int(stream.length[0]))
hb_face = get_pango_font_hb_face(pango_font)
svg_data = get_hb_face_blob_data(hb_face, ot_color='svg')
if svg_data:
# Do as explained in specification
# https://learn.microsoft.com/typography/opentype/spec/svg
tree = ElementTree.fromstring(svg_data)
Expand All @@ -1254,13 +1250,9 @@ def draw_first_line(stream, textbox, text_overflow, block_ellipsis, matrix):
a = d = font.widths[glyph] / 1000 / font.upem * font_size
emojis.append([image, font, a, d, x_advance, 0])
elif font.png:
hb_font = pango.pango_font_get_hb_font(pango_font)
hb_blob = ffi.gc(
harfbuzz.hb_ot_color_glyph_reference_png(hb_font, glyph),
harfbuzz.hb_blob_destroy)
hb_data = harfbuzz.hb_blob_get_data(hb_blob, stream.length)
if hb_data != ffi.NULL:
png_data = ffi.unpack(hb_data, int(stream.length[0]))
hb_face = get_pango_font_hb_face(pango_font)
png_data = get_hb_face_blob_data(hb_face, ot_color='png')
if png_data:
pillow_image = Image.open(BytesIO(png_data))
image_id = f'{font.hash}{glyph}'
image = RasterImage(pillow_image, image_id, png_data)
Expand Down
21 changes: 7 additions & 14 deletions weasyprint/pdf/stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,13 @@
from ..matrix import Matrix
from ..text.constants import PANGO_STRETCH_PERCENT
from ..text.ffi import ffi, harfbuzz, pango, units_to_double
from ..text.fonts import get_hb_face_blob_data, get_pango_font_hb_face


class Font:
def __init__(self, pango_font):
hb_font = pango.pango_font_get_hb_font(pango_font)
hb_face = harfbuzz.hb_font_get_face(hb_font)
hb_blob = ffi.gc(
harfbuzz.hb_face_reference_blob(hb_face),
harfbuzz.hb_blob_destroy)
with ffi.new('unsigned int *') as length:
hb_data = harfbuzz.hb_blob_get_data(hb_blob, length)
self.file_content = ffi.unpack(hb_data, int(length[0]))
self.index = harfbuzz.hb_face_get_index(hb_face)
hb_face = get_pango_font_hb_face(pango_font)
self.file_content = get_hb_face_blob_data(hb_face)

pango_metrics = pango.pango_font_get_metrics(pango_font, ffi.NULL)
self.description = description = ffi.gc(
Expand Down Expand Up @@ -88,18 +82,17 @@ def __init__(self, pango_font):

# Fonttools
full_font = io.BytesIO(self.file_content)
index = harfbuzz.hb_face_get_index(hb_face)
try:
self.ttfont = TTFont(full_font, fontNumber=self.index)
self.ttfont = TTFont(full_font, fontNumber=index)
except Exception:
LOGGER.warning('Unable to read font')
self.ttfont = None
self.bitmap = False
else:
self.bitmap = (
'EBDT' in self.ttfont and
'EBLC' in self.ttfont and (
'glyf' not in self.ttfont or
not self.ttfont['glyf'].glyphs))
'EBDT' in self.ttfont and 'EBLC' in self.ttfont and (
'glyf' not in self.ttfont or not self.ttfont['glyf'].glyphs))

# Various properties
self.italic_angle = 0 # TODO: this should be different
Expand Down
7 changes: 4 additions & 3 deletions weasyprint/text/ffi.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
typedef ... hb_face_t;
typedef ... hb_blob_t;
typedef uint32_t hb_codepoint_t;
hb_face_t * hb_font_get_face (hb_font_t *font);
hb_blob_t * hb_face_reference_blob (hb_face_t *face);
unsigned int hb_face_get_index (const hb_face_t *face);
unsigned int hb_face_get_upem (const hb_face_t *face);
Expand Down Expand Up @@ -226,12 +225,11 @@
PangoLayoutLine * pango_layout_get_line_readonly (
PangoLayout *layout, int line);
hb_font_t * pango_font_get_hb_font (PangoFont *font);
PangoFontDescription * pango_font_description_new (void);
void pango_font_description_free (PangoFontDescription *desc);
PangoFontDescription * pango_font_description_copy (
const PangoFontDescription *desc);
PangoFontMap* pango_font_get_font_map (PangoFont* font);
void pango_font_description_set_family (
PangoFontDescription *desc, const char *family);
Expand Down Expand Up @@ -390,12 +388,15 @@
// PangoFT2
typedef ... PangoFcFont;
typedef ... PangoFcFontMap;
PangoFontMap * pango_ft2_font_map_new (void);
void pango_fc_font_map_set_config (
PangoFcFontMap *fcfontmap, FcConfig *fcconfig);
void pango_fc_font_map_config_changed (PangoFcFontMap *fcfontmap);
hb_face_t* pango_fc_font_map_get_hb_face (
PangoFcFontMap* fcfontmap, PangoFcFont* fcfont);
''')


Expand Down
31 changes: 30 additions & 1 deletion weasyprint/text/fonts.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
CAPS_KEYS, EAST_ASIAN_KEYS, FONTCONFIG_STRETCH, FONTCONFIG_STYLE,
FONTCONFIG_WEIGHT, LIGATURE_KEYS, NUMERIC_KEYS, PANGO_STRETCH, PANGO_STYLE)
from .ffi import ( # isort:skip
ffi, fontconfig, gobject, pango, pangoft2, unicode_to_char_p,
ffi, fontconfig, gobject, harfbuzz, pango, pangoft2, unicode_to_char_p,
units_from_double)


Expand Down Expand Up @@ -333,6 +333,7 @@ def font_features(font_kerning='normal', font_variant_ligatures='normal',


def get_font_description(style):
"""Get font description string out of given style."""
font_description = ffi.gc(
pango.pango_font_description_new(),
pango.pango_font_description_free)
Expand All @@ -353,3 +354,31 @@ def get_font_description(style):
pango.pango_font_description_set_variations(
font_description, string)
return font_description


def get_pango_font_hb_face(pango_font):
"""Get Harfbuzz face out of given Pango font."""
fc_font = ffi.cast('PangoFcFont *', pango_font)
fontmap = ffi.cast(
'PangoFcFontMap *', pango.pango_font_get_font_map(pango_font))
return pangoft2.pango_fc_font_map_get_hb_face(fontmap, fc_font)


def get_hb_face_blob_data(hb_face, ot_color=None):
"""Get binary data out of given Harfbuzz face.
If ``ot_color`` is 'svg', return the SVG color glyph reference. If it’s 'png',
return the PNG color glyph reference. Otherwise, return the whole face blob.
"""
if ot_color == 'png':
function = harfbuzz.hb_ot_color_glyph_reference_svg
elif ot_color == 'svg':
function = harfbuzz.hb_ot_color_glyph_reference_png
else:
function = harfbuzz.hb_face_reference_blob
hb_blob = ffi.gc(function(hb_face), harfbuzz.hb_blob_destroy)
with ffi.new('unsigned int *') as length:
hb_data = harfbuzz.hb_blob_get_data(hb_blob, length)
if hb_data != ffi.NULL:
return ffi.unpack(hb_data, int(length[0]))

0 comments on commit 3a208fe

Please sign in to comment.