fontisan 0.2.1 → 0.2.3
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 +58 -392
- data/README.adoc +1509 -1430
- data/Rakefile +3 -2
- data/benchmark/variation_quick_bench.rb +4 -4
- data/docs/FONT_HINTING.adoc +562 -0
- data/docs/VARIABLE_FONT_OPERATIONS.adoc +599 -0
- data/lib/fontisan/base_collection.rb +296 -0
- data/lib/fontisan/cli.rb +10 -3
- data/lib/fontisan/collection/builder.rb +2 -1
- data/lib/fontisan/collection/offset_calculator.rb +2 -0
- data/lib/fontisan/commands/base_command.rb +5 -2
- data/lib/fontisan/commands/convert_command.rb +6 -2
- data/lib/fontisan/commands/info_command.rb +129 -5
- data/lib/fontisan/commands/instance_command.rb +8 -7
- data/lib/fontisan/commands/validate_command.rb +4 -1
- data/lib/fontisan/constants.rb +24 -24
- data/lib/fontisan/converters/format_converter.rb +8 -4
- data/lib/fontisan/converters/outline_converter.rb +21 -16
- data/lib/fontisan/converters/woff_writer.rb +8 -3
- data/lib/fontisan/font_loader.rb +120 -30
- data/lib/fontisan/font_writer.rb +2 -0
- data/lib/fontisan/formatters/text_formatter.rb +116 -19
- data/lib/fontisan/hints/hint_converter.rb +43 -47
- data/lib/fontisan/hints/hint_validator.rb +284 -0
- data/lib/fontisan/hints/postscript_hint_applier.rb +1 -3
- data/lib/fontisan/hints/postscript_hint_extractor.rb +78 -43
- data/lib/fontisan/hints/truetype_hint_extractor.rb +22 -26
- data/lib/fontisan/hints/truetype_instruction_analyzer.rb +261 -0
- data/lib/fontisan/hints/truetype_instruction_generator.rb +266 -0
- data/lib/fontisan/loading_modes.rb +4 -4
- data/lib/fontisan/models/collection_brief_info.rb +37 -0
- data/lib/fontisan/models/collection_info.rb +6 -1
- data/lib/fontisan/models/font_export.rb +2 -2
- data/lib/fontisan/models/font_info.rb +3 -30
- data/lib/fontisan/models/hint.rb +22 -23
- data/lib/fontisan/models/outline.rb +4 -1
- data/lib/fontisan/models/validation_report.rb +1 -1
- data/lib/fontisan/open_type_collection.rb +17 -220
- data/lib/fontisan/open_type_font.rb +3 -1
- data/lib/fontisan/optimizers/pattern_analyzer.rb +2 -1
- data/lib/fontisan/optimizers/subroutine_generator.rb +1 -1
- data/lib/fontisan/pipeline/output_writer.rb +8 -3
- data/lib/fontisan/pipeline/transformation_pipeline.rb +8 -3
- data/lib/fontisan/subset/table_subsetter.rb +5 -5
- data/lib/fontisan/tables/cff/charstring.rb +38 -12
- data/lib/fontisan/tables/cff/charstring_parser.rb +23 -11
- data/lib/fontisan/tables/cff/charstring_rebuilder.rb +14 -14
- data/lib/fontisan/tables/cff/dict_builder.rb +4 -1
- data/lib/fontisan/tables/cff/hint_operation_injector.rb +6 -4
- data/lib/fontisan/tables/cff/offset_recalculator.rb +1 -1
- data/lib/fontisan/tables/cff/private_dict_writer.rb +10 -4
- data/lib/fontisan/tables/cff/table_builder.rb +1 -1
- data/lib/fontisan/tables/cff2/charstring_parser.rb +14 -8
- data/lib/fontisan/tables/cff2/private_dict_blend_handler.rb +7 -6
- data/lib/fontisan/tables/cff2/region_matcher.rb +2 -2
- data/lib/fontisan/tables/cff2/table_builder.rb +26 -20
- data/lib/fontisan/tables/cff2/table_reader.rb +35 -33
- data/lib/fontisan/tables/cff2/variation_data_extractor.rb +2 -2
- data/lib/fontisan/tables/cff2.rb +1 -1
- data/lib/fontisan/tables/glyf/compound_glyph_resolver.rb +2 -1
- data/lib/fontisan/tables/glyf/curve_converter.rb +10 -4
- data/lib/fontisan/tables/glyf/glyph_builder.rb +27 -10
- data/lib/fontisan/tables/name.rb +4 -4
- data/lib/fontisan/true_type_collection.rb +29 -113
- data/lib/fontisan/true_type_font.rb +3 -1
- data/lib/fontisan/validation/checksum_validator.rb +2 -2
- data/lib/fontisan/variation/cache.rb +3 -1
- data/lib/fontisan/variation/converter.rb +2 -1
- data/lib/fontisan/variation/delta_applier.rb +2 -1
- data/lib/fontisan/variation/inspector.rb +2 -1
- data/lib/fontisan/variation/instance_generator.rb +2 -1
- data/lib/fontisan/variation/optimizer.rb +6 -3
- data/lib/fontisan/variation/subsetter.rb +32 -10
- data/lib/fontisan/variation/variation_preserver.rb +4 -1
- data/lib/fontisan/version.rb +1 -1
- data/lib/fontisan/woff2/glyf_transformer.rb +57 -30
- data/lib/fontisan/woff2_font.rb +31 -15
- data/lib/fontisan.rb +42 -2
- data/scripts/measure_optimization.rb +15 -7
- metadata +9 -2
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
= Variable Font Operations Guide
|
|
2
|
+
:toc:
|
|
3
|
+
:toclevels: 3
|
|
4
|
+
|
|
5
|
+
== Purpose
|
|
6
|
+
|
|
7
|
+
This guide provides comprehensive documentation for working with variable fonts in Fontisan, covering all supported operations including instance generation, format conversion, SVG generation, collection management, and validation.
|
|
8
|
+
|
|
9
|
+
== Variable Font Concepts
|
|
10
|
+
|
|
11
|
+
=== General
|
|
12
|
+
|
|
13
|
+
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.
|
|
14
|
+
|
|
15
|
+
Key components:
|
|
16
|
+
|
|
17
|
+
fvar table:: Defines the variation axes and named instances
|
|
18
|
+
gvar table:: (TrueType) Contains glyph variation data as delta tuples
|
|
19
|
+
CFF2 table:: (OpenType) Contains variation data as blend operators
|
|
20
|
+
avar table:: (Optional) Defines axis value mappings for non-linear interpolation
|
|
21
|
+
STAT table:: (Optional) Provides style attributes and axis value names
|
|
22
|
+
HVAR/VVAR/MVAR:: (Optional) Metrics variation tables
|
|
23
|
+
|
|
24
|
+
=== Variation Axes
|
|
25
|
+
|
|
26
|
+
Each axis represents a design dimension along which the font can vary:
|
|
27
|
+
|
|
28
|
+
Common registered axes::
|
|
29
|
+
* `wght` (Weight): 1-1000, typically 400 (Regular) to 700 (Bold)
|
|
30
|
+
* `wdth` (Width): 1-1000, typically 75 (Condensed) to 125 (Expanded)
|
|
31
|
+
* `slnt` (Slant): -90 to 90 degrees
|
|
32
|
+
* `ital` (Italic): 0 (Roman) or 1 (Italic)
|
|
33
|
+
* `opsz` (Optical Size): Point size for optical sizing
|
|
34
|
+
|
|
35
|
+
Custom axes:: Four-character tags starting with uppercase letter (e.g., `GRAD`)
|
|
36
|
+
|
|
37
|
+
=== Named Instances
|
|
38
|
+
|
|
39
|
+
Variable fonts can define named instances - predefined points in the design space with specific names (e.g., "Bold", "Light", "Condensed Bold").
|
|
40
|
+
|
|
41
|
+
== Instance Generation
|
|
42
|
+
|
|
43
|
+
=== General
|
|
44
|
+
|
|
45
|
+
Generate static font instances from variable fonts at specific variation coordinates.
|
|
46
|
+
|
|
47
|
+
=== Using the CLI
|
|
48
|
+
|
|
49
|
+
==== Generate instance at specific coordinates
|
|
50
|
+
|
|
51
|
+
[source,bash]
|
|
52
|
+
----
|
|
53
|
+
# Generate bold instance
|
|
54
|
+
fontisan instance variable.ttf --wght 700 --output bold.ttf
|
|
55
|
+
|
|
56
|
+
# Generate condensed bold
|
|
57
|
+
fontisan instance variable.ttf --wght 700 --wdth 75 --output condensed-bold.ttf
|
|
58
|
+
|
|
59
|
+
# Generate with all axes
|
|
60
|
+
fontisan instance variable.ttf --wght 600 --wdth 90 --slnt -12 --output custom.ttf
|
|
61
|
+
----
|
|
62
|
+
|
|
63
|
+
==== Use named instances
|
|
64
|
+
|
|
65
|
+
[source,bash]
|
|
66
|
+
----
|
|
67
|
+
# List available instances
|
|
68
|
+
fontisan instance variable.ttf --list-instances
|
|
69
|
+
|
|
70
|
+
# Generate using named instance
|
|
71
|
+
fontisan instance variable.ttf --named-instance 0 --output bold.ttf
|
|
72
|
+
fontisan instance variable.ttf --named-instance "Bold" --output bold.ttf
|
|
73
|
+
----
|
|
74
|
+
|
|
75
|
+
==== Output format options
|
|
76
|
+
|
|
77
|
+
[source,bash]
|
|
78
|
+
----
|
|
79
|
+
# Generate as TrueType (default)
|
|
80
|
+
fontisan instance variable.ttf --wght 700 --output bold.ttf
|
|
81
|
+
|
|
82
|
+
# Generate as OpenType/CFF
|
|
83
|
+
fontisan instance variable.ttf --wght 700 --to otf --output bold.otf
|
|
84
|
+
|
|
85
|
+
# Generate as WOFF2
|
|
86
|
+
fontisan instance variable.ttf --wght 700 --to woff2 --output bold.woff2
|
|
87
|
+
----
|
|
88
|
+
|
|
89
|
+
=== Using the Ruby API
|
|
90
|
+
|
|
91
|
+
==== Basic instance generation
|
|
92
|
+
|
|
93
|
+
[source,ruby]
|
|
94
|
+
----
|
|
95
|
+
require 'fontisan'
|
|
96
|
+
|
|
97
|
+
# Load variable font
|
|
98
|
+
font = Fontisan::FontLoader.load('variable.ttf')
|
|
99
|
+
|
|
100
|
+
# Generate instance at specific coordinates
|
|
101
|
+
writer = Fontisan::Variation::InstanceWriter.new(font)
|
|
102
|
+
instance_font = writer.generate_instance(wght: 700)
|
|
103
|
+
|
|
104
|
+
# Write to file
|
|
105
|
+
Fontisan::FontWriter.write_to_file(
|
|
106
|
+
instance_font.table_data,
|
|
107
|
+
'bold.ttf',
|
|
108
|
+
sfnt_version: 0x00010000
|
|
109
|
+
)
|
|
110
|
+
----
|
|
111
|
+
|
|
112
|
+
==== Generate with multiple axes
|
|
113
|
+
|
|
114
|
+
[source,ruby]
|
|
115
|
+
----
|
|
116
|
+
# Generate instance with multiple axis values
|
|
117
|
+
instance_font = writer.generate_instance(
|
|
118
|
+
wght: 700,
|
|
119
|
+
wdth: 75,
|
|
120
|
+
slnt: -8
|
|
121
|
+
)
|
|
122
|
+
----
|
|
123
|
+
|
|
124
|
+
==== Use named instances
|
|
125
|
+
|
|
126
|
+
[source,ruby]
|
|
127
|
+
----
|
|
128
|
+
# Get named instance information
|
|
129
|
+
fvar = font.table('fvar')
|
|
130
|
+
instance = fvar.instances[0]
|
|
131
|
+
|
|
132
|
+
# Extract coordinates from named instance
|
|
133
|
+
coordinates = instance[:coordinates]
|
|
134
|
+
axis_tags = fvar.axes.map(&:axis_tag)
|
|
135
|
+
axis_values = Hash[axis_tags.zip(coordinates)]
|
|
136
|
+
|
|
137
|
+
# Generate instance
|
|
138
|
+
instance_font = writer.generate_instance(axis_values)
|
|
139
|
+
----
|
|
140
|
+
|
|
141
|
+
== Format Conversion
|
|
142
|
+
|
|
143
|
+
=== General
|
|
144
|
+
|
|
145
|
+
Convert variable fonts between formats while preserving or converting variation data.
|
|
146
|
+
|
|
147
|
+
=== Variation Preservation Strategy
|
|
148
|
+
|
|
149
|
+
Compatible formats (same outline)::
|
|
150
|
+
* Variable TTF ↔ Variable TTF/WOFF/WOFF2: All variation tables preserved
|
|
151
|
+
* Variable OTF ↔ Variable OTF/WOFF/WOFF2: All variation tables preserved
|
|
152
|
+
|
|
153
|
+
Convertible formats (different outline)::
|
|
154
|
+
* Variable TTF ↔ Variable OTF: Common tables preserved (fvar, avar, STAT, metrics)
|
|
155
|
+
* Outline-specific tables require conversion (gvar ↔ CFF2 blend)
|
|
156
|
+
|
|
157
|
+
Unsupported formats::
|
|
158
|
+
* Variable fonts to SVG: Creates instance at default coordinates
|
|
159
|
+
|
|
160
|
+
=== Using the CLI
|
|
161
|
+
|
|
162
|
+
==== Convert with variation preservation
|
|
163
|
+
|
|
164
|
+
[source,bash]
|
|
165
|
+
----
|
|
166
|
+
# Variable TTF to WOFF2 (preserves all variation)
|
|
167
|
+
fontisan convert variable.ttf --to woff2 --output variable.woff2
|
|
168
|
+
|
|
169
|
+
# Variable OTF to WOFF2 (preserves all variation)
|
|
170
|
+
fontisan convert variable.otf --to woff2 --output variable.woff2
|
|
171
|
+
----
|
|
172
|
+
|
|
173
|
+
==== Convert between outline formats
|
|
174
|
+
|
|
175
|
+
[source,bash]
|
|
176
|
+
----
|
|
177
|
+
# Variable TTF to OTF (preserves common variation tables)
|
|
178
|
+
fontisan convert variable.ttf --to otf --output variable.otf
|
|
179
|
+
|
|
180
|
+
# Shows warning about gvar → CFF2 conversion not yet implemented
|
|
181
|
+
# Preserves: fvar, avar, STAT, HVAR, VVAR, MVAR
|
|
182
|
+
# Does not preserve: gvar (requires conversion to CFF2 blend operators)
|
|
183
|
+
----
|
|
184
|
+
|
|
185
|
+
==== Create static font from variable
|
|
186
|
+
|
|
187
|
+
[source,bash]
|
|
188
|
+
----
|
|
189
|
+
# Remove all variation data
|
|
190
|
+
fontisan convert variable.ttf --to ttf --output static.ttf --no-preserve-variation
|
|
191
|
+
|
|
192
|
+
# Creates static font at default variation coordinates
|
|
193
|
+
----
|
|
194
|
+
|
|
195
|
+
=== Using the Ruby API
|
|
196
|
+
|
|
197
|
+
==== Preserve variation during conversion
|
|
198
|
+
|
|
199
|
+
[source,ruby]
|
|
200
|
+
----
|
|
201
|
+
require 'fontisan'
|
|
202
|
+
|
|
203
|
+
# Load variable font
|
|
204
|
+
font = Fontisan::FontLoader.load('variable.ttf')
|
|
205
|
+
|
|
206
|
+
# Convert with variation preservation (default)
|
|
207
|
+
converter = Fontisan::Converters::FormatConverter.new
|
|
208
|
+
tables = converter.convert(font, :woff2, preserve_variation: true)
|
|
209
|
+
|
|
210
|
+
# Write output
|
|
211
|
+
Fontisan::FontWriter.write_to_file(tables, 'variable.woff2')
|
|
212
|
+
----
|
|
213
|
+
|
|
214
|
+
==== Create static font
|
|
215
|
+
|
|
216
|
+
[source,ruby]
|
|
217
|
+
----
|
|
218
|
+
# Convert without variation preservation
|
|
219
|
+
tables = converter.convert(font, :ttf, preserve_variation: false)
|
|
220
|
+
Fontisan::FontWriter.write_to_file(tables, 'static.ttf')
|
|
221
|
+
----
|
|
222
|
+
|
|
223
|
+
== SVG Generation from Variable Fonts
|
|
224
|
+
|
|
225
|
+
=== General
|
|
226
|
+
|
|
227
|
+
Generate SVG fonts from variable fonts at any point in the design space.
|
|
228
|
+
|
|
229
|
+
=== Using the CLI
|
|
230
|
+
|
|
231
|
+
==== Generate SVG at specific coordinates
|
|
232
|
+
|
|
233
|
+
[source,bash]
|
|
234
|
+
----
|
|
235
|
+
# At default coordinates
|
|
236
|
+
fontisan convert variable.ttf --to svg --output default.svg
|
|
237
|
+
|
|
238
|
+
# At specific weight
|
|
239
|
+
fontisan convert variable.ttf --to svg --output bold.svg --wght 700
|
|
240
|
+
|
|
241
|
+
# With multiple axes
|
|
242
|
+
fontisan convert variable.ttf --to svg --output custom.svg --wght 700 --wdth 75
|
|
243
|
+
----
|
|
244
|
+
|
|
245
|
+
==== Use named instance for SVG
|
|
246
|
+
|
|
247
|
+
[source,bash]
|
|
248
|
+
----
|
|
249
|
+
# Generate from named instance
|
|
250
|
+
fontisan convert variable.ttf --to svg --output bold.svg --instance-index 0
|
|
251
|
+
----
|
|
252
|
+
|
|
253
|
+
=== Using the Ruby API
|
|
254
|
+
|
|
255
|
+
==== Basic SVG generation
|
|
256
|
+
|
|
257
|
+
[source,ruby]
|
|
258
|
+
----
|
|
259
|
+
require 'fontisan'
|
|
260
|
+
|
|
261
|
+
# Load variable font
|
|
262
|
+
font = Fontisan::FontLoader.load('variable.ttf')
|
|
263
|
+
|
|
264
|
+
# Generate instance at specific coordinates
|
|
265
|
+
writer = Fontisan::Variation::InstanceWriter.new(font)
|
|
266
|
+
instance_font = writer.generate_instance(wght: 700)
|
|
267
|
+
|
|
268
|
+
# Convert to SVG
|
|
269
|
+
svg_generator = Fontisan::Variation::VariableSvgGenerator.new
|
|
270
|
+
svg_content = svg_generator.generate(instance_font)
|
|
271
|
+
|
|
272
|
+
# Write to file
|
|
273
|
+
File.write('bold.svg', svg_content)
|
|
274
|
+
----
|
|
275
|
+
|
|
276
|
+
== Collection Operations
|
|
277
|
+
|
|
278
|
+
=== General
|
|
279
|
+
|
|
280
|
+
Create variable font collections (TTC/OTC) with optimized table sharing.
|
|
281
|
+
|
|
282
|
+
=== Validation Requirements
|
|
283
|
+
|
|
284
|
+
All variable fonts in a collection must:
|
|
285
|
+
|
|
286
|
+
. Have the same variation type (all TrueType or all CFF2)
|
|
287
|
+
. Have the same variation axes (same tags, in same order)
|
|
288
|
+
. Have compatible axis ranges (min/max/default values)
|
|
289
|
+
|
|
290
|
+
=== Using the CLI
|
|
291
|
+
|
|
292
|
+
==== Create variable font collection
|
|
293
|
+
|
|
294
|
+
[source,bash]
|
|
295
|
+
----
|
|
296
|
+
# Merge variable TrueType fonts
|
|
297
|
+
fontisan pack var-regular.ttf var-bold.ttf var-italic.ttf --output family-var.ttc
|
|
298
|
+
|
|
299
|
+
# Merge variable OpenType fonts
|
|
300
|
+
fontisan pack var-regular.otf var-bold.otf --output family-var.otc --format otc
|
|
301
|
+
----
|
|
302
|
+
|
|
303
|
+
==== Extract from variable collection
|
|
304
|
+
|
|
305
|
+
[source,bash]
|
|
306
|
+
----
|
|
307
|
+
# Extract all fonts
|
|
308
|
+
fontisan unpack family-var.ttc --output-dir extracted/
|
|
309
|
+
|
|
310
|
+
# Extract specific font
|
|
311
|
+
fontisan unpack family-var.ttc --font-index 0 --output extracted/regular.ttf
|
|
312
|
+
----
|
|
313
|
+
|
|
314
|
+
=== Using the Ruby API
|
|
315
|
+
|
|
316
|
+
==== Build variable font collection
|
|
317
|
+
|
|
318
|
+
[source,ruby]
|
|
319
|
+
----
|
|
320
|
+
require 'fontisan'
|
|
321
|
+
|
|
322
|
+
# Load variable fonts
|
|
323
|
+
fonts = [
|
|
324
|
+
Fontisan::FontLoader.load('var-regular.ttf'),
|
|
325
|
+
Fontisan::FontLoader.load('var-bold.ttf'),
|
|
326
|
+
Fontisan::FontLoader.load('var-italic.ttf')
|
|
327
|
+
]
|
|
328
|
+
|
|
329
|
+
# Create collection
|
|
330
|
+
builder = Fontisan::Collection::Builder.new(fonts, format: :ttc)
|
|
331
|
+
builder.validate! # Validates variation compatibility
|
|
332
|
+
result = builder.build_to_file('family-var.ttc')
|
|
333
|
+
|
|
334
|
+
puts "Collection created: #{result[:output_path]}"
|
|
335
|
+
puts "Fonts: #{result[:num_fonts]}"
|
|
336
|
+
puts "Space saved: #{result[:space_savings]} bytes"
|
|
337
|
+
----
|
|
338
|
+
|
|
339
|
+
=== Table Sharing Strategy
|
|
340
|
+
|
|
341
|
+
Common variation tables (shared if identical)::
|
|
342
|
+
* fvar (Font Variations)
|
|
343
|
+
* avar (Axis Variations)
|
|
344
|
+
* STAT (Style Attributes)
|
|
345
|
+
* HVAR (Horizontal Metrics Variations)
|
|
346
|
+
* VVAR (Vertical Metrics Variations)
|
|
347
|
+
* MVAR (Metrics Variations)
|
|
348
|
+
|
|
349
|
+
Font-specific tables (always separate)::
|
|
350
|
+
* gvar (Glyph Variations - TrueType)
|
|
351
|
+
* CFF2 (with blend operators - OpenType)
|
|
352
|
+
|
|
353
|
+
== Validation
|
|
354
|
+
|
|
355
|
+
=== General
|
|
356
|
+
|
|
357
|
+
Validate variable font structure, axes, instances, and variation tables.
|
|
358
|
+
|
|
359
|
+
=== Using the CLI
|
|
360
|
+
|
|
361
|
+
==== Basic validation
|
|
362
|
+
|
|
363
|
+
[source,bash]
|
|
364
|
+
----
|
|
365
|
+
# Validate variable font
|
|
366
|
+
fontisan validate variable.ttf
|
|
367
|
+
|
|
368
|
+
# Verbose output
|
|
369
|
+
fontisan validate variable.ttf --verbose
|
|
370
|
+
----
|
|
371
|
+
|
|
372
|
+
=== Validation Checks
|
|
373
|
+
|
|
374
|
+
The validator performs the following checks:
|
|
375
|
+
|
|
376
|
+
==== fvar Table Structure
|
|
377
|
+
|
|
378
|
+
* Axes are defined
|
|
379
|
+
* Axis count matches actual number of axes
|
|
380
|
+
* Each axis has valid min/max/default values
|
|
381
|
+
* Axis tags are valid (4 ASCII letters)
|
|
382
|
+
|
|
383
|
+
==== Axis Validation
|
|
384
|
+
|
|
385
|
+
* Minimum value ≤ maximum value
|
|
386
|
+
* Default value within range [min, max]
|
|
387
|
+
* Axis tags follow naming conventions
|
|
388
|
+
|
|
389
|
+
==== Instance Validation
|
|
390
|
+
|
|
391
|
+
* Coordinate count matches axis count
|
|
392
|
+
* All coordinates within axis ranges
|
|
393
|
+
* Named instances have valid data
|
|
394
|
+
|
|
395
|
+
==== Variation Table Consistency
|
|
396
|
+
|
|
397
|
+
* TrueType variable fonts have gvar table
|
|
398
|
+
* CFF variable fonts have CFF2 table
|
|
399
|
+
* Font doesn't have both gvar and CFF2 (incompatible)
|
|
400
|
+
|
|
401
|
+
=== Using the Ruby API
|
|
402
|
+
|
|
403
|
+
==== Validate variable font
|
|
404
|
+
|
|
405
|
+
[source,ruby]
|
|
406
|
+
----
|
|
407
|
+
require 'fontisan'
|
|
408
|
+
require 'fontisan/validation/variable_font_validator'
|
|
409
|
+
|
|
410
|
+
# Load font
|
|
411
|
+
font = Fontisan::FontLoader.load('variable.ttf')
|
|
412
|
+
|
|
413
|
+
# Validate
|
|
414
|
+
validator = Fontisan::Validation::VariableFontValidator.new(font)
|
|
415
|
+
errors = validator.validate
|
|
416
|
+
|
|
417
|
+
if errors.empty?
|
|
418
|
+
puts "✓ Variable font is valid"
|
|
419
|
+
else
|
|
420
|
+
puts "✗ Found #{errors.length} errors:"
|
|
421
|
+
errors.each { |err| puts " - #{err}" }
|
|
422
|
+
end
|
|
423
|
+
----
|
|
424
|
+
|
|
425
|
+
== Performance Considerations
|
|
426
|
+
|
|
427
|
+
=== Instance Generation
|
|
428
|
+
|
|
429
|
+
* Instance generation creates a new static font with updated tables
|
|
430
|
+
* Performance: ~100-500ms for typical variable fonts
|
|
431
|
+
* Memory: Proportional to font size (usually 1-5 MB)
|
|
432
|
+
|
|
433
|
+
=== Format Conversion
|
|
434
|
+
|
|
435
|
+
* Variation-preserving conversions are fast (table copying)
|
|
436
|
+
* Outline conversion (TTF ↔ OTF) is slower (glyph processing)
|
|
437
|
+
* Optimization: Use batch processing for multiple conversions
|
|
438
|
+
|
|
439
|
+
=== Collection Building
|
|
440
|
+
|
|
441
|
+
* Table deduplication provides significant space savings
|
|
442
|
+
* Analysis overhead: ~50-200ms per font
|
|
443
|
+
* Recommended for font families with 3+ fonts
|
|
444
|
+
|
|
445
|
+
== Troubleshooting
|
|
446
|
+
|
|
447
|
+
=== Common Issues
|
|
448
|
+
|
|
449
|
+
==== Error: Cannot mix TrueType and CFF2 variable fonts
|
|
450
|
+
|
|
451
|
+
*Cause*: Attempting to create collection with mixed variation types
|
|
452
|
+
|
|
453
|
+
*Solution*: Separate fonts into different collections:
|
|
454
|
+
|
|
455
|
+
[source,bash]
|
|
456
|
+
----
|
|
457
|
+
# Create separate collections
|
|
458
|
+
fontisan pack var-ttf1.ttf var-ttf2.ttf --output ttf-collection.ttc
|
|
459
|
+
fontisan pack var-otf1.otf var-otf2.otf --output otf-collection.otc
|
|
460
|
+
----
|
|
461
|
+
|
|
462
|
+
==== Error: Variable fonts have different axes
|
|
463
|
+
|
|
464
|
+
*Cause*: Fonts have incompatible axis definitions
|
|
465
|
+
|
|
466
|
+
*Solution*: Verify all fonts have same axes:
|
|
467
|
+
|
|
468
|
+
[source,bash]
|
|
469
|
+
----
|
|
470
|
+
# Check axes for each font
|
|
471
|
+
fontisan variable font1.ttf
|
|
472
|
+
fontisan variable font2.ttf
|
|
473
|
+
|
|
474
|
+
# Ensure axes match: same tags, same order
|
|
475
|
+
----
|
|
476
|
+
|
|
477
|
+
==== Warning: Full variation conversion not yet implemented
|
|
478
|
+
|
|
479
|
+
*Cause*: Converting between TTF and OTF outline formats
|
|
480
|
+
|
|
481
|
+
*Impact*: Common variation tables preserved, but gvar/CFF2 not converted
|
|
482
|
+
|
|
483
|
+
*Workaround*: Generate instances instead of converting:
|
|
484
|
+
|
|
485
|
+
[source,bash]
|
|
486
|
+
----
|
|
487
|
+
# Instead of: fontisan convert var.ttf --to otf
|
|
488
|
+
# Use: fontisan instance var.ttf --wght 400 --to otf --output regular.otf
|
|
489
|
+
----
|
|
490
|
+
|
|
491
|
+
==== Instance generation produces unexpected results
|
|
492
|
+
|
|
493
|
+
*Cause*: Invalid axis coordinates
|
|
494
|
+
|
|
495
|
+
*Solution*: Check valid axis ranges:
|
|
496
|
+
|
|
497
|
+
[source,bash]
|
|
498
|
+
----
|
|
499
|
+
# List available axes and ranges
|
|
500
|
+
fontisan variable font.ttf
|
|
501
|
+
|
|
502
|
+
# Use coordinates within valid ranges
|
|
503
|
+
fontisan instance font.ttf --wght 600 # If wght range is 400-900
|
|
504
|
+
----
|
|
505
|
+
|
|
506
|
+
== Best Practices
|
|
507
|
+
|
|
508
|
+
=== Instance Generation
|
|
509
|
+
|
|
510
|
+
. List named instances before generating custom coordinates
|
|
511
|
+
. Validate coordinates are within axis ranges
|
|
512
|
+
. Use named instances when available for better compatibility
|
|
513
|
+
. Test generated instances before deployment
|
|
514
|
+
|
|
515
|
+
=== Format Conversion
|
|
516
|
+
|
|
517
|
+
. Always validate fonts after conversion
|
|
518
|
+
. Use `--preserve-variation` explicitly for clarity
|
|
519
|
+
. Check warning messages for conversion limitations
|
|
520
|
+
. Test converted fonts in target environment
|
|
521
|
+
|
|
522
|
+
=== Collection Building
|
|
523
|
+
|
|
524
|
+
. Validate individual fonts before packing
|
|
525
|
+
. Use `--analyze` flag to preview space savings
|
|
526
|
+
. Keep font variations consistent (same axes, ranges)
|
|
527
|
+
. Document shared vs. font-specific tables
|
|
528
|
+
|
|
529
|
+
=== Validation
|
|
530
|
+
|
|
531
|
+
. Run validation before distribution
|
|
532
|
+
. Use `--verbose` mode during development
|
|
533
|
+
. Fix errors before warnings
|
|
534
|
+
. Validate after each operation (conversion, instance generation)
|
|
535
|
+
|
|
536
|
+
== API Reference
|
|
537
|
+
|
|
538
|
+
=== InstanceWriter
|
|
539
|
+
|
|
540
|
+
[source,ruby]
|
|
541
|
+
----
|
|
542
|
+
# Initialize with variable font
|
|
543
|
+
writer = Fontisan::Variation::InstanceWriter.new(font)
|
|
544
|
+
|
|
545
|
+
# Generate instance
|
|
546
|
+
instance = writer.generate_instance(axis_values)
|
|
547
|
+
# axis_values: Hash of axis_tag => value
|
|
548
|
+
|
|
549
|
+
# Returns: Font object with variation tables removed
|
|
550
|
+
----
|
|
551
|
+
|
|
552
|
+
=== VariableSvgGenerator
|
|
553
|
+
|
|
554
|
+
[source,ruby]
|
|
555
|
+
----
|
|
556
|
+
# Initialize
|
|
557
|
+
generator = Fontisan::Variation::VariableSvgGenerator.new
|
|
558
|
+
|
|
559
|
+
# Generate SVG from font
|
|
560
|
+
svg_content = generator.generate(font)
|
|
561
|
+
# font: Can be variable or static font
|
|
562
|
+
# Returns: String containing SVG XML
|
|
563
|
+
----
|
|
564
|
+
|
|
565
|
+
=== VariableFontValidator
|
|
566
|
+
|
|
567
|
+
[source,ruby]
|
|
568
|
+
----
|
|
569
|
+
# Initialize with font
|
|
570
|
+
validator = Fontisan::Validation::VariableFontValidator.new(font)
|
|
571
|
+
|
|
572
|
+
# Validate
|
|
573
|
+
errors = validator.validate
|
|
574
|
+
# Returns: Array of error message strings
|
|
575
|
+
----
|
|
576
|
+
|
|
577
|
+
=== Collection::Builder
|
|
578
|
+
|
|
579
|
+
[source,ruby]
|
|
580
|
+
----
|
|
581
|
+
# Initialize with fonts
|
|
582
|
+
builder = Fontisan::Collection::Builder.new(fonts, options)
|
|
583
|
+
# options:
|
|
584
|
+
# format: :ttc or :otc
|
|
585
|
+
# optimize: true/false
|
|
586
|
+
|
|
587
|
+
# Validate before building
|
|
588
|
+
builder.validate!
|
|
589
|
+
|
|
590
|
+
# Build collection
|
|
591
|
+
result = builder.build_to_file(output_path)
|
|
592
|
+
# Returns: Hash with :binary, :space_savings, :statistics
|
|
593
|
+
----
|
|
594
|
+
|
|
595
|
+
== See Also
|
|
596
|
+
|
|
597
|
+
* link:VARIABLE_FONTS_ARCHITECTURE.md[Variable Fonts Architecture]
|
|
598
|
+
* link:VARIABLE_FONT_GUIDE.md[Variable Font Development Guide]
|
|
599
|
+
* link:../README.adoc[Fontisan README]
|