commit
6f2edb0ad3
5 changed files with 130 additions and 14 deletions
|
@ -9,10 +9,11 @@ requires "nim >= 1.4.8"
|
||||||
requires "vmath >= 1.1.4"
|
requires "vmath >= 1.1.4"
|
||||||
requires "chroma >= 0.2.5"
|
requires "chroma >= 0.2.5"
|
||||||
requires "zippy >= 0.9.7"
|
requires "zippy >= 0.9.7"
|
||||||
requires "flatty >= 0.2.4"
|
requires "flatty >= 0.3.0"
|
||||||
requires "nimsimd >= 1.0.0"
|
requires "nimsimd >= 1.0.0"
|
||||||
requires "bumpy >= 1.1.0"
|
requires "bumpy >= 1.1.0"
|
||||||
|
|
||||||
|
|
||||||
task bindings, "Generate bindings":
|
task bindings, "Generate bindings":
|
||||||
|
|
||||||
proc compile(libName: string, flags = "") =
|
proc compile(libName: string, flags = "") =
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import flatty/binny, math, pixie/common, pixie/paths, sets, strutils, tables,
|
import flatty/binny, flatty/encode, math, pixie/common, pixie/paths, sets,
|
||||||
unicode, vmath
|
strutils, tables, unicode, vmath
|
||||||
|
|
||||||
## See https://docs.microsoft.com/en-us/typography/opentype/spec/
|
## See https://docs.microsoft.com/en-us/typography/opentype/spec/
|
||||||
|
|
||||||
|
@ -86,6 +86,7 @@ type
|
||||||
nameID*: uint16
|
nameID*: uint16
|
||||||
length*: uint16
|
length*: uint16
|
||||||
offset*: uint16
|
offset*: uint16
|
||||||
|
text*: string
|
||||||
|
|
||||||
NameTable* = ref object
|
NameTable* = ref object
|
||||||
format*: uint16
|
format*: uint16
|
||||||
|
@ -655,6 +656,17 @@ proc parseNameTable(buf: string, offset: int): NameTable =
|
||||||
record.nameID = buf.readUint16(i + 6).swap()
|
record.nameID = buf.readUint16(i + 6).swap()
|
||||||
record.length = buf.readUint16(i + 8).swap()
|
record.length = buf.readUint16(i + 8).swap()
|
||||||
record.offset = buf.readUint16(i + 10).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.platformID == 3 and
|
||||||
|
record.encodingID == 1 and
|
||||||
|
record.languageID == 1033:
|
||||||
|
record.text = fromUTF16BE(record.text)
|
||||||
|
|
||||||
|
record.text = record.text
|
||||||
|
result.nameRecords.add(record)
|
||||||
i += 12
|
i += 12
|
||||||
|
|
||||||
proc parseOS2Table(buf: string, offset: int): OS2Table =
|
proc parseOS2Table(buf: string, offset: int): OS2Table =
|
||||||
|
@ -923,10 +935,19 @@ const cffStandardStrings = [
|
||||||
const TOP_DICT_META = {
|
const TOP_DICT_META = {
|
||||||
0: "version",
|
0: "version",
|
||||||
1: "notice",
|
1: "notice",
|
||||||
1200: "copyright",
|
|
||||||
2: "fullName",
|
2: "fullName",
|
||||||
3: "familyName",
|
3: "familyName",
|
||||||
4: "weight",
|
4: "weight",
|
||||||
|
5: "fontBBox",
|
||||||
|
|
||||||
|
13: "uniqueId",
|
||||||
|
14: "xuid",
|
||||||
|
15: "charset",
|
||||||
|
16: "encoding",
|
||||||
|
17: "charStrings",
|
||||||
|
18: "private",
|
||||||
|
|
||||||
|
1200: "copyright",
|
||||||
1201: "isFixedPitch",
|
1201: "isFixedPitch",
|
||||||
1202: "italicAngle",
|
1202: "italicAngle",
|
||||||
1203: "underlinePosition",
|
1203: "underlinePosition",
|
||||||
|
@ -934,14 +955,7 @@ const TOP_DICT_META = {
|
||||||
1205: "paintType",
|
1205: "paintType",
|
||||||
1206: "charstringType",
|
1206: "charstringType",
|
||||||
1207: "fontMatrix",
|
1207: "fontMatrix",
|
||||||
13: "uniqueId",
|
|
||||||
5: "fontBBox",
|
|
||||||
1208: "strokeWidth",
|
1208: "strokeWidth",
|
||||||
14: "xuid",
|
|
||||||
15: "charset",
|
|
||||||
16: "encoding",
|
|
||||||
17: "charStrings",
|
|
||||||
18: "private",
|
|
||||||
1230: "ros",
|
1230: "ros",
|
||||||
1231: "cidFontVersion",
|
1231: "cidFontVersion",
|
||||||
1232: "cidFontRevision",
|
1232: "cidFontRevision",
|
||||||
|
@ -2467,11 +2481,19 @@ proc isCCW*(opentype: OpenType): bool {.inline.} =
|
||||||
## CFF - true - counterclockwise
|
## CFF - true - counterclockwise
|
||||||
opentype.cff == nil
|
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 == 6 and record.languageID == 1033:
|
||||||
|
return record.text
|
||||||
|
|
||||||
|
proc parseOpenType*(buf: string, startLoc = 0): OpenType {.raises: [PixieError].} =
|
||||||
result = OpenType()
|
result = OpenType()
|
||||||
result.buf = buf
|
result.buf = buf
|
||||||
|
|
||||||
var i: int
|
var i: int = startLoc
|
||||||
|
|
||||||
buf.eofCheck(i + 12)
|
buf.eofCheck(i + 12)
|
||||||
|
|
||||||
|
@ -2527,5 +2549,32 @@ proc parseOpenType*(buf: string): OpenType {.raises: [PixieError].} =
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
raise newException(PixieError, "Missing required font table: " & e.msg)
|
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):
|
when defined(release):
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
|
@ -706,6 +706,21 @@ proc readTypeface*(filePath: string): Typeface {.raises: [PixieError].} =
|
||||||
|
|
||||||
result.filePath = filePath
|
result.filePath = filePath
|
||||||
|
|
||||||
|
proc readTypefaces*(filePath: string): seq[Typeface] {.raises: [PixieError].} =
|
||||||
|
## Loads a OpenType Collection (.ttc).
|
||||||
|
try:
|
||||||
|
for opentype in parseOpenTypeCollection(readFile(filePath)):
|
||||||
|
let typeface = Typeface()
|
||||||
|
typeface.opentype = opentype
|
||||||
|
result.add(typeface)
|
||||||
|
except IOError as e:
|
||||||
|
raise newException(PixieError, e.msg, e)
|
||||||
|
|
||||||
|
proc name*(typeface: Typeface): string =
|
||||||
|
## Returns the name of the font.
|
||||||
|
if typeface.opentype != nil:
|
||||||
|
return typeface.opentype.fullName
|
||||||
|
|
||||||
proc readFont*(filePath: string): Font {.raises: [PixieError].} =
|
proc readFont*(filePath: string): Font {.raises: [PixieError].} =
|
||||||
## Loads a font from a file.
|
## Loads a font from a file.
|
||||||
newFont(readTypeface(filePath))
|
newFont(readTypeface(filePath))
|
||||||
|
|
BIN
tests/fonts/PTSans.ttc
Normal file
BIN
tests/fonts/PTSans.ttc
Normal file
Binary file not shown.
|
@ -1,4 +1,4 @@
|
||||||
import pixie, pixie/fileformats/png, strformat, unicode
|
import pixie, pixie/fileformats/png, strformat, unicode, os
|
||||||
|
|
||||||
proc wh(image: Image): Vec2 =
|
proc wh(image: Image): Vec2 =
|
||||||
## Return with and height as a size vector.
|
## Return with and height as a size vector.
|
||||||
|
@ -1196,3 +1196,54 @@ block:
|
||||||
)
|
)
|
||||||
|
|
||||||
doDiff(image, "customlineheight")
|
doDiff(image, "customlineheight")
|
||||||
|
|
||||||
|
block:
|
||||||
|
var font = readTypefaces("tests/fonts/PTSans.ttc")[0].newFont
|
||||||
|
font.size = 72
|
||||||
|
let image = newImage(200, 100)
|
||||||
|
image.fill(rgba(255, 255, 255, 255))
|
||||||
|
image.fillText(font, "AbCd")
|
||||||
|
|
||||||
|
block:
|
||||||
|
var typefaces = readTypefaces("tests/fonts/PTSans.ttc")
|
||||||
|
for i, typeface in typefaces:
|
||||||
|
echo i, ": ", typeface.name
|
||||||
|
|
||||||
|
when defined(windows):
|
||||||
|
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:
|
||||||
|
if fileExists(file):
|
||||||
|
echo file
|
||||||
|
var typefaces = readTypefaces(file)
|
||||||
|
for i, typeface in typefaces:
|
||||||
|
echo i, ": ", typeface.name
|
||||||
|
|
Loading…
Reference in a new issue