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
data/README.adoc CHANGED
@@ -424,6 +424,11 @@ tables:
424
424
  length: 17870
425
425
  offset: 542992
426
426
  checksum: 701383168
427
+ - tag: OS/2
428
+ length: 96
429
+ offset: 392
430
+ checksum: 1193824355
431
+ ...
427
432
  ----
428
433
  ====
429
434
 
@@ -505,7 +510,7 @@ Syntax:
505
510
  $ fontisan unicode FONT_FILE [--format FORMAT]
506
511
  ----
507
512
 
508
- Where,
513
+ Where:
509
514
 
510
515
  `FONT_FILE`:: Path to the font file (OTF, TTF, or TTC)
511
516
  `FORMAT`:: Output format: `text` (default), `json`, or `yaml`
@@ -734,10 +739,8 @@ Dry-run mode: Preview of instance generation
734
739
  Coordinates:
735
740
  wght: 700.0
736
741
 
737
- Output would be written to: variable-instance.ttf
738
- Output
739
-
740
- format: same as input
742
+ Output file: variable-instance.ttf
743
+ Format: same as input
741
744
 
742
745
  Use without --dry-run to actually generate the instance.
743
746
  ----
@@ -1020,6 +1023,492 @@ Uses base64 encoding for binary data instead of hexadecimal, useful for
1020
1023
  JSON-based workflows.
1021
1024
  ====
1022
1025
 
1026
+ == Font validation
1027
+
1028
+ === General
1029
+
1030
+ Fontisan provides validation functionality to ensure font quality, structural
1031
+ integrity, and compliance with various standards.
1032
+
1033
+ The validation framework allows developers to create custom validators using a
1034
+ declarative DSL. Validators can perform checks on font tables, fields,
1035
+ structures, usability, instructions, and glyphs.
1036
+
1037
+ === Predefined profiles
1038
+
1039
+ Fontisan includes several predefined validation profiles for common use cases:
1040
+
1041
+ `indexability`:: Fast validation for font discovery and indexing (< 50ms). Uses
1042
+ BasicValidator with 8 essential checks. Loading mode: metadata.
1043
+
1044
+ `usability`:: Basic usability for font installation. Uses FontBookValidator with
1045
+ 26 checks including macOS Font Book compatibility. Loading mode: full.
1046
+
1047
+ `production`:: Comprehensive quality checks for production fonts (default
1048
+ profile). Uses OpenTypeValidator with 36 checks for OpenType spec compliance.
1049
+ Loading mode: full.
1050
+
1051
+ `web`:: Web font embedding and optimization validation. Uses WebFontValidator
1052
+ with 18 checks for web deployment. Loading mode: full.
1053
+
1054
+ `spec_compliance`:: Full OpenType specification compliance with detailed checks.
1055
+ Uses OpenTypeValidator with info-level severity for comprehensive analysis.
1056
+ Loading mode: full.
1057
+
1058
+ `default`:: Alias for the production profile.
1059
+
1060
+
1061
+ === Command-line usage
1062
+
1063
+ ==== Validate a font file against a predefined profile
1064
+
1065
+ .Validate with default profile
1066
+ [example]
1067
+ ====
1068
+ [source,shell]
1069
+ ----
1070
+ $ fontisan validate font.ttf
1071
+ ----
1072
+ ====
1073
+
1074
+ .Validate for web use
1075
+ [example]
1076
+ ====
1077
+ [source,shell]
1078
+ ----
1079
+ $ fontisan validate font.ttf -t web
1080
+ ----
1081
+ ====
1082
+
1083
+ .List available profiles
1084
+ [example]
1085
+ ====
1086
+ [source,shell]
1087
+ ----
1088
+ $ fontisan validate --list
1089
+ Available validation profiles:
1090
+ indexability - Fast validation for font discovery and indexing
1091
+ usability - Basic usability for installation
1092
+ production - Comprehensive quality checks
1093
+ web - Web embedding and optimization
1094
+ spec_compliance - Full OpenType spec compliance
1095
+ default - Default validation profile (alias for production)
1096
+ ----
1097
+ ====
1098
+
1099
+ .Full report to file
1100
+ [example]
1101
+ ====
1102
+ [source,shell]
1103
+ ----
1104
+ $ fontisan validate font.ttf -t production -r -o report.txt
1105
+ ----
1106
+ ====
1107
+
1108
+ .Summary with return value
1109
+ [example]
1110
+ ====
1111
+ [source,shell]
1112
+ ----
1113
+ $ fontisan validate font.ttf -S -R
1114
+ 0 errors, 2 warnings, 0 info
1115
+ $ echo $?
1116
+ 4 # Exit code 4 indicates warnings found
1117
+ ----
1118
+ ====
1119
+
1120
+ .Table format output
1121
+ [example]
1122
+ ====
1123
+ [source,shell]
1124
+ ----
1125
+ $ fontisan validate font.ttf -T
1126
+ CHECK_ID | STATUS | SEVERITY | TABLE
1127
+ ------------------------------------------------------------
1128
+ required_tables | PASS | error | N/A
1129
+ name_version | PASS | error | name
1130
+ family_name | PASS | error | name
1131
+ ...
1132
+ ----
1133
+ ====
1134
+
1135
+ ==== CLI options
1136
+
1137
+ -t, --test-list PROFILE:: Select validation profile (indexability, usability,
1138
+ production, web, spec_compliance, default)
1139
+
1140
+ -l, --list:: List available validation profiles
1141
+
1142
+ -o, --output FILE:: Write report to file instead of stdout
1143
+
1144
+ -r, --full-report:: Generate full detailed report
1145
+
1146
+ -R, --return-value-results:: Use return value to indicate results (0=none,
1147
+ 1=error, 2=fatal, 3=major, 4=minor, 5=info)
1148
+
1149
+ -S, --summary-report:: Generate brief summary report
1150
+
1151
+ -T, --table-report:: Generate tabular format report
1152
+
1153
+ -v, --verbose:: Enable verbose output
1154
+
1155
+ -w, --suppress-warnings:: Suppress warning output
1156
+
1157
+ -e, --exclude CHECKS:: Exclude specific checks (comma-separated list)
1158
+
1159
+
1160
+ === Ruby API usage
1161
+
1162
+ ==== Architecture
1163
+
1164
+ The validation framework in Ruby consists of:
1165
+
1166
+ DSL base class:: `Fontisan::Validators::Validator` provides a declarative syntax
1167
+ for defining validation checks
1168
+
1169
+ Table validation helpers::
1170
+ 56 helper methods across 8 core OpenType tables (name, head, maxp, hhea, glyf,
1171
+ cmap, post, OS/2) that perform specific validation checks
1172
+
1173
+ ValidationReport::
1174
+ Structured reports with individual check results, severity levels, and
1175
+ comprehensive issue tracking
1176
+
1177
+ ==== Using predefined profiles
1178
+
1179
+ .Validate with Ruby API
1180
+ [example]
1181
+ ====
1182
+ [source,ruby]
1183
+ ----
1184
+ require 'fontisan'
1185
+
1186
+ # Validate with default profile (production)
1187
+ report = Fontisan.validate('font.ttf')
1188
+ puts report.valid? # => true or false
1189
+
1190
+ # Validate with specific profile
1191
+ report = Fontisan.validate('font.ttf', profile: :web)
1192
+ puts "Errors: #{report.summary.errors}"
1193
+ puts "Warnings: #{report.summary.warnings}"
1194
+
1195
+ # Check validation status
1196
+ if report.valid?
1197
+ puts "Font is valid for web use!"
1198
+ else
1199
+ puts "Font has #{report.summary.errors} errors"
1200
+ end
1201
+ ----
1202
+ ====
1203
+
1204
+ .Query validation results
1205
+ [example]
1206
+ ====
1207
+ [source,ruby]
1208
+ ----
1209
+ report = Fontisan.validate('font.ttf', profile: :production)
1210
+
1211
+ # Get issues by severity
1212
+ fatal_issues = report.fatal_errors
1213
+ error_issues = report.errors_only
1214
+ warning_issues = report.warnings_only
1215
+ info_issues = report.info_only
1216
+
1217
+ # Get issues by category
1218
+ table_issues = report.issues_by_category('table_validation')
1219
+
1220
+ # Get check results
1221
+ failed_ids = report.failed_check_ids
1222
+ pass_rate = report.pass_rate
1223
+
1224
+ # Export to different formats
1225
+ yaml_output = report.to_yaml
1226
+ json_output = report.to_json
1227
+ summary = report.to_summary # "2 errors, 3 warnings, 0 info"
1228
+ ----
1229
+ ====
1230
+
1231
+ .Use validators directly
1232
+ [example]
1233
+ ====
1234
+ [source,ruby]
1235
+ ----
1236
+ require 'fontisan'
1237
+
1238
+ # Load font
1239
+ font = Fontisan::FontLoader.load('font.ttf')
1240
+
1241
+ # Use specific validator
1242
+ validator = Fontisan::Validators::OpenTypeValidator.new
1243
+ report = validator.validate(font)
1244
+
1245
+ # Check individual results
1246
+ name_check = report.result_of(:name_version)
1247
+ puts name_check.passed?
1248
+ puts name_check.severity
1249
+ ----
1250
+ ====
1251
+
1252
+ ==== Using custom validators
1253
+
1254
+ ===== General
1255
+
1256
+ Custom validators inherit from `Fontisan::Validators::Validator` and define
1257
+ validation logic using the DSL.
1258
+
1259
+ The DSL provides 6 check methods for different validation types:
1260
+
1261
+ * `check_table` - Validate table-level properties
1262
+ * `check_field` - Validate specific field values
1263
+ * `check_structure` - Validate font structure and relationships
1264
+ * `check_usability` - Validate usability and best practices
1265
+ * `check_instructions` - Validate TrueType instructions/hinting
1266
+ * `check_glyphs` - Validate individual glyphs
1267
+
1268
+ Each check receives a unique ID, severity level (:info, :warning, :error,
1269
+ :fatal), and a validation block.
1270
+
1271
+ .Creating a custom validator
1272
+ [example]
1273
+ ====
1274
+ [source,ruby]
1275
+ ----
1276
+ require 'fontisan/validators/validator'
1277
+
1278
+ # Define a custom validator
1279
+ class MyFontValidator < Fontisan::Validators::Validator
1280
+ private
1281
+
1282
+ def define_checks
1283
+ # Check name table
1284
+ check_table :name_version, 'name', severity: :error do |table|
1285
+ table.valid_version?
1286
+ end
1287
+
1288
+ check_table :family_name, 'name', severity: :error do |table|
1289
+ table.family_name_present?
1290
+ end
1291
+
1292
+ # Check head table
1293
+ check_table :head_magic, 'head', severity: :error do |table|
1294
+ table.valid_magic?
1295
+ end
1296
+
1297
+ check_table :units_per_em, 'head', severity: :error do |table|
1298
+ table.valid_units_per_em?
1299
+ end
1300
+
1301
+ # Check structure
1302
+ check_structure :required_tables, severity: :error do |font|
1303
+ %w[name head maxp hhea].all? { |tag| !font.table(tag).nil? }
1304
+ end
1305
+ end
1306
+ end
1307
+
1308
+ # Use the validator
1309
+ font = Fontisan::FontLoader.load('font.ttf')
1310
+ validator = MyFontValidator.new
1311
+ report = validator.validate(font)
1312
+
1313
+ # Check results
1314
+ puts report.valid? # => true/false
1315
+ puts report.status # => "valid", "valid_with_warnings", "invalid"
1316
+ puts report.summary.errors # => number of errors
1317
+ puts report.summary.warnings # => number of warnings
1318
+
1319
+ # Query specific checks
1320
+ result = report.result_of(:name_version)
1321
+ puts result.passed? # => true/false
1322
+ puts result.severity # => "error"
1323
+ puts result.messages # => array of messages
1324
+
1325
+ # Get all failed checks
1326
+ report.failed_checks.each do |check|
1327
+ puts "#{check.check_id}: #{check.messages.join(', ')}"
1328
+ end
1329
+
1330
+ # Serialize report
1331
+ puts report.to_yaml
1332
+ puts report.to_json
1333
+ ----
1334
+ ====
1335
+
1336
+
1337
+ .Using table validation helpers
1338
+ [example]
1339
+ ====
1340
+ The validation framework integrates with 56 built-in validation helpers across
1341
+ core OpenType tables. These helpers perform specific validation checks and
1342
+ return boolean values.
1343
+
1344
+ [source,ruby]
1345
+ ----
1346
+ class ComprehensiveValidator < Fontisan::Validators::Validator
1347
+ private
1348
+
1349
+ def define_checks
1350
+ # Name table validation helpers
1351
+ check_table :name_validation, 'name' do |table|
1352
+ table.valid_version? &&
1353
+ table.valid_encoding_heuristics? &&
1354
+ table.family_name_present? &&
1355
+ table.postscript_name_valid?
1356
+ end
1357
+
1358
+ # Head table validation helpers
1359
+ check_table :head_validation, 'head' do |table|
1360
+ table.valid_magic? &&
1361
+ table.valid_version? &&
1362
+ table.valid_units_per_em? &&
1363
+ table.valid_bounding_box? &&
1364
+ table.valid_index_to_loc_format? &&
1365
+ table.valid_glyph_data_format?
1366
+ end
1367
+
1368
+ # Maxp table validation helpers
1369
+ check_table :maxp_validation, 'maxp' do |table|
1370
+ table.valid_version? &&
1371
+ table.valid_num_glyphs? &&
1372
+ table.valid_max_zones? &&
1373
+ table.has_truetype_metrics? &&
1374
+ table.reasonable_metrics?
1375
+ end
1376
+ end
1377
+ end
1378
+ ----
1379
+ ====
1380
+
1381
+ .Handling validation results
1382
+ [example]
1383
+ ====
1384
+ [source,ruby]
1385
+ ----
1386
+ validator = MyFontValidator.new
1387
+ report = validator.validate(font)
1388
+
1389
+ # Overall validation status
1390
+ if report.valid?
1391
+ puts "Font is valid!"
1392
+ else
1393
+ puts "Font has issues:"
1394
+
1395
+ # Show errors
1396
+ report.errors.each do |error|
1397
+ puts " [ERROR] #{error.category}: #{error.message}"
1398
+ puts " Location: #{error.location}" if error.location
1399
+ end
1400
+
1401
+ # Show warnings
1402
+ report.warnings.each do |warning|
1403
+ puts " [WARN] #{warning.category}: #{warning.message}"
1404
+ end
1405
+ end
1406
+
1407
+ # Check specific validation results
1408
+ if report.result_of(:name_version)&.passed?
1409
+ puts "Name table version is valid"
1410
+ else
1411
+ puts "Name table version check failed"
1412
+ end
1413
+
1414
+ # Get summary statistics
1415
+ puts "\nValidation Summary:"
1416
+ puts " Total checks: #{report.check_results.count}"
1417
+ puts " Passed: #{report.passed_checks.count}"
1418
+ puts " Failed: #{report.failed_checks.count}"
1419
+ puts " Errors: #{report.summary.errors}"
1420
+ puts " Warnings: #{report.summary.warnings}"
1421
+ puts " Info: #{report.summary.info}"
1422
+ ----
1423
+ ====
1424
+
1425
+ ==== Validation helpers
1426
+
1427
+ ===== General
1428
+
1429
+ The validation framework provides 56 helper methods across 8 core OpenType
1430
+ tables. Each helper returns a boolean indicating whether the validation passed.
1431
+
1432
+ Name table:
1433
+
1434
+ * `valid_version?` - Check if version is 0 or 1
1435
+ * `valid_encoding_heuristics?` - Check platform/encoding combinations
1436
+ * `has_valid_platform_combos?` - Check for required platform combinations
1437
+ * `family_name_present?` - Check if family name exists and is non-empty
1438
+ * `postscript_name_present?` - Check if PostScript name exists and is non-empty
1439
+ * `postscript_name_valid?` - Check if PostScript name matches required pattern
1440
+
1441
+ Head table:
1442
+
1443
+ * `valid_magic?` - Check magic number (0x5F0F3CF5)
1444
+ * `valid_version?` - Check version is 1.0
1445
+ * `valid_units_per_em?` - Check units per em is valid (16-16384)
1446
+ * `valid_bounding_box?` - Check bounding box coordinates
1447
+ * `valid_index_to_loc_format?` - Check format is 0 or 1
1448
+ * `valid_glyph_data_format?` - Check format is 0
1449
+
1450
+ Maxp Table:
1451
+
1452
+ * `valid_version?` - Check version is 0.5 or 1.0
1453
+ * `valid_num_glyphs?` - Check num glyphs >= 1
1454
+ * `valid_max_zones?` - Check maxZones is 1 or 2
1455
+ * `has_truetype_metrics?` - Check TrueType metrics are present
1456
+ * `reasonable_metrics?` - Check metrics are within reasonable bounds
1457
+
1458
+ Hhea Table:
1459
+
1460
+ * `valid_version?` - Check version is 1.0
1461
+ * `valid_metric_data_format?` - Check format is 0
1462
+ * `valid_number_of_h_metrics?` - Check count >= 1
1463
+ * `valid_ascent_descent?` - Check signs are correct
1464
+ * `valid_line_gap?` - Check line gap >= 0
1465
+ * `valid_advance_width_max?` - Check max width > 0
1466
+ * `valid_caret_slope?` - Check caret slope values
1467
+ * `valid_x_max_extent?` - Check extent > 0
1468
+
1469
+ Glyf Table:
1470
+
1471
+ * `has_empty_glyphs?` - Check for empty glyphs
1472
+ * `has_clipped_glyphs?` - Check for clipped glyphs
1473
+ * `has_instructions?` - Check for TrueType instructions
1474
+ * `contours_valid?` - Check contour counts
1475
+ * `glyphs_accessible?` - Check glyph accessibility
1476
+
1477
+ Cmap Table:
1478
+
1479
+ * `valid_version?` - Check version is 0
1480
+ * `has_valid_subtables?` - Check subtable validity
1481
+ * `has_unicode_mapping?` - Check for Unicode support
1482
+ * `has_valid_bmp_coverage?` - Check BMP coverage
1483
+ * `has_valid_format4?` - Check format 4 subtable
1484
+ * `glyph_indices_valid?` - Check glyph index validity
1485
+ * `supports_platform?` - Check platform support
1486
+
1487
+ Post Table:
1488
+
1489
+ * `valid_version?` - Check version (1.0, 2.0, 2.5, 3.0, 4.0)
1490
+ * `valid_italic_angle?` - Check italic angle range
1491
+ * `valid_underline_position?` - Check underline metrics
1492
+ * `valid_underline_thickness?` - Check underline thickness
1493
+ * `valid_is_fixed_pitch?` - Check fixed pitch flag
1494
+ * `has_glyph_names?` - Check for glyph names
1495
+
1496
+ OS/2 Table:
1497
+
1498
+ * `valid_version?` - Check version (0-5)
1499
+ * `valid_weight_class?` - Check weight class (100-900)
1500
+ * `valid_width_class?` - Check width class (1-9)
1501
+ * `valid_vendor_id?` - Check vendor ID format
1502
+ * `valid_typo_metrics?` - Check typographic metrics
1503
+ * `valid_win_metrics?` - Check Windows metrics
1504
+ * `valid_unicode_ranges?` - Check Unicode range bits
1505
+ * `has_valid_panose?` - Check PANOSE classification
1506
+ * `valid_selection_flags?` - Check style selection flags
1507
+ * `valid_first_char_index?` - Check first character index
1508
+ * `valid_last_char_index?` - Check last character index
1509
+ * `valid_typo_ascender?` - Check typographic ascender
1510
+ * `valid_typo_descender?` - Check typographic descender
1511
+
1023
1512
 
1024
1513
  == Version information
1025
1514
 
@@ -1599,6 +2088,31 @@ converter.supported_targets(:ttf)
1599
2088
  # => [:ttf, :otf, :woff2, :svg]
1600
2089
  ----
1601
2090
 
2091
+ === Convert into WOFF2
2092
+
2093
+ ==== Validation
2094
+
2095
+ WOFF2 encoding can be validated automatically to ensure spec compliance:
2096
+
2097
+ .Validation example
2098
+ [example]
2099
+ ====
2100
+ [source,ruby]
2101
+ ----
2102
+ encoder = Fontisan::Converters::Woff2Encoder.new
2103
+ result = encoder.convert(font, {
2104
+ transform_tables: true,
2105
+ validate: true,
2106
+ validation_level: :strict # :strict, :standard, or :lenient
2107
+ })
2108
+
2109
+ report = result[:validation_report]
2110
+ puts "Valid: #{report.valid}"
2111
+ puts "Compression: #{report.info_issues.first.message}"
2112
+ ----
2113
+ ====
2114
+
2115
+
1602
2116
 
1603
2117
  === Validation
1604
2118
 
data/Rakefile CHANGED
@@ -87,6 +87,9 @@ namespace :fixtures do
87
87
 
88
88
  # Create file tasks for each font
89
89
  fonts.each do |name, config|
90
+ # Skip fonts that should not be downloaded (already committed)
91
+ next if config[:skip_download]
92
+
90
93
  file config[:marker] do
91
94
  if config[:single_file]
92
95
  download_single_file(name, config[:url], config[:marker])
@@ -97,16 +100,26 @@ namespace :fixtures do
97
100
  end
98
101
 
99
102
  desc "Download all test fixture fonts"
100
- task download: fonts.values.map { |config| config[:marker] }
103
+ task download: fonts.values.reject { |config| config[:skip_download] }.map { |config| config[:marker] }
101
104
 
102
105
  desc "Clean downloaded fixtures"
103
106
  task :clean do
104
- fonts.values.map { |config| config[:target_dir] }.each do |path|
105
- next unless File.exist?(path)
106
-
107
- puts "[fixtures:clean] Removing #{path}..."
108
- FileUtils.rm_rf(path)
109
- puts "[fixtures:clean] Removed #{path}"
107
+ fonts.values.reject { |config| config[:skip_download] }.each do |config|
108
+ if config[:single_file]
109
+ # For single files, just delete the marker file itself
110
+ if File.exist?(config[:marker])
111
+ puts "[fixtures:clean] Removing #{config[:marker]}..."
112
+ FileUtils.rm_f(config[:marker])
113
+ puts "[fixtures:clean] Removed #{config[:marker]}"
114
+ end
115
+ else
116
+ # For archives, delete the entire target directory
117
+ if File.exist?(config[:target_dir])
118
+ puts "[fixtures:clean] Removing #{config[:target_dir]}..."
119
+ FileUtils.rm_rf(config[:target_dir])
120
+ puts "[fixtures:clean] Removed #{config[:target_dir]}"
121
+ end
122
+ end
110
123
  end
111
124
  end
112
125
  end
data/lib/fontisan/cli.rb CHANGED
@@ -350,13 +350,62 @@ module Fontisan
350
350
  handle_error(e)
351
351
  end
352
352
 
353
- desc "validate FONT_FILE", "Validate font file structure and checksums"
354
- option :verbose, type: :boolean, default: false,
355
- desc: "Show detailed validation information"
353
+ desc "validate FONT_FILE", "Validate font file"
354
+ long_desc <<-DESC
355
+ Validate font file against quality checks and standards.
356
+
357
+ Test lists (-t/--test-list):
358
+ indexability - Fast indexing validation
359
+ usability - Installation compatibility
360
+ production - Comprehensive quality (default)
361
+ web - Web font readiness
362
+ spec_compliance - OpenType spec compliance
363
+ default - Production profile (alias)
364
+
365
+ Return values (with -R/--return-value-results):
366
+ 0 No results
367
+ 1 Execution errors
368
+ 2 Fatal errors found
369
+ 3 Major errors found
370
+ 4 Minor errors found
371
+ 5 Spec violations found
372
+ DESC
373
+
374
+ option :exclude, aliases: "-e", type: :array, desc: "Tests to exclude"
375
+ option :list, aliases: "-l", type: :boolean, desc: "List available tests"
376
+ option :output, aliases: "-o", type: :string, desc: "Output file"
377
+ option :full_report, aliases: "-r", type: :boolean, desc: "Full report"
378
+ option :return_value_results, aliases: "-R", type: :boolean, desc: "Use return value for results"
379
+ option :summary_report, aliases: "-S", type: :boolean, desc: "Summary report"
380
+ option :test_list, aliases: "-t", type: :string, default: "default", desc: "Tests to execute"
381
+ option :table_report, aliases: "-T", type: :boolean, desc: "Tabular report"
382
+ option :verbose, aliases: "-v", type: :boolean, desc: "Verbose output"
383
+ option :suppress_warnings, aliases: "-W", type: :boolean, desc: "Suppress warnings"
384
+
356
385
  def validate(font_file)
357
- command = Commands::ValidateCommand.new(font_file,
358
- verbose: options[:verbose])
359
- exit command.run
386
+ if options[:list]
387
+ list_available_tests
388
+ return
389
+ end
390
+
391
+ cmd = Commands::ValidateCommand.new(
392
+ input: font_file,
393
+ profile: options[:test_list],
394
+ exclude: options[:exclude] || [],
395
+ output: options[:output],
396
+ format: options[:format].to_sym,
397
+ full_report: options[:full_report],
398
+ summary_report: options[:summary_report],
399
+ table_report: options[:table_report],
400
+ verbose: options[:verbose],
401
+ suppress_warnings: options[:suppress_warnings],
402
+ return_value_results: options[:return_value_results]
403
+ )
404
+
405
+ exit cmd.run
406
+ rescue => e
407
+ error "Validation failed: #{e.message}"
408
+ exit 1
360
409
  end
361
410
 
362
411
  desc "export FONT_FILE", "Export font to TTX/YAML/JSON format"
@@ -555,5 +604,17 @@ module Fontisan
555
604
  warn message unless options[:quiet]
556
605
  exit 1
557
606
  end
607
+
608
+ # List available validation tests/profiles
609
+ #
610
+ # @return [void]
611
+ def list_available_tests
612
+ require_relative "validators/profile_loader"
613
+ profiles = Validators::ProfileLoader.all_profiles
614
+ puts "Available validation profiles:"
615
+ profiles.each do |profile_name, config|
616
+ puts " #{profile_name.to_s.ljust(20)} - #{config[:description]}"
617
+ end
618
+ end
558
619
  end
559
620
  end