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
@@ -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