pdf-core 0.4.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.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data/lib/pdf/core/annotations.rb +58 -21
- data/lib/pdf/core/byte_string.rb +6 -3
- data/lib/pdf/core/destinations.rb +77 -34
- data/lib/pdf/core/document_state.rb +121 -30
- data/lib/pdf/core/filter_list.rb +47 -6
- data/lib/pdf/core/filters.rb +31 -12
- data/lib/pdf/core/graphics_state.rb +80 -32
- data/lib/pdf/core/literal_string.rb +11 -10
- data/lib/pdf/core/name_tree.rb +118 -54
- data/lib/pdf/core/object_store.rb +82 -37
- data/lib/pdf/core/outline_item.rb +62 -7
- data/lib/pdf/core/outline_root.rb +21 -3
- data/lib/pdf/core/page.rb +232 -77
- data/lib/pdf/core/page_geometry.rb +56 -112
- data/lib/pdf/core/pdf_object.rb +93 -57
- data/lib/pdf/core/reference.rb +70 -30
- data/lib/pdf/core/renderer.rb +149 -68
- data/lib/pdf/core/stream.rb +48 -12
- data/lib/pdf/core/text.rb +300 -106
- data/lib/pdf/core/utils.rb +21 -0
- data/lib/pdf/core.rb +38 -25
- data/pdf-core.gemspec +43 -24
- data.tar.gz.sig +0 -0
- metadata +56 -64
- metadata.gz.sig +2 -0
- data/Gemfile +0 -3
- data/Rakefile +0 -11
- data/spec/filters_spec.rb +0 -34
- data/spec/name_tree_spec.rb +0 -122
- data/spec/object_store_spec.rb +0 -49
- data/spec/pdf_object_spec.rb +0 -172
- data/spec/reference_spec.rb +0 -62
- data/spec/spec_helper.rb +0 -32
- data/spec/stream_spec.rb +0 -59
|
@@ -1,26 +1,28 @@
|
|
|
1
|
-
#
|
|
2
|
-
#
|
|
3
|
-
# Implements graphics state saving and restoring
|
|
4
|
-
#
|
|
5
|
-
# Copyright January 2010, Michael Witrant. All Rights Reserved.
|
|
6
|
-
#
|
|
7
|
-
# This is free software. Please see the LICENSE and COPYING files for details
|
|
8
|
-
#
|
|
9
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
10
2
|
|
|
11
3
|
module PDF
|
|
12
4
|
module Core
|
|
5
|
+
# Graphics state saving and restoring
|
|
13
6
|
class GraphicStateStack
|
|
7
|
+
# Graphic state stack
|
|
14
8
|
attr_accessor :stack
|
|
15
9
|
|
|
10
|
+
# @param previous_state [GraphicState, nil]
|
|
16
11
|
def initialize(previous_state = nil)
|
|
17
12
|
self.stack = [GraphicState.new(previous_state)]
|
|
18
13
|
end
|
|
19
14
|
|
|
15
|
+
# Pushes graphic state onto stack
|
|
16
|
+
#
|
|
17
|
+
# @param graphic_state [GraphicState, nil]
|
|
18
|
+
# @return [void]
|
|
20
19
|
def save_graphic_state(graphic_state = nil)
|
|
21
20
|
stack.push(GraphicState.new(graphic_state || current_state))
|
|
22
21
|
end
|
|
23
22
|
|
|
23
|
+
# Restores previous graphic state
|
|
24
|
+
#
|
|
25
|
+
# @return [void]
|
|
24
26
|
def restore_graphic_state
|
|
25
27
|
if stack.empty?
|
|
26
28
|
raise PDF::Core::Errors::EmptyGraphicStateStack,
|
|
@@ -29,62 +31,108 @@ module PDF
|
|
|
29
31
|
stack.pop
|
|
30
32
|
end
|
|
31
33
|
|
|
34
|
+
# Current graphic state
|
|
35
|
+
#
|
|
36
|
+
# @return [GraphicState]
|
|
32
37
|
def current_state
|
|
33
38
|
stack.last
|
|
34
39
|
end
|
|
35
40
|
|
|
41
|
+
# Tells whether there are any saved graphic states
|
|
42
|
+
#
|
|
43
|
+
# @return [Boolean]
|
|
44
|
+
# @see #empty?
|
|
36
45
|
def present?
|
|
37
|
-
stack.
|
|
46
|
+
!stack.empty?
|
|
38
47
|
end
|
|
39
48
|
|
|
49
|
+
# Tells whether there are no saved graphic states
|
|
50
|
+
#
|
|
51
|
+
# @return [Boolean]
|
|
52
|
+
# @see #present?
|
|
40
53
|
def empty?
|
|
41
54
|
stack.empty?
|
|
42
55
|
end
|
|
43
|
-
|
|
44
56
|
end
|
|
45
57
|
|
|
58
|
+
# Graphics state.
|
|
59
|
+
# It's a *partial* represenation of PDF graphics state. Only the parts
|
|
60
|
+
# implemented in Prawn are present here.
|
|
61
|
+
#
|
|
46
62
|
# NOTE: This class may be a good candidate for a copy-on-write hash.
|
|
47
63
|
class GraphicState
|
|
48
|
-
|
|
49
|
-
|
|
64
|
+
# Color space
|
|
65
|
+
# @return [Hash]
|
|
66
|
+
attr_accessor :color_space
|
|
67
|
+
|
|
68
|
+
# Dash
|
|
69
|
+
# @return [Hash<[:dash, :space, :phase], [nil, Numeric]>]
|
|
70
|
+
attr_accessor :dash
|
|
71
|
+
|
|
72
|
+
# Line cap
|
|
73
|
+
# @return [Symbol]
|
|
74
|
+
attr_accessor :cap_style
|
|
75
|
+
|
|
76
|
+
# Line Join
|
|
77
|
+
# @return [Symbol]
|
|
78
|
+
attr_accessor :join_style
|
|
79
|
+
|
|
80
|
+
# Line width
|
|
81
|
+
# @return [Numberic]
|
|
82
|
+
attr_accessor :line_width
|
|
83
|
+
|
|
84
|
+
# Fill color
|
|
85
|
+
# @return [String]
|
|
86
|
+
attr_accessor :fill_color
|
|
50
87
|
|
|
88
|
+
# Stroke color
|
|
89
|
+
attr_accessor :stroke_color
|
|
90
|
+
|
|
91
|
+
# @param previous_state [GraphicState, nil]
|
|
51
92
|
def initialize(previous_state = nil)
|
|
52
93
|
if previous_state
|
|
53
94
|
initialize_copy(previous_state)
|
|
54
95
|
else
|
|
55
|
-
@color_space
|
|
56
|
-
@fill_color
|
|
57
|
-
@stroke_color =
|
|
58
|
-
@dash
|
|
59
|
-
@cap_style
|
|
60
|
-
@join_style
|
|
61
|
-
@line_width
|
|
96
|
+
@color_space = {}
|
|
97
|
+
@fill_color = '000000'
|
|
98
|
+
@stroke_color = '000000'
|
|
99
|
+
@dash = { dash: nil, space: nil, phase: 0 }
|
|
100
|
+
@cap_style = :butt
|
|
101
|
+
@join_style = :miter
|
|
102
|
+
@line_width = 1
|
|
62
103
|
end
|
|
63
104
|
end
|
|
64
105
|
|
|
106
|
+
# PDF representation of dash settings
|
|
107
|
+
#
|
|
108
|
+
# @return [String]
|
|
65
109
|
def dash_setting
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
110
|
+
return '[] 0 d' unless @dash[:dash]
|
|
111
|
+
|
|
112
|
+
array =
|
|
113
|
+
if @dash[:dash].is_a?(Array)
|
|
114
|
+
@dash[:dash]
|
|
115
|
+
else
|
|
116
|
+
[@dash[:dash], @dash[:space]]
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
"[#{PDF::Core.real_params(array)}] #{PDF::Core.real(@dash[:phase])} d"
|
|
71
120
|
end
|
|
72
121
|
|
|
73
122
|
private
|
|
74
123
|
|
|
75
124
|
def initialize_copy(other)
|
|
76
125
|
# mutable state
|
|
77
|
-
@color_space
|
|
78
|
-
@fill_color
|
|
126
|
+
@color_space = other.color_space.dup
|
|
127
|
+
@fill_color = other.fill_color.dup
|
|
79
128
|
@stroke_color = other.stroke_color.dup
|
|
80
|
-
@dash
|
|
129
|
+
@dash = other.dash.dup
|
|
81
130
|
|
|
82
131
|
# immutable state that doesn't need to be duped
|
|
83
|
-
@cap_style
|
|
84
|
-
@join_style
|
|
85
|
-
@line_width
|
|
132
|
+
@cap_style = other.cap_style
|
|
133
|
+
@join_style = other.join_style
|
|
134
|
+
@line_width = other.line_width
|
|
86
135
|
end
|
|
87
136
|
end
|
|
88
137
|
end
|
|
89
138
|
end
|
|
90
|
-
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
module PDF
|
|
3
4
|
module Core
|
|
4
|
-
# This is used to differentiate strings that must be encoded as
|
|
5
|
-
#
|
|
6
|
-
# the PDF hexadecimal format.
|
|
5
|
+
# This is used to differentiate strings that must be encoded as a *literal*
|
|
6
|
+
# string, versus those that can be encoded in the PDF hexadecimal format.
|
|
7
7
|
#
|
|
8
|
-
# Some features of the PDF format appear to require that literal
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
|
|
8
|
+
# Some features of the PDF format appear to require that literal strings be
|
|
9
|
+
# used. One such feature is the `Dest` key of a link annotation; if a hex
|
|
10
|
+
# encoded string is used there, the links do not work (as tested in Mac OS
|
|
11
|
+
# X Preview, and Adobe Acrobat Reader).
|
|
12
|
+
#
|
|
13
|
+
# @api private
|
|
14
|
+
class LiteralString < String
|
|
14
15
|
end
|
|
15
16
|
end
|
|
16
17
|
end
|
data/lib/pdf/core/name_tree.rb
CHANGED
|
@@ -1,22 +1,39 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
# Copyright November 2008, Jamis Buck. All Rights Reserved.
|
|
6
|
-
#
|
|
7
|
-
# This is free software. Please see the LICENSE and COPYING files for details.
|
|
8
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'pdf/core/utils'
|
|
4
|
+
|
|
9
5
|
module PDF
|
|
10
6
|
module Core
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
# Name Tree for PDF
|
|
8
|
+
#
|
|
9
|
+
# @api private
|
|
10
|
+
module NameTree
|
|
11
|
+
# Name Tree node
|
|
12
|
+
#
|
|
13
|
+
# @api private
|
|
14
|
+
class Node
|
|
15
|
+
# Child nodes
|
|
16
|
+
# @return [Array<Node>]
|
|
13
17
|
attr_reader :children
|
|
18
|
+
|
|
19
|
+
# Children number limit
|
|
20
|
+
# @return [Integer]
|
|
14
21
|
attr_reader :limit
|
|
22
|
+
|
|
23
|
+
# @return [Prawn::Document]
|
|
15
24
|
attr_reader :document
|
|
25
|
+
|
|
26
|
+
# Parent node
|
|
27
|
+
# @return [Node]
|
|
16
28
|
attr_accessor :parent
|
|
29
|
+
|
|
30
|
+
# @return [Reference]
|
|
17
31
|
attr_accessor :ref
|
|
18
32
|
|
|
19
|
-
|
|
33
|
+
# @param document [Prawn::Document] owning document
|
|
34
|
+
# @param limit [Integer] Children limit
|
|
35
|
+
# @param parent [Node] Parent node
|
|
36
|
+
def initialize(document, limit, parent = nil)
|
|
20
37
|
@document = document
|
|
21
38
|
@children = []
|
|
22
39
|
@limit = limit
|
|
@@ -24,22 +41,37 @@ module PDF
|
|
|
24
41
|
@ref = nil
|
|
25
42
|
end
|
|
26
43
|
|
|
44
|
+
# Tells whether there are any children nodes
|
|
45
|
+
#
|
|
46
|
+
# @return [Boolean]
|
|
27
47
|
def empty?
|
|
28
48
|
children.empty?
|
|
29
49
|
end
|
|
30
50
|
|
|
51
|
+
# Number of all (including nested) children nodes
|
|
52
|
+
#
|
|
53
|
+
# @return [Integer]
|
|
31
54
|
def size
|
|
32
|
-
leaf? ? children.size : children.
|
|
55
|
+
leaf? ? children.size : children.sum(&:size)
|
|
33
56
|
end
|
|
34
57
|
|
|
58
|
+
# Tells whether this is a leaf node. A leaf node is the one that has no
|
|
59
|
+
# children or only {Value} children.
|
|
60
|
+
#
|
|
61
|
+
# @return [Boolean]
|
|
35
62
|
def leaf?
|
|
36
63
|
children.empty? || children.first.is_a?(Value)
|
|
37
64
|
end
|
|
38
65
|
|
|
66
|
+
# Adds a value
|
|
67
|
+
#
|
|
68
|
+
# @param name [String]
|
|
69
|
+
# @param value [any]
|
|
39
70
|
def add(name, value)
|
|
40
71
|
self << Value.new(name, value)
|
|
41
72
|
end
|
|
42
73
|
|
|
74
|
+
# @return [Hash] a hash representation of this node
|
|
43
75
|
def to_hash
|
|
44
76
|
hash = {}
|
|
45
77
|
|
|
@@ -47,12 +79,13 @@ module PDF
|
|
|
47
79
|
if leaf?
|
|
48
80
|
hash[:Names] = children if leaf?
|
|
49
81
|
else
|
|
50
|
-
hash[:Kids] = children.map
|
|
82
|
+
hash[:Kids] = children.map(&:ref)
|
|
51
83
|
end
|
|
52
84
|
|
|
53
|
-
|
|
85
|
+
hash
|
|
54
86
|
end
|
|
55
87
|
|
|
88
|
+
# @return [String] the least (in lexicographic order) value name
|
|
56
89
|
def least
|
|
57
90
|
if leaf?
|
|
58
91
|
children.first.name
|
|
@@ -61,6 +94,7 @@ module PDF
|
|
|
61
94
|
end
|
|
62
95
|
end
|
|
63
96
|
|
|
97
|
+
# @return [String] the greatest (in lexicographic order) value name
|
|
64
98
|
def greatest
|
|
65
99
|
if leaf?
|
|
66
100
|
children.last.name
|
|
@@ -69,6 +103,10 @@ module PDF
|
|
|
69
103
|
end
|
|
70
104
|
end
|
|
71
105
|
|
|
106
|
+
# Insert value maintaining order and rebalancing tree if needed.
|
|
107
|
+
#
|
|
108
|
+
# @param value [Value]
|
|
109
|
+
# @return [value]
|
|
72
110
|
def <<(value)
|
|
73
111
|
if children.empty?
|
|
74
112
|
children << value
|
|
@@ -76,98 +114,124 @@ module PDF
|
|
|
76
114
|
children.insert(insertion_point(value), value)
|
|
77
115
|
split! if children.length > limit
|
|
78
116
|
else
|
|
79
|
-
fit = children.
|
|
80
|
-
fit
|
|
117
|
+
fit = children.find { |child| child >= value }
|
|
118
|
+
fit ||= children.last
|
|
81
119
|
fit << value
|
|
82
120
|
end
|
|
83
121
|
|
|
84
122
|
value
|
|
85
123
|
end
|
|
86
124
|
|
|
87
|
-
|
|
88
|
-
|
|
125
|
+
# This is a compatibility method to allow uniform comparison between
|
|
126
|
+
# nodes and values.
|
|
127
|
+
#
|
|
128
|
+
# @api private
|
|
129
|
+
# @return [Boolean]
|
|
130
|
+
# @see Value#<=>
|
|
131
|
+
def >=(other)
|
|
132
|
+
children.empty? || children.last >= other
|
|
89
133
|
end
|
|
90
134
|
|
|
135
|
+
# Split the tree at the node.
|
|
136
|
+
#
|
|
137
|
+
# @return [void]
|
|
91
138
|
def split!
|
|
92
139
|
if parent
|
|
93
140
|
parent.split(self)
|
|
94
141
|
else
|
|
95
|
-
left
|
|
142
|
+
left = new_node(self)
|
|
143
|
+
right = new_node(self)
|
|
96
144
|
split_children(self, left, right)
|
|
97
145
|
children.replace([left, right])
|
|
98
146
|
end
|
|
99
147
|
end
|
|
100
148
|
|
|
101
149
|
# Returns a deep copy of this node, without copying expensive things
|
|
102
|
-
# like the ref to
|
|
150
|
+
# like the `ref` to `document`.
|
|
103
151
|
#
|
|
152
|
+
# @return [Node]
|
|
104
153
|
def deep_copy
|
|
105
154
|
node = dup
|
|
106
|
-
node.instance_variable_set(
|
|
107
|
-
|
|
108
|
-
node.instance_variable_set("@ref",
|
|
109
|
-
node.ref ? node.ref.deep_copy : nil)
|
|
155
|
+
node.instance_variable_set(:@children, Utils.deep_clone(children))
|
|
156
|
+
node.instance_variable_set(:@ref, node.ref ? node.ref.deep_copy : nil)
|
|
110
157
|
node
|
|
111
158
|
end
|
|
112
159
|
|
|
113
160
|
protected
|
|
114
161
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
162
|
+
def split(node)
|
|
163
|
+
new_child = new_node(self)
|
|
164
|
+
split_children(node, node, new_child)
|
|
165
|
+
index = children.index(node)
|
|
166
|
+
children.insert(index + 1, new_child)
|
|
167
|
+
split! if children.length > limit
|
|
168
|
+
end
|
|
122
169
|
|
|
123
170
|
private
|
|
124
171
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
172
|
+
def new_node(parent = nil)
|
|
173
|
+
node = Node.new(document, limit, parent)
|
|
174
|
+
node.ref = document.ref!(node)
|
|
175
|
+
node
|
|
176
|
+
end
|
|
130
177
|
|
|
131
|
-
|
|
132
|
-
|
|
178
|
+
def split_children(node, left, right)
|
|
179
|
+
half = (node.limit + 1) / 2
|
|
133
180
|
|
|
134
|
-
|
|
181
|
+
left_children = node.children[0...half]
|
|
182
|
+
right_children = node.children[half..]
|
|
135
183
|
|
|
136
|
-
|
|
137
|
-
|
|
184
|
+
left.children.replace(left_children)
|
|
185
|
+
right.children.replace(right_children)
|
|
138
186
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
end
|
|
187
|
+
unless node.leaf?
|
|
188
|
+
left_children.each { |child| child.parent = left }
|
|
189
|
+
right_children.each { |child| child.parent = right }
|
|
143
190
|
end
|
|
191
|
+
end
|
|
144
192
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
end
|
|
149
|
-
return children.length
|
|
193
|
+
def insertion_point(value)
|
|
194
|
+
children.each_with_index do |child, index|
|
|
195
|
+
return index if child >= value
|
|
150
196
|
end
|
|
197
|
+
children.length
|
|
198
|
+
end
|
|
151
199
|
end
|
|
152
200
|
|
|
153
|
-
|
|
201
|
+
# # Name Tree value
|
|
202
|
+
#
|
|
203
|
+
# @api private
|
|
204
|
+
class Value
|
|
154
205
|
include Comparable
|
|
155
206
|
|
|
207
|
+
# @return [String]
|
|
156
208
|
attr_reader :name
|
|
209
|
+
|
|
210
|
+
# @return [any]
|
|
157
211
|
attr_reader :value
|
|
158
212
|
|
|
213
|
+
# @param name [String]
|
|
214
|
+
# @param value [any]
|
|
159
215
|
def initialize(name, value)
|
|
160
|
-
@name
|
|
216
|
+
@name = PDF::Core::LiteralString.new(name)
|
|
217
|
+
@value = value
|
|
161
218
|
end
|
|
162
219
|
|
|
163
|
-
|
|
164
|
-
|
|
220
|
+
# @param other [Value]
|
|
221
|
+
# @return [-1, 0, 1]
|
|
222
|
+
# @see Object#<=>
|
|
223
|
+
# @see Enumerable
|
|
224
|
+
def <=>(other)
|
|
225
|
+
name <=> other.name
|
|
165
226
|
end
|
|
166
227
|
|
|
228
|
+
# @return [String] a string containing a human-readable representation
|
|
229
|
+
# of this value object
|
|
167
230
|
def inspect
|
|
168
231
|
"#<Value: #{name.inspect} : #{value.inspect}>"
|
|
169
232
|
end
|
|
170
233
|
|
|
234
|
+
# @return [String] a string representation of this value
|
|
171
235
|
def to_s
|
|
172
236
|
"#{name} : #{value}"
|
|
173
237
|
end
|
|
@@ -1,106 +1,151 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
# Implements PDF object repository
|
|
4
|
-
#
|
|
5
|
-
# Copyright August 2009, Brad Ediger. All Rights Reserved.
|
|
6
|
-
#
|
|
7
|
-
# This is free software. Please see the LICENSE and COPYING files for details.
|
|
1
|
+
# frozen_string_literal: true
|
|
8
2
|
|
|
9
3
|
module PDF
|
|
10
4
|
module Core
|
|
11
|
-
|
|
5
|
+
# PDF object repository
|
|
6
|
+
#
|
|
7
|
+
# @api private
|
|
8
|
+
class ObjectStore
|
|
12
9
|
include Enumerable
|
|
13
10
|
|
|
11
|
+
# Minimum PDF version
|
|
12
|
+
# @return [Float]
|
|
14
13
|
attr_reader :min_version
|
|
15
14
|
|
|
15
|
+
# @param opts [Hash]
|
|
16
|
+
# @option opts :info [Hash] Documnt info dict
|
|
17
|
+
# @option opts :print_scaling [:none, nil] (nil) Print scaling viewer
|
|
18
|
+
# option
|
|
16
19
|
def initialize(opts = {})
|
|
17
20
|
@objects = {}
|
|
18
21
|
@identifiers = []
|
|
19
22
|
|
|
20
|
-
@info
|
|
21
|
-
@root
|
|
23
|
+
@info ||= ref(opts[:info] || {}).identifier
|
|
24
|
+
@root ||= ref(Type: :Catalog).identifier
|
|
22
25
|
if opts[:print_scaling] == :none
|
|
23
|
-
root.data[:ViewerPreferences] = {:
|
|
26
|
+
root.data[:ViewerPreferences] = { PrintScaling: :None }
|
|
24
27
|
end
|
|
25
28
|
if pages.nil?
|
|
26
|
-
root.data[:Pages] = ref(:
|
|
29
|
+
root.data[:Pages] = ref(Type: :Pages, Count: 0, Kids: [])
|
|
27
30
|
end
|
|
28
31
|
end
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
# Wrap an object into a reference.
|
|
34
|
+
#
|
|
35
|
+
# @param data [Hash, Array, Numeric, String, Symbol, Date, Time, nil]
|
|
36
|
+
# object data
|
|
37
|
+
# @return [Reference]
|
|
38
|
+
def ref(data)
|
|
39
|
+
push(size + 1, data)
|
|
32
40
|
end
|
|
33
41
|
|
|
42
|
+
# Document info dict reference
|
|
43
|
+
#
|
|
44
|
+
# @return [Reference]
|
|
34
45
|
def info
|
|
35
46
|
@objects[@info]
|
|
36
47
|
end
|
|
37
48
|
|
|
49
|
+
# Document root dict reference
|
|
50
|
+
#
|
|
51
|
+
# @return [Reference]
|
|
38
52
|
def root
|
|
39
53
|
@objects[@root]
|
|
40
54
|
end
|
|
41
55
|
|
|
56
|
+
# Document pages reference
|
|
57
|
+
#
|
|
58
|
+
# @return [Reference]
|
|
42
59
|
def pages
|
|
43
60
|
root.data[:Pages]
|
|
44
61
|
end
|
|
45
62
|
|
|
63
|
+
# Number of pages in the document
|
|
64
|
+
#
|
|
65
|
+
# @return [Integer]
|
|
46
66
|
def page_count
|
|
47
67
|
pages.data[:Count]
|
|
48
68
|
end
|
|
49
69
|
|
|
50
70
|
# Adds the given reference to the store and returns the reference object.
|
|
51
|
-
# If the object provided is not a PDF::Core::Reference, one is created
|
|
52
|
-
# arguments provided.
|
|
71
|
+
# If the object provided is not a PDF::Core::Reference, one is created
|
|
72
|
+
# from the arguments provided.
|
|
53
73
|
#
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
74
|
+
# @overload push(reference)
|
|
75
|
+
# @param reference [Reference]
|
|
76
|
+
# @return [reference]
|
|
77
|
+
# @overload push(id, data)
|
|
78
|
+
# @param id [Integer] reference identifier
|
|
79
|
+
# @param data [Hash, Array, Numeric, String, Symbol, Date, Time, nil]
|
|
80
|
+
# object data
|
|
81
|
+
# @return [Reference] - the added reference
|
|
82
|
+
def push(*args)
|
|
83
|
+
reference =
|
|
84
|
+
if args.first.is_a?(PDF::Core::Reference)
|
|
85
|
+
args.first
|
|
86
|
+
else
|
|
87
|
+
PDF::Core::Reference.new(*args)
|
|
88
|
+
end
|
|
60
89
|
|
|
61
90
|
@objects[reference.identifier] = reference
|
|
62
91
|
@identifiers << reference.identifier
|
|
63
92
|
reference
|
|
64
93
|
end
|
|
65
94
|
|
|
66
|
-
|
|
95
|
+
alias << push
|
|
67
96
|
|
|
97
|
+
# Iterate over document object references.
|
|
98
|
+
#
|
|
99
|
+
# @yieldparam ref [Reference]
|
|
100
|
+
# @return [void]
|
|
68
101
|
def each
|
|
69
102
|
@identifiers.each do |id|
|
|
70
|
-
yield
|
|
103
|
+
yield(@objects[id])
|
|
71
104
|
end
|
|
72
105
|
end
|
|
73
106
|
|
|
107
|
+
# Get object reference by its identifier.
|
|
108
|
+
#
|
|
109
|
+
# @param id [Integer] object identifier
|
|
110
|
+
# @return [Reference]
|
|
74
111
|
def [](id)
|
|
75
112
|
@objects[id]
|
|
76
113
|
end
|
|
77
114
|
|
|
115
|
+
# Number of object references in the document.
|
|
116
|
+
#
|
|
117
|
+
# @return [Integer]
|
|
78
118
|
def size
|
|
79
119
|
@identifiers.size
|
|
80
120
|
end
|
|
81
|
-
|
|
121
|
+
alias length size
|
|
82
122
|
|
|
83
|
-
#
|
|
84
|
-
#
|
|
123
|
+
# Get page reference identifier by page number.Pages are indexed starting
|
|
124
|
+
# at 1 (**not 0**).
|
|
85
125
|
#
|
|
126
|
+
# @example
|
|
127
|
+
# !!!ruby
|
|
86
128
|
# object_id_for_page(1)
|
|
87
|
-
#
|
|
129
|
+
# #=> 5
|
|
88
130
|
# object_id_for_page(10)
|
|
89
|
-
#
|
|
131
|
+
# #=> 87
|
|
90
132
|
# object_id_for_page(-11)
|
|
91
|
-
#
|
|
133
|
+
# #=> 17
|
|
92
134
|
#
|
|
93
|
-
|
|
94
|
-
|
|
135
|
+
# @param page [Integer] page number
|
|
136
|
+
# @return [Integer] page object identifier
|
|
137
|
+
def object_id_for_page(page)
|
|
138
|
+
page -= 1 if page.positive?
|
|
95
139
|
flat_page_ids = get_page_objects(pages).flatten
|
|
96
|
-
flat_page_ids[
|
|
140
|
+
flat_page_ids[page]
|
|
97
141
|
end
|
|
98
142
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
143
|
+
private
|
|
144
|
+
|
|
145
|
+
# returns an array with the object IDs for all pages
|
|
146
|
+
def get_page_objects(pages)
|
|
147
|
+
pages.data[:Kids].map(&:identifier)
|
|
102
148
|
end
|
|
103
149
|
end
|
|
104
150
|
end
|
|
105
151
|
end
|
|
106
|
-
|