ucode 0.2.2 → 0.3.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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -1
  3. data/config/unicode17_universal_glyph_set.yml +1 -1
  4. data/lib/ucode/cli.rb +1 -35
  5. data/lib/ucode/code_chart/extractor.rb +1 -9
  6. data/lib/ucode/code_chart/writer.rb +1 -1
  7. data/lib/ucode/commands/build.rb +3 -26
  8. data/lib/ucode/commands/canonical_build.rb +5 -8
  9. data/lib/ucode/commands/universal_set.rb +5 -3
  10. data/lib/ucode/commands.rb +0 -1
  11. data/lib/ucode/coordinator/enrichment/bidi.rb +35 -0
  12. data/lib/ucode/coordinator/enrichment/binary.rb +38 -0
  13. data/lib/ucode/coordinator/enrichment/casing.rb +55 -0
  14. data/lib/ucode/coordinator/enrichment/cjk.rb +49 -0
  15. data/lib/ucode/coordinator/enrichment/display.rb +36 -0
  16. data/lib/ucode/coordinator/enrichment/emoji.rb +36 -0
  17. data/lib/ucode/coordinator/enrichment/identity.rb +42 -0
  18. data/lib/ucode/coordinator/enrichment/indic.rb +32 -0
  19. data/lib/ucode/coordinator/enrichment/names.rb +63 -0
  20. data/lib/ucode/coordinator/enrichment/segmentation.rb +34 -0
  21. data/lib/ucode/coordinator/enrichment.rb +51 -0
  22. data/lib/ucode/coordinator/range_lookup.rb +65 -0
  23. data/lib/ucode/coordinator.rb +4 -276
  24. data/lib/ucode/error.rb +0 -8
  25. data/lib/ucode/glyphs/embedded_fonts/catalog.rb +32 -299
  26. data/lib/ucode/glyphs/embedded_fonts/codepoint_mapper.rb +130 -0
  27. data/lib/ucode/glyphs/embedded_fonts/content_stream_correlator.rb +25 -124
  28. data/lib/ucode/glyphs/embedded_fonts/font_entry.rb +0 -1
  29. data/lib/ucode/glyphs/embedded_fonts/pdf_indexer.rb +236 -0
  30. data/lib/ucode/glyphs/embedded_fonts/{source.rb → pdf_location.rb} +5 -5
  31. data/lib/ucode/glyphs/embedded_fonts/positional_matcher.rb +162 -0
  32. data/lib/ucode/glyphs/embedded_fonts/raw_font_descriptor.rb +24 -0
  33. data/lib/ucode/glyphs/embedded_fonts/renderer.rb +0 -2
  34. data/lib/ucode/glyphs/embedded_fonts/trace_correlator.rb +116 -0
  35. data/lib/ucode/glyphs/embedded_fonts/trace_glyph.rb +27 -0
  36. data/lib/ucode/glyphs/embedded_fonts/trace_parser.rb +50 -0
  37. data/lib/ucode/glyphs/embedded_fonts/trace_runner.rb +53 -0
  38. data/lib/ucode/glyphs/embedded_fonts/writer.rb +0 -4
  39. data/lib/ucode/glyphs/embedded_fonts.rb +9 -1
  40. data/lib/ucode/glyphs/pdf_fetcher.rb +7 -50
  41. data/lib/ucode/glyphs/resolver_factory.rb +45 -0
  42. data/lib/ucode/glyphs/sources/pillar1_embedded_tounicode.rb +1 -1
  43. data/lib/ucode/glyphs.rb +5 -14
  44. data/lib/ucode/version.rb +1 -1
  45. data/lib/ucode.rb +0 -2
  46. metadata +24 -16
  47. data/lib/ucode/commands/glyphs.rb +0 -94
  48. data/lib/ucode/glyphs/cell_extractor.rb +0 -130
  49. data/lib/ucode/glyphs/dvisvgm_renderer.rb +0 -29
  50. data/lib/ucode/glyphs/grid.rb +0 -30
  51. data/lib/ucode/glyphs/grid_detector.rb +0 -165
  52. data/lib/ucode/glyphs/monolith_page_map.rb +0 -181
  53. data/lib/ucode/glyphs/mutool_renderer.rb +0 -28
  54. data/lib/ucode/glyphs/page_renderer.rb +0 -234
  55. data/lib/ucode/glyphs/path_bbox.rb +0 -62
  56. data/lib/ucode/glyphs/pdf2svg_renderer.rb +0 -26
  57. data/lib/ucode/glyphs/pdftocairo_renderer.rb +0 -32
  58. data/lib/ucode/glyphs/pipeline.rb +0 -105
  59. data/lib/ucode/glyphs/writer.rb +0 -250
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ucode
4
+ module Glyphs
5
+ module EmbeddedFonts
6
+ # Correlates specimen glyphs (CID font without `/ToUnicode`) to
7
+ # their Unicode codepoints via positional matching against hex
8
+ # codepoint labels on the same chart page.
9
+ #
10
+ # Adapter for the `mutool trace` XML format: parses {TraceGlyph}
11
+ # arrays, partitions into specimens and labels, auto-detects the
12
+ # label font by proximity, then delegates matching to
13
+ # {PositionalMatcher}.
14
+ #
15
+ # The label font auto-detection is the only piece of "intelligence"
16
+ # in this adapter — everything else is format translation. The
17
+ # matching algorithm lives in {PositionalMatcher} and is shared
18
+ # with {ContentStreamCorrelator}.
19
+ class TraceCorrelator
20
+ # Proximity radius (in PDF points) for counting how often each
21
+ # non-specimen font's hex-char glyphs appear near a specimen.
22
+ # Code Charts dedicate one small font to the codepoint labels;
23
+ # body text and headers are farther away.
24
+ LABEL_PROXIMITY_RADIUS = 50.0
25
+ private_constant :LABEL_PROXIMITY_RADIUS
26
+
27
+ # @param specimen_font_name [String] the BaseFont name of the
28
+ # CID font whose glyphs need correlation
29
+ def initialize(specimen_font_name:)
30
+ @specimen_font_name = specimen_font_name
31
+ end
32
+
33
+ # @param trace_glyphs [Array<TraceGlyph>]
34
+ # @return [Hash{Integer=>Integer}] codepoint => gid
35
+ def correlate(trace_glyphs)
36
+ specimens = select_specimens(trace_glyphs)
37
+ return {} if specimens.empty?
38
+
39
+ labels = select_labels(trace_glyphs)
40
+ return {} if labels.empty?
41
+
42
+ PositionalMatcher.match(
43
+ specimens.map { |g| to_position(g) },
44
+ labels.map { |g| to_position(g) },
45
+ )
46
+ end
47
+
48
+ private
49
+
50
+ def select_specimens(trace_glyphs)
51
+ trace_glyphs.select { |g| g.font_name == @specimen_font_name }
52
+ end
53
+
54
+ def select_labels(trace_glyphs)
55
+ label_font = detect_label_font(trace_glyphs)
56
+ return [] unless label_font
57
+
58
+ trace_glyphs.select { |g| hex_char_from?(g, label_font) }
59
+ end
60
+
61
+ def hex_char_from?(glyph, font_name)
62
+ glyph.font_name == font_name && glyph.unicode&.match?(/\A[0-9A-Fa-f]\z/)
63
+ end
64
+
65
+ def to_position(glyph)
66
+ PositionalMatcher::Position.new(
67
+ x: glyph.x,
68
+ y: glyph.y,
69
+ font_ref: glyph.font_name,
70
+ glyph_id: glyph.gid,
71
+ text: glyph.unicode,
72
+ )
73
+ end
74
+
75
+ # The label font is the non-specimen font whose hex-char glyphs
76
+ # appear most often in close proximity to specimen glyphs.
77
+ # Code Charts dedicate one small font to the codepoint labels;
78
+ # body text, headers, and character names use other fonts that
79
+ # may also contain hex chars but are not co-located with
80
+ # specimens.
81
+ def detect_label_font(trace_glyphs)
82
+ specimens = select_specimens(trace_glyphs)
83
+ return nil if specimens.empty?
84
+
85
+ candidates = select_hex_candidates(trace_glyphs)
86
+ return nil if candidates.empty?
87
+
88
+ counts = proximity_counts(specimens, candidates)
89
+ return nil if counts.empty?
90
+
91
+ counts.max_by { |_, n| n }.first
92
+ end
93
+
94
+ def select_hex_candidates(trace_glyphs)
95
+ trace_glyphs.select do |g|
96
+ g.font_name != @specimen_font_name &&
97
+ g.unicode&.match?(/\A[0-9A-Fa-f]\z/)
98
+ end
99
+ end
100
+
101
+ def proximity_counts(specimens, candidates)
102
+ counts = Hash.new(0)
103
+ radius_sq = LABEL_PROXIMITY_RADIUS * LABEL_PROXIMITY_RADIUS
104
+ specimens.each do |spec|
105
+ candidates.each do |g|
106
+ dx = spec.x - g.x
107
+ dy = spec.y - g.y
108
+ counts[g.font_name] += 1 if dx * dx + dy * dy < radius_sq
109
+ end
110
+ end
111
+ counts
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ucode
4
+ module Glyphs
5
+ module EmbeddedFonts
6
+ # Value object for one glyph emitted by `mutool trace`.
7
+ #
8
+ # Each `<g>` element in the trace XML maps to one TraceGlyph:
9
+ #
10
+ # <g unicode="�" glyph="174" x="237.06" y="673.92" adv=".62"/>
11
+ #
12
+ # The `font_name` is inherited from the enclosing `<span>`:
13
+ #
14
+ # <span font="GPJAHB+WolofGaraySansSerif" ...>
15
+ # <g .../>
16
+ # </span>
17
+ TraceGlyph = Struct.new(
18
+ :font_name,
19
+ :gid,
20
+ :x,
21
+ :y,
22
+ :unicode,
23
+ keyword_init: true,
24
+ )
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "nokogiri"
4
+
5
+ module Ucode
6
+ module Glyphs
7
+ module EmbeddedFonts
8
+ # Parses the XML output of `mutool trace <pdf> <page>` into an
9
+ # array of {TraceGlyph} instances.
10
+ #
11
+ # The trace XML uses a flat `<span font="...">` → `<g glyph="..."
12
+ # x="..." y="..." unicode="..."/>` structure. Nokogiri walks
13
+ # the tree; the parser maps each `<g>` to a TraceGlyph,
14
+ # inheriting the font_name from the enclosing span.
15
+ #
16
+ # Pure function — no I/O, no PDF access. Callers inject the XML
17
+ # string (typically from {TraceRunner}).
18
+ module TraceParser
19
+ class << self
20
+ # @param xml [String] raw mutool trace XML
21
+ # @return [Array<TraceGlyph>] one per `<g>` element; empty
22
+ # if the XML is empty or has no `<g>` elements
23
+ def parse(xml)
24
+ return [] if xml.nil? || xml.strip.empty?
25
+
26
+ doc = Nokogiri::XML(xml)
27
+ doc.css("span").flat_map { |span| glyphs_in_span(span) }
28
+ end
29
+
30
+ private
31
+
32
+ def glyphs_in_span(span)
33
+ font_name = span[:font]
34
+ span.css("g").map { |g| build_glyph(font_name, g) }
35
+ end
36
+
37
+ def build_glyph(font_name, g)
38
+ TraceGlyph.new(
39
+ font_name: font_name,
40
+ gid: g[:glyph]&.to_i,
41
+ x: g[:x]&.to_f,
42
+ y: g[:y]&.to_f,
43
+ unicode: g[:unicode],
44
+ )
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+ require "pathname"
5
+
6
+ require "ucode/error"
7
+
8
+ module Ucode
9
+ module Glyphs
10
+ module EmbeddedFonts
11
+ # Thin I/O wrapper around `mutool trace <pdf> <page>`.
12
+ #
13
+ # Runs mutool on the given pages, captures the XML output,
14
+ # delegates parsing to {TraceParser}, and returns a flat
15
+ # `Array<TraceGlyph>` across all pages.
16
+ #
17
+ # The only class in the trace pipeline that touches the
18
+ # filesystem / spawns subprocesses. Everything upstream
19
+ # (parser, correlator) is pure.
20
+ class TraceRunner
21
+ # @param pdf_path [Pathname, String]
22
+ def initialize(pdf_path)
23
+ @pdf_path = Pathname.new(pdf_path)
24
+ end
25
+
26
+ # @param page_numbers [Array<Integer>] 1-based PDF page numbers
27
+ # @return [Array<TraceGlyph>]
28
+ def trace(page_numbers)
29
+ page_numbers.flat_map { |page| trace_page(page) }
30
+ end
31
+
32
+ private
33
+
34
+ def trace_page(page)
35
+ xml = run_mutool(page)
36
+ TraceParser.parse(xml)
37
+ end
38
+
39
+ def run_mutool(page)
40
+ out, err, status = Open3.capture3(
41
+ "mutool", "trace", @pdf_path.to_s, page.to_s,
42
+ )
43
+ unless status.success?
44
+ raise Ucode::EmbeddedFontsMissingError,
45
+ "mutool trace failed: #{(out + err).strip}"
46
+ end
47
+
48
+ out + err
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -2,10 +2,6 @@
2
2
 
3
3
  require "pathname"
4
4
 
5
- require_relative "renderer"
6
- require_relative "../../repo/atomic_writes"
7
- require_relative "../../repo/paths"
8
-
9
5
  module Ucode
10
6
  module Glyphs
11
7
  module EmbeddedFonts
@@ -36,12 +36,20 @@ module Ucode
36
36
  # `mutool info` (font enumeration) and `mutool show -b -o` (raw
37
37
  # stream extraction).
38
38
  module EmbeddedFonts
39
- autoload :Source, "ucode/glyphs/embedded_fonts/source"
39
+ autoload :PdfLocation, "ucode/glyphs/embedded_fonts/pdf_location"
40
40
  autoload :ToUnicode, "ucode/glyphs/embedded_fonts/tounicode"
41
41
  autoload :FontEntry, "ucode/glyphs/embedded_fonts/font_entry"
42
+ autoload :RawFontDescriptor, "ucode/glyphs/embedded_fonts/raw_font_descriptor"
43
+ autoload :PdfIndexer, "ucode/glyphs/embedded_fonts/pdf_indexer"
44
+ autoload :CodepointMapper, "ucode/glyphs/embedded_fonts/codepoint_mapper"
42
45
  autoload :Catalog, "ucode/glyphs/embedded_fonts/catalog"
43
46
  autoload :ContentStreamCorrelator,
44
47
  "ucode/glyphs/embedded_fonts/content_stream_correlator"
48
+ autoload :PositionalMatcher, "ucode/glyphs/embedded_fonts/positional_matcher"
49
+ autoload :TraceGlyph, "ucode/glyphs/embedded_fonts/trace_glyph"
50
+ autoload :TraceParser, "ucode/glyphs/embedded_fonts/trace_parser"
51
+ autoload :TraceCorrelator, "ucode/glyphs/embedded_fonts/trace_correlator"
52
+ autoload :TraceRunner, "ucode/glyphs/embedded_fonts/trace_runner"
45
53
  autoload :Svg, "ucode/glyphs/embedded_fonts/svg"
46
54
  autoload :Renderer, "ucode/glyphs/embedded_fonts/renderer"
47
55
  autoload :Writer, "ucode/glyphs/embedded_fonts/writer"
@@ -1,42 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "pathname"
4
- require "open3"
5
4
 
6
5
  require "ucode/cache"
7
6
  require "ucode/fetch/code_charts"
8
- require "ucode/glyphs/monolith_page_map"
9
7
 
10
8
  module Ucode
11
9
  module Glyphs
12
10
  # Resolves a Unicode block to its source PDF on disk.
13
11
  #
14
- # Primary source: the per-block PDF cached at
12
+ # Source: the per-block PDF cached at
15
13
  # `<cache>/<version>/pdfs/U<XXXX>.pdf` (downloaded from
16
14
  # `unicode.org/charts/PDF/` by `Ucode::Fetch::CodeCharts`).
17
- #
18
- # Fallback: slice the page range from the monolith `CodeCharts.pdf`.
19
- # The page range is resolved by `MonolithPageMap` from the PDF's
20
- # bookmark outline, cached under `data/codecharts_page_map.json`.
21
15
  class PdfFetcher
22
16
  # @param version [String] UCD version, used as the cache namespace.
23
- # @param monolith_path [String, Pathname, nil] path to the full
24
- # `CodeCharts.pdf`. Pass nil to disable monolith fallback.
25
- # @param blocks [Array<Ucode::Models::Block>] required for monolith
26
- # fallback — used to match bookmark titles to block first-cps.
27
- # @param page_map_cache [String, Pathname, nil] where to read/write
28
- # the monolith page-map JSON cache.
29
- def initialize(version, monolith_path: nil, blocks: [], page_map_cache: nil)
17
+ def initialize(version)
30
18
  @version = version
31
- @monolith_path = monolith_path && Pathname.new(monolith_path)
32
- @blocks = blocks
33
- @page_map_cache = page_map_cache
34
19
  end
35
20
 
36
21
  # Resolve the per-block PDF for `block_first_cp`, fetching from the
37
22
  # network if missing. Returns the local PDF path, or nil if the
38
- # block's PDF is unavailable (network failure + no monolith, or
39
- # monolith lacks the requested block).
23
+ # block's PDF is unavailable (network failure).
40
24
  #
41
25
  # @param block_first_cp [Integer] first codepoint of the block;
42
26
  # also the PDF's URL slug per unicode.org's naming convention.
@@ -47,9 +31,7 @@ module Ucode
47
31
  return path if path.exist? && !force
48
32
 
49
33
  download(block_first_cp)
50
- return path if path.exist?
51
-
52
- slice_from_monolith(block_first_cp)
34
+ path if path.exist?
53
35
  end
54
36
 
55
37
  private
@@ -65,38 +47,13 @@ module Ucode
65
47
  def download(block_first_cp)
66
48
  Fetch::CodeCharts.call(@version, block_first_cps: [block_first_cp])
67
49
  rescue StandardError => e
68
- # Network failures fall through to monolith fallback. We do not
69
- # swallow programming errors (NoMethodError etc.) — only fetch
70
- # failures (network, checksum, HTTP).
50
+ # Network failures return nil so callers can fall back to other
51
+ # tiers. We do not swallow programming errors (NoMethodError
52
+ # etc.) — only fetch failures (network, checksum, HTTP).
71
53
  return if e.is_a?(Ucode::FetchError)
72
54
 
73
55
  raise
74
56
  end
75
-
76
- def slice_from_monolith(block_first_cp)
77
- return unless @monolith_path&.exist?
78
-
79
- entry = page_map[block_first_cp]
80
- return unless entry && entry.start_page && entry.end_page
81
-
82
- slice_pages(entry.start_page, entry.end_page, per_block_path(block_first_cp))
83
- end
84
-
85
- def page_map
86
- @page_map ||= MonolithPageMap.load(
87
- monolith_path: @monolith_path,
88
- blocks: @blocks,
89
- cache_path: @page_map_cache,
90
- )
91
- end
92
-
93
- def slice_pages(start_page, end_page, out_path)
94
- out_path.dirname.mkpath
95
- cmd = ["pdftk", @monolith_path.to_s, "cat",
96
- "#{start_page}-#{end_page}", "output", out_path.to_s]
97
- _out, status = Open3.capture2e(*cmd)
98
- status.success? ? out_path : nil
99
- end
100
57
  end
101
58
  end
102
59
  end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+
5
+ module Ucode
6
+ module Glyphs
7
+ # Single injection point for the 4-tier {Resolver}.
8
+ #
9
+ # Both CanonicalBuildCommand and UniversalSet::BuildCommand need the
10
+ # same shape: open a Database, load the SourceConfig, run a
11
+ # SourceBuilder, wrap the resulting tier-1 sources in a Resolver.
12
+ # Extracting it here gives tests one seam to mock (or bypass) and
13
+ # prevents drift between the two call sites.
14
+ module ResolverFactory
15
+ DEFAULT_INSTALL = false
16
+ private_constant :DEFAULT_INSTALL
17
+
18
+ # @param version [String] UCD version, used to open the Database
19
+ # when one is not supplied.
20
+ # @param source_config_path [String, Pathname, nil] override path
21
+ # to the Tier 1 font config YAML; nil uses the default.
22
+ # @param install [Boolean] pass through to SourceBuilder#tier1_sources
23
+ # — whether to fontist-install missing fonts eagerly.
24
+ # @param database [Ucode::Database, nil] an already-open Database,
25
+ # to skip re-opening when the caller already has one.
26
+ # @return [Ucode::Glyphs::Resolver]
27
+ def self.build(version:, source_config_path: nil,
28
+ install: DEFAULT_INSTALL, database: nil)
29
+ db = database || Ucode::Database.open(version)
30
+ config = SourceConfig.new(path: resolve_config_path(source_config_path))
31
+ builder = SourceBuilder.new(config: config, database: db)
32
+ Resolver.new(sources: builder.tier1_sources(install: install))
33
+ end
34
+
35
+ # @api private
36
+ def self.resolve_config_path(path)
37
+ return SourceConfig::DEFAULT_PATH if path.nil?
38
+ return path if path.is_a?(Pathname)
39
+
40
+ Pathname.new(path)
41
+ end
42
+ private_class_method :resolve_config_path
43
+ end
44
+ end
45
+ end
@@ -31,7 +31,7 @@ module Ucode
31
31
  # @param renderer [EmbeddedFonts::Renderer] the renderer to
32
32
  # delegate to. Callers typically construct it with the
33
33
  # {EmbeddedFonts::Catalog} built from the resolved Code
34
- # Charts {EmbeddedFonts::Source}. To enable pillar-2
34
+ # Charts {EmbeddedFonts::PdfLocation}. To enable pillar-2
35
35
  # fallback, that Catalog must be constructed with
36
36
  # +correlator_configs:+.
37
37
  def initialize(renderer:)
data/lib/ucode/glyphs.rb CHANGED
@@ -3,29 +3,20 @@
3
3
  module Ucode
4
4
  # Glyphs — converts Code Charts PDF pages into per-codepoint SVGs.
5
5
  #
6
- # Pipeline: fetch per-block PDF render to SVG → detect grid → extract
7
- # cell normalize viewBoxwrite glyph.svg.
6
+ # The current pipeline is the 4-tier sourcing strategy:
7
+ # Tier 1 (real fonts)Pillar 1 (embedded CIDFont + ToUnicode) →
8
+ # Pillar 2 (positional correlation) → Pillar 3 (Last Resort UFO).
9
+ # See {EmbeddedFonts} for Pillar 1 + 2 and {LastResort} for Pillar 3.
8
10
  #
9
11
  # Vector extraction only. NEVER run OCR.
10
12
  module Glyphs
11
13
  autoload :PdfFetcher, "ucode/glyphs/pdf_fetcher"
12
- autoload :PageRenderer, "ucode/glyphs/page_renderer"
13
- autoload :MutoolRenderer, "ucode/glyphs/mutool_renderer"
14
- autoload :Pdf2svgRenderer, "ucode/glyphs/pdf2svg_renderer"
15
- autoload :DvisvgmRenderer, "ucode/glyphs/dvisvgm_renderer"
16
- autoload :PdftocairoRenderer, "ucode/glyphs/pdftocairo_renderer"
17
- autoload :Grid, "ucode/glyphs/grid"
18
- autoload :PathBbox, "ucode/glyphs/path_bbox"
19
- autoload :GridDetector, "ucode/glyphs/grid_detector"
20
- autoload :CellExtractor, "ucode/glyphs/cell_extractor"
21
- autoload :MonolithPageMap, "ucode/glyphs/monolith_page_map"
22
- autoload :Writer, "ucode/glyphs/writer"
23
- autoload :Pipeline, "ucode/glyphs/pipeline"
24
14
  autoload :LastResort, "ucode/glyphs/last_resort"
25
15
  autoload :EmbeddedFonts, "ucode/glyphs/embedded_fonts"
26
16
  autoload :RealFonts, "ucode/glyphs/real_fonts"
27
17
  autoload :Source, "ucode/glyphs/source"
28
18
  autoload :Resolver, "ucode/glyphs/resolver"
19
+ autoload :ResolverFactory, "ucode/glyphs/resolver_factory"
29
20
  autoload :SourceConfig, "ucode/glyphs/source_config"
30
21
  autoload :SourceBuilder, "ucode/glyphs/source_builder"
31
22
  autoload :Sources, "ucode/glyphs/sources"
data/lib/ucode/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ucode
4
- VERSION = "0.2.2"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/ucode.rb CHANGED
@@ -32,8 +32,6 @@ module Ucode
32
32
  autoload :UnknownVersionError, "ucode/error"
33
33
  autoload :UnknownBlockError, "ucode/error"
34
34
  autoload :GlyphError, "ucode/error"
35
- autoload :PdfRenderError, "ucode/error"
36
- autoload :GridDetectionError, "ucode/error"
37
35
  autoload :LastResortMissingError, "ucode/error"
38
36
  autoload :EmbeddedFontsMissingError, "ucode/error"
39
37
  autoload :CodeChartNotFoundError, "ucode/error"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ucode
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-07-01 00:00:00.000000000 Z
11
+ date: 2026-07-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64
@@ -321,7 +321,6 @@ files:
321
321
  - lib/ucode/commands/canonical_build.rb
322
322
  - lib/ucode/commands/fetch.rb
323
323
  - lib/ucode/commands/font_coverage.rb
324
- - lib/ucode/commands/glyphs.rb
325
324
  - lib/ucode/commands/lookup.rb
326
325
  - lib/ucode/commands/parse.rb
327
326
  - lib/ucode/commands/release.rb
@@ -329,7 +328,19 @@ files:
329
328
  - lib/ucode/commands/universal_set.rb
330
329
  - lib/ucode/config.rb
331
330
  - lib/ucode/coordinator.rb
331
+ - lib/ucode/coordinator/enrichment.rb
332
+ - lib/ucode/coordinator/enrichment/bidi.rb
333
+ - lib/ucode/coordinator/enrichment/binary.rb
334
+ - lib/ucode/coordinator/enrichment/casing.rb
335
+ - lib/ucode/coordinator/enrichment/cjk.rb
336
+ - lib/ucode/coordinator/enrichment/display.rb
337
+ - lib/ucode/coordinator/enrichment/emoji.rb
338
+ - lib/ucode/coordinator/enrichment/identity.rb
339
+ - lib/ucode/coordinator/enrichment/indic.rb
340
+ - lib/ucode/coordinator/enrichment/names.rb
341
+ - lib/ucode/coordinator/enrichment/segmentation.rb
332
342
  - lib/ucode/coordinator/indices.rb
343
+ - lib/ucode/coordinator/range_lookup.rb
333
344
  - lib/ucode/database.rb
334
345
  - lib/ucode/db_builder.rb
335
346
  - lib/ucode/error.rb
@@ -342,19 +353,23 @@ files:
342
353
  - lib/ucode/fetch/ucd_zip.rb
343
354
  - lib/ucode/fetch/unihan_zip.rb
344
355
  - lib/ucode/glyphs.rb
345
- - lib/ucode/glyphs/cell_extractor.rb
346
- - lib/ucode/glyphs/dvisvgm_renderer.rb
347
356
  - lib/ucode/glyphs/embedded_fonts.rb
348
357
  - lib/ucode/glyphs/embedded_fonts/catalog.rb
358
+ - lib/ucode/glyphs/embedded_fonts/codepoint_mapper.rb
349
359
  - lib/ucode/glyphs/embedded_fonts/content_stream_correlator.rb
350
360
  - lib/ucode/glyphs/embedded_fonts/font_entry.rb
361
+ - lib/ucode/glyphs/embedded_fonts/pdf_indexer.rb
362
+ - lib/ucode/glyphs/embedded_fonts/pdf_location.rb
363
+ - lib/ucode/glyphs/embedded_fonts/positional_matcher.rb
364
+ - lib/ucode/glyphs/embedded_fonts/raw_font_descriptor.rb
351
365
  - lib/ucode/glyphs/embedded_fonts/renderer.rb
352
- - lib/ucode/glyphs/embedded_fonts/source.rb
353
366
  - lib/ucode/glyphs/embedded_fonts/svg.rb
354
367
  - lib/ucode/glyphs/embedded_fonts/tounicode.rb
368
+ - lib/ucode/glyphs/embedded_fonts/trace_correlator.rb
369
+ - lib/ucode/glyphs/embedded_fonts/trace_glyph.rb
370
+ - lib/ucode/glyphs/embedded_fonts/trace_parser.rb
371
+ - lib/ucode/glyphs/embedded_fonts/trace_runner.rb
355
372
  - lib/ucode/glyphs/embedded_fonts/writer.rb
356
- - lib/ucode/glyphs/grid.rb
357
- - lib/ucode/glyphs/grid_detector.rb
358
373
  - lib/ucode/glyphs/last_resort.rb
359
374
  - lib/ucode/glyphs/last_resort/cmap_index.rb
360
375
  - lib/ucode/glyphs/last_resort/contents.rb
@@ -363,14 +378,7 @@ files:
363
378
  - lib/ucode/glyphs/last_resort/source.rb
364
379
  - lib/ucode/glyphs/last_resort/svg.rb
365
380
  - lib/ucode/glyphs/last_resort/writer.rb
366
- - lib/ucode/glyphs/monolith_page_map.rb
367
- - lib/ucode/glyphs/mutool_renderer.rb
368
- - lib/ucode/glyphs/page_renderer.rb
369
- - lib/ucode/glyphs/path_bbox.rb
370
- - lib/ucode/glyphs/pdf2svg_renderer.rb
371
381
  - lib/ucode/glyphs/pdf_fetcher.rb
372
- - lib/ucode/glyphs/pdftocairo_renderer.rb
373
- - lib/ucode/glyphs/pipeline.rb
374
382
  - lib/ucode/glyphs/real_fonts.rb
375
383
  - lib/ucode/glyphs/real_fonts/block_coverage.rb
376
384
  - lib/ucode/glyphs/real_fonts/cmap_cache.rb
@@ -380,6 +388,7 @@ files:
380
388
  - lib/ucode/glyphs/real_fonts/unicode_17_blocks.rb
381
389
  - lib/ucode/glyphs/real_fonts/writer.rb
382
390
  - lib/ucode/glyphs/resolver.rb
391
+ - lib/ucode/glyphs/resolver_factory.rb
383
392
  - lib/ucode/glyphs/source.rb
384
393
  - lib/ucode/glyphs/source_builder.rb
385
394
  - lib/ucode/glyphs/source_config.rb
@@ -397,7 +406,6 @@ files:
397
406
  - lib/ucode/glyphs/universal_set/manifest_writer.rb
398
407
  - lib/ucode/glyphs/universal_set/pre_build_check.rb
399
408
  - lib/ucode/glyphs/universal_set/validator.rb
400
- - lib/ucode/glyphs/writer.rb
401
409
  - lib/ucode/index.rb
402
410
  - lib/ucode/index_builder.rb
403
411
  - lib/ucode/models.rb