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



109
110
111
# File 'lib/fontisan/tables/name.rb', line 109

def decoded_names_cache
  @decoded_names_cache
end

Instance Method Details

#after_read_hookObject

Hook that gets called after all fields are read



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

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



126
127
128
# File 'lib/fontisan/tables/name.rb', line 126

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.



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

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



118
119
120
121
# File 'lib/fontisan/tables/name.rb', line 118

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



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

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



251
252
253
254
# File 'lib/fontisan/tables/name.rb', line 251

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



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

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



259
260
261
262
# File 'lib/fontisan/tables/name.rb', line 259

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



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

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



194
195
196
197
198
# File 'lib/fontisan/tables/name.rb', line 194

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



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

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



203
204
205
# File 'lib/fontisan/tables/name.rb', line 203

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