1//========================================================================
2//
3// pdf-inspector.cc
4//
5// Copyright 2005 Jonathan Blandford <jrb@redhat.com>
6// Copyright 2018 Adam Reichold <adam.reichold@t-online.de>
7// Copyright 2019, 2022 Albert Astals Cid <aacid@kde.org>
8//
9//========================================================================
10
11#include <config.h>
12
13#include <goo/gmem.h>
14#include <goo/GooTimer.h>
15#include <splash/SplashTypes.h>
16#include <splash/SplashBitmap.h>
17#include "Object.h"
18#include "ProfileData.h"
19#include "GfxState.h"
20
21#include <gdk/gdk.h>
22#include "CairoOutputDev.h"
23
24#include "PDFDoc.h"
25#include "GlobalParams.h"
26#include "ErrorCodes.h"
27#include <gtk/gtk.h>
28
29// Mapping
30#include "pdf-operators.c"
31
32enum
33{
34 OP_STRING,
35 OP_COUNT,
36 OP_TOTAL,
37 OP_MIN,
38 OP_MAX,
39 N_COLUMNS
40};
41
42class PdfInspector
43{
44public:
45 PdfInspector();
46
47 void set_file_name(const char *file_name);
48 void load(const char *file_name);
49 void run();
50 void error_dialog(const char *error_message);
51 void analyze_page(int page);
52
53private:
54 static void on_file_activated(GtkWidget *widget, PdfInspector *inspector);
55 static void on_selection_changed(GtkTreeSelection *selection, PdfInspector *inspector);
56 static void on_analyze_clicked(GtkWidget *widget, PdfInspector *inspector);
57
58 GtkBuilder *builder;
59 GtkTreeModel *model;
60 PDFDoc *doc;
61 CairoOutputDev *output;
62};
63
64PdfInspector::PdfInspector()
65{
66 GtkWidget *widget;
67 GError *error = nullptr;
68
69 builder = gtk_builder_new();
70
71 if (!gtk_builder_add_from_file(builder, SRC_DIR "/pdf-inspector.ui", error: &error)) {
72 g_warning("Couldn't load builder file: %s", error->message);
73 g_error_free(error);
74 }
75
76 widget = GTK_WIDGET(gtk_builder_get_object(builder, "pdf_file_chooser_button"));
77 g_signal_connect(widget, "selection-changed", G_CALLBACK(on_file_activated), this);
78
79 widget = GTK_WIDGET(gtk_builder_get_object(builder, "analyze_button"));
80 g_signal_connect(widget, "clicked", G_CALLBACK(on_analyze_clicked), this);
81
82 // setup the TreeView
83 widget = GTK_WIDGET(gtk_builder_get_object(builder, "pdf_tree_view"));
84 g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)), "changed", G_CALLBACK(on_selection_changed), this);
85 model = (GtkTreeModel *)gtk_list_store_new(n_columns: N_COLUMNS, G_TYPE_STRING, G_TYPE_INT, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
86 gtk_tree_view_set_model(GTK_TREE_VIEW(widget), model);
87 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(widget), position: 0, title: "Operation", cell: gtk_cell_renderer_text_new(), "text", OP_STRING, NULL);
88
89 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(widget), position: 1, title: "Count", cell: gtk_cell_renderer_text_new(), "text", OP_COUNT, NULL);
90
91 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(widget), position: 2, title: "Elapsed", cell: gtk_cell_renderer_text_new(), "text", OP_TOTAL, NULL);
92
93 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(widget), position: 3, title: "Min", cell: gtk_cell_renderer_text_new(), "text", OP_MIN, NULL);
94
95 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(widget), position: 4, title: "Max", cell: gtk_cell_renderer_text_new(), "text", OP_MAX, NULL);
96
97 for (int i = 0; i < N_COLUMNS; i++) {
98 GtkTreeViewColumn *column;
99
100 column = gtk_tree_view_get_column(GTK_TREE_VIEW(widget), n: i);
101 gtk_tree_view_column_set_sort_column_id(tree_column: column, sort_column_id: i);
102 }
103 doc = nullptr;
104 output = new CairoOutputDev();
105 output->setPrinting(false);
106
107 // set up initial widgets
108 load(file_name: nullptr);
109}
110
111void PdfInspector::set_file_name(const char *file_name)
112{
113 GtkWidget *widget;
114
115 widget = GTK_WIDGET(gtk_builder_get_object(builder, "pdf_file_chooser_button"));
116 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(widget), filename: file_name);
117}
118
119void PdfInspector::on_file_activated(GtkWidget *widget, PdfInspector *inspector)
120{
121 gchar *file_name;
122
123 file_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
124 if (file_name) {
125 inspector->load(file_name);
126 }
127
128 g_free(mem: file_name);
129}
130
131void PdfInspector::on_selection_changed(GtkTreeSelection *selection, PdfInspector *inspector)
132{
133 GtkWidget *label;
134 size_t i;
135 GtkTreeModel *model;
136 GtkTreeIter iter;
137 gchar *op = nullptr;
138
139 label = GTK_WIDGET(gtk_builder_get_object(inspector->builder, "description_label"));
140 gtk_label_set_markup(GTK_LABEL(label), str: "<i>No Description</i>");
141
142 if (gtk_tree_selection_get_selected(selection, model: &model, iter: &iter)) {
143 gtk_tree_model_get(tree_model: model, iter: &iter, OP_STRING, &op, -1);
144 }
145
146 if (op == nullptr) {
147 return;
148 }
149
150 for (i = 0; i < G_N_ELEMENTS(op_mapping); i++) {
151
152 if (!strcmp(s1: op, s2: op_mapping[i].op)) {
153 gchar *text;
154 text = g_strdup_printf(format: "<i>%s</i>", op_mapping[i].description);
155 gtk_label_set_markup(GTK_LABEL(label), str: text);
156 g_free(mem: text);
157 break;
158 }
159 }
160
161 g_free(mem: op);
162}
163
164void PdfInspector::on_analyze_clicked(GtkWidget *widget, PdfInspector *inspector)
165{
166 GtkWidget *spin;
167 int page;
168
169 spin = GTK_WIDGET(gtk_builder_get_object(inspector->builder, "pdf_spin"));
170
171 page = (int)gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin));
172
173 inspector->analyze_page(page);
174}
175
176void PdfInspector::analyze_page(int page)
177{
178 GtkWidget *label;
179 char *text;
180 cairo_t *cr;
181 cairo_surface_t *surface;
182
183 label = GTK_WIDGET(gtk_builder_get_object(builder, "pdf_total_label"));
184
185 output->startProfile();
186 gtk_list_store_clear(GTK_LIST_STORE(model));
187
188 GooTimer timer;
189 surface = cairo_image_surface_create(format: CAIRO_FORMAT_RGB24, width: doc->getPageCropWidth(page: page + 1), height: doc->getPageCropHeight(page: page + 1));
190 cr = cairo_create(target: surface);
191 cairo_surface_destroy(surface);
192 output->setCairo(cr);
193 cairo_destroy(cr);
194 doc->displayPage(out: output, page: page + 1, hDPI: 72, vDPI: 72, rotate: 0, useMediaBox: false, crop: true, printing: false);
195 output->setCairo(nullptr);
196
197 // Total time;
198 text = g_strdup_printf(format: "%g", timer.getElapsed());
199 gtk_label_set_text(GTK_LABEL(label), str: text);
200 g_free(mem: text);
201
202 // Individual times;
203 auto hash = output->endProfile();
204 for (const auto &kvp : *hash) {
205 GtkTreeIter tree_iter;
206 const auto *const data_p = &kvp.second;
207
208 gtk_list_store_append(GTK_LIST_STORE(model), iter: &tree_iter);
209 gtk_list_store_set(GTK_LIST_STORE(model), iter: &tree_iter, OP_STRING, kvp.first.c_str(), OP_COUNT, data_p->getCount(), OP_TOTAL, data_p->getTotal(), OP_MIN, data_p->getMin(), OP_MAX, data_p->getMax(), -1);
210 }
211}
212
213void PdfInspector::load(const char *file_name)
214{
215 GtkWidget *spin;
216 GtkWidget *button;
217 GtkWidget *label;
218
219 // kill the old PDF file
220 if (doc != nullptr) {
221 delete doc;
222 doc = nullptr;
223 }
224
225 // load the new file
226 if (file_name) {
227 doc = new PDFDoc(std::make_unique<GooString>(args&: file_name));
228 }
229
230 if (doc && !doc->isOk()) {
231 this->error_dialog(error_message: "Failed to load file.");
232 delete doc;
233 doc = nullptr;
234 }
235
236 spin = GTK_WIDGET(gtk_builder_get_object(builder, "pdf_spin"));
237 button = GTK_WIDGET(gtk_builder_get_object(builder, "analyze_button"));
238 label = GTK_WIDGET(gtk_builder_get_object(builder, "pdf_total_label"));
239 gtk_label_set_text(GTK_LABEL(label), str: "");
240
241 if (doc) {
242 gtk_widget_set_sensitive(widget: spin, TRUE);
243 gtk_widget_set_sensitive(widget: button, TRUE);
244 gtk_widget_set_sensitive(widget: label, TRUE);
245 gtk_spin_button_set_range(GTK_SPIN_BUTTON(spin), min: 0, max: doc->getNumPages() - 1);
246 gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), value: 0);
247
248 output->startDoc(docA: doc);
249 } else {
250 gtk_widget_set_sensitive(widget: spin, FALSE);
251 gtk_widget_set_sensitive(widget: button, FALSE);
252 gtk_widget_set_sensitive(widget: label, FALSE);
253 }
254}
255
256void PdfInspector::error_dialog(const char *error_message)
257{
258 g_warning("%s", error_message);
259}
260
261void PdfInspector::run()
262{
263 GtkWidget *dialog;
264
265 dialog = GTK_WIDGET(gtk_builder_get_object(builder, "pdf_dialog"));
266
267 gtk_dialog_run(GTK_DIALOG(dialog));
268}
269
270int main(int argc, char *argv[])
271{
272 const char *file_name = nullptr;
273 PdfInspector *inspector;
274
275 gtk_init(argc: &argc, argv: &argv);
276
277 globalParams = std::make_unique<GlobalParams>();
278 globalParams->setProfileCommands(true);
279 globalParams->setPrintCommands(true);
280
281 if (argc == 2) {
282 file_name = argv[1];
283 } else if (argc > 2) {
284 fprintf(stderr, format: "usage: %s [PDF-FILE]\n", argv[0]);
285 return -1;
286 }
287
288 inspector = new PdfInspector();
289
290 if (file_name) {
291 inspector->set_file_name(file_name);
292 }
293
294 inspector->run();
295
296 delete inspector;
297
298 return 0;
299}
300

source code of poppler/test/pdf-inspector.cc