Back to index

lightning-sunbird  0.9+nobinonly
cairo-output-stream.c
Go to the documentation of this file.
00001 /* cairo_output_stream.c: Output stream abstraction
00002  * 
00003  * Copyright © 2005 Red Hat, Inc
00004  *
00005  * This library is free software; you can redistribute it and/or
00006  * modify it either under the terms of the GNU Lesser General Public
00007  * License version 2.1 as published by the Free Software Foundation
00008  * (the "LGPL") or, at your option, under the terms of the Mozilla
00009  * Public License Version 1.1 (the "MPL"). If you do not alter this
00010  * notice, a recipient may use your version of this file under either
00011  * the MPL or the LGPL.
00012  *
00013  * You should have received a copy of the LGPL along with this library
00014  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
00015  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00016  * You should have received a copy of the MPL along with this library
00017  * in the file COPYING-MPL-1.1
00018  *
00019  * The contents of this file are subject to the Mozilla Public License
00020  * Version 1.1 (the "License"); you may not use this file except in
00021  * compliance with the License. You may obtain a copy of the License at
00022  * http://www.mozilla.org/MPL/
00023  *
00024  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
00025  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
00026  * the specific language governing rights and limitations.
00027  *
00028  * The Original Code is cairo_output_stream.c as distributed with the
00029  *   cairo graphics library.
00030  *
00031  * The Initial Developer of the Original Code is Red Hat, Inc.
00032  *
00033  * Author(s):
00034  *     Kristian Høgsberg <krh@redhat.com>
00035  */
00036 
00037 #include <stdio.h>
00038 #include <locale.h>
00039 #include <ctype.h>
00040 #include "cairoint.h"
00041 
00042 #ifdef _MSC_VER
00043 #define snprintf _snprintf
00044 #endif /* _MSC_VER */
00045 
00046 struct _cairo_output_stream {
00047     cairo_write_func_t             write_data;
00048     void                    *closure;
00049     cairo_bool_t            owns_closure_is_file;
00050     unsigned long           position;
00051     cairo_status_t          status;
00052 };
00053 
00054 cairo_output_stream_t *
00055 _cairo_output_stream_create (cairo_write_func_t         write_data,
00056                           void                   *closure)
00057 {
00058     cairo_output_stream_t *stream;
00059 
00060     stream = malloc (sizeof (cairo_output_stream_t));
00061     if (stream == NULL)
00062        return NULL;
00063 
00064     stream->write_data = write_data;
00065     stream->closure = closure;
00066     stream->owns_closure_is_file = FALSE;
00067     stream->position = 0;
00068     stream->status = CAIRO_STATUS_SUCCESS;
00069 
00070     return stream;
00071 }
00072 
00073 void
00074 _cairo_output_stream_destroy (cairo_output_stream_t *stream)
00075 {
00076     if (stream->owns_closure_is_file) {
00077        FILE *file = stream->closure;
00078        fflush (file);
00079        fclose (file);
00080     }
00081     free (stream);
00082 }
00083 
00084 cairo_status_t
00085 _cairo_output_stream_write (cairo_output_stream_t *stream,
00086                          const void *data, size_t length)
00087 {
00088     if (length == 0)
00089        return CAIRO_STATUS_SUCCESS;
00090 
00091     stream->status = stream->write_data (stream->closure, data, length);
00092     stream->position += length;
00093 
00094     return stream->status;
00095 }
00096 
00097 void
00098 _cairo_output_stream_write_hex_string (cairo_output_stream_t *stream,
00099                                    const char *data,
00100                                    size_t length)
00101 {
00102     const char hex_chars[] = "0123456789abcdef";
00103     char buffer[2];
00104     int i, column;
00105 
00106     for (i = 0, column = 0; i < length; i++, column++) {
00107        if (column == 38) {
00108            _cairo_output_stream_write (stream, "\n", 1);
00109            column = 0;
00110        }
00111        buffer[0] = hex_chars[(data[i] >> 4) & 0x0f];
00112        buffer[1] = hex_chars[data[i] & 0x0f];
00113        _cairo_output_stream_write (stream, buffer, 2);
00114     }
00115 }
00116 
00117 /* Format a double in a locale independent way and trim trailing
00118  * zeros.  Based on code from Alex Larson <alexl@redhat.com>.
00119  * http://mail.gnome.org/archives/gtk-devel-list/2001-October/msg00087.html
00120  */
00121 
00122 static int
00123 dtostr (char *buffer, size_t size, double d)
00124 {
00125   struct lconv *locale_data;
00126   const char *decimal_point;
00127   int decimal_point_len;
00128   char *p;
00129   int decimal_len;
00130 
00131   snprintf (buffer, size, "%f", d);
00132     
00133   locale_data = localeconv ();
00134   decimal_point = locale_data->decimal_point;
00135   decimal_point_len = strlen (decimal_point);
00136   
00137   assert (decimal_point_len != 0);
00138   p = buffer;
00139                          
00140   if (*p == '+' || *p == '-')
00141       p++;
00142 
00143   while (isdigit (*p))
00144       p++;
00145                                    
00146   if (strncmp (p, decimal_point, decimal_point_len) == 0) {
00147       *p = '.';
00148       decimal_len = strlen (p + decimal_point_len);
00149       memmove (p + 1, p + decimal_point_len, decimal_len);
00150       p[1 + decimal_len] = 0;
00151 
00152       /* Remove trailing zeros and decimal point if possible. */
00153       for (p = p + decimal_len; *p == '0'; p--)
00154          *p = 0;
00155 
00156       if (*p == '.') {
00157          *p = 0;
00158          p--;
00159       }
00160   }
00161                                            
00162   return p + 1 - buffer;
00163 }
00164 
00165 
00166 enum {
00167     LENGTH_MODIFIER_LONG = 0x100
00168 };
00169 
00170 /* Here's a limited reimplementation of printf.  The reason for doing
00171  * this is primarily to special case handling of doubles.  We want
00172  * locale independent formatting of doubles and we want to trim
00173  * trailing zeros.  This is handled by dtostr() above, and the code
00174  * below handles everything else by calling snprintf() to do the
00175  * formatting.  This functionality is only for internal use and we
00176  * only implement the formats we actually use.
00177  */
00178 
00179 cairo_status_t
00180 _cairo_output_stream_vprintf (cairo_output_stream_t *stream,
00181                            const char *fmt, va_list ap)
00182 {
00183     char buffer[512];
00184     char *p;
00185     const char *f;
00186     int length_modifier;
00187 
00188     f = fmt;
00189     p = buffer;
00190     while (*f != '\0') {
00191        if (p == buffer + sizeof (buffer)) {
00192            _cairo_output_stream_write (stream, buffer, sizeof (buffer));
00193            p = buffer;
00194        }
00195 
00196        if (*f != '%') {
00197            *p++ = *f++;
00198            continue;
00199        }
00200 
00201        f++;
00202 
00203        _cairo_output_stream_write (stream, buffer, p - buffer);
00204        p = buffer;
00205 
00206        length_modifier = 0;
00207        if (*f == 'l') {
00208            length_modifier = LENGTH_MODIFIER_LONG;
00209            f++;
00210        }
00211 
00212        switch (*f | length_modifier) {
00213        case '%':
00214            buffer[0] = *f;
00215            buffer[1] = 0;
00216            break;
00217        case 'd':
00218            snprintf (buffer, sizeof buffer, "%d", va_arg (ap, int));
00219            break;
00220        case 'd' | LENGTH_MODIFIER_LONG:
00221            snprintf (buffer, sizeof buffer, "%ld", va_arg (ap, long int));
00222            break;
00223        case 'u':
00224            snprintf (buffer, sizeof buffer, "%u", va_arg (ap, unsigned int));
00225            break;
00226        case 'u' | LENGTH_MODIFIER_LONG:
00227            snprintf (buffer, sizeof buffer, "%lu", va_arg (ap, long unsigned int));
00228            break;
00229        case 'o':
00230            snprintf (buffer, sizeof buffer, "%o", va_arg (ap, int));
00231            break;
00232        case 's':
00233            snprintf (buffer, sizeof buffer, "%s", va_arg (ap, const char *));
00234            break;
00235        case 'f':
00236            dtostr (buffer, sizeof buffer, va_arg (ap, double));
00237            break;
00238        case 'c':
00239            buffer[0] = va_arg (ap, int);
00240            buffer[1] = 0;
00241            break;
00242        default:
00243            ASSERT_NOT_REACHED;
00244        }
00245        p = buffer + strlen (buffer);
00246        f++;
00247     }
00248     
00249     _cairo_output_stream_write (stream, buffer, p - buffer);
00250 
00251     return stream->status;
00252 }
00253 
00254 cairo_status_t
00255 _cairo_output_stream_printf (cairo_output_stream_t *stream,
00256                           const char *fmt, ...)
00257 {
00258     va_list ap;
00259     cairo_status_t status;    
00260 
00261     va_start (ap, fmt);
00262 
00263     status = _cairo_output_stream_vprintf (stream, fmt, ap);
00264 
00265     va_end (ap);
00266 
00267     return status;
00268 }
00269 
00270 long
00271 _cairo_output_stream_get_position (cairo_output_stream_t *stream)
00272 {
00273     return stream->position;
00274 }
00275 
00276 cairo_status_t
00277 _cairo_output_stream_get_status (cairo_output_stream_t *stream)
00278 {
00279     return stream->status;
00280 }
00281 
00282 
00283 /* Maybe this should be a configure time option, so embedded targets
00284  * don't have to pull in stdio. */
00285 
00286 static cairo_status_t
00287 stdio_write (void *closure, const unsigned char *data, unsigned int length)
00288 {
00289     FILE *fp = closure;
00290 
00291     if (fwrite (data, 1, length, fp) == length)
00292        return CAIRO_STATUS_SUCCESS;
00293 
00294     return CAIRO_STATUS_WRITE_ERROR;
00295 }
00296 
00297 cairo_output_stream_t *
00298 _cairo_output_stream_create_for_file (const char *filename)
00299 {
00300     FILE *fp;
00301     cairo_output_stream_t *stream;
00302 
00303     fp = fopen (filename, "wb");
00304     if (fp == NULL)
00305        return NULL;
00306     
00307     stream = _cairo_output_stream_create (stdio_write, fp);
00308     if (stream == NULL)
00309        fclose (fp);
00310     stream->owns_closure_is_file = TRUE;
00311 
00312     return stream;
00313 }