ucode 0.1.0 → 0.1.1
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/CHANGELOG.md +72 -0
- data/Gemfile.lock +2 -2
- data/TODO.full/00-README.md +116 -0
- data/TODO.full/01-panglyph-vision.md +112 -0
- data/TODO.full/02-panglyph-repo-bootstrap.md +184 -0
- data/TODO.full/03-panglyph-font-builder.md +201 -0
- data/TODO.full/04-panglyph-publish-pipeline.md +126 -0
- data/TODO.full/05-ucode-0-1-1-release.md +139 -0
- data/TODO.full/06-fontisan-remove-audit.md +142 -0
- data/TODO.full/07-fontisan-remove-ucd.md +125 -0
- data/TODO.full/08-archive-private-bin-build.md +143 -0
- data/TODO.full/09-archive-public-structure.md +164 -0
- data/TODO.full/10-fontist-org-woff-glyphs.md +131 -0
- data/TODO.full/11-fontist-org-audit-coverage.md +140 -0
- data/TODO.full/12-implementation-order.md +216 -0
- data/TODO.full/13-fontisan-font-writer-api.md +189 -0
- data/TODO.full/14-fontisan-table-writers.md +66 -0
- data/TODO.full/15-panglyph-builder-real.md +82 -0
- data/TODO.full/16-archive-public-sync-workflows.md +167 -0
- data/TODO.full/17-fontist-org-font-picker.md +73 -0
- data/TODO.full/18-comprehensive-spec-coverage.md +64 -0
- data/TODO.full/19-ucode-0-1-2-patch.md +32 -0
- data/TODO.full/20-fontisan-0-2-23-release.md +52 -0
- data/TODO.new/00-README.md +30 -0
- data/TODO.new/23-universal-glyph-set-source-map.md +312 -0
- data/TODO.new/24-universal-glyph-set-build.md +189 -0
- data/TODO.new/25-font-audit-against-universal-set.md +195 -0
- data/TODO.new/26-missing-glyph-reporter.md +189 -0
- data/TODO.new/27-fontist-org-consumer-integration.md +200 -0
- data/TODO.new/28-implementation-order-update.md +187 -0
- data/TODO.new/29-universal-set-curation-uc17.md +312 -0
- data/TODO.new/30-tier1-font-acquisition.md +241 -0
- data/TODO.new/31-universal-set-production-build.md +205 -0
- data/TODO.new/32-uc17-coverage-matrix.md +165 -0
- data/TODO.new/33-specialist-font-acquisition-refresh.md +138 -0
- data/TODO.new/34-pillar2-content-stream-correlator.md +147 -0
- data/TODO.new/35-universal-set-production-run.md +160 -0
- data/TODO.new/36-per-font-coverage-audit.md +145 -0
- data/TODO.new/37-coverage-highlight-reporter.md +125 -0
- data/TODO.new/38-fontist-org-glyph-consumer.md +141 -0
- data/TODO.new/39-implementation-order-update-32-38.md +258 -0
- data/TODO.new/40-archive-private-uses-ucode-audit.md +124 -0
- data/TODO.new/41-ucode-unicode-archive-bridge.md +160 -0
- data/config/specialist_fonts.yml +102 -0
- data/config/unicode17_tier1_fonts.yml +42 -0
- data/config/unicode17_universal_glyph_set.yml +293 -0
- data/lib/ucode/audit/block_aggregator.rb +57 -29
- data/lib/ucode/audit/browser/face_page.rb +128 -0
- data/lib/ucode/audit/browser/glyph_panel.rb +124 -0
- data/lib/ucode/audit/browser/library_page.rb +74 -0
- data/lib/ucode/audit/browser/missing_glyph_page.rb +87 -0
- data/lib/ucode/audit/browser/template.rb +47 -0
- data/lib/ucode/audit/browser/templates/face.css +200 -0
- data/lib/ucode/audit/browser/templates/face.html.erb +41 -0
- data/lib/ucode/audit/browser/templates/face.js +298 -0
- data/lib/ucode/audit/browser/templates/library.css +119 -0
- data/lib/ucode/audit/browser/templates/library.html.erb +42 -0
- data/lib/ucode/audit/browser/templates/library.js +99 -0
- data/lib/ucode/audit/browser/templates/missing_glyph_page.css +119 -0
- data/lib/ucode/audit/browser/templates/missing_glyph_page.html.erb +58 -0
- data/lib/ucode/audit/browser/templates/missing_glyph_page.js +2 -0
- data/lib/ucode/audit/browser.rb +32 -0
- data/lib/ucode/audit/context.rb +27 -1
- data/lib/ucode/audit/coverage_reference.rb +103 -0
- data/lib/ucode/audit/differ.rb +121 -0
- data/lib/ucode/audit/emitter/block_emitter.rb +52 -0
- data/lib/ucode/audit/emitter/codepoint_emitter.rb +87 -0
- data/lib/ucode/audit/emitter/collection_emitter.rb +80 -0
- data/lib/ucode/audit/emitter/face_directory.rb +212 -0
- data/lib/ucode/audit/emitter/glyph_emitter.rb +48 -0
- data/lib/ucode/audit/emitter/index_emitter.rb +149 -0
- data/lib/ucode/audit/emitter/library_emitter.rb +96 -0
- data/lib/ucode/audit/emitter/paths.rb +312 -0
- data/lib/ucode/audit/emitter/plane_emitter.rb +29 -0
- data/lib/ucode/audit/emitter/script_emitter.rb +29 -0
- data/lib/ucode/audit/emitter.rb +29 -0
- data/lib/ucode/audit/extractors/aggregations.rb +31 -2
- data/lib/ucode/audit/face_auditor.rb +86 -0
- data/lib/ucode/audit/formatters/audit_diff_text.rb +112 -0
- data/lib/ucode/audit/formatters/audit_text.rb +411 -0
- data/lib/ucode/audit/formatters/color.rb +48 -0
- data/lib/ucode/audit/formatters/library_summary_text.rb +98 -0
- data/lib/ucode/audit/formatters/text_formatter.rb +83 -0
- data/lib/ucode/audit/formatters.rb +23 -0
- data/lib/ucode/audit/library_aggregator.rb +86 -0
- data/lib/ucode/audit/library_auditor.rb +105 -0
- data/lib/ucode/audit/release/emitter.rb +152 -0
- data/lib/ucode/audit/release/face_card.rb +93 -0
- data/lib/ucode/audit/release/formula_audits.rb +50 -0
- data/lib/ucode/audit/release/library_index_builder.rb +78 -0
- data/lib/ucode/audit/release/manifest_builder.rb +127 -0
- data/lib/ucode/audit/release.rb +42 -0
- data/lib/ucode/audit/ucd_only_reference.rb +81 -0
- data/lib/ucode/audit/universal_set_reference.rb +136 -0
- data/lib/ucode/audit.rb +31 -0
- data/lib/ucode/cli.rb +339 -33
- data/lib/ucode/commands/audit/browser_command.rb +82 -0
- data/lib/ucode/commands/audit/collection_command.rb +103 -0
- data/lib/ucode/commands/audit/compare_command.rb +188 -0
- data/lib/ucode/commands/audit/font_command.rb +140 -0
- data/lib/ucode/commands/audit/library_command.rb +87 -0
- data/lib/ucode/commands/audit/reference_builder.rb +64 -0
- data/lib/ucode/commands/audit.rb +20 -0
- data/lib/ucode/commands/block_feed.rb +73 -0
- data/lib/ucode/commands/canonical_build.rb +138 -0
- data/lib/ucode/commands/fetch.rb +37 -1
- data/lib/ucode/commands/release.rb +115 -0
- data/lib/ucode/commands/universal_set.rb +211 -0
- data/lib/ucode/commands.rb +5 -0
- data/lib/ucode/coordinator/indices.rb +11 -0
- data/lib/ucode/coordinator.rb +138 -5
- data/lib/ucode/error.rb +30 -2
- data/lib/ucode/fetch/font_fetcher/result.rb +39 -0
- data/lib/ucode/fetch/font_fetcher.rb +16 -0
- data/lib/ucode/fetch/specialist_font_fetcher.rb +280 -0
- data/lib/ucode/fetch.rb +7 -3
- data/lib/ucode/glyphs/real_fonts/cmap_cache.rb +74 -0
- data/lib/ucode/glyphs/real_fonts.rb +1 -0
- data/lib/ucode/glyphs/resolver.rb +62 -0
- data/lib/ucode/glyphs/source.rb +48 -0
- data/lib/ucode/glyphs/source_builder.rb +61 -0
- data/lib/ucode/glyphs/source_config/coverage_assertion.rb +79 -0
- data/lib/ucode/glyphs/source_config/gap_report.rb +54 -0
- data/lib/ucode/glyphs/source_config.rb +104 -0
- data/lib/ucode/glyphs/sources/pillar1_embedded_tounicode.rb +63 -0
- data/lib/ucode/glyphs/sources/pillar3_last_resort.rb +51 -0
- data/lib/ucode/glyphs/sources/tier1_real_font.rb +104 -0
- data/lib/ucode/glyphs/sources.rb +20 -0
- data/lib/ucode/glyphs/universal_set/builder.rb +161 -0
- data/lib/ucode/glyphs/universal_set/coverage_report.rb +139 -0
- data/lib/ucode/glyphs/universal_set/idempotency.rb +86 -0
- data/lib/ucode/glyphs/universal_set/manifest_accumulator.rb +195 -0
- data/lib/ucode/glyphs/universal_set/manifest_writer.rb +61 -0
- data/lib/ucode/glyphs/universal_set/pre_build_check.rb +197 -0
- data/lib/ucode/glyphs/universal_set/validator.rb +204 -0
- data/lib/ucode/glyphs/universal_set.rb +45 -0
- data/lib/ucode/glyphs.rb +6 -0
- data/lib/ucode/models/audit/baseline.rb +6 -0
- data/lib/ucode/models/audit/block_summary.rb +7 -0
- data/lib/ucode/models/audit/codepoint_provenance.rb +39 -0
- data/lib/ucode/models/audit/release_face.rb +42 -0
- data/lib/ucode/models/audit/release_formula.rb +33 -0
- data/lib/ucode/models/audit/release_manifest.rb +43 -0
- data/lib/ucode/models/audit/release_universal_set.rb +37 -0
- data/lib/ucode/models/audit.rb +9 -0
- data/lib/ucode/models/block.rb +2 -0
- data/lib/ucode/models/build_report.rb +109 -0
- data/lib/ucode/models/codepoint/glyph.rb +42 -0
- data/lib/ucode/models/codepoint.rb +3 -0
- data/lib/ucode/models/glyph_source.rb +86 -0
- data/lib/ucode/models/glyph_source_map.rb +138 -0
- data/lib/ucode/models/specialist_font.rb +70 -0
- data/lib/ucode/models/specialist_font_manifest.rb +48 -0
- data/lib/ucode/models/unihan_entry.rb +81 -9
- data/lib/ucode/models/unihan_field.rb +21 -0
- data/lib/ucode/models/universal_set_entry.rb +47 -0
- data/lib/ucode/models/universal_set_manifest.rb +78 -0
- data/lib/ucode/models/validation_report.rb +99 -0
- data/lib/ucode/models.rb +9 -0
- data/lib/ucode/parsers/named_sequences.rb +5 -5
- data/lib/ucode/parsers/unihan.rb +50 -19
- data/lib/ucode/repo/aggregate_writer.rb +34 -2
- data/lib/ucode/repo/block_feed_emitter.rb +153 -0
- data/lib/ucode/repo/build_report_accumulator.rb +138 -0
- data/lib/ucode/repo/build_report_writer.rb +46 -0
- data/lib/ucode/repo/build_validator.rb +229 -0
- data/lib/ucode/repo/codepoint_writer.rb +50 -1
- data/lib/ucode/repo/paths.rb +8 -0
- data/lib/ucode/repo.rb +4 -0
- data/lib/ucode/version.rb +1 -1
- data/schema/block-feed.output.schema.yml +134 -0
- metadata +143 -2
- data/ucode.gemspec +0 -56
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# 07 — fontisan: remove UCD/UCDXML subsystem
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Strip the UCD/UCDXML parsing subsystem out of `fontisan`. ucode now
|
|
6
|
+
owns UCD parsing (`ucode parse`, `ucode fetch ucd`); fontisan's
|
|
7
|
+
`lib/fontisan/ucd/`, `lib/fontisan/models/ucd/`, and `config/ucd.yml`
|
|
8
|
+
are dead code that misleads consumers about who's the UCD authority.
|
|
9
|
+
|
|
10
|
+
Pairs with TODO 06 (audit removal). Both land together as the 0.3.0
|
|
11
|
+
release of fontisan.
|
|
12
|
+
|
|
13
|
+
## Why now
|
|
14
|
+
|
|
15
|
+
- ucode's UCD parse is the canonical path. It's faster, more complete
|
|
16
|
+
(parses NamesList, Unihan, all auxiliary + extracted files), and
|
|
17
|
+
ships its own UCD cache.
|
|
18
|
+
- fontisan's UCD subsystem depended on `ucd.all.flat.xml` — which was
|
|
19
|
+
removed from the Unicode distribution in favor of UAX#44 text files.
|
|
20
|
+
The "real-shape-parsing" branch was an attempt to fix this; ucode
|
|
21
|
+
superseded it.
|
|
22
|
+
- Leaving UCD in fontisan creates two sources of truth for "what does
|
|
23
|
+
U+XXXX mean" — exactly the anti-pattern the audit migration fought.
|
|
24
|
+
|
|
25
|
+
## Scope
|
|
26
|
+
|
|
27
|
+
### Code
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
lib/fontisan/ucd.rb # DELETE
|
|
31
|
+
lib/fontisan/ucd/ # DELETE entire directory
|
|
32
|
+
index_builder.rb
|
|
33
|
+
xml_parser.rb
|
|
34
|
+
text_file_parser.rb
|
|
35
|
+
...
|
|
36
|
+
lib/fontisan/models/ucd.rb # DELETE
|
|
37
|
+
lib/fontisan/models/ucd/ # DELETE entire directory
|
|
38
|
+
block.rb
|
|
39
|
+
script.rb
|
|
40
|
+
codepoint.rb
|
|
41
|
+
...
|
|
42
|
+
config/ucd.yml # DELETE (was auto-downloaded UCD config)
|
|
43
|
+
lib/fontisan/commands/ucdxml_command.rb # DELETE (if exists)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Tests
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
spec/fontisan/ucd/ # DELETE
|
|
50
|
+
spec/fontisan/models/ucd/ # DELETE
|
|
51
|
+
spec/fontisan/commands/ucdxml_command_spec.rb # DELETE (if exists)
|
|
52
|
+
spec/fixtures/ucd/ # DELETE (test fixtures)
|
|
53
|
+
spec/fixtures/ucd.all.flat.xml # DELETE
|
|
54
|
+
spec/fixtures/ucd.all.flat.zip # DELETE
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### CLI registration
|
|
58
|
+
|
|
59
|
+
`lib/fontisan/cli.rb` — remove any UCD-related subcommands
|
|
60
|
+
(`ucdxml` if present).
|
|
61
|
+
|
|
62
|
+
### Documentation
|
|
63
|
+
|
|
64
|
+
- `README.md` — remove UCD references
|
|
65
|
+
- `docs/ucd.md` (if exists) — delete
|
|
66
|
+
|
|
67
|
+
## Branch context
|
|
68
|
+
|
|
69
|
+
The current `fix/ucdxml-real-shape-parsing` branch in fontisan was an
|
|
70
|
+
attempt to revive the UCD-XML parser. That work is now superseded by
|
|
71
|
+
ucode's UAX#44 text-file parsing (more reliable, doesn't depend on the
|
|
72
|
+
removed `.flat.xml` artifact).
|
|
73
|
+
|
|
74
|
+
This TODO **abandons** `fix/ucdxml-real-shape-parsing` and removes the
|
|
75
|
+
subsystem entirely. The branch's exploration work is preserved in git
|
|
76
|
+
history but not merged.
|
|
77
|
+
|
|
78
|
+
## Migration path
|
|
79
|
+
|
|
80
|
+
Consumers currently calling `Fontisan::UCD::IndexBuilder` etc. should
|
|
81
|
+
switch to ucode's API:
|
|
82
|
+
|
|
83
|
+
```ruby
|
|
84
|
+
# Before (fontisan 0.2.x):
|
|
85
|
+
index = Fontisan::UCD::IndexBuilder.new(version: "17.0.0").build
|
|
86
|
+
index.block_for_cp(0x4E00) # => "CJK_Unified_Ideographs"
|
|
87
|
+
|
|
88
|
+
# After (ucode 0.1.1+):
|
|
89
|
+
Ucode::Coordinator.new.indices_for(
|
|
90
|
+
ucd_dir: "/path/to/ucd",
|
|
91
|
+
unihan_dir: "/path/to/unihan"
|
|
92
|
+
).blocks # => sorted array of Block records
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Document this in the CHANGELOG entry (combined with TODO 06's entry).
|
|
96
|
+
|
|
97
|
+
## Version bump
|
|
98
|
+
|
|
99
|
+
Same release as TODO 06: **0.3.0** (breaking change).
|
|
100
|
+
|
|
101
|
+
## Acceptance
|
|
102
|
+
|
|
103
|
+
- [ ] `lib/fontisan/ucd.rb` and `lib/fontisan/ucd/` deleted
|
|
104
|
+
- [ ] `lib/fontisan/models/ucd.rb` and `lib/fontisan/models/ucd/` deleted
|
|
105
|
+
- [ ] `config/ucd.yml` deleted
|
|
106
|
+
- [ ] Any `ucdxml` CLI subcommand removed
|
|
107
|
+
- [ ] All UCD-related specs + fixtures deleted
|
|
108
|
+
- [ ] `bundle exec rspec` passes
|
|
109
|
+
- [ ] `bundle exec rubocop` clean
|
|
110
|
+
- [ ] CHANGELOG entry documents the removal + migration path
|
|
111
|
+
- [ ] `fix/ucdxml-real-shape-parsing` branch marked as abandoned (closed
|
|
112
|
+
with "superseded by ucode" comment)
|
|
113
|
+
|
|
114
|
+
## Dependencies / blockers
|
|
115
|
+
|
|
116
|
+
- **TODO.new 10** — Aggregations-UCD rewrite (must be complete; verified
|
|
117
|
+
by ucode's specs passing with real Unicode 17 data).
|
|
118
|
+
- **TODO 06** — companion audit removal; both ship in 0.3.0.
|
|
119
|
+
|
|
120
|
+
## References
|
|
121
|
+
|
|
122
|
+
- `lib/fontisan/ucd.rb` (slated for deletion)
|
|
123
|
+
- [TODO.new/10](../TODO.new/10-aggregations-ucd-rewrite.md) — ucode UCD aggregations
|
|
124
|
+
- [TODO 06](06-fontisan-remove-audit.md) — companion cleanup
|
|
125
|
+
- `fix/ucdxml-real-shape-parsing` branch (to be abandoned)
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# 08 — fontist-archive-private bin/build uses ucode audit + fontisan convert
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Refactor `fontist-archive-private/bin/build` so it:
|
|
6
|
+
|
|
7
|
+
1. Calls `ucode audit font` (instead of the dead `Fontisan::Commands::AuditCommand`)
|
|
8
|
+
for every matched font face.
|
|
9
|
+
2. Calls `fontisan ConvertCommand` (unchanged) for WOFF specimen
|
|
10
|
+
generation (open-license only).
|
|
11
|
+
3. Drops the UCD-stub hack (lines 13-21) — ucode has its own UCD.
|
|
12
|
+
|
|
13
|
+
Pairs with TODO.new/40 (same refactor). This file is the production
|
|
14
|
+
checklist + acceptance criteria.
|
|
15
|
+
|
|
16
|
+
## Why now
|
|
17
|
+
|
|
18
|
+
- TODO 06 + TODO 07 land together, removing fontisan's audit + UCD.
|
|
19
|
+
- bin/build currently `require "fontisan"` and references
|
|
20
|
+
`Fontisan::Commands::AuditCommand` — will break loudly after 0.3.0.
|
|
21
|
+
- The UCD stub hack returns empty aggregations — every audit YAML
|
|
22
|
+
currently has `blocks: []`, `unicode_scripts: []`. Consumers see
|
|
23
|
+
per-font cmap but no per-block fill ratios.
|
|
24
|
+
|
|
25
|
+
## Scope
|
|
26
|
+
|
|
27
|
+
### Phase A — Swap audit invocation
|
|
28
|
+
|
|
29
|
+
1. **Replace the audit call** (lines ~100-115 of `bin/build`):
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
# OLD (broken — uses removed AuditCommand + UCD stub hack)
|
|
33
|
+
cmd = Fontisan::Commands::AuditCommand.new(
|
|
34
|
+
face_path, font_index: font_index, no_codepoints: false
|
|
35
|
+
)
|
|
36
|
+
report = cmd.run
|
|
37
|
+
report = report.is_a?(Array) ? report.first : report
|
|
38
|
+
File.write(audit_path, report.to_yaml)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
with:
|
|
42
|
+
|
|
43
|
+
```ruby
|
|
44
|
+
# NEW — shell out to ucode (decouples bin/build from ucode's internal API)
|
|
45
|
+
args = ["ucode", "audit", "font", face_path,
|
|
46
|
+
"--font-index", font_index.to_s,
|
|
47
|
+
"--output", audit_path]
|
|
48
|
+
args += ["--reference-universal-set", universal_set_path] if universal_set_path
|
|
49
|
+
success = system(*args,
|
|
50
|
+
out: verbose ? $stdout : File::NULL,
|
|
51
|
+
err: verbose ? $stderr : File::NULL)
|
|
52
|
+
warn "WARN audit #{slug}: ucode exited #{$?.exitstatus}" if verbose && !success
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
2. **Keep the WOFF call** (lines ~125-140) — `fontisan ConvertCommand`
|
|
56
|
+
stays as-is.
|
|
57
|
+
|
|
58
|
+
3. **Remove the UCD stub hack** (lines 13-21 of `bin/build`):
|
|
59
|
+
|
|
60
|
+
```ruby
|
|
61
|
+
# DELETE THIS:
|
|
62
|
+
module Fontisan
|
|
63
|
+
module Audit
|
|
64
|
+
class Context
|
|
65
|
+
def ucd
|
|
66
|
+
@ucd ||= { version: nil, blocks_index: nil, scripts_index: nil,
|
|
67
|
+
warning: "ucd_skipped" }
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
4. **Update Gemfile**:
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
# fontist-archive-private/Gemfile
|
|
78
|
+
source "https://rubygems.org"
|
|
79
|
+
gem "ucode", "~> 0.1" # NEW (audit tool)
|
|
80
|
+
gem "fontisan", "~> 0.3" # BUMPED (audit + UCD removed)
|
|
81
|
+
gem "excavate" # unchanged (archive extraction)
|
|
82
|
+
gem "rake" # dev
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Phase B — Universal-set reference (after TODO.new 35)
|
|
86
|
+
|
|
87
|
+
5. Once the universal glyph set is published (TODO.new 35 + TODO.new 41),
|
|
88
|
+
pass `--reference-universal-set=<path>` so audits include per-block
|
|
89
|
+
coverage comparison against the canonical glyphs.
|
|
90
|
+
|
|
91
|
+
6. The universal set lives in `fontist-archive-public/unicode/universal-glyph-set/`.
|
|
92
|
+
bin/build checks it out shallow before invoking audits:
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
# Pseudocode — runs once per CI workflow, not per formula
|
|
96
|
+
universal_set_path = sync_universal_set_from_public_archive
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Phase C — CI workflow updates
|
|
100
|
+
|
|
101
|
+
7. `.github/workflows/build.yml`:
|
|
102
|
+
- Bump fontisan gem to 0.3.0+
|
|
103
|
+
- Add ucode gem (0.1.1+) install step
|
|
104
|
+
- Add a pre-step that fetches the universal set from
|
|
105
|
+
`fontist-archive-public/unicode/universal-glyph-set/`
|
|
106
|
+
- Otherwise matrix unchanged (ubuntu for google/sil/manual, macOS
|
|
107
|
+
for macos)
|
|
108
|
+
|
|
109
|
+
### Phase D — Test fixtures + acceptance
|
|
110
|
+
|
|
111
|
+
8. Add a fixture-driven test: small formula YAML + small TTF → run
|
|
112
|
+
bin/build locally → assert audit YAML has:
|
|
113
|
+
- Populated `blocks:` array (not empty)
|
|
114
|
+
- Populated `unicode_scripts:` array (not empty)
|
|
115
|
+
- `ucode_version:` field (not `fontisan_version:` for audit producer)
|
|
116
|
+
- `coverage:` section when universal-set reference is passed
|
|
117
|
+
|
|
118
|
+
## Acceptance
|
|
119
|
+
|
|
120
|
+
- [ ] `bin/build` invokes `ucode audit font` (not Fontisan)
|
|
121
|
+
- [ ] UCD stub hack removed
|
|
122
|
+
- [ ] Audit YAMLs include populated `blocks:` and `unicode_scripts:`
|
|
123
|
+
- [ ] Audit YAML carries `ucode_version` field (replaces `fontisan_version`)
|
|
124
|
+
- [ ] WOFF conversion still works (`Fontisan::ConvertCommand`)
|
|
125
|
+
- [ ] Gemfile lists `ucode` and `fontisan` (0.3.0+) as runtime deps
|
|
126
|
+
- [ ] At least one formula end-to-end produces a complete audit YAML
|
|
127
|
+
- [ ] GHA workflow runs to completion on a small formula subset
|
|
128
|
+
|
|
129
|
+
## Dependencies / blockers
|
|
130
|
+
|
|
131
|
+
- **TODO 05** — ucode 0.1.1 published (bin/build will `gem install ucode`)
|
|
132
|
+
- **TODO 06** + **TODO 07** — fontisan 0.3.0 published (bin/build can't
|
|
133
|
+
require both old and new fontisan)
|
|
134
|
+
- **TODO.new 35** + **TODO.new 41** — universal-set production + archive
|
|
135
|
+
bridge (Phase B depends on these)
|
|
136
|
+
|
|
137
|
+
## References
|
|
138
|
+
|
|
139
|
+
- `fontist-archive-private/bin/build` — current implementation
|
|
140
|
+
- [TODO 06](06-fontisan-remove-audit.md) — fontisan audit removal
|
|
141
|
+
- [TODO 07](07-fontisan-remove-ucd.md) — fontisan UCD removal
|
|
142
|
+
- [TODO.new 40](../TODO.new/40-archive-private-uses-ucode-audit.md) — earlier sketch
|
|
143
|
+
- `fontist.org/coverage-architecture.md` — updated architecture doc
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# 09 — fontist-archive-public structure: coverage/ + woff/ + unicode/ + panglyph/
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Define the canonical directory structure of `fontist-archive-public`
|
|
6
|
+
once all data streams are wired. Today it has `coverage/` (fontisan
|
|
7
|
+
audit YAMLs) + `woff/` (open-license specimens) + `fonts.json`. We
|
|
8
|
+
need to add `unicode/` (ucode's Unicode data) and `panglyph/` (the
|
|
9
|
+
universal font).
|
|
10
|
+
|
|
11
|
+
## Why a separate TODO
|
|
12
|
+
|
|
13
|
+
Three independent pipelines now feed fontist-archive-public:
|
|
14
|
+
|
|
15
|
+
1. **fontist-archive-private CI** (per-formula) → `coverage/` + `woff/`
|
|
16
|
+
2. **ucode CI** (per-Unicode-version) → `unicode/block-feed/` +
|
|
17
|
+
`unicode/universal-glyph-set/` + `unicode/codepoints-{version}.tar.zst`
|
|
18
|
+
3. **panglyph CI** (per-release) → `panglyph/v{X.Y.Z}/` + `manifest.json`
|
|
19
|
+
|
|
20
|
+
Each is owned by a different repo and syncs on a different cadence.
|
|
21
|
+
This TODO defines how they coexist in one archive without colliding.
|
|
22
|
+
|
|
23
|
+
## Target structure
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
fontist-archive-public/
|
|
27
|
+
├── README.md # canonical index
|
|
28
|
+
│
|
|
29
|
+
├── coverage/ # ← from fontist-archive-private CI
|
|
30
|
+
│ └── {formula_slug}/{PSName}.yaml # per-face audit YAMLs
|
|
31
|
+
│ google/abeezee/ABeeZee-Regular.yaml
|
|
32
|
+
│ manual/inter/Inter-Bold.yaml
|
|
33
|
+
│ macos/...
|
|
34
|
+
│
|
|
35
|
+
├── woff/ # ← from fontist-archive-private CI (open-license only)
|
|
36
|
+
│ └── {formula_slug}/{PSName}.woff2
|
|
37
|
+
│ google/abeezee/ABeeZee-Regular.woff2
|
|
38
|
+
│ (NO macos/ — proprietary)
|
|
39
|
+
│
|
|
40
|
+
├── fonts.json # font registry (canonical name → formula slugs)
|
|
41
|
+
├── font-metadata.json # per-face metadata (weight, style, etc.)
|
|
42
|
+
│
|
|
43
|
+
├── unicode/ # ← from ucode CI (TODO.new 41)
|
|
44
|
+
│ ├── block-feed/ # compact per-block Unicode data feed
|
|
45
|
+
│ │ ├── unicode-blocks.json
|
|
46
|
+
│ │ ├── unicode-version.json
|
|
47
|
+
│ │ └── unicode/blocks/<slug>.json
|
|
48
|
+
│ │
|
|
49
|
+
│ ├── universal-glyph-set/ # one SVG per codepoint (TODO.new 35)
|
|
50
|
+
│ │ ├── manifest.json # version, counts, generatedAt
|
|
51
|
+
│ │ ├── entries/U+XXXX.json # per-glyph provenance
|
|
52
|
+
│ │ └── glyphs/U+XXXX.svg
|
|
53
|
+
│ │
|
|
54
|
+
│ ├── codepoints-{version}.tar.zst # per-codepoint detailed JSONs (TODO.new 41 §Phase A.3)
|
|
55
|
+
│ └── codepoints-index.json # quick lookup: cp_int → {block, name, ...}
|
|
56
|
+
│
|
|
57
|
+
├── panglyph/ # ← from panglyph CI (TODO 04)
|
|
58
|
+
│ ├── manifest.json # latest version + version index
|
|
59
|
+
│ └── v17.0.0/ # per-release directory
|
|
60
|
+
│ ├── panglyph-unicode17.ttf
|
|
61
|
+
│ ├── panglyph-unicode17.woff2
|
|
62
|
+
│ ├── panglyph-unicode17.otf
|
|
63
|
+
│ ├── coverage-report.json
|
|
64
|
+
│ └── source-manifest.json # OFL provenance per source font
|
|
65
|
+
│
|
|
66
|
+
├── bin/
|
|
67
|
+
│ ├── sync-from-private # existing: pulls coverage/ + woff/
|
|
68
|
+
│ ├── sync-from-ucode # NEW: pulls unicode/
|
|
69
|
+
│ └── sync-from-panglyph # NEW: pulls panglyph/
|
|
70
|
+
│
|
|
71
|
+
└── .github/workflows/
|
|
72
|
+
├── sync-private.yml # triggers on archive-private push
|
|
73
|
+
├── sync-ucode.yml # triggers on ucode publish workflow
|
|
74
|
+
└── sync-panglyph.yml # triggers on panglyph tag
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Three sync workflows
|
|
78
|
+
|
|
79
|
+
### sync-private.yml (existing — minor update)
|
|
80
|
+
|
|
81
|
+
Triggers on push to `fontist/fontist-archive-private` main.
|
|
82
|
+
- Clones private shallow
|
|
83
|
+
- Copies `coverage/` (ALL audit YAML — metadata is public)
|
|
84
|
+
- Copies `woff/` (open-license only — checks each formula's license)
|
|
85
|
+
- Updates `fonts.json` + `font-metadata.json`
|
|
86
|
+
- Commits + pushes to public
|
|
87
|
+
|
|
88
|
+
### sync-ucode.yml (NEW — TODO.new 41)
|
|
89
|
+
|
|
90
|
+
Triggers on workflow_run of `fontist/ucode`'s `publish-unicode-archive.yml`.
|
|
91
|
+
- Clones ucode's published artifacts (Release asset OR direct git push
|
|
92
|
+
from ucode CI — design decision in TODO.new 41)
|
|
93
|
+
- Syncs `unicode/block-feed/`, `unicode/universal-glyph-set/`
|
|
94
|
+
- Replaces `unicode/codepoints-{version}.tar.zst`
|
|
95
|
+
- Updates `unicode/codepoints-index.json` (regenerated from per-cp JSONs)
|
|
96
|
+
|
|
97
|
+
### sync-panglyph.yml (NEW — TODO 04)
|
|
98
|
+
|
|
99
|
+
Triggers on tag push to `fontist/panglyph`.
|
|
100
|
+
- Clones panglyph Release assets
|
|
101
|
+
- Creates `panglyph/v{X.Y.Z}/` directory
|
|
102
|
+
- Updates `panglyph/manifest.json` (latest version pointer)
|
|
103
|
+
|
|
104
|
+
## Conflict resolution
|
|
105
|
+
|
|
106
|
+
The three workflows can run concurrently. They write to disjoint
|
|
107
|
+
directories (`coverage/`, `unicode/`, `panglyph/`), so git conflicts
|
|
108
|
+
are unlikely. If two syncs race, the second one's commit fails cleanly
|
|
109
|
+
(Git pre-receive hook rejects non-fast-forward) and re-runs on the
|
|
110
|
+
next trigger.
|
|
111
|
+
|
|
112
|
+
## Manifest of manifests
|
|
113
|
+
|
|
114
|
+
Top-level `archive-public/manifest.json`:
|
|
115
|
+
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"updated_at": "2026-...",
|
|
119
|
+
"coverage": {
|
|
120
|
+
"total_formulas": 4283,
|
|
121
|
+
"total_faces": 12000,
|
|
122
|
+
"last_sync": "2026-..."
|
|
123
|
+
},
|
|
124
|
+
"woff": {
|
|
125
|
+
"total_faces": 9500,
|
|
126
|
+
"last_sync": "2026-..."
|
|
127
|
+
},
|
|
128
|
+
"unicode": {
|
|
129
|
+
"ucd_version": "17.0.0",
|
|
130
|
+
"block_count": 346,
|
|
131
|
+
"codepoint_count": 299382,
|
|
132
|
+
"universal_set_built_at": "2026-..."
|
|
133
|
+
},
|
|
134
|
+
"panglyph": {
|
|
135
|
+
"latest": "17.0.0",
|
|
136
|
+
"released_at": "2026-..."
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
fontist.org's fetch-data.sh reads this to display "data refreshed X
|
|
142
|
+
hours ago" on the site.
|
|
143
|
+
|
|
144
|
+
## Acceptance
|
|
145
|
+
|
|
146
|
+
- [ ] `unicode/` directory exists with block-feed + universal-glyph-set
|
|
147
|
+
- [ ] `panglyph/` directory exists with at least one version
|
|
148
|
+
- [ ] Three sync workflows exist + run independently
|
|
149
|
+
- [ ] `archive-public/manifest.json` reflects the current state
|
|
150
|
+
- [ ] No git conflicts when 2+ syncs run concurrently (disjoint paths)
|
|
151
|
+
- [ ] fontist.org's fetch-data.sh can pull all four data streams
|
|
152
|
+
|
|
153
|
+
## Dependencies / blockers
|
|
154
|
+
|
|
155
|
+
- **TODO.new 41** — ucode → archive bridge (the `unicode/` sync)
|
|
156
|
+
- **TODO 04** — panglyph publish (the `panglyph/` sync)
|
|
157
|
+
- **TODO 08** — archive-private uses ucode (the `coverage/` sync content changes)
|
|
158
|
+
|
|
159
|
+
## References
|
|
160
|
+
|
|
161
|
+
- `fontist/fontist-archive-public` repo (current state)
|
|
162
|
+
- `fontist.org/scripts/fetch-data.sh` — consumer of all four streams
|
|
163
|
+
- [TODO.new 41](../TODO.new/41-ucode-unicode-archive-bridge.md) — ucode publishing
|
|
164
|
+
- [TODO 04](04-panglyph-publish-pipeline.md) — panglyph publishing
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# 10 — fontist.org: per-font WOFF glyph rendering (open-license fonts)
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Render font specimens on fontist.org using actual WOFF files from
|
|
6
|
+
`fontist-archive-public/woff/`. Currently the site has the WOFFs in
|
|
7
|
+
`public/fonts/*.woff2` but the unicode browser uses system fallback
|
|
8
|
+
fonts (via `displayChar(cp, category)`) which renders tofu for rare
|
|
9
|
+
scripts.
|
|
10
|
+
|
|
11
|
+
For open-license fonts, fontist.org should inject `@font-face` for the
|
|
12
|
+
specific font slug and render chars using that font directly.
|
|
13
|
+
|
|
14
|
+
## Why a separate TODO
|
|
15
|
+
|
|
16
|
+
The fontist.org unicode browser today shows:
|
|
17
|
+
- Block grid: chars rendered via system fallback
|
|
18
|
+
- Char detail: same system fallback
|
|
19
|
+
|
|
20
|
+
Users want to see "what does this codepoint look like in Inter / Noto
|
|
21
|
+
Sans / FSung?" — not "what does my OS render it as?"
|
|
22
|
+
|
|
23
|
+
For open-license fonts (Noto family, Google Fonts, OFL SIL fonts),
|
|
24
|
+
fontist-archive-public/woff/ has the WOFF2 file. fontist.org can
|
|
25
|
+
inject `@font-face` per-font and render codepoints using the active
|
|
26
|
+
font.
|
|
27
|
+
|
|
28
|
+
For proprietary fonts (Apple system fonts, Microsoft core fonts), the
|
|
29
|
+
WOFFs aren't redistributable and the site can't serve them. Those
|
|
30
|
+
fonts show coverage data only (TODO 11).
|
|
31
|
+
|
|
32
|
+
## Scope
|
|
33
|
+
|
|
34
|
+
### Phase A — Font injection helper
|
|
35
|
+
|
|
36
|
+
1. New composable: `src/composables/useFontFace.ts`:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
export function useFontFace(slug: string, familyName?: string) {
|
|
40
|
+
const css = `
|
|
41
|
+
@font-face {
|
|
42
|
+
font-family: '${familyName || slug}';
|
|
43
|
+
src: url('/fonts/${slug}.woff2') format('woff2');
|
|
44
|
+
font-display: swap;
|
|
45
|
+
}
|
|
46
|
+
`
|
|
47
|
+
// Inject into document head (deduped by slug)
|
|
48
|
+
// ...
|
|
49
|
+
return { fontFamily: `'${familyName || slug}'` }
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
2. Used by:
|
|
54
|
+
- `UnicodeBlockGrid.vue` when the user picks an active font
|
|
55
|
+
- `UnicodeCharPage.vue` to render the specimen glyph using the
|
|
56
|
+
active font
|
|
57
|
+
- `FontStylePage.vue` to render specimens in the font detail page
|
|
58
|
+
|
|
59
|
+
3. **Active font state** — top-level Vue ref (Pinia store or
|
|
60
|
+
provide/inject). User selects via a font picker on the unicode
|
|
61
|
+
browser pages.
|
|
62
|
+
|
|
63
|
+
### Phase B — Font picker UI
|
|
64
|
+
|
|
65
|
+
4. New component: `src/components/FontPicker.vue`:
|
|
66
|
+
- Lists all open-license fonts in `public/fonts/`
|
|
67
|
+
- Search box + family grouping
|
|
68
|
+
- On select: sets active font + persists to localStorage
|
|
69
|
+
|
|
70
|
+
5. Position on unicode browser:
|
|
71
|
+
- Top of `/unicode` page (sticky)
|
|
72
|
+
- Per-font pages (`/fonts/{slug}/unicode`) — auto-selects that font
|
|
73
|
+
|
|
74
|
+
### Phase C — Block grid rendering with active font
|
|
75
|
+
|
|
76
|
+
6. `UnicodeBlockGrid.vue` — accept a `fontSlug` prop. When set:
|
|
77
|
+
- Inject `@font-face` via useFontFace
|
|
78
|
+
- Apply `font-family: var(--active-font)` to each cell
|
|
79
|
+
- Cells with chars the active font doesn't cover get a visual
|
|
80
|
+
indicator (grayed-out / strikethrough)
|
|
81
|
+
|
|
82
|
+
7. **Coverage overlay** — small indicator on each cell showing whether
|
|
83
|
+
the active font covers it (green dot = yes, gray dot = no). Source:
|
|
84
|
+
ucode audit data (TODO 11).
|
|
85
|
+
|
|
86
|
+
### Phase D — Char detail rendering
|
|
87
|
+
|
|
88
|
+
8. `UnicodeCharPage.vue` — when an active font is set:
|
|
89
|
+
- The large glyph at top renders in the active font (not system fallback)
|
|
90
|
+
- Show a small badge: "Rendered in {font name}" + a toggle to switch
|
|
91
|
+
to "system fallback" for comparison
|
|
92
|
+
|
|
93
|
+
9. When no active font: keep current behavior (system fallback).
|
|
94
|
+
|
|
95
|
+
### Phase E — Performance
|
|
96
|
+
|
|
97
|
+
10. **Lazy font loading** — WOFF2 files can be large (Noto Sans CJK JP
|
|
98
|
+
is 18MB). Don't preload all fonts; only load the active font's WOFF.
|
|
99
|
+
|
|
100
|
+
11. **Font preloading hints** for the most-commonly-picked fonts
|
|
101
|
+
(Noto Sans, Inter, etc.) via `<link rel="preload">` on the home
|
|
102
|
+
page.
|
|
103
|
+
|
|
104
|
+
12. **CDN-friendly URLs** — `public/fonts/{slug}.woff2` should have
|
|
105
|
+
long cache headers + immutable filenames (sha-suffixed? TODO
|
|
106
|
+
separate).
|
|
107
|
+
|
|
108
|
+
## Acceptance
|
|
109
|
+
|
|
110
|
+
- [ ] `useFontFace` composable exists + injects `@font-face` cleanly
|
|
111
|
+
- [ ] `FontPicker.vue` lists all open-license WOFFs in `public/fonts/`
|
|
112
|
+
- [ ] Block grid renders cells in the active font
|
|
113
|
+
- [ ] Char detail renders the large glyph in the active font
|
|
114
|
+
- [ ] Per-cell coverage indicator (green = covered, gray = missing)
|
|
115
|
+
- [ ] Lazy font loading (no preload of unused WOFFs)
|
|
116
|
+
- [ ] Active font persists across page navigation (localStorage)
|
|
117
|
+
|
|
118
|
+
## Dependencies / blockers
|
|
119
|
+
|
|
120
|
+
- **fontist-archive-public** — must have `woff/` populated (TODO 09)
|
|
121
|
+
- **TODO 11** — coverage data from ucode audit (for the green/gray
|
|
122
|
+
indicators)
|
|
123
|
+
|
|
124
|
+
## References
|
|
125
|
+
|
|
126
|
+
- `src/components/UnicodeBlockGrid.vue` — current grid component
|
|
127
|
+
- `src/pages/UnicodeCharPage.vue` — current char page
|
|
128
|
+
- `src/pages/FontStylePage.vue` — existing font detail page (may
|
|
129
|
+
already inject @font-face for specimens — reuse the pattern)
|
|
130
|
+
- `public/fonts/*.woff2` — existing WOFF specimens
|
|
131
|
+
- [TODO 11](11-fontist-org-audit-coverage.md) — coverage data layer
|