Class: Fontisan::Type1::PFAParser

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

Overview

Parser for PFA (Printer Font ASCII) format

[‘PFAparser`](lib/fontisan/type1/pfa_parser.rb) parses the ASCII PFA format used for storing Adobe Type 1 fonts, primarily on Unix/Linux systems.

The PFA format is pure ASCII text with encrypted portions marked by ‘currentfile eexec` and terminated by 512 ASCII zeros.

Format structure:

  • Clear text: Font dictionary and initial data

  • Encrypted portion: Starts with ‘currentfile eexec`

  • Encrypted data: Binary data encoded as hexadecimal

  • End marker: 512 ASCII zeros (‘0’)

  • Cleartext again: Font dictionary closing

Examples:

Parse a PFA file

parser = Fontisan::Type1::PFAparser.new
result = parser.parse(File.read('font.pfa'))
puts result.clear_text    # => "!PS-AdobeFont-1.0..."
puts result.encrypted_hex # => Encrypted hex string

See Also:

Constant Summary collapse

EEXEC_MARKER =

Markers in PFA format

"currentfile eexec"
ZERO_MARKER =

512 ASCII zeros mark the end of encrypted portion

"0" * 512

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#clear_textString (readonly)

Returns Clear text portion (before eexec).

Returns:

  • (String)

    Clear text portion (before eexec)



34
35
36
# File 'lib/fontisan/type1/pfa_parser.rb', line 34

def clear_text
  @clear_text
end

#encrypted_hexString (readonly)

Returns Encrypted portion as hex string.

Returns:

  • (String)

    Encrypted portion as hex string



37
38
39
# File 'lib/fontisan/type1/pfa_parser.rb', line 37

def encrypted_hex
  @encrypted_hex
end

#trailing_textString (readonly)

Returns Trailing text after zeros (if any).

Returns:

  • (String)

    Trailing text after zeros (if any)



40
41
42
# File 'lib/fontisan/type1/pfa_parser.rb', line 40

def trailing_text
  @trailing_text
end

Class Method Details

.pfa_file?(data) ⇒ Boolean

Check if this appears to be a PFA file

Examples:

Check if file is PFA format

if Fontisan::Type1::PFAParser.pfa_file?(data)
  # Handle PFA format
end

Parameters:

  • data (String)

    Text data to check

Returns:

  • (Boolean)

    True if data appears to be PFA format



114
115
116
117
118
119
120
121
# File 'lib/fontisan/type1/pfa_parser.rb', line 114

def self.pfa_file?(data)
  return false if data.nil?
  return false if data.length < 15

  # Check for Adobe Type 1 font header
  data.include?("%!PS-AdobeFont-1.0") ||
    data.include?("%!PS-Adobe-3.0 Resource-Font")
end

Instance Method Details

#encrypted_binaryString

Get encrypted hex as binary data

Decodes the hexadecimal encrypted portion to binary.

Returns:

  • (String)

    Binary encrypted data



128
129
130
131
132
133
# File 'lib/fontisan/type1/pfa_parser.rb', line 128

def encrypted_binary
  return "" if @encrypted_hex.nil? || @encrypted_hex.empty?

  # Convert hex string to binary
  [@encrypted_hex.gsub(/\s/, "")].pack("H*")
end

#parse(data) ⇒ PFAParser

Parse PFA format data

Parameters:

  • data (String)

    ASCII PFA data

Returns:

Raises:

  • (ArgumentError)

    If data is nil or empty

  • (Fontisan::Error)

    If PFA format is invalid



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/fontisan/type1/pfa_parser.rb', line 48

def parse(data)
  raise ArgumentError, "Data cannot be nil" if data.nil?
  raise ArgumentError, "Data cannot be empty" if data.empty?

  # Normalize line endings
  data = normalize_line_endings(data)

  # Find eexec marker
  eexec_index = data.index(EEXEC_MARKER)
  if eexec_index.nil?
    # No eexec marker - entire file is clear text
    @clear_text = data
    @encrypted_hex = ""
    @trailing_text = ""
    return self
  end

  # Clear text is everything before and including eexec marker
  @clear_text = data[0...eexec_index + EEXEC_MARKER.length]

  # Look for zeros after eexec marker
  after_eexec = data[eexec_index + EEXEC_MARKER.length..]

  # Skip whitespace after eexec marker
  encrypted_start = skip_whitespace(after_eexec, 0)
  encrypted_data = after_eexec[encrypted_start..]

  # Find zero marker
  zero_index = encrypted_data.index(ZERO_MARKER)
  if zero_index.nil?
    raise Fontisan::Error,
          "Invalid PFA: cannot find zero marker after eexec"
  end

  # Extract encrypted hex data (before zeros)
  @encrypted_hex = encrypted_data[0...zero_index].strip

  # Extract trailing text (after zeros)
  trailing_start = zero_index + ZERO_MARKER.length
  trailing_start = skip_whitespace(encrypted_data, trailing_start)

  @trailing_text = if trailing_start < encrypted_data.length
                     encrypted_data[trailing_start..]
                   else
                     ""
                   end

  self
end

#parsed?Boolean

Check if parser has parsed data

Returns:

  • (Boolean)

    True if data has been parsed



101
102
103
# File 'lib/fontisan/type1/pfa_parser.rb', line 101

def parsed?
  !@clear_text.nil?
end