fontisan 0.2.11 → 0.2.13

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 +294 -52
  3. data/Gemfile +5 -0
  4. data/README.adoc +163 -2
  5. data/docs/CONVERSION_GUIDE.adoc +633 -0
  6. data/docs/TYPE1_FONTS.adoc +445 -0
  7. data/lib/fontisan/cli.rb +177 -6
  8. data/lib/fontisan/commands/convert_command.rb +32 -1
  9. data/lib/fontisan/commands/info_command.rb +83 -2
  10. data/lib/fontisan/config/conversion_matrix.yml +132 -4
  11. data/lib/fontisan/constants.rb +12 -0
  12. data/lib/fontisan/conversion_options.rb +378 -0
  13. data/lib/fontisan/converters/collection_converter.rb +45 -10
  14. data/lib/fontisan/converters/format_converter.rb +17 -5
  15. data/lib/fontisan/converters/outline_converter.rb +78 -4
  16. data/lib/fontisan/converters/type1_converter.rb +1234 -0
  17. data/lib/fontisan/font_loader.rb +46 -3
  18. data/lib/fontisan/hints/hint_converter.rb +4 -1
  19. data/lib/fontisan/type1/afm_generator.rb +436 -0
  20. data/lib/fontisan/type1/afm_parser.rb +298 -0
  21. data/lib/fontisan/type1/agl.rb +456 -0
  22. data/lib/fontisan/type1/cff_to_type1_converter.rb +302 -0
  23. data/lib/fontisan/type1/charstring_converter.rb +240 -0
  24. data/lib/fontisan/type1/charstrings.rb +408 -0
  25. data/lib/fontisan/type1/conversion_options.rb +243 -0
  26. data/lib/fontisan/type1/decryptor.rb +183 -0
  27. data/lib/fontisan/type1/encodings.rb +697 -0
  28. data/lib/fontisan/type1/font_dictionary.rb +576 -0
  29. data/lib/fontisan/type1/generator.rb +220 -0
  30. data/lib/fontisan/type1/inf_generator.rb +332 -0
  31. data/lib/fontisan/type1/pfa_generator.rb +369 -0
  32. data/lib/fontisan/type1/pfa_parser.rb +159 -0
  33. data/lib/fontisan/type1/pfb_generator.rb +314 -0
  34. data/lib/fontisan/type1/pfb_parser.rb +166 -0
  35. data/lib/fontisan/type1/pfm_generator.rb +610 -0
  36. data/lib/fontisan/type1/pfm_parser.rb +433 -0
  37. data/lib/fontisan/type1/private_dict.rb +342 -0
  38. data/lib/fontisan/type1/seac_expander.rb +501 -0
  39. data/lib/fontisan/type1/ttf_to_type1_converter.rb +327 -0
  40. data/lib/fontisan/type1/upm_scaler.rb +118 -0
  41. data/lib/fontisan/type1.rb +75 -0
  42. data/lib/fontisan/type1_font.rb +318 -0
  43. data/lib/fontisan/version.rb +1 -1
  44. data/lib/fontisan.rb +2 -0
  45. metadata +30 -3
  46. data/docs/DOCUMENTATION_SUMMARY.md +0 -141
@@ -0,0 +1,318 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "type1"
4
+
5
+ module Fontisan
6
+ # Adobe Type 1 Font handler
7
+ #
8
+ # [`Type1Font`](lib/fontisan/type1_font.rb) provides parsing and conversion
9
+ # capabilities for Adobe Type 1 fonts in PFB (Printer Font Binary) and
10
+ # PFA (Printer Font ASCII) formats.
11
+ #
12
+ # Type 1 fonts were the standard for digital typography in the 1980s-1990s
13
+ # and consist of:
14
+ # - Font dictionary with metadata (FontInfo, FontName, Encoding, etc.)
15
+ # - Private dictionary with hinting and spacing information
16
+ # - CharStrings (glyph outline descriptions)
17
+ # - eexec encryption for protection
18
+ #
19
+ # @example Load a PFB file
20
+ # font = Fontisan::Type1Font.from_file('font.pfb')
21
+ # puts font.font_name
22
+ # puts font.version
23
+ #
24
+ # @example Load a PFA file
25
+ # font = Fontisan::Type1Font.from_file('font.pfa')
26
+ # puts font.full_name
27
+ #
28
+ # @example Access decrypted font data
29
+ # font = Fontisan::Type1Font.from_file('font.pfb')
30
+ # puts font.decrypted_data
31
+ #
32
+ # @see https://www.adobe.com/devnet/font/pdfs/Type1.pdf
33
+ class Type1Font
34
+ # @return [String, nil] File path if loaded from file
35
+ attr_reader :file_path
36
+
37
+ # @return [Symbol] Format type (:pfb or :pfa)
38
+ attr_reader :format
39
+
40
+ # @return [Symbol] Loading mode (:metadata or :full)
41
+ attr_reader :loading_mode
42
+
43
+ # @return [String, nil] Decrypted font data
44
+ attr_reader :decrypted_data
45
+
46
+ # @return [FontDictionary, nil] Font dictionary
47
+ def font_dictionary
48
+ parse_dictionaries! unless @font_dictionary
49
+ @font_dictionary
50
+ end
51
+
52
+ # @return [PrivateDict, nil] Private dictionary
53
+ def private_dict
54
+ parse_dictionaries! unless @private_dict
55
+ @private_dict
56
+ end
57
+
58
+ # @return [CharStrings, nil] CharStrings dictionary
59
+ def charstrings
60
+ parse_dictionaries! unless @charstrings
61
+ @charstrings
62
+ end
63
+
64
+ # Initialize a new Type1Font instance
65
+ #
66
+ # @param data [String] Font file data (binary or text)
67
+ # @param format [Symbol] Format type (:pfb or :pfa, auto-detected if nil)
68
+ # @param file_path [String, nil] Optional file path for reference
69
+ # @param mode [Symbol] Loading mode (:metadata or :full, default: :full)
70
+ def initialize(data, format: nil, file_path: nil, mode: :full)
71
+ @file_path = file_path
72
+ @format = format || detect_format(data)
73
+ @data = data
74
+ @loading_mode = mode
75
+
76
+ parse_font_data
77
+ end
78
+
79
+ # Load Type 1 font from file
80
+ #
81
+ # @param file_path [String] Path to PFB or PFA file
82
+ # @param mode [Symbol] Loading mode (:metadata or :full, default: :full)
83
+ # @return [Type1Font] Loaded font instance
84
+ # @raise [ArgumentError] If file_path is nil
85
+ # @raise [Fontisan::Error] If file cannot be read or parsed
86
+ #
87
+ # @example Load PFB file
88
+ # font = Fontisan::Type1Font.from_file('font.pfb')
89
+ #
90
+ # @example Load PFA file
91
+ # font = Fontisan::Type1Font.from_file('font.pfa')
92
+ def self.from_file(file_path, mode: :full)
93
+ raise ArgumentError, "File path cannot be nil" if file_path.nil?
94
+
95
+ unless File.exist?(file_path)
96
+ raise Fontisan::Error, "File not found: #{file_path}"
97
+ end
98
+
99
+ # Read file
100
+ data = File.binread(file_path)
101
+
102
+ new(data, file_path: file_path, mode: mode)
103
+ end
104
+
105
+ # Get clear text portion (before eexec)
106
+ #
107
+ # @return [String] Clear text font dictionary
108
+ def clear_text
109
+ @clear_text ||= ""
110
+ end
111
+
112
+ # Get encrypted portion (as hex string for PFA)
113
+ #
114
+ # @return [String] Encrypted portion
115
+ def encrypted_portion
116
+ @encrypted_portion ||= ""
117
+ end
118
+
119
+ # Check if font has been decrypted
120
+ #
121
+ # @return [Boolean] True if font data has been decrypted
122
+ def decrypted?
123
+ !@decrypted_data.nil?
124
+ end
125
+
126
+ # Check if font is encrypted
127
+ #
128
+ # @return [Boolean] True if font has eexec encrypted portion
129
+ def encrypted?
130
+ !@encrypted_portion.nil? && !@encrypted_portion.empty?
131
+ end
132
+
133
+ # Decrypt the font if not already decrypted
134
+ #
135
+ # @return [String] Decrypted font data
136
+ def decrypt!
137
+ return @decrypted_data if decrypted?
138
+
139
+ if @encrypted_portion.nil? || @encrypted_portion.empty?
140
+ @decrypted_data = @clear_text
141
+ else
142
+ encrypted_binary = if @format == :pfa
143
+ # Convert hex string to binary
144
+ [@encrypted_portion.gsub(/\s/, "")].pack("H*")
145
+ else
146
+ @encrypted_portion
147
+ end
148
+
149
+ @decrypted_data = @clear_text +
150
+ Type1::Decryptor.eexec_decrypt(encrypted_binary)
151
+ end
152
+
153
+ @decrypted_data
154
+ end
155
+
156
+ # Parse font dictionaries from decrypted data
157
+ #
158
+ # Parses the font dictionary, private dictionary, and CharStrings
159
+ # from the decrypted font data.
160
+ #
161
+ # @return [void]
162
+ def parse_dictionaries!
163
+ decrypt! unless decrypted?
164
+
165
+ # Parse font dictionary
166
+ @font_dictionary = Type1::FontDictionary.parse(@decrypted_data)
167
+
168
+ # Parse private dictionary
169
+ @private_dict = Type1::PrivateDict.parse(@decrypted_data)
170
+
171
+ # Parse CharStrings
172
+ @charstrings = Type1::CharStrings.parse(@decrypted_data, @private_dict)
173
+ end
174
+
175
+ # Get font name from font dictionary
176
+ #
177
+ # @return [String, nil] Font name or nil if not found
178
+ def font_name
179
+ return @font_dictionary&.font_name if @font_dictionary
180
+
181
+ extract_dictionary_value("/FontName")
182
+ end
183
+
184
+ # Get full name from FontInfo
185
+ #
186
+ # @return [String, nil] Full name or nil if not found
187
+ def full_name
188
+ return @font_dictionary&.font_info&.full_name if @font_dictionary
189
+
190
+ extract_fontinfo_value("FullName")
191
+ end
192
+
193
+ # Get family name from FontInfo
194
+ #
195
+ # @return [String, nil] Family name or nil if not found
196
+ def family_name
197
+ return @font_dictionary&.font_info&.family_name if @font_dictionary
198
+
199
+ extract_fontinfo_value("FamilyName")
200
+ end
201
+
202
+ # Get version from FontInfo
203
+ #
204
+ # @return [String, nil] Version or nil if not found
205
+ def version
206
+ return @font_dictionary&.font_info&.version if @font_dictionary
207
+
208
+ extract_fontinfo_value("version")
209
+ end
210
+
211
+ # Get list of glyph names
212
+ #
213
+ # @return [Array<String>] Glyph names
214
+ def glyph_names
215
+ return [] unless @charstrings
216
+
217
+ @charstrings.glyph_names
218
+ end
219
+
220
+ # Check if dictionaries have been parsed
221
+ #
222
+ # @return [Boolean] True if dictionaries have been parsed
223
+ def parsed_dictionaries?
224
+ !@font_dictionary.nil?
225
+ end
226
+
227
+ private
228
+
229
+ # Parse font data based on format
230
+ def parse_font_data
231
+ case @format
232
+ when :pfb
233
+ parse_pfb
234
+ when :pfa
235
+ parse_pfa
236
+ else
237
+ raise Fontisan::Error, "Unknown format: #{@format}"
238
+ end
239
+ end
240
+
241
+ # Parse PFB format
242
+ def parse_pfb
243
+ parser = Type1::PFBParser.new
244
+ parser.parse(@data)
245
+
246
+ # PFB has alternating ASCII and binary parts
247
+ # ASCII parts contain font dictionary
248
+ # Binary parts contain encrypted CharStrings
249
+ @clear_text = parser.ascii_text
250
+ @encrypted_portion = parser.binary_data
251
+ end
252
+
253
+ # Parse PFA format
254
+ def parse_pfa
255
+ parser = Type1::PFAParser.new
256
+ parser.parse(@data)
257
+
258
+ @clear_text = parser.clear_text
259
+ @encrypted_portion = parser.encrypted_hex
260
+ end
261
+
262
+ # Detect format from data
263
+ #
264
+ # @param data [String] Font data
265
+ # @return [Symbol] Detected format (:pfb or :pfa)
266
+ def detect_format(data)
267
+ if Type1::PFBParser.pfb_file?(data)
268
+ :pfb
269
+ elsif Type1::PFAParser.pfa_file?(data)
270
+ :pfa
271
+ else
272
+ raise Fontisan::Error,
273
+ "Cannot detect Type 1 format: not a valid PFB or PFA file"
274
+ end
275
+ end
276
+
277
+ # Extract value from font dictionary
278
+ #
279
+ # @param key [String] Dictionary key (e.g., "/FontName")
280
+ # @return [String, nil] Value or nil if not found
281
+ def extract_dictionary_value(key)
282
+ text = decrypted? ? @decrypted_data : @clear_text
283
+
284
+ # Look for /FontName /name def pattern
285
+ pattern = /#{Regexp.escape(key)}\s+\/([^\s]+)\s+def/
286
+ match = text.match(pattern)
287
+ return nil unless match
288
+
289
+ match[1]
290
+ end
291
+
292
+ # Extract value from FontInfo dictionary
293
+ #
294
+ # @param key [String] FontInfo key (e.g., "FullName")
295
+ # @return [String, nil] Value or nil if not found
296
+ def extract_fontinfo_value(key)
297
+ text = decrypted? ? @decrypted_data : @clear_text
298
+
299
+ # Look for (FullName) readonly (value) readonly pattern
300
+ # This pattern handles nested parentheses in values
301
+ pattern = /\(#{Regexp.escape(key)}\)\s+readonly\s+(\([^()]*\)|\((?:[^()]*\([^()]*\)[^()]*)*\))\s+readonly/
302
+ match = text.match(pattern)
303
+ return match[1].gsub(/^\(|\)$/, "") if match
304
+
305
+ # Look for /FullName (value) def pattern
306
+ pattern = /\/#{Regexp.escape(key)}\s+\(([^)]+)\)\s+def/
307
+ match = text.match(pattern)
308
+ return match[1] if match
309
+
310
+ # Look for /FullName (value) readonly readonly pattern
311
+ pattern = /\/#{Regexp.escape(key)}\s+(\([^()]*\)|\((?:[^()]*\([^()]*\)[^()]*)*\))\s+readonly\s+readonly/
312
+ match = text.match(pattern)
313
+ return match[1].gsub(/^\(|\)$/, "") if match
314
+
315
+ nil
316
+ end
317
+ end
318
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fontisan
4
- VERSION = "0.2.11"
4
+ VERSION = "0.2.13"
5
5
  end
data/lib/fontisan.rb CHANGED
@@ -83,6 +83,7 @@ require_relative "fontisan/true_type_collection"
83
83
  require_relative "fontisan/open_type_collection"
84
84
  require_relative "fontisan/woff_font"
85
85
  require_relative "fontisan/woff2_font"
86
+ require_relative "fontisan/type1_font"
86
87
 
87
88
  # Font extensions for table-based construction
88
89
  require_relative "fontisan/true_type_font_extensions"
@@ -166,6 +167,7 @@ require_relative "fontisan/collection/writer"
166
167
  require_relative "fontisan/collection/builder"
167
168
 
168
169
  # Format conversion infrastructure
170
+ require_relative "fontisan/conversion_options"
169
171
  require_relative "fontisan/converters/conversion_strategy"
170
172
  require_relative "fontisan/converters/table_copier"
171
173
  require_relative "fontisan/converters/outline_converter"
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.11
4
+ version: 0.2.13
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-17 00:00:00.000000000 Z
11
+ date: 2026-01-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64
@@ -120,9 +120,10 @@ files:
120
120
  - docs/APPLE_LEGACY_FONTS.adoc
121
121
  - docs/COLLECTION_VALIDATION.adoc
122
122
  - docs/COLOR_FONTS.adoc
123
- - docs/DOCUMENTATION_SUMMARY.md
123
+ - docs/CONVERSION_GUIDE.adoc
124
124
  - docs/EXTRACT_TTC_MIGRATION.md
125
125
  - docs/FONT_HINTING.adoc
126
+ - docs/TYPE1_FONTS.adoc
126
127
  - docs/VALIDATION.adoc
127
128
  - docs/VARIABLE_FONT_OPERATIONS.adoc
128
129
  - docs/WOFF_WOFF2_FORMATS.adoc
@@ -168,6 +169,7 @@ files:
168
169
  - lib/fontisan/config/variable_settings.yml
169
170
  - lib/fontisan/config/woff2_settings.yml
170
171
  - lib/fontisan/constants.rb
172
+ - lib/fontisan/conversion_options.rb
171
173
  - lib/fontisan/converters/cff_table_builder.rb
172
174
  - lib/fontisan/converters/collection_converter.rb
173
175
  - lib/fontisan/converters/conversion_strategy.rb
@@ -178,6 +180,7 @@ files:
178
180
  - lib/fontisan/converters/outline_optimizer.rb
179
181
  - lib/fontisan/converters/svg_generator.rb
180
182
  - lib/fontisan/converters/table_copier.rb
183
+ - lib/fontisan/converters/type1_converter.rb
181
184
  - lib/fontisan/converters/woff2_encoder.rb
182
185
  - lib/fontisan/converters/woff_writer.rb
183
186
  - lib/fontisan/dfont_collection.rb
@@ -348,6 +351,30 @@ files:
348
351
  - lib/fontisan/true_type_collection.rb
349
352
  - lib/fontisan/true_type_font.rb
350
353
  - lib/fontisan/true_type_font_extensions.rb
354
+ - lib/fontisan/type1.rb
355
+ - lib/fontisan/type1/afm_generator.rb
356
+ - lib/fontisan/type1/afm_parser.rb
357
+ - lib/fontisan/type1/agl.rb
358
+ - lib/fontisan/type1/cff_to_type1_converter.rb
359
+ - lib/fontisan/type1/charstring_converter.rb
360
+ - lib/fontisan/type1/charstrings.rb
361
+ - lib/fontisan/type1/conversion_options.rb
362
+ - lib/fontisan/type1/decryptor.rb
363
+ - lib/fontisan/type1/encodings.rb
364
+ - lib/fontisan/type1/font_dictionary.rb
365
+ - lib/fontisan/type1/generator.rb
366
+ - lib/fontisan/type1/inf_generator.rb
367
+ - lib/fontisan/type1/pfa_generator.rb
368
+ - lib/fontisan/type1/pfa_parser.rb
369
+ - lib/fontisan/type1/pfb_generator.rb
370
+ - lib/fontisan/type1/pfb_parser.rb
371
+ - lib/fontisan/type1/pfm_generator.rb
372
+ - lib/fontisan/type1/pfm_parser.rb
373
+ - lib/fontisan/type1/private_dict.rb
374
+ - lib/fontisan/type1/seac_expander.rb
375
+ - lib/fontisan/type1/ttf_to_type1_converter.rb
376
+ - lib/fontisan/type1/upm_scaler.rb
377
+ - lib/fontisan/type1_font.rb
351
378
  - lib/fontisan/utilities/brotli_wrapper.rb
352
379
  - lib/fontisan/utilities/checksum_calculator.rb
353
380
  - lib/fontisan/utils/thread_pool.rb
@@ -1,141 +0,0 @@
1
- # Fontisan Documentation Summary
2
-
3
- This document provides an overview of all Fontisan documentation files and their purpose.
4
-
5
- ## Main Documentation
6
-
7
- ### README.adoc
8
- Primary user guide and reference documentation for Fontisan.
9
-
10
- ## Feature Guides
11
-
12
- ### Font Hinting
13
- - **File**: `docs/FONT_HINTING.adoc`
14
- - **Purpose**: Documentation for font hinting features in Fontisan
15
- - **Topics**: TrueType and PostScript hinting, hint extraction and application
16
-
17
- ### Variable Font Operations
18
- - **File**: `docs/VARIABLE_FONT_OPERATIONS.adoc`
19
- - **Purpose**: Guide for working with variable fonts
20
- - **Topics**: Instance generation, axis manipulation, variation preservation
21
-
22
- ### TTC Migration Guide
23
- - **File**: `docs/EXTRACT_TTC_MIGRATION.md`
24
- - **Purpose**: Migration guide for users of the `extract-ttc` gem
25
- - **Topics**: Moving from extract-ttc to Fontisan pack/unpack commands
26
-
27
- ### Web Font Formats
28
- - **File**: `docs/WOFF_WOFF2_FORMATS.adoc`
29
- - **Purpose**: Documentation for WOFF and WOFF2 web font formats
30
- - **Topics**: Conversion to/from WOFF/WOFF2, web optimization
31
-
32
- ### Color Fonts
33
- - **File**: `docs/COLOR_FONTS.adoc`
34
- - **Purpose**: Guide for color font formats
35
- - **Topics**: COLR/CPAL, SVG-in-OpenType, sbix color fonts
36
-
37
- ### Validation
38
- - **File**: `docs/VALIDATION.adoc`
39
- - **Purpose**: Font validation framework documentation
40
- - **Topics**: Validation profiles, quality checks, OpenType spec compliance
41
-
42
- ### Apple Legacy Fonts
43
- - **File**: `docs/APPLE_LEGACY_FONTS.adoc`
44
- - **Purpose**: Documentation for Apple legacy font formats
45
- - **Topics**: dfont format, Mac suitcase fonts
46
-
47
- ### Collection Validation
48
- - **File**: `docs/COLLECTION_VALIDATION.adoc`
49
- - **Purpose**: Guide for validating font collections (TTC/OTC/dfont)
50
- - **Topics**: Collection-specific validation, profile selection
51
-
52
- ## CLI Commands Reference
53
-
54
- Fontisan provides the following CLI commands:
55
-
56
- | Command | Purpose |
57
- |---------|---------|
58
- | `info` | Display font information |
59
- | `ls` | List contents (fonts in collection or font summary) |
60
- | `tables` | List OpenType tables |
61
- | `glyphs` | List glyph names |
62
- | `unicode` | List Unicode to glyph mappings |
63
- | `variable` | Display variable font information |
64
- | `optical-size` | Display optical size information |
65
- | `scripts` | List supported scripts from GSUB/GPOS tables |
66
- | `features` | List GSUB/GPOS features |
67
- | `subset` | Subset a font to specific glyphs |
68
- | `convert` | Convert font to different format |
69
- | `instance` | Generate static font instance from variable font |
70
- | `pack` | Pack multiple fonts into TTC/OTC collection |
71
- | `unpack` | Unpack fonts from TTC/OTC collection |
72
- | `validate` | Validate font file |
73
- | `export` | Export font to TTX/YAML/JSON format |
74
- | `dump-table` | Dump raw table data to stdout |
75
- | `version` | Display version information |
76
-
77
- ## Ruby API Reference
78
-
79
- ### Fontisan Module Methods
80
-
81
- #### `Fontisan.info(path, brief: false, font_index: 0)`
82
- Get font information. Supports both full and brief modes.
83
-
84
- - **Parameters**:
85
- - `path` (String): Path to font file
86
- - `brief` (Boolean): Use brief mode for fast identification (default: false)
87
- - `font_index` (Integer): Index for TTC/OTC files (default: 0)
88
- - **Returns**: `Models::FontInfo`, `Models::CollectionInfo`, or `Models::CollectionBriefInfo`
89
- - **Example**:
90
- ```ruby
91
- info = Fontisan.info("font.ttf", brief: true)
92
- puts info.family_name
93
- ```
94
-
95
- #### `Fontisan.validate(path, profile: :default, options: {})`
96
- Validate a font file using specified profile.
97
-
98
- - **Parameters**:
99
- - `path` (String): Path to font file
100
- - `profile` (Symbol/String): Validation profile (default: :default)
101
- - `options` (Hash): Additional validation options
102
- - **Available Profiles**:
103
- - `:indexability` - Fast validation for font discovery
104
- - `:usability` - Basic usability for installation
105
- - `:production` - Comprehensive quality checks (default)
106
- - `:web` - Web embedding and optimization
107
- - `:spec_compliance` - Full OpenType spec compliance
108
- - `:default` - Alias for production profile
109
- - **Returns**: `Models::ValidationReport`
110
- - **Example**:
111
- ```ruby
112
- report = Fontisan.validate("font.ttf", profile: :web)
113
- puts "Errors: #{report.summary.errors}"
114
- ```
115
-
116
- ### Fontisan::FontLoader
117
-
118
- Module for loading fonts in different modes.
119
-
120
- - **Methods**:
121
- - `load(path, mode: :full)` - Load a font file
122
- - **Loading Modes**:
123
- - `:full` - Load all tables
124
- - `:metadata` - Load only metadata tables (name, head, hhea, maxp, OS/2, post)
125
- - `:tables` - Load specific tables only
126
- - `:structure` - Load structure tables only
127
-
128
- ## Verification
129
-
130
- Documentation examples are verified by `spec/documentation_examples_spec.rb`.
131
-
132
- This spec ensures that:
133
- 1. All CLI commands referenced in documentation exist
134
- 2. All Ruby API methods are available
135
- 3. All documentation files are present
136
- 4. Command examples reference valid commands
137
-
138
- Run verification with:
139
- ```bash
140
- bundle exec rspec spec/documentation_examples_spec.rb -v
141
- ```