origami 1.2.7 → 2.0.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 (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