"Initial commit to Gerrit"
[profile/ivi/libgsf.git] / thumbnailer / main.c
1 /* GNOME thumbnailer for Office files
2  * Copyright (C) 2005 Novell, Inc.
3  *
4  * Authors:
5  *   Federico Mena-Quintero <federico@novell.com>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 2.1 of the GNU Lesser General Public
9  * License as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
19  * USA
20  */
21
22 #include <gsf/gsf-input-memory.h>
23 #include <gsf/gsf-input-stdio.h>
24 #include <gsf/gsf-infile.h>
25 #include <gsf/gsf-infile-msole.h>
26 #include <gsf/gsf-infile-zip.h>
27 #include <gsf/gsf-meta-names.h>
28 #include <gsf/gsf-msole-utils.h>
29 #include <gsf/gsf-utils.h>
30 #include <gsf/gsf-clip-data.h>
31 #include <gsf/gsf-open-pkg-utils.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <glib.h>
35 #include <gsf-config.h>
36
37 #ifdef HAVE_GDK_PIXBUF
38 #include <gdk-pixbuf/gdk-pixbuf.h>
39 #endif
40
41 #ifdef HAVE_SETRLIMI
42 #include <sys/resource.h>
43 #endif
44
45 G_GNUC_NORETURN static void
46 show_error_string_and_exit (const char *str)
47 {
48         g_printerr ("error: %s\n", str);
49         exit (EXIT_FAILURE);
50 }
51
52 G_GNUC_NORETURN static void
53 show_error_and_exit (GError *error)
54 {
55         if (error)
56                 show_error_string_and_exit (error->message);
57         else
58                 show_error_string_and_exit ("an error happened, and we didn't get a GError.  Exiting.");
59 }
60
61 static void
62 call_convert (const char *in_filename, const char *out_filename, int thumb_size)
63 {
64         char *in_quote;
65         char *out_quote;
66         char *cmd_line;
67         GError *error;
68         gint exit_status;
69
70 #ifdef HAVE_GDK_PIXBUF
71         GdkPixbuf* pixbuf;
72
73         pixbuf = gdk_pixbuf_new_from_file_at_scale (in_filename,
74                                                     thumb_size, thumb_size,
75                                                     TRUE, NULL);
76         if (pixbuf) {
77                 gboolean success = gdk_pixbuf_save (pixbuf,
78                                                     out_filename, "png",
79                                                     NULL, NULL);
80                 g_object_unref (pixbuf);
81                 if (success)
82                         return;
83         }
84 #endif
85
86         in_quote = g_shell_quote (in_filename);
87         out_quote = g_shell_quote (out_filename);
88         cmd_line = g_strdup_printf ("convert %s +matte -thumbnail %dx%d png:%s",
89                                     in_quote,
90                                     thumb_size, thumb_size,
91                                     out_quote);
92         g_printerr ("calling %s\n", cmd_line);
93         g_free (in_quote);
94         g_free (out_quote);
95
96         error = NULL;
97         if (!g_spawn_command_line_sync (cmd_line, NULL, NULL, &exit_status, &error))
98                 show_error_and_exit (error);
99
100         g_free (cmd_line);
101 }
102
103 static void
104 write_thumbnail (const char *filename, gconstpointer data, gsize size, int thumb_size)
105 {
106         char *tmp_name;
107         int fd;
108         FILE *file;
109
110         tmp_name = g_strdup_printf ("%s.XXXXXX", filename);
111         fd = g_mkstemp (tmp_name);
112         if (fd == -1) {
113                 perror ("Could not create temporary file");
114                 exit (EXIT_FAILURE);
115         }
116
117         file = fdopen (fd, "w");
118         if (!file) {
119                 show_error_string_and_exit ("Could not open temporary file for writing");
120                 exit (EXIT_FAILURE);
121         }
122
123         if (fwrite (data, 1, size, file) != size) {
124                 perror ("Could not write data to output file");
125                 exit (EXIT_FAILURE);
126         }
127
128         if (fclose (file) != 0) {
129                 perror ("Could not close oputput file");
130                 exit (EXIT_FAILURE);
131         }
132
133         call_convert (tmp_name, filename, thumb_size);
134         unlink (tmp_name);
135 }
136
137 static void
138 zip_thumbnail (GsfInfile *infile, const char *out_filename, int thumb_size)
139 {
140         GsfInput *thumbnail;
141
142         /* Office Document thumbnail */
143         if (NULL != (thumbnail = gsf_infile_child_by_vname (infile,
144                         "Thumbnails", "thumbnail.png", NULL))) {
145                 gsf_off_t len = gsf_input_remaining (thumbnail);
146                 guint8 const *data = gsf_input_read (thumbnail, len, NULL);
147                 write_thumbnail (out_filename, data, len, thumb_size);
148                 g_object_unref (thumbnail);
149         /* Check MS Office Open thumbnail */
150         } else if (NULL != (thumbnail = gsf_open_pkg_open_rel_by_type (GSF_INPUT(infile), 
151                                 "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail",
152                                                                                                                                    NULL))) {
153                 gsf_off_t len = gsf_input_remaining (thumbnail);
154                 guint8 const *data = gsf_input_read (thumbnail, len, NULL);
155                 write_thumbnail (out_filename, data, len, thumb_size);
156                 g_object_unref (thumbnail);
157         } else
158                 show_error_string_and_exit ("Could not find thumbnail in zip file");
159 }
160
161 static void
162 msole_thumbnail (GsfInfile *infile, const char *out_filename, int thumb_size)
163 {
164         GsfInput        *summary_stream;
165         GsfDocMetaData  *meta_data;
166         GsfDocProp      *thumb_doc_prop;
167         GValue const    *thumb_value;
168         GsfClipData     *clip_data;
169         GsfClipFormat    clip_format;
170         gconstpointer    data;
171         gsize            size;
172         GError          *error;
173
174         summary_stream = gsf_infile_child_by_name (infile, "\05SummaryInformation");
175         if (!summary_stream)
176                 show_error_string_and_exit ("Could not find the SummaryInformation stream");
177
178         meta_data = gsf_doc_meta_data_new ();
179         error = gsf_msole_metadata_read (summary_stream, meta_data);
180         if (error)
181                 show_error_and_exit (error);
182
183         thumb_doc_prop = gsf_doc_meta_data_lookup (meta_data, GSF_META_NAME_THUMBNAIL);
184         if (!thumb_doc_prop)
185                 show_error_string_and_exit ("The metadata does not have a thumbnail property");
186
187         thumb_value = gsf_doc_prop_get_val (thumb_doc_prop);
188         if (!thumb_value)
189                 show_error_string_and_exit ("We got the thumbnail property, but it didn't have a value!?");
190
191         clip_data = GSF_CLIP_DATA (g_value_get_object (thumb_value));
192
193         clip_format = gsf_clip_data_get_format (clip_data);
194
195         if (clip_format == GSF_CLIP_FORMAT_WINDOWS_CLIPBOARD) {
196                 GsfClipFormatWindows win_format;
197
198                 error = NULL;
199                 win_format = gsf_clip_data_get_windows_clipboard_format (clip_data, &error);
200                 if (win_format == GSF_CLIP_FORMAT_WINDOWS_ERROR)
201                         show_error_and_exit (error);
202         }
203
204         error = NULL;
205         data = gsf_clip_data_peek_real_data (clip_data, &size, &error);
206
207         if (!data)
208                 show_error_and_exit (error);
209
210         write_thumbnail (out_filename, data, size, thumb_size);
211
212         g_object_unref (clip_data);
213
214         g_object_unref (meta_data);
215         g_object_unref (summary_stream);
216 }
217
218 static void
219 read_thumbnail_and_write (const char *in_filename, const char *out_filename, int thumb_size)
220 {
221         GsfInput  *input;
222         GsfInfile *infile;
223         GError    *error;
224
225         input = gsf_input_mmap_new (in_filename, NULL);
226         if (!input) {
227                 error = NULL;
228                 input = gsf_input_stdio_new (in_filename, &error);
229                 if (!input)
230                         show_error_and_exit (error);
231         }
232
233         input = gsf_input_uncompress (input);
234
235         error = NULL;
236         if (NULL != (infile = gsf_infile_msole_new (input, &error)))
237                 msole_thumbnail (infile, out_filename, thumb_size);
238         else if (NULL != (infile = gsf_infile_zip_new (input, &error)))
239                 zip_thumbnail (infile, out_filename, thumb_size);
240         else
241                 show_error_and_exit (error);
242
243         g_object_unref (infile);
244         g_object_unref (input);
245 }
246
247 #define MAX_HELPER_MEMORY (256 * 1024 * 1024)   /* 256 MB */
248 #define MAX_HELPER_SECONDS (5)                  /* 5 seconds */
249
250 static void
251 set_resource_limits (void)
252 {
253 #ifdef HAVE_SETRLIMI
254         struct rlimit limit;
255
256         /* We call convert(1) from ImageMagick, which is especially scary when converting
257          * WMF thumbnails into PNGs.  Convert(1) is known to leak tons of memory and CPU
258          * time on certain WMFs.  So, we'll put a cap on how much resources it can use.
259          */
260
261         limit.rlim_cur = MAX_HELPER_MEMORY;
262         limit.rlim_max = MAX_HELPER_MEMORY;
263         setrlimit (RLIMIT_AS, &limit);
264
265         limit.rlim_cur = MAX_HELPER_SECONDS;
266         limit.rlim_max = MAX_HELPER_SECONDS;
267         setrlimit (RLIMIT_CPU, &limit);
268 #endif
269 }
270
271 /* Command-line options */
272 static int   option_size = -1;
273 static char *option_input_filename = NULL;
274 static char *option_output_filename = NULL;
275
276 static GOptionEntry option_entries[] = {
277         { "input", 'i', 0, G_OPTION_ARG_FILENAME, &option_input_filename,
278           "Name of file for which to create a thumbnail",
279           "filename" },
280         { "output", 'o', 0, G_OPTION_ARG_FILENAME, &option_output_filename,
281           "Name of the file to put the thumbnail",
282           "filename" },
283         { "size", 's', 0, G_OPTION_ARG_INT, &option_size,
284           "Size of thumbnail in pixels; the thumbnail will be at most N*N pixels large",
285           "N" },
286         { NULL, 0, 0, 0, NULL, NULL, NULL }
287 };
288
289 int
290 main (int argc, char **argv)
291 {
292         GOptionContext *option_ctx;
293
294         set_resource_limits ();
295
296         option_ctx = g_option_context_new ("Options");
297         g_option_context_add_main_entries (option_ctx, option_entries, NULL); /* FIXME: no translation domain */
298         if (!g_option_context_parse (option_ctx, &argc, &argv, NULL)
299             || option_size == -1
300             || option_input_filename == NULL
301             || option_output_filename == NULL) {
302                 g_printerr ("Invalid usage; type \"%s --help\" for instructions.  All the options must be used.\n", argv[0]);
303                 exit (EXIT_FAILURE);
304         }
305
306         gsf_init ();
307         read_thumbnail_and_write (option_input_filename, option_output_filename, option_size);
308
309         return 0;
310 }