aboutsummaryrefslogtreecommitdiff
path: root/Lib/fontTools/merge/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/fontTools/merge/__init__.py')
-rw-r--r--Lib/fontTools/merge/__init__.py363
1 files changed, 184 insertions, 179 deletions
diff --git a/Lib/fontTools/merge/__init__.py b/Lib/fontTools/merge/__init__.py
index 97106489..8d8a5213 100644
--- a/Lib/fontTools/merge/__init__.py
+++ b/Lib/fontTools/merge/__init__.py
@@ -4,7 +4,11 @@
from fontTools import ttLib
import fontTools.merge.base
-from fontTools.merge.cmap import computeMegaGlyphOrder, computeMegaCmap, renameCFFCharStrings
+from fontTools.merge.cmap import (
+ computeMegaGlyphOrder,
+ computeMegaCmap,
+ renameCFFCharStrings,
+)
from fontTools.merge.layout import layoutPreMerge, layoutPostMerge
from fontTools.merge.options import Options
import fontTools.merge.tables
@@ -15,191 +19,192 @@ import logging
log = logging.getLogger("fontTools.merge")
-timer = Timer(logger=logging.getLogger(__name__+".timer"), level=logging.INFO)
+timer = Timer(logger=logging.getLogger(__name__ + ".timer"), level=logging.INFO)
class Merger(object):
- """Font merger.
-
- This class merges multiple files into a single OpenType font, taking into
- account complexities such as OpenType layout (``GSUB``/``GPOS``) tables and
- cross-font metrics (e.g. ``hhea.ascent`` is set to the maximum value across
- all the fonts).
+ """Font merger.
+
+ This class merges multiple files into a single OpenType font, taking into
+ account complexities such as OpenType layout (``GSUB``/``GPOS``) tables and
+ cross-font metrics (e.g. ``hhea.ascent`` is set to the maximum value across
+ all the fonts).
+
+ If multiple glyphs map to the same Unicode value, and the glyphs are considered
+ sufficiently different (that is, they differ in any of paths, widths, or
+ height), then subsequent glyphs are renamed and a lookup in the ``locl``
+ feature will be created to disambiguate them. For example, if the arguments
+ are an Arabic font and a Latin font and both contain a set of parentheses,
+ the Latin glyphs will be renamed to ``parenleft#1`` and ``parenright#1``,
+ and a lookup will be inserted into the to ``locl`` feature (creating it if
+ necessary) under the ``latn`` script to substitute ``parenleft`` with
+ ``parenleft#1`` etc.
+
+ Restrictions:
+
+ - All fonts must have the same units per em.
+ - If duplicate glyph disambiguation takes place as described above then the
+ fonts must have a ``GSUB`` table.
+
+ Attributes:
+ options: Currently unused.
+ """
+
+ def __init__(self, options=None):
+ if not options:
+ options = Options()
+
+ self.options = options
+
+ def _openFonts(self, fontfiles):
+ fonts = [ttLib.TTFont(fontfile) for fontfile in fontfiles]
+ for font, fontfile in zip(fonts, fontfiles):
+ font._merger__fontfile = fontfile
+ font._merger__name = font["name"].getDebugName(4)
+ return fonts
+
+ def merge(self, fontfiles):
+ """Merges fonts together.
+
+ Args:
+ fontfiles: A list of file names to be merged
+
+ Returns:
+ A :class:`fontTools.ttLib.TTFont` object. Call the ``save`` method on
+ this to write it out to an OTF file.
+ """
+ #
+ # Settle on a mega glyph order.
+ #
+ fonts = self._openFonts(fontfiles)
+ glyphOrders = [list(font.getGlyphOrder()) for font in fonts]
+ computeMegaGlyphOrder(self, glyphOrders)
+
+ # Take first input file sfntVersion
+ sfntVersion = fonts[0].sfntVersion
+
+ # Reload fonts and set new glyph names on them.
+ fonts = self._openFonts(fontfiles)
+ for font, glyphOrder in zip(fonts, glyphOrders):
+ font.setGlyphOrder(glyphOrder)
+ if "CFF " in font:
+ renameCFFCharStrings(self, glyphOrder, font["CFF "])
+
+ cmaps = [font["cmap"] for font in fonts]
+ self.duplicateGlyphsPerFont = [{} for _ in fonts]
+ computeMegaCmap(self, cmaps)
+
+ mega = ttLib.TTFont(sfntVersion=sfntVersion)
+ mega.setGlyphOrder(self.glyphOrder)
+
+ for font in fonts:
+ self._preMerge(font)
+
+ self.fonts = fonts
+
+ allTags = reduce(set.union, (list(font.keys()) for font in fonts), set())
+ allTags.remove("GlyphOrder")
+
+ for tag in sorted(allTags):
+ if tag in self.options.drop_tables:
+ continue
+
+ with timer("merge '%s'" % tag):
+ tables = [font.get(tag, NotImplemented) for font in fonts]
+
+ log.info("Merging '%s'.", tag)
+ clazz = ttLib.getTableClass(tag)
+ table = clazz(tag).merge(self, tables)
+ # XXX Clean this up and use: table = mergeObjects(tables)
+
+ if table is not NotImplemented and table is not False:
+ mega[tag] = table
+ log.info("Merged '%s'.", tag)
+ else:
+ log.info("Dropped '%s'.", tag)
+
+ del self.duplicateGlyphsPerFont
+ del self.fonts
+
+ self._postMerge(mega)
+
+ return mega
+
+ def mergeObjects(self, returnTable, logic, tables):
+ # Right now we don't use self at all. Will use in the future
+ # for options and logging.
+
+ allKeys = set.union(
+ set(),
+ *(vars(table).keys() for table in tables if table is not NotImplemented),
+ )
+ for key in allKeys:
+ try:
+ mergeLogic = logic[key]
+ except KeyError:
+ try:
+ mergeLogic = logic["*"]
+ except KeyError:
+ raise Exception(
+ "Don't know how to merge key %s of class %s"
+ % (key, returnTable.__class__.__name__)
+ )
+ if mergeLogic is NotImplemented:
+ continue
+ value = mergeLogic(getattr(table, key, NotImplemented) for table in tables)
+ if value is not NotImplemented:
+ setattr(returnTable, key, value)
+
+ return returnTable
+
+ def _preMerge(self, font):
+ layoutPreMerge(font)
+
+ def _postMerge(self, font):
+ layoutPostMerge(font)
+
+ if "OS/2" in font:
+ # https://github.com/fonttools/fonttools/issues/2538
+ # TODO: Add an option to disable this?
+ font["OS/2"].recalcAvgCharWidth(font)
+
+
+__all__ = ["Options", "Merger", "main"]
- If multiple glyphs map to the same Unicode value, and the glyphs are considered
- sufficiently different (that is, they differ in any of paths, widths, or
- height), then subsequent glyphs are renamed and a lookup in the ``locl``
- feature will be created to disambiguate them. For example, if the arguments
- are an Arabic font and a Latin font and both contain a set of parentheses,
- the Latin glyphs will be renamed to ``parenleft#1`` and ``parenright#1``,
- and a lookup will be inserted into the to ``locl`` feature (creating it if
- necessary) under the ``latn`` script to substitute ``parenleft`` with
- ``parenleft#1`` etc.
-
- Restrictions:
-
- - All fonts must have the same units per em.
- - If duplicate glyph disambiguation takes place as described above then the
- fonts must have a ``GSUB`` table.
-
- Attributes:
- options: Currently unused.
- """
-
- def __init__(self, options=None):
-
- if not options:
- options = Options()
-
- self.options = options
-
- def _openFonts(self, fontfiles):
- fonts = [ttLib.TTFont(fontfile) for fontfile in fontfiles]
- for font,fontfile in zip(fonts, fontfiles):
- font._merger__fontfile = fontfile
- font._merger__name = font['name'].getDebugName(4)
- return fonts
-
- def merge(self, fontfiles):
- """Merges fonts together.
-
- Args:
- fontfiles: A list of file names to be merged
-
- Returns:
- A :class:`fontTools.ttLib.TTFont` object. Call the ``save`` method on
- this to write it out to an OTF file.
- """
- #
- # Settle on a mega glyph order.
- #
- fonts = self._openFonts(fontfiles)
- glyphOrders = [list(font.getGlyphOrder()) for font in fonts]
- computeMegaGlyphOrder(self, glyphOrders)
-
- # Take first input file sfntVersion
- sfntVersion = fonts[0].sfntVersion
-
- # Reload fonts and set new glyph names on them.
- fonts = self._openFonts(fontfiles)
- for font,glyphOrder in zip(fonts, glyphOrders):
- font.setGlyphOrder(glyphOrder)
- if 'CFF ' in font:
- renameCFFCharStrings(self, glyphOrder, font['CFF '])
-
- cmaps = [font['cmap'] for font in fonts]
- self.duplicateGlyphsPerFont = [{} for _ in fonts]
- computeMegaCmap(self, cmaps)
-
- mega = ttLib.TTFont(sfntVersion=sfntVersion)
- mega.setGlyphOrder(self.glyphOrder)
-
- for font in fonts:
- self._preMerge(font)
-
- self.fonts = fonts
-
- allTags = reduce(set.union, (list(font.keys()) for font in fonts), set())
- allTags.remove('GlyphOrder')
-
- for tag in allTags:
- if tag in self.options.drop_tables:
- continue
-
- with timer("merge '%s'" % tag):
- tables = [font.get(tag, NotImplemented) for font in fonts]
-
- log.info("Merging '%s'.", tag)
- clazz = ttLib.getTableClass(tag)
- table = clazz(tag).merge(self, tables)
- # XXX Clean this up and use: table = mergeObjects(tables)
-
- if table is not NotImplemented and table is not False:
- mega[tag] = table
- log.info("Merged '%s'.", tag)
- else:
- log.info("Dropped '%s'.", tag)
-
- del self.duplicateGlyphsPerFont
- del self.fonts
-
- self._postMerge(mega)
-
- return mega
-
- def mergeObjects(self, returnTable, logic, tables):
- # Right now we don't use self at all. Will use in the future
- # for options and logging.
-
- allKeys = set.union(set(), *(vars(table).keys() for table in tables if table is not NotImplemented))
- for key in allKeys:
- try:
- mergeLogic = logic[key]
- except KeyError:
- try:
- mergeLogic = logic['*']
- except KeyError:
- raise Exception("Don't know how to merge key %s of class %s" %
- (key, returnTable.__class__.__name__))
- if mergeLogic is NotImplemented:
- continue
- value = mergeLogic(getattr(table, key, NotImplemented) for table in tables)
- if value is not NotImplemented:
- setattr(returnTable, key, value)
-
- return returnTable
-
- def _preMerge(self, font):
- layoutPreMerge(font)
-
- def _postMerge(self, font):
- layoutPostMerge(font)
-
- if "OS/2" in font:
- # https://github.com/fonttools/fonttools/issues/2538
- # TODO: Add an option to disable this?
- font["OS/2"].recalcAvgCharWidth(font)
-
-
-__all__ = [
- 'Options',
- 'Merger',
- 'main'
-]
@timer("make one with everything (TOTAL TIME)")
def main(args=None):
- """Merge multiple fonts into one"""
- from fontTools import configLogger
-
- if args is None:
- args = sys.argv[1:]
-
- options = Options()
- args = options.parse_opts(args, ignore_unknown=['output-file'])
- outfile = 'merged.ttf'
- fontfiles = []
- for g in args:
- if g.startswith('--output-file='):
- outfile = g[14:]
- continue
- fontfiles.append(g)
-
- if len(args) < 1:
- print("usage: pyftmerge font...", file=sys.stderr)
- return 1
-
- configLogger(level=logging.INFO if options.verbose else logging.WARNING)
- if options.timing:
- timer.logger.setLevel(logging.DEBUG)
- else:
- timer.logger.disabled = True
-
- merger = Merger(options=options)
- font = merger.merge(fontfiles)
- with timer("compile and save font"):
- font.save(outfile)
+ """Merge multiple fonts into one"""
+ from fontTools import configLogger
+
+ if args is None:
+ args = sys.argv[1:]
+
+ options = Options()
+ args = options.parse_opts(args, ignore_unknown=["output-file"])
+ outfile = "merged.ttf"
+ fontfiles = []
+ for g in args:
+ if g.startswith("--output-file="):
+ outfile = g[14:]
+ continue
+ fontfiles.append(g)
+
+ if len(args) < 1:
+ print("usage: pyftmerge font...", file=sys.stderr)
+ return 1
+
+ configLogger(level=logging.INFO if options.verbose else logging.WARNING)
+ if options.timing:
+ timer.logger.setLevel(logging.DEBUG)
+ else:
+ timer.logger.disabled = True
+
+ merger = Merger(options=options)
+ font = merger.merge(fontfiles)
+ with timer("compile and save font"):
+ font.save(outfile)
if __name__ == "__main__":
- sys.exit(main())
+ sys.exit(main())