origami-docspring 2.2.0 → 2.3.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.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/examples/attachments/attachment.rb +7 -8
  4. data/examples/attachments/nested_document.rb +6 -5
  5. data/examples/encryption/encryption.rb +5 -4
  6. data/examples/events/events.rb +7 -6
  7. data/examples/flash/flash.rb +10 -9
  8. data/examples/forms/javascript.rb +14 -13
  9. data/examples/forms/xfa.rb +67 -66
  10. data/examples/javascript/hello_world.rb +6 -5
  11. data/examples/javascript/js_emulation.rb +26 -26
  12. data/examples/loop/goto.rb +12 -11
  13. data/examples/loop/named.rb +17 -16
  14. data/examples/signature/signature.rb +11 -11
  15. data/examples/uri/javascript.rb +25 -24
  16. data/examples/uri/open-uri.rb +5 -4
  17. data/examples/uri/submitform.rb +11 -10
  18. data/lib/origami/3d.rb +330 -334
  19. data/lib/origami/acroform.rb +267 -268
  20. data/lib/origami/actions.rb +266 -278
  21. data/lib/origami/annotations.rb +659 -670
  22. data/lib/origami/array.rb +192 -196
  23. data/lib/origami/boolean.rb +66 -70
  24. data/lib/origami/catalog.rb +360 -363
  25. data/lib/origami/collections.rb +132 -133
  26. data/lib/origami/compound.rb +125 -129
  27. data/lib/origami/destinations.rb +226 -237
  28. data/lib/origami/dictionary.rb +155 -154
  29. data/lib/origami/encryption.rb +967 -923
  30. data/lib/origami/extensions/fdf.rb +270 -275
  31. data/lib/origami/extensions/ppklite.rb +323 -328
  32. data/lib/origami/filespec.rb +170 -173
  33. data/lib/origami/filters/ascii.rb +162 -167
  34. data/lib/origami/filters/ccitt/tables.rb +248 -252
  35. data/lib/origami/filters/ccitt.rb +309 -312
  36. data/lib/origami/filters/crypt.rb +31 -34
  37. data/lib/origami/filters/dct.rb +47 -50
  38. data/lib/origami/filters/flate.rb +57 -60
  39. data/lib/origami/filters/jbig2.rb +50 -53
  40. data/lib/origami/filters/jpx.rb +40 -43
  41. data/lib/origami/filters/lzw.rb +151 -155
  42. data/lib/origami/filters/predictors.rb +250 -255
  43. data/lib/origami/filters/runlength.rb +111 -115
  44. data/lib/origami/filters.rb +319 -325
  45. data/lib/origami/font.rb +173 -177
  46. data/lib/origami/functions.rb +62 -66
  47. data/lib/origami/graphics/colors.rb +203 -208
  48. data/lib/origami/graphics/instruction.rb +79 -81
  49. data/lib/origami/graphics/path.rb +141 -144
  50. data/lib/origami/graphics/patterns.rb +156 -160
  51. data/lib/origami/graphics/render.rb +51 -47
  52. data/lib/origami/graphics/state.rb +144 -142
  53. data/lib/origami/graphics/text.rb +185 -188
  54. data/lib/origami/graphics/xobject.rb +818 -804
  55. data/lib/origami/graphics.rb +25 -26
  56. data/lib/origami/header.rb +63 -65
  57. data/lib/origami/javascript.rb +718 -651
  58. data/lib/origami/linearization.rb +284 -285
  59. data/lib/origami/metadata.rb +156 -135
  60. data/lib/origami/name.rb +98 -100
  61. data/lib/origami/null.rb +49 -51
  62. data/lib/origami/numeric.rb +133 -135
  63. data/lib/origami/obfuscation.rb +180 -182
  64. data/lib/origami/object.rb +634 -631
  65. data/lib/origami/optionalcontent.rb +147 -149
  66. data/lib/origami/outline.rb +46 -48
  67. data/lib/origami/outputintents.rb +76 -77
  68. data/lib/origami/page.rb +637 -596
  69. data/lib/origami/parser.rb +214 -221
  70. data/lib/origami/parsers/fdf.rb +44 -45
  71. data/lib/origami/parsers/pdf/lazy.rb +147 -154
  72. data/lib/origami/parsers/pdf/linear.rb +104 -109
  73. data/lib/origami/parsers/pdf.rb +109 -107
  74. data/lib/origami/parsers/ppklite.rb +44 -46
  75. data/lib/origami/pdf.rb +886 -896
  76. data/lib/origami/reference.rb +116 -120
  77. data/lib/origami/signature.rb +617 -625
  78. data/lib/origami/stream.rb +560 -558
  79. data/lib/origami/string.rb +366 -368
  80. data/lib/origami/template/patterns.rb +50 -52
  81. data/lib/origami/template/widgets.rb +111 -114
  82. data/lib/origami/trailer.rb +153 -157
  83. data/lib/origami/tree.rb +55 -57
  84. data/lib/origami/version.rb +19 -19
  85. data/lib/origami/webcapture.rb +87 -90
  86. data/lib/origami/xfa/config.rb +409 -414
  87. data/lib/origami/xfa/connectionset.rb +113 -117
  88. data/lib/origami/xfa/datasets.rb +38 -42
  89. data/lib/origami/xfa/localeset.rb +33 -37
  90. data/lib/origami/xfa/package.rb +49 -52
  91. data/lib/origami/xfa/pdf.rb +54 -59
  92. data/lib/origami/xfa/signature.rb +33 -37
  93. data/lib/origami/xfa/sourceset.rb +34 -38
  94. data/lib/origami/xfa/stylesheet.rb +35 -39
  95. data/lib/origami/xfa/template.rb +1630 -1634
  96. data/lib/origami/xfa/xdc.rb +33 -37
  97. data/lib/origami/xfa/xfa.rb +132 -123
  98. data/lib/origami/xfa/xfdf.rb +34 -38
  99. data/lib/origami/xfa/xmpmeta.rb +34 -38
  100. data/lib/origami/xfa.rb +50 -53
  101. data/lib/origami/xreftable.rb +462 -462
  102. data/lib/origami.rb +37 -38
  103. data/test/test_actions.rb +22 -20
  104. data/test/test_annotations.rb +54 -52
  105. data/test/test_forms.rb +23 -21
  106. data/test/test_native_types.rb +82 -78
  107. data/test/test_object_tree.rb +25 -24
  108. data/test/test_pages.rb +43 -41
  109. data/test/test_pdf.rb +2 -0
  110. data/test/test_pdf_attachment.rb +23 -21
  111. data/test/test_pdf_create.rb +16 -15
  112. data/test/test_pdf_encrypt.rb +69 -66
  113. data/test/test_pdf_parse.rb +131 -129
  114. data/test/test_pdf_parse_lazy.rb +53 -53
  115. data/test/test_pdf_sign.rb +67 -67
  116. data/test/test_streams.rb +145 -143
  117. data/test/test_xrefs.rb +46 -45
  118. metadata +64 -8
@@ -1,176 +1,169 @@
1
- =begin
2
-
3
- This file is part of Origami, PDF manipulation framework for Ruby
4
- Copyright (C) 2016 Guillaume Delugré.
5
-
6
- Origami is free software: you can redistribute it and/or modify
7
- it under the terms of the GNU Lesser General Public License as published by
8
- the Free Software Foundation, either version 3 of the License, or
9
- (at your option) any later version.
10
-
11
- Origami is distributed in the hope that it will be useful,
12
- but WITHOUT ANY WARRANTY; without even the implied warranty of
13
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
- GNU Lesser General Public License for more details.
15
-
16
- You should have received a copy of the GNU Lesser General Public License
17
- along with Origami. If not, see <http://www.gnu.org/licenses/>.
18
-
19
- =end
20
-
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # This file is part of Origami, PDF manipulation framework for Ruby
5
+ # Copyright (C) 2016 Guillaume Delugré.
6
+ #
7
+ # Origami is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published by
9
+ # the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # Origami is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Lesser General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with Origami. If not, see <http://www.gnu.org/licenses/>.
19
+ #
21
20
 
22
21
  require 'origami/parsers/pdf'
23
22
 
24
23
  module Origami
24
+ class PDF
25
+ #
26
+ # Create a new PDF lazy Parser.
27
+ #
28
+ class LazyParser < Parser
29
+ def parse(stream)
30
+ super
31
+
32
+ pdf = parse_initialize
33
+ revisions = []
34
+
35
+ # Locate the last xref offset at the end of the file.
36
+ xref_offset = locate_last_xref_offset
37
+
38
+ while xref_offset && (xref_offset != 0)
39
+
40
+ # Create a new revision based on the xref section offset.
41
+ revision = parse_revision(pdf, xref_offset)
42
+
43
+ # Locate the previous xref section.
44
+ xref_offset = if revision.xrefstm? && revision.xrefstm[:Prev].is_a?(Integer)
45
+ revision.xrefstm[:Prev].to_i
46
+ elsif revision.trailer[:Prev].is_a?(Integer)
47
+ revision.trailer[:Prev].to_i
48
+ end
49
+
50
+ # Prepend the revision.
51
+ revisions.unshift(revision)
52
+ end
25
53
 
26
- class PDF
27
-
28
- #
29
- # Create a new PDF lazy Parser.
30
- #
31
- class LazyParser < Parser
32
- def parse(stream)
33
- super
34
-
35
- pdf = parse_initialize
36
- revisions = []
37
-
38
- # Locate the last xref offset at the end of the file.
39
- xref_offset = locate_last_xref_offset
40
-
41
- while xref_offset and xref_offset != 0
42
-
43
- # Create a new revision based on the xref section offset.
44
- revision = parse_revision(pdf, xref_offset)
45
-
46
- # Locate the previous xref section.
47
- if revision.xrefstm? and revision.xrefstm[:Prev].is_a?(Integer)
48
- xref_offset = revision.xrefstm[:Prev].to_i
49
- elsif revision.trailer[:Prev].is_a?(Integer)
50
- xref_offset = revision.trailer[:Prev].to_i
51
- else
52
- xref_offset = nil
53
- end
54
-
55
- # Prepend the revision.
56
- revisions.unshift(revision)
57
- end
58
-
59
- pdf.revisions.clear
60
- revisions.each do |rev|
61
- pdf.revisions.push(rev)
62
- pdf.insert(rev.xrefstm) if rev.xrefstm?
63
- end
64
-
65
- parse_finalize(pdf)
66
-
67
- pdf
68
- end
54
+ pdf.revisions.clear
55
+ revisions.each do |rev|
56
+ pdf.revisions.push(rev)
57
+ pdf.insert(rev.xrefstm) if rev.xrefstm?
58
+ end
69
59
 
70
- private
60
+ parse_finalize(pdf)
71
61
 
72
- #
73
- # The document is scanned starting from the end, by locating the last startxref token.
74
- #
75
- def locate_last_xref_offset
76
- # Set the scanner position at the end.
77
- @data.terminate
62
+ pdf
63
+ end
78
64
 
79
- # Locate the startxref token.
80
- until @data.match?(/#{Trailer::XREF_TOKEN}/)
81
- raise ParsingError, "No xref token found" if @data.pos == 0
82
- @data.pos -= 1
83
- end
65
+ private
84
66
 
85
- # Extract the offset of the last xref section.
86
- trailer = Trailer.parse(@data, self)
87
- raise ParsingError, "Cannot locate xref section" if trailer.startxref.zero?
67
+ #
68
+ # The document is scanned starting from the end, by locating the last startxref token.
69
+ #
70
+ def locate_last_xref_offset
71
+ # Set the scanner position at the end.
72
+ @data.terminate
88
73
 
89
- trailer.startxref
90
- end
74
+ # Locate the startxref token.
75
+ until @data.match?(/#{Trailer::XREF_TOKEN}/)
76
+ raise ParsingError, "No xref token found" if @data.pos == 0
77
+ @data.pos -= 1
78
+ end
91
79
 
92
- #
93
- # In the LazyParser, the revisions are parsed by jumping through the cross-references (table or streams).
94
- #
95
- def parse_revision(pdf, offset)
96
- raise ParsingError, "Invalid xref offset" unless offset.between?(0, @data.string.size - 1)
80
+ # Extract the offset of the last xref section.
81
+ trailer = Trailer.parse(@data, self)
82
+ raise ParsingError, "Cannot locate xref section" if trailer.startxref.zero?
97
83
 
98
- @data.pos = offset
84
+ trailer.startxref
85
+ end
99
86
 
100
- # Create a new revision.
101
- revision = PDF::Revision.new(pdf)
87
+ #
88
+ # In the LazyParser, the revisions are parsed by jumping through the cross-references (table or streams).
89
+ #
90
+ def parse_revision(pdf, offset)
91
+ raise ParsingError, "Invalid xref offset" unless offset.between?(0, @data.string.size - 1)
102
92
 
103
- # Regular xref section.
104
- if @data.match?(/#{XRef::Section::TOKEN}/)
105
- parse_revision_from_xreftable(revision)
93
+ @data.pos = offset
106
94
 
107
- # The xrefs are stored in a stream.
108
- else
109
- parse_revision_from_xrefstm(revision)
110
- end
95
+ # Create a new revision.
96
+ revision = PDF::Revision.new(pdf)
111
97
 
112
- revision
113
- end
98
+ # Regular xref section.
99
+ if @data.match?(/#{XRef::Section::TOKEN}/)
100
+ parse_revision_from_xreftable(revision)
114
101
 
115
- #
116
- # Assume the current pointer is at the xreftable of the revision.
117
- # We are expecting:
118
- # - a regular xref table, starting with xref
119
- # - a revision trailer
120
- #
121
- # The trailer may hold a XRefStm entry in case of hybrid references.
122
- #
123
- def parse_revision_from_xreftable(revision)
124
- xreftable = parse_xreftable
125
- raise ParsingError, "Cannot parse xref section" if xreftable.nil?
126
-
127
- revision.xreftable = xreftable
128
- revision.trailer = parse_trailer
129
-
130
- # Handle hybrid cross-references.
131
- if revision.trailer[:XRefStm].is_a?(Integer)
132
- begin
133
- offset = revision.trailer[:XRefStm].to_i
134
- xrefstm = parse_object(offset)
135
-
136
- if xrefstm.is_a?(XRefStream)
137
- revision.xrefstm = xrefstm
138
- else
139
- warn "Invalid xref stream at offset #{offset}"
140
- end
141
-
142
- rescue
143
- warn "Cannot parse xref stream at offset #{offset}"
144
- end
145
- end
146
- end
102
+ # The xrefs are stored in a stream.
103
+ else
104
+ parse_revision_from_xrefstm(revision)
105
+ end
147
106
 
148
- #
149
- # Assume the current pointer is at the xref stream of the revision.
150
- #
151
- # The XRefStream should normally be at the end of the revision.
152
- # We scan after the object for a trailer token.
153
- #
154
- # The revision is allowed not to have a trailer, and the stream
155
- # dictionary will be used as the trailer dictionary in that case.
156
- #
157
- def parse_revision_from_xrefstm(revision)
158
- xrefstm = parse_object
159
- raise ParsingError, "Invalid xref stream" unless xrefstm.is_a?(XRefStream)
160
-
161
- revision.xrefstm = xrefstm
162
-
163
- # Search for the trailer.
164
- if @data.skip_until Regexp.union(Trailer::XREF_TOKEN, *Trailer::TOKENS)
165
- @data.pos -= @data.matched_size
166
-
167
- revision.trailer = parse_trailer
168
- else
169
- warn "No trailer found."
170
- revision.trailer = Trailer.new
171
- end
107
+ revision
108
+ end
109
+
110
+ #
111
+ # Assume the current pointer is at the xreftable of the revision.
112
+ # We are expecting:
113
+ # - a regular xref table, starting with xref
114
+ # - a revision trailer
115
+ #
116
+ # The trailer may hold a XRefStm entry in case of hybrid references.
117
+ #
118
+ def parse_revision_from_xreftable(revision)
119
+ xreftable = parse_xreftable
120
+ raise ParsingError, "Cannot parse xref section" if xreftable.nil?
121
+
122
+ revision.xreftable = xreftable
123
+ revision.trailer = parse_trailer
124
+
125
+ # Handle hybrid cross-references.
126
+ if revision.trailer[:XRefStm].is_a?(Integer)
127
+ begin
128
+ offset = revision.trailer[:XRefStm].to_i
129
+ xrefstm = parse_object(offset)
130
+
131
+ if xrefstm.is_a?(XRefStream)
132
+ revision.xrefstm = xrefstm
133
+ else
134
+ warn "Invalid xref stream at offset #{offset}"
172
135
  end
136
+ rescue
137
+ warn "Cannot parse xref stream at offset #{offset}"
138
+ end
139
+ end
140
+ end
141
+
142
+ #
143
+ # Assume the current pointer is at the xref stream of the revision.
144
+ #
145
+ # The XRefStream should normally be at the end of the revision.
146
+ # We scan after the object for a trailer token.
147
+ #
148
+ # The revision is allowed not to have a trailer, and the stream
149
+ # dictionary will be used as the trailer dictionary in that case.
150
+ #
151
+ def parse_revision_from_xrefstm(revision)
152
+ xrefstm = parse_object
153
+ raise ParsingError, "Invalid xref stream" unless xrefstm.is_a?(XRefStream)
154
+
155
+ revision.xrefstm = xrefstm
156
+
157
+ # Search for the trailer.
158
+ if @data.skip_until Regexp.union(Trailer::XREF_TOKEN, *Trailer::TOKENS)
159
+ @data.pos -= @data.matched_size
160
+
161
+ revision.trailer = parse_trailer
162
+ else
163
+ warn "No trailer found."
164
+ revision.trailer = Trailer.new
173
165
  end
166
+ end
174
167
  end
175
-
168
+ end
176
169
  end
@@ -1,122 +1,117 @@
1
- =begin
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # This file is part of Origami, PDF manipulation framework for Ruby
5
+ # Copyright (C) 2016 Guillaume Delugré.
6
+ #
7
+ # Origami is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published by
9
+ # the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # Origami is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Lesser General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with Origami. If not, see <http://www.gnu.org/licenses/>.
19
+ #
2
20
 
3
- This file is part of Origami, PDF manipulation framework for Ruby
4
- Copyright (C) 2016 Guillaume Delugré.
21
+ require 'origami/parsers/pdf'
5
22
 
6
- Origami is free software: you can redistribute it and/or modify
7
- it under the terms of the GNU Lesser General Public License as published by
8
- the Free Software Foundation, either version 3 of the License, or
9
- (at your option) any later version.
23
+ module Origami
24
+ class PDF
25
+ #
26
+ # Create a new PDF linear Parser.
27
+ #
28
+ class LinearParser < Parser
29
+ def parse(stream)
30
+ super
10
31
 
11
- Origami is distributed in the hope that it will be useful,
12
- but WITHOUT ANY WARRANTY; without even the implied warranty of
13
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
- GNU Lesser General Public License for more details.
32
+ pdf = parse_initialize
15
33
 
16
- You should have received a copy of the GNU Lesser General Public License
17
- along with Origami. If not, see <http://www.gnu.org/licenses/>.
34
+ #
35
+ # Parse each revision
36
+ #
37
+ revision = 0
38
+ until @data.eos?
39
+ begin
40
+ pdf.add_new_revision unless revision.zero?
41
+
42
+ parse_revision(pdf, revision)
43
+ revision += 1
44
+ rescue
45
+ error "Cannot read : " + (@data.peek(10) + "...").inspect
46
+ error "Stopped on exception : " + $!.message
47
+ warn $!.backtrace.join($/)
48
+
49
+ break
50
+ end
51
+ end
18
52
 
19
- =end
53
+ pdf.loaded!
20
54
 
55
+ parse_finalize(pdf)
56
+ end
21
57
 
22
- require 'origami/parsers/pdf'
58
+ private
23
59
 
24
- module Origami
60
+ def parse_revision(pdf, revision_no)
61
+ revision = pdf.revisions[revision_no]
25
62
 
26
- class PDF
63
+ info "...Parsing revision #{revision_no + 1}..."
64
+ loop do
65
+ break if (object = parse_object).nil?
66
+ pdf.insert(object)
67
+ end
27
68
 
28
- #
29
- # Create a new PDF linear Parser.
30
- #
31
- class LinearParser < Parser
32
- def parse(stream)
33
- super
34
-
35
- pdf = parse_initialize
36
-
37
- #
38
- # Parse each revision
39
- #
40
- revision = 0
41
- until @data.eos? do
42
- begin
43
- pdf.add_new_revision unless revision.zero?
44
-
45
- parse_revision(pdf, revision)
46
- revision = revision + 1
47
-
48
- rescue
49
- error "Cannot read : " + (@data.peek(10) + "...").inspect
50
- error "Stopped on exception : " + $!.message
51
- STDERR.puts $!.backtrace.join($/)
52
-
53
- break
54
- end
55
- end
56
-
57
- pdf.loaded!
58
-
59
- parse_finalize(pdf)
60
- end
61
-
62
- private
63
-
64
- def parse_revision(pdf, revision_no)
65
- revision = pdf.revisions[revision_no]
66
-
67
- info "...Parsing revision #{revision_no + 1}..."
68
- loop do
69
- break if (object = parse_object).nil?
70
- pdf.insert(object)
71
- end
72
-
73
- revision.xreftable = parse_xreftable
74
- revision.trailer = parse_trailer
75
-
76
- locate_xref_streams(pdf, revision_no)
77
-
78
- revision
79
- end
80
-
81
- def locate_xref_streams(pdf, revision_no)
82
- revision = pdf.revisions[revision_no]
83
- trailer = revision.trailer
84
- xrefstm = nil
85
-
86
- # Try to match the location of the last startxref / XRefStm with an XRefStream.
87
- if trailer.startxref != 0
88
- xrefstm = pdf.get_object_by_offset(trailer.startxref)
89
- elsif trailer.key?(:XRefStm)
90
- xrefstm = pdf.get_object_by_offset(trailer[:XRefStm])
91
- end
92
-
93
- if xrefstm.is_a?(XRefStream)
94
- warn "Found a XRefStream for revision #{revision_no + 1} at #{xrefstm.reference}"
95
- revision.xrefstm = xrefstm
96
-
97
- if xrefstm.key?(:Prev)
98
- locate_prev_xref_streams(pdf, revision_no, xrefstm)
99
- end
100
- end
101
- end
102
-
103
- def locate_prev_xref_streams(pdf, revision_no, xrefstm)
104
- return unless revision_no > 0 and xrefstm.Prev.is_a?(Integer)
105
-
106
- prev_revision = pdf.revisions[revision_no - 1]
107
- prev_offset = xrefstm.Prev.to_i
108
- prev_xrefstm = pdf.get_object_by_offset(prev_offset)
109
-
110
- if prev_xrefstm.is_a?(XRefStream)
111
- warn "Found a previous XRefStream for revision #{revision_no} at #{prev_xrefstm.reference}"
112
- prev_revision.xrefstm = prev_xrefstm
113
-
114
- if prev_xrefstm.key?(:Prev)
115
- locate_prev_xref_streams(pdf, revision_no - 1, prev_xrefstm)
116
- end
117
- end
118
- end
69
+ revision.xreftable = parse_xreftable
70
+ revision.trailer = parse_trailer
71
+
72
+ locate_xref_streams(pdf, revision_no)
73
+
74
+ revision
75
+ end
76
+
77
+ def locate_xref_streams(pdf, revision_no)
78
+ revision = pdf.revisions[revision_no]
79
+ trailer = revision.trailer
80
+ xrefstm = nil
81
+
82
+ # Try to match the location of the last startxref / XRefStm with an XRefStream.
83
+ if trailer.startxref != 0
84
+ xrefstm = pdf.get_object_by_offset(trailer.startxref)
85
+ elsif trailer.key?(:XRefStm)
86
+ xrefstm = pdf.get_object_by_offset(trailer[:XRefStm])
119
87
  end
120
- end
121
88
 
89
+ if xrefstm.is_a?(XRefStream)
90
+ warn "Found a XRefStream for revision #{revision_no + 1} at #{xrefstm.reference}"
91
+ revision.xrefstm = xrefstm
92
+
93
+ if xrefstm.key?(:Prev)
94
+ locate_prev_xref_streams(pdf, revision_no, xrefstm)
95
+ end
96
+ end
97
+ end
98
+
99
+ def locate_prev_xref_streams(pdf, revision_no, xrefstm)
100
+ return unless (revision_no > 0) && xrefstm.Prev.is_a?(Integer)
101
+
102
+ prev_revision = pdf.revisions[revision_no - 1]
103
+ prev_offset = xrefstm.Prev.to_i
104
+ prev_xrefstm = pdf.get_object_by_offset(prev_offset)
105
+
106
+ if prev_xrefstm.is_a?(XRefStream)
107
+ warn "Found a previous XRefStream for revision #{revision_no} at #{prev_xrefstm.reference}"
108
+ prev_revision.xrefstm = prev_xrefstm
109
+
110
+ if prev_xrefstm.key?(:Prev)
111
+ locate_prev_xref_streams(pdf, revision_no - 1, prev_xrefstm)
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
122
117
  end