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.
- checksums.yaml +4 -4
- data/.rubocop.yml +103 -0
- data/.rubocop_todo.yml +106 -319
- data/Gemfile +1 -1
- data/README.adoc +81 -14
- data/Rakefile +12 -7
- data/benchmark/variation_quick_bench.rb +1 -1
- data/lib/fontisan/cli.rb +45 -13
- data/lib/fontisan/collection/dfont_builder.rb +2 -1
- data/lib/fontisan/commands/convert_command.rb +2 -4
- data/lib/fontisan/commands/info_command.rb +3 -3
- data/lib/fontisan/commands/pack_command.rb +2 -1
- data/lib/fontisan/commands/validate_command.rb +157 -6
- data/lib/fontisan/converters/collection_converter.rb +22 -13
- data/lib/fontisan/converters/svg_generator.rb +2 -1
- data/lib/fontisan/converters/woff2_encoder.rb +6 -6
- data/lib/fontisan/converters/woff_writer.rb +3 -1
- data/lib/fontisan/font_loader.rb +7 -6
- data/lib/fontisan/formatters/text_formatter.rb +18 -14
- data/lib/fontisan/hints/hint_converter.rb +1 -1
- data/lib/fontisan/hints/hint_validator.rb +13 -10
- data/lib/fontisan/hints/truetype_instruction_analyzer.rb +15 -8
- data/lib/fontisan/hints/truetype_instruction_generator.rb +1 -1
- data/lib/fontisan/models/collection_validation_report.rb +104 -0
- data/lib/fontisan/models/font_report.rb +24 -0
- data/lib/fontisan/models/validation_report.rb +7 -2
- data/lib/fontisan/open_type_font.rb +2 -3
- data/lib/fontisan/optimizers/charstring_rewriter.rb +1 -1
- data/lib/fontisan/optimizers/subroutine_optimizer.rb +6 -2
- data/lib/fontisan/subset/glyph_mapping.rb +2 -0
- data/lib/fontisan/subset/table_subsetter.rb +2 -2
- data/lib/fontisan/tables/cblc.rb +8 -4
- data/lib/fontisan/tables/cff/index.rb +2 -0
- data/lib/fontisan/tables/cff.rb +6 -3
- data/lib/fontisan/tables/cff2/private_dict_blend_handler.rb +1 -1
- data/lib/fontisan/tables/cff2.rb +1 -1
- data/lib/fontisan/tables/cmap.rb +5 -5
- data/lib/fontisan/tables/glyf.rb +8 -10
- data/lib/fontisan/tables/head.rb +3 -3
- data/lib/fontisan/tables/hhea.rb +4 -4
- data/lib/fontisan/tables/maxp.rb +2 -2
- data/lib/fontisan/tables/name.rb +1 -1
- data/lib/fontisan/tables/os2.rb +8 -8
- data/lib/fontisan/tables/post.rb +2 -2
- data/lib/fontisan/tables/sbix.rb +5 -4
- data/lib/fontisan/true_type_font.rb +2 -3
- data/lib/fontisan/utilities/checksum_calculator.rb +0 -44
- data/lib/fontisan/validation/collection_validator.rb +4 -2
- data/lib/fontisan/validators/basic_validator.rb +11 -21
- data/lib/fontisan/validators/font_book_validator.rb +29 -50
- data/lib/fontisan/validators/opentype_validator.rb +24 -28
- data/lib/fontisan/validators/validator.rb +87 -66
- data/lib/fontisan/validators/web_font_validator.rb +16 -21
- data/lib/fontisan/version.rb +1 -1
- data/lib/fontisan/woff2/glyf_transformer.rb +31 -8
- data/lib/fontisan/woff2/hmtx_transformer.rb +2 -1
- data/lib/fontisan/woff2/table_transformer.rb +4 -2
- data/lib/fontisan/woff2_font.rb +4 -2
- data/lib/fontisan/woff_font.rb +2 -2
- data/lib/fontisan.rb +2 -2
- data/scripts/compare_stack_aware.rb +1 -1
- data/scripts/measure_optimization.rb +1 -2
- 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,
|
|
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
|
|
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
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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
|
|
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
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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
|
|
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
|
-
|
|
333
|
-
|
|
334
|
-
|
|
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
|
|
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
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
|
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
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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
|
|
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
|
-
|
|
399
|
-
|
|
400
|
-
|
|
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,
|
|
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
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
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,
|
|
37
|
-
|
|
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,
|
|
42
|
-
|
|
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(
|
|
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,
|
|
57
|
-
|
|
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,
|
|
62
|
-
|
|
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(
|
|
63
|
+
glyf = font.table("glyf")
|
|
68
64
|
next true unless glyf
|
|
69
65
|
|
|
70
|
-
loca = font.table(
|
|
71
|
-
head = font.table(
|
|
72
|
-
maxp = font.table(
|
|
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,
|
|
78
|
-
|
|
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,
|
|
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
|
|
data/lib/fontisan/version.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
data/lib/fontisan/woff2_font.rb
CHANGED
|
@@ -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
|
-
#
|
|
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)
|
data/lib/fontisan/woff_font.rb
CHANGED
|
@@ -485,9 +485,9 @@ module Fontisan
|
|
|
485
485
|
|
|
486
486
|
return unless head_offset
|
|
487
487
|
|
|
488
|
-
#
|
|
488
|
+
# Calculate checksum directly from IO to avoid Windows Tempfile issues
|
|
489
489
|
File.open(path, "r+b") do |io|
|
|
490
|
-
checksum
|
|
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
|
|
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.
|
|
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-
|
|
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
|