1 /* Video4Linux2 frame source op for GEGL
2  *
3  * GEGL is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 3 of the License, or (at your option) any later version.
7  *
8  * GEGL is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
15  *
16  * Copyright 2019 Øyvind Kolås <pippin@gimp.org>
17  */
18 
19 #include "config.h"
20 #include <glib/gi18n-lib.h>
21 
22 #ifdef GEGL_PROPERTIES
23 
24 property_file_path (path, _("Path"), "")
25      description (_("file to load"))
26 
27 property_uri (uri, _("URI"), "")
28      description (_("uri of file to load"))
29 
30 property_int  (page,  _("Page"),  1)
31      description (_("Page to render"))
32      value_range (1, 10000)
33 
34 property_int  (pages,  _("Pages"),  1)
35      description (_("Total pages, provided as a visual read-only property"))
36      value_range (1, 10000)
37 
38 property_double (ppi,  _("PPI"),  200.0)
39      description (_("Point/pixels per inch"))
40      ui_range (72.0, 1000.0)
41      value_range (10.0, 2400.0)
42 
43 property_string (password,  _("Password"), "")
44      description (_("Password to use for decryption of PDF, or blank for none"))
45 
46 #else
47 
48 #define GEGL_OP_SOURCE
49 #define GEGL_OP_NAME     pdf_load
50 #define GEGL_OP_C_SOURCE pdf-load.c
51 
52 #include "gegl-op.h"
53 #include <poppler.h>
54 
55 typedef struct
56 {
57   char *path;
58   char *uri;
59 
60   int   page_no;
61   PopplerDocument *document;
62   PopplerPage     *page;
63   int width;
64   int height;
65   double scale;
66   double ppi;
67 } Priv;
68 
69 static GeglRectangle
70 get_bounding_box (GeglOperation *operation)
71 {
72   GeglRectangle result ={0,0,640,480};
73   GeglProperties *o = GEGL_PROPERTIES (operation);
74   Priv *p= (Priv*)o->user_data;
75   if (p)
76   {
77     result.width  = p->width;
78     result.height = p->height;
79   }
80   return result;
81 }
82 
83 static void
84 prepare (GeglOperation *operation)
85 {
86   GeglProperties *o = GEGL_PROPERTIES (operation);
87   Priv *p= (Priv*)o->user_data;
88   if (p == NULL)
89   {
90     p = g_new0 (Priv, 1);
91     o->user_data = (void*) p;
92   }
93 
94   if (p->path == NULL || strcmp (p->path, o->path) ||
95       p->uri  == NULL || strcmp (p->uri, o->uri))
96   {
97     const char *password = o->password[0]?o->password:NULL;
98 
99     if (p->path)
100       g_free (p->path);
101     if (p->uri)
102       g_free (p->uri);
103     if (p->document)
104       g_object_unref (p->document);
105     p->path = g_strdup (o->path);
106     p->uri = g_strdup (o->uri);
107 
108     if (p->uri[0])
109     {
110       p->document = poppler_document_new_from_file (p->uri, password, NULL);
111     }
112     else
113     {
114       GFile *file = g_file_new_for_path (p->path);
115       char *uri = g_file_get_uri (file);
116       p->document = poppler_document_new_from_file (uri, password, NULL);
117       g_free (uri);
118       g_object_unref (file);
119     }
120     p->page = NULL;
121     p->page_no = -1;
122   }
123 
124   if (p->page_no != o->page-1 || p->ppi != o->ppi)
125   {
126     double width, height;
127     p->scale = o->ppi / 72.0;
128     p->ppi = o->ppi;
129     p->page_no = o->page -1;
130     if (p->page)
131       g_object_unref (p->page);
132     o->pages = poppler_document_get_n_pages (p->document);
133     if (p->page_no >= 0 && p->page_no < o->pages)
134       p->page = poppler_document_get_page (p->document, p->page_no);
135     else
136       p->page = NULL;
137 
138     if (p->page)
139     {
140       poppler_page_get_size (p->page, &width, &height);
141       p->width = width * p->scale;
142       p->height = height * p->scale;
143     }
144     else
145     {
146       p->width = 23 * p->scale;
147       p->height = 42 * p->scale;
148     }
149   }
150 
151   gegl_operation_set_format (operation, "output",
152                             babl_format ("R'G'B'A u8"));
153 }
154 
155 static void
156 finalize (GObject *object)
157 {
158  GeglProperties *o = GEGL_PROPERTIES (object);
159 
160   if (o->user_data)
161     {
162       Priv *p = (Priv*)o->user_data;
163       if (p->page)
164         g_object_unref (p->page);
165       if (p->document)
166         g_object_unref (p->document);
167       if (p->path)
168         g_free (p->path);
169       g_clear_pointer (&o->user_data, g_free);
170     }
171 
172   G_OBJECT_CLASS (gegl_op_parent_class)->finalize (object);
173 }
174 
175 static gboolean
176 process (GeglOperation       *operation,
177          GeglBuffer          *output,
178          const GeglRectangle *result,
179          gint                 level)
180 {
181   GeglProperties *o = GEGL_PROPERTIES (operation);
182   Priv       *p = (Priv*)o->user_data;
183 
184   if (p->page)
185   {
186     cairo_surface_t   *surface;
187     cairo_t           *cr;
188     surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, p->width, p->height);
189     cr = cairo_create (surface);
190     cairo_set_source_rgb (cr, 1,1,1);
191     cairo_paint (cr);
192     cairo_scale (cr, p->scale, p->scale);
193 
194     if (p->page)
195       poppler_page_render (p->page, cr);
196 
197     cairo_surface_flush (surface);
198 
199     gegl_buffer_set (output,
200                      GEGL_RECTANGLE (0, 0, p->width, p->height),
201                      0,
202                      babl_format ("cairo-ARGB32"),
203                      cairo_image_surface_get_data (surface),
204                      cairo_image_surface_get_stride (surface));
205 
206     cairo_destroy (cr);
207     cairo_surface_destroy (surface);
208   }
209   return  TRUE;
210 }
211 
212 static GeglRectangle
213 get_cached_region (GeglOperation       *operation,
214                    const GeglRectangle *roi)
215 {
216   return get_bounding_box (operation);
217 }
218 
219 static void
220 gegl_op_class_init (GeglOpClass *klass)
221 {
222   GeglOperationClass       *operation_class;
223   GeglOperationSourceClass *source_class;
224 
225   G_OBJECT_CLASS (klass)->finalize = finalize;
226 
227   operation_class = GEGL_OPERATION_CLASS (klass);
228   source_class    = GEGL_OPERATION_SOURCE_CLASS (klass);
229 
230   source_class->process              = process;
231   operation_class->get_bounding_box  = get_bounding_box;
232   operation_class->get_cached_region = get_cached_region;
233   operation_class->prepare           = prepare;
234 
235   gegl_operation_handlers_register_loader(
236     "application/pdf", "gegl:pdf-load");
237   gegl_operation_handlers_register_loader(
238     ".pdf", "gegl:pdf-load");
239 
240   gegl_operation_class_set_keys (operation_class,
241     "name",         "gegl:pdf-load",
242     "title",        _("pdf loader"),
243     "categories",   "input",
244     "description",  _("PDF page decoder"),
245     NULL);
246 }
247 
248 #endif
249