blob: 7fae242613d9106f5bd6e53f9e34bedf524c0762 [file] [log] [blame]
//========================================================================
//
// 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.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), "<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;
}