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.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +56 -196
  3. data/Gemfile +1 -1
  4. data/docs/.gitignore +17 -0
  5. data/docs/.vitepress/config.ts +317 -0
  6. data/docs/.vitepress/theme/components/ApiMethod.vue +127 -0
  7. data/docs/.vitepress/theme/components/Badge.vue +51 -0
  8. data/docs/.vitepress/theme/components/FeatureComparison.vue +87 -0
  9. data/docs/.vitepress/theme/components/HeroCodeBlock.vue +98 -0
  10. data/docs/.vitepress/theme/components/WithinHero.vue +30 -0
  11. data/docs/.vitepress/theme/index.ts +22 -0
  12. data/docs/.vitepress/theme/style.css +330 -0
  13. data/docs/api/conversion-options.md +141 -0
  14. data/docs/api/converters/curve-converter.md +34 -0
  15. data/docs/api/converters/hint-converter.md +34 -0
  16. data/docs/api/converters/outline-converter.md +27 -0
  17. data/docs/api/font-loader.md +111 -0
  18. data/docs/api/font-writer.md +103 -0
  19. data/docs/api/index.md +79 -0
  20. data/docs/api/models/glyph-accessor.md +43 -0
  21. data/docs/api/models/glyph.md +40 -0
  22. data/docs/api/models/table-analyzer.md +35 -0
  23. data/docs/api/sfnt-font.md +53 -0
  24. data/docs/api/type1-font.md +43 -0
  25. data/docs/api/validators/font-validator.md +31 -0
  26. data/docs/api/validators/helper.md +36 -0
  27. data/docs/api/validators/profile.md +39 -0
  28. data/docs/cli/convert.md +87 -0
  29. data/docs/cli/dump-table.md +110 -0
  30. data/docs/cli/export.md +176 -0
  31. data/docs/cli/features.md +124 -0
  32. data/docs/cli/glyphs.md +90 -0
  33. data/docs/cli/index.md +208 -0
  34. data/docs/cli/info.md +254 -0
  35. data/docs/cli/instance.md +122 -0
  36. data/docs/cli/ls.md +95 -0
  37. data/docs/cli/optical-size.md +94 -0
  38. data/docs/cli/pack.md +125 -0
  39. data/docs/cli/scripts.md +105 -0
  40. data/docs/cli/subset.md +39 -0
  41. data/docs/cli/tables.md +84 -0
  42. data/docs/cli/unicode.md +101 -0
  43. data/docs/cli/validate.md +48 -0
  44. data/docs/cli/variable.md +126 -0
  45. data/docs/cli/version.md +46 -0
  46. data/docs/guide/cli/convert.md +108 -0
  47. data/docs/guide/cli/export.md +138 -0
  48. data/docs/guide/cli/index.md +99 -0
  49. data/docs/guide/cli/info.md +144 -0
  50. data/docs/guide/cli/pack.md +155 -0
  51. data/docs/guide/cli/subset.md +118 -0
  52. data/docs/guide/cli/validate.md +139 -0
  53. data/docs/guide/color-fonts/bitmaps.md +177 -0
  54. data/docs/guide/color-fonts/colr-cpal.md +175 -0
  55. data/docs/guide/color-fonts/index.md +140 -0
  56. data/docs/guide/color-fonts/svg.md +154 -0
  57. data/docs/guide/color.md +51 -0
  58. data/docs/guide/comparisons/font-validator.md +222 -0
  59. data/docs/guide/comparisons/fonttools.md +200 -0
  60. data/docs/guide/comparisons/index.md +83 -0
  61. data/docs/guide/comparisons/lcdf-typetools.md +205 -0
  62. data/docs/guide/contributing.md +279 -0
  63. data/docs/guide/conversion/collections.md +251 -0
  64. data/docs/guide/conversion/curves.md +246 -0
  65. data/docs/guide/conversion/index.md +157 -0
  66. data/docs/guide/conversion/options.md +251 -0
  67. data/docs/guide/conversion/ttf-otf.md +184 -0
  68. data/docs/guide/conversion/type1.md +208 -0
  69. data/docs/guide/conversion/web.md +240 -0
  70. data/docs/guide/conversion.md +39 -0
  71. data/docs/guide/formats/collections.md +147 -0
  72. data/docs/guide/formats/dfont.md +99 -0
  73. data/docs/guide/formats/index.md +65 -0
  74. data/docs/guide/formats/otf.md +103 -0
  75. data/docs/guide/formats/svg.md +97 -0
  76. data/docs/guide/formats/ttf.md +105 -0
  77. data/docs/guide/formats/type1.md +118 -0
  78. data/docs/guide/formats/woff.md +115 -0
  79. data/docs/guide/hinting/autohint.md +141 -0
  80. data/docs/guide/hinting/conversion.md +161 -0
  81. data/docs/guide/hinting/index.md +86 -0
  82. data/docs/guide/hinting/postscript.md +149 -0
  83. data/docs/guide/hinting/truetype.md +135 -0
  84. data/docs/guide/hinting.md +44 -0
  85. data/docs/guide/index.md +152 -0
  86. data/docs/guide/installation.md +116 -0
  87. data/docs/guide/migrations/extract-ttc.md +549 -0
  88. data/docs/guide/migrations/font-validator.md +260 -0
  89. data/docs/guide/migrations/fonttools.md +208 -0
  90. data/docs/guide/migrations/index.md +64 -0
  91. data/docs/guide/migrations/otfinfo.md +197 -0
  92. data/docs/guide/quick-start.md +204 -0
  93. data/docs/guide/type1.md +58 -0
  94. data/docs/guide/universal-outline.md +151 -0
  95. data/docs/guide/validation/custom.md +195 -0
  96. data/docs/guide/validation/helpers.md +188 -0
  97. data/docs/guide/validation/index.md +132 -0
  98. data/docs/guide/validation/profiles.md +156 -0
  99. data/docs/guide/validation.md +47 -0
  100. data/docs/guide/variable-fonts/advanced.md +231 -0
  101. data/docs/guide/variable-fonts/axes.md +209 -0
  102. data/docs/guide/variable-fonts/conversion.md +197 -0
  103. data/docs/guide/variable-fonts/index.md +84 -0
  104. data/docs/guide/variable-fonts/instances.md +187 -0
  105. data/docs/guide/variable-fonts/named-instances.md +194 -0
  106. data/docs/guide/variable-fonts/static.md +168 -0
  107. data/docs/guide/variable.md +58 -0
  108. data/docs/guide/woff.md +59 -0
  109. data/docs/index.md +136 -0
  110. data/docs/lychee.toml +37 -0
  111. data/docs/package-lock.json +2560 -0
  112. data/docs/package.json +15 -0
  113. data/docs/public/apple-touch-icon.png +0 -0
  114. data/docs/public/favicon-96x96.png +0 -0
  115. data/docs/public/favicon.ico +0 -0
  116. data/docs/public/favicon.svg +1 -0
  117. data/docs/public/logo-full.svg +1 -0
  118. data/docs/public/logo.svg +1 -0
  119. data/docs/public/site.webmanifest +21 -0
  120. data/docs/public/web-app-manifest-192x192.png +0 -0
  121. data/docs/public/web-app-manifest-512x512.png +0 -0
  122. data/fontisan.gemspec +1 -1
  123. data/lib/fontisan/commands/features_command.rb +0 -1
  124. data/lib/fontisan/commands/info_command.rb +5 -5
  125. data/lib/fontisan/commands/scripts_command.rb +0 -1
  126. data/lib/fontisan/converters/format_converter.rb +2 -1
  127. data/lib/fontisan/converters/type1_converter.rb +65 -60
  128. data/lib/fontisan/hints/hint_converter.rb +2 -1
  129. data/lib/fontisan/loading_modes.rb +0 -2
  130. data/lib/fontisan/open_type_font.rb +0 -40
  131. data/lib/fontisan/sfnt_font.rb +41 -22
  132. data/lib/fontisan/tables/glyf/compound_glyph.rb +0 -1
  133. data/lib/fontisan/true_type_collection.rb +8 -8
  134. data/lib/fontisan/true_type_font.rb +1 -59
  135. data/lib/fontisan/type1/afm_parser.rb +2 -1
  136. data/lib/fontisan/type1/cff_to_type1_converter.rb +24 -19
  137. data/lib/fontisan/type1/private_dict.rb +28 -7
  138. data/lib/fontisan/type1/seac_expander.rb +22 -17
  139. data/lib/fontisan/variable/delta_applicator.rb +3 -3
  140. data/lib/fontisan/variation/optimizer.rb +0 -1
  141. data/lib/fontisan/version.rb +1 -1
  142. data/lib/fontisan/woff2_font.rb +2 -2
  143. data/lib/fontisan/woff_font.rb +3 -3
  144. data/lib/fontisan.rb +3 -2
  145. 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
+ ```