pdf-core 0.8.1 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 72b9c9e3df4caa328d5e4166d6843e77d4eb2ca8
4
- data.tar.gz: 7833e82447f0f3ae5f25a0c9c5ad3f973442944b
2
+ SHA256:
3
+ metadata.gz: 23799dd91a7e3063a84724ecbdea6989de1b7131f1727bc6923c76b1637f0a87
4
+ data.tar.gz: 73538fe3357a195e9c3a39f7dfb1e498d0c3a49fc3c8cc6b4b0e72cc054dbd54
5
5
  SHA512:
6
- metadata.gz: 1240f1c5ff136fb1df4ca8a34a1cc1461fea31b672ae9340385ed0e8d7c61d300259e4ffa7eabee3362969de5de70d234604dac376e0b9ed822cffaece07931b
7
- data.tar.gz: dcd7098604c252a3cb8315a02ba58ceabca12de4fa51319ecb2ed4a1f4bf3054d79f1defdea0085a32743d8eb3daf9265f1f305c480511fa8d23f56f8f3b02cd
6
+ metadata.gz: e90a4a8303c8633452743ad491b21c57034551beb284458da03c0c69d135c41bcfe349027ed7ded6bf55e71f8f7dee0997898342cea7f4ed8725bdb1de1182fc
7
+ data.tar.gz: 5d048fdd3adf42181118c0340a2df17ec60e9c32e92f4699bc7e2929d508ee70d5d7888ce7d9a8b12ec06aae8423158ff184321f87aa10449f8ddc721b239f22
checksums.yaml.gz.sig CHANGED
Binary file
@@ -10,10 +10,43 @@ module PDF
10
10
  module Core
11
11
  # Provides very low-level support for annotations.
12
12
  #
13
- module Annotations #:nodoc:
14
- # Adds a new annotation (section 8.4 in PDF spec) to the current page.
15
- # +options+ must be a Hash describing the annotation.
13
+ # @api private
14
+ module Annotations
15
+ # Adds a new annotation (section *8.4 Annotations* in PDF 1.7 spec) to the
16
+ # current page.
16
17
  #
18
+ # @param options [Hash] Annotation options. This is basically an `Annot`
19
+ # dict as decribed in the PDF spec.
20
+ # @option options [Symbol<:Text, :Link, :FreeText, :Line, :Square,
21
+ # :Circle, :Polygon, :PolyLine, :Highlight, :Underline, :Squiggly,
22
+ # :StrikeOut, :Stamp, :Caret, :Ink, :Popup, :FileAttachment, :Sound,
23
+ # :Movie, :Widget, :Screen, :PrinterMark, :TrapNet, :Watermark, :3D>]
24
+ # :Subtype The type of annotation
25
+ # @option options [Array<Numeric, 4>] :Rect The annotation rectangle
26
+ # @option options [String] :Contents Text to be displayed for the
27
+ # annotation or, if this type of annotation does not display text, an
28
+ # alternate description of the annotation's contents in human-readable
29
+ # form.
30
+ # @option options [PDF::Core::Reference] :P An indirect reference to the
31
+ # page object with which this annotation is associated.
32
+ # @option options [String] :NM The annotation name, a text string uniquely
33
+ # identifying it among all the annotations on its page.
34
+ # @option options [Date, Time, String] :M The date and time when the
35
+ # annotation was most recently modified
36
+ # @option options [Integer] :F A set of flags specifying various
37
+ # characteristics of the annotation
38
+ # @option options [Hash] :AP An appearance dictionary specifying how the
39
+ # annotation is presented visually on the page
40
+ # @option options [Symbol] :AS The annotation's appearance state
41
+ # @option options [Array<(Numeric, Array<Numeric>)>] :Border the
42
+ # characteristics of the annotation's border
43
+ # @option options [Array<Float>] :C Color
44
+ # @option options [Integer] :StructParent The integer key of the
45
+ # annotation's entry in the structural parent tree
46
+ # @option options [Hash] :OC An optional content group or optional content
47
+ # membership dictionary
48
+ #
49
+ # @return [options]
17
50
  def annotate(options)
18
51
  state.page.dictionary.data[:Annots] ||= []
19
52
  options = sanitize_annotation_hash(options)
@@ -21,23 +54,28 @@ module PDF
21
54
  options
22
55
  end
23
56
 
24
- # A convenience method for creating Text annotations. +rect+ must be an
25
- # array of four numbers, describing the bounds of the annotation.
26
- # +contents+ should be a string, to be shown when the annotation is
27
- # activated.
57
+ # A convenience method for creating `Text` annotations.
58
+ #
59
+ # @param rect [Array<Numeric>] An array of four numbers,
60
+ # describing the bounds of the annotation.
61
+ # @param contents [String] Contents of the annotation
28
62
  #
63
+ # @return [Hash] Annotation dictionary
29
64
  def text_annotation(rect, contents, options = {})
30
65
  options = options.merge(Subtype: :Text, Rect: rect, Contents: contents)
31
66
  annotate(options)
32
67
  end
33
68
 
34
- # A convenience method for creating Link annotations. +rect+ must be an
35
- # array of four numbers, describing the bounds of the annotation. The
36
- # +options+ hash should include either :Dest (describing the target
37
- # destination, usually as a string that has been recorded in the
38
- # document's Dests tree), or :A (describing an action to perform on
39
- # clicking the link), or :PA (for describing a URL to link to).
69
+ # A convenience method for creating `Link` annotations.
70
+ #
71
+ # @param rect [Array<Numeric>] An array of four numbers,
72
+ # describing the bounds of the annotation.
73
+ # @param options [Hash] Should include either `:Dest` (describing the target
74
+ # destination, usually as a string that has been recorded in the
75
+ # document's `Dests` tree), or `:A` (describing an action to perform on
76
+ # clicking the link), or `:PA` (for describing a URL to link to).
40
77
  #
78
+ # @return [Hash] Annotation dictionary
41
79
  def link_annotation(rect, options = {})
42
80
  options = options.merge(Subtype: :Link, Rect: rect)
43
81
  annotate(options)
@@ -1,11 +1,12 @@
1
-
2
1
  # frozen_string_literal: true
3
2
 
4
3
  module PDF
5
4
  module Core
6
5
  # This is used to differentiate strings that must be encoded as
7
6
  # a byte string, such as binary data from encrypted strings.
8
- class ByteString < String #:nodoc:
7
+ #
8
+ # @api private
9
+ class ByteString < String
9
10
  end
10
11
  end
11
12
  end
@@ -1,90 +1,130 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Implements destination support for PDF
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
-
9
3
  module PDF
10
4
  module Core
11
- module Destinations #:nodoc:
5
+ # Implements destination support for PDF
6
+ #
7
+ # @api private
8
+ module Destinations
12
9
  # The maximum number of children to fit into a single node in the Dests
13
10
  # tree.
14
- NAME_TREE_CHILDREN_LIMIT = 20 #:nodoc:
11
+ #
12
+ # @private
13
+ NAME_TREE_CHILDREN_LIMIT = 20
15
14
 
16
- # The Dests name tree in the Name dictionary (see
17
- # Prawn::Document::Internal#names). This name tree is used to store named
18
- # destinations (PDF spec 8.2.1). (For more on name trees, see section
19
- # 3.8.4 in the PDF spec.)
15
+ # The `:Dests` name tree in the Name dictionary. This name tree is used to
16
+ # store named destinations (PDF 1.7 spec 8.2.1). (For more on name trees,
17
+ # see section 3.8.5 in the PDF 1.7 spec.)
20
18
  #
19
+ # @return [PDF::Core::Reference<PDF::Core::NameTree::Node>]
20
+ # @see Prawn::Document::Internal#names
21
21
  def dests
22
22
  names.data[:Dests] ||= ref!(
23
- PDF::Core::NameTree::Node.new(self, NAME_TREE_CHILDREN_LIMIT)
23
+ PDF::Core::NameTree::Node.new(self, NAME_TREE_CHILDREN_LIMIT),
24
24
  )
25
25
  end
26
26
 
27
- # Adds a new destination to the dests name tree (see #dests). The
28
- # +reference+ parameter will be converted into a PDF::Core::Reference if
29
- # it is not already one.
27
+ # Adds a new destination to the Dests name tree.
30
28
  #
29
+ # @param name [Symbol] Destination name
30
+ # @param reference [PDF::Core::Reference, Array, Hash] Destination
31
+ # definition, will be converted into a {PDF::Core::Reference} if it is
32
+ # not already one.
33
+ # @return [void]
34
+ # @see #dests
31
35
  def add_dest(name, reference)
32
36
  reference = ref!(reference) unless reference.is_a?(PDF::Core::Reference)
33
37
  dests.data.add(name, reference)
34
38
  end
35
39
 
36
- # Return a Dest specification for a specific location (and optional zoom
40
+ # Builds a Dest specification for a specific location (and optional zoom
37
41
  # level).
38
42
  #
43
+ # @param left [Numeric]
44
+ # @param top [Numeric]
45
+ # @param zoom [Numeric]
46
+ # @param dest_page [PDF::Core::Page]
47
+ # @return [Array(PDF::Core::Reference, :XYZ, Numeric, Numeric, [Numeric, null])] a Dest
48
+ # specification for a specific location
39
49
  def dest_xyz(left, top, zoom = nil, dest_page = page)
40
50
  [dest_page.dictionary, :XYZ, left, top, zoom]
41
51
  end
42
52
 
43
- # Return a Dest specification that will fit the given page into the
53
+ # builds a Dest specification that will fit the given page into the
44
54
  # viewport.
45
55
  #
56
+ # @param dest_page [PDF::Core::Page]
57
+ # @return [Array(PDF::Core::Reference, :Fit)] a Dest specification for a page fitting
58
+ # viewport
46
59
  def dest_fit(dest_page = page)
47
60
  [dest_page.dictionary, :Fit]
48
61
  end
49
62
 
50
- # Return a Dest specification that will fit the given page horizontally
63
+ # Builds a Dest specification that will fit the given page horizontally
51
64
  # into the viewport, aligned vertically at the given top coordinate.
52
65
  #
66
+ # @param top [Numeric]
67
+ # @param dest_page [PDF::Core::Page]
68
+ # @return [Array(PDF::Core::Reference, :FitH, Numeric)] a Dest specification for a page
69
+ # content fitting horizontally at a given top coordinate
53
70
  def dest_fit_horizontally(top, dest_page = page)
54
71
  [dest_page.dictionary, :FitH, top]
55
72
  end
56
73
 
57
- # Return a Dest specification that will fit the given page vertically
74
+ # Build a Dest specification that will fit the given page vertically
58
75
  # into the viewport, aligned horizontally at the given left coordinate.
59
76
  #
77
+ # @param left [Numeric]
78
+ # @param dest_page [PDF::Core::Page]
79
+ # @return [Array(Hash, :FitV, Numeric)] a Dest specification for a page
80
+ # content fitting vertically at a given left coordinate
60
81
  def dest_fit_vertically(left, dest_page = page)
61
82
  [dest_page.dictionary, :FitV, left]
62
83
  end
63
84
 
64
- # Return a Dest specification that will fit the given rectangle into the
85
+ # Builds a Dest specification that will fit the given rectangle into the
65
86
  # viewport, for the given page.
66
87
  #
88
+ # @param left [Numeric]
89
+ # @param bottom [Numeric]
90
+ # @param right [Numeric]
91
+ # @param top [Numeric]
92
+ # @param dest_page [PDF::Core::Page]
93
+ # @return [Array(Hash, :FitR, Numeric, Numeric, Numeric, Numeric)]
94
+ # a Dest specification for a page fitting the given rectangle in the
95
+ # viewport
67
96
  def dest_fit_rect(left, bottom, right, top, dest_page = page)
68
97
  [dest_page.dictionary, :FitR, left, bottom, right, top]
69
98
  end
70
99
 
71
- # Return a Dest specfication that will fit the given page's bounding box
100
+ # Builds a Dest specification that will fit the given page's bounding box
72
101
  # into the viewport.
73
102
  #
103
+ # @param dest_page [PDF::Core::Page]
104
+ # @return [Array(PDF::Core::Reference, :FitB)] a Dest specification for a page fitting
105
+ # bounding box into viewport
74
106
  def dest_fit_bounds(dest_page = page)
75
107
  [dest_page.dictionary, :FitB]
76
108
  end
77
109
 
78
- # Same as #dest_fit_horizontally, but works on the page's bounding box
110
+ # Same as {#dest_fit_horizontally}, but works on the page's bounding box
79
111
  # instead of the entire page.
80
112
  #
113
+ # @param top [Numeric]
114
+ # @param dest_page [PDF::Core::Page]
115
+ # @return [Array(PDF::Core::Reference, :FitBH, Numeric)] a Dest specification for a page
116
+ # bounding box fitting horizontally at a given top coordinate
81
117
  def dest_fit_bounds_horizontally(top, dest_page = page)
82
118
  [dest_page.dictionary, :FitBH, top]
83
119
  end
84
120
 
85
- # Same as #dest_fit_vertically, but works on the page's bounding box
121
+ # Same as {#dest_fit_vertically}, but works on the page's bounding box
86
122
  # instead of the entire page.
87
123
  #
124
+ # @param left [Numeric]
125
+ # @param dest_page [PDF::Core::Page]
126
+ # @return [Array(PDF::Core::Reference, :FitBV, Numeric)] a Dest specification for a page
127
+ # bounding box fitting vertically at a given top coordinate
88
128
  def dest_fit_bounds_vertically(left, dest_page = page)
89
129
  [dest_page.dictionary, :FitBV, left]
90
130
  end
@@ -2,7 +2,21 @@
2
2
 
3
3
  module PDF
4
4
  module Core
5
- class DocumentState #:nodoc:
5
+ # Low-level PDF document representation mostly for keeping intermediate
6
+ # state while document is being constructed.
7
+ #
8
+ # @api private
9
+ class DocumentState
10
+ # @param options [Hash<Symbol, any>]
11
+ # @option options :info [Hash] Document's information dictionary
12
+ # @option options :print_scaling [:none, nil] Viewr preference for
13
+ # printing scaling
14
+ # @option options :trailer [Hash] ({}) File trailer
15
+ # @option options :compress [Boolean] (false) Whether to compress streams
16
+ # @option options :encrypt [Boolean] (false) Whether to encrypt the
17
+ # document
18
+ # @option options :encryption_key [String] (nil) Encryption key. Must be
19
+ # provided if `:encrypt` is `true`
6
20
  def initialize(options)
7
21
  normalize_metadata(options)
8
22
 
@@ -10,38 +24,87 @@ module PDF
10
24
  if options[:print_scaling]
11
25
  PDF::Core::ObjectStore.new(
12
26
  info: options[:info],
13
- print_scaling: options[:print_scaling]
27
+ print_scaling: options[:print_scaling],
14
28
  )
15
29
  else
16
30
  PDF::Core::ObjectStore.new(info: options[:info])
17
31
  end
18
32
 
19
- @version = 1.3
20
- @pages = []
21
- @page = nil
22
- @trailer = options.fetch(:trailer, {})
23
- @compress = options.fetch(:compress, false)
24
- @encrypt = options.fetch(:encrypt, false)
25
- @encryption_key = options[:encryption_key]
26
- @skip_encoding = options.fetch(:skip_encoding, false)
33
+ @version = 1.3
34
+ @pages = []
35
+ @page = nil
36
+ @trailer = options.fetch(:trailer, {})
37
+ @compress = options.fetch(:compress, false)
38
+ @encrypt = options.fetch(:encrypt, false)
39
+ @encryption_key = options[:encryption_key]
40
+ @skip_encoding = options.fetch(:skip_encoding, false)
27
41
  @before_render_callbacks = []
28
42
  @on_page_create_callback = nil
29
43
  end
30
44
 
31
- attr_accessor :store, :version, :pages, :page, :trailer, :compress,
32
- :encrypt, :encryption_key, :skip_encoding,
33
- :before_render_callbacks, :on_page_create_callback
45
+ # Object store
46
+ # @return [PDF::Core::ObjectStore]
47
+ attr_accessor :store
34
48
 
49
+ # PDF version used in this document
50
+ # @return [Float]
51
+ attr_accessor :version
52
+
53
+ # Document pages
54
+ # @return [Array<PDF::Core::Page>]
55
+ attr_accessor :pages
56
+
57
+ # Current page
58
+ # @return [PDF::Core::Page]
59
+ attr_accessor :page
60
+
61
+ # Document trailer dict
62
+ # @return [Hash]
63
+ attr_accessor :trailer
64
+
65
+ # Whether to compress streams
66
+ # @return [Boolean]
67
+ attr_accessor :compress
68
+
69
+ # Whether to encrypt document
70
+ # @return [Boolean]
71
+ attr_accessor :encrypt
72
+
73
+ # Encryption key
74
+ # @return [String, nil]
75
+ attr_accessor :encryption_key
76
+
77
+ # @deprecated Unused
78
+ attr_accessor :skip_encoding
79
+
80
+ # Before render callbacks
81
+ # @return [Array<Proc>]
82
+ attr_accessor :before_render_callbacks
83
+
84
+ # A block to call when a new page is created
85
+ # @return [Proc, nil]
86
+ attr_accessor :on_page_create_callback
87
+
88
+ # Loads pages from object store. Only does it when there are no pages
89
+ # loaded and there are some pages in the store.
90
+ #
91
+ # @return [0] if no pages were loaded
92
+ # @return [Array<PDF::Core::Page>] if pages were laded
35
93
  def populate_pages_from_store(document)
36
94
  return 0 if @store.page_count <= 0 || !@pages.empty?
37
95
 
38
96
  count = (1..@store.page_count)
39
- @pages = count.map do |index|
40
- orig_dict_id = @store.object_id_for_page(index)
41
- PDF::Core::Page.new(document, object_id: orig_dict_id)
42
- end
97
+ @pages =
98
+ count.map { |index|
99
+ orig_dict_id = @store.object_id_for_page(index)
100
+ PDF::Core::Page.new(document, object_id: orig_dict_id)
101
+ }
43
102
  end
44
103
 
104
+ # Adds Prawn metadata to document info
105
+ #
106
+ # @param options [Hash]
107
+ # @return [Hash] Document `info` hash
45
108
  def normalize_metadata(options)
46
109
  options[:info] ||= {}
47
110
  options[:info][:Creator] ||= 'Prawn'
@@ -50,24 +113,44 @@ module PDF
50
113
  options[:info]
51
114
  end
52
115
 
116
+ # Insert a page at the specified position.
117
+ #
118
+ # @param page [PDF::Core::Page]
119
+ # @param page_number [Integer]
120
+ # @return [void]
53
121
  def insert_page(page, page_number)
54
122
  pages.insert(page_number, page)
55
123
  store.pages.data[:Kids].insert(page_number, page.dictionary)
56
124
  store.pages.data[:Count] += 1
57
125
  end
58
126
 
127
+ # Execute page creation callback if one is defined
128
+ #
129
+ # @param doc [Prawn::Document]
130
+ # @return [void]
59
131
  def on_page_create_action(doc)
60
132
  on_page_create_callback[doc] if on_page_create_callback
61
133
  end
62
134
 
135
+ # Executes before render callbacks
136
+ #
137
+ # @param _doc [Prawn::Document] Unused
138
+ # @return [void]
63
139
  def before_render_actions(_doc)
64
140
  before_render_callbacks.each { |c| c.call(self) }
65
141
  end
66
142
 
143
+ # Number of pages in the document
144
+ #
145
+ # @return [Integer]
67
146
  def page_count
68
147
  pages.length
69
148
  end
70
149
 
150
+ # Renders document body to the output
151
+ #
152
+ # @param output [#<<]
153
+ # @return [void]
71
154
  def render_body(output)
72
155
  store.each do |ref|
73
156
  ref.offset = output.size
@@ -2,11 +2,35 @@
2
2
 
3
3
  module PDF
4
4
  module Core
5
+ # A representation of a list of filters applied to a stream.
5
6
  class FilterList
7
+ # An exception one can expect when adding something to filter list that
8
+ # can not be interpreted as a filter.
9
+ class NotFilter < StandardError
10
+ # Generic default error message
11
+ DEFAULT_MESSAGE = 'Can not interpret input as a filter'
12
+
13
+ # Error message template with more details
14
+ MESSAGE_WITH_FILTER = 'Can not interpret input as a filter: %<filter>s'
15
+
16
+ def initialize(message = DEFAULT_MESSAGE, filter: nil)
17
+ if filter
18
+ super(format(MESSAGE_WITH_FILTER, filter: filter))
19
+ else
20
+ super(message)
21
+ end
22
+ end
23
+ end
24
+
6
25
  def initialize
7
26
  @list = []
8
27
  end
9
28
 
29
+ # Appends a filter to the list
30
+ #
31
+ # @param filter [Symbol, Hash] a filter to append
32
+ # @return [self]
33
+ # @raise [NotFilter]
10
34
  def <<(filter)
11
35
  case filter
12
36
  when Symbol
@@ -16,37 +40,52 @@ module PDF
16
40
  @list << [name, params]
17
41
  end
18
42
  else
19
- raise "Can not interpret input as filter: #{filter.inspect}"
43
+ raise NotFilter.new(filter: filter)
20
44
  end
21
45
 
22
46
  self
23
47
  end
24
48
 
49
+ # A normalized representation of the filter list
50
+ #
51
+ # @return [Array<Array<(Symbol, [Hash, nil])>>]
25
52
  def normalized
26
53
  @list
27
54
  end
28
55
  alias to_a normalized
29
56
 
57
+ # Names of filters in the list
58
+ #
59
+ # @return [Array<Symbol>]
30
60
  def names
31
61
  @list.map do |(name, _)|
32
62
  name
33
63
  end
34
64
  end
35
65
 
66
+ # Parameters of filters
67
+ #
68
+ # @return [Array<[Hash, nil]>]
36
69
  def decode_params
37
70
  @list.map do |(_, params)|
38
71
  params
39
72
  end
40
73
  end
41
74
 
75
+ # @return [String]
42
76
  def inspect
43
77
  @list.inspect
44
78
  end
45
79
 
46
- def each
47
- @list.each do |filter|
48
- yield(filter)
49
- end
80
+ # Iterates over filters
81
+ #
82
+ # @yield [(name, decode_params)] an array of filter name and decode
83
+ # parameters
84
+ # @yieldparam name [Symbol] filter name
85
+ # @yieldparam decode_params [Hash, nil] decode params
86
+ # @return [Array<Array<(Symbol, [Hash, nil])>>] normalized filter list
87
+ def each(&block)
88
+ @list.each(&block)
50
89
  end
51
90
  end
52
91
  end
@@ -1,32 +1,51 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # prawn/core/filters.rb : Implements stream filters
4
- #
5
- # Copyright February 2013, Alexander Mankuta. All Rights Reserved.
6
- #
7
- # This is free software. Please see the LICENSE and COPYING files for details.
8
-
9
3
  require 'zlib'
10
4
 
11
5
  module PDF
12
6
  module Core
7
+ # Stream filters
13
8
  module Filters
9
+ # zlib/deflate compression
14
10
  module FlateDecode
11
+ # Encode stream data
12
+ #
13
+ # @param stream [String] stream data
14
+ # @param _params [nil] unused, here for API compatibility
15
+ # @return [String]
15
16
  def self.encode(stream, _params = nil)
16
17
  Zlib::Deflate.deflate(stream)
17
18
  end
18
19
 
20
+ # Decode stream data
21
+ #
22
+ # @param stream [String] stream data
23
+ # @param _params [nil] unused, here for API compatibility
24
+ # @return [String]
19
25
  def self.decode(stream, _params = nil)
20
26
  Zlib::Inflate.inflate(stream)
21
27
  end
22
28
  end
23
29
 
24
- # Pass through stub
30
+ # Data encoding using DCT (discrete cosine transform) technique based on
31
+ # the JPEG standard.
32
+ #
33
+ # Pass through stub.
25
34
  module DCTDecode
35
+ # Encode stream data
36
+ #
37
+ # @param stream [String] stream data
38
+ # @param _params [nil] unused, here for API compatibility
39
+ # @return [String]
26
40
  def self.encode(stream, _params = nil)
27
41
  stream
28
42
  end
29
43
 
44
+ # Decode stream data
45
+ #
46
+ # @param stream [String] stream data
47
+ # @param _params [nil] unused, here for API compatibility
48
+ # @return [String]
30
49
  def self.decode(stream, _params = nil)
31
50
  stream
32
51
  end