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” 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



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

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



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

def count
  @count
end

#dataString (readonly)

Returns Binary string containing all data.

Returns:

  • (String)

    Binary string containing all data



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

def data
  @data
end

#off_sizeInteger (readonly)

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

Returns:

  • (Integer)

    Size of offset values (1-4 bytes)



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

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)



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

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



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

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



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

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



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

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



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

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



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

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



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

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