fontisan 0.4.11 → 0.4.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.adoc +27 -1
- data/docs/AFDKO_MIGRATION.adoc +175 -0
- data/docs/FEATURE_PARITY.adoc +263 -0
- data/docs/STITCHER_GUIDE.adoc +100 -6
- data/docs/TTX.adoc +176 -0
- data/docs/UFO_COMPILATION.adoc +281 -3
- data/lib/fontisan/stitcher/source.rb +11 -7
- data/lib/fontisan/subset/table_subsetter.rb +104 -6
- data/lib/fontisan/svg/standalone_glyph.rb +129 -0
- data/lib/fontisan/svg.rb +1 -0
- data/lib/fontisan/ufo/cli.rb +34 -21
- data/lib/fontisan/ufo/compile/feature_writers/base.rb +56 -0
- data/lib/fontisan/ufo/compile/feature_writers/curs.rb +72 -0
- data/lib/fontisan/ufo/compile/feature_writers/gdef.rb +90 -0
- data/lib/fontisan/ufo/compile/feature_writers/kern.rb +51 -0
- data/lib/fontisan/ufo/compile/feature_writers/kern2.rb +57 -0
- data/lib/fontisan/ufo/compile/feature_writers/mark.rb +45 -0
- data/lib/fontisan/ufo/compile/feature_writers/mark_family_base.rb +122 -0
- data/lib/fontisan/ufo/compile/feature_writers/mkmk.rb +53 -0
- data/lib/fontisan/ufo/compile/feature_writers.rb +37 -0
- data/lib/fontisan/ufo/compile/filters/propagate_anchors.rb +102 -0
- data/lib/fontisan/ufo/compile/filters/remove_overlaps.rb +94 -0
- data/lib/fontisan/ufo/compile/filters/sort_contours.rb +69 -0
- data/lib/fontisan/ufo/compile/filters/transformations.rb +113 -0
- data/lib/fontisan/ufo/compile/filters.rb +12 -0
- data/lib/fontisan/ufo/compile.rb +1 -0
- data/lib/fontisan/ufo/convert/to_dfont.rb +41 -0
- data/lib/fontisan/ufo/convert/to_otc.rb +37 -0
- data/lib/fontisan/ufo/convert/to_otf.rb +18 -0
- data/lib/fontisan/ufo/convert/to_otf2.rb +20 -0
- data/lib/fontisan/ufo/convert/to_postscript.rb +59 -0
- data/lib/fontisan/ufo/convert/to_ttc.rb +35 -0
- data/lib/fontisan/ufo/convert/to_ttf.rb +21 -0
- data/lib/fontisan/ufo/convert/to_woff.rb +56 -0
- data/lib/fontisan/ufo/convert/to_woff2.rb +45 -0
- data/lib/fontisan/ufo/convert.rb +68 -5
- data/lib/fontisan/ufo/transformation.rb +11 -0
- data/lib/fontisan/version.rb +1 -1
- metadata +27 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e97f99deb241e5abd1bbdaeb27849efb3a4597bf2139fc8a1fa815fcccbfdfcb
|
|
4
|
+
data.tar.gz: ced71eac0e00e479581c5460e33672aa80ded30cd40cb590b5a09621c5441290
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cc72fc9f58560fddf2440ecead8a54060a06a6da38ac3de2212d893cb3dbb2dea88649c82c30ff8fb10d25389058a63770d4309e414856f031ab3d88659f4150
|
|
7
|
+
data.tar.gz: bac0c91aa712516256d2ae7fb7ccee7dd31fe1549ace5bb533ea9d2b27b7087da4b0b200412794b3677b44c43bae79c87cbcaa849db3f6d993d00dac91a1e3a3
|
data/README.adoc
CHANGED
|
@@ -15,7 +15,9 @@ and Adobe Type 1 fonts (OTF, TTF, OTC, TTC, dfont, PFB, PFA).
|
|
|
15
15
|
The gem provides both a Ruby library API and a command-line interface, with
|
|
16
16
|
structured output formats (YAML, JSON, text) via lutaml-model.
|
|
17
17
|
|
|
18
|
-
Fontisan is designed to replace the following tools
|
|
18
|
+
Fontisan is designed to replace the following tools (see
|
|
19
|
+
link:docs/FEATURE_PARITY.adoc[Feature Parity] for the full list and
|
|
20
|
+
per-tool coverage):
|
|
19
21
|
|
|
20
22
|
* `otfinfo` from http://www.lcdf.org/type/[LCDF Typetools]. Fontisan supports
|
|
21
23
|
all features provided by `otfinfo`, including extraction of font metadata,
|
|
@@ -30,6 +32,26 @@ validation, format conversion, and collection creation. See
|
|
|
30
32
|
link:docs/EXTRACT_TTC_MIGRATION.md[extract_ttc Migration Guide] for detailed
|
|
31
33
|
command mappings and usage examples.
|
|
32
34
|
|
|
35
|
+
* **Adobe AFDKO** — `makeotf`, `otf2ttf`, `ttx`, `mergeFonts`,
|
|
36
|
+
`checkOutlinesUFO`, `rotateFont`. See
|
|
37
|
+
link:docs/AFDKO_MIGRATION.adoc[AFDKO Migration Guide].
|
|
38
|
+
|
|
39
|
+
* **Python fontTools + ufoLib2 + ufo2ft** — UFO model layer, UFO
|
|
40
|
+
compile pipeline (TTF/OTF/CFF2 + variable), glyph filters (8/8),
|
|
41
|
+
feature writers (GDEF, kern, mark, mkmk, curs), `pyftsubset`,
|
|
42
|
+
`pyftmerge`, `varLib`. See link:docs/UFO_COMPILATION.adoc[UFO Compilation Guide].
|
|
43
|
+
|
|
44
|
+
* **`ttx` (TTX format)** — full read/write of the FontTools XML
|
|
45
|
+
format. See link:docs/TTX.adoc[TTX Support].
|
|
46
|
+
|
|
47
|
+
* **Google `woff2_compress` / `woff2_decompress`** — WOFF and
|
|
48
|
+
WOFF2 compression/decompression in pure Ruby. See
|
|
49
|
+
link:docs/WOFF_WOFF2_FORMATS.adoc[WOFF/WOFF2 Formats Guide].
|
|
50
|
+
|
|
51
|
+
* **Type 1 utilities** (`t1disasm`, `t1asm`, `t1embed`, `pfm2afm`)
|
|
52
|
+
— full PFB/PFA parse/generate, eexec decryption, PFM↔AFM
|
|
53
|
+
round-trip. See link:docs/TYPE1_FONTS.adoc[Type 1 Fonts Guide].
|
|
54
|
+
|
|
33
55
|
|
|
34
56
|
== Installation
|
|
35
57
|
|
|
@@ -60,11 +82,15 @@ Font compilation and assembly::
|
|
|
60
82
|
* UFO → TTF compilation with cubic-to-quadratic conversion and winding-order correction (see link:docs/UFO_COMPILATION.adoc[UFO Compilation Guide])
|
|
61
83
|
* UFO → OTF (CFF1) compilation with Type 2 charstring encoding
|
|
62
84
|
* UFO → OTF (CFF2) compilation with variable-font blend/vsindex operators (see link:docs/CFF2_SUPPORT.adoc[CFF2 Support Guide])
|
|
85
|
+
* UFO → all binary formats via the `Ufo::Convert` dispatcher: TTF, OTF, OTF2, WOFF, WOFF2, dfont, TTC, OTC, PFB, PFA
|
|
86
|
+
* UFO feature writers: GDEF glyph classification, GPOS kern/mark/mkmk/curs (see link:docs/UFO_COMPILATION.adoc[UFO Compilation Guide — Filters + Feature Writers])
|
|
87
|
+
* Glyph-processing filters: cubic_to_quadratic, decompose_components, flatten_components, reverse_contour_direction, transformations, sort_contours, propagate_anchors, remove_overlaps
|
|
63
88
|
* Multi-source font stitching with explicit subfont declaration and TTC/OTC collection output (see link:docs/STITCHER_GUIDE.adoc[Stitcher Guide])
|
|
64
89
|
* SVG path → UFO glyph conversion for chart-extracted glyphs (see link:docs/SVG_TO_GLYF.adoc[SVG to Glyph Guide])
|
|
65
90
|
* Variable font compilation: fvar, gvar, HVAR, MVAR, avar, STAT tables
|
|
66
91
|
* Signature-based glyph deduplication with 65,535 glyph cap enforcement
|
|
67
92
|
* Compound (composite) TrueType glyph flattening
|
|
93
|
+
* AFDKO migration: drop-in replacement for `makeotf`, `otf2ttf`, `ttx`, `mergeFonts`, `checkOutlinesUFO` (see link:docs/AFDKO_MIGRATION.adoc[AFDKO Migration Guide])
|
|
68
94
|
|
|
69
95
|
Color fonts and collections::
|
|
70
96
|
* Color font table builders: COLR v0, CPAL, SVG table, sbix, CBDT/CBLC (see link:docs/COLOR_FONTS.adoc[Color Fonts Guide])
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
= AFDKO Migration Guide
|
|
2
|
+
|
|
3
|
+
This guide maps commands and concepts from Adobe Font Development
|
|
4
|
+
Kit for OpenType (AFDKO) — `makeotf`, `otf2ttf`, `ttx`, `mergeFonts`,
|
|
5
|
+
`rotateFont` — to their pure-Ruby equivalents in fontisan.
|
|
6
|
+
|
|
7
|
+
fontisan's UFO pipeline replaces every AFDKO command for the
|
|
8
|
+
compile/convert/validate workflows. No external toolchain required.
|
|
9
|
+
|
|
10
|
+
== Command map
|
|
11
|
+
|
|
12
|
+
[cols="1,1,3", options="header"]
|
|
13
|
+
|===
|
|
14
|
+
| AFDKO | fontisan | Notes
|
|
15
|
+
|
|
16
|
+
| `makeotf -f font.ufo -o font.otf`
|
|
17
|
+
| `fontisan ufo build font.ufo --output font.otf --to otf`
|
|
18
|
+
| CFF1 output by default; pass `--to otf2` for CFF2 (variable fonts).
|
|
19
|
+
|
|
20
|
+
| `makeotf -f font.ufo -o font.ttf`
|
|
21
|
+
| `fontisan ufo build font.ufo --output font.ttf --to ttf`
|
|
22
|
+
| TrueType (glyf) output. `cubic_to_quadratic` and
|
|
23
|
+
`reverse_contour_direction` filters run automatically.
|
|
24
|
+
|
|
25
|
+
| `ttx font.ttf`
|
|
26
|
+
| `fontisan ufo convert font.ttf font.ufo`
|
|
27
|
+
| `ttx` produces XML; fontisan produces a typed UFO source directory.
|
|
28
|
+
|
|
29
|
+
| `otf2ttf font.otf`
|
|
30
|
+
| `fontisan convert font.otf font.ttf`
|
|
31
|
+
| Outline conversion (CFF → glyf) is handled by the
|
|
32
|
+
`OutlineConverter` strategy; no Python required.
|
|
33
|
+
|
|
34
|
+
| `mergeFonts out.ufo a.ufo b.ufo`
|
|
35
|
+
| Use `Fontisan::Stitcher` (see link:STITCHER_GUIDE.adoc[Stitcher Guide])
|
|
36
|
+
| Stitcher is more flexible — supports per-source codepoint remaps,
|
|
37
|
+
glyph-cap partitioning, and deduplication.
|
|
38
|
+
|
|
39
|
+
| `rotateFont` (font info updates)
|
|
40
|
+
| Edit `Fontisan::Ufo::Info` directly
|
|
41
|
+
| fontisan exposes the full UFO model in Ruby.
|
|
42
|
+
|
|
43
|
+
| `checkOutlinesUFO font.ufo`
|
|
44
|
+
| `fontisan ufo validate font.ufo`
|
|
45
|
+
| Plus the link:COLLECTION_VALIDATION.adoc[collection validator] for TTC/OTC.
|
|
46
|
+
|
|
47
|
+
| (no AFDKO equivalent)
|
|
48
|
+
| `fontisan ufo extract font.ufo A A.svg`
|
|
49
|
+
| Per-glyph standalone SVG export — new in fontisan.
|
|
50
|
+
|===
|
|
51
|
+
|
|
52
|
+
== Compile-time options
|
|
53
|
+
|
|
54
|
+
AFDKO uses a `font.ufo/lib.plist` `com.github.fonttools` manifest
|
|
55
|
+
plus a `FontMenuNameDB` / `GlyphOrderAndAliasDB` file pair. fontisan
|
|
56
|
+
takes those inline:
|
|
57
|
+
|
|
58
|
+
[source,ruby]
|
|
59
|
+
----
|
|
60
|
+
ufo = Fontisan::Ufo::Font.open("font.ufo")
|
|
61
|
+
ufo.info.family_name = "MyFont"
|
|
62
|
+
ufo.info.postscript_full_name = "MyFont-Regular"
|
|
63
|
+
|
|
64
|
+
# Apply a uniform scale before compile (AFDKO's -sh / -sz options)
|
|
65
|
+
Fontisan::Ufo::Compile::Filters.apply(
|
|
66
|
+
[:transformations],
|
|
67
|
+
ufo.layers.default_layer.glyphs.values,
|
|
68
|
+
matrix: [1.1, 0, 0, 1.1, 0, 0] # 110% scale
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
Fontisan::Ufo::Convert.convert(ufo, to: :otf, output_path: "font.otf")
|
|
72
|
+
----
|
|
73
|
+
|
|
74
|
+
== Variable fonts
|
|
75
|
+
|
|
76
|
+
AFDKO's `makeotf -f default.ufo -i instances/...` master-instance
|
|
77
|
+
model maps to fontisan's `Ufo::Compile::VariableTtf` /
|
|
78
|
+
`VariableOtf` compilers, which read the UFO's `fvar` + `gvar` +
|
|
79
|
+
designspace data directly.
|
|
80
|
+
|
|
81
|
+
[source,ruby]
|
|
82
|
+
----
|
|
83
|
+
ufo = Fontisan::Ufo::Font.open("MyVariable.ufo")
|
|
84
|
+
Fontisan::Ufo::Compile::VariableTtf.new(ufo).compile(output_path: "MyVariable.ttf")
|
|
85
|
+
----
|
|
86
|
+
|
|
87
|
+
For OTF (CFF2) variable fonts, use `Compile::VariableOtf` or pass
|
|
88
|
+
`to: :otf2` through the dispatcher.
|
|
89
|
+
|
|
90
|
+
== Filter pipelines
|
|
91
|
+
|
|
92
|
+
AFDKO has no first-class filter pipeline — you'd shell out to
|
|
93
|
+
Python fontTools `ufo2ft.filters.*`. fontisan ships the equivalent
|
|
94
|
+
filters in pure Ruby:
|
|
95
|
+
|
|
96
|
+
[source,ruby]
|
|
97
|
+
----
|
|
98
|
+
glyphs = ufo.layers.default_layer.glyphs.values
|
|
99
|
+
|
|
100
|
+
Fontisan::Ufo::Compile::Filters.apply(
|
|
101
|
+
%i[
|
|
102
|
+
decompose_components
|
|
103
|
+
propagate_anchors
|
|
104
|
+
cubic_to_quadratic
|
|
105
|
+
reverse_contour_direction
|
|
106
|
+
sort_contours
|
|
107
|
+
],
|
|
108
|
+
glyphs
|
|
109
|
+
)
|
|
110
|
+
----
|
|
111
|
+
|
|
112
|
+
Run them in this order: decompose first (so subsequent filters see
|
|
113
|
+
only simple glyphs), then propagate anchors (uses base glyphs'
|
|
114
|
+
anchors), then the TrueType-required conversions, finally sort.
|
|
115
|
+
|
|
116
|
+
== Output formats at a glance
|
|
117
|
+
|
|
118
|
+
fontisan can produce every format AFDKO can, plus a few AFDKO
|
|
119
|
+
cannot:
|
|
120
|
+
|
|
121
|
+
[cols="1,1", options="header"]
|
|
122
|
+
|===
|
|
123
|
+
| Format | Command
|
|
124
|
+
|
|
125
|
+
| TrueType (`.ttf`)
|
|
126
|
+
| `fontisan ufo build f.ufo --output f.ttf`
|
|
127
|
+
|
|
128
|
+
| OpenType CFF1 (`.otf`)
|
|
129
|
+
| `fontisan ufo build f.ufo --output f.otf --to otf`
|
|
130
|
+
|
|
131
|
+
| OpenType CFF2 (`.otf`, variable)
|
|
132
|
+
| `fontisan ufo build f.ufo --output f.otf --to otf2`
|
|
133
|
+
|
|
134
|
+
| WOFF (compressed TTF/OTF)
|
|
135
|
+
| `fontisan ufo convert f.ufo f.woff`
|
|
136
|
+
|
|
137
|
+
| WOFF2 (Brotli-compressed)
|
|
138
|
+
| `fontisan ufo convert f.ufo f.woff2`
|
|
139
|
+
|
|
140
|
+
| Mac dfont (resource fork)
|
|
141
|
+
| `fontisan ufo convert f.ufo f.dfont`
|
|
142
|
+
|
|
143
|
+
| TrueType Collection (`.ttc`)
|
|
144
|
+
| `fontisan ufo convert f.ufo f.ttc`
|
|
145
|
+
|
|
146
|
+
| OpenType Collection (`.otc`)
|
|
147
|
+
| `fontisan ufo convert f.ufo f.otc`
|
|
148
|
+
|
|
149
|
+
| PostScript Type 1 Binary (`.pfb`)
|
|
150
|
+
| `fontisan ufo convert f.ufo f.pfb`
|
|
151
|
+
|
|
152
|
+
| PostScript Type 1 ASCII (`.pfa`)
|
|
153
|
+
| `fontisan ufo convert f.ufo f.pfa`
|
|
154
|
+
|===
|
|
155
|
+
|
|
156
|
+
== Why migrate
|
|
157
|
+
|
|
158
|
+
* **No AFDKO install** — fontisan is a Ruby gem; no separate
|
|
159
|
+
toolchain to license, install, or version-pin.
|
|
160
|
+
* **No Python dependency** — fontisan is pure Ruby. Eliminates the
|
|
161
|
+
Python fontTools + ufo2ft + ufoLib2 stack from your build.
|
|
162
|
+
* **Single source of truth** — fontisan reads, writes, and round-trips
|
|
163
|
+
every format. No format-specific tools.
|
|
164
|
+
* **Programmatic API** — every CLI command has a Ruby equivalent
|
|
165
|
+
under `Fontisan::Ufo::*`. Scriptable in ways AFDKO shells cannot
|
|
166
|
+
match.
|
|
167
|
+
|
|
168
|
+
== See also
|
|
169
|
+
|
|
170
|
+
* link:UFO_COMPILATION.adoc[UFO Compilation Guide] — full reference
|
|
171
|
+
for the compile / convert / filter / CLI surface.
|
|
172
|
+
* link:STITCHER_GUIDE.adoc[Stitcher Guide] — replaces `mergeFonts`
|
|
173
|
+
with codepoint remaps, partition strategies, and dedup.
|
|
174
|
+
* link:CFF2_SUPPORT.adoc[CFF2 Support] — variable-font blend
|
|
175
|
+
operators.
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
= Feature Parity — Tools fontisan Replaces
|
|
2
|
+
|
|
3
|
+
fontisan is a pure-Ruby replacement for several established font
|
|
4
|
+
toolchains. This page lists every external tool fontisan can
|
|
5
|
+
substitute for, with notes on coverage and migration paths.
|
|
6
|
+
|
|
7
|
+
== Adobe Font Development Kit for OpenType (AFDKO)
|
|
8
|
+
|
|
9
|
+
The AFDKO ships a suite of command-line tools for font
|
|
10
|
+
development. fontisan replaces every one in pure Ruby.
|
|
11
|
+
|
|
12
|
+
[cols="1,1,3", options="header"]
|
|
13
|
+
|===
|
|
14
|
+
| AFDKO | fontisan | Notes
|
|
15
|
+
|
|
16
|
+
| `makeotf -f font.ufo -o font.otf`
|
|
17
|
+
| `fontisan ufo build font.ufo --output font.otf --to otf`
|
|
18
|
+
| CFF1 by default; `--to otf2` for CFF2 (variable fonts).
|
|
19
|
+
|
|
20
|
+
| `makeotf -f font.ufo -o font.ttf`
|
|
21
|
+
| `fontisan ufo build font.ufo --output font.ttf --to ttf`
|
|
22
|
+
| TrueType (glyf). `cubic_to_quadratic` +
|
|
23
|
+
`reverse_contour_direction` filters run automatically.
|
|
24
|
+
|
|
25
|
+
| `ttx font.ttf` / `ttx font.ttx`
|
|
26
|
+
| `fontisan export font.ttf --format ttx` / `fontisan info font.ttx`
|
|
27
|
+
| See link:TTX.adoc[TTX Support]. Byte-compatible output for
|
|
28
|
+
typed tables.
|
|
29
|
+
|
|
30
|
+
| `otf2ttf font.otf`
|
|
31
|
+
| `fontisan convert font.otf font.ttf`
|
|
32
|
+
| Outline conversion (CFF → glyf) via `OutlineConverter`.
|
|
33
|
+
|
|
34
|
+
| `mergeFonts out.ufo a.ufo b.ufo`
|
|
35
|
+
| `Fontisan::Stitcher` (see link:STITCHER_GUIDE.adoc[Stitcher Guide])
|
|
36
|
+
| More flexible: per-source codepoint remaps, partition
|
|
37
|
+
strategies (Plane/Block/Script), glyph-cap enforcement,
|
|
38
|
+
deduplication.
|
|
39
|
+
|
|
40
|
+
| `rotateFont` (font-info updates)
|
|
41
|
+
| Edit `Fontisan::Ufo::Info` directly
|
|
42
|
+
| fontisan exposes the full UFO model in Ruby.
|
|
43
|
+
|
|
44
|
+
| `checkOutlinesUFO font.ufo`
|
|
45
|
+
| `fontisan ufo validate font.ufo`
|
|
46
|
+
| Plus the link:COLLECTION_VALIDATION.adoc[collection validator]
|
|
47
|
+
for TTC/OTC.
|
|
48
|
+
|
|
49
|
+
| (no AFDKO equivalent)
|
|
50
|
+
| `fontisan ufo extract font.ufo A A.svg`
|
|
51
|
+
| Per-glyph standalone SVG export.
|
|
52
|
+
|===
|
|
53
|
+
|
|
54
|
+
link:AFDKO_MIGRATION.adoc[AFDKO Migration Guide] → full command
|
|
55
|
+
reference and pipeline examples.
|
|
56
|
+
|
|
57
|
+
== LCDF Typetools (`otfinfo`)
|
|
58
|
+
|
|
59
|
+
`otfinfo` from http://www.lcdf.org/type/[LCDF Typetools] is the
|
|
60
|
+
canonical font-metadata display tool. fontisan replaces it
|
|
61
|
+
entirely.
|
|
62
|
+
|
|
63
|
+
[cols="1,1", options="header"]
|
|
64
|
+
|===
|
|
65
|
+
| otfinfo | fontisan
|
|
66
|
+
|
|
67
|
+
| `otfinfo -i font.otf`
|
|
68
|
+
| `fontisan info font.otf`
|
|
69
|
+
|
|
70
|
+
| `otfinfo --brief font.otf` (fast)
|
|
71
|
+
| `fontisan info font.otf --brief` (~5× faster, metadata-only)
|
|
72
|
+
|
|
73
|
+
| Per-flag queries (`--family`, `--postscript-name`, etc.)
|
|
74
|
+
| `--fields` flag selects specific fields
|
|
75
|
+
|===
|
|
76
|
+
|
|
77
|
+
fontisan supports every field `otfinfo` produces, plus extras
|
|
78
|
+
(color capabilities, variable-font detail, OpenType layout
|
|
79
|
+
features). The `info` command auto-detects file format from
|
|
80
|
+
magic bytes — never trusts extensions.
|
|
81
|
+
|
|
82
|
+
== `extract_ttc`
|
|
83
|
+
|
|
84
|
+
https://github.com/fontist/extract_ttc[extract_ttc] extracts
|
|
85
|
+
fonts from a TrueType/OpenType Collection (`.ttc`/`.otc`).
|
|
86
|
+
fontisan replaces it with Docker-style subcommands:
|
|
87
|
+
|
|
88
|
+
[source,bash]
|
|
89
|
+
----
|
|
90
|
+
# extract_ttc
|
|
91
|
+
extract_ttc font.ttc
|
|
92
|
+
|
|
93
|
+
# fontisan
|
|
94
|
+
fontisan collection ls font.ttc # list faces
|
|
95
|
+
fontisan collection info font.ttc # per-face metadata
|
|
96
|
+
fontisan collection extract font.ttc # write each face to disk
|
|
97
|
+
----
|
|
98
|
+
|
|
99
|
+
link:docs/EXTRACT_TTC_MIGRATION.md[extract_ttc Migration Guide] →
|
|
100
|
+
full command mapping.
|
|
101
|
+
|
|
102
|
+
== Python fontTools + ufoLib2 + ufo2ft
|
|
103
|
+
|
|
104
|
+
fontTools is the dominant Python font library. fontisan replaces
|
|
105
|
+
its core surfaces in pure Ruby:
|
|
106
|
+
|
|
107
|
+
[cols="1,1,3", options="header"]
|
|
108
|
+
|===
|
|
109
|
+
| fontTools / ufoLib2 / ufo2ft | fontisan | Coverage
|
|
110
|
+
|
|
111
|
+
| `fontTools.ttx` (TTX I/O)
|
|
112
|
+
| `Fontisan::Export::TtxParser` + `TtxGenerator` (see link:TTX.adoc[TTX])
|
|
113
|
+
| Typed read/write of every standard table.
|
|
114
|
+
|
|
115
|
+
| `ufoLib2` (UFO model layer)
|
|
116
|
+
| `Fontisan::Ufo::*` (16 model classes)
|
|
117
|
+
| Substantially complete. Allotments: Info, Font, Layer, LayerSet,
|
|
118
|
+
Glyph, Contour, Point, Component, Anchor, Guideline, Image,
|
|
119
|
+
Kerning, Features, Lib, DataSet, ImageSet.
|
|
120
|
+
|
|
121
|
+
| `ufo2ft.compile*` (UFO compile)
|
|
122
|
+
| `Fontisan::Ufo::Compile::TtfCompiler` / `OtfCompiler` /
|
|
123
|
+
`Otf2Compiler` / `VariableTtf` / `VariableOtf`
|
|
124
|
+
| Full compile pipeline. TTF, OTF (CFF1), OTF2 (CFF2), variable.
|
|
125
|
+
|
|
126
|
+
| `ufo2ft.filters.*` (glyph filters)
|
|
127
|
+
| `Fontisan::Ufo::Compile::Filters::*`
|
|
128
|
+
| 8/8: cubic_to_quadratic, decompose_components,
|
|
129
|
+
flatten_components, reverse_contour_direction, transformations,
|
|
130
|
+
sort_contours, propagate_anchors, remove_overlaps (bbox approx).
|
|
131
|
+
|
|
132
|
+
| `ufo2ft.featureWriters.*` (GSUB/GPOS writers)
|
|
133
|
+
| `Fontisan::Ufo::Compile::FeatureWriters::*`
|
|
134
|
+
| 6/6 commonly-needed: Kern, Kern2, Gdef, Mark, Mkmk, Curs. Each
|
|
135
|
+
returns structured data for the table builders to encode.
|
|
136
|
+
|
|
137
|
+
| `fontTools.subset` (pyftsubset)
|
|
138
|
+
| `Fontisan::Subset::*` + `fontisan subset` CLI
|
|
139
|
+
| Profiles: PDF, web, minimal. Codepoint subset + table
|
|
140
|
+
stripping.
|
|
141
|
+
|
|
142
|
+
| `fontTools.merge` (pyftmerge)
|
|
143
|
+
| `Fontisan::Stitcher` (see link:STITCHER_GUIDE.adoc[Stitcher Guide])
|
|
144
|
+
| More flexible than pyftmerge: explicit per-face codepoint
|
|
145
|
+
binding, deduplication, partition by plane/block/script.
|
|
146
|
+
|
|
147
|
+
| `fontTools.varLib` (variable font assembly)
|
|
148
|
+
| `Fontisan::Ufo::Compile::VariableTtf` / `VariableOtf` /
|
|
149
|
+
`Avar` / `Hvar` / `Mvar` / `Stat` / `Gvar` / `Fvar`
|
|
150
|
+
| Full variable-font compile from UFO masters.
|
|
151
|
+
|===
|
|
152
|
+
|
|
153
|
+
== Type 1 font tools
|
|
154
|
+
|
|
155
|
+
fontisan parses and generates Type 1 fonts (`.pfb`, `.pfa`),
|
|
156
|
+
replacing several specialty tools:
|
|
157
|
+
|
|
158
|
+
[cols="1,1,3", options="header"]
|
|
159
|
+
|===
|
|
160
|
+
| Tool | fontisan | Notes
|
|
161
|
+
|
|
162
|
+
| `t1disasm` / `t1asm` (Type 1 dis/assembler)
|
|
163
|
+
| `Fontisan::Type1::PfbParser` / `PFBGenerator` /
|
|
164
|
+
`PFAGenerator`
|
|
165
|
+
| Round-trips PFB ↔ Type 1 charstring tokens.
|
|
166
|
+
|
|
167
|
+
| `t1embed` / `t1unembed` (font embedding)
|
|
168
|
+
| `Fontisan::Type1::Decryptor` + charstring layer
|
|
169
|
+
| Eexec decryption + charstring de-encryption.
|
|
170
|
+
|
|
171
|
+
| PFM Tools (`pfm2afm`, etc.)
|
|
172
|
+
| `Fontisan::Type1::PfmParser` + `AfmGenerator`
|
|
173
|
+
| Round-trip PFM ↔ AFM.
|
|
174
|
+
|===
|
|
175
|
+
|
|
176
|
+
link:TYPE1_FONTS.adoc[Type 1 Fonts Guide] → full reference.
|
|
177
|
+
|
|
178
|
+
== WOFF / WOFF2 compress / decompress
|
|
179
|
+
|
|
180
|
+
[cols="1,1,3", options="header"]
|
|
181
|
+
|===
|
|
182
|
+
| Tool | fontisan | Notes
|
|
183
|
+
|
|
184
|
+
| Google `woff2_compress`
|
|
185
|
+
| `Fontisan::Converters::WoffWriter`
|
|
186
|
+
| zlib compression for WOFF.
|
|
187
|
+
|
|
188
|
+
| Google `woff2_decompress`
|
|
189
|
+
| `Fontisan::FontLoader.load` (auto-detects WOFF2)
|
|
190
|
+
| Decompresses to in-memory TTF/OTF.
|
|
191
|
+
|
|
192
|
+
| Google `woff2_compress` (Brotli)
|
|
193
|
+
| `Fontisan::Converters::Woff2Encoder`
|
|
194
|
+
| Brotli compression for WOFF2 with optional table transforms.
|
|
195
|
+
|===
|
|
196
|
+
|
|
197
|
+
link:WOFF_WOFF2_FORMATS.adoc[WOFF/WOFF2 Formats Guide] → full
|
|
198
|
+
reference.
|
|
199
|
+
|
|
200
|
+
== Variable font tools
|
|
201
|
+
|
|
202
|
+
[cols="1,1,3", options="header"]
|
|
203
|
+
|===
|
|
204
|
+
| Tool | fontisan | Notes
|
|
205
|
+
|
|
206
|
+
| `fontTools.varLib.instancer`
|
|
207
|
+
| `Fontisan::Variation::Optimizer` + `InstantiateCommand`
|
|
208
|
+
| Generates static instances from a variable font.
|
|
209
|
+
|
|
210
|
+
| `fontTools.varLib.merger`
|
|
211
|
+
| `Fontisan::Ufo::Compile::VariableTtf` / `VariableOtf`
|
|
212
|
+
| Merges UFO masters into a variable font.
|
|
213
|
+
|
|
214
|
+
| `fontTools.ttLib` variable-font table read/write
|
|
215
|
+
| `Tables::Fvar`, `Tables::Gvar`, `Tables::Avar`, `Tables::Hvar`,
|
|
216
|
+
`Tables::Vvar`, `Tables::Mvar`, `Tables::Stat`
|
|
217
|
+
| Full read/write for every variation table.
|
|
218
|
+
|===
|
|
219
|
+
|
|
220
|
+
link:VARIABLE_FONT_OPERATIONS.adoc[Variable Font Operations] →
|
|
221
|
+
full reference.
|
|
222
|
+
|
|
223
|
+
== Validation
|
|
224
|
+
|
|
225
|
+
[cols="1,1,3", options="header"]
|
|
226
|
+
|===
|
|
227
|
+
| Tool | fontisan | Notes
|
|
228
|
+
|
|
229
|
+
| `fontTools.fontBuilder` (validation hooks)
|
|
230
|
+
| `Fontisan::Validators::*` (DSL framework)
|
|
231
|
+
| Profiles: indexability, usability, production, web,
|
|
232
|
+
spec_compliance, default. Each profile = set of checks.
|
|
233
|
+
|
|
234
|
+
| `fontTools.ttx` round-trip checks
|
|
235
|
+
| `fontisan validate font.ttf`
|
|
236
|
+
| Runs every check in the chosen profile.
|
|
237
|
+
|===
|
|
238
|
+
|
|
239
|
+
link:VALIDATION.adoc[Validation Guide] → full reference.
|
|
240
|
+
|
|
241
|
+
== What fontisan does NOT replace
|
|
242
|
+
|
|
243
|
+
For honesty:
|
|
244
|
+
|
|
245
|
+
* **Glyph editing GUI** — fontisan is a library, not a font
|
|
246
|
+
editor. Use FontForge, Glyphs, RoboFont, or FontLab for visual
|
|
247
|
+
editing; use fontisan for the build/inspect/validate pipeline.
|
|
248
|
+
* **Glyph drawing from scratch** — fontisan reads/writes outline
|
|
249
|
+
data but has no canvas/drawing primitives. Generate outlines in
|
|
250
|
+
an editor or programmatically via the UFO model.
|
|
251
|
+
* **Bitmap font rendering** — fontisan is for font files, not
|
|
252
|
+
rasterizing text. Use HarfBuzz + a renderer for that.
|
|
253
|
+
* **Heavy polygon clipping** — fontisan's `remove_overlaps`
|
|
254
|
+
filter uses a bounding-box approximation. For production-grade
|
|
255
|
+
boolean ops, preprocess in FontLab or use the `clipper` gem.
|
|
256
|
+
|
|
257
|
+
== See also
|
|
258
|
+
|
|
259
|
+
* link:UFO_COMPILATION.adoc[UFO Compilation Guide]
|
|
260
|
+
* link:TTX.adoc[TTX Support]
|
|
261
|
+
* link:STITCHER_GUIDE.adoc[Stitcher Guide]
|
|
262
|
+
* link:AFDKO_MIGRATION.adoc[AFDKO Migration Guide]
|
|
263
|
+
* link:EXTRACT_TTC_MIGRATION.md[extract_ttc Migration Guide]
|
data/docs/STITCHER_GUIDE.adoc
CHANGED
|
@@ -21,6 +21,13 @@ There are no defaults and no after-the-fact splitting.
|
|
|
21
21
|
|
|
22
22
|
| `add_source(label, font)` | Register a source font under a Symbol label
|
|
23
23
|
|
|
24
|
+
| `add_source(label, font, remap:)` | Register a source with a
|
|
25
|
+
`{src_codepoint => target_codepoint}` remap. Useful for donor
|
|
26
|
+
fonts whose glyphs live at non-canonical codepoints (PUA, ASCII-
|
|
27
|
+
mapped pre-Unicode fonts). Source codepoints not in the remap
|
|
28
|
+
are hidden — the donor's other coverage (often ASCII/PUA noise)
|
|
29
|
+
does not leak into the output.
|
|
30
|
+
|
|
24
31
|
| `include_range(range, from:, into:)` | Add codepoint range from `from` into `into`
|
|
25
32
|
|
|
26
33
|
| `include_codepoints(cps, from:, into:)` | Add an explicit Array of codepoints
|
|
@@ -81,6 +88,37 @@ Each subfont is compiled independently, then packed by
|
|
|
81
88
|
(head, name, OS/2, ...) are stored once and referenced from each
|
|
82
89
|
subfont's offset table.
|
|
83
90
|
|
|
91
|
+
== Donor remap (non-canonical codepoints)
|
|
92
|
+
|
|
93
|
+
Some donor fonts ship glyphs at codepoints that don't match the
|
|
94
|
+
canonical Unicode assignment — a keyboard-mapped font whose letters
|
|
95
|
+
live at ASCII slots, or a PUA-allocated pre-Unicode font. Pass a
|
|
96
|
+
`remap:` hash to `add_source` to expose those glyphs at their
|
|
97
|
+
canonical targets without mutating the donor's cmap:
|
|
98
|
+
|
|
99
|
+
[source,ruby]
|
|
100
|
+
----
|
|
101
|
+
donor = Fontisan::FontLoader.load("TolongSikiMappedToASCII.ttf")
|
|
102
|
+
|
|
103
|
+
stitcher = Fontisan::Stitcher.new
|
|
104
|
+
stitcher.add_source(:tolong_siki, donor, remap: {
|
|
105
|
+
0x41 => 0x11DB0, # ASCII A → Tolong Siki KA
|
|
106
|
+
0x42 => 0x11DB1, # ASCII B → Tolong Siki NGA
|
|
107
|
+
# ...
|
|
108
|
+
})
|
|
109
|
+
stitcher.include_codepoints((0x11DB0..0x11DC0).to_a,
|
|
110
|
+
from: :tolong_siki, into: :tolong)
|
|
111
|
+
----
|
|
112
|
+
|
|
113
|
+
With a remap set, the source answers `gid_for_codepoint(target)`
|
|
114
|
+
by translating through the inverse remap to the donor's source
|
|
115
|
+
codepoint. Source codepoints not in the remap are hidden — only
|
|
116
|
+
remapped coverage is exposed.
|
|
117
|
+
|
|
118
|
+
The donor's cmap is never mutated. `remap:` is a translation layer
|
|
119
|
+
at the source's API boundary; multiple sources on the same
|
|
120
|
+
Stitcher can have different remaps (or none at all).
|
|
121
|
+
|
|
84
122
|
`write_collection` returns a `Stitcher::CollectionResult` Struct with
|
|
85
123
|
the output `path`, total `bytes`, and one `Stitcher::SubfontStats`
|
|
86
124
|
per declared subfont (`name`, `glyph_count`, `codepoint_count`). Stats
|
|
@@ -188,13 +226,69 @@ A `Blueprint` is a `Struct.new(:partitions, keyword_init: true)`;
|
|
|
188
226
|
`Blueprint#apply_to(stitcher)` calls `apply_to` on each partition in
|
|
189
227
|
order and returns the list of declared subfont names.
|
|
190
228
|
|
|
191
|
-
===
|
|
229
|
+
=== ByBlock — partition by Unicode Blocks.txt
|
|
230
|
+
|
|
231
|
+
`PartitionStrategy::ByBlock` partitions codepoints by their Unicode
|
|
232
|
+
block. Each non-empty block becomes one partition. The full Unicode
|
|
233
|
+
16.0 block list (~340 entries covering BMP, SMP, SIP, TIP, SSP) is
|
|
234
|
+
inlined as `ByBlock::BLOCKS` so the partitioner is self-contained
|
|
235
|
+
(no external data file). Codepoints in unassigned planes fall into
|
|
236
|
+
`:block_other`.
|
|
237
|
+
|
|
238
|
+
[source,ruby]
|
|
239
|
+
----
|
|
240
|
+
bp = Fontisan::Stitcher::PartitionStrategy::ByBlock.new
|
|
241
|
+
blueprint = bp.call(cp_map)
|
|
242
|
+
blueprint.names # => [:block_basic_latin, :block_cjk_unified_ideographs, ...]
|
|
243
|
+
----
|
|
244
|
+
|
|
245
|
+
Every block is treated as atomic — if a single block alone exceeds
|
|
246
|
+
`cap`, ByBlock raises `PartitionCapExceededError` (callers must
|
|
247
|
+
either use ByPlane with carve-outs or implement a custom
|
|
248
|
+
partitioner).
|
|
249
|
+
|
|
250
|
+
=== ByScript — partition by Unicode script property
|
|
251
|
+
|
|
252
|
+
`PartitionStrategy::ByScript` partitions codepoints by Unicode
|
|
253
|
+
script (Latin, Greek, Cyrillic, Han, Hangul, etc.). Derived from
|
|
254
|
+
`ByBlock::BLOCKS` via a `SCRIPT_OF_BLOCK` map — keeps the data DRY.
|
|
255
|
+
|
|
256
|
+
Scripts that overflow `cap` are chunked with alphabetic suffixes
|
|
257
|
+
matching ByPlane's sub-split convention (e.g. `:script_han`,
|
|
258
|
+
`:script_han_a`, `:script_han_b`). Common and Inherited scripts
|
|
259
|
+
get their own buckets — useful for separating shared punctuation
|
|
260
|
+
and combining marks.
|
|
261
|
+
|
|
262
|
+
[source,ruby]
|
|
263
|
+
----
|
|
264
|
+
sp = Fontisan::Stitcher::PartitionStrategy::ByScript.new
|
|
265
|
+
blueprint = sp.call(cp_map)
|
|
266
|
+
blueprint.names # => [:script_common, :script_latin, :script_han, ...]
|
|
267
|
+
----
|
|
268
|
+
|
|
269
|
+
=== Choosing a strategy
|
|
270
|
+
|
|
271
|
+
|===
|
|
272
|
+
| Strategy | When to use
|
|
273
|
+
|
|
274
|
+
| `ByPlane`
|
|
275
|
+
| CJK-heavy fonts that need to respect the 65,535-glyph cap per
|
|
276
|
+
plane. Sub-splits only the SIP CJK extensions and the BMP
|
|
277
|
+
carve-out blocks.
|
|
278
|
+
|
|
279
|
+
| `ByBlock`
|
|
280
|
+
| Fonts that span many small Unicode blocks where each block
|
|
281
|
+
should land in its own subfont (e.g. for selective subsetting
|
|
282
|
+
per block). Each block is atomic.
|
|
283
|
+
|
|
284
|
+
| `ByScript`
|
|
285
|
+
| Web font delivery where each subfont should cover one script
|
|
286
|
+
(Latin-only file, Greek-only file, etc.). Chunks large scripts
|
|
287
|
+
(Han) when they overflow the cap.
|
|
288
|
+
|===
|
|
192
289
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
add `by_block.rb` / `by_script.rb` under
|
|
196
|
-
`lib/fontisan/stitcher/partition_strategy/` and an autoload entry in
|
|
197
|
-
`partition_strategy.rb`.
|
|
290
|
+
All three autoload from `lib/fontisan/stitcher/partition_strategy/`.
|
|
291
|
+
Adding a new strategy = new file + one autoload entry (OCP).
|
|
198
292
|
|
|
199
293
|
== Per-subfont metadata (Ufo::Info.for_subfont)
|
|
200
294
|
|