fontisan 0.4.10 → 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/Rakefile +53 -39
- 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/partition_strategy/by_block.rb +429 -0
- data/lib/fontisan/stitcher/partition_strategy/by_script.rb +305 -0
- data/lib/fontisan/stitcher/partition_strategy.rb +2 -0
- data/lib/fontisan/stitcher/source.rb +11 -7
- data/lib/fontisan/subset/table_subsetter.rb +227 -11
- data/lib/fontisan/svg/standalone_glyph.rb +129 -0
- data/lib/fontisan/svg.rb +1 -0
- data/lib/fontisan/tasks/fixture_downloader.rb +162 -0
- data/lib/fontisan/tasks.rb +11 -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
- data/lib/fontisan.rb +1 -0
- metadata +32 -2
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])
|
data/Rakefile
CHANGED
|
@@ -12,24 +12,25 @@ RuboCop::RakeTask.new
|
|
|
12
12
|
namespace :fixtures do
|
|
13
13
|
# Load centralized fixture configuration
|
|
14
14
|
require_relative "spec/support/fixture_fonts"
|
|
15
|
+
require "fontisan/tasks"
|
|
15
16
|
|
|
16
17
|
# Helper method to download a single file
|
|
17
18
|
def download_single_file(name, url, target_path)
|
|
18
|
-
require "open-uri"
|
|
19
|
-
|
|
20
19
|
puts "[fixtures:download] Downloading #{name}..."
|
|
21
|
-
FileUtils.mkdir_p(File.dirname(target_path))
|
|
22
20
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
Fontisan::Tasks::FixtureDownloader.new(
|
|
22
|
+
url: url,
|
|
23
|
+
destination: target_path,
|
|
24
|
+
).call
|
|
26
25
|
|
|
27
26
|
puts "[fixtures:download] #{name} downloaded successfully"
|
|
27
|
+
rescue Fontisan::Tasks::FixtureDownloader::Error => e
|
|
28
|
+
warn "[fixtures:download] #{name} failed after retries: #{e.message}"
|
|
29
|
+
raise
|
|
28
30
|
end
|
|
29
31
|
|
|
30
32
|
# Helper method to download and extract a font archive
|
|
31
33
|
def download_font(name, url, target_dir)
|
|
32
|
-
require "open-uri"
|
|
33
34
|
require "zip"
|
|
34
35
|
|
|
35
36
|
puts "[fixtures:download] Downloading #{name}..."
|
|
@@ -39,45 +40,58 @@ namespace :fixtures do
|
|
|
39
40
|
temp_path = File.join(Dir.tmpdir,
|
|
40
41
|
"fontisan_#{name}_#{Process.pid}_#{rand(10000)}.zip")
|
|
41
42
|
|
|
42
|
-
# Download using IO.copy_stream for better Windows compatibility
|
|
43
|
-
URI.open(url, "rb") do |remote|
|
|
44
|
-
File.open(temp_path, "wb") do |file|
|
|
45
|
-
IO.copy_stream(remote, file)
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
puts "[fixtures:download] Extracting #{name}..."
|
|
50
|
-
|
|
51
|
-
# Open zip file and ensure it's fully closed before we're done
|
|
52
|
-
zip_file = Zip::File.open(temp_path)
|
|
53
43
|
begin
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
44
|
+
Fontisan::Tasks::FixtureDownloader.new(
|
|
45
|
+
url: url,
|
|
46
|
+
destination: temp_path,
|
|
47
|
+
).call
|
|
48
|
+
|
|
49
|
+
puts "[fixtures:download] Extracting #{name}..."
|
|
50
|
+
|
|
51
|
+
# Open zip file and ensure it's fully closed before we're done
|
|
52
|
+
zip_file = Zip::File.open(temp_path)
|
|
53
|
+
begin
|
|
54
|
+
zip_file.each do |entry|
|
|
55
|
+
# Skip macOS metadata files and directories
|
|
56
|
+
next if entry.name.start_with?("__MACOSX/") || entry.name.include?("/._")
|
|
57
|
+
next if entry.directory?
|
|
58
|
+
|
|
59
|
+
# Ensure entry.name is relative by stripping leading slashes
|
|
60
|
+
relative_name = entry.name.sub(%r{^/+}, "")
|
|
61
|
+
|
|
62
|
+
dest_path = File.join(target_dir, relative_name)
|
|
63
|
+
FileUtils.mkdir_p(File.dirname(dest_path))
|
|
64
|
+
|
|
65
|
+
# Skip if file already exists
|
|
66
|
+
next if File.exist?(dest_path)
|
|
67
|
+
|
|
68
|
+
# Write the file content directly using binary mode
|
|
69
|
+
File.open(dest_path, "wb") do |file|
|
|
70
|
+
IO.copy_stream(entry.get_input_stream, file)
|
|
71
|
+
end
|
|
71
72
|
end
|
|
73
|
+
ensure
|
|
74
|
+
# Explicitly close the zip file to release file handle on Windows
|
|
75
|
+
zip_file&.close
|
|
72
76
|
end
|
|
73
77
|
ensure
|
|
74
|
-
#
|
|
75
|
-
|
|
78
|
+
# Clean up the temp zip explicitly so the temp dir doesn't fill
|
|
79
|
+
# up on repeated runs. On Windows the just-closed zip file
|
|
80
|
+
# handle can briefly hold a lock that surfaces as
|
|
81
|
+
# Errno::EACCES; swallow that one error so the rake task can
|
|
82
|
+
# complete (OS will sweep the temp file later).
|
|
83
|
+
begin
|
|
84
|
+
File.delete(temp_path) if File.exist?(temp_path)
|
|
85
|
+
rescue Errno::EACCES
|
|
86
|
+
warn "[fixtures:download] could not delete temp zip #{temp_path}; " \
|
|
87
|
+
"OS will clean it up"
|
|
88
|
+
end
|
|
76
89
|
end
|
|
77
90
|
|
|
78
|
-
# Temp file left in Dir.tmpdir - OS will clean it up automatically
|
|
79
|
-
|
|
80
91
|
puts "[fixtures:download] #{name} downloaded successfully"
|
|
92
|
+
rescue Fontisan::Tasks::FixtureDownloader::Error => e
|
|
93
|
+
warn "[fixtures:download] #{name} failed after retries: #{e.message}"
|
|
94
|
+
raise
|
|
81
95
|
rescue LoadError => e
|
|
82
96
|
warn "[fixtures:download] Error: Required gem not installed. Please run: gem install rubyzip"
|
|
83
97
|
raise e
|
|
@@ -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]
|