Merge pull request #3020 from fonttools/ttx-stdin
[ttx] support reading font/xml file from standard input as '-'
This commit is contained in:
commit
e26da961ec
@ -146,11 +146,13 @@ class TTFont(object):
|
|||||||
else:
|
else:
|
||||||
# assume "file" is a readable file object
|
# assume "file" is a readable file object
|
||||||
closeStream = False
|
closeStream = False
|
||||||
file.seek(0)
|
if file.seekable():
|
||||||
|
file.seek(0)
|
||||||
|
|
||||||
if not self.lazy:
|
if not self.lazy:
|
||||||
# read input file in memory and wrap a stream around it to allow overwriting
|
# read input file in memory and wrap a stream around it to allow overwriting
|
||||||
file.seek(0)
|
if file.seekable():
|
||||||
|
file.seek(0)
|
||||||
tmp = BytesIO(file.read())
|
tmp = BytesIO(file.read())
|
||||||
if hasattr(file, "name"):
|
if hasattr(file, "name"):
|
||||||
# save reference to input file name
|
# save reference to input file name
|
||||||
|
@ -5,8 +5,9 @@ TTX -- From OpenType To XML And Back
|
|||||||
|
|
||||||
If an input file is a TrueType or OpenType font file, it will be
|
If an input file is a TrueType or OpenType font file, it will be
|
||||||
decompiled to a TTX file (an XML-based text format).
|
decompiled to a TTX file (an XML-based text format).
|
||||||
If an input file is a TTX file, it will be compiled to whatever
|
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.
|
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
|
Output files are created so they are unique: an existing file is
|
||||||
never overwritten.
|
never overwritten.
|
||||||
@ -278,7 +279,13 @@ def ttList(input, output, options):
|
|||||||
|
|
||||||
@Timer(log, "Done dumping TTX in %(time).3f seconds")
|
@Timer(log, "Done dumping TTX in %(time).3f seconds")
|
||||||
def ttDump(input, output, options):
|
def ttDump(input, output, options):
|
||||||
log.info('Dumping "%s" to "%s"...', input, output)
|
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:
|
if options.unicodedata:
|
||||||
setUnicodeData(options.unicodedata)
|
setUnicodeData(options.unicodedata)
|
||||||
ttf = TTFont(
|
ttf = TTFont(
|
||||||
@ -302,7 +309,13 @@ def ttDump(input, output, options):
|
|||||||
|
|
||||||
@Timer(log, "Done compiling TTX in %(time).3f seconds")
|
@Timer(log, "Done compiling TTX in %(time).3f seconds")
|
||||||
def ttCompile(input, output, options):
|
def ttCompile(input, output, options):
|
||||||
log.info('Compiling "%s" to "%s"...' % (input, output))
|
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:
|
if options.useZopfli:
|
||||||
from fontTools.ttLib import sfnt
|
from fontTools.ttLib import sfnt
|
||||||
|
|
||||||
@ -315,7 +328,7 @@ def ttCompile(input, output, options):
|
|||||||
)
|
)
|
||||||
ttf.importXML(input)
|
ttf.importXML(input)
|
||||||
|
|
||||||
if options.recalcTimestamp is None and "head" in ttf:
|
if options.recalcTimestamp is None and "head" in ttf and input is not sys.stdin:
|
||||||
# use TTX file modification time for head "modified" timestamp
|
# use TTX file modification time for head "modified" timestamp
|
||||||
mtime = os.path.getmtime(input)
|
mtime = os.path.getmtime(input)
|
||||||
ttf["head"].modified = timestampSinceEpoch(mtime)
|
ttf["head"].modified = timestampSinceEpoch(mtime)
|
||||||
@ -324,12 +337,16 @@ def ttCompile(input, output, options):
|
|||||||
|
|
||||||
|
|
||||||
def guessFileType(fileName):
|
def guessFileType(fileName):
|
||||||
base, ext = os.path.splitext(fileName)
|
if fileName == "-":
|
||||||
try:
|
header = sys.stdin.buffer.peek(256)
|
||||||
with open(fileName, "rb") as f:
|
ext = ""
|
||||||
header = f.read(256)
|
else:
|
||||||
except IOError:
|
base, ext = os.path.splitext(fileName)
|
||||||
return None
|
try:
|
||||||
|
with open(fileName, "rb") as f:
|
||||||
|
header = f.read(256)
|
||||||
|
except IOError:
|
||||||
|
return None
|
||||||
|
|
||||||
if header.startswith(b"\xef\xbb\xbf<?xml"):
|
if header.startswith(b"\xef\xbb\xbf<?xml"):
|
||||||
header = header.lstrip(b"\xef\xbb\xbf")
|
header = header.lstrip(b"\xef\xbb\xbf")
|
||||||
@ -381,7 +398,7 @@ def parseOptions(args):
|
|||||||
raise getopt.GetoptError("Must specify at least one input file")
|
raise getopt.GetoptError("Must specify at least one input file")
|
||||||
|
|
||||||
for input in files:
|
for input in files:
|
||||||
if not os.path.isfile(input):
|
if input != "-" and not os.path.isfile(input):
|
||||||
raise getopt.GetoptError('File not found: "%s"' % input)
|
raise getopt.GetoptError('File not found: "%s"' % input)
|
||||||
tp = guessFileType(input)
|
tp = guessFileType(input)
|
||||||
if tp in ("OTF", "TTF", "TTC", "WOFF", "WOFF2"):
|
if tp in ("OTF", "TTF", "TTC", "WOFF", "WOFF2"):
|
||||||
@ -402,6 +419,8 @@ def parseOptions(args):
|
|||||||
if options.outputFile:
|
if options.outputFile:
|
||||||
output = options.outputFile
|
output = options.outputFile
|
||||||
else:
|
else:
|
||||||
|
if input == "-":
|
||||||
|
raise getopt.GetoptError("Must provide -o when reading from stdin")
|
||||||
output = makeOutputFileName(
|
output = makeOutputFileName(
|
||||||
input, options.outputDir, extension, options.overWrite
|
input, options.outputDir, extension, options.overWrite
|
||||||
)
|
)
|
||||||
|
@ -6,9 +6,11 @@ import getopt
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -996,6 +998,24 @@ def test_main_base_exception(tmpdir, monkeypatch, caplog):
|
|||||||
assert "Unhandled exception has occurred" in caplog.text
|
assert "Unhandled exception has occurred" in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
def test_main_ttf_dump_stdin_to_stdout(tmp_path):
|
||||||
|
inpath = Path("Tests").joinpath("ttx", "data", "TestTTF.ttf")
|
||||||
|
outpath = tmp_path / "TestTTF.ttx"
|
||||||
|
args = [sys.executable, "-m", "fontTools.ttx", "-q", "-o", "-", "-"]
|
||||||
|
with inpath.open("rb") as infile, outpath.open("w", encoding="utf-8") as outfile:
|
||||||
|
subprocess.run(args, check=True, stdin=infile, stdout=outfile)
|
||||||
|
assert outpath.is_file()
|
||||||
|
|
||||||
|
|
||||||
|
def test_main_ttx_compile_stdin_to_stdout(tmp_path):
|
||||||
|
inpath = Path("Tests").joinpath("ttx", "data", "TestTTF.ttx")
|
||||||
|
outpath = tmp_path / "TestTTF.ttf"
|
||||||
|
args = [sys.executable, "-m", "fontTools.ttx", "-q", "-o", "-", "-"]
|
||||||
|
with inpath.open("r", encoding="utf-8") as infile, outpath.open("wb") as outfile:
|
||||||
|
subprocess.run(args, check=True, stdin=infile, stdout=outfile)
|
||||||
|
assert outpath.is_file()
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------
|
# ---------------------------
|
||||||
# support functions for tests
|
# support functions for tests
|
||||||
# ---------------------------
|
# ---------------------------
|
||||||
|
Loading…
x
Reference in New Issue
Block a user