fontisan 0.1.0 → 0.2.1

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 (214) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +672 -69
  3. data/Gemfile +1 -0
  4. data/LICENSE +5 -1
  5. data/README.adoc +1477 -297
  6. data/Rakefile +63 -41
  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 +364 -4
  12. data/lib/fontisan/collection/builder.rb +341 -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 +317 -0
  16. data/lib/fontisan/collection/writer.rb +306 -0
  17. data/lib/fontisan/commands/base_command.rb +24 -1
  18. data/lib/fontisan/commands/convert_command.rb +218 -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 +286 -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 +203 -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 +79 -0
  37. data/lib/fontisan/converters/conversion_strategy.rb +96 -0
  38. data/lib/fontisan/converters/format_converter.rb +408 -0
  39. data/lib/fontisan/converters/outline_converter.rb +998 -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 +122 -15
  57. data/lib/fontisan/font_writer.rb +302 -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 +310 -0
  61. data/lib/fontisan/hints/postscript_hint_applier.rb +266 -0
  62. data/lib/fontisan/hints/postscript_hint_extractor.rb +354 -0
  63. data/lib/fontisan/hints/truetype_hint_applier.rb +117 -0
  64. data/lib/fontisan/hints/truetype_hint_extractor.rb +289 -0
  65. data/lib/fontisan/loading_modes.rb +115 -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 +405 -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 +321 -19
  88. data/lib/fontisan/open_type_font_extensions.rb +54 -0
  89. data/lib/fontisan/optimizers/charstring_rewriter.rb +161 -0
  90. data/lib/fontisan/optimizers/pattern_analyzer.rb +308 -0
  91. data/lib/fontisan/optimizers/stack_tracker.rb +246 -0
  92. data/lib/fontisan/optimizers/subroutine_builder.rb +134 -0
  93. data/lib/fontisan/optimizers/subroutine_generator.rb +207 -0
  94. data/lib/fontisan/optimizers/subroutine_optimizer.rb +107 -0
  95. data/lib/fontisan/outline_extractor.rb +423 -0
  96. data/lib/fontisan/pipeline/format_detector.rb +249 -0
  97. data/lib/fontisan/pipeline/output_writer.rb +154 -0
  98. data/lib/fontisan/pipeline/strategies/base_strategy.rb +75 -0
  99. data/lib/fontisan/pipeline/strategies/instance_strategy.rb +93 -0
  100. data/lib/fontisan/pipeline/strategies/named_strategy.rb +118 -0
  101. data/lib/fontisan/pipeline/strategies/preserve_strategy.rb +56 -0
  102. data/lib/fontisan/pipeline/transformation_pipeline.rb +411 -0
  103. data/lib/fontisan/pipeline/variation_resolver.rb +165 -0
  104. data/lib/fontisan/subset/builder.rb +268 -0
  105. data/lib/fontisan/subset/glyph_mapping.rb +215 -0
  106. data/lib/fontisan/subset/options.rb +142 -0
  107. data/lib/fontisan/subset/profile.rb +152 -0
  108. data/lib/fontisan/subset/table_subsetter.rb +461 -0
  109. data/lib/fontisan/svg/font_face_generator.rb +278 -0
  110. data/lib/fontisan/svg/font_generator.rb +264 -0
  111. data/lib/fontisan/svg/glyph_generator.rb +168 -0
  112. data/lib/fontisan/svg/view_box_calculator.rb +137 -0
  113. data/lib/fontisan/tables/cff/cff_glyph.rb +176 -0
  114. data/lib/fontisan/tables/cff/charset.rb +282 -0
  115. data/lib/fontisan/tables/cff/charstring.rb +934 -0
  116. data/lib/fontisan/tables/cff/charstring_builder.rb +356 -0
  117. data/lib/fontisan/tables/cff/charstring_parser.rb +237 -0
  118. data/lib/fontisan/tables/cff/charstring_rebuilder.rb +172 -0
  119. data/lib/fontisan/tables/cff/charstrings_index.rb +162 -0
  120. data/lib/fontisan/tables/cff/dict.rb +351 -0
  121. data/lib/fontisan/tables/cff/dict_builder.rb +257 -0
  122. data/lib/fontisan/tables/cff/encoding.rb +274 -0
  123. data/lib/fontisan/tables/cff/header.rb +102 -0
  124. data/lib/fontisan/tables/cff/hint_operation_injector.rb +207 -0
  125. data/lib/fontisan/tables/cff/index.rb +237 -0
  126. data/lib/fontisan/tables/cff/index_builder.rb +170 -0
  127. data/lib/fontisan/tables/cff/offset_recalculator.rb +70 -0
  128. data/lib/fontisan/tables/cff/private_dict.rb +284 -0
  129. data/lib/fontisan/tables/cff/private_dict_writer.rb +125 -0
  130. data/lib/fontisan/tables/cff/table_builder.rb +221 -0
  131. data/lib/fontisan/tables/cff/top_dict.rb +236 -0
  132. data/lib/fontisan/tables/cff.rb +489 -0
  133. data/lib/fontisan/tables/cff2/blend_operator.rb +240 -0
  134. data/lib/fontisan/tables/cff2/charstring_parser.rb +591 -0
  135. data/lib/fontisan/tables/cff2/operand_stack.rb +232 -0
  136. data/lib/fontisan/tables/cff2/private_dict_blend_handler.rb +246 -0
  137. data/lib/fontisan/tables/cff2/region_matcher.rb +200 -0
  138. data/lib/fontisan/tables/cff2/table_builder.rb +574 -0
  139. data/lib/fontisan/tables/cff2/table_reader.rb +419 -0
  140. data/lib/fontisan/tables/cff2/variation_data_extractor.rb +212 -0
  141. data/lib/fontisan/tables/cff2.rb +346 -0
  142. data/lib/fontisan/tables/cvar.rb +203 -0
  143. data/lib/fontisan/tables/fvar.rb +2 -2
  144. data/lib/fontisan/tables/glyf/compound_glyph.rb +483 -0
  145. data/lib/fontisan/tables/glyf/compound_glyph_resolver.rb +136 -0
  146. data/lib/fontisan/tables/glyf/curve_converter.rb +343 -0
  147. data/lib/fontisan/tables/glyf/glyph_builder.rb +450 -0
  148. data/lib/fontisan/tables/glyf/simple_glyph.rb +382 -0
  149. data/lib/fontisan/tables/glyf.rb +235 -0
  150. data/lib/fontisan/tables/gvar.rb +231 -0
  151. data/lib/fontisan/tables/hhea.rb +124 -0
  152. data/lib/fontisan/tables/hmtx.rb +287 -0
  153. data/lib/fontisan/tables/hvar.rb +191 -0
  154. data/lib/fontisan/tables/loca.rb +322 -0
  155. data/lib/fontisan/tables/maxp.rb +192 -0
  156. data/lib/fontisan/tables/mvar.rb +185 -0
  157. data/lib/fontisan/tables/name.rb +99 -30
  158. data/lib/fontisan/tables/variation_common.rb +346 -0
  159. data/lib/fontisan/tables/vvar.rb +234 -0
  160. data/lib/fontisan/true_type_collection.rb +156 -2
  161. data/lib/fontisan/true_type_font.rb +321 -20
  162. data/lib/fontisan/true_type_font_extensions.rb +54 -0
  163. data/lib/fontisan/utilities/brotli_wrapper.rb +159 -0
  164. data/lib/fontisan/utilities/checksum_calculator.rb +60 -0
  165. data/lib/fontisan/utils/thread_pool.rb +134 -0
  166. data/lib/fontisan/validation/checksum_validator.rb +170 -0
  167. data/lib/fontisan/validation/consistency_validator.rb +197 -0
  168. data/lib/fontisan/validation/structure_validator.rb +198 -0
  169. data/lib/fontisan/validation/table_validator.rb +158 -0
  170. data/lib/fontisan/validation/validator.rb +152 -0
  171. data/lib/fontisan/validation/variable_font_validator.rb +218 -0
  172. data/lib/fontisan/variable/axis_normalizer.rb +215 -0
  173. data/lib/fontisan/variable/delta_applicator.rb +313 -0
  174. data/lib/fontisan/variable/glyph_delta_processor.rb +218 -0
  175. data/lib/fontisan/variable/instancer.rb +344 -0
  176. data/lib/fontisan/variable/metric_delta_processor.rb +282 -0
  177. data/lib/fontisan/variable/region_matcher.rb +208 -0
  178. data/lib/fontisan/variable/static_font_builder.rb +213 -0
  179. data/lib/fontisan/variable/table_updater.rb +219 -0
  180. data/lib/fontisan/variation/blend_applier.rb +199 -0
  181. data/lib/fontisan/variation/cache.rb +298 -0
  182. data/lib/fontisan/variation/cache_key_builder.rb +162 -0
  183. data/lib/fontisan/variation/converter.rb +375 -0
  184. data/lib/fontisan/variation/data_extractor.rb +86 -0
  185. data/lib/fontisan/variation/delta_applier.rb +266 -0
  186. data/lib/fontisan/variation/delta_parser.rb +228 -0
  187. data/lib/fontisan/variation/inspector.rb +275 -0
  188. data/lib/fontisan/variation/instance_generator.rb +273 -0
  189. data/lib/fontisan/variation/instance_writer.rb +341 -0
  190. data/lib/fontisan/variation/interpolator.rb +231 -0
  191. data/lib/fontisan/variation/metrics_adjuster.rb +318 -0
  192. data/lib/fontisan/variation/optimizer.rb +418 -0
  193. data/lib/fontisan/variation/parallel_generator.rb +150 -0
  194. data/lib/fontisan/variation/region_matcher.rb +221 -0
  195. data/lib/fontisan/variation/subsetter.rb +463 -0
  196. data/lib/fontisan/variation/table_accessor.rb +105 -0
  197. data/lib/fontisan/variation/tuple_variation_header.rb +51 -0
  198. data/lib/fontisan/variation/validator.rb +345 -0
  199. data/lib/fontisan/variation/variable_svg_generator.rb +268 -0
  200. data/lib/fontisan/variation/variation_context.rb +211 -0
  201. data/lib/fontisan/variation/variation_preserver.rb +288 -0
  202. data/lib/fontisan/version.rb +1 -1
  203. data/lib/fontisan/version.rb.orig +9 -0
  204. data/lib/fontisan/woff2/directory.rb +257 -0
  205. data/lib/fontisan/woff2/glyf_transformer.rb +666 -0
  206. data/lib/fontisan/woff2/header.rb +101 -0
  207. data/lib/fontisan/woff2/hmtx_transformer.rb +164 -0
  208. data/lib/fontisan/woff2/table_transformer.rb +163 -0
  209. data/lib/fontisan/woff2_font.rb +717 -0
  210. data/lib/fontisan/woff_font.rb +488 -0
  211. data/lib/fontisan.rb +132 -0
  212. data/scripts/compare_stack_aware.rb +187 -0
  213. data/scripts/measure_optimization.rb +141 -0
  214. metadata +234 -4
@@ -0,0 +1,236 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "dict"
4
+
5
+ module Fontisan
6
+ module Tables
7
+ class Cff
8
+ # CFF Top DICT structure
9
+ #
10
+ # The Top DICT contains font-level metadata and pointers to other CFF
11
+ # structures like CharStrings, Charset, Encoding, and Private DICT.
12
+ #
13
+ # Top DICT Operators (in addition to common DICT operators):
14
+ # - charset: Offset to Charset data
15
+ # - encoding: Offset to Encoding data
16
+ # - charstrings: Offset to CharStrings INDEX
17
+ # - private: Size and offset to Private DICT
18
+ # - font_bbox: Font bounding box [xMin, yMin, xMax, yMax]
19
+ # - unique_id: Unique ID for this font
20
+ # - xuid: Extended unique ID array
21
+ # - ros: CIDFont registry, ordering, supplement
22
+ # - cidcount: Number of CIDs in CIDFont
23
+ # - fdarray: Offset to Font DICT INDEX (CIDFont)
24
+ # - fdselect: Offset to FDSelect data (CIDFont)
25
+ #
26
+ # Reference: CFF specification section 9 "Top DICT"
27
+ # https://adobe-type-tools.github.io/font-tech-notes/pdfs/5176.CFF.pdf
28
+ #
29
+ # @example Parsing a Top DICT
30
+ # top_dict_data = cff.top_dict_index[0]
31
+ # top_dict = Fontisan::Tables::Cff::TopDict.new(top_dict_data)
32
+ # puts top_dict[:charstrings] # => offset to CharStrings
33
+ # puts top_dict[:charset] # => offset to Charset
34
+ class TopDict < Dict
35
+ # Top DICT specific operators
36
+ #
37
+ # These extend the common operators defined in the base Dict class
38
+ TOP_DICT_OPERATORS = {
39
+ 5 => :font_bbox,
40
+ 13 => :unique_id,
41
+ 14 => :xuid,
42
+ 15 => :charset,
43
+ 16 => :encoding,
44
+ 17 => :charstrings,
45
+ 18 => :private,
46
+ [12, 30] => :ros,
47
+ [12, 31] => :cid_font_version,
48
+ [12, 32] => :cid_font_revision,
49
+ [12, 33] => :cid_font_type,
50
+ [12, 34] => :cid_count,
51
+ [12, 35] => :uid_base,
52
+ [12, 36] => :fd_array,
53
+ [12, 37] => :fd_select,
54
+ [12, 38] => :font_name,
55
+ }.freeze
56
+
57
+ # Default values for Top DICT operators
58
+ #
59
+ # These are used when an operator is not present in the DICT
60
+ DEFAULTS = {
61
+ is_fixed_pitch: false,
62
+ italic_angle: 0,
63
+ underline_position: -100,
64
+ underline_thickness: 50,
65
+ paint_type: 0,
66
+ charstring_type: 2,
67
+ font_matrix: [0.001, 0, 0, 0.001, 0, 0],
68
+ unique_id: nil,
69
+ font_bbox: [0, 0, 0, 0],
70
+ stroke_width: 0,
71
+ charset: 0, # Offset 0 = ISOAdobe charset
72
+ encoding: 0, # Offset 0 = Standard encoding
73
+ cid_count: 8720,
74
+ }.freeze
75
+
76
+ # Get a value with default fallback
77
+ #
78
+ # @param key [Symbol] Operator name
79
+ # @return [Object] Value or default value
80
+ def fetch(key, default = nil)
81
+ @dict.fetch(key, DEFAULTS.fetch(key, default))
82
+ end
83
+
84
+ # Get the charset offset
85
+ #
86
+ # Charset determines which glyphs are present and their SIDs
87
+ #
88
+ # Special values:
89
+ # - 0: ISOAdobe charset
90
+ # - 1: Expert charset
91
+ # - 2: Expert Subset charset
92
+ # - Otherwise: Offset to custom charset
93
+ #
94
+ # @return [Integer] Charset offset or predefined charset ID
95
+ def charset
96
+ fetch(:charset)
97
+ end
98
+
99
+ # Get the encoding offset
100
+ #
101
+ # Encoding maps character codes to glyph indices
102
+ #
103
+ # Special values:
104
+ # - 0: Standard encoding
105
+ # - 1: Expert encoding
106
+ # - Otherwise: Offset to custom encoding
107
+ #
108
+ # @return [Integer] Encoding offset or predefined encoding ID
109
+ def encoding
110
+ fetch(:encoding)
111
+ end
112
+
113
+ # Get the CharStrings offset
114
+ #
115
+ # CharStrings INDEX contains the glyph programs (outline data)
116
+ #
117
+ # @return [Integer, nil] Offset to CharStrings INDEX
118
+ def charstrings
119
+ @dict[:charstrings]
120
+ end
121
+
122
+ # Get the Private DICT size and offset
123
+ #
124
+ # The private operator stores [size, offset] as a two-element array
125
+ #
126
+ # @return [Array<Integer>, nil] [size, offset] or nil if not present
127
+ def private
128
+ @dict[:private]
129
+ end
130
+
131
+ # Get the Private DICT size
132
+ #
133
+ # @return [Integer, nil] Size in bytes, or nil if no Private DICT
134
+ def private_size
135
+ private&.first
136
+ end
137
+
138
+ # Get the Private DICT offset
139
+ #
140
+ # @return [Integer, nil] Offset in bytes, or nil if no Private DICT
141
+ def private_offset
142
+ private&.last
143
+ end
144
+
145
+ # Get the font bounding box
146
+ #
147
+ # @return [Array<Integer>] [xMin, yMin, xMax, yMax]
148
+ def font_bbox
149
+ fetch(:font_bbox)
150
+ end
151
+
152
+ # Get the font matrix
153
+ #
154
+ # Transform from glyph space to user space
155
+ #
156
+ # @return [Array<Float>] 6-element affine transformation matrix
157
+ def font_matrix
158
+ fetch(:font_matrix)
159
+ end
160
+
161
+ # Check if this is a CIDFont
162
+ #
163
+ # CIDFonts have the ROS (Registry-Ordering-Supplement) operator
164
+ #
165
+ # @return [Boolean] True if CIDFont
166
+ def cid_font?
167
+ has_key?(:ros)
168
+ end
169
+
170
+ # Get the ROS (Registry, Ordering, Supplement) for CIDFonts
171
+ #
172
+ # @return [Array<Integer>, nil] [registry_sid, ordering_sid, supplement]
173
+ def ros
174
+ @dict[:ros]
175
+ end
176
+
177
+ # Get the CID count for CIDFonts
178
+ #
179
+ # @return [Integer] Number of CIDs
180
+ def cid_count
181
+ fetch(:cid_count)
182
+ end
183
+
184
+ # Get the FDArray offset for CIDFonts
185
+ #
186
+ # FDArray is a Font DICT INDEX for CIDFonts
187
+ #
188
+ # @return [Integer, nil] Offset to FDArray
189
+ def fd_array
190
+ @dict[:fd_array]
191
+ end
192
+
193
+ # Get the FDSelect offset for CIDFonts
194
+ #
195
+ # FDSelect maps CIDs to Font DICTs in FDArray
196
+ #
197
+ # @return [Integer, nil] Offset to FDSelect
198
+ def fd_select
199
+ @dict[:fd_select]
200
+ end
201
+
202
+ # Get the CharString type
203
+ #
204
+ # @return [Integer] CharString type (typically 2 for Type 2 CharStrings)
205
+ def charstring_type
206
+ fetch(:charstring_type)
207
+ end
208
+
209
+ # Check if the font has a custom charset
210
+ #
211
+ # @return [Boolean] True if charset is custom (not 0, 1, or 2)
212
+ def custom_charset?
213
+ charset_val = charset
214
+ charset_val && charset_val > 2
215
+ end
216
+
217
+ # Check if the font has a custom encoding
218
+ #
219
+ # @return [Boolean] True if encoding is custom (not 0 or 1)
220
+ def custom_encoding?
221
+ encoding_val = encoding
222
+ encoding_val && encoding_val > 1
223
+ end
224
+
225
+ private
226
+
227
+ # Get Top DICT specific operators
228
+ #
229
+ # @return [Hash] Top DICT operators merged with base operators
230
+ def derived_operators
231
+ TOP_DICT_OPERATORS
232
+ end
233
+ end
234
+ end
235
+ end
236
+ end