pdfwalker 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,23 @@
1
+ =begin
2
+
3
+ This file is part of PDF Walker, a graphical PDF file browser
4
+ Copyright (C) 2017 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/>.
18
+
19
+ =end
20
+
21
+ module PDFWalker
22
+ VERSION = "1.0.0"
23
+ end
@@ -0,0 +1,300 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ =begin
4
+
5
+ This file is part of PDF Walker, a graphical PDF file browser
6
+ Copyright (C) 2017 Guillaume Delugré.
7
+
8
+ PDF Walker is free software: you can redistribute it and/or modify
9
+ it under the terms of the GNU General Public License as published by
10
+ the Free Software Foundation, either version 3 of the License, or
11
+ (at your option) any later version.
12
+
13
+ PDF Walker is distributed in the hope that it will be useful,
14
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ GNU General Public License for more details.
17
+
18
+ You should have received a copy of the GNU General Public License
19
+ along with PDF Walker. If not, see <http://www.gnu.org/licenses/>.
20
+
21
+ =end
22
+
23
+ require 'origami'
24
+ require 'gtk2'
25
+
26
+ include Gtk
27
+
28
+ require_relative 'menu'
29
+ require_relative 'version'
30
+ require_relative 'about'
31
+ require_relative 'file'
32
+ require_relative 'hexview'
33
+ require_relative 'treeview'
34
+ require_relative 'textview'
35
+ require_relative 'imgview'
36
+ require_relative 'config'
37
+ require_relative 'properties'
38
+ require_relative 'xrefs'
39
+ require_relative 'signing'
40
+
41
+ module PDFWalker #:nodoc:all
42
+
43
+ class Walker < Window
44
+ attr_reader :treeview, :hexview, :objectview
45
+ attr_reader :explorer_history
46
+ attr_reader :config
47
+ attr_reader :filename
48
+
49
+ def self.start(file = nil)
50
+ Gtk.init
51
+ Walker.new(file)
52
+ Gtk.main
53
+ end
54
+
55
+ def initialize(target_file = nil)
56
+ super("PDF Walker")
57
+
58
+ @config = Walker::Config.new
59
+ @opened = nil
60
+
61
+ @last_search_result = []
62
+ @last_search =
63
+ {
64
+ :expr => "",
65
+ :regexp => false,
66
+ :type => :body
67
+ }
68
+
69
+ @explorer_history = []
70
+
71
+ init_interface
72
+
73
+ open(target_file)
74
+ end
75
+
76
+ def error(msg)
77
+ dialog = Gtk::MessageDialog.new(self,
78
+ Gtk::Dialog::DESTROY_WITH_PARENT,
79
+ Gtk::MessageDialog::ERROR,
80
+ Gtk::MessageDialog::BUTTONS_CLOSE,
81
+ msg
82
+ )
83
+
84
+ dialog.run
85
+ dialog.destroy
86
+ end
87
+
88
+ def reload
89
+ @treeview.load(@opened) if @opened
90
+ end
91
+
92
+ def search
93
+ dialog = Gtk::Dialog.new("Search...",
94
+ self,
95
+ Gtk::Dialog::MODAL | Gtk::Dialog::DESTROY_WITH_PARENT,
96
+ [Gtk::Stock::FIND, Gtk::Dialog::RESPONSE_OK],
97
+ [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL]
98
+ )
99
+
100
+ entry = Gtk::Entry.new
101
+ entry.signal_connect('activate') { dialog.response(Gtk::Dialog::RESPONSE_OK) }
102
+ entry.text = @last_search[:expr]
103
+
104
+ button_bydata = Gtk::RadioButton.new("In object body")
105
+ button_byname = Gtk::RadioButton.new(button_bydata, "In object name")
106
+ button_regexp = Gtk::CheckButton.new("Regular expression")
107
+
108
+ button_bydata.set_active(true) if @last_search[:type] == :body
109
+ button_byname.set_active(true) if @last_search[:type] == :name
110
+ button_regexp.set_active(@last_search[:regexp])
111
+
112
+ hbox = Gtk::HBox.new
113
+ hbox.pack_start Gtk::Label.new("Search for expression ")
114
+ hbox.pack_start entry
115
+
116
+ dialog.vbox.pack_start(hbox)
117
+ dialog.vbox.pack_start(button_bydata)
118
+ dialog.vbox.pack_start(button_byname)
119
+ dialog.vbox.pack_end(button_regexp)
120
+
121
+ dialog.signal_connect('response') do |_, response|
122
+ if response != Gtk::Dialog::RESPONSE_OK
123
+ dialog.destroy
124
+ next
125
+ end
126
+
127
+ search_document(entry.text, regexp: button_regexp.active?, type: (button_bydata.active? ? 'body' : 'name'))
128
+ end
129
+
130
+ dialog.show_all
131
+ end
132
+
133
+ def goto_catalog
134
+ @treeview.goto(@opened.Catalog.reference)
135
+ end
136
+
137
+ def goto_docinfo
138
+ @treeview.goto(@opened.document_info.reference) if @opened.document_info?
139
+ end
140
+
141
+ def goto_metadata
142
+ @treeview.goto(@opened.Catalog.Metadata.reference) if @opened.metadata?
143
+ end
144
+
145
+ def goto_object
146
+ dialog = Gtk::Dialog.new("Jump to object...",
147
+ self,
148
+ Gtk::Dialog::MODAL | Gtk::Dialog::DESTROY_WITH_PARENT,
149
+ [Gtk::Stock::OK, Gtk::Dialog::RESPONSE_OK],
150
+ [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL]
151
+ )
152
+
153
+ entry = Gtk::Entry.new
154
+ entry.signal_connect('activate') { dialog.response(Gtk::Dialog::RESPONSE_OK) }
155
+
156
+ dialog.vbox.pack_start Gtk::Label.new("Object number: ")
157
+ dialog.vbox.pack_start entry
158
+ dialog.show_all
159
+
160
+ no = 0
161
+ dialog.run do |response|
162
+ no = entry.text.to_i if response == Gtk::Dialog::RESPONSE_OK
163
+
164
+ dialog.destroy
165
+ end
166
+
167
+ return unless no > 0
168
+
169
+ obj = @opened[no]
170
+ if obj.nil?
171
+ error("Object #{no} not found.")
172
+ else
173
+ @treeview.goto(obj)
174
+ end
175
+ end
176
+
177
+ private
178
+
179
+ def init_interface
180
+ signal_connect('destroy') {
181
+ @config.save
182
+ Gtk.main_quit
183
+ }
184
+
185
+ add_events(Gdk::Event::KEY_RELEASE_MASK)
186
+ signal_connect('key_release_event') { |_w, event|
187
+ if event.keyval == Gdk::Keyval::GDK_F1 then about
188
+ elsif event.keyval == Gdk::Keyval::GDK_Escape and @opened and not @explorer_history.empty?
189
+ @treeview.goto(@explorer_history.pop)
190
+ end
191
+ }
192
+
193
+ create_menus
194
+ create_treeview
195
+ create_hexview
196
+ create_objectview
197
+ create_panels
198
+ create_statusbar
199
+
200
+ @vbox = Gtk::VBox.new
201
+ @vbox.pack_start(@menu, false, false)
202
+ @vbox.pack_start(@hpaned)
203
+ @vbox.pack_end(@statusbar, false, false)
204
+
205
+ add @vbox
206
+
207
+ set_default_size(self.screen.width * 0.5, self.screen.height * 0.5)
208
+ show_all
209
+ end
210
+
211
+ def search_document(expr, regexp: false, type: 'body')
212
+ search =
213
+ {
214
+ expr: expr,
215
+ regexp: regexp,
216
+ type: type,
217
+ }
218
+
219
+ if search == @last_search
220
+ @last_search_result.push @last_search_result.shift
221
+ results = @last_search_result
222
+ else
223
+ expr = Regexp.new(expr) if search[:regexp]
224
+
225
+ results =
226
+ if search[:type] == 'body'
227
+ @opened.grep(expr)
228
+ else
229
+ @opened.ls(expr, follow_references: false)
230
+ end
231
+
232
+ @last_search = search
233
+ end
234
+
235
+ goto_next_search_result(results)
236
+ end
237
+
238
+ def goto_next_search_result(results)
239
+ if results.empty?
240
+ error("No result found.")
241
+ else
242
+ if results != @last_search_result
243
+ # Reset the previous search highlighting.
244
+ @last_search_result.each do |obj| @treeview.highlight(obj, nil) end
245
+
246
+ # Highlight the new results.
247
+ results.each do |obj| @treeview.highlight(obj, "lightpink") end
248
+
249
+ @last_search_result = results
250
+ end
251
+
252
+ @treeview.goto(results.first, follow_references: false)
253
+ end
254
+ end
255
+
256
+ def create_panels
257
+ @hpaned = Gtk::HPaned.new
258
+
259
+ @treepanel = Gtk::ScrolledWindow.new.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
260
+ @treepanel.add @treeview
261
+
262
+ @vpaned = Gtk::VPaned.new
263
+ @vpaned.pack1(@objectview, true, false)
264
+ @vpaned.pack2(@hexview, true, false)
265
+
266
+ @hpaned.pack1(@treepanel, true, false)
267
+ @hpaned.pack2(@vpaned, true, false)
268
+ end
269
+
270
+ def create_statusbar
271
+ @statusbar = Gtk::Statusbar.new
272
+
273
+ @main_context = @statusbar.get_context_id 'Main'
274
+ @statusbar.push(@main_context, 'No file selected')
275
+ end
276
+
277
+ def start_profiling
278
+ if @help_menu_profile.active?
279
+ require 'ruby-prof'
280
+ RubyProf.start
281
+ end
282
+
283
+ result = yield
284
+
285
+ if @help_menu_profile.active?
286
+ result = RubyProf.stop
287
+ multiprinter = RubyProf::MultiPrinter.new(result)
288
+
289
+ Dir.mkdir(@config.profile_output_dir) unless Dir.exist?(@config.profile_output_dir)
290
+
291
+ multiprinter.print(path: @config.profile_output_dir, profile: File.basename(filename))
292
+ end
293
+
294
+ result
295
+ end
296
+ end
297
+
298
+ end
299
+
300
+ PDFWalker::Walker.start if __FILE__ == $0
@@ -0,0 +1,75 @@
1
+ =begin
2
+
3
+ This file is part of PDF Walker, a graphical PDF file browser
4
+ Copyright (C) 2017 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/>.
18
+
19
+ =end
20
+
21
+ module PDFWalker
22
+
23
+ class Walker < Window
24
+ def show_xrefs(target)
25
+ XrefsDialog.new(self, target)
26
+ end
27
+ end
28
+
29
+ class XrefsDialog < Dialog
30
+ OBJCOL = 0
31
+ REFCOL = 1
32
+ NAMECOL = 2
33
+
34
+ def initialize(parent, target)
35
+ super("Xrefs to #{target.reference}", parent, Dialog::MODAL, [Stock::CLOSE, Dialog::RESPONSE_NONE])
36
+ @parent = parent
37
+
38
+ @list = ListStore.new(Object, String, String)
39
+ @view = TreeView.new(@list)
40
+
41
+ column = Gtk::TreeViewColumn.new("Origin", Gtk::CellRendererText.new, text: REFCOL)
42
+ @view.append_column(column)
43
+
44
+ column = Gtk::TreeViewColumn.new("Objects", Gtk::CellRendererText.new, text: NAMECOL)
45
+ @view.append_column(column)
46
+
47
+ target.xrefs.each { |obj|
48
+ str = obj.type.to_s
49
+ iter = @list.append
50
+ @list.set_value(iter, OBJCOL, obj)
51
+
52
+ obj = obj.parent until obj.indirect?
53
+ @list.set_value(iter, REFCOL, obj.reference.to_s)
54
+
55
+ @list.set_value(iter, NAMECOL, str)
56
+ }
57
+
58
+ @view.signal_connect("row_activated") { |_tree, _path, _column|
59
+ if @view.selection.selected
60
+ from = @list.get_value(@view.selection.selected, OBJCOL)
61
+ @parent.treeview.goto(from)
62
+ end
63
+ }
64
+
65
+ scroll = ScrolledWindow.new.set_policy(POLICY_NEVER, POLICY_AUTOMATIC)
66
+ scroll.add(@view)
67
+ vbox.add(scroll)
68
+
69
+ set_default_size(200, 200)
70
+
71
+ signal_connect('response') { destroy }
72
+ show_all
73
+ end
74
+ end
75
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pdfwalker
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Guillaume Delugré
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-10-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: gtk2
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: ruby-gtkhex
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: origami
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.1'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.1'
55
+ description: PDFWalker is a GTK frontend to explore the internals of a PDF document
56
+ with Origami
57
+ email: origami@subvert.technology
58
+ executables:
59
+ - pdfwalker
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - COPYING
64
+ - bin/pdfwalker
65
+ - lib/pdfwalker.rb
66
+ - lib/pdfwalker/about.rb
67
+ - lib/pdfwalker/config.rb
68
+ - lib/pdfwalker/file.rb
69
+ - lib/pdfwalker/hexview.rb
70
+ - lib/pdfwalker/imgview.rb
71
+ - lib/pdfwalker/menu.rb
72
+ - lib/pdfwalker/properties.rb
73
+ - lib/pdfwalker/signing.rb
74
+ - lib/pdfwalker/textview.rb
75
+ - lib/pdfwalker/treeview.rb
76
+ - lib/pdfwalker/version.rb
77
+ - lib/pdfwalker/walker.rb
78
+ - lib/pdfwalker/xrefs.rb
79
+ homepage: http://github.com/gdelugre/pdfwalker
80
+ licenses:
81
+ - GPL-3.0+
82
+ metadata: {}
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubyforge_project:
99
+ rubygems_version: 2.6.13
100
+ signing_key:
101
+ specification_version: 4
102
+ summary: a PDF document explorer
103
+ test_files: []