ucode 0.1.0 → 0.2.0
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/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/reference_factory.rb +66 -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 +32 -0
- data/lib/ucode/cli.rb +366 -41
- 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.rb +19 -0
- data/lib/ucode/commands/block_feed.rb +73 -0
- data/lib/ucode/commands/build.rb +4 -0
- data/lib/ucode/commands/canonical_build.rb +137 -0
- data/lib/ucode/commands/fetch.rb +49 -15
- data/lib/ucode/commands/glyphs.rb +25 -67
- data/lib/ucode/commands/lookup.rb +11 -11
- data/lib/ucode/commands/parse.rb +7 -5
- data/lib/ucode/commands/release.rb +114 -0
- data/lib/ucode/commands/universal_set.rb +207 -0
- data/lib/ucode/commands.rb +5 -0
- data/lib/ucode/coordinator/indices.rb +49 -2
- 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/pipeline.rb +106 -0
- 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 +7 -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 +82 -288
- 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/writers/blocks_writer.rb +73 -0
- data/lib/ucode/repo/writers/enums_writer.rb +38 -0
- data/lib/ucode/repo/writers/indexes_writer.rb +53 -0
- data/lib/ucode/repo/writers/manifest_writer.rb +78 -0
- data/lib/ucode/repo/writers/named_sequences_writer.rb +47 -0
- data/lib/ucode/repo/writers/planes_writer.rb +82 -0
- data/lib/ucode/repo/writers/relationships_writer.rb +71 -0
- data/lib/ucode/repo/writers/scripts_writer.rb +54 -0
- data/lib/ucode/repo/writers.rb +20 -0
- data/lib/ucode/repo.rb +5 -0
- data/lib/ucode/version.rb +1 -1
- data/schema/block-feed.output.schema.yml +134 -0
- metadata +158 -4
- data/Gemfile.lock +0 -406
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ucode
|
|
4
|
+
module Glyphs
|
|
5
|
+
# Universal glyph set — one SVG per assigned Unicode codepoint,
|
|
6
|
+
# sourced via the 4-tier resolver using the curated Tier 1 config
|
|
7
|
+
# from TODO 23. The set is the canonical reference for "what
|
|
8
|
+
# Unicode 17 looks like" — every codepoint has exactly one glyph,
|
|
9
|
+
# with documented provenance, in a flat layout designed for fast
|
|
10
|
+
# lookup by audits (TODO 25) and the fontist.org consumer
|
|
11
|
+
# (TODO 27).
|
|
12
|
+
#
|
|
13
|
+
# Output layout (per TODO 24):
|
|
14
|
+
#
|
|
15
|
+
# output/universal_glyph_set/
|
|
16
|
+
# ├── manifest.json # one entry per codepoint + totals
|
|
17
|
+
# ├── glyphs/
|
|
18
|
+
# │ ├── U+0000.svg
|
|
19
|
+
# │ └── ...
|
|
20
|
+
# └── reports/
|
|
21
|
+
# ├── by_tier.json # tier-1: N1, pillar-1: N2, ...
|
|
22
|
+
# ├── by_block.json # per-block tier breakdown
|
|
23
|
+
# └── gaps.json # assigned codepoints with no glyph
|
|
24
|
+
#
|
|
25
|
+
# Components:
|
|
26
|
+
#
|
|
27
|
+
# - {Builder} drains a codepoint stream through the resolver and
|
|
28
|
+
# writes glyphs + manifest atomically.
|
|
29
|
+
# - {ManifestAccumulator} is the thread-safe tally that produces
|
|
30
|
+
# the final {Ucode::Models::UniversalSetManifest}.
|
|
31
|
+
# - {ManifestWriter} emits the manifest and per-tier / per-block /
|
|
32
|
+
# gaps reports under the output root.
|
|
33
|
+
# - {Idempotency} wraps {Ucode::Repo::AtomicWrites} with the
|
|
34
|
+
# "skip if SVG unchanged" semantic documented in TODO 24.
|
|
35
|
+
module UniversalSet
|
|
36
|
+
autoload :Builder, "ucode/glyphs/universal_set/builder"
|
|
37
|
+
autoload :ManifestAccumulator, "ucode/glyphs/universal_set/manifest_accumulator"
|
|
38
|
+
autoload :ManifestWriter, "ucode/glyphs/universal_set/manifest_writer"
|
|
39
|
+
autoload :Idempotency, "ucode/glyphs/universal_set/idempotency"
|
|
40
|
+
autoload :PreBuildCheck, "ucode/glyphs/universal_set/pre_build_check"
|
|
41
|
+
autoload :Validator, "ucode/glyphs/universal_set/validator"
|
|
42
|
+
autoload :CoverageReport, "ucode/glyphs/universal_set/coverage_report"
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
data/lib/ucode/glyphs.rb
CHANGED
|
@@ -20,8 +20,15 @@ module Ucode
|
|
|
20
20
|
autoload :CellExtractor, "ucode/glyphs/cell_extractor"
|
|
21
21
|
autoload :MonolithPageMap, "ucode/glyphs/monolith_page_map"
|
|
22
22
|
autoload :Writer, "ucode/glyphs/writer"
|
|
23
|
+
autoload :Pipeline, "ucode/glyphs/pipeline"
|
|
23
24
|
autoload :LastResort, "ucode/glyphs/last_resort"
|
|
24
25
|
autoload :EmbeddedFonts, "ucode/glyphs/embedded_fonts"
|
|
25
26
|
autoload :RealFonts, "ucode/glyphs/real_fonts"
|
|
27
|
+
autoload :Source, "ucode/glyphs/source"
|
|
28
|
+
autoload :Resolver, "ucode/glyphs/resolver"
|
|
29
|
+
autoload :SourceConfig, "ucode/glyphs/source_config"
|
|
30
|
+
autoload :SourceBuilder, "ucode/glyphs/source_builder"
|
|
31
|
+
autoload :Sources, "ucode/glyphs/sources"
|
|
32
|
+
autoload :UniversalSet, "ucode/glyphs/universal_set"
|
|
26
33
|
end
|
|
27
34
|
end
|
|
@@ -18,6 +18,11 @@ module Ucode
|
|
|
18
18
|
attribute :fontisan_version, :string
|
|
19
19
|
attribute :source, :string
|
|
20
20
|
attribute :generated_at, :string
|
|
21
|
+
# Which CoverageReference produced the per-block counts.
|
|
22
|
+
# "ucd" for UcdOnlyReference (default), "universal-set" for
|
|
23
|
+
# UniversalSetReference (TODO 25). nil on legacy reports —
|
|
24
|
+
# consumers should treat nil as "ucd".
|
|
25
|
+
attribute :reference_kind, :string
|
|
21
26
|
|
|
22
27
|
key_value do
|
|
23
28
|
map "unicode_version", to: :unicode_version
|
|
@@ -25,6 +30,7 @@ module Ucode
|
|
|
25
30
|
map "fontisan_version", to: :fontisan_version
|
|
26
31
|
map "source", to: :source
|
|
27
32
|
map "generated_at", to: :generated_at
|
|
33
|
+
map "reference_kind", to: :reference_kind
|
|
28
34
|
end
|
|
29
35
|
end
|
|
30
36
|
end
|
|
@@ -30,6 +30,12 @@ module Ucode
|
|
|
30
30
|
attribute :status, :string
|
|
31
31
|
attribute :missing_codepoints, :integer, collection: true, default: -> { [] }
|
|
32
32
|
attribute :covered_codepoints, :integer, collection: true, default: -> { [] }
|
|
33
|
+
# Per-codepoint provenance for the missing set. Populated only
|
|
34
|
+
# when the audit ran against a UniversalSetReference (TODO 25).
|
|
35
|
+
# Empty for UCD-only audits — the field serializes as [] and
|
|
36
|
+
# consumers treat that as "no provenance available".
|
|
37
|
+
attribute :missing_codepoint_provenance, CodepointProvenance,
|
|
38
|
+
collection: true, default: -> { [] }
|
|
33
39
|
|
|
34
40
|
key_value do
|
|
35
41
|
map "name", to: :name
|
|
@@ -44,6 +50,7 @@ module Ucode
|
|
|
44
50
|
map "status", to: :status
|
|
45
51
|
map "missing_codepoints", to: :missing_codepoints
|
|
46
52
|
map "covered_codepoints", to: :covered_codepoints
|
|
53
|
+
map "missing_codepoint_provenance", to: :missing_codepoint_provenance
|
|
47
54
|
end
|
|
48
55
|
|
|
49
56
|
# Derive the canonical status string for a block given its
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Ucode
|
|
6
|
+
module Models
|
|
7
|
+
module Audit
|
|
8
|
+
# Per-codepoint provenance row attached to a {BlockSummary}'s
|
|
9
|
+
# `missing_codepoint_provenance` collection.
|
|
10
|
+
#
|
|
11
|
+
# Populated only when the audit used a
|
|
12
|
+
# {Ucode::Audit::UniversalSetReference}. UCD-only audits omit
|
|
13
|
+
# the field entirely — preserving the legacy wire shape.
|
|
14
|
+
#
|
|
15
|
+
# Wire shape (one entry per missing codepoint):
|
|
16
|
+
#
|
|
17
|
+
# {
|
|
18
|
+
# "codepoint": 10981,
|
|
19
|
+
# "tier": "tier-1",
|
|
20
|
+
# "source": "lentariso"
|
|
21
|
+
# }
|
|
22
|
+
#
|
|
23
|
+
# `tier` and `source` mirror the universal-set manifest
|
|
24
|
+
# ({UniversalSetEntry}) and let downstream renderers (TODO 26)
|
|
25
|
+
# display the missing glyph + its provenance next to each row.
|
|
26
|
+
class CodepointProvenance < Lutaml::Model::Serializable
|
|
27
|
+
attribute :codepoint, :integer
|
|
28
|
+
attribute :tier, :string
|
|
29
|
+
attribute :source, :string
|
|
30
|
+
|
|
31
|
+
key_value do
|
|
32
|
+
map "codepoint", to: :codepoint
|
|
33
|
+
map "tier", to: :tier
|
|
34
|
+
map "source", to: :source
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Ucode
|
|
6
|
+
module Models
|
|
7
|
+
module Audit
|
|
8
|
+
# Per-face entry in a {ReleaseFormulaEntry}.
|
|
9
|
+
#
|
|
10
|
+
# Compact card view derived from a per-face {AuditReport}. Carries
|
|
11
|
+
# identity + coverage totals + relative paths into the release
|
|
12
|
+
# tree. The fontist.org renderer iterates these cards to build its
|
|
13
|
+
# font index; clicking a card fetches the per-face audit subtree
|
|
14
|
+
# at `index_path`.
|
|
15
|
+
class ReleaseFaceEntry < Lutaml::Model::Serializable
|
|
16
|
+
attribute :postscript_name, :string
|
|
17
|
+
attribute :family_name, :string
|
|
18
|
+
attribute :weight_class, :integer
|
|
19
|
+
attribute :total_codepoints, :integer
|
|
20
|
+
attribute :covered_codepoints, :integer
|
|
21
|
+
attribute :blocks_complete, :integer
|
|
22
|
+
attribute :blocks_partial, :integer
|
|
23
|
+
attribute :source_sha256, :string
|
|
24
|
+
attribute :index_path, :string
|
|
25
|
+
attribute :html_path, :string
|
|
26
|
+
|
|
27
|
+
key_value do
|
|
28
|
+
map "postscript_name", to: :postscript_name
|
|
29
|
+
map "family_name", to: :family_name
|
|
30
|
+
map "weight_class", to: :weight_class
|
|
31
|
+
map "total_codepoints", to: :total_codepoints
|
|
32
|
+
map "covered_codepoints", to: :covered_codepoints
|
|
33
|
+
map "blocks_complete", to: :blocks_complete
|
|
34
|
+
map "blocks_partial", to: :blocks_partial
|
|
35
|
+
map "source_sha256", to: :source_sha256
|
|
36
|
+
map "index_path", to: :index_path
|
|
37
|
+
map "html_path", to: :html_path
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Ucode
|
|
6
|
+
module Models
|
|
7
|
+
module Audit
|
|
8
|
+
# Per-formula entry in a {ReleaseManifest}.
|
|
9
|
+
#
|
|
10
|
+
# A formula is a single fontist formula (one installable unit).
|
|
11
|
+
# Each formula contributes one or more face entries to the release
|
|
12
|
+
# tree. The `slug` is the formula's URL-safe identifier used as
|
|
13
|
+
# the directory name under `<release_root>/audit/<slug>/`.
|
|
14
|
+
#
|
|
15
|
+
# `source_path` records where the original library audit ran so a
|
|
16
|
+
# consumer reading the manifest can trace the audit back to its
|
|
17
|
+
# input directory.
|
|
18
|
+
class ReleaseFormulaEntry < Lutaml::Model::Serializable
|
|
19
|
+
attribute :slug, :string
|
|
20
|
+
attribute :source_path, :string
|
|
21
|
+
attribute :faces_total, :integer
|
|
22
|
+
attribute :faces, ReleaseFaceEntry, collection: true, default: -> { [] }
|
|
23
|
+
|
|
24
|
+
key_value do
|
|
25
|
+
map "slug", to: :slug
|
|
26
|
+
map "source_path", to: :source_path
|
|
27
|
+
map "faces_total", to: :faces_total
|
|
28
|
+
map "faces", to: :faces
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Ucode
|
|
6
|
+
module Models
|
|
7
|
+
module Audit
|
|
8
|
+
# Top-level release manifest for the fontist.org-consumable
|
|
9
|
+
# artifact (TODO 27).
|
|
10
|
+
#
|
|
11
|
+
# One manifest per release tree at `<release_root>/manifest.json`.
|
|
12
|
+
# Records the ucode/unicode versions, optional source-config
|
|
13
|
+
# sha256 (for Tier 1 curation provenance), aggregate formula/face
|
|
14
|
+
# counts, the universal-set reference section, and the per-formula
|
|
15
|
+
# face index.
|
|
16
|
+
#
|
|
17
|
+
# fontist.org's `scripts/fetch-data.sh` reads this manifest first
|
|
18
|
+
# to decide whether to fetch the universal-set zip and which
|
|
19
|
+
# per-formula audit subtrees to pull.
|
|
20
|
+
class ReleaseManifest < Lutaml::Model::Serializable
|
|
21
|
+
attribute :ucode_version, :string
|
|
22
|
+
attribute :unicode_version, :string
|
|
23
|
+
attribute :generated_at, :string
|
|
24
|
+
attribute :source_config_sha256, :string
|
|
25
|
+
attribute :formulas_total, :integer
|
|
26
|
+
attribute :faces_total, :integer
|
|
27
|
+
attribute :universal_set, ReleaseUniversalSet
|
|
28
|
+
attribute :formulas, ReleaseFormulaEntry, collection: true, default: -> { [] }
|
|
29
|
+
|
|
30
|
+
key_value do
|
|
31
|
+
map "ucode_version", to: :ucode_version
|
|
32
|
+
map "unicode_version", to: :unicode_version
|
|
33
|
+
map "generated_at", to: :generated_at
|
|
34
|
+
map "source_config_sha256", to: :source_config_sha256
|
|
35
|
+
map "formulas_total", to: :formulas_total
|
|
36
|
+
map "faces_total", to: :faces_total
|
|
37
|
+
map "universal_set", to: :universal_set
|
|
38
|
+
map "formulas", to: :formulas
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Ucode
|
|
6
|
+
module Models
|
|
7
|
+
module Audit
|
|
8
|
+
# Universal-set reference section of a {ReleaseManifest}.
|
|
9
|
+
#
|
|
10
|
+
# Records whether the release tree carries a co-located universal
|
|
11
|
+
# glyph set (TODO 24) and where to find it. fontist.org consumes
|
|
12
|
+
# this to decide whether to render missing-codepoint thumbnails
|
|
13
|
+
# from the universal set or fall back to text-only chips.
|
|
14
|
+
#
|
|
15
|
+
# When `available` is false, `reason` carries a short diagnostic
|
|
16
|
+
# string ("universal-set directory not found at <path>"). The
|
|
17
|
+
# other fields are nil.
|
|
18
|
+
class ReleaseUniversalSet < Lutaml::Model::Serializable
|
|
19
|
+
attribute :available, Lutaml::Model::Type::Boolean
|
|
20
|
+
attribute :manifest_path, :string
|
|
21
|
+
attribute :glyphs_dir, :string
|
|
22
|
+
attribute :unicode_version, :string
|
|
23
|
+
attribute :totals, :hash, default: -> { {} }
|
|
24
|
+
attribute :reason, :string
|
|
25
|
+
|
|
26
|
+
key_value do
|
|
27
|
+
map "available", to: :available
|
|
28
|
+
map "manifest_path", to: :manifest_path
|
|
29
|
+
map "glyphs_dir", to: :glyphs_dir
|
|
30
|
+
map "unicode_version", to: :unicode_version
|
|
31
|
+
map "totals", to: :totals
|
|
32
|
+
map "reason", to: :reason
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
data/lib/ucode/models/audit.rb
CHANGED
|
@@ -23,6 +23,7 @@ module Ucode
|
|
|
23
23
|
autoload :PlaneSummary, "ucode/models/audit/plane_summary"
|
|
24
24
|
autoload :Discrepancy, "ucode/models/audit/discrepancy"
|
|
25
25
|
autoload :CodepointDetail, "ucode/models/audit/codepoint_detail"
|
|
26
|
+
autoload :CodepointProvenance, "ucode/models/audit/codepoint_provenance"
|
|
26
27
|
|
|
27
28
|
# Ported from fontisan (namespace swap + minor renames)
|
|
28
29
|
autoload :AuditReport, "ucode/models/audit/audit_report"
|
|
@@ -45,6 +46,14 @@ module Ucode
|
|
|
45
46
|
autoload :DuplicateGroup, "ucode/models/audit/duplicate_group"
|
|
46
47
|
autoload :LibrarySummary, "ucode/models/audit/library_summary"
|
|
47
48
|
autoload :AuditDiff, "ucode/models/audit/audit_diff"
|
|
49
|
+
|
|
50
|
+
# Release-tree models (TODO 27) — fontist.org-consumable
|
|
51
|
+
# artifact manifest. ReleaseManifest is the top-level shape;
|
|
52
|
+
# the others are nested entries.
|
|
53
|
+
autoload :ReleaseManifest, "ucode/models/audit/release_manifest"
|
|
54
|
+
autoload :ReleaseFormulaEntry, "ucode/models/audit/release_formula"
|
|
55
|
+
autoload :ReleaseFaceEntry, "ucode/models/audit/release_face"
|
|
56
|
+
autoload :ReleaseUniversalSet, "ucode/models/audit/release_universal_set"
|
|
48
57
|
end
|
|
49
58
|
end
|
|
50
59
|
end
|
data/lib/ucode/models/block.rb
CHANGED
|
@@ -13,6 +13,7 @@ module Ucode
|
|
|
13
13
|
attribute :range_first, :integer
|
|
14
14
|
attribute :range_last, :integer
|
|
15
15
|
attribute :plane_number, :integer
|
|
16
|
+
attribute :age, :string
|
|
16
17
|
attribute :codepoint_ids, :string, collection: true, default: -> { [] }
|
|
17
18
|
|
|
18
19
|
key_value do
|
|
@@ -21,6 +22,7 @@ module Ucode
|
|
|
21
22
|
map "range_first", to: :range_first
|
|
22
23
|
map "range_last", to: :range_last
|
|
23
24
|
map "plane_number", to: :plane_number
|
|
25
|
+
map "age", to: :age
|
|
24
26
|
map "codepoint_ids", to: :codepoint_ids
|
|
25
27
|
end
|
|
26
28
|
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Ucode
|
|
6
|
+
module Models
|
|
7
|
+
# Build report for one canonical Unicode dataset run. The
|
|
8
|
+
# deliverable spec'd in TODO 21: emitted at the end of a Mode 1
|
|
9
|
+
# build as `output/build-report.json`, summarizing what got built,
|
|
10
|
+
# how (per-tier), per-block, and any failures.
|
|
11
|
+
#
|
|
12
|
+
# The model is passive — accumulation logic lives in
|
|
13
|
+
# {Ucode::Repo::BuildReportAccumulator}; this class only describes
|
|
14
|
+
# the wire shape and handles (de)serialization via lutaml-model.
|
|
15
|
+
#
|
|
16
|
+
# Wire format (see TODO 21):
|
|
17
|
+
#
|
|
18
|
+
# {
|
|
19
|
+
# "unicode_version": "17.0.0",
|
|
20
|
+
# "ucode_version": "0.2.0",
|
|
21
|
+
# "generated_at": "2026-07-01T12:00:00Z",
|
|
22
|
+
# "totals": { "assigned": 150012, "built": 150012,
|
|
23
|
+
# "skipped": 0, "failed": 0 },
|
|
24
|
+
# "by_tier": { "tier-1": 150012, "pillar-1": 3000, ... },
|
|
25
|
+
# "by_block": [
|
|
26
|
+
# { "name": "Basic Latin", "assigned": 128, "built": 128,
|
|
27
|
+
# "tier_breakdown": { "tier-1": 128 } },
|
|
28
|
+
# ...
|
|
29
|
+
# ],
|
|
30
|
+
# "failures": []
|
|
31
|
+
# }
|
|
32
|
+
#
|
|
33
|
+
# `by_tier` counts overlap across tiers (a codepoint attempted via
|
|
34
|
+
# Tier 1 but falling through to Pillar 1 is counted in both);
|
|
35
|
+
# `built` per-codepoint is the tier that actually produced its
|
|
36
|
+
# glyph.
|
|
37
|
+
class BuildReport < Lutaml::Model::Serializable
|
|
38
|
+
# Total counts for the run.
|
|
39
|
+
class Totals < Lutaml::Model::Serializable
|
|
40
|
+
attribute :assigned, :integer, default: 0
|
|
41
|
+
attribute :built, :integer, default: 0
|
|
42
|
+
attribute :skipped, :integer, default: 0
|
|
43
|
+
attribute :failed, :integer, default: 0
|
|
44
|
+
|
|
45
|
+
key_value do
|
|
46
|
+
map "assigned", to: :assigned
|
|
47
|
+
map "built", to: :built
|
|
48
|
+
map "skipped", to: :skipped
|
|
49
|
+
map "failed", to: :failed
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Per-block rollup. One entry per Unicode block in the run.
|
|
54
|
+
class BlockSummary < Lutaml::Model::Serializable
|
|
55
|
+
attribute :name, :string
|
|
56
|
+
attribute :assigned, :integer, default: 0
|
|
57
|
+
attribute :built, :integer, default: 0
|
|
58
|
+
attribute :tier_breakdown, :hash, default: -> { {} }
|
|
59
|
+
|
|
60
|
+
key_value do
|
|
61
|
+
map "name", to: :name
|
|
62
|
+
map "assigned", to: :assigned
|
|
63
|
+
map "built", to: :built
|
|
64
|
+
map "tier_breakdown", to: :tier_breakdown
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# One failure record. `codepoint` is the integer codepoint (or
|
|
69
|
+
# nil if the failure is structural); `tier` is the resolver tier
|
|
70
|
+
# that raised (or nil); `error_class` and `message` carry the
|
|
71
|
+
# exception details; `backtrace` is optional.
|
|
72
|
+
class Failure < Lutaml::Model::Serializable
|
|
73
|
+
attribute :codepoint, :integer
|
|
74
|
+
attribute :block_name, :string
|
|
75
|
+
attribute :tier, :string
|
|
76
|
+
attribute :error_class, :string
|
|
77
|
+
attribute :message, :string
|
|
78
|
+
attribute :backtrace, :string, collection: true, default: -> { [] }
|
|
79
|
+
|
|
80
|
+
key_value do
|
|
81
|
+
map "codepoint", to: :codepoint
|
|
82
|
+
map "block_name", to: :block_name
|
|
83
|
+
map "tier", to: :tier
|
|
84
|
+
map "error_class", to: :error_class
|
|
85
|
+
map "message", to: :message
|
|
86
|
+
map "backtrace", to: :backtrace
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
attribute :unicode_version, :string
|
|
91
|
+
attribute :ucode_version, :string
|
|
92
|
+
attribute :generated_at, :string
|
|
93
|
+
attribute :totals, Totals
|
|
94
|
+
attribute :by_tier, :hash, default: -> { {} }
|
|
95
|
+
attribute :by_block, BlockSummary, collection: true, default: -> { [] }
|
|
96
|
+
attribute :failures, Failure, collection: true, default: -> { [] }
|
|
97
|
+
|
|
98
|
+
key_value do
|
|
99
|
+
map "unicode_version", to: :unicode_version
|
|
100
|
+
map "ucode_version", to: :ucode_version
|
|
101
|
+
map "generated_at", to: :generated_at
|
|
102
|
+
map "totals", to: :totals
|
|
103
|
+
map "by_tier", to: :by_tier
|
|
104
|
+
map "by_block", to: :by_block
|
|
105
|
+
map "failures", to: :failures
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Ucode
|
|
6
|
+
module Models
|
|
7
|
+
class CodePoint < Lutaml::Model::Serializable
|
|
8
|
+
# Glyph bundle for one codepoint. Records where the SVG lives on
|
|
9
|
+
# disk and which resolver tier produced it.
|
|
10
|
+
#
|
|
11
|
+
# The `svg_path` is relative to the codepoint's own directory
|
|
12
|
+
# (always "glyph.svg" — the layout is fixed in {Ucode::Repo::Paths}).
|
|
13
|
+
# The `source` bundle carries the resolver tier name and
|
|
14
|
+
# provenance string, so the dataset is debuggable end-to-end:
|
|
15
|
+
# every glyph in the build can be traced back to its origin
|
|
16
|
+
# (real font, embedded ToUnicode, correlator, or Last Resort).
|
|
17
|
+
class Glyph < Lutaml::Model::Serializable
|
|
18
|
+
# Provenance bundle for a glyph — which tier of the 4-tier
|
|
19
|
+
# resolver produced it. The Ruby class name `Source` mirrors
|
|
20
|
+
# the wire field name; it is unrelated to the
|
|
21
|
+
# {Ucode::Glyphs::Source} abstract base.
|
|
22
|
+
class Source < Lutaml::Model::Serializable
|
|
23
|
+
attribute :tier, :string
|
|
24
|
+
attribute :provenance, :string
|
|
25
|
+
|
|
26
|
+
key_value do
|
|
27
|
+
map "tier", to: :tier
|
|
28
|
+
map "provenance", to: :provenance
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
attribute :svg_path, :string, default: -> { "glyph.svg" }
|
|
33
|
+
attribute :source, Source
|
|
34
|
+
|
|
35
|
+
key_value do
|
|
36
|
+
map "svg_path", to: :svg_path
|
|
37
|
+
map "source", to: :source
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -26,6 +26,7 @@ module Ucode
|
|
|
26
26
|
autoload :Emoji, "ucode/models/codepoint/emoji"
|
|
27
27
|
autoload :Identifier, "ucode/models/codepoint/identifier"
|
|
28
28
|
autoload :Normalization, "ucode/models/codepoint/normalization"
|
|
29
|
+
autoload :Glyph, "ucode/models/codepoint/glyph"
|
|
29
30
|
|
|
30
31
|
# Identity + scalar attributes
|
|
31
32
|
attribute :cp, :integer
|
|
@@ -74,6 +75,7 @@ module Ucode
|
|
|
74
75
|
collection: true, default: -> { [] }
|
|
75
76
|
attribute :unihan, "Ucode::Models::UnihanEntry"
|
|
76
77
|
attribute :names_list, "Ucode::Models::NamesListEntry"
|
|
78
|
+
attribute :glyph, Glyph
|
|
77
79
|
|
|
78
80
|
key_value do
|
|
79
81
|
map "codepoint", to: :cp
|
|
@@ -116,6 +118,7 @@ module Ucode
|
|
|
116
118
|
map "standardized_variants", to: :standardized_variants
|
|
117
119
|
map "unihan", to: :unihan
|
|
118
120
|
map "names_list", to: :names_list
|
|
121
|
+
map "glyph", to: :glyph
|
|
119
122
|
end
|
|
120
123
|
end
|
|
121
124
|
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Ucode
|
|
6
|
+
module Models
|
|
7
|
+
# One Tier 1 font entry inside a {GlyphSourceMap}. Corresponds to
|
|
8
|
+
# one `- kind: …` item under a block's `sources:` list in
|
|
9
|
+
# `config/unicode17_universal_glyph_set.yml`.
|
|
10
|
+
#
|
|
11
|
+
# This is the typed representation of a font curation choice. The
|
|
12
|
+
# {Ucode::Glyphs::Sources::Tier1RealFont} source consumes it to
|
|
13
|
+
# resolve and load the font; the resolver treats each entry as an
|
|
14
|
+
# independent tier-1 attempt.
|
|
15
|
+
#
|
|
16
|
+
# Wire shape (YAML / JSON identical):
|
|
17
|
+
#
|
|
18
|
+
# kind: fontist # one of: fontist, path, system
|
|
19
|
+
# label: noto-sans # human + provenance key
|
|
20
|
+
# priority: 1 # lower wins; resolver tries in order
|
|
21
|
+
# license: OFL # optional; OFL / PROPRIETARY / etc.
|
|
22
|
+
# provenance: "Google Noto Sans" # optional citation
|
|
23
|
+
# path: "/abs/font.ttf" # required when kind == :path
|
|
24
|
+
#
|
|
25
|
+
# `kind` is stored as a plain string on the wire (lutaml-model has
|
|
26
|
+
# no Symbol adapter for key_value); the {#kind_sym} reader casts it
|
|
27
|
+
# for internal dispatch.
|
|
28
|
+
class GlyphSource < Lutaml::Model::Serializable
|
|
29
|
+
KIND_FONTIST = "fontist"
|
|
30
|
+
KIND_PATH = "path"
|
|
31
|
+
KIND_SYSTEM = "system"
|
|
32
|
+
KINDS = [KIND_FONTIST, KIND_PATH, KIND_SYSTEM].freeze
|
|
33
|
+
private_constant :KIND_FONTIST, :KIND_PATH, :KIND_SYSTEM, :KINDS
|
|
34
|
+
|
|
35
|
+
attribute :kind, :string
|
|
36
|
+
attribute :label, :string
|
|
37
|
+
attribute :priority, :integer, default: -> { 100 }
|
|
38
|
+
attribute :license, :string
|
|
39
|
+
attribute :provenance, :string
|
|
40
|
+
attribute :path, :string
|
|
41
|
+
|
|
42
|
+
key_value do
|
|
43
|
+
map "kind", to: :kind
|
|
44
|
+
map "label", to: :label
|
|
45
|
+
map "priority", to: :priority
|
|
46
|
+
map "license", to: :license
|
|
47
|
+
map "provenance", to: :provenance
|
|
48
|
+
map "path", to: :path
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @return [Symbol] :fontist, :path, :system; raises if kind is
|
|
52
|
+
# blank — every entry must declare its kind.
|
|
53
|
+
def kind_sym
|
|
54
|
+
raise ArgumentError, "GlyphSource#kind is required" if kind.nil? || kind.empty?
|
|
55
|
+
|
|
56
|
+
kind.to_sym
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# @return [Boolean] true when this entry requires a `path` field
|
|
60
|
+
# (kind == :path). Used by the loader to validate structure.
|
|
61
|
+
def requires_path?
|
|
62
|
+
kind_sym == :path
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Renders this source as the legacy font-spec string consumed by
|
|
66
|
+
# {Ucode::Glyphs::RealFonts::FontLocator}: `label=/path/to/font`
|
|
67
|
+
# for kind=path, or `label` (the fontist formula name) for
|
|
68
|
+
# kind=fontist. The locator's `locate` understands both shapes.
|
|
69
|
+
#
|
|
70
|
+
# This is the one adapter method that lets the typed model
|
|
71
|
+
# integrate with the existing locator without rewriting it.
|
|
72
|
+
#
|
|
73
|
+
# @return [String]
|
|
74
|
+
def to_font_spec
|
|
75
|
+
case kind_sym
|
|
76
|
+
when :path
|
|
77
|
+
raise ArgumentError, "GlyphSource#{label} has kind=path but no path" if path.nil? || path.empty?
|
|
78
|
+
|
|
79
|
+
"#{label}=#{path}"
|
|
80
|
+
when :fontist, :system
|
|
81
|
+
label
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|