pdf-core 0.5.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  # Describes PDF page geometries
4
4
  #
@@ -8,7 +8,6 @@
8
8
 
9
9
  module PDF
10
10
  module Core
11
-
12
11
  # Dimensions pulled from PDF::Writer, rubyforge.org/projects/ruby-pdf
13
12
  #
14
13
  # All of these dimensions are in PDF Points (1/72 inch)
@@ -68,59 +67,58 @@ module PDF
68
67
  # TABLOID:: => 792.00 x 1224.00
69
68
  #
70
69
  module PageGeometry
71
-
72
- SIZES = { "4A0" => [4767.87, 6740.79],
73
- "2A0" => [3370.39, 4767.87],
74
- "A0" => [2383.94, 3370.39],
75
- "A1" => [1683.78, 2383.94],
76
- "A2" => [1190.55, 1683.78],
77
- "A3" => [841.89, 1190.55],
78
- "A4" => [595.28, 841.89],
79
- "A5" => [419.53, 595.28],
80
- "A6" => [297.64, 419.53],
81
- "A7" => [209.76, 297.64],
82
- "A8" => [147.40, 209.76],
83
- "A9" => [104.88, 147.40],
84
- "A10" => [73.70, 104.88],
85
- "B0" => [2834.65, 4008.19],
86
- "B1" => [2004.09, 2834.65],
87
- "B2" => [1417.32, 2004.09],
88
- "B3" => [1000.63, 1417.32],
89
- "B4" => [708.66, 1000.63],
90
- "B5" => [498.90, 708.66],
91
- "B6" => [354.33, 498.90],
92
- "B7" => [249.45, 354.33],
93
- "B8" => [175.75, 249.45],
94
- "B9" => [124.72, 175.75],
95
- "B10" => [87.87, 124.72],
96
- "C0" => [2599.37, 3676.54],
97
- "C1" => [1836.85, 2599.37],
98
- "C2" => [1298.27, 1836.85],
99
- "C3" => [918.43, 1298.27],
100
- "C4" => [649.13, 918.43],
101
- "C5" => [459.21, 649.13],
102
- "C6" => [323.15, 459.21],
103
- "C7" => [229.61, 323.15],
104
- "C8" => [161.57, 229.61],
105
- "C9" => [113.39, 161.57],
106
- "C10" => [79.37, 113.39],
107
- "RA0" => [2437.80, 3458.27],
108
- "RA1" => [1729.13, 2437.80],
109
- "RA2" => [1218.90, 1729.13],
110
- "RA3" => [864.57, 1218.90],
111
- "RA4" => [609.45, 864.57],
112
- "SRA0" => [2551.18, 3628.35],
113
- "SRA1" => [1814.17, 2551.18],
114
- "SRA2" => [1275.59, 1814.17],
115
- "SRA3" => [907.09, 1275.59],
116
- "SRA4" => [637.80, 907.09],
117
- "EXECUTIVE" => [521.86, 756.00],
118
- "FOLIO" => [612.00, 936.00],
119
- "LEGAL" => [612.00, 1008.00],
120
- "LETTER" => [612.00, 792.00],
121
- "TABLOID" => [792.00, 1224.00] }
122
-
70
+ SIZES = {
71
+ '4A0' => [4767.87, 6740.79],
72
+ '2A0' => [3370.39, 4767.87],
73
+ 'A0' => [2383.94, 3370.39],
74
+ 'A1' => [1683.78, 2383.94],
75
+ 'A2' => [1190.55, 1683.78],
76
+ 'A3' => [841.89, 1190.55],
77
+ 'A4' => [595.28, 841.89],
78
+ 'A5' => [419.53, 595.28],
79
+ 'A6' => [297.64, 419.53],
80
+ 'A7' => [209.76, 297.64],
81
+ 'A8' => [147.40, 209.76],
82
+ 'A9' => [104.88, 147.40],
83
+ 'A10' => [73.70, 104.88],
84
+ 'B0' => [2834.65, 4008.19],
85
+ 'B1' => [2004.09, 2834.65],
86
+ 'B2' => [1417.32, 2004.09],
87
+ 'B3' => [1000.63, 1417.32],
88
+ 'B4' => [708.66, 1000.63],
89
+ 'B5' => [498.90, 708.66],
90
+ 'B6' => [354.33, 498.90],
91
+ 'B7' => [249.45, 354.33],
92
+ 'B8' => [175.75, 249.45],
93
+ 'B9' => [124.72, 175.75],
94
+ 'B10' => [87.87, 124.72],
95
+ 'C0' => [2599.37, 3676.54],
96
+ 'C1' => [1836.85, 2599.37],
97
+ 'C2' => [1298.27, 1836.85],
98
+ 'C3' => [918.43, 1298.27],
99
+ 'C4' => [649.13, 918.43],
100
+ 'C5' => [459.21, 649.13],
101
+ 'C6' => [323.15, 459.21],
102
+ 'C7' => [229.61, 323.15],
103
+ 'C8' => [161.57, 229.61],
104
+ 'C9' => [113.39, 161.57],
105
+ 'C10' => [79.37, 113.39],
106
+ 'RA0' => [2437.80, 3458.27],
107
+ 'RA1' => [1729.13, 2437.80],
108
+ 'RA2' => [1218.90, 1729.13],
109
+ 'RA3' => [864.57, 1218.90],
110
+ 'RA4' => [609.45, 864.57],
111
+ 'SRA0' => [2551.18, 3628.35],
112
+ 'SRA1' => [1814.17, 2551.18],
113
+ 'SRA2' => [1275.59, 1814.17],
114
+ 'SRA3' => [907.09, 1275.59],
115
+ 'SRA4' => [637.80, 907.09],
116
+ 'EXECUTIVE' => [521.86, 756.00],
117
+ 'FOLIO' => [612.00, 936.00],
118
+ 'LEGAL' => [612.00, 1008.00],
119
+ 'LETTER' => [612.00, 792.00],
120
+ 'TABLOID' => [792.00, 1224.00]
121
+ }.freeze
123
122
  end
124
123
  end
125
124
  end
126
-
@@ -1,5 +1,5 @@
1
- # encoding: utf-8
2
- #
1
+ # frozen_string_literal: true
2
+
3
3
  # pdf_object.rb : Handles Ruby to PDF object serialization
4
4
  #
5
5
  # Copyright April 2008, Gregory Brown. All Rights Reserved.
@@ -13,89 +13,98 @@ module PDF
13
13
  module_function
14
14
 
15
15
  def real(num)
16
- num.to_f.round(4)
16
+ format('%<number>.5f', number: num).sub(/((?<!\.)0)+\z/, '')
17
17
  end
18
18
 
19
19
  def real_params(array)
20
- array.map { |e| real(e) }.join(" ")
20
+ array.map { |e| real(e) }.join(' ')
21
21
  end
22
22
 
23
23
  def utf8_to_utf16(str)
24
- "\xFE\xFF".force_encoding(::Encoding::UTF_16BE) + str.encode(::Encoding::UTF_16BE)
24
+ (+"\xFE\xFF").force_encoding(::Encoding::UTF_16BE) +
25
+ str.encode(::Encoding::UTF_16BE)
25
26
  end
26
27
 
27
28
  # encodes any string into a hex representation. The result is a string
28
29
  # with only 0-9 and a-f characters. That result is valid ASCII so tag
29
30
  # it as such to account for behaviour of different ruby VMs
30
31
  def string_to_hex(str)
31
- str.unpack("H*").first.force_encoding(::Encoding::US_ASCII)
32
+ str.unpack1('H*').force_encoding(::Encoding::US_ASCII)
32
33
  end
33
34
 
35
+ ESCAPED_NAME_CHARACTERS = (1..32).to_a + [35, 40, 41, 47, 60, 62] + (127..255).to_a
36
+
34
37
  # Serializes Ruby objects to their PDF equivalents. Most primitive objects
35
38
  # will work as expected, but please note that Name objects are represented
36
- # by Ruby Symbol objects and Dictionary objects are represented by Ruby hashes
37
- # (keyed by symbols)
39
+ # by Ruby Symbol objects and Dictionary objects are represented by Ruby
40
+ # hashes (keyed by symbols)
38
41
  #
39
42
  # Examples:
40
43
  #
41
- # PdfObject(true) #=> "true"
42
- # PdfObject(false) #=> "false"
43
- # PdfObject(1.2124) #=> "1.2124"
44
- # PdfObject("foo bar") #=> "(foo bar)"
45
- # PdfObject(:Symbol) #=> "/Symbol"
46
- # PdfObject(["foo",:bar, [1,2]]) #=> "[foo /bar [1 2]]"
44
+ # pdf_object(true) #=> "true"
45
+ # pdf_object(false) #=> "false"
46
+ # pdf_object(1.2124) #=> "1.2124"
47
+ # pdf_object('foo bar') #=> "(foo bar)"
48
+ # pdf_object(:Symbol) #=> "/Symbol"
49
+ # pdf_object(['foo',:bar, [1,2]]) #=> "[foo /bar [1 2]]"
47
50
  #
48
- def PdfObject(obj, in_content_stream = false)
49
- case(obj)
50
- when NilClass then "null"
51
- when TrueClass then "true"
52
- when FalseClass then "false"
51
+ def pdf_object(obj, in_content_stream = false)
52
+ case obj
53
+ when NilClass then 'null'
54
+ when TrueClass then 'true'
55
+ when FalseClass then 'false'
53
56
  when Numeric
54
- obj = real(obj) unless obj.kind_of?(Integer)
57
+ obj = real(obj) unless obj.is_a?(Integer)
55
58
 
56
- String(obj) # NOTE: this can fail on huge floating point numbers,
57
- # but it seems unlikely to ever happen in practice.
59
+ # NOTE: this can fail on huge floating point numbers, but it seems
60
+ # unlikely to ever happen in practice.
61
+ num_string = String(obj)
62
+
63
+ # Truncate trailing fraction zeroes
64
+ num_string.sub(/(\d*)((\.0*$)|(\.0*[1-9]*)0*$)/, '\1\4')
58
65
  when Array
59
- "[" << obj.map { |e| PdfObject(e, in_content_stream) }.join(' ') << "]"
66
+ "[#{obj.map { |e| pdf_object(e, in_content_stream) }.join(' ')}]"
60
67
  when PDF::Core::LiteralString
61
- obj = obj.gsub(/[\\\n\r\t\b\f\(\)]/n) { |m| "\\#{m}" }
68
+ obj = obj.gsub(/[\\\n\r\t\b\f()]/) { |m| "\\#{m}" }
62
69
  "(#{obj})"
63
70
  when Time
64
- obj = obj.strftime("D:%Y%m%d%H%M%S%z").chop.chop + "'00'"
65
- obj = obj.gsub(/[\\\n\r\t\b\f\(\)]/n) { |m| "\\#{m}" }
71
+ obj = "#{obj.strftime('D:%Y%m%d%H%M%S%z').chop.chop}'00'"
72
+ obj = obj.gsub(/[\\\n\r\t\b\f()]/) { |m| "\\#{m}" }
66
73
  "(#{obj})"
67
74
  when PDF::Core::ByteString
68
- "<" << obj.unpack("H*").first << ">"
75
+ "<#{obj.unpack1('H*')}>"
69
76
  when String
70
77
  obj = utf8_to_utf16(obj) unless in_content_stream
71
- "<" << string_to_hex(obj) << ">"
72
- when Symbol
73
- "/" + obj.to_s.unpack("C*").map { |n|
74
- if n < 33 || n > 126 || [35,40,41,47,60,62].include?(n)
75
- "#" + n.to_s(16).upcase
76
- else
77
- [n].pack("C*")
78
- end
79
- }.join
78
+ "<#{string_to_hex(obj)}>"
79
+ when Symbol
80
+ name_string =
81
+ obj.to_s.unpack('C*').map do |n|
82
+ if ESCAPED_NAME_CHARACTERS.include?(n)
83
+ "##{n.to_s(16).upcase}"
84
+ else
85
+ [n].pack('C*')
86
+ end
87
+ end.join
88
+ "/#{name_string}"
80
89
  when ::Hash
81
- output = "<< "
82
- obj.each do |k,v|
83
- unless String === k || Symbol === k
90
+ output = +'<< '
91
+ obj.each do |k, v|
92
+ unless k.is_a?(String) || k.is_a?(Symbol)
84
93
  raise PDF::Core::Errors::FailedObjectConversion,
85
- "A PDF Dictionary must be keyed by names"
94
+ 'A PDF Dictionary must be keyed by names'
86
95
  end
87
- output << PdfObject(k.to_sym, in_content_stream) << " " <<
88
- PdfObject(v, in_content_stream) << "\n"
96
+ output << pdf_object(k.to_sym, in_content_stream) << ' ' <<
97
+ pdf_object(v, in_content_stream) << "\n"
89
98
  end
90
- output << ">>"
99
+ output << '>>'
91
100
  when PDF::Core::Reference
92
101
  obj.to_s
93
102
  when PDF::Core::NameTree::Node
94
- PdfObject(obj.to_hash)
103
+ pdf_object(obj.to_hash)
95
104
  when PDF::Core::NameTree::Value
96
- PdfObject(obj.name) + " " + PdfObject(obj.value)
105
+ "#{pdf_object(obj.name)} #{pdf_object(obj.value)}"
97
106
  when PDF::Core::OutlineRoot, PDF::Core::OutlineItem
98
- PdfObject(obj.to_hash)
107
+ pdf_object(obj.to_hash)
99
108
  else
100
109
  raise PDF::Core::Errors::FailedObjectConversion,
101
110
  "This object cannot be serialized to PDF (#{obj.inspect})"
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  # reference.rb : Implementation of PDF indirect objects
4
4
  #
@@ -6,13 +6,19 @@
6
6
  #
7
7
  # This is free software. Please see the LICENSE and COPYING files for details.
8
8
 
9
+ require 'pdf/core/utils'
9
10
 
10
11
  module PDF
11
12
  module Core
12
13
  class Reference #:nodoc:
13
-
14
14
  attr_accessor :gen, :data, :offset, :stream, :identifier
15
15
 
16
+ class CannotAttachStream < StandardError
17
+ def initialize(message = 'Cannot attach stream to a non-dictionary object')
18
+ super
19
+ end
20
+ end
21
+
16
22
  def initialize(id, data)
17
23
  @identifier = id
18
24
  @gen = 0
@@ -21,18 +27,22 @@ module PDF
21
27
  end
22
28
 
23
29
  def object
24
- output = "#{@identifier} #{gen} obj\n"
25
- unless @stream.empty?
26
- output << PDF::Core::PdfObject(data.merge @stream.data) << "\n" << @stream.object
30
+ output = +"#{@identifier} #{gen} obj\n"
31
+ if @stream.empty?
32
+ output << PDF::Core.pdf_object(data) << "\n"
27
33
  else
28
- output << PDF::Core::PdfObject(data) << "\n"
34
+ output << PDF::Core.pdf_object(data.merge(@stream.data)) <<
35
+ "\n" << @stream.object
29
36
  end
30
37
 
31
38
  output << "endobj\n"
32
39
  end
33
40
 
34
41
  def <<(io)
35
- raise "Cannot attach stream to non-dictionary object" unless @data.is_a?(::Hash)
42
+ unless @data.is_a?(::Hash)
43
+ raise CannotAttachStream
44
+ end
45
+
36
46
  (@stream ||= Stream.new) << io
37
47
  end
38
48
 
@@ -43,22 +53,22 @@ module PDF
43
53
  # Creates a deep copy of this ref. If +share+ is provided, shares the
44
54
  # given dictionary entries between the old ref and the new.
45
55
  #
46
- def deep_copy(share=[])
56
+ def deep_copy(share = [])
47
57
  r = dup
48
58
 
49
59
  case r.data
50
60
  when ::Hash
51
61
  # Copy each entry not in +share+.
52
62
  (r.data.keys - share).each do |k|
53
- r.data[k] = Marshal.load(Marshal.dump(r.data[k]))
63
+ r.data[k] = Utils.deep_clone(r.data[k])
54
64
  end
55
65
  when PDF::Core::NameTree::Node
56
66
  r.data = r.data.deep_copy
57
67
  else
58
- r.data = Marshal.load(Marshal.dump(r.data))
68
+ r.data = Utils.deep_clone(r.data)
59
69
  end
60
70
 
61
- r.stream = Marshal.load(Marshal.dump(r.stream))
71
+ r.stream = Utils.deep_clone(r.stream)
62
72
  r
63
73
  end
64
74
 
@@ -68,11 +78,5 @@ module PDF
68
78
  @stream = other_ref.stream
69
79
  end
70
80
  end
71
-
72
- module_function
73
-
74
- def Reference(*args, &block) #:nodoc:
75
- Reference.new(*args, &block)
76
- end
77
81
  end
78
82
  end
@@ -1,4 +1,6 @@
1
- require "stringio"
1
+ # frozen_string_literal: true
2
+
3
+ require 'stringio'
2
4
 
3
5
  module PDF
4
6
  module Core
@@ -6,7 +8,7 @@ module PDF
6
8
  def initialize(state)
7
9
  @state = state
8
10
  @state.populate_pages_from_store(self)
9
-
11
+
10
12
  min_version(state.store.min_version) if state.store.min_version
11
13
 
12
14
  @page_number = 0
@@ -14,8 +16,8 @@ module PDF
14
16
 
15
17
  attr_reader :state
16
18
 
17
- # Creates a new Reference and adds it to the Document's object
18
- # list. The +data+ argument is anything that Prawn::PdfObject() can convert.
19
+ # Creates a new Reference and adds it to the Document's object list. The
20
+ # +data+ argument is anything that Prawn.pdf_object() can convert.
19
21
  #
20
22
  # Returns the identifier which points to the reference in the ObjectStore
21
23
  #
@@ -50,7 +52,7 @@ module PDF
50
52
  #
51
53
  # pdf.add_content("#{PDF::Core.real_params([x1, y1])} m") # move
52
54
  # pdf.add_content("#{PDF::Core.real_params([ x2, y2 ])} l") # draw path
53
- # pdf.add_content("S") # stroke
55
+ # pdf.add_content('S') # stroke
54
56
  #
55
57
  def add_content(str)
56
58
  save_graphics_state if graphic_state.nil?
@@ -62,7 +64,7 @@ module PDF
62
64
  # dictionary do not incur the additional overhead.
63
65
  #
64
66
  def names
65
- state.store.root.data[:Names] ||= ref!(:Type => :Names)
67
+ state.store.root.data[:Names] ||= ref!(Type: :Names)
66
68
  end
67
69
 
68
70
  # Returns true if the Names dictionary is in use for this document.
@@ -80,29 +82,36 @@ module PDF
80
82
  # Defines a block to be called just before a new page is started.
81
83
  #
82
84
  def on_page_create(&block)
83
- if block_given?
84
- state.on_page_create_callback = block
85
- else
86
- state.on_page_create_callback = nil
87
- end
85
+ state.on_page_create_callback =
86
+ if block_given?
87
+ block
88
+ end
88
89
  end
89
90
 
90
91
  def start_new_page(options = {})
91
- if last_page = state.page
92
+ last_page = state.page
93
+ if last_page
92
94
  last_page_size = last_page.size
93
95
  last_page_layout = last_page.layout
94
96
  last_page_margins = last_page.margins
95
97
  end
96
98
 
97
- page_options = {:size => options[:size] || last_page_size,
98
- :layout => options[:layout] || last_page_layout,
99
- :margins => last_page_margins}
99
+ page_options = {
100
+ size: options[:size] || last_page_size,
101
+ layout: options[:layout] || last_page_layout,
102
+ margins: last_page_margins
103
+ }
100
104
  if last_page
101
- new_graphic_state = last_page.graphic_state.dup if last_page.graphic_state
105
+ if last_page.graphic_state
106
+ new_graphic_state = last_page.graphic_state.dup
107
+ end
102
108
 
103
- #erase the color space so that it gets reset on new page for fussy pdf-readers
104
- new_graphic_state.color_space = {} if new_graphic_state
105
- page_options.merge!(:graphic_state => new_graphic_state)
109
+ # Erase the color space so that it gets reset on new page for fussy
110
+ # pdf-readers
111
+ if new_graphic_state
112
+ new_graphic_state.color_space = {}
113
+ end
114
+ page_options[:graphic_state] = new_graphic_state
106
115
  end
107
116
 
108
117
  state.page = PDF::Core::Page.new(self, page_options)
@@ -121,10 +130,10 @@ module PDF
121
130
  # draw on it.
122
131
  #
123
132
  # See Prawn::Document#number_pages for a sample usage of this capability.
124
-
125
- def go_to_page(k)
126
- @page_number = k
127
- state.page = state.pages[k-1]
133
+
134
+ def go_to_page(page_number)
135
+ @page_number = page_number
136
+ state.page = state.pages[page_number - 1]
128
137
  end
129
138
 
130
139
  def finalize_all_page_contents
@@ -161,18 +170,16 @@ module PDF
161
170
  if output.instance_of?(StringIO)
162
171
  str = output.string
163
172
  str.force_encoding(::Encoding::ASCII_8BIT)
164
- return str
165
- else
166
- return nil
173
+ str
167
174
  end
168
175
  end
169
176
 
170
177
  # Renders the PDF document to file.
171
178
  #
172
- # pdf.render_file "foo.pdf"
179
+ # pdf.render_file 'foo.pdf'
173
180
  #
174
181
  def render_file(filename)
175
- File.open(filename, "wb") { |f| render(f) }
182
+ File.open(filename, 'wb') { |f| render(f) }
176
183
  end
177
184
 
178
185
  # Write out the PDF Header, as per spec 3.4.1
@@ -201,7 +208,7 @@ module PDF
201
208
  output << "0 #{state.store.size + 1}\n"
202
209
  output << "0000000000 65535 f \n"
203
210
  state.store.each do |ref|
204
- output.printf("%010d", ref.offset)
211
+ output.printf('%<offset>010d', offset: ref.offset)
205
212
  output << " 00000 n \n"
206
213
  end
207
214
  end
@@ -209,24 +216,26 @@ module PDF
209
216
  # Write out the PDF Trailer, as per spec 3.4.4
210
217
  #
211
218
  def render_trailer(output)
212
- trailer_hash = {:Size => state.store.size + 1,
213
- :Root => state.store.root,
214
- :Info => state.store.info}
219
+ trailer_hash = {
220
+ Size: state.store.size + 1,
221
+ Root: state.store.root,
222
+ Info: state.store.info
223
+ }
215
224
  trailer_hash.merge!(state.trailer) if state.trailer
216
225
 
217
226
  output << "trailer\n"
218
- output << PDF::Core::PdfObject(trailer_hash) << "\n"
227
+ output << PDF::Core.pdf_object(trailer_hash) << "\n"
219
228
  output << "startxref\n"
220
229
  output << @xref_offset << "\n"
221
- output << "%%EOF" << "\n"
230
+ output << '%%EOF' << "\n"
222
231
  end
223
232
 
224
233
  def open_graphics_state
225
- add_content "q"
234
+ add_content 'q'
226
235
  end
227
236
 
228
237
  def close_graphics_state
229
- add_content "Q"
238
+ add_content 'Q'
230
239
  end
231
240
 
232
241
  def save_graphics_state(graphic_state = nil)
@@ -242,7 +251,7 @@ module PDF
242
251
  # false otherwise
243
252
  #
244
253
  def compression_enabled?
245
- !!state.compress
254
+ state.compress
246
255
  end
247
256
 
248
257
  # Pops the last saved graphics state off the graphics state stack and