Add TTC parser.

This commit is contained in:
treeform 2022-05-14 09:42:22 -07:00
parent 33fa4e6390
commit dffe86074f
4 changed files with 120 additions and 2 deletions

View file

@ -86,6 +86,7 @@ type
nameID*: uint16
length*: uint16
offset*: uint16
text*: string
NameTable* = ref object
format*: uint16
@ -429,6 +430,23 @@ proc readVersion16Dot16(buf: string, offset: int): float32 =
failUnsupported("invalid version format")
majorDigit.float32 + minorDigit.float32 / 10
proc convertUTF16(input: string): string =
## Converts UTF16 Big Endian to UTF8 string.
var i = 0
while i < input.len:
var u1 = input.readUInt16(i)
i += 2
if u1 - 0xd800 >= 0x800:
result.add Rune(u1.int)
else:
var u2 = input.readUInt16(i)
i += 2
if ((u1 and 0xfc00) == 0xd800) and ((u2 and 0xfc00) == 0xdc00):
result.add Rune((u1.uint32 shl 10) + u2.uint32 - 0x35fdc00)
else:
# Error, produce tofu character.
result.add ""
proc parseCmapTable(buf: string, offset: int): CmapTable =
var i = offset
buf.eofCheck(i + 4)
@ -655,6 +673,14 @@ proc parseNameTable(buf: string, offset: int): NameTable =
record.nameID = buf.readUint16(i + 6).swap()
record.length = buf.readUint16(i + 8).swap()
record.offset = buf.readUint16(i + 10).swap()
record.text = buf[
(offset + result.stringOffset.int + record.offset.int) ..<
(offset + result.stringOffset.int + record.offset.int + record.length.int)
]
if record.encodingID in {1, 3}:
record.text = convertUTF16(record.text)
record.text = record.text
result.nameRecords.add(record)
i += 12
proc parseOS2Table(buf: string, offset: int): OS2Table =
@ -2467,11 +2493,19 @@ proc isCCW*(opentype: OpenType): bool {.inline.} =
## CFF - true - counterclockwise
opentype.cff == nil
proc parseOpenType*(buf: string): OpenType {.raises: [PixieError].} =
proc fullName*(opentype: OpenType): string =
## Returns full name of the font if available.
if opentype.cff != nil:
return opentype.cff.topDict.fullName
for record in opentype.name.nameRecords:
if record.nameID == 1:
return record.text
proc parseOpenType*(buf: string, startLoc = 0): OpenType {.raises: [PixieError].} =
result = OpenType()
result.buf = buf
var i: int
var i: int = startLoc
buf.eofCheck(i + 12)
@ -2527,5 +2561,32 @@ proc parseOpenType*(buf: string): OpenType {.raises: [PixieError].} =
except KeyError as e:
raise newException(PixieError, "Missing required font table: " & e.msg)
proc parseOpenTypeCollection*(buf: string): seq[OpenType] {.raises: [PixieError].} =
## Reads a true/open type collection and returns seq of OpenType files.
var i: int
buf.eofCheck(i + 12)
let tag = buf[0 ..< 4]
if tag != "ttcf":
failUnsupported("invalid ttc file")
let
majorVersion = buf.readUint16(i + 4).swap()
minorVersion = buf.readUint16(i + 6).swap()
numFonts = buf.readUint32(i + 8).swap()
if majorVersion notin {1, 2} and minorVersion != 0:
failUnsupported("ttc version")
var tableDirectoryOffsets: seq[uint32]
i += 12
for n in 0 ..< numFonts:
buf.eofCheck(i + 4)
tableDirectoryOffsets.add(buf.readUint32(i).swap())
i += 4
for dir in tableDirectoryOffsets:
result.add(parseOpenType(buf, dir.int))
when defined(release):
{.pop.}

View file

@ -496,6 +496,10 @@ proc parseOtf*(buf: string): Typeface {.raises: [PixieError].} =
proc parseTtf*(buf: string): Typeface {.raises: [PixieError].} =
parseOtf(buf)
proc parseTtc*(buf: string): Typeface {.raises: [PixieError].} =
result = Typeface()
result.opentype = parseOpenTypeCollection(buf)[0]
proc parseSvgFont*(buf: string): Typeface {.raises: [PixieError].} =
result = Typeface()
result.svgFont = svgfont.parseSvgFont(buf)
@ -697,6 +701,8 @@ proc readTypeface*(filePath: string): Typeface {.raises: [PixieError].} =
parseTtf(readFile(filePath))
of ".otf":
parseOtf(readFile(filePath))
of ".ttc":
parseTtc(readFile(filePath))
of ".svg":
parseSvgFont(readFile(filePath))
else:

Binary file not shown.

51
tests/test_fonts_ttc.nim Normal file
View file

@ -0,0 +1,51 @@
import pixie, strformat, unicode, pixie/fontformats/opentype
block:
var font = readFont("/Windows/Fonts/simsun.ttc")
font.size = 72
let image = newImage(220, 100)
image.fill(rgba(255, 255, 255, 255))
image.fillText(font, "大目鳥")
image.writeFile("ttc.png")
block:
var fonts = parseOpenTypeCollection(readFile("/Windows/Fonts/simsun.ttc"))
for font in fonts:
echo font.fullName
block:
let files = @[
"/Windows/Fonts/batang.ttc",
"/Windows/Fonts/BIZ-UDGothicB.ttc",
"/Windows/Fonts/BIZ-UDGothicR.ttc",
"/Windows/Fonts/BIZ-UDMinchoM.ttc",
"/Windows/Fonts/cambria.ttc",
"/Windows/Fonts/gulim.ttc",
"/Windows/Fonts/meiryo.ttc",
"/Windows/Fonts/meiryob.ttc",
"/Windows/Fonts/mingliub.ttc",
"/Windows/Fonts/msgothic.ttc",
"/Windows/Fonts/msjh.ttc",
"/Windows/Fonts/msjhbd.ttc",
"/Windows/Fonts/msjhl.ttc",
"/Windows/Fonts/msmincho.ttc",
"/Windows/Fonts/msyh.ttc",
"/Windows/Fonts/msyhbd.ttc",
"/Windows/Fonts/msyhl.ttc",
"/Windows/Fonts/simsun.ttc",
"/Windows/Fonts/Sitka.ttc",
"/Windows/Fonts/SitkaB.ttc",
"/Windows/Fonts/SitkaI.ttc",
"/Windows/Fonts/SitkaZ.ttc",
"/Windows/Fonts/UDDigiKyokashoN-B.ttc",
"/Windows/Fonts/UDDigiKyokashoN-R.ttc",
"/Windows/Fonts/YuGothB.ttc",
"/Windows/Fonts/YuGothL.ttc",
"/Windows/Fonts/YuGothM.ttc",
"/Windows/Fonts/YuGothR.ttc",
]
for file in files:
echo file
var fonts = parseOpenTypeCollection(readFile(file))
for i, font in fonts:
echo " ", i, ": ", font.fullName