Class: Fontisan::Stitcher::GlyphCopier

Inherits:
Object
  • Object
show all
Defined in:
lib/fontisan/stitcher/glyph_copier.rb

Overview

Owns the mechanical concerns of copying glyphs from donor sources into a target UFO font.

The Stitcher's per-subfont build needs to:

- inject a `.notdef` glyph at GID 0
- copy outline glyphs from donor sources, optionally skipping
donors the caller names (e.g. the CBDT source, whose glyphs
take a different code path)
- register each copied glyph with the deduplicator so visually
identical glyphs from different donors share a gid

GlyphCopier centralizes that logic in one stateless-per-call collaborator. The Stitcher constructs one with a deduplicator and calls its methods in the order the cmap contract requires (see Fontisan::Ufo::Compile::Cmap for first-wins semantics).

Instance Method Summary collapse

Constructor Details

#initialize(deduplicator) ⇒ GlyphCopier

Returns a new instance of GlyphCopier.

Parameters:

  • deduplicator (Stitcher::Deduplicator, nil)

    when non-nil, visually identical glyphs share a gid in the target. Pass nil to disable deduplication.



24
25
26
# File 'lib/fontisan/stitcher/glyph_copier.rb', line 24

def initialize(deduplicator)
  @deduplicator = deduplicator
end

Instance Method Details

#copy_outlines(bindings, target, skip_sources: []) ⇒ Object

Copy outline glyphs from bindings into target, skipping any sources the caller names (e.g. the CBDT source).

Bindings are sorted by [codepoint, donor_gid] for deterministic output. For each binding:

- if deduplicator has seen a visually identical glyph that's
already in the target, attach the codepoint to that glyph
(extra unicode mapping);
- otherwise copy the glyph under a unique name and register
with the deduplicator.

Parameters:

  • bindings (Array<Hash>)

    stitch bindings

  • target (Ufo::Font)

    target UFO

  • skip_sources (Array<Stitcher::Source>) (defaults to: [])

    sources whose bindings should be skipped (e.g. CBDT placeholder source)



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/fontisan/stitcher/glyph_copier.rb', line 65

def copy_outlines(bindings, target, skip_sources: [])
  sorted_bindings(bindings).each do |binding|
    next if binding[:donor_gid].zero?
    next if skip_sources.any? { |s| s.equal?(binding[:source]) }

    glyph = binding[:source].glyph_for_gid(binding[:donor_gid])
    next unless glyph

    canonical = @deduplicator&.find(glyph)
    if canonical && target.glyphs.key?(canonical)
      add_extra_unicode(target, canonical, binding[:codepoint])
    else
      name = UniqueGlyphName.in(target, glyph.name)
      copy_glyph_into(target, name: name, source: binding[:source],
                              donor_gid: binding[:donor_gid],
                              codepoint: binding[:codepoint])
      @deduplicator&.register(glyph, name)
    end
  end
end

#inject_notdef(bindings, target) ⇒ Object

Inject a .notdef glyph at GID 0 of target.

If a binding exists with donor_gid == 0, copy the donor's .notdef; otherwise synthesize an empty one. The injected glyph is registered with the deduplicator so subsequent .notdef references deduplicate against it.

Parameters:

  • bindings (Array<Hash>)

    stitch bindings to search

  • target (Ufo::Font)

    target UFO



37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/fontisan/stitcher/glyph_copier.rb', line 37

def inject_notdef(bindings, target)
  notdef_binding = bindings.find { |b| b[:donor_gid].zero? }
  if notdef_binding
    copy_glyph_into(target, name: ".notdef",
                            source: notdef_binding[:source],
                            donor_gid: 0)
  else
    target.layers.default_layer.add(Ufo::Glyph.new(name: ".notdef"))
  end
  dedup_target = target.glyphs[".notdef"]
  @deduplicator&.register(dedup_target, ".notdef") if dedup_target
end