[cairo-commit] boilerplate/cairo-boilerplate.c boilerplate/cairo-boilerplate.h boilerplate/cairo-boilerplate-pdf.c boilerplate/cairo-boilerplate-svg.c configure.in test/any2ppm.c test/.gitignore test/Makefile.am

Chris Wilson ickle at kemper.freedesktop.org
Tue Aug 19 03:15:53 PDT 2008


 boilerplate/cairo-boilerplate-pdf.c |   63 ++-
 boilerplate/cairo-boilerplate-svg.c |   63 ++-
 boilerplate/cairo-boilerplate.c     |  141 ++++++++
 boilerplate/cairo-boilerplate.h     |    7 
 configure.in                        |    9 
 test/.gitignore                     |    3 
 test/Makefile.am                    |    7 
 test/any2ppm.c                      |  594 ++++++++++++++++++++++++++++++++++++
 8 files changed, 851 insertions(+), 36 deletions(-)

New commits:
commit 776844eb9e4b0eb70621242212d732dfefcb6d8e
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Aug 18 18:50:00 2008 +0100

    [boilerplate] Daemonic conversion utility.
    
    In order to achieve substantial speed improvements the external conversion
    utilities are rewritten as a daemon that communicates with the test suite
    over a local socket. This is faster as it avoids the libtool and dynamic
    linker overhead for each invocation, the caches persist between tests and
    we no longer require a round trip through libpng.
    
    The daemon is started automatically by the test suite and if communication
    cannot be established then it falls back to using a pipe to a normal
    conversion utility. The daemon will then persist for 60 seconds waiting
    for further connections.
    
    Of course any memory leak (stares at poppler) is exacerbated.

diff --git a/boilerplate/cairo-boilerplate-pdf.c b/boilerplate/cairo-boilerplate-pdf.c
index 791951b..b0d9571 100644
--- a/boilerplate/cairo-boilerplate-pdf.c
+++ b/boilerplate/cairo-boilerplate-pdf.c
@@ -107,13 +107,11 @@ _cairo_boilerplate_pdf_create_surface (const char		 *name,
     return surface;
 }
 
-cairo_status_t
-_cairo_boilerplate_pdf_surface_write_to_png (cairo_surface_t *surface, const char *filename)
+static cairo_status_t
+_cairo_boilerplate_pdf_finish (pdf_target_closure_t	*ptc,
+			       cairo_surface_t		*surface)
 {
-    pdf_target_closure_t *ptc = cairo_surface_get_user_data (surface, &pdf_closure_key);
-    char    command[4096];
     cairo_status_t status;
-    int exitstatus;
 
     /* Both surface and ptc->target were originally created at the
      * same dimensions. We want a 1:1 copy here, so we first clear any
@@ -149,6 +147,21 @@ _cairo_boilerplate_pdf_surface_write_to_png (cairo_surface_t *surface, const cha
     if (status)
 	return status;
 
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_boilerplate_pdf_surface_write_to_png (cairo_surface_t *surface, const char *filename)
+{
+    pdf_target_closure_t *ptc = cairo_surface_get_user_data (surface, &pdf_closure_key);
+    char    command[4096];
+    cairo_status_t status;
+    int exitstatus;
+
+    status = _cairo_boilerplate_pdf_finish (ptc, surface);
+    if (status)
+	return status;
+
     sprintf (command, "./pdf2png %s %s 1",
 	     ptc->filename, filename);
 
@@ -163,28 +176,42 @@ _cairo_boilerplate_pdf_surface_write_to_png (cairo_surface_t *surface, const cha
     return CAIRO_STATUS_SUCCESS;
 }
 
-cairo_surface_t *
-_cairo_boilerplate_pdf_get_image_surface (cairo_surface_t *surface,
-					  int width,
-					  int height)
+static cairo_surface_t *
+_cairo_boilerplate_pdf_convert_to_image (cairo_surface_t *surface)
 {
     pdf_target_closure_t *ptc = cairo_surface_get_user_data (surface,
 							     &pdf_closure_key);
-    char *filename;
     cairo_status_t status;
+    FILE *file;
+    cairo_surface_t *image;
 
-    xasprintf (&filename, "%s.png", ptc->filename);
-    status = _cairo_boilerplate_pdf_surface_write_to_png (surface, filename);
+    status = _cairo_boilerplate_pdf_finish (ptc, surface);
     if (status)
 	return cairo_boilerplate_surface_create_in_error (status);
 
-    surface = cairo_boilerplate_get_image_surface_from_png (filename,
-							    width,
-							    height,
-							    ptc->target == NULL);
+    file = cairo_boilerplate_open_any2ppm (ptc->filename, 1);
+    if (file == NULL)
+	return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_READ_ERROR);
+
+    image = cairo_boilerplate_image_surface_create_from_ppm_stream (file);
+    fclose (file);
 
-    remove (filename);
-    free (filename);
+    return image;
+}
+
+cairo_surface_t *
+_cairo_boilerplate_pdf_get_image_surface (cairo_surface_t *surface,
+					  int width,
+					  int height)
+{
+    cairo_surface_t *image;
+
+    image = _cairo_boilerplate_pdf_convert_to_image (surface);
+    cairo_surface_set_device_offset (image,
+				     cairo_image_surface_get_width (image) - width,
+				     cairo_image_surface_get_height (image) - height);
+    surface = _cairo_boilerplate_get_image_surface (image, width, height);
+    cairo_surface_destroy (image);
 
     return surface;
 }
diff --git a/boilerplate/cairo-boilerplate-svg.c b/boilerplate/cairo-boilerplate-svg.c
index afa5634..80354ef 100644
--- a/boilerplate/cairo-boilerplate-svg.c
+++ b/boilerplate/cairo-boilerplate-svg.c
@@ -100,13 +100,11 @@ _cairo_boilerplate_svg_create_surface (const char		 *name,
     return surface;
 }
 
-cairo_status_t
-_cairo_boilerplate_svg_surface_write_to_png (cairo_surface_t *surface, const char *filename)
+static cairo_status_t
+_cairo_boilerplate_svg_finish (svg_target_closure_t	*ptc,
+			       cairo_surface_t		*surface)
 {
-    svg_target_closure_t *ptc = cairo_surface_get_user_data (surface, &svg_closure_key);
-    char    command[4096];
     cairo_status_t status;
-    int exitstatus;
 
     /* Both surface and ptc->target were originally created at the
      * same dimensions. We want a 1:1 copy here, so we first clear any
@@ -142,6 +140,21 @@ _cairo_boilerplate_svg_surface_write_to_png (cairo_surface_t *surface, const cha
     if (status)
 	return status;
 
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_boilerplate_svg_surface_write_to_png (cairo_surface_t *surface, const char *filename)
+{
+    svg_target_closure_t *ptc = cairo_surface_get_user_data (surface, &svg_closure_key);
+    char    command[4096];
+    cairo_status_t status;
+    int exitstatus;
+
+    status = _cairo_boilerplate_svg_finish (ptc, surface);
+    if (status)
+	return status;
+
     sprintf (command, "./svg2png %s %s",
 	     ptc->filename, filename);
 
@@ -156,28 +169,42 @@ _cairo_boilerplate_svg_surface_write_to_png (cairo_surface_t *surface, const cha
     return CAIRO_STATUS_SUCCESS;
 }
 
-cairo_surface_t *
-_cairo_boilerplate_svg_get_image_surface (cairo_surface_t *surface,
-					  int width,
-					  int height)
+static cairo_surface_t *
+_cairo_boilerplate_svg_convert_to_image (cairo_surface_t *surface)
 {
     svg_target_closure_t *ptc = cairo_surface_get_user_data (surface,
 							     &svg_closure_key);
-    char *filename;
     cairo_status_t status;
+    FILE *file;
+    cairo_surface_t *image;
 
-    xasprintf (&filename, "%s.png", ptc->filename);
-    status = _cairo_boilerplate_svg_surface_write_to_png (surface, filename);
+    status = _cairo_boilerplate_svg_finish (ptc, surface);
     if (status)
 	return cairo_boilerplate_surface_create_in_error (status);
 
-    surface = cairo_boilerplate_get_image_surface_from_png (filename,
-							    width,
-							    height,
-							    FALSE);
+    file = cairo_boilerplate_open_any2ppm (ptc->filename, 0);
+    if (file == NULL)
+	return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_READ_ERROR);
+
+    image = cairo_boilerplate_image_surface_create_from_ppm_stream (file);
+    fclose (file);
 
-    remove (filename);
-    free (filename);
+    return image;
+}
+
+cairo_surface_t *
+_cairo_boilerplate_svg_get_image_surface (cairo_surface_t *surface,
+					  int width,
+					  int height)
+{
+    cairo_surface_t *image;
+
+    image = _cairo_boilerplate_svg_convert_to_image (surface);
+    cairo_surface_set_device_offset (image,
+				     cairo_image_surface_get_width (image) - width,
+				     cairo_image_surface_get_height (image) - height);
+    surface = _cairo_boilerplate_get_image_surface (image, width, height);
+    cairo_surface_destroy (image);
 
     return surface;
 }
diff --git a/boilerplate/cairo-boilerplate.c b/boilerplate/cairo-boilerplate.c
index 0ddcde5..20cf0c9 100644
--- a/boilerplate/cairo-boilerplate.c
+++ b/boilerplate/cairo-boilerplate.c
@@ -68,6 +68,18 @@
 #include <ctype.h>
 #include <assert.h>
 
+#if HAVE_UNISTD_H && HAVE_FCNTL_H && HAVE_SIGNAL_H && HAVE_SYS_STAT_H && HAVE_SYS_SOCKET_H && HAVE_SYS_UN_H
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#define HAS_DAEMON 1
+#define SOCKET_PATH "./.any2ppm"
+#endif
+
 cairo_content_t
 cairo_boilerplate_content (cairo_content_t content)
 {
@@ -620,3 +632,132 @@ cairo_boilerplate_scaled_font_set_max_glyphs_cached (cairo_scaled_font_t *scaled
 
     scaled_font->glyphs->max_size = max_glyphs;
 }
+
+#if HAS_DAEMON
+static int
+any2ppm_daemon_exists (void)
+{
+    struct stat st;
+    int fd;
+    char buf[80];
+    int pid;
+    int ret;
+
+    if (stat (SOCKET_PATH, &st) < 0)
+	return 0;
+
+    fd = open (SOCKET_PATH ".pid", O_RDONLY);
+    if (fd < 0)
+	return 0;
+
+    pid = 0;
+    ret = read (fd, buf, sizeof (buf) - 1);
+    if (ret > 0) {
+	buf[ret] = '\0';
+	pid = atoi (buf);
+    }
+    close (fd);
+
+    return pid > 0 && kill (pid, 0) != -1;
+}
+#endif
+
+FILE *
+cairo_boilerplate_open_any2ppm (const char *filename,
+				int page)
+{
+    char command[4096];
+#if HAS_DAEMON
+    int sk;
+    struct sockaddr_un addr;
+    int len;
+
+    if (! any2ppm_daemon_exists ()) {
+	if (system ("./any2ppm") != 0)
+	    goto POPEN;
+    }
+
+    sk = socket (PF_UNIX, SOCK_STREAM, 0);
+    if (sk == -1)
+	goto POPEN;
+
+    memset (&addr, 0, sizeof (addr));
+    addr.sun_family = AF_UNIX;
+    strcpy (addr.sun_path, SOCKET_PATH);
+
+    if (connect (sk, (struct sockaddr *) &addr, sizeof (addr)) == -1) {
+	close (sk);
+	goto POPEN;
+    }
+
+    len = sprintf (command, "%s %d\n", filename, page);
+    if (write (sk, command, len) != len) {
+	close (sk);
+	goto POPEN;
+    }
+
+    return fdopen (sk, "r");
+
+POPEN:
+#endif
+    sprintf (command, "./any2ppm %s %d", filename, page);
+    return popen (command, "r");
+}
+
+cairo_surface_t *
+cairo_boilerplate_image_surface_create_from_ppm_stream (FILE *file)
+{
+    char format;
+    int width, height, stride;
+    int x, y;
+    unsigned char *data;
+    cairo_surface_t *image = NULL;
+
+    if (fscanf (file, "P%c %d %d 255\n", &format, &width, &height) != 3)
+	goto FAIL;
+
+    switch (format) {
+    case '7': /* XXX */
+	image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+	break;
+    case '6':
+	image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
+	break;
+    case '5':
+	image = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
+	break;
+    default:
+	goto FAIL;
+    }
+    if (cairo_surface_status (image))
+	goto FAIL;
+
+    data = cairo_image_surface_get_data (image);
+    stride = cairo_image_surface_get_stride (image);
+    for (y = 0; y < height; y++) {
+	unsigned char *buf = data + y *stride;
+	switch (format) {
+	case '7':
+	    if (fread (buf, 4, width, file) != (size_t) width)
+		goto FAIL;
+	    break;
+	case '6':
+	    for (x = 0; x < width; x++) {
+		if (fread (buf, 1, 3, file) != 3)
+		    goto FAIL;
+		buf += 4;
+	    }
+	    break;
+	case '5':
+	    if (fread (buf, 1, width, file) != (size_t) width)
+		goto FAIL;
+	    break;
+	}
+    }
+
+    return image;
+
+FAIL:
+    cairo_surface_destroy (image);
+    return cairo_boilerplate_surface_create_in_error (CAIRO_STATUS_READ_ERROR);
+}
diff --git a/boilerplate/cairo-boilerplate.h b/boilerplate/cairo-boilerplate.h
index ccd30ff..844eed9 100644
--- a/boilerplate/cairo-boilerplate.h
+++ b/boilerplate/cairo-boilerplate.h
@@ -169,6 +169,13 @@ cairo_boilerplate_get_image_surface_from_png (const char *filename,
 cairo_surface_t *
 cairo_boilerplate_surface_create_in_error (cairo_status_t status);
 
+FILE *
+cairo_boilerplate_open_any2ppm (const char *filename,
+				int page);
+
+cairo_surface_t *
+cairo_boilerplate_image_surface_create_from_ppm_stream (FILE *file);
+
 #include "xmalloc.h"
 
 #endif
diff --git a/configure.in b/configure.in
index c8ab95c..2e4ce97 100644
--- a/configure.in
+++ b/configure.in
@@ -733,6 +733,12 @@ AC_SUBST(LIBRSVG_CFLAGS)
 AC_SUBST(LIBRSVG_LIBS)
 
 dnl ===========================================================================
+dnl Build the external converter if we have any of the test backends
+AM_CONDITIONAL(BUILD_ANY2PPM,
+	       test "x$test_svg" = "xyes" \
+	         -o "x$test_pdf" = "xyes" ) # -o "x$test_ps"  = "xyes")
+
+dnl ===========================================================================
 dnl dump backend checking results
 AC_CACHE_SAVE
 
@@ -741,6 +747,9 @@ dnl Checks for precise integer types
 AC_CHECK_HEADERS([stdint.h signal.h setjmp.h inttypes.h sys/int_types.h])
 AC_CHECK_TYPES([uint64_t, uint128_t])
 
+dnl Check for socket support for any2ppm daemon
+AC_CHECK_HEADERS([fcntl.h unistd.h signal.h sys/stat.h sys/socket.h sys/poll.h sys/un.h])
+
 dnl ===========================================================================
 dnl check for CPU affinity support
 AC_CHECK_HEADERS([sched.h], [
diff --git a/test/.gitignore b/test/.gitignore
index a0c275e..8590d8f 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -9,6 +9,9 @@ a1-image-sample
 a1-mask
 a1-traps-sample
 a8-mask
+any2ppm
+.any2ppm
+.any2ppm.pid
 big-line
 big-trap
 bilevel-image
diff --git a/test/Makefile.am b/test/Makefile.am
index dd1cb90..aa7a03b 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -869,6 +869,13 @@ endif
 
 check_PROGRAMS += imagediff png-flatten
 
+if BUILD_ANY2PPM
+check_PROGRAMS += any2ppm
+any2ppm_CFLAGS = $(POPPLER_CFLAGS) $(LIBRSVG_CFLAGS)
+# add LDADD, so poppler/librsvg uses "our" cairo
+any2ppm_LDADD  = $(LDADD) $(POPPLER_LIBS) $(LIBRSVG_LIBS)
+endif
+
 if CAIRO_CAN_TEST_PDF_SURFACE
 check_PROGRAMS += pdf2png
 pdf2png_CFLAGS = $(POPPLER_CFLAGS)
diff --git a/test/any2ppm.c b/test/any2ppm.c
new file mode 100644
index 0000000..5fe8b9e
--- /dev/null
+++ b/test/any2ppm.c
@@ -0,0 +1,594 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris at chris-wilson.co.uk>
+ *
+ * Adapted from pdf2png.c:
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Kristian Høgsberg <krh at redhat.com>
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <cairo.h>
+
+#if CAIRO_CAN_TEST_PDF_SURFACE
+#include <poppler.h>
+#endif
+
+#if CAIRO_CAN_TEST_SVG_SURFACE
+#include <librsvg/rsvg.h>
+#include <librsvg/rsvg-cairo.h>
+#endif
+
+#if CAIRO_CAN_TEST_PS_SURFACE
+#endif
+
+#if HAVE_FCNTL_H && HAVE_SIGNAL_H && HAVE_SYS_STAT_H && HAVE_SYS_SOCKET_H && HAVE_SYS_POLL_H && HAVE_SYS_UN_H
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <sys/un.h>
+
+#define SOCKET_PATH "./.any2ppm"
+#define TIMEOUT 60000 /* 60 seconds */
+
+#define CAN_RUN_AS_DAEMON 1
+#endif
+
+#define ARRAY_LENGTH(A) (sizeof (A) / sizeof (A[0]))
+
+static int
+_write (int fd,
+	char *buf, int maxlen, int buflen,
+	const unsigned char *src, int srclen)
+{
+    if (buflen < 0)
+	return buflen;
+
+    while (srclen) {
+	int len;
+
+	len = buflen + srclen;
+	if (len > maxlen)
+	    len = maxlen;
+	len -= buflen;
+
+	memcpy (buf + buflen, src, len);
+	buflen += len;
+	srclen -= len;
+	src += len;
+
+	if (buflen == maxlen) {
+	    if (write (fd, buf, maxlen) != maxlen)
+		return -1;
+	    buflen = 0;
+	}
+    }
+
+    return buflen;
+}
+
+static const char *
+write_ppm (cairo_surface_t *surface, int fd)
+{
+    char buf[4096];
+    cairo_format_t format;
+    const char *format_str;
+    const unsigned char *data;
+    int len;
+    int width, height, stride;
+    int i, j;
+
+    format = cairo_image_surface_get_format (surface);
+    switch (format) {
+    case CAIRO_FORMAT_ARGB32:
+	/* XXX need true alpha for svg */
+	format_str = "P7";
+	break;
+    case CAIRO_FORMAT_RGB24:
+	format_str = "P6";
+	break;
+    case CAIRO_FORMAT_A8:
+	format_str = "P5";
+	break;
+    case CAIRO_FORMAT_A1:
+    default:
+	return "unhandled image format";
+    }
+
+    data = cairo_image_surface_get_data (surface);
+    height = cairo_image_surface_get_height (surface);
+    width = cairo_image_surface_get_width (surface);
+    stride = cairo_image_surface_get_stride (surface);
+
+    len = sprintf (buf, "%s %d %d 255\n", format_str, width, height);
+    for (j = 0; j < height; j++) {
+	const unsigned char *row = data + stride * j;
+
+	switch ((int) format) {
+	case CAIRO_FORMAT_ARGB32:
+	    len = _write (fd,
+			  buf, sizeof (buf), len,
+			  row, 4 * width);
+	    break;
+	case CAIRO_FORMAT_RGB24:
+	    for (i = 0; i < width; i++) {
+		len = _write (fd,
+			      buf, sizeof (buf), len,
+			      row, 3);
+		row += 4;
+	    }
+	    break;
+	case CAIRO_FORMAT_A8:
+	    len = _write (fd,
+			  buf, sizeof (buf), len,
+			  row, width);
+	    break;
+	}
+	if (len < 0)
+	    return "write failed";
+    }
+
+    if (len) {
+	if (write (fd, buf, len) != len)
+	    return "write failed";
+    }
+
+    return NULL;
+}
+
+#if CAIRO_CAN_TEST_PDF_SURFACE
+/* adapted from pdf2png.c */
+static const char *
+_poppler_render_page (const char *filename,
+		      const char *page_label,
+		      cairo_surface_t **surface_out)
+{
+    PopplerDocument *document;
+    PopplerPage *page;
+    double width, height;
+    GError *error = NULL;
+    gchar *absolute, *uri;
+    cairo_surface_t *surface;
+    cairo_t *cr;
+    cairo_status_t status;
+
+    if (g_path_is_absolute (filename)) {
+	absolute = g_strdup (filename);
+    } else {
+	gchar *dir = g_get_current_dir ();
+	absolute = g_build_filename (dir, filename, (gchar *) 0);
+	g_free (dir);
+    }
+
+    uri = g_filename_to_uri (absolute, NULL, &error);
+    g_free (absolute);
+    if (uri == NULL)
+	return error->message; /* XXX g_error_free (error) */
+
+    document = poppler_document_new_from_file (uri, NULL, &error);
+    g_free (uri);
+    if (document == NULL)
+	return error->message; /* XXX g_error_free (error) */
+
+    page = poppler_document_get_page_by_label (document, page_label);
+    g_object_unref (document);
+    if (page == NULL)
+	return "page not found";
+
+    poppler_page_get_size (page, &width, &height);
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
+    cr = cairo_create (surface);
+
+    cairo_set_source_rgb (cr, 1., 1., 1.);
+    cairo_paint (cr);
+
+    poppler_page_render (page, cr);
+    g_object_unref (page);
+
+    status = cairo_status (cr);
+    cairo_destroy (cr);
+
+    if (status) {
+	cairo_surface_destroy (surface);
+	return  cairo_status_to_string (status);
+    }
+
+    *surface_out = surface;
+    return NULL;
+}
+
+static const char *
+pdf_convert (char **argv, int fd)
+{
+    const char *err;
+    cairo_surface_t *surface = NULL; /* silence compiler warning */
+
+    err = _poppler_render_page (argv[0], argv[1], &surface);
+    if (err != NULL)
+	return err;
+
+    err = write_ppm (surface, fd);
+    cairo_surface_destroy (surface);
+
+    return err;
+}
+#endif
+
+#if CAIRO_CAN_TEST_SVG_SURFACE
+static const char *
+_rsvg_render_page (const char *filename,
+		   cairo_surface_t **surface_out)
+{
+    RsvgHandle *handle;
+    RsvgDimensionData dimensions;
+    GError *error = NULL;
+    cairo_surface_t *surface;
+    cairo_t *cr;
+    cairo_status_t status;
+
+    handle = rsvg_handle_new_from_file (filename, &error);
+    if (handle == NULL)
+	return error->message; /* XXX g_error_free */
+
+    rsvg_handle_get_dimensions (handle, &dimensions);
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+					  dimensions.width,
+					  dimensions.height);
+    cr = cairo_create (surface);
+
+    rsvg_handle_render_cairo (handle, cr);
+    g_object_unref (handle);
+
+    status = cairo_status (cr);
+    cairo_destroy (cr);
+
+    if (status) {
+	cairo_surface_destroy (surface);
+	return  cairo_status_to_string (status);
+    }
+
+    *surface_out = surface;
+    return NULL;
+}
+
+static const char *
+svg_convert (char **argv, int fd)
+{
+    const char *err;
+    cairo_surface_t *surface = NULL; /* silence compiler warning */
+
+    err = _rsvg_render_page (argv[0], &surface);
+    if (err != NULL)
+	return err;
+
+    err = write_ppm (surface, fd);
+    cairo_surface_destroy (surface);
+
+    return err;
+}
+#endif
+
+#if CAIRO_CAN_TEST_PS_SURFACE
+static const char *
+ps_convert (char **argv, int fd)
+{
+    /* XXX libspectre */
+
+    return "no method to convert PS";
+}
+#endif
+
+static const char *
+convert (char **argv, int fd)
+{
+    static const struct converter {
+	const char *type;
+	const char *(*func) (char **, int);
+    } converters[] = {
+#if CAIRO_CAN_TEST_PDF_SURFACE
+	{ "pdf", pdf_convert },
+#endif
+#if CAIRO_CAN_TEST_PS_SURFACE
+	{ "ps", ps_convert },
+#endif
+#if CAIRO_CAN_TEST_SVG_SURFACE
+	{ "svg", svg_convert },
+#endif
+	{ NULL, NULL }
+    };
+    const struct converter *converter = converters;
+    char *type;
+
+    type = strchr (argv[0], '.');
+    if (type == NULL)
+	return "no file extension";
+    type++;
+
+    while (converter->type) {
+	if (strcmp (type, converter->type) == 0)
+	    return converter->func (argv, fd);
+	converter++;
+    }
+    return "no converter";
+}
+
+#if CAN_RUN_AS_DAEMON
+static int
+_getline (int fd, char **linep, size_t *lenp)
+{
+    char *line;
+    size_t len, i;
+    ssize_t ret;
+
+    line = *linep;
+    if (line == NULL) {
+	line = malloc (1024);
+	if (line == NULL)
+	    return -1;
+	line[0] = '\0';
+	len = 1024;
+    } else
+	len = *lenp;
+
+    /* XXX simple, but ugly! */
+    i = 0;
+    do {
+	if (i == len - 1) {
+	    char *nline;
+
+	    nline = realloc (line, len + 1024);
+	    if (nline == NULL)
+		goto out;
+
+	    line = nline;
+	    len += 1024;
+	}
+
+	ret = read (fd, line + i, 1);
+	if (ret == -1 || ret == 0)
+	    goto out;
+    } while (line[i++] != '\n');
+
+out:
+    line[i] = '\0';
+    *linep = line;
+    *lenp = len;
+    return i-1;
+}
+
+static int
+split_line (char *line, char *argv[], int max_argc)
+{
+    int i = 0;
+
+    max_argc--; /* leave one spare for the trailing NULL */
+
+    argv[i++] = line;
+    while (i < max_argc && (line = strchr (line, ' ')) != NULL) {
+	*line++ = '\0';
+	argv[i++] = line;
+    }
+
+    /* chomp the newline */
+    line = strchr (argv[i-1], '\n');
+    if (line != NULL)
+	*line = '\0';
+
+    argv[i] = NULL;
+
+    return i;
+}
+
+static int
+any2ppm_daemon_exists (void)
+{
+    struct stat st;
+    int fd;
+    char buf[80];
+    int pid;
+    int ret;
+
+    if (stat (SOCKET_PATH, &st) < 0)
+	return 0;
+
+    fd = open (SOCKET_PATH ".pid", O_RDONLY);
+    if (fd < 0)
+	return 0;
+
+    pid = 0;
+    ret = read (fd, buf, sizeof (buf) - 1);
+    if (ret > 0) {
+	buf[ret] = '\0';
+	pid = atoi (buf);
+    }
+    close (fd);
+
+    return pid > 0 && kill (pid, 0) == 0;
+}
+
+static int
+write_pid_file (void)
+{
+    int fd;
+    char buf[80];
+    int ret;
+
+    fd = open (SOCKET_PATH ".pid", O_CREAT | O_TRUNC | O_WRONLY, 0666);
+    if (fd < 0)
+	return 0;
+
+    ret = sprintf (buf, "%d\n", getpid ());
+    ret = write (fd, buf, ret) == ret;
+    close (fd);
+
+    return ret;
+}
+
+static const char *
+any2ppm_daemon (void)
+{
+    int timeout = TIMEOUT;
+    struct pollfd pfd;
+    int sk, fd;
+    long flags;
+    struct sockaddr_un addr;
+    char *line = NULL;
+    size_t len = 0;
+
+#ifdef SIGPIPE
+    signal (SIGPIPE, SIG_IGN);
+#endif
+
+    /* XXX racy! */
+    if (getenv ("ANY2PPM_FORCE") == NULL && any2ppm_daemon_exists ())
+	return "any2ppm daemon already running";
+
+    unlink (SOCKET_PATH);
+
+    sk = socket (PF_UNIX, SOCK_STREAM, 0);
+    if (sk == -1)
+	return "unable to create socket";
+
+    memset (&addr, 0, sizeof (addr));
+    addr.sun_family = AF_UNIX;
+    strcpy (addr.sun_path, SOCKET_PATH);
+    if (bind (sk, (struct sockaddr *) &addr, sizeof (addr)) == -1) {
+	close (sk);
+	return "unable to bind socket";
+    }
+
+    flags = fcntl (sk, F_GETFL);
+    if (flags == -1 || fcntl (sk, F_SETFL, flags | O_NONBLOCK) == -1) {
+	close (sk);
+	return "unable to set socket to non-blocking";
+    }
+
+    if (listen (sk, 5) == -1) {
+	close (sk);
+	return "unable to listen on socket";
+    }
+
+    /* ready for client connection - detach from parent/terminal */
+    if (getenv ("ANY2PPM_NODAEMON") == NULL && daemon (1, 0) == -1) {
+	close (sk);
+	return "unable to detach from parent";
+    }
+
+    if (! write_pid_file ()) {
+	close (sk);
+	return "unable to write pid file";
+    }
+
+    if (getenv ("ANY2PPM_TIMEOUT") != NULL) {
+	timeout = atoi (getenv ("ANY2PPM_TIMEOUT"));
+	if (timeout == 0)
+	    timeout = -1;
+	if (timeout > 0)
+	    timeout *= 1000; /* convert env (in seconds) to milliseconds */
+    }
+
+    pfd.fd = sk;
+    pfd.events = POLLIN;
+    pfd.revents = 0; /* valgrind */
+    while (poll (&pfd, 1, timeout) > 0) {
+	while ((fd = accept (sk, NULL, NULL)) != -1) {
+	    if (_getline (fd, &line, &len) != -1) {
+		char *argv[10];
+
+		if (split_line (line, argv, ARRAY_LENGTH (argv)) > 0)
+		    convert (argv, fd);
+	    }
+	    close (fd);
+	}
+    }
+    close (sk);
+    unlink (SOCKET_PATH);
+    unlink (SOCKET_PATH ".pid");
+
+    free (line);
+    return NULL;
+}
+#else
+static const char *
+any2ppm_daemon (void)
+{
+    return "daemon not compiled in.";
+}
+#endif
+
+int
+main (int argc, char **argv)
+{
+    const char *err;
+
+    g_type_init ();
+
+#if CAIRO_CAN_TEST_SVG_SURFACE
+    rsvg_init ();
+    rsvg_set_default_dpi (72.0);
+#endif
+
+    if (argc == 1)
+	err = any2ppm_daemon ();
+    else
+	err = convert (argv + 1, 1);
+    if (err != NULL) {
+	fprintf (stderr, "Failed to run converter: %s\n", err);
+	return EXIT_FAILURE;
+    }
+
+    return EXIT_SUCCESS;
+}


More information about the cairo-commit mailing list