prawn-templates 0.0.4 → 0.0.5

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