prawn 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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