pdfwalker 1.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.
@@ -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: []