diff --git a/pixie.nimble b/pixie.nimble index 5c43ff2..1223c54 100644 --- a/pixie.nimble +++ b/pixie.nimble @@ -1,4 +1,4 @@ -version = "5.0.6" +version = "5.0.7" author = "Andre von Houck and Ryan Oldenburg" description = "Full-featured 2d graphics library for Nim." license = "MIT" diff --git a/src/pixie/fontformats/opentype.nim b/src/pixie/fontformats/opentype.nim index 3d0493f..14704bf 100644 --- a/src/pixie/fontformats/opentype.nim +++ b/src/pixie/fontformats/opentype.nim @@ -455,6 +455,7 @@ proc parseCmapTable(buf: string, offset: int): CmapTable = let format = buf.readUint16(i + 0).swap() if format == 4: + # https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#format-4-segment-mapping-to-delta-values type Format4 = object format: uint16 length: uint16 @@ -518,8 +519,46 @@ proc parseCmapTable(buf: string, offset: int): CmapTable = if c != 65535: result.runeToGlyphId[Rune(c)] = glyphId.uint16 result.glyphIdToRune[glyphId.uint16] = Rune(c) + + elif format == 12: + # https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#format-12-segmented-coverage + type Format12 = object + format: uint16 + reserved: uint16 + length: uint32 + language: uint32 + numGroups: uint32 + + buf.eofCheck(i + 16) + + var subTable: Format12 + subTable.format = format + subTable.reserved = buf.readUint16(i + 2).swap() + subTable.length = buf.readUint32(i + 4).swap() + subTable.language = buf.readUint32(i + 8).swap() + subTable.numGroups = buf.readUint32(i + 12).swap() + i += 16 + + buf.eofCheck(i + subTable.numGroups.int * 12) + + for k in 0 ..< subTable.numGroups: + let startCharCode = buf.readUint32(i + 0).swap() + let endCharCode = buf.readUint32(i + 4).swap() + let startGlyphId = buf.readUint32(i + 8).swap() + + for c in startCharCode .. endCharCode: + let glyphId = startGlyphId + (c - startCharCode) + if glyphId > uint16.high: + # TODO: currently only 16 bit glyph ids are supported + raise newException(PixieError, "Found glyph outside of uint16 range: " & $glyphId) + + result.runeToGlyphId[Rune(c)] = uint16(glyphId) + result.glyphIdToRune[uint16(glyphId)] = Rune(c) + + i += 12 + else: - # TODO implement other Windows encodingIDs + # TODO implement other windows formats discard else: # TODO implement other cmap platformIDs diff --git a/tests/fonts/NotoEmoji.otf b/tests/fonts/NotoEmoji.otf new file mode 100644 index 0000000..a91dfbe Binary files /dev/null and b/tests/fonts/NotoEmoji.otf differ diff --git a/tests/fonts/masters/emoji.png b/tests/fonts/masters/emoji.png new file mode 100644 index 0000000..c6b6af4 Binary files /dev/null and b/tests/fonts/masters/emoji.png differ diff --git a/tests/test_fonts.nim b/tests/test_fonts.nim index c4f608d..da50d65 100644 --- a/tests/test_fonts.nim +++ b/tests/test_fonts.nim @@ -4,6 +4,21 @@ proc wh(image: Image): Vec2 = ## Return with and height as a size vector. vec2(image.width.float32, image.height.float32) +block: + var font = readFont("tests/fonts/NotoEmoji.otf") + font.size = 26 + let image = newImage(800, 300) + image.fill(rgba(255, 255, 255, 255)) + image.fillText(font, """ +๐Ÿš‘๐Ÿ‘๐Ÿ‘ญ๐Ÿ”‰๐Ÿšท๐Ÿฆฃ๐Ÿ’†๐Ÿ”๐Ÿ’บ๐Ÿšต๐Ÿ•ฆ๐Ÿ”ฆ๐Ÿ—“๐ŸฆŸ๐Ÿ˜ถ๐Ÿฆ„โŒ›๐Ÿ™๐Ÿ˜„๐Ÿ‡ฝ +๐Ÿ ๐Ÿ’“๐Ÿฆ—๐ŸŽญ๐Ÿ›๐Ÿ”ด๐Ÿซ•๐Ÿงถ๐Ÿ–๐Ÿฆ๐Ÿ‹๐ŸŒ—๐Ÿ›ฌ๐Ÿ•๐Ÿ’ก๐Ÿ‘‰๐ŸŽฏ๐Ÿ•”๐Ÿš๐Ÿšฒ +๐Ÿต๐ŸŽ๐Ÿ’ณ๐Ÿฅฌ๐ŸŸฆ๐Ÿช˜๐Ÿ“ ๐Ÿ“Š๐ŸŽง๐ŸŽฆ๐ŸŽ๐ŸŒŒ๐Ÿชฒ๐Ÿฆฉ๐Ÿคขโ˜Ž๐Ÿšบ๐Ÿšพ๐Ÿ‘บ๐Ÿšƒ +๐Ÿจ๐ŸŒ†๐Ÿฅ‰๐Ÿ’ญ๐Ÿ—ณ๐Ÿฆต๐ŸŸช๐Ÿ“†๐Ÿฅฎโฏ๐Ÿฉด๐Ÿ’ท๐Ÿฆฒโž—๐ŸŒถ๐Ÿงœ๐Ÿ––โฐ๐Ÿ›—๐Ÿ”ป +๐Ÿ“๐Ÿงž๐Ÿ˜ƒ๐ŸŒด๐Ÿšถ๓พ ซ๐Ÿฆ™๐Ÿ”Žโฒ๐Ÿ”ต๐Ÿ–โ˜ฆ๐Ÿ˜ช๐ŸŒฏ๐Ÿ™†๐Ÿ‡บ๐Ÿ˜‚๐Ÿ…๐Ÿ‡ฟ๐ŸšŸ๐Ÿคœ +๐Ÿ“ผ๐Ÿ‘ฐ๐Ÿ๐Ÿ“ฝโ˜ช๐Ÿ”„๐Ÿค๐Ÿ”ง๐Ÿฆธ๐Ÿฐ๐Ÿณ๐Ÿ”œ๐ŸŽฅ๐Ÿš‹๐Ÿ‡ซ๐Ÿฆจ๐Ÿœ๐Ÿ†–๐Ÿค๐Ÿช–โ""") + + image.xray("tests/fonts/masters/emoji.png") + block: var font = readFont("tests/fonts/Roboto-Regular_1.ttf") font.size = 24