Module: TTFunk::Table::Cmap::Format04

Defined in:
lib/ttfunk/table/cmap/format04.rb

Overview

Format 4: Segment mapping to delta values.

This module conditionally extends Subtable.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#code_mapHash{Integer => Integer} (readonly)

Code map.

Returns:

  • (Hash{Integer => Integer})


16
17
18
# File 'lib/ttfunk/table/cmap/format04.rb', line 16

def code_map
  @code_map
end

#languageInteger (readonly)

Language.

Returns:

  • (Integer)


12
13
14
# File 'lib/ttfunk/table/cmap/format04.rb', line 12

def language
  @language
end

Class Method Details

.encode(charmap) ⇒ Hash

Encode the encoding record to format 4.

Parameters:

  • charmap (Hash{Integer => Integer})

    a hash mapping character codes to glyph IDs from the original font.

Returns:

  • (Hash)
    • ‘:charmap` (Hash{Integer => Hash}) keys are the characrers in `charset`, values are hashes:

      • ‘:old` (Integer) - glyph ID in the original font.

      • ‘:new` (Integer) - glyph ID in the subset font.

      that maps the characters in charmap to a

    • ‘:subtable` (String) - serialized encoding record.

    • ‘:max_glyph_id` (Integer) - maximum glyph ID in the new font.



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/ttfunk/table/cmap/format04.rb', line 30

def self.encode(charmap)
  end_codes = []
  start_codes = []
  next_id = 0
  last = difference = nil

  glyph_map = { 0 => 0 }
  new_map =
    charmap.keys.sort.each_with_object({}) do |code, map|
      old = charmap[code]
      glyph_map[old] ||= next_id += 1
      map[code] = { old: old, new: glyph_map[old] }

      delta = glyph_map[old] - code
      if last.nil? || delta != difference
        end_codes << last if last
        start_codes << code
        difference = delta
      end
      last = code

      map
    end

  end_codes << last if last
  end_codes << 0xFFFF
  start_codes << 0xFFFF
  segcount = start_codes.length

  # build the conversion tables
  deltas = []
  range_offsets = []
  glyph_indices = []
  offset = 0

  start_codes.zip(end_codes).each_with_index do |(a, b), segment|
    if a == 0xFFFF
      # We want the final 0xFFFF code to map to glyph 0.
      # The glyph index is calculated as glyph = charcode + delta,
      # which means that delta must be -0xFFFF to map character code
      # 0xFFFF to glyph 0.
      deltas << -0xFFFF
      range_offsets << 0
      break
    end

    start_glyph_id = new_map[a][:new]

    if a - start_glyph_id >= 0x8000
      deltas << 0
      range_offsets << (2 * (glyph_indices.length + segcount - segment))
      a.upto(b) { |code| glyph_indices << new_map[code][:new] }
    else
      deltas << (-a + start_glyph_id)
      range_offsets << 0
    end

    offset += 2
  end

  # format, length, language
  subtable = [
    4, 16 + (8 * segcount) + (2 * glyph_indices.length), 0,
  ].pack('nnn')

  search_range = 2 * (2**Integer(Math.log(segcount) / Math.log(2)))
  entry_selector = Integer(Math.log(search_range / 2) / Math.log(2))
  range_shift = (2 * segcount) - search_range
  subtable << [
    segcount * 2, search_range, entry_selector, range_shift,
  ].pack('nnnn')

  subtable << end_codes.pack('n*') << "\0\0" << start_codes.pack('n*')
  subtable << deltas.pack('n*') << range_offsets.pack('n*')
  subtable << glyph_indices.pack('n*')

  { charmap: new_map, subtable: subtable, max_glyph_id: next_id + 1 }
end

Instance Method Details

#[](code) ⇒ Integer

Get glyph ID for character code.

Parameters:

  • code (Integer)

    character code.

Returns:

  • (Integer)

    glyph ID.



113
114
115
# File 'lib/ttfunk/table/cmap/format04.rb', line 113

def [](code)
  @code_map[code] || 0
end

#supported?true

Is this encoding record format supported?

Returns:

  • (true)


120
121
122
# File 'lib/ttfunk/table/cmap/format04.rb', line 120

def supported?
  true
end