[gegl] operations: add pdf-loading op



commit 82ce52fac4e8574b08f15c9213d1e547e71006f1
Author: Øyvind Kolås <pippin gimp org>
Date:   Thu Feb 21 00:29:03 2019 +0100

    operations: add pdf-loading op
    
    A simple page renderer that depends on poppler-glib, it render each page to a
    linear buffer and becomes very slow for high ppi, default ppi set to 300 which
    is sufficient to read and zoom in on visible details on most text based PDFs.
    
    Maybe chunking/clipping the rendering with cairo would be better for high
    resolution.

 configure.ac                    |  22 ++++
 operations/external/Makefile.am |   8 ++
 operations/external/pdf-load.c  | 241 ++++++++++++++++++++++++++++++++++++++++
 po/POTFILES.in                  |   1 +
 4 files changed, 272 insertions(+)
---
diff --git a/configure.ac b/configure.ac
index 359cb0f9d..50f0ab852 100644
--- a/configure.ac
+++ b/configure.ac
@@ -24,6 +24,7 @@ m4_define([gegl_interface_age], [0])
 m4_define([gegl_binary_age],
           [m4_eval(100 * gegl_minor_version + gegl_micro_version)])
 
+  mrg_add_binding (mrg, "return", NULL, label, do_commandline_run, o);
 m4_define([gegl_api_version], [gegl_major_version.gegl_minor_version])
 
 m4_define([stability_version_number],
@@ -53,6 +54,7 @@ m4_define([lua_required_version], [5.1.0])
 m4_define([openexr_required_version], [1.6.1])
 m4_define([libraw_required_version], [0.15.4])
 m4_define([pango_required_version], [1.38.0])
+m4_define([popplerglib_required_version], [0.71.0])
 m4_define([zlib_required_version], [1.2.0])
 m4_define([png_required_version], [1.6.0])
 m4_define([sdl_required_version], [1.2.0])
@@ -724,6 +726,25 @@ AM_CONDITIONAL(HAVE_CAIRO, test "$have_cairo" = "yes")
 AC_SUBST(CAIRO_CFLAGS) 
 AC_SUBST(CAIRO_LIBS) 
 
+##############
+# poppler-glib
+##############
+
+AC_ARG_WITH(popplerglib, [  --without-popplerglib       build without poppler support])
+
+
+have_popplerglib="no"
+if test "x$with_popplerglib" != "xno"; then
+  PKG_CHECK_MODULES(POPPLERGLIB, poppler-glib >= popplerglib_required_version,
+    have_popplerglib="yes",
+    have_popplerglib="no  (usable popplerglib not found)")
+fi
+
+AM_CONDITIONAL(HAVE_POPPLERGLIB, test "x$have_popplerglib" = "xyes")
+
+AC_SUBST(POPPLERGLIB_CFLAGS)
+AC_SUBST(POPPLERGLIB_LIBS)
+
 
 ########################
 # Checks for Pango stuff
@@ -1410,6 +1431,7 @@ Optional dependencies:
   Cairo:           $have_cairo
   Pango:           $have_pango
   pangocairo:      $have_pangocairo
+  poppler-glib:    $have_popplerglib
   GDKPixbuf:       $have_gdk_pixbuf
   JPEG:            $jpeg_ok
   zlib:            $have_zlib
diff --git a/operations/external/Makefile.am b/operations/external/Makefile.am
index bfc337d32..498ffb062 100644
--- a/operations/external/Makefile.am
+++ b/operations/external/Makefile.am
@@ -187,5 +187,13 @@ npd_la_CFLAGS = $(AM_CFLAGS) $(NPD_CFLAGS) $(CAIRO_CFLAGS)
 npd_la_LIBADD = $(op_libs) $(NPD_LIBS) $(CAIRO_LIBS)
 endif
 
+if HAVE_POPPLERGLIB
+ops += pdf-load.la
+pdf_load_la_SOURCES = pdf-load.c
+pdf_load_la_LIBADD = $(op_libs) $(POPPLERGLIB_LIBS)
+pdf_load_la_CFLAGS = $(AM_CFLAGS) $(POPPLERGLIB_CFLAGS)
+
+endif
+
 opdir = $(libdir)/gegl-@GEGL_API_VERSION@
 op_LTLIBRARIES = $(ops)
diff --git a/operations/external/pdf-load.c b/operations/external/pdf-load.c
new file mode 100644
index 000000000..5ab606353
--- /dev/null
+++ b/operations/external/pdf-load.c
@@ -0,0 +1,241 @@
+/* Video4Linux2 frame source op for GEGL
+ *
+ * GEGL 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 3 of the License, or (at your option) any later version.
+ *
+ * GEGL 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 GEGL; if not, see <https://www.gnu.org/licenses/>.
+ *
+ * Copyright 2019 Øyvind Kolås <pippin gimp org>
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+#ifdef GEGL_PROPERTIES
+
+property_file_path (path, _("Path"), "")
+     description (_("file to load"))
+
+property_file_path (uri, _("URI"), "")
+     description (_("uri of file to load"))
+
+property_int  (page,  _("Page"),  1)
+     description (_("Page to render"))
+     value_range (1, 10000)
+
+property_int  (pages,  _("Pages"),  1)
+     description (_("Total pages, provided as a visual read-only property"))
+     value_range (1, 10000)
+
+property_double (ppi,  _("PPI"),  300.0)
+     description (_("Point/pixels per inch"))
+     ui_range (72.0, 1000.0)
+     value_range (10.0, 2400.0)
+
+#else
+
+#define GEGL_OP_SOURCE
+#define GEGL_OP_NAME     pdf_load
+#define GEGL_OP_C_SOURCE pdf-load.c
+
+#include "gegl-op.h"
+#include <poppler.h>
+
+typedef struct
+{
+  char *path;
+  char *uri;
+
+  int   page_no;
+  PopplerDocument *document;
+  PopplerPage     *page;
+  int width;
+  int height;
+  double scale;
+  double ppi;
+} Priv;
+
+static GeglRectangle
+get_bounding_box (GeglOperation *operation)
+{
+  GeglRectangle result ={0,0,640,480};
+  GeglProperties *o = GEGL_PROPERTIES (operation);
+  Priv *p= (Priv*)o->user_data;
+  if (p)
+  {
+    result.width  = p->width;
+    result.height = p->height;
+  }
+  return result;
+}
+
+static void
+prepare (GeglOperation *operation)
+{
+  GeglProperties *o = GEGL_PROPERTIES (operation);
+  Priv *p= (Priv*)o->user_data;
+  if (p == NULL)
+  {
+    p = g_new0 (Priv, 1);
+    o->user_data = (void*) p;
+  }
+
+  if (p->path == NULL || strcmp (p->path, o->path) ||
+      p->uri  == NULL || strcmp (p->uri, o->uri))
+  {
+    if (p->path)
+      g_free (p->path);
+    if (p->uri)
+      g_free (p->uri);
+    if (p->document)
+      g_object_unref (p->document);
+    p->path = g_strdup (o->path);
+    p->uri = g_strdup (o->uri);
+
+    if (p->uri[0])
+    {
+      p->document = poppler_document_new_from_file (p->uri, NULL, NULL);
+    }
+    else
+    {
+      char *uri = g_strdup_printf ("file://%s", p->path);
+      p->document = poppler_document_new_from_file (uri, NULL, NULL);
+      g_free (uri);
+    }
+    p->page = NULL;
+    p->page_no = -1;
+  }
+
+  if (p->page_no != o->page-1 || p->ppi != o->ppi)
+  {
+    double width, height;
+    p->scale = o->ppi / 72.0;
+    p->ppi = o->ppi;
+    p->page_no = o->page -1;
+    if (p->page)
+      g_object_unref (p->page);
+    o->pages = poppler_document_get_n_pages (p->document);
+    if (p->page_no >= 0 && p->page_no < o->pages)
+      p->page = poppler_document_get_page (p->document, p->page_no);
+    else
+      p->page = NULL;
+
+    if (p->page)
+    {
+      poppler_page_get_size (p->page, &width, &height);
+      p->width = width * p->scale;
+      p->height = height * p->scale;
+    }
+    else
+    {
+      p->width = 23 * p->scale;
+      p->height = 42 * p->scale;
+    }
+  }
+
+  gegl_operation_set_format (operation, "output",
+                            babl_format ("R'G'B'A u8"));
+}
+
+static void
+finalize (GObject *object)
+{
+ GeglProperties *o = GEGL_PROPERTIES (object);
+
+  if (o->user_data)
+    {
+      Priv *p = (Priv*)o->user_data;
+      if (p->page)
+        g_object_unref (p->page);
+      if (p->document)
+        g_object_unref (p->document);
+      if (p->path)
+        g_free (p->path);
+      g_clear_pointer (&o->user_data, g_free);
+    }
+
+  G_OBJECT_CLASS (gegl_op_parent_class)->finalize (object);
+}
+
+static gboolean
+process (GeglOperation       *operation,
+         GeglBuffer          *output,
+         const GeglRectangle *result,
+         gint                 level)
+{
+  GeglProperties *o = GEGL_PROPERTIES (operation);
+  Priv       *p = (Priv*)o->user_data;
+
+  if (p->page)
+  {
+    cairo_surface_t   *surface;
+    cairo_t           *cr;
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, p->width, p->height);
+    cr = cairo_create (surface);
+    cairo_set_source_rgb (cr, 1,1,1);
+    cairo_paint (cr);
+    cairo_scale (cr, p->scale, p->scale);
+
+    if (p->page)
+      poppler_page_render (p->page, cr);
+
+    cairo_surface_flush (surface);
+
+    gegl_buffer_set (output,
+                     GEGL_RECTANGLE (0, 0, p->width, p->height),
+                     0,
+                     babl_format ("cairo-ARGB32"),
+                     cairo_image_surface_get_data (surface),
+                     cairo_image_surface_get_stride (surface));
+
+    cairo_destroy (cr);
+    cairo_surface_destroy (surface);
+  }
+  return  TRUE;
+}
+
+static GeglRectangle
+get_cached_region (GeglOperation       *operation,
+                   const GeglRectangle *roi)
+{
+  return get_bounding_box (operation);
+}
+
+static void
+gegl_op_class_init (GeglOpClass *klass)
+{
+  GeglOperationClass       *operation_class;
+  GeglOperationSourceClass *source_class;
+
+  G_OBJECT_CLASS (klass)->finalize = finalize;
+
+  operation_class = GEGL_OPERATION_CLASS (klass);
+  source_class    = GEGL_OPERATION_SOURCE_CLASS (klass);
+
+  source_class->process              = process;
+  operation_class->get_bounding_box  = get_bounding_box;
+  operation_class->get_cached_region = get_cached_region;
+  operation_class->prepare           = prepare;
+
+  gegl_operation_handlers_register_loader(
+    "application/pdf", "gegl:pdf-load");
+  gegl_operation_handlers_register_loader(
+    ".pdf", "gegl:pdf-load");
+
+  gegl_operation_class_set_keys (operation_class,
+    "name",         "gegl:pdf-load",
+    "title",        _("pdf loader"),
+    "categories",   "input",
+    "description",  _("PDF page decoder"),
+    NULL);
+}
+
+#endif
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 888f128cb..edd95c59e 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -260,6 +260,7 @@ operations/workshop/external/lens-correct.c
 operations/workshop/external/line-profile.c
 operations/workshop/external/spyrograph.c
 operations/workshop/external/v4l2.c
+operations/workshop/external/pdf-load.c
 operations/workshop/generated/average.c
 operations/workshop/generated/blend-reflect.c
 operations/workshop/generated/negation.c


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]