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.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +103 -0
  3. data/.rubocop_todo.yml +65 -361
  4. data/CHANGELOG.md +116 -0
  5. data/Gemfile +1 -1
  6. data/README.adoc +106 -27
  7. data/Rakefile +12 -7
  8. data/benchmark/variation_quick_bench.rb +1 -1
  9. data/docs/APPLE_LEGACY_FONTS.adoc +173 -0
  10. data/docs/COLLECTION_VALIDATION.adoc +143 -0
  11. data/docs/COLOR_FONTS.adoc +127 -0
  12. data/docs/DOCUMENTATION_SUMMARY.md +141 -0
  13. data/docs/FONT_HINTING.adoc +9 -1
  14. data/docs/VALIDATION.adoc +254 -0
  15. data/docs/WOFF_WOFF2_FORMATS.adoc +94 -0
  16. data/lib/fontisan/cli.rb +45 -13
  17. data/lib/fontisan/collection/dfont_builder.rb +2 -1
  18. data/lib/fontisan/commands/convert_command.rb +2 -4
  19. data/lib/fontisan/commands/info_command.rb +3 -3
  20. data/lib/fontisan/commands/pack_command.rb +2 -1
  21. data/lib/fontisan/commands/validate_command.rb +157 -6
  22. data/lib/fontisan/converters/collection_converter.rb +22 -13
  23. data/lib/fontisan/converters/svg_generator.rb +2 -1
  24. data/lib/fontisan/converters/woff2_encoder.rb +6 -6
  25. data/lib/fontisan/converters/woff_writer.rb +3 -1
  26. data/lib/fontisan/font_loader.rb +7 -6
  27. data/lib/fontisan/formatters/text_formatter.rb +18 -14
  28. data/lib/fontisan/hints/hint_converter.rb +1 -1
  29. data/lib/fontisan/hints/hint_validator.rb +13 -10
  30. data/lib/fontisan/hints/truetype_instruction_analyzer.rb +15 -8
  31. data/lib/fontisan/hints/truetype_instruction_generator.rb +1 -1
  32. data/lib/fontisan/models/collection_validation_report.rb +104 -0
  33. data/lib/fontisan/models/font_report.rb +24 -0
  34. data/lib/fontisan/models/validation_report.rb +7 -2
  35. data/lib/fontisan/open_type_font.rb +18 -425
  36. data/lib/fontisan/optimizers/charstring_rewriter.rb +1 -1
  37. data/lib/fontisan/optimizers/subroutine_optimizer.rb +6 -2
  38. data/lib/fontisan/sfnt_font.rb +699 -0
  39. data/lib/fontisan/sfnt_table.rb +264 -0
  40. data/lib/fontisan/subset/glyph_mapping.rb +2 -0
  41. data/lib/fontisan/subset/table_subsetter.rb +2 -2
  42. data/lib/fontisan/tables/cblc.rb +8 -4
  43. data/lib/fontisan/tables/cff/index.rb +2 -0
  44. data/lib/fontisan/tables/cff.rb +6 -3
  45. data/lib/fontisan/tables/cff2/private_dict_blend_handler.rb +1 -1
  46. data/lib/fontisan/tables/cff2.rb +1 -1
  47. data/lib/fontisan/tables/cmap.rb +5 -5
  48. data/lib/fontisan/tables/cmap_table.rb +231 -0
  49. data/lib/fontisan/tables/glyf.rb +8 -10
  50. data/lib/fontisan/tables/glyf_table.rb +255 -0
  51. data/lib/fontisan/tables/head.rb +3 -3
  52. data/lib/fontisan/tables/head_table.rb +111 -0
  53. data/lib/fontisan/tables/hhea.rb +4 -4
  54. data/lib/fontisan/tables/hhea_table.rb +255 -0
  55. data/lib/fontisan/tables/hmtx_table.rb +191 -0
  56. data/lib/fontisan/tables/loca_table.rb +212 -0
  57. data/lib/fontisan/tables/maxp.rb +2 -2
  58. data/lib/fontisan/tables/maxp_table.rb +258 -0
  59. data/lib/fontisan/tables/name.rb +1 -1
  60. data/lib/fontisan/tables/name_table.rb +176 -0
  61. data/lib/fontisan/tables/os2.rb +8 -8
  62. data/lib/fontisan/tables/os2_table.rb +329 -0
  63. data/lib/fontisan/tables/post.rb +2 -2
  64. data/lib/fontisan/tables/post_table.rb +183 -0
  65. data/lib/fontisan/tables/sbix.rb +5 -4
  66. data/lib/fontisan/true_type_font.rb +12 -464
  67. data/lib/fontisan/utilities/checksum_calculator.rb +0 -44
  68. data/lib/fontisan/validation/collection_validator.rb +4 -2
  69. data/lib/fontisan/validators/basic_validator.rb +11 -21
  70. data/lib/fontisan/validators/font_book_validator.rb +29 -50
  71. data/lib/fontisan/validators/opentype_validator.rb +24 -28
  72. data/lib/fontisan/validators/validator.rb +87 -66
  73. data/lib/fontisan/validators/web_font_validator.rb +16 -21
  74. data/lib/fontisan/version.rb +1 -1
  75. data/lib/fontisan/woff2/glyf_transformer.rb +31 -8
  76. data/lib/fontisan/woff2/hmtx_transformer.rb +2 -1
  77. data/lib/fontisan/woff2/table_transformer.rb +4 -2
  78. data/lib/fontisan/woff2_font.rb +4 -2
  79. data/lib/fontisan/woff_font.rb +46 -30
  80. data/lib/fontisan.rb +2 -2
  81. data/scripts/compare_stack_aware.rb +1 -1
  82. data/scripts/measure_optimization.rb +1 -2
  83. metadata +23 -2
data/Gemfile CHANGED
@@ -6,7 +6,7 @@ source "https://rubygems.org"
6
6
  gemspec
7
7
 
8
8
  gem "canon", "~> 0.1.3"
9
- gem "openssl"
9
+ gem "openssl", "~> 3.0"
10
10
  gem "rake"
11
11
  gem "rspec"
12
12
  gem "rubocop"
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 severity levels
72
- * Collection management (pack/unpack TTC/OTC files with table deduplication)
73
- * Support for TTF, OTF, TTC, OTC font formats (production ready)
74
- * Apple legacy font support: 'true' signature TrueType fonts and dfont (Data Fork Font) format
75
- * WOFF format support (reading complete, writing functional, pending full integration)
76
- * WOFF2 format support (reading complete with table transformations, writing planned)
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
- * TTX/YAML/JSON export (complete)
79
- * Command-line interface with 18 commands
80
- * Ruby library API for programmatic access
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 several predefined validation profiles for common use cases:
1049
+ Fontisan includes validation profiles for different use cases:
1038
1050
 
1039
- `indexability`:: Fast validation for font discovery and indexing (< 50ms). Uses
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`:: Basic usability for font installation. Uses FontBookValidator with
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 for production fonts (default
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 and optimization validation. Uses WebFontValidator
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 with detailed checks.
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: fonts.values.reject { |config| config[:skip_download] }.map { |config| config[:marker] }
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
- else
121
+ elsif File.exist?(config[:target_dir])
116
122
  # For archives, delete the entire target directory
117
- if File.exist?(config[:target_dir])
118
- puts "[fixtures:clean] Removing #{config[:target_dir]}..."
119
- FileUtils.rm_rf(config[:target_dir])
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")
@@ -18,7 +18,7 @@ puts "Font: #{FONT_PATH}"
18
18
  puts
19
19
 
20
20
  # Test coordinates
21
- coords = Array.new(4) { |i| { "wght" => 300 + i * 200 } }
21
+ coords = Array.new(4) { |i| { "wght" => 300 + (i * 200) } }
22
22
  puts "Generating #{coords.size} instances"
23
23
  puts
24
24
 
@@ -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
+ ----