/* GNOME thumbnailer for Office files * Copyright (C) 2005 Novell, Inc. * * Authors: * Federico Mena-Quintero * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2.1 of the GNU Lesser General Public * License as published by the Free Software Foundation. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 * USA */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SETRLIMI #include #endif G_GNUC_NORETURN static void show_error_string_and_exit (const char *str) { g_printerr ("error: %s\n", str); exit (EXIT_FAILURE); } G_GNUC_NORETURN static void show_error_and_exit (GError *error) { if (error) show_error_string_and_exit (error->message); else show_error_string_and_exit ("an error happened, and we didn't get a GError. Exiting."); } static void call_convert (const char *in_filename, const char *out_filename, int thumb_size) { char *in_quote; char *out_quote; char *cmd_line; GError *error; gint exit_status; in_quote = g_shell_quote (in_filename); out_quote = g_shell_quote (out_filename); cmd_line = g_strdup_printf ("convert %s +matte -thumbnail %dx%d png:%s", in_quote, thumb_size, thumb_size, out_quote); g_printerr ("calling %s\n", cmd_line); g_free (in_quote); g_free (out_quote); error = NULL; if (!g_spawn_command_line_sync (cmd_line, NULL, NULL, &exit_status, &error)) show_error_and_exit (error); g_free (cmd_line); } static void write_thumbnail (const char *filename, gconstpointer data, gsize size, int thumb_size) { char *tmp_name; int fd; FILE *file; tmp_name = g_strdup_printf ("%s.XXXXXX", filename); fd = g_mkstemp (tmp_name); if (fd == -1) { perror ("Could not create temporary file"); exit (EXIT_FAILURE); } file = fdopen (fd, "w"); if (!file) { show_error_string_and_exit ("Could not open temporary file for writing"); exit (EXIT_FAILURE); } if (fwrite (data, 1, size, file) != size) { perror ("Could not write data to output file"); exit (EXIT_FAILURE); } if (fclose (file) != 0) { perror ("Could not close oputput file"); exit (EXIT_FAILURE); } call_convert (tmp_name, filename, thumb_size); unlink (tmp_name); } static void zip_thumbnail (GsfInfile *infile, const char *out_filename, int thumb_size) { GsfInput *thumbnail; /* Office Document thumbnail */ if (NULL != (thumbnail = gsf_infile_child_by_vname (infile, "Thumbnails", "thumbnail.png", NULL))) { gsf_off_t len = gsf_input_remaining (thumbnail); guint8 const *data = gsf_input_read (thumbnail, len, NULL); write_thumbnail (out_filename, data, len, thumb_size); g_object_unref (thumbnail); /* Check MS Office Open thumbnail */ } else show_error_string_and_exit ("Could not find thumbnail in zip file"); } static void msole_thumbnail (GsfInfile *infile, const char *out_filename, int thumb_size) { GsfInput *summary_stream; GsfDocMetaData *meta_data; GsfDocProp *thumb_doc_prop; GValue const *thumb_value; GsfClipData *clip_data; GsfClipFormat clip_format; gconstpointer data; gsize size; GError *error; summary_stream = gsf_infile_child_by_name (infile, "\05SummaryInformation"); if (!summary_stream) show_error_string_and_exit ("Could not find the SummaryInformation stream"); meta_data = gsf_doc_meta_data_new (); error = gsf_msole_metadata_read (summary_stream, meta_data); if (error) show_error_and_exit (error); thumb_doc_prop = gsf_doc_meta_data_lookup (meta_data, GSF_META_NAME_THUMBNAIL); if (!thumb_doc_prop) show_error_string_and_exit ("The metadata does not have a thumbnail property"); thumb_value = gsf_doc_prop_get_val (thumb_doc_prop); if (!thumb_value) show_error_string_and_exit ("We got the thumbnail property, but it didn't have a value!?"); clip_data = GSF_CLIP_DATA (g_value_get_object (thumb_value)); clip_format = gsf_clip_data_get_format (clip_data); if (clip_format == GSF_CLIP_FORMAT_WINDOWS_CLIPBOARD) { GsfClipFormatWindows win_format; error = NULL; win_format = gsf_clip_data_get_windows_clipboard_format (clip_data, &error); if (win_format == GSF_CLIP_FORMAT_WINDOWS_ERROR) show_error_and_exit (error); } error = NULL; data = gsf_clip_data_peek_real_data (clip_data, &size, &error); if (!data) show_error_and_exit (error); write_thumbnail (out_filename, data, size, thumb_size); g_object_unref (clip_data); g_object_unref (meta_data); g_object_unref (summary_stream); } static void read_thumbnail_and_write (const char *in_filename, const char *out_filename, int thumb_size) { GsfInput *input; GsfInfile *infile; GError *error; input = gsf_input_mmap_new (in_filename, NULL); if (!input) { error = NULL; input = gsf_input_stdio_new (in_filename, &error); if (!input) show_error_and_exit (error); } input = gsf_input_uncompress (input); error = NULL; if (NULL != (infile = gsf_infile_msole_new (input, &error))) msole_thumbnail (infile, out_filename, thumb_size); else if (NULL != (infile = gsf_infile_zip_new (input, &error))) zip_thumbnail (infile, out_filename, thumb_size); else show_error_and_exit (error); g_object_unref (infile); g_object_unref (input); } #define MAX_HELPER_MEMORY (256 * 1024 * 1024) /* 256 MB */ #define MAX_HELPER_SECONDS (5) /* 5 seconds */ static void set_resource_limits (void) { #ifdef HAVE_SETRLIMI struct rlimit limit; /* We call convert(1) from ImageMagick, which is especially scary when converting * WMF thumbnails into PNGs. Convert(1) is known to leak tons of memory and CPU * time on certain WMFs. So, we'll put a cap on how much resources it can use. */ limit.rlim_cur = MAX_HELPER_MEMORY; limit.rlim_max = MAX_HELPER_MEMORY; setrlimit (RLIMIT_AS, &limit); limit.rlim_cur = MAX_HELPER_SECONDS; limit.rlim_max = MAX_HELPER_SECONDS; setrlimit (RLIMIT_CPU, &limit); #endif } /* Command-line options */ static int option_size = -1; static char *option_input_filename = NULL; static char *option_output_filename = NULL; static GOptionEntry option_entries[] = { { "input", 'i', 0, G_OPTION_ARG_FILENAME, &option_input_filename, "Name of file for which to create a thumbnail", "filename" }, { "output", 'o', 0, G_OPTION_ARG_FILENAME, &option_output_filename, "Name of the file to put the thumbnail", "filename" }, { "size", 's', 0, G_OPTION_ARG_INT, &option_size, "Size of thumbnail in pixels; the thumbnail will be at most N*N pixels large", "N" }, { NULL, 0, 0, 0, NULL, NULL, NULL } }; int main (int argc, char **argv) { GOptionContext *option_ctx; set_resource_limits (); option_ctx = g_option_context_new ("Options"); g_option_context_add_main_entries (option_ctx, option_entries, NULL); /* FIXME: no translation domain */ if (!g_option_context_parse (option_ctx, &argc, &argv, NULL) || option_size == -1 || option_input_filename == NULL || option_output_filename == NULL) { g_printerr ("Invalid usage; type \"%s --help\" for instructions. All the options must be used.\n", argv[0]); exit (EXIT_FAILURE); } gsf_init (); read_thumbnail_and_write (option_input_filename, option_output_filename, option_size); return 0; }