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_binaryString (readonly)

Returns Encrypted portion as binary data.

Returns:

  • (String)

    Encrypted portion as binary data



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

def encrypted_binary
  @encrypted_binary
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)



43
44
45
# File 'lib/fontisan/type1/pfa_parser.rb', line 43

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



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

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

#parse(data) ⇒ PFAParser

Parse PFA format data

Handles both standard PFA (hex-encoded encrypted data with zero marker) and .t1 format (binary encrypted data without zero marker).

Parameters:

  • data (String)

    ASCII PFA data or .t1 format data

Returns:

Raises:

  • (ArgumentError)

    If data is nil or empty

  • (Fontisan::Error)

    If PFA format is invalid



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
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/fontisan/type1/pfa_parser.rb', line 54

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 = ""
    @encrypted_binary = ""
    @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 (optional for .t1 format)
  zero_index = encrypted_data.index(ZERO_MARKER)

  if zero_index
    # Standard PFA format with zero marker
    # Extract encrypted hex data (before zeros)
    @encrypted_hex = encrypted_data[0...zero_index].strip
    @encrypted_binary = [@encrypted_hex.gsub(/\s/, "")].pack("H*")

    # 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
  else
    # .t1 format - binary encrypted data without zero marker
    # Treat everything after eexec as binary encrypted data
    @encrypted_binary = encrypted_data.lstrip
    @encrypted_hex = @encrypted_binary.unpack1("H*")
    @trailing_text = ""
  end

  self
end

#parsed?Boolean

Check if parser has parsed data

Returns:

  • (Boolean)

    True if data has been parsed



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

def parsed?
  !@clear_text.nil?
end