fontisan 0.4.7 → 0.4.8

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/CHANGELOG.md +13 -0
  3. data/docs/.vitepress/config.ts +0 -7
  4. data/docs/cli/index.md +5 -28
  5. data/docs/index.md +0 -2
  6. data/lib/fontisan/cli.rb +29 -8
  7. data/lib/fontisan/collection/reader/stats.rb +23 -0
  8. data/lib/fontisan/collection/reader.rb +90 -0
  9. data/lib/fontisan/collection.rb +1 -0
  10. data/lib/fontisan/commands/convert_command.rb +96 -18
  11. data/lib/fontisan/commands/multi_format_output.rb +59 -0
  12. data/lib/fontisan/commands/validate_collection_command.rb +121 -0
  13. data/lib/fontisan/commands.rb +2 -0
  14. data/lib/fontisan/error.rb +25 -0
  15. data/lib/fontisan/models.rb +0 -1
  16. data/lib/fontisan/stitcher/collection_result.rb +18 -0
  17. data/lib/fontisan/stitcher/partition_strategy/base.rb +23 -0
  18. data/lib/fontisan/stitcher/partition_strategy/blueprint.rb +24 -0
  19. data/lib/fontisan/stitcher/partition_strategy/by_plane.rb +131 -0
  20. data/lib/fontisan/stitcher/partition_strategy/partition.rb +24 -0
  21. data/lib/fontisan/stitcher/partition_strategy.rb +22 -0
  22. data/lib/fontisan/stitcher.rb +44 -10
  23. data/lib/fontisan/ufo/compile/name.rb +2 -2
  24. data/lib/fontisan/ufo/info.rb +48 -0
  25. data/lib/fontisan/unicode/plane.rb +56 -0
  26. data/lib/fontisan/unicode.rb +17 -0
  27. data/lib/fontisan/version.rb +1 -1
  28. data/lib/fontisan.rb +2 -2
  29. metadata +13 -18
  30. data/docs/cli/audit.md +0 -337
  31. data/lib/fontisan/cldr/aggregator.rb +0 -33
  32. data/lib/fontisan/cldr/cache_manager.rb +0 -110
  33. data/lib/fontisan/cldr/config.rb +0 -59
  34. data/lib/fontisan/cldr/download_error.rb +0 -9
  35. data/lib/fontisan/cldr/downloader.rb +0 -79
  36. data/lib/fontisan/cldr/error.rb +0 -8
  37. data/lib/fontisan/cldr/index.rb +0 -64
  38. data/lib/fontisan/cldr/index_builder.rb +0 -72
  39. data/lib/fontisan/cldr/unicode_set_parser.rb +0 -189
  40. data/lib/fontisan/cldr/unknown_version_error.rb +0 -9
  41. data/lib/fontisan/cldr/version_resolver.rb +0 -91
  42. data/lib/fontisan/cldr.rb +0 -23
  43. data/lib/fontisan/cli/cldr_cli.rb +0 -85
  44. data/lib/fontisan/config/cldr.yml +0 -22
  45. data/lib/fontisan/models/cldr/language_coverage.rb +0 -31
  46. data/lib/fontisan/models/cldr.rb +0 -12
data/lib/fontisan.rb CHANGED
@@ -72,10 +72,10 @@ module Fontisan
72
72
  autoload :VariationDataCorruptedError, "fontisan/error"
73
73
  autoload :MultipleCbdtSourcesError, "fontisan/error"
74
74
  autoload :GlyphLimitExceededError, "fontisan/error"
75
+ autoload :PartitionCapExceededError, "fontisan/error"
75
76
 
76
77
  # Namespace hubs (each hub declares its own child autoloads)
77
78
  autoload :Binary, "fontisan/binary"
78
- autoload :Cldr, "fontisan/cldr"
79
79
  autoload :Collection, "fontisan/collection"
80
80
  autoload :Commands, "fontisan/commands"
81
81
  autoload :Converters, "fontisan/converters"
@@ -92,6 +92,7 @@ module Fontisan
92
92
  autoload :Tables, "fontisan/tables"
93
93
  autoload :Type1, "fontisan/type1"
94
94
  autoload :Ufo, "fontisan/ufo"
95
+ autoload :Unicode, "fontisan/unicode"
95
96
  autoload :Utilities, "fontisan/utilities"
96
97
  autoload :Utils, "fontisan/utils"
97
98
  autoload :Validation, "fontisan/validation"
@@ -120,7 +121,6 @@ module Fontisan
120
121
  autoload :TrueTypeFont, "fontisan/true_type_font"
121
122
  autoload :TrueTypeFontExtensions, "fontisan/true_type_font_extensions"
122
123
  autoload :Type1Font, "fontisan/type1_font"
123
- autoload :CldrCli, "fontisan/cli/cldr_cli"
124
124
  autoload :Woff2Font, "fontisan/woff2_font"
125
125
  autoload :WoffFont, "fontisan/woff_font"
126
126
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fontisan
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.7
4
+ version: 0.4.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -173,7 +173,6 @@ files:
173
173
  - docs/api/validators/font-validator.md
174
174
  - docs/api/validators/helper.md
175
175
  - docs/api/validators/profile.md
176
- - docs/cli/audit.md
177
176
  - docs/cli/convert.md
178
177
  - docs/cli/dump-table.md
179
178
  - docs/cli/export.md
@@ -277,24 +276,13 @@ files:
277
276
  - lib/fontisan/binary.rb
278
277
  - lib/fontisan/binary/base_record.rb
279
278
  - lib/fontisan/binary/structures.rb
280
- - lib/fontisan/cldr.rb
281
- - lib/fontisan/cldr/aggregator.rb
282
- - lib/fontisan/cldr/cache_manager.rb
283
- - lib/fontisan/cldr/config.rb
284
- - lib/fontisan/cldr/download_error.rb
285
- - lib/fontisan/cldr/downloader.rb
286
- - lib/fontisan/cldr/error.rb
287
- - lib/fontisan/cldr/index.rb
288
- - lib/fontisan/cldr/index_builder.rb
289
- - lib/fontisan/cldr/unicode_set_parser.rb
290
- - lib/fontisan/cldr/unknown_version_error.rb
291
- - lib/fontisan/cldr/version_resolver.rb
292
279
  - lib/fontisan/cli.rb
293
- - lib/fontisan/cli/cldr_cli.rb
294
280
  - lib/fontisan/collection.rb
295
281
  - lib/fontisan/collection/builder.rb
296
282
  - lib/fontisan/collection/dfont_builder.rb
297
283
  - lib/fontisan/collection/offset_calculator.rb
284
+ - lib/fontisan/collection/reader.rb
285
+ - lib/fontisan/collection/reader/stats.rb
298
286
  - lib/fontisan/collection/shared_logic.rb
299
287
  - lib/fontisan/collection/table_analyzer.rb
300
288
  - lib/fontisan/collection/table_deduplicator.rb
@@ -309,6 +297,7 @@ files:
309
297
  - lib/fontisan/commands/info_command.rb
310
298
  - lib/fontisan/commands/instance_command.rb
311
299
  - lib/fontisan/commands/ls_command.rb
300
+ - lib/fontisan/commands/multi_format_output.rb
312
301
  - lib/fontisan/commands/optical_size_command.rb
313
302
  - lib/fontisan/commands/pack_command.rb
314
303
  - lib/fontisan/commands/scripts_command.rb
@@ -316,9 +305,9 @@ files:
316
305
  - lib/fontisan/commands/tables_command.rb
317
306
  - lib/fontisan/commands/unicode_command.rb
318
307
  - lib/fontisan/commands/unpack_command.rb
308
+ - lib/fontisan/commands/validate_collection_command.rb
319
309
  - lib/fontisan/commands/validate_command.rb
320
310
  - lib/fontisan/commands/variable_command.rb
321
- - lib/fontisan/config/cldr.yml
322
311
  - lib/fontisan/config/collection_settings.yml
323
312
  - lib/fontisan/config/conversion_matrix.yml
324
313
  - lib/fontisan/config/export_settings.yml
@@ -379,8 +368,6 @@ files:
379
368
  - lib/fontisan/models/all_scripts_features_info.rb
380
369
  - lib/fontisan/models/bitmap_glyph.rb
381
370
  - lib/fontisan/models/bitmap_strike.rb
382
- - lib/fontisan/models/cldr.rb
383
- - lib/fontisan/models/cldr/language_coverage.rb
384
371
  - lib/fontisan/models/collection_brief_info.rb
385
372
  - lib/fontisan/models/collection_font_summary.rb
386
373
  - lib/fontisan/models/collection_info.rb
@@ -444,9 +431,15 @@ files:
444
431
  - lib/fontisan/sfnt_font.rb
445
432
  - lib/fontisan/sfnt_table.rb
446
433
  - lib/fontisan/stitcher.rb
434
+ - lib/fontisan/stitcher/collection_result.rb
447
435
  - lib/fontisan/stitcher/deduplicator.rb
448
436
  - lib/fontisan/stitcher/glyph_limit.rb
449
437
  - lib/fontisan/stitcher/glyph_signature.rb
438
+ - lib/fontisan/stitcher/partition_strategy.rb
439
+ - lib/fontisan/stitcher/partition_strategy/base.rb
440
+ - lib/fontisan/stitcher/partition_strategy/blueprint.rb
441
+ - lib/fontisan/stitcher/partition_strategy/by_plane.rb
442
+ - lib/fontisan/stitcher/partition_strategy/partition.rb
450
443
  - lib/fontisan/stitcher/selector.rb
451
444
  - lib/fontisan/stitcher/selector/codepoints.rb
452
445
  - lib/fontisan/stitcher/selector/gid.rb
@@ -642,6 +635,8 @@ files:
642
635
  - lib/fontisan/ufo/reader.rb
643
636
  - lib/fontisan/ufo/transformation.rb
644
637
  - lib/fontisan/ufo/writer.rb
638
+ - lib/fontisan/unicode.rb
639
+ - lib/fontisan/unicode/plane.rb
645
640
  - lib/fontisan/utilities.rb
646
641
  - lib/fontisan/utilities/brotli_wrapper.rb
647
642
  - lib/fontisan/utilities/checksum_calculator.rb
data/docs/cli/audit.md DELETED
@@ -1,337 +0,0 @@
1
- ---
2
- title: audit
3
- ---
4
-
5
- # audit
6
-
7
- Produce a complete per-face font audit report — identity, style, metrics,
8
- coverage, licensing, hinting, color capabilities, variable-font detail,
9
- OpenType layout features, and Unicode/CLDR aggregation.
10
-
11
- `audit` is the successor to `otfinfo`: it covers everything `otfinfo`
12
- reports plus a great deal more (coverage, hinting, color, variation,
13
- layout, language coverage), and supports collections, compare mode, and
14
- whole-library summaries.
15
-
16
- ## Quick Reference
17
-
18
- ```bash
19
- # One face
20
- fontisan audit FONT.ttf
21
-
22
- # Collection (one report per face)
23
- fontisan audit COLLECTION.ttc
24
-
25
- # Whole library
26
- fontisan audit DIR/ --recursive --summary
27
-
28
- # Compare two fonts or saved reports
29
- fontisan audit --compare A.ttf B.ttf
30
- ```
31
-
32
- ## Variants
33
-
34
- | Variant | What it does | Output |
35
- |---------|-------------|--------|
36
- | `audit FONT.ttf` | One AuditReport for the single face | `AuditReport` |
37
- | `audit COLLECTION.ttc` | One AuditReport per face (in source order) | `Array<AuditReport>` |
38
- | `audit DIR/ --recursive --summary` | Walk the directory, summarize the library | `LibrarySummary` |
39
- | `audit --compare A B` | Diff two faces or two saved reports | `AuditDiff` |
40
-
41
- ## Options
42
-
43
- | Option | Description |
44
- |--------|-------------|
45
- | `--format FORMAT` | Output format: `text` (default), `yaml`, `json` |
46
- | `--output PATH`, `-o` | Write to a directory (collection/library), a file (single/compare), or stdout |
47
- | `--font-index N` | Audit only face N of a collection (default: all) |
48
- | `--brief` | Fast inventory — skip metrics/hinting/color/layout/UCD/CLDR |
49
- | `--all-codepoints` | Include the full per-codepoint list (defaults to a compact range view) |
50
- | `--ucd-version VER` | Aggregate against this UCD version (`latest` to probe) |
51
- | `--with-language-coverage` | Compute CLDR language coverage % (auto-downloads CLDR on first use) |
52
- | `--cldr-version VER` | CLDR version to use (`latest` to probe) |
53
- | `--compare` | Diff two inputs (requires exactly two paths) |
54
- | `--recursive` | Library mode: walk into subdirectories |
55
- | `--summary` | Library mode: produce a `LibrarySummary` over a directory |
56
-
57
- ## Brief Mode
58
-
59
- `--brief` runs only the cheap name-table extractors (provenance, identity,
60
- style, licensing, coverage) and skips metrics, hinting, color,
61
- variation, OpenType layout, UCD block/script aggregation, and CLDR
62
- language coverage. Useful for taking a fast inventory of large libraries.
63
-
64
- ```bash
65
- fontisan audit FONT.ttf --brief
66
- fontisan audit DIR/ --recursive --summary --brief
67
- ```
68
-
69
- Note: `audit --brief` is distinct from `info --brief`. `info --brief`
70
- loads only 6 tables; `audit --brief` still loads the full font (Coverage
71
- reads `cmap`) but selects a cheaper extractor subset.
72
-
73
- ## Single-Face Audit
74
-
75
- ```bash
76
- # Text formatter (default)
77
- fontisan audit FONT.ttf
78
-
79
- # YAML (machine-readable)
80
- fontisan audit FONT.ttf --format yaml
81
-
82
- # JSON
83
- fontisan audit FONT.ttf --format json | jq '.licensing'
84
-
85
- # Write to disk
86
- fontisan audit FONT.ttf -o report.yaml
87
- ```
88
-
89
- Sample text output (truncated):
90
-
91
- ```
92
- NotoSans-Regular
93
- ================================================================================
94
- generated_at: 2026-06-24T18:11:39Z fontisan: 0.2.20
95
- source_sha256: f5f552c8c5edb61fe6efb824baf4d4de47b1a8689ab4925ff43f7bd6a4ebece5
96
- source_file: /path/to/NotoSans-Regular.ttf
97
- source_format: ttf layout: single face (1/1)
98
-
99
- IDENTITY
100
- Family: Noto Sans
101
- Subfamily: Regular
102
- Full name: Noto Sans Regular
103
- PostScript: NotoSans-Regular
104
- Version: Version 2.015; ttfautohint (v1.8.4.7-5d5b)
105
- Revision: 2.0149993896484375
106
-
107
- STYLE
108
- Weight class: 400 (Regular)
109
- Width class: 5 (Medium)
110
- Bold: no
111
- Italic: no
112
- PANOSE: 2 11 5 2 4 5 4 2 2 4
113
-
114
- COVERAGE
115
- Codepoints: 3094
116
- Glyphs: 4515
117
- cmap subtables: 4, 12
118
- Ranges (top 10): U+0000-U+0000, U+000D-U+000D, U+0020-U+007E, ...
119
- Unicode scripts: Latin, ...
120
-
121
- LICENSING
122
- License URL: https://scripts.sil.org/OFL
123
- Embedding: 0x0000 (Editable embedding)
124
-
125
- METRICS
126
- Units per em: 1000
127
- Ascender: 1160
128
- Descender: -288
129
- ...
130
-
131
- UNICODE BLOCKS
132
- Basic Latin 95 / 128 74.2%
133
- Latin-1 Supplement 96 / 128 75.0%
134
- ...
135
-
136
- VARIABLE FONT
137
- Axes: wght (100–900, default 400)
138
- Named instances: 9
139
-
140
- OPENTYPE LAYOUT
141
- Scripts: latn, cyrl, grek, ...
142
- Features: c2sc, calt, case, dlig, dnom, frac, kern, liga, ...
143
-
144
- LANGUAGE COVERAGE
145
- en: 99.2%
146
- fr: 97.4%
147
- ...
148
- ```
149
-
150
- ## Collections
151
-
152
- For TTC/OTC/dfont, one report is produced per face in source order.
153
-
154
- ```bash
155
- # Audit every face
156
- fontisan audit COLLECTION.ttc
157
-
158
- # Audit only face 2
159
- fontisan audit COLLECTION.ttc --font-index 2
160
-
161
- # Write one file per face into a directory
162
- fontisan audit COLLECTION.ttc -o reports/
163
-
164
- # Resulting files use the postscript name with a 2-digit index prefix:
165
- # 00-NotoSans-Regular.yaml
166
- # 01-NotoSans-Bold.yaml
167
- # 02-NotoSerif-Italic.yaml
168
- ```
169
-
170
- ## Compare Mode
171
-
172
- `--compare` diffs two inputs. Each input is one of:
173
-
174
- - A previously saved audit report (`.yaml` / `.yml` / `.json`)
175
- - A font file (audited on-the-fly)
176
-
177
- Mixed inputs are allowed — useful for tracking a font's evolution
178
- against a checked-in baseline.
179
-
180
- ```bash
181
- # Two live fonts
182
- fontisan audit --compare a.ttf b.ttf
183
-
184
- # Saved baseline vs. live
185
- fontisan audit --compare baseline.yaml new.ttf
186
-
187
- # Two saved reports
188
- fontisan audit --compare v1.yaml v2.yaml -o diff.yaml
189
- ```
190
-
191
- The output is an `AuditDiff` containing:
192
-
193
- - `field_changes` — scalar field-level changes (e.g. weight_class 400 → 700)
194
- - `codepoints` — added/removed/unchanged codepoint counts
195
- - `added_features` / `removed_features`
196
- - `added_scripts` / `removed_scripts`
197
- - `added_blocks` / `removed_blocks`
198
- - `added_languages` / `removed_languages`
199
-
200
- ## Library Mode
201
-
202
- Point `audit` at a directory with `--recursive` and/or `--summary` to
203
- scan a whole library of fonts.
204
-
205
- ```bash
206
- # Flat directory
207
- fontisan audit lib/ --summary
208
-
209
- # Walk subdirectories
210
- fontisan audit lib/ --recursive --summary
211
-
212
- # YAML for downstream processing
213
- fontisan audit lib/ --recursive --summary --format yaml -o library.yaml
214
- ```
215
-
216
- The output is a `LibrarySummary` containing:
217
-
218
- - `root_path`, `total_files`, `total_faces`, `scanned_extensions`
219
- - `aggregate_metrics` — total codepoints/glyphs/bytes summed across faces
220
- - `script_coverage` — per-script face counts and lists
221
- - `duplicate_groups` — files grouped by `source_sha256` (size > 1)
222
- - `license_distribution` — face counts per `license_url`
223
- - `per_face_reports` — the full per-face reports used to aggregate
224
-
225
- Files that fail to load (corrupt, unsupported) are listed on stderr as
226
- `skipped <path>` and excluded from the summary.
227
-
228
- ## UCD Aggregation
229
-
230
- By default, audit aggregates codepoints against the configured-default
231
- UCD version, producing:
232
-
233
- - `blocks` — per-Unicode-block coverage rows (name, range, total, covered, fill_ratio, complete)
234
- - `unicode_scripts` — distinct scripts present in the font
235
-
236
- Override with `--ucd-version`:
237
-
238
- ```bash
239
- # Use a specific UCD version
240
- fontisan audit FONT.ttf --ucd-version 16.0.0
241
-
242
- # Probe and use the latest
243
- fontisan audit FONT.ttf --ucd-version latest
244
- ```
245
-
246
- Manage the local UCD cache with `fontisan ucd`:
247
-
248
- ```bash
249
- fontisan ucd status
250
- fontisan ucd list
251
- fontisan ucd download 17.0.0
252
- ```
253
-
254
- ## CLDR Language Coverage
255
-
256
- `--with-language-coverage` computes per-language coverage % using CLDR
257
- exemplar sets. The first invocation downloads the CLDR data
258
- (~MBs); subsequent invocations use the cache.
259
-
260
- ```bash
261
- fontisan audit FONT.ttf --with-language-coverage
262
-
263
- # Use a specific CLDR version
264
- fontisan audit FONT.ttf --with-language-coverage --cldr-version 45
265
- ```
266
-
267
- Manage the CLDR cache with `fontisan cldr`:
268
-
269
- ```bash
270
- fontisan cldr status
271
- fontisan cldr list
272
- fontisan cldr download 45
273
- ```
274
-
275
- ## Ruby API
276
-
277
- ### Single face
278
-
279
- ```ruby
280
- require "fontisan"
281
-
282
- # Returns an AuditReport for a single font, or an Array<AuditReport>
283
- # for a collection (one per face).
284
- report = Fontisan::Commands::AuditCommand.new("font.ttf",
285
- ucd_version: "17.0.0").run
286
-
287
- puts report.family_name
288
- puts report.total_codepoints
289
- puts report.licensing.license_url
290
- puts report.blocks.first.fill_ratio
291
- ```
292
-
293
- ### Compare
294
-
295
- ```ruby
296
- diff = Fontisan::Commands::AuditCompareCommand.new(
297
- "baseline.yaml", "new.ttf", ucd_version: "17.0.0"
298
- ).run
299
-
300
- diff.field_changes.each { |c| puts "#{c.field}: #{c.left} → #{c.right}" }
301
- puts "added codepoints: #{diff.codepoints.added_count}"
302
- ```
303
-
304
- ### Library summary
305
-
306
- ```ruby
307
- cmd = Fontisan::Commands::AuditLibraryCommand.new(
308
- "lib/", recursive: true, options: { ucd_version: "17.0.0" }
309
- )
310
- summary = cmd.run
311
- cmd.skipped.each { |path| warn "skipped #{path}" }
312
-
313
- summary.script_coverage.each do |row|
314
- puts "#{row.script}: #{row.face_count} faces"
315
- end
316
- summary.duplicate_groups.each do |group|
317
- puts "duplicate #{group.source_sha256[0,8]}: #{group.files.join(', ')}"
318
- end
319
- ```
320
-
321
- ### Brief mode
322
-
323
- ```ruby
324
- # Use the audit_brief: key (NOT brief:, which would trigger metadata-only
325
- # font loading via BaseCommand).
326
- report = Fontisan::Commands::AuditCommand.new(
327
- "font.ttf", audit_brief: true, ucd_version: "17.0.0"
328
- ).run
329
- ```
330
-
331
- ## Related Commands
332
-
333
- - [info](/cli/info) — Lighter-weight font metadata (replaces `otfinfo -i`)
334
- - [tables](/cli/tables) — Raw OpenType table listing
335
- - [features](/cli/features) — Just GSUB/GPOS features
336
- - [scripts](/cli/scripts) — Just supported scripts
337
- - [validate](/cli/validate) — Pass/fail quality checks (audit is descriptive; validate is prescriptive)
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Fontisan
4
- module Cldr
5
- # Produces audit-ready per-language coverage from a codepoint list
6
- # and a Cldr::Index of per-language exemplar sets.
7
- #
8
- # Pure: no I/O, no side effects.
9
- module Aggregator
10
- module_function
11
-
12
- # @param codepoints [Enumerable<Integer>] font's codepoints
13
- # @param languages_index [Cldr::Index]
14
- # @return [Array<Models::Cldr::LanguageCoverage>] sorted by
15
- # descending coverage_ratio, then by language name
16
- def aggregate(codepoints, languages_index)
17
- font_set = Set.new(codepoints)
18
-
19
- languages_index.entries.map do |lang, required_set|
20
- covered = (font_set & required_set).size
21
- total = required_set.size
22
- Models::Cldr::LanguageCoverage.new(
23
- language: lang,
24
- covered: covered,
25
- total: total,
26
- coverage_ratio: total.zero? ? 0.0 : covered.fdiv(total).round(4),
27
- fully_supported: total.positive? && covered == total,
28
- )
29
- end.sort_by { |lc| [lc.coverage_ratio * -1, lc.language] }
30
- end
31
- end
32
- end
33
- end
@@ -1,110 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "pathname"
4
-
5
- module Fontisan
6
- module Cldr
7
- # Manages the on-disk CLDR cache layout.
8
- #
9
- # Cache root resolution honors `XDG_CONFIG_HOME` per the XDG Base
10
- # Directory Specification. Falls back to `~/.config` on Unix.
11
- #
12
- # Layout:
13
- #
14
- # <root>/
15
- # <version>/
16
- # json/ # extracted CLDR JSON archive
17
- # cldr-json/
18
- # cldr-characters-full/
19
- # main/<lang>/characters.json
20
- # index/
21
- # languages.yml # built index of per-language codepoint sets
22
- #
23
- # No network access — all methods are pure filesystem operations.
24
- module CacheManager
25
- LANGUAGES_INDEX_FILENAME = "languages.yml"
26
- private_constant :LANGUAGES_INDEX_FILENAME
27
-
28
- class << self
29
- # Root path of the CLDR cache.
30
- # @return [Pathname]
31
- def root
32
- base = xdg_config_home || File.join(Dir.home, ".config")
33
- Pathname.new(base).join("fontisan", "cldr")
34
- end
35
-
36
- # Per-version directory.
37
- # @param version [String] e.g. "46.0.0"
38
- # @return [Pathname]
39
- def version_dir(version)
40
- root.join(version)
41
- end
42
-
43
- # Directory where the raw CLDR JSON archive is extracted.
44
- # @param version [String]
45
- # @return [Pathname]
46
- def json_dir(version)
47
- version_dir(version).join("json")
48
- end
49
-
50
- # Directory containing the per-language characters.json files
51
- # inside the extracted archive.
52
- # @param version [String]
53
- # @return [Pathname]
54
- def characters_main_dir(version)
55
- json_dir(version).join("cldr-json", "cldr-characters-full", "main")
56
- end
57
-
58
- # Directory holding the derived language index for a version.
59
- # @param version [String]
60
- # @return [Pathname]
61
- def index_dir(version)
62
- version_dir(version).join("index")
63
- end
64
-
65
- def languages_index_path(version)
66
- index_dir(version).join(LANGUAGES_INDEX_FILENAME)
67
- end
68
-
69
- # True if the extracted JSON archive is present for this version.
70
- # @param version [String]
71
- # @return [Boolean]
72
- def cached?(version)
73
- characters_main_dir(version).exist?
74
- end
75
-
76
- # All versions currently in the cache (sorted ascending).
77
- # @return [Array<String>]
78
- def cached_versions
79
- return [] unless root.exist?
80
-
81
- root.children.select(&:directory?).map { |p| p.basename.to_s }.sort
82
- end
83
-
84
- # Create the version directory and json/index subdirs.
85
- # Idempotent.
86
- # @param version [String]
87
- def ensure_version_dir!(version)
88
- json_dir(version).mkpath
89
- index_dir(version).mkpath
90
- end
91
-
92
- # Remove a version from the cache. No-op if absent.
93
- # @param version [String]
94
- def remove_version(version)
95
- dir = version_dir(version)
96
- dir.rmtree if dir.exist?
97
- end
98
-
99
- private
100
-
101
- def xdg_config_home
102
- env = ENV["XDG_CONFIG_HOME"]
103
- return nil if env.nil? || env.empty?
104
-
105
- env
106
- end
107
- end
108
- end
109
- end
110
- end
@@ -1,59 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "yaml"
4
-
5
- module Fontisan
6
- module Cldr
7
- # Single source of truth for CLDR version selection.
8
- #
9
- # Wraps `lib/fontisan/config/cldr.yml`. Loads the YAML once at first
10
- # access and memoizes. All other Cldr::* classes resolve versions,
11
- # URLs, and known-version validation through this module.
12
- module Config
13
- CONFIG_PATH = File.expand_path("../config/cldr.yml", __dir__)
14
- private_constant :CONFIG_PATH
15
-
16
- class << self
17
- # The CLDR version Fontisan uses by default for auto-download and
18
- # `fontisan cldr download` (no args). String like "46.0.0".
19
- def default_version
20
- data[:default_version]
21
- end
22
-
23
- # Array of CLDR version strings this Fontisan release recognizes.
24
- # Used by VersionResolver to reject unknown versions early.
25
- def known_versions
26
- data[:known_versions]
27
- end
28
-
29
- # Base URL for fetching CLDR JSON artifacts.
30
- def base_url
31
- data[:base_url]
32
- end
33
-
34
- # Listing URL for `--latest` probing.
35
- def listing_url
36
- data[:listing_url]
37
- end
38
-
39
- # Full URL to the CLDR JSON full archive for a version.
40
- # @param version [String] e.g. "46.0.0"
41
- # @return [String]
42
- def archive_url_for(version)
43
- "#{base_url}/#{version}/cldr-#{version}-json-full.zip"
44
- end
45
-
46
- # True if the version appears in `known_versions`.
47
- def known?(version)
48
- known_versions.include?(version)
49
- end
50
-
51
- private
52
-
53
- def data
54
- @data ||= YAML.load_file(CONFIG_PATH).transform_keys(&:to_sym)
55
- end
56
- end
57
- end
58
- end
59
- end
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Fontisan
4
- module Cldr
5
- # Raised by Cldr::Downloader when the upstream HTTP fetch or the zip
6
- # extraction fails. Caught by AuditCommand to degrade-with-warning.
7
- class DownloadError < Cldr::Error; end
8
- end
9
- end