prawn-templates 0.0.4 → 0.0.5

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.
data/LICENSE ADDED
@@ -0,0 +1,56 @@
1
+ Prawn::Templates is copyrighted free software produced by James Healy
2
+ along with community contributions.
3
+
4
+ Licensing terms follow:
5
+
6
+ You can redistribute Prawn::Templates and/or modify it under either the terms of the GPLv2
7
+ or GPLv3 (see GPLv2 and GPLv3 files), or the conditions below:
8
+
9
+ 1. You may make and give away verbatim copies of the source form of the
10
+ software without restriction, provided that you duplicate all of the
11
+ original copyright notices and associated disclaimers.
12
+
13
+ 2. You may modify your copy of the software in any way, provided that
14
+ you do at least ONE of the following:
15
+
16
+ a) place your modifications in the Public Domain or otherwise
17
+ make them Freely Available, such as by posting said
18
+ modifications to Usenet or an equivalent medium, or by allowing
19
+ the author to include your modifications in the software.
20
+
21
+ b) use the modified software only within your corporation or
22
+ organization.
23
+
24
+ c) rename any non-standard executables so the names do not conflict
25
+ with standard executables, which must also be provided.
26
+
27
+ d) make other distribution arrangements with the author.
28
+
29
+ 3. You may distribute the software in object code or executable
30
+ form, provided that you do at least ONE of the following:
31
+
32
+ a) distribute the executables and library files of the software,
33
+ together with instructions (in the manual page or equivalent)
34
+ on where to get the original distribution.
35
+
36
+ b) accompany the distribution with the machine-readable source of
37
+ the software.
38
+
39
+ c) give non-standard executables non-standard names, with
40
+ instructions on where to get the original software distribution.
41
+
42
+ d) make other distribution arrangements with the author.
43
+
44
+ 4. You may modify and include the part of the software into any other
45
+ software (possibly commercial).
46
+
47
+ 5. The scripts and library files supplied as input to or produced as
48
+ output from the software do not automatically fall under the
49
+ copyright of the software, but belong to whomever generated them,
50
+ and may be sold commercially, and may be aggregated with this
51
+ software.
52
+
53
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
54
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
55
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
56
+ PURPOSE.
@@ -5,28 +5,36 @@ module PDF
5
5
  normalize_metadata(options)
6
6
 
7
7
  if options[:template]
8
- if options[:print_scaling]
9
- @store = PDF::Core::ObjectStore.new(:template => options[:template], :print_scaling => options[:print_scaling])
10
- else
11
- @store = PDF::Core::ObjectStore.new(:template => options[:template])
12
- end
8
+ @store =
9
+ if options[:print_scaling]
10
+ PDF::Core::ObjectStore.new(
11
+ template: options[:template],
12
+ print_scaling: options[:print_scaling]
13
+ )
14
+ else
15
+ PDF::Core::ObjectStore.new(template: options[:template])
16
+ end
13
17
  @store.info.data.merge!(options[:info]) if options[:info]
14
18
  else
15
- if options[:print_scaling]
16
- @store = PDF::Core::ObjectStore.new(:info => options[:info], :print_scaling => options[:print_scaling])
17
- else
18
- @store = PDF::Core::ObjectStore.new(:info => options[:info])
19
- end
19
+ @store =
20
+ if options[:print_scaling]
21
+ PDF::Core::ObjectStore.new(
22
+ info: options[:info],
23
+ print_scaling: options[:print_scaling]
24
+ )
25
+ else
26
+ PDF::Core::ObjectStore.new(info: options[:info])
27
+ end
20
28
  end
21
29
 
22
- @version = 1.3
23
- @pages = []
24
- @page = nil
25
- @trailer = options.fetch(:trailer, {})
26
- @compress = options.fetch(:compress, false)
27
- @encrypt = options.fetch(:encrypt, false)
28
- @encryption_key = options[:encryption_key]
29
- @skip_encoding = options.fetch(:skip_encoding, false)
30
+ @version = 1.3
31
+ @pages = []
32
+ @page = nil
33
+ @trailer = options.fetch(:trailer, {})
34
+ @compress = options.fetch(:compress, false)
35
+ @encrypt = options.fetch(:encrypt, false)
36
+ @encryption_key = options[:encryption_key]
37
+ @skip_encoding = options.fetch(:skip_encoding, false)
30
38
  @before_render_callbacks = []
31
39
  @on_page_create_callback = nil
32
40
  end
@@ -8,14 +8,19 @@ module PDF
8
8
  load_file(opts[:template]) if opts[:template]
9
9
 
10
10
  @info ||= ref(opts[:info] || {}).identifier
11
- @root ||= ref(:Type => :Catalog).identifier
11
+ @root ||= ref(Type: :Catalog).identifier
12
12
  if opts[:print_scaling] == :none
13
- root.data[:ViewerPreferences] = { :PrintScaling => :None }
13
+ root.data[:ViewerPreferences] = { PrintScaling: :None }
14
14
  end
15
15
  if pages.nil?
16
- root.data[:Pages] = ref(:Type => :Pages, :Count => 0, :Kids => [])
16
+ root.data[:Pages] = ref(Type: :Pages, Count: 0, Kids: [])
17
17
  end
18
18
  end
19
+
20
+ def utf8?(str)
21
+ str.force_encoding(::Encoding::UTF_8)
22
+ str.valid_encoding?
23
+ end
19
24
  end
20
25
  end
21
26
  end
data/lib/pdf/core/page.rb CHANGED
@@ -1,9 +1,29 @@
1
1
  module PDF
2
2
  module Core
3
3
  class Page #:nodoc:
4
- # As per the PDF spec, each page can have multiple content streams. This will
5
- # add a fresh, empty content stream this the page, mainly for use in loading
6
- # template files.
4
+ def initialize(document, options = {})
5
+ @document = document
6
+ @margins = options[:margins] || {
7
+ left: 36,
8
+ right: 36,
9
+ top: 36,
10
+ bottom: 36
11
+ }
12
+ @crops = options[:crops] || ZERO_INDENTS
13
+ @bleeds = options[:bleeds] || ZERO_INDENTS
14
+ @trims = options[:trims] || ZERO_INDENTS
15
+ @art_indents = options[:art_indents] || ZERO_INDENTS
16
+ @stack = GraphicStateStack.new(options[:graphic_state])
17
+ if options[:object_id]
18
+ init_from_object(options)
19
+ else
20
+ init_new_page(options)
21
+ end
22
+ end
23
+
24
+ # As per the PDF spec, each page can have multiple content streams. This
25
+ # will add a fresh, empty content stream this the page, mainly for use in
26
+ # loading template files.
7
27
  #
8
28
  def new_content_stream
9
29
  return if in_stamp_stream?
@@ -11,22 +31,68 @@ module PDF
11
31
  unless dictionary.data[:Contents].is_a?(Array)
12
32
  dictionary.data[:Contents] = [content]
13
33
  end
14
- @content = document.ref({})
34
+ @content = document.ref({})
15
35
  dictionary.data[:Contents] << document.state.store[@content]
16
36
  document.open_graphics_state
17
37
  end
18
38
 
39
+ def imported_page?
40
+ @imported_page
41
+ end
42
+
43
+ def dimensions
44
+ return inherited_dictionary_value(:MediaBox) if imported_page?
45
+
46
+ coords = PDF::Core::PageGeometry::SIZES[size] || size
47
+ [0, 0] +
48
+ case layout
49
+ when :portrait
50
+ coords
51
+ when :landscape
52
+ coords.reverse
53
+ else
54
+ raise PDF::Core::Errors::InvalidPageLayout,
55
+ 'Layout must be either :portrait or :landscape'
56
+ end
57
+ end
58
+
19
59
  def init_from_object(options)
20
60
  @dictionary = options[:object_id].to_i
21
- dictionary.data[:Parent] = document.state.store.pages if options[:page_template]
61
+ if options[:page_template]
62
+ dictionary.data[:Parent] = document.state.store.pages
63
+ end
22
64
 
23
65
  unless dictionary.data[:Contents].is_a?(Array) # content only on leafs
24
- @content = dictionary.data[:Contents].identifier
66
+ @content = dictionary.data[:Contents].identifier
25
67
  end
26
68
 
27
- @stamp_stream = nil
28
- @stamp_dictionary = nil
29
- @imported_page = true
69
+ @stamp_stream = nil
70
+ @stamp_dictionary = nil
71
+ @imported_page = true
72
+ end
73
+
74
+ def init_new_page(options)
75
+ @size = options[:size] || 'LETTER'
76
+ @layout = options[:layout] || :portrait
77
+
78
+ @stamp_stream = nil
79
+ @stamp_dictionary = nil
80
+ @imported_page = false
81
+
82
+ @content = document.ref({})
83
+ content << 'q' << "\n"
84
+ @dictionary = document.ref(
85
+ Type: :Page,
86
+ Parent: document.state.store.pages,
87
+ MediaBox: dimensions,
88
+ CropBox: crop_box,
89
+ BleedBox: bleed_box,
90
+ TrimBox: trim_box,
91
+ ArtBox: art_box,
92
+ Contents: content
93
+ )
94
+
95
+ resources[:ProcSet] = [:PDF, :Text, :ImageB, :ImageC, :ImageI]
30
96
  end
31
97
  end
32
98
  end
@@ -1,7 +1,7 @@
1
1
  module Prawn
2
2
  class Document
3
3
  module Internals
4
- delegate [ :open_graphics_state ] => :renderer
4
+ delegate [:open_graphics_state] => :renderer
5
5
 
6
6
  # adds a new, empty content stream to each page. Used in templating so
7
7
  # that imported content streams can be left pristine
@@ -3,12 +3,12 @@ require 'pdf/reader'
3
3
  require 'pdf/core'
4
4
  require 'prawn/text'
5
5
 
6
- require_relative "../pdf/core/document_state"
7
- require_relative "../pdf/core/errors"
8
- require_relative "../pdf/core/object_store"
9
- require_relative "../pdf/core/page"
10
- require_relative "text"
11
- require_relative "document/internals"
6
+ require_relative '../pdf/core/document_state'
7
+ require_relative '../pdf/core/errors'
8
+ require_relative '../pdf/core/object_store'
9
+ require_relative '../pdf/core/page'
10
+ require_relative 'text'
11
+ require_relative 'document/internals'
12
12
 
13
13
  module Prawn
14
14
  # @private
@@ -26,24 +26,28 @@ module Prawn
26
26
  def start_new_page(options = {})
27
27
  return super unless options[:template]
28
28
 
29
- if last_page = state.page
30
- last_page_size = last_page.size
31
- last_page_layout = last_page.layout
29
+ last_page = state.page
30
+ if last_page
31
+ last_page_size = last_page.size
32
+ last_page_layout = last_page.layout
32
33
  last_page_margins = last_page.margins.dup
33
34
  end
34
35
 
35
36
  page_options = {
36
- :size => options[:size] || last_page_size,
37
- :layout => options[:layout] || last_page_layout,
38
- :margins => last_page_margins
37
+ size: options[:size] || last_page_size,
38
+ layout: options[:layout] || last_page_layout,
39
+ margins: last_page_margins
39
40
  }
40
41
  if last_page
41
- new_graphic_state = last_page.graphic_state.dup if last_page.graphic_state
42
+ if last_page.graphic_state
43
+ new_graphic_state = last_page.graphic_state.dup
44
+ end
42
45
 
43
- # erase the color space so that it gets reset on new page for fussy pdf-readers
46
+ # erase the color space so that it gets reset on new page for fussy
47
+ # pdf-readers
44
48
  new_graphic_state.color_space = {} if new_graphic_state
45
49
 
46
- page_options.merge!(:graphic_state => new_graphic_state)
50
+ page_options[:graphic_state] = new_graphic_state
47
51
  end
48
52
 
49
53
  merge_template_options(page_options, options)
@@ -67,7 +71,11 @@ module Prawn
67
71
  state.insert_page(state.page, @page_number)
68
72
  @page_number += 1
69
73
 
70
- canvas { image(@background, :scale => @background_scale, :at => bounds.top_left) } if @background
74
+ if @background
75
+ canvas do
76
+ image(@background, scale: @background_scale, at: bounds.top_left)
77
+ end
78
+ end
71
79
  @y = @bounding_box.absolute_top
72
80
 
73
81
  float do
@@ -77,8 +85,11 @@ module Prawn
77
85
  end
78
86
 
79
87
  def merge_template_options(page_options, options)
80
- object_id = state.store.import_page(options[:template], options[:template_page] || 1)
81
- page_options.merge!(:object_id => object_id, :page_template => true)
88
+ object_id = state.store.import_page(
89
+ options[:template],
90
+ options[:template_page] || 1
91
+ )
92
+ page_options.merge!(object_id: object_id, page_template: true)
82
93
  end
83
94
 
84
95
  module ObjectStoreExtensions
@@ -96,32 +107,37 @@ module Prawn
96
107
  #
97
108
  def import_page(input, page_num)
98
109
  @loaded_objects = {}
99
- if template_id = indexed_template(input, page_num)
100
- return template_id
101
- end
110
+ template_id = indexed_template(input, page_num)
111
+ return template_id if template_id
102
112
 
103
113
  io = if input.respond_to?(:seek) && input.respond_to?(:read)
104
114
  input
105
115
  elsif File.file?(input.to_s)
106
116
  StringIO.new(File.binread(input.to_s))
107
117
  else
108
- fail ArgumentError, "input must be an IO-like object or a filename"
118
+ raise ArgumentError, 'input must be an IO-like object or a ' \
119
+ 'filename'
109
120
  end
110
121
 
111
122
  hash = indexed_hash(input, io)
112
- ref = hash.page_references[page_num - 1]
123
+ ref = hash.page_references[page_num - 1]
113
124
 
114
125
  if ref.nil?
115
126
  nil
116
127
  else
117
- index_template(input, page_num, load_object_graph(hash, ref).identifier)
128
+ index_template(
129
+ input, page_num,
130
+ load_object_graph(hash, ref).identifier
131
+ )
118
132
  end
119
133
 
120
- rescue PDF::Reader::MalformedPDFError, PDF::Reader::InvalidObjectError => e
121
- msg = "Error reading template file. If you are sure it's a valid PDF, it may be a bug.\n#{e.message}"
134
+ rescue PDF::Reader::MalformedPDFError,
135
+ PDF::Reader::InvalidObjectError => e
136
+ msg = 'Error reading template file. If you are sure it\'s a valid PDF,'\
137
+ " it may be a bug.\n#{e.message}"
122
138
  raise PDF::Core::Errors::TemplateError, msg
123
139
  rescue PDF::Reader::UnsupportedFeatureError
124
- msg = "Template file contains unsupported PDF features"
140
+ msg = 'Template file contains unsupported PDF features'
125
141
  raise PDF::Core::Errors::TemplateError, msg
126
142
  end
127
143
 
@@ -181,8 +197,8 @@ module Prawn
181
197
  #
182
198
  def load_file(template)
183
199
  unless (template.respond_to?(:seek) && template.respond_to?(:read)) ||
184
- File.file?(template)
185
- fail ArgumentError, "#{template} does not exist"
200
+ File.file?(template)
201
+ raise ArgumentError, "#{template} does not exist"
186
202
  end
187
203
 
188
204
  hash = PDF::Reader::ObjectHash.new(template)
@@ -191,8 +207,9 @@ module Prawn
191
207
  @min_version = hash.pdf_version.to_f
192
208
 
193
209
  if hash.trailer[:Encrypt]
194
- msg = "Template file is an encrypted PDF, it can't be used as a template"
195
- fail PDF::Core::Errors::TemplateError, msg
210
+ msg = 'Template file is an encrypted PDF, it can\'t be used as a '\
211
+ 'template'
212
+ raise PDF::Core::Errors::TemplateError, msg
196
213
  end
197
214
 
198
215
  if src_info
@@ -202,11 +219,13 @@ module Prawn
202
219
  if src_root
203
220
  @root = load_object_graph(hash, src_root).identifier
204
221
  end
205
- rescue PDF::Reader::MalformedPDFError, PDF::Reader::InvalidObjectError => e
206
- msg = "Error reading template file. If you are sure it's a valid PDF, it may be a bug.\n#{e.message}"
222
+ rescue PDF::Reader::MalformedPDFError,
223
+ PDF::Reader::InvalidObjectError => e
224
+ msg = 'Error reading template file. If you are sure it\'s a valid PDF,'\
225
+ " it may be a bug.\n#{e.message}"
207
226
  raise PDF::Core::Errors::TemplateError, msg
208
227
  rescue PDF::Reader::UnsupportedFeatureError
209
- msg = "Template file contains unsupported PDF features"
228
+ msg = 'Template file contains unsupported PDF features'
210
229
  raise PDF::Core::Errors::TemplateError, msg
211
230
  end
212
231
 
@@ -220,7 +239,9 @@ module Prawn
220
239
  @loaded_objects ||= {}
221
240
  case object
222
241
  when ::Hash then
223
- object.each { |key, value| object[key] = load_object_graph(hash, value) }
242
+ object.each do |key, value|
243
+ object[key] = load_object_graph(hash, value)
244
+ end
224
245
  object
225
246
  when Array then
226
247
  object.map { |item| load_object_graph(hash, item) }
@@ -228,7 +249,7 @@ module Prawn
228
249
  unless @loaded_objects.key?(object.id)
229
250
  @loaded_objects[object.id] = ref(nil)
230
251
  new_obj = load_object_graph(hash, hash[object])
231
- if new_obj.kind_of?(PDF::Reader::Stream)
252
+ if new_obj.is_a?(PDF::Reader::Stream)
232
253
  stream_dict = load_object_graph(hash, new_obj.hash)
233
254
  @loaded_objects[object.id].data = stream_dict
234
255
  @loaded_objects[object.id] << new_obj.data
@@ -238,11 +259,11 @@ module Prawn
238
259
  end
239
260
  @loaded_objects[object.id]
240
261
  when PDF::Reader::Stream
241
- # Stream is a subclass of string, so this is here to prevent the stream
242
- # being wrapped in a LiteralString
262
+ # Stream is a subclass of string, so this is here to prevent the
263
+ # stream being wrapped in a LiteralString
243
264
  object
244
265
  when String
245
- is_utf8?(object) ? object : PDF::Core::ByteString.new(object)
266
+ utf8?(object) ? object : PDF::Core::ByteString.new(object)
246
267
  else
247
268
  object
248
269
  end
@@ -251,7 +272,17 @@ module Prawn
251
272
  end
252
273
  end
253
274
 
254
- Prawn::Document::VALID_OPTIONS << :template
275
+ if Prawn::Document::VALID_OPTIONS.frozen?
276
+ Prawn::Document.const_set(
277
+ :VALID_OPTIONS,
278
+ (Prawn::Document.send(
279
+ :remove_const,
280
+ :VALID_OPTIONS
281
+ ).dup << :template).freeze
282
+ )
283
+ else
284
+ Prawn::Document::VALID_OPTIONS << :template
285
+ end
255
286
  Prawn::Document.extensions << Prawn::Templates
256
287
 
257
288
  PDF::Core::ObjectStore.send(:include, Prawn::Templates::ObjectStoreExtensions)