Spaces:
Running
Running
static int requested_page = 0; | |
static gboolean cairo_output = FALSE; | |
static gboolean splash_output = FALSE; | |
static gboolean args_are_fds = FALSE; | |
static const char **file_arguments = nullptr; | |
static const GOptionEntry options[] = { { "cairo", 'c', 0, G_OPTION_ARG_NONE, &cairo_output, "Cairo Output Device", nullptr }, | |
{ "splash", 's', 0, G_OPTION_ARG_NONE, &splash_output, "Splash Output Device", nullptr }, | |
{ "page", 'p', 0, G_OPTION_ARG_INT, &requested_page, "Page number", "PAGE" }, | |
{ "fd", 'f', 0, G_OPTION_ARG_NONE, &args_are_fds, "File descriptors", nullptr }, | |
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &file_arguments, nullptr, "PDF-FILES…" }, | |
{} }; | |
static GList *view_list = nullptr; | |
//------------------------------------------------------------------------ | |
//------------------------------------------------------------------------ | |
// GDKSplashOutputDev | |
//------------------------------------------------------------------------ | |
class GDKSplashOutputDev : public SplashOutputDev | |
{ | |
public: | |
GDKSplashOutputDev(GdkScreen *screen, void (*redrawCbkA)(void *data), void *redrawCbkDataA, SplashColor sc); | |
~GDKSplashOutputDev() override; | |
//----- initialization and control | |
// End a page. | |
void endPage() override; | |
// Dump page contents to display. | |
void dump() override; | |
//----- update text state | |
void updateFont(GfxState *state) override; | |
//----- special access | |
// Clear out the document (used when displaying an empty window). | |
void clear(); | |
// Copy the rectangle (srcX, srcY, width, height) to (destX, destY) | |
// in destDC. | |
void redraw(int srcX, int srcY, cairo_t *cr, int destX, int destY, int width, int height); | |
private: | |
int incrementalUpdate; | |
void (*redrawCbk)(void *data); | |
void *redrawCbkData; | |
}; | |
typedef struct | |
{ | |
PopplerDocument *doc; | |
GtkWidget *drawing_area; | |
GtkWidget *spin_button; | |
cairo_surface_t *surface; | |
GDKSplashOutputDev *out; | |
} View; | |
//------------------------------------------------------------------------ | |
// Constants and macros | |
//------------------------------------------------------------------------ | |
//------------------------------------------------------------------------ | |
// GDKSplashOutputDev | |
//------------------------------------------------------------------------ | |
GDKSplashOutputDev::GDKSplashOutputDev(GdkScreen *screen, void (*redrawCbkA)(void *data), void *redrawCbkDataA, SplashColor sc) : SplashOutputDev(splashModeRGB8, 4, false, sc), incrementalUpdate(1) | |
{ | |
redrawCbk = redrawCbkA; | |
redrawCbkData = redrawCbkDataA; | |
} | |
GDKSplashOutputDev::~GDKSplashOutputDev() { } | |
void GDKSplashOutputDev::clear() | |
{ | |
startDoc(nullptr); | |
startPage(0, nullptr, nullptr); | |
} | |
void GDKSplashOutputDev::endPage() | |
{ | |
SplashOutputDev::endPage(); | |
if (!incrementalUpdate) { | |
(*redrawCbk)(redrawCbkData); | |
} | |
} | |
void GDKSplashOutputDev::dump() | |
{ | |
if (incrementalUpdate && redrawCbk) { | |
(*redrawCbk)(redrawCbkData); | |
} | |
} | |
void GDKSplashOutputDev::updateFont(GfxState *state) | |
{ | |
SplashOutputDev::updateFont(state); | |
} | |
void GDKSplashOutputDev::redraw(int srcX, int srcY, cairo_t *cr, int destX, int destY, int width, int height) | |
{ | |
GdkPixbuf *pixbuf; | |
int gdk_rowstride; | |
gdk_rowstride = getBitmap()->getRowSize(); | |
pixbuf = gdk_pixbuf_new_from_data(getBitmap()->getDataPtr() + srcY * gdk_rowstride + srcX * 3, GDK_COLORSPACE_RGB, FALSE, 8, width, height, gdk_rowstride, nullptr, nullptr); | |
gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0); | |
cairo_paint(cr); | |
g_object_unref(pixbuf); | |
} | |
static gboolean drawing_area_draw(GtkWidget *drawing_area, cairo_t *cr, View *view) | |
{ | |
GdkRectangle document; | |
GdkRectangle clip; | |
GdkRectangle draw; | |
document.x = 0; | |
document.y = 0; | |
if (cairo_output) { | |
document.width = cairo_image_surface_get_width(view->surface); | |
document.height = cairo_image_surface_get_height(view->surface); | |
} else { | |
document.width = view->out->getBitmapWidth(); | |
document.height = view->out->getBitmapHeight(); | |
} | |
if (!gdk_cairo_get_clip_rectangle(cr, &clip)) { | |
return FALSE; | |
} | |
if (!gdk_rectangle_intersect(&document, &clip, &draw)) { | |
return FALSE; | |
} | |
if (cairo_output) { | |
cairo_set_source_surface(cr, view->surface, 0, 0); | |
cairo_paint(cr); | |
} else { | |
view->out->redraw(draw.x, draw.y, cr, draw.x, draw.y, draw.width, draw.height); | |
} | |
return TRUE; | |
} | |
static void view_set_page(View *view, int page) | |
{ | |
int w, h; | |
if (cairo_output) { | |
cairo_t *cr; | |
double width, height; | |
PopplerPage *poppler_page; | |
poppler_page = poppler_document_get_page(view->doc, page); | |
poppler_page_get_size(poppler_page, &width, &height); | |
w = (int)ceil(width); | |
h = (int)ceil(height); | |
if (view->surface) { | |
cairo_surface_destroy(view->surface); | |
} | |
view->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); | |
cr = cairo_create(view->surface); | |
poppler_page_render(poppler_page, cr); | |
cairo_set_operator(cr, CAIRO_OPERATOR_DEST_OVER); | |
cairo_set_source_rgb(cr, 1., 1., 1.); | |
cairo_paint(cr); | |
cairo_destroy(cr); | |
g_object_unref(poppler_page); | |
} else { | |
view->doc->doc->displayPage(view->out, page + 1, 72, 72, 0, false, true, true); | |
w = view->out->getBitmapWidth(); | |
h = view->out->getBitmapHeight(); | |
} | |
gtk_widget_set_size_request(view->drawing_area, w, h); | |
gtk_widget_queue_draw(view->drawing_area); | |
gtk_spin_button_set_value(GTK_SPIN_BUTTON(view->spin_button), page); | |
} | |
static void redraw_callback(void *data) | |
{ | |
View *view = (View *)data; | |
gtk_widget_queue_draw(view->drawing_area); | |
} | |
static void view_free(View *view) | |
{ | |
if (G_UNLIKELY(!view)) { | |
return; | |
} | |
g_object_unref(view->doc); | |
delete view->out; | |
cairo_surface_destroy(view->surface); | |
g_slice_free(View, view); | |
} | |
static void destroy_window_callback(GtkWindow *window, View *view) | |
{ | |
view_list = g_list_remove(view_list, view); | |
view_free(view); | |
if (!view_list) { | |
gtk_main_quit(); | |
} | |
} | |
static void page_changed_callback(GtkSpinButton *button, View *view) | |
{ | |
int page; | |
page = gtk_spin_button_get_value_as_int(button); | |
view_set_page(view, page); | |
} | |
static View *view_new(PopplerDocument *doc) | |
{ | |
View *view; | |
GtkWidget *window; | |
GtkWidget *sw; | |
GtkWidget *vbox, *hbox; | |
guint n_pages; | |
PopplerPage *page; | |
view = g_slice_new0(View); | |
view->doc = doc; | |
window = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
g_signal_connect(window, "destroy", G_CALLBACK(destroy_window_callback), view); | |
page = poppler_document_get_page(doc, 0); | |
if (page) { | |
double width, height; | |
poppler_page_get_size(page, &width, &height); | |
gtk_window_set_default_size(GTK_WINDOW(window), (gint)width, (gint)height); | |
g_object_unref(page); | |
} | |
vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); | |
view->drawing_area = gtk_drawing_area_new(); | |
sw = gtk_scrolled_window_new(nullptr, nullptr); | |
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); | |
gtk_container_add(GTK_CONTAINER(sw), view->drawing_area); | |
gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw), view->drawing_area); | |
gtk_widget_show(view->drawing_area); | |
gtk_box_pack_end(GTK_BOX(vbox), sw, TRUE, TRUE, 0); | |
gtk_widget_show(sw); | |
hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); | |
n_pages = poppler_document_get_n_pages(doc); | |
view->spin_button = gtk_spin_button_new_with_range(0, n_pages - 1, 1); | |
g_signal_connect(view->spin_button, "value-changed", G_CALLBACK(page_changed_callback), view); | |
gtk_box_pack_end(GTK_BOX(hbox), view->spin_button, FALSE, TRUE, 0); | |
gtk_widget_show(view->spin_button); | |
gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); | |
gtk_widget_show(hbox); | |
gtk_container_add(GTK_CONTAINER(window), vbox); | |
gtk_widget_show(vbox); | |
gtk_widget_show(window); | |
if (!cairo_output) { | |
SplashColor sc = { 255, 255, 255 }; | |
view->out = new GDKSplashOutputDev(gtk_widget_get_screen(window), redraw_callback, (void *)view, sc); | |
view->out->startDoc(view->doc->doc); | |
} | |
g_signal_connect(view->drawing_area, "draw", G_CALLBACK(drawing_area_draw), view); | |
return view; | |
} | |
int main(int argc, char *argv[]) | |
{ | |
GOptionContext *ctx; | |
if (argc == 1) { | |
char *basename = g_path_get_basename(argv[0]); | |
g_printerr("usage: %s PDF-FILES…\n", basename); | |
g_free(basename); | |
return -1; | |
} | |
ctx = g_option_context_new(nullptr); | |
g_option_context_add_main_entries(ctx, options, "main"); | |
g_option_context_parse(ctx, &argc, &argv, nullptr); | |
g_option_context_free(ctx); | |
gtk_init(&argc, &argv); | |
globalParams = std::make_unique<GlobalParams>(); | |
for (int i = 0; file_arguments[i]; i++) { | |
View *view; | |
GFile *file; | |
PopplerDocument *doc = nullptr; | |
GError *error = nullptr; | |
const char *arg; | |
arg = file_arguments[i]; | |
if (args_are_fds) { | |
char *end; | |
gint64 v; | |
errno = 0; | |
end = nullptr; | |
v = g_ascii_strtoll(arg, &end, 10); | |
if (errno || end == arg || v == -1 || v < G_MININT || v > G_MAXINT) { | |
g_set_error(&error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, "Failed to parse \"%s\" as file descriptor number", arg); | |
} else { | |
doc = poppler_document_new_from_fd(int(v), nullptr, &error); | |
} | |
} else | |
{ | |
file = g_file_new_for_commandline_arg(arg); | |
doc = poppler_document_new_from_gfile(file, nullptr, nullptr, &error); | |
if (!doc) { | |
gchar *uri; | |
uri = g_file_get_uri(file); | |
g_prefix_error(&error, "%s: ", uri); | |
g_free(uri); | |
} | |
g_object_unref(file); | |
} | |
if (doc) { | |
view = view_new(doc); | |
view_list = g_list_prepend(view_list, view); | |
view_set_page(view, CLAMP(requested_page, 0, poppler_document_get_n_pages(doc) - 1)); | |
} else { | |
g_printerr("Error opening document: %s\n", error->message); | |
g_error_free(error); | |
} | |
} | |
if (view_list != nullptr) { | |
gtk_main(); | |
} | |
return 0; | |
} | |