/* 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 the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Author(s): * Kristian Høgsberg */ #define _BSD_SOURCE /* for snprintf() */ #include "cairoint.h" #include "cairo-output-stream-private.h" #include "cairo-compiler-private.h" #include #include #include #include /* Numbers printed with %f are printed with this number of significant * digits after the decimal. */ #define SIGNIFICANT_DIGITS_AFTER_DECIMAL 6 /* Numbers printed with %g are assumed to only have %CAIRO_FIXED_FRAC_BITS * bits of precision available after the decimal point. * * FIXED_POINT_DECIMAL_DIGITS specifies the minimum number of decimal * digits after the decimal point required to preserve the available * precision. * * The conversion is: * * * FIXED_POINT_DECIMAL_DIGITS = ceil( CAIRO_FIXED_FRAC_BITS * ln(2)/ln(10) ) * * * We can replace ceil(x) with (int)(x+1) since x will never be an * integer for any likely value of %CAIRO_FIXED_FRAC_BITS. */ #define FIXED_POINT_DECIMAL_DIGITS ((int)(CAIRO_FIXED_FRAC_BITS*0.301029996 + 1)) void _cairo_output_stream_init (cairo_output_stream_t *stream, cairo_output_stream_write_func_t write_func, cairo_output_stream_close_func_t close_func) { stream->write_func = write_func; stream->close_func = close_func; stream->position = 0; stream->status = CAIRO_STATUS_SUCCESS; stream->closed = FALSE; } cairo_status_t _cairo_output_stream_fini (cairo_output_stream_t *stream) { return _cairo_output_stream_close (stream); } const cairo_output_stream_t _cairo_output_stream_nil = { NULL, /* write_func */ NULL, /* close_func */ 0, /* position */ CAIRO_STATUS_NO_MEMORY, FALSE /* closed */ }; static const cairo_output_stream_t _cairo_output_stream_nil_write_error = { NULL, /* write_func */ NULL, /* close_func */ 0, /* position */ CAIRO_STATUS_WRITE_ERROR, FALSE /* closed */ }; typedef struct _cairo_output_stream_with_closure { cairo_output_stream_t base; cairo_write_func_t write_func; cairo_close_func_t close_func; void *closure; } cairo_output_stream_with_closure_t; static cairo_status_t closure_write (cairo_output_stream_t *stream, const unsigned char *data, unsigned int length) { cairo_output_stream_with_closure_t *stream_with_closure = (cairo_output_stream_with_closure_t *) stream; if (stream_with_closure->write_func == NULL) return CAIRO_STATUS_SUCCESS; return stream_with_closure->write_func (stream_with_closure->closure, data, length); } static cairo_status_t closure_close (cairo_output_stream_t *stream) { cairo_output_stream_with_closure_t *stream_with_closure = (cairo_output_stream_with_closure_t *) stream; if (stream_with_closure->close_func != NULL) return stream_with_closure->close_func (stream_with_closure->closure); else return CAIRO_STATUS_SUCCESS; } cairo_output_stream_t * _cairo_output_stream_create (cairo_write_func_t write_func, cairo_close_func_t close_func, void *closure) { cairo_output_stream_with_closure_t *stream; stream = malloc (sizeof (cairo_output_stream_with_closure_t)); if (stream == NULL) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (&stream->base, closure_write, closure_close); stream->write_func = write_func; stream->close_func = close_func; stream->closure = closure; return &stream->base; } cairo_output_stream_t * _cairo_output_stream_create_in_error (cairo_status_t status) { cairo_output_stream_t *stream; /* check for the common ones */ if (status == CAIRO_STATUS_NO_MEMORY) return (cairo_output_stream_t *) &_cairo_output_stream_nil; if (status == CAIRO_STATUS_WRITE_ERROR) return (cairo_output_stream_t *) &_cairo_output_stream_nil_write_error; stream = malloc (sizeof (cairo_output_stream_t)); if (stream == NULL) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (stream, NULL, NULL); stream->status = status; return stream; } cairo_status_t _cairo_output_stream_close (cairo_output_stream_t *stream) { cairo_status_t status; if (stream->closed) return stream->status; if (stream == &_cairo_output_stream_nil || stream == &_cairo_output_stream_nil_write_error) { return stream->status; } if (stream->close_func) { status = stream->close_func (stream); /* Don't overwrite a pre-existing status failure. */ if (stream->status == CAIRO_STATUS_SUCCESS) stream->status = status; } stream->closed = TRUE; return stream->status; } cairo_status_t _cairo_output_stream_destroy (cairo_output_stream_t *stream) { cairo_status_t status; assert (stream != NULL); if (stream == &_cairo_output_stream_nil || stream == &_cairo_output_stream_nil_write_error) { return stream->status; } status = _cairo_output_stream_fini (stream); free (stream); return status; } void _cairo_output_stream_write (cairo_output_stream_t *stream, const void *data, size_t length) { if (length == 0) return; if (stream->status) return; stream->status = stream->write_func (stream, data, length); stream->position += length; } void _cairo_output_stream_write_hex_string (cairo_output_stream_t *stream, const unsigned char *data, size_t length) { const char hex_chars[] = "0123456789abcdef"; char buffer[2]; unsigned int i, column; if (stream->status) return; for (i = 0, column = 0; i < length; i++, column++) { if (column == 38) { _cairo_output_stream_write (stream, "\n", 1); column = 0; } buffer[0] = hex_chars[(data[i] >> 4) & 0x0f]; buffer[1] = hex_chars[data[i] & 0x0f]; _cairo_output_stream_write (stream, buffer, 2); } } /* Format a double in a locale independent way and trim trailing * zeros. Based on code from Alex Larson . * http://mail.gnome.org/archives/gtk-devel-list/2001-October/msg00087.html * * The code in the patch is copyright Red Hat, Inc under the LGPL, but * has been relicensed under the LGPL/MPL dual license for inclusion * into cairo (see COPYING). -- Kristian Høgsberg */ static void _cairo_dtostr (char *buffer, size_t size, double d, cairo_bool_t limited_precision) { struct lconv *locale_data; const char *decimal_point; int decimal_point_len; char *p; int decimal_len; int num_zeros, decimal_digits; /* Omit the minus sign from negative zero. */ if (d == 0.0) d = 0.0; locale_data = localeconv (); decimal_point = locale_data->decimal_point; decimal_point_len = strlen (decimal_point); assert (decimal_point_len != 0); if (limited_precision) { snprintf (buffer, size, "%.*f", FIXED_POINT_DECIMAL_DIGITS, d); } else { /* Using "%f" to print numbers less than 0.1 will result in * reduced precision due to the default 6 digits after the * decimal point. * * For numbers is < 0.1, we print with maximum precision and count * the number of zeros between the decimal point and the first * significant digit. We then print the number again with the * number of decimal places that gives us the required number of * significant digits. This ensures the number is correctly * rounded. */ if (fabs (d) >= 0.1) { snprintf (buffer, size, "%f", d); } else { snprintf (buffer, size, "%.18f", d); p = buffer; if (*p == '+' || *p == '-') p++; while (isdigit (*p)) p++; if (strncmp (p, decimal_point, decimal_point_len) == 0) p += decimal_point_len; num_zeros = 0; while (*p++ == '0') num_zeros++; decimal_digits = num_zeros + SIGNIFICANT_DIGITS_AFTER_DECIMAL; if (decimal_digits < 18) snprintf (buffer, size, "%.*f", decimal_digits, d); } } 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--; } } } 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. */ void _cairo_output_stream_vprintf (cairo_output_stream_t *stream, const char *fmt, va_list ap) { #define SINGLE_FMT_BUFFER_SIZE 32 char buffer[512], single_fmt[SINGLE_FMT_BUFFER_SIZE]; int single_fmt_length; char *p; const char *f, *start; int length_modifier, width; cairo_bool_t var_width; if (stream->status) return; 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; } start = f; f++; if (*f == '0') f++; var_width = FALSE; if (*f == '*') { var_width = TRUE; f++; } while (isdigit (*f)) f++; length_modifier = 0; if (*f == 'l') { length_modifier = LENGTH_MODIFIER_LONG; f++; } /* The only format strings exist in the cairo implementation * itself. So there's an internal consistency problem if any * of them is larger than our format buffer size. */ single_fmt_length = f - start + 1; assert (single_fmt_length + 1 <= SINGLE_FMT_BUFFER_SIZE); /* Reuse the format string for this conversion. */ memcpy (single_fmt, start, single_fmt_length); single_fmt[single_fmt_length] = '\0'; /* Flush contents of buffer before snprintf()'ing into it. */ _cairo_output_stream_write (stream, buffer, p - buffer); /* We group signed and unsigned together in this switch, the * only thing that matters here is the size of the arguments, * since we're just passing the data through to sprintf(). */ switch (*f | length_modifier) { case '%': buffer[0] = *f; buffer[1] = 0; break; case 'd': case 'u': case 'o': case 'x': case 'X': if (var_width) { width = va_arg (ap, int); snprintf (buffer, sizeof buffer, single_fmt, width, va_arg (ap, int)); } else { snprintf (buffer, sizeof buffer, single_fmt, va_arg (ap, int)); } break; case 'd' | LENGTH_MODIFIER_LONG: case 'u' | LENGTH_MODIFIER_LONG: case 'o' | LENGTH_MODIFIER_LONG: case 'x' | LENGTH_MODIFIER_LONG: case 'X' | LENGTH_MODIFIER_LONG: if (var_width) { width = va_arg (ap, int); snprintf (buffer, sizeof buffer, single_fmt, width, va_arg (ap, long int)); } else { snprintf (buffer, sizeof buffer, single_fmt, va_arg (ap, long int)); } break; case 's': snprintf (buffer, sizeof buffer, single_fmt, va_arg (ap, const char *)); break; case 'f': _cairo_dtostr (buffer, sizeof buffer, va_arg (ap, double), FALSE); break; case 'g': _cairo_dtostr (buffer, sizeof buffer, va_arg (ap, double), TRUE); break; case 'c': buffer[0] = va_arg (ap, int); buffer[1] = 0; break; default: ASSERT_NOT_REACHED; } p = buffer + strlen (buffer); f++; } _cairo_output_stream_write (stream, buffer, p - buffer); } void _cairo_output_stream_printf (cairo_output_stream_t *stream, const char *fmt, ...) { va_list ap; va_start (ap, fmt); _cairo_output_stream_vprintf (stream, fmt, ap); va_end (ap); } 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. */ typedef struct _stdio_stream { cairo_output_stream_t base; FILE *file; } stdio_stream_t; static cairo_status_t stdio_write (cairo_output_stream_t *base, const unsigned char *data, unsigned int length) { stdio_stream_t *stream = (stdio_stream_t *) base; if (fwrite (data, 1, length, stream->file) != length) return _cairo_error (CAIRO_STATUS_WRITE_ERROR); return CAIRO_STATUS_SUCCESS; } static cairo_status_t stdio_flush (cairo_output_stream_t *base) { stdio_stream_t *stream = (stdio_stream_t *) base; fflush (stream->file); if (ferror (stream->file)) return _cairo_error (CAIRO_STATUS_WRITE_ERROR); else return CAIRO_STATUS_SUCCESS; } static cairo_status_t stdio_close (cairo_output_stream_t *base) { cairo_status_t status; stdio_stream_t *stream = (stdio_stream_t *) base; status = stdio_flush (base); fclose (stream->file); return status; } cairo_output_stream_t * _cairo_output_stream_create_for_file (FILE *file) { stdio_stream_t *stream; if (file == NULL) { _cairo_error_throw (CAIRO_STATUS_WRITE_ERROR); return (cairo_output_stream_t *) &_cairo_output_stream_nil_write_error; } stream = malloc (sizeof *stream); if (stream == NULL) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (&stream->base, stdio_write, stdio_flush); stream->file = file; return &stream->base; } cairo_output_stream_t * _cairo_output_stream_create_for_filename (const char *filename) { stdio_stream_t *stream; FILE *file; if (filename == NULL) return _cairo_null_stream_create (); file = fopen (filename, "wb"); if (file == NULL) { switch (errno) { case ENOMEM: _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; default: _cairo_error_throw (CAIRO_STATUS_WRITE_ERROR); return (cairo_output_stream_t *) &_cairo_output_stream_nil_write_error; } } stream = malloc (sizeof *stream); if (stream == NULL) { fclose (file); _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (&stream->base, stdio_write, stdio_close); stream->file = file; return &stream->base; } typedef struct _memory_stream { cairo_output_stream_t base; cairo_array_t array; } memory_stream_t; static cairo_status_t memory_write (cairo_output_stream_t *base, const unsigned char *data, unsigned int length) { memory_stream_t *stream = (memory_stream_t *) base; return _cairo_array_append_multiple (&stream->array, data, length); } static cairo_status_t memory_close (cairo_output_stream_t *base) { memory_stream_t *stream = (memory_stream_t *) base; _cairo_array_fini (&stream->array); return CAIRO_STATUS_SUCCESS; } cairo_output_stream_t * _cairo_memory_stream_create (void) { memory_stream_t *stream; stream = malloc (sizeof *stream); if (stream == NULL) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (&stream->base, memory_write, memory_close); _cairo_array_init (&stream->array, 1); return &stream->base; } void _cairo_memory_stream_copy (cairo_output_stream_t *base, cairo_output_stream_t *dest) { memory_stream_t *stream = (memory_stream_t *) base; if (dest->status) return; if (base->status) { dest->status = base->status; return; } _cairo_output_stream_write (dest, _cairo_array_index (&stream->array, 0), _cairo_array_num_elements (&stream->array)); } int _cairo_memory_stream_length (cairo_output_stream_t *base) { memory_stream_t *stream = (memory_stream_t *) base; return _cairo_array_num_elements (&stream->array); } static cairo_status_t null_write (cairo_output_stream_t *base, const unsigned char *data, unsigned int length) { return CAIRO_STATUS_SUCCESS; } cairo_output_stream_t * _cairo_null_stream_create (void) { cairo_output_stream_t *stream; stream = malloc (sizeof *stream); if (stream == NULL) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_output_stream_t *) &_cairo_output_stream_nil; } _cairo_output_stream_init (stream, null_write, NULL); return stream; }