| //======================================================================== |
| // |
| // pdf-inspector.cc |
| // |
| // Copyright 2005 Jonathan Blandford <jrb@redhat.com> |
| // Copyright 2018 Adam Reichold <adam.reichold@t-online.de> |
| // Copyright 2019, 2022 Albert Astals Cid <aacid@kde.org> |
| // |
| //======================================================================== |
| |
| #include <config.h> |
| |
| #include <goo/gmem.h> |
| #include <goo/GooTimer.h> |
| #include <splash/SplashTypes.h> |
| #include <splash/SplashBitmap.h> |
| #include "Object.h" |
| #include "ProfileData.h" |
| #include "GfxState.h" |
| |
| #include <gdk/gdk.h> |
| #include "CairoOutputDev.h" |
| |
| #include "PDFDoc.h" |
| #include "GlobalParams.h" |
| #include "ErrorCodes.h" |
| #include <gtk/gtk.h> |
| |
| // Mapping |
| #include "pdf-operators.h" |
| |
| enum |
| { |
| OP_STRING, |
| OP_COUNT, |
| OP_TOTAL, |
| OP_MIN, |
| OP_MAX, |
| N_COLUMNS |
| }; |
| |
| class PdfInspector |
| { |
| public: |
| PdfInspector(); |
| |
| void set_file_name(const char *file_name); |
| void load(const char *file_name); |
| void run(); |
| void error_dialog(const char *error_message); |
| void analyze_page(int page); |
| |
| private: |
| static void on_file_activated(GtkWidget *widget, PdfInspector *inspector); |
| static void on_selection_changed(GtkTreeSelection *selection, PdfInspector *inspector); |
| static void on_analyze_clicked(GtkWidget *widget, PdfInspector *inspector); |
| |
| GtkBuilder *builder; |
| GtkTreeModel *model; |
| PDFDoc *doc; |
| CairoOutputDev *output; |
| }; |
| |
| PdfInspector::PdfInspector() |
| { |
| GtkWidget *widget; |
| GError *error = nullptr; |
| |
| builder = gtk_builder_new(); |
| |
| if (!gtk_builder_add_from_file(builder, SRC_DIR "/pdf-inspector.ui", &error)) { |
| g_warning("Couldn't load builder file: %s", error->message); |
| g_error_free(error); |
| } |
| |
| widget = GTK_WIDGET(gtk_builder_get_object(builder, "pdf_file_chooser_button")); |
| g_signal_connect(widget, "selection-changed", G_CALLBACK(on_file_activated), this); |
| |
| widget = GTK_WIDGET(gtk_builder_get_object(builder, "analyze_button")); |
| g_signal_connect(widget, "clicked", G_CALLBACK(on_analyze_clicked), this); |
| |
| // setup the TreeView |
| widget = GTK_WIDGET(gtk_builder_get_object(builder, "pdf_tree_view")); |
| g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)), "changed", G_CALLBACK(on_selection_changed), this); |
| model = (GtkTreeModel *)gtk_list_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_INT, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_DOUBLE); |
| gtk_tree_view_set_model(GTK_TREE_VIEW(widget), model); |
| gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(widget), 0, "Operation", gtk_cell_renderer_text_new(), "text", OP_STRING, NULL); |
| |
| gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(widget), 1, "Count", gtk_cell_renderer_text_new(), "text", OP_COUNT, NULL); |
| |
| gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(widget), 2, "Elapsed", gtk_cell_renderer_text_new(), "text", OP_TOTAL, NULL); |
| |
| gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(widget), 3, "Min", gtk_cell_renderer_text_new(), "text", OP_MIN, NULL); |
| |
| gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(widget), 4, "Max", gtk_cell_renderer_text_new(), "text", OP_MAX, NULL); |
| |
| for (int i = 0; i < N_COLUMNS; i++) { |
| GtkTreeViewColumn *column; |
| |
| column = gtk_tree_view_get_column(GTK_TREE_VIEW(widget), i); |
| gtk_tree_view_column_set_sort_column_id(column, i); |
| } |
| doc = nullptr; |
| output = new CairoOutputDev(); |
| output->setPrinting(false); |
| |
| // set up initial widgets |
| load(nullptr); |
| } |
| |
| void PdfInspector::set_file_name(const char *file_name) |
| { |
| GtkWidget *widget; |
| |
| widget = GTK_WIDGET(gtk_builder_get_object(builder, "pdf_file_chooser_button")); |
| gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(widget), file_name); |
| } |
| |
| void PdfInspector::on_file_activated(GtkWidget *widget, PdfInspector *inspector) |
| { |
| gchar *file_name; |
| |
| file_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget)); |
| if (file_name) { |
| inspector->load(file_name); |
| } |
| |
| g_free(file_name); |
| } |
| |
| void PdfInspector::on_selection_changed(GtkTreeSelection *selection, PdfInspector *inspector) |
| { |
| GtkWidget *label; |
| size_t i; |
| GtkTreeModel *model; |
| GtkTreeIter iter; |
| gchar *op = nullptr; |
| |
| label = GTK_WIDGET(gtk_builder_get_object(inspector->builder, "description_label")); |
| gtk_label_set_markup(GTK_LABEL(label), "<i>No Description</i>"); |
| |
| if (gtk_tree_selection_get_selected(selection, &model, &iter)) { |
| gtk_tree_model_get(model, &iter, OP_STRING, &op, -1); |
| } |
| |
| if (op == nullptr) { |
| return; |
| } |
| |
| for (i = 0; i < G_N_ELEMENTS(op_mapping); i++) { |
| |
| if (!strcmp(op, op_mapping[i].op)) { |
| gchar *text; |
| text = g_strdup_printf("<i>%s</i>", op_mapping[i].description); |
| gtk_label_set_markup(GTK_LABEL(label), text); |
| g_free(text); |
| break; |
| } |
| } |
| |
| g_free(op); |
| } |
| |
| void PdfInspector::on_analyze_clicked(GtkWidget *widget, PdfInspector *inspector) |
| { |
| GtkWidget *spin; |
| int page; |
| |
| spin = GTK_WIDGET(gtk_builder_get_object(inspector->builder, "pdf_spin")); |
| |
| page = (int)gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)); |
| |
| inspector->analyze_page(page); |
| } |
| |
| void PdfInspector::analyze_page(int page) |
| { |
| GtkWidget *label; |
| char *text; |
| cairo_t *cr; |
| cairo_surface_t *surface; |
| |
| label = GTK_WIDGET(gtk_builder_get_object(builder, "pdf_total_label")); |
| |
| output->startProfile(); |
| gtk_list_store_clear(GTK_LIST_STORE(model)); |
| |
| GooTimer timer; |
| surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, doc->getPageCropWidth(page + 1), doc->getPageCropHeight(page + 1)); |
| cr = cairo_create(surface); |
| cairo_surface_destroy(surface); |
| output->setCairo(cr); |
| cairo_destroy(cr); |
| doc->displayPage(output, page + 1, 72, 72, 0, false, true, false); |
| output->setCairo(nullptr); |
| |
| // Total time; |
| text = g_strdup_printf("%g", timer.getElapsed()); |
| gtk_label_set_text(GTK_LABEL(label), text); |
| g_free(text); |
| |
| // Individual times; |
| auto hash = output->endProfile(); |
| for (const auto &kvp : *hash) { |
| GtkTreeIter tree_iter; |
| const auto *const data_p = &kvp.second; |
| |
| gtk_list_store_append(GTK_LIST_STORE(model), &tree_iter); |
| gtk_list_store_set(GTK_LIST_STORE(model), &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); |
| } |
| } |
| |
| void PdfInspector::load(const char *file_name) |
| { |
| GtkWidget *spin; |
| GtkWidget *button; |
| GtkWidget *label; |
| |
| // kill the old PDF file |
| if (doc != nullptr) { |
| delete doc; |
| doc = nullptr; |
| } |
| |
| // load the new file |
| if (file_name) { |
| doc = new PDFDoc(std::make_unique<GooString>(file_name)); |
| } |
| |
| if (doc && !doc->isOk()) { |
| this->error_dialog("Failed to load file."); |
| delete doc; |
| doc = nullptr; |
| } |
| |
| spin = GTK_WIDGET(gtk_builder_get_object(builder, "pdf_spin")); |
| button = GTK_WIDGET(gtk_builder_get_object(builder, "analyze_button")); |
| label = GTK_WIDGET(gtk_builder_get_object(builder, "pdf_total_label")); |
| gtk_label_set_text(GTK_LABEL(label), ""); |
| |
| if (doc) { |
| gtk_widget_set_sensitive(spin, TRUE); |
| gtk_widget_set_sensitive(button, TRUE); |
| gtk_widget_set_sensitive(label, TRUE); |
| gtk_spin_button_set_range(GTK_SPIN_BUTTON(spin), 0, doc->getNumPages() - 1); |
| gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), 0); |
| |
| output->startDoc(doc); |
| } else { |
| gtk_widget_set_sensitive(spin, FALSE); |
| gtk_widget_set_sensitive(button, FALSE); |
| gtk_widget_set_sensitive(label, FALSE); |
| } |
| } |
| |
| void PdfInspector::error_dialog(const char *error_message) |
| { |
| g_warning("%s", error_message); |
| } |
| |
| void PdfInspector::run() |
| { |
| GtkWidget *dialog; |
| |
| dialog = GTK_WIDGET(gtk_builder_get_object(builder, "pdf_dialog")); |
| |
| gtk_dialog_run(GTK_DIALOG(dialog)); |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| const char *file_name = nullptr; |
| PdfInspector *inspector; |
| |
| gtk_init(&argc, &argv); |
| |
| globalParams = std::make_unique<GlobalParams>(); |
| globalParams->setProfileCommands(true); |
| globalParams->setPrintCommands(true); |
| |
| if (argc == 2) { |
| file_name = argv[1]; |
| } else if (argc > 2) { |
| fprintf(stderr, "usage: %s [PDF-FILE]\n", argv[0]); |
| return -1; |
| } |
| |
| inspector = new PdfInspector(); |
| |
| if (file_name) { |
| inspector->set_file_name(file_name); |
| } |
| |
| inspector->run(); |
| |
| delete inspector; |
| |
| return 0; |
| } |