Class: Fontisan::Type1Font

Inherits:
Object
  • Object
show all
Defined in:
lib/fontisan/type1_font.rb

Overview

Adobe Type 1 Font handler

[‘Type1Font`](lib/fontisan/type1_font.rb) provides parsing and conversion capabilities for Adobe Type 1 fonts in PFB (Printer Font Binary) and PFA (Printer Font ASCII) formats.

Type 1 fonts were the standard for digital typography in the 1980s-1990s and consist of:

  • Font dictionary with metadata (FontInfo, FontName, Encoding, etc.)

  • Private dictionary with hinting and spacing information

  • CharStrings (glyph outline descriptions)

  • eexec encryption for protection

Examples:

Load a PFB file

font = Fontisan::Type1Font.from_file('font.pfb')
puts font.font_name
puts font.version

Load a PFA file

font = Fontisan::Type1Font.from_file('font.pfa')
puts font.full_name

Access decrypted font data

font = Fontisan::Type1Font.from_file('font.pfb')
puts font.decrypted_data

See Also:

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(data, format: nil, file_path: nil) ⇒ Type1Font

Initialize a new Type1Font instance

Parameters:

  • data (String)

    Font file data (binary or text)

  • format (Symbol) (defaults to: nil)

    Format type (:pfb or :pfa, auto-detected if nil)

  • file_path (String, nil) (defaults to: nil)

    Optional file path for reference



57
58
59
60
61
62
63
# File 'lib/fontisan/type1_font.rb', line 57

def initialize(data, format: nil, file_path: nil)
  @file_path = file_path
  @format = format || detect_format(data)
  @data = data

  parse_font_data
end

Instance Attribute Details

#charstringsCharStrings? (readonly)

Returns CharStrings dictionary.

Returns:

  • (CharStrings, nil)

    CharStrings dictionary



50
51
52
# File 'lib/fontisan/type1_font.rb', line 50

def charstrings
  @charstrings
end

#decrypted_dataString? (readonly)

Returns Decrypted font data.

Returns:

  • (String, nil)

    Decrypted font data



41
42
43
# File 'lib/fontisan/type1_font.rb', line 41

def decrypted_data
  @decrypted_data
end

#file_pathString? (readonly)

Returns File path if loaded from file.

Returns:

  • (String, nil)

    File path if loaded from file



35
36
37
# File 'lib/fontisan/type1_font.rb', line 35

def file_path
  @file_path
end

#font_dictionaryFontDictionary? (readonly)

Returns Font dictionary.

Returns:

  • (FontDictionary, nil)

    Font dictionary



44
45
46
# File 'lib/fontisan/type1_font.rb', line 44

def font_dictionary
  @font_dictionary
end

#formatSymbol (readonly)

Returns Format type (:pfb or :pfa).

Returns:

  • (Symbol)

    Format type (:pfb or :pfa)



38
39
40
# File 'lib/fontisan/type1_font.rb', line 38

def format
  @format
end

#private_dictPrivateDict? (readonly)

Returns Private dictionary.

Returns:

  • (PrivateDict, nil)

    Private dictionary



47
48
49
# File 'lib/fontisan/type1_font.rb', line 47

def private_dict
  @private_dict
end

Class Method Details

.from_file(file_path) ⇒ Type1Font

Load Type 1 font from file

Examples:

Load PFB file

font = Fontisan::Type1Font.from_file('font.pfb')

Load PFA file

font = Fontisan::Type1Font.from_file('font.pfa')

Parameters:

  • file_path (String)

    Path to PFB or PFA file

Returns:

Raises:

  • (ArgumentError)

    If file_path is nil

  • (Fontisan::Error)

    If file cannot be read or parsed



77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/fontisan/type1_font.rb', line 77

def self.from_file(file_path)
  raise ArgumentError, "File path cannot be nil" if file_path.nil?

  unless File.exist?(file_path)
    raise Fontisan::Error, "File not found: #{file_path}"
  end

  # Read file
  data = File.binread(file_path)

  new(data, file_path: file_path)
end

Instance Method Details

#clear_textString

Get clear text portion (before eexec)

Returns:

  • (String)

    Clear text font dictionary



93
94
95
# File 'lib/fontisan/type1_font.rb', line 93

def clear_text
  @clear_text ||= ""
end

#decrypt!String

Decrypt the font if not already decrypted

Returns:

  • (String)

    Decrypted font data



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/fontisan/type1_font.rb', line 149

def decrypt!
  return @decrypted_data if decrypted?

  if @encrypted_portion.nil? || @encrypted_portion.empty?
    @decrypted_data = @clear_text
  else
    encrypted_binary = if @format == :pfa
                         # Convert hex string to binary
                         [@encrypted_portion.gsub(/\s/, "")].pack("H*")
                       else
                         @encrypted_portion
                       end

    @decrypted_data = @clear_text +
      Type1::Decryptor.eexec_decrypt(encrypted_binary)
  end

  @decrypted_data
end

#decrypted?Boolean

Check if font has been decrypted

Returns:

  • (Boolean)

    True if font data has been decrypted



135
136
137
# File 'lib/fontisan/type1_font.rb', line 135

def decrypted?
  !@decrypted_data.nil?
end

#encrypted?Boolean

Check if font is encrypted

Returns:

  • (Boolean)

    True if font has eexec encrypted portion



142
143
144
# File 'lib/fontisan/type1_font.rb', line 142

def encrypted?
  !@encrypted_portion.nil? && !@encrypted_portion.empty?
end

#encrypted_portionString

Get encrypted portion (as hex string for PFA)

Returns:

  • (String)

    Encrypted portion



100
101
102
# File 'lib/fontisan/type1_font.rb', line 100

def encrypted_portion
  @encrypted_portion ||= ""
end

#family_nameString?

Get family name from FontInfo

Returns:

  • (String, nil)

    Family name or nil if not found



121
122
123
# File 'lib/fontisan/type1_font.rb', line 121

def family_name
  extract_fontinfo_value("FamilyName")
end

#font_nameString?

Get font name from font dictionary

Returns:

  • (String, nil)

    Font name or nil if not found



107
108
109
# File 'lib/fontisan/type1_font.rb', line 107

def font_name
  extract_dictionary_value("/FontName")
end

#full_nameString?

Get full name from FontInfo

Returns:

  • (String, nil)

    Full name or nil if not found



114
115
116
# File 'lib/fontisan/type1_font.rb', line 114

def full_name
  extract_fontinfo_value("FullName")
end

#glyph_namesArray<String>

Get list of glyph names

Returns:

  • (Array<String>)

    Glyph names



227
228
229
230
231
# File 'lib/fontisan/type1_font.rb', line 227

def glyph_names
  return [] unless @charstrings

  @charstrings.glyph_names
end

#parse_dictionaries!void

This method returns an undefined value.

Parse font dictionaries from decrypted data

Parses the font dictionary, private dictionary, and CharStrings from the decrypted font data.



175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/fontisan/type1_font.rb', line 175

def parse_dictionaries!
  decrypt! unless decrypted?

  # Parse font dictionary
  @font_dictionary = Type1::FontDictionary.parse(@decrypted_data)

  # Parse private dictionary
  @private_dict = Type1::PrivateDict.parse(@decrypted_data)

  # Parse CharStrings
  @charstrings = Type1::CharStrings.parse(@decrypted_data, @private_dict)
end

#parsed_dictionaries?Boolean

Check if dictionaries have been parsed

Returns:

  • (Boolean)

    True if dictionaries have been parsed



236
237
238
# File 'lib/fontisan/type1_font.rb', line 236

def parsed_dictionaries?
  !@font_dictionary.nil?
end

#versionString?

Get version from FontInfo

Returns:

  • (String, nil)

    Version or nil if not found



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

def version
  extract_fontinfo_value("version")
end