Module: Fontisan::Ufo::Compile::Gpos

Defined in:
lib/fontisan/ufo/compile/gpos.rb

Overview

Builds the OpenType GPOS (Glyph Positioning) table from UFO kerning data. Emits a minimal but valid GPOS with:

- ScriptList: DFLT script, default language system
- FeatureList: `kern` feature (feature tag "kern")
- LookupList: one PairPos lookup (format 1, individual pairs)

Each kerning pair from the UFO source becomes a PairPosRecord with an x-advance adjustment on the first glyph.

Constant Summary collapse

FEATURE_KERN =
"kern"
SCRIPT_DFLT =
"DFLT"
LANGSYS_DEFAULT =
0
VALUE_X_PLACEMENT =

ValueFormat flags (which fields are present in a ValueRecord)

0x0001
VALUE_Y_PLACEMENT =
0x0002
VALUE_X_ADVANCE =
0x0004
VALUE_Y_ADVANCE =
0x0008

Class Method Summary collapse

Class Method Details

.build(font, glyphs:) ⇒ String?

Returns GPOS table bytes, or nil if no kerning.

Parameters:

Returns:

  • (String, nil)

    GPOS table bytes, or nil if no kerning



33
34
35
36
37
38
# File 'lib/fontisan/ufo/compile/gpos.rb', line 33

def self.build(font, glyphs:)
  pairs = collect_kerning_pairs(font, glyphs)
  return nil if pairs.empty?

  build_gpos_table(pairs)
end

.collect_kerning_pairs(font, glyphs) ⇒ Array<[Integer, Integer, Integer]>

Collect kerning pairs from the UFO model. Each pair is (gid1, gid2, x_advance_delta). Resolves class-based kerning keys (e.g. "@MMK_L_A @MMK_R_T") by expanding group members into individual glyph pairs via font.groups.

Returns:

  • (Array<[Integer, Integer, Integer]>)


47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/fontisan/ufo/compile/gpos.rb', line 47

def self.collect_kerning_pairs(font, glyphs)
  name_to_gid = {}
  glyphs.each_with_index { |g, gid| name_to_gid[g.name] = gid }

  pairs = []
  font.kerning.each_pair do |key, value|
    names = key.split
    next unless names.size == 2

    left_names = resolve_kerning_side(names[0], font)
    right_names = resolve_kerning_side(names[1], font)

    left_names.each do |ln|
      right_names.each do |rn|
        gid1 = name_to_gid[ln]
        gid2 = name_to_gid[rn]
        next unless gid1 && gid2

        pairs << [gid1, gid2, value.to_i]
      end
    end
  end

  pairs.sort_by { |a| [a[0], a[1]] }
end

.resolve_kerning_side(name, font) ⇒ Array<String>

Resolve a kerning key side to individual glyph names. If name is a group reference (starts with "@"), returns all group members. Otherwise returns [name] for an individual glyph.

Parameters:

Returns:

  • (Array<String>)

    glyph names



80
81
82
83
84
85
# File 'lib/fontisan/ufo/compile/gpos.rb', line 80

def self.resolve_kerning_side(name, font)
  return [name] unless name.start_with?("@")

  members = font.groups.glyphs(name)
  members.empty? ? [] : members
end