pdf-core 0.8.1 → 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,27 +1,28 @@
1
-
2
1
  # frozen_string_literal: true
3
2
 
4
- #
5
- # Implements graphics state saving and restoring
6
- #
7
- # Copyright January 2010, Michael Witrant. All Rights Reserved.
8
- #
9
- # This is free software. Please see the LICENSE and COPYING files for details
10
- #
11
-
12
3
  module PDF
13
4
  module Core
5
+ # Graphics state saving and restoring
14
6
  class GraphicStateStack
7
+ # Graphic state stack
15
8
  attr_accessor :stack
16
9
 
10
+ # @param previous_state [GraphicState, nil]
17
11
  def initialize(previous_state = nil)
18
12
  self.stack = [GraphicState.new(previous_state)]
19
13
  end
20
14
 
15
+ # Pushes graphic state onto stack
16
+ #
17
+ # @param graphic_state [GraphicState, nil]
18
+ # @return [void]
21
19
  def save_graphic_state(graphic_state = nil)
22
20
  stack.push(GraphicState.new(graphic_state || current_state))
23
21
  end
24
22
 
23
+ # Restores previous graphic state
24
+ #
25
+ # @return [void]
25
26
  def restore_graphic_state
26
27
  if stack.empty?
27
28
  raise PDF::Core::Errors::EmptyGraphicStateStack,
@@ -30,64 +31,107 @@ module PDF
30
31
  stack.pop
31
32
  end
32
33
 
34
+ # Current graphic state
35
+ #
36
+ # @return [GraphicState]
33
37
  def current_state
34
38
  stack.last
35
39
  end
36
40
 
41
+ # Tells whether there are any saved graphic states
42
+ #
43
+ # @return [Boolean]
44
+ # @see #empty?
37
45
  def present?
38
46
  !stack.empty?
39
47
  end
40
48
 
49
+ # Tells whether there are no saved graphic states
50
+ #
51
+ # @return [Boolean]
52
+ # @see #present?
41
53
  def empty?
42
54
  stack.empty?
43
55
  end
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
- attr_accessor :color_space, :dash, :cap_style, :join_style, :line_width,
49
- :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
50
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 = '000000'
96
+ @color_space = {}
97
+ @fill_color = '000000'
57
98
  @stroke_color = '000000'
58
- @dash = { dash: nil, space: nil, phase: 0 }
59
- @cap_style = :butt
60
- @join_style = :miter
61
- @line_width = 1
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
110
  return '[] 0 d' unless @dash[:dash]
67
111
 
68
- array = if @dash[:dash].is_a?(Array)
69
- @dash[:dash]
70
- else
71
- [@dash[:dash], @dash[:space]]
72
- end
112
+ array =
113
+ if @dash[:dash].is_a?(Array)
114
+ @dash[:dash]
115
+ else
116
+ [@dash[:dash], @dash[:space]]
117
+ end
73
118
 
74
- "[#{PDF::Core.real_params(array)}] "\
75
- "#{PDF::Core.real(@dash[:phase])} d"
119
+ "[#{PDF::Core.real_params(array)}] #{PDF::Core.real(@dash[:phase])} d"
76
120
  end
77
121
 
78
122
  private
79
123
 
80
124
  def initialize_copy(other)
81
125
  # mutable state
82
- @color_space = other.color_space.dup
83
- @fill_color = other.fill_color.dup
126
+ @color_space = other.color_space.dup
127
+ @fill_color = other.fill_color.dup
84
128
  @stroke_color = other.stroke_color.dup
85
- @dash = other.dash.dup
129
+ @dash = other.dash.dup
86
130
 
87
131
  # immutable state that doesn't need to be duped
88
- @cap_style = other.cap_style
89
- @join_style = other.join_style
90
- @line_width = other.line_width
132
+ @cap_style = other.cap_style
133
+ @join_style = other.join_style
134
+ @line_width = other.line_width
91
135
  end
92
136
  end
93
137
  end
@@ -1,18 +1,17 @@
1
-
2
1
  # frozen_string_literal: true
3
2
 
4
3
  module PDF
5
4
  module Core
6
- # This is used to differentiate strings that must be encoded as
7
- # a *literal* string, versus those that can be encoded in
8
- # 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
+ #
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).
9
12
  #
10
- # Some features of the PDF format appear to require that literal
11
- # strings be used. One such feature is the /Dest key of a link
12
- # annotation; if a hex encoded string is used there, the links
13
- # do not work (as tested in Mac OS X Preview, and Adobe Acrobat
14
- # Reader).
15
- class LiteralString < String #:nodoc:
13
+ # @api private
14
+ class LiteralString < String
16
15
  end
17
16
  end
18
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
- leaf? ? children.size : children.map(&:size).reduce(:+)
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
@@ -78,7 +114,7 @@ module PDF
78
114
  children.insert(insertion_point(value), value)
79
115
  split! if children.length > limit
80
116
  else
81
- fit = children.detect { |child| child >= value }
117
+ fit = children.find { |child| child >= value }
82
118
  fit ||= children.last
83
119
  fit << value
84
120
  end
@@ -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,13 +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',
111
- 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)
112
157
  node
113
158
  end
114
159
 
@@ -134,7 +179,7 @@ module PDF
134
179
  half = (node.limit + 1) / 2
135
180
 
136
181
  left_children = node.children[0...half]
137
- right_children = node.children[half..-1]
182
+ right_children = node.children[half..]
138
183
 
139
184
  left.children.replace(left_children)
140
185
  right.children.replace(right_children)
@@ -153,25 +198,40 @@ module PDF
153
198
  end
154
199
  end
155
200
 
156
- class Value #:nodoc:
201
+ # # Name Tree value
202
+ #
203
+ # @api private
204
+ class Value
157
205
  include Comparable
158
206
 
207
+ # @return [String]
159
208
  attr_reader :name
209
+
210
+ # @return [any]
160
211
  attr_reader :value
161
212
 
213
+ # @param name [String]
214
+ # @param value [any]
162
215
  def initialize(name, value)
163
216
  @name = PDF::Core::LiteralString.new(name)
164
217
  @value = value
165
218
  end
166
219
 
220
+ # @param other [Value]
221
+ # @return [-1, 0, 1]
222
+ # @see Object#<=>
223
+ # @see Enumerable
167
224
  def <=>(other)
168
225
  name <=> other.name
169
226
  end
170
227
 
228
+ # @return [String] a string containing a human-readable representation
229
+ # of this value object
171
230
  def inspect
172
231
  "#<Value: #{name.inspect} : #{value.inspect}>"
173
232
  end
174
233
 
234
+ # @return [String] a string representation of this value
175
235
  def to_s
176
236
  "#{name} : #{value}"
177
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,10 +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,
7
- :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
8
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]
9
54
  def initialize(title, parent, options)
10
55
  @closed = options[:closed]
11
56
  @title = title
@@ -13,15 +58,18 @@ module PDF
13
58
  @count = 0
14
59
  end
15
60
 
61
+ # A hash representation of this outline item.
62
+ #
63
+ # @return [Hash]
16
64
  def to_hash
17
65
  hash = {
18
66
  Title: title,
19
67
  Parent: parent,
20
- Count: closed ? -count : count
68
+ Count: closed ? -count : count,
21
69
  }
22
70
  [
23
71
  { First: first }, { Last: last }, { Next: defined?(@next) && @next },
24
- { Prev: prev }, { Dest: dest }
72
+ { Prev: prev }, { Dest: dest },
25
73
  ].each do |h|
26
74
  unless h.values.first.nil?
27
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