1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 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 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
|
# frozen_string_literal: true
module TTFunk
class Table
class Cmap
# Format 4: Segment mapping to delta values.
#
# This module conditionally extends {TTFunk::Table::Cmap::Subtable}.
module Format04
# Language.
# @return [Integer]
attr_reader :language
# Code map.
# @return [Hash{Integer => Integer}]
attr_reader :code_map
# Encode the encoding record to format 4.
#
# @param charmap [Hash{Integer => Integer}] a hash mapping character
# codes to glyph IDs from the original font.
# @return [Hash]
# * `:charmap` (<tt>Hash{Integer => Hash}</tt>) keys are the characrers in
# `charset`, values are hashes:
# * `:old` (<tt>Integer</tt>) - glyph ID in the original font.
# * `:new` (<tt>Integer</tt>) - glyph ID in the subset font.
# that maps the characters in charmap to a
# * `:subtable` (<tt>String</tt>) - serialized encoding record.
# * `:max_glyph_id` (<tt>Integer</tt>) - maximum glyph ID in the new font.
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
# Get glyph ID for character code.
#
# @param code [Integer] character code.
# @return [Integer] glyph ID.
def [](code)
@code_map[code] || 0
end
# Is this encoding record format supported?
#
# @return [true]
def supported?
true
end
private
def parse_cmap!
length, @language, segcount_x2 = read(6, 'nnn')
segcount = segcount_x2 / 2
io.read(6) # skip searching hints
end_code = read(segcount_x2, 'n*')
io.read(2) # skip reserved value
start_code = read(segcount_x2, 'n*')
id_delta = read_signed(segcount)
id_range_offset = read(segcount_x2, 'n*')
glyph_ids = read(length - io.pos + @offset, 'n*')
@code_map = {}
end_code.each_with_index do |tail, i|
start_code[i].upto(tail) do |code|
if (id_range_offset[i]).zero?
glyph_id = code + id_delta[i]
else
index = (id_range_offset[i] / 2) + (code - start_code[i]) - (segcount - i)
# Because some TTF fonts are broken
glyph_id = glyph_ids[index] || 0
glyph_id += id_delta[i] if glyph_id != 0
end
@code_map[code] = glyph_id & 0xFFFF
end
end
end
end
end
end
end
|