Thumbnails for PostScript/PDF files



Reasoning that one can never have enough eye-candy I've experimented with 
adding thumbnail generation for ps and PDF files to libnautilus-private.

Attached is a patch against nautilus-2.0.0 (I have to use garnome rather than 
CVS I'm afraid).  It's also the first time I've sent a patch so I suspect the 
format I've used is less than ideal.

The patch invokes "gs" on any ps files it finds on local drives, attempting 
to create a temporary png of the first page of the document.  This gets 
loaded and scaled down to thumbnail-size.

The patch works (kind-of) but has numerous issues:
- probably not secure
- if the conversion fails the thumbnailing thread gets very confused, and 
tends to hang Nautilus
- it blocks the thumbnailing thread; so if the conversion takes a long time 
you won't get any other thumbnails for ages...  This is common for 
reasonable-sized input files.
- there probably ought to be a speed tradeoff option for this kind of thing 
given the impact on CPU load of running gs.
- it can work with PDF files as well, but I removed this as it failed too 
often (with the consequences as mentioned above)
- the resulting thumbnails don't look as good as I hoped they might.  They 
would probably look better if we embedded the image of the document content 
inside the frame that gets used for text files.  But I'm not sure how to do 
that robustly.
- gs spits out info to stdout (this can be suppressed by adding a "-dQUIET" 
when the command-line is generated, but it's useful for now for debugging)

Ultimately I'd like later versions of Nautilus to have an extensible system 
for generating thumbnails, with support for many different file formats (not 
just image files), and I hope this is a step in the right direction. If 
nothing else it highlights some pitfalls :-)

Any thoughts?

Dave
diff --recursive -x*.o -x*.server -U5 download/nautilus-2.0.0/libnautilus-private/nautilus-icon-factory.c work/nautilus-2.0.0/libnautilus-private/nautilus-icon-factory.c
--- download/nautilus-2.0.0/libnautilus-private/nautilus-icon-factory.c	Sun May 12 00:53:49 2002
+++ work/nautilus-2.0.0/libnautilus-private/nautilus-icon-factory.c	Thu Jul 18 19:54:46 2002
@@ -1199,10 +1199,22 @@
 	/* handle SVG files */
 	if (uri == NULL && icon_name == NULL
 	    && nautilus_file_is_mime_type (file, "image/svg")) {
 		uri = g_strdup (file_uri);
 	}
+
+	/* handle local ps  files */
+	if (uri == NULL && icon_name == NULL ) {
+		if (nautilus_file_is_mime_type (file, "application/postscript")) {
+			if (is_local) {
+				uri = nautilus_get_thumbnail_uri (file);
+				if (uri == NULL) {
+					icon_name = g_strdup (ICON_NAME_THUMBNAIL_LOADING);
+				}
+			}
+		}
+	}
 	
 	/* Get the generic icon set for this file. */
         g_free (file_uri);
         if (icon_name == NULL) {
 		icon_name = get_icon_name_for_file (file);
diff --recursive -x*.o -x*.server -U5 download/nautilus-2.0.0/libnautilus-private/nautilus-thumbnails.c work/nautilus-2.0.0/libnautilus-private/nautilus-thumbnails.c
--- download/nautilus-2.0.0/libnautilus-private/nautilus-thumbnails.c	Sat Jun  1 20:35:14 2002
+++ work/nautilus-2.0.0/libnautilus-private/nautilus-thumbnails.c	Fri Jul 19 13:42:55 2002
@@ -856,10 +856,129 @@
 	}
 	g_free (invalid_uri);
 }
 
 
+
+GdkPixbuf *
+nautilus_thumbnail_invoke_gs (const char *uri)
+{
+	/* This should only be called for local files.
+	   We create a filename within the temp directory.
+	   We then invoke "gs" on the uri, outputting the result as a 256 colour png to the
+	   temporary filename.
+	   Finally we load the png and delete the tempfile. */
+	int tmp_file_handle;
+	gchar *command_line, *local_path;
+	gchar *tmp_file = NULL;
+	GdkPixbuf *image = NULL;
+	gboolean success = FALSE;
+	
+
+#ifdef DEBUG_THUMBNAILS
+	g_message("(Thumbnail Thread) nautilus_thumbnail_invoke_gs (%s)\n",uri);
+#endif
+
+#if 1
+	/* Obtain a temporary filename */
+	/* FIXME: is this method secure? */
+	tmp_file_handle = g_file_open_tmp (NULL, &tmp_file, NULL);
+
+	if (-1 != tmp_file_handle) {
+		close(tmp_file_handle);
+	}
+#else
+	tmp_file = g_strdup("/home/david/temp.png");
+#endif
+
+	if (tmp_file) {
+#ifdef DEBUG_THUMBNAILS
+		g_message ("(Thumbnail Thread) tmp_file has name \"%s\"\n", tmp_file);
+#endif
+		
+		/* This function should only be called for local URIs, hence it should be possible to convert from a URI into a regular filename */
+		local_path = gnome_vfs_get_local_path_from_uri (uri);
+		
+#ifdef DEBUG_THUMBNAILS
+		g_message ("(Thumbnail Thread) local_path = \"%s\"\n", local_path);
+#endif
+		
+		
+		if (local_path) {
+			
+			/* The command line invokes gs with the following options:
+			   -sDEVICE=png256 specifies 256 colour png output.  I experimented with png16m but it took much longer and with no improvement in quality.
+			   -sOutputFile specifies the output filename
+			   There follows the input filename
+			*/
+			command_line = g_strdup_printf ("gs -dNOPAUSE -dBATCH -dSAFER -sDEVICE=png256 -sOutputFile=%s -dLastPage=1 %s",tmp_file, local_path);
+			
+#ifdef DEBUG_THUMBNAILS
+			g_message ("(Thumbnail Thread) command_line = \"%s\"\n", command_line);
+#endif
+			
+			/* FIXME: how much of a security risk is this? */
+			success = g_spawn_command_line_sync (command_line,
+							     NULL, /* gchar **standard_output */
+							     NULL, /* gchar **standard_error */
+							     NULL, /* gint *exit_status */
+							     NULL /* GError **error */);
+			
+			g_free (command_line);
+			g_free(local_path);
+		}
+		
+		if (success) {
+#ifdef DEBUG_THUMBNAILS
+			g_message ("(Thumbnail Thread) command_line was a success; trying to load = \"%s\"\n", tmp_file);
+#endif
+			
+			image = gdk_pixbuf_new_from_file (tmp_file, NULL);
+			
+#ifdef DEBUG_THUMBNAILS
+			if (image) {
+				g_message ("(Thumbnail Thread) image was loaded OK (%d wide by %d high)\n", gdk_pixbuf_get_width (image), gdk_pixbuf_get_height (image) );
+			} else {
+				g_message ("(Thumbnail Thread) image failed to load\n");
+			}
+
+			/* Explicitly delete the temp file to avoid filling obscure directories on the user's hard disk with large image files */
+			remove (tmp_file);
+#endif
+		} else {
+#ifdef DEBUG_THUMBNAILS
+			g_message ("(Thumbnail Thread) command_line was a failure\n");
+#endif
+		}
+
+		g_free (tmp_file);
+	}
+
+	return image;
+}
+
+GdkPixbuf *
+nautilus_thumbnail_load_ps (const char *uri)
+{
+	GdkPixbuf *image = NULL;
+
+#ifdef DEBUG_THUMBNAILS
+	g_message("(Thumbnail Thread) nautilus_thumbnail_load_ps (%s)\n",uri);
+#endif
+
+#if 1
+	image = nautilus_thumbnail_invoke_gs (uri);
+#else
+	image = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
+				64, 256);
+
+	gdk_pixbuf_fill (image, 0xff00ff);
+#endif
+
+	return image;
+}
+
 /* This creates one thumbnail in the thumbnail thread. */
 static void
 thumbnail_thread_make_thumbnail (NautilusThumbnailInfo *info)
 {
 	GdkPixbuf* full_size_image = NULL;
@@ -905,10 +1024,12 @@
 #ifdef HAVE_LIBJPEG
 	} else if (eel_strcasecmp (info->mime_type, "image/jpeg") == 0) {
 		full_size_image = nautilus_thumbnail_load_scaled_jpeg
 			(info->image_uri, 96, 96);
 #endif
+	} else if (eel_strcasecmp (info->mime_type, "application/postscript") == 0) {
+		full_size_image = nautilus_thumbnail_load_ps (info->image_uri);
 	} else {
 		full_size_image = eel_gdk_pixbuf_load (info->image_uri);
 	}
 			
 	/* If we have managed to create a pixbuf from the image, scale it to


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