Class: Ucode::Glyphs::EmbeddedFonts::CodepointMapper

Inherits:
Object
  • Object
show all
Defined in:
lib/ucode/glyphs/embedded_fonts/codepoint_mapper.rb,
lib/ucode/glyphs/embedded_fonts/codepoint_mapper/strategy.rb,
lib/ucode/glyphs/embedded_fonts/codepoint_mapper/trace_strategy.rb,
lib/ucode/glyphs/embedded_fonts/codepoint_mapper/tounicode_strategy.rb,
lib/ucode/glyphs/embedded_fonts/codepoint_mapper/correlator_strategy.rb

Overview

Resolves codepoint → GID for one Type0 font via a chain of Strategy subclasses.

The chain is partitioned by Strategy#positional?:

* Intrinsic strategies (ToUnicodeStrategy) read the font's
own CMap. Tried in chain order; the first non-empty result
wins.
* Positional strategies (CorrelatorStrategy, TraceStrategy)
attribute glyphs by chart-grid geometry. Expensive — they
shell out to mutool per page — so they only run when an
intrinsic strategy cannot cover the requested block scope.

When positional strategies run, their results merge over the intrinsic result with positional precedence: chart geometry is authoritative for in-block specimens, while a font's CMap can be misleading (Enclosed Ideographic Supplement, where the embedded CJKSymbols font's CMap maps its CIDs to the composing ideographs rather than the squared characters themselves).

Two escape hatches let callers control the gating:

* `block_range:` — when set, an intrinsic result with zero
in-block intersection is treated as "wrong scope" and
dropped, so positional strategies can take over. Auto-detect
for the U1F200 class of failure (Option 1).
* `force_positional_for_font_ids:` — Type0 font object IDs
that always go through positional attribution, regardless
of whether the intrinsic strategy succeeded. Escape hatch
for partial-overlap cases where the CMap covers some
in-block codepoints but positional attribution is still
desired for the rest (Option 2).

Adding a new strategy = one Strategy subclass + (if positional) a positional? override + one entry in the chain. No edit to #map (Open/Closed Principle).

Defined Under Namespace

Classes: CorrelatorStrategy, Strategy, ToUnicodeStrategy, TraceStrategy

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(strategies:, block_range: nil, force_positional_for_font_ids: Set.new) ⇒ CodepointMapper

Returns a new instance of CodepointMapper.

Parameters:

  • strategies (Array<Strategy>)

    chain; partitioned by Strategy#positional? at map time

  • block_range (Range<Integer>, nil) (defaults to: nil)

    codepoint scope the caller is extracting. nil = intrinsic result is always trusted (legacy behavior; the caller doesn't know or care about block scope).

  • force_positional_for_font_ids (Set<Integer>) (defaults to: Set.new)

    Type0 font object IDs that always trigger positional attribution



59
60
61
62
63
64
# File 'lib/ucode/glyphs/embedded_fonts/codepoint_mapper.rb', line 59

def initialize(strategies:, block_range: nil,
               force_positional_for_font_ids: Set.new)
  @strategies = strategies
  @block_range = block_range
  @force_positional_for_font_ids = force_positional_for_font_ids
end

Class Method Details

.build(source:, correlator_configs:, indexer:, block_range: nil, force_positional_for_font_ids: Set.new, mutool_show: Mutool::Show.new, mutool_draw: Mutool::Draw.new, mutool_trace: Mutool::Trace.new) ⇒ CodepointMapper

Convenience builder — wires up the default 3-strategy chain with default Mutool wrappers. Callers that need to inject stubs for tests should construct strategies directly and pass them to #initialize.

Returns:



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/ucode/glyphs/embedded_fonts/codepoint_mapper.rb', line 72

def self.build(source:, correlator_configs:, indexer:,
               block_range: nil, force_positional_for_font_ids: Set.new,
               mutool_show: Mutool::Show.new,
               mutool_draw: Mutool::Draw.new,
               mutool_trace: Mutool::Trace.new)
  trace_cache = PageTraceCache.new(
    pdf: source.pdf_path,
    page_count: indexer.page_count,
    mutool: mutool_trace,
  )
  strategies = [
    ToUnicodeStrategy.new(source: source, mutool_show: mutool_show),
    CorrelatorStrategy.new(source: source,
                           correlator_configs: correlator_configs,
                           mutool_draw: mutool_draw),
    TraceStrategy.new(cache: trace_cache, indexer: indexer),
  ]
  new(strategies: strategies,
      block_range: block_range,
      force_positional_for_font_ids: force_positional_for_font_ids)
end

Instance Method Details

#map(descriptor) ⇒ Hash{Integer=>Integer}

Returns codepoint => gid; empty when no strategy produces a mapping.

Parameters:

Returns:

  • (Hash{Integer=>Integer})

    codepoint => gid; empty when no strategy produces a mapping



97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/ucode/glyphs/embedded_fonts/codepoint_mapper.rb', line 97

def map(descriptor)
  return {} unless descriptor.cid_map_kind == :identity

  positional, intrinsic = partition_strategies
  intrinsic_result = run_intrinsic(descriptor, intrinsic)
  return intrinsic_result if positional.empty?
  return intrinsic_result unless needs_positional?(descriptor,
                                                   intrinsic_result)

  positional_result = run_positional(descriptor, positional)
  merge_with_positional_precedence(intrinsic_result, positional_result)
end