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.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +221 -49
- data/README.adoc +519 -5
- data/Rakefile +20 -7
- data/lib/fontisan/cli.rb +67 -6
- data/lib/fontisan/commands/base_command.rb +2 -19
- data/lib/fontisan/commands/convert_command.rb +16 -13
- data/lib/fontisan/commands/info_command.rb +88 -0
- data/lib/fontisan/commands/validate_command.rb +107 -151
- data/lib/fontisan/config/conversion_matrix.yml +58 -20
- data/lib/fontisan/converters/outline_converter.rb +6 -3
- data/lib/fontisan/converters/svg_generator.rb +45 -0
- data/lib/fontisan/converters/woff2_encoder.rb +84 -13
- data/lib/fontisan/models/bitmap_glyph.rb +123 -0
- data/lib/fontisan/models/bitmap_strike.rb +94 -0
- data/lib/fontisan/models/color_glyph.rb +57 -0
- data/lib/fontisan/models/color_layer.rb +53 -0
- data/lib/fontisan/models/color_palette.rb +60 -0
- data/lib/fontisan/models/font_info.rb +26 -0
- data/lib/fontisan/models/svg_glyph.rb +89 -0
- data/lib/fontisan/models/validation_report.rb +227 -0
- data/lib/fontisan/open_type_font.rb +6 -0
- data/lib/fontisan/optimizers/charstring_rewriter.rb +19 -8
- data/lib/fontisan/optimizers/pattern_analyzer.rb +4 -2
- data/lib/fontisan/optimizers/subroutine_builder.rb +6 -5
- data/lib/fontisan/optimizers/subroutine_optimizer.rb +5 -2
- data/lib/fontisan/pipeline/output_writer.rb +2 -2
- data/lib/fontisan/pipeline/transformation_pipeline.rb +4 -8
- data/lib/fontisan/tables/cbdt.rb +169 -0
- data/lib/fontisan/tables/cblc.rb +290 -0
- data/lib/fontisan/tables/cff.rb +6 -12
- data/lib/fontisan/tables/cmap.rb +82 -2
- data/lib/fontisan/tables/colr.rb +291 -0
- data/lib/fontisan/tables/cpal.rb +281 -0
- data/lib/fontisan/tables/glyf/glyph_builder.rb +5 -1
- data/lib/fontisan/tables/glyf.rb +118 -0
- data/lib/fontisan/tables/head.rb +60 -0
- data/lib/fontisan/tables/hhea.rb +74 -0
- data/lib/fontisan/tables/maxp.rb +60 -0
- data/lib/fontisan/tables/name.rb +76 -0
- data/lib/fontisan/tables/os2.rb +113 -0
- data/lib/fontisan/tables/post.rb +57 -0
- data/lib/fontisan/tables/sbix.rb +379 -0
- data/lib/fontisan/tables/svg.rb +301 -0
- data/lib/fontisan/true_type_font.rb +6 -0
- data/lib/fontisan/validators/basic_validator.rb +85 -0
- data/lib/fontisan/validators/font_book_validator.rb +130 -0
- data/lib/fontisan/validators/opentype_validator.rb +112 -0
- data/lib/fontisan/validators/profile_loader.rb +139 -0
- data/lib/fontisan/validators/validator.rb +484 -0
- data/lib/fontisan/validators/web_font_validator.rb +102 -0
- data/lib/fontisan/version.rb +1 -1
- data/lib/fontisan/woff2/directory.rb +40 -11
- data/lib/fontisan/woff2/table_transformer.rb +506 -73
- data/lib/fontisan/woff2_font.rb +29 -9
- data/lib/fontisan/woff_font.rb +17 -4
- data/lib/fontisan.rb +90 -6
- metadata +20 -9
- data/lib/fontisan/config/validation_rules.yml +0 -149
- data/lib/fontisan/validation/checksum_validator.rb +0 -170
- data/lib/fontisan/validation/consistency_validator.rb +0 -197
- data/lib/fontisan/validation/structure_validator.rb +0 -198
- data/lib/fontisan/validation/table_validator.rb +0 -158
- data/lib/fontisan/validation/validator.rb +0 -152
- 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
|
|
738
|
-
|
|
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.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
|
354
|
-
|
|
355
|
-
|
|
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
|
-
|
|
358
|
-
|
|
359
|
-
|
|
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
|