fontisan 0.4.32 → 0.4.34

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9e6bea227da5d5c91a615e71d69d65cd7f8c98b7f374719e0f81da4cccc65c4c
4
- data.tar.gz: f38990f5900782d7881a2a4b676d09c1f656599f1281bb63086ed0f4be975d9b
3
+ metadata.gz: 1c278ba38670bfc65e41805d14d6bc175ae451f0e5400e4b25036bf3ae6a84f6
4
+ data.tar.gz: 59ec85b42b6879f589c13429e8e490a41eb32257530207647203c010ee461182
5
5
  SHA512:
6
- metadata.gz: 2094827e33d51d8352391ff13ca61b450b17e51a6b588ae3ea4844b4ffd05d0e2845c1c031616af0ae140488367ddcc29cbedc2d808053d006063d9d53f803ba
7
- data.tar.gz: cbb253b31ba6f06442c3b08eda2acb046f26546652b80fa5b28140144ffeba415bdb92b9a9f4ef6db7398acd15364df1f800dfcf6ba5d19c899ea3b509f69317
6
+ metadata.gz: 17b9aeaeeb0589379b2ff65413aabb3db15c4d0dd323bd4891e4863a9dca2fdb7190fd795ebcd62fa16d6d570e7f7bb50c740b9947c0b42a5e673380e0164cd2
7
+ data.tar.gz: c9b35dd44d43fc634b1bf07aa84d2bd99527e4695636424cbf46aca757615b09d6dc27f3512e701ab8676d1111f51f980dd97d0d0fd82c47f4177592f9b94c95
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2026-07-09 14:55:09 UTC using RuboCop version 1.86.1.
3
+ # on 2026-07-11 04:55:54 UTC using RuboCop version 1.86.1.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
@@ -12,182 +12,39 @@ Gemspec/RequiredRubyVersion:
12
12
  - 'docs/node_modules/speakingurl/speakingurl-rails.gemspec'
13
13
  - 'fontisan.gemspec'
14
14
 
15
- # Offense count: 276
16
- # This cop supports safe autocorrection (--autocorrect).
17
- # Configuration parameters: EnforcedStyle, IndentationWidth.
18
- # SupportedStyles: with_first_argument, with_fixed_indentation
19
- Layout/ArgumentAlignment:
20
- Enabled: false
21
-
22
- # Offense count: 22
23
- # This cop supports safe autocorrection (--autocorrect).
24
- # Configuration parameters: EnforcedStyle, IndentationWidth.
25
- # SupportedStyles: with_first_element, with_fixed_indentation
26
- Layout/ArrayAlignment:
27
- Exclude:
28
- - 'lib/fontisan/collection/writer.rb'
29
- - 'lib/fontisan/tables/cff2/index_builder.rb'
30
- - 'lib/fontisan/ufo/compile/cbdt_cblc.rb'
31
- - 'lib/fontisan/ufo/compile/cff.rb'
32
- - 'lib/fontisan/ufo/compile/cmap.rb'
33
- - 'lib/fontisan/ufo/compile/cpal.rb'
34
- - 'lib/fontisan/ufo/compile/gvar.rb'
35
- - 'lib/fontisan/ufo/compile/item_variation_store.rb'
36
- - 'lib/fontisan/ufo/compile/sbix.rb'
37
- - 'lib/fontisan/ufo/compile/stat.rb'
38
- - 'lib/fontisan/ufo/info.rb'
39
- - 'spec/fontisan/stitcher/glyph_signature_spec.rb'
40
- - 'spec/fontisan/svg_to_glyf/contour_builder_spec.rb'
41
- - 'spec/fontisan/ufo/compile/feature_writers/mkmk_spec.rb'
42
- - 'spec/fontisan/ufo/compile/filters/transformations_spec.rb'
43
-
44
- # Offense count: 1
45
- # This cop supports safe autocorrection (--autocorrect).
46
- # Configuration parameters: IndentationWidth.
47
- Layout/AssignmentIndentation:
48
- Exclude:
49
- - 'lib/fontisan/woff2/glyf_loca_reconstruct.rb'
50
-
51
- # Offense count: 58
52
- # This cop supports safe autocorrection (--autocorrect).
53
- # Configuration parameters: EnforcedStyleAlignWith.
54
- # SupportedStylesAlignWith: either, start_of_block, start_of_line
55
- Layout/BlockAlignment:
56
- Enabled: false
57
-
58
- # Offense count: 55
59
- # This cop supports safe autocorrection (--autocorrect).
60
- Layout/BlockEndNewline:
61
- Enabled: false
62
-
63
- # Offense count: 29
64
- # This cop supports safe autocorrection (--autocorrect).
65
- Layout/ClosingParenthesisIndentation:
66
- Exclude:
67
- - 'benchmark/compile_benchmark.rb'
68
- - 'lib/fontisan/ufo/compile/fvar.rb'
69
- - 'lib/fontisan/ufo/writer.rb'
70
- - 'spec/fontisan/stitcher/deduplicator_spec.rb'
71
- - 'spec/fontisan/stitcher/include_codepoints_map_spec.rb'
72
- - 'spec/fontisan/svg_to_glyf/affine_transform_spec.rb'
73
- - 'spec/fontisan/ufo/compile/filters/propagate_anchors_spec.rb'
74
- - 'spec/fontisan/ufo/compile_gvar_spec.rb'
75
- - 'spec/fontisan/ufo/compile_item_variation_store_spec.rb'
76
- - 'spec/fontisan/ufo/filters_spec.rb'
77
-
78
- # Offense count: 2
79
- # This cop supports safe autocorrection (--autocorrect).
80
- # Configuration parameters: AllowForAlignment.
81
- Layout/CommentIndentation:
82
- Exclude:
83
- - 'lib/fontisan/ufo/compile/cff.rb'
84
-
85
15
  # Offense count: 4
86
16
  # This cop supports safe autocorrection (--autocorrect).
87
- Layout/ElseAlignment:
88
- Exclude:
89
- - 'lib/fontisan/commands/validate_collection_command.rb'
90
- - 'lib/fontisan/converters/collection_converter.rb'
91
- - 'spec/fontisan/woff2/sfnt_checksum_spec.rb'
92
-
93
- # Offense count: 4
94
- # This cop supports safe autocorrection (--autocorrect).
95
- # Configuration parameters: EnforcedStyleAlignWith.
96
- # SupportedStylesAlignWith: keyword, variable, start_of_line
97
- Layout/EndAlignment:
98
- Exclude:
99
- - 'lib/fontisan/commands/validate_collection_command.rb'
100
- - 'lib/fontisan/converters/collection_converter.rb'
101
- - 'spec/fontisan/woff2/sfnt_checksum_spec.rb'
102
-
103
- # Offense count: 29
104
- # This cop supports safe autocorrection (--autocorrect).
105
- # Configuration parameters: EnforcedStyle, IndentationWidth.
106
- # SupportedStyles: consistent, consistent_relative_to_receiver, special_for_inner_method_call, special_for_inner_method_call_in_parentheses
107
- Layout/FirstArgumentIndentation:
108
- Exclude:
109
- - 'benchmark/compile_benchmark.rb'
110
- - 'lib/fontisan/ufo/compile/fvar.rb'
111
- - 'lib/fontisan/ufo/writer.rb'
112
- - 'spec/fontisan/stitcher/deduplicator_spec.rb'
113
- - 'spec/fontisan/stitcher/include_codepoints_map_spec.rb'
114
- - 'spec/fontisan/svg_to_glyf/affine_transform_spec.rb'
115
- - 'spec/fontisan/ufo/compile/filters/propagate_anchors_spec.rb'
116
- - 'spec/fontisan/ufo/compile_gvar_spec.rb'
117
- - 'spec/fontisan/ufo/compile_item_variation_store_spec.rb'
118
- - 'spec/fontisan/ufo/filters_spec.rb'
119
-
120
- # Offense count: 14
121
- # This cop supports safe autocorrection (--autocorrect).
122
- # Configuration parameters: EnforcedStyle, IndentationWidth.
123
- # SupportedStyles: special_inside_parentheses, consistent, align_brackets
124
- Layout/FirstArrayElementIndentation:
125
- Exclude:
126
- - 'spec/fontisan/stitcher/glyph_signature_spec.rb'
127
- - 'spec/fontisan/ufo/compile/feature_writers/mark_spec.rb'
128
-
129
- # Offense count: 194
130
- # This cop supports safe autocorrection (--autocorrect).
131
17
  # Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle.
132
18
  # SupportedHashRocketStyles: key, separator, table
133
19
  # SupportedColonStyles: key, separator, table
134
20
  # SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit
135
21
  Layout/HashAlignment:
136
- Enabled: false
137
-
138
- # Offense count: 1
139
- # This cop supports safe autocorrection (--autocorrect).
140
- # Configuration parameters: EnforcedStyle.
141
- # SupportedStyles: normal, indented_internal_methods
142
- Layout/IndentationConsistency:
143
22
  Exclude:
144
- - 'lib/fontisan/ufo/compile/cff.rb'
23
+ - 'lib/fontisan/ufo/compile/variable_otf.rb'
24
+ - 'spec/fontisan/ufo/compile/cff2_spec.rb'
145
25
 
146
- # Offense count: 121
26
+ # Offense count: 9
147
27
  # This cop supports safe autocorrection (--autocorrect).
148
28
  # Configuration parameters: Width, EnforcedStyleAlignWith, AllowedPatterns.
149
29
  # SupportedStylesAlignWith: start_of_line, relative_to_receiver
150
30
  Layout/IndentationWidth:
151
- Enabled: false
31
+ Exclude:
32
+ - 'lib/fontisan/commands/audit_command.rb'
33
+ - 'lib/fontisan/ufo/compile/glyf_loca.rb'
152
34
 
153
- # Offense count: 2235
35
+ # Offense count: 1969
154
36
  # This cop supports safe autocorrection (--autocorrect).
155
37
  # Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
156
38
  # URISchemes: http, https
157
39
  Layout/LineLength:
158
40
  Enabled: false
159
41
 
160
- # Offense count: 7
161
- # This cop supports safe autocorrection (--autocorrect).
162
- # Configuration parameters: EnforcedStyle.
163
- # SupportedStyles: symmetrical, new_line, same_line
164
- Layout/MultilineArrayBraceLayout:
165
- Exclude:
166
- - 'spec/fontisan/stitcher/glyph_signature_spec.rb'
167
- - 'spec/fontisan/ufo/compile/feature_writers/mark_spec.rb'
168
-
169
- # Offense count: 29
170
- # This cop supports safe autocorrection (--autocorrect).
171
- # Configuration parameters: EnforcedStyle.
172
- # SupportedStyles: symmetrical, new_line, same_line
173
- Layout/MultilineMethodCallBraceLayout:
174
- Exclude:
175
- - 'benchmark/compile_benchmark.rb'
176
- - 'lib/fontisan/ufo/compile/fvar.rb'
177
- - 'lib/fontisan/ufo/writer.rb'
178
- - 'spec/fontisan/stitcher/deduplicator_spec.rb'
179
- - 'spec/fontisan/stitcher/include_codepoints_map_spec.rb'
180
- - 'spec/fontisan/svg_to_glyf/affine_transform_spec.rb'
181
- - 'spec/fontisan/ufo/compile/filters/propagate_anchors_spec.rb'
182
- - 'spec/fontisan/ufo/compile_gvar_spec.rb'
183
- - 'spec/fontisan/ufo/compile_item_variation_store_spec.rb'
184
- - 'spec/fontisan/ufo/filters_spec.rb'
185
-
186
- # Offense count: 225
42
+ # Offense count: 2
187
43
  # This cop supports safe autocorrection (--autocorrect).
188
44
  # Configuration parameters: AllowInHeredoc.
189
45
  Layout/TrailingWhitespace:
190
- Enabled: false
46
+ Exclude:
47
+ - 'lib/fontisan/commands/audit_command.rb'
191
48
 
192
49
  # Offense count: 2
193
50
  Lint/CopDirectiveSyntax:
@@ -248,7 +105,7 @@ Lint/StructNewOverride:
248
105
  Exclude:
249
106
  - 'lib/fontisan/optimizers/pattern_analyzer.rb'
250
107
 
251
- # Offense count: 24
108
+ # Offense count: 33
252
109
  # This cop supports safe autocorrection (--autocorrect).
253
110
  # Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions.
254
111
  # NotImplementedExceptions: NotImplementedError
@@ -261,12 +118,12 @@ Lint/UselessConstantScoping:
261
118
  - 'lib/fontisan/conversion_options.rb'
262
119
  - 'lib/fontisan/type1/charstrings.rb'
263
120
 
264
- # Offense count: 715
121
+ # Offense count: 752
265
122
  # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
266
123
  Metrics/AbcSize:
267
124
  Enabled: false
268
125
 
269
- # Offense count: 34
126
+ # Offense count: 37
270
127
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
271
128
  # AllowedMethods: refine
272
129
  Metrics/BlockLength:
@@ -284,28 +141,28 @@ Metrics/CollectionLiteralLength:
284
141
  - 'lib/fontisan/type1/agl.rb'
285
142
  - 'lib/fontisan/type1/encodings.rb'
286
143
 
287
- # Offense count: 367
144
+ # Offense count: 375
288
145
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
289
146
  Metrics/CyclomaticComplexity:
290
147
  Enabled: false
291
148
 
292
- # Offense count: 990
149
+ # Offense count: 1019
293
150
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
294
151
  Metrics/MethodLength:
295
152
  Max: 135
296
153
 
297
- # Offense count: 30
154
+ # Offense count: 31
298
155
  # Configuration parameters: CountKeywordArgs.
299
156
  Metrics/ParameterLists:
300
157
  Max: 39
301
158
  MaxOptionalParameters: 4
302
159
 
303
- # Offense count: 279
160
+ # Offense count: 289
304
161
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
305
162
  Metrics/PerceivedComplexity:
306
163
  Enabled: false
307
164
 
308
- # Offense count: 202
165
+ # Offense count: 209
309
166
  # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
310
167
  # AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to
311
168
  Naming/MethodParameterName:
@@ -371,7 +228,7 @@ RSpec/AnyInstance:
371
228
  RSpec/ContextWording:
372
229
  Enabled: false
373
230
 
374
- # Offense count: 39
231
+ # Offense count: 40
375
232
  # Configuration parameters: IgnoredMetadata.
376
233
  RSpec/DescribeClass:
377
234
  Enabled: false
@@ -387,7 +244,7 @@ RSpec/DescribeMethod:
387
244
  - 'spec/fontisan/type1/seac_expander_spec.rb'
388
245
  - 'spec/fontisan/type1_real_fonts_spec.rb'
389
246
 
390
- # Offense count: 1710
247
+ # Offense count: 1726
391
248
  # Configuration parameters: CountAsOne.
392
249
  RSpec/ExampleLength:
393
250
  Max: 66
@@ -465,7 +322,7 @@ RSpec/MultipleDescribes:
465
322
  - 'spec/fontisan/utils/thread_pool_spec.rb'
466
323
  - 'spec/fontisan/variation/cache_spec.rb'
467
324
 
468
- # Offense count: 2126
325
+ # Offense count: 2149
469
326
  RSpec/MultipleExpectations:
470
327
  Max: 19
471
328
 
@@ -529,16 +386,6 @@ Rake/MethodDefinitionInTask:
529
386
  Exclude:
530
387
  - 'Rakefile'
531
388
 
532
- # Offense count: 68
533
- # This cop supports safe autocorrection (--autocorrect).
534
- # Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, AllowedMethods, AllowedPatterns, AllowBracesOnProceduralOneLiners, BracesRequiredMethods.
535
- # SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces
536
- # ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object
537
- # FunctionalMethods: let, let!, subject, watch
538
- # AllowedMethods: lambda, proc, it
539
- Style/BlockDelimiters:
540
- Enabled: false
541
-
542
389
  # Offense count: 6
543
390
  # This cop supports safe autocorrection (--autocorrect).
544
391
  Style/ComparableClamp:
@@ -573,47 +420,9 @@ Style/HashLikeCase:
573
420
  - 'lib/fontisan/commands/unpack_command.rb'
574
421
  - 'lib/fontisan/models/validation_report.rb'
575
422
 
576
- # Offense count: 16
577
- # This cop supports safe autocorrection (--autocorrect).
578
- Style/MultilineIfModifier:
579
- Exclude:
580
- - 'lib/fontisan/svg/standalone_glyph.rb'
581
- - 'lib/fontisan/svg_to_glyf/geometry/transform_parser.rb'
582
- - 'lib/fontisan/ufo/cli.rb'
583
- - 'lib/fontisan/ufo/convert.rb'
584
- - 'lib/fontisan/ufo/convert/from_bin_data.rb'
585
- - 'lib/fontisan/ufo/convert/to_dfont.rb'
586
- - 'lib/fontisan/ufo/convert/to_otc.rb'
587
- - 'lib/fontisan/ufo/convert/to_postscript.rb'
588
- - 'lib/fontisan/ufo/convert/to_woff.rb'
589
- - 'lib/fontisan/ufo/convert/to_woff2.rb'
590
- - 'lib/fontisan/ufo/plist.rb'
591
- - 'lib/fontisan/woff2/collection_decoder.rb'
592
- - 'lib/fontisan/woff2/collection_encoder.rb'
593
- - 'spec/support/fixture_downloader.rb'
594
-
595
- # Offense count: 4
596
- # This cop supports safe autocorrection (--autocorrect).
597
- Style/MultilineTernaryOperator:
598
- Exclude:
599
- - 'lib/fontisan/commands/validate_collection_command.rb'
600
- - 'lib/fontisan/converters/collection_converter.rb'
601
- - 'spec/fontisan/woff2/sfnt_checksum_spec.rb'
602
-
603
423
  # Offense count: 1
604
424
  # Configuration parameters: AllowedMethods.
605
425
  # AllowedMethods: respond_to_missing?
606
426
  Style/OptionalBooleanParameter:
607
427
  Exclude:
608
428
  - 'lib/fontisan/conversion_options.rb'
609
-
610
- # Offense count: 22
611
- # This cop supports safe autocorrection (--autocorrect).
612
- # Configuration parameters: EnforcedStyleForMultiline.
613
- # SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma
614
- Style/TrailingCommaInArguments:
615
- Exclude:
616
- - 'benchmark/compile_benchmark.rb'
617
- - 'spec/fontisan/ufo/compile/filters/propagate_anchors_spec.rb'
618
- - 'spec/fontisan/ufo/compile_gvar_spec.rb'
619
- - 'spec/fontisan/ufo/filters_spec.rb'
@@ -8,25 +8,25 @@ Each file is `NN-short-name.md` where `NN` is the priority order.
8
8
  ### P0 — Correctness gaps (open bugs / silent failures)
9
9
  - [x] ~~[01 — CBDT/CBLC GID-stable propagation](01-cbdt-cblc-gid-stable-propagation.md)~~ ✓ Done (PR #115)
10
10
  - [x] ~~[02 — Collection-mode outline-priority regression](02-collection-outline-priority.md)~~ ✓ Already fixed
11
- - [15 — CFF/CFF2 subsetter strategy](15-cff-cff2-subsetter-strategy.md) Head crash fixed, profiles updated; CFF→UFO extraction done (TODO #10b); blocked on TODO #05 for full round-trip (PR #108 unskipped the test; now passing with TODO 01)
11
+ - [x] ~~[15 — CFF/CFF2 subsetter strategy](15-cff-cff2-subsetter-strategy.md)~~ Done CFF via UFO round-trip, CFF2 via standalone INDEX filter with VStore preservation
12
12
 
13
13
  ### P1 — High-value features
14
- - [03 — `fontisan audit` command (identity+style+features lens)](03-fontisan-audit-command.md)
14
+ - [x] ~~[03 — `fontisan audit` command (identity+style+features lens)](03-fontisan-audit-command.md)~~ ✓ Done (Phase 1: identity, style, coverage, OpenType layout, provenance; CLI `fontisan audit` with YAML/JSON output)
15
15
  - [x] ~~[04 — UFO composite glyph encoding](04-ufo-composite-glyph-encoding.md)~~ ✓ Done
16
16
  - [x] ~~[05 — OTF compiler real CFF charstrings](05-otf-compiler-real-cff.md)~~ ✓ Already working (discovered in PR #117 — Cff.build produces real Type 2 charstrings; stale TODO marker removed)
17
17
 
18
18
  ### P2 — Specialist feature parity
19
19
  - [x] ~~[07 — CPAL v1 header fields](07-cpal-v1-header-fields.md)~~ ✓ Done (v0.4.25)
20
20
  - [x] ~~[08 — CFF standard string table](08-cff-standard-string-table.md)~~ ✓ Done (full 391 SIDs per Adobe TN 5176)
21
- - [06 — CFF2 blend/vsindex operators](06-cff2-blend-vsindex-operators.md)
22
- - [09 — Type 1 seac expansion](09-type1-seac-expansion.md)
23
- - [10 — UFO image set + feature writers](10-ufo-image-set-feature-writers.md)
21
+ - [x] ~~[06 — CFF2 blend/vsindex operators](06-cff2-blend-vsindex-operators.md)~~ ✓ Done — `Cff2.build_variable` emits blend operators in charstrings; `VariableOtf` passes masters through; VStore embedded with regions
22
+ - [x] ~~[09 — Type 1 seac expansion](09-type1-seac-expansion.md)~~ ✓ Done — recursive seac expansion with cycle detection; `CharStrings.from_hash` factory added for clean test construction
23
+ - [x] ~~[10 — UFO image set + feature writers](10-ufo-image-set-feature-writers.md)~~ ✓ Done — `ImageSet` loads/writes UFO 3 images directory; `FeatureCompiler` parses liga/kern FEA constructs; `Compile::Gsub` builds LigatureSubst GSUB
24
24
  - [x] ~~[11 — kern groups.plist support](11-kern-groups-plist.md)~~ ✓ Done (Groups model in PR #116 + GPOS group resolution in this PR)
25
25
 
26
26
  ### P3 — Code-quality cleanup
27
27
  - [x] ~~[13 — Split `OctokitFetcher` out of `fixture_downloader.rb`](13-split-octokit-fetcher.md)~~ ✓ Done (v0.4.24)
28
28
  - [x] ~~[12 — `cbdt_fixture.rb` full BinData conversion](12-cbdt-fixture-bindata-conversion.md)~~ ✓ Done (critical tables converted; remaining os2/name/hmtx/cmap verified correct, BinData conversion is diminishing returns)
29
- - [14 — Rubocop baseline chip (per-namespace)](14-rubocop-baseline-chip.md)
29
+ - [x] ~~[14 — Rubocop baseline chip (per-namespace)](14-rubocop-baseline-chip.md)~~ ✓ Partially done — baseline reduced from 619 to 428 lines; all namespaces pass `rubocop` with 0 offenses; remaining baseline offenses are non-autocorrectable manual fixes (~7h total per TODO estimate)
30
30
 
31
31
  ## Convention
32
32
 
data/lib/fontisan/cli.rb CHANGED
@@ -31,6 +31,25 @@ module Fontisan
31
31
  desc "stitch", "Stitch glyphs from multiple source fonts into one output"
32
32
  subcommand "stitch", StitcherCli
33
33
 
34
+ desc "audit FONT_FILE", "Produce a structured audit report (identity, style, coverage, layout)"
35
+ option :output, type: :string,
36
+ desc: "Output file path. For collections, use a directory path."
37
+ option :no_codepoints, type: :boolean, default: false,
38
+ desc: "Omit the codepoint list from the report"
39
+ option :font_index, type: :numeric, default: nil,
40
+ desc: "Audit only the given face of a collection"
41
+ # Produce a structured font audit report (YAML or JSON).
42
+ def audit(font_file)
43
+ cmd_options = options.dup
44
+ cmd_options[:include_codepoints] = !options[:no_codepoints]
45
+ command = Commands::AuditCommand.new(font_file, cmd_options)
46
+ result = command.run
47
+ write_audit_result(result, options[:output])
48
+ rescue Errno::ENOENT
49
+ warn "File not found: #{font_file}" unless options[:quiet]
50
+ exit 1
51
+ end
52
+
34
53
  desc "info PATH", "Display font information"
35
54
  option :brief, type: :boolean, default: false,
36
55
  desc: "Brief mode - only essential info (5x faster, uses metadata loading)",
@@ -732,6 +751,52 @@ module Fontisan
732
751
  puts output unless options[:quiet]
733
752
  end
734
753
 
754
+ # Write the audit result to stdout or a file. For a single report,
755
+ # the output path (if given) is the file path. For a collection,
756
+ # the output path is a directory; each face is written as
757
+ # `{font_index:02d}-{postscript_name}.yaml`.
758
+ def write_audit_result(result, output_path)
759
+ if result.is_a?(Array)
760
+ write_audit_collection(result, output_path)
761
+ else
762
+ write_audit_single(result, output_path)
763
+ end
764
+ end
765
+
766
+ def write_audit_single(report, output_path)
767
+ content = serialize_audit(report)
768
+ if output_path
769
+ FileUtils.mkpath(File.dirname(output_path)) unless File.dirname(output_path) == "."
770
+ File.write(output_path, content)
771
+ else
772
+ puts content unless options[:quiet]
773
+ end
774
+ end
775
+
776
+ def write_audit_collection(reports, output_path)
777
+ if output_path
778
+ FileUtils.mkpath(output_path)
779
+ reports.each do |report|
780
+ name = audit_face_filename(report)
781
+ File.write(File.join(output_path, name), serialize_audit(report))
782
+ end
783
+ say("Wrote #{reports.size} reports to #{output_path}") unless options[:quiet]
784
+ else
785
+ puts serialize_audit(reports) unless options[:quiet]
786
+ end
787
+ end
788
+
789
+ def serialize_audit(report)
790
+ options[:format] == "json" ? report.to_json : report.to_yaml
791
+ end
792
+
793
+ def audit_face_filename(report)
794
+ safe_name = report.postscript_name&.gsub(%r{[^A-Za-z0-9._-]}, "_") || "font"
795
+ suffix = options[:format] == "json" ? "json" : "yaml"
796
+ idx = report.font_index || 0
797
+ format("%<idx>02d-%<name>s.%<suffix>s", idx: idx, name: safe_name, suffix: suffix)
798
+ end
799
+
735
800
  def serialize_report(report, format)
736
801
  format == :json ? report.to_json : report.to_yaml
737
802
  end