fontisan 0.2.6 → 0.2.8

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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +103 -0
  3. data/.rubocop_todo.yml +107 -318
  4. data/Gemfile +1 -1
  5. data/README.adoc +127 -17
  6. data/Rakefile +12 -7
  7. data/benchmark/variation_quick_bench.rb +1 -1
  8. data/lib/fontisan/base_collection.rb +5 -33
  9. data/lib/fontisan/cli.rb +45 -13
  10. data/lib/fontisan/collection/dfont_builder.rb +2 -1
  11. data/lib/fontisan/collection/shared_logic.rb +54 -0
  12. data/lib/fontisan/commands/convert_command.rb +2 -4
  13. data/lib/fontisan/commands/info_command.rb +3 -3
  14. data/lib/fontisan/commands/pack_command.rb +2 -1
  15. data/lib/fontisan/commands/validate_command.rb +157 -6
  16. data/lib/fontisan/converters/collection_converter.rb +22 -13
  17. data/lib/fontisan/converters/svg_generator.rb +2 -1
  18. data/lib/fontisan/converters/woff2_encoder.rb +6 -6
  19. data/lib/fontisan/converters/woff_writer.rb +3 -1
  20. data/lib/fontisan/dfont_collection.rb +84 -0
  21. data/lib/fontisan/font_loader.rb +9 -9
  22. data/lib/fontisan/formatters/text_formatter.rb +18 -14
  23. data/lib/fontisan/hints/hint_converter.rb +1 -1
  24. data/lib/fontisan/hints/hint_validator.rb +13 -10
  25. data/lib/fontisan/hints/truetype_instruction_analyzer.rb +15 -8
  26. data/lib/fontisan/hints/truetype_instruction_generator.rb +1 -1
  27. data/lib/fontisan/models/collection_validation_report.rb +104 -0
  28. data/lib/fontisan/models/font_report.rb +24 -0
  29. data/lib/fontisan/models/validation_report.rb +7 -2
  30. data/lib/fontisan/open_type_font.rb +2 -3
  31. data/lib/fontisan/optimizers/charstring_rewriter.rb +1 -1
  32. data/lib/fontisan/optimizers/subroutine_optimizer.rb +6 -2
  33. data/lib/fontisan/subset/glyph_mapping.rb +2 -0
  34. data/lib/fontisan/subset/table_subsetter.rb +2 -2
  35. data/lib/fontisan/tables/cblc.rb +8 -4
  36. data/lib/fontisan/tables/cff/index.rb +2 -0
  37. data/lib/fontisan/tables/cff.rb +6 -3
  38. data/lib/fontisan/tables/cff2/private_dict_blend_handler.rb +1 -1
  39. data/lib/fontisan/tables/cff2.rb +1 -1
  40. data/lib/fontisan/tables/cmap.rb +5 -5
  41. data/lib/fontisan/tables/glyf.rb +8 -10
  42. data/lib/fontisan/tables/head.rb +3 -3
  43. data/lib/fontisan/tables/hhea.rb +4 -4
  44. data/lib/fontisan/tables/maxp.rb +2 -2
  45. data/lib/fontisan/tables/name.rb +1 -1
  46. data/lib/fontisan/tables/os2.rb +8 -8
  47. data/lib/fontisan/tables/post.rb +2 -2
  48. data/lib/fontisan/tables/sbix.rb +5 -4
  49. data/lib/fontisan/true_type_font.rb +2 -3
  50. data/lib/fontisan/utilities/checksum_calculator.rb +0 -44
  51. data/lib/fontisan/validation/collection_validator.rb +4 -2
  52. data/lib/fontisan/validators/basic_validator.rb +11 -21
  53. data/lib/fontisan/validators/font_book_validator.rb +29 -50
  54. data/lib/fontisan/validators/opentype_validator.rb +24 -28
  55. data/lib/fontisan/validators/validator.rb +87 -66
  56. data/lib/fontisan/validators/web_font_validator.rb +16 -21
  57. data/lib/fontisan/version.rb +1 -1
  58. data/lib/fontisan/woff2/glyf_transformer.rb +31 -8
  59. data/lib/fontisan/woff2/hmtx_transformer.rb +2 -1
  60. data/lib/fontisan/woff2/table_transformer.rb +4 -2
  61. data/lib/fontisan/woff2_font.rb +4 -2
  62. data/lib/fontisan/woff_font.rb +2 -2
  63. data/lib/fontisan.rb +2 -2
  64. data/scripts/compare_stack_aware.rb +1 -1
  65. data/scripts/measure_optimization.rb +1 -2
  66. metadata +5 -2
@@ -177,7 +177,7 @@ module Fontisan
177
177
  #
178
178
  # @return [Boolean] True if version is 0-5
179
179
  def valid_version?
180
- version && version.between?(0, 5)
180
+ version&.between?(0, 5)
181
181
  end
182
182
 
183
183
  # Validation helper: Check if weight class is valid
@@ -186,7 +186,7 @@ module Fontisan
186
186
  #
187
187
  # @return [Boolean] True if weight class is valid
188
188
  def valid_weight_class?
189
- us_weight_class && us_weight_class.between?(1, 1000)
189
+ us_weight_class&.between?(1, 1000)
190
190
  end
191
191
 
192
192
  # Validation helper: Check if width class is valid
@@ -195,7 +195,7 @@ module Fontisan
195
195
  #
196
196
  # @return [Boolean] True if width class is 1-9
197
197
  def valid_width_class?
198
- us_width_class && us_width_class.between?(1, 9)
198
+ us_width_class&.between?(1, 9)
199
199
  end
200
200
 
201
201
  # Validation helper: Check if vendor ID is present
@@ -213,7 +213,7 @@ module Fontisan
213
213
  #
214
214
  # @return [Boolean] True if typo metrics have correct signs
215
215
  def valid_typo_metrics?
216
- s_typo_ascender > 0 && s_typo_descender < 0 && s_typo_line_gap >= 0
216
+ s_typo_ascender.positive? && s_typo_descender.negative? && s_typo_line_gap >= 0
217
217
  end
218
218
 
219
219
  # Validation helper: Check if Win metrics are valid
@@ -222,7 +222,7 @@ module Fontisan
222
222
  #
223
223
  # @return [Boolean] True if Win ascent and descent are positive
224
224
  def valid_win_metrics?
225
- us_win_ascent > 0 && us_win_descent > 0
225
+ us_win_ascent.positive? && us_win_descent.positive?
226
226
  end
227
227
 
228
228
  # Validation helper: Check if Unicode ranges are set
@@ -240,7 +240,7 @@ module Fontisan
240
240
  #
241
241
  # @return [Boolean] True if PANOSE seems to be set
242
242
  def has_panose?
243
- panose && panose.any? { |val| val != 0 }
243
+ panose&.any? { |val| val != 0 }
244
244
  end
245
245
 
246
246
  # Validation helper: Check if embedding permissions are set
@@ -270,9 +270,9 @@ module Fontisan
270
270
  #
271
271
  # @return [Boolean] True if metrics are present (or not required)
272
272
  def has_x_height_cap_height?
273
- return true if version < 2 # Not required for v0-1
273
+ return true if version < 2 # Not required for v0-1
274
274
 
275
- !sx_height.nil? && !s_cap_height.nil? && sx_height > 0 && s_cap_height > 0
275
+ !sx_height.nil? && !s_cap_height.nil? && sx_height.positive? && s_cap_height.positive?
276
276
  end
277
277
 
278
278
  # Validation helper: Check if first/last char indices are reasonable
@@ -177,7 +177,7 @@ module Fontisan
177
177
  #
178
178
  # @return [Boolean] True if is_fixed_pitch is 0 or 1
179
179
  def valid_fixed_pitch_flag?
180
- is_fixed_pitch == 0 || is_fixed_pitch == 1
180
+ [0, 1].include?(is_fixed_pitch)
181
181
  end
182
182
 
183
183
  # Validation helper: Check if glyph names are available
@@ -198,7 +198,7 @@ module Fontisan
198
198
  def complete_version_2_data?
199
199
  return true unless version == 2.0
200
200
 
201
- !num_glyphs_v2.nil? && num_glyphs_v2 > 0 && !remaining_data.empty?
201
+ !num_glyphs_v2.nil? && num_glyphs_v2.positive? && !remaining_data.empty?
202
202
  end
203
203
  end
204
204
  end
@@ -166,7 +166,8 @@ module Fontisan
166
166
  # Sample first few glyphs to detect formats
167
167
  strike[:graphic_types]&.each do |type|
168
168
  format_name = GRAPHIC_TYPE_NAMES[type]
169
- formats << format_name if format_name && !["dupe", "mask"].include?(format_name)
169
+ formats << format_name if format_name && !["dupe",
170
+ "mask"].include?(format_name)
170
171
  end
171
172
  end
172
173
  formats.uniq.compact
@@ -312,12 +313,12 @@ module Fontisan
312
313
 
313
314
  # Check if offsets are valid
314
315
  next if glyph_offset >= strike_size || next_glyph_offset > strike_size
315
- next if next_glyph_offset <= glyph_offset # Empty glyph
316
+ next if next_glyph_offset <= glyph_offset # Empty glyph
316
317
 
317
318
  # Calculate absolute offset in table
318
319
  # glyph_offset is relative to strike start, so add strike_offset
319
320
  absolute_offset = strike_offset + glyph_offset
320
- next if absolute_offset + 8 > raw_data.length # Need at least header
321
+ next if absolute_offset + 8 > raw_data.length # Need at least header
321
322
 
322
323
  # Read graphic type (skip originOffsetX and originOffsetY = 4 bytes)
323
324
  io = StringIO.new(raw_data)
@@ -345,7 +346,7 @@ module Fontisan
345
346
  next_offset = strike[:glyph_offsets][glyph_id + 1]
346
347
 
347
348
  return nil unless offset && next_offset
348
- return nil if next_offset <= offset # Empty glyph
349
+ return nil if next_offset <= offset # Empty glyph
349
350
 
350
351
  # Calculate absolute position in table
351
352
  absolute_offset = strike[:base_offset] + offset
@@ -563,10 +563,9 @@ module Fontisan
563
563
  # @param path [String] Path to the TTF file
564
564
  # @return [void]
565
565
  def update_checksum_adjustment_in_file(path)
566
- # Use tempfile-based checksum calculation for Windows compatibility
567
- # This keeps the tempfile alive until we're done with the checksum
568
566
  File.open(path, "r+b") do |io|
569
- checksum, _tmpfile = Utilities::ChecksumCalculator.calculate_checksum_from_io_with_tempfile(io)
567
+ # Calculate checksum directly from IO to avoid Windows Tempfile issues
568
+ checksum = Utilities::ChecksumCalculator.calculate_checksum_from_io(io)
570
569
 
571
570
  # Calculate adjustment
572
571
  adjustment = Utilities::ChecksumCalculator.calculate_adjustment(checksum)
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "stringio"
4
- require "tempfile"
5
4
  require_relative "../constants"
6
5
 
7
6
  module Fontisan
@@ -101,49 +100,6 @@ module Fontisan
101
100
 
102
101
  sum
103
102
  end
104
-
105
- # Calculate checksum from an IO object using a tempfile for Windows compatibility.
106
- #
107
- # This method creates a temporary file from the IO content to ensure proper
108
- # file handle semantics on Windows, where file handles must remain open
109
- # for checksum calculation. The tempfile reference is returned alongside
110
- # the checksum to prevent premature garbage collection on Windows.
111
- #
112
- # @param io [IO] the IO object to read from (must be rewindable)
113
- # @return [Array<Integer, Tempfile>] array containing [checksum, tempfile]
114
- # The checksum value and the tempfile that must be kept alive until
115
- # the caller is done with the checksum.
116
- #
117
- # @example
118
- # checksum, tmpfile = ChecksumCalculator.calculate_checksum_from_io_with_tempfile(io)
119
- # # Use checksum...
120
- # # tmpfile will be GC'd when it goes out of scope, which is safe
121
- #
122
- # @note On Windows, Ruby's Tempfile automatically deletes the temp file when
123
- # the Tempfile object is garbage collected. In multi-threaded environments,
124
- # this can cause PermissionDenied errors if the file is deleted while
125
- # another thread is still using it. By returning the tempfile reference,
126
- # the caller can ensure it remains alive until all operations complete.
127
- def self.calculate_checksum_from_io_with_tempfile(io)
128
- io.rewind
129
-
130
- # Create a tempfile to handle Windows file locking issues
131
- tmpfile = Tempfile.new(["font", ".ttf"])
132
- tmpfile.binmode
133
-
134
- # Copy IO content to tempfile
135
- IO.copy_stream(io, tmpfile)
136
- tmpfile.close
137
-
138
- # Calculate checksum from the tempfile
139
- checksum = calculate_file_checksum(tmpfile.path)
140
-
141
- # Return both checksum and tempfile to keep it alive
142
- # The caller must keep the tempfile reference until done with checksum
143
- [checksum, tmpfile]
144
- end
145
-
146
- private_class_method :calculate_checksum_from_io
147
103
  end
148
104
  end
149
105
  end
@@ -68,7 +68,8 @@ module Fontisan
68
68
  issues = []
69
69
 
70
70
  return ["Font array cannot be empty"] if fonts.nil? || fonts.empty?
71
- return ["Invalid format: #{format}"] unless %i[ttc otc dfont].include?(format)
71
+ return ["Invalid format: #{format}"] unless %i[ttc otc
72
+ dfont].include?(format)
72
73
 
73
74
  case format
74
75
  when :ttc
@@ -100,7 +101,8 @@ module Fontisan
100
101
  # @raise [ArgumentError] if invalid
101
102
  def validate_format!(format)
102
103
  unless %i[ttc otc dfont].include?(format)
103
- raise ArgumentError, "Invalid format: #{format}. Must be :ttc, :otc, or :dfont"
104
+ raise ArgumentError,
105
+ "Invalid format: #{format}. Must be :ttc, :otc, or :dfont"
104
106
  end
105
107
  end
106
108
 
@@ -46,39 +46,29 @@ module Fontisan
46
46
  end
47
47
 
48
48
  # Check 2: Name table version must be valid (0 or 1)
49
- check_table :name_version, "name", severity: :error do |table|
50
- table.valid_version?
51
- end
49
+ check_table :name_version, "name", severity: :error, &:valid_version?
52
50
 
53
51
  # Check 3: Family name must be present and non-empty
54
- check_table :family_name, "name", severity: :error do |table|
55
- table.family_name_present?
56
- end
52
+ check_table :family_name, "name", severity: :error,
53
+ &:family_name_present?
57
54
 
58
55
  # Check 4: PostScript name must be valid (alphanumeric + hyphens)
59
- check_table :postscript_name, "name", severity: :error do |table|
60
- table.postscript_name_valid?
61
- end
56
+ check_table :postscript_name, "name", severity: :error,
57
+ &:postscript_name_valid?
62
58
 
63
59
  # Check 5: Head table magic number must be correct
64
- check_table :head_magic, "head", severity: :error do |table|
65
- table.valid_magic?
66
- end
60
+ check_table :head_magic, "head", severity: :error, &:valid_magic?
67
61
 
68
62
  # Check 6: Units per em must be valid (16-16384)
69
- check_table :units_per_em, "head", severity: :error do |table|
70
- table.valid_units_per_em?
71
- end
63
+ check_table :units_per_em, "head", severity: :error,
64
+ &:valid_units_per_em?
72
65
 
73
66
  # Check 7: Number of glyphs must be at least 1 (.notdef)
74
- check_table :num_glyphs, "maxp", severity: :error do |table|
75
- table.valid_num_glyphs?
76
- end
67
+ check_table :num_glyphs, "maxp", severity: :error, &:valid_num_glyphs?
77
68
 
78
69
  # Check 8: Maxp metrics should be reasonable (not absurd values)
79
- check_table :reasonable_metrics, "maxp", severity: :warning do |table|
80
- table.reasonable_metrics?
81
- end
70
+ check_table :reasonable_metrics, "maxp", severity: :warning,
71
+ &:reasonable_metrics?
82
72
  end
83
73
  end
84
74
  end
@@ -36,94 +36,73 @@ module Fontisan
36
36
  super
37
37
 
38
38
  # Check 9: Name table Windows Unicode English encoding
39
- check_table :name_windows_encoding, 'name', severity: :error do |table|
39
+ check_table :name_windows_encoding, "name", severity: :error do |table|
40
40
  table.has_valid_platform_combos?([3, 1, 0x0409]) # Windows Unicode English
41
41
  end
42
42
 
43
43
  # Check 10: Name table Mac Roman English encoding
44
- check_table :name_mac_encoding, 'name', severity: :warning do |table|
44
+ check_table :name_mac_encoding, "name", severity: :warning do |table|
45
45
  table.has_valid_platform_combos?([1, 0, 0]) # Mac Roman English
46
46
  end
47
47
 
48
48
  # Check 11: OS/2 table version must be valid
49
- check_table :os2_version, 'OS/2', severity: :error do |table|
50
- table.valid_version?
51
- end
49
+ check_table :os2_version, "OS/2", severity: :error, &:valid_version?
52
50
 
53
51
  # Check 12: OS/2 weight class must be valid (1-1000)
54
- check_table :os2_weight_class, 'OS/2', severity: :error do |table|
55
- table.valid_weight_class?
56
- end
52
+ check_table :os2_weight_class, "OS/2", severity: :error,
53
+ &:valid_weight_class?
57
54
 
58
55
  # Check 13: OS/2 width class must be valid (1-9)
59
- check_table :os2_width_class, 'OS/2', severity: :error do |table|
60
- table.valid_width_class?
61
- end
56
+ check_table :os2_width_class, "OS/2", severity: :error,
57
+ &:valid_width_class?
62
58
 
63
59
  # Check 14: OS/2 vendor ID should be present
64
- check_table :os2_vendor_id, 'OS/2', severity: :warning do |table|
65
- table.has_vendor_id?
66
- end
60
+ check_table :os2_vendor_id, "OS/2", severity: :warning, &:has_vendor_id?
67
61
 
68
62
  # Check 15: OS/2 PANOSE classification should be present
69
- check_table :os2_panose, 'OS/2', severity: :info do |table|
70
- table.has_panose?
71
- end
63
+ check_table :os2_panose, "OS/2", severity: :info, &:has_panose?
72
64
 
73
65
  # Check 16: OS/2 typographic metrics must be valid
74
- check_table :os2_typo_metrics, 'OS/2', severity: :error do |table|
75
- table.valid_typo_metrics?
76
- end
66
+ check_table :os2_typo_metrics, "OS/2", severity: :error,
67
+ &:valid_typo_metrics?
77
68
 
78
69
  # Check 17: OS/2 Windows metrics must be valid
79
- check_table :os2_win_metrics, 'OS/2', severity: :error do |table|
80
- table.valid_win_metrics?
81
- end
70
+ check_table :os2_win_metrics, "OS/2", severity: :error,
71
+ &:valid_win_metrics?
82
72
 
83
73
  # Check 18: OS/2 Unicode ranges should be present
84
- check_table :os2_unicode_ranges, 'OS/2', severity: :warning do |table|
85
- table.has_unicode_ranges?
86
- end
74
+ check_table :os2_unicode_ranges, "OS/2", severity: :warning,
75
+ &:has_unicode_ranges?
87
76
 
88
77
  # Check 19: Head table bounding box must be valid
89
- check_table :head_bounding_box, 'head', severity: :error do |table|
90
- table.valid_bounding_box?
91
- end
78
+ check_table :head_bounding_box, "head", severity: :error,
79
+ &:valid_bounding_box?
92
80
 
93
81
  # Check 20: Hhea ascent/descent must be valid
94
- check_table :hhea_ascent_descent, 'hhea', severity: :error do |table|
95
- table.valid_ascent_descent?
96
- end
82
+ check_table :hhea_ascent_descent, "hhea", severity: :error,
83
+ &:valid_ascent_descent?
97
84
 
98
85
  # Check 21: Hhea line gap should be valid
99
- check_table :hhea_line_gap, 'hhea', severity: :warning do |table|
100
- table.valid_line_gap?
101
- end
86
+ check_table :hhea_line_gap, "hhea", severity: :warning,
87
+ &:valid_line_gap?
102
88
 
103
89
  # Check 22: Hhea horizontal metrics count must be valid
104
- check_table :hhea_metrics_count, 'hhea', severity: :error do |table|
105
- table.valid_number_of_h_metrics?
106
- end
90
+ check_table :hhea_metrics_count, "hhea", severity: :error,
91
+ &:valid_number_of_h_metrics?
107
92
 
108
93
  # Check 23: Post table version must be valid
109
- check_table :post_version, 'post', severity: :error do |table|
110
- table.valid_version?
111
- end
94
+ check_table :post_version, "post", severity: :error, &:valid_version?
112
95
 
113
96
  # Check 24: Post table italic angle should be valid
114
- check_table :post_italic_angle, 'post', severity: :warning do |table|
115
- table.valid_italic_angle?
116
- end
97
+ check_table :post_italic_angle, "post", severity: :warning,
98
+ &:valid_italic_angle?
117
99
 
118
100
  # Check 25: Post table underline metrics should be present
119
- check_table :post_underline, 'post', severity: :info do |table|
120
- table.has_underline_metrics?
121
- end
101
+ check_table :post_underline, "post", severity: :info,
102
+ &:has_underline_metrics?
122
103
 
123
104
  # Check 26: Cmap table must have subtables
124
- check_table :cmap_subtables, 'cmap', severity: :error do |table|
125
- table.has_subtables?
126
- end
105
+ check_table :cmap_subtables, "cmap", severity: :error, &:has_subtables?
127
106
  end
128
107
  end
129
108
  end
@@ -33,45 +33,44 @@ module Fontisan
33
33
  super
34
34
 
35
35
  # Check 27: Maxp TrueType metrics (only for version 1.0)
36
- check_table :maxp_truetype_metrics, 'maxp', severity: :warning do |table|
36
+ check_table :maxp_truetype_metrics, "maxp",
37
+ severity: :warning do |table|
37
38
  !table.version_1_0? || table.has_truetype_metrics?
38
39
  end
39
40
 
40
41
  # Check 28: Maxp max zones must be valid
41
- check_table :maxp_zones, 'maxp', severity: :error do |table|
42
- table.valid_max_zones?
43
- end
42
+ check_table :maxp_zones, "maxp", severity: :error, &:valid_max_zones?
44
43
 
45
44
  # Check 29: Glyf glyphs must be accessible (TrueType fonts only)
46
45
  check_glyphs :glyf_accessible, severity: :error do |font|
47
- glyf = font.table('glyf')
46
+ glyf = font.table("glyf")
48
47
  next true unless glyf # Skip if CFF font
49
48
 
50
- loca = font.table('loca')
51
- head = font.table('head')
52
- maxp = font.table('maxp')
49
+ loca = font.table("loca")
50
+ head = font.table("head")
51
+ maxp = font.table("maxp")
53
52
  glyf.all_glyphs_accessible?(loca, head, maxp.num_glyphs)
54
53
  end
55
54
 
56
55
  # Check 30: Glyf glyphs should not be clipped
57
56
  check_glyphs :glyf_no_clipping, severity: :warning do |font|
58
- glyf = font.table('glyf')
57
+ glyf = font.table("glyf")
59
58
  next true unless glyf
60
59
 
61
- loca = font.table('loca')
62
- head = font.table('head')
63
- maxp = font.table('maxp')
60
+ loca = font.table("loca")
61
+ head = font.table("head")
62
+ maxp = font.table("maxp")
64
63
  glyf.no_clipped_glyphs?(loca, head, maxp.num_glyphs)
65
64
  end
66
65
 
67
66
  # Check 31: Glyf contour counts must be valid
68
67
  check_glyphs :glyf_valid_contours, severity: :error do |font|
69
- glyf = font.table('glyf')
68
+ glyf = font.table("glyf")
70
69
  next true unless glyf
71
70
 
72
- loca = font.table('loca')
73
- head = font.table('head')
74
- maxp = font.table('maxp')
71
+ loca = font.table("loca")
72
+ head = font.table("head")
73
+ maxp = font.table("maxp")
75
74
 
76
75
  (0...maxp.num_glyphs).all? do |glyph_id|
77
76
  glyf.valid_contour_count?(glyph_id, loca, head)
@@ -79,29 +78,26 @@ module Fontisan
79
78
  end
80
79
 
81
80
  # Check 32: Cmap must have Unicode mapping
82
- check_table :cmap_unicode_mapping, 'cmap', severity: :error do |table|
83
- table.has_unicode_mapping?
84
- end
81
+ check_table :cmap_unicode_mapping, "cmap", severity: :error,
82
+ &:has_unicode_mapping?
85
83
 
86
84
  # Check 33: Cmap should have BMP coverage
87
- check_table :cmap_bmp_coverage, 'cmap', severity: :warning do |table|
88
- table.has_bmp_coverage?
89
- end
85
+ check_table :cmap_bmp_coverage, "cmap", severity: :warning,
86
+ &:has_bmp_coverage?
90
87
 
91
88
  # Check 34: Cmap must have format 4 subtable
92
- check_table :cmap_format4, 'cmap', severity: :error do |table|
93
- table.has_format_4_subtable?
94
- end
89
+ check_table :cmap_format4, "cmap", severity: :error,
90
+ &:has_format_4_subtable?
95
91
 
96
92
  # Check 35: Cmap glyph indices must be valid
97
93
  check_structure :cmap_glyph_indices, severity: :error do |font|
98
- cmap = font.table('cmap')
99
- maxp = font.table('maxp')
94
+ cmap = font.table("cmap")
95
+ maxp = font.table("maxp")
100
96
  cmap.valid_glyph_indices?(maxp.num_glyphs)
101
97
  end
102
98
 
103
99
  # Check 36: Table checksums (info level - many fonts have mismatches)
104
- check_structure :checksum_valid, severity: :info do |font|
100
+ check_structure :checksum_valid, severity: :info do |_font|
105
101
  # Table checksum validation (info level - for reference)
106
102
  # Most fonts have checksum mismatches, so we make it info not error
107
103
  true # Placeholder - actual checksum validation if desired