class TTFunk::Table::Cff::FdSelector

Constants

ARRAY_ENTRY_SIZE
ARRAY_FORMAT
RANGE_ENTRY_SIZE
RANGE_FORMAT

Attributes

count[R]
entries[R]
n_glyphs[R]
top_dict[R]

Public Class Methods

new(top_dict, file, offset, length = nil) click to toggle source
Calls superclass method TTFunk::SubTable::new
# File lib/ttfunk/table/cff/fd_selector.rb, line 17
def initialize(top_dict, file, offset, length = nil)
  @top_dict = top_dict
  super(file, offset, length)
end

Public Instance Methods

[](glyph_id) click to toggle source
# File lib/ttfunk/table/cff/fd_selector.rb, line 22
def [](glyph_id)
  case format_sym
  when :array_format
    entries[glyph_id]

  when :range_format
    if (entry = range_cache[glyph_id])
      return entry
    end

    range, entry =
      entries.bsearch do |rng, _|
      if rng.cover?(glyph_id)
        0
      elsif glyph_id < rng.first
        -1
      else
        1
      end
    end

    range.each { |i| range_cache[i] = entry }
    entry
  end
end
each() { |self| ... } click to toggle source
# File lib/ttfunk/table/cff/fd_selector.rb, line 48
def each
  return to_enum(__method__) unless block_given?

  count.times { |i| yield self[i] }
end
encode(mapping) click to toggle source

mapping is new -> old glyph ids

# File lib/ttfunk/table/cff/fd_selector.rb, line 55
def encode(mapping)
  # get list of [new_gid, fd_index] pairs
  new_indices =
    mapping.keys.sort.map do |new_gid|
      [new_gid, self[mapping[new_gid]]]
    end

  ranges = rangify_gids(new_indices)
  total_range_size = ranges.size * RANGE_ENTRY_SIZE
  total_array_size = new_indices.size * ARRAY_ENTRY_SIZE

  ''.b.tap do |result|
    if total_array_size <= total_range_size
      result << [ARRAY_FORMAT].pack('C')
      result << new_indices.map(&:last).pack('C*')
    else
      result << [RANGE_FORMAT, ranges.size].pack('Cn')
      ranges.each { |range| result << range.pack('nC') }

      # "A sentinel GID follows the last range element and serves to
      # delimit the last range in the array. (The sentinel GID is set
      # equal to the number of glyphs in the font. That is, its value
      # is 1 greater than the last GID in the font)."
      result << [new_indices.size].pack('n')
    end
  end
end

Private Instance Methods

format_sym() click to toggle source
# File lib/ttfunk/table/cff/fd_selector.rb, line 142
def format_sym
  case @format
  when ARRAY_FORMAT then :array_format
  when RANGE_FORMAT then :range_format
  else
    raise Error, "unsupported fd select format '#{@format}'"
  end
end
parse!() click to toggle source
# File lib/ttfunk/table/cff/fd_selector.rb, line 105
def parse!
  @format = read(1, 'C').first
  @length = 1

  case format_sym
  when :array_format
    @n_glyphs = top_dict.charstrings_index.count
    data = io.read(n_glyphs)
    @length += data.bytesize
    @count = data.bytesize
    @entries = data.bytes

  when :range_format
    # +2 for sentinel GID, +2 for num_ranges
    num_ranges = read(2, 'n').first
    @length += (num_ranges * RANGE_ENTRY_SIZE) + 4

    ranges = Array.new(num_ranges) { read(RANGE_ENTRY_SIZE, 'nC') }

    @entries =
      ranges.each_cons(2).map do |first, second|
        first_gid, fd_index = first
        second_gid, = second
        [(first_gid...second_gid), fd_index]
      end

    # read the sentinel GID, otherwise known as the number of glyphs
    # in the font
    @n_glyphs = read(2, 'n').first

    last_start_gid, last_fd_index = ranges.last
    @entries << [(last_start_gid...(n_glyphs + 1)), last_fd_index]

    @count = entries.reduce(0) { |sum, entry| sum + entry.first.size }
  end
end
range_cache() click to toggle source
# File lib/ttfunk/table/cff/fd_selector.rb, line 85
def range_cache
  @range_cache ||= {}
end
rangify_gids(values) click to toggle source

values is an array of [new_gid, fd_index] pairs

# File lib/ttfunk/table/cff/fd_selector.rb, line 90
def rangify_gids(values)
  start_gid = 0

  [].tap do |ranges|
    values.each_cons(2) do |(_, first_idx), (sec_gid, sec_idx)|
      if first_idx != sec_idx
        ranges << [start_gid, first_idx]
        start_gid = sec_gid
      end
    end

    ranges << [start_gid, values.last[1]]
  end
end