fontisan 0.2.3 → 0.2.5

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +221 -49
  3. data/README.adoc +519 -5
  4. data/Rakefile +20 -7
  5. data/lib/fontisan/cli.rb +67 -6
  6. data/lib/fontisan/commands/base_command.rb +2 -19
  7. data/lib/fontisan/commands/convert_command.rb +16 -13
  8. data/lib/fontisan/commands/info_command.rb +88 -0
  9. data/lib/fontisan/commands/validate_command.rb +107 -151
  10. data/lib/fontisan/config/conversion_matrix.yml +58 -20
  11. data/lib/fontisan/converters/outline_converter.rb +6 -3
  12. data/lib/fontisan/converters/svg_generator.rb +45 -0
  13. data/lib/fontisan/converters/woff2_encoder.rb +84 -13
  14. data/lib/fontisan/models/bitmap_glyph.rb +123 -0
  15. data/lib/fontisan/models/bitmap_strike.rb +94 -0
  16. data/lib/fontisan/models/color_glyph.rb +57 -0
  17. data/lib/fontisan/models/color_layer.rb +53 -0
  18. data/lib/fontisan/models/color_palette.rb +60 -0
  19. data/lib/fontisan/models/font_info.rb +26 -0
  20. data/lib/fontisan/models/svg_glyph.rb +89 -0
  21. data/lib/fontisan/models/validation_report.rb +227 -0
  22. data/lib/fontisan/open_type_font.rb +6 -0
  23. data/lib/fontisan/optimizers/charstring_rewriter.rb +19 -8
  24. data/lib/fontisan/optimizers/pattern_analyzer.rb +4 -2
  25. data/lib/fontisan/optimizers/subroutine_builder.rb +6 -5
  26. data/lib/fontisan/optimizers/subroutine_optimizer.rb +5 -2
  27. data/lib/fontisan/pipeline/output_writer.rb +2 -2
  28. data/lib/fontisan/pipeline/transformation_pipeline.rb +4 -8
  29. data/lib/fontisan/tables/cbdt.rb +169 -0
  30. data/lib/fontisan/tables/cblc.rb +290 -0
  31. data/lib/fontisan/tables/cff.rb +6 -12
  32. data/lib/fontisan/tables/cmap.rb +82 -2
  33. data/lib/fontisan/tables/colr.rb +291 -0
  34. data/lib/fontisan/tables/cpal.rb +281 -0
  35. data/lib/fontisan/tables/glyf/glyph_builder.rb +5 -1
  36. data/lib/fontisan/tables/glyf.rb +118 -0
  37. data/lib/fontisan/tables/head.rb +60 -0
  38. data/lib/fontisan/tables/hhea.rb +74 -0
  39. data/lib/fontisan/tables/maxp.rb +60 -0
  40. data/lib/fontisan/tables/name.rb +76 -0
  41. data/lib/fontisan/tables/os2.rb +113 -0
  42. data/lib/fontisan/tables/post.rb +57 -0
  43. data/lib/fontisan/tables/sbix.rb +379 -0
  44. data/lib/fontisan/tables/svg.rb +301 -0
  45. data/lib/fontisan/true_type_font.rb +6 -0
  46. data/lib/fontisan/validators/basic_validator.rb +85 -0
  47. data/lib/fontisan/validators/font_book_validator.rb +130 -0
  48. data/lib/fontisan/validators/opentype_validator.rb +112 -0
  49. data/lib/fontisan/validators/profile_loader.rb +139 -0
  50. data/lib/fontisan/validators/validator.rb +484 -0
  51. data/lib/fontisan/validators/web_font_validator.rb +102 -0
  52. data/lib/fontisan/version.rb +1 -1
  53. data/lib/fontisan/woff2/directory.rb +40 -11
  54. data/lib/fontisan/woff2/table_transformer.rb +506 -73
  55. data/lib/fontisan/woff2_font.rb +29 -9
  56. data/lib/fontisan/woff_font.rb +17 -4
  57. data/lib/fontisan.rb +90 -6
  58. metadata +20 -9
  59. data/lib/fontisan/config/validation_rules.yml +0 -149
  60. data/lib/fontisan/validation/checksum_validator.rb +0 -170
  61. data/lib/fontisan/validation/consistency_validator.rb +0 -197
  62. data/lib/fontisan/validation/structure_validator.rb +0 -198
  63. data/lib/fontisan/validation/table_validator.rb +0 -158
  64. data/lib/fontisan/validation/validator.rb +0 -152
  65. data/lib/fontisan/validation/variable_font_validator.rb +0 -218
@@ -1,218 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Fontisan
4
- module Validation
5
- # VariableFontValidator validates variable font structure
6
- #
7
- # Validates:
8
- # - fvar table structure
9
- # - Axis definitions and ranges
10
- # - Instance definitions
11
- # - Variation table consistency
12
- # - Metrics variation tables
13
- #
14
- # @example Validate a variable font
15
- # validator = VariableFontValidator.new(font)
16
- # errors = validator.validate
17
- # puts "Found #{errors.length} errors" if errors.any?
18
- class VariableFontValidator
19
- # Initialize validator with font
20
- #
21
- # @param font [TrueTypeFont, OpenTypeFont] Font to validate
22
- def initialize(font)
23
- @font = font
24
- @errors = []
25
- end
26
-
27
- # Validate variable font
28
- #
29
- # @return [Array<String>] Array of error messages
30
- def validate
31
- return [] unless @font.has_table?("fvar")
32
-
33
- validate_fvar_structure
34
- validate_axes
35
- validate_instances
36
- validate_variation_tables
37
- validate_metrics_variation
38
-
39
- @errors
40
- end
41
-
42
- private
43
-
44
- # Validate fvar table structure
45
- #
46
- # @return [void]
47
- def validate_fvar_structure
48
- fvar = @font.table("fvar")
49
- return unless fvar
50
-
51
- if !fvar.respond_to?(:axes) || fvar.axes.nil? || fvar.axes.empty?
52
- @errors << "fvar: No axes defined"
53
- return
54
- end
55
-
56
- if fvar.respond_to?(:axis_count) && fvar.axis_count != fvar.axes.length
57
- @errors << "fvar: Axis count mismatch (expected #{fvar.axis_count}, got #{fvar.axes.length})"
58
- end
59
- end
60
-
61
- # Validate all axes
62
- #
63
- # @return [void]
64
- def validate_axes
65
- fvar = @font.table("fvar")
66
- return unless fvar.respond_to?(:axes)
67
-
68
- fvar.axes.each_with_index do |axis, index|
69
- validate_axis_range(axis, index)
70
- validate_axis_tag(axis, index)
71
- end
72
- end
73
-
74
- # Validate axis range values
75
- #
76
- # @param axis [Object] Axis object
77
- # @param index [Integer] Axis index
78
- # @return [void]
79
- def validate_axis_range(axis, index)
80
- return unless axis.respond_to?(:min_value) && axis.respond_to?(:max_value)
81
-
82
- if axis.min_value > axis.max_value
83
- tag = axis.respond_to?(:axis_tag) ? axis.axis_tag : "axis #{index}"
84
- @errors << "Axis #{tag}: min_value (#{axis.min_value}) > max_value (#{axis.max_value})"
85
- end
86
-
87
- if axis.respond_to?(:default_value) && (axis.default_value < axis.min_value || axis.default_value > axis.max_value)
88
- tag = axis.respond_to?(:axis_tag) ? axis.axis_tag : "axis #{index}"
89
- @errors << "Axis #{tag}: default_value (#{axis.default_value}) out of range [#{axis.min_value}, #{axis.max_value}]"
90
- end
91
- end
92
-
93
- # Validate axis tag format
94
- #
95
- # @param axis [Object] Axis object
96
- # @param index [Integer] Axis index
97
- # @return [void]
98
- def validate_axis_tag(axis, index)
99
- return unless axis.respond_to?(:axis_tag)
100
-
101
- tag = axis.axis_tag
102
- unless tag.is_a?(String) && tag.length == 4 && tag =~ /^[a-zA-Z]{4}$/
103
- @errors << "Axis #{index}: invalid tag '#{tag}' (must be 4 ASCII letters)"
104
- end
105
- end
106
-
107
- # Validate named instances
108
- #
109
- # @return [void]
110
- def validate_instances
111
- fvar = @font.table("fvar")
112
- return unless fvar.respond_to?(:instances)
113
- return unless fvar.instances
114
-
115
- fvar.instances.each_with_index do |instance, idx|
116
- validate_instance_coordinates(instance, idx, fvar)
117
- end
118
- end
119
-
120
- # Validate instance coordinates
121
- #
122
- # @param instance [Object] Instance object
123
- # @param idx [Integer] Instance index
124
- # @param fvar [Object] fvar table
125
- # @return [void]
126
- def validate_instance_coordinates(instance, idx, fvar)
127
- return unless instance.is_a?(Hash) && instance[:coordinates]
128
-
129
- coords = instance[:coordinates]
130
- axis_count = fvar.respond_to?(:axis_count) ? fvar.axis_count : fvar.axes.length
131
-
132
- if coords.length != axis_count
133
- @errors << "Instance #{idx}: coordinate count mismatch (expected #{axis_count}, got #{coords.length})"
134
- end
135
-
136
- coords.each_with_index do |value, axis_idx|
137
- next if axis_idx >= fvar.axes.length
138
-
139
- axis = fvar.axes[axis_idx]
140
- next unless axis.respond_to?(:min_value) && axis.respond_to?(:max_value)
141
-
142
- if value < axis.min_value || value > axis.max_value
143
- tag = axis.respond_to?(:axis_tag) ? axis.axis_tag : "axis #{axis_idx}"
144
- @errors << "Instance #{idx}: coordinate for #{tag} (#{value}) out of range [#{axis.min_value}, #{axis.max_value}]"
145
- end
146
- end
147
- end
148
-
149
- # Validate variation tables
150
- #
151
- # @return [void]
152
- def validate_variation_tables
153
- has_gvar = @font.has_table?("gvar")
154
- has_cff2 = @font.has_table?("CFF2")
155
- has_glyf = @font.has_table?("glyf")
156
- has_cff = @font.has_table?("CFF ")
157
-
158
- # TrueType variable fonts should have gvar
159
- if has_glyf && !has_gvar
160
- @errors << "TrueType variable font missing gvar table"
161
- end
162
-
163
- # CFF variable fonts should have CFF2
164
- if has_cff && !has_cff2
165
- @errors << "CFF variable font missing CFF2 table"
166
- end
167
-
168
- # Can't have both gvar and CFF2
169
- if has_gvar && has_cff2
170
- @errors << "Font has both gvar and CFF2 tables (incompatible)"
171
- end
172
- end
173
-
174
- # Validate metrics variation tables
175
- #
176
- # @return [void]
177
- def validate_metrics_variation
178
- validate_hvar if @font.has_table?("HVAR")
179
- validate_vvar if @font.has_table?("VVAR")
180
- validate_mvar if @font.has_table?("MVAR")
181
- end
182
-
183
- # Validate HVAR table
184
- #
185
- # @return [void]
186
- def validate_hvar
187
- # HVAR validation would go here
188
- # For now, just check it exists
189
- hvar = @font.table_data["HVAR"]
190
- if hvar.nil? || hvar.empty?
191
- @errors << "HVAR table is empty"
192
- end
193
- end
194
-
195
- # Validate VVAR table
196
- #
197
- # @return [void]
198
- def validate_vvar
199
- # VVAR validation would go here
200
- vvar = @font.table_data["VVAR"]
201
- if vvar.nil? || vvar.empty?
202
- @errors << "VVAR table is empty"
203
- end
204
- end
205
-
206
- # Validate MVAR table
207
- #
208
- # @return [void]
209
- def validate_mvar
210
- # MVAR validation would go here
211
- mvar = @font.table_data["MVAR"]
212
- if mvar.nil? || mvar.empty?
213
- @errors << "MVAR table is empty"
214
- end
215
- end
216
- end
217
- end
218
- end