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
@@ -0,0 +1,141 @@
1
+ # Fontisan Documentation Summary
2
+
3
+ This document provides an overview of all Fontisan documentation files and their purpose.
4
+
5
+ ## Main Documentation
6
+
7
+ ### README.adoc
8
+ Primary user guide and reference documentation for Fontisan.
9
+
10
+ ## Feature Guides
11
+
12
+ ### Font Hinting
13
+ - **File**: `docs/FONT_HINTING.adoc`
14
+ - **Purpose**: Documentation for font hinting features in Fontisan
15
+ - **Topics**: TrueType and PostScript hinting, hint extraction and application
16
+
17
+ ### Variable Font Operations
18
+ - **File**: `docs/VARIABLE_FONT_OPERATIONS.adoc`
19
+ - **Purpose**: Guide for working with variable fonts
20
+ - **Topics**: Instance generation, axis manipulation, variation preservation
21
+
22
+ ### TTC Migration Guide
23
+ - **File**: `docs/EXTRACT_TTC_MIGRATION.md`
24
+ - **Purpose**: Migration guide for users of the `extract-ttc` gem
25
+ - **Topics**: Moving from extract-ttc to Fontisan pack/unpack commands
26
+
27
+ ### Web Font Formats
28
+ - **File**: `docs/WOFF_WOFF2_FORMATS.adoc`
29
+ - **Purpose**: Documentation for WOFF and WOFF2 web font formats
30
+ - **Topics**: Conversion to/from WOFF/WOFF2, web optimization
31
+
32
+ ### Color Fonts
33
+ - **File**: `docs/COLOR_FONTS.adoc`
34
+ - **Purpose**: Guide for color font formats
35
+ - **Topics**: COLR/CPAL, SVG-in-OpenType, sbix color fonts
36
+
37
+ ### Validation
38
+ - **File**: `docs/VALIDATION.adoc`
39
+ - **Purpose**: Font validation framework documentation
40
+ - **Topics**: Validation profiles, quality checks, OpenType spec compliance
41
+
42
+ ### Apple Legacy Fonts
43
+ - **File**: `docs/APPLE_LEGACY_FONTS.adoc`
44
+ - **Purpose**: Documentation for Apple legacy font formats
45
+ - **Topics**: dfont format, Mac suitcase fonts
46
+
47
+ ### Collection Validation
48
+ - **File**: `docs/COLLECTION_VALIDATION.adoc`
49
+ - **Purpose**: Guide for validating font collections (TTC/OTC/dfont)
50
+ - **Topics**: Collection-specific validation, profile selection
51
+
52
+ ## CLI Commands Reference
53
+
54
+ Fontisan provides the following CLI commands:
55
+
56
+ | Command | Purpose |
57
+ |---------|---------|
58
+ | `info` | Display font information |
59
+ | `ls` | List contents (fonts in collection or font summary) |
60
+ | `tables` | List OpenType tables |
61
+ | `glyphs` | List glyph names |
62
+ | `unicode` | List Unicode to glyph mappings |
63
+ | `variable` | Display variable font information |
64
+ | `optical-size` | Display optical size information |
65
+ | `scripts` | List supported scripts from GSUB/GPOS tables |
66
+ | `features` | List GSUB/GPOS features |
67
+ | `subset` | Subset a font to specific glyphs |
68
+ | `convert` | Convert font to different format |
69
+ | `instance` | Generate static font instance from variable font |
70
+ | `pack` | Pack multiple fonts into TTC/OTC collection |
71
+ | `unpack` | Unpack fonts from TTC/OTC collection |
72
+ | `validate` | Validate font file |
73
+ | `export` | Export font to TTX/YAML/JSON format |
74
+ | `dump-table` | Dump raw table data to stdout |
75
+ | `version` | Display version information |
76
+
77
+ ## Ruby API Reference
78
+
79
+ ### Fontisan Module Methods
80
+
81
+ #### `Fontisan.info(path, brief: false, font_index: 0)`
82
+ Get font information. Supports both full and brief modes.
83
+
84
+ - **Parameters**:
85
+ - `path` (String): Path to font file
86
+ - `brief` (Boolean): Use brief mode for fast identification (default: false)
87
+ - `font_index` (Integer): Index for TTC/OTC files (default: 0)
88
+ - **Returns**: `Models::FontInfo`, `Models::CollectionInfo`, or `Models::CollectionBriefInfo`
89
+ - **Example**:
90
+ ```ruby
91
+ info = Fontisan.info("font.ttf", brief: true)
92
+ puts info.family_name
93
+ ```
94
+
95
+ #### `Fontisan.validate(path, profile: :default, options: {})`
96
+ Validate a font file using specified profile.
97
+
98
+ - **Parameters**:
99
+ - `path` (String): Path to font file
100
+ - `profile` (Symbol/String): Validation profile (default: :default)
101
+ - `options` (Hash): Additional validation options
102
+ - **Available Profiles**:
103
+ - `:indexability` - Fast validation for font discovery
104
+ - `:usability` - Basic usability for installation
105
+ - `:production` - Comprehensive quality checks (default)
106
+ - `:web` - Web embedding and optimization
107
+ - `:spec_compliance` - Full OpenType spec compliance
108
+ - `:default` - Alias for production profile
109
+ - **Returns**: `Models::ValidationReport`
110
+ - **Example**:
111
+ ```ruby
112
+ report = Fontisan.validate("font.ttf", profile: :web)
113
+ puts "Errors: #{report.summary.errors}"
114
+ ```
115
+
116
+ ### Fontisan::FontLoader
117
+
118
+ Module for loading fonts in different modes.
119
+
120
+ - **Methods**:
121
+ - `load(path, mode: :full)` - Load a font file
122
+ - **Loading Modes**:
123
+ - `:full` - Load all tables
124
+ - `:metadata` - Load only metadata tables (name, head, hhea, maxp, OS/2, post)
125
+ - `:tables` - Load specific tables only
126
+ - `:structure` - Load structure tables only
127
+
128
+ ## Verification
129
+
130
+ Documentation examples are verified by `spec/documentation_examples_spec.rb`.
131
+
132
+ This spec ensures that:
133
+ 1. All CLI commands referenced in documentation exist
134
+ 2. All Ruby API methods are available
135
+ 3. All documentation files are present
136
+ 4. Command examples reference valid commands
137
+
138
+ Run verification with:
139
+ ```bash
140
+ bundle exec rspec spec/documentation_examples_spec.rb -v
141
+ ```
@@ -541,9 +541,12 @@ The hint system has comprehensive test coverage:
541
541
  * **Total Tests**: 306 (100% passing)
542
542
  * **Generator Tests**: 45
543
543
  * **Validator Tests**: 48
544
+ * **Applier Tests**: 87 (PostScript: 63, TrueType: 24)
545
+ * **Extractor Tests**: 23 (PostScript: 11, TrueType: 12)
546
+ * **Converter Tests**: 29
544
547
  * **Round-Trip Tests**: 16
548
+ * **Integration Tests**: 28 (Conversion: 11, Application: 17)
545
549
  * **Analyzer Tests**: 30
546
- * **Integration Tests**: 167
547
550
 
548
551
  Test files:
549
552
 
@@ -552,7 +555,12 @@ Test files:
552
555
  * link:../spec/fontisan/hints/hint_validator_spec.rb[hint_validator_spec.rb]
553
556
  * link:../spec/fontisan/hints/hint_round_trip_spec.rb[hint_round_trip_spec.rb]
554
557
  * link:../spec/fontisan/hints/hint_converter_spec.rb[hint_converter_spec.rb]
558
+ * link:../spec/fontisan/hints/postscript_hint_applier_spec.rb[postscript_hint_applier_spec.rb]
559
+ * link:../spec/fontisan/hints/postscript_hint_extractor_spec.rb[postscript_hint_extractor_spec.rb]
560
+ * link:../spec/fontisan/hints/truetype_hint_applier_spec.rb[truetype_hint_applier_spec.rb]
561
+ * link:../spec/fontisan/hints/truetype_hint_extractor_spec.rb[truetype_hint_extractor_spec.rb]
555
562
  * link:../spec/fontisan/hints/hint_conversion_integration_spec.rb[hint_conversion_integration_spec.rb]
563
+ * link:../spec/fontisan/hints/hint_application_integration_spec.rb[hint_application_integration_spec.rb]
556
564
 
557
565
  == References
558
566
 
@@ -0,0 +1,254 @@
1
+ = Font Validation Framework
2
+
3
+ == General
4
+
5
+ Fontisan provides a comprehensive validation framework for ensuring font quality,
6
+ structural integrity, and compliance with OpenType specifications.
7
+
8
+ The framework uses a declarative DSL for defining validation checks and supports
9
+ multiple validation profiles for different use cases.
10
+
11
+ == Validation profiles
12
+
13
+ Fontisan includes predefined validation profiles:
14
+
15
+ `indexability`:: Fast font discovery and indexing (8 checks, metadata-only, ~5x faster)
16
+
17
+ `usability`:: Font installation compatibility (26 checks, macOS Font Book focused)
18
+
19
+ `production`:: Comprehensive production quality (37 checks, OpenType spec compliance - default)
20
+
21
+ `web`:: Web font embedding readiness (18 checks for web deployment)
22
+
23
+ `spec_compliance`:: Full OpenType specification compliance (detailed analysis mode)
24
+
25
+ [source,shell]
26
+ ----
27
+ $ fontisan validate --list
28
+ Available validation profiles:
29
+ indexability - Fast validation for font discovery and indexing
30
+ usability - Basic usability for installation
31
+ production - Comprehensive quality checks
32
+ web - Web embedding and optimization
33
+ spec_compliance - Full OpenType spec compliance
34
+ default - Default validation profile (alias for production)
35
+ ----
36
+
37
+ == Command-line validation
38
+
39
+ .Validate with default profile
40
+ [example]
41
+ ====
42
+ [source,shell]
43
+ ----
44
+ $ fontisan validate font.ttf
45
+
46
+ Font: font.ttf
47
+ Status: VALID
48
+
49
+ Summary:
50
+ Checks performed: 37
51
+ Passed: 37
52
+ Failed: 0
53
+
54
+ Errors: 0
55
+ Warnings: 0
56
+ Info: 0
57
+ ----
58
+ ====
59
+
60
+ .Validate for web use with summary
61
+ [example]
62
+ ====
63
+ [source,shell]
64
+ ----
65
+ $ fontisan validate font.ttf -t web -S
66
+
67
+ Font: font.ttf
68
+ Status: VALID
69
+
70
+ Summary:
71
+ Checks performed: 18
72
+ Passed: 17
73
+ Failed: 1
74
+
75
+ Errors: 1
76
+ Warnings: 0
77
+
78
+ Failed checks:
79
+ web_font_tables - Missing required GSUB table
80
+ ----
81
+ ====
82
+
83
+ .Validation with table format output
84
+ [example]
85
+ ====
86
+ [source,shell]
87
+ ----
88
+ $ fontisan validate font.ttf -T
89
+
90
+ CHECK_ID | STATUS | SEVERITY | TABLE
91
+ ------------------------------------------------------------
92
+ required_tables | PASS | error | N/A
93
+ name_version | PASS | error | name
94
+ family_name | PASS | error | name
95
+ ----
96
+ ====
97
+ ====
98
+
99
+ == Ruby API usage
100
+
101
+ === Using predefined profiles
102
+
103
+ [source,ruby]
104
+ ----
105
+ require 'fontisan'
106
+
107
+ # Validate with default profile (production)
108
+ report = Fontisan.validate('font.ttf')
109
+ puts report.valid? # => true or false
110
+
111
+ # Validate with specific profile
112
+ report = Fontisan.validate('font.ttf', profile: :web)
113
+ puts "Errors: #{report.summary.errors}"
114
+ puts "Warnings: #{report.summary.warnings}"
115
+
116
+ # Check validation status
117
+ if report.valid?
118
+ puts "Font is valid for web use!"
119
+ else
120
+ puts "Font has #{report.summary.errors} errors"
121
+ end
122
+ ----
123
+
124
+ === Query validation results
125
+
126
+ [source,ruby]
127
+ ----
128
+ report = Fontisan.validate('font.ttf', profile: :production)
129
+
130
+ # Get issues by severity
131
+ fatal_issues = report.fatal_errors
132
+ error_issues = report.errors_only
133
+ warning_issues = report.warnings_only
134
+ info_issues = report.info_only
135
+
136
+ # Get issues by category
137
+ table_issues = report.issues_by_category('table_validation')
138
+
139
+ # Get check results
140
+ failed_ids = report.failed_check_ids
141
+ pass_rate = report.pass_rate
142
+
143
+ # Export to different formats
144
+ yaml_output = report.to_yaml
145
+ json_output = report.to_json
146
+ summary = report.to_summary # "2 errors, 3 warnings, 0 info"
147
+ ----
148
+
149
+ == Creating custom validators
150
+
151
+ === General
152
+
153
+ Custom validators inherit from `Fontisan::Validators::Validator` and define
154
+ validation logic using the DSL.
155
+
156
+ The DSL provides 6 check methods:
157
+
158
+ * `check_table` - Validate table-level properties
159
+ * `check_field` - Validate specific field values
160
+ * `check_structure` - Validate font structure and relationships
161
+ * `check_usability` - Validate usability and best practices
162
+ * `check_instructions` - Validate TrueType instructions/hinting
163
+ * `check_glyphs` - Validate individual glyphs
164
+
165
+ .Creating a custom validator
166
+ [example]
167
+ ====
168
+ [source,ruby]
169
+ ----
170
+ require 'fontisan/validators/validator'
171
+
172
+ class MyFontValidator < Fontisan::Validators::Validator
173
+ private
174
+
175
+ def define_checks
176
+ # Check name table
177
+ check_table :name_validation, 'name', severity: :error do |table|
178
+ table.valid_version? &&
179
+ table.valid_encoding_heuristics? &&
180
+ table.family_name_present? &&
181
+ table.postscript_name_valid?
182
+ end
183
+
184
+ # Check head table
185
+ check_table :head_validation, 'head', severity: :error do |table|
186
+ table.valid_magic? &&
187
+ table.valid_version? &&
188
+ table.valid_units_per_em?
189
+ end
190
+
191
+ # Check structure
192
+ check_structure :required_tables, severity: :error do |font|
193
+ %w[name head maxp hhea].all? { |tag| !font.table(tag).nil? }
194
+ end
195
+ end
196
+ end
197
+
198
+ # Use the validator
199
+ font = Fontisan::FontLoader.load('font.ttf')
200
+ validator = MyFontValidator.new
201
+ report = validator.validate(font)
202
+
203
+ puts report.valid? # => true/false
204
+ puts report.summary.errors # => number of errors
205
+ puts report.summary.warnings # => number of warnings
206
+ ----
207
+ ====
208
+
209
+ == Validation helpers
210
+
211
+ The validation framework provides 56 helper methods across 8 core OpenType
212
+ tables. Each helper returns a boolean indicating whether the validation passed.
213
+
214
+ .Name table helpers:
215
+ [example]
216
+ ====
217
+ [source,ruby]
218
+ ----
219
+ class MyValidator < Fontisan::Validators::Validator
220
+ private
221
+
222
+ def define_checks
223
+ check_table :name_validation, 'name', severity: :error do |table|
224
+ table.valid_version? &&
225
+ table.valid_encoding_heuristics? &&
226
+ table.family_name_present? &&
227
+ table.postscript_name_valid?
228
+ end
229
+ end
230
+ end
231
+ ----
232
+ ====
233
+
234
+ .Head table helpers:
235
+ [example]
236
+ ====
237
+ [source,ruby]
238
+ ----
239
+ class MyValidator < Fontisan::Validators::Validator
240
+ private
241
+
242
+ def define_checks
243
+ check_table :head_validation, 'head', severity: :error do |table|
244
+ table.valid_magic? &&
245
+ table.valid_version? &&
246
+ table.valid_units_per_em? &&
247
+ table.valid_bounding_box? &&
248
+ table.valid_index_to_loc_format? &&
249
+ table.valid_glyph_data_format?
250
+ end
251
+ end
252
+ end
253
+ ----
254
+ ====
@@ -0,0 +1,94 @@
1
+ = WOFF and WOFF2 Font Format Support
2
+
3
+ == General
4
+
5
+ Fontisan provides comprehensive support for Web Open Font Format (WOFF) and WOFF2,
6
+ including reading, writing, and conversion between these and other font formats.
7
+
8
+ WOFF is a wrapper around SFNT fonts (TTF/OTF) using zlib compression, while WOFF2
9
+ uses Brotli compression for better compression ratios and table transformations.
10
+
11
+ === Display WOFF/WOFF2 font information
12
+
13
+ Show metadata specific to WOFF/WOFF2 fonts including version, compression details,
14
+ and original font flavor.
15
+
16
+ [source,shell]
17
+ ----
18
+ $ fontisan info FONT.woff [--format FORMAT]
19
+ ----
20
+
21
+ Where,
22
+
23
+ `FONT.woff`:: Path to the WOFF or WOFF2 file
24
+ `FORMAT`:: Output format: `text` (default), `json`, or `yaml`
25
+
26
+ .Major version from WOFF/WOFF2 header
27
+ [example]
28
+ ====
29
+ [source,shell]
30
+ ----
31
+ $ fontisan info /path/to/your/font.woff
32
+
33
+ Font type: WOFF (TrueType)
34
+ Family: Example Font
35
+ WOFF Major Version: 1
36
+ WOFF Minor Version: 0
37
+ WOFF Metadata: 0 entries
38
+ WOFF Private Data: 0 bytes
39
+ Original Font Flavor: TrueType
40
+ ----
41
+ ====
42
+
43
+ === WOFF2 validation
44
+
45
+ Fontisan validates WOFF2 fonts according to the specification, checking:
46
+
47
+ * Required table presence and order
48
+ * Table transformation flags (glyf, loca transformations)
49
+ * Checksum validation for transformed tables
50
+ * Compression format validation
51
+
52
+ .Validation example
53
+ [example]
54
+ ====
55
+ [source,shell]
56
+ ----
57
+ $ fontisan validate font.woff2 -t web
58
+
59
+ CHECK_ID | STATUS | SEVERITY
60
+ --------------------------------------------------------------
61
+ woff2_required_tables | PASS | error
62
+ woff2_table_transformations | PASS | error
63
+ woff2_checksum_validation | PASS | error
64
+ ----
65
+ ====
66
+
67
+ === Convert fonts to WOFF/WOFF2
68
+
69
+ Convert TTF/OTF fonts to web-optimized formats.
70
+
71
+ [source,shell]
72
+ ----
73
+ $ fontisan convert INPUT.ttf --to woff --output output.woff
74
+ $ fontisan convert INPUT.ttf --to woff2 --output output.woff2
75
+ ----
76
+
77
+ .Conversion with optimization
78
+ [example]
79
+ ====
80
+ [source,shell]
81
+ ----
82
+ $ fontisan convert font.ttf --to woff2 --output font.woff2
83
+
84
+ Converting font.ttf to WOFF2...
85
+ Input: 245.8 KB (TTF)
86
+ Output: 89.2 KB (WOFF2)
87
+ Ratio: 63.7% reduction
88
+
89
+ Compression details:
90
+ Tables: 12
91
+ Brotli compression: Applied
92
+ Transformed tables: glyf, loca
93
+ ----
94
+ ====
data/lib/fontisan/cli.rb CHANGED
@@ -367,18 +367,27 @@ module Fontisan
367
367
  handle_error(e)
368
368
  end
369
369
 
370
- desc "validate FONT_FILE", "Validate font file"
370
+ desc "validate [FONT_FILE]", "Validate font file"
371
371
  long_desc <<-DESC
372
372
  Validate font file against quality checks and standards.
373
373
 
374
- Test lists (-t/--test-list):
375
- indexability - Fast indexing validation
374
+ Supports individual fonts (TTF/OTF) and font collections (TTC/OTC/dfont).
375
+ For collections, validates each font and provides a summary report.
376
+
377
+ Profiles (-p/--profile):
378
+ indexability - Fast indexing validation (metadata only, 8 checks)
376
379
  usability - Installation compatibility
377
380
  production - Comprehensive quality (default)
378
381
  web - Web font readiness
379
382
  spec_compliance - OpenType spec compliance
380
383
  default - Production profile (alias)
381
384
 
385
+ Collection validation with profiles:
386
+ When validating TTC/OTC/dfont collections, the selected profile
387
+ determines which tables are loaded and which checks are performed
388
+ for each font in the collection. Use 'indexability' for quick
389
+ validation or 'production' for comprehensive quality checks.
390
+
382
391
  Return values (with -R/--return-value-results):
383
392
  0 No results
384
393
  1 Execution errors
@@ -389,25 +398,41 @@ module Fontisan
389
398
  DESC
390
399
 
391
400
  option :exclude, aliases: "-e", type: :array, desc: "Tests to exclude"
392
- option :list, aliases: "-l", type: :boolean, desc: "List available tests"
401
+ option :list, aliases: "-l", type: :boolean, desc: "List available profiles"
393
402
  option :output, aliases: "-o", type: :string, desc: "Output file"
394
403
  option :full_report, aliases: "-r", type: :boolean, desc: "Full report"
395
- option :return_value_results, aliases: "-R", type: :boolean, desc: "Use return value for results"
396
- option :summary_report, aliases: "-S", type: :boolean, desc: "Summary report"
397
- option :test_list, aliases: "-t", type: :string, default: "default", desc: "Tests to execute"
404
+ option :return_value_results, aliases: "-R", type: :boolean,
405
+ desc: "Use return value for results"
406
+ option :summary_report, aliases: "-S", type: :boolean,
407
+ desc: "Summary report"
408
+ option :profile, aliases: "-p", type: :string, default: "default",
409
+ desc: "Validation profile"
398
410
  option :table_report, aliases: "-T", type: :boolean, desc: "Tabular report"
399
411
  option :verbose, aliases: "-v", type: :boolean, desc: "Verbose output"
400
- option :suppress_warnings, aliases: "-W", type: :boolean, desc: "Suppress warnings"
412
+ option :suppress_warnings, aliases: "-W", type: :boolean,
413
+ desc: "Suppress warnings"
401
414
 
402
- def validate(font_file)
415
+ def validate(*font_files)
403
416
  if options[:list]
404
417
  list_available_tests
405
418
  return
406
419
  end
407
420
 
421
+ # Validate argument count
422
+ if font_files.empty?
423
+ raise(Thor::Error, "FONT_FILE is required unless using --list")
424
+ elsif font_files.size > 1
425
+ raise(Thor::Error,
426
+ "Too many arguments. validate accepts only one font file.\n" \
427
+ "To validate multiple files, run validate separately for each.\n" \
428
+ "To validate all fonts in a collection, use: fontisan validate collection.ttc")
429
+ end
430
+
431
+ font_file = font_files.first
432
+
408
433
  cmd = Commands::ValidateCommand.new(
409
434
  input: font_file,
410
- profile: options[:test_list],
435
+ profile: options[:profile],
411
436
  exclude: options[:exclude] || [],
412
437
  output: options[:output],
413
438
  format: options[:format].to_sym,
@@ -416,11 +441,18 @@ module Fontisan
416
441
  table_report: options[:table_report],
417
442
  verbose: options[:verbose],
418
443
  suppress_warnings: options[:suppress_warnings],
419
- return_value_results: options[:return_value_results]
444
+ return_value_results: options[:return_value_results],
420
445
  )
421
446
 
422
447
  exit cmd.run
423
- rescue => e
448
+ rescue Thor::Error => e
449
+ unless options[:quiet]
450
+ warn "ERROR: #{e.message}"
451
+ warn
452
+ help("validate")
453
+ end
454
+ exit 1
455
+ rescue StandardError => e
424
456
  error "Validation failed: #{e.message}"
425
457
  exit 1
426
458
  end
@@ -546,7 +578,7 @@ module Fontisan
546
578
  puts " Format: #{result[:format].upcase}"
547
579
  puts " Fonts: #{result[:num_fonts]}"
548
580
  puts " Size: #{format_size(result[:output_size] || result[:total_size])}"
549
- if result[:space_savings] && result[:space_savings].positive?
581
+ if result[:space_savings]&.positive?
550
582
  puts " Space saved: #{format_size(result[:space_savings])}"
551
583
  puts " Sharing: #{result[:statistics][:sharing_percentage]}%"
552
584
  end
@@ -86,7 +86,8 @@ module Fontisan
86
86
  map_size = 28 + 2 + 8 + (sfnt_binaries.size * 12)
87
87
 
88
88
  # Step 4: Build resource map
89
- resource_map = build_resource_map(sfnt_binaries, resource_data.bytesize, map_size)
89
+ resource_map = build_resource_map(sfnt_binaries,
90
+ resource_data.bytesize, map_size)
90
91
 
91
92
  # Step 5: Build header
92
93
  header = build_header(resource_data.bytesize, resource_map.bytesize)
@@ -73,7 +73,7 @@ module Fontisan
73
73
  @instance_index = opts[:instance_index]
74
74
  @preserve_variation = opts[:preserve_variation]
75
75
  @preserve_hints = opts.fetch(:preserve_hints, false)
76
- @collection_target_format = opts.fetch(:target_format, 'preserve').to_s
76
+ @collection_target_format = opts.fetch(:target_format, "preserve").to_s
77
77
  @validate = !opts[:no_validate]
78
78
  end
79
79
 
@@ -196,7 +196,7 @@ module Fontisan
196
196
  output: @output_path,
197
197
  target_format: @collection_target_format,
198
198
  verbose: @options[:verbose],
199
- }
199
+ },
200
200
  )
201
201
 
202
202
  # Display results
@@ -245,8 +245,6 @@ module Fontisan
245
245
  :otc
246
246
  when ".dfont"
247
247
  :dfont
248
- else
249
- nil
250
248
  end
251
249
  end
252
250
  end
@@ -311,7 +311,7 @@ module Fontisan
311
311
  start_glyph_id: strike_rec.start_glyph_index,
312
312
  end_glyph_id: strike_rec.end_glyph_index,
313
313
  bit_depth: strike_rec.bit_depth,
314
- num_glyphs: strike_rec.glyph_range.size
314
+ num_glyphs: strike_rec.glyph_range.size,
315
315
  )
316
316
  end
317
317
  formats << "PNG" # CBDT typically contains PNG data
@@ -329,8 +329,8 @@ module Fontisan
329
329
  ppem: strike[:ppem],
330
330
  start_glyph_id: 0,
331
331
  end_glyph_id: strike[:num_glyphs] - 1,
332
- bit_depth: 32, # sbix is typically 32-bit
333
- num_glyphs: strike[:num_glyphs]
332
+ bit_depth: 32, # sbix is typically 32-bit
333
+ num_glyphs: strike[:num_glyphs],
334
334
  )
335
335
  end
336
336
  end
@@ -260,7 +260,8 @@ module Fontisan
260
260
  # @return [Symbol] Parsed format (:ttc, :otc, or :dfont)
261
261
  # @raise [ArgumentError] if format is invalid
262
262
  def parse_format(format)
263
- return format if format.is_a?(Symbol) && %i[ttc otc dfont].include?(format)
263
+ return format if format.is_a?(Symbol) && %i[ttc otc
264
+ dfont].include?(format)
264
265
 
265
266
  case format.to_s.downcase
266
267
  when "ttc"