Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

print: Use Poppler to render pages to print #366

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ jobs:
apt-get update
apt-get upgrade -y
apt-get build-dep -y xdg-desktop-portal-gtk
apt-get install -y libpoppler-glib-dev

- uses: actions/checkout@v2

Expand Down
4 changes: 4 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ PKG_CHECK_MODULES(GTK, [xdg-desktop-portal >= 1.5 glib-2.0 >= 2.44 gio-unix-2.0
AC_SUBST(GTK_CFLAGS)
AC_SUBST(GTK_LIBS)

PKG_CHECK_MODULES(POPPLER, [ gtk+-3.0 >= 3.14 gio-unix-2.0 poppler >= 0.16.7 poppler-glib >= 0.16.7 ])
AC_SUBST(POPPLER_CFLAGS)
AC_SUBST(POPPLER_LIBS)

PKG_CHECK_MODULES(GTK_X11, gtk+-x11-3.0,
have_gtk_x11=yes, have_gtk_x11=no)
if test "$have_gtk_x11" = "yes"; then
Expand Down
16 changes: 16 additions & 0 deletions src/Makefile.am.inc
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ xdg_desktop_portal_gtk_CPPFLAGS = \
-DLOCALEDIR=\"$(localedir)\" \
-I$(top_srcdir)/src \
-I$(top_builddir)/src \
-DLIBEXECDIR=\"$(libexecdir)\" \
$(NULL)

noinst_PROGRAMS = \
Expand All @@ -285,3 +286,18 @@ testappchooser_SOURCES = \
nodist_testappchooser_SOURCES = \
src/resources.c \
$(NULL)

pdftoraw_SOURCES = \
src/pdftoraw.c \
$(NULL)

pdftorawdir = $(libexecdir)/xdg-desktop-portal-gtk-utils
pdftoraw_PROGRAMS = \
pdftoraw \
$(NULL)
pdftoraw_LDADD = $(BASE_LIBS) $(POPPLER_LIBS) -lm
pdftoraw_CFLAGS = $(BASE_CFLAGS) $(POPPLER_CFLAGS)
pdftoraw_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_builddir)/src \
$(NULL)
210 changes: 210 additions & 0 deletions src/pdftoraw.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
/*
* Copyright (C) 2021 Thierry HUCHARD <[email protected]>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/

#include <unistd.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <gio/gio.h>
#include <poppler.h>
#include <gdk/gdk.h>


// 1 inch = 72 points
#define PTS 72.0

// Resolution for the printer, 150 my choice
#define DPI 150.0

G_DEFINE_AUTOPTR_CLEANUP_FUNC (PopplerDocument, g_object_unref);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (PopplerPage, g_object_unref);

static gchar *filename = "";
static gboolean opt_test = FALSE;
static gboolean opt_pages = FALSE;
static int opt_raw = -1;
static int opt_width = -1;
static int opt_height = -1;

static GOptionEntry entries[] = {
{ "file", 'f', 0, G_OPTION_ARG_FILENAME, &filename, "PDF file path", NULL },
{ "test", 't', 0, G_OPTION_ARG_NONE, &opt_test, "Test if the file is a PDF file", NULL },
{ "pages", 'p', 0, G_OPTION_ARG_NONE, &opt_pages, "Returns the number of pages in the PDF file", NULL },
{ "width", 'w', 0, G_OPTION_ARG_INT, &opt_width, "Give the size of the page provided in arguments", NULL},
{ "height", 'W', 0, G_OPTION_ARG_INT, &opt_height, "Gives the height of the page provided in arguments", NULL},
{ "raw", 'r', 0, G_OPTION_ARG_INT, &opt_raw, "Retrieves data in pixels from the page provided as arguments", NULL},
{ NULL }
};

static int
pdf_number_pages_get (PopplerDocument *doc)
{
return poppler_document_get_n_pages (doc);
}

static void
pdf_page_raw_get (PopplerDocument *doc,
int page_nr)
{
cairo_surface_t *s = NULL;
cairo_t *cr = NULL;
unsigned char *data = NULL;
g_autoptr (PopplerPage) page = NULL;
g_autoptr (GdkPixbuf) pix = NULL;
double width = 0;
double height = 0;
int w = 0;
int h = 0;

page = poppler_document_get_page (doc, page_nr);
if (page == NULL)
{
g_error("failure poppler_document_get_page");
return;
}
poppler_page_get_size (page, &width, &height);
width = DPI * width / PTS;
height = DPI * height / PTS;
w = (int)ceil(width);
h = (int)ceil(height);
s = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
if (s == NULL)
{
g_error ("failure cairo_image_surface_create");
return;
TingPing marked this conversation as resolved.
Show resolved Hide resolved
}
cr = cairo_create (s);
if (cr == NULL)
{
cairo_surface_destroy (s);
g_error ("failure cairo_create");
return;
}
cairo_scale (cr, DPI / PTS, DPI / PTS);
cairo_save (cr);
poppler_page_render_for_printing (page, cr);
cairo_restore (cr);
pix = gdk_pixbuf_get_from_surface (s, 0, 0, w, h);
cairo_surface_destroy (s);
data = gdk_pixbuf_get_pixels (pix);
TingPing marked this conversation as resolved.
Show resolved Hide resolved
fwrite (data, 1, (w * h * 4), stdout);
cairo_destroy (cr);
}

static int
pdf_page_width_get (PopplerDocument *doc,
int page_nr)
{
g_autoptr (PopplerPage) page = NULL;
double width = 0;
int w = 0;

page = poppler_document_get_page (doc, page_nr);
if (page == NULL)
return -1;
poppler_page_get_size (page, &width, NULL);
width = DPI * width / PTS;
w = (int)ceil (width);
return w;
}

static int
pdf_page_height_get (PopplerDocument *doc,
int page_nr)
{
g_autoptr (PopplerPage) page = NULL;
double height = 0;
int h = 0;

page = poppler_document_get_page (doc, page_nr);
if (page == NULL)
return -1;
poppler_page_get_size (page, NULL, &height);
height = DPI * height / PTS;
h = (int)ceil (height);
return h;
}

int
main (int argc, char **argv)
{
g_autoptr (PopplerDocument) doc = NULL;
GError *err = NULL;
ThierryHFR marked this conversation as resolved.
Show resolved Hide resolved
g_autoptr (GFile) in = NULL;

ThierryHFR marked this conversation as resolved.
Show resolved Hide resolved
GOptionContext *context;
TingPing marked this conversation as resolved.
Show resolved Hide resolved

context = g_option_context_new ("- PDF utility for portal backends");
g_option_context_add_main_entries (context, entries, NULL);
if (!g_option_context_parse (context, &argc, &argv, &err))
{
g_printerr ("%s: %s", g_get_application_name (), err->message);
g_printerr ("\n");
g_printerr ("Try \"%s --help\" for more information.",
g_get_prgname ());
g_printerr ("\n");
exit (EXIT_FAILURE);
return 1;
}

if (filename == NULL)
{
g_printerr ("%s", g_option_context_get_help (context, TRUE, NULL));
TingPing marked this conversation as resolved.
Show resolved Hide resolved
g_option_context_free (context);
exit (EXIT_FAILURE);
}
in = g_file_new_for_path (filename);
doc = poppler_document_new_from_gfile (in, NULL, NULL, &err);
if (err)
{
g_error_free (err);
g_printerr ("%s", g_option_context_get_help (context, TRUE, NULL));
g_option_context_free (context);
exit (EXIT_FAILURE);
}

if (opt_test)
{
printf ("1");
}
else if (opt_raw > -1)
{
pdf_page_raw_get (doc, opt_raw);
}
else if (opt_pages)
{
printf ("%d", pdf_number_pages_get (doc));
}
else if (opt_width > -1)
{
printf ("%d", pdf_page_width_get (doc, opt_width));
}
else if (opt_height > -1)
{
printf ("%d", pdf_page_height_get (doc, opt_height));
}
else
{
g_printerr ("%s", g_option_context_get_help (context, TRUE, NULL));
g_option_context_free (context);
exit (EXIT_FAILURE);
}
g_option_context_free (context);
return 0;
}

Loading