Parent

Prawn::Font::TTF

Constants

UNICODE_CMAP_TEMPLATE

Attributes

ttf[R]
subsets[R]

Public Class Methods

new(document, name, options={}) click to toggle source
    # File lib/prawn/font/ttf.rb, line 22
22:       def initialize(document, name, options={})
23:         super
24: 
25:         @ttf              = read_ttf_file
26:         @subsets          = TTFunk::SubsetCollection.new(@ttf)
27: 
28:         @attributes       = {}
29:         @bounding_boxes   = {} 
30:         @char_widths      = {}   
31:         @has_kerning_data = @ttf.kerning.exists? && @ttf.kerning.tables.any?
32: 
33:         @ascender         = Integer(@ttf.ascent * scale_factor)
34:         @descender        = Integer(@ttf.descent * scale_factor)
35:         @line_gap         = Integer(@ttf.line_gap * scale_factor)
36:       end

Public Instance Methods

basename() click to toggle source
     # File lib/prawn/font/ttf.rb, line 106
106:       def basename
107:         @basename ||= @ttf.name.postscript_name
108:       end
bbox() click to toggle source

The font bbox, as an array of integers

    # File lib/prawn/font/ttf.rb, line 58
58:       def bbox
59:         @bbox ||= @ttf.bbox.map { |i| Integer(i * scale_factor) }
60:       end
cap_height() click to toggle source
     # File lib/prawn/font/ttf.rb, line 126
126:       def cap_height
127:         @cap_height ||= begin
128:           height = @ttf.os2.exists? && @ttf.os2.cap_height || 0
129:           height == 0 ? ascender : height
130:         end
131:       end
encode_text(text,options={}) click to toggle source

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 font subset number, and the following entry element is either a string or an array (for kerned text).

The text parameter must be UTF8-encoded.

     # File lib/prawn/font/ttf.rb, line 75
 75:       def encode_text(text,options={})
 76:         text = text.chomp
 77: 
 78:         if options[:kerning]
 79:           last_subset = nil
 80:           kern(text).inject([]) do |result, element| 
 81:             if element.is_a?(Numeric)
 82:               result.last[1] = [result.last[1]] unless result.last[1].is_a?(Array)
 83:               result.last[1] << element
 84:               result
 85:             else
 86:               encoded = @subsets.encode(element)
 87: 
 88:               if encoded.first[0] == last_subset
 89:                 result.last[1] << encoded.first[1]
 90:                 encoded.shift
 91:               end
 92: 
 93:               if encoded.any?
 94:                 last_subset = encoded.last[0]
 95:                 result + encoded
 96:               else
 97:                 result
 98:               end
 99:             end
100:           end
101:         else
102:           @subsets.encode(text.unpack("U*"))
103:         end
104:       end
family_class() click to toggle source
     # File lib/prawn/font/ttf.rb, line 139
139:       def family_class
140:         @family_class ||= (@ttf.os2.exists? && @ttf.os2.family_class || 0) >> 8
141:       end
glyph_present?(char) click to toggle source
     # File lib/prawn/font/ttf.rb, line 188
188:       def glyph_present?(char)
189:         code = char.unpack("U*").first
190:         cmap[code] > 0
191:       end
has_kerning_data?() click to toggle source

Returns true if the font has kerning data, false otherwise

    # File lib/prawn/font/ttf.rb, line 63
63:       def has_kerning_data?
64:         @has_kerning_data 
65:       end
italic_angle() click to toggle source
     # File lib/prawn/font/ttf.rb, line 115
115:       def italic_angle
116:         @italic_angle ||= if @ttf.postscript.exists?
117:           raw = @ttf.postscript.italic_angle
118:           hi, low = raw >> 16, raw & 0xFF
119:           hi = -((hi ^ 0xFFFF) + 1) if hi & 0x8000 != 0
120:           "#{hi}.#{low}".to_f
121:         else
122:           0
123:         end
124:       end
normalize_encoding(text) click to toggle source
     # File lib/prawn/font/ttf.rb, line 162
162:       def normalize_encoding(text)
163:         if text.respond_to?(:encode)
164:           # if we're running under a M17n aware VM, ensure the string provided is
165:           # UTF-8 (by converting it if necessary)
166:           begin
167:             text.encode("UTF-8")
168:           rescue
169:             raise Prawn::Errors::IncompatibleStringEncoding, "Encoding " +
170:             "#{text.encoding} can not be transparently converted to UTF-8. " +
171:             "Please ensure the encoding of the string you are attempting " +
172:             "to use is set correctly"
173:           end
174:         else
175:           # on a non M17N aware VM, use unpack as a hackish way to verify the
176:           # string is valid utf-8. I thought it was better than loading iconv
177:           # though.
178:           begin
179:             text.unpack("U*")
180:             return text.dup
181:           rescue
182:             raise Prawn::Errors::IncompatibleStringEncoding, "The string you " +
183:               "are attempting to render is not encoded in valid UTF-8."
184:           end
185:         end
186:       end
pdf_flags() click to toggle source
     # File lib/prawn/font/ttf.rb, line 151
151:       def pdf_flags
152:         @flags ||= begin
153:           flags = 0
154:           flags |= 0x0001 if @ttf.postscript.fixed_pitch?
155:           flags |= 0x0002 if serif?
156:           flags |= 0x0008 if script?
157:           flags |= 0x0040 if italic_angle != 0
158:           flags |= 0x0004 # assume the font contains at least some non-latin characters
159:         end
160:       end
script?() click to toggle source
     # File lib/prawn/font/ttf.rb, line 147
147:       def script?
148:         @script ||= family_class == 10
149:       end
serif?() click to toggle source
     # File lib/prawn/font/ttf.rb, line 143
143:       def serif?
144:         @serif ||= [1,2,3,4,5,7].include?(family_class)
145:       end
stemV() click to toggle source

not sure how to compute this for true-type fonts...

     # File lib/prawn/font/ttf.rb, line 111
111:       def stemV
112:         0
113:       end
unicode?() click to toggle source
    # File lib/prawn/font/ttf.rb, line 18
18:       def unicode?
19:         true
20:       end
x_height() click to toggle source
     # File lib/prawn/font/ttf.rb, line 133
133:       def x_height
134:         # FIXME: seems like if os2 table doesn't exist, we could
135:         # just find the height of the lower-case 'x' glyph?
136:         @ttf.os2.exists? && @ttf.os2.x_height || 0
137:       end

Private Instance Methods

character_width_by_code(code) click to toggle source
     # File lib/prawn/font/ttf.rb, line 234
234:       def character_width_by_code(code)    
235:         return 0 unless cmap[code]
236:         @char_widths[code] ||= Integer(hmtx.widths[cmap[code]] * scale_factor)
237:       end
cid_to_gid_map() click to toggle source
     # File lib/prawn/font/ttf.rb, line 225
225:       def cid_to_gid_map
226:         max = cmap.code_map.keys.max
227:         (0..max).map { |cid| cmap[cid] }.pack("n*")
228:       end
cmap() click to toggle source
     # File lib/prawn/font/ttf.rb, line 195
195:       def cmap
196:         @cmap ||= @ttf.cmap.unicode.first or raise("no unicode cmap for font")
197:       end
embed(reference, subset) click to toggle source
     # File lib/prawn/font/ttf.rb, line 254
254:       def embed(reference, subset)
255:         font_content = @subsets[subset].encode
256: 
257:         # FIXME: we need postscript_name and glyph widths from the font
258:         # subset. Perhaps this could be done by querying the subset,
259:         # rather than by parsing the font that the subset produces?
260:         font = TTFunk::File.new(font_content)
261: 
262:         # empirically, it looks like Adobe Reader will not display fonts
263:         # if their font name is more than 33 bytes long. Strange. But true.
264:         basename = font.name.postscript_name[0, 33].gsub("\00"","")
265: 
266:         raise "Can't detect a postscript name for #{file}" if basename.nil?
267: 
268:         compressed_font = Zlib::Deflate.deflate(font_content)
269: 
270:         fontfile = @document.ref!(:Length => compressed_font.size,
271:                                  :Length1 => font_content.size,
272:                                  :Filter => :FlateDecode )
273:         fontfile << compressed_font
274: 
275:         descriptor = @document.ref!(:Type        => :FontDescriptor,
276:                                    :FontName    => basename.to_sym,
277:                                    :FontFile2   => fontfile,
278:                                    :FontBBox    => bbox,
279:                                    :Flags       => pdf_flags,
280:                                    :StemV       => stemV,
281:                                    :ItalicAngle => italic_angle,
282:                                    :Ascent      => ascender,
283:                                    :Descent     => descender,
284:                                    :CapHeight   => cap_height,
285:                                    :XHeight     => x_height)
286: 
287:         hmtx = font.horizontal_metrics
288:         widths = font.cmap.tables.first.code_map.map { |gid|
289:           Integer(hmtx.widths[gid] * scale_factor) }[32..1]
290: 
291:         # It would be nice to have Encoding set for the macroman subsets,
292:         # and only do a ToUnicode cmap for non-encoded unicode subsets.
293:         # However, apparently Adobe Reader won't render MacRoman encoded
294:         # subsets if original font contains unicode characters. (It has to
295:         # be some flag or something that ttfunk is simply copying over...
296:         # but I can't figure out which flag that is.)
297:         #
298:         # For now, it's simplest to just create a unicode cmap for every font.
299:         # It offends my inner purist, but it'll do.
300: 
301:         map = @subsets[subset].to_unicode_map
302: 
303:         ranges = [[]]
304:         lines = map.keys.sort.inject("") do |s, code|
305:           ranges << [] if ranges.last.length >= 100
306:           unicode = map[code]
307:           ranges.last << "<%02x><%04x>" % [code, unicode]
308:         end
309: 
310:         range_blocks = ranges.inject("") do |s, list|
311:           s << "%d beginbfchar\n%s\nendbfchar\n" % [list.length, list.join("\n")]
312:         end
313: 
314:         to_unicode_cmap = UNICODE_CMAP_TEMPLATE % range_blocks.strip
315: 
316:         cmap = @document.ref!({})
317:         cmap << to_unicode_cmap
318:         cmap.compress_stream
319: 
320:         reference.data.update(:Subtype => :TrueType,
321:                               :BaseFont => basename.to_sym,
322:                               :FontDescriptor => descriptor,
323:                               :FirstChar => 32,
324:                               :LastChar => 255,
325:                               :Widths => @document.ref!(widths),
326:                               :ToUnicode => cmap)
327:       end
hmtx() click to toggle source
     # File lib/prawn/font/ttf.rb, line 230
230:       def hmtx
231:         @hmtx ||= @ttf.horizontal_metrics
232:       end
kern(string) click to toggle source

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.

     # File lib/prawn/font/ttf.rb, line 204
204:       def kern(string)
205:         a = []
206: 
207:         string.unpack("U*").each do |r|
208:           if a.empty?
209:             a << [r]
210:           elsif (kern = kern_pairs_table[[cmap[a.last.last], cmap[r]]])
211:             kern *= scale_factor
212:             a << -kern << [r]
213:           else
214:             a.last << r
215:           end
216:         end
217: 
218:         a
219:       end
kern_pairs_table() click to toggle source
     # File lib/prawn/font/ttf.rb, line 221
221:       def kern_pairs_table
222:         @kerning_data ||= has_kerning_data? ? @ttf.kerning.tables.first.pairs : {}
223:       end
read_ttf_file() click to toggle source
     # File lib/prawn/font/ttf.rb, line 350
350:       def read_ttf_file
351:         TTFunk::File.open(@name)
352:       end
register(subset) click to toggle source
     # File lib/prawn/font/ttf.rb, line 243
243:       def register(subset)
244:         temp_name = @ttf.name.postscript_name.gsub("\00"","").to_sym
245:         ref = @document.ref!(:Type => :Font, :BaseFont => temp_name)
246: 
247:         # Embed the font metrics in the document after everything has been 
248:         # drawn, just before the document is emitted.
249:         @document.before_render { |doc| embed(ref, subset) }
250: 
251:         ref
252:       end
scale_factor() click to toggle source
     # File lib/prawn/font/ttf.rb, line 239
239:       def scale_factor
240:         @scale ||= 1000.0 / @ttf.header.units_per_em
241:       end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.