# 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:           next if key.nil?
 86:           op = "#{key.downcase}=".to_sym
 87: 
 88:             # I probably need to deal with MetricsSet. The default value is
 89:             # 0, which is writing direction 0 (W0X). If MetricsSet 1 is
 90:             # specified, then only writing direction 1 is present (W1X). If
 91:             # MetricsSet 2 is specified, then both W0X and W1X are present.
 92: 
 93:             # Measurements are always 1/1000th of a scale factor (point
 94:             # size). So a 12pt character with a width of 222 is going to be
 95:             # 222 * 12 / 1000 or 2.664 points wide.
 96:           case key
 97:           when 'FontName', 'FullName', 'FamilyName', 'Weight',
 98:             'IsFixedPitch', 'CharacterSet', 'Version', 'EncodingScheme'
 99:               # These values are string values.
100:             font.__send__(op, values.join(" "))
101:           when 'ItalicAngle', 'UnderlinePosition', 'UnderlineThickness',
102:             'CapHeight', 'XHeight', 'Ascender', 'Descender', 'StdHW',
103:             'StdVW', 'StartCharMetrics'
104:               # These values are floating point values.
105:             font.__send__(op, values.join(" ").to_f)
106:           when 'FontBBox'
107:               # These values are an array of floating point values
108:             font.fontbbox = values.map { |el| el.to_f }
109:           when 'C', 'CH'
110:               # Individual Character Metrics Values:
111:               #   C  <character number>
112:               #   CH <hex character number>
113:               #     One of C or CH must be provided. Specifies the encoding
114:               #     number for the character. -1 if the character is not
115:               #     encoded in the font.
116:               #
117:               #   WX  <x width number>
118:               #   W0X <x0 width number>
119:               #   W1X <x1 width number>
120:               #     Character width in x for writing direction 0 (WX, W0X)
121:               #     or 1 (W1X) where y is 0. Optional.
122:               #
123:               #   WY  <y width number>
124:               #   W0Y <y0 width number>
125:               #   W1Y <y1 width number>
126:               #     Character width in y for writing direction 0 (WY, W0Y)
127:               #     or 1 (W1Y) where x is 0. Optional.
128:               #
129:               #   W  <x width> <y width>
130:               #   W0 <x0 width> <y0 width>
131:               #   W1 <x1 width> <y1 width>
132:               #     Character width in x, y for writing direction 0 (W, W0)
133:               #     or 1 (W1). Optional.
134:               #
135:               #   VV <x number> <y number>
136:               #     Same as VVector in the global font definition, but for
137:               #     this single character. Optional.
138:               #
139:               #   N <name>
140:               #     The PostScript name of the font. Optional.
141:               #   
142:               #   B <llx> <lly> <urx> <ury>
143:               #     Character bounding box for the lower left corner and the
144:               #     upper right corner. Optional.
145:               #
146:               #   L <sucessor> <ligature>
147:               #     Ligature sequence where both <successor> and <ligature>
148:               #     are N <names>. For the fragment "N f; L i fi; L l fl",
149:               #     two ligatures are defined: fi and fl. Optional,
150:               #     multiples permitted.
151:               #
152:               # C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ;
153:             bits = line.chomp.strip.split(/;/).collect { |r| r.strip }
154:             dtmp = {}
155: 
156:             bits.each do |bit|
157:               b = bit.split
158:               if b.size > 2
159:                 dtmp[b[0]] = []
160:                 b[1..-1].each do |z|
161:                   if z =~ NUMBER
162:                     dtmp[b[0]] << z.to_f
163:                   else
164:                     dtmp[b[0]] << z
165:                   end
166:                 end
167:               elsif b.size == 2
168:                 if b[0] == 'C' and b[1] =~ NUMBER
169:                   dtmp[b[0]] = b[1].to_i
170:                 elsif b[0] == 'CH'
171:                   dtmp['C'] = b[1].to_i(16)
172:                 elsif b[1] =~ NUMBER
173:                   dtmp[b[0]] = b[1].to_f
174:                 else
175:                   dtmp[b[0]] = b[1]
176:                 end
177:               end
178:             end
179: 
180:             font.c[dtmp['N']] = dtmp
181:             font.c[dtmp['C']] = dtmp unless dtmp['C'].nil?
182:           when 'KPX' # KPX Adieresis yacute -40
183:             # KPX: Kerning Pair
184:             font.kpx[values[0]] = { }
185:             font.kpx[values[0]][values[1]] = values[2].to_f
186:           end
187:         end
188:         font.path = afm_file
189:       end rescue nil # Ignore file errors
190:       break unless font.nil?
191:     end
192: 
193:     raise ArgumentError, "Font #{font_name} not found." if font.nil?
194:     font
195:   end