lib/prawn/font/ttf.rb in prawn-1.0.0.rc2 vs lib/prawn/font/ttf.rb in prawn-1.0.0
- old
+ new
@@ -1,35 +1,37 @@
# encoding: utf-8
# prawn/font/ttf.rb : Implements AFM font support for Prawn
#
-# Copyright May 2008, Gregory Brown / James Healy / Jamis Buck
+# Copyright May 2008, Gregory Brown / James Healy / Jamis Buck
# All Rights Reserved.
#
# This is free software. Please see the LICENSE and COPYING files for details.
require 'ttfunk'
require 'ttfunk/subset_collection'
module Prawn
class Font
+
+ # @private
class TTF < Font
attr_reader :ttf, :subsets
def unicode?
true
end
-
+
def initialize(document, name, options={})
super
@ttf = read_ttf_file
@subsets = TTFunk::SubsetCollection.new(@ttf)
@attributes = {}
- @bounding_boxes = {}
- @char_widths = {}
+ @bounding_boxes = {}
+ @char_widths = {}
@has_kerning_data = @ttf.kerning.exists? && @ttf.kerning.tables.any?
@ascender = Integer(@ttf.ascent * scale_factor)
@descender = Integer(@ttf.descent * scale_factor)
@line_gap = Integer(@ttf.line_gap * scale_factor)
@@ -40,30 +42,30 @@
scale = (options[:size] || size) / 1000.0
if options[:kerning]
kern(string).inject(0) do |s,r|
if r.is_a?(Numeric)
s - r
- else
+ else
r.inject(s) { |s2, u| s2 + character_width_by_code(u) }
end
end * scale
else
string.codepoints.inject(0) do |s,r|
s + character_width_by_code(r)
end * scale
end
- end
-
+ end
+
# The font bbox, as an array of integers
- #
+ #
def bbox
@bbox ||= @ttf.bbox.map { |i| Integer(i * scale_factor) }
end
# Returns true if the font has kerning data, false otherwise
def has_kerning_data?
- @has_kerning_data
+ @has_kerning_data
end
# Perform any changes to the string that need to happen
# before it is rendered to the canvas. Returns an array of
# subset "chunks", where the even-numbered indices are the
@@ -75,11 +77,11 @@
def encode_text(text,options={})
text = text.chomp
if options[:kerning]
last_subset = nil
- kern(text).inject([]) do |result, element|
+ kern(text).inject([]) do |result, element|
if element.is_a?(Numeric)
result.last[1] = [result.last[1]] unless result.last[1].is_a?(Array)
result.last[1] << element
result
else
@@ -100,11 +102,11 @@
end
else
@subsets.encode(text.unpack("U*"))
end
end
-
+
def basename
@basename ||= @ttf.name.postscript_name
end
# not sure how to compute this for true-type fonts...
@@ -158,39 +160,47 @@
flags |= 0x0004 # assume the font contains at least some non-latin characters
end
end
def normalize_encoding(text)
- text.normalize_to_utf8
+ begin
+ text.encode(::Encoding::UTF_8)
+ rescue => e
+ puts e
+ raise Prawn::Errors::IncompatibleStringEncoding, "Encoding " +
+ "#{text.encoding} can not be transparently converted to UTF-8. " +
+ "Please ensure the encoding of the string you are attempting " +
+ "to use is set correctly"
+ end
end
def glyph_present?(char)
code = char.codepoints.first
cmap[code] > 0
end
# Returns the number of characters in +str+ (a UTF-8-encoded string).
#
def character_count(str)
- str.unicode_length
+ str.length
end
private
def cmap
@cmap ||= @ttf.cmap.unicode.first or raise("no unicode cmap for font")
end
-
+
# +string+ must be UTF8-encoded.
#
# Returns an array. If an element is a numeric, it represents the
# kern amount to inject at that position. Otherwise, the element
# is an array of UTF-16 characters.
def kern(string)
a = []
- string.codepoints do |r|
+ string.each_codepoint do |r|
if a.empty?
a << [r]
elsif (kern = kern_pairs_table[[cmap[a.last.last], cmap[r]]])
kern *= scale_factor
a << -kern << [r]
@@ -213,29 +223,29 @@
def hmtx
@hmtx ||= @ttf.horizontal_metrics
end
- def character_width_by_code(code)
+ def character_width_by_code(code)
return 0 unless cmap[code]
# Some TTF fonts have nonzero widths for \n (UTF-8 / ASCII code: 10).
# Patch around this as we'll never be drawing a newline with a width.
return 0.0 if code == 10
@char_widths[code] ||= Integer(hmtx.widths[cmap[code]] * scale_factor)
- end
+ end
def scale_factor
@scale ||= 1000.0 / @ttf.header.units_per_em
end
def register(subset)
temp_name = @ttf.name.postscript_name.gsub("\0","").to_sym
ref = @document.ref!(:Type => :Font, :BaseFont => temp_name)
- # Embed the font metrics in the document after everything has been
+ # Embed the font metrics in the document after everything has been
# drawn, just before the document is emitted.
@document.before_render { |doc| embed(ref, subset) }
ref
end
@@ -252,16 +262,14 @@
# if their font name is more than 33 bytes long. Strange. But true.
basename = font.name.postscript_name[0, 33].gsub("\0","")
raise "Can't detect a postscript name for #{file}" if basename.nil?
- compressed_font = Zlib::Deflate.deflate(font_content)
+ fontfile = @document.ref!(:Length1 => font_content.size)
+ fontfile.stream << font_content
+ fontfile.stream.compress!
- fontfile = @document.ref!(:Length1 => font_content.size,
- :Filter => :FlateDecode )
- fontfile << compressed_font
-
descriptor = @document.ref!(:Type => :FontDescriptor,
:FontName => basename.to_sym,
:FontFile2 => fontfile,
:FontBBox => bbox,
:Flags => pdf_flags,
@@ -287,11 +295,11 @@
# It offends my inner purist, but it'll do.
map = @subsets[subset].to_unicode_map
ranges = [[]]
- lines = map.keys.sort.inject("") do |s, code|
+ map.keys.sort.inject("") do |s, code|
ranges << [] if ranges.last.length >= 100
unicode = map[code]
ranges.last << "<%02x><%04x>" % [code, unicode]
end
@@ -301,10 +309,10 @@
to_unicode_cmap = UNICODE_CMAP_TEMPLATE % range_blocks.strip
cmap = @document.ref!({})
cmap << to_unicode_cmap
- cmap.compress_stream
+ cmap.stream.compress!
reference.data.update(:Subtype => :TrueType,
:BaseFont => basename.to_sym,
:FontDescriptor => descriptor,
:FirstChar => 32,