Better fallback

This commit is contained in:
treeform 2021-10-01 10:43:10 -07:00
parent 8a88cd29fa
commit a8e1943556
2 changed files with 67 additions and 30 deletions

View file

@ -112,51 +112,65 @@ proc hasGlyph*(typeface: Typeface, rune: Rune): bool {.inline.} =
else:
typeface.svgFont.hasGlyph(rune)
proc fallbackTypeface*(typeface: Typeface, rune: Rune): Typeface =
## Looks through fallback typefaces to find one that has the glyph.
if typeface.hasGlyph(rune):
return typeface
for fallback in typeface.fallbacks:
let typeface = fallback.fallbackTypeface(rune)
if typeface != nil:
return typeface
proc getGlyphPath*(
typeface: Typeface, rune: Rune
): Path {.inline, raises: [PixieError].} =
## The glyph path for the rune.
result = newPath()
if typeface.hasGlyph(rune):
if typeface.opentype != nil:
result.addPath(typeface.opentype.getGlyphPath(rune))
else:
result.addPath(typeface.svgFont.getGlyphPath(rune))
let typeface2 = typeface.fallbackTypeface(rune)
if typeface2 == nil:
return
if typeface2.opentype != nil:
result.addPath(typeface2.opentype.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)))
result.addPath(typeface2.svgFont.getGlyphPath(rune))
# Apply typeface ratio.
let ratio = typeface.scale / typeface2.scale
if ratio != 1.0:
result.transform(scale(vec2(ratio, ratio)))
proc getAdvance*(typeface: Typeface, rune: Rune): float32 {.inline, raises: [].} =
## The advance for the rune in pixels.
if typeface.hasGlyph(rune):
if typeface.opentype != nil:
return typeface.opentype.getAdvance(rune)
else:
return typeface.svgFont.getAdvance(rune)
else:
for fallback in typeface.fallbacks:
if fallback.hasGlyph(rune):
result = fallback.getAdvance(rune)
let ratio = typeface.scale / fallback.scale
result *= ratio
return
let typeface2 = typeface.fallbackTypeface(rune)
if typeface2 == nil:
return
if typeface.opentype != nil:
return typeface.opentype.getAdvance(rune)
else:
return typeface.svgFont.getAdvance(rune)
if typeface2.opentype != nil:
result = typeface2.opentype.getAdvance(rune)
else:
result = typeface2.svgFont.getAdvance(rune)
# Apply typeface ratio.
result *= typeface.scale / typeface2.scale
proc getKerningAdjustment*(
typeface: Typeface, left, right: Rune
): float32 {.inline, raises: [].} =
## The kerning adjustment for the rune pair, in pixels.
if typeface.opentype != nil:
typeface.opentype.getKerningAdjustment(left, right)
else:
typeface.svgfont.getKerningAdjustment(left, right)
let
typefaceRight = typeface.fallbackTypeface(right)
typefaceLeft = typeface.fallbackTypeface(left)
# Only do kerning if both typefaces are the same.
if typefaceRight == typefaceLeft:
if typefaceRight.opentype != nil:
result = typefaceRight.opentype.getKerningAdjustment(left, right)
else:
result = typefaceRight.svgfont.getKerningAdjustment(left, right)
# Apply typeface ratio.
result *= typeface.scale / typefaceRight.scale
proc scale*(font: Font): float32 {.inline, raises: [].} =
## The scale factor to transform font units into pixels.

View file

@ -1068,3 +1068,26 @@ block:
image.fillText(font, "Grumpy ウィザード make 有毒な醸造 for the 悪い女王 and Jack.")
doDiff(image, "fallback")
block:
let
font = readFont("tests/fonts/Inter-Regular.ttf")
typeface1 = readTypeface("tests/fonts/Aclonica-Regular_1.ttf")
typeface2 = readTypeface("tests/fonts/Ubuntu-Regular_1.ttf")
typeface3 = readTypeface("tests/fonts/NotoSansJP-Regular.ttf")
# font
# |.... typeface1
# |.... typeface2
# |.... typeface3 (with JP)
font.typeface.fallbacks.add(typeface1)
typeface1.fallbacks.add(typeface2)
typeface1.fallbacks.add(typeface3)
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, "fallback2")