summaryrefslogtreecommitdiff
path: root/src/cairo-output-stream.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cairo-output-stream.c')
-rw-r--r--src/cairo-output-stream.c285
1 files changed, 285 insertions, 0 deletions
diff --git a/src/cairo-output-stream.c b/src/cairo-output-stream.c
new file mode 100644
index 000000000..14b4486a6
--- /dev/null
+++ b/src/cairo-output-stream.c
@@ -0,0 +1,285 @@
+/* cairo_output_stream.c: Output stream abstraction
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is cairo_output_stream.c as distributed with the
+ * cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Author(s):
+ * Kristian Høgsberg <krh@redhat.com>
+ */
+
+#include <stdio.h>
+#include <locale.h>
+#include <ctype.h>
+#include "cairoint.h"
+
+struct _cairo_output_stream {
+ cairo_write_func_t write_data;
+ void *closure;
+ cairo_bool_t owns_closure_is_file;
+ unsigned long position;
+ cairo_status_t status;
+};
+
+cairo_output_stream_t *
+_cairo_output_stream_create (cairo_write_func_t write_data,
+ void *closure)
+{
+ cairo_output_stream_t *stream;
+
+ stream = malloc (sizeof (cairo_output_stream_t));
+ if (stream == NULL)
+ return NULL;
+
+ stream->write_data = write_data;
+ stream->closure = closure;
+ stream->owns_closure_is_file = FALSE;
+ stream->position = 0;
+ stream->status = CAIRO_STATUS_SUCCESS;
+
+ return stream;
+}
+
+void
+_cairo_output_stream_destroy (cairo_output_stream_t *stream)
+{
+ if (stream->owns_closure_is_file) {
+ FILE *file = stream->closure;
+ fflush (file);
+ fclose (file);
+ }
+ free (stream);
+}
+
+cairo_status_t
+_cairo_output_stream_write (cairo_output_stream_t *stream,
+ const void *data, size_t length)
+{
+ if (length == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ stream->status = stream->write_data (stream->closure, data, length);
+ stream->position += length;
+
+ return stream->status;
+}
+
+/* Format a double in a locale independent way and trim trailing
+ * zeros. Based on code from Alex Larson <alexl@redhat.com>.
+ * http://mail.gnome.org/archives/gtk-devel-list/2001-October/msg00087.html
+ */
+
+static int
+dtostr (char *buffer, size_t size, double d)
+{
+ struct lconv *locale_data;
+ const char *decimal_point;
+ int decimal_point_len;
+ char *p;
+ int decimal_len;
+
+ snprintf (buffer, size, "%f", d);
+
+ locale_data = localeconv ();
+ decimal_point = locale_data->decimal_point;
+ decimal_point_len = strlen (decimal_point);
+
+ assert (decimal_point_len != 0);
+ p = buffer;
+
+ if (*p == '+' || *p == '-')
+ p++;
+
+ while (isdigit (*p))
+ p++;
+
+ if (strncmp (p, decimal_point, decimal_point_len) == 0) {
+ *p = '.';
+ decimal_len = strlen (p + decimal_point_len);
+ memmove (p + 1, p + decimal_point_len, decimal_len);
+ p[1 + decimal_len] = 0;
+
+ /* Remove trailing zeros and decimal point if possible. */
+ for (p = p + decimal_len; *p == '0'; p--)
+ *p = 0;
+
+ if (*p == '.') {
+ *p = 0;
+ p--;
+ }
+ }
+
+ return p + 1 - buffer;
+}
+
+
+enum {
+ LENGTH_MODIFIER_LONG = 0x100
+};
+
+/* Here's a limited reimplementation of printf. The reason for doing
+ * this is primarily to special case handling of doubles. We want
+ * locale independent formatting of doubles and we want to trim
+ * trailing zeros. This is handled by dtostr() above, and the code
+ * below handles everything else by calling snprintf() to do the
+ * formatting. This functionality is only for internal use and we
+ * only implement the formats we actually use.
+ */
+
+cairo_status_t
+_cairo_output_stream_vprintf (cairo_output_stream_t *stream,
+ const char *fmt, va_list ap)
+{
+ char buffer[512];
+ char *p;
+ const char *f;
+ int length_modifier;
+
+ f = fmt;
+ p = buffer;
+ while (*f != '\0') {
+ if (p == buffer + sizeof (buffer)) {
+ _cairo_output_stream_write (stream, buffer, sizeof (buffer));
+ p = buffer;
+ }
+
+ if (*f != '%') {
+ *p++ = *f++;
+ continue;
+ }
+
+ f++;
+
+ _cairo_output_stream_write (stream, buffer, p - buffer);
+ p = buffer;
+
+ length_modifier = 0;
+ if (*f == 'l') {
+ length_modifier = LENGTH_MODIFIER_LONG;
+ f++;
+ }
+
+ switch (*f | length_modifier) {
+ case '%':
+ p[0] = *f;
+ p[1] = 0;
+ break;
+ case 'd':
+ snprintf (buffer, sizeof buffer, "%d", va_arg (ap, int));
+ break;
+ case 'd' | LENGTH_MODIFIER_LONG:
+ snprintf (buffer, sizeof buffer, "%ld", va_arg (ap, long int));
+ break;
+ case 'u':
+ snprintf (buffer, sizeof buffer, "%u", va_arg (ap, unsigned int));
+ break;
+ case 'u' | LENGTH_MODIFIER_LONG:
+ snprintf (buffer, sizeof buffer, "%lu", va_arg (ap, long unsigned int));
+ break;
+ case 'o':
+ snprintf (buffer, sizeof buffer, "%o", va_arg (ap, int));
+ break;
+ case 's':
+ snprintf (buffer, sizeof buffer, "%s", va_arg (ap, const char *));
+ break;
+ case 'f':
+ dtostr (buffer, sizeof buffer, va_arg (ap, double));
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ }
+ p = buffer + strlen (buffer);
+ f++;
+ }
+
+ _cairo_output_stream_write (stream, buffer, p - buffer);
+
+ return stream->status;
+}
+
+cairo_status_t
+_cairo_output_stream_printf (cairo_output_stream_t *stream,
+ const char *fmt, ...)
+{
+ va_list ap;
+ cairo_status_t status;
+
+ va_start (ap, fmt);
+
+ status = _cairo_output_stream_vprintf (stream, fmt, ap);
+
+ va_end (ap);
+
+ return status;
+}
+
+long
+_cairo_output_stream_get_position (cairo_output_stream_t *stream)
+{
+ return stream->position;
+}
+
+cairo_status_t
+_cairo_output_stream_get_status (cairo_output_stream_t *stream)
+{
+ return stream->status;
+}
+
+
+/* Maybe this should be a configure time option, so embedded targets
+ * don't have to pull in stdio. */
+
+static cairo_status_t
+stdio_write (void *closure, const unsigned char *data, unsigned int length)
+{
+ FILE *fp = closure;
+
+ if (fwrite (data, 1, length, fp) == length)
+ return CAIRO_STATUS_SUCCESS;
+
+ return CAIRO_STATUS_WRITE_ERROR;
+}
+
+cairo_output_stream_t *
+_cairo_output_stream_create_for_file (const char *filename)
+{
+ FILE *fp;
+ cairo_output_stream_t *stream;
+
+ fp = fopen (filename, "wb");
+ if (fp == NULL)
+ return NULL;
+
+ stream = _cairo_output_stream_create (stdio_write, fp);
+ if (stream == NULL)
+ fclose (fp);
+ stream->owns_closure_is_file = TRUE;
+
+ return stream;
+}