forked from KolibriOS/kolibrios
765 lines
20 KiB
C
765 lines
20 KiB
C
|
/* 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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 <krh@redhat.com>
|
||
|
*/
|
||
|
|
||
|
#define _BSD_SOURCE /* for snprintf() */
|
||
|
#include "cairoint.h"
|
||
|
|
||
|
#include "cairo-output-stream-private.h"
|
||
|
#include "cairo-error-private.h"
|
||
|
#include "cairo-compiler-private.h"
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <locale.h>
|
||
|
#include <errno.h>
|
||
|
|
||
|
/* 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:
|
||
|
*
|
||
|
* <programlisting>
|
||
|
* FIXED_POINT_DECIMAL_DIGITS = ceil( CAIRO_FIXED_FRAC_BITS * ln(2)/ln(10) )
|
||
|
* </programlisting>
|
||
|
*
|
||
|
* 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_flush_func_t flush_func,
|
||
|
cairo_output_stream_close_func_t close_func)
|
||
|
{
|
||
|
stream->write_func = write_func;
|
||
|
stream->flush_func = flush_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, /* flush_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, /* flush_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 (unlikely (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, NULL, 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 (unlikely (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, NULL);
|
||
|
stream->status = status;
|
||
|
|
||
|
return stream;
|
||
|
}
|
||
|
|
||
|
cairo_status_t
|
||
|
_cairo_output_stream_flush (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->flush_func) {
|
||
|
status = stream->flush_func (stream);
|
||
|
/* Don't overwrite a pre-existing status failure. */
|
||
|
if (stream->status == CAIRO_STATUS_SUCCESS)
|
||
|
stream->status = status;
|
||
|
}
|
||
|
|
||
|
return stream->status;
|
||
|
}
|
||
|
|
||
|
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 <alexl@redhat.com>.
|
||
|
* 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 <krh@redhat.com>
|
||
|
*/
|
||
|
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 (_cairo_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 (_cairo_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 (_cairo_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 (unlikely (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, 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 (unlikely (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_flush, 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 (unlikely (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, NULL, memory_close);
|
||
|
_cairo_array_init (&stream->array, 1);
|
||
|
|
||
|
return &stream->base;
|
||
|
}
|
||
|
|
||
|
cairo_status_t
|
||
|
_cairo_memory_stream_destroy (cairo_output_stream_t *abstract_stream,
|
||
|
unsigned char **data_out,
|
||
|
unsigned long *length_out)
|
||
|
{
|
||
|
memory_stream_t *stream;
|
||
|
cairo_status_t status;
|
||
|
|
||
|
status = abstract_stream->status;
|
||
|
if (unlikely (status))
|
||
|
return _cairo_output_stream_destroy (abstract_stream);
|
||
|
|
||
|
stream = (memory_stream_t *) abstract_stream;
|
||
|
|
||
|
*length_out = _cairo_array_num_elements (&stream->array);
|
||
|
*data_out = malloc (*length_out);
|
||
|
if (unlikely (*data_out == NULL)) {
|
||
|
status = _cairo_output_stream_destroy (abstract_stream);
|
||
|
assert (status == CAIRO_STATUS_SUCCESS);
|
||
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||
|
}
|
||
|
memcpy (*data_out, _cairo_array_index (&stream->array, 0), *length_out);
|
||
|
|
||
|
return _cairo_output_stream_destroy (abstract_stream);
|
||
|
}
|
||
|
|
||
|
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 (unlikely (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, NULL);
|
||
|
|
||
|
return stream;
|
||
|
}
|