Class: Fontisan::Tables::Cff::Index

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/fontisan/tables/cff/index.rb

Overview

CFF INDEX structure

INDEX is a fundamental data structure used throughout CFF for storing arrays of variable-length data items. It's used for:

  • Name INDEX (font names)
  • String INDEX (string data)
  • Global Subr INDEX (global subroutines)
  • Local Subr INDEX (local subroutines)
  • CharStrings INDEX (glyph programs)

Structure:

  • count (Card16): Number of objects stored in INDEX
  • offSize (OffSize): Size of offset values (1-4 bytes)
  • offset (Offset): Array of offsets to data
  • data: The actual data bytes

Offsets are relative to the byte before the data array. The first offset is always 1, not 0. The last offset points one byte past the end of the data.

Reference: CFF specification section 5 "INDEX Data" https://adobe-type-tools.github.io/font-tech-notes/pdfs/5176.CFF.pdf

Examples:

Reading an INDEX

index = Fontisan::Tables::Cff::Index.new(data)
puts index.count  # => 3
puts index[0]  # => first item data
index.each { |item| puts item }

Direct Known Subclasses

CharstringsIndex

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(io, start_offset: 0) ⇒ Index

Initialize an INDEX from binary data

Parameters:

  • io (IO, StringIO, String)

    Binary data to parse

  • start_offset (Integer) (defaults to: 0)

    Starting byte offset in the data



58
59
60
61
62
63
64
# File 'lib/fontisan/tables/cff/index.rb', line 58

def initialize(io, start_offset: 0)
  @io = io.is_a?(String) ? StringIO.new(io) : io
  @start_offset = start_offset
  @io.seek(start_offset) if @io.respond_to?(:seek)

  parse!
end

Instance Attribute Details

#countInteger (readonly)

Returns Number of items in the INDEX.

Returns:

  • (Integer)

    Number of items in the INDEX



40
41
42
# File 'lib/fontisan/tables/cff/index.rb', line 40

def count
  @count
end

#dataString (readonly)

Returns Binary string containing all data.

Returns:

  • (String)

    Binary string containing all data



49
50
51
# File 'lib/fontisan/tables/cff/index.rb', line 49

def data
  @data
end

#off_sizeInteger (readonly)

Returns Size of offset values (1-4 bytes).

Returns:

  • (Integer)

    Size of offset values (1-4 bytes)



43
44
45
# File 'lib/fontisan/tables/cff/index.rb', line 43

def off_size
  @off_size
end

#offsetsArray<Integer> (readonly)

Returns Array of offsets (count + 1 elements).

Returns:

  • (Array<Integer>)

    Array of offsets (count + 1 elements)



46
47
48
# File 'lib/fontisan/tables/cff/index.rb', line 46

def offsets
  @offsets
end

#start_offsetInteger (readonly)

Returns Byte offset where this INDEX starts in the source data.

Returns:

  • (Integer)

    Byte offset where this INDEX starts in the source data



52
53
54
# File 'lib/fontisan/tables/cff/index.rb', line 52

def start_offset
  @start_offset
end

Instance Method Details

#[](index) ⇒ String?

Get the item at the specified index

Parameters:

  • index (Integer)

    Zero-based index of item to retrieve

Returns:

  • (String, nil)

    Binary data for the item, or nil if out of bounds



70
71
72
73
74
75
76
77
78
79
80
# File 'lib/fontisan/tables/cff/index.rb', line 70

def [](index)
  return nil if index.negative? || index >= count
  return "" if count.zero?

  # Offsets are 1-based in the data array
  start_pos = offsets[index] - 1
  end_pos = offsets[index + 1] - 1
  length = end_pos - start_pos

  data[start_pos, length]
end

#each {|String| ... } ⇒ Enumerator

Iterate over each item in the INDEX

Yields:

  • (String)

    Binary data for each item

Returns:

  • (Enumerator)

    If no block given



86
87
88
89
90
91
92
# File 'lib/fontisan/tables/cff/index.rb', line 86

def each
  return enum_for(:each) unless block_given?

  count.times do |i|
    yield self[i]
  end
end

#empty?Boolean

Check if the INDEX is empty

Returns:

  • (Boolean)

    True if count is 0



104
105
106
# File 'lib/fontisan/tables/cff/index.rb', line 104

def empty?
  count.zero?
end

#item_size(index) ⇒ Integer?

Get the size of a specific item

Parameters:

  • index (Integer)

    Zero-based index of item

Returns:

  • (Integer, nil)

    Size in bytes, or nil if out of bounds



112
113
114
115
116
117
# File 'lib/fontisan/tables/cff/index.rb', line 112

def item_size(index)
  return nil if index.negative? || index >= count
  return 0 if count.zero?

  offsets[index + 1] - offsets[index]
end

#to_aArray<String>

Get all items as an array

Returns:

  • (Array<String>)

    Array of binary data strings



97
98
99
# File 'lib/fontisan/tables/cff/index.rb', line 97

def to_a
  Array.new(count) { |i| self[i] }
end

#total_sizeInteger

Calculate total size of the INDEX in bytes

This includes the count, offSize, offset array, and data.

Returns:

  • (Integer)

    Total size in bytes



124
125
126
127
128
129
# File 'lib/fontisan/tables/cff/index.rb', line 124

def total_size
  return 2 if count.zero? # Just the count field

  # count (2) + offSize (1) + offset array + data
  2 + 1 + ((count + 1) * off_size) + data.bytesize
end