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.
@@ -1,26 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- #
4
- # Implements graphics state saving and restoring
5
- #
6
- # Copyright January 2010, Michael Witrant. All Rights Reserved.
7
- #
8
- # This is free software. Please see the LICENSE and COPYING files for details
9
- #
10
-
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,37 +31,81 @@ 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
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
56
  end
44
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
+ #
45
62
  # NOTE: This class may be a good candidate for a copy-on-write hash.
46
63
  class GraphicState
47
- attr_accessor :color_space, :dash, :cap_style, :join_style, :line_width, :fill_color, :stroke_color
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
87
+
88
+ # Stroke color
89
+ attr_accessor :stroke_color
48
90
 
91
+ # @param previous_state [GraphicState, nil]
49
92
  def initialize(previous_state = nil)
50
93
  if previous_state
51
94
  initialize_copy(previous_state)
52
95
  else
53
- @color_space = {}
54
- @fill_color = '000000'
96
+ @color_space = {}
97
+ @fill_color = '000000'
55
98
  @stroke_color = '000000'
56
- @dash = { dash: nil, space: nil, phase: 0 }
57
- @cap_style = :butt
58
- @join_style = :miter
59
- @line_width = 1
99
+ @dash = { dash: nil, space: nil, phase: 0 }
100
+ @cap_style = :butt
101
+ @join_style = :miter
102
+ @line_width = 1
60
103
  end
61
104
  end
62
105
 
106
+ # PDF representation of dash settings
107
+ #
108
+ # @return [String]
63
109
  def dash_setting
64
110
  return '[] 0 d' unless @dash[:dash]
65
111
 
@@ -70,23 +116,22 @@ module PDF
70
116
  [@dash[:dash], @dash[:space]]
71
117
  end
72
118
 
73
- "[#{PDF::Core.real_params(array)}] "\
74
- "#{PDF::Core.real(@dash[:phase])} d"
119
+ "[#{PDF::Core.real_params(array)}] #{PDF::Core.real(@dash[:phase])} d"
75
120
  end
76
121
 
77
122
  private
78
123
 
79
124
  def initialize_copy(other)
80
125
  # mutable state
81
- @color_space = other.color_space.dup
82
- @fill_color = other.fill_color.dup
126
+ @color_space = other.color_space.dup
127
+ @fill_color = other.fill_color.dup
83
128
  @stroke_color = other.stroke_color.dup
84
- @dash = other.dash.dup
129
+ @dash = other.dash.dup
85
130
 
86
131
  # immutable state that doesn't need to be duped
87
- @cap_style = other.cap_style
88
- @join_style = other.join_style
89
- @line_width = other.line_width
132
+ @cap_style = other.cap_style
133
+ @join_style = other.join_style
134
+ @line_width = other.line_width
90
135
  end
91
136
  end
92
137
  end
@@ -2,16 +2,16 @@
2
2
 
3
3
  module PDF
4
4
  module Core
5
- # This is used to differentiate strings that must be encoded as
6
- # a *literal* string, versus those that can be encoded in
7
- # 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.
8
7
  #
9
- # Some features of the PDF format appear to require that literal
10
- # strings be used. One such feature is the /Dest key of a link
11
- # annotation; if a hex encoded string is used there, the links
12
- # do not work (as tested in Mac OS X Preview, and Adobe Acrobat
13
- # Reader).
14
- class LiteralString < String #:nodoc:
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
15
15
  end
16
16
  end
17
17
  end
@@ -2,22 +2,37 @@
2
2
 
3
3
  require 'pdf/core/utils'
4
4
 
5
- # name_tree.rb : Implements NameTree for PDF
6
- #
7
- # Copyright November 2008, Jamis Buck. All Rights Reserved.
8
- #
9
- # This is free software. Please see the LICENSE and COPYING files for details.
10
- #
11
5
  module PDF
12
6
  module Core
13
- module NameTree #:nodoc:
14
- class Node #:nodoc:
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>]
15
17
  attr_reader :children
18
+
19
+ # Children number limit
20
+ # @return [Integer]
16
21
  attr_reader :limit
22
+
23
+ # @return [Prawn::Document]
17
24
  attr_reader :document
25
+
26
+ # Parent node
27
+ # @return [Node]
18
28
  attr_accessor :parent
29
+
30
+ # @return [Reference]
19
31
  attr_accessor :ref
20
32
 
33
+ # @param document [Prawn::Document] owning document
34
+ # @param limit [Integer] Children limit
35
+ # @param parent [Node] Parent node
21
36
  def initialize(document, limit, parent = nil)
22
37
  @document = document
23
38
  @children = []
@@ -26,22 +41,37 @@ module PDF
26
41
  @ref = nil
27
42
  end
28
43
 
44
+ # Tells whether there are any children nodes
45
+ #
46
+ # @return [Boolean]
29
47
  def empty?
30
48
  children.empty?
31
49
  end
32
50
 
51
+ # Number of all (including nested) children nodes
52
+ #
53
+ # @return [Integer]
33
54
  def size
34
55
  leaf? ? children.size : children.sum(&:size)
35
56
  end
36
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]
37
62
  def leaf?
38
63
  children.empty? || children.first.is_a?(Value)
39
64
  end
40
65
 
66
+ # Adds a value
67
+ #
68
+ # @param name [String]
69
+ # @param value [any]
41
70
  def add(name, value)
42
71
  self << Value.new(name, value)
43
72
  end
44
73
 
74
+ # @return [Hash] a hash representation of this node
45
75
  def to_hash
46
76
  hash = {}
47
77
 
@@ -55,6 +85,7 @@ module PDF
55
85
  hash
56
86
  end
57
87
 
88
+ # @return [String] the least (in lexicographic order) value name
58
89
  def least
59
90
  if leaf?
60
91
  children.first.name
@@ -63,6 +94,7 @@ module PDF
63
94
  end
64
95
  end
65
96
 
97
+ # @return [String] the greatest (in lexicographic order) value name
66
98
  def greatest
67
99
  if leaf?
68
100
  children.last.name
@@ -71,6 +103,10 @@ module PDF
71
103
  end
72
104
  end
73
105
 
106
+ # Insert value maintaining order and rebalancing tree if needed.
107
+ #
108
+ # @param value [Value]
109
+ # @return [value]
74
110
  def <<(value)
75
111
  if children.empty?
76
112
  children << value
@@ -86,10 +122,19 @@ module PDF
86
122
  value
87
123
  end
88
124
 
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#<=>
89
131
  def >=(other)
90
132
  children.empty? || children.last >= other
91
133
  end
92
134
 
135
+ # Split the tree at the node.
136
+ #
137
+ # @return [void]
93
138
  def split!
94
139
  if parent
95
140
  parent.split(self)
@@ -102,12 +147,13 @@ module PDF
102
147
  end
103
148
 
104
149
  # Returns a deep copy of this node, without copying expensive things
105
- # like the ref to @document.
150
+ # like the `ref` to `document`.
106
151
  #
152
+ # @return [Node]
107
153
  def deep_copy
108
154
  node = dup
109
- node.instance_variable_set('@children', Utils.deep_clone(children))
110
- node.instance_variable_set('@ref', 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)
111
157
  node
112
158
  end
113
159
 
@@ -133,7 +179,7 @@ module PDF
133
179
  half = (node.limit + 1) / 2
134
180
 
135
181
  left_children = node.children[0...half]
136
- right_children = node.children[half..-1]
182
+ right_children = node.children[half..]
137
183
 
138
184
  left.children.replace(left_children)
139
185
  right.children.replace(right_children)
@@ -152,25 +198,40 @@ module PDF
152
198
  end
153
199
  end
154
200
 
155
- class Value #:nodoc:
201
+ # # Name Tree value
202
+ #
203
+ # @api private
204
+ class Value
156
205
  include Comparable
157
206
 
207
+ # @return [String]
158
208
  attr_reader :name
209
+
210
+ # @return [any]
159
211
  attr_reader :value
160
212
 
213
+ # @param name [String]
214
+ # @param value [any]
161
215
  def initialize(name, value)
162
216
  @name = PDF::Core::LiteralString.new(name)
163
217
  @value = value
164
218
  end
165
219
 
220
+ # @param other [Value]
221
+ # @return [-1, 0, 1]
222
+ # @see Object#<=>
223
+ # @see Enumerable
166
224
  def <=>(other)
167
225
  name <=> other.name
168
226
  end
169
227
 
228
+ # @return [String] a string containing a human-readable representation
229
+ # of this value object
170
230
  def inspect
171
231
  "#<Value: #{name.inspect} : #{value.inspect}>"
172
232
  end
173
233
 
234
+ # @return [String] a string representation of this value
174
235
  def to_s
175
236
  "#{name} : #{value}"
176
237
  end
@@ -1,24 +1,27 @@
1
1
  # frozen_string_literal: true
2
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.
8
-
9
3
  module PDF
10
4
  module Core
11
- class ObjectStore #:nodoc:
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 ||= ref(opts[:info] || {}).identifier
21
- @root ||= ref(Type: :Catalog).identifier
23
+ @info ||= ref(opts[:info] || {}).identifier
24
+ @root ||= ref(Type: :Catalog).identifier
22
25
  if opts[:print_scaling] == :none
23
26
  root.data[:ViewerPreferences] = { PrintScaling: :None }
24
27
  end
@@ -27,22 +30,39 @@ module PDF
27
30
  end
28
31
  end
29
32
 
30
- def ref(data, &block)
31
- push(size + 1, data, &block)
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
@@ -51,12 +71,20 @@ module PDF
51
71
  # If the object provided is not a PDF::Core::Reference, one is created
52
72
  # from the arguments provided.
53
73
  #
54
- def push(*args, &block)
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)
55
83
  reference =
56
84
  if args.first.is_a?(PDF::Core::Reference)
57
85
  args.first
58
86
  else
59
- PDF::Core::Reference.new(*args, &block)
87
+ PDF::Core::Reference.new(*args)
60
88
  end
61
89
 
62
90
  @objects[reference.identifier] = reference
@@ -66,36 +94,58 @@ module PDF
66
94
 
67
95
  alias << push
68
96
 
97
+ # Iterate over document object references.
98
+ #
99
+ # @yieldparam ref [Reference]
100
+ # @return [void]
69
101
  def each
70
102
  @identifiers.each do |id|
71
- yield @objects[id]
103
+ yield(@objects[id])
72
104
  end
73
105
  end
74
106
 
107
+ # Get object reference by its identifier.
108
+ #
109
+ # @param id [Integer] object identifier
110
+ # @return [Reference]
75
111
  def [](id)
76
112
  @objects[id]
77
113
  end
78
114
 
115
+ # Number of object references in the document.
116
+ #
117
+ # @return [Integer]
79
118
  def size
80
119
  @identifiers.size
81
120
  end
82
121
  alias length size
83
122
 
84
- # returns the object ID for a particular page in the document. Pages
85
- # are indexed starting at 1 (not 0!).
123
+ # Get page reference identifier by page number.Pages are indexed starting
124
+ # at 1 (**not 0**).
86
125
  #
126
+ # @example
127
+ # !!!ruby
87
128
  # object_id_for_page(1)
88
- # => 5
129
+ # #=> 5
89
130
  # object_id_for_page(10)
90
- # => 87
131
+ # #=> 87
91
132
  # object_id_for_page(-11)
92
- # => 17
133
+ # #=> 17
93
134
  #
135
+ # @param page [Integer] page number
136
+ # @return [Integer] page object identifier
94
137
  def object_id_for_page(page)
95
138
  page -= 1 if page.positive?
96
139
  flat_page_ids = get_page_objects(pages).flatten
97
140
  flat_page_ids[page]
98
141
  end
142
+
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)
148
+ end
99
149
  end
100
150
  end
101
151
  end
@@ -2,9 +2,55 @@
2
2
 
3
3
  module PDF
4
4
  module Core
5
- class OutlineItem #:nodoc:
6
- attr_accessor :count, :first, :last, :next, :prev, :parent, :title, :dest, :closed
5
+ # Outline item.
6
+ #
7
+ # @api private
8
+ # @see # PDF 1.7 spec, section 8.2.2 Document Outline
9
+ class OutlineItem
10
+ # The total number of its open descendants at all lower levels of the
11
+ # outline hierarchy.
12
+ # @return [Integer]
13
+ attr_accessor :count
7
14
 
15
+ # The first of this item’s immediate children in the outline hierarchy.
16
+ # @return [Reference<PDF::Core::OutlineItem>]
17
+ attr_accessor :first
18
+
19
+ # The last of this item’s immediate children in the outline hierarchy.
20
+ # @return [Reference<PDF::Core::OutlineItem>]
21
+ attr_accessor :last
22
+
23
+ # The next item at this outline level.
24
+ # @return [Reference<PDF::Core::OutlineItem>]
25
+ attr_accessor :next
26
+
27
+ # The previous item at this outline level.
28
+ # @return [Reference<PDF::Core::OutlineItem>]
29
+ attr_accessor :prev
30
+
31
+ # The parent of this item in the outline hierarchy.
32
+ # @return [Reference<[PDF::Core::OutlineItem, PDF::Core::OutlineRoot]>]
33
+ attr_accessor :parent
34
+
35
+ # The text to be displayed on the screen for this item.
36
+ # @return [String]
37
+ attr_accessor :title
38
+
39
+ # The destination to be displayed when this item is activated.
40
+ # @return [String]
41
+ # @return [Symbol]
42
+ # @return [Array]
43
+ # @see Destinations
44
+ attr_accessor :dest
45
+
46
+ # Is this item open or closed.
47
+ # @return [Boolean]
48
+ attr_accessor :closed
49
+
50
+ # @param title [String]
51
+ # @param parent [PDF::Core::OutlineRoot, PDF::Core::OutlineItem]
52
+ # @param options [Hash]
53
+ # @option options :closed [Boolean]
8
54
  def initialize(title, parent, options)
9
55
  @closed = options[:closed]
10
56
  @title = title
@@ -12,15 +58,18 @@ module PDF
12
58
  @count = 0
13
59
  end
14
60
 
61
+ # A hash representation of this outline item.
62
+ #
63
+ # @return [Hash]
15
64
  def to_hash
16
65
  hash = {
17
66
  Title: title,
18
67
  Parent: parent,
19
- Count: closed ? -count : count
68
+ Count: closed ? -count : count,
20
69
  }
21
70
  [
22
71
  { First: first }, { Last: last }, { Next: defined?(@next) && @next },
23
- { Prev: prev }, { Dest: dest }
72
+ { Prev: prev }, { Dest: dest },
24
73
  ].each do |h|
25
74
  unless h.values.first.nil?
26
75
  hash.merge!(h)
@@ -2,13 +2,29 @@
2
2
 
3
3
  module PDF
4
4
  module Core
5
- class OutlineRoot #:nodoc:
6
- attr_accessor :count, :first, :last
5
+ # Document Outline root.
6
+ #
7
+ # @api private
8
+ # @see # PDF 1.7 spec, section 8.2.2 Document Outline
9
+ class OutlineRoot
10
+ # The total number of open items at all levels of the outline.
11
+ # @return [Integer]
12
+ attr_accessor :count
13
+
14
+ # The first top-level item in the outline.
15
+ # @return [Reference]
16
+ attr_accessor :first
17
+
18
+ # The last top-level item in the outline.
19
+ # @return [Reference]
20
+ attr_accessor :last
7
21
 
8
22
  def initialize
9
23
  @count = 0
10
24
  end
11
25
 
26
+ # Hash representation of the outline root
27
+ # @return [Hash]
12
28
  def to_hash
13
29
  { Type: :Outlines, Count: count, First: first, Last: last }
14
30
  end