module HexaPDF::Font::TrueType::Table::CmapSubtable::Format4

def self.mapper(end_codes, start_codes, id_deltas, id_range_offsets, glyph_indexes)

:nodoc:
def self.mapper(end_codes, start_codes, id_deltas, id_range_offsets, glyph_indexes)
  compute_glyph_id = lambda do |index, code|
    offset = id_range_offsets[index]
    if offset == 0
      glyph_id = (code + id_deltas[index]) % 65536
    else
      glyph_id = glyph_indexes[offset - end_codes.length + (code - start_codes[index])]
      glyph_id ||= 0 # Handle invalid subtable entries
      glyph_id = (glyph_id + id_deltas[index]) % 65536 if glyph_id != 0
    end
    glyph_id
  end
  code_map = Hash.new do |h, code|
    i = end_codes.bsearch_index {|c| c >= code }
    glyph_id = (i && start_codes[i] <= code ? compute_glyph_id.call(i, code) : 0)
    h[code] = glyph_id unless glyph_id == 0
  end
  gid_map = {}
  end_codes.length.times do |i|
    start_codes[i].upto(end_codes[i]) do |code|
      gid_map[compute_glyph_id.call(i, code)] = code
    end
  end
  [code_map, gid_map]
end

def self.parse(io, length)

It is assumed that the first six bytes of the subtable have already been consumed.

returns the contained code map.
Parses the format 4 cmap subtable from the given IO at the current position and

Format4.parse(io, length) -> code_map
:call-seq:
def self.parse(io, length)
  seg_count_x2 = io.read(8).unpack1('n')
  end_codes = io.read(seg_count_x2).unpack('n*')
  io.pos += 2
  start_codes = io.read(seg_count_x2).unpack('n*')
  id_deltas = io.read(seg_count_x2).unpack('n*')
  id_range_offsets = io.read(seg_count_x2).unpack('n*').map!.with_index do |offset, idx|
    # Change offsets to indexes, starting from the id_range_offsets array
    offset == 0 ? offset : offset / 2 + idx
  end
  glyph_indexes = io.read(length - 16 - seg_count_x2 * 4).unpack('n*')
  mapper(end_codes, start_codes, id_deltas, id_range_offsets, glyph_indexes)
end