From c0f31b276fa6a99bd3a7de8c1c82b6a8704315be Mon Sep 17 00:00:00 2001 From: Andre von Houck Date: Fri, 17 Sep 2021 06:25:13 -0700 Subject: [PATCH] Add font fallback. --- src/pixie/fonts.nim | 62 +++++++++++++++++++++--------- tests/fonts/diffs/fallback.png | Bin 0 -> 16027 bytes tests/fonts/masters/fallback.png | Bin 0 -> 8000 bytes tests/fonts/rendered/fallback.png | Bin 0 -> 16304 bytes tests/test_fonts.nim | 11 ++++++ 5 files changed, 55 insertions(+), 18 deletions(-) create mode 100644 tests/fonts/diffs/fallback.png create mode 100644 tests/fonts/masters/fallback.png create mode 100644 tests/fonts/rendered/fallback.png diff --git a/src/pixie/fonts.nim b/src/pixie/fonts.nim index f2d019a..ec7d585 100644 --- a/src/pixie/fonts.nim +++ b/src/pixie/fonts.nim @@ -12,6 +12,7 @@ type opentype: OpenType svgFont: SvgFont filePath*: string + fallbacks*: seq[Typeface] Font* = ref object typeface*: Typeface @@ -53,6 +54,13 @@ type # tcSmallCaps # tcSmallCapsForced +proc scale*(typeface: Typeface): float32 {.inline, raises: [].} = + ## The scale factor to transform font units into pixels. + if typeface.opentype != nil: + typeface.opentype.head.unitsPerEm.float32 + else: + typeface.svgFont.unitsPerEm + proc ascent*(typeface: Typeface): float32 {.raises: [].} = ## The font ascender value in font units. if typeface.opentype != nil: @@ -97,17 +105,6 @@ proc isCCW(typeface: Typeface): bool {.inline.} = if typeface.opentype != nil: return typeface.opentype.isCCW() -proc getGlyphPath*( - typeface: Typeface, rune: Rune -): Path {.inline, raises: [PixieError].} = - ## The glyph path for the rune. - result = newPath() - if rune.uint32 > SP.uint32: # Empty paths for control runes (not tofu) - if typeface.opentype != nil: - result.addPath(typeface.opentype.getGlyphPath(rune)) - else: - result.addPath(typeface.svgFont.getGlyphPath(rune)) - proc hasGlyph*(typeface: Typeface, rune: Rune): bool {.inline.} = ## Returns if there is a glyph for this rune. if rune.uint32 > SP.uint32: # Empty paths for control runes (not tofu) @@ -118,12 +115,44 @@ proc hasGlyph*(typeface: Typeface, rune: Rune): bool {.inline.} = else: false +proc getGlyphPath*( + typeface: Typeface, rune: Rune +): Path {.inline, raises: [PixieError].} = + ## The glyph path for the rune. + result = newPath() + if rune.uint32 > SP.uint32: # Empty paths for control runes (not tofu) + if typeface.hasGlyph(rune): + if typeface.opentype != nil: + result.addPath(typeface.opentype.getGlyphPath(rune)) + else: + result.addPath(typeface.svgFont.getGlyphPath(rune)) + else: + for fallback in typeface.fallbacks: + if fallback.hasGlyph(rune): + result = fallback.getGlyphPath(rune) + let ratio = typeface.scale / fallback.scale + result.transform(scale(vec2(ratio, ratio))) + proc getAdvance*(typeface: Typeface, rune: Rune): float32 {.inline, raises: [].} = ## The advance for the rune in pixels. - if typeface.opentype != nil: - typeface.opentype.getAdvance(rune) + if typeface.hasGlyph(rune): + if typeface.opentype != nil: + return typeface.opentype.getAdvance(rune) + else: + return typeface.svgFont.getAdvance(rune) else: - typeface.svgFont.getAdvance(rune) + for fallback in typeface.fallbacks: + if fallback.hasGlyph(rune): + result = fallback.getAdvance(rune) + let ratio = typeface.scale / fallback.scale + result *= ratio + return + + if typeface.opentype != nil: + return typeface.opentype.getAdvance(rune) + else: + return typeface.svgFont.getAdvance(rune) + proc getKerningAdjustment*( typeface: Typeface, left, right: Rune @@ -136,10 +165,7 @@ proc getKerningAdjustment*( proc scale*(font: Font): float32 {.inline, raises: [].} = ## The scale factor to transform font units into pixels. - if font.typeface.opentype != nil: - font.size / font.typeface.opentype.head.unitsPerEm.float32 - else: - font.size / font.typeface.svgFont.unitsPerEm + font.size / font.typeface.scale proc defaultLineHeight*(font: Font): float32 {.inline, raises: [].} = ## The default line height in pixels for the current font size. diff --git a/tests/fonts/diffs/fallback.png b/tests/fonts/diffs/fallback.png new file mode 100644 index 0000000000000000000000000000000000000000..80016e6835db274b2312b6c844f1ce7f1d828ebf GIT binary patch literal 16027 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
u3(1D@m-GRlwLyk%$Ov> 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 NnVVP{*BU;K{XejuqM85z literal 0 HcmV?d00001 diff --git a/tests/fonts/rendered/fallback.png b/tests/fonts/rendered/fallback.png new file mode 100644 index 0000000000000000000000000000000000000000..a200ca0c7aadcc354eb60d391caa15218d016789 GIT binary patch literal 16304 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 literal 0 HcmV?d00001 diff --git a/tests/test_fonts.nim b/tests/test_fonts.nim index b4a34e3..b583d59 100644 --- a/tests/test_fonts.nim +++ b/tests/test_fonts.nim @@ -1057,3 +1057,14 @@ block: image.fillText(font, "Grumpy wizards make toxic brew for the evil Queen and Jack.") doDiff(image, "cff_strikethrough") + +block: + var font = readFont("tests/fonts/Inter-Regular.ttf") + var typeface = readTypeface("tests/fonts/NotoSansJP-Regular.ttf") + font.typeface.fallbacks.add(typeface) + font.size = 26 + let image = newImage(800, 100) + image.fill(rgba(255, 255, 255, 255)) + image.fillText(font, "Grumpy ウィザード make 有毒な醸造 for the 悪い女王 and Jack.") + + doDiff(image, "fallback")