fontisan 0.2.13 → 0.2.16
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_todo.yml +56 -196
- data/Gemfile +1 -1
- data/docs/.gitignore +17 -0
- data/docs/.vitepress/config.ts +317 -0
- data/docs/.vitepress/theme/components/ApiMethod.vue +127 -0
- data/docs/.vitepress/theme/components/Badge.vue +51 -0
- data/docs/.vitepress/theme/components/FeatureComparison.vue +87 -0
- data/docs/.vitepress/theme/components/HeroCodeBlock.vue +98 -0
- data/docs/.vitepress/theme/components/WithinHero.vue +30 -0
- data/docs/.vitepress/theme/index.ts +22 -0
- data/docs/.vitepress/theme/style.css +330 -0
- data/docs/api/conversion-options.md +141 -0
- data/docs/api/converters/curve-converter.md +34 -0
- data/docs/api/converters/hint-converter.md +34 -0
- data/docs/api/converters/outline-converter.md +27 -0
- data/docs/api/font-loader.md +111 -0
- data/docs/api/font-writer.md +103 -0
- data/docs/api/index.md +79 -0
- data/docs/api/models/glyph-accessor.md +43 -0
- data/docs/api/models/glyph.md +40 -0
- data/docs/api/models/table-analyzer.md +35 -0
- data/docs/api/sfnt-font.md +53 -0
- data/docs/api/type1-font.md +43 -0
- data/docs/api/validators/font-validator.md +31 -0
- data/docs/api/validators/helper.md +36 -0
- data/docs/api/validators/profile.md +39 -0
- data/docs/cli/convert.md +87 -0
- data/docs/cli/dump-table.md +110 -0
- data/docs/cli/export.md +176 -0
- data/docs/cli/features.md +124 -0
- data/docs/cli/glyphs.md +90 -0
- data/docs/cli/index.md +208 -0
- data/docs/cli/info.md +254 -0
- data/docs/cli/instance.md +122 -0
- data/docs/cli/ls.md +95 -0
- data/docs/cli/optical-size.md +94 -0
- data/docs/cli/pack.md +125 -0
- data/docs/cli/scripts.md +105 -0
- data/docs/cli/subset.md +39 -0
- data/docs/cli/tables.md +84 -0
- data/docs/cli/unicode.md +101 -0
- data/docs/cli/validate.md +48 -0
- data/docs/cli/variable.md +126 -0
- data/docs/cli/version.md +46 -0
- data/docs/guide/cli/convert.md +108 -0
- data/docs/guide/cli/export.md +138 -0
- data/docs/guide/cli/index.md +99 -0
- data/docs/guide/cli/info.md +144 -0
- data/docs/guide/cli/pack.md +155 -0
- data/docs/guide/cli/subset.md +118 -0
- data/docs/guide/cli/validate.md +139 -0
- data/docs/guide/color-fonts/bitmaps.md +177 -0
- data/docs/guide/color-fonts/colr-cpal.md +175 -0
- data/docs/guide/color-fonts/index.md +140 -0
- data/docs/guide/color-fonts/svg.md +154 -0
- data/docs/guide/color.md +51 -0
- data/docs/guide/comparisons/font-validator.md +222 -0
- data/docs/guide/comparisons/fonttools.md +200 -0
- data/docs/guide/comparisons/index.md +83 -0
- data/docs/guide/comparisons/lcdf-typetools.md +205 -0
- data/docs/guide/contributing.md +279 -0
- data/docs/guide/conversion/collections.md +251 -0
- data/docs/guide/conversion/curves.md +246 -0
- data/docs/guide/conversion/index.md +157 -0
- data/docs/guide/conversion/options.md +251 -0
- data/docs/guide/conversion/ttf-otf.md +184 -0
- data/docs/guide/conversion/type1.md +208 -0
- data/docs/guide/conversion/web.md +240 -0
- data/docs/guide/conversion.md +39 -0
- data/docs/guide/formats/collections.md +147 -0
- data/docs/guide/formats/dfont.md +99 -0
- data/docs/guide/formats/index.md +65 -0
- data/docs/guide/formats/otf.md +103 -0
- data/docs/guide/formats/svg.md +97 -0
- data/docs/guide/formats/ttf.md +105 -0
- data/docs/guide/formats/type1.md +118 -0
- data/docs/guide/formats/woff.md +115 -0
- data/docs/guide/hinting/autohint.md +141 -0
- data/docs/guide/hinting/conversion.md +161 -0
- data/docs/guide/hinting/index.md +86 -0
- data/docs/guide/hinting/postscript.md +149 -0
- data/docs/guide/hinting/truetype.md +135 -0
- data/docs/guide/hinting.md +44 -0
- data/docs/guide/index.md +152 -0
- data/docs/guide/installation.md +116 -0
- data/docs/guide/migrations/extract-ttc.md +549 -0
- data/docs/guide/migrations/font-validator.md +260 -0
- data/docs/guide/migrations/fonttools.md +208 -0
- data/docs/guide/migrations/index.md +64 -0
- data/docs/guide/migrations/otfinfo.md +197 -0
- data/docs/guide/quick-start.md +204 -0
- data/docs/guide/type1.md +58 -0
- data/docs/guide/universal-outline.md +151 -0
- data/docs/guide/validation/custom.md +195 -0
- data/docs/guide/validation/helpers.md +188 -0
- data/docs/guide/validation/index.md +132 -0
- data/docs/guide/validation/profiles.md +156 -0
- data/docs/guide/validation.md +47 -0
- data/docs/guide/variable-fonts/advanced.md +231 -0
- data/docs/guide/variable-fonts/axes.md +209 -0
- data/docs/guide/variable-fonts/conversion.md +197 -0
- data/docs/guide/variable-fonts/index.md +84 -0
- data/docs/guide/variable-fonts/instances.md +187 -0
- data/docs/guide/variable-fonts/named-instances.md +194 -0
- data/docs/guide/variable-fonts/static.md +168 -0
- data/docs/guide/variable.md +58 -0
- data/docs/guide/woff.md +59 -0
- data/docs/index.md +136 -0
- data/docs/lychee.toml +37 -0
- data/docs/package-lock.json +2560 -0
- data/docs/package.json +15 -0
- data/docs/public/apple-touch-icon.png +0 -0
- data/docs/public/favicon-96x96.png +0 -0
- data/docs/public/favicon.ico +0 -0
- data/docs/public/favicon.svg +1 -0
- data/docs/public/logo-full.svg +1 -0
- data/docs/public/logo.svg +1 -0
- data/docs/public/site.webmanifest +21 -0
- data/docs/public/web-app-manifest-192x192.png +0 -0
- data/docs/public/web-app-manifest-512x512.png +0 -0
- data/fontisan.gemspec +1 -1
- data/lib/fontisan/commands/features_command.rb +0 -1
- data/lib/fontisan/commands/info_command.rb +5 -5
- data/lib/fontisan/commands/scripts_command.rb +0 -1
- data/lib/fontisan/converters/format_converter.rb +2 -1
- data/lib/fontisan/converters/type1_converter.rb +65 -60
- data/lib/fontisan/hints/hint_converter.rb +2 -1
- data/lib/fontisan/loading_modes.rb +0 -2
- data/lib/fontisan/open_type_font.rb +0 -40
- data/lib/fontisan/sfnt_font.rb +41 -22
- data/lib/fontisan/tables/glyf/compound_glyph.rb +0 -1
- data/lib/fontisan/true_type_collection.rb +8 -8
- data/lib/fontisan/true_type_font.rb +1 -59
- data/lib/fontisan/type1/afm_parser.rb +2 -1
- data/lib/fontisan/type1/cff_to_type1_converter.rb +24 -19
- data/lib/fontisan/type1/private_dict.rb +28 -7
- data/lib/fontisan/type1/seac_expander.rb +22 -17
- data/lib/fontisan/variable/delta_applicator.rb +3 -3
- data/lib/fontisan/variation/optimizer.rb +0 -1
- data/lib/fontisan/version.rb +1 -1
- data/lib/fontisan/woff2_font.rb +2 -2
- data/lib/fontisan/woff_font.rb +3 -3
- data/lib/fontisan.rb +3 -2
- metadata +122 -4
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Quick Start
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Quick Start
|
|
6
|
+
|
|
7
|
+
This guide covers the most common Fontisan workflows.
|
|
8
|
+
|
|
9
|
+
## Loading Fonts
|
|
10
|
+
|
|
11
|
+
Fontisan automatically detects font formats:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
require 'fontisan'
|
|
15
|
+
|
|
16
|
+
# TrueType
|
|
17
|
+
font = Fontisan::FontLoader.load('font.ttf')
|
|
18
|
+
|
|
19
|
+
# OpenType
|
|
20
|
+
font = Fontisan::FontLoader.load('font.otf')
|
|
21
|
+
|
|
22
|
+
# WOFF/WOFF2
|
|
23
|
+
font = Fontisan::FontLoader.load('font.woff')
|
|
24
|
+
font = Fontisan::FontLoader.load('font.woff2')
|
|
25
|
+
|
|
26
|
+
# Type 1
|
|
27
|
+
font = Fontisan::FontLoader.load('font.pfb')
|
|
28
|
+
font = Fontisan::FontLoader.load('font.pfa')
|
|
29
|
+
|
|
30
|
+
# Collections
|
|
31
|
+
fonts = Fontisan::FontLoader.load('fonts.ttc')
|
|
32
|
+
fonts = Fontisan::FontLoader.load('fonts.otc')
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Font Information
|
|
36
|
+
|
|
37
|
+
### Basic Information
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
font = Fontisan::FontLoader.load('font.ttf')
|
|
41
|
+
|
|
42
|
+
# Access tables directly
|
|
43
|
+
name_table = font.tables['name']
|
|
44
|
+
head_table = font.tables['head']
|
|
45
|
+
|
|
46
|
+
# Get family name
|
|
47
|
+
family = name_table.family_name
|
|
48
|
+
|
|
49
|
+
# Get style
|
|
50
|
+
style = name_table.subfamily_name
|
|
51
|
+
|
|
52
|
+
# Get metrics
|
|
53
|
+
units_per_em = head_table.units_per_em
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Using CLI
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Get comprehensive info
|
|
60
|
+
fontisan info font.ttf
|
|
61
|
+
|
|
62
|
+
# Output in different formats
|
|
63
|
+
fontisan info font.ttf --format yaml
|
|
64
|
+
fontisan info font.ttf --format json
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Font Conversion
|
|
68
|
+
|
|
69
|
+
### Basic Conversion
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
# TTF to OTF
|
|
73
|
+
Fontisan.convert('input.ttf', output_format: :otf)
|
|
74
|
+
|
|
75
|
+
# OTF to WOFF2
|
|
76
|
+
Fontisan.convert('input.otf', output_format: :woff2)
|
|
77
|
+
|
|
78
|
+
# Type 1 to OTF
|
|
79
|
+
Fontisan.convert('input.pfb', output_format: :otf)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### With Options
|
|
83
|
+
|
|
84
|
+
```ruby
|
|
85
|
+
options = Fontisan::ConversionOptions.new(
|
|
86
|
+
from: :ttf,
|
|
87
|
+
to: :otf,
|
|
88
|
+
opening: { autohint: true, convert_curves: true },
|
|
89
|
+
generating: { hinting_mode: 'auto' }
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
Fontisan.convert('input.ttf', output_format: :otf, options: options)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Using CLI
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# Basic conversion
|
|
99
|
+
fontisan convert input.ttf --to otf --output output.otf
|
|
100
|
+
|
|
101
|
+
# Show recommended options
|
|
102
|
+
fontisan convert input.ttf --to otf --show-options
|
|
103
|
+
|
|
104
|
+
# Use a preset
|
|
105
|
+
fontisan convert font.pfb --to otf --preset type1_to_modern --output output.otf
|
|
106
|
+
|
|
107
|
+
# With autohint
|
|
108
|
+
fontisan convert input.ttf --to otf --autohint --hinting-mode auto --output output.otf
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Font Validation
|
|
112
|
+
|
|
113
|
+
### Using Ruby API
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
# Validate with default profile
|
|
117
|
+
result = Fontisan::FontValidator.validate('font.ttf')
|
|
118
|
+
|
|
119
|
+
# Validate with specific profile
|
|
120
|
+
result = Fontisan::FontValidator.validate('font.ttf', profile: :google_fonts)
|
|
121
|
+
|
|
122
|
+
# Check results
|
|
123
|
+
if result.passed?
|
|
124
|
+
puts "✓ Font is valid"
|
|
125
|
+
else
|
|
126
|
+
puts "✗ Validation failed:"
|
|
127
|
+
result.errors.each do |error|
|
|
128
|
+
puts " - #{error.code}: #{error.message}"
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Using CLI
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# Validate with Google Fonts profile
|
|
137
|
+
fontisan validate font.ttf --profile google_fonts
|
|
138
|
+
|
|
139
|
+
# Validate with Microsoft profile
|
|
140
|
+
fontisan validate font.ttf --profile microsoft
|
|
141
|
+
|
|
142
|
+
# Validate multiple files
|
|
143
|
+
fontisan validate fonts/*.ttf --profile production
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Working with Collections
|
|
147
|
+
|
|
148
|
+
### List Fonts in Collection
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
fontisan ls fonts.ttc
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Extract Fonts from Collection
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
# Extract all fonts
|
|
158
|
+
fontisan unpack fonts.ttc --output-dir ./extracted
|
|
159
|
+
|
|
160
|
+
# Extract with format conversion
|
|
161
|
+
fontisan unpack fonts.ttc --output-dir ./extracted --format otf
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Create Collection
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# Pack fonts into TTC
|
|
168
|
+
fontisan pack font1.ttf font2.ttf font3.ttf --output fonts.ttc
|
|
169
|
+
|
|
170
|
+
# Pack with deduplication
|
|
171
|
+
fontisan pack font1.ttf font2.ttf --output fonts.ttc --deduplicate
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Variable Fonts
|
|
175
|
+
|
|
176
|
+
### Get Axis Information
|
|
177
|
+
|
|
178
|
+
```ruby
|
|
179
|
+
font = Fontisan::FontLoader.load('variable-font.ttf')
|
|
180
|
+
fvar = font.tables['fvar']
|
|
181
|
+
|
|
182
|
+
fvar.axes.each do |axis|
|
|
183
|
+
puts "#{axis.tag}: #{axis.min_value} - #{axis.max_value}"
|
|
184
|
+
end
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Generate Instance
|
|
188
|
+
|
|
189
|
+
```ruby
|
|
190
|
+
# Generate static instance
|
|
191
|
+
instance = Fontisan::Variation::InstanceGenerator.new(font).generate(
|
|
192
|
+
'wght' => 700,
|
|
193
|
+
'ital' => 1
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
Fontisan::FontWriter.write(instance, 'bold-italic.ttf')
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Next Steps
|
|
200
|
+
|
|
201
|
+
- [Font Formats](/guide/formats/) — Detailed format documentation
|
|
202
|
+
- [Conversion Guide](/guide/conversion/) — Advanced conversion options
|
|
203
|
+
- [Validation](/guide/validation/) — Validation profiles and helpers
|
|
204
|
+
- [CLI Reference](/guide/cli/) — Complete CLI documentation
|
data/docs/guide/type1.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
outline: [2, 3]
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Type 1 Fonts
|
|
6
|
+
|
|
7
|
+
Fontisan provides comprehensive support for Adobe Type 1 fonts.
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
Adobe Type 1 fonts are a legacy format based on PostScript outlines. Fontisan can read, validate, and convert these fonts to modern formats.
|
|
12
|
+
|
|
13
|
+
## Reading Type 1 Fonts
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
require 'fontisan'
|
|
17
|
+
|
|
18
|
+
# Load a Type 1 font
|
|
19
|
+
font = Fontisan::Type1.load('font.pfb')
|
|
20
|
+
|
|
21
|
+
# Access font properties
|
|
22
|
+
puts font.family_name
|
|
23
|
+
puts font.full_name
|
|
24
|
+
puts font.num_glyphs
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Converting Type 1 Fonts
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
# Convert to TrueType
|
|
31
|
+
Fontisan.convert('font.pfb', output_format: :ttf)
|
|
32
|
+
|
|
33
|
+
# Convert to OpenType
|
|
34
|
+
Fontisan.convert('font.pfb', output_format: :otf)
|
|
35
|
+
|
|
36
|
+
# Convert to WOFF2
|
|
37
|
+
Fontisan.convert('font.pfb', output_format: :woff2)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Handling Encodings
|
|
41
|
+
|
|
42
|
+
Type 1 fonts may use various encodings:
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
font = Fontisan::Type1.load('font.pfb')
|
|
46
|
+
|
|
47
|
+
# Get the font encoding
|
|
48
|
+
puts font.encoding
|
|
49
|
+
|
|
50
|
+
# List available glyphs
|
|
51
|
+
font.glyphs.each do |glyph|
|
|
52
|
+
puts "#{glyph.name}: #{glyph.codepoint}"
|
|
53
|
+
end
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Related
|
|
57
|
+
|
|
58
|
+
- [Font Conversion](/guide/conversion) - General conversion guide
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Universal Outline Model
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Universal Outline Model
|
|
6
|
+
|
|
7
|
+
The Fontisan Universal Outline Model (UOM) provides a format-agnostic representation of glyph contours that enables seamless conversion between different font formats.
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
The UOM is based on a self-stable algorithm for converting soft glyph contours to outline format. This architecture allows:
|
|
12
|
+
|
|
13
|
+
- Importing glyphs from any font format (TrueType, OpenType, CFF)
|
|
14
|
+
- Converting glyph elements between formats
|
|
15
|
+
- Operating on outlines with transformations
|
|
16
|
+
- Serialization and storage
|
|
17
|
+
|
|
18
|
+
## Core Components
|
|
19
|
+
|
|
20
|
+
### Locker
|
|
21
|
+
|
|
22
|
+
The Locker is an object-oriented model for storing imported outlines and glyphs. Storage is based on monotonic spirals computed from 2D points and curves.
|
|
23
|
+
|
|
24
|
+
**Features:**
|
|
25
|
+
- Invisible conversion from TrueType, CFF OpenType, and ColorGlyph formats
|
|
26
|
+
- Preserves original glyph geometry
|
|
27
|
+
- Supports compound glyph decomposition
|
|
28
|
+
|
|
29
|
+
### Translator
|
|
30
|
+
|
|
31
|
+
The Translator is an object-oriented model for converting between PostScript CFF charsets.
|
|
32
|
+
|
|
33
|
+
**Features:**
|
|
34
|
+
- PostScript Type 2/3/composite encoding and decoding
|
|
35
|
+
- CFF INDEX structure building
|
|
36
|
+
- CFF DICT structure building
|
|
37
|
+
- Charset conversion
|
|
38
|
+
|
|
39
|
+
### ColorGlyph
|
|
40
|
+
|
|
41
|
+
ColorGlyph provides support for layered CFF color glyphs with on-demand rasterization.
|
|
42
|
+
|
|
43
|
+
**Features:**
|
|
44
|
+
- Composite font support
|
|
45
|
+
- Multi-layer color font representation
|
|
46
|
+
- CFF fonts stacked on top of each other
|
|
47
|
+
- Advanced color glyphs
|
|
48
|
+
- Raster image support (PNG/JPG) combined with TrueType outlines
|
|
49
|
+
|
|
50
|
+
## Universal Fonts
|
|
51
|
+
|
|
52
|
+
Fontisan provides universal font capabilities:
|
|
53
|
+
|
|
54
|
+
| Capability | Description |
|
|
55
|
+
|------------|-------------|
|
|
56
|
+
| Import | Import TrueType contours into UOM |
|
|
57
|
+
| Operate | Transform, clean, improve, and render outlines |
|
|
58
|
+
| Convert | Convert UOM contours to TTF/OTF |
|
|
59
|
+
| Serialize | Save and share font structures |
|
|
60
|
+
| Color Support | Work with advanced color fonts |
|
|
61
|
+
|
|
62
|
+
## Universal Glyphs
|
|
63
|
+
|
|
64
|
+
The universal glyph model enables:
|
|
65
|
+
|
|
66
|
+
- Universal Outline Model (UOM) for TrueType contours and CFF color glyphs
|
|
67
|
+
- Repository for custom fonts
|
|
68
|
+
- Custom Unicode assignments and configuration
|
|
69
|
+
- Outline import/export (TrueType and OTF/CFF)
|
|
70
|
+
- Rendering for advanced font types
|
|
71
|
+
- Universal layer stacking for color glyph combinations
|
|
72
|
+
|
|
73
|
+
## Universal Color Layers
|
|
74
|
+
|
|
75
|
+
Universal color layers support advanced color font operations:
|
|
76
|
+
|
|
77
|
+
| Feature | Description |
|
|
78
|
+
|---------|-------------|
|
|
79
|
+
| Import | Import embedded TTF/OTF color layers |
|
|
80
|
+
| Assemble | Assemble from individual TTF/OTF slices |
|
|
81
|
+
| Manage | Advanced layer map management in TTF color fonts |
|
|
82
|
+
| Blend | Advanced color layer blending style management |
|
|
83
|
+
| Convert | Gray/Overprint/Color-Full image comps and layer conversion |
|
|
84
|
+
| Raster | Smart vector combos from raster images |
|
|
85
|
+
| PNG | Import and generate PNG block ruler layers |
|
|
86
|
+
|
|
87
|
+
## Curve Conversion
|
|
88
|
+
|
|
89
|
+
The UOM handles bidirectional curve conversion:
|
|
90
|
+
|
|
91
|
+
- **TrueType → CFF**: Quadratic to cubic curve conversion
|
|
92
|
+
- **CFF → TrueType**: Cubic to quadratic curve conversion
|
|
93
|
+
- **CFF2 Support**: Variable font CFF2 outlines
|
|
94
|
+
|
|
95
|
+
## Ruby API
|
|
96
|
+
|
|
97
|
+
### Loading Outlines
|
|
98
|
+
|
|
99
|
+
```ruby
|
|
100
|
+
require 'fontisan'
|
|
101
|
+
|
|
102
|
+
# Load font and access universal outlines
|
|
103
|
+
font = Fontisan::FontLoader.load('font.ttf')
|
|
104
|
+
|
|
105
|
+
# Access glyphs through universal model
|
|
106
|
+
glyphs = font.glyphs
|
|
107
|
+
|
|
108
|
+
glyphs.each do |glyph|
|
|
109
|
+
# Access universal outline representation
|
|
110
|
+
outline = glyph.universal_outline
|
|
111
|
+
|
|
112
|
+
# Get contour count
|
|
113
|
+
puts "Glyph #{glyph.name}: #{outline.contour_count} contours"
|
|
114
|
+
|
|
115
|
+
# Access points and curves
|
|
116
|
+
outline.contours.each do |contour|
|
|
117
|
+
contour.commands.each do |cmd|
|
|
118
|
+
case cmd.type
|
|
119
|
+
when :move_to
|
|
120
|
+
puts " Move to #{cmd.x}, #{cmd.y}"
|
|
121
|
+
when :line_to
|
|
122
|
+
puts " Line to #{cmd.x}, #{cmd.y}"
|
|
123
|
+
when :quad_to
|
|
124
|
+
puts " Quadratic curve to #{cmd.x}, #{cmd.y}"
|
|
125
|
+
when :curve_to
|
|
126
|
+
puts " Cubic curve to #{cmd.x}, #{cmd.y}"
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Converting Outlines
|
|
134
|
+
|
|
135
|
+
```ruby
|
|
136
|
+
# Convert between curve types
|
|
137
|
+
converter = Fontisan::Converters::CurveConverter.new
|
|
138
|
+
|
|
139
|
+
# TrueType (quadratic) to CFF (cubic)
|
|
140
|
+
cff_outlines = converter.quad_to_cubic(truetype_outlines)
|
|
141
|
+
|
|
142
|
+
# CFF (cubic) to TrueType (quadratic)
|
|
143
|
+
ttf_outlines = converter.cubic_to_quad(cff_outlines)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Related Documentation
|
|
147
|
+
|
|
148
|
+
- [Curve Converter API](/api/converters/curve-converter) — Curve conversion details
|
|
149
|
+
- [Outline Converter API](/api/converters/outline-converter) — Full outline conversion
|
|
150
|
+
- [Font Conversion Guide](/guide/conversion/) — Conversion workflows
|
|
151
|
+
- [Color Fonts Guide](/guide/color-fonts/) — Color font support
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Custom Validators
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Custom Validators
|
|
6
|
+
|
|
7
|
+
Create custom validation rules using Fontisan's validation DSL.
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
Custom validators inherit from `Fontisan::Validators::Validator` and define validation logic using the DSL.
|
|
12
|
+
|
|
13
|
+
## DSL Methods
|
|
14
|
+
|
|
15
|
+
The DSL provides 6 check methods:
|
|
16
|
+
|
|
17
|
+
| Method | Description |
|
|
18
|
+
|--------|-------------|
|
|
19
|
+
| `check_table` | Validate table-level properties |
|
|
20
|
+
| `check_field` | Validate specific field values |
|
|
21
|
+
| `check_structure` | Validate font structure and relationships |
|
|
22
|
+
| `check_usability` | Validate usability and best practices |
|
|
23
|
+
| `check_instructions` | Validate TrueType instructions/hinting |
|
|
24
|
+
| `check_glyphs` | Validate individual glyphs |
|
|
25
|
+
|
|
26
|
+
## Creating a Validator
|
|
27
|
+
|
|
28
|
+
### Basic Structure
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
require 'fontisan/validators/validator'
|
|
32
|
+
|
|
33
|
+
class MyFontValidator < Fontisan::Validators::Validator
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def define_checks
|
|
37
|
+
# Define your checks here
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Table Validation
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
def define_checks
|
|
46
|
+
# Check name table
|
|
47
|
+
check_table :name_validation, 'name', severity: :error do |table|
|
|
48
|
+
table.valid_version? &&
|
|
49
|
+
table.valid_encoding_heuristics? &&
|
|
50
|
+
table.family_name_present? &&
|
|
51
|
+
table.postscript_name_valid?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Check head table
|
|
55
|
+
check_table :head_validation, 'head', severity: :error do |table|
|
|
56
|
+
table.valid_magic? &&
|
|
57
|
+
table.valid_version? &&
|
|
58
|
+
table.valid_units_per_em?
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Structure Validation
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
def define_checks
|
|
67
|
+
check_structure :required_tables, severity: :error do |font|
|
|
68
|
+
%w[name head maxp hhea].all? { |tag| !font.table(tag).nil? }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
check_structure :optional_tables, severity: :warning do |font|
|
|
72
|
+
# Check for recommended optional tables
|
|
73
|
+
%w[cmap hmtx].all? { |tag| !font.table(tag).nil? }
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Field Validation
|
|
79
|
+
|
|
80
|
+
```ruby
|
|
81
|
+
def define_checks
|
|
82
|
+
check_field :units_per_em, 'head.units_per_em', severity: :error do |value|
|
|
83
|
+
[1000, 2048].include?(value)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
check_field :font_revision, 'head.font_revision', severity: :warning do |value|
|
|
87
|
+
value > 0 && value < 100
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Glyph Validation
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
def define_checks
|
|
96
|
+
check_glyphs :glyph_bounds, severity: :warning do |glyph|
|
|
97
|
+
# Check if glyph has valid bounds
|
|
98
|
+
!glyph.bounds.nil? &&
|
|
99
|
+
glyph.bounds.min_x >= -10000 &&
|
|
100
|
+
glyph.bounds.max_x <= 10000
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Severity Levels
|
|
106
|
+
|
|
107
|
+
| Severity | Description |
|
|
108
|
+
|----------|-------------|
|
|
109
|
+
| `:fatal` | Critical error, font unusable |
|
|
110
|
+
| `:error` | Error, font may not work correctly |
|
|
111
|
+
| `:warning` | Warning, best practice violation |
|
|
112
|
+
| `:info` | Informational, no impact |
|
|
113
|
+
|
|
114
|
+
## Using Custom Validators
|
|
115
|
+
|
|
116
|
+
```ruby
|
|
117
|
+
# Load font
|
|
118
|
+
font = Fontisan::FontLoader.load('font.ttf')
|
|
119
|
+
|
|
120
|
+
# Create validator instance
|
|
121
|
+
validator = MyFontValidator.new(font)
|
|
122
|
+
|
|
123
|
+
# Run validation
|
|
124
|
+
report = validator.validate
|
|
125
|
+
|
|
126
|
+
# Check results
|
|
127
|
+
if report.valid?
|
|
128
|
+
puts "Font is valid!"
|
|
129
|
+
else
|
|
130
|
+
report.errors.each do |error|
|
|
131
|
+
puts "#{error.check_id}: #{error.message}"
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Complete Example
|
|
137
|
+
|
|
138
|
+
```ruby
|
|
139
|
+
require 'fontisan/validators/validator'
|
|
140
|
+
|
|
141
|
+
class WebFontValidator < Fontisan::Validators::Validator
|
|
142
|
+
private
|
|
143
|
+
|
|
144
|
+
def define_checks
|
|
145
|
+
# Required tables for web fonts
|
|
146
|
+
check_structure :web_required_tables, severity: :error do |font|
|
|
147
|
+
required = %w[name head maxp hhea cmap hmtx]
|
|
148
|
+
required.all? { |tag| !font.table(tag).nil? }
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Name records completeness
|
|
152
|
+
check_table :name_completeness, 'name', severity: :error do |table|
|
|
153
|
+
[
|
|
154
|
+
table.family_name_present?,
|
|
155
|
+
table.subfamily_name_present?,
|
|
156
|
+
table.unique_id_present?,
|
|
157
|
+
table.full_name_present?,
|
|
158
|
+
table.version_present?
|
|
159
|
+
].all?
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# UPM should be power of 2 for web
|
|
163
|
+
check_field :web_units_per_em, 'head.units_per_em', severity: :info do |value|
|
|
164
|
+
[256, 512, 1024, 2048, 4096].include?(value)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Check for GSUB (recommended for web)
|
|
168
|
+
check_structure :web_gsub_presence, severity: :info do |font|
|
|
169
|
+
!font.table('GSUB').nil?
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Use the validator
|
|
175
|
+
font = Fontisan::FontLoader.load('font.ttf')
|
|
176
|
+
validator = WebFontValidator.new(font)
|
|
177
|
+
report = validator.validate
|
|
178
|
+
|
|
179
|
+
puts report.to_summary
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Integrating with Profiles
|
|
183
|
+
|
|
184
|
+
Custom validators can be integrated into validation profiles:
|
|
185
|
+
|
|
186
|
+
```ruby
|
|
187
|
+
# Create a custom profile
|
|
188
|
+
profile = Fontisan::Validators::ValidationProfile.new(
|
|
189
|
+
name: :my_custom,
|
|
190
|
+
validators: [MyFontValidator]
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
# Use the profile
|
|
194
|
+
report = Fontisan.validate('font.ttf', profile: profile)
|
|
195
|
+
```
|