Class | PDF::Writer::FontMetrics |
In: |
lib/pdf/writer/fontmetrics.rb
|
Parent: | Object |
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 |
differences | [RW] | |
encoding | [RW] | |
font_num | [RW] | |
path | [RW] |
# 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.
# 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