Class: Fontisan::Tables::Name

Inherits:
Binary::BaseRecord show all
Extended by:
Registered
Defined in:
lib/fontisan/tables/name.rb

Overview

BinData structure for the 'name' (Naming Table) table

The name table allows multilingual strings to be associated with the font. These strings can represent copyright notices, font names, family names, style names, and other information.

Reference: OpenType specification, name table

Examples:

Reading a name table

data = File.binread("font.ttf", length, name_offset)
name = Fontisan::Tables::Name.read(data)
puts name.english_name(Fontisan::Tables::Name::FAMILY)

Constant Summary collapse

0
FAMILY =
1
SUBFAMILY =
2
UNIQUE_ID =
3
FULL_NAME =
4
VERSION =
5
POSTSCRIPT_NAME =
6
TRADEMARK =
7
MANUFACTURER =
8
DESIGNER =
9
DESCRIPTION =
10
VENDOR_URL =
11
DESIGNER_URL =
12
LICENSE_DESCRIPTION =
13
LICENSE_URL =
14
PREFERRED_FAMILY =
16
PREFERRED_SUBFAMILY =
17
COMPATIBLE_FULL =
18
SAMPLE_TEXT =
19
POSTSCRIPT_CID =
20
WWS_FAMILY =
21
WWS_SUBFAMILY =
22
PLATFORM_UNICODE =

Platform IDs

0
PLATFORM_MACINTOSH =
1
PLATFORM_WINDOWS =
3
WINDOWS_LANGUAGE_EN_US =

Windows language ID for US English

0x0409
MAC_LANGUAGE_ENGLISH =

Mac language ID for English

0

Instance Attribute Summary collapse

Attributes inherited from Binary::BaseRecord

#raw_data

Instance Method Summary collapse

Methods included from Registered

register_tag

Methods inherited from Binary::BaseRecord

read

Instance Attribute Details

#decoded_names_cacheObject

Cache for decoded names



112
113
114
# File 'lib/fontisan/tables/name.rb', line 112

def decoded_names_cache
  @decoded_names_cache
end

Instance Method Details

#after_read_hookObject

Hook that gets called after all fields are read



115
116
117
118
# File 'lib/fontisan/tables/name.rb', line 115

def after_read_hook
  # Don't decode anything yet - wait for request
  @decoded_names_cache = {}
end

#countInteger

Get the count of name records (for backward compatibility)

Returns:

  • (Integer)

    Number of name records



129
130
131
# File 'lib/fontisan/tables/name.rb', line 129

def count
  record_count
end

#decode_all_stringsvoid

This method returns an undefined value.

Decode all strings from the string storage area

This method can be called explicitly to decode all name records upfront. Useful for testing or when you know you'll need all strings. By default, strings are decoded lazily on demand.



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/fontisan/tables/name.rb', line 140

def decode_all_strings
  # Get the raw string storage as a plain Ruby binary string
  storage_bytes = string_storage.to_s.b

  return if storage_bytes.empty?

  name_records.each do |record|
    # Extract string data from storage using offset and length
    offset = record.string_offset
    length = record.string_length

    # Validate bounds
    next if offset.nil? || length.nil?
    next if offset + length > storage_bytes.bytesize
    next if length.zero?

    # Slice the bytes from storage
    string_data = storage_bytes.byteslice(offset, length)
    record.decode_string(string_data) if string_data && !string_data.empty?
  end
end

#do_read(io) ⇒ Object

Make sure we call our hook after BinData finishes reading



121
122
123
124
# File 'lib/fontisan/tables/name.rb', line 121

def do_read(io)
  super
  after_read_hook
end

#english_name(name_id) ⇒ String?

Find an English name for the given name ID

Priority: Platform 3 (Windows) with language 0x0409 (US English) Fallback: Platform 1 (Mac) with language 0

Parameters:

  • name_id (Integer)

    The name ID to search for

Returns:

  • (String, nil)

    The decoded string or nil if not found



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/fontisan/tables/name.rb', line 169

def english_name(name_id)
  # Check cache first
  return @decoded_names_cache[name_id] if @decoded_names_cache.key?(name_id)

  # Find record (don't decode yet)
  record = find_name_record(
    name_id,
    platform: PLATFORM_WINDOWS,
    language: WINDOWS_LANGUAGE_EN_US,
  )

  record ||= find_name_record(
    name_id,
    platform: PLATFORM_MACINTOSH,
    language: MAC_LANGUAGE_ENGLISH,
  )

  return nil unless record

  # Decode only this one record
  decoded = decode_name_record(record)
  @decoded_names_cache[name_id] = decoded
  decoded
end

#family_name_present?Boolean

Validation helper: Check if family name is present and non-empty

Returns:

  • (Boolean)

    True if family name exists and is not empty



254
255
256
257
# File 'lib/fontisan/tables/name.rb', line 254

def family_name_present?
  name = english_name(FAMILY)
  !name.nil? && !name.empty?
end

#has_valid_platform_combos?(*combos) ⇒ Boolean

Validation helper: Check if required platform combinations exist

Examples:

Check for Windows English name

name.has_valid_platform_combos?([3, 1, 0x0409])

Parameters:

  • combos (Array<Array<Integer>>)

    Array of [platform_id, encoding_id, language_id] arrays

Returns:

  • (Boolean)

    True if all required combinations are present



241
242
243
244
245
246
247
248
249
# File 'lib/fontisan/tables/name.rb', line 241

def has_valid_platform_combos?(*combos)
  combos.all? do |platform_id, encoding_id, language_id|
    name_records.any? do |rec|
      rec.platform_id == platform_id &&
        rec.encoding_id == encoding_id &&
        rec.language_id == language_id
    end
  end
end

#postscript_name_present?Boolean

Validation helper: Check if PostScript name is present and non-empty

Returns:

  • (Boolean)

    True if PostScript name exists and is not empty



262
263
264
265
# File 'lib/fontisan/tables/name.rb', line 262

def postscript_name_present?
  name = english_name(POSTSCRIPT_NAME)
  !name.nil? && !name.empty?
end

#postscript_name_valid?Boolean

Validation helper: Check if PostScript name is valid

PostScript names must contain only ASCII alphanumerics and hyphens

Returns:

  • (Boolean)

    True if PostScript name matches the required pattern



272
273
274
275
276
277
# File 'lib/fontisan/tables/name.rb', line 272

def postscript_name_valid?
  name = english_name(POSTSCRIPT_NAME)
  return false if name.nil? || name.empty?

  name.match?(/^[A-Za-z0-9-]+$/)
end

#valid?Boolean

Validate the table

Returns:

  • (Boolean)

    True if the table is valid



197
198
199
200
201
# File 'lib/fontisan/tables/name.rb', line 197

def valid?
  !format.nil?
rescue StandardError
  false
end

#valid_encoding_heuristics?Boolean

Validation helper: Check if encoding combinations are valid

According to OpenType spec, certain platform/encoding combinations are valid:

  • Platform 0 (Unicode): encoding 0-6
  • Platform 1 (Mac): encoding 0-32
  • Platform 3 (Windows): encoding 0-10

Returns:

  • (Boolean)

    True if all encoding heuristics are valid



218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/fontisan/tables/name.rb', line 218

def valid_encoding_heuristics?
  name_records.all? do |rec|
    case rec.platform_id
    when PLATFORM_UNICODE
      rec.encoding_id.between?(0, 6)
    when PLATFORM_MACINTOSH
      rec.encoding_id.between?(0, 32)
    when PLATFORM_WINDOWS
      rec.encoding_id.between?(0, 10)
    else
      # Unknown platform - consider invalid
      false
    end
  end
end

#valid_version?Boolean

Validation helper: Check if version is valid (0 or 1)

Returns:

  • (Boolean)

    True if version is 0 or 1



206
207
208
# File 'lib/fontisan/tables/name.rb', line 206

def valid_version?
  [0, 1].include?(format)
end