Add font fallback.

This commit is contained in:
Andre von Houck 2021-09-17 06:25:13 -07:00 committed by treeform
parent ce05442e2e
commit c0f31b276f
5 changed files with 55 additions and 18 deletions

View file

@ -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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -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")