spans
|
@ -326,6 +326,25 @@ proc strokePolygon*(
|
|||
path.polygon(pos, size, sides)
|
||||
mask.strokePath(path, strokeWidth)
|
||||
|
||||
proc fillText*(
|
||||
target: Image | Mask,
|
||||
arrangement: Arrangement,
|
||||
transform: Vec2 | Mat3 = vec2(0, 0)
|
||||
) =
|
||||
## Fills the text arrangement.
|
||||
for spanIndex, (start, stop) in arrangement.spans:
|
||||
let font = arrangement.fonts[spanIndex]
|
||||
for runeIndex in start .. stop:
|
||||
var path = font.typeface.getGlyphPath(arrangement.runes[runeIndex])
|
||||
path.transform(
|
||||
translate(arrangement.positions[runeIndex]) *
|
||||
scale(vec2(font.scale))
|
||||
)
|
||||
when type(target) is Image:
|
||||
target.fillPath(path, font.paint, transform)
|
||||
else: # target is Mask
|
||||
target.fillPath(path, transform)
|
||||
|
||||
proc fillText*(
|
||||
target: Image | Mask,
|
||||
font: Font,
|
||||
|
@ -334,18 +353,33 @@ proc fillText*(
|
|||
bounds = vec2(0, 0),
|
||||
hAlign = haLeft,
|
||||
vAlign = vaTop
|
||||
) =
|
||||
) {.inline.} =
|
||||
## Typesets and fills the text. Optional parameters:
|
||||
## transform: translation or matrix to apply
|
||||
## bounds: width determines wrapping and hAlign, height for vAlign
|
||||
## hAlign: horizontal alignment of the text
|
||||
## vAlign: vertical alignment of the text
|
||||
let arrangement = font.typeset(text, bounds, hAlign, vAlign)
|
||||
for i in 0 ..< arrangement.runes.len:
|
||||
when type(target) is Image:
|
||||
target.fillPath(arrangement.getPath(i), font.paint, transform)
|
||||
else: # target is Mask
|
||||
target.fillPath(arrangement.getPath(i), transform)
|
||||
fillText(target, font.typeset(text, bounds, hAlign, vAlign), transform)
|
||||
|
||||
proc strokeText*(
|
||||
target: Image | Mask,
|
||||
arrangement: Arrangement,
|
||||
transform: Vec2 | Mat3 = vec2(0, 0),
|
||||
strokeWidth = 1.0
|
||||
) =
|
||||
## Strokes the text arrangement.
|
||||
for spanIndex, (start, stop) in arrangement.spans:
|
||||
let font = arrangement.fonts[spanIndex]
|
||||
for runeIndex in start .. stop:
|
||||
var path = font.typeface.getGlyphPath(arrangement.runes[runeIndex])
|
||||
path.transform(
|
||||
translate(arrangement.positions[runeIndex]) *
|
||||
scale(vec2(font.scale))
|
||||
)
|
||||
when type(target) is Image:
|
||||
target.strokePath(path, font.paint, transform, strokeWidth)
|
||||
else: # target is Mask
|
||||
target.strokePath(path, transform, strokeWidth)
|
||||
|
||||
proc strokeText*(
|
||||
target: Image | Mask,
|
||||
|
@ -356,17 +390,15 @@ proc strokeText*(
|
|||
bounds = vec2(0, 0),
|
||||
hAlign = haLeft,
|
||||
vAlign = vaTop
|
||||
) =
|
||||
) {.inline.} =
|
||||
## Typesets and strokes the text. Optional parameters:
|
||||
## transform: translation or matrix to apply
|
||||
## bounds: width determines wrapping and hAlign, height for vAlign
|
||||
## hAlign: horizontal alignment of the text
|
||||
## vAlign: vertical alignment of the text
|
||||
let arrangement = font.typeset(text, bounds, hAlign, vAlign)
|
||||
for i in 0 ..< arrangement.runes.len:
|
||||
when type(target) is Image:
|
||||
target.strokePath(
|
||||
arrangement.getPath(i), font.paint, transform, strokeWidth
|
||||
)
|
||||
else: # target is Mask
|
||||
target.strokePath(arrangement.getPath(i), transform, strokeWidth)
|
||||
strokeText(
|
||||
target,
|
||||
font.typeset(text, bounds, hAlign, vAlign),
|
||||
transform,
|
||||
strokeWidth
|
||||
)
|
||||
|
|
|
@ -19,8 +19,13 @@ type
|
|||
textCase*: TextCase
|
||||
noKerningAdjustments*: bool ## Optionally disable kerning pair adjustments
|
||||
|
||||
Arrangement* = ref object
|
||||
Span* = ref object
|
||||
text*: string
|
||||
font*: Font
|
||||
|
||||
Arrangement* = ref object
|
||||
spans*: seq[(int, int)]
|
||||
fonts*: seq[Font]
|
||||
runes*: seq[Rune]
|
||||
positions*: seq[Vec2]
|
||||
selectionRects*: seq[Rect]
|
||||
|
@ -62,6 +67,10 @@ proc lineGap*(typeface: Typeface): float32 {.inline.} =
|
|||
if typeface.opentype != nil:
|
||||
result = typeface.opentype.hhea.lineGap.float32
|
||||
|
||||
proc lineHeight*(typeface: Typeface): float32 {.inline.} =
|
||||
## The default line height in font units.
|
||||
typeface.ascent - typeface.descent + typeface.lineGap
|
||||
|
||||
proc getGlyphPath*(typeface: Typeface, rune: Rune): Path {.inline.} =
|
||||
## The glyph path for the rune.
|
||||
if rune.uint32 > SP.uint32: # Empty paths for control runes (not tofu)
|
||||
|
@ -99,6 +108,11 @@ proc defaultLineHeight*(font: Font): float32 {.inline.} =
|
|||
font.typeface.ascent - font.typeface.descent + font.typeface.lineGap
|
||||
round(fontUnits * font.scale)
|
||||
|
||||
proc newSpan*(text: string, font: Font): Span =
|
||||
result = Span()
|
||||
result.text = text
|
||||
result.font = font
|
||||
|
||||
proc convertTextCase(runes: var seq[Rune], textCase: TextCase) =
|
||||
case textCase:
|
||||
of tcNormal:
|
||||
|
@ -120,8 +134,7 @@ proc canWrap(rune: Rune): bool {.inline.} =
|
|||
rune == Rune(32) or rune.isWhiteSpace()
|
||||
|
||||
proc typeset*(
|
||||
font: Font,
|
||||
text: string,
|
||||
spans: seq[Span],
|
||||
bounds = vec2(0, 0),
|
||||
hAlign = haLeft,
|
||||
vAlign = vaTop,
|
||||
|
@ -133,133 +146,222 @@ proc typeset*(
|
|||
## hAlign: horizontal alignment of the text
|
||||
## vAlign: vertical alignment of the text
|
||||
## wrap: enable/disable text wrapping
|
||||
|
||||
result = Arrangement()
|
||||
result.font = font
|
||||
|
||||
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)
|
||||
block: # Walk and filter the spans
|
||||
var start: int
|
||||
for span in spans:
|
||||
var
|
||||
i = 0
|
||||
rune: Rune
|
||||
runes: seq[Rune]
|
||||
while i < span.text.len:
|
||||
fastRuneAt(span.text, i, rune, true)
|
||||
# Ignore control runes (0 - 31) except LF for now
|
||||
if rune.uint32 >= SP.uint32 or rune.uint32 == LF.uint32:
|
||||
runes.add(rune)
|
||||
|
||||
if result.runes.len == 0:
|
||||
# No runes to typeset, early return
|
||||
return
|
||||
if runes.len > 0:
|
||||
runes.convertTextCase(span.font.textCase)
|
||||
result.runes.add(runes)
|
||||
result.spans.add((start, start + runes.len - 1))
|
||||
result.fonts.add(span.font)
|
||||
start += runes.len
|
||||
|
||||
result.runes.convertTextCase(font.textCase)
|
||||
result.positions.setLen(result.runes.len)
|
||||
result.selectionRects.setLen(result.runes.len)
|
||||
|
||||
let lineHeight =
|
||||
if font.lineheight >= 0:
|
||||
font.lineheight
|
||||
else:
|
||||
font.defaultLineHeight
|
||||
|
||||
proc advance(font: Font, runes: seq[Rune], i: int): float32 {.inline.} =
|
||||
if not font.noKerningAdjustments and i + 1 < runes.len:
|
||||
result += font.typeface.getKerningAdjustment(runes[i], runes[i + 1])
|
||||
result += font.typeface.getAdvance(runes[i])
|
||||
result *= font.scale
|
||||
|
||||
var fontUnitInitialY = font.typeface.ascent + font.typeface.lineGap / 2
|
||||
if lineHeight != font.defaultLineHeight:
|
||||
fontUnitInitialY += (
|
||||
(lineHeight / font.scale) -
|
||||
(font.typeface.ascent - font.typeface.descent + font.typeface.lineGap)
|
||||
) / 2
|
||||
let initialY = round(fontUnitInitialY * font.scale)
|
||||
var lines = @[(0, 0)] # (start, stop)
|
||||
|
||||
var
|
||||
at: Vec2
|
||||
prevCanWrap: int
|
||||
at.y = initialY
|
||||
for i, rune in result.runes:
|
||||
if rune == LF:
|
||||
let advance = font.typeface.getAdvance(SP) * font.scale
|
||||
result.positions[i] = at
|
||||
result.selectionRects[i] = rect(at.x, at.y - initialY, advance, lineHeight)
|
||||
at.x = 0
|
||||
at.y += lineHeight
|
||||
prevCanWrap = 0
|
||||
else:
|
||||
if rune.canWrap():
|
||||
prevCanWrap = i
|
||||
|
||||
let advance = advance(font, result.runes, i)
|
||||
if wrap and rune != SP and bounds.x > 0 and at.x + advance > bounds.x:
|
||||
# Wrap to new line
|
||||
at.x = 0
|
||||
at.y += lineHeight
|
||||
|
||||
# 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
|
||||
result.selectionRects[j].xy = vec2(at.x, at.y - initialY)
|
||||
at.x += advance(font, result.runes, j)
|
||||
|
||||
result.positions[i] = at
|
||||
result.selectionRects[i] = rect(at.x, at.y - initialY, advance, lineHeight)
|
||||
at.x += advance
|
||||
|
||||
if hAlign != haLeft:
|
||||
# Since horizontal alignment adjustments are different for each line,
|
||||
# find the start and stop of each line of text.
|
||||
block: # Arrange the glyphs horizontally first (handling line breaks)
|
||||
var
|
||||
lines: seq[(uint32, uint32)] # (start, stop)
|
||||
start: uint32
|
||||
prevY = result.positions[0].y
|
||||
for i, pos in result.positions:
|
||||
if pos.y != prevY:
|
||||
lines.add((start, i.uint32 - 1))
|
||||
start = i.uint32
|
||||
prevY = pos.y
|
||||
lines.add((start, result.positions.len.uint32 - 1))
|
||||
at: Vec2
|
||||
prevCanWrap: int
|
||||
for spanIndex, (start, stop) in result.spans:
|
||||
let font = result.fonts[spanIndex]
|
||||
for runeIndex in start .. stop:
|
||||
let rune = result.runes[runeIndex]
|
||||
if rune == LF:
|
||||
let advance = font.typeface.getAdvance(SP) * font.scale
|
||||
result.positions[runeIndex] = at
|
||||
result.selectionRects[runeIndex] = rect(at.x, at.y, advance, 0)
|
||||
at.x = 0
|
||||
at.y += 1
|
||||
prevCanWrap = 0
|
||||
lines[^1][1] = runeIndex
|
||||
lines.add((runeIndex + 1, 0))
|
||||
else:
|
||||
if rune.canWrap():
|
||||
prevCanWrap = runeIndex
|
||||
|
||||
for (start, stop) in lines:
|
||||
var furthestX: float32
|
||||
for i in countdown(stop, start):
|
||||
if result.runes[i] != SP and result.runes[i] != LF:
|
||||
furthestX = result.selectionRects[i].x + result.selectionRects[i].w
|
||||
break
|
||||
let advance = advance(font, result.runes, runeIndex)
|
||||
if wrap and rune != SP and bounds.x > 0 and at.x + advance > bounds.x:
|
||||
# Wrap to new line
|
||||
at.x = 0
|
||||
at.y += 1
|
||||
|
||||
var xAdjustment: float32
|
||||
case hAlign:
|
||||
of haLeft:
|
||||
var lineStart = runeIndex
|
||||
|
||||
# Go back and wrap glyphs after the wrap index down to the next line
|
||||
if prevCanWrap > 0 and prevCanWrap != runeIndex:
|
||||
for i in prevCanWrap + 1 ..< runeIndex:
|
||||
result.positions[i] = at
|
||||
result.selectionRects[i].xy = vec2(at.x, at.y)
|
||||
at.x += advance(font, result.runes, i)
|
||||
dec lineStart
|
||||
|
||||
lines[^1][1] = lineStart - 1
|
||||
lines.add((lineStart, 0))
|
||||
|
||||
result.positions[runeIndex] = at
|
||||
result.selectionRects[runeIndex] = rect(at.x, at.y, advance, 0)
|
||||
at.x += advance
|
||||
|
||||
lines[^1][1] = result.runes.len - 1
|
||||
|
||||
if hAlign != haLeft:
|
||||
# Since horizontal alignment adjustments are different for each line,
|
||||
# find the start and stop of each line of text.
|
||||
for (start, stop) in lines:
|
||||
var furthestX: float32
|
||||
for i in countdown(stop, start):
|
||||
if result.runes[i] != SP and result.runes[i] != LF:
|
||||
furthestX = result.selectionRects[i].x + result.selectionRects[i].w
|
||||
break
|
||||
|
||||
var xAdjustment: float32
|
||||
case hAlign:
|
||||
of haLeft:
|
||||
discard
|
||||
of haCenter:
|
||||
xAdjustment = (bounds.x - furthestX) / 2
|
||||
of haRight:
|
||||
xAdjustment = bounds.x - furthestX
|
||||
|
||||
if xAdjustment != 0:
|
||||
for i in start .. stop:
|
||||
result.positions[i].x += xAdjustment
|
||||
result.selectionRects[i].x += xAdjustment
|
||||
|
||||
block: # Nudge selection rects to pixel grid
|
||||
var at = result.selectionRects[0]
|
||||
at.x = round(at.x)
|
||||
for rect in result.selectionRects.mitems:
|
||||
if rect.y == at.y:
|
||||
rect.x = at.x
|
||||
rect.w = round(rect.w)
|
||||
at.x = rect.x + rect.w
|
||||
else:
|
||||
rect.w = round(rect.w)
|
||||
at.x = rect.w
|
||||
at.y = rect.y
|
||||
|
||||
block: # Arrange the lines vertically
|
||||
let initialY = block:
|
||||
var maxInitialY: float32
|
||||
block outer:
|
||||
for spanIndex, (start, stop) in result.spans:
|
||||
let
|
||||
font = result.fonts[spanIndex]
|
||||
lineHeight =
|
||||
if font.lineheight >= 0:
|
||||
font.lineheight
|
||||
else:
|
||||
font.defaultLineHeight
|
||||
var fontUnitInitialY = font.typeface.ascent + font.typeface.lineGap / 2
|
||||
if lineHeight != font.defaultLineHeight:
|
||||
fontUnitInitialY += (
|
||||
(lineHeight / font.scale) - font.typeface.lineHeight
|
||||
) / 2
|
||||
maxInitialY = max(maxInitialY, round(fontUnitInitialY * font.scale))
|
||||
for runeIndex in start .. stop:
|
||||
if runeIndex == lines[0][1]:
|
||||
break outer
|
||||
maxInitialY
|
||||
|
||||
var lineHeights = newSeq[float32](lines.len)
|
||||
block: # Compute each line's line height
|
||||
var line: int
|
||||
for spanIndex, (start, stop) in result.spans:
|
||||
let
|
||||
font = result.fonts[spanIndex]
|
||||
fontLineHeight =
|
||||
if font.lineheight >= 0:
|
||||
font.lineheight
|
||||
else:
|
||||
font.defaultLineHeight
|
||||
lineHeights[line] = max(lineHeights[line], fontLineHeight)
|
||||
for runeIndex in start .. stop:
|
||||
if line + 1 < lines.len and runeIndex == lines[line][1]:
|
||||
inc line
|
||||
lineHeights[line] = max(lineHeights[line], fontLineHeight)
|
||||
# Handle when span and line endings coincide
|
||||
if line + 1 < lines.len and stop == lines[line][1]:
|
||||
inc line
|
||||
lineHeights[line] = max(lineHeights[line], fontLineHeight)
|
||||
|
||||
block: # Vertically position the glyphs
|
||||
var
|
||||
line: int
|
||||
baseline = initialY
|
||||
for spanIndex, (start, stop) in result.spans:
|
||||
let
|
||||
font = result.fonts[spanIndex]
|
||||
lineHeight =
|
||||
if font.lineheight >= 0:
|
||||
font.lineheight
|
||||
else:
|
||||
font.defaultLineHeight
|
||||
for runeIndex in start .. stop:
|
||||
if line + 1 < lines.len and runeIndex == lines[line + 1][0]:
|
||||
inc line
|
||||
baseline += lineHeights[line]
|
||||
result.positions[runeIndex].y = baseline
|
||||
result.selectionRects[runeIndex].y =
|
||||
baseline - round(font.typeface.ascent * font.scale)
|
||||
result.selectionRects[runeIndex].h = lineHeight
|
||||
|
||||
if vAlign != vaTop:
|
||||
let
|
||||
finalSelectionRect = result.selectionRects[^1]
|
||||
furthestY = finalSelectionRect.y + finalSelectionRect.h
|
||||
|
||||
var yAdjustment: float32
|
||||
case vAlign:
|
||||
of vaTop:
|
||||
discard
|
||||
of haCenter:
|
||||
xAdjustment = (bounds.x - furthestX) / 2
|
||||
of haRight:
|
||||
xAdjustment = bounds.x - furthestX
|
||||
of vaMiddle:
|
||||
yAdjustment = round((bounds.y - furthestY) / 2)
|
||||
of vaBottom:
|
||||
yAdjustment = bounds.y - furthestY
|
||||
|
||||
if xAdjustment != 0:
|
||||
for i in start .. stop:
|
||||
result.positions[i].x += xAdjustment
|
||||
result.selectionRects[i].x += xAdjustment
|
||||
if yAdjustment != 0:
|
||||
for i in 0 ..< result.positions.len:
|
||||
result.positions[i].y += yAdjustment
|
||||
result.selectionRects[i].y += yAdjustment
|
||||
|
||||
if vAlign != vaTop:
|
||||
let
|
||||
finalSelectionRect = result.selectionRects[^1]
|
||||
furthestY = finalSelectionRect.y + finalSelectionRect.h
|
||||
|
||||
var yAdjustment: float32
|
||||
case vAlign:
|
||||
of vaTop:
|
||||
discard
|
||||
of vaMiddle:
|
||||
yAdjustment = round((bounds.y - furthestY) / 2)
|
||||
of vaBottom:
|
||||
yAdjustment = bounds.y - furthestY
|
||||
|
||||
if yAdjustment != 0:
|
||||
for i in 0 ..< result.positions.len:
|
||||
result.positions[i].y += yAdjustment
|
||||
result.selectionRects[i].y += yAdjustment
|
||||
proc typeset*(
|
||||
font: Font,
|
||||
text: string,
|
||||
bounds = vec2(0, 0),
|
||||
hAlign = haLeft,
|
||||
vAlign = vaTop,
|
||||
wrap = true
|
||||
): Arrangement {.inline.} =
|
||||
## Lays out the character glyphs and returns the arrangement.
|
||||
## Optional parameters:
|
||||
## bounds: width determines wrapping and hAlign, height for vAlign
|
||||
## hAlign: horizontal alignment of the text
|
||||
## vAlign: vertical alignment of the text
|
||||
## wrap: enable/disable text wrapping
|
||||
typeset(@[newSpan(text, font)], bounds, hAlign, vAlign, wrap)
|
||||
|
||||
proc computeBounds*(arrangement: Arrangement): Vec2 =
|
||||
if arrangement.runes.len > 0:
|
||||
|
@ -274,13 +376,8 @@ proc computeBounds*(font: Font, text: string): Vec2 {.inline.} =
|
|||
## Computes the width and height of the text in pixels.
|
||||
font.typeset(text).computeBounds()
|
||||
|
||||
proc getPath*(arrangement: Arrangement, index: int): Path =
|
||||
## Returns the path for the rune index.
|
||||
result = arrangement.font.typeface.getGlyphPath(arrangement.runes[index])
|
||||
result.transform(
|
||||
translate(arrangement.positions[index]) *
|
||||
scale(vec2(arrangement.font.scale))
|
||||
)
|
||||
proc computeBounds*(spans: seq[Span]): Vec2 {.inline.} =
|
||||
typeset(spans).computeBounds()
|
||||
|
||||
proc parseOtf*(buf: string): Font =
|
||||
result.typeface = Typeface()
|
||||
|
|
|
@ -14,6 +14,6 @@ timeIt "typeset":
|
|||
|
||||
timeIt "rasterize":
|
||||
image.fill(rgba(255, 255, 255, 255))
|
||||
image.fillText(font, text, rgba(0, 0, 0, 255), bounds = image.wh)
|
||||
image.fillText(font, text, bounds = image.wh)
|
||||
# mask.fill(0)
|
||||
# mask.fillText(font, text, bounds = mask.wh)
|
||||
|
|
BIN
tests/fonts/diffs/selection_rects1.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
tests/fonts/diffs/selection_rects2.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
tests/fonts/diffs/spans1.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
tests/fonts/diffs/spans2.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
tests/fonts/masters/selection_rects1.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
tests/fonts/masters/selection_rects2.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
tests/fonts/masters/spans1.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
tests/fonts/masters/spans2.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
tests/fonts/rendered/selection_rects1.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
tests/fonts/rendered/selection_rects2.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
tests/fonts/rendered/spans1.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
tests/fonts/rendered/spans2.png
Normal file
After Width: | Height: | Size: 31 KiB |
|
@ -13,7 +13,7 @@ block:
|
|||
font.size = 24
|
||||
|
||||
let bounds = font.computeBounds("Word")
|
||||
doAssert bounds == vec2(56.05078125, 28)
|
||||
doAssert bounds == vec2(57, 28)
|
||||
|
||||
block:
|
||||
var font = readFont("tests/fonts/Roboto-Regular_1.ttf")
|
||||
|
@ -629,3 +629,63 @@ block:
|
|||
image.fillText(font, "Text")
|
||||
|
||||
image.writeFile("tests/fonts/image_paint_fill.png")
|
||||
|
||||
block:
|
||||
var font1 = readFont("tests/fonts/Roboto-Regular_1.ttf")
|
||||
font1.size = 80
|
||||
|
||||
var font2 = readFont("tests/fonts/Aclonica-Regular_1.ttf")
|
||||
font2.size = 100
|
||||
|
||||
var font3 = readFont("tests/fonts/Ubuntu-Regular_1.ttf")
|
||||
font3.size = 48
|
||||
|
||||
let spans = @[
|
||||
newSpan("One span ", font1),
|
||||
newSpan("Two span", font2),
|
||||
newSpan(" Three span", font3)
|
||||
]
|
||||
|
||||
let image = newImage(700, 250)
|
||||
image.fill(rgba(255, 255, 255, 255))
|
||||
|
||||
let arrangement = typeset(spans, bounds = image.wh)
|
||||
|
||||
image.fillText(arrangement)
|
||||
|
||||
doDiff(image, "spans1")
|
||||
|
||||
for i, rect in arrangement.selectionRects:
|
||||
image.fillRect(rect, rgba(128, 128, 128, 128))
|
||||
|
||||
doDiff(image, "selection_rects1")
|
||||
|
||||
block:
|
||||
var font1 = readFont("tests/fonts/Roboto-Regular_1.ttf")
|
||||
font1.size = 80
|
||||
|
||||
var font2 = readFont("tests/fonts/Aclonica-Regular_1.ttf")
|
||||
font2.size = 100
|
||||
|
||||
var font3 = readFont("tests/fonts/Ubuntu-Regular_1.ttf")
|
||||
font3.size = 48
|
||||
|
||||
let spans = @[
|
||||
newSpan("One span ", font1),
|
||||
newSpan("Two span", font2),
|
||||
newSpan(" Three span", font3)
|
||||
]
|
||||
|
||||
let image = newImage(475, 400)
|
||||
image.fill(rgba(255, 255, 255, 255))
|
||||
|
||||
let arrangement = typeset(spans, bounds = image.wh)
|
||||
|
||||
image.fillText(arrangement)
|
||||
|
||||
doDiff(image, "spans2")
|
||||
|
||||
for i, rect in arrangement.selectionRects:
|
||||
image.fillRect(rect, rgba(128, 128, 128, 128))
|
||||
|
||||
doDiff(image, "selection_rects2")
|
||||
|
|