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
|
@@ -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
|
+
```
|
data/docs/FONT_HINTING.adoc
CHANGED
|
@@ -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
|
-
|
|
375
|
-
|
|
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
|
|
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,
|
|
396
|
-
|
|
397
|
-
option :
|
|
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,
|
|
412
|
+
option :suppress_warnings, aliases: "-W", type: :boolean,
|
|
413
|
+
desc: "Suppress warnings"
|
|
401
414
|
|
|
402
|
-
def validate(
|
|
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[:
|
|
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]
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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"
|