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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.adoc +27 -1
  3. data/docs/AFDKO_MIGRATION.adoc +175 -0
  4. data/docs/FEATURE_PARITY.adoc +263 -0
  5. data/docs/STITCHER_GUIDE.adoc +100 -6
  6. data/docs/TTX.adoc +176 -0
  7. data/docs/UFO_COMPILATION.adoc +281 -3
  8. data/lib/fontisan/stitcher/source.rb +11 -7
  9. data/lib/fontisan/subset/table_subsetter.rb +104 -6
  10. data/lib/fontisan/svg/standalone_glyph.rb +129 -0
  11. data/lib/fontisan/svg.rb +1 -0
  12. data/lib/fontisan/ufo/cli.rb +34 -21
  13. data/lib/fontisan/ufo/compile/feature_writers/base.rb +56 -0
  14. data/lib/fontisan/ufo/compile/feature_writers/curs.rb +72 -0
  15. data/lib/fontisan/ufo/compile/feature_writers/gdef.rb +90 -0
  16. data/lib/fontisan/ufo/compile/feature_writers/kern.rb +51 -0
  17. data/lib/fontisan/ufo/compile/feature_writers/kern2.rb +57 -0
  18. data/lib/fontisan/ufo/compile/feature_writers/mark.rb +45 -0
  19. data/lib/fontisan/ufo/compile/feature_writers/mark_family_base.rb +122 -0
  20. data/lib/fontisan/ufo/compile/feature_writers/mkmk.rb +53 -0
  21. data/lib/fontisan/ufo/compile/feature_writers.rb +37 -0
  22. data/lib/fontisan/ufo/compile/filters/propagate_anchors.rb +102 -0
  23. data/lib/fontisan/ufo/compile/filters/remove_overlaps.rb +94 -0
  24. data/lib/fontisan/ufo/compile/filters/sort_contours.rb +69 -0
  25. data/lib/fontisan/ufo/compile/filters/transformations.rb +113 -0
  26. data/lib/fontisan/ufo/compile/filters.rb +12 -0
  27. data/lib/fontisan/ufo/compile.rb +1 -0
  28. data/lib/fontisan/ufo/convert/to_dfont.rb +41 -0
  29. data/lib/fontisan/ufo/convert/to_otc.rb +37 -0
  30. data/lib/fontisan/ufo/convert/to_otf.rb +18 -0
  31. data/lib/fontisan/ufo/convert/to_otf2.rb +20 -0
  32. data/lib/fontisan/ufo/convert/to_postscript.rb +59 -0
  33. data/lib/fontisan/ufo/convert/to_ttc.rb +35 -0
  34. data/lib/fontisan/ufo/convert/to_ttf.rb +21 -0
  35. data/lib/fontisan/ufo/convert/to_woff.rb +56 -0
  36. data/lib/fontisan/ufo/convert/to_woff2.rb +45 -0
  37. data/lib/fontisan/ufo/convert.rb +68 -5
  38. data/lib/fontisan/ufo/transformation.rb +11 -0
  39. data/lib/fontisan/version.rb +1 -1
  40. metadata +27 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1ed3f50509fb7f223b2cdcfdf7e59345e83b5ca2d11b2a0806574651687c4582
4
- data.tar.gz: ccb6cabe8c99dfcc060acc7f46d26efd9961b5384365ab404552dad539dc4555
3
+ metadata.gz: e97f99deb241e5abd1bbdaeb27849efb3a4597bf2139fc8a1fa815fcccbfdfcb
4
+ data.tar.gz: ced71eac0e00e479581c5460e33672aa80ded30cd40cb590b5a09621c5441290
5
5
  SHA512:
6
- metadata.gz: b977d4f9ac972020514daca43a14917784c713258f73223597c8a48dd2b32fcf3ccb9adbe2bd343db7ac43e63eaba8943a883580ec3c7bb7dec23f6a0f1e1fe9
7
- data.tar.gz: 5c17de9dc3c60f44e472e94c19a1ca33435805ed81f261cc357dfcfd6d197f3109531ae8ad4b3a842e52f4f04de19501c0e860793212b87e874b63f01d99d8f3
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]
@@ -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
- === Future: ByBlock, ByScript
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
- ByBlock (Unicode Blocks.txt, ~350 entries) and ByScript (Scripts.txt)
194
- are tracked as a follow-up. The framework is already open for them:
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