fontisan 0.2.7 → 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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +103 -0
  3. data/.rubocop_todo.yml +106 -319
  4. data/Gemfile +1 -1
  5. data/README.adoc +81 -14
  6. data/Rakefile +12 -7
  7. data/benchmark/variation_quick_bench.rb +1 -1
  8. data/lib/fontisan/cli.rb +45 -13
  9. data/lib/fontisan/collection/dfont_builder.rb +2 -1
  10. data/lib/fontisan/commands/convert_command.rb +2 -4
  11. data/lib/fontisan/commands/info_command.rb +3 -3
  12. data/lib/fontisan/commands/pack_command.rb +2 -1
  13. data/lib/fontisan/commands/validate_command.rb +157 -6
  14. data/lib/fontisan/converters/collection_converter.rb +22 -13
  15. data/lib/fontisan/converters/svg_generator.rb +2 -1
  16. data/lib/fontisan/converters/woff2_encoder.rb +6 -6
  17. data/lib/fontisan/converters/woff_writer.rb +3 -1
  18. data/lib/fontisan/font_loader.rb +7 -6
  19. data/lib/fontisan/formatters/text_formatter.rb +18 -14
  20. data/lib/fontisan/hints/hint_converter.rb +1 -1
  21. data/lib/fontisan/hints/hint_validator.rb +13 -10
  22. data/lib/fontisan/hints/truetype_instruction_analyzer.rb +15 -8
  23. data/lib/fontisan/hints/truetype_instruction_generator.rb +1 -1
  24. data/lib/fontisan/models/collection_validation_report.rb +104 -0
  25. data/lib/fontisan/models/font_report.rb +24 -0
  26. data/lib/fontisan/models/validation_report.rb +7 -2
  27. data/lib/fontisan/open_type_font.rb +2 -3
  28. data/lib/fontisan/optimizers/charstring_rewriter.rb +1 -1
  29. data/lib/fontisan/optimizers/subroutine_optimizer.rb +6 -2
  30. data/lib/fontisan/subset/glyph_mapping.rb +2 -0
  31. data/lib/fontisan/subset/table_subsetter.rb +2 -2
  32. data/lib/fontisan/tables/cblc.rb +8 -4
  33. data/lib/fontisan/tables/cff/index.rb +2 -0
  34. data/lib/fontisan/tables/cff.rb +6 -3
  35. data/lib/fontisan/tables/cff2/private_dict_blend_handler.rb +1 -1
  36. data/lib/fontisan/tables/cff2.rb +1 -1
  37. data/lib/fontisan/tables/cmap.rb +5 -5
  38. data/lib/fontisan/tables/glyf.rb +8 -10
  39. data/lib/fontisan/tables/head.rb +3 -3
  40. data/lib/fontisan/tables/hhea.rb +4 -4
  41. data/lib/fontisan/tables/maxp.rb +2 -2
  42. data/lib/fontisan/tables/name.rb +1 -1
  43. data/lib/fontisan/tables/os2.rb +8 -8
  44. data/lib/fontisan/tables/post.rb +2 -2
  45. data/lib/fontisan/tables/sbix.rb +5 -4
  46. data/lib/fontisan/true_type_font.rb +2 -3
  47. data/lib/fontisan/utilities/checksum_calculator.rb +0 -44
  48. data/lib/fontisan/validation/collection_validator.rb +4 -2
  49. data/lib/fontisan/validators/basic_validator.rb +11 -21
  50. data/lib/fontisan/validators/font_book_validator.rb +29 -50
  51. data/lib/fontisan/validators/opentype_validator.rb +24 -28
  52. data/lib/fontisan/validators/validator.rb +87 -66
  53. data/lib/fontisan/validators/web_font_validator.rb +16 -21
  54. data/lib/fontisan/version.rb +1 -1
  55. data/lib/fontisan/woff2/glyf_transformer.rb +31 -8
  56. data/lib/fontisan/woff2/hmtx_transformer.rb +2 -1
  57. data/lib/fontisan/woff2/table_transformer.rb +4 -2
  58. data/lib/fontisan/woff2_font.rb +4 -2
  59. data/lib/fontisan/woff_font.rb +2 -2
  60. data/lib/fontisan.rb +2 -2
  61. data/scripts/compare_stack_aware.rb +1 -1
  62. data/scripts/measure_optimization.rb +1 -2
  63. metadata +4 -2
@@ -80,7 +80,8 @@ module Fontisan
80
80
  # @param block [Proc] Check logic that receives (table, value) as parameters
81
81
  def check_field(check_id, field_key, severity: :error, &block)
82
82
  unless @current_table_context
83
- raise ArgumentError, "check_field must be called within check_table block"
83
+ raise ArgumentError,
84
+ "check_field must be called within check_table block"
84
85
  end
85
86
 
86
87
  @checks << {
@@ -194,7 +195,7 @@ module Fontisan
194
195
  issues: [],
195
196
  }
196
197
  end
197
- rescue => e
198
+ rescue StandardError => e
198
199
  {
199
200
  check_id: check_def[:id],
200
201
  passed: false,
@@ -239,7 +240,7 @@ module Fontisan
239
240
 
240
241
  begin
241
242
  result = check_def[:block].call(table)
242
- passed = result != false && result != nil
243
+ passed = result != false && !result.nil?
243
244
 
244
245
  {
245
246
  check_id: check_def[:id],
@@ -247,12 +248,16 @@ module Fontisan
247
248
  severity: check_def[:severity].to_s,
248
249
  messages: passed ? [] : ["Table '#{table_tag}' validation failed"],
249
250
  table: table_tag,
250
- issues: passed ? [] : [{
251
- severity: check_def[:severity].to_s,
252
- category: "table_validation",
253
- table: table_tag,
254
- message: "Table '#{table_tag}' failed validation",
255
- }],
251
+ issues: if passed
252
+ []
253
+ else
254
+ [{
255
+ severity: check_def[:severity].to_s,
256
+ category: "table_validation",
257
+ table: table_tag,
258
+ message: "Table '#{table_tag}' failed validation",
259
+ }]
260
+ end,
256
261
  }
257
262
  ensure
258
263
  @current_table_context = old_context
@@ -290,12 +295,10 @@ module Fontisan
290
295
  # Get field value
291
296
  value = if table.respond_to?(field_key)
292
297
  table.public_send(field_key)
293
- else
294
- nil
295
298
  end
296
299
 
297
300
  result = check_def[:block].call(table, value)
298
- passed = result != false && result != nil
301
+ passed = result != false && !result.nil?
299
302
 
300
303
  {
301
304
  check_id: check_def[:id],
@@ -304,13 +307,17 @@ module Fontisan
304
307
  messages: passed ? [] : ["Field '#{field_key}' validation failed"],
305
308
  table: table_tag,
306
309
  field: field_key.to_s,
307
- issues: passed ? [] : [{
308
- severity: check_def[:severity].to_s,
309
- category: "field_validation",
310
- table: table_tag,
311
- field: field_key.to_s,
312
- message: "Field '#{field_key}' in table '#{table_tag}' failed validation",
313
- }],
310
+ issues: if passed
311
+ []
312
+ else
313
+ [{
314
+ severity: check_def[:severity].to_s,
315
+ category: "field_validation",
316
+ table: table_tag,
317
+ field: field_key.to_s,
318
+ message: "Field '#{field_key}' in table '#{table_tag}' failed validation",
319
+ }]
320
+ end,
314
321
  }
315
322
  end
316
323
 
@@ -321,18 +328,22 @@ module Fontisan
321
328
  # @return [Hash] Check result
322
329
  def execute_structure_check(font, check_def)
323
330
  result = check_def[:block].call(font)
324
- passed = result != false && result != nil
331
+ passed = result != false && !result.nil?
325
332
 
326
333
  {
327
334
  check_id: check_def[:id],
328
335
  passed: passed,
329
336
  severity: check_def[:severity].to_s,
330
337
  messages: passed ? [] : ["Structure validation failed"],
331
- issues: passed ? [] : [{
332
- severity: check_def[:severity].to_s,
333
- category: "structure",
334
- message: "Font structure validation failed for check '#{check_def[:id]}'",
335
- }],
338
+ issues: if passed
339
+ []
340
+ else
341
+ [{
342
+ severity: check_def[:severity].to_s,
343
+ category: "structure",
344
+ message: "Font structure validation failed for check '#{check_def[:id]}'",
345
+ }]
346
+ end,
336
347
  }
337
348
  end
338
349
 
@@ -343,18 +354,22 @@ module Fontisan
343
354
  # @return [Hash] Check result
344
355
  def execute_usability_check(font, check_def)
345
356
  result = check_def[:block].call(font)
346
- passed = result != false && result != nil
357
+ passed = result != false && !result.nil?
347
358
 
348
359
  {
349
360
  check_id: check_def[:id],
350
361
  passed: passed,
351
362
  severity: check_def[:severity].to_s,
352
363
  messages: passed ? [] : ["Usability check failed"],
353
- issues: passed ? [] : [{
354
- severity: check_def[:severity].to_s,
355
- category: "usability",
356
- message: "Font usability check failed for '#{check_def[:id]}'",
357
- }],
364
+ issues: if passed
365
+ []
366
+ else
367
+ [{
368
+ severity: check_def[:severity].to_s,
369
+ category: "usability",
370
+ message: "Font usability check failed for '#{check_def[:id]}'",
371
+ }]
372
+ end,
358
373
  }
359
374
  end
360
375
 
@@ -365,18 +380,22 @@ module Fontisan
365
380
  # @return [Hash] Check result
366
381
  def execute_instruction_check(font, check_def)
367
382
  result = check_def[:block].call(font)
368
- passed = result != false && result != nil
383
+ passed = result != false && !result.nil?
369
384
 
370
385
  {
371
386
  check_id: check_def[:id],
372
387
  passed: passed,
373
388
  severity: check_def[:severity].to_s,
374
389
  messages: passed ? [] : ["Instruction validation failed"],
375
- issues: passed ? [] : [{
376
- severity: check_def[:severity].to_s,
377
- category: "instructions",
378
- message: "TrueType instruction check failed for '#{check_def[:id]}'",
379
- }],
390
+ issues: if passed
391
+ []
392
+ else
393
+ [{
394
+ severity: check_def[:severity].to_s,
395
+ category: "instructions",
396
+ message: "TrueType instruction check failed for '#{check_def[:id]}'",
397
+ }]
398
+ end,
380
399
  }
381
400
  end
382
401
 
@@ -387,18 +406,22 @@ module Fontisan
387
406
  # @return [Hash] Check result
388
407
  def execute_glyph_check(font, check_def)
389
408
  result = check_def[:block].call(font)
390
- passed = result != false && result != nil
409
+ passed = result != false && !result.nil?
391
410
 
392
411
  {
393
412
  check_id: check_def[:id],
394
413
  passed: passed,
395
414
  severity: check_def[:severity].to_s,
396
415
  messages: passed ? [] : ["Glyph validation failed"],
397
- issues: passed ? [] : [{
398
- severity: check_def[:severity].to_s,
399
- category: "glyphs",
400
- message: "Glyph validation failed for check '#{check_def[:id]}'",
401
- }],
416
+ issues: if passed
417
+ []
418
+ else
419
+ [{
420
+ severity: check_def[:severity].to_s,
421
+ category: "glyphs",
422
+ message: "Glyph validation failed for check '#{check_def[:id]}'",
423
+ }]
424
+ end,
402
425
  }
403
426
  end
404
427
 
@@ -408,7 +431,7 @@ module Fontisan
408
431
  # @param all_results [Array<Hash>] All check results
409
432
  # @param elapsed [Float] Elapsed time in seconds
410
433
  # @return [ValidationReport] Complete report
411
- def build_report(font, all_results, elapsed)
434
+ def build_report(font, all_results, _elapsed)
412
435
  # Extract font path from font object
413
436
  font_path = if font.respond_to?(:path)
414
437
  font.path
@@ -438,28 +461,26 @@ module Fontisan
438
461
  report.check_results << check_result
439
462
 
440
463
  # Add issues to main report
441
- if result[:issues]
442
- result[:issues].each do |issue_data|
443
- case issue_data[:severity]
444
- when "error", "fatal"
445
- report.add_error(
446
- issue_data[:category] || "validation",
447
- issue_data[:message],
448
- issue_data[:table] || issue_data[:field],
449
- )
450
- when "warning"
451
- report.add_warning(
452
- issue_data[:category] || "validation",
453
- issue_data[:message],
454
- issue_data[:table] || issue_data[:field],
455
- )
456
- when "info"
457
- report.add_info(
458
- issue_data[:category] || "validation",
459
- issue_data[:message],
460
- issue_data[:table] || issue_data[:field],
461
- )
462
- end
464
+ result[:issues]&.each do |issue_data|
465
+ case issue_data[:severity]
466
+ when "error", "fatal"
467
+ report.add_error(
468
+ issue_data[:category] || "validation",
469
+ issue_data[:message],
470
+ issue_data[:table] || issue_data[:field],
471
+ )
472
+ when "warning"
473
+ report.add_warning(
474
+ issue_data[:category] || "validation",
475
+ issue_data[:message],
476
+ issue_data[:table] || issue_data[:field],
477
+ )
478
+ when "info"
479
+ report.add_info(
480
+ issue_data[:category] || "validation",
481
+ issue_data[:message],
482
+ issue_data[:table] || issue_data[:field],
483
+ )
463
484
  end
464
485
  end
465
486
  end
@@ -33,18 +33,16 @@ module Fontisan
33
33
  super
34
34
 
35
35
  # Check 9: OS/2 embedding permissions must allow web use
36
- check_table :embedding_permissions, 'OS/2', severity: :error do |table|
37
- table.has_embedding_permissions?
38
- end
36
+ check_table :embedding_permissions, "OS/2", severity: :error,
37
+ &:has_embedding_permissions?
39
38
 
40
39
  # Check 10: OS/2 version should be present
41
- check_table :os2_version_web, 'OS/2', severity: :warning do |table|
42
- table.valid_version?
43
- end
40
+ check_table :os2_version_web, "OS/2", severity: :warning,
41
+ &:valid_version?
44
42
 
45
43
  # Check 11: Glyph complexity should be reasonable for web
46
44
  check_glyphs :no_complex_glyphs, severity: :warning do |font|
47
- maxp = font.table('maxp')
45
+ maxp = font.table("maxp")
48
46
  next true unless maxp.version_1_0?
49
47
 
50
48
  # Check max points and contours are reasonable for web rendering
@@ -53,33 +51,30 @@ module Fontisan
53
51
  end
54
52
 
55
53
  # Check 12: Cmap must have Unicode mapping for web
56
- check_table :character_coverage, 'cmap', severity: :error do |table|
57
- table.has_unicode_mapping?
58
- end
54
+ check_table :character_coverage, "cmap", severity: :error,
55
+ &:has_unicode_mapping?
59
56
 
60
57
  # Check 13: Cmap should have BMP coverage
61
- check_table :cmap_bmp_web, 'cmap', severity: :warning do |table|
62
- table.has_bmp_coverage?
63
- end
58
+ check_table :cmap_bmp_web, "cmap", severity: :warning,
59
+ &:has_bmp_coverage?
64
60
 
65
61
  # Check 14: Glyf glyphs must be accessible (web browsers need this)
66
62
  check_glyphs :glyph_accessible_web, severity: :error do |font|
67
- glyf = font.table('glyf')
63
+ glyf = font.table("glyf")
68
64
  next true unless glyf
69
65
 
70
- loca = font.table('loca')
71
- head = font.table('head')
72
- maxp = font.table('maxp')
66
+ loca = font.table("loca")
67
+ head = font.table("head")
68
+ maxp = font.table("maxp")
73
69
  glyf.all_glyphs_accessible?(loca, head, maxp.num_glyphs)
74
70
  end
75
71
 
76
72
  # Check 15: Head table must have valid bounding box
77
- check_table :head_bbox_web, 'head', severity: :error do |table|
78
- table.valid_bounding_box?
79
- end
73
+ check_table :head_bbox_web, "head", severity: :error,
74
+ &:valid_bounding_box?
80
75
 
81
76
  # Check 16: Hhea metrics must be valid for web rendering
82
- check_table :hhea_metrics_web, 'hhea', severity: :error do |table|
77
+ check_table :hhea_metrics_web, "hhea", severity: :error do |table|
83
78
  table.valid_ascent_descent? && table.valid_number_of_h_metrics?
84
79
  end
85
80
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fontisan
4
- VERSION = "0.2.7"
4
+ VERSION = "0.2.8"
5
5
  end
@@ -265,16 +265,33 @@ module Fontisan
265
265
  glyph_io, bbox_io, instruction_io)
266
266
  # Read end points of contours
267
267
  end_pts_of_contours = []
268
+ max_points_per_glyph = 100000 # Sanity limit
269
+
268
270
  num_contours.times do
269
271
  if end_pts_of_contours.empty?
270
- end_pts_of_contours << read_255_uint16(n_points_io)
272
+ val = read_255_uint16(n_points_io)
273
+ end_pts_of_contours << val
271
274
  else
272
275
  delta = read_255_uint16(n_points_io)
273
- end_pts_of_contours << end_pts_of_contours.last + delta + 1
276
+ next_end_pt = end_pts_of_contours.last + delta + 1
277
+
278
+ # Sanity check to prevent explosion from corrupted data
279
+ if delta > 10000 || next_end_pt > max_points_per_glyph
280
+ # Data appears corrupted, stop reading
281
+ break
282
+ end
283
+
284
+ end_pts_of_contours << next_end_pt
274
285
  end
275
286
  end
276
287
 
277
- total_points = end_pts_of_contours.last + 1
288
+ # Handle case where stream was corrupted
289
+ if end_pts_of_contours.empty?
290
+ # Return minimal empty glyph
291
+ return ["\x00\x00"].pack("n") * 5 # Empty glyph: num_contours=0, bbox=0
292
+ end
293
+
294
+ total_points = [end_pts_of_contours.last + 1, max_points_per_glyph].min
278
295
 
279
296
  # Read flags
280
297
  flags = read_flags(flag_io, total_points)
@@ -517,6 +534,15 @@ instruction_io, variable_font: false)
517
534
  flags = []
518
535
 
519
536
  while flags.size < count
537
+ # Safety check to prevent infinite loops with corrupted streams
538
+ if flags.size > 200000
539
+ # Stream appears corrupted, pad with zeros
540
+ while flags.size < count
541
+ flags << 0
542
+ end
543
+ break
544
+ end
545
+
520
546
  # EOF protection for variable fonts
521
547
  break if io.eof? || (io.size - io.pos) < 1
522
548
 
@@ -527,15 +553,12 @@ instruction_io, variable_font: false)
527
553
  break if io.eof? || (io.size - io.pos) < 1
528
554
 
529
555
  repeat_count = read_uint8(io)
556
+ # Safety check on repeat count
557
+ repeat_count = [repeat_count, 100].min
530
558
  repeat_count.times { flags << flag }
531
559
  end
532
560
  end
533
561
 
534
- # Pad with zero flags if needed
535
- while flags.size < count
536
- flags << 0
537
- end
538
-
539
562
  flags
540
563
  end
541
564
 
@@ -33,7 +33,8 @@ module Fontisan
33
33
  # @param glyf_lsbs [Array<Integer>, nil] LSB values from glyf bboxes (optional)
34
34
  # @return [String] Standard hmtx table data
35
35
  # @raise [InvalidFontError] If data is corrupted or invalid
36
- def self.reconstruct(transformed_data, num_glyphs, num_h_metrics, glyf_lsbs = nil)
36
+ def self.reconstruct(transformed_data, num_glyphs, num_h_metrics,
37
+ glyf_lsbs = nil)
37
38
  io = StringIO.new(transformed_data)
38
39
 
39
40
  # Read transformation flags
@@ -138,7 +138,8 @@ module Fontisan
138
138
  num_glyphs = maxp_table.num_glyphs
139
139
 
140
140
  # Parse hmtx table
141
- advance_widths, lsbs = parse_hmtx_table(hmtx_data, num_h_metrics, num_glyphs)
141
+ advance_widths, lsbs = parse_hmtx_table(hmtx_data, num_h_metrics,
142
+ num_glyphs)
142
143
 
143
144
  # Build transformed hmtx table
144
145
  build_transformed_hmtx(advance_widths, lsbs, num_h_metrics, num_glyphs)
@@ -388,7 +389,8 @@ module Fontisan
388
389
  # @param num_h_metrics [Integer] Number of hMetric entries
389
390
  # @param num_glyphs [Integer] Total number of glyphs
390
391
  # @return [String] Transformed hmtx data
391
- def build_transformed_hmtx(advance_widths, lsbs, num_h_metrics, num_glyphs)
392
+ def build_transformed_hmtx(advance_widths, lsbs, num_h_metrics,
393
+ num_glyphs)
392
394
  data = String.new(encoding: Encoding::BINARY)
393
395
 
394
396
  # Flags: Use proportional encoding (not explicit) and explicit LSBs
@@ -666,7 +666,8 @@ module Fontisan
666
666
  offset += padding
667
667
  end
668
668
 
669
- # Write table directory
669
+ # Write table directory (all entries first)
670
+ # rubocop:disable Style/CombinableLoops - Must write directory entries first, then data
670
671
  table_records.each do |record|
671
672
  sfnt_data << record[:tag].ljust(4, "\x00")
672
673
  sfnt_data << [record[:checksum]].pack("N")
@@ -674,7 +675,7 @@ module Fontisan
674
675
  sfnt_data << [record[:length]].pack("N")
675
676
  end
676
677
 
677
- # Write table data with padding
678
+ # Then write all table data with padding
678
679
  table_records.each do |record|
679
680
  sfnt_data << record[:data]
680
681
 
@@ -683,6 +684,7 @@ module Fontisan
683
684
  Constants::TABLE_ALIGNMENT
684
685
  sfnt_data << ("\x00" * padding) if padding.positive?
685
686
  end
687
+ # rubocop:enable Style/CombinableLoops
686
688
 
687
689
  # Update checksumAdjustment in head table
688
690
  update_checksum_in_memory(sfnt_data, table_records)
@@ -485,9 +485,9 @@ module Fontisan
485
485
 
486
486
  return unless head_offset
487
487
 
488
- # Use tempfile-based checksum calculation for Windows compatibility
488
+ # Calculate checksum directly from IO to avoid Windows Tempfile issues
489
489
  File.open(path, "r+b") do |io|
490
- checksum, _tmpfile = Utilities::ChecksumCalculator.calculate_checksum_from_io_with_tempfile(io)
490
+ checksum = Utilities::ChecksumCalculator.calculate_checksum_from_io(io)
491
491
 
492
492
  # Calculate adjustment
493
493
  adjustment = Utilities::ChecksumCalculator.calculate_adjustment(checksum)
data/lib/fontisan.rb CHANGED
@@ -112,6 +112,8 @@ require_relative "fontisan/models/scripts_info"
112
112
  require_relative "fontisan/models/features_info"
113
113
  require_relative "fontisan/models/all_scripts_features_info"
114
114
  require_relative "fontisan/models/validation_report"
115
+ require_relative "fontisan/models/font_report"
116
+ require_relative "fontisan/models/collection_validation_report"
115
117
  require_relative "fontisan/models/font_export"
116
118
  require_relative "fontisan/models/collection_font_summary"
117
119
  require_relative "fontisan/models/collection_info"
@@ -319,8 +321,6 @@ module Fontisan
319
321
  end
320
322
 
321
323
  class << self
322
- private
323
-
324
324
  # Get loading mode for validation profile
325
325
  #
326
326
  # Temporarily disabled - will be reimplemented with new DSL framework
@@ -153,7 +153,7 @@ def main
153
153
  puts "\nStack-Aware Efficiency: #{efficiency_ratio}% of normal optimization"
154
154
  end
155
155
 
156
- time_overhead = ((stack_aware[:time] / unoptimized[:time] - 1) * 100).round(2)
156
+ time_overhead = (((stack_aware[:time] / unoptimized[:time]) - 1) * 100).round(2)
157
157
  puts "\nProcessing Time Overhead:"
158
158
  puts " Stack-Aware vs Unoptimized: +#{time_overhead}%"
159
159
 
@@ -96,8 +96,7 @@ def print_summary(results)
96
96
  puts "\n\n#{'=' * 80}"
97
97
  puts "SUMMARY TABLE"
98
98
  puts "=" * 80
99
- puts format("%-30s %8s %12s %12s %10s %8s",
100
- "Font", "Glyphs", "Before", "After", "Saved", "Reduction")
99
+ puts "Font Glyphs Before After Saved Reduction"
101
100
  puts "-" * 80
102
101
 
103
102
  results.each do |r|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fontisan
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.7
4
+ version: 0.2.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-01-06 00:00:00.000000000 Z
11
+ date: 2026-01-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64
@@ -203,12 +203,14 @@ files:
203
203
  - lib/fontisan/models/collection_font_summary.rb
204
204
  - lib/fontisan/models/collection_info.rb
205
205
  - lib/fontisan/models/collection_list_info.rb
206
+ - lib/fontisan/models/collection_validation_report.rb
206
207
  - lib/fontisan/models/color_glyph.rb
207
208
  - lib/fontisan/models/color_layer.rb
208
209
  - lib/fontisan/models/color_palette.rb
209
210
  - lib/fontisan/models/features_info.rb
210
211
  - lib/fontisan/models/font_export.rb
211
212
  - lib/fontisan/models/font_info.rb
213
+ - lib/fontisan/models/font_report.rb
212
214
  - lib/fontisan/models/font_summary.rb
213
215
  - lib/fontisan/models/glyph_info.rb
214
216
  - lib/fontisan/models/glyph_outline.rb