Class: HexaPDF::Font::Type1::AFMParser

Inherits:
Object
  • Object
show all
Defined in:
lib/hexapdf/font/type1/afm_parser.rb

Overview

Parses files in the AFM file format.

Note that this implementation isn’t a full AFM parser, only what is needed for parsing the AFM files for the 14 PDF core fonts is implemented. However, if need be it should be adaptable to other AFM files.

For information on the AFM file format have a look at Adobe technical note #5004 - Adobe Font Metrics File Format Specification Version 4.1, available at the Adobe website.

How Parsing Works

AFM is a line oriented format. Each line consists of one or more values of supported types (string, name, number, integer, array, boolean) which are separated by whitespace characters (space, newline, tab) except for the string type which just uses everything until the end of the line.

This parser reads in line by line and the type parsing functions parse a value from the front of the line and then remove the parsed part from the line, including trailing whitespace characters.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(io) ⇒ AFMParser

Creates a new parse for the given IO stream.



79
80
81
# File 'lib/hexapdf/font/type1/afm_parser.rb', line 79

def initialize(io)
  @io = io
end

Class Method Details

.parse(source) ⇒ Object

:call-seq:

Parser.parse(filename)       -> font_metrics
Parser.parse(io)             -> font_metrics

Parses the IO or file and returns a FontMetrics object.



70
71
72
73
74
75
76
# File 'lib/hexapdf/font/type1/afm_parser.rb', line 70

def self.parse(source)
  if source.respond_to?(:read)
    new(source).parse
  else
    File.open(source) {|file| new(file).parse }
  end
end

Instance Method Details

#parseObject

Parses the AFM file and returns a FontMetrics object.



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
110
111
112
113
# File 'lib/hexapdf/font/type1/afm_parser.rb', line 84

def parse
  @metrics = FontMetrics.new
  sections = []
  each_line do
    case (command = parse_name)
    when /\AStart/
      sections.push(command)
      case command
      when 'StartCharMetrics' then parse_character_metrics
      when 'StartKernPairs' then parse_kerning_pairs
      end
    when /\AEnd/
      sections.pop
      break if sections.empty? && command == 'EndFontMetrics'
    else
      if sections.empty?
        parse_global_font_information(command.to_sym)
      end
    end
  end

  if @metrics.bounding_box && !@metrics.descender
    @metrics.descender = @metrics.bounding_box[1]
  end
  if @metrics.bounding_box && !@metrics.ascender
    @metrics.ascender = @metrics.bounding_box[3]
  end

  @metrics
end