prawn 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. data/Rakefile +3 -1
  2. data/data/fonts/Action Man.dfont +0 -0
  3. data/examples/general/measurement_units.rb +2 -2
  4. data/examples/graphics/image_flow.rb +2 -2
  5. data/examples/graphics/stroke_bounds.rb +1 -1
  6. data/examples/m17n/win_ansi_charset.rb +3 -3
  7. data/examples/text/dfont.rb +49 -0
  8. data/examples/text/flowing_text_with_header_and_footer.rb +2 -48
  9. data/examples/text/font_calculations.rb +7 -6
  10. data/examples/text/font_size.rb +4 -4
  11. data/examples/text/text_flow.rb +1 -1
  12. data/lib/prawn.rb +6 -3
  13. data/lib/prawn/compatibility.rb +12 -17
  14. data/lib/prawn/document.rb +10 -10
  15. data/lib/prawn/document/internals.rb +8 -3
  16. data/lib/prawn/document/text.rb +39 -57
  17. data/lib/prawn/document/text/box.rb +1 -2
  18. data/lib/prawn/document/text/wrapping.rb +59 -0
  19. data/lib/prawn/errors.rb +0 -8
  20. data/lib/prawn/font.rb +192 -277
  21. data/lib/prawn/font/afm.rb +199 -0
  22. data/lib/prawn/font/dfont.rb +31 -0
  23. data/lib/prawn/font/ttf.rb +318 -0
  24. data/lib/prawn/graphics.rb +7 -2
  25. data/lib/prawn/images/png.rb +1 -1
  26. data/lib/prawn/reference.rb +7 -4
  27. data/spec/font_spec.rb +154 -61
  28. data/spec/text_spec.rb +47 -6
  29. data/vendor/pdf-inspector/lib/pdf/inspector.rb +1 -1
  30. data/vendor/ttfunk/example.rb +42 -2
  31. data/vendor/ttfunk/lib/ttfunk.rb +96 -42
  32. data/vendor/ttfunk/lib/ttfunk/directory.rb +17 -0
  33. data/vendor/ttfunk/lib/ttfunk/encoding/mac_roman.rb +88 -0
  34. data/vendor/ttfunk/lib/ttfunk/encoding/windows_1252.rb +69 -0
  35. data/vendor/ttfunk/lib/ttfunk/reader.rb +44 -0
  36. data/vendor/ttfunk/lib/ttfunk/resource_file.rb +78 -0
  37. data/vendor/ttfunk/lib/ttfunk/subset.rb +18 -0
  38. data/vendor/ttfunk/lib/ttfunk/subset/base.rb +141 -0
  39. data/vendor/ttfunk/lib/ttfunk/subset/mac_roman.rb +46 -0
  40. data/vendor/ttfunk/lib/ttfunk/subset/unicode.rb +48 -0
  41. data/vendor/ttfunk/lib/ttfunk/subset/unicode_8bit.rb +63 -0
  42. data/vendor/ttfunk/lib/ttfunk/subset/windows_1252.rb +51 -0
  43. data/vendor/ttfunk/lib/ttfunk/subset_collection.rb +72 -0
  44. data/vendor/ttfunk/lib/ttfunk/table.rb +37 -18
  45. data/vendor/ttfunk/lib/ttfunk/table/cmap.rb +24 -84
  46. data/vendor/ttfunk/lib/ttfunk/table/cmap/format00.rb +54 -0
  47. data/vendor/ttfunk/lib/ttfunk/table/cmap/format04.rb +126 -0
  48. data/vendor/ttfunk/lib/ttfunk/table/cmap/subtable.rb +79 -0
  49. data/vendor/ttfunk/lib/ttfunk/table/glyf.rb +64 -0
  50. data/vendor/ttfunk/lib/ttfunk/table/glyf/compound.rb +81 -0
  51. data/vendor/ttfunk/lib/ttfunk/table/glyf/simple.rb +37 -0
  52. data/vendor/ttfunk/lib/ttfunk/table/head.rb +38 -19
  53. data/vendor/ttfunk/lib/ttfunk/table/hhea.rb +35 -21
  54. data/vendor/ttfunk/lib/ttfunk/table/hmtx.rb +40 -13
  55. data/vendor/ttfunk/lib/ttfunk/table/kern.rb +69 -38
  56. data/vendor/ttfunk/lib/ttfunk/table/kern/format0.rb +62 -0
  57. data/vendor/ttfunk/lib/ttfunk/table/loca.rb +43 -0
  58. data/vendor/ttfunk/lib/ttfunk/table/maxp.rb +34 -11
  59. data/vendor/ttfunk/lib/ttfunk/table/name.rb +109 -42
  60. data/vendor/ttfunk/lib/ttfunk/table/os2.rb +78 -0
  61. data/vendor/ttfunk/lib/ttfunk/table/post.rb +91 -0
  62. data/vendor/ttfunk/lib/ttfunk/table/post/format10.rb +43 -0
  63. data/vendor/ttfunk/lib/ttfunk/table/post/format20.rb +35 -0
  64. data/vendor/ttfunk/lib/ttfunk/table/post/format25.rb +23 -0
  65. data/vendor/ttfunk/lib/ttfunk/table/post/format30.rb +17 -0
  66. data/vendor/ttfunk/lib/ttfunk/table/post/format40.rb +17 -0
  67. data/vendor/ttfunk/lib/ttfunk/table/simple.rb +14 -0
  68. metadata +54 -25
  69. data/examples/table/addressbook.csv +0 -6
  70. data/examples/table/cell.rb +0 -40
  71. data/examples/table/currency.csv +0 -1834
  72. data/examples/table/fancy_table.rb +0 -62
  73. data/examples/table/ruport_formatter.rb +0 -53
  74. data/examples/table/table.rb +0 -51
  75. data/examples/table/table_alignment.rb +0 -18
  76. data/examples/table/table_border_color.rb +0 -17
  77. data/examples/table/table_colspan.rb +0 -19
  78. data/examples/table/table_header_color.rb +0 -19
  79. data/examples/table/table_header_underline.rb +0 -15
  80. data/lib/prawn/document/table.rb +0 -338
  81. data/lib/prawn/font/cmap.rb +0 -59
  82. data/lib/prawn/font/metrics.rb +0 -378
  83. data/lib/prawn/font/wrapping.rb +0 -47
  84. data/lib/prawn/graphics/cell.rb +0 -264
  85. data/spec/metrics_spec.rb +0 -62
  86. data/spec/table_spec.rb +0 -179
  87. data/vendor/ttfunk/lib/ttfunk/table/directory.rb +0 -25
@@ -1,59 +0,0 @@
1
- # encoding: utf-8
2
-
3
- # cmap.rb : class for building ToUnicode CMaps for Type0 fonts
4
- #
5
- # Copyright May 2008, Gregory Brown / James Healy. All Rights Reserved.
6
- #
7
- # This is free software. Please see the LICENSE and COPYING files for details.
8
-
9
- module Prawn
10
- class Font
11
- class CMap #:nodoc:
12
-
13
- def initialize
14
- @codes = {}
15
- end
16
-
17
- def [](c)
18
- @codes[c]
19
- end
20
-
21
- def []=(c, v)
22
- @codes[c] = v
23
- end
24
-
25
- def to_s
26
- # TODO: learn what all this means. I just copied the basic structure
27
- # from an existing PDF
28
- # TODO: make this more efficient. The mapping can be specified in
29
- # ranges instead of one -> one
30
- res = "12 dict begin\n"
31
- res << "begincmap\n"
32
- res << "/CIDSystemInfo\n"
33
- res << "<< /Registry (Adobe)\n"
34
- res << "/Ordering (UCS)\n"
35
- res << "/Supplement 0\n"
36
- res << ">> def\n"
37
- res << "/CMapName /Adobe-Identity-UCS def\n"
38
- res << "/CMapType 2 def\n"
39
- res << "begincodespacerange\n"
40
- res << "<0000> <ffff>\n"
41
- res << "endcodespacerange\n"
42
- res << "9 beginbfchar\n"
43
- @codes.keys.sort.each do |key|
44
- val = @codes[key]
45
- ccode = val.to_s(16)
46
- ccode = ("0" * (4 - ccode.size)) + ccode
47
- unicode = key.to_s(16)
48
- unicode = ("0" * (4 - unicode.size)) + unicode
49
- res << "<#{ccode}> <#{unicode}>\n"
50
- end
51
- res << "endbfchar\n"
52
- res << "endcmap\n"
53
- res << "CMapName currentdict /CMap defineresource pop\n"
54
- res << "end\n"
55
- res << "end\n"
56
- end
57
- end
58
- end
59
- end
@@ -1,378 +0,0 @@
1
- # encoding: utf-8
2
-
3
- # metrics.rb : Font metrics parsers for AFM and TTF.
4
- #
5
- # Font::Metrics::Adobe is mainly a port of CPAN's Font::AFM
6
- # http://search.cpan.org/~gaas/Font-AFM-1.19/AFM.pm
7
- #
8
- # Copyright May 2008, Gregory Brown / James Edward Gray II. All Rights Reserved.
9
- #
10
- # This is free software. Please see the LICENSE and COPYING files for details.
11
-
12
- require 'prawn/encoding'
13
-
14
- module Prawn
15
- class Font
16
- class Metrics #:nodoc:
17
-
18
- include Prawn::Font::Wrapping
19
-
20
- def self.[](font)
21
- data[font] ||= (font.match(/\.ttf$/i) ? TTF : Adobe).new(font)
22
- end
23
-
24
- def self.data
25
- @data ||= {}
26
- end
27
-
28
- def string_height(string,options={})
29
- string = naive_wrap(string, options[:line_width], options[:font_size])
30
- string.lines.to_a.length * font_height(options[:font_size])
31
- end
32
-
33
- def font_height(size)
34
- (ascender - descender + line_gap) * size / 1000.0
35
- end
36
-
37
- class Adobe < Metrics #:nodoc:
38
- attr_reader :attributes
39
-
40
- def initialize(font_name)
41
- @attributes = {}
42
- @glyph_widths = {}
43
- @bounding_boxes = {}
44
- @kern_pairs = {}
45
-
46
- file = font_name.sub(/\.afm$/,'') + '.afm'
47
- unless file[0..0] == "/"
48
- file = find_font(file)
49
- end
50
-
51
- parse_afm(file)
52
- end
53
-
54
- def bbox
55
- fontbbox.split(/\s+/).map { |e| Integer(e) }
56
- end
57
-
58
- # calculates the width of the supplied string.
59
- #
60
- # String *must* be encoded as WinAnsi
61
- #
62
- def string_width(string, font_size, options = {})
63
- scale = font_size / 1000.0
64
-
65
- if options[:kerning]
66
- kern(string).inject(0) do |s,r|
67
- if r.is_a? String
68
- s + string_width(r, font_size, :kerning => false)
69
- else
70
- s - (r * scale)
71
- end
72
- end
73
- else
74
- string.unpack("C*").inject(0) do |s,r|
75
- s + latin_glyphs_table[r]
76
- end * scale
77
- end
78
- end
79
-
80
- # converts a string into an array with spacing offsets
81
- # bewteen characters that need to be kerned
82
- #
83
- # String *must* be encoded as WinAnsi
84
- #
85
- def kern(string)
86
- kerned = string.unpack("C*").inject([]) do |a,r|
87
- if a.last.is_a? Array
88
- if k = latin_kern_pairs_table[[a.last.last, r]]
89
- a << k << [r]
90
- else
91
- a.last << r
92
- end
93
- else
94
- a << [r]
95
- end
96
- a
97
- end
98
-
99
- kerned.map { |r|
100
- i = r.is_a?(Array) ? r.pack("C*") : r
101
- i.force_encoding("ISO-8859-1") if i.respond_to?(:force_encoding)
102
- i.is_a?(Numeric) ? -i : i
103
- }
104
- end
105
-
106
- def latin_kern_pairs_table
107
- @kern_pairs_table ||= @kern_pairs.inject({}) do |h,p|
108
- h[p[0].map { |n| Encoding::WinAnsi::CHARACTERS.index(n) }] = p[1]
109
- h
110
- end
111
- end
112
-
113
- def latin_glyphs_table
114
- @glyphs_table ||= (0..255).map do |i|
115
- @glyph_widths[Encoding::WinAnsi::CHARACTERS[i]].to_i
116
- end
117
- end
118
-
119
- def ascender
120
- @attributes["ascender"].to_i
121
- end
122
-
123
- def descender
124
- @attributes["descender"].to_i
125
- end
126
-
127
- def line_gap
128
- Float(bbox[3] - bbox[1]) - (ascender - descender)
129
- end
130
-
131
- # Hackish, but does the trick for now.
132
- def method_missing(method, *args, &block)
133
- name = method.to_s.delete("_")
134
- @attributes.include?(name) ? @attributes[name] : super
135
- end
136
-
137
- def metrics_path
138
- if m = ENV['METRICS']
139
- @metrics_path ||= m.split(':')
140
- else
141
- @metrics_path ||= [
142
- ".", "/usr/lib/afm",
143
- "/usr/local/lib/afm",
144
- "/usr/openwin/lib/fonts/afm/",
145
- Prawn::BASEDIR+'/data/fonts/']
146
- end
147
- end
148
-
149
- def has_kerning_data?
150
- true
151
- end
152
-
153
- def type0?
154
- false
155
- end
156
-
157
- # perform any changes to the string that need to happen
158
- # before it is rendered to the canvas
159
- #
160
- # String *must* be encoded as WinAnsi
161
- #
162
- def convert_text(text, options={})
163
- options[:kerning] ? kern(text) : text
164
- end
165
-
166
- private
167
-
168
- def find_font(file)
169
- metrics_path.find { |f| File.exist? "#{f}/#{file}" } + "/#{file}"
170
- rescue NoMethodError
171
- raise Prawn::Errors::UnknownFont,
172
- "Couldn't find the font: #{file} in any of:\n" +
173
- @metrics_path.join("\n")
174
- end
175
-
176
- def parse_afm(file_name)
177
- section = []
178
-
179
- File.foreach(file_name) do |line|
180
- case line
181
- when /^Start(\w+)/
182
- section.push $1
183
- next
184
- when /^End(\w+)/
185
- section.pop
186
- next
187
- end
188
-
189
- case section
190
- when ["FontMetrics", "CharMetrics"]
191
- next unless line =~ /^CH?\s/
192
-
193
- name = line[/\bN\s+(\.?\w+)\s*;/, 1]
194
- @glyph_widths[name] = line[/\bWX\s+(\d+)\s*;/, 1].to_i
195
- @bounding_boxes[name] = line[/\bB\s+([^;]+);/, 1].to_s.rstrip
196
- when ["FontMetrics", "KernData", "KernPairs"]
197
- next unless line =~ /^KPX\s+(\.?\w+)\s+(\.?\w+)\s+(-?\d+)/
198
- @kern_pairs[[$1, $2]] = $3.to_i
199
- when ["FontMetrics", "KernData", "TrackKern"],
200
- ["FontMetrics", "Composites"]
201
- next
202
- else
203
- parse_generic_afm_attribute(line)
204
- end
205
- end
206
- end
207
-
208
- def parse_generic_afm_attribute(line)
209
- line =~ /(^\w+)\s+(.*)/
210
- key, value = $1.to_s.downcase, $2
211
-
212
- @attributes[key] = @attributes[key] ?
213
- Array(@attributes[key]) << value : value
214
- end
215
- end
216
-
217
- class TTF < Metrics #:nodoc:
218
-
219
- attr_accessor :ttf
220
-
221
- def initialize(font)
222
- @ttf = TTFunk::File.new(font)
223
- @attributes = {}
224
- @glyph_widths = {}
225
- @bounding_boxes = {}
226
- @char_widths = {}
227
- @has_kerning_data = !! @ttf.kern? && @ttf.kern.sub_tables[0]
228
- end
229
-
230
- def cmap
231
- @cmap ||= @ttf.cmap.formats[4]
232
- end
233
-
234
- def string_width(string, font_size, options = {})
235
- scale = font_size / 1000.0
236
- if options[:kerning]
237
- kern(string,:skip_conversion => true).inject(0) do |s,r|
238
- if r.is_a? String
239
- s + string_width(r, font_size, :kerning => false)
240
- else
241
- s + r * scale
242
- end
243
- end
244
- else
245
- string.unpack("U*").inject(0) do |s,r|
246
- s + character_width_by_code(r)
247
- end * scale
248
- end
249
- end
250
-
251
- # TODO: NASTY.
252
- def kern(string,options={})
253
- a = []
254
-
255
- string.unpack("U*").each do |r|
256
- if a.last.is_a? Array
257
- if kern = kern_pairs_table[[cmap[a.last.last], cmap[r]]]
258
- kern *= scale_factor
259
- a << kern << [r]
260
- else
261
- a.last << r
262
- end
263
- else
264
- a << [r]
265
- end
266
- a
267
- end
268
-
269
- a.map { |r|
270
- if options[:skip_conversion]
271
- r.is_a?(Array) ? r.pack("U*") : r
272
- else
273
- i = r.is_a?(Array) ? r.pack("U*") : r
274
- x = if i.is_a?(String)
275
- unicode_codepoints = i.unpack("U*")
276
- glyph_codes = unicode_codepoints.map { |u| cmap[u] }
277
- glyph_codes.pack("n*")
278
- else
279
- i
280
- end
281
- x.is_a?(Numeric) ? -x : x
282
- end
283
- }
284
- end
285
-
286
- def glyph_widths
287
- glyphs = cmap.values.uniq.sort
288
- first_glyph = glyphs.shift
289
- widths = [first_glyph, [Integer(hmtx[first_glyph][0] * scale_factor)]]
290
- prev_glyph = first_glyph
291
- glyphs.each do |glyph|
292
- unless glyph == prev_glyph + 1
293
- widths << glyph
294
- widths << []
295
- end
296
- widths.last << Integer(hmtx[glyph][0] * scale_factor )
297
- prev_glyph = glyph
298
- end
299
- widths
300
- end
301
-
302
- def bbox
303
- [:x_min, :y_min, :x_max, :y_max].map do |atr|
304
- Integer(@ttf.head.send(atr)) * scale_factor
305
- end
306
- end
307
-
308
- def ascender
309
- Integer(@ttf.hhea.ascent * scale_factor)
310
- end
311
-
312
- def descender
313
- Integer(@ttf.hhea.descent * scale_factor)
314
- end
315
-
316
- def line_gap
317
- Integer(@ttf.hhea.line_gap * scale_factor)
318
- end
319
-
320
- def basename
321
- @basename ||= @ttf.name.postscript_name
322
- end
323
-
324
- # TODO: instead of creating a map that contains every glyph in the font,
325
- # only include the glyphs that were used
326
- def to_unicode_cmap
327
- return @to_unicode if @to_unicode
328
- @to_unicode = Prawn::Font::CMap.new
329
- unicode_for_glyph = cmap.invert
330
- glyphs = unicode_for_glyph.keys.uniq.sort
331
- glyphs.each do |glyph|
332
- @to_unicode[unicode_for_glyph[glyph]] = glyph
333
- end
334
- @to_unicode
335
- end
336
-
337
- def kern_pairs_table
338
- @kerning_data ||= has_kerning_data? ? @ttf.kern.sub_tables[0] : {}
339
- end
340
-
341
- def has_kerning_data?
342
- @has_kerning_data
343
- end
344
-
345
- def type0?
346
- true
347
- end
348
-
349
- def convert_text(text,options)
350
- text = text.chomp
351
- if options[:kerning]
352
- kern(text)
353
- else
354
- unicode_codepoints = text.unpack("U*")
355
- glyph_codes = unicode_codepoints.map { |u| cmap[u] }
356
- text = glyph_codes.pack("n*")
357
- end
358
- end
359
-
360
- private
361
-
362
- def hmtx
363
- @hmtx ||= @ttf.hmtx.values
364
- end
365
-
366
- def character_width_by_code(code)
367
- return 0 unless cmap[code]
368
- @char_widths[code] ||= Integer(hmtx[cmap[code]][0] * scale_factor)
369
- end
370
-
371
- def scale_factor
372
- @scale ||= 1000 * Float(@ttf.head.units_per_em)**-1
373
- end
374
-
375
- end
376
- end
377
- end
378
- end
@@ -1,47 +0,0 @@
1
- # encoding: utf-8
2
-
3
- # wrapping.rb : Implementation of naive text wrap
4
- #
5
- # Copyright May 2008, Michael Daines. All Rights Reserved.
6
- #
7
- # This is free software. Please see the LICENSE and COPYING files for details.
8
- module Prawn
9
- class Font
10
- module Wrapping #:nodoc:
11
- ruby_18 { $KCODE="U" }
12
-
13
- # TODO: Replace with TeX optimal algorithm
14
- def naive_wrap(string, line_width, font_size, options = {})
15
- scan_pattern = options[:mode] == :character ? /./ : /\S+|\s+/
16
-
17
- output = ""
18
- string.lines.each do |line|
19
- accumulated_width = 0
20
- segments = line.scan(scan_pattern)
21
-
22
- segments.each do |segment|
23
- segment_width = string_width(segment, font_size,
24
- :kerning => options[:kerning])
25
-
26
- if (accumulated_width + segment_width).round > line_width.round
27
- output = "#{output.sub(/[ \t]*\n?(\n*)\z/, "\n\\1")}"
28
-
29
- if segment =~ /\s/
30
- accumulated_width = 0
31
- else
32
- output << segment
33
- accumulated_width = segment_width
34
- end
35
- else
36
- output << segment
37
- accumulated_width += segment_width
38
- end
39
- end
40
- end
41
-
42
- output
43
- end
44
-
45
- end
46
- end
47
- end