pdf-core 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/pdf/core/page.rb CHANGED
@@ -1,34 +1,93 @@
1
1
  # frozen_string_literal: true
2
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
3
  require_relative 'graphics_state'
11
4
 
12
5
  module PDF
13
6
  module Core
14
- class Page #:nodoc:
15
- attr_accessor :art_indents, :bleeds, :crops, :document, :margins, :stack, :trims
16
- attr_writer :content, :dictionary
7
+ # Low-level representation of a PDF page
8
+ #
9
+ # @api private
10
+ class Page
11
+ # Page art box indents relative to page edges.
12
+ #
13
+ # @return [Hash<[:left, :right, :top, :bottom], Numeric>
14
+ attr_accessor :art_indents
15
+
16
+ # Page bleed box indents.
17
+ #
18
+ # @return [Hash<[:left, :right, :top, :bottom], Numeric>
19
+ attr_accessor :bleeds
20
+
21
+ # Page crop box indents.
22
+ #
23
+ # @return [Hash<[:left, :right, :top, :bottom], Numeric>
24
+ attr_accessor :crops
25
+
26
+ # Page trim box indents.
27
+ #
28
+ # @return [Hash<[:left, :right, :top, :bottom], Numeric>
29
+ attr_accessor :trims
30
+
31
+ # Page margins.
32
+ #
33
+ # @return [Hash<[:left, :right, :top, :bottom], Numeric>
34
+ attr_accessor :margins
17
35
 
36
+ # Owning document.
37
+ #
38
+ # @return [Prawn::Document]
39
+ attr_accessor :document
40
+
41
+ # Graphic state stack.
42
+ #
43
+ # @return [GraphicStateStack]
44
+ attr_accessor :stack
45
+
46
+ # Page content stream reference.
47
+ #
48
+ # @return [PDF::Core::Reference<Hash>]
49
+ attr_writer :content
50
+
51
+ # Page dictionary reference.
52
+ #
53
+ # @return [PDF::Core::Reference<Hash>]
54
+ attr_writer :dictionary
55
+
56
+ # A convenince constant of no indents.
18
57
  ZERO_INDENTS = {
19
58
  left: 0,
20
59
  bottom: 0,
21
60
  right: 0,
22
- top: 0
61
+ top: 0,
23
62
  }.freeze
24
63
 
64
+ # @param document [Prawn::Document]
65
+ # @param options [Hash]
66
+ # @option options :margins [Hash{:left, :right, :top, :bottom => Number}, nil]
67
+ # ({ left: 0, right: 0, top: 0, bottom: 0 }) Page margins
68
+ # @option options :crop [Hash{:left, :right, :top, :bottom => Number}, nil] (ZERO_INDENTS)
69
+ # Page crop box
70
+ # @option options :bleed [Hash{:left, :right, :top, :bottom => Number}, nil] (ZERO_INDENTS)
71
+ # Page bleed box
72
+ # @option options :trims [Hash{:left, :right, :top, :bottom => Number}, nil] (ZERO_INDENTS)
73
+ # Page trim box
74
+ # @option options :art_indents [Hash{:left, :right, :top, :bottom => Number}, Numeric>, nil] (ZERO_INDENTS)
75
+ # Page art box indents.
76
+ # @option options :graphic_state [PDF::Core::GraphicState, nil] (nil)
77
+ # Initial graphic state
78
+ # @option options :size [String, Array<Numeric>, nil] ('LETTER')
79
+ # Page size. A string identifies a named page size defined in
80
+ # {PageGeometry}. An array must be a two element array specifying width
81
+ # and height in points.
82
+ # @option options :layout [:portrait, :landscape, nil] (:portrait)
83
+ # Page orientation.
25
84
  def initialize(document, options = {})
26
85
  @document = document
27
86
  @margins = options[:margins] || {
28
87
  left: 36,
29
88
  right: 36,
30
89
  top: 36,
31
- bottom: 36
90
+ bottom: 36,
32
91
  }
33
92
  @crops = options[:crops] || ZERO_INDENTS
34
93
  @bleeds = options[:bleeds] || ZERO_INDENTS
@@ -51,16 +110,23 @@ module PDF
51
110
  BleedBox: bleed_box,
52
111
  TrimBox: trim_box,
53
112
  ArtBox: art_box,
54
- Contents: content
113
+ Contents: content,
55
114
  )
56
115
 
57
116
  resources[:ProcSet] = %i[PDF Text ImageB ImageC ImageI]
58
117
  end
59
118
 
119
+ # Current graphic state.
120
+ #
121
+ # @return [PDF::Core::GraphicState]
60
122
  def graphic_state
61
123
  stack.current_state
62
124
  end
63
125
 
126
+ # Page layout.
127
+ #
128
+ # @return [:portrait] if page is talled than wider
129
+ # @return [:landscape] otherwise
64
130
  def layout
65
131
  return @layout if defined?(@layout) && @layout
66
132
 
@@ -72,17 +138,29 @@ module PDF
72
138
  end
73
139
  end
74
140
 
141
+ # Page size.
142
+ #
143
+ # @return [Array<Numeric>] a two-element array containing width and height
144
+ # of the page.
75
145
  def size
76
- defined?(@size) && @size || dimensions[2, 2]
146
+ (defined?(@size) && @size) || dimensions[2, 2]
77
147
  end
78
148
 
149
+ # Are we drawing to a stamp right now?
150
+ #
151
+ # @return [Boolean]
79
152
  def in_stamp_stream?
80
153
  !@stamp_stream.nil?
81
154
  end
82
155
 
156
+ # Draw to stamp.
157
+ #
158
+ # @param dictionary [PDF::Core::Reference<Hash>] stamp dictionary
159
+ # @yield outputs to the stamp
160
+ # @return [void]
83
161
  def stamp_stream(dictionary)
84
162
  @stamp_dictionary = dictionary
85
- @stamp_stream = @stamp_dictionary.stream
163
+ @stamp_stream = @stamp_dictionary.stream
86
164
  graphic_stack_size = stack.stack.size
87
165
 
88
166
  document.save_graphics_state
@@ -93,19 +171,30 @@ module PDF
93
171
  document.restore_graphics_state
94
172
  end
95
173
 
96
- @stamp_stream = nil
97
- @stamp_dictionary = nil
174
+ @stamp_stream = nil
175
+ @stamp_dictionary = nil
98
176
  end
99
177
 
178
+ # Current content stream. Can be either the page content stream or a stamp
179
+ # content stream.
180
+ #
181
+ # @return [PDF::Core::Reference<Hash>]
100
182
  def content
101
183
  @stamp_stream || document.state.store[@content]
102
184
  end
103
185
 
186
+ # Current content dictionary. Can be either the page dictionary or a stamp
187
+ # dictionary.
188
+ #
189
+ # @return [PDF::Core::Reference<Hash>]
104
190
  def dictionary
105
- defined?(@stamp_dictionary) && @stamp_dictionary ||
191
+ (defined?(@stamp_dictionary) && @stamp_dictionary) ||
106
192
  document.state.store[@dictionary]
107
193
  end
108
194
 
195
+ # Page resources dictionary.
196
+ #
197
+ # @return [Hash]
109
198
  def resources
110
199
  if dictionary.data[:Resources]
111
200
  document.deref(dictionary.data[:Resources])
@@ -114,6 +203,9 @@ module PDF
114
203
  end
115
204
  end
116
205
 
206
+ # Fonts dictionary.
207
+ #
208
+ # @return [Hash]
117
209
  def fonts
118
210
  if resources[:Font]
119
211
  document.deref(resources[:Font])
@@ -122,6 +214,9 @@ module PDF
122
214
  end
123
215
  end
124
216
 
217
+ # External objects dictionary.
218
+ #
219
+ # @return [Hash]
125
220
  def xobjects
126
221
  if resources[:XObject]
127
222
  document.deref(resources[:XObject])
@@ -130,6 +225,9 @@ module PDF
130
225
  end
131
226
  end
132
227
 
228
+ # Graphic state parameter dictionary.
229
+ #
230
+ # @return [Hash]
133
231
  def ext_gstates
134
232
  if resources[:ExtGState]
135
233
  document.deref(resources[:ExtGState])
@@ -138,6 +236,9 @@ module PDF
138
236
  end
139
237
  end
140
238
 
239
+ # Finalize page.
240
+ #
241
+ # @return [void]
141
242
  def finalize
142
243
  if dictionary.data[:Contents].is_a?(Array)
143
244
  dictionary.data[:Contents].each do |stream|
@@ -148,9 +249,12 @@ module PDF
148
249
  end
149
250
  end
150
251
 
252
+ # Page dimensions.
253
+ #
254
+ # @return [Array<Numeric>]
151
255
  def dimensions
152
256
  coords = PDF::Core::PageGeometry::SIZES[size] || size
153
- [0, 0] +
257
+ coords =
154
258
  case layout
155
259
  when :portrait
156
260
  coords
@@ -160,45 +264,66 @@ module PDF
160
264
  raise PDF::Core::Errors::InvalidPageLayout,
161
265
  'Layout must be either :portrait or :landscape'
162
266
  end
267
+ [0, 0].concat(coords)
163
268
  end
164
269
 
270
+ # A rectangle, expressed in default user space units, defining the extent
271
+ # of the page's meaningful content (including potential white space) as
272
+ # intended by the page's creator.
273
+ #
274
+ # @return [Array<Numeric>]
165
275
  def art_box
166
276
  left, bottom, right, top = dimensions
167
277
  [
168
278
  left + art_indents[:left],
169
279
  bottom + art_indents[:bottom],
170
280
  right - art_indents[:right],
171
- top - art_indents[:top]
281
+ top - art_indents[:top],
172
282
  ]
173
283
  end
174
284
 
285
+ # Page bleed box. A rectangle, expressed in default user space units,
286
+ # defining the region to which the contents of the page should be clipped
287
+ # when output in a production environment.
288
+ #
289
+ # @return [Array<Numeric>]
175
290
  def bleed_box
176
291
  left, bottom, right, top = dimensions
177
292
  [
178
293
  left + bleeds[:left],
179
294
  bottom + bleeds[:bottom],
180
295
  right - bleeds[:right],
181
- top - bleeds[:top]
296
+ top - bleeds[:top],
182
297
  ]
183
298
  end
184
299
 
300
+ # A rectangle, expressed in default user space units, defining the visible
301
+ # region of default user space. When the page is displayed or printed, its
302
+ # contents are to be clipped (cropped) to this rectangle and then imposed
303
+ # on the output medium in some implementation-defined manner.
304
+ #
305
+ # @return [Array<Numeric>]
185
306
  def crop_box
186
307
  left, bottom, right, top = dimensions
187
308
  [
188
309
  left + crops[:left],
189
310
  bottom + crops[:bottom],
190
311
  right - crops[:right],
191
- top - crops[:top]
312
+ top - crops[:top],
192
313
  ]
193
314
  end
194
315
 
316
+ # A rectangle, expressed in default user space units, defining the
317
+ # intended dimensions of the finished page after trimming.
318
+ #
319
+ # @return [Array<Numeric>]
195
320
  def trim_box
196
321
  left, bottom, right, top = dimensions
197
322
  [
198
323
  left + trims[:left],
199
324
  bottom + trims[:bottom],
200
325
  right - trims[:right],
201
- top - trims[:top]
326
+ top - trims[:top],
202
327
  ]
203
328
  end
204
329
 
@@ -9,64 +9,10 @@
9
9
  module PDF
10
10
  module Core
11
11
  # Dimensions pulled from PDF::Writer, rubyforge.org/projects/ruby-pdf
12
- #
13
- # All of these dimensions are in PDF Points (1/72 inch)
14
- #
15
- # ===Inbuilt Sizes:
16
- #
17
- #
18
- # 4A0:: => 4767.87 x 6740.79
19
- # 2A0:: => 3370.39 x 4767.87
20
- # A0:: => 2383.94 x 3370.39
21
- # A1:: => 1683.78 x 2383.94
22
- # A2:: => 1190.55 x 1683.78
23
- # A3:: => 841.89 x 1190.55
24
- # A4:: => 595.28 x 841.89
25
- # A5:: => 419.53 x 595.28
26
- # A6:: => 297.64 x 419.53
27
- # A7:: => 209.76 x 297.64
28
- # A8:: => 147.40 x 209.76
29
- # A9:: => 104.88 x 147.40
30
- # A10:: => 73.70 x 104.88
31
- # B0:: => 2834.65 x 4008.19
32
- # B1:: => 2004.09 x 2834.65
33
- # B2:: => 1417.32 x 2004.09
34
- # B3:: => 1000.63 x 1417.32
35
- # B4:: => 708.66 x 1000.63
36
- # B5:: => 498.90 x 708.66
37
- # B6:: => 354.33 x 498.90
38
- # B7:: => 249.45 x 354.33
39
- # B8:: => 175.75 x 249.45
40
- # B9:: => 124.72 x 175.75
41
- # B10:: => 87.87 x 124.72
42
- # C0:: => 2599.37 x 3676.54
43
- # C1:: => 1836.85 x 2599.37
44
- # C2:: => 1298.27 x 1836.85
45
- # C3:: => 918.43 x 1298.27
46
- # C4:: => 649.13 x 918.43
47
- # C5:: => 459.21 x 649.13
48
- # C6:: => 323.15 x 459.21
49
- # C7:: => 229.61 x 323.15
50
- # C8:: => 161.57 x 229.61
51
- # C9:: => 113.39 x 161.57
52
- # C10:: => 79.37 x 113.39
53
- # RA0:: => 2437.80 x 3458.27
54
- # RA1:: => 1729.13 x 2437.80
55
- # RA2:: => 1218.90 x 1729.13
56
- # RA3:: => 864.57 x 1218.90
57
- # RA4:: => 609.45 x 864.57
58
- # SRA0:: => 2551.18 x 3628.35
59
- # SRA1:: => 1814.17 x 2551.18
60
- # SRA2:: => 1275.59 x 1814.17
61
- # SRA3:: => 907.09 x 1275.59
62
- # SRA4:: => 637.80 x 907.09
63
- # EXECUTIVE:: => 521.86 x 756.00
64
- # FOLIO:: => 612.00 x 936.00
65
- # LEGAL:: => 612.00 x 1008.00
66
- # LETTER:: => 612.00 x 792.00
67
- # TABLOID:: => 792.00 x 1224.00
68
- #
69
12
  module PageGeometry
13
+ # Named page sizes.
14
+ #
15
+ # All of these dimensions are in PDF Points (1/72 inch).
70
16
  SIZES = {
71
17
  '4A0' => [4767.87, 6740.79],
72
18
  '2A0' => [3370.39, 4767.87],
@@ -117,7 +63,7 @@ module PDF
117
63
  'FOLIO' => [612.00, 936.00],
118
64
  'LEGAL' => [612.00, 1008.00],
119
65
  'LETTER' => [612.00, 792.00],
120
- 'TABLOID' => [792.00, 1224.00]
66
+ 'TABLOID' => [792.00, 1224.00],
121
67
  }.freeze
122
68
  end
123
69
  end
@@ -1,45 +1,62 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # pdf_object.rb : Handles Ruby to PDF object serialization
4
- #
5
- # Copyright April 2008, Gregory Brown. All Rights Reserved.
6
- #
7
- # This is free software. Please see the LICENSE and COPYING files for details.
8
-
9
- # Top level Module
10
- #
11
3
  module PDF
12
4
  module Core
13
5
  module_function
14
6
 
7
+ # Serializes floating number into a string
8
+ #
9
+ # @param num [Numeric]
10
+ # @return [String]
15
11
  def real(num)
16
- format('%<number>.5f', number: num).sub(/((?<!\.)0)+\z/, '')
12
+ result = format('%.5f', num)
13
+ result.sub!(/((?<!\.)0)+\z/, '')
14
+ result
17
15
  end
18
16
 
17
+ # Serializes a n array of numbers. This is specifically for use in PDF
18
+ # content streams.
19
+ #
20
+ # @param array [Array<Numeric>]
21
+ # @return [String]
19
22
  def real_params(array)
20
23
  array.map { |e| real(e) }.join(' ')
21
24
  end
22
25
 
26
+ # Converts string to UTF-16BE encoding as expected by PDF.
27
+ #
28
+ # @param str [String]
29
+ # @return [String]
30
+ # @api private
23
31
  def utf8_to_utf16(str)
24
- (+"\xFE\xFF").force_encoding(::Encoding::UTF_16BE) +
32
+ (+"\xFE\xFF").force_encoding(::Encoding::UTF_16BE) <<
25
33
  str.encode(::Encoding::UTF_16BE)
26
34
  end
27
35
 
28
- # encodes any string into a hex representation. The result is a string
36
+ # Encodes any string into a hex representation. The result is a string
29
37
  # with only 0-9 and a-f characters. That result is valid ASCII so tag
30
- # it as such to account for behaviour of different ruby VMs
38
+ # it as such to account for behaviour of different ruby VMs.
39
+ #
40
+ # @param str [String]
41
+ # @return [String]
31
42
  def string_to_hex(str)
32
43
  str.unpack1('H*').force_encoding(::Encoding::US_ASCII)
33
44
  end
34
45
 
46
+ # Characters to escape in name objects
47
+ # @api private
35
48
  ESCAPED_NAME_CHARACTERS = (1..32).to_a + [35, 40, 41, 47, 60, 62] + (127..255).to_a
36
49
 
50
+ # How to escape special characters in literal strings
51
+ # @api private
52
+ STRING_ESCAPE_MAP = { '(' => '\(', ')' => '\)', '\\' => '\\\\', "\r" => '\r' }.freeze
53
+
37
54
  # Serializes Ruby objects to their PDF equivalents. Most primitive objects
38
55
  # will work as expected, but please note that Name objects are represented
39
56
  # by Ruby Symbol objects and Dictionary objects are represented by Ruby
40
57
  # hashes (keyed by symbols)
41
58
  #
42
- # Examples:
59
+ # Examples:
43
60
  #
44
61
  # pdf_object(true) #=> "true"
45
62
  # pdf_object(false) #=> "false"
@@ -48,28 +65,34 @@ module PDF
48
65
  # pdf_object(:Symbol) #=> "/Symbol"
49
66
  # pdf_object(['foo',:bar, [1,2]]) #=> "[foo /bar [1 2]]"
50
67
  #
68
+ # @param obj [nil, Boolean, Numeric, Array, Hash, Time, Symbol, String,
69
+ # PDF::Core::ByteString, PDF::Core::LiteralString,
70
+ # PDF::Core::NameTree::Node, PDF::Core::NameTree::Value,
71
+ # PDF::Core::OutlineRoot, PDF::Core::OutlineItem, PDF::Core::Reference]
72
+ # Object to serialise
73
+ # @param in_content_stream [Boolean] Specifies whther to use content stream
74
+ # format or object format
75
+ # @return [String]
76
+ # @raise [PDF::Core::Errors::FailedObjectConversion]
51
77
  def pdf_object(obj, in_content_stream = false)
52
78
  case obj
53
- when NilClass then 'null'
54
- when TrueClass then 'true'
79
+ when NilClass then 'null'
80
+ when TrueClass then 'true'
55
81
  when FalseClass then 'false'
56
82
  when Numeric
57
- obj = real(obj) unless obj.is_a?(Integer)
58
-
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)
83
+ num_string = obj.is_a?(Integer) ? String(obj) : real(obj)
62
84
 
63
85
  # Truncate trailing fraction zeroes
64
- num_string.sub(/(\d*)((\.0*$)|(\.0*[1-9]*)0*$)/, '\1\4')
86
+ num_string.sub!(/(\d*)((\.0*$)|(\.0*[1-9]*)0*$)/, '\1\4')
87
+ num_string
65
88
  when Array
66
89
  "[#{obj.map { |e| pdf_object(e, in_content_stream) }.join(' ')}]"
67
90
  when PDF::Core::LiteralString
68
- obj = obj.gsub(/[\\\n\r\t\b\f()]/) { |m| "\\#{m}" }
91
+ obj = obj.gsub(/[\\\r()]/, STRING_ESCAPE_MAP)
69
92
  "(#{obj})"
70
93
  when Time
71
94
  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}" }
95
+ obj = obj.gsub(/[\\\r()]/, STRING_ESCAPE_MAP)
73
96
  "(#{obj})"
74
97
  when PDF::Core::ByteString
75
98
  "<#{obj.unpack1('H*')}>"
@@ -77,18 +100,18 @@ module PDF
77
100
  obj = utf8_to_utf16(obj) unless in_content_stream
78
101
  "<#{string_to_hex(obj)}>"
79
102
  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}"
103
+ (@symbol_str_cache ||= {})[obj] ||= (+'/') << obj.to_s.unpack('C*').map { |n|
104
+ if ESCAPED_NAME_CHARACTERS.include?(n)
105
+ "##{n.to_s(16).upcase}"
106
+ else
107
+ n.chr
108
+ end
109
+ }.join
89
110
  when ::Hash
90
111
  output = +'<< '
91
- obj.each do |k, v|
112
+ obj
113
+ .sort_by { |k, _v| k.to_s }
114
+ .each do |(k, v)|
92
115
  unless k.is_a?(String) || k.is_a?(Symbol)
93
116
  raise PDF::Core::Errors::FailedObjectConversion,
94
117
  'A PDF Dictionary must be keyed by names'
@@ -99,12 +122,10 @@ module PDF
99
122
  output << '>>'
100
123
  when PDF::Core::Reference
101
124
  obj.to_s
102
- when PDF::Core::NameTree::Node
125
+ when PDF::Core::NameTree::Node, PDF::Core::OutlineRoot, PDF::Core::OutlineItem
103
126
  pdf_object(obj.to_hash)
104
127
  when PDF::Core::NameTree::Value
105
128
  "#{pdf_object(obj.name)} #{pdf_object(obj.value)}"
106
- when PDF::Core::OutlineRoot, PDF::Core::OutlineItem
107
- pdf_object(obj.to_hash)
108
129
  else
109
130
  raise PDF::Core::Errors::FailedObjectConversion,
110
131
  "This object cannot be serialized to PDF (#{obj.inspect})"
@@ -1,31 +1,54 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # reference.rb : Implementation of PDF indirect objects
4
- #
5
- # Copyright April 2008, Gregory Brown. All Rights Reserved.
6
- #
7
- # This is free software. Please see the LICENSE and COPYING files for details.
8
-
9
3
  require 'pdf/core/utils'
10
4
 
11
5
  module PDF
12
6
  module Core
13
- class Reference #:nodoc:
14
- attr_accessor :gen, :data, :offset, :stream, :identifier
7
+ # PDF indirect objects
8
+ #
9
+ # @api private
10
+ class Reference
11
+ # Object identifier
12
+ # @return [Integer]
13
+ attr_accessor :identifier
14
+
15
+ # Object generation
16
+ # @return [Integer]
17
+ attr_accessor :gen
18
+
19
+ # Object data
20
+ # @return [any]
21
+ attr_accessor :data
22
+
23
+ # Offset of the serialized object in the document
24
+ # @return [Integer]
25
+ attr_accessor :offset
26
+
27
+ # Object stream
28
+ # @return [Stream]
29
+ attr_accessor :stream
15
30
 
31
+ # In PDF only dict object can have a stream attached. This exception
32
+ # indicates someone tried to add a stream to another kind of object.
16
33
  class CannotAttachStream < StandardError
34
+ # @param message [String] Error message
17
35
  def initialize(message = 'Cannot attach stream to a non-dictionary object')
18
36
  super
19
37
  end
20
38
  end
21
39
 
40
+ # @param id [Integer] Object identifier
41
+ # @param data [any] Object data
22
42
  def initialize(id, data)
23
43
  @identifier = id
24
- @gen = 0
25
- @data = data
26
- @stream = Stream.new
44
+ @gen = 0
45
+ @data = data
46
+ @stream = Stream.new
27
47
  end
28
48
 
49
+ # Serialized PDF object
50
+ #
51
+ # @return [String]
29
52
  def object
30
53
  output = +"#{@identifier} #{gen} obj\n"
31
54
  if @stream.empty?
@@ -38,6 +61,11 @@ module PDF
38
61
  output << "endobj\n"
39
62
  end
40
63
 
64
+ # Appends data to object stream
65
+ #
66
+ # @param io [String] data
67
+ # @return [io]
68
+ # @raise [CannotAttachStream] if object is not a dict
41
69
  def <<(io)
42
70
  unless @data.is_a?(::Hash)
43
71
  raise CannotAttachStream
@@ -46,13 +74,18 @@ module PDF
46
74
  (@stream ||= Stream.new) << io
47
75
  end
48
76
 
77
+ # Object reference in PDF format
78
+ #
79
+ # @return [String]
49
80
  def to_s
50
81
  "#{@identifier} #{gen} R"
51
82
  end
52
83
 
53
- # Creates a deep copy of this ref. If +share+ is provided, shares the
54
- # given dictionary entries between the old ref and the new.
84
+ # Creates a deep copy of this ref.
55
85
  #
86
+ # @param share [Array<Symbol>] a list of dictionary entries to share
87
+ # between the old ref and the new
88
+ # @return [Reference]
56
89
  def deep_copy(share = [])
57
90
  r = dup
58
91
 
@@ -73,8 +106,11 @@ module PDF
73
106
  end
74
107
 
75
108
  # Replaces the data and stream with that of other_ref.
109
+ #
110
+ # @param other_ref [Reference]
111
+ # @return [void]
76
112
  def replace(other_ref)
77
- @data = other_ref.data
113
+ @data = other_ref.data
78
114
  @stream = other_ref.stream
79
115
  end
80
116
  end