prawn 0.14.0 → 0.15.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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -1
  3. data/Rakefile +12 -0
  4. data/lib/prawn.rb +9 -21
  5. data/lib/prawn/document.rb +95 -68
  6. data/lib/prawn/document/bounding_box.rb +22 -4
  7. data/lib/prawn/document/column_box.rb +2 -0
  8. data/lib/prawn/document/graphics_state.rb +1 -1
  9. data/lib/prawn/document/internals.rb +2 -2
  10. data/lib/prawn/document/snapshot.rb +2 -1
  11. data/lib/prawn/document/span.rb +2 -0
  12. data/lib/prawn/encoding.rb +1 -1
  13. data/lib/prawn/font.rb +89 -75
  14. data/lib/prawn/font/afm.rb +3 -0
  15. data/lib/prawn/font/dfont.rb +1 -0
  16. data/lib/prawn/font/ttf.rb +2 -0
  17. data/lib/prawn/font_metric_cache.rb +3 -1
  18. data/lib/prawn/graphics.rb +2 -14
  19. data/lib/prawn/graphics/cap_style.rb +1 -0
  20. data/lib/prawn/graphics/color.rb +1 -0
  21. data/lib/prawn/graphics/dash.rb +3 -2
  22. data/lib/prawn/graphics/join_style.rb +2 -0
  23. data/lib/prawn/graphics/patterns.rb +1 -0
  24. data/lib/prawn/graphics/transformation.rb +1 -0
  25. data/lib/prawn/graphics/transparency.rb +2 -0
  26. data/lib/prawn/image_handler.rb +2 -0
  27. data/lib/prawn/images.rb +5 -0
  28. data/lib/prawn/images/image.rb +1 -0
  29. data/lib/prawn/images/jpg.rb +3 -0
  30. data/lib/prawn/images/png.rb +2 -0
  31. data/lib/prawn/layout.rb +8 -13
  32. data/lib/prawn/layout/grid.rb +15 -3
  33. data/lib/prawn/measurement_extensions.rb +4 -0
  34. data/lib/prawn/measurements.rb +2 -0
  35. data/lib/prawn/outline.rb +3 -1
  36. data/lib/prawn/repeater.rb +3 -1
  37. data/lib/prawn/security.rb +15 -7
  38. data/lib/prawn/security/arcfour.rb +52 -0
  39. data/lib/prawn/soft_mask.rb +3 -1
  40. data/lib/prawn/stamp.rb +2 -0
  41. data/lib/prawn/table.rb +2 -0
  42. data/lib/prawn/table/cell.rb +4 -1
  43. data/lib/prawn/table/cell/image.rb +1 -2
  44. data/lib/prawn/table/cell/in_table.rb +2 -0
  45. data/lib/prawn/table/cell/span_dummy.rb +1 -0
  46. data/lib/prawn/table/cells.rb +7 -2
  47. data/lib/prawn/table/column_width_calculator.rb +8 -2
  48. data/lib/prawn/text.rb +4 -2
  49. data/lib/prawn/text/box.rb +3 -0
  50. data/lib/prawn/text/formatted/arranger.rb +2 -0
  51. data/lib/prawn/text/formatted/box.rb +55 -50
  52. data/lib/prawn/text/formatted/fragment.rb +2 -0
  53. data/lib/prawn/text/formatted/line_wrap.rb +1 -0
  54. data/lib/prawn/text/formatted/parser.rb +2 -0
  55. data/lib/prawn/text/formatted/wrap.rb +2 -0
  56. data/lib/prawn/utilities.rb +5 -3
  57. data/manual/graphics/common_lines.rb +2 -0
  58. data/manual/text/group.rb +2 -0
  59. data/manual/text/text.rb +1 -1
  60. data/prawn.gemspec +4 -3
  61. data/spec/grid_spec.rb +11 -0
  62. data/spec/object_store_spec.rb +1 -96
  63. data/spec/reference_spec.rb +0 -57
  64. data/spec/spec_helper.rb +7 -0
  65. data/spec/table_spec.rb +26 -0
  66. metadata +172 -185
  67. data/lib/pdf/core.rb +0 -35
  68. data/lib/pdf/core/annotations.rb +0 -60
  69. data/lib/pdf/core/byte_string.rb +0 -9
  70. data/lib/pdf/core/destinations.rb +0 -90
  71. data/lib/pdf/core/document_state.rb +0 -79
  72. data/lib/pdf/core/filter_list.rb +0 -51
  73. data/lib/pdf/core/filters.rb +0 -36
  74. data/lib/pdf/core/graphics_state.rb +0 -89
  75. data/lib/pdf/core/literal_string.rb +0 -16
  76. data/lib/pdf/core/name_tree.rb +0 -177
  77. data/lib/pdf/core/object_store.rb +0 -311
  78. data/lib/pdf/core/outline.rb +0 -315
  79. data/lib/pdf/core/page.rb +0 -212
  80. data/lib/pdf/core/page_geometry.rb +0 -126
  81. data/lib/pdf/core/pdf_object.rb +0 -99
  82. data/lib/pdf/core/reference.rb +0 -103
  83. data/lib/pdf/core/stream.rb +0 -98
  84. data/lib/pdf/core/text.rb +0 -275
  85. data/lib/prawn/templates.rb +0 -75
  86. data/spec/filters_spec.rb +0 -34
  87. data/spec/name_tree_spec.rb +0 -112
  88. data/spec/pdf_object_spec.rb +0 -172
  89. data/spec/stream_spec.rb +0 -58
  90. data/spec/template_spec_obsolete.rb +0 -352
@@ -1,16 +0,0 @@
1
- # encoding: utf-8
2
- module PDF
3
- module Core
4
- # This is used to differentiate strings that must be encoded as
5
- # a *literal* string, versus those that can be encoded in
6
- # the PDF hexadecimal format.
7
- #
8
- # Some features of the PDF format appear to require that literal
9
- # strings be used. One such feature is the /Dest key of a link
10
- # annotation; if a hex encoded string is used there, the links
11
- # do not work (as tested in Mac OS X Preview, and Adobe Acrobat
12
- # Reader).
13
- class LiteralString < String #:nodoc:
14
- end
15
- end
16
- end
@@ -1,177 +0,0 @@
1
- # encoding: utf-8
2
-
3
- # name_tree.rb : Implements NameTree for PDF
4
- #
5
- # Copyright November 2008, Jamis Buck. All Rights Reserved.
6
- #
7
- # This is free software. Please see the LICENSE and COPYING files for details.
8
- #
9
- module PDF
10
- module Core
11
- module NameTree #:nodoc:
12
- class Node #:nodoc:
13
- attr_reader :children
14
- attr_reader :limit
15
- attr_reader :document
16
- attr_accessor :parent
17
- attr_accessor :ref
18
-
19
- def initialize(document, limit, parent=nil)
20
- @document = document
21
- @children = []
22
- @limit = limit
23
- @parent = parent
24
- @ref = nil
25
- end
26
-
27
- def empty?
28
- children.empty?
29
- end
30
-
31
- def size
32
- leaf? ? children.size : children.inject(0) { |sum, child| sum + child.size }
33
- end
34
-
35
- def leaf?
36
- children.empty? || children.first.is_a?(Value)
37
- end
38
-
39
- def add(name, value)
40
- self << Value.new(name, value)
41
- end
42
-
43
- def to_hash
44
- hash = {}
45
-
46
- hash[:Limits] = [least, greatest] if parent
47
- if leaf?
48
- hash[:Names] = children if leaf?
49
- else
50
- hash[:Kids] = children.map { |child| child.ref }
51
- end
52
-
53
- return hash
54
- end
55
-
56
- def least
57
- if leaf?
58
- children.first.name
59
- else
60
- children.first.least
61
- end
62
- end
63
-
64
- def greatest
65
- if leaf?
66
- children.last.name
67
- else
68
- children.last.greatest
69
- end
70
- end
71
-
72
- def <<(value)
73
- if children.empty?
74
- children << value
75
- elsif leaf?
76
- children.insert(insertion_point(value), value)
77
- split! if children.length > limit
78
- else
79
- fit = children.detect { |child| child >= value }
80
- fit = children.last unless fit
81
- fit << value
82
- end
83
-
84
- value
85
- end
86
-
87
- def >=(value)
88
- children.empty? || children.last >= value
89
- end
90
-
91
- def split!
92
- if parent
93
- parent.split(self)
94
- else
95
- left, right = new_node(self), new_node(self)
96
- split_children(self, left, right)
97
- children.replace([left, right])
98
- end
99
- end
100
-
101
- # Returns a deep copy of this node, without copying expensive things
102
- # like the ref to @document.
103
- #
104
- def deep_copy
105
- node = dup
106
- node.instance_variable_set("@children",
107
- Marshal.load(Marshal.dump(children)))
108
- node.instance_variable_set("@ref",
109
- node.ref ? node.ref.deep_copy : nil)
110
- node
111
- end
112
-
113
- protected
114
-
115
- def split(node)
116
- new_child = new_node(self)
117
- split_children(node, node, new_child)
118
- index = children.index(node)
119
- children.insert(index+1, new_child)
120
- split! if children.length > limit
121
- end
122
-
123
- private
124
-
125
- def new_node(parent=nil)
126
- node = Node.new(document, limit, parent)
127
- node.ref = document.ref!(node)
128
- return node
129
- end
130
-
131
- def split_children(node, left, right)
132
- half = (node.limit+1)/2
133
-
134
- left_children, right_children = node.children[0...half], node.children[half..-1]
135
-
136
- left.children.replace(left_children)
137
- right.children.replace(right_children)
138
-
139
- unless node.leaf?
140
- left_children.each { |child| child.parent = left }
141
- right_children.each { |child| child.parent = right }
142
- end
143
- end
144
-
145
- def insertion_point(value)
146
- children.each_with_index do |child, index|
147
- return index if child >= value
148
- end
149
- return children.length
150
- end
151
- end
152
-
153
- class Value #:nodoc:
154
- include Comparable
155
-
156
- attr_reader :name
157
- attr_reader :value
158
-
159
- def initialize(name, value)
160
- @name, @value = PDF::Core::LiteralString.new(name), value
161
- end
162
-
163
- def <=>(leaf)
164
- name <=> leaf.name
165
- end
166
-
167
- def inspect
168
- "#<Value: #{name.inspect} : #{value.inspect}>"
169
- end
170
-
171
- def to_s
172
- "#{name} : #{value}"
173
- end
174
- end
175
- end
176
- end
177
- end
@@ -1,311 +0,0 @@
1
- # encoding: utf-8
2
-
3
- # Implements PDF object repository
4
- #
5
- # Copyright August 2009, Brad Ediger. All Rights Reserved.
6
- #
7
- # This is free software. Please see the LICENSE and COPYING files for details.
8
-
9
-
10
- require 'pdf/reader'
11
-
12
- module PDF
13
- module Core
14
- class ObjectStore #:nodoc:
15
- include Enumerable
16
-
17
- attr_reader :min_version
18
-
19
- BASE_OBJECTS = %w[info pages root]
20
-
21
- def initialize(opts = {})
22
- @objects = {}
23
- @identifiers = []
24
-
25
- load_file(opts[:template]) if opts[:template]
26
-
27
- @info ||= ref(opts[:info] || {}).identifier
28
- @root ||= ref(:Type => :Catalog).identifier
29
- if opts[:print_scaling] == :none
30
- root.data[:ViewerPreferences] = {:PrintScaling => :None}
31
- end
32
- if pages.nil?
33
- root.data[:Pages] = ref(:Type => :Pages, :Count => 0, :Kids => [])
34
- end
35
- end
36
-
37
- def ref(data, &block)
38
- push(size + 1, data, &block)
39
- end
40
-
41
- def info
42
- @objects[@info]
43
- end
44
-
45
- def root
46
- @objects[@root]
47
- end
48
-
49
- def pages
50
- root.data[:Pages]
51
- end
52
-
53
- def page_count
54
- pages.data[:Count]
55
- end
56
-
57
- # Adds the given reference to the store and returns the reference object.
58
- # If the object provided is not a PDF::Core::Reference, one is created from the
59
- # arguments provided.
60
- #
61
- def push(*args, &block)
62
- reference = if args.first.is_a?(PDF::Core::Reference)
63
- args.first
64
- else
65
- PDF::Core::Reference.new(*args, &block)
66
- end
67
-
68
- @objects[reference.identifier] = reference
69
- @identifiers << reference.identifier
70
- reference
71
- end
72
-
73
- alias_method :<<, :push
74
-
75
- def each
76
- @identifiers.each do |id|
77
- yield @objects[id]
78
- end
79
- end
80
-
81
- def [](id)
82
- @objects[id]
83
- end
84
-
85
- def size
86
- @identifiers.size
87
- end
88
- alias_method :length, :size
89
-
90
- def compact
91
- # Clear live markers
92
- each { |o| o.live = false }
93
-
94
- # Recursively mark reachable objects live, starting from the roots
95
- # (the only objects referenced in the trailer)
96
- root.mark_live
97
- info.mark_live
98
-
99
- # Renumber live objects to eliminate gaps (shrink the xref table)
100
- if @objects.any?{ |_, o| !o.live }
101
- new_id = 1
102
- new_objects = {}
103
- new_identifiers = []
104
-
105
- each do |obj|
106
- if obj.live
107
- obj.identifier = new_id
108
- new_objects[new_id] = obj
109
- new_identifiers << new_id
110
- new_id += 1
111
- end
112
- end
113
-
114
- @objects = new_objects
115
- @identifiers = new_identifiers
116
- end
117
- end
118
-
119
- # returns the object ID for a particular page in the document. Pages
120
- # are indexed starting at 1 (not 0!).
121
- #
122
- # object_id_for_page(1)
123
- # => 5
124
- # object_id_for_page(10)
125
- # => 87
126
- # object_id_for_page(-11)
127
- # => 17
128
- #
129
- def object_id_for_page(k)
130
- k -= 1 if k > 0
131
- flat_page_ids = get_page_objects(pages).flatten
132
- flat_page_ids[k]
133
- end
134
-
135
- # imports all objects required to render a page from another PDF. The
136
- # objects are added to the current object store, but NOT linked
137
- # anywhere.
138
- #
139
- # The object ID of the root Page object is returned, it's up to the
140
- # calling code to link that into the document structure somewhere. If
141
- # this isn't done the imported objects will just be removed when the
142
- # store is compacted.
143
- #
144
- # Imports nothing and returns nil if the requested page number doesn't
145
- # exist. page_num is 1 indexed, so 1 indicates the first page.
146
- #
147
- def import_page(input, page_num)
148
- @loaded_objects = {}
149
- if template_id = indexed_template(input, page_num)
150
- return template_id
151
- end
152
-
153
- io = if input.respond_to?(:seek) && input.respond_to?(:read)
154
- input
155
- elsif File.file?(input.to_s)
156
- StringIO.new(File.binread(input.to_s))
157
- else
158
- raise ArgumentError, "input must be an IO-like object or a filename"
159
- end
160
-
161
- # unless File.file?(filename)
162
- # raise ArgumentError, "#{filename} does not exist"
163
- # end
164
-
165
- hash = indexed_hash(input, io)
166
- ref = hash.page_references[page_num - 1]
167
-
168
- if ref.nil?
169
- nil
170
- else
171
- index_template(input, page_num, load_object_graph(hash, ref).identifier)
172
- end
173
-
174
- rescue PDF::Reader::MalformedPDFError, PDF::Reader::InvalidObjectError => e
175
- msg = "Error reading template file. If you are sure it's a valid PDF, it may be a bug.\n#{e.message}"
176
- raise PDF::Core::Errors::TemplateError, msg
177
- rescue PDF::Reader::UnsupportedFeatureError
178
- msg = "Template file contains unsupported PDF features"
179
- raise PDF::Core::Errors::TemplateError, msg
180
- end
181
-
182
- private
183
-
184
- # An index for page templates so that their loaded object graph
185
- # can be reused without multiple loading
186
- def template_index
187
- @template_index ||= {}
188
- end
189
-
190
- # An index for the read object hash of a pdf template so that the
191
- # object hash does not need to be parsed multiple times when using
192
- # different pages of the pdf as page templates
193
- def hash_index
194
- @hash_index ||= {}
195
- end
196
-
197
- # returns the indexed object graph identifier for a template page if
198
- # it exists
199
- def indexed_template(input, page_number)
200
- key = indexing_key(input)
201
- template_index[key] && template_index[key][page_number]
202
- end
203
-
204
- # indexes the identifier for a page from a template
205
- def index_template(input, page_number, id)
206
- (template_index[indexing_key(input)] ||= {})[page_number] ||= id
207
- end
208
-
209
- # reads and indexes a new IO for a template
210
- # if the IO has been indexed already then the parsed object hash
211
- # is returned directly
212
- def indexed_hash(input, io)
213
- hash_index[indexing_key(input)] ||= PDF::Reader::ObjectHash.new(io)
214
- end
215
-
216
- # the index key for the input.
217
- # uses object_id so that both a string filename or an IO stream can be
218
- # indexed and reused provided the same object gets used in multiple page
219
- # template calls.
220
- def indexing_key(input)
221
- input.object_id
222
- end
223
-
224
- # returns a nested array of object IDs for all pages in this object store.
225
- #
226
- def get_page_objects(obj)
227
- if obj.data[:Type] == :Page
228
- obj.identifier
229
- elsif obj.data[:Type] == :Pages
230
- obj.data[:Kids].map { |kid| get_page_objects(kid) }
231
- end
232
- end
233
-
234
- # takes a source PDF and uses it as a template for this document.
235
- #
236
- def load_file(template)
237
- unless (template.respond_to?(:seek) && template.respond_to?(:read)) ||
238
- File.file?(template)
239
- raise ArgumentError, "#{template} does not exist"
240
- end
241
-
242
- hash = PDF::Reader::ObjectHash.new(template)
243
- src_info = hash.trailer[:Info]
244
- src_root = hash.trailer[:Root]
245
- @min_version = hash.pdf_version.to_f
246
-
247
- if hash.trailer[:Encrypt]
248
- msg = "Template file is an encrypted PDF, it can't be used as a template"
249
- raise PDF::Core::Errors::TemplateError, msg
250
- end
251
-
252
- if src_info
253
- @info = load_object_graph(hash, src_info).identifier
254
- end
255
-
256
- if src_root
257
- @root = load_object_graph(hash, src_root).identifier
258
- end
259
- rescue PDF::Reader::MalformedPDFError, PDF::Reader::InvalidObjectError => e
260
- msg = "Error reading template file. If you are sure it's a valid PDF, it may be a bug.\n#{e.message}"
261
- raise PDF::Core::Errors::TemplateError, msg
262
- rescue PDF::Reader::UnsupportedFeatureError
263
- msg = "Template file contains unsupported PDF features"
264
- raise PDF::Core::Errors::TemplateError, msg
265
- end
266
-
267
- # recurse down an object graph from a source PDF, importing all the
268
- # indirect objects we find.
269
- #
270
- # hash is the PDF::Reader::ObjectHash to extract objects from, object is
271
- # the object to extract.
272
- #
273
- def load_object_graph(hash, object)
274
- @loaded_objects ||= {}
275
- case object
276
- when ::Hash then
277
- object.each { |key,value| object[key] = load_object_graph(hash, value) }
278
- object
279
- when Array then
280
- object.map { |item| load_object_graph(hash, item)}
281
- when PDF::Reader::Reference then
282
- unless @loaded_objects.has_key?(object.id)
283
- @loaded_objects[object.id] = ref(nil)
284
- new_obj = load_object_graph(hash, hash[object])
285
- if new_obj.kind_of?(PDF::Reader::Stream)
286
- stream_dict = load_object_graph(hash, new_obj.hash)
287
- @loaded_objects[object.id].data = stream_dict
288
- @loaded_objects[object.id] << new_obj.data
289
- else
290
- @loaded_objects[object.id].data = new_obj
291
- end
292
- end
293
- @loaded_objects[object.id]
294
- when PDF::Reader::Stream
295
- # Stream is a subclass of string, so this is here to prevent the stream
296
- # being wrapped in a LiteralString
297
- object
298
- when String
299
- is_utf8?(object) ? object : PDF::Core::ByteString.new(object)
300
- else
301
- object
302
- end
303
- end
304
-
305
- def is_utf8?(str)
306
- str.force_encoding(::Encoding::UTF_8)
307
- str.valid_encoding?
308
- end
309
- end
310
- end
311
- end