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

Inherits:
Object
  • Object
show all
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



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

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



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

def count
  @count
end

#dataString (readonly)

Returns Binary string containing all data.

Returns:

  • (String)

    Binary string containing all data



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

def data
  @data
end

#off_sizeInteger (readonly)

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

Returns:

  • (Integer)

    Size of offset values (1-4 bytes)



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

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)



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

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



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

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



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

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



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

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



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

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



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

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



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

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