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



55
56
57
58
59
60
61
# File 'lib/fontisan/tables/cff/index.rb', line 55

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

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



67
68
69
70
71
72
73
74
75
76
77
# File 'lib/fontisan/tables/cff/index.rb', line 67

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



83
84
85
86
87
88
89
# File 'lib/fontisan/tables/cff/index.rb', line 83

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



101
102
103
# File 'lib/fontisan/tables/cff/index.rb', line 101

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



109
110
111
112
113
114
# File 'lib/fontisan/tables/cff/index.rb', line 109

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



94
95
96
# File 'lib/fontisan/tables/cff/index.rb', line 94

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



121
122
123
124
125
126
# File 'lib/fontisan/tables/cff/index.rb', line 121

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