origami 1.2.7 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (162) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +66 -0
  3. data/README.md +112 -0
  4. data/bin/config/pdfcop.conf.yml +232 -233
  5. data/bin/gui/about.rb +27 -37
  6. data/bin/gui/config.rb +108 -117
  7. data/bin/gui/file.rb +416 -365
  8. data/bin/gui/gtkhex.rb +1138 -1153
  9. data/bin/gui/hexview.rb +55 -57
  10. data/bin/gui/imgview.rb +48 -51
  11. data/bin/gui/menu.rb +388 -386
  12. data/bin/gui/properties.rb +114 -130
  13. data/bin/gui/signing.rb +571 -617
  14. data/bin/gui/textview.rb +77 -95
  15. data/bin/gui/treeview.rb +382 -387
  16. data/bin/gui/walker.rb +227 -232
  17. data/bin/gui/xrefs.rb +56 -60
  18. data/bin/pdf2pdfa +53 -57
  19. data/bin/pdf2ruby +212 -228
  20. data/bin/pdfcop +338 -348
  21. data/bin/pdfdecompress +58 -65
  22. data/bin/pdfdecrypt +56 -60
  23. data/bin/pdfencrypt +75 -80
  24. data/bin/pdfexplode +185 -182
  25. data/bin/pdfextract +201 -218
  26. data/bin/pdfmetadata +83 -82
  27. data/bin/pdfsh +4 -5
  28. data/bin/pdfwalker +1 -2
  29. data/bin/shell/.irbrc +45 -82
  30. data/bin/shell/console.rb +105 -130
  31. data/bin/shell/hexdump.rb +40 -64
  32. data/examples/README.md +34 -0
  33. data/examples/attachments/attachment.rb +38 -0
  34. data/examples/attachments/nested_document.rb +51 -0
  35. data/examples/encryption/encryption.rb +28 -0
  36. data/{samples/actions/triggerevents/trigger.rb → examples/events/events.rb} +13 -16
  37. data/examples/flash/flash.rb +37 -0
  38. data/{samples → examples}/flash/helloworld.swf +0 -0
  39. data/examples/forms/javascript.rb +54 -0
  40. data/examples/forms/xfa.rb +115 -0
  41. data/examples/javascript/hello_world.rb +22 -0
  42. data/examples/javascript/js_emulation.rb +54 -0
  43. data/examples/loop/goto.rb +32 -0
  44. data/examples/loop/named.rb +33 -0
  45. data/examples/signature/signature.rb +65 -0
  46. data/examples/uri/javascript.rb +56 -0
  47. data/examples/uri/open-uri.rb +21 -0
  48. data/examples/uri/submitform.rb +47 -0
  49. data/lib/origami.rb +29 -42
  50. data/lib/origami/3d.rb +350 -225
  51. data/lib/origami/acroform.rb +262 -288
  52. data/lib/origami/actions.rb +268 -288
  53. data/lib/origami/annotations.rb +697 -722
  54. data/lib/origami/array.rb +258 -184
  55. data/lib/origami/boolean.rb +74 -84
  56. data/lib/origami/catalog.rb +397 -434
  57. data/lib/origami/collections.rb +144 -0
  58. data/lib/origami/destinations.rb +233 -194
  59. data/lib/origami/dictionary.rb +253 -232
  60. data/lib/origami/encryption.rb +1274 -1243
  61. data/lib/origami/export.rb +232 -268
  62. data/lib/origami/extensions/fdf.rb +307 -220
  63. data/lib/origami/extensions/ppklite.rb +368 -435
  64. data/lib/origami/filespec.rb +197 -0
  65. data/lib/origami/filters.rb +301 -295
  66. data/lib/origami/filters/ascii.rb +177 -180
  67. data/lib/origami/filters/ccitt.rb +528 -535
  68. data/lib/origami/filters/crypt.rb +26 -35
  69. data/lib/origami/filters/dct.rb +46 -52
  70. data/lib/origami/filters/flate.rb +95 -94
  71. data/lib/origami/filters/jbig2.rb +49 -55
  72. data/lib/origami/filters/jpx.rb +38 -44
  73. data/lib/origami/filters/lzw.rb +189 -183
  74. data/lib/origami/filters/predictors.rb +221 -235
  75. data/lib/origami/filters/runlength.rb +103 -104
  76. data/lib/origami/font.rb +173 -186
  77. data/lib/origami/functions.rb +67 -81
  78. data/lib/origami/graphics.rb +25 -21
  79. data/lib/origami/graphics/colors.rb +178 -187
  80. data/lib/origami/graphics/instruction.rb +79 -85
  81. data/lib/origami/graphics/path.rb +142 -148
  82. data/lib/origami/graphics/patterns.rb +160 -167
  83. data/lib/origami/graphics/render.rb +43 -50
  84. data/lib/origami/graphics/state.rb +138 -153
  85. data/lib/origami/graphics/text.rb +188 -205
  86. data/lib/origami/graphics/xobject.rb +819 -815
  87. data/lib/origami/header.rb +63 -78
  88. data/lib/origami/javascript.rb +596 -597
  89. data/lib/origami/linearization.rb +285 -290
  90. data/lib/origami/metadata.rb +139 -148
  91. data/lib/origami/name.rb +112 -148
  92. data/lib/origami/null.rb +53 -62
  93. data/lib/origami/numeric.rb +162 -175
  94. data/lib/origami/obfuscation.rb +186 -174
  95. data/lib/origami/object.rb +593 -573
  96. data/lib/origami/outline.rb +42 -47
  97. data/lib/origami/outputintents.rb +73 -82
  98. data/lib/origami/page.rb +703 -592
  99. data/lib/origami/parser.rb +238 -290
  100. data/lib/origami/parsers/fdf.rb +41 -33
  101. data/lib/origami/parsers/pdf.rb +75 -95
  102. data/lib/origami/parsers/pdf/lazy.rb +137 -0
  103. data/lib/origami/parsers/pdf/linear.rb +64 -66
  104. data/lib/origami/parsers/ppklite.rb +34 -70
  105. data/lib/origami/pdf.rb +1030 -1005
  106. data/lib/origami/reference.rb +102 -102
  107. data/lib/origami/signature.rb +591 -609
  108. data/lib/origami/stream.rb +668 -551
  109. data/lib/origami/string.rb +397 -373
  110. data/lib/origami/template/patterns.rb +56 -0
  111. data/lib/origami/template/widgets.rb +151 -0
  112. data/lib/origami/trailer.rb +144 -158
  113. data/lib/origami/tree.rb +62 -0
  114. data/lib/origami/version.rb +23 -0
  115. data/lib/origami/webcapture.rb +88 -79
  116. data/lib/origami/xfa.rb +2863 -2882
  117. data/lib/origami/xreftable.rb +472 -384
  118. data/test/dataset/calc.pdf +85 -0
  119. data/test/dataset/crypto.pdf +82 -0
  120. data/test/dataset/empty.pdf +49 -0
  121. data/test/test_actions.rb +27 -0
  122. data/test/test_annotations.rb +90 -0
  123. data/test/test_pages.rb +31 -0
  124. data/test/test_pdf.rb +16 -0
  125. data/test/test_pdf_attachment.rb +34 -0
  126. data/test/test_pdf_create.rb +24 -0
  127. data/test/test_pdf_encrypt.rb +95 -0
  128. data/test/test_pdf_parse.rb +96 -0
  129. data/test/test_pdf_sign.rb +58 -0
  130. data/test/test_streams.rb +182 -0
  131. data/test/test_xrefs.rb +67 -0
  132. metadata +88 -58
  133. data/README +0 -67
  134. data/bin/pdf2graph +0 -121
  135. data/bin/pdfcocoon +0 -104
  136. data/lib/origami/file.rb +0 -233
  137. data/samples/README.txt +0 -45
  138. data/samples/actions/launch/calc.rb +0 -87
  139. data/samples/actions/launch/winparams.rb +0 -22
  140. data/samples/actions/loop/loopgoto.rb +0 -24
  141. data/samples/actions/loop/loopnamed.rb +0 -21
  142. data/samples/actions/named/named.rb +0 -31
  143. data/samples/actions/samba/smbrelay.rb +0 -26
  144. data/samples/actions/webbug/submitform.js +0 -26
  145. data/samples/actions/webbug/webbug-browser.rb +0 -68
  146. data/samples/actions/webbug/webbug-js.rb +0 -67
  147. data/samples/actions/webbug/webbug-reader.rb +0 -90
  148. data/samples/attachments/attach.rb +0 -40
  149. data/samples/attachments/attached.txt +0 -1
  150. data/samples/crypto/crypto.rb +0 -28
  151. data/samples/digsig/signed.rb +0 -46
  152. data/samples/exploits/cve-2008-2992-utilprintf.rb +0 -87
  153. data/samples/exploits/cve-2009-0927-geticon.rb +0 -65
  154. data/samples/exploits/exploit_customdictopen.rb +0 -55
  155. data/samples/exploits/getannots.rb +0 -69
  156. data/samples/flash/flash.rb +0 -31
  157. data/samples/javascript/attached.txt +0 -1
  158. data/samples/javascript/js.rb +0 -52
  159. data/templates/patterns.rb +0 -66
  160. data/templates/widgets.rb +0 -173
  161. data/templates/xdp.rb +0 -92
  162. data/test/ts_pdf.rb +0 -50
@@ -0,0 +1,56 @@
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
+
21
+ module Origami
22
+
23
+ module Template
24
+
25
+ class AxialGradient < Graphics::Pattern::Shading::Axial
26
+ def initialize(from, to, color0, color1, coeff = 1)
27
+ super()
28
+
29
+ set_indirect(true)
30
+
31
+ x, y = from
32
+ tx, ty = to
33
+
34
+ c0 = Graphics::Color.to_a(color0)
35
+ c1 = Graphics::Color.to_a(color1)
36
+
37
+ space =
38
+ case c0.size
39
+ when 1 then Graphics::Color::Space::DEVICE_GRAY
40
+ when 3 then Graphics::Color::Space::DEVICE_RGB
41
+ when 4 then Graphics::Color::Space::DEVICE_CMYK
42
+ end
43
+
44
+ f = Function::Exponential.new
45
+ f.Domain = [ 0.0, 1.0 ]
46
+ f.N = coeff
47
+ f.C0, f.C1 = c0, c1
48
+
49
+ self.ColorSpace = space
50
+ self.Coords = [ x, y, tx, ty ]
51
+ self.Function = f
52
+ self.Extend = [ true, true ]
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,151 @@
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
+
21
+ module Origami
22
+
23
+ module Template
24
+
25
+ class Button < Annotation::Widget::PushButton
26
+ def initialize(caption, id: nil, x:, y:, width:, height:)
27
+ super()
28
+
29
+ set_indirect(true)
30
+
31
+ self.H = Annotation::Widget::Highlight::INVERT
32
+ self.Rect = [ x, y, x + width, y + height ]
33
+ self.F = Annotation::Flags::PRINT
34
+ self.T = id
35
+
36
+ appstm = Annotation::AppearanceStream.new.setFilter(:FlateDecode)
37
+ appstm.BBox = [ 0, 0, width, height ]
38
+ appstm.Matrix = [ 1, 0, 0, 1, 0, 0 ]
39
+
40
+ appstm.draw_rectangle(0, 0, width, height,
41
+ fill: true, stroke: false, fill_color: Graphics::Color::RGB.new(0xE6, 0xE6, 0xFA))
42
+
43
+ appstm.draw_polygon([[1,1],[1,height-1],[width-1,height-1],[width-2,height-2],[2,height-2],[2,2]],
44
+ fill: true, stroke: false, fill_color: Graphics::Color::GrayScale.new(1.0))
45
+
46
+ appstm.draw_polygon([[width-1,height-1],[width-1,1],[1,1],[2,2],[width-2,2],[width-2,height-2]],
47
+ fill: true, stroke: false, fill_color: Graphics::Color::RGB.new(130, 130, 130))
48
+
49
+ appstm.draw_rectangle(0.5, 0.5, width-1, height-1,
50
+ fill: false, stroke: true, stroke_color: Graphics::Color::GrayScale.new(0.0))
51
+
52
+ text_width = 4.75 * caption.length
53
+ appstm.write(caption,
54
+ x: (width - text_width)/2, y: height/2-5, size: 10)
55
+
56
+ appstm.Resources = Resources.new
57
+ set_normal_appearance(appstm)
58
+ end
59
+ end
60
+
61
+ class Edit < Annotation::Widget::Text
62
+ def initialize(id, x:, y:, width:, height:)
63
+ super()
64
+
65
+ set_indirect(true)
66
+
67
+ self.Rect = [ x, y, x+width, y+height ]
68
+ self.F = Annotation::Flags::PRINT
69
+ self.T = id
70
+ self.DA = '/F1 12 Tf 0 g'
71
+
72
+ appstm = Annotation::AppearanceStream.new.setFilter(:FlateDecode)
73
+ appstm.BBox = [ 0, 0, width, height ]
74
+ appstm.Matrix = [ 1, 0, 0, 1, 0, 0 ]
75
+
76
+ appstm.draw_rectangle(0, 0, width, height,
77
+ fill: false, stroke: true, stroke_color: Graphics::Color::GrayScale.new(0.0))
78
+
79
+ appstm.draw_polygon([[1,1],[1,height-1],[width-1,height-1],[width-2,height-2],[2,height-2],[2,2]],
80
+ fill: true, stroke: false, fill_color: Graphics::Color::RGB.new(130, 130, 130))
81
+
82
+ appstm.draw_polygon([[width-1,height-1],[width-1,1],[1,1],[2,2],[width-2,2],[width-2,height-2]],
83
+ fill: true, stroke: false, fill_color: Graphics::Color::GrayScale.new(1.0))
84
+
85
+ appstm.draw_rectangle(0.5, 0.5, width-1, height-1,
86
+ fill: false, stroke: true, stroke_color: Graphics::Color::GrayScale.new(0.0))
87
+
88
+ set_normal_appearance(appstm)
89
+ end
90
+ end
91
+
92
+ class MultiLineEdit < Edit
93
+ def initialize(id, x:, y:, width:, height:)
94
+ super(id, x: x, y: y, width: width, height: height)
95
+
96
+ self.Ff ||= 0
97
+ self.Ff |= Annotation::Widget::Text::Flags::MULTILINE
98
+ end
99
+ end
100
+
101
+ class RichTextEdit < MultiLineEdit
102
+ def initialize(id, x: , y:, width:, height:)
103
+ super(id, x: x, y: y, width: width, height: height)
104
+
105
+ self.F |= Annotation::Flags::READONLY
106
+ self.Ff |= (Annotation::Widget::Text::Flags::RICHTEXT | Field::Flags::READONLY)
107
+ end
108
+ end
109
+
110
+ class PasswordEdit < Edit
111
+ def initialize(id, x:, y:, width:, height:)
112
+ super(id, x: x, y: y, width: width, height: height)
113
+
114
+ self.Ff ||= 0
115
+ self.Ff |= Annotation::Widget::Text::Flags::PASSWORD
116
+ end
117
+ end
118
+
119
+ class TextPanel < Annotation::FreeText
120
+ def initialize(id, x:, y:, width:, height:)
121
+ super()
122
+
123
+ set_indirect(true)
124
+
125
+ self.Rect = [ x, y, x + width, y + height ]
126
+ self.F = Annotation::Flags::PRINT
127
+ self.NM = id
128
+ self.DA = '/F1 12 Tf 0 g'
129
+
130
+ appstm = Annotation::AppearanceStream.new.setFilter(:FlateDecode)
131
+ appstm.BBox = [ 0, 0, width, height ]
132
+ appstm.Matrix = [ 1, 0, 0, 1, 0, 0 ]
133
+
134
+ appstm.draw_rectangle(0, 0, width, height,
135
+ fill: false, stroke: true, stroke_color: Graphics::Color::GrayScale.new(0.0))
136
+
137
+ appstm.draw_polygon([[1,1],[1,height-1],[width-1,height-1],[width-2,height-2],[2,height-2],[2,2]],
138
+ fill: true, stroke: false, fill_color: Graphics::Color::RGB.new(130, 130, 130))
139
+
140
+ appstm.draw_polygon([[width-1,height-1],[width-1,1],[1,1],[2,2],[width-2,2],[width-2,height-2]],
141
+ fill: true, stroke: false, fill_color: Graphics::Color::GrayScale.new(1.0))
142
+
143
+ appstm.draw_rectangle(0.5, 0.5, width-1, height-1,
144
+ fill: false, stroke: true, stroke_color: Graphics::Color::GrayScale.new(0.0))
145
+
146
+ set_normal_appearance(appstm)
147
+ end
148
+ end
149
+ end
150
+
151
+ end
@@ -1,181 +1,167 @@
1
1
  =begin
2
2
 
3
- = File
4
- trailer.rb
5
-
6
- = Info
7
- This file is part of Origami, PDF manipulation framework for Ruby
8
- Copyright (C) 2010 Guillaume Delugré <guillaume AT security-labs DOT org>
9
- All right reserved.
10
-
11
- Origami is free software: you can redistribute it and/or modify
12
- it under the terms of the GNU Lesser General Public License as published by
13
- the Free Software Foundation, either version 3 of the License, or
14
- (at your option) any later version.
15
-
16
- Origami is distributed in the hope that it will be useful,
17
- but WITHOUT ANY WARRANTY; without even the implied warranty of
18
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
- GNU Lesser General Public License for more details.
20
-
21
- You should have received a copy of the GNU Lesser General Public License
22
- along with Origami. If not, see <http://www.gnu.org/licenses/>.
3
+ This file is part of Origami, PDF manipulation framework for Ruby
4
+ Copyright (C) 2016 Guillaume Delugré.
23
5
 
24
- =end
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.
25
15
 
26
- require 'digest/md5'
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
27
20
 
28
21
  module Origami
29
22
 
30
- class PDF
31
-
32
- private
23
+ class PDF
33
24
 
34
- def has_attr?(attr) #:nodoc:
35
- not get_doc_attr(attr).nil?
36
- end
25
+ private
37
26
 
38
- def get_doc_attr(attr) #:nodoc:
27
+ def trailer_key?(attr) #:nodoc:
28
+ !!trailer_key(attr)
29
+ end
30
+
31
+ def trailer_key(attr) #:nodoc:
32
+
33
+ @revisions.reverse_each do |rev|
34
+ if rev.trailer.has_dictionary? and not rev.trailer[attr].nil?
35
+ return rev.trailer[attr].solve
36
+ elsif rev.has_xrefstm?
37
+ xrefstm = rev.xrefstm
38
+ if xrefstm.is_a?(XRefStream) and xrefstm.has_field?(attr)
39
+ return xrefstm[attr].solve
40
+ end
41
+ end
42
+ end
39
43
 
40
- @revisions.reverse_each do |rev|
41
- if rev.trailer.has_dictionary? and not rev.trailer.dictionary[attr].nil?
42
- return rev.trailer.send(attr)
43
- else
44
- xrefstm = get_object_by_offset(rev.trailer.startxref)
45
- if xrefstm.is_a?(XRefStream) and xrefstm.has_field?(attr)
46
- return xrefstm.send(attr)
47
- end
44
+ nil
48
45
  end
49
- end
50
46
 
51
- nil
52
- end
47
+ def get_trailer_info #:nodoc:
48
+ #
49
+ # First look for a standard trailer dictionary
50
+ #
51
+ if @revisions.last.trailer.has_dictionary?
52
+ @revisions.last.trailer
53
+
54
+ #
55
+ # Otherwise look for a xref stream.
56
+ #
57
+ else
58
+ @revisions.last.xrefstm
59
+ end
60
+ end
53
61
 
54
- def get_trailer_info #:nodoc:
55
-
56
- #
57
- # First look for a standard trailer dictionary
58
- #
59
- if @revisions.last.trailer.has_dictionary?
60
- @revisions.last.trailer
61
-
62
- #
63
- # Otherwise look for a xref stream.
64
- #
65
- else
66
- xrefstm = get_object_by_offset(@revisions.last.trailer.startxref)
67
- xrefstm if xrefstm.is_a?(XRefStream)
68
- end
62
+ def generate_id
63
+ info = get_trailer_info
64
+ if info.nil?
65
+ raise InvalidPDFError, "Cannot access trailer information"
66
+ end
67
+
68
+ id = HexaString.new Random.new.bytes 16
69
+ info.ID = [ id, id ]
70
+ end
69
71
  end
70
-
71
- def gen_id
72
- fileInfo = get_trailer_info
73
- if fileInfo.nil?
74
- raise InvalidPDFError, "Cannot access trailer information"
75
- end
76
-
77
- id = Digest::MD5.hexdigest( rand.to_s )
78
- fileInfo.ID = [ id, id ]
72
+
73
+ class InvalidTrailerError < Error #:nodoc:
79
74
  end
80
75
 
81
- end
82
-
83
- class InvalidTrailerError < Exception #:nodoc:
84
- end
85
-
86
- # Forward declarations.
87
- class Catalog < Dictionary; end
88
- class Metadata < Dictionary; end
89
-
90
- #
91
- # Class representing a PDF file Trailer.
92
- #
93
- class Trailer
94
-
95
- include StandardObject
96
-
97
- TOKENS = %w{ trailer %%EOF } #:nodoc:
98
- XREF_TOKEN = "startxref" #:nodoc:
99
-
100
- @@regexp_open = Regexp.new(WHITESPACES + TOKENS.first + WHITESPACES)
101
- @@regexp_xref = Regexp.new(WHITESPACES + XREF_TOKEN + WHITESPACES + "(\\d+)")
102
- @@regexp_close = Regexp.new(WHITESPACES + TOKENS.last + WHITESPACES)
103
-
104
- attr_accessor :pdf
105
- attr_accessor :startxref
106
- attr_reader :dictionary
107
-
108
- field :Size, :Type => Integer, :Required => true
109
- field :Prev, :Type => Integer
110
- field :Root, :Type => Catalog, :Required => true
111
- field :Encrypt, :Type => Dictionary
112
- field :Info, :Type => Metadata
113
- field :ID, :Type => Array
114
- field :XRefStm, :Type => Integer
76
+ # Forward declarations.
77
+ class Catalog < Dictionary; end
78
+ class Metadata < Dictionary; end
115
79
 
116
80
  #
117
- # Creates a new Trailer.
118
- # _startxref_:: The file _offset_ to the XRef::Section.
119
- # _dictionary_:: A hash of attributes to set in the Trailer Dictionary.
81
+ # Class representing a PDF file Trailer.
120
82
  #
121
- def initialize(startxref = 0, dictionary = {})
122
-
123
- @startxref, self.dictionary = startxref, dictionary && Dictionary.new(dictionary)
124
- end
125
-
126
- def self.parse(stream, parser = nil) #:nodoc:
127
-
128
- if stream.skip(@@regexp_open)
129
- dictionary = Dictionary.parse(stream, parser)
130
- else
131
- dictionary = nil
132
- end
133
-
134
- if not stream.scan(@@regexp_xref)
135
- #raise InvalidTrailerError, "Cannot get startxref value"
136
- end
137
-
138
- startxref = (stream[3] && stream[3].to_i)
139
-
140
- if not stream.scan(@@regexp_close)
141
- #raise InvalidTrailerError, "No %%EOF token found"
142
- end
143
-
144
- Trailer.new(startxref, dictionary && dictionary.to_h)
145
- end
146
-
147
- def [](key)
148
- @dictionary[key] if has_dictionary?
149
- end
150
-
151
- def []=(key,val)
152
- @dictionary[key] = val
153
- end
83
+ class Trailer
84
+ include StandardObject
85
+
86
+ TOKENS = %w{ trailer %%EOF } #:nodoc:
87
+ XREF_TOKEN = "startxref" #:nodoc:
88
+
89
+ @@regexp_open = Regexp.new(WHITESPACES + TOKENS.first + WHITESPACES)
90
+ @@regexp_xref = Regexp.new(WHITESPACES + XREF_TOKEN + WHITESPACES + "(?<startxref>\\d+)")
91
+ @@regexp_close = Regexp.new(WHITESPACES + TOKENS.last + WHITESPACES)
92
+
93
+ attr_accessor :document
94
+ attr_accessor :startxref
95
+ attr_reader :dictionary
96
+
97
+ field :Size, :Type => Integer, :Required => true
98
+ field :Prev, :Type => Integer
99
+ field :Root, :Type => Catalog, :Required => true
100
+ field :Encrypt, :Type => Encryption::Standard::Dictionary
101
+ field :Info, :Type => Metadata
102
+ field :ID, :Type => Array.of(String, length: 2)
103
+ field :XRefStm, :Type => Integer
104
+
105
+ #
106
+ # Creates a new Trailer.
107
+ # _startxref_:: The file _offset_ to the XRef::Section.
108
+ # _dictionary_:: A hash of attributes to set in the Trailer Dictionary.
109
+ #
110
+ def initialize(startxref = 0, dictionary = {})
111
+ @startxref, self.dictionary = startxref, dictionary && Dictionary.new(dictionary)
112
+ end
154
113
 
155
- def dictionary=(dict)
156
- dict.parent = self if dict
157
- @dictionary = dict
158
- end
159
-
160
- def has_dictionary?
161
- not @dictionary.nil?
162
- end
163
-
164
- #
165
- # Outputs self into PDF code.
166
- #
167
- def to_s
168
-
169
- content = ""
170
- if self.has_dictionary?
171
- content << TOKENS.first << EOL << @dictionary.to_s << EOL
172
- end
173
-
174
- content << XREF_TOKEN << EOL << @startxref.to_s << EOL << TOKENS.last << EOL
175
-
176
- content
114
+ def self.parse(stream, parser = nil) #:nodoc:
115
+
116
+ if stream.skip(@@regexp_open)
117
+ dictionary = Dictionary.parse(stream, parser)
118
+ else
119
+ dictionary = nil
120
+ end
121
+
122
+ if not stream.scan(@@regexp_xref)
123
+ #raise InvalidTrailerError, "Cannot get startxref value"
124
+ end
125
+
126
+ startxref = stream['startxref'].to_i
127
+
128
+ if not stream.scan(@@regexp_close)
129
+ #raise InvalidTrailerError, "No %%EOF token found"
130
+ end
131
+
132
+ Trailer.new(startxref, dictionary)
133
+ end
134
+
135
+ def [](key)
136
+ @dictionary[key] if has_dictionary?
137
+ end
138
+
139
+ def []=(key,val)
140
+ @dictionary[key] = val
141
+ end
142
+
143
+ def dictionary=(dict)
144
+ dict.parent = self if dict
145
+ @dictionary = dict
146
+ end
147
+
148
+ def has_dictionary?
149
+ not @dictionary.nil?
150
+ end
151
+
152
+ #
153
+ # Outputs self into PDF code.
154
+ #
155
+ def to_s
156
+ content = ""
157
+ if self.has_dictionary?
158
+ content << TOKENS.first << EOL << @dictionary.to_s << EOL
159
+ end
160
+
161
+ content << XREF_TOKEN << EOL << @startxref.to_s << EOL << TOKENS.last << EOL
162
+
163
+ content
164
+ end
177
165
  end
178
-
179
- end
180
166
 
181
167
  end