pdf-core 0.9.0 → 0.10.0

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/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