diff --git a/src/crossglyph/cpfont/convert.py b/src/crossglyph/cpfont/convert.py index 1c4b4c8..624d7ac 100644 --- a/src/crossglyph/cpfont/convert.py +++ b/src/crossglyph/cpfont/convert.py @@ -388,6 +388,7 @@ def resolve_style_coverage(primary_face, fallback_faces, intervals, "all_glyphs", # [(GlyphProps, packed_bytes), ...] "total_bitmap_size", # int "advanceY", "ascender", "descender", + "max_ink_top", # int: tallest ink above the baseline in this style "kern_left_classes", "kern_right_classes", "kern_matrix", "kern_left_class_count", "kern_right_class_count", "ligature_pairs", @@ -1382,6 +1383,14 @@ def load_glyph_for_face(target_face, code_point, face_index=0): total_bitmap_size += len(packed) all_glyphs.append((glyph, packed)) + # Tallest ink above the baseline, over every glyph that has ink. A reader + # that places a page's first line from it can keep tall marks, such as + # Vietnamese double accents, off the screen edge. Empty glyphs have width + # and height 0, so they cannot raise it; a style with no ink reports 0, + # which a reader takes as "unknown". + max_ink_top = max((g.top for g, _ in all_glyphs if g.width and g.height), + default=0) + # Get font metrics from the primary font. This keeps line metrics stable for # the selected family even when a fallback contributes some glyphs. load_glyph_for_face(face, ord('|')) @@ -1473,6 +1482,7 @@ def load_glyph_for_face(target_face, code_point, face_index=0): advanceY=advanceY, ascender=ascender, descender=descender, + max_ink_top=max_ink_top, kern_left_classes=kern_left_classes, kern_right_classes=kern_right_classes, kern_matrix=kern_matrix, @@ -1604,8 +1614,13 @@ def generate_cpfont_multistyle(style_fonts, size, intervals, output_path, # Build style TOC entries # Each entry: styleId(1) + pad(3) + intervalCount(4) + glyphCount(4) + # advanceY(1) + ascender(2) + descender(2) + kernL(2) + kernR(2) + - # kernLCls(1) + kernRCls(1) + ligCount(1) + dataOffset(4) + reserved(4) = 32 - STYLE_TOC_FORMAT = " 0 + assert struct.unpack_from("