fontisan 0.2.7 → 0.2.9
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/.rubocop.yml +103 -0
- data/.rubocop_todo.yml +65 -361
- data/CHANGELOG.md +116 -0
- data/Gemfile +1 -1
- data/README.adoc +106 -27
- data/Rakefile +12 -7
- data/benchmark/variation_quick_bench.rb +1 -1
- data/docs/APPLE_LEGACY_FONTS.adoc +173 -0
- data/docs/COLLECTION_VALIDATION.adoc +143 -0
- data/docs/COLOR_FONTS.adoc +127 -0
- data/docs/DOCUMENTATION_SUMMARY.md +141 -0
- data/docs/FONT_HINTING.adoc +9 -1
- data/docs/VALIDATION.adoc +254 -0
- data/docs/WOFF_WOFF2_FORMATS.adoc +94 -0
- data/lib/fontisan/cli.rb +45 -13
- data/lib/fontisan/collection/dfont_builder.rb +2 -1
- data/lib/fontisan/commands/convert_command.rb +2 -4
- data/lib/fontisan/commands/info_command.rb +3 -3
- data/lib/fontisan/commands/pack_command.rb +2 -1
- data/lib/fontisan/commands/validate_command.rb +157 -6
- data/lib/fontisan/converters/collection_converter.rb +22 -13
- data/lib/fontisan/converters/svg_generator.rb +2 -1
- data/lib/fontisan/converters/woff2_encoder.rb +6 -6
- data/lib/fontisan/converters/woff_writer.rb +3 -1
- data/lib/fontisan/font_loader.rb +7 -6
- data/lib/fontisan/formatters/text_formatter.rb +18 -14
- data/lib/fontisan/hints/hint_converter.rb +1 -1
- data/lib/fontisan/hints/hint_validator.rb +13 -10
- data/lib/fontisan/hints/truetype_instruction_analyzer.rb +15 -8
- data/lib/fontisan/hints/truetype_instruction_generator.rb +1 -1
- data/lib/fontisan/models/collection_validation_report.rb +104 -0
- data/lib/fontisan/models/font_report.rb +24 -0
- data/lib/fontisan/models/validation_report.rb +7 -2
- data/lib/fontisan/open_type_font.rb +18 -425
- data/lib/fontisan/optimizers/charstring_rewriter.rb +1 -1
- data/lib/fontisan/optimizers/subroutine_optimizer.rb +6 -2
- data/lib/fontisan/sfnt_font.rb +699 -0
- data/lib/fontisan/sfnt_table.rb +264 -0
- data/lib/fontisan/subset/glyph_mapping.rb +2 -0
- data/lib/fontisan/subset/table_subsetter.rb +2 -2
- data/lib/fontisan/tables/cblc.rb +8 -4
- data/lib/fontisan/tables/cff/index.rb +2 -0
- data/lib/fontisan/tables/cff.rb +6 -3
- data/lib/fontisan/tables/cff2/private_dict_blend_handler.rb +1 -1
- data/lib/fontisan/tables/cff2.rb +1 -1
- data/lib/fontisan/tables/cmap.rb +5 -5
- data/lib/fontisan/tables/cmap_table.rb +231 -0
- data/lib/fontisan/tables/glyf.rb +8 -10
- data/lib/fontisan/tables/glyf_table.rb +255 -0
- data/lib/fontisan/tables/head.rb +3 -3
- data/lib/fontisan/tables/head_table.rb +111 -0
- data/lib/fontisan/tables/hhea.rb +4 -4
- data/lib/fontisan/tables/hhea_table.rb +255 -0
- data/lib/fontisan/tables/hmtx_table.rb +191 -0
- data/lib/fontisan/tables/loca_table.rb +212 -0
- data/lib/fontisan/tables/maxp.rb +2 -2
- data/lib/fontisan/tables/maxp_table.rb +258 -0
- data/lib/fontisan/tables/name.rb +1 -1
- data/lib/fontisan/tables/name_table.rb +176 -0
- data/lib/fontisan/tables/os2.rb +8 -8
- data/lib/fontisan/tables/os2_table.rb +329 -0
- data/lib/fontisan/tables/post.rb +2 -2
- data/lib/fontisan/tables/post_table.rb +183 -0
- data/lib/fontisan/tables/sbix.rb +5 -4
- data/lib/fontisan/true_type_font.rb +12 -464
- data/lib/fontisan/utilities/checksum_calculator.rb +0 -44
- data/lib/fontisan/validation/collection_validator.rb +4 -2
- data/lib/fontisan/validators/basic_validator.rb +11 -21
- data/lib/fontisan/validators/font_book_validator.rb +29 -50
- data/lib/fontisan/validators/opentype_validator.rb +24 -28
- data/lib/fontisan/validators/validator.rb +87 -66
- data/lib/fontisan/validators/web_font_validator.rb +16 -21
- data/lib/fontisan/version.rb +1 -1
- data/lib/fontisan/woff2/glyf_transformer.rb +31 -8
- data/lib/fontisan/woff2/hmtx_transformer.rb +2 -1
- data/lib/fontisan/woff2/table_transformer.rb +4 -2
- data/lib/fontisan/woff2_font.rb +4 -2
- data/lib/fontisan/woff_font.rb +46 -30
- data/lib/fontisan.rb +2 -2
- data/scripts/compare_stack_aware.rb +1 -1
- data/scripts/measure_optimization.rb +1 -2
- metadata +23 -2
data/Gemfile
CHANGED
data/README.adoc
CHANGED
|
@@ -56,40 +56,52 @@ gem install fontisan
|
|
|
56
56
|
|
|
57
57
|
== Features
|
|
58
58
|
|
|
59
|
+
Color fonts and collections::
|
|
60
|
+
* Color font support: COLR/CPAL layered glyphs, sbix bitmap glyphs, and SVG table (see link:docs/COLOR_FONTS.adoc[Color Fonts Guide])
|
|
61
|
+
* Font collection validation with per-font reporting (see link:docs/COLLECTION_VALIDATION.adoc[Collection Validation Guide])
|
|
62
|
+
|
|
63
|
+
Font analysis and information::
|
|
59
64
|
* Bidirectional font hint conversion (see link:docs/FONT_HINTING.adoc[Font Hinting Guide])
|
|
60
65
|
* Extract comprehensive font metadata (name, version, designer, license, etc.)
|
|
66
|
+
* Brief mode for fast font indexing (5x faster, metadata-only loading)
|
|
61
67
|
* List OpenType tables with checksums and offsets
|
|
62
68
|
* Extract glyph names from post table
|
|
63
69
|
* Display Unicode codepoint to glyph index mappings
|
|
64
70
|
* Analyze variable font axes and named instances
|
|
65
|
-
* Generate static font instances from variable fonts
|
|
66
71
|
* Display optical size information
|
|
67
72
|
* List supported scripts from GSUB/GPOS tables
|
|
68
73
|
* List OpenType features (ligatures, kerning, etc.) by script
|
|
69
74
|
* Dump raw binary table data for analysis
|
|
75
|
+
|
|
76
|
+
Font operations::
|
|
77
|
+
* Generate static font instances from variable fonts
|
|
70
78
|
* Font subsetting with multiple profiles (PDF, web, minimal)
|
|
71
|
-
* Font validation with multiple
|
|
72
|
-
* Collection management (pack/unpack TTC/OTC files with table deduplication)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
*
|
|
76
|
-
* WOFF2 format support
|
|
79
|
+
* Font validation framework with multiple profiles (indexability, usability, production, web, spec_compliance) (see link:docs/VALIDATION.adoc[Validation Guide])
|
|
80
|
+
* Collection management (pack/unpack TTC/OTC/dfont files with table deduplication)
|
|
81
|
+
|
|
82
|
+
Font format support::
|
|
83
|
+
* TTF, OTF, TTC, OTC font formats (production ready)
|
|
84
|
+
* WOFF/WOFF2 format support with reading, writing, and conversion (see link:docs/WOFF_WOFF2_FORMATS.adoc[WOFF/WOFF2 Guide])
|
|
85
|
+
* Apple legacy font support: 'true' signature TrueType fonts and dfont format (see link:docs/APPLE_LEGACY_FONTS.adoc[Apple Legacy Fonts Guide])
|
|
77
86
|
* SVG font generation (complete)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
*
|
|
81
|
-
* Structured output in YAML, JSON, and text formats
|
|
82
|
-
* Universal outline model for format-agnostic glyph representation (complete)
|
|
87
|
+
|
|
88
|
+
Font engineering::
|
|
89
|
+
* Universal outline model for format-agnostic glyph representation
|
|
83
90
|
* CFF CharString encoding/decoding (complete)
|
|
84
91
|
* CFF INDEX structure building (complete)
|
|
85
92
|
* CFF DICT structure building (complete)
|
|
86
93
|
* TrueType curve converter for bi-directional quadratic/cubic conversion (complete)
|
|
87
94
|
* Compound glyph decomposition with transformation support (complete)
|
|
88
95
|
* CFF subroutine optimization for space-efficient OTF generation (preview mode)
|
|
89
|
-
* Various loading modes for high-performance font indexing (5x faster)
|
|
90
96
|
* Bidirectional hint conversion (TrueType ↔ PostScript) with validation (complete)
|
|
91
97
|
* CFF2 variable font support for PostScript hint conversion (complete)
|
|
92
98
|
|
|
99
|
+
Export and interfaces::
|
|
100
|
+
* TTX/YAML/JSON export (complete)
|
|
101
|
+
* Command-line interface with 18 commands
|
|
102
|
+
* Ruby library API for programmatic access
|
|
103
|
+
* Structured output in YAML, JSON, and text formats
|
|
104
|
+
|
|
93
105
|
|
|
94
106
|
== Font information
|
|
95
107
|
|
|
@@ -1034,26 +1046,19 @@ structures, usability, instructions, and glyphs.
|
|
|
1034
1046
|
|
|
1035
1047
|
=== Predefined profiles
|
|
1036
1048
|
|
|
1037
|
-
Fontisan includes
|
|
1049
|
+
Fontisan includes validation profiles for different use cases:
|
|
1038
1050
|
|
|
1039
|
-
`indexability`:: Fast
|
|
1040
|
-
BasicValidator with 8 essential checks. Loading mode: metadata.
|
|
1051
|
+
`indexability`:: Fast font discovery and indexing (~5x faster, 8 checks, metadata-only)
|
|
1041
1052
|
|
|
1042
|
-
`usability`::
|
|
1043
|
-
26 checks including macOS Font Book compatibility. Loading mode: full.
|
|
1053
|
+
`usability`:: Font installation compatibility (26 checks, macOS Font Book focused)
|
|
1044
1054
|
|
|
1045
|
-
`production`:: Comprehensive quality checks
|
|
1046
|
-
profile). Uses OpenTypeValidator with 36 checks for OpenType spec compliance.
|
|
1047
|
-
Loading mode: full.
|
|
1055
|
+
`production`:: Comprehensive production quality (36 checks, OpenType spec compliance - default)
|
|
1048
1056
|
|
|
1049
|
-
`web`:: Web font embedding
|
|
1050
|
-
with 18 checks for web deployment. Loading mode: full.
|
|
1057
|
+
`web`:: Web font embedding readiness (18 checks for web deployment)
|
|
1051
1058
|
|
|
1052
|
-
`spec_compliance`:: Full OpenType specification compliance
|
|
1053
|
-
Uses OpenTypeValidator with info-level severity for comprehensive analysis.
|
|
1054
|
-
Loading mode: full.
|
|
1059
|
+
`spec_compliance`:: Full OpenType specification compliance (detailed analysis mode)
|
|
1055
1060
|
|
|
1056
|
-
`default`:: Alias for the production profile
|
|
1061
|
+
`default`:: Alias for the production profile
|
|
1057
1062
|
|
|
1058
1063
|
|
|
1059
1064
|
=== Command-line usage
|
|
@@ -1154,6 +1159,80 @@ production, web, spec_compliance, default)
|
|
|
1154
1159
|
|
|
1155
1160
|
-e, --exclude CHECKS:: Exclude specific checks (comma-separated list)
|
|
1156
1161
|
|
|
1162
|
+
=== Collection validation
|
|
1163
|
+
|
|
1164
|
+
The `validate` command automatically detects and validates font collections (TTC, OTC, dfont).
|
|
1165
|
+
When validating a collection, fontisan validates all fonts in the collection and displays
|
|
1166
|
+
per-font results with an overall summary.
|
|
1167
|
+
|
|
1168
|
+
.Validation profiles with collections
|
|
1169
|
+
[example]
|
|
1170
|
+
====
|
|
1171
|
+
All validation profiles work with font collections. The selected profile determines:
|
|
1172
|
+
|
|
1173
|
+
* **Which tables are loaded** - metadata vs full mode
|
|
1174
|
+
* **Which checks are performed** - number and type of validations
|
|
1175
|
+
* **Performance characteristics** - indexability is ~5x faster than production
|
|
1176
|
+
|
|
1177
|
+
For large collections (CJK fonts with 30+ fonts), use the `indexability` profile for fast
|
|
1178
|
+
validation when scanning for font discovery, or `production` for comprehensive quality
|
|
1179
|
+
checks.
|
|
1180
|
+
|
|
1181
|
+
[source,shell]
|
|
1182
|
+
----
|
|
1183
|
+
# Quick validation for indexing (metadata mode, 8 checks, ~5x faster)
|
|
1184
|
+
$ fontisan validate /path/to/font.ttc -t indexability
|
|
1185
|
+
|
|
1186
|
+
# Comprehensive validation (full mode, 37 checks)
|
|
1187
|
+
$ fontisan validate /path/to/font.ttc -t production
|
|
1188
|
+
|
|
1189
|
+
# Web font readiness validation
|
|
1190
|
+
$ fontisan validate /path/to/font.ttc -t web
|
|
1191
|
+
----
|
|
1192
|
+
|
|
1193
|
+
The collection validation output includes:
|
|
1194
|
+
|
|
1195
|
+
* *Collection header* - Path, type, and number of fonts
|
|
1196
|
+
* *Summary* - Total errors, warnings, and info across all fonts
|
|
1197
|
+
* *Per-font sections* - Individual validation results for each font with:
|
|
1198
|
+
** Font index and name
|
|
1199
|
+
** Font path in `collection.ttc:index` format
|
|
1200
|
+
** Individual font status (VALID/INVALID/VALID_WITH_WARNINGS)
|
|
1201
|
+
** Font-specific errors and warnings
|
|
1202
|
+
** Exit codes* - For collections, uses the "worst" status across all fonts:
|
|
1203
|
+
** 0 = All fonts valid
|
|
1204
|
+
** 2 = Any font has fatal errors
|
|
1205
|
+
** 3 = Any font has errors (and no fatal)
|
|
1206
|
+
** 4 = Any font has warnings (and no errors)
|
|
1207
|
+
** 5 = Any font has info issues (and no errors or warnings)
|
|
1208
|
+
====
|
|
1209
|
+
|
|
1210
|
+
.Validate a font collection
|
|
1211
|
+
[example]
|
|
1212
|
+
====
|
|
1213
|
+
[source,shell]
|
|
1214
|
+
----
|
|
1215
|
+
$ fontisan validate /path/to/font.ttc
|
|
1216
|
+
Collection: /path/to/font.ttc
|
|
1217
|
+
Type: TTC
|
|
1218
|
+
Fonts: 4
|
|
1219
|
+
|
|
1220
|
+
Summary:
|
|
1221
|
+
Total Errors: 14
|
|
1222
|
+
Total Warnings: 8
|
|
1223
|
+
Total Info: 0
|
|
1224
|
+
|
|
1225
|
+
=== Font 0: Lucida Grande ===
|
|
1226
|
+
Font: /path/to/font.ttc:0
|
|
1227
|
+
Status: INVALID
|
|
1228
|
+
...
|
|
1229
|
+
|
|
1230
|
+
=== Font 1: Lucida Grande Bold ===
|
|
1231
|
+
Font: /path/to/font.ttc:1
|
|
1232
|
+
Status: INVALID
|
|
1233
|
+
...
|
|
1234
|
+
----
|
|
1235
|
+
====
|
|
1157
1236
|
|
|
1158
1237
|
=== Ruby API usage
|
|
1159
1238
|
|
data/Rakefile
CHANGED
|
@@ -8,6 +8,7 @@ require "rubocop/rake_task"
|
|
|
8
8
|
|
|
9
9
|
RuboCop::RakeTask.new
|
|
10
10
|
|
|
11
|
+
# rubocop:disable Metrics/BlockLength
|
|
11
12
|
namespace :fixtures do
|
|
12
13
|
# Load centralized fixture configuration
|
|
13
14
|
require_relative "spec/support/fixture_fonts"
|
|
@@ -99,8 +100,13 @@ namespace :fixtures do
|
|
|
99
100
|
end
|
|
100
101
|
end
|
|
101
102
|
|
|
103
|
+
# Compute download task prerequisites (marker files for non-skipped fonts)
|
|
104
|
+
download_prerequisites = fonts.values.reject do |config|
|
|
105
|
+
config[:skip_download]
|
|
106
|
+
end.map { |config| config[:marker] }
|
|
107
|
+
|
|
102
108
|
desc "Download all test fixture fonts"
|
|
103
|
-
task download:
|
|
109
|
+
task download: download_prerequisites
|
|
104
110
|
|
|
105
111
|
desc "Clean downloaded fixtures"
|
|
106
112
|
task :clean do
|
|
@@ -112,17 +118,16 @@ namespace :fixtures do
|
|
|
112
118
|
FileUtils.rm_f(config[:marker])
|
|
113
119
|
puts "[fixtures:clean] Removed #{config[:marker]}"
|
|
114
120
|
end
|
|
115
|
-
|
|
121
|
+
elsif File.exist?(config[:target_dir])
|
|
116
122
|
# For archives, delete the entire target directory
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
puts "[fixtures:clean] Removed #{config[:target_dir]}"
|
|
121
|
-
end
|
|
123
|
+
puts "[fixtures:clean] Removing #{config[:target_dir]}..."
|
|
124
|
+
FileUtils.rm_rf(config[:target_dir])
|
|
125
|
+
puts "[fixtures:clean] Removed #{config[:target_dir]}"
|
|
122
126
|
end
|
|
123
127
|
end
|
|
124
128
|
end
|
|
125
129
|
end
|
|
130
|
+
# rubocop:enable Metrics/BlockLength
|
|
126
131
|
|
|
127
132
|
# RSpec task depends on fixtures
|
|
128
133
|
RSpec::Core::RakeTask.new(spec: "fixtures:download")
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
= Apple Legacy Font Format Support
|
|
2
|
+
|
|
3
|
+
== General
|
|
4
|
+
|
|
5
|
+
Fontisan provides support for Apple's legacy font formats that predate modern
|
|
6
|
+
OpenType standards:
|
|
7
|
+
|
|
8
|
+
* **'true' signature TrueType fonts** - Older Mac fonts with 'true' sfnt version
|
|
9
|
+
* **dfont (Data Fork Font)** - Mac suitcase format with fonts in data fork
|
|
10
|
+
|
|
11
|
+
These formats require special handling due to their unique structures and
|
|
12
|
+
historical platform-specific conventions.
|
|
13
|
+
|
|
14
|
+
=== 'true' signature TrueType fonts
|
|
15
|
+
|
|
16
|
+
Traditional TrueType fonts use 'true' (0x74727565) as the SFNT version instead
|
|
17
|
+
of the modern 0x00010000. These were common on classic Mac OS.
|
|
18
|
+
|
|
19
|
+
[source,shell]
|
|
20
|
+
----
|
|
21
|
+
$ fontisan info legacy_font.ttf
|
|
22
|
+
|
|
23
|
+
Font type: TrueType (Not Variable)
|
|
24
|
+
SFNT Version: 'true' (legacy)
|
|
25
|
+
Family: Legacy Font
|
|
26
|
+
...
|
|
27
|
+
|
|
28
|
+
Detection:
|
|
29
|
+
Fontisan automatically detects 'true' signature fonts
|
|
30
|
+
Treats them as standard TrueType for all operations
|
|
31
|
+
Preserves signature format when writing
|
|
32
|
+
----
|
|
33
|
+
|
|
34
|
+
=== dfont (Data Fork Font) format
|
|
35
|
+
|
|
36
|
+
The dfont format stores complete Mac font suitcases in the data fork, supporting
|
|
37
|
+
multiple fonts in a single file with resource-fork style structure.
|
|
38
|
+
|
|
39
|
+
[source,shell]
|
|
40
|
+
----
|
|
41
|
+
$ fontisan ls family.dfont
|
|
42
|
+
|
|
43
|
+
Font 0: Arial
|
|
44
|
+
Family: Arial
|
|
45
|
+
Subfamily: Regular
|
|
46
|
+
|
|
47
|
+
Font 1: Arial
|
|
48
|
+
Family: Arial
|
|
49
|
+
Subfamily: Bold
|
|
50
|
+
|
|
51
|
+
Font 2: Arial
|
|
52
|
+
Family: Arial
|
|
53
|
+
Subfamily: Italic
|
|
54
|
+
----
|
|
55
|
+
|
|
56
|
+
.dfont structure
|
|
57
|
+
[example]
|
|
58
|
+
====
|
|
59
|
+
[source,text]
|
|
60
|
+
----
|
|
61
|
+
dfont format structure:
|
|
62
|
+
|
|
63
|
+
Data Fork Map:
|
|
64
|
+
Resource data: Contains all font resources
|
|
65
|
+
Resource map: Index of resources by type and ID
|
|
66
|
+
Postscript names: Font name mapping
|
|
67
|
+
|
|
68
|
+
Supported fonts in dfont:
|
|
69
|
+
- TrueType fonts ('true' signature)
|
|
70
|
+
- OpenType fonts (CFF or TrueType outlines)
|
|
71
|
+
- Mixed formats in single dfont
|
|
72
|
+
|
|
73
|
+
Features:
|
|
74
|
+
- Automatic resource fork extraction
|
|
75
|
+
- Font list with metadata
|
|
76
|
+
- Extract individual fonts by index
|
|
77
|
+
- Unpack all fonts to directory
|
|
78
|
+
- Validate fonts within dfont
|
|
79
|
+
====
|
|
80
|
+
----
|
|
81
|
+
|
|
82
|
+
=== Working with dfont files
|
|
83
|
+
|
|
84
|
+
==== List fonts in dfont
|
|
85
|
+
|
|
86
|
+
[source,shell]
|
|
87
|
+
----
|
|
88
|
+
$ fontisan ls family.dfont
|
|
89
|
+
|
|
90
|
+
Font 0: Arial Regular
|
|
91
|
+
Family: Arial
|
|
92
|
+
Subfamily: Regular
|
|
93
|
+
PostScript: Arial
|
|
94
|
+
|
|
95
|
+
Font 1: Arial Bold
|
|
96
|
+
Family: Arial
|
|
97
|
+
Subfamily: Bold
|
|
98
|
+
PostScript: Arial-Bold
|
|
99
|
+
----
|
|
100
|
+
|
|
101
|
+
==== Show dfont info
|
|
102
|
+
|
|
103
|
+
[source,shell]
|
|
104
|
+
----
|
|
105
|
+
$ fontisan info family.dfont
|
|
106
|
+
|
|
107
|
+
Collection: family.dfont
|
|
108
|
+
Type: dfont
|
|
109
|
+
Fonts: 3
|
|
110
|
+
|
|
111
|
+
Font 0 (offset: 256):
|
|
112
|
+
Font type: TrueType (Not Variable)
|
|
113
|
+
Family: Arial
|
|
114
|
+
Subfamily: Regular
|
|
115
|
+
...
|
|
116
|
+
----
|
|
117
|
+
|
|
118
|
+
==== Extract fonts from dfont
|
|
119
|
+
|
|
120
|
+
[source,shell]
|
|
121
|
+
----
|
|
122
|
+
# Extract all fonts
|
|
123
|
+
$ fontisan unpack family.dfont extracted_fonts/
|
|
124
|
+
|
|
125
|
+
# Extract specific font
|
|
126
|
+
$ fontisan unpack family.dfont --font-index 0 Arial.ttf
|
|
127
|
+
----
|
|
128
|
+
|
|
129
|
+
==== Validate dfont
|
|
130
|
+
|
|
131
|
+
[source,shell]
|
|
132
|
+
----
|
|
133
|
+
$ fontisan validate family.dfont -t indexability
|
|
134
|
+
|
|
135
|
+
Collection: family.dfont
|
|
136
|
+
Type: dfont
|
|
137
|
+
Fonts: 3
|
|
138
|
+
|
|
139
|
+
Summary:
|
|
140
|
+
Total Errors: 0
|
|
141
|
+
Total Warnings: 2
|
|
142
|
+
|
|
143
|
+
=== Font 0: Arial Regular ===
|
|
144
|
+
Font: family.dfont:0
|
|
145
|
+
Status: VALID_WITH_WARNINGS
|
|
146
|
+
...
|
|
147
|
+
----
|
|
148
|
+
|
|
149
|
+
=== Ruby API for dfont
|
|
150
|
+
|
|
151
|
+
[source,ruby]
|
|
152
|
+
----
|
|
153
|
+
require 'fontisan'
|
|
154
|
+
|
|
155
|
+
# Load dfont collection
|
|
156
|
+
dfont = Fontisan::FontLoader.load('family.dfont')
|
|
157
|
+
|
|
158
|
+
# Access as collection
|
|
159
|
+
puts "Font count: #{dfont.font_count}"
|
|
160
|
+
File.open('family.dfont', 'rb') do |io|
|
|
161
|
+
fonts = dfont.extract_fonts(io)
|
|
162
|
+
fonts.each_with_index do |font, i|
|
|
163
|
+
puts "Font #{i}: #{font.family_name}"
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Extract specific font
|
|
168
|
+
font = Fontisan::FontLoader.load('family.dfont', font_index: 0)
|
|
169
|
+
|
|
170
|
+
# Validate dfont
|
|
171
|
+
report = Fontisan.validate('family.dfont')
|
|
172
|
+
puts report.valid? # => true/false
|
|
173
|
+
----
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
= Font Collection Validation
|
|
2
|
+
|
|
3
|
+
== General
|
|
4
|
+
|
|
5
|
+
Fontisan provides validation support for font collections (TTC, OTC, dfont),
|
|
6
|
+
enabling quality assurance across multiple fonts in a single file.
|
|
7
|
+
|
|
8
|
+
Collection validation ensures all fonts in a collection meet quality standards
|
|
9
|
+
while providing per-font and aggregate reporting.
|
|
10
|
+
|
|
11
|
+
== Collection validation output
|
|
12
|
+
|
|
13
|
+
When validating a collection, Fontisan displays:
|
|
14
|
+
|
|
15
|
+
* *Collection header* - Path, type, and number of fonts
|
|
16
|
+
* *Summary* - Total errors, warnings, and info across all fonts
|
|
17
|
+
* *Per-font sections* - Individual validation results for each font with:
|
|
18
|
+
** Font index and name
|
|
19
|
+
** Font path in `collection.ttc:index` format
|
|
20
|
+
** Individual font status (VALID/INVALID/VALID_WITH_WARNINGS)
|
|
21
|
+
** Font-specific errors and warnings
|
|
22
|
+
|
|
23
|
+
.Using the CLI to validate a font collection
|
|
24
|
+
[example]
|
|
25
|
+
====
|
|
26
|
+
[source,shell]
|
|
27
|
+
----
|
|
28
|
+
$ fontisan validate /path/to/font.ttc
|
|
29
|
+
|
|
30
|
+
Collection: /path/to/font.ttc
|
|
31
|
+
Type: TTC
|
|
32
|
+
Fonts: 4
|
|
33
|
+
|
|
34
|
+
Summary:
|
|
35
|
+
Total Errors: 14
|
|
36
|
+
Total Warnings: 8
|
|
37
|
+
Total Info: 0
|
|
38
|
+
|
|
39
|
+
=== Font 0: Lucida Grande ===
|
|
40
|
+
Font: /path/to/font.ttc:0
|
|
41
|
+
Status: INVALID
|
|
42
|
+
|
|
43
|
+
Errors:
|
|
44
|
+
name_table.family_name_present - Family name is empty
|
|
45
|
+
head_table.valid_magic - Invalid magic number
|
|
46
|
+
|
|
47
|
+
Warnings:
|
|
48
|
+
os2_table.valid_version - Version 5 requires Windows 10+
|
|
49
|
+
|
|
50
|
+
=== Font 1: Lucida Grande Bold ===
|
|
51
|
+
Font: /path/to/font.ttc:1
|
|
52
|
+
Status: VALID
|
|
53
|
+
...
|
|
54
|
+
----
|
|
55
|
+
====
|
|
56
|
+
|
|
57
|
+
== Validation profiles with collections
|
|
58
|
+
|
|
59
|
+
All validation profiles work with font collections. The selected profile determines:
|
|
60
|
+
|
|
61
|
+
* **Which tables are loaded** - metadata vs full mode
|
|
62
|
+
* **Which checks are performed** - number and type of validations
|
|
63
|
+
* **Performance characteristics** - indexability is ~5x faster than production
|
|
64
|
+
|
|
65
|
+
.Using validation profiles with collections
|
|
66
|
+
[example]
|
|
67
|
+
====
|
|
68
|
+
[source,shell]
|
|
69
|
+
----
|
|
70
|
+
# Quick validation for indexing (metadata mode, 8 checks, ~5x faster)
|
|
71
|
+
$ fontisan validate /path/to/font.ttc -t indexability
|
|
72
|
+
|
|
73
|
+
# Comprehensive validation (full mode, 37 checks)
|
|
74
|
+
$ fontisan validate /path/to/font.ttc -t production
|
|
75
|
+
|
|
76
|
+
# Web font readiness validation
|
|
77
|
+
$ fontisan validate /path/to/font.ttc -t web
|
|
78
|
+
----
|
|
79
|
+
====
|
|
80
|
+
|
|
81
|
+
== Collection validation exit codes
|
|
82
|
+
|
|
83
|
+
For collections, the validation command uses the "worst" status across all fonts:
|
|
84
|
+
|
|
85
|
+
* **0** = All fonts valid
|
|
86
|
+
* **2** = Any font has fatal errors
|
|
87
|
+
* **3** = Any font has errors (and no fatal)
|
|
88
|
+
* **4** = Any font has warnings (and no errors)
|
|
89
|
+
* **5** = Any font has info issues (and no errors or warnings)
|
|
90
|
+
|
|
91
|
+
.Checking collection validation exit codes
|
|
92
|
+
[example]
|
|
93
|
+
====
|
|
94
|
+
[source,shell]
|
|
95
|
+
----
|
|
96
|
+
$ fontisan validate font.ttc -S -R
|
|
97
|
+
|
|
98
|
+
Collection: font.ttc
|
|
99
|
+
Type: TTC
|
|
100
|
+
Fonts: 3
|
|
101
|
+
|
|
102
|
+
Summary:
|
|
103
|
+
Total Errors: 2
|
|
104
|
+
Total Warnings: 5
|
|
105
|
+
|
|
106
|
+
$ echo $?
|
|
107
|
+
3 # Exit code 3 indicates errors found
|
|
108
|
+
----
|
|
109
|
+
====
|
|
110
|
+
|
|
111
|
+
== Ruby API for collection validation
|
|
112
|
+
|
|
113
|
+
.Using the Ruby API for collection validation
|
|
114
|
+
[example]
|
|
115
|
+
====
|
|
116
|
+
[source,ruby]
|
|
117
|
+
----
|
|
118
|
+
require 'fontisan'
|
|
119
|
+
|
|
120
|
+
# Validate collection
|
|
121
|
+
report = Fontisan.validate('font.ttc', profile: :production)
|
|
122
|
+
|
|
123
|
+
# Check overall status
|
|
124
|
+
puts report.overall_status # "valid", "valid_with_warnings", "invalid"
|
|
125
|
+
|
|
126
|
+
# Access per-font reports
|
|
127
|
+
report.font_reports.each_with_index do |font_report, i|
|
|
128
|
+
puts "Font #{i}: #{font_report.font_name}"
|
|
129
|
+
puts " Status: #{font_report.report.status}"
|
|
130
|
+
puts " Errors: #{font_report.report.summary.errors}"
|
|
131
|
+
puts " Warnings: #{font_report.report.summary.warnings}"
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Get aggregate statistics
|
|
135
|
+
puts "Total fonts: #{report.num_fonts}"
|
|
136
|
+
|
|
137
|
+
# Count valid/invalid fonts (requires iteration)
|
|
138
|
+
valid_count = report.font_reports.count { |fr| fr.report.valid? }
|
|
139
|
+
invalid_count = report.font_reports.count { |fr| !fr.report.valid? }
|
|
140
|
+
puts "Valid fonts: #{valid_count}"
|
|
141
|
+
puts "Invalid fonts: #{invalid_count}"
|
|
142
|
+
----
|
|
143
|
+
====
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
= Color Font Support
|
|
2
|
+
|
|
3
|
+
== General
|
|
4
|
+
|
|
5
|
+
Fontisan provides support for color fonts through multiple formats:
|
|
6
|
+
|
|
7
|
+
* **COLR/CPAL** - OpenType color layered glyphs (SVG-like layering in font)
|
|
8
|
+
* **sbix** - Apple color bitmap glyphs (embedded raster images)
|
|
9
|
+
* **SVG** - OpenType SVG color glyphs (embedded SVG vector graphics)
|
|
10
|
+
|
|
11
|
+
Color fonts enable rich typography with gradients, images, and artistic effects
|
|
12
|
+
beyond standard outline fonts.
|
|
13
|
+
|
|
14
|
+
=== COLR/CPAL color layered glyphs
|
|
15
|
+
|
|
16
|
+
The COLR table defines color layers using base glyphs and paint layers,
|
|
17
|
+
with the CPAL table defining color palettes.
|
|
18
|
+
|
|
19
|
+
[source,shell]
|
|
20
|
+
----
|
|
21
|
+
$ fontisan tables FONT.ttf --format yaml | grep -A 50 "tag: COLR"
|
|
22
|
+
----
|
|
23
|
+
|
|
24
|
+
.COLR table structure
|
|
25
|
+
[example]
|
|
26
|
+
====
|
|
27
|
+
[source,yaml]
|
|
28
|
+
----
|
|
29
|
+
---
|
|
30
|
+
tag: COLR
|
|
31
|
+
version: 0
|
|
32
|
+
num_base_glyphs: 15
|
|
33
|
+
num_layers: 47
|
|
34
|
+
num_palettes: 1
|
|
35
|
+
color_records: 8
|
|
36
|
+
|
|
37
|
+
Base glyphs:
|
|
38
|
+
- base_glyph: emoji_smile
|
|
39
|
+
layers:
|
|
40
|
+
- glyph: emoji_smile_layer0
|
|
41
|
+
palette_index: 1
|
|
42
|
+
color_index: 0
|
|
43
|
+
- glyph: emoji_smile_layer1
|
|
44
|
+
palette_index: 1
|
|
45
|
+
color_index: 1
|
|
46
|
+
----
|
|
47
|
+
====
|
|
48
|
+
|
|
49
|
+
=== sbix color bitmap glyphs
|
|
50
|
+
|
|
51
|
+
Apple's sbix table embeds raster images (PNG, JPG) for color glyphs.
|
|
52
|
+
Each glyph can have multiple bitmap strikes at different sizes.
|
|
53
|
+
|
|
54
|
+
[source,shell]
|
|
55
|
+
----
|
|
56
|
+
$ fontisan tables FONT.ttf | grep -A 20 "tag: sbix"
|
|
57
|
+
----
|
|
58
|
+
|
|
59
|
+
.sbix structure
|
|
60
|
+
[example]
|
|
61
|
+
====
|
|
62
|
+
[source,text]
|
|
63
|
+
----
|
|
64
|
+
sbix 245892 bytes (offset: 542992, checksum: 0x12345678)
|
|
65
|
+
|
|
66
|
+
Strikes:
|
|
67
|
+
Strike 0: 72 PPI
|
|
68
|
+
Bitmaps: 150 glyphs
|
|
69
|
+
Format: PNG
|
|
70
|
+
Glyph 1 (emoji_smile): 128x128 pixels, 8.2 KB
|
|
71
|
+
Glyph 2 (emoji_heart): 128x128 pixels, 7.8 KB
|
|
72
|
+
----
|
|
73
|
+
====
|
|
74
|
+
|
|
75
|
+
=== SVG table color glyphs
|
|
76
|
+
|
|
77
|
+
The SVG table contains inline SVG graphics for color glyphs, supporting
|
|
78
|
+
gradients, transparency, and complex vector artwork.
|
|
79
|
+
|
|
80
|
+
[source,shell]
|
|
81
|
+
----
|
|
82
|
+
$ fontisan dump-table FONT.ttf SVG > svg_content.svg
|
|
83
|
+
----
|
|
84
|
+
|
|
85
|
+
.SVG table structure
|
|
86
|
+
[example]
|
|
87
|
+
====
|
|
88
|
+
[source,text]
|
|
89
|
+
----
|
|
90
|
+
svg 45678 bytes (offset: 788760, checksum: 0xABCDEF01)
|
|
91
|
+
|
|
92
|
+
SVG Documents: 12
|
|
93
|
+
Glyph 1 (emoji_flag_us): 1.2 KB
|
|
94
|
+
Glyph 2 (emoji_heart): 0.8 KB
|
|
95
|
+
Glyph 3 (gradient_icon): 2.1 KB
|
|
96
|
+
----
|
|
97
|
+
====
|
|
98
|
+
|
|
99
|
+
=== Ruby API for color fonts
|
|
100
|
+
|
|
101
|
+
Access color font data through the Ruby API.
|
|
102
|
+
|
|
103
|
+
[source,ruby]
|
|
104
|
+
----
|
|
105
|
+
require 'fontisan'
|
|
106
|
+
|
|
107
|
+
font = Fontisan::FontLoader.load('color_font.ttf')
|
|
108
|
+
|
|
109
|
+
# Access COLR/CPAL data
|
|
110
|
+
colr = font.table('COLR')
|
|
111
|
+
cpal = font.table('CPAL')
|
|
112
|
+
|
|
113
|
+
puts "Base glyphs: #{colr.num_base_glyph_records}"
|
|
114
|
+
puts "Palettes: #{cpal.num_palettes}"
|
|
115
|
+
|
|
116
|
+
# Access sbix strikes
|
|
117
|
+
sbix = font.table('sbix')
|
|
118
|
+
sbix.strikes.each do |strike|
|
|
119
|
+
puts "Strike: #{strike[:ppi]} PPI, #{strike[:num_glyphs]} glyphs"
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Access SVG data
|
|
123
|
+
svg = font.table('SVG')
|
|
124
|
+
svg.document_records.each do |record|
|
|
125
|
+
puts "Glyphs #{record.start_glyph_id}-#{record.end_glyph_id}: #{record.svg_doc_length} bytes"
|
|
126
|
+
end
|
|
127
|
+
----
|