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