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
@@ -1,46 +1,36 @@
1
1
  =begin
2
2
 
3
- = File
4
- about.rb
5
-
6
- = Info
7
- This file is part of PDF Walker, a graphical PDF file browser
8
- Copyright (C) 2010 Guillaume Delugré <guillaume AT security-labs DOT org>
9
- All right reserved.
10
-
11
- PDF Walker is free software: you can redistribute it and/or modify
12
- it under the terms of the GNU 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
- PDF Walker 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 General Public License for more details.
20
-
21
- You should have received a copy of the GNU General Public License
22
- along with PDF Walker. If not, see <http://www.gnu.org/licenses/>.
3
+ This file is part of PDF Walker, a graphical PDF file browser
4
+ Copyright (C) 2016 Guillaume Delugré.
5
+
6
+ PDF Walker is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU 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
+ PDF Walker 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 General Public License for more details.
15
+
16
+ You should have received a copy of the GNU General Public License
17
+ along with PDF Walker. If not, see <http://www.gnu.org/licenses/>.
23
18
 
24
19
  =end
25
20
 
26
21
  module PDFWalker
27
22
 
28
- class Walker < Window
29
-
30
- def about
31
-
32
- AboutDialog.show(self,
33
- {
34
- :name => "PDF Walker",
35
- :program_name => "PDF Walker",
36
- :version => Origami::VERSION,
37
- :copyright => "Copyright (C) 2010\nGuillaume Delugre <guillaume AT security-labs DOT org>\nAll right reserved.",
38
- :comments => "A graphical PDF parser front-end",
39
- :license => File.read("#{File.dirname(__FILE__)}/COPYING")
40
- })
41
-
23
+ class Walker < Window
24
+
25
+ def about
26
+ AboutDialog.show(self,
27
+ name: "PDF Walker",
28
+ program_name: "PDF Walker",
29
+ version: Origami::VERSION,
30
+ copyright: "Copyright (C) 2016\nGuillaume Delugré",
31
+ comments: "A graphical PDF parser front-end",
32
+ license: File.read(File.join(__dir__, "COPYING"))
33
+ )
34
+ end
42
35
  end
43
-
44
- end
45
-
46
36
  end
@@ -1,128 +1,119 @@
1
1
  =begin
2
2
 
3
- = File
4
- config.rb
5
-
6
- = Info
7
- This file is part of PDF Walker, a graphical PDF file browser
8
- Copyright (C) 2010 Guillaume Delugré <guillaume AT security-labs DOT org>
9
- All right reserved.
10
-
11
- PDF Walker is free software: you can redistribute it and/or modify
12
- it under the terms of the GNU 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
- PDF Walker 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 General Public License for more details.
20
-
21
- You should have received a copy of the GNU General Public License
22
- along with PDF Walker. If not, see <http://www.gnu.org/licenses/>.
3
+ This file is part of PDF Walker, a graphical PDF file browser
4
+ Copyright (C) 2016 Guillaume Delugré.
5
+
6
+ PDF Walker is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU 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
+ PDF Walker 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 General Public License for more details.
15
+
16
+ You should have received a copy of the GNU General Public License
17
+ along with PDF Walker. If not, see <http://www.gnu.org/licenses/>.
23
18
 
24
19
  =end
25
20
 
21
+ require 'origami'
26
22
  require 'yaml'
27
23
 
28
24
  module PDFWalker
29
-
30
- class Walker < Window
31
-
32
- class Config
33
-
34
- DEFAULT_CONFIG_FILE = "#{File.expand_path("~")}/.pdfwalker.conf.yml"
35
- DEFAULT_CONFIG =
36
- {
37
- "Debug" =>
38
- {
39
- "Profiling" => false,
40
- "ProfilingOutputDir" => "prof",
41
- "Verbosity" => Parser::VERBOSE_INSANE,
42
- "IgnoreFileHeader" => true
43
- },
44
-
45
- "UI" =>
46
- {
47
- "LastOpenedDocuments" => []
48
- }
49
- }
50
-
51
- NLOG_RECENT_FILES = 5
52
-
53
- def initialize(configfile = DEFAULT_CONFIG_FILE)
54
-
55
- begin
56
- @conf = YAML.load(File.open(configfile)) or DEFAULT_CONFIG
57
- rescue Exception
58
- ensure
59
- @filename = configfile
60
- set_missing_values
61
- end
62
-
63
- end
64
-
65
- def last_opened_file(filepath)
66
- @conf["UI"]['LastOpenedDocuments'].push(filepath).uniq!
67
- @conf["UI"]['LastOpenedDocuments'].delete_at(0) while @conf["UI"]['LastOpenedDocuments'].size > NLOG_RECENT_FILES
68
-
69
- save
70
- end
71
-
72
- def recent_files(n = NLOG_RECENT_FILES)
73
- @conf["UI"]['LastOpenedDocuments'].last(n).reverse
74
- end
75
-
76
- def set_profiling(bool)
77
- @conf["Debug"]['Profiling'] = bool
78
- save
79
- end
80
-
81
- def profile?
82
- @conf["Debug"]['Profiling']
83
- end
84
-
85
- def profile_output_dir
86
- @conf["Debug"]['ProfilingOutputDir']
87
- end
88
-
89
- def set_ignore_header(bool)
90
- @conf["Debug"]['IgnoreFileHeader'] = bool
91
- save
92
- end
93
-
94
- def ignore_header?
95
- @conf["Debug"]['IgnoreFileHeader']
96
- end
97
-
98
- def set_verbosity(level)
99
- @conf["Debug"]['Verbosity'] = level
100
- save
101
- end
102
-
103
- def verbosity
104
- @conf["Debug"]['Verbosity']
105
- end
106
-
107
- def save
108
- File.open(@filename, "w").write(@conf.to_yaml)
109
- end
110
-
111
- private
112
-
113
- def set_missing_values
114
- @conf ||= {}
115
-
116
- DEFAULT_CONFIG.each_key do |cat|
117
- @conf[cat] = {} unless @conf.include?(cat)
118
-
119
- DEFAULT_CONFIG[cat].each_pair do |key, value|
120
- @conf[cat][key] = value unless @conf[cat].include?(key)
121
- end
25
+
26
+ class Walker < Window
27
+
28
+ class Config
29
+ DEFAULT_CONFIG_FILE = "#{File.expand_path("~")}/.pdfwalker.conf.yml"
30
+ DEFAULT_CONFIG =
31
+ {
32
+ "Debug" =>
33
+ {
34
+ "Profiling" => false,
35
+ "ProfilingOutputDir" => "prof",
36
+ "Verbosity" => Origami::Parser::VERBOSE_TRACE,
37
+ "IgnoreFileHeader" => true
38
+ },
39
+
40
+ "UI" =>
41
+ {
42
+ "LastOpenedDocuments" => []
43
+ }
44
+ }
45
+ NLOG_RECENT_FILES = 5
46
+
47
+ def initialize(configfile = DEFAULT_CONFIG_FILE)
48
+ begin
49
+ @conf = YAML.load(File.open(configfile)) or DEFAULT_CONFIG
50
+ rescue
51
+ ensure
52
+ @filename = configfile
53
+ set_missing_values
54
+ end
55
+ end
56
+
57
+ def last_opened_file(filepath)
58
+ @conf["UI"]['LastOpenedDocuments'].push(filepath).uniq!
59
+ @conf["UI"]['LastOpenedDocuments'].delete_at(0) while @conf["UI"]['LastOpenedDocuments'].size > NLOG_RECENT_FILES
60
+
61
+ save
62
+ end
63
+
64
+ def recent_files(n = NLOG_RECENT_FILES)
65
+ @conf["UI"]['LastOpenedDocuments'].last(n).reverse
66
+ end
67
+
68
+ def set_profiling(bool)
69
+ @conf["Debug"]['Profiling'] = bool
70
+ save
71
+ end
72
+
73
+ def profile?
74
+ @conf["Debug"]['Profiling']
75
+ end
76
+
77
+ def profile_output_dir
78
+ @conf["Debug"]['ProfilingOutputDir']
79
+ end
80
+
81
+ def set_ignore_header(bool)
82
+ @conf["Debug"]['IgnoreFileHeader'] = bool
83
+ save
84
+ end
85
+
86
+ def ignore_header?
87
+ @conf["Debug"]['IgnoreFileHeader']
88
+ end
89
+
90
+ def set_verbosity(level)
91
+ @conf["Debug"]['Verbosity'] = level
92
+ save
93
+ end
94
+
95
+ def verbosity
96
+ @conf["Debug"]['Verbosity']
97
+ end
98
+
99
+ def save
100
+ File.open(@filename, "w").write(@conf.to_yaml)
101
+ end
102
+
103
+ private
104
+
105
+ def set_missing_values
106
+ @conf ||= {}
107
+
108
+ DEFAULT_CONFIG.each_key do |cat|
109
+ @conf[cat] = {} unless @conf.include?(cat)
110
+
111
+ DEFAULT_CONFIG[cat].each_pair do |key, value|
112
+ @conf[cat][key] = value unless @conf[cat].include?(key)
113
+ end
114
+ end
115
+ end
122
116
  end
123
- end
117
+
124
118
  end
125
-
126
- end
127
-
128
119
  end
@@ -1,385 +1,436 @@
1
1
  =begin
2
2
 
3
- = File
4
- file.rb
5
-
6
- = Info
7
- This file is part of PDF Walker, a graphical PDF file browser
8
- Copyright (C) 2010 Guillaume Delugr� <guillaume AT security-labs DOT org>
9
- All right reserved.
10
-
11
- PDF Walker is free software: you can redistribute it and/or modify
12
- it under the terms of the GNU 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
- PDF Walker 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 General Public License for more details.
20
-
21
- You should have received a copy of the GNU General Public License
22
- along with PDF Walker. If not, see <http://www.gnu.org/licenses/>.
3
+ This file is part of PDF Walker, a graphical PDF file browser
4
+ Copyright (C) 2016 Guillaume Delugré.
5
+
6
+ PDF Walker is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU 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
+ PDF Walker 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 General Public License for more details.
15
+
16
+ You should have received a copy of the GNU General Public License
17
+ along with PDF Walker. If not, see <http://www.gnu.org/licenses/>.
23
18
 
24
19
  =end
25
20
 
26
21
  require 'origami'
27
- include Origami
28
22
 
29
23
  module PDFWalker
30
24
 
31
- class Walker < Window
32
-
33
- attr_reader :opened
34
- attr_reader :explore_history
35
-
36
- def close
37
-
38
- @opened = nil
39
- @filename = ''
40
- @explorer_history.clear
41
-
42
- @treeview.clear
43
- @objectview.clear
44
- @hexview.clear
45
-
46
- [
47
- @file_menu_close, @file_menu_saveas, @file_menu_serialize, @file_menu_refresh,
48
- @document_menu_search,
49
- @document_menu_gotocatalog, @document_menu_gotopage, @document_menu_gotorev, @document_menu_gotoobj,
50
- @document_menu_properties, @document_menu_sign, @document_menu_ur
51
- ].each do |menu|
52
- menu.sensitive = false
53
- end
54
-
55
- @statusbar.pop(@main_context)
56
-
57
- GC.start
58
- end
59
-
60
- def open(filename = nil)
61
-
62
- dialog = Gtk::FileChooserDialog.new("Open PDF File",
63
- self,
64
- FileChooser::ACTION_OPEN,
65
- nil,
66
- [Stock::CANCEL, Dialog::RESPONSE_CANCEL],
67
- [Stock::OPEN, Dialog::RESPONSE_ACCEPT])
68
-
69
- last_file = @config.recent_files.first
70
- unless last_file.nil?
71
- last_folder = last_file[0..last_file.size - File.basename(last_file).size - 1]
72
- dialog.set_current_folder(last_folder) if File.directory?(last_folder)
73
- end
74
-
75
- dialog.filter = FileFilter.new.add_pattern("*.acrodata").add_pattern("*.pdf").add_pattern("*.fdf")
76
-
77
- if filename or dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
78
-
79
- create_progressbar
80
-
81
- filename ||= dialog.filename
82
- dialog.destroy
83
-
84
- begin
85
-
86
- if @help_menu_profile.active?
87
- require 'ruby-prof'
88
- RubyProf.start
89
- end
90
-
91
- target = parsefile(filename)
92
-
93
- if @help_menu_profile.active?
94
- result = RubyProf.stop
95
- txtprinter = RubyProf::FlatPrinter.new(result)
96
- htmlprinter = RubyProf::GraphHtmlPrinter.new(result)
97
- txtprinter.print(File.new("#{@config.profile_output_dir}/#{File.basename(filename)}.log", "w"))
98
- htmlprinter.print(File.new("#{@config.profile_output_dir}/#{File.basename(filename)}.log.html", "w"))
99
- end
100
-
101
- if target
102
- close if @opened
103
- @opened = target
104
- @filename = filename
105
-
106
- @config.last_opened_file(filename)
107
- @config.save
108
- update_recent_menu
109
-
110
- @last_search_result = []
111
- @last_search =
112
- {
113
- :expr => "",
114
- :regexp => false,
115
- :type => :body
116
- }
25
+ class Walker < Window
26
+ attr_reader :opened
27
+ attr_reader :explore_history
117
28
 
118
- self.reload
119
-
120
- [
121
- @file_menu_close, @file_menu_saveas, @file_menu_serialize, @file_menu_refresh,
122
- @document_menu_search,
123
- @document_menu_gotocatalog, @document_menu_gotopage, @document_menu_gotorev, @document_menu_gotoobj,
124
- @document_menu_properties, @document_menu_sign, @document_menu_ur
125
- ].each do |menu|
126
- menu.sensitive = true
127
- end
128
-
29
+ def close
30
+ @opened = nil
31
+ @filename = ''
129
32
  @explorer_history.clear
130
-
131
- @statusbar.push(@main_context, "Viewing #{filename}")
132
-
133
- if @opened.is_a?(PDF)
134
- pagemenu = Menu.new
135
- @document_menu_gotopage.remove_submenu
136
- page_index = 1
137
- @opened.pages.each do |page|
138
- pagemenu.append(item = MenuItem.new(page_index.to_s).show)
139
- item.signal_connect("activate") do @treeview.goto(page) end
140
- page_index = page_index + 1
141
- end
142
- @document_menu_gotopage.set_submenu(pagemenu)
143
-
144
- revmenu = Menu.new
145
- @document_menu_gotorev.remove_submenu
146
- rev_index = 1
147
- @opened.revisions.each do |rev|
148
- revmenu.append(item = MenuItem.new(rev_index.to_s).show)
149
- item.signal_connect("activate") do @treeview.goto(rev) end
150
- rev_index = rev_index + 1
151
- end
152
- @document_menu_gotorev.set_submenu(revmenu)
153
-
154
- goto_catalog
33
+
34
+ @treeview.clear
35
+ @objectview.clear
36
+ @hexview.clear
37
+
38
+ # disable all menus.
39
+ [
40
+ @file_menu_close, @file_menu_saveas, @file_menu_serialize, @file_menu_refresh,
41
+ @document_menu_search,
42
+ @document_menu_gotocatalog, @document_menu_gotodocinfo, @document_menu_gotometadata,
43
+ @document_menu_gotopage, @document_menu_gotofield, @document_menu_gotorev, @document_menu_gotoobj,
44
+ @document_menu_properties, @document_menu_sign, @document_menu_ur
45
+ ].each do |menu|
46
+ menu.sensitive = false
155
47
  end
156
- end
157
-
158
- rescue Exception => e
159
- error("Error while parsing file.\n#{e} (#{e.class})\n" + e.backtrace.join("\n"))
48
+
49
+ @statusbar.pop(@main_context)
50
+
51
+ GC.start
160
52
  end
161
-
162
- close_progressbar
163
- self.activate_focus
164
-
165
- else
166
- dialog.destroy
167
- end
168
-
169
- end
170
-
171
- def deserialize
172
-
173
- dialog = Gtk::FileChooserDialog.new("Open dump file",
174
- self,
175
- FileChooser::ACTION_OPEN,
176
- nil,
177
- [Stock::CANCEL, Dialog::RESPONSE_CANCEL],
178
- [Stock::OPEN, Dialog::RESPONSE_ACCEPT])
179
-
180
- dialog.current_folder = "#{Dir.pwd}/dumps"
181
- dialog.filter = FileFilter.new.add_pattern("*.gz")
182
-
183
- if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
184
-
185
- if @opened then close end
186
- filename = dialog.filename
187
-
188
- begin
189
-
190
- @opened = PDF.deserialize(filename)
191
-
192
- self.reload
193
-
194
- [ @file_menu_close, @file_menu_saveas, @file_menu_serialize, @file_menu_refresh,
195
- @document_menu_properties, @document_menu_sign, @document_menu_ur ].each do |menu|
196
- menu.sensitive = true
197
- end
198
-
199
- @explorer_history.clear
200
-
201
- @statusbar.push(@main_context, "Viewing dump of #{filename}")
202
-
203
- rescue Exception => e
204
- error("This file cannot be loaded.\n#{e} (#{e.class})")
53
+
54
+ def open(filename = nil)
55
+ dialog = Gtk::FileChooserDialog.new("Open PDF File",
56
+ self,
57
+ FileChooser::ACTION_OPEN,
58
+ nil,
59
+ [Stock::CANCEL, Dialog::RESPONSE_CANCEL],
60
+ [Stock::OPEN, Dialog::RESPONSE_ACCEPT])
61
+
62
+ last_file = @config.recent_files.first
63
+ unless last_file.nil?
64
+ last_folder = File.dirname(last_file)
65
+ dialog.set_current_folder(last_folder) if File.directory?(last_folder)
66
+ end
67
+
68
+ dialog.filter = FileFilter.new.add_pattern("*.acrodata").add_pattern("*.pdf").add_pattern("*.fdf")
69
+
70
+ if filename.nil? and dialog.run != Gtk::Dialog::RESPONSE_ACCEPT
71
+ dialog.destroy
72
+ return
73
+ end
74
+
75
+ create_progressbar
76
+
77
+ filename ||= dialog.filename
78
+ dialog.destroy
79
+
80
+ begin
81
+ if @help_menu_profile.active?
82
+ require 'ruby-prof'
83
+ RubyProf.start
84
+ end
85
+
86
+ target = parse_file(filename)
87
+
88
+ if @help_menu_profile.active?
89
+ result = RubyProf.stop
90
+ multiprinter = RubyProf::MultiPrinter.new(result)
91
+
92
+ Dir.mkdir(@config.profile_output_dir) unless Dir.exists?(@config.profile_output_dir)
93
+
94
+ multiprinter.print(path: @config.profile_output_dir, profile: File.basename(filename))
95
+ end
96
+
97
+ if target
98
+ close if @opened
99
+ @opened = target
100
+ @filename = filename
101
+
102
+ @config.last_opened_file(filename)
103
+ @config.save
104
+ update_recent_menu
105
+
106
+ @last_search_result = []
107
+ @last_search =
108
+ {
109
+ :expr => "",
110
+ :regexp => false,
111
+ :type => :body
112
+ }
113
+
114
+ self.reload
115
+
116
+ # Enable basic file menus.
117
+ [
118
+ @file_menu_close, @file_menu_refresh,
119
+ ].each do |menu|
120
+ menu.sensitive = true
121
+ end
122
+
123
+ @explorer_history.clear
124
+
125
+ @statusbar.push(@main_context, "Viewing #{filename}")
126
+
127
+ if @opened.is_a?(Origami::PDF)
128
+
129
+ # Enable save and document menu.
130
+ [
131
+ @file_menu_saveas, @file_menu_serialize,
132
+ @document_menu_search,
133
+ @document_menu_gotocatalog, @document_menu_gotopage, @document_menu_gotorev, @document_menu_gotoobj,
134
+ @document_menu_properties, @document_menu_sign, @document_menu_ur
135
+ ].each do |menu|
136
+ menu.sensitive = true
137
+ end
138
+
139
+ @document_menu_gotodocinfo.sensitive = true if @opened.document_info?
140
+ @document_menu_gotometadata.sensitive = true if @opened.metadata?
141
+ @document_menu_gotofield.sensitive = true if @opened.form?
142
+
143
+ page_menu = Menu.new
144
+ @document_menu_gotopage.remove_submenu
145
+ @opened.each_page.with_index(1) do |page, index|
146
+ page_menu.append(item = MenuItem.new(index.to_s).show)
147
+ item.signal_connect("activate") do @treeview.goto(page) end
148
+ end
149
+ @document_menu_gotopage.set_submenu(page_menu)
150
+
151
+ field_menu = Menu.new
152
+ @document_menu_gotofield.remove_submenu
153
+ @opened.each_field do |field|
154
+ field_name = field.T || "<unnamed field>"
155
+ field_menu.append(item = MenuItem.new(field_name).show)
156
+ item.signal_connect("activate") do @treeview.goto(field) end
157
+ end
158
+ @document_menu_gotofield.set_submenu(field_menu)
159
+
160
+ rev_menu = Menu.new
161
+ @document_menu_gotorev.remove_submenu
162
+ rev_index = 1
163
+ @opened.revisions.each do |rev|
164
+ rev_menu.append(item = MenuItem.new(rev_index.to_s).show)
165
+ item.signal_connect("activate") do @treeview.goto(rev) end
166
+ rev_index = rev_index + 1
167
+ end
168
+ @document_menu_gotorev.set_submenu(rev_menu)
169
+
170
+ goto_catalog
171
+ end
172
+ end
173
+
174
+ rescue
175
+ error("Error while parsing file.\n#{$!} (#{$!.class})\n" + $!.backtrace.join("\n"))
176
+ end
177
+
178
+ close_progressbar
179
+ self.activate_focus
205
180
  end
206
181
 
207
- end
208
-
209
- dialog.destroy
210
-
211
- end
212
-
213
- def serialize
214
-
215
- dialog = Gtk::FileChooserDialog.new("Save dump file",
216
- self,
217
- Gtk::FileChooser::ACTION_SAVE,
218
- nil,
219
- [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
220
- [Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT]
221
- )
222
-
223
- dialog.do_overwrite_confirmation = true
224
- dialog.current_folder = "#{Dir.pwd}/dumps"
225
- dialog.current_name = "#{File.basename(@filename)}.dmp.gz"
226
- dialog.filter = FileFilter.new.add_pattern("*.gz")
227
-
228
- if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
229
- @opened.serialize(dialog.filename)
230
- end
231
-
232
- dialog.destroy
233
- end
234
-
235
- def save_data(caption, data, filename = "")
236
-
237
- dialog = Gtk::FileChooserDialog.new(caption,
238
- self,
239
- Gtk::FileChooser::ACTION_SAVE,
240
- nil,
241
- [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
242
- [Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT]
243
- )
244
-
245
- dialog.do_overwrite_confirmation = true
246
- dialog.current_name = File.basename(filename)
247
- dialog.filter = FileFilter.new.add_pattern("*.*")
248
-
249
- if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
250
- fd = File.open(dialog.filename, "w").binmode
251
- fd << data
252
- fd.close
253
- end
254
-
255
- dialog.destroy
256
- end
257
-
258
- def save
259
-
260
- dialog = Gtk::FileChooserDialog.new("Save PDF file",
261
- self,
262
- Gtk::FileChooser::ACTION_SAVE,
263
- nil,
264
- [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
265
- [Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT]
266
- )
267
-
268
- dialog.filter = FileFilter.new.add_pattern("*.acrodata").add_pattern("*.pdf").add_pattern("*.fdf")
269
-
270
- folder = @filename[0..@filename.size - File.basename(@filename).size - 1]
271
- dialog.set_current_folder(folder)
272
-
273
- if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
274
- @opened.save(dialog.filename)
275
- end
276
-
277
- dialog.destroy
278
- end
182
+ def deserialize
183
+ dialog = Gtk::FileChooserDialog.new("Open dump file",
184
+ self,
185
+ FileChooser::ACTION_OPEN,
186
+ nil,
187
+ [Stock::CANCEL, Dialog::RESPONSE_CANCEL],
188
+ [Stock::OPEN, Dialog::RESPONSE_ACCEPT]
189
+ )
279
190
 
280
- def save_dot
281
-
282
- dialog = Gtk::FileChooserDialog.new("Save dot file",
283
- self,
284
- Gtk::FileChooser::ACTION_SAVE,
285
- nil,
286
- [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
287
- [Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT]
288
- )
289
-
290
- dialog.filter = FileFilter.new.add_pattern("*.dot")
291
-
292
- folder = @filename[0..@filename.size - File.basename(@filename).size - 1]
293
- dialog.set_current_folder(folder)
294
-
295
- if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
296
- @opened.export_to_graph(dialog.filename)
297
- end
298
-
299
- dialog.destroy
300
- end
301
-
302
- def save_graphml
303
-
304
- dialog = Gtk::FileChooserDialog.new("Save GraphML file",
305
- self,
306
- Gtk::FileChooser::ACTION_SAVE,
307
- nil,
308
- [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
309
- [Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT]
310
- )
311
-
312
- dialog.filter = FileFilter.new.add_pattern("*.graphml")
313
-
314
- folder = @filename[0..@filename.size - File.basename(@filename).size - 1]
315
- dialog.set_current_folder(folder)
316
-
317
- if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
318
- @opened.export_to_graphml(dialog.filename)
319
- end
320
-
321
- dialog.destroy
322
- end
191
+ dialog.current_folder = File.join(Dir.pwd, "dumps")
192
+ dialog.filter = FileFilter.new.add_pattern("*.gz")
193
+
194
+ if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
195
+ close if @opened
196
+ filename = dialog.filename
197
+
198
+ begin
199
+ @opened = Origami::PDF.deserialize(filename)
200
+ self.reload
201
+
202
+ [
203
+ @file_menu_close, @file_menu_saveas, @file_menu_serialize, @file_menu_refresh,
204
+ @document_menu_search,
205
+ @document_menu_gotocatalog, @document_menu_gotopage, @document_menu_gotorev, @document_menu_gotoobj,
206
+ @document_menu_properties, @document_menu_sign, @document_menu_ur
207
+ ].each do |menu|
208
+ menu.sensitive = true
209
+ end
210
+
211
+ @document_menu_gotodocinfo.sensitive = true if @opened.document_info?
212
+ @document_menu_gotometadata.sensitive = true if @opened.metadata?
213
+ @document_menu_gotofield.sensitive = true if @opened.form?
323
214
 
324
- private
325
-
326
- def parsefile(filename)
327
- update_bar = lambda { |obj|
328
- if @progressbar then @progressbar.pulse end
329
- while (Gtk.events_pending?) do Gtk.main_iteration end
330
- }
331
-
332
- prompt_passwd = lambda {
333
- passwd = ""
334
-
335
- dialog = Gtk::Dialog.new(
336
- "This document is encrypted",
337
- nil,
338
- Gtk::Dialog::MODAL,
339
- [ Gtk::Stock::OK, Gtk::Dialog::RESPONSE_OK ],
340
- [ Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL ]
341
- )
342
-
343
- dialog.set_default_response(Gtk::Dialog::RESPONSE_OK)
344
-
345
- label = Gtk::Label.new("Please enter password:")
346
- entry = Gtk::Entry.new
347
- entry.signal_connect('activate') {
348
- dialog.response(Gtk::Dialog::RESPONSE_OK)
349
- }
350
-
351
- dialog.vbox.add(label)
352
- dialog.vbox.add(entry)
353
- dialog.show_all
354
-
355
- dialog.run do |response|
356
- if response == Gtk::Dialog::RESPONSE_OK
357
- passwd = entry.text
358
- end
215
+ @explorer_history.clear
216
+
217
+ @statusbar.push(@main_context, "Viewing dump of #{filename}")
218
+ rescue
219
+ error("This file cannot be loaded.\n#{$!} (#{$!.class})")
220
+ end
221
+ end
222
+
223
+ dialog.destroy
359
224
  end
360
-
361
- dialog.destroy
362
-
363
- return passwd
364
- }
365
-
366
- PDF.read(filename,
367
- :verbosity => Parser::VERBOSE_INSANE,
368
- :ignoreerrors => false,
369
- :callback => update_bar,
370
- :prompt_password => prompt_passwd
371
- )
372
- end
373
-
374
- def create_progressbar
375
- @progresswin = Dialog.new("Parsing file...", self, Dialog::MODAL)
376
- @progresswin.vbox.add(@progressbar = ProgressBar.new.set_pulse_step(0.05))
377
- @progresswin.show_all
378
- end
379
-
380
- def close_progressbar
381
- @progresswin.close
382
- end
383
- end
384
225
 
226
+ def serialize
227
+ dialog = Gtk::FileChooserDialog.new("Save dump file",
228
+ self,
229
+ Gtk::FileChooser::ACTION_SAVE,
230
+ nil,
231
+ [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
232
+ [Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT]
233
+ )
234
+
235
+ dialog.do_overwrite_confirmation = true
236
+ dialog.current_folder = File.join(Dir.pwd, "dumps")
237
+ dialog.current_name = "#{File.basename(@filename)}.dmp.gz"
238
+ dialog.filter = FileFilter.new.add_pattern("*.gz")
239
+
240
+ if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
241
+ begin
242
+ @opened.serialize(dialog.filename)
243
+ rescue
244
+ error("Error: #{$!.message}")
245
+ end
246
+ end
247
+
248
+ dialog.destroy
249
+ end
250
+
251
+ def save_data(caption, data, filename = "")
252
+ dialog = Gtk::FileChooserDialog.new(caption,
253
+ self,
254
+ Gtk::FileChooser::ACTION_SAVE,
255
+ nil,
256
+ [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
257
+ [Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT]
258
+ )
259
+
260
+ dialog.do_overwrite_confirmation = true
261
+ dialog.current_name = File.basename(filename)
262
+ dialog.filter = FileFilter.new.add_pattern("*.*")
263
+
264
+ if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
265
+ begin
266
+ File.binwrite(dialog.filename, data)
267
+ rescue
268
+ error("Error: #{$!.message}")
269
+ end
270
+ end
271
+
272
+ dialog.destroy
273
+ end
274
+
275
+ def save
276
+ dialog = Gtk::FileChooserDialog.new("Save PDF file",
277
+ self,
278
+ Gtk::FileChooser::ACTION_SAVE,
279
+ nil,
280
+ [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
281
+ [Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT]
282
+ )
283
+
284
+ dialog.filter = FileFilter.new.add_pattern("*.acrodata").add_pattern("*.pdf").add_pattern("*.fdf")
285
+
286
+ folder = File.dirname(@filename)
287
+ dialog.set_current_folder(folder)
288
+
289
+ if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
290
+ begin
291
+ @opened.save(dialog.filename)
292
+ rescue
293
+ error("#{$!.class}: #{$!.message}\n#{$!.backtrace.join($/)}")
294
+ end
295
+ end
296
+
297
+ dialog.destroy
298
+ end
299
+
300
+ def save_dot
301
+ dialog = Gtk::FileChooserDialog.new("Save dot file",
302
+ self,
303
+ Gtk::FileChooser::ACTION_SAVE,
304
+ nil,
305
+ [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
306
+ [Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT]
307
+ )
308
+
309
+ dialog.filter = FileFilter.new.add_pattern("*.dot")
310
+
311
+ folder = File.dirname(@filename)
312
+ dialog.set_current_folder(folder)
313
+
314
+ if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
315
+ begin
316
+ @opened.export_to_graph(dialog.filename)
317
+ rescue
318
+ error("Error: #{$!.message}")
319
+ end
320
+ end
321
+
322
+ dialog.destroy
323
+ end
324
+
325
+ def save_graphml
326
+ dialog = Gtk::FileChooserDialog.new("Save GraphML file",
327
+ self,
328
+ Gtk::FileChooser::ACTION_SAVE,
329
+ nil,
330
+ [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
331
+ [Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT]
332
+ )
333
+
334
+ dialog.filter = FileFilter.new.add_pattern("*.graphml")
335
+
336
+ folder = File.dirname(@filename)
337
+ dialog.set_current_folder(folder)
338
+
339
+ if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
340
+ begin
341
+ @opened.export_to_graphml(dialog.filename)
342
+ rescue
343
+ error("Error: #{$!.message}")
344
+ end
345
+ end
346
+
347
+ dialog.destroy
348
+ end
349
+
350
+ private
351
+
352
+ def parse_file(path)
353
+ update_bar = lambda { |obj|
354
+ @progressbar.pulse if @progressbar
355
+ Gtk.main_iteration while Gtk.events_pending?
356
+ }
357
+
358
+ prompt_passwd = lambda {
359
+ passwd = ""
360
+
361
+ dialog = Gtk::Dialog.new(
362
+ "This document is encrypted",
363
+ nil,
364
+ Gtk::Dialog::MODAL,
365
+ [ Gtk::Stock::OK, Gtk::Dialog::RESPONSE_OK ],
366
+ [ Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL ]
367
+ )
368
+
369
+ dialog.set_default_response(Gtk::Dialog::RESPONSE_OK)
370
+
371
+ label = Gtk::Label.new("Please enter password:")
372
+ entry = Gtk::Entry.new
373
+ entry.signal_connect('activate') {
374
+ dialog.response(Gtk::Dialog::RESPONSE_OK)
375
+ }
376
+
377
+ dialog.vbox.add(label)
378
+ dialog.vbox.add(entry)
379
+ dialog.show_all
380
+
381
+ dialog.run do |response|
382
+ if response == Gtk::Dialog::RESPONSE_OK
383
+ passwd = entry.text
384
+ end
385
+ end
386
+
387
+ dialog.destroy
388
+ return passwd
389
+ }
390
+
391
+ #
392
+ # Try to detect the file type of the document.
393
+ # Fallback to PDF if none is found.
394
+ #
395
+ file_type = detect_file_type(path)
396
+ if file_type.nil?
397
+ file_type = Origami::PDF
398
+ force_mode = true
399
+ else
400
+ force_mode = false
401
+ end
402
+
403
+ file_type.read(path,
404
+ verbosity: Origami::Parser::VERBOSE_TRACE,
405
+ ignore_errors: false,
406
+ callback: update_bar,
407
+ prompt_password: prompt_passwd,
408
+ force: force_mode
409
+ )
410
+ end
411
+
412
+ def detect_file_type(path)
413
+ supported_types = [ Origami::PDF, Origami::FDF, Origami::PPKLite ]
414
+
415
+ File.open(path, 'rb') do |file|
416
+ data = file.read(128)
417
+
418
+ supported_types.each do |type|
419
+ return type if data.match(type::Header::MAGIC)
420
+ end
421
+ end
422
+
423
+ nil
424
+ end
425
+
426
+ def create_progressbar
427
+ @progresswin = Dialog.new("Parsing file...", self, Dialog::MODAL)
428
+ @progresswin.vbox.add(@progressbar = ProgressBar.new.set_pulse_step(0.05))
429
+ @progresswin.show_all
430
+ end
431
+
432
+ def close_progressbar
433
+ @progresswin.close
434
+ end
435
+ end
385
436
  end