more tests

This commit is contained in:
Ryan Oldenburg 2021-05-03 15:41:38 -05:00
parent cbfccea851
commit eafd18fa7e
131 changed files with 570 additions and 121 deletions

View file

@ -332,12 +332,22 @@ proc fillText*(
text: string, text: string,
color: SomeColor, color: SomeColor,
transform: Vec2 | Mat3 = vec2(0, 0), transform: Vec2 | Mat3 = vec2(0, 0),
bounds = vec2(0, 0) bounds = vec2(0, 0),
hAlign = haLeft,
vAlign = vaTop,
textCase = tcNormal,
wrap = true,
kerning = true
) = ) =
let typeset = font.typeset(text, bounds) for path in font.typesetPaths(
for i in 0 ..< typeset.runes.len: text,
var path = font.getGlyphPath(typeset.runes[i]) bounds,
path.transform(translate(typeset.positions[i]) * scale(vec2(font.scale))) hAlign,
vAlign,
textCase,
wrap,
kerning
):
image.fillPath(path, color, transform) image.fillPath(path, color, transform)
proc fillText*( proc fillText*(
@ -345,12 +355,22 @@ proc fillText*(
font: Font, font: Font,
text: string, text: string,
transform: Vec2 | Mat3 = vec2(0, 0), transform: Vec2 | Mat3 = vec2(0, 0),
bounds = vec2(0, 0) bounds = vec2(0, 0),
hAlign = haLeft,
vAlign = vaTop,
textCase = tcNormal,
wrap = true,
kerning = true
) = ) =
let typeset = font.typeset(text, bounds) for path in font.typesetPaths(
for i in 0 ..< typeset.runes.len: text,
var path = font.getGlyphPath(typeset.runes[i]) bounds,
path.transform(translate(typeset.positions[i]) * scale(vec2(font.scale))) hAlign,
vAlign,
textCase,
wrap,
kerning
):
mask.fillPath(path, transform) mask.fillPath(path, transform)
proc strokeText*( proc strokeText*(
@ -360,12 +380,22 @@ proc strokeText*(
color: SomeColor, color: SomeColor,
transform: Vec2 | Mat3 = vec2(0, 0), transform: Vec2 | Mat3 = vec2(0, 0),
strokeWidth = 1.0, strokeWidth = 1.0,
bounds = vec2(0, 0) bounds = vec2(0, 0),
hAlign = haLeft,
vAlign = vaTop,
textCase = tcNormal,
wrap = true,
kerning = true
) = ) =
let typeset = font.typeset(text, bounds) for path in font.typesetPaths(
for i in 0 ..< typeset.runes.len: text,
var path = font.getGlyphPath(typeset.runes[i]) bounds,
path.transform(translate(typeset.positions[i]) * scale(vec2(font.scale))) hAlign,
vAlign,
textCase,
wrap,
kerning
):
image.strokePath(path, color, transform, strokeWidth) image.strokePath(path, color, transform, strokeWidth)
proc strokeText*( proc strokeText*(
@ -374,10 +404,20 @@ proc strokeText*(
text: string, text: string,
transform: Vec2 | Mat3 = vec2(0, 0), transform: Vec2 | Mat3 = vec2(0, 0),
strokeWidth = 1.0, strokeWidth = 1.0,
bounds = vec2(0, 0) bounds = vec2(0, 0),
hAlign = haLeft,
vAlign = vaTop,
textCase = tcNormal,
wrap = true,
kerning = true
) = ) =
let typeset = font.typeset(text, bounds) for path in font.typesetPaths(
for i in 0 ..< typeset.runes.len: text,
var path = font.getGlyphPath(typeset.runes[i]) bounds,
path.transform(translate(typeset.positions[i]) * scale(vec2(font.scale))) hAlign,
vAlign,
textCase,
wrap,
kerning
):
mask.strokePath(path, transform, strokeWidth) mask.strokePath(path, transform, strokeWidth)

View file

@ -235,6 +235,26 @@ type
pairValueCount: uint16 pairValueCount: uint16
pairValueRecords: seq[PairValueRecord] pairValueRecords: seq[PairValueRecord]
Class2Record = object
valueRecord1: ValueRecord
valueRecord2: ValueRecord
Class1Record = object
class2Records: seq[Class2Record]
ClassRangeRecord = object
startGlyphID: uint16
endGlyphID: uint16
class: uint16
ClassDef = object
classFormat: uint16
startGlyphID: uint16
glyphCount: uint16
classValueArray: seq[uint16]
classRangeCount: uint16
classRangeRecords: seq[ClassRangeRecord]
PairPos = ref object PairPos = ref object
posFormat: uint16 posFormat: uint16
coverageOffset: uint16 coverageOffset: uint16
@ -243,6 +263,13 @@ type
pairSetCount: uint16 pairSetCount: uint16
pairSetOffsets: seq[uint16] pairSetOffsets: seq[uint16]
pairSets: seq[PairSet] pairSets: seq[PairSet]
classDef1Offset: uint16
classDef2Offset: uint16
class1Count: uint16
class2Count: uint16
class1Records: seq[Class1Record]
classDef1: ClassDef
classDef2: ClassDef
coverage: Coverage coverage: Coverage
Lookup = object Lookup = object
@ -865,6 +892,66 @@ proc parsePairSet(
buf, i + j * pairValueRecordSize, valueFormat1, valueFormat2 buf, i + j * pairValueRecordSize, valueFormat1, valueFormat2
) )
proc parseClass2Record(
buf: string, offset: int, valueFormat1, valueFormat2: uint16
): Class2Record =
result.valueRecord1 = parseValueRecord(buf, offset, valueFormat1)
result.valueRecord2 = parseValueRecord(buf, offset, valueFormat2)
proc parseClass1Record(
buf: string, offset: int, valueFormat1, valueFormat2, class2Count: uint16
): Class1Record =
var i = offset
let class2RecordSize = (
countSetBits(valueFormat1) + countSetBits(valueFormat2)
) * 2
result.class2Records.setLen(class2Count.int)
for j in 0 ..< class2Count.int:
result.class2Records[j] =
parseClass2Record(buf, i, valueFormat1, valueFormat2)
i += class2RecordSize
proc parseClassRangeRecord(buf: string, offset: int): ClassRangeRecord =
buf.eofCheck(offset + 6)
result.startGlyphID = buf.readUint16(offset + 0).swap()
result.endGlyphID = buf.readUint16(offset + 2).swap()
result.class = buf.readUint16(offset + 4).swap()
proc parseClassDef(buf: string, offset: int): ClassDef =
var i = offset
buf.eofCheck(i + 2)
result.classFormat = buf.readUint16(i + 0).swap()
i += 2
case result.classFormat:
of 1:
buf.eofCheck(i + 4)
result.startGlyphID = buf.readUint16(i + 0).swap()
result.glyphCount = buf.readUint16(i + 2).swap()
i += 4
buf.eofCheck(i + result.glyphCount.int * 2)
result.classValueArray = buf.readUint16Seq(i + 0, result.glyphCount.int)
of 2:
buf.eofCheck(i + 2)
result.classRangeCount = buf.readUint16(i + 0).swap()
i += 2
result.classRangeRecords.setLen(result.classRangeCount.int)
for j in 0 ..< result.classRangeCount.int:
result.classRangeRecords[j] = parseClassRangeRecord(buf, i)
i += sizeof(ClassRangeRecord)
else:
failUnsupported()
proc parsePairPos(buf: string, offset: int): PairPos = proc parsePairPos(buf: string, offset: int): PairPos =
var i = offset var i = offset
@ -902,7 +989,36 @@ proc parsePairPos(buf: string, offset: int): PairPos =
result.coverage = parseCoverage(buf, offset + result.coverageOffset.int) result.coverage = parseCoverage(buf, offset + result.coverageOffset.int)
of 2: of 2:
discard result = PairPos()
result.posFormat = posFormat
buf.eofCheck(i + 14)
result.coverageOffset = buf.readUint16(i + 0).swap()
result.valueFormat1 = buf.readUint16(i + 2).swap()
result.valueFormat2 = buf.readUint16(i + 4).swap()
result.classDef1Offset = buf.readUint16(i + 6).swap()
result.classDef2Offset = buf.readUint16(i + 8).swap()
result.class1Count = buf.readUint16(i + 10).swap()
result.class2Count = buf.readUint16(i + 12).swap()
i += 14
let class2RecordSize = (
countSetBits(result.valueFormat1) + countSetBits(result.valueFormat2)
) * 2
result.class1Records.setLen(result.class1Count.int)
for j in 0 ..< result.class1Count.int:
result.class1Records[j] = parseClass1Record(
buf, i, result.valueFormat1, result.valueFormat2, result.class2Count
)
i += class2RecordSize
result.classDef1 = parseClassDef(buf, offset + result.classDef1Offset.int)
result.classDef2 = parseClassDef(buf, offset + result.classDef2Offset.int)
result.coverage = parseCoverage(buf, offset + result.coverageOffset.int)
else: else:
failUnsupported() failUnsupported()
@ -1244,12 +1360,21 @@ proc getGlyphPath*(opentype: OpenType, rune: Rune): Path =
opentype.glyphPaths[rune].transform(scale(vec2(1, -1))) opentype.glyphPaths[rune].transform(scale(vec2(1, -1)))
opentype.glyphPaths[rune] opentype.glyphPaths[rune]
proc getGlyphAdvance*(opentype: OpenType, rune: Rune): float32 = proc getLeftSideBearing*(opentype: OpenType, rune: Rune): float32 =
let glyphId = opentype.getGlyphId(rune).int let glyphId = opentype.getGlyphId(rune).int
if glyphId < opentype.hmtx.hMetrics.len: if glyphId < opentype.hmtx.hMetrics.len:
opentype.hmtx.hMetrics[glyphId].advanceWidth.float32 result = opentype.hmtx.hMetrics[glyphId].leftSideBearing.float32
else: else:
opentype.hmtx.hMetrics[^1].advanceWidth.float32 let index = glyphId - opentype.hmtx.hMetrics.len
if index > 0 and index < opentype.hmtx.leftSideBearings.len:
result = opentype.hmtx.leftSideBearings[index].float32
proc getAdvance*(opentype: OpenType, rune: Rune): float32 =
let glyphId = opentype.getGlyphId(rune).int
if glyphId < opentype.hmtx.hMetrics.len:
result = opentype.hmtx.hMetrics[glyphId].advanceWidth.float32
else:
result = opentype.hmtx.hMetrics[^1].advanceWidth.float32
proc getKerningAdjustment*(opentype: OpenType, left, right: Rune): float32 = proc getKerningAdjustment*(opentype: OpenType, left, right: Rune): float32 =
let pair = (left, right) let pair = (left, right)
@ -1328,21 +1453,93 @@ proc parseOpenType*(buf: string): OpenType =
value += result.kerningPairs[key] value += result.kerningPairs[key]
result.kerningPairs[key] = value result.kerningPairs[key] = value
if "GPOS" in result.tableRecords: # if "GPOS" in result.tableRecords:
result.gpos = parseGposTable(buf, result.tableRecords["GPOS"].offset.int) # result.gpos = parseGposTable(buf, result.tableRecords["GPOS"].offset.int)
if result.gpos != nil and result.gpos.lookupList.pairPos != nil: # if result.gpos != nil and result.gpos.lookupList.pairPos != nil:
case result.gpos.lookupList.pairPos.coverage.coverageFormat: # # case result.gpos.lookupList.pairPos.coverage.coverageFormat:
of 1: # # of 1:
echo result.gpos.lookupList.pairPos.coverage.glyphCount # # echo "GLYPH COUNT:", result.gpos.lookupList.pairPos.coverage.glyphCount
of 2: # # of 2:
echo result.gpos.lookupList.pairPos.coverage.rangeCount # # echo "RANGE COUNT:", result.gpos.lookupList.pairPos.coverage.rangeCount
else: # # else:
failUnsupported() # # failUnsupported()
for pairSet in result.gpos.lookupList.pairPos.pairSets: # case result.gpos.lookupList.pairPos.posFormat:
for pairValue in pairSet.pairValueRecords: # of 1:
discard # echo "posFormat 1 not implemented"
# of 2:
# proc classDefFormat1(classDef: ClassDef): Table[uint16, uint16] =
# for i in 0.uint16 ..< classDef.glyphCount:
# result[classDef.startGlyphID + i] = classDef.classValueArray[i]
# proc classDefFormat2(classDef: ClassDef): Table[uint16, uint16] =
# for record in classDef.classRangeRecords:
# if record.startGlyphID > record.endGlyphID:
# failUnsupported()
# for glyphId in record.startGlyphID .. record.endGlyphID:
# result[glyphId] = record.class
# var glyphIdToClass1: Table[uint16, uint16]
# case result.gpos.lookupList.pairPos.classDef1.classFormat:
# of 1:
# glyphIdToClass1 =
# classDefFormat1(result.gpos.lookupList.pairPos.classDef1)
# of 2:
# glyphIdToClass1 =
# classDefFormat2(result.gpos.lookupList.pairPos.classDef1)
# else:
# failUnsupported()
# var glyphIdToClass2: Table[uint16, uint16]
# case result.gpos.lookupList.pairPos.classDef2.classFormat:
# of 1:
# glyphIdToClass2 =
# classDefFormat1(result.gpos.lookupList.pairPos.classDef2)
# of 2:
# glyphIdToClass2 =
# classDefFormat2(result.gpos.lookupList.pairPos.classDef2)
# else:
# failUnsupported()
# var runeToClass1: Table[Rune, uint16]
# for glyphId, class in glyphIdToClass1:
# if glyphId in result.cmap.glyphIdToRune:
# let rune = result.cmap.glyphIdToRune[glyphId]
# runeToClass1[rune] = class
# var runeToClass2: Table[Rune, uint16]
# for glyphId, class in glyphIdToClass2:
# if glyphId in result.cmap.glyphIdToRune:
# let rune = result.cmap.glyphIdToRune[glyphId]
# runeToClass2[rune] = class
# var classPairs: Table[(uint16, uint16), Class2Record]
# for i, class1Record in result.gpos.lookupList.pairPos.class1Records:
# for j, class2Record in class1Record.class2Records:
# classPairs[(i.uint16, j.uint16)] = class2Record
# for left in result.cmap.runeToGlyphId.keys:
# for right in result.cmap.runeToGlyphId.keys:
# var leftClass, rightClass: uint16
# if left in runeToClass1:
# leftClass = runeToClass1[left]
# if right in runeToClass2:
# rightClass = runeToClass2[right]
# let pair = (leftClass, rightClass)
# if pair in classPairs:
# let classPair = classPairs[pair]
# if classPair.valueRecord1.xAdvance != 0:
# result.kerningPairs[(left, right)] =
# classPair.valueRecord1.xAdvance.float32
# else:
# failUnsupported()
# echo Rune(32) in result.cmap.runeToGlyphId
# echo getAdvance(result, Rune(32))
# echo result.head.unitsPerEm
# echo result.getGlyphId(Rune(32))
when defined(release): when defined(release):
{.pop.} {.pop.}

View file

@ -2,7 +2,7 @@ import pixie/common, pixie/paths, strutils, tables, unicode, vmath, xmlparser, x
type SvgFont* = ref object type SvgFont* = ref object
unitsPerEm*, ascent*, descent*: float32 unitsPerEm*, ascent*, descent*: float32
glyphAdvances: Table[Rune, float32] advances: Table[Rune, float32]
glyphPaths: Table[Rune, Path] glyphPaths: Table[Rune, Path]
kerningPairs: Table[(Rune, Rune), float32] kerningPairs: Table[(Rune, Rune), float32]
missingGlyphAdvance: float32 missingGlyphAdvance: float32
@ -14,9 +14,9 @@ proc getGlyphPath*(svgFont: SvgFont, rune: Rune): Path =
else: else:
svgFont.missingGlyphPath svgFont.missingGlyphPath
proc getGlyphAdvance*(svgFont: SvgFont, rune: Rune): float32 = proc getAdvance*(svgFont: SvgFont, rune: Rune): float32 =
if rune in svgFont.glyphAdvances: if rune in svgFont.advances:
svgFont.glyphAdvances[rune] svgFont.advances[rune]
else: else:
svgFont.missingGlyphAdvance svgFont.missingGlyphAdvance
@ -74,7 +74,7 @@ proc parseSvgFont*(buf: string): SvgFont =
var advance = defaultAdvance var advance = defaultAdvance
if node.attr("horiz-adv-x").len > 0: if node.attr("horiz-adv-x").len > 0:
advance = node.parseFloat("horiz-adv-x") advance = node.parseFloat("horiz-adv-x")
result.glyphAdvances[rune] = advance result.advances[rune] = advance
result.glyphPaths[rune] = parsePath(node.attr("d")) result.glyphPaths[rune] = parsePath(node.attr("d"))
result.glyphPaths[rune].transform(scale(vec2(1, -1))) result.glyphPaths[rune].transform(scale(vec2(1, -1)))
else: else:

View file

@ -4,13 +4,16 @@ import pixie/fontformats/opentype, pixie/fontformats/svgfont, pixie/paths,
const AutoLineHeight* = -1.float32 ## Use default line height for the font size const AutoLineHeight* = -1.float32 ## Use default line height for the font size
type type
Font* = ref object Typeface = ref object
opentype: OpenType opentype: OpenType
svgFont: SvgFont svgFont: SvgFont
Font* = object
typeface*: Typeface
size*: float32 ## Font size in pixels. size*: float32 ## Font size in pixels.
lineHeight*: float32 ## The line height in pixels or AutoLineHeight for the font's default line height. lineHeight*: float32 ## The line height in pixels or AutoLineHeight for the font's default line height.
TypesetText* = ref object Typesetting* = ref object
runes*: seq[Rune] runes*: seq[Rune]
positions*: seq[Vec2] positions*: seq[Vec2]
@ -32,56 +35,59 @@ type
# tcSmallCaps # tcSmallCaps
# tcSmallCapsForced # tcSmallCapsForced
proc ascent(font: Font): float32 {.inline.} = proc ascent(typeface: Typeface): float32 {.inline.} =
## The font ascender value in font units. ## The font ascender value in font units.
if font.opentype != nil: if typeface.opentype != nil:
font.opentype.hhea.ascender.float32 typeface.opentype.hhea.ascender.float32
else: else:
font.svgFont.ascent typeface.svgFont.ascent
proc descent(font: Font): float32 {.inline.} = proc descent(typeface: Typeface): float32 {.inline.} =
## The font descender value in font units. ## The font descender value in font units.
if font.opentype != nil: if typeface.opentype != nil:
font.opentype.hhea.descender.float32 typeface.opentype.hhea.descender.float32
else: else:
font.svgFont.descent typeface.svgFont.descent
proc lineGap(font: Font): float32 {.inline.} = proc lineGap(typeface: Typeface): float32 {.inline.} =
## The font line gap value in font units. ## The font line gap value in font units.
if font.opentype != nil: if typeface.opentype != nil:
result = font.opentype.hhea.lineGap.float32 result = typeface.opentype.hhea.lineGap.float32
proc getGlyphPath*(font: Font, rune: Rune): Path {.inline.} = proc getGlyphPath*(typeface: Typeface, rune: Rune): Path {.inline.} =
## The glyph path for the rune. ## The glyph path for the rune.
if font.opentype != nil: if typeface.opentype != nil:
font.opentype.getGlyphPath(rune) typeface.opentype.getGlyphPath(rune)
else: else:
font.svgFont.getGlyphPath(rune) typeface.svgFont.getGlyphPath(rune)
proc getGlyphAdvance(font: Font, rune: Rune): float32 {.inline.} = proc getAdvance(typeface: Typeface, rune: Rune): float32 {.inline.} =
## The advance for the rune in pixels. ## The advance for the rune in pixels.
if font.opentype != nil: if typeface.opentype != nil:
font.opentype.getGlyphAdvance(rune) typeface.opentype.getAdvance(rune)
else: else:
font.svgFont.getGlyphAdvance(rune) typeface.svgFont.getAdvance(rune)
proc getKerningAdjustment(font: Font, left, right: Rune): float32 {.inline.} = proc getKerningAdjustment(
typeface: Typeface, left, right: Rune
): float32 {.inline.} =
## The kerning adjustment for the rune pair, in pixels. ## The kerning adjustment for the rune pair, in pixels.
if font.opentype != nil: if typeface.opentype != nil:
font.opentype.getKerningAdjustment(left, right) typeface.opentype.getKerningAdjustment(left, right)
else: else:
font.svgfont.getKerningAdjustment(left, right) typeface.svgfont.getKerningAdjustment(left, right)
proc scale*(font: Font): float32 {.inline.} = proc scale*(font: Font): float32 {.inline.} =
## The scale factor to transform font units into pixels. ## The scale factor to transform font units into pixels.
if font.opentype != nil: if font.typeface.opentype != nil:
font.size / font.opentype.head.unitsPerEm.float32 font.size / font.typeface.opentype.head.unitsPerEm.float32
else: else:
font.size / font.svgFont.unitsPerEm font.size / font.typeface.svgFont.unitsPerEm
proc defaultLineHeight*(font: Font): float32 {.inline.} = proc defaultLineHeight*(font: Font): float32 {.inline.} =
## The default line height in pixels for the current font size. ## The default line height in pixels for the current font size.
let fontUnits = (font.ascent + abs(font.descent) + font.lineGap) let fontUnits =
font.typeface.ascent - font.typeface.descent + font.typeface.lineGap
round(fontUnits * font.scale) round(fontUnits * font.scale)
proc convertTextCase(runes: var seq[Rune], textCase: TextCase) = proc convertTextCase(runes: var seq[Rune], textCase: TextCase) =
@ -107,12 +113,13 @@ proc typeset*(
bounds = vec2(0, 0), bounds = vec2(0, 0),
hAlign = haLeft, hAlign = haLeft,
vAlign = vaTop, vAlign = vaTop,
textCase = tcNormal textCase = tcNormal,
): TypesetText = wrap = true,
result = TypesetText() kerning = true
): Typesetting =
result = Typesetting()
result.runes = toRunes(text) result.runes = toRunes(text)
result.runes.convertTextCase(textCase) result.runes.convertTextCase(textCase)
result.positions.setLen(result.runes.len) result.positions.setLen(result.runes.len)
let lineHeight = let lineHeight =
@ -121,22 +128,24 @@ proc typeset*(
else: else:
font.defaultLineHeight font.defaultLineHeight
proc glyphAdvance(runes: seq[Rune], font: Font, i: int): float32 {.inline.} = proc glyphAdvance(
if i + 1 < runes.len: font: Font, runes: seq[Rune], i: int, kerning: bool
result += font.getKerningAdjustment(runes[i], runes[i + 1]) ): float32 {.inline.} =
result += font.getGlyphAdvance(runes[i]) if kerning and i + 1 < runes.len:
result += font.typeface.getKerningAdjustment(runes[i], runes[i + 1])
result += font.typeface.getAdvance(runes[i])
result *= font.scale result *= font.scale
var var
at: Vec2 at: Vec2
prevCanWrap: int prevCanWrap: int
at.y = round(font.ascent * font.scale) at.y = round(font.typeface.ascent * font.scale)
at.y += (lineheight - font.defaultLineHeight) / 2 at.y += (lineheight - font.defaultLineHeight) / 2
for i, rune in result.runes: for i, rune in result.runes:
if rune.canWrap(): if rune.canWrap():
prevCanWrap = i prevCanWrap = i
let advance = glyphAdvance(result.runes, font, i) let advance = glyphAdvance(font, result.runes, i, kerning)
if rune != Rune(32) and bounds.x > 0 and at.x + advance > bounds.x: if rune != Rune(32) and bounds.x > 0 and at.x + advance > bounds.x:
# Wrap to new line # Wrap to new line
at.x = 0 at.x = 0
@ -146,14 +155,40 @@ proc typeset*(
if prevCanWrap > 0 and prevCanWrap != i: if prevCanWrap > 0 and prevCanWrap != i:
for j in prevCanWrap + 1 ..< i: for j in prevCanWrap + 1 ..< i:
result.positions[j] = at result.positions[j] = at
at.x += glyphAdvance(result.runes, font, j) at.x += glyphAdvance(font, result.runes, j, kerning)
result.positions[i] = at result.positions[i] = at
at.x += advance at.x += advance
iterator typesetPaths*(
font: Font,
text: string,
bounds = vec2(0, 0),
hAlign = haLeft,
vAlign = vaTop,
textCase = tcNormal,
wrap = true,
kerning = true
): Path =
let typesetText = font.typeset(
text,
bounds,
hAlign,
vAlign,
textCase,
wrap,
kerning
)
for i in 0 ..< typesetText.runes.len:
var path = font.typeface.getGlyphPath(typesetText.runes[i])
path.transform(
translate(typesetText.positions[i]) * scale(vec2(font.scale))
)
yield path
proc parseOtf*(buf: string): Font = proc parseOtf*(buf: string): Font =
result = Font() result.typeface = Typeface()
result.opentype = parseOpenType(buf) result.typeface.opentype = parseOpenType(buf)
result.size = 12 result.size = 12
result.lineHeight = AutoLineHeight result.lineHeight = AutoLineHeight
@ -161,7 +196,7 @@ proc parseTtf*(buf: string): Font =
parseOtf(buf) parseOtf(buf)
proc parseSvgFont*(buf: string): Font = proc parseSvgFont*(buf: string): Font =
result = Font() result.typeface = Typeface()
result.svgFont = svgfont.parseSvgFont(buf) result.typeface.svgFont = svgfont.parseSvgFont(buf)
result.size = 12 result.size = 12
result.lineHeight = AutoLineHeight result.lineHeight = AutoLineHeight

View file

@ -2,7 +2,7 @@ import benchy, pixie
const text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis in quam in nulla bibendum luctus. Integer dui lectus, ultricies commodo enim quis, laoreet lacinia erat. Vivamus ultrices maximus risus, non aliquam quam sagittis quis. Ut nec diam vitae tortor interdum ullamcorper in aliquet velit. Ut sed lobortis mi. Nulla venenatis lectus varius justo lacinia, quis sollicitudin nunc ultrices. Donec a suscipit arcu, id egestas neque. Nullam commodo pharetra est. Nullam gravida nibh eget quam venenatis lacinia. Vestibulum et libero arcu. Sed dignissim enim eros. Nullam eleifend luctus erat sed luctus. Nunc tincidunt, mi nec tincidunt tristique, ex nulla lobortis sem, sit amet finibus purus justo non massa." const text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis in quam in nulla bibendum luctus. Integer dui lectus, ultricies commodo enim quis, laoreet lacinia erat. Vivamus ultrices maximus risus, non aliquam quam sagittis quis. Ut nec diam vitae tortor interdum ullamcorper in aliquet velit. Ut sed lobortis mi. Nulla venenatis lectus varius justo lacinia, quis sollicitudin nunc ultrices. Donec a suscipit arcu, id egestas neque. Nullam commodo pharetra est. Nullam gravida nibh eget quam venenatis lacinia. Vestibulum et libero arcu. Sed dignissim enim eros. Nullam eleifend luctus erat sed luctus. Nunc tincidunt, mi nec tincidunt tristique, ex nulla lobortis sem, sit amet finibus purus justo non massa."
let font = readFont("tests/fonts/Roboto-Regular.ttf") var font = readFont("tests/fonts/Roboto-Regular.ttf")
font.size = 16 font.size = 16
timeIt "typeset": timeIt "typeset":
@ -13,7 +13,7 @@ let
mask = newMask(500, 300) mask = newMask(500, 300)
timeIt "rasterize": timeIt "rasterize":
# image.fill(rgba(255, 255, 255, 255)) image.fill(rgba(255, 255, 255, 255))
# image.fillText(font, text, rgba(0, 0, 0, 255), bounds = image.wh) image.fillText(font, text, rgba(0, 0, 0, 255), bounds = image.wh)
mask.fill(0) # mask.fill(0)
mask.fillText(font, text, bounds = mask.wh) # mask.fillText(font, text, bounds = mask.wh)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3 KiB

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 711 B

After

Width:  |  Height:  |  Size: 709 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8 KiB

After

Width:  |  Height:  |  Size: 8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Some files were not shown because too many files have changed in this diff Show more