1 /* poppler-media.cc: glib interface to MediaRendition
2  *
3  * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19 
20 #include "config.h"
21 
22 #include <errno.h>
23 #include <glib/gstdio.h>
24 
25 #include "poppler-media.h"
26 #include "poppler-private.h"
27 
28 /**
29  * SECTION: poppler-media
30  * @short_description: Media
31  * @title: PopplerMedia
32  */
33 
34 typedef struct _PopplerMediaClass PopplerMediaClass;
35 
36 struct _PopplerMedia
37 {
38   GObject   parent_instance;
39 
40   gchar  *filename;
41 
42   gchar  *mime_type;
43   Stream *stream;
44 };
45 
46 struct _PopplerMediaClass
47 {
48   GObjectClass parent_class;
49 };
50 
51 G_DEFINE_TYPE (PopplerMedia, poppler_media, G_TYPE_OBJECT);
52 
53 static void
poppler_media_finalize(GObject * object)54 poppler_media_finalize (GObject *object)
55 {
56   PopplerMedia *media = POPPLER_MEDIA(object);
57 
58   if (media->filename) {
59     g_free (media->filename);
60     media->filename = NULL;
61   }
62 
63   if (media->mime_type) {
64     g_free (media->mime_type);
65     media->mime_type = NULL;
66   }
67 
68   if (media->stream) {
69     media->stream->decRef();
70     media->stream = NULL;
71   }
72 
73   G_OBJECT_CLASS (poppler_media_parent_class)->finalize (object);
74 }
75 
76 static void
poppler_media_class_init(PopplerMediaClass * klass)77 poppler_media_class_init (PopplerMediaClass *klass)
78 {
79   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
80 
81   gobject_class->finalize = poppler_media_finalize;
82 }
83 
84 static void
poppler_media_init(PopplerMedia * media)85 poppler_media_init (PopplerMedia *media)
86 {
87 }
88 
89 PopplerMedia *
_poppler_media_new(MediaRendition * poppler_media)90 _poppler_media_new (MediaRendition *poppler_media)
91 {
92   PopplerMedia *media;
93 
94   g_assert (poppler_media != NULL);
95 
96   media = POPPLER_MEDIA (g_object_new (POPPLER_TYPE_MEDIA, NULL));
97 
98   if (poppler_media->getIsEmbedded()) {
99     GooString* mime_type;
100 
101     media->stream = poppler_media->getEmbbededStream();
102     mime_type = poppler_media->getContentType();
103     if (mime_type)
104       media->mime_type = g_strdup (mime_type->getCString());
105   } else {
106     media->filename = g_strdup (poppler_media->getFileName()->getCString());
107   }
108 
109   return media;
110 }
111 
112 /**
113 * poppler_media_get_filename:
114 * @poppler_media: a #PopplerMedia
115 *
116 * Returns the media clip filename, in case of non-embedded media. filename might be
117 * a local relative or absolute path or a URI
118 *
119 * Return value: a filename, return value is owned by #PopplerMedia and should not be freed
120 *
121 * Since: 0.14
122 */
123 const gchar *
poppler_media_get_filename(PopplerMedia * poppler_media)124 poppler_media_get_filename (PopplerMedia *poppler_media)
125 {
126   g_return_val_if_fail (POPPLER_IS_MEDIA (poppler_media), NULL);
127   g_return_val_if_fail (poppler_media->stream == NULL, NULL);
128 
129   return poppler_media->filename;
130 }
131 
132 /**
133  * poppler_media_is_embedded:
134  * @poppler_media: a #PopplerMedia
135  *
136  * Whether the media clip is embedded in the PDF. If the result is %TRUE, the embedded stream
137  * can be saved with poppler_media_save() or poppler_media_save_to_callback() function.
138  * If the result is %FALSE, the media clip filename can be retrieved with
139  * poppler_media_get_file_name() function.
140  *
141  * Return value: %TRUE if media clip is embedded, %FALSE otherwise
142  *
143  * Since: 0.14
144  */
145 gboolean
poppler_media_is_embedded(PopplerMedia * poppler_media)146 poppler_media_is_embedded (PopplerMedia *poppler_media)
147 {
148   g_return_val_if_fail (POPPLER_IS_MEDIA (poppler_media), FALSE);
149 
150   return poppler_media->stream != NULL;
151 }
152 
153 /**
154  * poppler_media_get_mime_type:
155  * @poppler_media: a #PopplerMedia
156  *
157  * Returns the media clip mime-type
158  *
159  * Return value: the mime-type, return value is owned by #PopplerMedia and should not be freed
160  *
161  * Since: 0.14
162  */
163 const gchar *
poppler_media_get_mime_type(PopplerMedia * poppler_media)164 poppler_media_get_mime_type (PopplerMedia *poppler_media)
165 {
166   g_return_val_if_fail (POPPLER_IS_MEDIA (poppler_media), NULL);
167 
168   return poppler_media->mime_type;
169 }
170 
171 static gboolean
save_helper(const gchar * buf,gsize count,gpointer data,GError ** error)172 save_helper (const gchar  *buf,
173 	     gsize         count,
174 	     gpointer      data,
175 	     GError      **error)
176 {
177   FILE *f = (FILE *) data;
178   gsize n;
179 
180   n = fwrite (buf, 1, count, f);
181   if (n != count)
182     {
183       g_set_error (error,
184 		   G_FILE_ERROR,
185 		   g_file_error_from_errno (errno),
186 		   "Error writing to media file: %s",
187 		   g_strerror (errno));
188       return FALSE;
189     }
190 
191   return TRUE;
192 }
193 
194 /**
195  * poppler_media_save:
196  * @poppler_media: a #PopplerMedia
197  * @filename: name of file to save
198  * @error: (allow-none): return location for error, or %NULL.
199  *
200  * Saves embedded stream of @poppler_media to a file indicated by @filename.
201  * If @error is set, %FALSE will be returned.
202  * Possible errors include those in the #G_FILE_ERROR domain
203  * and whatever the save function generates.
204  *
205  * Return value: %TRUE, if the file successfully saved
206  *
207  * Since: 0.14
208  */
209 gboolean
poppler_media_save(PopplerMedia * poppler_media,const char * filename,GError ** error)210 poppler_media_save (PopplerMedia *poppler_media,
211 		    const char   *filename,
212 		    GError      **error)
213 {
214   gboolean result;
215   FILE *f;
216 
217   g_return_val_if_fail (POPPLER_IS_MEDIA (poppler_media), FALSE);
218   g_return_val_if_fail (poppler_media->stream != NULL, FALSE);
219 
220   f = g_fopen (filename, "wb");
221 
222   if (f == NULL)
223     {
224       gchar *display_name = g_filename_display_name (filename);
225       g_set_error (error,
226 		   G_FILE_ERROR,
227 		   g_file_error_from_errno (errno),
228 		   "Failed to open '%s' for writing: %s",
229 		   display_name,
230 		   g_strerror (errno));
231       g_free (display_name);
232       return FALSE;
233     }
234 
235   result = poppler_media_save_to_callback (poppler_media, save_helper, f, error);
236 
237   if (fclose (f) < 0)
238     {
239       gchar *display_name = g_filename_display_name (filename);
240       g_set_error (error,
241 		   G_FILE_ERROR,
242 		   g_file_error_from_errno (errno),
243 		   "Failed to close '%s', all data may not have been saved: %s",
244 		   display_name,
245 		   g_strerror (errno));
246       g_free (display_name);
247       return FALSE;
248     }
249 
250   return result;
251 }
252 
253 #define BUF_SIZE 1024
254 
255 /**
256  * poppler_media_save_to_callback:
257  * @poppler_media: a #PopplerMedia
258  * @save_func: (scope call): a function that is called to save each block of data that the save routine generates.
259  * @user_data: user data to pass to the save function.
260  * @error: (allow-none): return location for error, or %NULL.
261  *
262  * Saves embedded stream of @poppler_media by feeding the produced data to @save_func. Can be used
263  * when you want to store the media clip stream to something other than a file, such as
264  * an in-memory buffer or a socket. If @error is set, %FALSE will be
265  * returned. Possible errors include those in the #G_FILE_ERROR domain and
266  * whatever the save function generates.
267  *
268  * Return value: %TRUE, if the save successfully completed
269  *
270  * Since: 0.14
271  */
272 gboolean
poppler_media_save_to_callback(PopplerMedia * poppler_media,PopplerMediaSaveFunc save_func,gpointer user_data,GError ** error)273 poppler_media_save_to_callback (PopplerMedia        *poppler_media,
274 				PopplerMediaSaveFunc save_func,
275 				gpointer             user_data,
276 				GError             **error)
277 {
278   Stream *stream;
279   gchar buf[BUF_SIZE];
280   int i;
281   gboolean eof_reached = FALSE;
282 
283   g_return_val_if_fail (POPPLER_IS_MEDIA (poppler_media), FALSE);
284   g_return_val_if_fail (poppler_media->stream != NULL, FALSE);
285 
286   stream = poppler_media->stream;
287   stream->reset();
288 
289   do
290     {
291       int data;
292 
293       for (i = 0; i < BUF_SIZE; i++)
294 	{
295 	  data = stream->getChar ();
296 	  if (data == EOF)
297 	    {
298 	      eof_reached = TRUE;
299 	      break;
300 	    }
301 	  buf[i] = data;
302 	}
303 
304       if (i > 0)
305 	{
306 	  if (! (save_func) (buf, i, user_data, error))
307 	    {
308 	      stream->close ();
309 	      return FALSE;
310 	    }
311 	}
312     }
313   while (! eof_reached);
314 
315   stream->close ();
316 
317   return TRUE;
318 }
319