fontisan 0.1.0 → 0.2.0

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 (185) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +529 -65
  3. data/Gemfile +1 -0
  4. data/LICENSE +5 -1
  5. data/README.adoc +1301 -275
  6. data/Rakefile +27 -2
  7. data/benchmark/variation_quick_bench.rb +47 -0
  8. data/docs/EXTRACT_TTC_MIGRATION.md +549 -0
  9. data/fontisan.gemspec +4 -1
  10. data/lib/fontisan/binary/base_record.rb +22 -1
  11. data/lib/fontisan/cli.rb +309 -0
  12. data/lib/fontisan/collection/builder.rb +260 -0
  13. data/lib/fontisan/collection/offset_calculator.rb +227 -0
  14. data/lib/fontisan/collection/table_analyzer.rb +204 -0
  15. data/lib/fontisan/collection/table_deduplicator.rb +241 -0
  16. data/lib/fontisan/collection/writer.rb +306 -0
  17. data/lib/fontisan/commands/base_command.rb +8 -1
  18. data/lib/fontisan/commands/convert_command.rb +291 -0
  19. data/lib/fontisan/commands/export_command.rb +161 -0
  20. data/lib/fontisan/commands/info_command.rb +40 -6
  21. data/lib/fontisan/commands/instance_command.rb +295 -0
  22. data/lib/fontisan/commands/ls_command.rb +113 -0
  23. data/lib/fontisan/commands/pack_command.rb +241 -0
  24. data/lib/fontisan/commands/subset_command.rb +245 -0
  25. data/lib/fontisan/commands/unpack_command.rb +338 -0
  26. data/lib/fontisan/commands/validate_command.rb +178 -0
  27. data/lib/fontisan/commands/variable_command.rb +30 -1
  28. data/lib/fontisan/config/collection_settings.yml +56 -0
  29. data/lib/fontisan/config/conversion_matrix.yml +212 -0
  30. data/lib/fontisan/config/export_settings.yml +66 -0
  31. data/lib/fontisan/config/subset_profiles.yml +100 -0
  32. data/lib/fontisan/config/svg_settings.yml +60 -0
  33. data/lib/fontisan/config/validation_rules.yml +149 -0
  34. data/lib/fontisan/config/variable_settings.yml +99 -0
  35. data/lib/fontisan/config/woff2_settings.yml +77 -0
  36. data/lib/fontisan/constants.rb +69 -0
  37. data/lib/fontisan/converters/conversion_strategy.rb +96 -0
  38. data/lib/fontisan/converters/format_converter.rb +259 -0
  39. data/lib/fontisan/converters/outline_converter.rb +936 -0
  40. data/lib/fontisan/converters/svg_generator.rb +244 -0
  41. data/lib/fontisan/converters/table_copier.rb +117 -0
  42. data/lib/fontisan/converters/woff2_encoder.rb +416 -0
  43. data/lib/fontisan/converters/woff_writer.rb +391 -0
  44. data/lib/fontisan/error.rb +203 -0
  45. data/lib/fontisan/export/exporter.rb +262 -0
  46. data/lib/fontisan/export/table_serializer.rb +255 -0
  47. data/lib/fontisan/export/transformers/font_to_ttx.rb +172 -0
  48. data/lib/fontisan/export/transformers/head_transformer.rb +96 -0
  49. data/lib/fontisan/export/transformers/hhea_transformer.rb +59 -0
  50. data/lib/fontisan/export/transformers/maxp_transformer.rb +63 -0
  51. data/lib/fontisan/export/transformers/name_transformer.rb +63 -0
  52. data/lib/fontisan/export/transformers/os2_transformer.rb +121 -0
  53. data/lib/fontisan/export/transformers/post_transformer.rb +51 -0
  54. data/lib/fontisan/export/ttx_generator.rb +527 -0
  55. data/lib/fontisan/export/ttx_parser.rb +300 -0
  56. data/lib/fontisan/font_loader.rb +121 -12
  57. data/lib/fontisan/font_writer.rb +301 -0
  58. data/lib/fontisan/formatters/text_formatter.rb +102 -0
  59. data/lib/fontisan/glyph_accessor.rb +503 -0
  60. data/lib/fontisan/hints/hint_converter.rb +177 -0
  61. data/lib/fontisan/hints/postscript_hint_applier.rb +185 -0
  62. data/lib/fontisan/hints/postscript_hint_extractor.rb +254 -0
  63. data/lib/fontisan/hints/truetype_hint_applier.rb +71 -0
  64. data/lib/fontisan/hints/truetype_hint_extractor.rb +162 -0
  65. data/lib/fontisan/loading_modes.rb +113 -0
  66. data/lib/fontisan/metrics_calculator.rb +277 -0
  67. data/lib/fontisan/models/collection_font_summary.rb +52 -0
  68. data/lib/fontisan/models/collection_info.rb +76 -0
  69. data/lib/fontisan/models/collection_list_info.rb +37 -0
  70. data/lib/fontisan/models/font_export.rb +158 -0
  71. data/lib/fontisan/models/font_summary.rb +48 -0
  72. data/lib/fontisan/models/glyph_outline.rb +343 -0
  73. data/lib/fontisan/models/hint.rb +233 -0
  74. data/lib/fontisan/models/outline.rb +664 -0
  75. data/lib/fontisan/models/table_sharing_info.rb +40 -0
  76. data/lib/fontisan/models/ttx/glyph_order.rb +31 -0
  77. data/lib/fontisan/models/ttx/tables/binary_table.rb +67 -0
  78. data/lib/fontisan/models/ttx/tables/head_table.rb +74 -0
  79. data/lib/fontisan/models/ttx/tables/hhea_table.rb +74 -0
  80. data/lib/fontisan/models/ttx/tables/maxp_table.rb +55 -0
  81. data/lib/fontisan/models/ttx/tables/name_table.rb +45 -0
  82. data/lib/fontisan/models/ttx/tables/os2_table.rb +157 -0
  83. data/lib/fontisan/models/ttx/tables/post_table.rb +50 -0
  84. data/lib/fontisan/models/ttx/ttfont.rb +49 -0
  85. data/lib/fontisan/models/validation_report.rb +203 -0
  86. data/lib/fontisan/open_type_collection.rb +156 -2
  87. data/lib/fontisan/open_type_font.rb +296 -10
  88. data/lib/fontisan/optimizers/charstring_rewriter.rb +161 -0
  89. data/lib/fontisan/optimizers/pattern_analyzer.rb +308 -0
  90. data/lib/fontisan/optimizers/stack_tracker.rb +246 -0
  91. data/lib/fontisan/optimizers/subroutine_builder.rb +134 -0
  92. data/lib/fontisan/optimizers/subroutine_generator.rb +207 -0
  93. data/lib/fontisan/optimizers/subroutine_optimizer.rb +107 -0
  94. data/lib/fontisan/outline_extractor.rb +423 -0
  95. data/lib/fontisan/subset/builder.rb +268 -0
  96. data/lib/fontisan/subset/glyph_mapping.rb +215 -0
  97. data/lib/fontisan/subset/options.rb +142 -0
  98. data/lib/fontisan/subset/profile.rb +152 -0
  99. data/lib/fontisan/subset/table_subsetter.rb +461 -0
  100. data/lib/fontisan/svg/font_face_generator.rb +278 -0
  101. data/lib/fontisan/svg/font_generator.rb +264 -0
  102. data/lib/fontisan/svg/glyph_generator.rb +168 -0
  103. data/lib/fontisan/svg/view_box_calculator.rb +137 -0
  104. data/lib/fontisan/tables/cff/cff_glyph.rb +176 -0
  105. data/lib/fontisan/tables/cff/charset.rb +282 -0
  106. data/lib/fontisan/tables/cff/charstring.rb +905 -0
  107. data/lib/fontisan/tables/cff/charstring_builder.rb +322 -0
  108. data/lib/fontisan/tables/cff/charstrings_index.rb +162 -0
  109. data/lib/fontisan/tables/cff/dict.rb +351 -0
  110. data/lib/fontisan/tables/cff/dict_builder.rb +242 -0
  111. data/lib/fontisan/tables/cff/encoding.rb +274 -0
  112. data/lib/fontisan/tables/cff/header.rb +102 -0
  113. data/lib/fontisan/tables/cff/index.rb +237 -0
  114. data/lib/fontisan/tables/cff/index_builder.rb +170 -0
  115. data/lib/fontisan/tables/cff/private_dict.rb +284 -0
  116. data/lib/fontisan/tables/cff/top_dict.rb +236 -0
  117. data/lib/fontisan/tables/cff.rb +487 -0
  118. data/lib/fontisan/tables/cff2/blend_operator.rb +240 -0
  119. data/lib/fontisan/tables/cff2/charstring_parser.rb +591 -0
  120. data/lib/fontisan/tables/cff2/operand_stack.rb +232 -0
  121. data/lib/fontisan/tables/cff2.rb +341 -0
  122. data/lib/fontisan/tables/cvar.rb +242 -0
  123. data/lib/fontisan/tables/fvar.rb +2 -2
  124. data/lib/fontisan/tables/glyf/compound_glyph.rb +483 -0
  125. data/lib/fontisan/tables/glyf/compound_glyph_resolver.rb +136 -0
  126. data/lib/fontisan/tables/glyf/curve_converter.rb +343 -0
  127. data/lib/fontisan/tables/glyf/glyph_builder.rb +450 -0
  128. data/lib/fontisan/tables/glyf/simple_glyph.rb +382 -0
  129. data/lib/fontisan/tables/glyf.rb +235 -0
  130. data/lib/fontisan/tables/gvar.rb +270 -0
  131. data/lib/fontisan/tables/hhea.rb +124 -0
  132. data/lib/fontisan/tables/hmtx.rb +287 -0
  133. data/lib/fontisan/tables/hvar.rb +191 -0
  134. data/lib/fontisan/tables/loca.rb +322 -0
  135. data/lib/fontisan/tables/maxp.rb +192 -0
  136. data/lib/fontisan/tables/mvar.rb +185 -0
  137. data/lib/fontisan/tables/name.rb +99 -30
  138. data/lib/fontisan/tables/variation_common.rb +346 -0
  139. data/lib/fontisan/tables/vvar.rb +234 -0
  140. data/lib/fontisan/true_type_collection.rb +156 -2
  141. data/lib/fontisan/true_type_font.rb +297 -11
  142. data/lib/fontisan/utilities/brotli_wrapper.rb +159 -0
  143. data/lib/fontisan/utilities/checksum_calculator.rb +18 -0
  144. data/lib/fontisan/utils/thread_pool.rb +134 -0
  145. data/lib/fontisan/validation/checksum_validator.rb +170 -0
  146. data/lib/fontisan/validation/consistency_validator.rb +197 -0
  147. data/lib/fontisan/validation/structure_validator.rb +198 -0
  148. data/lib/fontisan/validation/table_validator.rb +158 -0
  149. data/lib/fontisan/validation/validator.rb +152 -0
  150. data/lib/fontisan/variable/axis_normalizer.rb +215 -0
  151. data/lib/fontisan/variable/delta_applicator.rb +313 -0
  152. data/lib/fontisan/variable/glyph_delta_processor.rb +218 -0
  153. data/lib/fontisan/variable/instancer.rb +344 -0
  154. data/lib/fontisan/variable/metric_delta_processor.rb +282 -0
  155. data/lib/fontisan/variable/region_matcher.rb +208 -0
  156. data/lib/fontisan/variable/static_font_builder.rb +213 -0
  157. data/lib/fontisan/variable/table_updater.rb +219 -0
  158. data/lib/fontisan/variation/blend_applier.rb +199 -0
  159. data/lib/fontisan/variation/cache.rb +298 -0
  160. data/lib/fontisan/variation/cache_key_builder.rb +162 -0
  161. data/lib/fontisan/variation/converter.rb +268 -0
  162. data/lib/fontisan/variation/data_extractor.rb +86 -0
  163. data/lib/fontisan/variation/delta_applier.rb +266 -0
  164. data/lib/fontisan/variation/delta_parser.rb +228 -0
  165. data/lib/fontisan/variation/inspector.rb +275 -0
  166. data/lib/fontisan/variation/instance_generator.rb +273 -0
  167. data/lib/fontisan/variation/interpolator.rb +231 -0
  168. data/lib/fontisan/variation/metrics_adjuster.rb +318 -0
  169. data/lib/fontisan/variation/optimizer.rb +418 -0
  170. data/lib/fontisan/variation/parallel_generator.rb +150 -0
  171. data/lib/fontisan/variation/region_matcher.rb +221 -0
  172. data/lib/fontisan/variation/subsetter.rb +463 -0
  173. data/lib/fontisan/variation/table_accessor.rb +105 -0
  174. data/lib/fontisan/variation/validator.rb +345 -0
  175. data/lib/fontisan/variation/variation_context.rb +211 -0
  176. data/lib/fontisan/version.rb +1 -1
  177. data/lib/fontisan/woff2/directory.rb +257 -0
  178. data/lib/fontisan/woff2/header.rb +101 -0
  179. data/lib/fontisan/woff2/table_transformer.rb +163 -0
  180. data/lib/fontisan/woff2_font.rb +712 -0
  181. data/lib/fontisan/woff_font.rb +483 -0
  182. data/lib/fontisan.rb +120 -0
  183. data/scripts/compare_stack_aware.rb +187 -0
  184. data/scripts/measure_optimization.rb +141 -0
  185. metadata +205 -4
@@ -0,0 +1,233 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fontisan
4
+ module Models
5
+ # Universal hint representation supporting both TrueType and PostScript hints
6
+ #
7
+ # Hints are instructions that improve font rendering at small sizes by
8
+ # providing information about how to align features to the pixel grid.
9
+ # This model provides a format-agnostic representation that can be
10
+ # converted between TrueType instructions and PostScript hint operators.
11
+ #
12
+ # **Hint Types:**
13
+ #
14
+ # - `:stem` - Vertical or horizontal stem hints (PostScript hstem/vstem)
15
+ # - `:stem3` - Multiple stem hints (PostScript hstem3/vstem3)
16
+ # - `:flex` - Flex hints for smooth curves (PostScript flex)
17
+ # - `:counter` - Counter control hints (PostScript counter)
18
+ # - `:hint_replacement` - Hint replacement (PostScript hintmask)
19
+ # - `:delta` - Delta hints for pixel-level adjustments (TrueType DELTA)
20
+ # - `:interpolate` - Interpolation hints (TrueType IUP)
21
+ # - `:shift` - Shift hints (TrueType SHP)
22
+ # - `:align` - Alignment hints (TrueType ALIGNRP)
23
+ #
24
+ # @example Creating a stem hint
25
+ # hint = Fontisan::Models::Hint.new(
26
+ # type: :stem,
27
+ # data: { position: 100, width: 50, orientation: :vertical }
28
+ # )
29
+ #
30
+ # @example Converting to TrueType
31
+ # tt_instructions = hint.to_truetype
32
+ #
33
+ # @example Converting to PostScript
34
+ # ps_operators = hint.to_postscript
35
+ class Hint
36
+ # @return [Symbol] Hint type
37
+ attr_reader :type
38
+
39
+ # @return [Hash] Hint-specific data
40
+ attr_reader :data
41
+
42
+ # @return [Symbol] Source format (:truetype or :postscript)
43
+ attr_reader :source_format
44
+
45
+ # Initialize a new hint
46
+ #
47
+ # @param type [Symbol] Hint type
48
+ # @param data [Hash] Hint-specific data
49
+ # @param source_format [Symbol] Source format (optional)
50
+ def initialize(type:, data:, source_format: nil)
51
+ @type = type
52
+ @data = data
53
+ @source_format = source_format
54
+ end
55
+
56
+ # Convert hint to TrueType instruction format
57
+ #
58
+ # @return [Array<Integer>] TrueType instruction bytes
59
+ def to_truetype
60
+ case type
61
+ when :stem
62
+ convert_stem_to_truetype
63
+ when :flex
64
+ convert_flex_to_truetype
65
+ when :counter
66
+ convert_counter_to_truetype
67
+ when :delta
68
+ # Already in TrueType format
69
+ data[:instructions] || []
70
+ when :interpolate
71
+ # IUP instruction
72
+ [0x30] # IUP[y], or [0x31] for IUP[x]
73
+ when :shift
74
+ # SHP instruction
75
+ data[:instructions] || []
76
+ when :align
77
+ # ALIGNRP instruction
78
+ [0x3C]
79
+ else
80
+ # Unknown hint type - return empty
81
+ []
82
+ end
83
+ end
84
+
85
+ # Convert hint to PostScript hint format
86
+ #
87
+ # @return [Hash] PostScript hint operators and arguments
88
+ def to_postscript
89
+ case type
90
+ when :stem
91
+ convert_stem_to_postscript
92
+ when :stem3
93
+ convert_stem3_to_postscript
94
+ when :flex
95
+ convert_flex_to_postscript
96
+ when :counter
97
+ convert_counter_to_postscript
98
+ when :hint_replacement
99
+ # Hintmask operator
100
+ { operator: :hintmask, args: data[:mask] || [] }
101
+ when :delta, :interpolate, :shift, :align
102
+ # TrueType-specific hints don't have direct PS equivalents
103
+ # Return approximation using stem hints
104
+ approximate_as_postscript
105
+ else
106
+ # Unknown hint type
107
+ {}
108
+ end
109
+ end
110
+
111
+ # Check if hint is compatible with target format
112
+ #
113
+ # @param format [Symbol] Target format (:truetype or :postscript)
114
+ # @return [Boolean] True if compatible
115
+ def compatible_with?(format)
116
+ case format
117
+ when :truetype
118
+ # Most PostScript hints can be converted to TrueType
119
+ %i[stem flex counter delta interpolate shift align].include?(type)
120
+ when :postscript
121
+ # Most TrueType hints can be approximated in PostScript
122
+ %i[stem stem3 flex counter hint_replacement].include?(type)
123
+ else
124
+ false
125
+ end
126
+ end
127
+
128
+ private
129
+
130
+ # Convert stem hint to TrueType instructions
131
+ def convert_stem_to_truetype
132
+ position = data[:position] || 0
133
+ width = data[:width] || 0
134
+ orientation = data[:orientation] || :vertical
135
+
136
+ # TrueType uses MDAP (Move Direct Absolute Point) and MDRP (Move Direct Relative Point)
137
+ # to control stem positioning
138
+ instructions = []
139
+
140
+ if orientation == :vertical
141
+ # Vertical stem: use Y-axis instructions
142
+ instructions << 0x2E # MDAP[rnd] - mark reference point
143
+ instructions << 0xC0 # MDRP[min,rnd,black] - move relative point
144
+ else
145
+ # Horizontal stem: use X-axis instructions
146
+ instructions << 0x2F # MDAP[rnd]
147
+ instructions << 0xC0 # MDRP[min,rnd,black]
148
+ end
149
+
150
+ instructions
151
+ end
152
+
153
+ # Convert flex hint to TrueType instructions
154
+ def convert_flex_to_truetype
155
+ # Flex hints ensure smooth curves
156
+ # TrueType approximates with smooth curve flags on contour points
157
+ # Return empty as this is handled at the contour level
158
+ []
159
+ end
160
+
161
+ # Convert counter hint to TrueType instructions
162
+ def convert_counter_to_truetype
163
+ # Counter hints control interior space
164
+ # TrueType uses control value program (CVT) for this
165
+ # Return empty as this requires CVT table modification
166
+ []
167
+ end
168
+
169
+ # Convert stem hint to PostScript operators
170
+ def convert_stem_to_postscript
171
+ position = data[:position] || 0
172
+ width = data[:width] || 0
173
+ orientation = data[:orientation] || :vertical
174
+
175
+ operator = orientation == :vertical ? :vstem : :hstem
176
+
177
+ {
178
+ operator: operator,
179
+ args: [position, width],
180
+ }
181
+ end
182
+
183
+ # Convert stem3 hint to PostScript operators
184
+ def convert_stem3_to_postscript
185
+ stems = data[:stems] || []
186
+ orientation = data[:orientation] || :vertical
187
+
188
+ operator = orientation == :vertical ? :vstem3 : :hstem3
189
+
190
+ # Flatten stem positions and widths
191
+ args = stems.flat_map { |s| [s[:position], s[:width]] }
192
+
193
+ {
194
+ operator: operator,
195
+ args: args,
196
+ }
197
+ end
198
+
199
+ # Convert flex hint to PostScript operators
200
+ def convert_flex_to_postscript
201
+ points = data[:points] || []
202
+
203
+ {
204
+ operator: :flex,
205
+ args: points.flat_map { |p| [p[:x], p[:y]] },
206
+ }
207
+ end
208
+
209
+ # Convert counter hint to PostScript operators
210
+ def convert_counter_to_postscript
211
+ zones = data[:zones] || []
212
+
213
+ {
214
+ operator: :counter,
215
+ args: zones,
216
+ }
217
+ end
218
+
219
+ # Approximate TrueType-specific hints as PostScript stem hints
220
+ def approximate_as_postscript
221
+ # Best effort: create a stem hint from available data
222
+ if data[:position] && data[:width]
223
+ {
224
+ operator: :vstem,
225
+ args: [data[:position], data[:width]],
226
+ }
227
+ else
228
+ {}
229
+ end
230
+ end
231
+ end
232
+ end
233
+ end