aboutsummaryrefslogtreecommitdiff
path: root/Lib/fontTools/subset/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/fontTools/subset/__init__.py')
-rw-r--r--Lib/fontTools/subset/__init__.py890
1 files changed, 377 insertions, 513 deletions
diff --git a/Lib/fontTools/subset/__init__.py b/Lib/fontTools/subset/__init__.py
index 53b440da..f687b056 100644
--- a/Lib/fontTools/subset/__init__.py
+++ b/Lib/fontTools/subset/__init__.py
@@ -8,15 +8,12 @@ from fontTools.ttLib.tables import otTables
from fontTools.otlLib.maxContextCalc import maxCtxFont
from fontTools.pens.basePen import NullPen
from fontTools.misc.loggingTools import Timer
-from fontTools.subset.util import _add_method, _uniq_sort
from fontTools.subset.cff import *
-from fontTools.subset.svg import *
import sys
import struct
import array
import logging
from collections import Counter, defaultdict
-from functools import reduce
from types import MethodType
__usage__ = "pyftsubset font-file [glyph...] [--option=value]..."
@@ -24,100 +21,82 @@ __usage__ = "pyftsubset font-file [glyph...] [--option=value]..."
__doc__="""\
pyftsubset -- OpenType font subsetter and optimizer
-pyftsubset is an OpenType font subsetter and optimizer, based on fontTools.
-It accepts any TT- or CFF-flavored OpenType (.otf or .ttf) or WOFF (.woff)
-font file. The subsetted glyph set is based on the specified glyphs
-or characters, and specified OpenType layout features.
-
-The tool also performs some size-reducing optimizations, aimed for using
-subset fonts as webfonts. Individual optimizations can be enabled or
-disabled, and are enabled by default when they are safe.
-
-Usage: """+__usage__+"""
-
-At least one glyph or one of --gids, --gids-file, --glyphs, --glyphs-file,
---text, --text-file, --unicodes, or --unicodes-file, must be specified.
-
-Args:
-
-font-file
- The input font file.
-glyph
- Specify one or more glyph identifiers to include in the subset. Must be
- PS glyph names, or the special string '*' to keep the entire glyph set.
-
-Initial glyph set specification
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-These options populate the initial glyph set. Same option can appear
-multiple times, and the results are accummulated.
-
---gids=<NNN>[,<NNN>...]
- Specify comma/whitespace-separated list of glyph IDs or ranges as decimal
- numbers. For example, --gids=10-12,14 adds glyphs with numbers 10, 11,
- 12, and 14.
-
---gids-file=<path>
- Like --gids but reads from a file. Anything after a '#' on any line is
- ignored as comments.
-
---glyphs=<glyphname>[,<glyphname>...]
- Specify comma/whitespace-separated PS glyph names to add to the subset.
- Note that only PS glyph names are accepted, not gidNNN, U+XXXX, etc
- that are accepted on the command line. The special string '*' will keep
- the entire glyph set.
-
---glyphs-file=<path>
- Like --glyphs but reads from a file. Anything after a '#' on any line
- is ignored as comments.
-
---text=<text>
- Specify characters to include in the subset, as UTF-8 string.
-
---text-file=<path>
- Like --text but reads from a file. Newline character are not added to
- the subset.
-
---unicodes=<XXXX>[,<XXXX>...]
- Specify comma/whitespace-separated list of Unicode codepoints or
- ranges as hex numbers, optionally prefixed with 'U+', 'u', etc.
- For example, --unicodes=41-5a,61-7a adds ASCII letters, so does
- the more verbose --unicodes=U+0041-005A,U+0061-007A.
- The special strings '*' will choose all Unicode characters mapped
- by the font.
-
---unicodes-file=<path>
- Like --unicodes, but reads from a file. Anything after a '#' on any
- line in the file is ignored as comments.
-
---ignore-missing-glyphs
- Do not fail if some requested glyphs or gids are not available in
- the font.
-
---no-ignore-missing-glyphs
- Stop and fail if some requested glyphs or gids are not available
- in the font. [default]
-
---ignore-missing-unicodes [default]
- Do not fail if some requested Unicode characters (including those
- indirectly specified using --text or --text-file) are not available
- in the font.
-
---no-ignore-missing-unicodes
- Stop and fail if some requested Unicode characters are not available
- in the font.
- Note the default discrepancy between ignoring missing glyphs versus
- unicodes. This is for historical reasons and in the future
- --no-ignore-missing-unicodes might become default.
-
-Other options
-^^^^^^^^^^^^^
-
-For the other options listed below, to see the current value of the option,
-pass a value of '?' to it, with or without a '='.
-
-Examples::
-
+ pyftsubset is an OpenType font subsetter and optimizer, based on fontTools.
+ It accepts any TT- or CFF-flavored OpenType (.otf or .ttf) or WOFF (.woff)
+ font file. The subsetted glyph set is based on the specified glyphs
+ or characters, and specified OpenType layout features.
+
+ The tool also performs some size-reducing optimizations, aimed for using
+ subset fonts as webfonts. Individual optimizations can be enabled or
+ disabled, and are enabled by default when they are safe.
+
+Usage:
+ """+__usage__+"""
+
+ At least one glyph or one of --gids, --gids-file, --glyphs, --glyphs-file,
+ --text, --text-file, --unicodes, or --unicodes-file, must be specified.
+
+Arguments:
+ font-file
+ The input font file.
+ glyph
+ Specify one or more glyph identifiers to include in the subset. Must be
+ PS glyph names, or the special string '*' to keep the entire glyph set.
+
+Initial glyph set specification:
+ These options populate the initial glyph set. Same option can appear
+ multiple times, and the results are accummulated.
+ --gids=<NNN>[,<NNN>...]
+ Specify comma/whitespace-separated list of glyph IDs or ranges as
+ decimal numbers. For example, --gids=10-12,14 adds glyphs with
+ numbers 10, 11, 12, and 14.
+ --gids-file=<path>
+ Like --gids but reads from a file. Anything after a '#' on any line
+ is ignored as comments.
+ --glyphs=<glyphname>[,<glyphname>...]
+ Specify comma/whitespace-separated PS glyph names to add to the subset.
+ Note that only PS glyph names are accepted, not gidNNN, U+XXXX, etc
+ that are accepted on the command line. The special string '*' will keep
+ the entire glyph set.
+ --glyphs-file=<path>
+ Like --glyphs but reads from a file. Anything after a '#' on any line
+ is ignored as comments.
+ --text=<text>
+ Specify characters to include in the subset, as UTF-8 string.
+ --text-file=<path>
+ Like --text but reads from a file. Newline character are not added to
+ the subset.
+ --unicodes=<XXXX>[,<XXXX>...]
+ Specify comma/whitespace-separated list of Unicode codepoints or
+ ranges as hex numbers, optionally prefixed with 'U+', 'u', etc.
+ For example, --unicodes=41-5a,61-7a adds ASCII letters, so does
+ the more verbose --unicodes=U+0041-005A,U+0061-007A.
+ The special strings '*' will choose all Unicode characters mapped
+ by the font.
+ --unicodes-file=<path>
+ Like --unicodes, but reads from a file. Anything after a '#' on any
+ line in the file is ignored as comments.
+ --ignore-missing-glyphs
+ Do not fail if some requested glyphs or gids are not available in
+ the font.
+ --no-ignore-missing-glyphs
+ Stop and fail if some requested glyphs or gids are not available
+ in the font. [default]
+ --ignore-missing-unicodes [default]
+ Do not fail if some requested Unicode characters (including those
+ indirectly specified using --text or --text-file) are not available
+ in the font.
+ --no-ignore-missing-unicodes
+ Stop and fail if some requested Unicode characters are not available
+ in the font.
+ Note the default discrepancy between ignoring missing glyphs versus
+ unicodes. This is for historical reasons and in the future
+ --no-ignore-missing-unicodes might become default.
+
+Other options:
+ For the other options listed below, to see the current value of the option,
+ pass a value of '?' to it, with or without a '='.
+ Examples:
$ pyftsubset --glyph-names?
Current setting for 'glyph-names' is: False
$ ./pyftsubset --name-IDs=?
@@ -126,299 +105,239 @@ Examples::
Current setting for 'hinting' is: True
Current setting for 'hinting' is: False
-Output options
-^^^^^^^^^^^^^^
-
---output-file=<path>
- The output font file. If not specified, the subsetted font
- will be saved in as font-file.subset.
-
---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 the Google Zopfli algorithm to compress WOFF. The output is 3-8 %
- smaller than pure zlib, but the compression speed is much slower.
- The Zopfli Python bindings are available at:
- https://pypi.python.org/pypi/zopfli
-
-Glyph set expansion
-^^^^^^^^^^^^^^^^^^^
-
-These options control how additional glyphs are added to the subset.
-
---retain-gids
- Retain glyph indices; just empty glyphs not needed in-place.
-
---notdef-glyph
- Add the '.notdef' glyph to the subset (ie, keep it). [default]
-
---no-notdef-glyph
- Drop the '.notdef' glyph unless specified in the glyph set. This
- saves a few bytes, but is not possible for Postscript-flavored
- fonts, as those require '.notdef'. For TrueType-flavored fonts,
- this works fine as long as no unsupported glyphs are requested
- from the font.
-
---notdef-outline
- Keep the outline of '.notdef' glyph. The '.notdef' glyph outline is
- used when glyphs not supported by the font are to be shown. It is not
- needed otherwise.
-
---no-notdef-outline
- When including a '.notdef' glyph, remove its outline. This saves
- a few bytes. [default]
-
---recommended-glyphs
- Add glyphs 0, 1, 2, and 3 to the subset, as recommended for
- TrueType-flavored fonts: '.notdef', 'NULL' or '.null', 'CR', 'space'.
- Some legacy software might require this, but no modern system does.
-
---no-recommended-glyphs
- Do not add glyphs 0, 1, 2, and 3 to the subset, unless specified in
- glyph set. [default]
-
---no-layout-closure
- Do not expand glyph set to add glyphs produced by OpenType layout
- features. Instead, OpenType layout features will be subset to only
- rules that are relevant to the otherwise-specified glyph set.
-
---layout-features[+|-]=<feature>[,<feature>...]
- Specify (=), add to (+=) or exclude from (-=) the comma-separated
- set of OpenType layout feature tags that will be preserved.
- Glyph variants used by the preserved features are added to the
- specified subset glyph set. By default, 'calt', 'ccmp', 'clig', 'curs',
- 'dnom', 'frac', 'kern', 'liga', 'locl', 'mark', 'mkmk', 'numr', 'rclt',
- 'rlig', 'rvrn', and all features required for script shaping are
- preserved. To see the full list, try '--layout-features=?'.
- Use '*' to keep all features.
- Multiple --layout-features options can be provided if necessary.
- Examples:
-
- --layout-features+=onum,pnum,ss01
- * Keep the default set of features and 'onum', 'pnum', 'ss01'.
- --layout-features-='mark','mkmk'
- * Keep the default set of features but drop 'mark' and 'mkmk'.
- --layout-features='kern'
- * Only keep the 'kern' feature, drop all others.
- --layout-features=''
- * Drop all features.
- --layout-features='*'
- * Keep all features.
- --layout-features+=aalt --layout-features-=vrt2
- * Keep default set of features plus 'aalt', but drop 'vrt2'.
-
---layout-scripts[+|-]=<script>[,<script>...]
- Specify (=), add to (+=) or exclude from (-=) the comma-separated
- set of OpenType layout script tags that will be preserved. LangSys tags
- can be appended to script tag, separated by '.', for example:
- 'arab.dflt,arab.URD,latn.TRK'. By default all scripts are retained ('*').
-
-Hinting options
-^^^^^^^^^^^^^^^
-
---hinting
- Keep hinting [default]
-
---no-hinting
- Drop glyph-specific hinting and font-wide hinting tables, as well
- as remove hinting-related bits and pieces from other tables (eg. GPOS).
- See --hinting-tables for list of tables that are dropped by default.
- Instructions and hints are stripped from 'glyf' and 'CFF ' tables
- respectively. This produces (sometimes up to 30%) smaller fonts that
- are suitable for extremely high-resolution systems, like high-end
- mobile devices and retina displays.
-
-Optimization options
-^^^^^^^^^^^^^^^^^^^^
-
---desubroutinize
- Remove CFF use of subroutinizes. Subroutinization is a way to make CFF
- fonts smaller. For small subsets however, desubroutinizing might make
- the font smaller. It has even been reported that desubroutinized CFF
- fonts compress better (produce smaller output) WOFF and WOFF2 fonts.
- Also see note under --no-hinting.
-
---no-desubroutinize [default]
- Leave CFF subroutinizes as is, only throw away unused subroutinizes.
-
-Font table options
-^^^^^^^^^^^^^^^^^^
-
---drop-tables[+|-]=<table>[,<table>...]
- Specify (=), add to (+=) or exclude from (-=) the comma-separated
- set of tables that will be be dropped.
- By default, the following tables are dropped:
- 'BASE', 'JSTF', 'DSIG', 'EBDT', 'EBLC', 'EBSC', 'PCLT', 'LTSH'
- and Graphite tables: 'Feat', 'Glat', 'Gloc', 'Silf', 'Sill'.
- The tool will attempt to subset the remaining tables.
-
- Examples:
-
- --drop-tables-='BASE'
- * Drop the default set of tables but keep 'BASE'.
-
- --drop-tables+=GSUB
- * Drop the default set of tables and 'GSUB'.
-
- --drop-tables=DSIG
- * Only drop the 'DSIG' table, keep all others.
-
- --drop-tables=
- * Keep all tables.
-
---no-subset-tables+=<table>[,<table>...]
- Add to the set of tables that will not be subsetted.
- By default, the following tables are included in this list, as
- they do not need subsetting (ignore the fact that 'loca' is listed
- here): 'gasp', 'head', 'hhea', 'maxp', 'vhea', 'OS/2', 'loca', 'name',
- 'cvt ', 'fpgm', 'prep', 'VMDX', 'DSIG', 'CPAL', 'MVAR', 'cvar', 'STAT'.
- By default, tables that the tool does not know how to subset and are not
- specified here will be dropped from the font, unless --passthrough-tables
- option is passed.
-
- Example:
-
- --no-subset-tables+=FFTM
- * Keep 'FFTM' table in the font by preventing subsetting.
-
---passthrough-tables
- Do not drop tables that the tool does not know how to subset.
-
---no-passthrough-tables
- Tables that the tool does not know how to subset and are not specified
- in --no-subset-tables will be dropped from the font. [default]
-
---hinting-tables[-]=<table>[,<table>...]
- Specify (=), add to (+=) or exclude from (-=) the list of font-wide
- hinting tables that will be dropped if --no-hinting is specified.
-
- Examples:
-
- --hinting-tables-='VDMX'
- * Drop font-wide hinting tables except 'VDMX'.
- --hinting-tables=''
- * Keep all font-wide hinting tables (but strip hints from glyphs).
-
---legacy-kern
- Keep TrueType 'kern' table even when OpenType 'GPOS' is available.
-
---no-legacy-kern
- Drop TrueType 'kern' table if OpenType 'GPOS' is available. [default]
-
-Font naming options
-^^^^^^^^^^^^^^^^^^^
-
-These options control what is retained in the 'name' table. For numerical
-codes, see: http://www.microsoft.com/typography/otspec/name.htm
-
---name-IDs[+|-]=<nameID>[,<nameID>...]
- Specify (=), add to (+=) or exclude from (-=) the set of 'name' table
- entry nameIDs that will be preserved. By default, only nameIDs between 0
- and 6 are preserved, the rest are dropped. Use '*' to keep all entries.
-
- Examples:
-
- --name-IDs+=7,8,9
- * Also keep Trademark, Manufacturer and Designer name entries.
- --name-IDs=''
- * Drop all 'name' table entries.
- --name-IDs='*'
- * keep all 'name' table entries
-
---name-legacy
- Keep legacy (non-Unicode) 'name' table entries (0.x, 1.x etc.).
- XXX Note: This might be needed for some fonts that have no Unicode name
- entires for English. See: https://github.com/fonttools/fonttools/issues/146
-
---no-name-legacy
- Drop legacy (non-Unicode) 'name' table entries [default]
-
---name-languages[+|-]=<langID>[,<langID>]
- Specify (=), add to (+=) or exclude from (-=) the set of 'name' table
- langIDs that will be preserved. By default only records with langID
- 0x0409 (English) are preserved. Use '*' to keep all langIDs.
-
---obfuscate-names
- Make the font unusable as a system font by replacing name IDs 1, 2, 3, 4,
- and 6 with dummy strings (it is still fully functional as webfont).
-
-Glyph naming and encoding options
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
---glyph-names
- Keep PS glyph names in TT-flavored fonts. In general glyph names are
- not needed for correct use of the font. However, some PDF generators
- and PDF viewers might rely on glyph names to extract Unicode text
- from PDF documents.
---no-glyph-names
- Drop PS glyph names in TT-flavored fonts, by using 'post' table
- version 3.0. [default]
---legacy-cmap
- Keep the legacy 'cmap' subtables (0.x, 1.x, 4.x etc.).
---no-legacy-cmap
- Drop the legacy 'cmap' subtables. [default]
---symbol-cmap
- Keep the 3.0 symbol 'cmap'.
---no-symbol-cmap
- Drop the 3.0 symbol 'cmap'. [default]
-
-Other font-specific options
-^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
---recalc-bounds
- Recalculate font bounding boxes.
---no-recalc-bounds
- Keep original font bounding boxes. This is faster and still safe
- for all practical purposes. [default]
---recalc-timestamp
- Set font 'modified' timestamp to current time.
---no-recalc-timestamp
- Do not modify font 'modified' timestamp. [default]
---canonical-order
- Order tables as recommended in the OpenType standard. This is not
- required by the standard, nor by any known implementation.
---no-canonical-order
- Keep original order of font tables. This is faster. [default]
---prune-unicode-ranges
- Update the 'OS/2 ulUnicodeRange*' bits after subsetting. The Unicode
- ranges defined in the OpenType specification v1.7 are intersected with
- the Unicode codepoints specified in the font's Unicode 'cmap' subtables:
- when no overlap is found, the bit will be switched off. However, it will
- *not* be switched on if an intersection is found. [default]
---no-prune-unicode-ranges
- Don't change the 'OS/2 ulUnicodeRange*' bits.
---recalc-average-width
- Update the 'OS/2 xAvgCharWidth' field after subsetting.
---no-recalc-average-width
- Don't change the 'OS/2 xAvgCharWidth' field. [default]
---recalc-max-context
- Update the 'OS/2 usMaxContext' field after subsetting.
---no-recalc-max-context
- Don't change the 'OS/2 usMaxContext' field. [default]
---font-number=<number>
- Select font number for TrueType Collection (.ttc/.otc), starting from 0.
---pretty-svg
- When subsetting SVG table, use lxml pretty_print=True option to indent
- the XML output (only recommended for debugging purposes).
-
-Application options
-^^^^^^^^^^^^^^^^^^^
-
---verbose
- Display verbose information of the subsetting process.
---timing
- Display detailed timing information of the subsetting process.
---xml
- Display the TTX XML representation of subsetted font.
-
-Example
-^^^^^^^
-
-Produce a subset containing the characters ' !"#$%' without performing
-size-reducing optimizations::
+Output options:
+ --output-file=<path>
+ The output font file. If not specified, the subsetted font
+ will be saved in as font-file.subset.
+ --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 the Google Zopfli algorithm to compress WOFF. The output is 3-8 %
+ smaller than pure zlib, but the compression speed is much slower.
+ The Zopfli Python bindings are available at:
+ https://pypi.python.org/pypi/zopfli
+
+Glyph set expansion:
+ These options control how additional glyphs are added to the subset.
+ --retain-gids
+ Retain glyph indices; just empty glyphs not needed in-place.
+ --notdef-glyph
+ Add the '.notdef' glyph to the subset (ie, keep it). [default]
+ --no-notdef-glyph
+ Drop the '.notdef' glyph unless specified in the glyph set. This
+ saves a few bytes, but is not possible for Postscript-flavored
+ fonts, as those require '.notdef'. For TrueType-flavored fonts,
+ this works fine as long as no unsupported glyphs are requested
+ from the font.
+ --notdef-outline
+ Keep the outline of '.notdef' glyph. The '.notdef' glyph outline is
+ used when glyphs not supported by the font are to be shown. It is not
+ needed otherwise.
+ --no-notdef-outline
+ When including a '.notdef' glyph, remove its outline. This saves
+ a few bytes. [default]
+ --recommended-glyphs
+ Add glyphs 0, 1, 2, and 3 to the subset, as recommended for
+ TrueType-flavored fonts: '.notdef', 'NULL' or '.null', 'CR', 'space'.
+ Some legacy software might require this, but no modern system does.
+ --no-recommended-glyphs
+ Do not add glyphs 0, 1, 2, and 3 to the subset, unless specified in
+ glyph set. [default]
+ --no-layout-closure
+ Do not expand glyph set to add glyphs produced by OpenType layout
+ features. Instead, OpenType layout features will be subset to only
+ rules that are relevant to the otherwise-specified glyph set.
+ --layout-features[+|-]=<feature>[,<feature>...]
+ Specify (=), add to (+=) or exclude from (-=) the comma-separated
+ set of OpenType layout feature tags that will be preserved.
+ Glyph variants used by the preserved features are added to the
+ specified subset glyph set. By default, 'calt', 'ccmp', 'clig', 'curs',
+ 'dnom', 'frac', 'kern', 'liga', 'locl', 'mark', 'mkmk', 'numr', 'rclt',
+ 'rlig', 'rvrn', and all features required for script shaping are
+ preserved. To see the full list, try '--layout-features=?'.
+ Use '*' to keep all features.
+ Multiple --layout-features options can be provided if necessary.
+ Examples:
+ --layout-features+=onum,pnum,ss01
+ * Keep the default set of features and 'onum', 'pnum', 'ss01'.
+ --layout-features-='mark','mkmk'
+ * Keep the default set of features but drop 'mark' and 'mkmk'.
+ --layout-features='kern'
+ * Only keep the 'kern' feature, drop all others.
+ --layout-features=''
+ * Drop all features.
+ --layout-features='*'
+ * Keep all features.
+ --layout-features+=aalt --layout-features-=vrt2
+ * Keep default set of features plus 'aalt', but drop 'vrt2'.
+ --layout-scripts[+|-]=<script>[,<script>...]
+ Specify (=), add to (+=) or exclude from (-=) the comma-separated
+ set of OpenType layout script tags that will be preserved. LangSys tags
+ can be appended to script tag, separated by '.', for example:
+ 'arab.dflt,arab.URD,latn.TRK'. By default all scripts are retained ('*').
+
+Hinting options:
+ --hinting
+ Keep hinting [default]
+ --no-hinting
+ Drop glyph-specific hinting and font-wide hinting tables, as well
+ as remove hinting-related bits and pieces from other tables (eg. GPOS).
+ See --hinting-tables for list of tables that are dropped by default.
+ Instructions and hints are stripped from 'glyf' and 'CFF ' tables
+ respectively. This produces (sometimes up to 30%) smaller fonts that
+ are suitable for extremely high-resolution systems, like high-end
+ mobile devices and retina displays.
+
+Optimization options:
+ --desubroutinize
+ Remove CFF use of subroutinizes. Subroutinization is a way to make CFF
+ fonts smaller. For small subsets however, desubroutinizing might make
+ the font smaller. It has even been reported that desubroutinized CFF
+ fonts compress better (produce smaller output) WOFF and WOFF2 fonts.
+ Also see note under --no-hinting.
+ --no-desubroutinize [default]
+ Leave CFF subroutinizes as is, only throw away unused subroutinizes.
+
+Font table options:
+ --drop-tables[+|-]=<table>[,<table>...]
+ Specify (=), add to (+=) or exclude from (-=) the comma-separated
+ set of tables that will be be dropped.
+ By default, the following tables are dropped:
+ 'BASE', 'JSTF', 'DSIG', 'EBDT', 'EBLC', 'EBSC', 'SVG ', 'PCLT', 'LTSH'
+ and Graphite tables: 'Feat', 'Glat', 'Gloc', 'Silf', 'Sill'.
+ The tool will attempt to subset the remaining tables.
+ Examples:
+ --drop-tables-='SVG '
+ * Drop the default set of tables but keep 'SVG '.
+ --drop-tables+=GSUB
+ * Drop the default set of tables and 'GSUB'.
+ --drop-tables=DSIG
+ * Only drop the 'DSIG' table, keep all others.
+ --drop-tables=
+ * Keep all tables.
+ --no-subset-tables+=<table>[,<table>...]
+ Add to the set of tables that will not be subsetted.
+ By default, the following tables are included in this list, as
+ they do not need subsetting (ignore the fact that 'loca' is listed
+ here): 'gasp', 'head', 'hhea', 'maxp', 'vhea', 'OS/2', 'loca', 'name',
+ 'cvt ', 'fpgm', 'prep', 'VMDX', 'DSIG', 'CPAL', 'MVAR', 'cvar', 'STAT'.
+ By default, tables that the tool does not know how to subset and are not
+ specified here will be dropped from the font, unless --passthrough-tables
+ option is passed.
+ Example:
+ --no-subset-tables+=FFTM
+ * Keep 'FFTM' table in the font by preventing subsetting.
+ --passthrough-tables
+ Do not drop tables that the tool does not know how to subset.
+ --no-passthrough-tables
+ Tables that the tool does not know how to subset and are not specified
+ in --no-subset-tables will be dropped from the font. [default]
+ --hinting-tables[-]=<table>[,<table>...]
+ Specify (=), add to (+=) or exclude from (-=) the list of font-wide
+ hinting tables that will be dropped if --no-hinting is specified,
+ Examples:
+ --hinting-tables-='VDMX'
+ * Drop font-wide hinting tables except 'VDMX'.
+ --hinting-tables=''
+ * Keep all font-wide hinting tables (but strip hints from glyphs).
+ --legacy-kern
+ Keep TrueType 'kern' table even when OpenType 'GPOS' is available.
+ --no-legacy-kern
+ Drop TrueType 'kern' table if OpenType 'GPOS' is available. [default]
+
+Font naming options:
+ These options control what is retained in the 'name' table. For numerical
+ codes, see: http://www.microsoft.com/typography/otspec/name.htm
+ --name-IDs[+|-]=<nameID>[,<nameID>...]
+ Specify (=), add to (+=) or exclude from (-=) the set of 'name' table
+ entry nameIDs that will be preserved. By default, only nameIDs between 0
+ and 6 are preserved, the rest are dropped. Use '*' to keep all entries.
+ Examples:
+ --name-IDs+=7,8,9
+ * Also keep Trademark, Manufacturer and Designer name entries.
+ --name-IDs=''
+ * Drop all 'name' table entries.
+ --name-IDs='*'
+ * keep all 'name' table entries
+ --name-legacy
+ Keep legacy (non-Unicode) 'name' table entries (0.x, 1.x etc.).
+ XXX Note: This might be needed for some fonts that have no Unicode name
+ entires for English. See: https://github.com/fonttools/fonttools/issues/146
+ --no-name-legacy
+ Drop legacy (non-Unicode) 'name' table entries [default]
+ --name-languages[+|-]=<langID>[,<langID>]
+ Specify (=), add to (+=) or exclude from (-=) the set of 'name' table
+ langIDs that will be preserved. By default only records with langID
+ 0x0409 (English) are preserved. Use '*' to keep all langIDs.
+ --obfuscate-names
+ Make the font unusable as a system font by replacing name IDs 1, 2, 3, 4,
+ and 6 with dummy strings (it is still fully functional as webfont).
+
+Glyph naming and encoding options:
+ --glyph-names
+ Keep PS glyph names in TT-flavored fonts. In general glyph names are
+ not needed for correct use of the font. However, some PDF generators
+ and PDF viewers might rely on glyph names to extract Unicode text
+ from PDF documents.
+ --no-glyph-names
+ Drop PS glyph names in TT-flavored fonts, by using 'post' table
+ version 3.0. [default]
+ --legacy-cmap
+ Keep the legacy 'cmap' subtables (0.x, 1.x, 4.x etc.).
+ --no-legacy-cmap
+ Drop the legacy 'cmap' subtables. [default]
+ --symbol-cmap
+ Keep the 3.0 symbol 'cmap'.
+ --no-symbol-cmap
+ Drop the 3.0 symbol 'cmap'. [default]
+
+Other font-specific options:
+ --recalc-bounds
+ Recalculate font bounding boxes.
+ --no-recalc-bounds
+ Keep original font bounding boxes. This is faster and still safe
+ for all practical purposes. [default]
+ --recalc-timestamp
+ Set font 'modified' timestamp to current time.
+ --no-recalc-timestamp
+ Do not modify font 'modified' timestamp. [default]
+ --canonical-order
+ Order tables as recommended in the OpenType standard. This is not
+ required by the standard, nor by any known implementation.
+ --no-canonical-order
+ Keep original order of font tables. This is faster. [default]
+ --prune-unicode-ranges
+ Update the 'OS/2 ulUnicodeRange*' bits after subsetting. The Unicode
+ ranges defined in the OpenType specification v1.7 are intersected with
+ the Unicode codepoints specified in the font's Unicode 'cmap' subtables:
+ when no overlap is found, the bit will be switched off. However, it will
+ *not* be switched on if an intersection is found. [default]
+ --no-prune-unicode-ranges
+ Don't change the 'OS/2 ulUnicodeRange*' bits.
+ --recalc-average-width
+ Update the 'OS/2 xAvgCharWidth' field after subsetting.
+ --no-recalc-average-width
+ Don't change the 'OS/2 xAvgCharWidth' field. [default]
+ --recalc-max-context
+ Update the 'OS/2 usMaxContext' field after subsetting.
+ --no-recalc-max-context
+ Don't change the 'OS/2 usMaxContext' field. [default]
+ --font-number=<number>
+ Select font number for TrueType Collection (.ttc/.otc), starting from 0.
+
+Application options:
+ --verbose
+ Display verbose information of the subsetting process.
+ --timing
+ Display detailed timing information of the subsetting process.
+ --xml
+ Display the TTX XML representation of subsetted font.
+
+Example:
+ Produce a subset containing the characters ' !"#$%' without performing
+ size-reducing optimizations:
$ pyftsubset font.ttf --unicodes="U+0020-0025" \\
--layout-features='*' --glyph-names --symbol-cmap --legacy-cmap \\
@@ -443,6 +362,26 @@ log.glyphs = MethodType(_log_glyphs, log)
timer = Timer(logger=logging.getLogger("fontTools.subset.timer"))
+def _add_method(*clazzes):
+ """Returns a decorator function that adds a new method to one or
+ more classes."""
+ def wrapper(method):
+ done = []
+ for clazz in clazzes:
+ if clazz in done: continue # Support multiple names of a clazz
+ done.append(clazz)
+ assert clazz.__name__ != 'DefaultTable', \
+ 'Oops, table class not found.'
+ assert not hasattr(clazz, method.__name__), \
+ "Oops, class '%s' has method '%s'." % (clazz.__name__,
+ method.__name__)
+ setattr(clazz, method.__name__, method)
+ return None
+ return wrapper
+
+def _uniq_sort(l):
+ return sorted(set(l))
+
def _dict_subset(d, glyphs):
return {g:d[g] for g in glyphs}
@@ -588,17 +527,6 @@ def subset_glyphs(self, s):
else:
assert 0, "unknown format: %s" % self.Format
-@_add_method(otTables.Device)
-def is_hinting(self):
- return self.DeltaFormat in (1,2,3)
-
-@_add_method(otTables.ValueRecord)
-def prune_hints(self):
- for name in ['XPlaDevice', 'YPlaDevice', 'XAdvDevice', 'YAdvDevice']:
- v = getattr(self, name, None)
- if v is not None and v.is_hinting():
- delattr(self, name)
-
@_add_method(otTables.SinglePos)
def subset_glyphs(self, s):
if self.Format == 1:
@@ -615,27 +543,14 @@ def subset_glyphs(self, s):
@_add_method(otTables.SinglePos)
def prune_post_subset(self, font, options):
- if self.Value is None:
- assert self.ValueFormat == 0
- return True
-
- # Shrink ValueFormat
- if self.Format == 1:
- if not options.hinting:
- self.Value.prune_hints()
- self.ValueFormat = self.Value.getEffectiveFormat()
- elif self.Format == 2:
- if not options.hinting:
- for v in self.Value:
- v.prune_hints()
- self.ValueFormat = reduce(int.__or__, [v.getEffectiveFormat() for v in self.Value], 0)
-
+ if not options.hinting:
+ # Drop device tables
+ self.ValueFormat &= ~0x00F0
# Downgrade to Format 1 if all ValueRecords are the same
if self.Format == 2 and all(v == self.Value[0] for v in self.Value):
self.Format = 1
self.Value = self.Value[0] if self.ValueFormat != 0 else None
del self.ValueCount
-
return True
@_add_method(otTables.PairPos)
@@ -672,22 +587,10 @@ def subset_glyphs(self, s):
@_add_method(otTables.PairPos)
def prune_post_subset(self, font, options):
if not options.hinting:
- attr1, attr2 = {
- 1: ('PairSet', 'PairValueRecord'),
- 2: ('Class1Record', 'Class2Record'),
- }[self.Format]
-
- self.ValueFormat1 = self.ValueFormat2 = 0
- for row in getattr(self, attr1):
- for r in getattr(row, attr2):
- if r.Value1:
- r.Value1.prune_hints()
- self.ValueFormat1 |= r.Value1.getEffectiveFormat()
- if r.Value2:
- r.Value2.prune_hints()
- self.ValueFormat2 |= r.Value2.getEffectiveFormat()
-
- return bool(self.ValueFormat1 | self.ValueFormat2)
+ # Drop device tables
+ self.ValueFormat1 &= ~0x00F0
+ self.ValueFormat2 &= ~0x00F0
+ return True
@_add_method(otTables.CursivePos)
def subset_glyphs(self, s):
@@ -703,15 +606,9 @@ def subset_glyphs(self, s):
@_add_method(otTables.Anchor)
def prune_hints(self):
- if self.Format == 2:
- self.Format = 1
- elif self.Format == 3:
- for name in ('XDeviceTable', 'YDeviceTable'):
- v = getattr(self, name, None)
- if v is not None and v.is_hinting():
- setattr(self, name, None)
- if self.XDeviceTable is None and self.YDeviceTable is None:
- self.Format = 1
+ # Drop device tables / contour anchor point
+ self.ensureDecompiled()
+ self.Format = 1
@_add_method(otTables.CursivePos)
def prune_post_subset(self, font, options):
@@ -816,6 +713,7 @@ def subset_glyphs(self, s):
@_add_method(otTables.MarkMarkPos)
def prune_post_subset(self, font, options):
if not options.hinting:
+ # Drop device tables or contour anchor point
for m in self.Mark1Array.MarkRecord:
if m.MarkAnchor:
m.MarkAnchor.prune_hints()
@@ -1078,7 +976,7 @@ def closure_glyphs(self, s, cur_glyphs):
chaos.update(range(seqi, len(getattr(r, c.Input))+2))
lookup.closure_glyphs(s, cur_glyphs=pos_glyphs)
elif self.Format == 3:
- if not all(x is not None and x.intersect(s.glyphs) for x in c.RuleData(self)):
+ if not all(x.intersect(s.glyphs) for x in c.RuleData(self)):
return []
r = self
input_coverages = getattr(r, c.Input)
@@ -1173,7 +1071,7 @@ def subset_glyphs(self, s):
return bool(rss)
elif self.Format == 3:
- return all(x is not None and x.subset(s.glyphs) for x in c.RuleData(self))
+ return all(x.subset(s.glyphs) for x in c.RuleData(self))
else:
assert 0, "unknown format: %s" % self.Format
@@ -1368,13 +1266,7 @@ def subset_lookups(self, lookup_indices):
self.LookupListIndex = [lookup_indices.index(l)
for l in self.LookupListIndex]
self.LookupCount = len(self.LookupListIndex)
- # keep 'size' feature even if it contains no lookups; but drop any other
- # empty feature (e.g. FeatureParams for stylistic set names)
- # https://github.com/fonttools/fonttools/issues/2324
- return (
- self.LookupCount or
- isinstance(self.FeatureParams, otTables.FeatureParamsSize)
- )
+ return self.LookupCount or self.FeatureParams
@_add_method(otTables.FeatureList)
def subset_lookups(self, lookup_indices):
@@ -2115,7 +2007,7 @@ def closure_glyphs(self, s):
self.ColorLayers = self._decompileColorLayersV0(self.table)
self.ColorLayersV1 = {
rec.BaseGlyph: rec.Paint
- for rec in self.table.BaseGlyphList.BaseGlyphPaintRecord
+ for rec in self.table.BaseGlyphV1List.BaseGlyphV1Record
}
decompose = s.glyphs
@@ -2139,48 +2031,31 @@ def subset_glyphs(self, s):
from fontTools.colorLib.unbuilder import unbuildColrV1
from fontTools.colorLib.builder import buildColrV1, populateCOLRv0
- # only include glyphs after COLR closure, which in turn comes after cmap and GSUB
- # closure, but importantly before glyf/CFF closures. COLR layers can refer to
- # composite glyphs, and that's ok, since glyf/CFF closures happen after COLR closure
- # and take care of those. If we also included glyphs resulting from glyf/CFF closures
- # when deciding which COLR base glyphs to retain, then we may end up with a situation
- # whereby a COLR base glyph is kept, not because directly requested (cmap)
- # or substituted (GSUB) or referenced by another COLRv1 PaintColrGlyph, but because
- # it corresponds to (has same GID as) a non-COLR glyph that happens to be used as a
- # component in glyf or CFF table. Best case scenario we retain more glyphs than
- # required; worst case we retain incomplete COLR records that try to reference
- # glyphs that are no longer in the final subset font.
- # https://github.com/fonttools/fonttools/issues/2461
- s.glyphs = s.glyphs_colred
-
self.ColorLayers = {g: self.ColorLayers[g] for g in s.glyphs if g in self.ColorLayers}
if self.version == 0:
return bool(self.ColorLayers)
- colorGlyphsV1 = unbuildColrV1(self.table.LayerList, self.table.BaseGlyphList)
- self.table.LayerList, self.table.BaseGlyphList = buildColrV1(
+ colorGlyphsV1 = unbuildColrV1(self.table.LayerV1List, self.table.BaseGlyphV1List)
+ self.table.LayerV1List, self.table.BaseGlyphV1List = buildColrV1(
{g: colorGlyphsV1[g] for g in colorGlyphsV1 if g in s.glyphs}
)
del self.ColorLayersV1
- if self.table.ClipList is not None:
- clips = self.table.ClipList.clips
- self.table.ClipList.clips = {g: clips[g] for g in clips if g in s.glyphs}
-
layersV0 = self.ColorLayers
- if not self.table.BaseGlyphList.BaseGlyphPaintRecord:
+ if not self.table.BaseGlyphV1List.BaseGlyphV1Record:
# no more COLRv1 glyphs: downgrade to version 0
self.version = 0
del self.table
return bool(layersV0)
- populateCOLRv0(
- self.table,
- {
- g: [(layer.name, layer.colorID) for layer in layersV0[g]]
- for g in layersV0
- },
- )
+ if layersV0:
+ populateCOLRv0(
+ self.table,
+ {
+ g: [(layer.name, layer.colorID) for layer in layersV0[g]]
+ for g in layersV0
+ },
+ )
del self.ColorLayers
# TODO: also prune ununsed varIndices in COLR.VarStore
@@ -2195,11 +2070,11 @@ def prune_post_subset(self, font, options):
colors_by_index = defaultdict(list)
def collect_colors_by_index(paint):
- if hasattr(paint, "PaletteIndex"): # either solid colors...
- colors_by_index[paint.PaletteIndex].append(paint)
+ if hasattr(paint, "Color"): # either solid colors...
+ colors_by_index[paint.Color.PaletteIndex].append(paint.Color)
elif hasattr(paint, "ColorLine"): # ... or gradient color stops
for stop in paint.ColorLine.ColorStop:
- colors_by_index[stop.PaletteIndex].append(stop)
+ colors_by_index[stop.Color.PaletteIndex].append(stop.Color)
if colr.version == 0:
for layers in colr.ColorLayers.values():
@@ -2209,12 +2084,10 @@ def prune_post_subset(self, font, options):
if colr.table.LayerRecordArray:
for layer in colr.table.LayerRecordArray.LayerRecord:
colors_by_index[layer.PaletteIndex].append(layer)
- for record in colr.table.BaseGlyphList.BaseGlyphPaintRecord:
+ for record in colr.table.BaseGlyphV1List.BaseGlyphV1Record:
record.Paint.traverse(colr.table, collect_colors_by_index)
- # don't remap palette entry index 0xFFFF, this is always the foreground color
- # https://github.com/fonttools/fonttools/issues/2257
- retained_palette_indices = set(colors_by_index.keys()) - {0xFFFF}
+ retained_palette_indices = set(colors_by_index.keys())
for palette in self.palettes:
palette[:] = [c for i, c in enumerate(palette) if i in retained_palette_indices]
assert len(palette) == len(retained_palette_indices)
@@ -2328,7 +2201,7 @@ def subset_glyphs(self, s):
def remapComponentsFast(self, glyphidmap):
if not self.data or struct.unpack(">h", self.data[:2])[0] >= 0:
return # Not composite
- data = self.data = bytearray(self.data)
+ data = array.array("B", self.data)
i = 10
more = 1
while more:
@@ -2348,6 +2221,8 @@ def remapComponentsFast(self, glyphidmap):
elif flags & 0x0080: i += 8 # WE_HAVE_A_TWO_BY_TWO
more = flags & 0x0020 # MORE_COMPONENTS
+ self.data = data.tobytes()
+
@_add_method(ttLib.getTableClass('glyf'))
def closure_glyphs(self, s):
glyphSet = self.glyphs
@@ -2370,7 +2245,7 @@ def prune_pre_subset(self, font, options):
g = self[self.glyphOrder[0]]
# Yay, easy!
g.__dict__.clear()
- g.data = b''
+ g.data = ""
return True
@_add_method(ttLib.getTableClass('glyf'))
@@ -2385,7 +2260,7 @@ def subset_glyphs(self, s):
Glyph = ttLib.getTableModule('glyf').Glyph
for g in s.glyphs_emptied:
self.glyphs[g] = Glyph()
- self.glyphs[g].data = b''
+ self.glyphs[g].data = ''
self.glyphOrder = [g for g in self.glyphOrder if g in s.glyphs or g in s.glyphs_emptied]
# Don't drop empty 'glyf' tables, otherwise 'loca' doesn't get subset.
return True
@@ -2579,7 +2454,7 @@ class Options(object):
# spaces in tag names (e.g. "SVG ", "cvt ") are stripped by the argument parser
_drop_tables_default = ['BASE', 'JSTF', 'DSIG', 'EBDT', 'EBLC',
- 'EBSC', 'PCLT', 'LTSH']
+ 'EBSC', 'SVG', 'PCLT', 'LTSH']
_drop_tables_default += ['Feat', 'Glat', 'Gloc', 'Silf', 'Sill'] # Graphite
_no_subset_tables_default = ['avar', 'fvar',
'gasp', 'head', 'hhea', 'maxp',
@@ -2646,7 +2521,6 @@ class Options(object):
self.timing = False
self.xml = False
self.font_number = -1
- self.pretty_svg = False
self.set(**kwargs)
@@ -2785,7 +2659,7 @@ class Subsetter(object):
def _closure_glyphs(self, font):
realGlyphs = set(font.getGlyphOrder())
- self.orig_glyph_order = glyph_order = font.getGlyphOrder()
+ glyph_order = font.getGlyphOrder()
self.glyphs_requested = set()
self.glyphs_requested.update(self.glyph_names_requested)
@@ -2865,7 +2739,6 @@ class Subsetter(object):
log.info("Closed glyph list over '%s': %d glyphs after",
table, len(self.glyphs))
log.glyphs(self.glyphs, font=font)
- setattr(self, f"glyphs_{table.lower()}ed", frozenset(self.glyphs))
if 'glyf' in font:
with timer("close glyph list over 'glyf'"):
@@ -2905,24 +2778,6 @@ class Subsetter(object):
self.reverseEmptiedGlyphMap = {g:order[g] for g in self.glyphs_emptied}
- if not self.options.retain_gids:
- new_glyph_order = [
- g for g in glyph_order if g in self.glyphs_retained
- ]
- else:
- new_glyph_order = [
- g for g in glyph_order
- if font.getGlyphID(g) <= self.last_retained_order
- ]
- # We'll call font.setGlyphOrder() at the end of _subset_glyphs when all
- # tables have been subsetted. Below, we use the new glyph order to get
- # a map from old to new glyph indices, which can be useful when
- # subsetting individual tables (e.g. SVG) that refer to GIDs.
- self.new_glyph_order = new_glyph_order
- self.glyph_index_map = {
- order[new_glyph_order[i]]: i
- for i in range(len(new_glyph_order))
- }
log.info("Retaining %d glyphs", len(self.glyphs_retained))
@@ -2952,7 +2807,14 @@ class Subsetter(object):
del font[tag]
with timer("subset GlyphOrder"):
- font.setGlyphOrder(self.new_glyph_order)
+ glyphOrder = font.getGlyphOrder()
+ if not self.options.retain_gids:
+ glyphOrder = [g for g in glyphOrder if g in self.glyphs_retained]
+ else:
+ glyphOrder = [g for g in glyphOrder if font.getGlyphID(g) <= self.last_retained_order]
+
+ font.setGlyphOrder(glyphOrder)
+ font._buildReverseGlyphOrderDict()
def _prune_post_subset(self, font):
@@ -3001,11 +2863,13 @@ class Subsetter(object):
@timer("load font")
def load_font(fontFile,
options,
+ allowVID=False,
checkChecksums=0,
dontLoadGlyphNames=False,
lazy=True):
font = ttLib.TTFont(fontFile,
+ allowVID=allowVID,
checkChecksums=checkChecksums,
recalcBBoxes=options.recalc_bounds,
recalcTimestamp=options.recalc_timestamp,