Class: Fontisan::Tables::Name

Inherits:
Binary::BaseRecord show all
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

Instance Method Summary collapse

Methods inherited from Binary::BaseRecord

#raw_data, read

Instance Attribute Details

#decoded_names_cacheObject

Cache for decoded names



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

def decoded_names_cache
  @decoded_names_cache
end

Instance Method Details

#after_read_hookObject

Hook that gets called after all fields are read



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

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



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

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.



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

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



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

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



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

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



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

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



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

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



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

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



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

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



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

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



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

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



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

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