class PDF::Writer::FontMetrics
Constants
- KEYS
- METRICS_PATH
- NUMBER
Attributes
differences[RW]
encoding[RW]
font_num[RW]
path[RW]
Public Class Methods
new()
click to toggle source
# File lib/pdf/writer/fontmetrics.rb 21 def initialize 22 @kpx = {} 23 @c = {} 24 @version = 1 25 @font_num = nil 26 end
open(font_name)
click to toggle source
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.
# File lib/pdf/writer/fontmetrics.rb 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_line 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_as_rfm()
click to toggle source
Save the loaded font metrics file as a binary marshaled value.
# File lib/pdf/writer/fontmetrics.rb 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