handle control runes, newline

This commit is contained in:
Ryan Oldenburg 2021-05-05 19:45:13 -05:00
parent e5a463aaa3
commit 5c6725c4e3
5 changed files with 75 additions and 32 deletions

View file

@ -1,7 +1,10 @@
import bumpy, pixie/fontformats/opentype, pixie/fontformats/svgfont, pixie/paths,
unicode, vmath
const AutoLineHeight* = -1.float32 ## Use default line height for the font size
const
AutoLineHeight* = -1.float32 ## Use default line height for the font size
LF = Rune(10)
SP = Rune(32)
type
Typeface* = ref object
@ -58,10 +61,11 @@ proc lineGap*(typeface: Typeface): float32 {.inline.} =
proc getGlyphPath*(typeface: Typeface, rune: Rune): Path {.inline.} =
## The glyph path for the rune.
if typeface.opentype != nil:
typeface.opentype.getGlyphPath(rune)
else:
typeface.svgFont.getGlyphPath(rune)
if rune.uint32 > SP.uint32: # Empty paths for control runes (not tofu)
if typeface.opentype != nil:
result = typeface.opentype.getGlyphPath(rune)
else:
result = typeface.svgFont.getGlyphPath(rune)
proc getAdvance*(typeface: Typeface, rune: Rune): float32 {.inline.} =
## The advance for the rune in pixels.
@ -121,9 +125,20 @@ proc typeset*(
): Arrangement =
result = Arrangement()
result.font = font
result.runes = toRunes(text)
block: # Walk and filter runes
var
i = 0
rune: Rune
while i < text.len:
fastRuneAt(text, i, rune, true)
# Ignore control runes (0 - 31) except LF for now
if rune.uint32 >= SP.uint32 or rune.uint32 == LF.uint32:
result.runes.add(rune)
result.runes.convertTextCase(textCase)
result.positions.setLen(result.runes.len)
result.selectionRects.setLen(result.runes.len)
let lineHeight =
if font.lineheight >= 0:
@ -145,31 +160,38 @@ proc typeset*(
at.y = round((font.typeface.ascent + font.typeface.lineGap / 2) * font.scale)
at.y += (lineheight - font.defaultLineHeight) / 2
for i, rune in result.runes:
if rune.canWrap():
prevCanWrap = i
let advance = advance(font, result.runes, i, kerning)
if rune != Rune(32) and bounds.x > 0 and at.x + advance > bounds.x:
# Wrap to new line
if rune == LF:
result.positions[i] = at
at.x = 0
at.y += lineHeight
prevCanWrap = 0
else:
if rune.canWrap():
prevCanWrap = i
# Go back and wrap glyphs after the wrap index down to the next line
if prevCanWrap > 0 and prevCanWrap != i:
for j in prevCanWrap + 1 ..< i:
result.positions[j] = at
at.x += advance(font, result.runes, j, kerning)
let advance = advance(font, result.runes, i, kerning)
if rune != SP and bounds.x > 0 and at.x + advance > bounds.x:
# Wrap to new line
at.x = 0
at.y += lineHeight
result.positions[i] = at
at.x += advance
# Go back and wrap glyphs after the wrap index down to the next line
if prevCanWrap > 0 and prevCanWrap != i:
for j in prevCanWrap + 1 ..< i:
result.positions[j] = at
at.x += advance(font, result.runes, j, kerning)
result.positions[i] = at
at.x += advance
iterator paths*(arrangement: Arrangement): Path =
for i in 0 ..< arrangement.runes.len:
var path = arrangement.font.typeface.getGlyphPath(arrangement.runes[i])
path.transform(
translate(arrangement.positions[i]) * scale(vec2(arrangement.font.scale))
)
yield path
if arrangement.runes[i].uint32 > SP.uint32: # Don't draw control runes
var path = arrangement.font.typeface.getGlyphPath(arrangement.runes[i])
path.transform(
translate(arrangement.positions[i]) * scale(vec2(arrangement.font.scale))
)
yield path
proc parseOtf*(buf: string): Font =
result.typeface = Typeface()

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -1,5 +1,13 @@
import pixie, pixie/fileformats/png, strformat
proc doDiff(rendered: Image, name: string) =
rendered.writeFile(&"tests/fonts/rendered/{name}.png")
let
master = readImage(&"tests/fonts/masters/{name}.png")
(diffScore, diffImage) = diff(master, rendered)
echo &"{name} score: {diffScore}"
diffImage.writeFile(&"tests/fonts/diffs/{name}.png")
block:
var font = readFont("tests/fonts/Roboto-Regular_1.ttf")
font.size = 64
@ -65,14 +73,6 @@ block:
mask.fillText(font, "Ubuntu ")
writeFile("tests/fonts/svg_ubuntu.png", mask.encodePng())
proc doDiff(rendered: Image, name: string) =
rendered.writeFile(&"tests/fonts/rendered/{name}.png")
let
master = readImage(&"tests/fonts/masters/{name}.png")
(diffScore, diffImage) = diff(master, rendered)
echo &"{name} score: {diffScore}"
diffImage.writeFile(&"tests/fonts/diffs/{name}.png")
block:
var font = readFont("tests/fonts/Roboto-Regular_1.ttf")
font.size = 72
@ -512,3 +512,24 @@ block:
)
doDiff(image, "pairs3")
block:
var font = readFont("tests/fonts/Roboto-Regular_1.ttf")
font.size = 18
let image = newImage(200, 150)
image.fill(rgba(255, 255, 255, 255))
image.fillText(
font,
"""First line
Second line
Third line
Fourth line
Fifth line
Sixth line
Seventh line""",
rgba(0, 0, 0, 255),
bounds = image.wh
)
doDiff(image, "lines1")