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.
@@ -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