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,187 @@
1
+ ---
2
+ title: Instance Generation
3
+ ---
4
+
5
+ # Instance Generation
6
+
7
+ Generate static font instances from variable fonts at specific variation coordinates.
8
+
9
+ ## CLI Usage
10
+
11
+ ### Basic Instance Generation
12
+
13
+ ```bash
14
+ # Generate at specific weight
15
+ fontisan instance variable.ttf --wght 700 --output bold.ttf
16
+
17
+ # Generate at specific weight and width
18
+ fontisan instance variable.ttf --wght 700 --wdth 75 --output condensed-bold.ttf
19
+
20
+ # Generate with all axes
21
+ fontisan instance variable.ttf --wght 600 --wdth 90 --slnt -12 --output custom.ttf
22
+ ```
23
+
24
+ ### Using Named Instances
25
+
26
+ ```bash
27
+ # List available instances
28
+ fontisan instance variable.ttf --list-instances
29
+
30
+ # Generate using instance index
31
+ fontisan instance variable.ttf --named-instance 0 --output thin.ttf
32
+
33
+ # Generate using instance name
34
+ fontisan instance variable.ttf --named-instance "Bold" --output bold.ttf
35
+ ```
36
+
37
+ ### Output Format
38
+
39
+ ```bash
40
+ # Generate as TrueType (default)
41
+ fontisan instance variable.ttf --wght 700 --output bold.ttf
42
+
43
+ # Generate as OpenType/CFF
44
+ fontisan instance variable.ttf --wght 700 --to otf --output bold.otf
45
+
46
+ # Generate as WOFF2
47
+ fontisan instance.ttf --wght 700 --to woff2 --output bold.woff2
48
+ ```
49
+
50
+ ## Ruby API
51
+
52
+ ### Basic Generation
53
+
54
+ ```ruby
55
+ require 'fontisan'
56
+
57
+ # Load variable font
58
+ font = Fontisan::FontLoader.load('variable.ttf')
59
+
60
+ # Generate instance at specific coordinates
61
+ writer = Fontisan::Variation::InstanceWriter.new(font)
62
+ instance_font = writer.generate_instance(wght: 700)
63
+
64
+ # Write to file
65
+ Fontisan::FontWriter.write(
66
+ instance_font,
67
+ 'bold.ttf'
68
+ )
69
+ ```
70
+
71
+ ### Multiple Axes
72
+
73
+ ```ruby
74
+ # Generate instance with multiple axis values
75
+ instance_font = writer.generate_instance(
76
+ wght: 700,
77
+ wdth: 75,
78
+ slnt: -8
79
+ )
80
+ ```
81
+
82
+ ### Using Named Instances
83
+
84
+ ```ruby
85
+ # Get named instance information
86
+ fvar = font.tables['fvar']
87
+ instance = fvar.instances[0]
88
+
89
+ # Extract coordinates from named instance
90
+ coordinates = {}
91
+ fvar.axes.each_with_index do |axis, i|
92
+ coordinates[axis.tag] = instance.coordinates[i]
93
+ end
94
+
95
+ # Generate instance
96
+ instance_font = writer.generate_instance(coordinates)
97
+ ```
98
+
99
+ ## Batch Generation
100
+
101
+ ### Generate All Named Instances
102
+
103
+ ```ruby
104
+ font = Fontisan::FontLoader.load('variable.ttf')
105
+ fvar = font.tables['fvar']
106
+ writer = Fontisan::Variation::InstanceWriter.new(font)
107
+
108
+ fvar.instances.each do |instance|
109
+ # Build coordinates hash
110
+ coordinates = {}
111
+ fvar.axes.each_with_index do |axis, i|
112
+ coordinates[axis.tag] = instance.coordinates[i]
113
+ end
114
+
115
+ # Generate instance
116
+ instance_font = writer.generate_instance(coordinates)
117
+
118
+ # Write with instance name
119
+ filename = instance.name.downcase.gsub(/\s+/, '-') + '.ttf'
120
+ Fontisan::FontWriter.write(instance_font, filename)
121
+ end
122
+ ```
123
+
124
+ ### Generate Weight Range
125
+
126
+ ```ruby
127
+ # Generate instances for common weights
128
+ weights = {
129
+ 'thin' => 100,
130
+ 'light' => 300,
131
+ 'regular' => 400,
132
+ 'medium' => 500,
133
+ 'semibold' => 600,
134
+ 'bold' => 700,
135
+ 'black' => 900
136
+ }
137
+
138
+ weights.each do |name, wght|
139
+ instance_font = writer.generate_instance(wght: wght)
140
+ Fontisan::FontWriter.write(instance_font, "#{name}.ttf")
141
+ end
142
+ ```
143
+
144
+ ## Instance Quality
145
+
146
+ ### Coordinate Validation
147
+
148
+ Coordinates must be within axis ranges:
149
+
150
+ ```ruby
151
+ fvar = font.tables['fvar']
152
+
153
+ fvar.axes.each do |axis|
154
+ min = axis.min_value
155
+ max = axis.max_value
156
+
157
+ if value < min || value > max
158
+ raise "Value #{value} out of range for #{axis.tag}"
159
+ end
160
+ end
161
+ ```
162
+
163
+ ### Default Values
164
+
165
+ When an axis is not specified, the default value is used:
166
+
167
+ ```ruby
168
+ # If wght is not specified, uses default from fvar
169
+ instance_font = writer.generate_instance(wdth: 75)
170
+ ```
171
+
172
+ ## Performance
173
+
174
+ ### Caching
175
+
176
+ Fontisan caches intermediate computations for faster instance generation:
177
+
178
+ ```ruby
179
+ # Create writer once
180
+ writer = Fontisan::Variation::InstanceWriter.new(font)
181
+
182
+ # Generate multiple instances efficiently
183
+ (100..900).step(100).each do |wght|
184
+ instance_font = writer.generate_instance(wght: wght)
185
+ # ...
186
+ end
187
+ ```
@@ -0,0 +1,194 @@
1
+ ---
2
+ title: Named Instances
3
+ ---
4
+
5
+ # Named Instances
6
+
7
+ Work with named instances in variable fonts.
8
+
9
+ ## Overview
10
+
11
+ Named instances are predefined points in the design space with specific names. They provide:
12
+ - User-friendly names for common variations
13
+ - Consistent styling across applications
14
+ - Easy access to frequently used configurations
15
+
16
+ ## Listing Named Instances
17
+
18
+ ### CLI
19
+
20
+ ```bash
21
+ fontisan instance variable.ttf --list-instances
22
+
23
+ # Example output:
24
+ # Named Instances: 6
25
+ # 0: Thin (wght=100)
26
+ # 1: Light (wght=300)
27
+ # 2: Regular (wght=400)
28
+ # 3: Medium (wght=500)
29
+ # 4: Bold (wght=700)
30
+ # 5: Black (wght=900)
31
+ ```
32
+
33
+ ### API
34
+
35
+ ```ruby
36
+ font = Fontisan::FontLoader.load('variable.ttf')
37
+ fvar = font.tables['fvar']
38
+
39
+ fvar.instances.each_with_index do |instance, index|
40
+ puts "#{index}: #{instance.name}"
41
+ instance.coordinates.each do |tag, value|
42
+ puts " #{tag} = #{value}"
43
+ end
44
+ end
45
+ ```
46
+
47
+ ## Instance Properties
48
+
49
+ | Property | Description |
50
+ |----------|-------------|
51
+ | `name` | Human-readable instance name |
52
+ | `coordinates` | Hash of axis tag → value |
53
+
54
+ ## Generating Named Instances
55
+
56
+ ### By Index
57
+
58
+ ```bash
59
+ # Generate first named instance
60
+ fontisan instance variable.ttf --named-instance 0 --output thin.ttf
61
+
62
+ # Generate third named instance
63
+ fontisan instance variable.ttf --named-instance 2 --output regular.ttf
64
+ ```
65
+
66
+ ### By Name
67
+
68
+ ```bash
69
+ # Generate by name
70
+ fontisan instance variable.ttf --named-instance "Bold" --output bold.ttf
71
+ ```
72
+
73
+ ### API
74
+
75
+ ```ruby
76
+ font = Fontisan::FontLoader.load('variable.ttf')
77
+ fvar = font.tables['fvar']
78
+ writer = Fontisan::Variation::InstanceWriter.new(font)
79
+
80
+ # Find instance by name
81
+ bold_instance = fvar.instances.find { |i| i.name == "Bold" }
82
+
83
+ if bold_instance
84
+ # Build coordinates hash
85
+ coordinates = {}
86
+ fvar.axes.each_with_index do |axis, i|
87
+ coordinates[axis.tag] = bold_instance.coordinates[i]
88
+ end
89
+
90
+ # Generate instance
91
+ bold_font = writer.generate_instance(coordinates)
92
+ Fontisan::FontWriter.write(bold_font, 'bold.ttf')
93
+ end
94
+ ```
95
+
96
+ ## Generating All Named Instances
97
+
98
+ ```ruby
99
+ font = Fontisan::FontLoader.load('variable.ttf')
100
+ fvar = font.tables['fvar']
101
+ writer = Fontisan::Variation::InstanceWriter.new(font)
102
+
103
+ fvar.instances.each do |instance|
104
+ # Build coordinates
105
+ coordinates = {}
106
+ fvar.axes.each_with_index do |axis, i|
107
+ coordinates[axis.tag] = instance.coordinates[i]
108
+ end
109
+
110
+ # Generate instance
111
+ instance_font = writer.generate_instance(coordinates)
112
+
113
+ # Create safe filename
114
+ filename = instance.name.downcase.gsub(/[^a-z0-9]+, '-') + '.ttf'
115
+
116
+ Fontisan::FontWriter.write(instance_font, filename)
117
+ puts "Generated: #{filename}"
118
+ end
119
+ ```
120
+
121
+ ## Instance Names
122
+
123
+ Instance names follow conventions:
124
+
125
+ ### Weight-Based
126
+
127
+ - Thin
128
+ - Extra Light
129
+ - Light
130
+ - Regular
131
+ - Medium
132
+ - Semi Bold
133
+ - Bold
134
+ - Extra Bold
135
+ - Black
136
+
137
+ ### Width-Based
138
+
139
+ - Ultra Condensed
140
+ - Extra Condensed
141
+ - Condensed
142
+ - Semi Condensed
143
+ - Normal
144
+ - Semi Expanded
145
+ - Expanded
146
+ - Extra Expanded
147
+ - Ultra Expanded
148
+
149
+ ### Combinations
150
+
151
+ - Condensed Bold
152
+ - Expanded Light
153
+ - Semi Condensed Medium
154
+
155
+ ## STAT Table
156
+
157
+ The STAT table provides additional instance information:
158
+
159
+ ```ruby
160
+ stat = font.tables['STAT']
161
+
162
+ if stat
163
+ stat.axis_values.each do |axis_value|
164
+ puts "#{axis_value.name}"
165
+ puts " Axis: #{axis_value.axis_tag}"
166
+ puts " Value: #{axis_value.value}"
167
+ puts " Linked: #{axis_value.linked_value}" if axis_value.linked_value
168
+ end
169
+ end
170
+ ```
171
+
172
+ ## Instance Validation
173
+
174
+ Validate instances before generation:
175
+
176
+ ```ruby
177
+ fvar = font.tables['fvar']
178
+
179
+ fvar.instances.each do |instance|
180
+ valid = true
181
+
182
+ fvar.axes.each_with_index do |axis, i|
183
+ value = instance.coordinates[i]
184
+ if value < axis.min_value || value > axis.max_value
185
+ puts "Warning: #{instance.name} has #{axis.tag}=#{value} out of range"
186
+ valid = false
187
+ end
188
+ end
189
+
190
+ if valid
191
+ puts "#{instance.name}: Valid"
192
+ end
193
+ end
194
+ ```
@@ -0,0 +1,168 @@
1
+ ---
2
+ title: Static Fonts
3
+ ---
4
+
5
+ # Static Fonts
6
+
7
+ Convert variable fonts to static fonts.
8
+
9
+ ## Overview
10
+
11
+ Static fonts are traditional fonts with fixed designs. Converting a variable font to static creates a snapshot at specific coordinates.
12
+
13
+ ## When to Use Static Fonts
14
+
15
+ - **Legacy Compatibility** — Older systems that don't support variable fonts
16
+ - **Performance** — Smaller file size for single weights
17
+ - **Simplicity** — No need for variation handling
18
+ - **Distribution** — Some platforms don't support variable fonts
19
+
20
+ ## Creating Static Fonts
21
+
22
+ ### At Default Coordinates
23
+
24
+ ```bash
25
+ # Create static font at default variation
26
+ fontisan convert variable.ttf --to ttf --output static.ttf
27
+ ```
28
+
29
+ ### At Specific Coordinates
30
+
31
+ ```bash
32
+ # Create static bold font
33
+ fontisan instance variable.ttf --wght 700 --output bold-static.ttf
34
+ ```
35
+
36
+ ### Remove Variation Data
37
+
38
+ ```bash
39
+ # Explicitly remove variation tables
40
+ fontisan convert variable.ttf --to ttf --no-preserve-variation --output static.ttf
41
+ ```
42
+
43
+ ## API Usage
44
+
45
+ ### Default Coordinates
46
+
47
+ ```ruby
48
+ font = Fontisan::FontLoader.load('variable.ttf')
49
+
50
+ # Generate at default (empty hash = all default values)
51
+ writer = Fontisan::Variation::InstanceWriter.new(font)
52
+ static_font = writer.generate_instance({})
53
+
54
+ Fontisan::FontWriter.write(static_font, 'static.ttf')
55
+ ```
56
+
57
+ ### Specific Coordinates
58
+
59
+ ```ruby
60
+ # Generate static at specific coordinates
61
+ static_bold = writer.generate_instance(wght: 700)
62
+ Fontisan::FontWriter.write(static_bold, 'bold-static.ttf')
63
+
64
+ # Generate static with multiple axes
65
+ static_condensed_bold = writer.generate_instance(wght: 700, wdth: 75)
66
+ Fontisan::FontWriter.write(static_condensed_bold, 'condensed-bold.ttf')
67
+ ```
68
+
69
+ ## What Gets Removed
70
+
71
+ When creating a static font, these tables are removed:
72
+
73
+ | Table | Status |
74
+ |-------|--------|
75
+ | fvar | Removed |
76
+ | gvar | Removed |
77
+ | CFF2 | Converted to CFF |
78
+ | avar | Removed |
79
+ | HVAR | Applied to hmtx, removed |
80
+ | VVAR | Applied to vmtx, removed |
81
+ | MVAR | Applied to tables, removed |
82
+
83
+ ## What Gets Generated
84
+
85
+ The static font contains:
86
+
87
+ | Table | Source |
88
+ |-------|--------|
89
+ | glyf | Interpolated from gvar |
90
+ | CFF | Interpolated from CFF2 |
91
+ | hmtx | Interpolated from HVAR |
92
+ | vmtx | Interpolated from VVAR |
93
+ | Other tables | Copied unchanged |
94
+
95
+ ## Batch Generation
96
+
97
+ ### Generate Weight Range
98
+
99
+ ```ruby
100
+ font = Fontisan::FontLoader.load('variable.ttf')
101
+ writer = Fontisan::Variation::InstanceWriter.new(font)
102
+
103
+ weights = {
104
+ 'thin' => 100,
105
+ 'light' => 300,
106
+ 'regular' => 400,
107
+ 'medium' => 500,
108
+ 'semibold' => 600,
109
+ 'bold' => 700,
110
+ 'extrabold' => 800,
111
+ 'black' => 900
112
+ }
113
+
114
+ weights.each do |name, wght|
115
+ static_font = writer.generate_instance(wght: wght)
116
+
117
+ # Also convert to WOFF2 for web
118
+ options = Fontisan::ConversionOptions.from_preset(:web_optimized)
119
+ Fontisan::FontWriter.write(static_font, "#{name}.woff2", options: options)
120
+ end
121
+ ```
122
+
123
+ ### Generate 2D Grid
124
+
125
+ ```ruby
126
+ # Generate grid of weight × width combinations
127
+ weights = [100, 400, 700]
128
+ widths = [75, 100, 125]
129
+
130
+ weights.each do |wght|
131
+ widths.each do |wdth|
132
+ static_font = writer.generate_instance(wght: wght, wdth: wdth)
133
+ filename = "wght-#{wght}-wdth-#{wdth}.ttf"
134
+ Fontisan::FontWriter.write(static_font, filename)
135
+ end
136
+ end
137
+ ```
138
+
139
+ ## File Size Comparison
140
+
141
+ | Font Type | Typical Size |
142
+ |-----------|--------------|
143
+ | Variable font | 100-300 KB |
144
+ | Static (single) | 20-50 KB |
145
+ | Static (9 weights) | 180-450 KB |
146
+
147
+ Variable fonts are smaller when you need multiple variations. Static fonts are smaller for single variations.
148
+
149
+ ## Quality Considerations
150
+
151
+ ### Interpolation Accuracy
152
+
153
+ Static fonts are mathematically precise interpolations:
154
+
155
+ ```ruby
156
+ # The interpolated outlines match the variable font exactly
157
+ # at the specified coordinates
158
+ ```
159
+
160
+ ### Hinting
161
+
162
+ - TrueType hints are interpolated along with outlines
163
+ - Some hint instructions may not apply to new coordinates
164
+ - Consider re-hinting after generation
165
+
166
+ ### Subpixel Rendering
167
+
168
+ Static fonts may render differently than variable fonts at the same coordinates due to hinting differences.
@@ -0,0 +1,58 @@
1
+ # Variable Fonts
2
+
3
+ Fontisan provides tools for working with OpenType variable fonts.
4
+
5
+ ## Overview
6
+
7
+ Variable fonts (OpenType Font Variations) allow a single font file to contain multiple variations along design axes (weight, width, slant, etc.).
8
+
9
+ ## Reading Variable Fonts
10
+
11
+ ```ruby
12
+ require 'fontisan'
13
+
14
+ # Load a variable font
15
+ font = Fontisan.load('variable-font.ttf')
16
+
17
+ # Check if font is variable
18
+ if font.variable?
19
+ puts "This is a variable font"
20
+ end
21
+ ```
22
+
23
+ ## Font Axes
24
+
25
+ ```ruby
26
+ # Get available axes
27
+ font.axes.each do |axis|
28
+ puts "#{axis.tag}: #{axis.name}"
29
+ puts " Range: #{axis.min} - #{axis.max}"
30
+ puts " Default: #{axis.default}"
31
+ end
32
+ ```
33
+
34
+ ## Named Instances
35
+
36
+ ```ruby
37
+ # Get named instances
38
+ font.named_instances.each do |instance|
39
+ puts "#{instance.name}:"
40
+ instance.coordinates.each do |tag, value|
41
+ puts " #{tag}: #{value}"
42
+ end
43
+ end
44
+ ```
45
+
46
+ ## Working with Variations
47
+
48
+ ```ruby
49
+ # Create a variation
50
+ variation = font.variation('wght' => 700, 'wdth' => 100)
51
+
52
+ # Export as static font
53
+ variation.export('font-bold.ttf')
54
+ ```
55
+
56
+ ## Related
57
+
58
+ - [Color Fonts](/guide/color) - Color font support
@@ -0,0 +1,59 @@
1
+ # WOFF/WOFF2 Formats
2
+
3
+ Fontisan supports the Web Open Font Format (WOFF and WOFF2).
4
+
5
+ ## Overview
6
+
7
+ WOFF and WOFF2 are web font formats optimized for use on websites:
8
+
9
+ - **WOFF** - Compressed version of TrueType/OpenType with metadata support
10
+ - **WOFF2** - Improved compression using Brotli algorithm (typically 30% smaller than WOFF)
11
+
12
+ ## Converting to WOFF
13
+
14
+ ```ruby
15
+ require 'fontisan'
16
+
17
+ # Convert to WOFF
18
+ Fontisan.convert('font.ttf', output_format: :woff)
19
+
20
+ # Convert to WOFF2 (recommended for web)
21
+ Fontisan.convert('font.ttf', output_format: :woff2)
22
+ ```
23
+
24
+ ## Metadata
25
+
26
+ WOFF files can contain extended metadata:
27
+
28
+ ```ruby
29
+ # Add metadata when converting
30
+ Fontisan.convert('font.ttf',
31
+ output_format: :woff2,
32
+ metadata: {
33
+ unique_id: 'my-font-1.0',
34
+ license: 'OFL',
35
+ license_url: 'https://scripts.sil.org/OFL',
36
+ description: 'My custom font'
37
+ }
38
+ )
39
+ ```
40
+
41
+ ## Extracting from WOFF
42
+
43
+ ```ruby
44
+ # Extract original font from WOFF
45
+ Fontisan.convert('font.woff', output_format: :ttf)
46
+ Fontisan.convert('font.woff2', output_format: :otf)
47
+ ```
48
+
49
+ ## Compression Comparison
50
+
51
+ | Format | Size (relative) |
52
+ |--------|----------------|
53
+ | TTF/OTF | 100% |
54
+ | WOFF | ~70% |
55
+ | WOFF2 | ~50% |
56
+
57
+ ## Related
58
+
59
+ - [Font Conversion](/guide/conversion) - General conversion guide