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,231 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Advanced Variable Font Topics
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Advanced Variable Font Topics
|
|
6
|
+
|
|
7
|
+
Advanced operations for variable fonts.
|
|
8
|
+
|
|
9
|
+
## avar Table
|
|
10
|
+
|
|
11
|
+
The avar (Axis Variation) table defines non-linear axis value mappings.
|
|
12
|
+
|
|
13
|
+
### What avar Does
|
|
14
|
+
|
|
15
|
+
Without avar, interpolation is linear:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
wght 400 → wght 500 → wght 600 → wght 700
|
|
19
|
+
50% 50% 50%
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
With avar, you can have non-linear interpolation:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
wght 400 → wght 500 → wght 600 → wght 700
|
|
26
|
+
70% 20% 10%
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Checking for avar
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
font = Fontisan::FontLoader.load('variable.ttf')
|
|
33
|
+
avar = font.tables['avar']
|
|
34
|
+
|
|
35
|
+
if avar
|
|
36
|
+
puts "Non-linear interpolation detected"
|
|
37
|
+
|
|
38
|
+
avar.axis_mappings.each do |axis_tag, mapping|
|
|
39
|
+
puts "#{axis_tag}:"
|
|
40
|
+
mapping.each do |from, to|
|
|
41
|
+
puts " #{from} → #{to}"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## STAT Table
|
|
48
|
+
|
|
49
|
+
The STAT (Style Attributes) table provides style attributes and axis value names.
|
|
50
|
+
|
|
51
|
+
### STAT Benefits
|
|
52
|
+
|
|
53
|
+
- Better font menu organization
|
|
54
|
+
- Consistent naming across fonts
|
|
55
|
+
- Style linking information
|
|
56
|
+
|
|
57
|
+
### Reading STAT
|
|
58
|
+
|
|
59
|
+
```ruby
|
|
60
|
+
stat = font.tables['STAT']
|
|
61
|
+
|
|
62
|
+
if stat
|
|
63
|
+
# Get design coordinates
|
|
64
|
+
stat.design_axis_values.each do |axis_value|
|
|
65
|
+
puts "#{axis_value.axis_tag}: #{axis_value.name}"
|
|
66
|
+
puts " Value: #{axis_value.value}"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Metrics Variation Tables
|
|
72
|
+
|
|
73
|
+
### HVAR (Horizontal Metrics Variation)
|
|
74
|
+
|
|
75
|
+
```ruby
|
|
76
|
+
hvar = font.tables['HVAR']
|
|
77
|
+
|
|
78
|
+
if hvar
|
|
79
|
+
# Get advance width variation
|
|
80
|
+
width_delta = hvar.advance_width_delta(glyph_id, coordinates)
|
|
81
|
+
# Apply to base width
|
|
82
|
+
final_width = base_width + width_delta
|
|
83
|
+
end
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### MVAR (Metrics Variation)
|
|
87
|
+
|
|
88
|
+
```ruby
|
|
89
|
+
mvar = font.tables['MVAR']
|
|
90
|
+
|
|
91
|
+
if mvar
|
|
92
|
+
# Get OS/2 typo ascender delta
|
|
93
|
+
ascender_delta = mvar.metric_delta('hasc', coordinates)
|
|
94
|
+
end
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### VVAR (Vertical Metrics Variation)
|
|
98
|
+
|
|
99
|
+
```ruby
|
|
100
|
+
vvar = font.tables['VVAR']
|
|
101
|
+
|
|
102
|
+
if vvar
|
|
103
|
+
# Get vertical advance delta
|
|
104
|
+
v_advance_delta = vvar.advance_height_delta(glyph_id, coordinates)
|
|
105
|
+
end
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Delta Processing
|
|
109
|
+
|
|
110
|
+
### Understanding Deltas
|
|
111
|
+
|
|
112
|
+
Variable fonts store changes (deltas) from the default:
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
# Default glyph outline
|
|
116
|
+
default_outline = font.glyphs[glyph_id].outline
|
|
117
|
+
|
|
118
|
+
# Delta at wght=700
|
|
119
|
+
delta = gvar.delta_for_glyph(glyph_id, wght: 700)
|
|
120
|
+
|
|
121
|
+
# Apply delta to get bold outline
|
|
122
|
+
bold_outline = default_outline + delta
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Interpolation
|
|
126
|
+
|
|
127
|
+
```ruby
|
|
128
|
+
# Interpolate between two masters
|
|
129
|
+
weight = 600 # Between regular (400) and bold (700)
|
|
130
|
+
|
|
131
|
+
# Calculate interpolation factor
|
|
132
|
+
t = (weight - 400) / (700 - 400) # 0.667
|
|
133
|
+
|
|
134
|
+
# Interpolate delta
|
|
135
|
+
interpolated_delta = gvar.interpolate(glyph_id, t, wght: weight)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## CFF2 Variable Fonts
|
|
139
|
+
|
|
140
|
+
### CFF2 vs CFF
|
|
141
|
+
|
|
142
|
+
| Feature | CFF | CFF2 |
|
|
143
|
+
|---------|-----|------|
|
|
144
|
+
| Variation | No | Yes |
|
|
145
|
+
| CharStrings | Standard | With blend operators |
|
|
146
|
+
| Private DICT | Per-font | Per-local |
|
|
147
|
+
|
|
148
|
+
### Reading CFF2
|
|
149
|
+
|
|
150
|
+
```ruby
|
|
151
|
+
cff2 = font.tables['CFF2']
|
|
152
|
+
|
|
153
|
+
if cff2
|
|
154
|
+
# Get blend values for coordinates
|
|
155
|
+
blend_values = cff2.blend_values(glyph_id, coordinates)
|
|
156
|
+
|
|
157
|
+
# Access variation store
|
|
158
|
+
variation_store = cff2.variation_store
|
|
159
|
+
end
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Validation
|
|
163
|
+
|
|
164
|
+
### Variable Font Validation
|
|
165
|
+
|
|
166
|
+
```ruby
|
|
167
|
+
# Validate variable font structure
|
|
168
|
+
result = Fontisan.validate('variable.ttf', profile: :production)
|
|
169
|
+
|
|
170
|
+
# Check variable-specific issues
|
|
171
|
+
if result.valid?
|
|
172
|
+
# Verify fvar/gvar consistency
|
|
173
|
+
fvar = font.tables['fvar']
|
|
174
|
+
gvar = font.tables['gvar']
|
|
175
|
+
|
|
176
|
+
if fvar && gvar
|
|
177
|
+
# Check axis count matches
|
|
178
|
+
unless gvar.axis_count == fvar.axes.length
|
|
179
|
+
puts "Warning: gvar axis count mismatch"
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Common Issues
|
|
186
|
+
|
|
187
|
+
- **Missing gvar/CFF2**: Font has fvar but no variation data
|
|
188
|
+
- **Axis mismatch**: gvar axis count differs from fvar
|
|
189
|
+
- **Invalid coordinates**: Instance coordinates outside axis ranges
|
|
190
|
+
- **Missing default instance**: No default coordinates defined
|
|
191
|
+
|
|
192
|
+
## Performance Optimization
|
|
193
|
+
|
|
194
|
+
### Caching
|
|
195
|
+
|
|
196
|
+
```ruby
|
|
197
|
+
# Create writer once for multiple instances
|
|
198
|
+
writer = Fontisan::Variation::InstanceWriter.new(font)
|
|
199
|
+
|
|
200
|
+
# Cache is reused across calls
|
|
201
|
+
instances = weights.map { |w| writer.generate_instance(wght: w) }
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Batch Processing
|
|
205
|
+
|
|
206
|
+
```ruby
|
|
207
|
+
# Process multiple fonts in parallel
|
|
208
|
+
fonts = Dir.glob('fonts/*.ttf').map { |f| Fontisan::FontLoader.load(f) }
|
|
209
|
+
|
|
210
|
+
fonts.map do |font|
|
|
211
|
+
Thread.new do
|
|
212
|
+
writer = Fontisan::Variation::InstanceWriter.new(font)
|
|
213
|
+
writer.generate_instance(wght: 700)
|
|
214
|
+
end
|
|
215
|
+
end.each(&:join)
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## SVG Generation
|
|
219
|
+
|
|
220
|
+
### Limitations
|
|
221
|
+
|
|
222
|
+
Variable fonts cannot be directly exported to SVG. Generate static instances first:
|
|
223
|
+
|
|
224
|
+
```ruby
|
|
225
|
+
# Generate instance
|
|
226
|
+
writer = Fontisan::Variation::InstanceWriter.new(font)
|
|
227
|
+
static = writer.generate_instance(wght: 700)
|
|
228
|
+
|
|
229
|
+
# Export to SVG
|
|
230
|
+
Fontisan::FontWriter.write(static, 'bold.svg', format: :svg)
|
|
231
|
+
```
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Axes and Instances
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Axes and Instances
|
|
6
|
+
|
|
7
|
+
Work with variation axes and named instances in variable fonts.
|
|
8
|
+
|
|
9
|
+
## Getting Axis Information
|
|
10
|
+
|
|
11
|
+
### CLI
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Show axis information
|
|
15
|
+
fontisan info variable.ttf
|
|
16
|
+
|
|
17
|
+
# Example output:
|
|
18
|
+
# Variable Font Axes: 2
|
|
19
|
+
# wght (Weight): 100 - 900, default: 400
|
|
20
|
+
# wdth (Width): 75 - 125, default: 100
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### API
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
font = Fontisan::FontLoader.load('variable.ttf')
|
|
27
|
+
fvar = font.tables['fvar']
|
|
28
|
+
|
|
29
|
+
# List axes
|
|
30
|
+
fvar.axes.each do |axis|
|
|
31
|
+
puts "Tag: #{axis.tag}"
|
|
32
|
+
puts " Min: #{axis.min_value}"
|
|
33
|
+
puts " Max: #{axis.max_value}"
|
|
34
|
+
puts " Default: #{axis.default_value}"
|
|
35
|
+
puts " Name: #{axis.name}"
|
|
36
|
+
end
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Axis Properties
|
|
40
|
+
|
|
41
|
+
Each axis has these properties:
|
|
42
|
+
|
|
43
|
+
| Property | Description |
|
|
44
|
+
|----------|-------------|
|
|
45
|
+
| `tag` | Four-character axis identifier (e.g., `wght`) |
|
|
46
|
+
| `min_value` | Minimum value on the axis |
|
|
47
|
+
| `max_value` | Maximum value on the axis |
|
|
48
|
+
| `default_value` | Default value when no variation applied |
|
|
49
|
+
| `name` | Human-readable axis name |
|
|
50
|
+
|
|
51
|
+
## Registered Axes
|
|
52
|
+
|
|
53
|
+
### Weight (`wght`)
|
|
54
|
+
|
|
55
|
+
Controls font weight.
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
# Range: typically 100-900
|
|
59
|
+
# Common values:
|
|
60
|
+
# 100 - Thin
|
|
61
|
+
# 200 - Extra Light
|
|
62
|
+
# 300 - Light
|
|
63
|
+
# 400 - Regular
|
|
64
|
+
# 500 - Medium
|
|
65
|
+
# 600 - Semi Bold
|
|
66
|
+
# 700 - Bold
|
|
67
|
+
# 800 - Extra Bold
|
|
68
|
+
# 900 - Black
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Width (`wdth`)
|
|
72
|
+
|
|
73
|
+
Controls font width.
|
|
74
|
+
|
|
75
|
+
```ruby
|
|
76
|
+
# Range: typically 50-200 (percentage)
|
|
77
|
+
# Common values:
|
|
78
|
+
# 50 - Ultra Condensed
|
|
79
|
+
# 62.5 - Extra Condensed
|
|
80
|
+
# 75 - Condensed
|
|
81
|
+
# 87.5 - Semi Condensed
|
|
82
|
+
# 100 - Normal
|
|
83
|
+
# 112.5 - Semi Expanded
|
|
84
|
+
# 125 - Expanded
|
|
85
|
+
# 150 - Extra Expanded
|
|
86
|
+
# 200 - Ultra Expanded
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Slant (`slnt`)
|
|
90
|
+
|
|
91
|
+
Controls slant angle.
|
|
92
|
+
|
|
93
|
+
```ruby
|
|
94
|
+
# Range: -90 to 90 degrees
|
|
95
|
+
# Common values:
|
|
96
|
+
# 0 - Upright
|
|
97
|
+
# -12 - Typical italic slant
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Italic (`ital`)
|
|
101
|
+
|
|
102
|
+
Binary italic toggle.
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
# Range: 0-1
|
|
106
|
+
# 0 - Roman (upright)
|
|
107
|
+
# 1 - Italic
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Optical Size (`opsz`)
|
|
111
|
+
|
|
112
|
+
Controls optical sizing.
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
# Range: varies by font
|
|
116
|
+
# Example values:
|
|
117
|
+
# 8 - Caption
|
|
118
|
+
# 12 - Text
|
|
119
|
+
# 24 - Subhead
|
|
120
|
+
# 72 - Display
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Named Instances
|
|
124
|
+
|
|
125
|
+
### List Named Instances
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
# CLI
|
|
129
|
+
fontisan instance variable.ttf --list-instances
|
|
130
|
+
|
|
131
|
+
# Example output:
|
|
132
|
+
# Named Instances: 6
|
|
133
|
+
# 0: Thin (wght=100)
|
|
134
|
+
# 1: Light (wght=300)
|
|
135
|
+
# 2: Regular (wght=400)
|
|
136
|
+
# 3: Medium (wght=500)
|
|
137
|
+
# 4: Bold (wght=700)
|
|
138
|
+
# 5: Black (wght=900)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### API Access
|
|
142
|
+
|
|
143
|
+
```ruby
|
|
144
|
+
font = Fontisan::FontLoader.load('variable.ttf')
|
|
145
|
+
fvar = font.tables['fvar']
|
|
146
|
+
|
|
147
|
+
# List named instances
|
|
148
|
+
fvar.instances.each_with_index do |instance, index|
|
|
149
|
+
puts "#{index}: #{instance.name}"
|
|
150
|
+
instance.coordinates.each do |tag, value|
|
|
151
|
+
puts " #{tag} = #{value}"
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Using Named Instances
|
|
157
|
+
|
|
158
|
+
### CLI
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
# Generate using instance index
|
|
162
|
+
fontisan instance variable.ttf --named-instance 0 --output thin.ttf
|
|
163
|
+
|
|
164
|
+
# Generate using instance name
|
|
165
|
+
fontisan instance variable.ttf --named-instance "Bold" --output bold.ttf
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### API
|
|
169
|
+
|
|
170
|
+
```ruby
|
|
171
|
+
# Get named instance coordinates
|
|
172
|
+
fvar = font.tables['fvar']
|
|
173
|
+
instance = fvar.instances.find { |i| i.name == "Bold" }
|
|
174
|
+
|
|
175
|
+
# Generate instance with those coordinates
|
|
176
|
+
writer = Fontisan::Variation::InstanceWriter.new(font)
|
|
177
|
+
bold_font = writer.generate_instance(instance.coordinates)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## avar Table
|
|
181
|
+
|
|
182
|
+
The avar (Axis Variation) table defines non-linear axis value mappings.
|
|
183
|
+
|
|
184
|
+
```ruby
|
|
185
|
+
# Check for avar table
|
|
186
|
+
avar = font.tables['avar']
|
|
187
|
+
|
|
188
|
+
if avar
|
|
189
|
+
puts "Non-linear interpolation present"
|
|
190
|
+
avar.mappings.each do |tag, map|
|
|
191
|
+
puts "#{tag}: #{map}"
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## STAT Table
|
|
197
|
+
|
|
198
|
+
The STAT (Style Attributes) table provides style attributes and axis value names.
|
|
199
|
+
|
|
200
|
+
```ruby
|
|
201
|
+
stat = font.tables['STAT']
|
|
202
|
+
|
|
203
|
+
if stat
|
|
204
|
+
# Get axis values
|
|
205
|
+
stat.axis_values.each do |axis_value|
|
|
206
|
+
puts "#{axis_value.name}: #{axis_value.value}"
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
```
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Format Conversion
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Format Conversion
|
|
6
|
+
|
|
7
|
+
Convert variable fonts between formats while preserving or converting variation data.
|
|
8
|
+
|
|
9
|
+
## Conversion Strategy
|
|
10
|
+
|
|
11
|
+
### Compatible Formats (Same Outline)
|
|
12
|
+
|
|
13
|
+
- Variable TTF ↔ Variable TTF/WOFF/WOFF2: All variation tables preserved
|
|
14
|
+
- Variable OTF ↔ Variable OTF/WOFF/WOFF2: All variation tables preserved
|
|
15
|
+
|
|
16
|
+
### Convertible Formats (Different Outline)
|
|
17
|
+
|
|
18
|
+
- Variable TTF ↔ Variable OTF: Common tables preserved (fvar, avar, STAT, metrics)
|
|
19
|
+
- Outline-specific tables require conversion (gvar ↔ CFF2 blend)
|
|
20
|
+
|
|
21
|
+
### Unsupported
|
|
22
|
+
|
|
23
|
+
- Variable fonts to SVG: Creates instance at default coordinates
|
|
24
|
+
|
|
25
|
+
## CLI Usage
|
|
26
|
+
|
|
27
|
+
### Preserve Variation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Variable TTF to WOFF2 (preserves all variation)
|
|
31
|
+
fontisan convert variable.ttf --to woff2 --output variable.woff2
|
|
32
|
+
|
|
33
|
+
# Variable OTF to WOFF2 (preserves all variation)
|
|
34
|
+
fontisan convert variable.otf --to woff2 --output variable.woff2
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Convert Outline Format
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Variable TTF to OTF (preserves common variation tables)
|
|
41
|
+
fontisan convert variable.ttf --to otf --output variable.otf
|
|
42
|
+
|
|
43
|
+
# Note: Shows warning about gvar → CFF2 conversion
|
|
44
|
+
# Preserves: fvar, avar, STAT, HVAR, VVAR, MVAR
|
|
45
|
+
# Does not preserve: gvar (requires conversion to CFF2)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Create Static Font
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Remove all variation data
|
|
52
|
+
fontisan convert variable.ttf --to ttf --output static.ttf --no-preserve-variation
|
|
53
|
+
|
|
54
|
+
# Creates static font at default variation coordinates
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Ruby API
|
|
58
|
+
|
|
59
|
+
### Preserve Variation
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
font = Fontisan::FontLoader.load('variable.ttf')
|
|
63
|
+
|
|
64
|
+
# Convert with variation preservation
|
|
65
|
+
options = Fontisan::ConversionOptions.new(
|
|
66
|
+
preserve_variation: true
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
Fontisan::FontWriter.write(font, 'variable.woff2', options: options)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Convert Outline Format
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
# Variable TTF to OTF
|
|
76
|
+
converter = Fontisan::Converters::OutlineConverter.new
|
|
77
|
+
|
|
78
|
+
options = Fontisan::ConversionOptions.new(
|
|
79
|
+
from: :ttf,
|
|
80
|
+
to: :otf,
|
|
81
|
+
preserve_variation: true # Preserves fvar, avar, STAT
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
result = converter.convert(font, options: options)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Remove Variation
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
# Create static font at default coordinates
|
|
91
|
+
writer = Fontisan::Variation::InstanceWriter.new(font)
|
|
92
|
+
static_font = writer.generate_instance({}) # Empty hash = default values
|
|
93
|
+
|
|
94
|
+
Fontisan::FontWriter.write(static_font, 'static.ttf')
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Tables Preserved
|
|
98
|
+
|
|
99
|
+
### Same Outline Format
|
|
100
|
+
|
|
101
|
+
All variation tables preserved:
|
|
102
|
+
|
|
103
|
+
| Table | Preserved |
|
|
104
|
+
|-------|-----------|
|
|
105
|
+
| fvar | ✓ |
|
|
106
|
+
| gvar | ✓ (TrueType) |
|
|
107
|
+
| CFF2 | ✓ (OpenType) |
|
|
108
|
+
| avar | ✓ |
|
|
109
|
+
| STAT | ✓ |
|
|
110
|
+
| HVAR | ✓ |
|
|
111
|
+
| VVAR | ✓ |
|
|
112
|
+
| MVAR | ✓ |
|
|
113
|
+
|
|
114
|
+
### Cross Outline Format
|
|
115
|
+
|
|
116
|
+
Common tables preserved:
|
|
117
|
+
|
|
118
|
+
| Table | Preserved |
|
|
119
|
+
|-------|-----------|
|
|
120
|
+
| fvar | ✓ |
|
|
121
|
+
| avar | ✓ |
|
|
122
|
+
| STAT | ✓ |
|
|
123
|
+
| HVAR | ✓ |
|
|
124
|
+
| VVAR | ✓ |
|
|
125
|
+
| MVAR | ✓ |
|
|
126
|
+
| gvar | ✗ (requires CFF2 conversion) |
|
|
127
|
+
| CFF2 | ✗ (requires gvar conversion) |
|
|
128
|
+
|
|
129
|
+
## Limitations
|
|
130
|
+
|
|
131
|
+
### gvar ↔ CFF2
|
|
132
|
+
|
|
133
|
+
Converting between TrueType (gvar) and OpenType (CFF2) variation data is not fully implemented:
|
|
134
|
+
|
|
135
|
+
- **TTF → OTF**: gvar variation data is lost
|
|
136
|
+
- **OTF → TTF**: CFF2 blend operators are lost
|
|
137
|
+
|
|
138
|
+
The fvar, avar, and STAT tables are preserved, but the actual glyph variation data is not.
|
|
139
|
+
|
|
140
|
+
### Workaround
|
|
141
|
+
|
|
142
|
+
Generate static instances before conversion:
|
|
143
|
+
|
|
144
|
+
```ruby
|
|
145
|
+
# Generate instances from variable TTF
|
|
146
|
+
writer = Fontisan::Variation::InstanceWriter.new(font)
|
|
147
|
+
instance = writer.generate_instance(wght: 700)
|
|
148
|
+
|
|
149
|
+
# Convert instance to OTF
|
|
150
|
+
converter = Fontisan::Converters::OutlineConverter.new
|
|
151
|
+
otf_font = converter.convert(instance, options: otf_options)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Web Font Conversion
|
|
155
|
+
|
|
156
|
+
### Variable WOFF2
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
# Smallest file size with variation
|
|
160
|
+
fontisan convert variable.ttf --to woff2 --output variable.woff2
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Benefits:
|
|
164
|
+
- 30-50% smaller than variable TTF
|
|
165
|
+
- All variation preserved
|
|
166
|
+
- Best for web delivery
|
|
167
|
+
|
|
168
|
+
### Instance Generation for Web
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
# Generate instances as WOFF2
|
|
172
|
+
fontisan instance variable.ttf --wght 700 --to woff2 --output bold.woff2
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Examples
|
|
176
|
+
|
|
177
|
+
### Convert Variable Font Family
|
|
178
|
+
|
|
179
|
+
```ruby
|
|
180
|
+
require 'fontisan'
|
|
181
|
+
|
|
182
|
+
# Load variable font
|
|
183
|
+
font = Fontisan::FontLoader.load('variable.ttf')
|
|
184
|
+
|
|
185
|
+
# Convert to WOFF2
|
|
186
|
+
options = Fontisan::ConversionOptions.from_preset(:web_optimized)
|
|
187
|
+
Fontisan::FontWriter.write(font, 'variable.woff2', options: options)
|
|
188
|
+
|
|
189
|
+
# Generate key instances
|
|
190
|
+
fvar = font.tables['fvar']
|
|
191
|
+
writer = Fontisan::Variation::InstanceWriter.new(font)
|
|
192
|
+
|
|
193
|
+
[400, 500, 600, 700].each do |wght|
|
|
194
|
+
instance = writer.generate_instance(wght: wght)
|
|
195
|
+
Fontisan::FontWriter.write(instance, "weight-#{wght}.woff2", options: options)
|
|
196
|
+
end
|
|
197
|
+
```
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Variable Fonts Overview
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Variable Fonts Overview
|
|
6
|
+
|
|
7
|
+
Variable fonts are OpenType fonts that contain multiple variations of a typeface in a single file. Instead of having separate font files for different weights, widths, or styles, a variable font uses variation axes to interpolate between different design extremes.
|
|
8
|
+
|
|
9
|
+
## Key Tables
|
|
10
|
+
|
|
11
|
+
| Table | Description |
|
|
12
|
+
|-------|-------------|
|
|
13
|
+
| `fvar` | Defines variation axes and named instances |
|
|
14
|
+
| `gvar` | (TrueType) Glyph variation data as delta tuples |
|
|
15
|
+
| `CFF2` | (OpenType) Variation data as blend operators |
|
|
16
|
+
| `avar` | (Optional) Axis value mappings for non-linear interpolation |
|
|
17
|
+
| `STAT` | (Optional) Style attributes and axis value names |
|
|
18
|
+
| `HVAR/VVAR/MVAR` | (Optional) Metrics variation tables |
|
|
19
|
+
|
|
20
|
+
## Variation Axes
|
|
21
|
+
|
|
22
|
+
Each axis represents a design dimension along which the font can vary.
|
|
23
|
+
|
|
24
|
+
### Common Registered Axes
|
|
25
|
+
|
|
26
|
+
| Tag | Name | Range | Typical Values |
|
|
27
|
+
|-----|------|-------|----------------|
|
|
28
|
+
| `wght` | Weight | 1-1000 | 400 (Regular) to 700 (Bold) |
|
|
29
|
+
| `wdth` | Width | 1-1000 | 75 (Condensed) to 125 (Expanded) |
|
|
30
|
+
| `slnt` | Slant | -90 to 90 | Degrees |
|
|
31
|
+
| `ital` | Italic | 0 or 1 | 0 (Roman), 1 (Italic) |
|
|
32
|
+
| `opsz` | Optical Size | Varies | Point size for optical sizing |
|
|
33
|
+
|
|
34
|
+
### Custom Axes
|
|
35
|
+
|
|
36
|
+
Custom axes use four-character tags starting with uppercase letter:
|
|
37
|
+
|
|
38
|
+
- `GRAD` — Grade
|
|
39
|
+
- `XOPQ` — X-Optical-Size
|
|
40
|
+
- `YOPQ` — Y-Optical-Size
|
|
41
|
+
- `XTRA` — X-Transparency
|
|
42
|
+
- And many others...
|
|
43
|
+
|
|
44
|
+
## Named Instances
|
|
45
|
+
|
|
46
|
+
Variable fonts can define named instances — predefined points in the design space with specific names:
|
|
47
|
+
|
|
48
|
+
- "Regular"
|
|
49
|
+
- "Bold"
|
|
50
|
+
- "Light"
|
|
51
|
+
- "Condensed Bold"
|
|
52
|
+
- "Expanded Italic"
|
|
53
|
+
|
|
54
|
+
## Guides
|
|
55
|
+
|
|
56
|
+
- [Axes & Instances](/guide/variable-fonts/axes) — Working with axes and named instances
|
|
57
|
+
- [Instance Generation](/guide/variable-fonts/instances) — Generate static fonts from variable fonts
|
|
58
|
+
- [Format Conversion](/guide/variable-fonts/conversion) — Convert variable fonts between formats
|
|
59
|
+
- [Named Instances](/guide/variable-fonts/named-instances) — Work with named instances
|
|
60
|
+
- [Static Fonts](/guide/variable-fonts/static) — Convert to static fonts
|
|
61
|
+
- [Advanced Topics](/guide/variable-fonts/advanced) — Advanced variable font operations
|
|
62
|
+
|
|
63
|
+
## Quick Start
|
|
64
|
+
|
|
65
|
+
### Get Axis Information
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
font = Fontisan::FontLoader.load('variable.ttf')
|
|
69
|
+
fvar = font.tables['fvar']
|
|
70
|
+
|
|
71
|
+
fvar.axes.each do |axis|
|
|
72
|
+
puts "#{axis.tag}: #{axis.min_value} - #{axis.max_value}"
|
|
73
|
+
end
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Generate Instance
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Generate bold instance
|
|
80
|
+
fontisan instance variable.ttf --wght 700 --output bold.ttf
|
|
81
|
+
|
|
82
|
+
# Generate with multiple axes
|
|
83
|
+
fontisan instance variable.ttf --wght 700 --wdth 75 --output condensed-bold.ttf
|
|
84
|
+
```
|