[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