fontisan 0.2.4 → 0.2.6

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +168 -32
  3. data/README.adoc +673 -1091
  4. data/lib/fontisan/cli.rb +94 -13
  5. data/lib/fontisan/collection/dfont_builder.rb +315 -0
  6. data/lib/fontisan/commands/convert_command.rb +118 -7
  7. data/lib/fontisan/commands/pack_command.rb +129 -22
  8. data/lib/fontisan/commands/validate_command.rb +107 -151
  9. data/lib/fontisan/config/conversion_matrix.yml +175 -1
  10. data/lib/fontisan/constants.rb +8 -0
  11. data/lib/fontisan/converters/collection_converter.rb +438 -0
  12. data/lib/fontisan/converters/woff2_encoder.rb +7 -29
  13. data/lib/fontisan/dfont_collection.rb +185 -0
  14. data/lib/fontisan/font_loader.rb +91 -6
  15. data/lib/fontisan/models/validation_report.rb +227 -0
  16. data/lib/fontisan/parsers/dfont_parser.rb +192 -0
  17. data/lib/fontisan/pipeline/transformation_pipeline.rb +4 -8
  18. data/lib/fontisan/tables/cmap.rb +82 -2
  19. data/lib/fontisan/tables/glyf.rb +118 -0
  20. data/lib/fontisan/tables/head.rb +60 -0
  21. data/lib/fontisan/tables/hhea.rb +74 -0
  22. data/lib/fontisan/tables/maxp.rb +60 -0
  23. data/lib/fontisan/tables/name.rb +76 -0
  24. data/lib/fontisan/tables/os2.rb +113 -0
  25. data/lib/fontisan/tables/post.rb +57 -0
  26. data/lib/fontisan/true_type_font.rb +8 -46
  27. data/lib/fontisan/validation/collection_validator.rb +265 -0
  28. data/lib/fontisan/validators/basic_validator.rb +85 -0
  29. data/lib/fontisan/validators/font_book_validator.rb +130 -0
  30. data/lib/fontisan/validators/opentype_validator.rb +112 -0
  31. data/lib/fontisan/validators/profile_loader.rb +139 -0
  32. data/lib/fontisan/validators/validator.rb +484 -0
  33. data/lib/fontisan/validators/web_font_validator.rb +102 -0
  34. data/lib/fontisan/version.rb +1 -1
  35. data/lib/fontisan.rb +78 -6
  36. metadata +13 -12
  37. data/lib/fontisan/config/validation_rules.yml +0 -149
  38. data/lib/fontisan/validation/checksum_validator.rb +0 -170
  39. data/lib/fontisan/validation/consistency_validator.rb +0 -197
  40. data/lib/fontisan/validation/structure_validator.rb +0 -198
  41. data/lib/fontisan/validation/table_validator.rb +0 -158
  42. data/lib/fontisan/validation/validator.rb +0 -152
  43. data/lib/fontisan/validation/variable_font_validator.rb +0 -218
  44. data/lib/fontisan/validation/woff2_header_validator.rb +0 -278
  45. data/lib/fontisan/validation/woff2_table_validator.rb +0 -270
  46. data/lib/fontisan/validation/woff2_validator.rb +0 -248
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "basic_validator"
4
+
5
+ module Fontisan
6
+ module Validators
7
+ # FontBookValidator provides macOS Font Book installation compatibility checks
8
+ #
9
+ # This validator extends BasicValidator with additional checks needed for
10
+ # fonts to be successfully installed and used in macOS Font Book. It ensures
11
+ # proper encoding combinations, OS/2 metrics, and other macOS-specific
12
+ # requirements.
13
+ #
14
+ # The validator inherits all 8 checks from BasicValidator and adds 12 new
15
+ # checks focusing on:
16
+ # - Name table encoding combinations (Windows and Mac)
17
+ # - OS/2 table metrics and metadata
18
+ # - Head table bounding box
19
+ # - Hhea table metrics
20
+ # - Post table metadata
21
+ # - Cmap subtables
22
+ #
23
+ # @example Using FontBookValidator
24
+ # validator = FontBookValidator.new
25
+ # report = validator.validate(font)
26
+ # puts "Font is Font Book compatible" if report.valid?
27
+ class FontBookValidator < BasicValidator
28
+ private
29
+
30
+ # Define Font Book compatibility checks
31
+ #
32
+ # Calls super to inherit BasicValidator's 8 checks, then adds 12 new checks.
33
+ # All checks use helpers from Week 1 table implementations.
34
+ def define_checks
35
+ # Inherit BasicValidator checks (8 checks)
36
+ super
37
+
38
+ # Check 9: Name table Windows Unicode English encoding
39
+ check_table :name_windows_encoding, 'name', severity: :error do |table|
40
+ table.has_valid_platform_combos?([3, 1, 0x0409]) # Windows Unicode English
41
+ end
42
+
43
+ # Check 10: Name table Mac Roman English encoding
44
+ check_table :name_mac_encoding, 'name', severity: :warning do |table|
45
+ table.has_valid_platform_combos?([1, 0, 0]) # Mac Roman English
46
+ end
47
+
48
+ # Check 11: OS/2 table version must be valid
49
+ check_table :os2_version, 'OS/2', severity: :error do |table|
50
+ table.valid_version?
51
+ end
52
+
53
+ # Check 12: OS/2 weight class must be valid (1-1000)
54
+ check_table :os2_weight_class, 'OS/2', severity: :error do |table|
55
+ table.valid_weight_class?
56
+ end
57
+
58
+ # Check 13: OS/2 width class must be valid (1-9)
59
+ check_table :os2_width_class, 'OS/2', severity: :error do |table|
60
+ table.valid_width_class?
61
+ end
62
+
63
+ # Check 14: OS/2 vendor ID should be present
64
+ check_table :os2_vendor_id, 'OS/2', severity: :warning do |table|
65
+ table.has_vendor_id?
66
+ end
67
+
68
+ # Check 15: OS/2 PANOSE classification should be present
69
+ check_table :os2_panose, 'OS/2', severity: :info do |table|
70
+ table.has_panose?
71
+ end
72
+
73
+ # Check 16: OS/2 typographic metrics must be valid
74
+ check_table :os2_typo_metrics, 'OS/2', severity: :error do |table|
75
+ table.valid_typo_metrics?
76
+ end
77
+
78
+ # Check 17: OS/2 Windows metrics must be valid
79
+ check_table :os2_win_metrics, 'OS/2', severity: :error do |table|
80
+ table.valid_win_metrics?
81
+ end
82
+
83
+ # Check 18: OS/2 Unicode ranges should be present
84
+ check_table :os2_unicode_ranges, 'OS/2', severity: :warning do |table|
85
+ table.has_unicode_ranges?
86
+ end
87
+
88
+ # Check 19: Head table bounding box must be valid
89
+ check_table :head_bounding_box, 'head', severity: :error do |table|
90
+ table.valid_bounding_box?
91
+ end
92
+
93
+ # Check 20: Hhea ascent/descent must be valid
94
+ check_table :hhea_ascent_descent, 'hhea', severity: :error do |table|
95
+ table.valid_ascent_descent?
96
+ end
97
+
98
+ # Check 21: Hhea line gap should be valid
99
+ check_table :hhea_line_gap, 'hhea', severity: :warning do |table|
100
+ table.valid_line_gap?
101
+ end
102
+
103
+ # Check 22: Hhea horizontal metrics count must be valid
104
+ check_table :hhea_metrics_count, 'hhea', severity: :error do |table|
105
+ table.valid_number_of_h_metrics?
106
+ end
107
+
108
+ # Check 23: Post table version must be valid
109
+ check_table :post_version, 'post', severity: :error do |table|
110
+ table.valid_version?
111
+ end
112
+
113
+ # Check 24: Post table italic angle should be valid
114
+ check_table :post_italic_angle, 'post', severity: :warning do |table|
115
+ table.valid_italic_angle?
116
+ end
117
+
118
+ # Check 25: Post table underline metrics should be present
119
+ check_table :post_underline, 'post', severity: :info do |table|
120
+ table.has_underline_metrics?
121
+ end
122
+
123
+ # Check 26: Cmap table must have subtables
124
+ check_table :cmap_subtables, 'cmap', severity: :error do |table|
125
+ table.has_subtables?
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "font_book_validator"
4
+
5
+ module Fontisan
6
+ module Validators
7
+ # OpenTypeValidator provides comprehensive OpenType specification compliance checks
8
+ #
9
+ # This validator extends FontBookValidator with additional checks ensuring full
10
+ # OpenType specification compliance. It validates glyph data, character mappings,
11
+ # and cross-table consistency.
12
+ #
13
+ # The validator inherits all checks from FontBookValidator (18 checks from
14
+ # FontBookValidator + 8 from BasicValidator = 26 total) and adds 10 new checks:
15
+ # - Maxp TrueType metrics validation
16
+ # - Glyf table structure and accessibility
17
+ # - Cmap Unicode mapping and coverage
18
+ # - Cross-table consistency checks
19
+ #
20
+ # @example Using OpenTypeValidator
21
+ # validator = OpenTypeValidator.new
22
+ # report = validator.validate(font)
23
+ # puts "Font is OpenType compliant" if report.valid?
24
+ class OpenTypeValidator < FontBookValidator
25
+ private
26
+
27
+ # Define OpenType specification compliance checks
28
+ #
29
+ # Calls super to inherit FontBookValidator's checks, then adds 10 new checks.
30
+ # All checks use helpers from Week 1 table implementations.
31
+ def define_checks
32
+ # Inherit FontBookValidator checks (26 checks total)
33
+ super
34
+
35
+ # Check 27: Maxp TrueType metrics (only for version 1.0)
36
+ check_table :maxp_truetype_metrics, 'maxp', severity: :warning do |table|
37
+ !table.version_1_0? || table.has_truetype_metrics?
38
+ end
39
+
40
+ # Check 28: Maxp max zones must be valid
41
+ check_table :maxp_zones, 'maxp', severity: :error do |table|
42
+ table.valid_max_zones?
43
+ end
44
+
45
+ # Check 29: Glyf glyphs must be accessible (TrueType fonts only)
46
+ check_glyphs :glyf_accessible, severity: :error do |font|
47
+ glyf = font.table('glyf')
48
+ next true unless glyf # Skip if CFF font
49
+
50
+ loca = font.table('loca')
51
+ head = font.table('head')
52
+ maxp = font.table('maxp')
53
+ glyf.all_glyphs_accessible?(loca, head, maxp.num_glyphs)
54
+ end
55
+
56
+ # Check 30: Glyf glyphs should not be clipped
57
+ check_glyphs :glyf_no_clipping, severity: :warning do |font|
58
+ glyf = font.table('glyf')
59
+ next true unless glyf
60
+
61
+ loca = font.table('loca')
62
+ head = font.table('head')
63
+ maxp = font.table('maxp')
64
+ glyf.no_clipped_glyphs?(loca, head, maxp.num_glyphs)
65
+ end
66
+
67
+ # Check 31: Glyf contour counts must be valid
68
+ check_glyphs :glyf_valid_contours, severity: :error do |font|
69
+ glyf = font.table('glyf')
70
+ next true unless glyf
71
+
72
+ loca = font.table('loca')
73
+ head = font.table('head')
74
+ maxp = font.table('maxp')
75
+
76
+ (0...maxp.num_glyphs).all? do |glyph_id|
77
+ glyf.valid_contour_count?(glyph_id, loca, head)
78
+ end
79
+ end
80
+
81
+ # Check 32: Cmap must have Unicode mapping
82
+ check_table :cmap_unicode_mapping, 'cmap', severity: :error do |table|
83
+ table.has_unicode_mapping?
84
+ end
85
+
86
+ # Check 33: Cmap should have BMP coverage
87
+ check_table :cmap_bmp_coverage, 'cmap', severity: :warning do |table|
88
+ table.has_bmp_coverage?
89
+ end
90
+
91
+ # Check 34: Cmap must have format 4 subtable
92
+ check_table :cmap_format4, 'cmap', severity: :error do |table|
93
+ table.has_format_4_subtable?
94
+ end
95
+
96
+ # Check 35: Cmap glyph indices must be valid
97
+ check_structure :cmap_glyph_indices, severity: :error do |font|
98
+ cmap = font.table('cmap')
99
+ maxp = font.table('maxp')
100
+ cmap.valid_glyph_indices?(maxp.num_glyphs)
101
+ end
102
+
103
+ # Check 36: Table checksums (info level - many fonts have mismatches)
104
+ check_structure :checksum_valid, severity: :info do |font|
105
+ # Table checksum validation (info level - for reference)
106
+ # Most fonts have checksum mismatches, so we make it info not error
107
+ true # Placeholder - actual checksum validation if desired
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "basic_validator"
4
+ require_relative "font_book_validator"
5
+ require_relative "opentype_validator"
6
+ require_relative "web_font_validator"
7
+
8
+ module Fontisan
9
+ module Validators
10
+ # ProfileLoader manages validation profiles and loads appropriate validators
11
+ #
12
+ # This class provides a registry of validation profiles, each configured for
13
+ # specific use cases. Profiles define which validator to use, loading mode,
14
+ # and severity thresholds.
15
+ #
16
+ # Available profiles:
17
+ # - indexability: Fast validation for font discovery (BasicValidator)
18
+ # - usability: Basic usability for installation (FontBookValidator)
19
+ # - production: Comprehensive quality checks (OpenTypeValidator)
20
+ # - web: Web embedding and optimization (WebFontValidator)
21
+ # - spec_compliance: Full OpenType spec compliance (OpenTypeValidator)
22
+ # - default: Alias for production profile
23
+ #
24
+ # @example Loading a profile
25
+ # validator = ProfileLoader.load(:production)
26
+ # report = validator.validate(font)
27
+ #
28
+ # @example Getting profile info
29
+ # info = ProfileLoader.profile_info(:web)
30
+ # puts info[:description]
31
+ class ProfileLoader
32
+ # Profile definitions (hardcoded, no YAML)
33
+ PROFILES = {
34
+ indexability: {
35
+ name: "Font Indexability",
36
+ description: "Fast validation for font discovery and indexing",
37
+ validator: "BasicValidator",
38
+ loading_mode: "metadata",
39
+ severity_threshold: "error",
40
+ },
41
+ usability: {
42
+ name: "Font Usability",
43
+ description: "Basic usability for installation",
44
+ validator: "FontBookValidator",
45
+ loading_mode: "full",
46
+ severity_threshold: "warning",
47
+ },
48
+ production: {
49
+ name: "Production Quality",
50
+ description: "Comprehensive quality checks",
51
+ validator: "OpenTypeValidator",
52
+ loading_mode: "full",
53
+ severity_threshold: "warning",
54
+ },
55
+ web: {
56
+ name: "Web Font Readiness",
57
+ description: "Web embedding and optimization",
58
+ validator: "WebFontValidator",
59
+ loading_mode: "full",
60
+ severity_threshold: "warning",
61
+ },
62
+ spec_compliance: {
63
+ name: "OpenType Specification",
64
+ description: "Full OpenType spec compliance",
65
+ validator: "OpenTypeValidator",
66
+ loading_mode: "full",
67
+ severity_threshold: "info",
68
+ },
69
+ default: {
70
+ name: "Default Profile",
71
+ description: "Default validation profile (alias for production)",
72
+ validator: "OpenTypeValidator",
73
+ loading_mode: "full",
74
+ severity_threshold: "warning",
75
+ },
76
+ }.freeze
77
+
78
+ class << self
79
+ # Load a validator for the specified profile
80
+ #
81
+ # @param profile_name [Symbol, String] Profile name
82
+ # @return [Validator] Validator instance for the profile
83
+ # @raise [ArgumentError] if profile name is unknown
84
+ #
85
+ # @example Load production validator
86
+ # validator = ProfileLoader.load(:production)
87
+ def load(profile_name)
88
+ profile_name = profile_name.to_sym
89
+ profile_config = PROFILES[profile_name]
90
+
91
+ unless profile_config
92
+ raise ArgumentError,
93
+ "Unknown profile: #{profile_name}. " \
94
+ "Available profiles: #{available_profiles.join(', ')}"
95
+ end
96
+
97
+ validator_class_name = profile_config[:validator]
98
+ validator_class = Validators.const_get(validator_class_name)
99
+ validator_class.new
100
+ end
101
+
102
+ # Get list of available profile names
103
+ #
104
+ # @return [Array<Symbol>] Array of profile names
105
+ #
106
+ # @example List available profiles
107
+ # ProfileLoader.available_profiles
108
+ # # => [:indexability, :usability, :production, :web, :spec_compliance, :default]
109
+ def available_profiles
110
+ PROFILES.keys
111
+ end
112
+
113
+ # Get profile configuration
114
+ #
115
+ # @param profile_name [Symbol, String] Profile name
116
+ # @return [Hash, nil] Profile configuration or nil if not found
117
+ #
118
+ # @example Get profile info
119
+ # info = ProfileLoader.profile_info(:web)
120
+ # puts info[:description]
121
+ def profile_info(profile_name)
122
+ PROFILES[profile_name.to_sym]
123
+ end
124
+
125
+ # Get all profiles with their configurations
126
+ #
127
+ # @return [Hash] All profile configurations
128
+ #
129
+ # @example Get all profiles
130
+ # ProfileLoader.all_profiles.each do |name, config|
131
+ # puts "#{name}: #{config[:description]}"
132
+ # end
133
+ def all_profiles
134
+ PROFILES
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end