prawn 0.14.0 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
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,315 +0,0 @@
1
- module PDF
2
- module Core
3
- # The Outline class organizes the outline tree items for the document.
4
- # Note that the prev and parent instance variables are adjusted while navigating
5
- # through the nested blocks. These variables along with the presence or absense
6
- # of blocks are the primary means by which the relations for the various
7
- # OutlineItems and the OutlineRoot are set. Unfortunately, the best way to
8
- # understand how this works is to follow the method calls through a real example.
9
- #
10
- # Some ideas for the organization of this class were gleaned from name_tree. In
11
- # particular the way in which the OutlineItems are finally rendered into document
12
- # objects in PdfObject through a hash.
13
- #
14
- class Outline
15
-
16
- extend Forwardable
17
- def_delegator :@document, :page_number
18
-
19
- attr_accessor :parent
20
- attr_accessor :prev
21
- attr_accessor :document
22
- attr_accessor :items
23
-
24
- def initialize(document)
25
- @document = document
26
- @parent = root
27
- @prev = nil
28
- @items = {}
29
- end
30
-
31
- # Defines/Updates an outline for the document.
32
- # The outline is an optional nested index that appears on the side of a PDF
33
- # document usually with direct links to pages. The outline DSL is defined by nested
34
- # blocks involving two methods: section and page; see the documentation on those methods
35
- # for their arguments and options. Note that one can also use outline#update
36
- # to add more sections to the end of the outline tree using the same syntax and scope.
37
- #
38
- # The syntax is best illustrated with an example:
39
- #
40
- # Prawn::Document.generate(outlined_document.pdf) do
41
- # text "Page 1. This is the first Chapter. "
42
- # start_new_page
43
- # text "Page 2. More in the first Chapter. "
44
- # start_new_page
45
- # outline.define do
46
- # section 'Chapter 1', :destination => 1, :closed => true do
47
- # page :destination => 1, :title => 'Page 1'
48
- # page :destination => 2, :title => 'Page 2'
49
- # end
50
- # end
51
- # start_new_page do
52
- # outline.update do
53
- # section 'Chapter 2', :destination => 2, do
54
- # page :destination => 3, :title => 'Page 3'
55
- # end
56
- # end
57
- # end
58
- #
59
- def define(&block)
60
- instance_eval(&block) if block
61
- end
62
-
63
- alias :update :define
64
-
65
- # Inserts an outline section to the outline tree (see outline#define).
66
- # Although you will probably choose to exclusively use outline#define so
67
- # that your outline tree is contained and easy to manage, this method
68
- # gives you the option to insert sections to the outline tree at any point
69
- # during document generation. This method allows you to add a child subsection
70
- # to any other item at any level in the outline tree.
71
- # Currently the only way to locate the place of entry is with the title for the
72
- # item. If your title names are not unique consider using define_outline.
73
- # The method takes the following arguments:
74
- # title: a string that must match an outline title to add the subsection to
75
- # position: either :first or :last(the default) where the subsection will be placed relative
76
- # to other child elements. If you need to position your subsection in between
77
- # other elements then consider using #insert_section_after
78
- # block: uses the same DSL syntax as outline#define, for example:
79
- #
80
- # Consider using this method inside of outline.update if you want to have the outline object
81
- # to be scoped as self (see #insert_section_after example).
82
- #
83
- # go_to_page 2
84
- # start_new_page
85
- # text "Inserted Page"
86
- # outline.add_subsection_to :title => 'Page 2', :first do
87
- # outline.page :destination => page_number, :title => "Inserted Page"
88
- # end
89
- #
90
- def add_subsection_to(title, position = :last, &block)
91
- @parent = items[title]
92
- raise Prawn::Errors::UnknownOutlineTitle,
93
- "\n No outline item with title: '#{title}' exists in the outline tree" unless @parent
94
- @prev = position == :first ? nil : @parent.data.last
95
- nxt = position == :first ? @parent.data.first : nil
96
- insert_section(nxt, &block)
97
- end
98
-
99
- # Inserts an outline section to the outline tree (see outline#define).
100
- # Although you will probably choose to exclusively use outline#define so
101
- # that your outline tree is contained and easy to manage, this method
102
- # gives you the option to insert sections to the outline tree at any point
103
- # during document generation. Unlike outline.add_section, this method allows
104
- # you to enter a section after any other item at any level in the outline tree.
105
- # Currently the only way to locate the place of entry is with the title for the
106
- # item. If your title names are not unique consider using define_outline.
107
- # The method takes the following arguments:
108
- # title: the title of other section or page to insert new section after
109
- # block: uses the same DSL syntax as outline#define, for example:
110
- #
111
- # go_to_page 2
112
- # start_new_page
113
- # text "Inserted Page"
114
- # update_outline do
115
- # insert_section_after :title => 'Page 2' do
116
- # page :destination => page_number, :title => "Inserted Page"
117
- # end
118
- # end
119
- #
120
- def insert_section_after(title, &block)
121
- @prev = items[title]
122
- raise Prawn::Errors::UnknownOutlineTitle,
123
- "\n No outline item with title: '#{title}' exists in the outline tree" unless @prev
124
- @parent = @prev.data.parent
125
- nxt = @prev.data.next
126
- insert_section(nxt, &block)
127
- end
128
-
129
- # See outline#define above for documentation on how this is used in that context
130
- #
131
- # Adds an outine section to the outline tree.
132
- # Although you will probably choose to exclusively use outline#define so
133
- # that your outline tree is contained and easy to manage, this method
134
- # gives you the option to add sections to the outline tree at any point
135
- # during document generation. When not being called from within another #section block
136
- # the section will be added at the top level after the other root elements of the outline.
137
- # For more flexible placement try using outline#insert_section_after and/or
138
- # outline#add_subsection_to
139
- # Takes the following arguments:
140
- # title: the outline text that appears for the section.
141
- # options: destination - optional integer defining the page number for a destination link
142
- # to the top of the page (using a :FIT destination).
143
- # - or an array with a custom destination (see the #dest_* methods of the
144
- # PDF::Destination module)
145
- # closed - whether the section should show its nested outline elements.
146
- # - defaults to false.
147
- # block: more nested subsections and/or page blocks
148
- #
149
- # example usage:
150
- #
151
- # outline.section 'Added Section', :destination => 3 do
152
- # outline.page :destionation => 3, :title => 'Page 3'
153
- # end
154
- def section(title, options = {}, &block)
155
- add_outline_item(title, options, &block)
156
- end
157
-
158
- # See Outline#define above for more documentation on how it is used in that context
159
- #
160
- # Adds a page to the outline.
161
- # Although you will probably choose to exclusively use outline#define so
162
- # that your outline tree is contained and easy to manage, this method also
163
- # gives you the option to add pages to the root of outline tree at any point
164
- # during document generation. Note that the page will be added at the
165
- # top level after the other root outline elements. For more flexible placement try
166
- # using outline#insert_section_after and/or outline#add_subsection_to.
167
- #
168
- # Takes the following arguments:
169
- # options:
170
- # title - REQUIRED. The outline text that appears for the page.
171
- # destination - optional integer defining the page number for a destination link
172
- # to the top of the page (using a :FIT destination).
173
- # - or an array with a custom destination (see the #dest_* methods of the
174
- # PDF::Destination module)
175
- # closed - whether the section should show its nested outline elements.
176
- # - defaults to false.
177
- # example usage:
178
- #
179
- # outline.page :title => "Very Last Page"
180
- # Note: this method is almost identical to section except that it does not accept a block
181
- # thereby defining the outline item as a leaf on the outline tree structure.
182
- def page(options = {})
183
- if options[:title]
184
- title = options[:title]
185
- else
186
- raise Prawn::Errors::RequiredOption,
187
- "\nTitle is a required option for page"
188
- end
189
- add_outline_item(title, options)
190
- end
191
-
192
- private
193
-
194
- # The Outline dictionary (12.3.3) for this document. It is
195
- # lazily initialized, so that documents that do not have an outline
196
- # do not incur the additional overhead.
197
- def root
198
- document.state.store.root.data[:Outlines] ||= document.ref!(OutlineRoot.new)
199
- end
200
-
201
- def add_outline_item(title, options, &block)
202
- outline_item = create_outline_item(title, options)
203
- set_relations(outline_item)
204
- increase_count
205
- set_variables_for_block(outline_item, block)
206
- block.call if block
207
- reset_parent(outline_item)
208
- end
209
-
210
- def create_outline_item(title, options)
211
- outline_item = OutlineItem.new(title, parent, options)
212
-
213
- case options[:destination]
214
- when Integer
215
- page_index = options[:destination] - 1
216
- outline_item.dest = [document.state.pages[page_index].dictionary, :Fit]
217
- when Array
218
- outline_item.dest = options[:destination]
219
- end
220
-
221
- outline_item.prev = prev if @prev
222
- items[title] = document.ref!(outline_item)
223
- end
224
-
225
- def set_relations(outline_item)
226
- prev.data.next = outline_item if prev
227
- parent.data.first = outline_item unless prev
228
- parent.data.last = outline_item
229
- end
230
-
231
- def increase_count
232
- counting_parent = parent
233
- while counting_parent
234
- counting_parent.data.count += 1
235
- if counting_parent == root
236
- counting_parent = nil
237
- else
238
- counting_parent = counting_parent.data.parent
239
- end
240
- end
241
- end
242
-
243
- def set_variables_for_block(outline_item, block)
244
- self.prev = block ? nil : outline_item
245
- self.parent = outline_item if block
246
- end
247
-
248
- def reset_parent(outline_item)
249
- if parent == outline_item
250
- self.prev = outline_item
251
- self.parent = outline_item.data.parent
252
- end
253
- end
254
-
255
- def insert_section(nxt, &block)
256
- last = @parent.data.last
257
- if block
258
- block.call
259
- end
260
- adjust_relations(nxt, last)
261
- reset_root_positioning
262
- end
263
-
264
- def adjust_relations(nxt, last)
265
- if nxt
266
- nxt.data.prev = @prev
267
- @prev.data.next = nxt
268
- @parent.data.last = last
269
- end
270
- end
271
-
272
- def reset_root_positioning
273
- @parent = root
274
- @prev = root.data.last
275
- end
276
-
277
- end
278
-
279
- class OutlineRoot #:nodoc:
280
- attr_accessor :count, :first, :last
281
-
282
- def initialize
283
- @count = 0
284
- end
285
-
286
- def to_hash
287
- {:Type => :Outlines, :Count => count, :First => first, :Last => last}
288
- end
289
- end
290
-
291
- class OutlineItem #:nodoc:
292
- attr_accessor :count, :first, :last, :next, :prev, :parent, :title, :dest, :closed
293
-
294
- def initialize(title, parent, options)
295
- @closed = options[:closed]
296
- @title = title
297
- @parent = parent
298
- @count = 0
299
- end
300
-
301
- def to_hash
302
- hash = { :Title => title,
303
- :Parent => parent,
304
- :Count => closed ? -count : count }
305
- [{:First => first}, {:Last => last}, {:Next => defined?(@next) && @next},
306
- {:Prev => prev}, {:Dest => dest}].each do |h|
307
- unless h.values.first.nil?
308
- hash.merge!(h)
309
- end
310
- end
311
- hash
312
- end
313
- end
314
- end
315
- end
@@ -1,212 +0,0 @@
1
- # encoding: utf-8
2
-
3
- # prawn/core/page.rb : Implements low-level representation of a PDF page
4
- #
5
- # Copyright February 2010, Gregory Brown. All Rights Reserved.
6
- #
7
- # This is free software. Please see the LICENSE and COPYING files for details.
8
- #
9
-
10
- require_relative 'graphics_state'
11
-
12
- module PDF
13
- module Core
14
- class Page #:nodoc:
15
- attr_accessor :document, :margins, :stack
16
- attr_writer :content, :dictionary
17
-
18
- def initialize(document, options={})
19
- @document = document
20
- @margins = options[:margins] || { :left => 36,
21
- :right => 36,
22
- :top => 36,
23
- :bottom => 36 }
24
- @stack = GraphicStateStack.new(options[:graphic_state])
25
- if options[:object_id]
26
- init_from_object(options)
27
- else
28
- init_new_page(options)
29
- end
30
- end
31
-
32
- def graphic_state
33
- stack.current_state
34
- end
35
-
36
- def layout
37
- return @layout if defined?(@layout) && @layout
38
-
39
- mb = dictionary.data[:MediaBox]
40
- if mb[3] > mb[2]
41
- :portrait
42
- else
43
- :landscape
44
- end
45
- end
46
-
47
- def size
48
- defined?(@size) && @size || dimensions[2,2]
49
- end
50
-
51
- def in_stamp_stream?
52
- !!@stamp_stream
53
- end
54
-
55
- def stamp_stream(dictionary)
56
- @stamp_stream = ""
57
- @stamp_dictionary = dictionary
58
- graphic_stack_size = stack.stack.size
59
-
60
- document.save_graphics_state
61
- document.send(:freeze_stamp_graphics)
62
- yield if block_given?
63
-
64
- until graphic_stack_size == stack.stack.size
65
- document.restore_graphics_state
66
- end
67
-
68
- @stamp_dictionary << @stamp_stream
69
-
70
- @stamp_stream = nil
71
- @stamp_dictionary = nil
72
- end
73
-
74
- def content
75
- @stamp_stream || document.state.store[@content]
76
- end
77
-
78
- # As per the PDF spec, each page can have multiple content streams. This will
79
- # add a fresh, empty content stream this the page, mainly for use in loading
80
- # template files.
81
- #
82
- def new_content_stream
83
- return if in_stamp_stream?
84
-
85
- unless dictionary.data[:Contents].is_a?(Array)
86
- dictionary.data[:Contents] = [content]
87
- end
88
- @content = document.ref({})
89
- dictionary.data[:Contents] << document.state.store[@content]
90
- document.open_graphics_state
91
- end
92
-
93
- def dictionary
94
- defined?(@stamp_dictionary) && @stamp_dictionary || document.state.store[@dictionary]
95
- end
96
-
97
- def resources
98
- if dictionary.data[:Resources]
99
- document.deref(dictionary.data[:Resources])
100
- else
101
- dictionary.data[:Resources] = {}
102
- end
103
- end
104
-
105
- def fonts
106
- if resources[:Font]
107
- document.deref(resources[:Font])
108
- else
109
- resources[:Font] = {}
110
- end
111
- end
112
-
113
- def xobjects
114
- if resources[:XObject]
115
- document.deref(resources[:XObject])
116
- else
117
- resources[:XObject] = {}
118
- end
119
- end
120
-
121
- def ext_gstates
122
- if resources[:ExtGState]
123
- document.deref(resources[:ExtGState])
124
- else
125
- resources[:ExtGState] = {}
126
- end
127
- end
128
-
129
- def finalize
130
- if dictionary.data[:Contents].is_a?(Array)
131
- dictionary.data[:Contents].each do |stream|
132
- stream.stream.compress! if document.compression_enabled?
133
- end
134
- else
135
- content.stream.compress! if document.compression_enabled?
136
- end
137
- end
138
-
139
- def imported_page?
140
- @imported_page
141
- end
142
-
143
- def dimensions
144
- return inherited_dictionary_value(:MediaBox) if imported_page?
145
-
146
- coords = PDF::Core::PageGeometry::SIZES[size] || size
147
- [0,0] + case(layout)
148
- when :portrait
149
- coords
150
- when :landscape
151
- coords.reverse
152
- else
153
- raise PDF::Core::Errors::InvalidPageLayout,
154
- "Layout must be either :portrait or :landscape"
155
- end
156
- end
157
-
158
- private
159
-
160
- def init_from_object(options)
161
- @dictionary = options[:object_id].to_i
162
- dictionary.data[:Parent] = document.state.store.pages if options[:page_template]
163
-
164
- unless dictionary.data[:Contents].is_a?(Array) # content only on leafs
165
- @content = dictionary.data[:Contents].identifier
166
- end
167
-
168
- @stamp_stream = nil
169
- @stamp_dictionary = nil
170
- @imported_page = true
171
- end
172
-
173
- def init_new_page(options)
174
- @size = options[:size] || "LETTER"
175
- @layout = options[:layout] || :portrait
176
-
177
- @stamp_stream = nil
178
- @stamp_dictionary = nil
179
- @imported_page = false
180
-
181
- @content = document.ref({})
182
- content << "q" << "\n"
183
- @dictionary = document.ref(:Type => :Page,
184
- :Parent => document.state.store.pages,
185
- :MediaBox => dimensions,
186
- :Contents => content)
187
-
188
- resources[:ProcSet] = [:PDF, :Text, :ImageB, :ImageC, :ImageI]
189
- end
190
-
191
- # some entries in the Page dict can be inherited from parent Pages dicts.
192
- #
193
- # Starting with the current page dict, this method will walk up the
194
- # inheritance chain return the first value that is found for key
195
- #
196
- # inherited_dictionary_value(:MediaBox)
197
- # => [ 0, 0, 595, 842 ]
198
- #
199
- def inherited_dictionary_value(key, local_dict = nil)
200
- local_dict ||= dictionary.data
201
-
202
- if local_dict.has_key?(key)
203
- local_dict[key]
204
- elsif local_dict.has_key?(:Parent)
205
- inherited_dictionary_value(key, local_dict[:Parent].data)
206
- else
207
- nil
208
- end
209
- end
210
- end
211
- end
212
- end