diff --git a/src/pixie/fontformats/opentype.nim b/src/pixie/fontformats/opentype.nim index 1cb5233..3168dfe 100644 --- a/src/pixie/fontformats/opentype.nim +++ b/src/pixie/fontformats/opentype.nim @@ -543,6 +543,8 @@ proc parseKernTable(buf: string, offset: int): KernTable = failUnsupported() subTable.length = buf.readUint16(i + 2).swap() subTable.coverage = buf.readUint16(i + 4).swap() + if subTable.coverage shr 8 != 0: + failUnsupported() subTable.nPairs = buf.readUint16(i + 6).swap() subTable.searchRange = buf.readUint16(i + 8).swap() subTable.entrySelector = buf.readUint16(i + 10).swap() diff --git a/src/pixie/fonts.nim b/src/pixie/fonts.nim index f695139..b3c260c 100644 --- a/src/pixie/fonts.nim +++ b/src/pixie/fonts.nim @@ -40,24 +40,6 @@ proc lineGap*(font: Font): float32 {.inline.} = ## The font line gap value in font units. font.opentype.hhea.lineGap.float32 -proc getGlyphPath*(font: Font, rune: Rune): Path = - if rune notin font.glyphPaths: - font.glyphPaths[rune] = font.opentype.parseGlyph(rune) - font.glyphPaths[rune].transform(scale(vec2(1, -1))) - font.glyphPaths[rune] - -proc getGlyphAdvance*(font: Font, rune: Rune): float32 = - let glyphId = font.opentype.getGlyphId(rune).int - if glyphId < font.opentype.hmtx.hMetrics.len: - font.opentype.hmtx.hMetrics[glyphId].advanceWidth.float32 - else: - font.opentype.hmtx.hMetrics[^1].advanceWidth.float32 - -proc getKerningAdjustment*(font: Font, left, right: Rune): float32 = - let pair = (left, right) - if pair in font.kerningPairs: - result = font.kerningPairs[pair] - proc scale*(font: Font): float32 = ## The scale factor to transform font units into pixels. font.size / font.opentype.head.unitsPerEm.float32 @@ -66,6 +48,29 @@ proc defaultLineHeight*(font: Font): float32 = ## The default line height in pixels for the current font size. round((font.ascent + abs(font.descent) + font.lineGap) * font.scale) +proc getGlyphPath*(font: Font, rune: Rune): Path = + ## The glyph path for the parameter rune. + if rune notin font.glyphPaths: + font.glyphPaths[rune] = font.opentype.parseGlyph(rune) + font.glyphPaths[rune].transform(scale(vec2(1, -1))) + font.glyphPaths[rune] + +proc getGlyphAdvance*(font: Font, rune: Rune): float32 = + ## The advance for the parameter rune in pixels. + let glyphId = font.opentype.getGlyphId(rune).int + if glyphId < font.opentype.hmtx.hMetrics.len: + result = font.opentype.hmtx.hMetrics[glyphId].advanceWidth.float32 + else: + result = font.opentype.hmtx.hMetrics[^1].advanceWidth.float32 + result *= font.scale + +proc getKerningAdjustment*(font: Font, left, right: Rune): float32 = + ## The kerning adjustment for the parameter rune pair, in pixels. + let pair = (left, right) + if pair in font.kerningPairs: + result = font.kerningPairs[pair] + result *= font.scale + proc convertTextCase(runes: var seq[Rune], textCase: TextCase) = case textCase: of tcNormal: @@ -111,9 +116,9 @@ proc typeset*( prevCanWrap = i if i > 0: - at.x += font.getKerningAdjustment(runes[i - 1], rune) * font.scale + at.x += font.getKerningAdjustment(runes[i - 1], rune) - let advance = font.getGlyphAdvance(rune) * font.scale + let advance = font.getGlyphAdvance(rune) if bounds.x > 0 and at.x + advance > bounds.x: # Wrap to new line at.x = 0 at.y += lineHeight @@ -122,9 +127,9 @@ proc typeset*( if prevCanWrap > 0 and prevCanWrap != i: for j in prevCanWrap + 1 ..< i: if j > 0: - at.x += font.getKerningAdjustment(runes[j - 1], runes[j]) * font.scale + at.x += font.getKerningAdjustment(runes[j - 1], runes[j]) positions[j] = at - at.x += font.getGlyphAdvance(runes[j]) * font.scale + at.x += font.getGlyphAdvance(runes[j]) positions[i] = at at.x += advance @@ -142,14 +147,22 @@ proc parseOtf*(buf: string): Font = if result.opentype.kern != nil: for table in result.opentype.kern.subTables: - for pair in table.kernPairs: - if pair.value != 0 and - pair.left in result.opentype.cmap.glyphIdToRune and - pair.right in result.opentype.cmap.glyphIdToRune: - result.kerningPairs[( - result.opentype.cmap.glyphIdToRune[pair.left], - result.opentype.cmap.glyphIdToRune[pair.right] - )] = pair.value.float32 + if (table.coverage and 1) != 0: # Horizontal data + for pair in table.kernPairs: + if pair.value != 0 and + pair.left in result.opentype.cmap.glyphIdToRune and + pair.right in result.opentype.cmap.glyphIdToRune: + let key = ( + result.opentype.cmap.glyphIdToRune[pair.left], + result.opentype.cmap.glyphIdToRune[pair.right] + ) + var value = pair.value.float32 + if key in result.kerningPairs: + if (table.coverage and 0b1000) != 0: # Override + discard + else: # Accumulate + value += result.kerningPairs[key] + result.kerningPairs[key] = value proc parseTtf*(buf: string): Font = parseOtf(buf) diff --git a/tests/fonts_megatest.nim b/tests/fonts_megatest.nim new file mode 100644 index 0000000..bddd2f5 --- /dev/null +++ b/tests/fonts_megatest.nim @@ -0,0 +1,10 @@ +import common, pixie + +# Clone https://github.com/google/fonts +# Check out commit ebaa6a7aab9b700da4e30a4682687acdf427eae7 + +let fontPaths = findAllFonts("../fonts") + +for fontPath in fontPaths: + echo fontPath + let font = readFont(fontPath) diff --git a/tests/megatest.nim b/tests/icons_megatest.nim similarity index 100% rename from tests/megatest.nim rename to tests/icons_megatest.nim