//======================================================================== // // pdf-inspector.cc // // Copyright 2005 Jonathan Blandford // Copyright 2018 Adam Reichold // Copyright 2019, 2022 Albert Astals Cid // //======================================================================== #include #include #include #include #include #include "Object.h" #include "ProfileData.h" #include "GfxState.h" #include #include "CairoOutputDev.h" #include "PDFDoc.h" #include "GlobalParams.h" #include "ErrorCodes.h" #include // Mapping #include "pdf-operators.c" 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), "No Description"); 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("%s", 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(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->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; }