up follow livre
This commit is contained in:
parent
b4b4398bb0
commit
3a7a3849ae
12242 changed files with 2564461 additions and 6914 deletions
479
venv/lib/python3.13/site-packages/fontTools/ttx.py
Normal file
479
venv/lib/python3.13/site-packages/fontTools/ttx.py
Normal file
|
|
@ -0,0 +1,479 @@
|
|||
"""\
|
||||
usage: ttx [options] inputfile1 [... inputfileN]
|
||||
|
||||
TTX -- From OpenType To XML And Back
|
||||
|
||||
If an input file is a TrueType or OpenType font file, it will be
|
||||
decompiled to a TTX file (an XML-based text format).
|
||||
If an input file is a TTX file, it will be compiled to whatever
|
||||
format the data is in, a TrueType or OpenType/CFF font file.
|
||||
A special input value of - means read from the standard input.
|
||||
|
||||
Output files are created so they are unique: an existing file is
|
||||
never overwritten.
|
||||
|
||||
General options
|
||||
===============
|
||||
|
||||
-h Help print this message.
|
||||
--version show version and exit.
|
||||
-d <outputfolder> Specify a directory where the output files are
|
||||
to be created.
|
||||
-o <outputfile> Specify a file to write the output to. A special
|
||||
value of - would use the standard output.
|
||||
-f Overwrite existing output file(s), ie. don't append
|
||||
numbers.
|
||||
-v Verbose: more messages will be written to stdout
|
||||
about what is being done.
|
||||
-q Quiet: No messages will be written to stdout about
|
||||
what is being done.
|
||||
-a allow virtual glyphs ID's on compile or decompile.
|
||||
|
||||
Dump options
|
||||
============
|
||||
|
||||
-l List table info: instead of dumping to a TTX file, list
|
||||
some minimal info about each table.
|
||||
-t <table> Specify a table to dump. Multiple -t options
|
||||
are allowed. When no -t option is specified, all tables
|
||||
will be dumped.
|
||||
-x <table> Specify a table to exclude from the dump. Multiple
|
||||
-x options are allowed. -t and -x are mutually exclusive.
|
||||
-s Split tables: save the TTX data into separate TTX files per
|
||||
table and write one small TTX file that contains references
|
||||
to the individual table dumps. This file can be used as
|
||||
input to ttx, as long as the table files are in the
|
||||
same directory.
|
||||
-g Split glyf table: Save the glyf data into separate TTX files
|
||||
per glyph and write a small TTX for the glyf table which
|
||||
contains references to the individual TTGlyph elements.
|
||||
NOTE: specifying -g implies -s (no need for -s together
|
||||
with -g)
|
||||
-i Do NOT disassemble TT instructions: when this option is
|
||||
given, all TrueType programs (glyph programs, the font
|
||||
program and the pre-program) will be written to the TTX
|
||||
file as hex data instead of assembly. This saves some time
|
||||
and makes the TTX file smaller.
|
||||
-z <format> Specify a bitmap data export option for EBDT:
|
||||
{'raw', 'row', 'bitwise', 'extfile'} or for the CBDT:
|
||||
{'raw', 'extfile'} Each option does one of the following:
|
||||
|
||||
-z raw
|
||||
export the bitmap data as a hex dump
|
||||
-z row
|
||||
export each row as hex data
|
||||
-z bitwise
|
||||
export each row as binary in an ASCII art style
|
||||
-z extfile
|
||||
export the data as external files with XML references
|
||||
|
||||
If no export format is specified 'raw' format is used.
|
||||
-e Don't ignore decompilation errors, but show a full traceback
|
||||
and abort.
|
||||
-y <number> Select font number for TrueType Collection (.ttc/.otc),
|
||||
starting from 0.
|
||||
--unicodedata <UnicodeData.txt>
|
||||
Use custom database file to write character names in the
|
||||
comments of the cmap TTX output.
|
||||
--newline <value>
|
||||
Control how line endings are written in the XML file. It
|
||||
can be 'LF', 'CR', or 'CRLF'. If not specified, the
|
||||
default platform-specific line endings are used.
|
||||
|
||||
Compile options
|
||||
===============
|
||||
|
||||
-m Merge with TrueType-input-file: specify a TrueType or
|
||||
OpenType font file to be merged with the TTX file. This
|
||||
option is only valid when at most one TTX file is specified.
|
||||
-b Don't recalc glyph bounding boxes: use the values in the
|
||||
TTX file as-is.
|
||||
--recalc-timestamp
|
||||
Set font 'modified' timestamp to current time.
|
||||
By default, the modification time of the TTX file will be
|
||||
used.
|
||||
--no-recalc-timestamp
|
||||
Keep the original font 'modified' timestamp.
|
||||
--flavor <type>
|
||||
Specify flavor of output font file. May be 'woff' or 'woff2'.
|
||||
Note that WOFF2 requires the Brotli Python extension,
|
||||
available at https://github.com/google/brotli
|
||||
--with-zopfli
|
||||
Use Zopfli instead of Zlib to compress WOFF. The Python
|
||||
extension is available at https://pypi.python.org/pypi/zopfli
|
||||
--optimize-font-speed
|
||||
Enable optimizations that prioritize speed over file size.
|
||||
This mainly affects how glyf t able and gvar / VARC tables are
|
||||
compiled. The produced fonts will be larger, but rendering
|
||||
performance will be improved with HarfBuzz and other text
|
||||
layout engines.
|
||||
"""
|
||||
|
||||
from fontTools.ttLib import OPTIMIZE_FONT_SPEED, TTFont, TTLibError
|
||||
from fontTools.misc.macCreatorType import getMacCreatorAndType
|
||||
from fontTools.unicode import setUnicodeData
|
||||
from fontTools.misc.textTools import Tag, tostr
|
||||
from fontTools.misc.timeTools import timestampSinceEpoch
|
||||
from fontTools.misc.loggingTools import Timer
|
||||
from fontTools.misc.cliTools import makeOutputFileName
|
||||
import os
|
||||
import sys
|
||||
import getopt
|
||||
import re
|
||||
import logging
|
||||
|
||||
|
||||
log = logging.getLogger("fontTools.ttx")
|
||||
|
||||
opentypeheaderRE = re.compile("""sfntVersion=['"]OTTO["']""")
|
||||
|
||||
|
||||
class Options(object):
|
||||
listTables = False
|
||||
outputDir = None
|
||||
outputFile = None
|
||||
overWrite = False
|
||||
verbose = False
|
||||
quiet = False
|
||||
splitTables = False
|
||||
splitGlyphs = False
|
||||
disassembleInstructions = True
|
||||
mergeFile = None
|
||||
recalcBBoxes = True
|
||||
ignoreDecompileErrors = True
|
||||
bitmapGlyphDataFormat = "raw"
|
||||
unicodedata = None
|
||||
newlinestr = "\n"
|
||||
recalcTimestamp = None
|
||||
flavor = None
|
||||
useZopfli = False
|
||||
optimizeFontSpeed = False
|
||||
|
||||
def __init__(self, rawOptions, numFiles):
|
||||
self.onlyTables = []
|
||||
self.skipTables = []
|
||||
self.fontNumber = -1
|
||||
for option, value in rawOptions:
|
||||
# general options
|
||||
if option == "-h":
|
||||
print(__doc__)
|
||||
sys.exit(0)
|
||||
elif option == "--version":
|
||||
from fontTools import version
|
||||
|
||||
print(version)
|
||||
sys.exit(0)
|
||||
elif option == "-d":
|
||||
if not os.path.isdir(value):
|
||||
raise getopt.GetoptError(
|
||||
"The -d option value must be an existing directory"
|
||||
)
|
||||
self.outputDir = value
|
||||
elif option == "-o":
|
||||
self.outputFile = value
|
||||
elif option == "-f":
|
||||
self.overWrite = True
|
||||
elif option == "-v":
|
||||
self.verbose = True
|
||||
elif option == "-q":
|
||||
self.quiet = True
|
||||
# dump options
|
||||
elif option == "-l":
|
||||
self.listTables = True
|
||||
elif option == "-t":
|
||||
# pad with space if table tag length is less than 4
|
||||
value = value.ljust(4)
|
||||
self.onlyTables.append(value)
|
||||
elif option == "-x":
|
||||
# pad with space if table tag length is less than 4
|
||||
value = value.ljust(4)
|
||||
self.skipTables.append(value)
|
||||
elif option == "-s":
|
||||
self.splitTables = True
|
||||
elif option == "-g":
|
||||
# -g implies (and forces) splitTables
|
||||
self.splitGlyphs = True
|
||||
self.splitTables = True
|
||||
elif option == "-i":
|
||||
self.disassembleInstructions = False
|
||||
elif option == "-z":
|
||||
validOptions = ("raw", "row", "bitwise", "extfile")
|
||||
if value not in validOptions:
|
||||
raise getopt.GetoptError(
|
||||
"-z does not allow %s as a format. Use %s"
|
||||
% (option, validOptions)
|
||||
)
|
||||
self.bitmapGlyphDataFormat = value
|
||||
elif option == "-y":
|
||||
self.fontNumber = int(value)
|
||||
# compile options
|
||||
elif option == "-m":
|
||||
self.mergeFile = value
|
||||
elif option == "-b":
|
||||
self.recalcBBoxes = False
|
||||
elif option == "-e":
|
||||
self.ignoreDecompileErrors = False
|
||||
elif option == "--unicodedata":
|
||||
self.unicodedata = value
|
||||
elif option == "--newline":
|
||||
validOptions = ("LF", "CR", "CRLF")
|
||||
if value == "LF":
|
||||
self.newlinestr = "\n"
|
||||
elif value == "CR":
|
||||
self.newlinestr = "\r"
|
||||
elif value == "CRLF":
|
||||
self.newlinestr = "\r\n"
|
||||
else:
|
||||
raise getopt.GetoptError(
|
||||
"Invalid choice for --newline: %r (choose from %s)"
|
||||
% (value, ", ".join(map(repr, validOptions)))
|
||||
)
|
||||
elif option == "--recalc-timestamp":
|
||||
self.recalcTimestamp = True
|
||||
elif option == "--no-recalc-timestamp":
|
||||
self.recalcTimestamp = False
|
||||
elif option == "--flavor":
|
||||
self.flavor = value
|
||||
elif option == "--with-zopfli":
|
||||
self.useZopfli = True
|
||||
elif option == "--optimize-font-speed":
|
||||
self.optimizeFontSpeed = True
|
||||
if self.verbose and self.quiet:
|
||||
raise getopt.GetoptError("-q and -v options are mutually exclusive")
|
||||
if self.verbose:
|
||||
self.logLevel = logging.DEBUG
|
||||
elif self.quiet:
|
||||
self.logLevel = logging.WARNING
|
||||
else:
|
||||
self.logLevel = logging.INFO
|
||||
if self.mergeFile and self.flavor:
|
||||
raise getopt.GetoptError("-m and --flavor options are mutually exclusive")
|
||||
if self.onlyTables and self.skipTables:
|
||||
raise getopt.GetoptError("-t and -x options are mutually exclusive")
|
||||
if self.mergeFile and numFiles > 1:
|
||||
raise getopt.GetoptError(
|
||||
"Must specify exactly one TTX source file when using -m"
|
||||
)
|
||||
if self.flavor != "woff" and self.useZopfli:
|
||||
raise getopt.GetoptError("--with-zopfli option requires --flavor 'woff'")
|
||||
|
||||
|
||||
def ttList(input, output, options):
|
||||
ttf = TTFont(input, fontNumber=options.fontNumber, lazy=True)
|
||||
reader = ttf.reader
|
||||
tags = sorted(reader.keys())
|
||||
print('Listing table info for "%s":' % input)
|
||||
format = " %4s %10s %8s %8s"
|
||||
print(format % ("tag ", " checksum", " length", " offset"))
|
||||
print(format % ("----", "----------", "--------", "--------"))
|
||||
for tag in tags:
|
||||
entry = reader.tables[tag]
|
||||
if ttf.flavor == "woff2":
|
||||
# WOFF2 doesn't store table checksums, so they must be calculated
|
||||
from fontTools.ttLib.sfnt import calcChecksum
|
||||
|
||||
data = entry.loadData(reader.transformBuffer)
|
||||
checkSum = calcChecksum(data)
|
||||
else:
|
||||
checkSum = int(entry.checkSum)
|
||||
if checkSum < 0:
|
||||
checkSum = checkSum + 0x100000000
|
||||
checksum = "0x%08X" % checkSum
|
||||
print(format % (tag, checksum, entry.length, entry.offset))
|
||||
print()
|
||||
ttf.close()
|
||||
|
||||
|
||||
@Timer(log, "Done dumping TTX in %(time).3f seconds")
|
||||
def ttDump(input, output, options):
|
||||
input_name = input
|
||||
if input == "-":
|
||||
input, input_name = sys.stdin.buffer, sys.stdin.name
|
||||
output_name = output
|
||||
if output == "-":
|
||||
output, output_name = sys.stdout, sys.stdout.name
|
||||
log.info('Dumping "%s" to "%s"...', input_name, output_name)
|
||||
if options.unicodedata:
|
||||
setUnicodeData(options.unicodedata)
|
||||
ttf = TTFont(
|
||||
input,
|
||||
0,
|
||||
ignoreDecompileErrors=options.ignoreDecompileErrors,
|
||||
fontNumber=options.fontNumber,
|
||||
)
|
||||
ttf.saveXML(
|
||||
output,
|
||||
tables=options.onlyTables,
|
||||
skipTables=options.skipTables,
|
||||
splitTables=options.splitTables,
|
||||
splitGlyphs=options.splitGlyphs,
|
||||
disassembleInstructions=options.disassembleInstructions,
|
||||
bitmapGlyphDataFormat=options.bitmapGlyphDataFormat,
|
||||
newlinestr=options.newlinestr,
|
||||
)
|
||||
ttf.close()
|
||||
|
||||
|
||||
@Timer(log, "Done compiling TTX in %(time).3f seconds")
|
||||
def ttCompile(input, output, options):
|
||||
input_name = input
|
||||
if input == "-":
|
||||
input, input_name = sys.stdin, sys.stdin.name
|
||||
output_name = output
|
||||
if output == "-":
|
||||
output, output_name = sys.stdout.buffer, sys.stdout.name
|
||||
log.info('Compiling "%s" to "%s"...' % (input_name, output))
|
||||
if options.useZopfli:
|
||||
from fontTools.ttLib import sfnt
|
||||
|
||||
sfnt.USE_ZOPFLI = True
|
||||
ttf = TTFont(
|
||||
options.mergeFile,
|
||||
flavor=options.flavor,
|
||||
recalcBBoxes=options.recalcBBoxes,
|
||||
recalcTimestamp=options.recalcTimestamp,
|
||||
)
|
||||
if options.optimizeFontSpeed:
|
||||
ttf.cfg[OPTIMIZE_FONT_SPEED] = options.optimizeFontSpeed
|
||||
ttf.importXML(input)
|
||||
|
||||
if options.recalcTimestamp is None and "head" in ttf and input is not sys.stdin:
|
||||
# use TTX file modification time for head "modified" timestamp
|
||||
mtime = os.path.getmtime(input)
|
||||
ttf["head"].modified = timestampSinceEpoch(mtime)
|
||||
|
||||
ttf.save(output)
|
||||
|
||||
|
||||
def guessFileType(fileName):
|
||||
if fileName == "-":
|
||||
header = sys.stdin.buffer.peek(256)
|
||||
ext = ""
|
||||
else:
|
||||
base, ext = os.path.splitext(fileName)
|
||||
try:
|
||||
with open(fileName, "rb") as f:
|
||||
header = f.read(256)
|
||||
except IOError:
|
||||
return None
|
||||
|
||||
if header.startswith(b"\xef\xbb\xbf<?xml"):
|
||||
header = header.lstrip(b"\xef\xbb\xbf")
|
||||
cr, tp = getMacCreatorAndType(fileName)
|
||||
if tp in ("sfnt", "FFIL"):
|
||||
return "TTF"
|
||||
if ext == ".dfont":
|
||||
return "TTF"
|
||||
head = Tag(header[:4])
|
||||
if head == "OTTO":
|
||||
return "OTF"
|
||||
elif head == "ttcf":
|
||||
return "TTC"
|
||||
elif head in ("\0\1\0\0", "true"):
|
||||
return "TTF"
|
||||
elif head == "wOFF":
|
||||
return "WOFF"
|
||||
elif head == "wOF2":
|
||||
return "WOFF2"
|
||||
elif head == "<?xm":
|
||||
# Use 'latin1' because that can't fail.
|
||||
header = tostr(header, "latin1")
|
||||
if opentypeheaderRE.search(header):
|
||||
return "OTX"
|
||||
else:
|
||||
return "TTX"
|
||||
return None
|
||||
|
||||
|
||||
def parseOptions(args):
|
||||
rawOptions, files = getopt.gnu_getopt(
|
||||
args,
|
||||
"ld:o:fvqht:x:sgim:z:baey:",
|
||||
[
|
||||
"unicodedata=",
|
||||
"recalc-timestamp",
|
||||
"no-recalc-timestamp",
|
||||
"flavor=",
|
||||
"version",
|
||||
"with-zopfli",
|
||||
"newline=",
|
||||
"optimize-font-speed",
|
||||
],
|
||||
)
|
||||
|
||||
options = Options(rawOptions, len(files))
|
||||
jobs = []
|
||||
|
||||
if not files:
|
||||
raise getopt.GetoptError("Must specify at least one input file")
|
||||
|
||||
for input in files:
|
||||
if input != "-" and not os.path.isfile(input):
|
||||
raise getopt.GetoptError('File not found: "%s"' % input)
|
||||
tp = guessFileType(input)
|
||||
if tp in ("OTF", "TTF", "TTC", "WOFF", "WOFF2"):
|
||||
extension = ".ttx"
|
||||
if options.listTables:
|
||||
action = ttList
|
||||
else:
|
||||
action = ttDump
|
||||
elif tp == "TTX":
|
||||
extension = "." + options.flavor if options.flavor else ".ttf"
|
||||
action = ttCompile
|
||||
elif tp == "OTX":
|
||||
extension = "." + options.flavor if options.flavor else ".otf"
|
||||
action = ttCompile
|
||||
else:
|
||||
raise getopt.GetoptError('Unknown file type: "%s"' % input)
|
||||
|
||||
if options.outputFile:
|
||||
output = options.outputFile
|
||||
else:
|
||||
if input == "-":
|
||||
raise getopt.GetoptError("Must provide -o when reading from stdin")
|
||||
output = makeOutputFileName(
|
||||
input, options.outputDir, extension, options.overWrite
|
||||
)
|
||||
# 'touch' output file to avoid race condition in choosing file names
|
||||
if action != ttList:
|
||||
open(output, "a").close()
|
||||
jobs.append((action, input, output))
|
||||
return jobs, options
|
||||
|
||||
|
||||
def process(jobs, options):
|
||||
for action, input, output in jobs:
|
||||
action(input, output, options)
|
||||
|
||||
|
||||
def main(args=None):
|
||||
"""Convert OpenType fonts to XML and back"""
|
||||
from fontTools import configLogger
|
||||
|
||||
if args is None:
|
||||
args = sys.argv[1:]
|
||||
try:
|
||||
jobs, options = parseOptions(args)
|
||||
except getopt.GetoptError as e:
|
||||
print("%s\nERROR: %s" % (__doc__, e), file=sys.stderr)
|
||||
sys.exit(2)
|
||||
|
||||
configLogger(level=options.logLevel)
|
||||
|
||||
try:
|
||||
process(jobs, options)
|
||||
except KeyboardInterrupt:
|
||||
log.error("(Cancelled.)")
|
||||
sys.exit(1)
|
||||
except SystemExit:
|
||||
raise
|
||||
except TTLibError as e:
|
||||
log.error(e)
|
||||
sys.exit(1)
|
||||
except:
|
||||
log.exception("Unhandled exception has occurred")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Loading…
Add table
Add a link
Reference in a new issue