Class PDF::Writer::FontMetrics
In: lib/pdf/writer/fontmetrics.rb
Parent: Object
Transaction::Simple SimpleTable TechBook Complex Action Procset FontDescriptor FontEncoding Destination Info Catalog Encryption Contents Pages Outline Outlines Annotation Page Font ViewerPreferences Image Hash OHash QuickRef FontMetrics ARC4 StrokeStyle PolygonPoint ImageInfo StdDev lib/pdf/simpletable.rb lib/pdf/techbook.rb lib/pdf/writer.rb lib/pdf/quickref.rb Math lib/pdf/writer/fontmetrics.rb lib/pdf/writer/ohash.rb lib/pdf/writer/arc4.rb lib/pdf/writer/strokestyle.rb lib/pdf/writer/graphics.rb lib/pdf/writer/object.rb lib/pdf/writer/object/image.rb External EN Lang OffsetReader lib/pdf/writer/graphics/imageinfo.rb Graphics lib/pdf/writer/object/outlines.rb lib/pdf/writer/object/destination.rb lib/pdf/writer/object/viewerpreferences.rb lib/pdf/writer/object/fontencoding.rb lib/pdf/writer/object/page.rb lib/pdf/writer/object/contents.rb lib/pdf/writer/object/procset.rb lib/pdf/writer/object/pages.rb lib/pdf/writer/object/info.rb lib/pdf/writer/object/encryption.rb lib/pdf/writer/object/catalog.rb lib/pdf/writer/object/outline.rb lib/pdf/writer/object/fontdescriptor.rb lib/pdf/writer/object/action.rb lib/pdf/writer/object/font.rb lib/pdf/writer/object/annotation.rb Object Writer lib/pdf/charts/stddev.rb Charts PDF dot/m_33_0.png

Methods

new   open   save_as_rfm  

Constants

METRICS_PATH = [ File.join(File.dirname(File.expand_path(__FILE__)), 'fonts') ]
KEYS = %w{FontName FullName FamilyName Weight ItalicAngle IsFixedPitch CharacterSet UnderlinePosition UnderlineThickness Version EncodingScheme CapHeight XHeight Ascender Descender StdHW StdVW StartCharMetrics FontBBox C KPX}
NUMBER = /^[+\-0-9.]+$/o

Attributes

differences  [RW] 
encoding  [RW] 
font_num  [RW] 
path  [RW] 

Public Class methods

[Source]

    # File lib/pdf/writer/fontmetrics.rb, line 21
21:   def initialize
22:     @kpx      = {}
23:     @c        = {}
24:     @version  = 1
25:     @font_num = nil
26:   end

Open the font file and return a PDF::Writer::FontMetrics object containing it. The font_name may specify just a font file or a full path. If a path is specified, that is the only place where the font file will be looked for.

[Source]

     # 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

Public Instance methods

Save the loaded font metrics file as a binary marshaled value.

[Source]

     # File lib/pdf/writer/fontmetrics.rb, line 198
198:   def save_as_rfm
199:     rfm = File.basename(@path).gsub(/\.afm.*$/, '')
200:     rfm << ".afm.rfm"
201:     File.open(rfm, "wb") { |file| file.write Marshal.dump(self) }
202:   end

[Validate]