# File lib/pdf/writer/fontmetrics.rb, line 39
 39:   def self.open(font_name)
 40:     file  = font_name.gsub(/\\/o, "/")
 41:     dir   = File.dirname(file)
 42:     name  = File.basename(file)
 43: 
 44:     metrics_path = []
 45: 
 46:       # Check to see if the directory is "." or a non-path
 47:     if dir == "."
 48:       metrics_path << dir << METRICS_PATH << $LOAD_PATH
 49:     elsif dir !~ %r{^(\w:|/)}o and dir.index("/")
 50:       METRICS_PATH.each { |path| metrics_path << File.join(path, dir) }
 51:       $LOAD_PATH.each { |path| metrics_path << File.join(path, dir) }
 52:     else
 53:       metric_path = [ dir ]
 54:     end
 55:     metrics_path.flatten!
 56: 
 57:     font = nil
 58:     afm = nil
 59: 
 60:     metrics_path.each do |path|
 61:       afm_file  = File.join(path, "#{name}.afm").gsub(/\.afm\.afm$/o, ".afm")
 62:       rfm_file  = "#{afm_file}.rfm"
 63: 
 64:         # Attempt to unmarshal an .afm.rfm file first. If it is loaded,
 65:         # we're in good shape.
 66:       begin
 67:         if File.exists?(rfm_file)
 68:           data = File.open(rfm_file, "rb") { |file| file.read }
 69:           font = Marshal.load(data)
 70:           return font
 71:         end
 72:       rescue
 73:         nil
 74:       end
 75: 
 76:         # Attempt to open and process the font.
 77:       File.open(afm_file, "rb") do |file|
 78:         font = PDF::Writer::FontMetrics.new
 79: 
 80:           # An AFM file contains key names followed by valuees.
 81:         file.each do |line|
 82:           line.chomp!
 83:           line.strip!
 84:           key, *values = line.split
 85:           op = "#{key.downcase}=".to_sym
 86: 
 87:             # I probably need to deal with MetricsSet. The default value is
 88:             # 0, which is writing direction 0 (W0X). If MetricsSet 1 is
 89:             # specified, then only writing direction 1 is present (W1X). If
 90:             # MetricsSet 2 is specified, then both W0X and W1X are present.
 91: 
 92:             # Measurements are always 1/1000th of a scale factor (point
 93:             # size). So a 12pt character with a width of 222 is going to be
 94:             # 222 * 12 / 1000 or 2.664 points wide.
 95:           case key
 96:           when 'FontName', 'FullName', 'FamilyName', 'Weight',
 97:             'IsFixedPitch', 'CharacterSet', 'Version', 'EncodingScheme'
 98:               # These values are string values.
 99:             font.__send__(op, values.join(" "))
100:           when 'ItalicAngle', 'UnderlinePosition', 'UnderlineThickness',
101:             'CapHeight', 'XHeight', 'Ascender', 'Descender', 'StdHW',
102:             'StdVW', 'StartCharMetrics'
103:               # These values are floating point values.
104:             font.__send__(op, values.join(" ").to_f)
105:           when 'FontBBox'
106:               # These values are an array of floating point values
107:             font.fontbbox = values.map { |el| el.to_f }
108:           when 'C', 'CH'
109:               # Individual Character Metrics Values:
110:               #   C  <character number>
111:               #   CH <hex character number>
112:               #     One of C or CH must be provided. Specifies the encoding
113:               #     number for the character. -1 if the character is not
114:               #     encoded in the font.
115:               #
116:               #   WX  <x width number>
117:               #   W0X <x0 width number>
118:               #   W1X <x1 width number>
119:               #     Character width in x for writing direction 0 (WX, W0X)
120:               #     or 1 (W1X) where y is 0. Optional.
121:               #
122:               #   WY  <y width number>
123:               #   W0Y <y0 width number>
124:               #   W1Y <y1 width number>
125:               #     Character width in y for writing direction 0 (WY, W0Y)
126:               #     or 1 (W1Y) where x is 0. Optional.
127:               #
128:               #   W  <x width> <y width>
129:               #   W0 <x0 width> <y0 width>
130:               #   W1 <x1 width> <y1 width>
131:               #     Character width in x, y for writing direction 0 (W, W0)
132:               #     or 1 (W1). Optional.
133:               #
134:               #   VV <x number> <y number>
135:               #     Same as VVector in the global font definition, but for
136:               #     this single character. Optional.
137:               #
138:               #   N <name>
139:               #     The PostScript name of the font. Optional.
140:               #   
141:               #   B <llx> <lly> <urx> <ury>
142:               #     Character bounding box for the lower left corner and the
143:               #     upper right corner. Optional.
144:               #
145:               #   L <sucessor> <ligature>
146:               #     Ligature sequence where both <successor> and <ligature>
147:               #     are N <names>. For the fragment "N f; L i fi; L l fl",
148:               #     two ligatures are defined: fi and fl. Optional,
149:               #     multiples permitted.
150:               #
151:               # C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ;
152:             bits = line.chomp.strip.split(/;/).collect { |r| r.strip }
153:             dtmp = {}
154: 
155:             bits.each do |bit|
156:               b = bit.split
157:               if b.size > 2
158:                 dtmp[b[0]] = []
159:                 b[1..-1].each do |z|
160:                   if z =~ NUMBER
161:                     dtmp[b[0]] << z.to_f
162:                   else
163:                     dtmp[b[0]] << z
164:                   end
165:                 end
166:               elsif b.size == 2
167:                 if b[0] == 'C' and b[1] =~ NUMBER
168:                   dtmp[b[0]] = b[1].to_i
169:                 elsif b[0] == 'CH'
170:                   dtmp['C'] = b[1].to_i(16)
171:                 elsif b[1] =~ NUMBER
172:                   dtmp[b[0]] = b[1].to_f
173:                 else
174:                   dtmp[b[0]] = b[1]
175:                 end
176:               end
177:             end
178: 
179:             font.c[dtmp['N']] = dtmp
180:             font.c[dtmp['C']] = dtmp unless dtmp['C'].nil?
181:           when 'KPX' # KPX Adieresis yacute -40
182:             # KPX: Kerning Pair
183:             font.kpx[values[0]] = { }
184:             font.kpx[values[0]][values[1]] = values[2].to_f
185:           end
186:         end
187:         font.path = afm_file
188:       end rescue nil # Ignore file errors
189:       break unless font.nil?
190:     end
191: 
192:     raise ArgumentError, "Font #{font_name} not found." if font.nil?
193:     font
194:   end