lib/prawn/font/afm.rb in prawn-0.15.0 vs lib/prawn/font/afm.rb in prawn-1.0.0.rc1
- old
+ new
@@ -4,17 +4,14 @@
#
# Copyright May 2008, Gregory Brown / James Healy. All Rights Reserved.
#
# This is free software. Please see the LICENSE and COPYING files for details.
-require_relative '../../prawn/encoding'
+require 'prawn/encoding'
module Prawn
class Font
-
- # @private
-
class AFM < Font
BUILT_INS = %w[ Courier Helvetica Times-Roman Symbol ZapfDingbats
Courier-Bold Courier-Oblique Courier-BoldOblique
Times-Bold Times-Italic Times-BoldItalic
Helvetica-Bold Helvetica-Oblique Helvetica-BoldOblique ]
@@ -28,12 +25,12 @@
@metrics_path ||= m.split(':')
else
@metrics_path ||= [
".", "/usr/lib/afm",
"/usr/local/lib/afm",
- "/usr/openwin/lib/fonts/afm",
- Prawn::DATADIR+'/fonts']
+ "/usr/openwin/lib/fonts/afm/",
+ Prawn::BASEDIR+'/data/fonts/']
end
end
attr_reader :attributes #:nodoc:
@@ -42,24 +39,20 @@
raise Prawn::Errors::UnknownFont, "#{name} is not a known font."
end
super
- @@winansi ||= Prawn::Encoding::WinAnsi.new # parse data/encodings/win_ansi.txt once only
- @@font_data ||= SynchronizedCache.new # parse each ATM font file once only
+ @attributes = {}
+ @glyph_widths = {}
+ @bounding_boxes = {}
+ @kern_pairs = {}
file_name = @name.dup
file_name << ".afm" unless file_name =~ /\.afm$/
file_name = file_name[0] == ?/ ? file_name : find_font(file_name)
- font_data = @@font_data[file_name] ||= parse_afm(file_name)
- @glyph_widths = font_data[:glyph_widths]
- @glyph_table = font_data[:glyph_table]
- @bounding_boxes = font_data[:bounding_boxes]
- @kern_pairs = font_data[:kern_pairs]
- @kern_pair_table = font_data[:kern_pair_table]
- @attributes = font_data[:attributes]
+ parse_afm(file_name)
@ascender = @attributes["ascender"].to_i
@descender = @attributes["descender"].to_i
@line_gap = Float(bbox[3] - bbox[1]) - (@ascender - @descender)
end
@@ -92,11 +85,11 @@
# built-in fonts only work with winansi encoding, so translate the
# string. Changes the encoding in-place, so the argument itself
# is replaced with a string in WinAnsi encoding.
#
def normalize_encoding(text)
- enc = @@winansi
+ enc = Prawn::Encoding::WinAnsi.new
text.unpack("U*").collect { |i| enc[i] }.pack("C*")
rescue ArgumentError
raise Prawn::Errors::IncompatibleStringEncoding,
"Arguments to text methods must be UTF-8 encoded"
end
@@ -155,11 +148,10 @@
"Couldn't find the font: #{file} in any of:\n" +
self.class.metrics_path.join("\n")
end
def parse_afm(file_name)
- data = {:glyph_widths => {}, :bounding_boxes => {}, :kern_pairs => {}, :attributes => {}}
section = []
File.foreach(file_name) do |line|
case line
when /^Start(\w+)/
@@ -172,45 +164,31 @@
case section
when ["FontMetrics", "CharMetrics"]
next unless line =~ /^CH?\s/
- name = line[/\bN\s+(\.?\w+)\s*;/, 1]
- data[:glyph_widths][name] = line[/\bWX\s+(\d+)\s*;/, 1].to_i
- data[:bounding_boxes][name] = line[/\bB\s+([^;]+);/, 1].to_s.rstrip
+ name = line[/\bN\s+(\.?\w+)\s*;/, 1]
+ @glyph_widths[name] = line[/\bWX\s+(\d+)\s*;/, 1].to_i
+ @bounding_boxes[name] = line[/\bB\s+([^;]+);/, 1].to_s.rstrip
when ["FontMetrics", "KernData", "KernPairs"]
next unless line =~ /^KPX\s+(\.?\w+)\s+(\.?\w+)\s+(-?\d+)/
- data[:kern_pairs][[$1, $2]] = $3.to_i
+ @kern_pairs[[$1, $2]] = $3.to_i
when ["FontMetrics", "KernData", "TrackKern"],
["FontMetrics", "Composites"]
next
else
- parse_generic_afm_attribute(line, data)
+ parse_generic_afm_attribute(line)
end
end
-
- # process data parsed from AFM file to build tables which
- # will be used when measuring and kerning text
- data[:glyph_table] = (0..255).map do |i|
- data[:glyph_widths][Encoding::WinAnsi::CHARACTERS[i]].to_i
- end
-
- character_hash = Hash[Encoding::WinAnsi::CHARACTERS.zip((0..Encoding::WinAnsi::CHARACTERS.size).to_a)]
- data[:kern_pair_table] = data[:kern_pairs].inject({}) do |h,p|
- h[p[0].map { |n| character_hash[n] }] = p[1]
- h
- end
-
- data.each_value { |hash| hash.freeze }
- data.freeze
end
- def parse_generic_afm_attribute(line, hash)
+ def parse_generic_afm_attribute(line)
line =~ /(^\w+)\s+(.*)/
key, value = $1.to_s.downcase, $2
- hash[:attributes][key] = hash[:attributes][key] ? Array(hash[:attributes][key]) << value : value
+ @attributes[key] = @attributes[key] ?
+ Array(@attributes[key]) << value : value
end
# converts a string into an array with spacing offsets
# bewteen characters that need to be kerned
#
@@ -218,29 +196,49 @@
#
def kern(string)
kerned = [[]]
last_byte = nil
- string.each_byte do |byte|
- if k = last_byte && @kern_pair_table[[last_byte, byte]]
+ kern_pairs = latin_kern_pairs_table
+
+ string.unpack("C*").each do |byte|
+ if k = last_byte && kern_pairs[[last_byte, byte]]
kerned << -k << [byte]
else
kerned.last << byte
- end
+ end
last_byte = byte
end
- kerned.map { |e|
+ kerned.map { |e|
e = (Array === e ? e.pack("C*") : e)
- e.respond_to?(:force_encoding) ? e.force_encoding(::Encoding::Windows_1252) : e
+ e.respond_to?(:force_encoding) ? e.force_encoding("Windows-1252") : e
}
end
+
+ def latin_kern_pairs_table
+ return @kern_pairs_table if defined?(@kern_pairs_table)
+
+ character_hash = Hash[Encoding::WinAnsi::CHARACTERS.zip((0..Encoding::WinAnsi::CHARACTERS.size).to_a)]
+ @kern_pairs_table = @kern_pairs.inject({}) do |h,p|
+ h[p[0].map { |n| character_hash[n] }] = p[1]
+ h
+ end
+ end
+
+ def latin_glyphs_table
+ @glyphs_table ||= (0..255).map do |i|
+ @glyph_widths[Encoding::WinAnsi::CHARACTERS[i]].to_i
+ end
+ end
private
def unscaled_width_of(string)
- string.bytes.inject(0) do |s,r|
- s + @glyph_table[r]
+ glyph_table = latin_glyphs_table
+
+ string.unpack("C*").inject(0) do |s,r|
+ s + glyph_table[r]
end
end
end
end
end