Back to index

lightning-sunbird  0.9+nobinonly
cairo-ps-surface.c
Go to the documentation of this file.
00001 /* cairo - a vector graphics library with display and print output
00002  *
00003  * Copyright © 2003 University of Southern California
00004  * Copyright © 2005 Red Hat, Inc
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it either under the terms of the GNU Lesser General Public
00008  * License version 2.1 as published by the Free Software Foundation
00009  * (the "LGPL") or, at your option, under the terms of the Mozilla
00010  * Public License Version 1.1 (the "MPL"). If you do not alter this
00011  * notice, a recipient may use your version of this file under either
00012  * the MPL or the LGPL.
00013  *
00014  * You should have received a copy of the LGPL along with this library
00015  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
00016  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00017  * You should have received a copy of the MPL along with this library
00018  * in the file COPYING-MPL-1.1
00019  *
00020  * The contents of this file are subject to the Mozilla Public License
00021  * Version 1.1 (the "License"); you may not use this file except in
00022  * compliance with the License. You may obtain a copy of the License at
00023  * http://www.mozilla.org/MPL/
00024  *
00025  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
00026  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
00027  * the specific language governing rights and limitations.
00028  *
00029  * The Original Code is the cairo graphics library.
00030  *
00031  * The Initial Developer of the Original Code is University of Southern
00032  * California.
00033  *
00034  * Contributor(s):
00035  *     Carl D. Worth <cworth@cworth.org>
00036  *     Kristian Høgsberg <krh@redhat.com>
00037  */
00038 
00039 #include "cairoint.h"
00040 #include "cairo-ps.h"
00041 #include "cairo-font-subset-private.h"
00042 #include "cairo-meta-surface-private.h"
00043 #include "cairo-ft-private.h"
00044 
00045 #include <time.h>
00046 #include <zlib.h>
00047 
00048 /* TODO:
00049  *
00050  * - Add document structure convention comments where appropriate.
00051  *
00052  * - Fix image compression.
00053  *
00054  * - Create a set of procs to use... specifically a trapezoid proc.
00055  */
00056 
00057 static const cairo_surface_backend_t cairo_ps_surface_backend;
00058 
00059 typedef struct cairo_ps_surface {
00060     cairo_surface_t base;
00061 
00062     /* PS-specific fields */
00063     cairo_output_stream_t *stream;
00064 
00065     double width;
00066     double height;
00067     double x_dpi;
00068     double y_dpi;
00069 
00070     cairo_surface_t *current_page;
00071     cairo_array_t pages;
00072     cairo_array_t fonts;
00073 } cairo_ps_surface_t;
00074 
00075 #define PS_SURFACE_DPI_DEFAULT 300.0
00076 
00077 static cairo_int_status_t
00078 _cairo_ps_surface_write_font_subsets (cairo_ps_surface_t *surface);
00079 
00080 static cairo_int_status_t
00081 _cairo_ps_surface_render_page (cairo_ps_surface_t *surface,
00082                             cairo_surface_t *page, int page_number);
00083 
00084 static cairo_surface_t *
00085 _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream,
00086                                          double              width,
00087                                          double              height)
00088 {
00089     cairo_ps_surface_t *surface;
00090 
00091     surface = malloc (sizeof (cairo_ps_surface_t));
00092     if (surface == NULL) {
00093        _cairo_error (CAIRO_STATUS_NO_MEMORY);
00094        return (cairo_surface_t*) &_cairo_surface_nil;
00095     }
00096 
00097     _cairo_surface_init (&surface->base, &cairo_ps_surface_backend);
00098 
00099     surface->stream = stream;
00100 
00101     surface->width  = width;
00102     surface->height = height;
00103     surface->x_dpi = PS_SURFACE_DPI_DEFAULT;
00104     surface->y_dpi = PS_SURFACE_DPI_DEFAULT;
00105     surface->base.device_x_scale = surface->x_dpi / 72.0;
00106     surface->base.device_y_scale = surface->y_dpi / 72.0;
00107 
00108     surface->current_page = _cairo_meta_surface_create (width,
00109                                                  height);
00110     if (surface->current_page->status) {
00111        free (surface);
00112        _cairo_error (CAIRO_STATUS_NO_MEMORY);
00113        return (cairo_surface_t*) &_cairo_surface_nil;
00114     }
00115 
00116     _cairo_array_init (&surface->pages, sizeof (cairo_surface_t *));
00117     _cairo_array_init (&surface->fonts, sizeof (cairo_font_subset_t *));
00118 
00119     return &surface->base;
00120 }
00121 
00122 cairo_surface_t *
00123 cairo_ps_surface_create (const char    *filename,
00124                       double              width_in_points,
00125                       double              height_in_points)
00126 {
00127     cairo_output_stream_t *stream;
00128 
00129     stream = _cairo_output_stream_create_for_file (filename);
00130     if (stream == NULL) {
00131        _cairo_error (CAIRO_STATUS_NO_MEMORY);
00132        return (cairo_surface_t*) &_cairo_surface_nil;
00133     }
00134 
00135     return _cairo_ps_surface_create_for_stream_internal (stream,
00136                                                   width_in_points,
00137                                                   height_in_points);
00138 }
00139 
00140 cairo_surface_t *
00141 cairo_ps_surface_create_for_stream (cairo_write_func_t  write_func,
00142                                 void             *closure,
00143                                 double           width_in_points,
00144                                 double           height_in_points)
00145 {
00146     cairo_output_stream_t *stream;
00147 
00148     stream = _cairo_output_stream_create (write_func, closure);
00149     if (stream == NULL) {
00150        _cairo_error (CAIRO_STATUS_NO_MEMORY);
00151        return (cairo_surface_t*) &_cairo_surface_nil;
00152     }
00153 
00154     return _cairo_ps_surface_create_for_stream_internal (stream,
00155                                                   width_in_points,
00156                                                   height_in_points);
00157 }
00158 
00169 void
00170 cairo_ps_surface_set_dpi (cairo_surface_t *surface,
00171                        double         x_dpi,
00172                        double         y_dpi)
00173 {
00174     cairo_ps_surface_t *ps_surface = (cairo_ps_surface_t *) surface;
00175 
00176     ps_surface->x_dpi = x_dpi;    
00177     ps_surface->y_dpi = y_dpi;    
00178     ps_surface->base.device_x_scale = ps_surface->x_dpi / 72.0;
00179     ps_surface->base.device_y_scale = ps_surface->y_dpi / 72.0;
00180 }
00181 
00182 static cairo_status_t
00183 _cairo_ps_surface_finish (void *abstract_surface)
00184 {
00185     cairo_ps_surface_t *surface = abstract_surface;
00186     cairo_surface_t *page;
00187     cairo_font_subset_t *subset;
00188     cairo_status_t status;
00189     int i;
00190     time_t now;
00191 
00192     now = time (NULL);
00193 
00194     /* Document header */
00195     _cairo_output_stream_printf (surface->stream,
00196                              "%%!PS-Adobe-3.0\n"
00197                              "%%%%Creator: Cairo (http://cairographics.org)\n"
00198                              "%%%%CreationDate: %s"
00199                              "%%%%Title: Some clever title\n"
00200                              "%%%%Pages: %d\n"
00201                              "%%%%BoundingBox: %f %f %f %f\n",
00202                              ctime (&now),
00203                              surface->pages.num_elements,
00204                              0.0, 0.0, 
00205                              surface->width,
00206                              surface->height);
00207 
00208     /* The "/FlateDecode filter" currently used is a feature of
00209      * LanguageLevel 3 */
00210     _cairo_output_stream_printf (surface->stream,
00211                              "%%%%DocumentData: Clean7Bit\n"
00212                              "%%%%LanguageLevel: 3\n"
00213                              "%%%%Orientation: Portrait\n"
00214                              "%%%%EndComments\n");
00215 
00216     _cairo_ps_surface_write_font_subsets (surface);
00217 
00218     status = CAIRO_STATUS_SUCCESS;
00219     for (i = 0; i < surface->pages.num_elements; i++) {
00220        _cairo_array_copy_element (&surface->pages, i, &page);
00221 
00222        status = _cairo_ps_surface_render_page (surface, page, i + 1);
00223        if (status)
00224            break;
00225     }
00226 
00227     /* Document footer */
00228     _cairo_output_stream_printf (surface->stream,
00229                              "%%%%Trailer\n"
00230                              "%%%%EOF\n");
00231 
00232     for (i = 0; i < surface->pages.num_elements; i++) {
00233        _cairo_array_copy_element (&surface->pages, i, &page);
00234        cairo_surface_destroy (page);
00235     }  
00236     _cairo_array_fini (&surface->pages);
00237 
00238     for (i = 0; i < surface->fonts.num_elements; i++) {
00239        _cairo_array_copy_element (&surface->fonts, i, &subset);
00240        _cairo_font_subset_destroy (subset);
00241     }  
00242     _cairo_array_fini (&surface->fonts);
00243 
00244     _cairo_output_stream_destroy (surface->stream);
00245     cairo_surface_destroy (surface->current_page);
00246 
00247     return status;
00248 }
00249 
00250 static cairo_int_status_t
00251 _cairo_ps_surface_composite (cairo_operator_t    operator,
00252                           cairo_pattern_t *src_pattern,
00253                           cairo_pattern_t *mask_pattern,
00254                           void            *abstract_dst,
00255                           int             src_x,
00256                           int             src_y,
00257                           int             mask_x,
00258                           int             mask_y,
00259                           int             dst_x,
00260                           int             dst_y,
00261                           unsigned int    width,
00262                           unsigned int    height)
00263 {
00264     cairo_ps_surface_t *surface = abstract_dst;
00265 
00266     return _cairo_surface_composite (operator,
00267                                  src_pattern,
00268                                  mask_pattern,
00269                                  surface->current_page,
00270                                  src_x,
00271                                  src_y,
00272                                  mask_x,
00273                                  mask_y,
00274                                  dst_x,
00275                                  dst_y,
00276                                  width,
00277                                  height);
00278 }
00279 
00280 static cairo_int_status_t
00281 _cairo_ps_surface_fill_rectangles (void                 *abstract_surface,
00282                                cairo_operator_t  operator,
00283                                const cairo_color_t      *color,
00284                                cairo_rectangle_t *rects,
00285                                int               num_rects)
00286 {
00287     cairo_ps_surface_t *surface = abstract_surface;
00288 
00289     return _cairo_surface_fill_rectangles (surface->current_page,
00290                                       operator,
00291                                       color,
00292                                       rects,
00293                                       num_rects);
00294 }
00295 
00296 static cairo_int_status_t
00297 _cairo_ps_surface_composite_trapezoids (cairo_operator_t       operator,
00298                                    cairo_pattern_t             *pattern,
00299                                    void                 *abstract_dst,
00300                                    cairo_antialias_t    antialias,
00301                                    int                  x_src,
00302                                    int                  y_src,
00303                                    int                  x_dst,
00304                                    int                  y_dst,
00305                                    unsigned int         width,
00306                                    unsigned int         height,
00307                                    cairo_trapezoid_t    *traps,
00308                                    int                  num_traps)
00309 {
00310     cairo_ps_surface_t *surface = abstract_dst;
00311 
00312     return _cairo_surface_composite_trapezoids (operator,
00313                                           pattern,
00314                                           surface->current_page,
00315                                           antialias,
00316                                           x_src,
00317                                           y_src,
00318                                           x_dst,
00319                                           y_dst,
00320                                           width,
00321                                           height,
00322                                           traps,
00323                                           num_traps);
00324 }
00325 
00326 static cairo_int_status_t
00327 _cairo_ps_surface_copy_page (void *abstract_surface)
00328 {
00329     cairo_ps_surface_t *surface = abstract_surface;
00330 
00331     /* FIXME: We need to copy the meta surface contents here */
00332 
00333     return _cairo_surface_show_page (&surface->base);
00334 }
00335 
00336 static cairo_int_status_t
00337 _cairo_ps_surface_show_page (void *abstract_surface)
00338 {
00339     cairo_ps_surface_t *surface = abstract_surface;
00340 
00341     _cairo_array_append (&surface->pages, &surface->current_page, 1);
00342     surface->current_page = _cairo_meta_surface_create (surface->width,
00343                                                  surface->height);
00344     if (surface->current_page->status)
00345        return CAIRO_STATUS_NO_MEMORY;
00346 
00347     return CAIRO_STATUS_SUCCESS;
00348 }
00349 
00350 static cairo_int_status_t
00351 _cairo_ps_surface_intersect_clip_path (void               *dst,
00352                                    cairo_path_fixed_t *path,
00353                                    cairo_fill_rule_t   fill_rule,
00354                                    double           tolerance,
00355                                    cairo_antialias_t   antialias)
00356 {
00357     cairo_ps_surface_t *surface = dst;
00358 
00359     return _cairo_surface_intersect_clip_path (surface->current_page,
00360                                           path,
00361                                           fill_rule,
00362                                           tolerance,
00363                                           antialias);
00364 }
00365 
00366 static cairo_int_status_t
00367 _cairo_ps_surface_get_extents (void               *abstract_surface,
00368                             cairo_rectangle_t *rectangle)
00369 {
00370     cairo_ps_surface_t *surface = abstract_surface;
00371 
00372     rectangle->x = 0;
00373     rectangle->y = 0;
00374 
00375     /* XXX: The conversion to integers here is pretty bogus, (not to
00376      * mention the aribitray limitation of width to a short(!). We
00377      * may need to come up with a better interface for get_size.
00378      */
00379     rectangle->width  = surface->width;
00380     rectangle->height = surface->height;
00381 
00382     return CAIRO_STATUS_SUCCESS;
00383 }
00384 
00385 static cairo_font_subset_t *
00386 _cairo_ps_surface_get_font (cairo_ps_surface_t  *surface,
00387                          cairo_scaled_font_t *scaled_font)
00388 {
00389     cairo_unscaled_font_t *unscaled_font;
00390     cairo_font_subset_t *subset;
00391     unsigned int num_fonts, i;
00392 
00393     /* XXX: Need to fix this to work with a general cairo_scaled_font_t. */
00394     if (! _cairo_scaled_font_is_ft (scaled_font))
00395        return NULL;
00396 
00397     /* XXX Why is this an ft specific function? */
00398     unscaled_font = _cairo_ft_scaled_font_get_unscaled_font (scaled_font);
00399 
00400     num_fonts = _cairo_array_num_elements (&surface->fonts);
00401     for (i = 0; i < num_fonts; i++) {
00402        _cairo_array_copy_element (&surface->fonts, i, &subset);
00403        if (subset->unscaled_font == unscaled_font)
00404            return subset;
00405     }
00406 
00407     subset = _cairo_font_subset_create (unscaled_font);
00408     if (subset == NULL)
00409        return NULL;
00410 
00411     subset->font_id = surface->fonts.num_elements;
00412     if (_cairo_array_append (&surface->fonts, &subset, 1) == NULL) {
00413        _cairo_font_subset_destroy (subset);
00414        return NULL;
00415     }
00416 
00417     return subset;
00418 }
00419 
00420 static cairo_int_status_t
00421 _cairo_ps_surface_show_glyphs (cairo_scaled_font_t      *scaled_font,
00422                             cairo_operator_t            operator,
00423                             cairo_pattern_t             *pattern,
00424                             void                 *abstract_surface,
00425                             int                  source_x,
00426                             int                  source_y,
00427                             int                  dest_x,
00428                             int                  dest_y,
00429                             unsigned int         width,
00430                             unsigned int         height,
00431                             const cairo_glyph_t  *glyphs,
00432                             int                  num_glyphs)
00433 {
00434     cairo_ps_surface_t *surface = abstract_surface;
00435     cairo_font_subset_t *subset;
00436     int i;
00437 
00438     /* XXX: Need to fix this to work with a general cairo_scaled_font_t. */
00439     if (! _cairo_scaled_font_is_ft (scaled_font))
00440        return CAIRO_INT_STATUS_UNSUPPORTED;
00441 
00442     /* Collect font subset info as we go. */
00443     subset = _cairo_ps_surface_get_font (surface, scaled_font);
00444     if (subset == NULL)
00445        return CAIRO_STATUS_NO_MEMORY;
00446 
00447     for (i = 0; i < num_glyphs; i++)
00448        _cairo_font_subset_use_glyph (subset, glyphs[i].index);
00449 
00450     return _cairo_surface_show_glyphs (scaled_font,
00451                                    operator,
00452                                    pattern,
00453                                    surface->current_page,
00454                                    source_x,
00455                                    source_y,
00456                                    dest_x,
00457                                    dest_y,
00458                                    width,
00459                                    height,
00460                                    glyphs,
00461                                    num_glyphs);
00462 }
00463 
00464 static cairo_int_status_t
00465 _cairo_ps_surface_fill_path (cairo_operator_t    operator,
00466                           cairo_pattern_t *pattern,
00467                           void            *abstract_dst,
00468                           cairo_path_fixed_t     *path,
00469                           cairo_fill_rule_t   fill_rule,
00470                           double           tolerance)
00471 {
00472     cairo_ps_surface_t *surface = abstract_dst;
00473 
00474     return _cairo_surface_fill_path (operator,
00475                                  pattern,
00476                                  surface->current_page,
00477                                  path,
00478                                  fill_rule,
00479                                  tolerance);
00480 }
00481 
00482 static const cairo_surface_backend_t cairo_ps_surface_backend = {
00483     NULL, /* create_similar */
00484     _cairo_ps_surface_finish,
00485     NULL, /* acquire_source_image */
00486     NULL, /* release_source_image */
00487     NULL, /* acquire_dest_image */
00488     NULL, /* release_dest_image */
00489     NULL, /* clone_similar */
00490     _cairo_ps_surface_composite,
00491     _cairo_ps_surface_fill_rectangles,
00492     _cairo_ps_surface_composite_trapezoids,
00493     _cairo_ps_surface_copy_page,
00494     _cairo_ps_surface_show_page,
00495     NULL, /* set_clip_region */
00496     _cairo_ps_surface_intersect_clip_path,
00497     _cairo_ps_surface_get_extents,
00498     _cairo_ps_surface_show_glyphs,
00499     _cairo_ps_surface_fill_path
00500 };
00501 
00502 static cairo_int_status_t
00503 _cairo_ps_surface_write_type42_dict (cairo_ps_surface_t  *surface,
00504                                  cairo_font_subset_t *subset)
00505 {
00506     const char *data;
00507     unsigned long data_size;
00508     cairo_status_t status;
00509     int i;
00510 
00511     status = CAIRO_STATUS_SUCCESS;
00512 
00513     /* FIXME: Figure out document structure convention for fonts */
00514 
00515     _cairo_output_stream_printf (surface->stream,
00516                              "11 dict begin\n"
00517                              "/FontType 42 def\n"
00518                              "/FontName /f%d def\n"
00519                              "/PaintType 0 def\n"
00520                              "/FontMatrix [ 1 0 0 1 0 0 ] def\n"
00521                              "/FontBBox [ 0 0 0 0 ] def\n"
00522                              "/Encoding 256 array def\n"
00523                              "0 1 255 { Encoding exch /.notdef put } for\n",
00524                              subset->font_id);
00525 
00526     /* FIXME: Figure out how subset->x_max etc maps to the /FontBBox */
00527 
00528     for (i = 1; i < subset->num_glyphs; i++)
00529        _cairo_output_stream_printf (surface->stream,
00530                                  "Encoding %d /g%d put\n", i, i);
00531 
00532     _cairo_output_stream_printf (surface->stream,
00533                              "/CharStrings %d dict dup begin\n"
00534                              "/.notdef 0 def\n",
00535                              subset->num_glyphs);
00536 
00537     for (i = 1; i < subset->num_glyphs; i++)
00538        _cairo_output_stream_printf (surface->stream,
00539                                  "/g%d %d def\n", i, i);
00540 
00541     _cairo_output_stream_printf (surface->stream,
00542                              "end readonly def\n");
00543 
00544     status = _cairo_font_subset_generate (subset, &data, &data_size);
00545 
00546     /* FIXME: We need to break up fonts bigger than 64k so we don't
00547      * exceed string size limitation.  At glyph boundaries.  Stupid
00548      * postscript. */
00549     _cairo_output_stream_printf (surface->stream,
00550                              "/sfnts [<");
00551 
00552     _cairo_output_stream_write_hex_string (surface->stream, data, data_size);
00553 
00554     _cairo_output_stream_printf (surface->stream,
00555                              ">] def\n"
00556                              "FontName currentdict end definefont pop\n");
00557 
00558     return CAIRO_STATUS_SUCCESS;
00559 }
00560 
00561 static cairo_int_status_t
00562 _cairo_ps_surface_write_font_subsets (cairo_ps_surface_t *surface)
00563 {
00564     cairo_font_subset_t *subset;
00565     int i;
00566 
00567     for (i = 0; i < surface->fonts.num_elements; i++) {
00568        _cairo_array_copy_element (&surface->fonts, i, &subset);
00569        _cairo_ps_surface_write_type42_dict (surface, subset);
00570     }
00571 
00572     return CAIRO_STATUS_SUCCESS;
00573 }
00574 
00575 typedef struct _cairo_ps_fallback_area cairo_ps_fallback_area_t;
00576 struct _cairo_ps_fallback_area {
00577     int x, y;
00578     unsigned int width, height;
00579     cairo_ps_fallback_area_t *next;
00580 };
00581 
00582 typedef struct _ps_output_surface {
00583     cairo_surface_t base;
00584     cairo_ps_surface_t *parent;
00585     cairo_ps_fallback_area_t *fallback_areas;
00586 } ps_output_surface_t;
00587 
00588 static cairo_int_status_t
00589 _ps_output_add_fallback_area (ps_output_surface_t *surface,
00590                            int x, int y,
00591                            unsigned int width,
00592                            unsigned int height)
00593 {
00594     cairo_ps_fallback_area_t *area;
00595     
00596     /* FIXME: Do a better job here.  Ideally, we would use a 32 bit
00597      * region type, but probably just computing bounding boxes would
00598      * also work fine. */
00599 
00600     area = malloc (sizeof (cairo_ps_fallback_area_t));
00601     if (area == NULL)
00602        return CAIRO_STATUS_NO_MEMORY;
00603 
00604     area->x = x;
00605     area->y = y;
00606     area->width = width;
00607     area->height = height;
00608     area->next = surface->fallback_areas;
00609 
00610     surface->fallback_areas = area;
00611 
00612     return CAIRO_STATUS_SUCCESS;
00613 }
00614 
00615 static cairo_status_t
00616 _ps_output_finish (void *abstract_surface)
00617 {
00618     ps_output_surface_t *surface = abstract_surface;
00619     cairo_ps_fallback_area_t *area, *next;
00620 
00621     for (area = surface->fallback_areas; area != NULL; area = next) {
00622        next = area->next;
00623        free (area);
00624     }
00625 
00626     return CAIRO_STATUS_SUCCESS;
00627 }
00628 
00629 static cairo_bool_t
00630 color_is_gray (cairo_color_t *color)
00631 {
00632     const double epsilon = 0.00001;
00633 
00634     return (fabs (color->red - color->green) < epsilon &&
00635            fabs (color->red - color->blue) < epsilon);
00636 }
00637 
00638 static cairo_bool_t
00639 color_is_translucent (const cairo_color_t *color)
00640 {
00641     return color->alpha < 0.999;
00642 }
00643 
00644 static cairo_bool_t
00645 pattern_is_translucent (cairo_pattern_t *abstract_pattern)
00646 {
00647     cairo_pattern_union_t *pattern;
00648 
00649     pattern = (cairo_pattern_union_t *) abstract_pattern;
00650     switch (pattern->base.type) {
00651     case CAIRO_PATTERN_SOLID:
00652        return color_is_translucent (&pattern->solid.color);
00653     case CAIRO_PATTERN_SURFACE:
00654     case CAIRO_PATTERN_LINEAR:
00655     case CAIRO_PATTERN_RADIAL:
00656        return FALSE;
00657     }  
00658 
00659     ASSERT_NOT_REACHED;
00660     return FALSE;
00661 }
00662 
00663 /* PS Output - this section handles output of the parts of the meta
00664  * surface we can render natively in PS. */
00665 
00666 static void *
00667 compress_dup (const void *data, unsigned long data_size,
00668              unsigned long *compressed_size)
00669 {
00670     void *compressed;
00671 
00672     /* Bound calculation taken from zlib. */
00673     *compressed_size = data_size + (data_size >> 12) + (data_size >> 14) + 11;
00674     compressed = malloc (*compressed_size);
00675     if (compressed == NULL)
00676        return NULL;
00677 
00678     compress (compressed, compressed_size, data, data_size);
00679 
00680     return compressed;
00681 }
00682 
00683 static cairo_status_t
00684 emit_image (cairo_ps_surface_t    *surface,
00685            cairo_image_surface_t *image,
00686            cairo_matrix_t     *matrix)
00687 {
00688     cairo_status_t status;
00689     unsigned char *rgb, *compressed;
00690     unsigned long rgb_size, compressed_size;
00691     cairo_surface_t *opaque;
00692     cairo_image_surface_t *opaque_image;
00693     cairo_pattern_union_t pattern;
00694     cairo_matrix_t d2i;
00695     int x, y, i;
00696 
00697     /* PostScript can not represent the alpha channel, so we blend the
00698        current image over a white RGB surface to eliminate it. */
00699 
00700     if (image->base.status)
00701        return image->base.status;
00702 
00703     if (image->format != CAIRO_FORMAT_RGB24) {
00704        opaque = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
00705                                         image->width,
00706                                         image->height);
00707        if (opaque->status) {
00708            status = CAIRO_STATUS_NO_MEMORY;
00709            goto bail0;
00710        }
00711     
00712        _cairo_pattern_init_for_surface (&pattern.surface, &image->base);
00713     
00714        _cairo_surface_composite (CAIRO_OPERATOR_DEST_OVER,
00715                               &pattern.base,
00716                               NULL,
00717                               opaque,
00718                               0, 0,
00719                               0, 0,
00720                               0, 0,
00721                               image->width,
00722                               image->height);
00723     
00724        _cairo_pattern_fini (&pattern.base);
00725        opaque_image = (cairo_image_surface_t *) opaque;
00726     } else {
00727        opaque = &image->base;
00728        opaque_image = image;
00729     }
00730 
00731     rgb_size = 3 * opaque_image->width * opaque_image->height;
00732     rgb = malloc (rgb_size);
00733     if (rgb == NULL) {
00734        status = CAIRO_STATUS_NO_MEMORY;
00735        goto bail1;
00736     }
00737 
00738     i = 0;
00739     for (y = 0; y < opaque_image->height; y++) {
00740        pixman_bits_t *pixel = (pixman_bits_t *) (opaque_image->data + y * opaque_image->stride);
00741        for (x = 0; x < opaque_image->width; x++, pixel++) {
00742            rgb[i++] = (*pixel & 0x00ff0000) >> 16;
00743            rgb[i++] = (*pixel & 0x0000ff00) >>  8;
00744            rgb[i++] = (*pixel & 0x000000ff) >>  0;
00745        }
00746     }
00747 
00748     compressed = compress_dup (rgb, rgb_size, &compressed_size);
00749     if (compressed == NULL) {
00750        status = CAIRO_STATUS_NO_MEMORY;
00751        goto bail2;
00752     }
00753 
00754     /* matrix transforms from user space to image space.  We need to
00755      * transform from device space to image space to compensate for
00756      * postscripts coordinate system. */
00757     cairo_matrix_init (&d2i, 1, 0, 0, 1, 0, 0);
00758     cairo_matrix_multiply (&d2i, &d2i, matrix);
00759 
00760     _cairo_output_stream_printf (surface->stream,
00761                              "/DeviceRGB setcolorspace\n"
00762                              "<<\n"
00763                              "     /ImageType 1\n"
00764                              "     /Width %d\n"
00765                              "     /Height %d\n"
00766                              "     /BitsPerComponent 8\n"
00767                              "     /Decode [ 0 1 0 1 0 1 ]\n"
00768                              "     /DataSource currentfile\n"
00769                              "     /ImageMatrix [ %f %f %f %f %f %f ]\n"
00770                              ">>\n"
00771                              "image\n",
00772                              opaque_image->width,
00773                              opaque_image->height,
00774                              d2i.xx, d2i.yx,
00775                              d2i.xy, d2i.yy,
00776                              d2i.x0, d2i.y0);
00777 
00778     /* Compressed image data */
00779     _cairo_output_stream_write (surface->stream, rgb, rgb_size);
00780     status = CAIRO_STATUS_SUCCESS;
00781 
00782     _cairo_output_stream_printf (surface->stream,
00783                              "\n");
00784 
00785     free (compressed);
00786  bail2:
00787     free (rgb);
00788  bail1:
00789     if (opaque_image != image)
00790        cairo_surface_destroy (opaque);
00791  bail0:
00792     return status;
00793 }
00794 
00795 static void
00796 emit_solid_pattern (cairo_ps_surface_t *surface,
00797                   cairo_solid_pattern_t *pattern)
00798 {
00799     if (color_is_gray (&pattern->color))
00800        _cairo_output_stream_printf (surface->stream,
00801                                  "%f setgray\n",
00802                                  pattern->color.red);
00803     else
00804        _cairo_output_stream_printf (surface->stream,
00805                                  "%f %f %f setrgbcolor\n",
00806                                  pattern->color.red,
00807                                  pattern->color.green,
00808                                  pattern->color.blue);
00809 }
00810 
00811 static void
00812 emit_surface_pattern (cairo_ps_surface_t *surface,
00813                     cairo_surface_pattern_t *pattern)
00814 {
00815 }
00816 
00817 static void
00818 emit_linear_pattern (cairo_ps_surface_t *surface,
00819                    cairo_linear_pattern_t *pattern)
00820 {
00821 }
00822 
00823 static void
00824 emit_radial_pattern (cairo_ps_surface_t *surface,
00825                    cairo_radial_pattern_t *pattern)
00826 {
00827 }
00828 
00829 static void
00830 emit_pattern (cairo_ps_surface_t *surface, cairo_pattern_t *pattern)
00831 {
00832     /* FIXME: We should keep track of what pattern is currently set in
00833      * the postscript file and only emit code if we're setting a
00834      * different pattern. */
00835 
00836     switch (pattern->type) {
00837     case CAIRO_PATTERN_SOLID:      
00838        emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern);
00839        break;
00840 
00841     case CAIRO_PATTERN_SURFACE:
00842        emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern);
00843        break;
00844 
00845     case CAIRO_PATTERN_LINEAR:
00846        emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern);
00847        break;
00848 
00849     case CAIRO_PATTERN_RADIAL:
00850        emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern);
00851        break;     
00852     }
00853 }
00854 
00855 
00856 static cairo_int_status_t
00857 _ps_output_composite (cairo_operator_t    operator,
00858                     cairo_pattern_t  *src_pattern,
00859                     cairo_pattern_t  *mask_pattern,
00860                     void           *abstract_dst,
00861                     int            src_x,
00862                     int            src_y,
00863                     int            mask_x,
00864                     int            mask_y,
00865                     int            dst_x,
00866                     int            dst_y,
00867                     unsigned int   width,
00868                     unsigned int   height)
00869 {
00870     ps_output_surface_t *surface = abstract_dst;
00871     cairo_output_stream_t *stream = surface->parent->stream;
00872     cairo_surface_pattern_t *surface_pattern;
00873     cairo_status_t status;
00874     cairo_image_surface_t *image;
00875     void *image_extra;
00876 
00877     if (mask_pattern) {
00878        /* FIXME: Investigate how this can be done... we'll probably
00879         * need pixmap fallbacks for this, though. */
00880        _cairo_output_stream_printf (stream,
00881                                  "%% _ps_output_composite: with mask\n");
00882        goto bail;
00883     }
00884 
00885     status = CAIRO_STATUS_SUCCESS;
00886     switch (src_pattern->type) {
00887     case CAIRO_PATTERN_SOLID:
00888        _cairo_output_stream_printf (stream,
00889                                  "%% _ps_output_composite: solid\n");
00890        goto bail;
00891 
00892     case CAIRO_PATTERN_SURFACE:
00893        surface_pattern = (cairo_surface_pattern_t *) src_pattern;
00894 
00895        if (src_pattern->extend != CAIRO_EXTEND_NONE) {
00896            _cairo_output_stream_printf (stream,
00897                                     "%% _ps_output_composite: repeating image\n");
00898            goto bail;
00899        }
00900            
00901 
00902        status = _cairo_surface_acquire_source_image (surface_pattern->surface,
00903                                                 &image,
00904                                                 &image_extra);
00905        if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
00906            _cairo_output_stream_printf (stream,
00907                                     "%% _ps_output_composite: src_pattern not available as image\n");
00908            goto bail;
00909        } else if (status) {
00910            break;
00911        }
00912        status = emit_image (surface->parent, image, &src_pattern->matrix);
00913        _cairo_surface_release_source_image (surface_pattern->surface,
00914                                         image, image_extra);
00915        break;
00916 
00917     case CAIRO_PATTERN_LINEAR:
00918     case CAIRO_PATTERN_RADIAL:
00919        _cairo_output_stream_printf (stream,
00920                                  "%% _ps_output_composite: gradient\n");
00921        goto bail;
00922     }
00923 
00924     return status;
00925 bail:
00926     return _ps_output_add_fallback_area (surface, dst_x, dst_y, width, height);
00927 }
00928 
00929 static cairo_int_status_t
00930 _ps_output_fill_rectangles (void          *abstract_surface,
00931                          cairo_operator_t  operator,
00932                          const cairo_color_t     *color,
00933                          cairo_rectangle_t       *rects,
00934                          int                      num_rects)
00935 {
00936     ps_output_surface_t *surface = abstract_surface;
00937     cairo_output_stream_t *stream = surface->parent->stream;
00938     cairo_solid_pattern_t solid;
00939     int i;
00940 
00941     if (!num_rects)
00942        return CAIRO_STATUS_SUCCESS;
00943     
00944     if (color_is_translucent (color)) {
00945        int min_x = rects[0].x;
00946        int min_y = rects[0].y;
00947        int max_x = rects[0].x + rects[0].width;
00948        int max_y = rects[0].y + rects[0].height;
00949 
00950        for (i = 1; i < num_rects; i++) {
00951            if (rects[i].x < min_x) min_x = rects[i].x;
00952            if (rects[i].y < min_y) min_y = rects[i].y;
00953            if (rects[i].x + rects[i].width > max_x) max_x = rects[i].x + rects[i].width;
00954            if (rects[i].y + rects[i].height > max_y) max_y = rects[i].y + rects[i].height;
00955        }
00956        return _ps_output_add_fallback_area (surface, min_x, min_y, max_x - min_x, max_y - min_y);
00957     }
00958        
00959     _cairo_output_stream_printf (stream,
00960                              "%% _ps_output_fill_rectangles\n");
00961 
00962     _cairo_pattern_init_solid (&solid, color);
00963     emit_pattern (surface->parent, &solid.base);
00964     _cairo_pattern_fini (&solid.base);
00965 
00966     _cairo_output_stream_printf (stream, "[");
00967     for (i = 0; i < num_rects; i++) {
00968       _cairo_output_stream_printf (stream,
00969                                " %d %d %d %d",
00970                                rects[i].x, rects[i].y,
00971                                rects[i].width, rects[i].height);
00972     }
00973 
00974     _cairo_output_stream_printf (stream, " ] rectfill\n");
00975 
00976     return CAIRO_STATUS_SUCCESS;
00977 }
00978 
00979 static double
00980 intersect (cairo_line_t *line, cairo_fixed_t y)
00981 {
00982     return _cairo_fixed_to_double (line->p1.x) +
00983        _cairo_fixed_to_double (line->p2.x - line->p1.x) *
00984        _cairo_fixed_to_double (y - line->p1.y) /
00985        _cairo_fixed_to_double (line->p2.y - line->p1.y);
00986 }
00987 
00988 static cairo_int_status_t
00989 _ps_output_composite_trapezoids (cairo_operator_t       operator,
00990                              cairo_pattern_t     *pattern,
00991                              void                *abstract_dst,
00992                              cairo_antialias_t   antialias,
00993                              int                 x_src,
00994                              int                 y_src,
00995                              int                 x_dst,
00996                              int                 y_dst,
00997                              unsigned int        width,
00998                              unsigned int        height,
00999                              cairo_trapezoid_t   *traps,
01000                              int                 num_traps)
01001 {
01002     ps_output_surface_t *surface = abstract_dst;
01003     cairo_output_stream_t *stream = surface->parent->stream;
01004     int i;
01005 
01006     if (pattern_is_translucent (pattern))
01007        return _ps_output_add_fallback_area (surface, x_dst, y_dst, width, height);
01008 
01009     _cairo_output_stream_printf (stream,
01010                              "%% _ps_output_composite_trapezoids\n");
01011 
01012     emit_pattern (surface->parent, pattern);
01013 
01014     for (i = 0; i < num_traps; i++) {
01015        double left_x1, left_x2, right_x1, right_x2, top, bottom;
01016 
01017        left_x1  = intersect (&traps[i].left, traps[i].top);
01018        left_x2  = intersect (&traps[i].left, traps[i].bottom);
01019        right_x1 = intersect (&traps[i].right, traps[i].top);
01020        right_x2 = intersect (&traps[i].right, traps[i].bottom);
01021        top      = _cairo_fixed_to_double (traps[i].top);
01022        bottom   = _cairo_fixed_to_double (traps[i].bottom);
01023 
01024        _cairo_output_stream_printf
01025            (stream,
01026             "%f %f moveto %f %f lineto %f %f lineto %f %f lineto "
01027             "closepath\n",
01028             left_x1, top,
01029             left_x2, bottom,
01030             right_x2, bottom,
01031             right_x1, top);
01032     }
01033 
01034     _cairo_output_stream_printf (stream,
01035                              "fill\n");
01036 
01037     return CAIRO_STATUS_SUCCESS;
01038 }
01039 
01040 typedef struct
01041 {
01042     cairo_output_stream_t *output_stream;
01043     cairo_bool_t has_current_point;
01044 } ps_output_path_info_t;
01045 
01046 static cairo_status_t
01047 _ps_output_path_move_to (void *closure, cairo_point_t *point)
01048 {
01049     ps_output_path_info_t *info = closure;
01050 
01051     _cairo_output_stream_printf (info->output_stream,
01052                              "%f %f moveto ",
01053                              _cairo_fixed_to_double (point->x),
01054                              _cairo_fixed_to_double (point->y));
01055     info->has_current_point = TRUE;
01056     
01057     return CAIRO_STATUS_SUCCESS;
01058 }
01059 
01060 static cairo_status_t
01061 _ps_output_path_line_to (void *closure, cairo_point_t *point)
01062 {
01063     ps_output_path_info_t *info = closure;
01064     const char *ps_operator;
01065 
01066     if (info->has_current_point)
01067        ps_operator = "lineto";
01068     else
01069        ps_operator = "moveto";
01070     
01071     _cairo_output_stream_printf (info->output_stream,
01072                              "%f %f %s ",
01073                              _cairo_fixed_to_double (point->x),
01074                              _cairo_fixed_to_double (point->y),
01075                              ps_operator);
01076     info->has_current_point = TRUE;
01077 
01078     return CAIRO_STATUS_SUCCESS;
01079 }
01080 
01081 static cairo_status_t
01082 _ps_output_path_curve_to (void          *closure,
01083                        cairo_point_t *b,
01084                        cairo_point_t *c,
01085                        cairo_point_t *d)
01086 {
01087     ps_output_path_info_t *info = closure;
01088 
01089     _cairo_output_stream_printf (info->output_stream,
01090                              "%f %f %f %f %f %f curveto ",
01091                              _cairo_fixed_to_double (b->x),
01092                              _cairo_fixed_to_double (b->y),
01093                              _cairo_fixed_to_double (c->x),
01094                              _cairo_fixed_to_double (c->y),
01095                              _cairo_fixed_to_double (d->x),
01096                              _cairo_fixed_to_double (d->y));
01097     
01098     return CAIRO_STATUS_SUCCESS;
01099 }
01100 
01101 static cairo_status_t
01102 _ps_output_path_close_path (void *closure)
01103 {
01104     ps_output_path_info_t *info = closure;
01105     
01106     _cairo_output_stream_printf (info->output_stream,
01107                              "closepath\n");
01108     info->has_current_point = FALSE;
01109 
01110     return CAIRO_STATUS_SUCCESS;
01111 }
01112 
01113 static cairo_int_status_t
01114 _ps_output_intersect_clip_path (void                *abstract_surface,
01115                             cairo_path_fixed_t *path,
01116                             cairo_fill_rule_t   fill_rule,
01117                             double            tolerance,
01118                             cairo_antialias_t   antialias)
01119 {
01120     ps_output_surface_t *surface = abstract_surface;
01121     cairo_output_stream_t *stream = surface->parent->stream;
01122     cairo_status_t status;
01123     ps_output_path_info_t info;
01124     const char *ps_operator;
01125 
01126     _cairo_output_stream_printf (stream,
01127                              "%% _ps_output_intersect_clip_path\n");
01128 
01129     if (path == NULL) {
01130        _cairo_output_stream_printf (stream, "initclip\n");
01131        return CAIRO_STATUS_SUCCESS;
01132     }
01133 
01134     info.output_stream = stream;
01135     info.has_current_point = FALSE;
01136 
01137     status = _cairo_path_fixed_interpret (path,
01138                                      CAIRO_DIRECTION_FORWARD,
01139                                      _ps_output_path_move_to,
01140                                      _ps_output_path_line_to,
01141                                      _ps_output_path_curve_to,
01142                                      _ps_output_path_close_path,
01143                                      &info);
01144 
01145     switch (fill_rule) {
01146     case CAIRO_FILL_RULE_WINDING:
01147        ps_operator = "clip";
01148        break;
01149     case CAIRO_FILL_RULE_EVEN_ODD:
01150        ps_operator = "eoclip";
01151        break;
01152     default:
01153        ASSERT_NOT_REACHED;
01154     }
01155 
01156     _cairo_output_stream_printf (stream,
01157                              "%s newpath\n",
01158                              ps_operator);
01159 
01160     return status;
01161 }
01162 
01163 
01164 static cairo_int_status_t
01165 _ps_output_show_glyphs (cairo_scaled_font_t      *scaled_font,
01166                      cairo_operator_t     operator,
01167                      cairo_pattern_t             *pattern,
01168                      void                 *abstract_surface,
01169                      int                  source_x,
01170                      int                  source_y,
01171                      int                  dest_x,
01172                      int                  dest_y,
01173                      unsigned int         width,
01174                      unsigned int         height,
01175                      const cairo_glyph_t  *glyphs,
01176                      int                  num_glyphs)
01177 {
01178     ps_output_surface_t *surface = abstract_surface;
01179     cairo_output_stream_t *stream = surface->parent->stream;
01180     cairo_font_subset_t *subset;
01181     int i, subset_index;
01182 
01183     /* XXX: Need to fix this to work with a general cairo_scaled_font_t. */
01184     if (! _cairo_scaled_font_is_ft (scaled_font))
01185        return CAIRO_INT_STATUS_UNSUPPORTED;
01186 
01187     if (pattern_is_translucent (pattern))
01188        return _ps_output_add_fallback_area (surface, dest_x, dest_y, width, height);
01189 
01190     _cairo_output_stream_printf (stream,
01191                              "%% _ps_output_show_glyphs\n");
01192 
01193     emit_pattern (surface->parent, pattern);
01194 
01195     /* FIXME: Need to optimize this so we only do this sequence if the
01196      * font isn't already set. */
01197 
01198     subset = _cairo_ps_surface_get_font (surface->parent, scaled_font);
01199     _cairo_output_stream_printf (stream,
01200                              "/f%d findfont\n"
01201                              "[ %f %f %f %f 0 0 ] makefont\n"
01202                              "setfont\n",
01203                              subset->font_id,
01204                              scaled_font->scale.xx,
01205                              scaled_font->scale.yx,
01206                              scaled_font->scale.xy,
01207                              -scaled_font->scale.yy);
01208 
01209     /* FIXME: Need to optimize per glyph code.  Should detect when
01210      * glyphs share the same baseline and when the spacing corresponds
01211      * to the glyph widths. */
01212 
01213     for (i = 0; i < num_glyphs; i++) {
01214        subset_index = _cairo_font_subset_use_glyph (subset, glyphs[i].index);
01215        _cairo_output_stream_printf (stream,
01216                                  "%f %f moveto (\\%o) show\n",
01217                                  glyphs[i].x,
01218                                  glyphs[i].y,
01219                                  subset_index);
01220        
01221     }
01222 
01223     return CAIRO_STATUS_SUCCESS;
01224 }
01225 
01226 static cairo_int_status_t
01227 _ps_output_fill_path (cairo_operator_t      operator,
01228                     cairo_pattern_t        *pattern,
01229                     void            *abstract_dst,
01230                     cairo_path_fixed_t *path,
01231                     cairo_fill_rule_t   fill_rule,
01232                     double           tolerance)
01233 {
01234     ps_output_surface_t *surface = abstract_dst;
01235     cairo_output_stream_t *stream = surface->parent->stream;
01236     cairo_int_status_t status;
01237     ps_output_path_info_t info;
01238     const char *ps_operator;
01239 
01240     if (pattern_is_translucent (pattern))
01241        return _ps_output_add_fallback_area (surface,
01242                                         0, 0,
01243                                         surface->parent->width,
01244                                         surface->parent->height);
01245     _cairo_output_stream_printf (stream,
01246                              "%% _ps_output_fill_path\n");
01247 
01248     emit_pattern (surface->parent, pattern);
01249 
01250     info.output_stream = stream;
01251     info.has_current_point = FALSE;
01252 
01253     status = _cairo_path_fixed_interpret (path,
01254                                      CAIRO_DIRECTION_FORWARD,
01255                                      _ps_output_path_move_to,
01256                                      _ps_output_path_line_to,
01257                                      _ps_output_path_curve_to,
01258                                      _ps_output_path_close_path,
01259                                      &info);
01260 
01261     switch (fill_rule) {
01262     case CAIRO_FILL_RULE_WINDING:
01263        ps_operator = "fill";
01264        break;
01265     case CAIRO_FILL_RULE_EVEN_ODD:
01266        ps_operator = "eofill";
01267        break;
01268     default:
01269        ASSERT_NOT_REACHED;
01270     }
01271 
01272     _cairo_output_stream_printf (stream,
01273                              "%s\n", ps_operator);
01274 
01275     return status;
01276 }
01277 
01278 static const cairo_surface_backend_t ps_output_backend = {
01279     NULL, /* create_similar */
01280     _ps_output_finish,
01281     NULL, /* acquire_source_image */
01282     NULL, /* release_source_image */
01283     NULL, /* acquire_dest_image */
01284     NULL, /* release_dest_image */
01285     NULL, /* clone_similar */
01286     _ps_output_composite,
01287     _ps_output_fill_rectangles,
01288     _ps_output_composite_trapezoids,
01289     NULL, /* copy_page */
01290     NULL, /* show_page */
01291     NULL, /* set_clip_region */
01292     _ps_output_intersect_clip_path,
01293     NULL, /* get_extents */
01294     _ps_output_show_glyphs,
01295     _ps_output_fill_path
01296 };
01297 
01298 static cairo_int_status_t
01299 _ps_output_render_fallbacks (cairo_surface_t *surface,
01300                           cairo_surface_t *page)
01301 {
01302     ps_output_surface_t *ps_output;
01303     cairo_surface_t *image;
01304     cairo_int_status_t status;
01305     cairo_matrix_t matrix;
01306     int width, height;
01307 
01308     ps_output = (ps_output_surface_t *) surface;
01309     if (ps_output->fallback_areas == NULL)
01310        return CAIRO_STATUS_SUCCESS;
01311 
01312     width = ps_output->parent->width * ps_output->parent->x_dpi / 72;
01313     height = ps_output->parent->height * ps_output->parent->y_dpi / 72;
01314 
01315     image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
01316     if (image->status)
01317        return CAIRO_STATUS_NO_MEMORY;
01318 
01319     status = _cairo_surface_fill_rectangle (image,
01320                                        CAIRO_OPERATOR_SOURCE,
01321                                        CAIRO_COLOR_WHITE,
01322                                        0, 0, width, height);
01323     if (status)
01324        goto bail;
01325 
01326     status = _cairo_meta_surface_replay (page, image);
01327     if (status)
01328        goto bail;
01329 
01330     matrix.xx = 1;
01331     matrix.xy = 0;
01332     matrix.yx = 0;
01333     matrix.yy = 1;
01334     matrix.x0 = 0;
01335     matrix.y0 = 0;
01336 
01337     status = emit_image (ps_output->parent,
01338                       (cairo_image_surface_t *) image, &matrix);
01339 
01340  bail:
01341     cairo_surface_destroy (image);
01342 
01343     return status;
01344 }
01345 
01346 static cairo_surface_t *
01347 _ps_output_surface_create (cairo_ps_surface_t *parent)
01348 {
01349     ps_output_surface_t *ps_output;
01350 
01351     ps_output = malloc (sizeof (ps_output_surface_t));
01352     if (ps_output == NULL) {
01353        _cairo_error (CAIRO_STATUS_NO_MEMORY);
01354        return (cairo_surface_t*) &_cairo_surface_nil;
01355     }
01356 
01357     _cairo_surface_init (&ps_output->base, &ps_output_backend);
01358     ps_output->parent = parent;
01359     ps_output->fallback_areas = NULL;
01360 
01361     return &ps_output->base;
01362 }
01363 
01364 static cairo_int_status_t
01365 _cairo_ps_surface_render_page (cairo_ps_surface_t *surface,
01366                             cairo_surface_t *page, int page_number)
01367 {
01368     cairo_surface_t *ps_output;
01369     cairo_int_status_t status;
01370 
01371     _cairo_output_stream_printf (surface->stream,
01372                              "%%%%Page: %d\n"
01373                              "gsave %f %f translate %f %f scale \n",
01374                              page_number,
01375                              0.0, surface->height,
01376                              1.0/surface->base.device_x_scale,
01377                              -1.0/surface->base.device_y_scale);
01378 
01379     ps_output = _ps_output_surface_create (surface);
01380     if (ps_output->status)
01381        return CAIRO_STATUS_NO_MEMORY;
01382 
01383     status = _cairo_meta_surface_replay (page, ps_output);
01384 
01385     _ps_output_render_fallbacks (ps_output, page);
01386 
01387     cairo_surface_destroy (ps_output);
01388 
01389     _cairo_output_stream_printf (surface->stream,
01390                              "showpage\n"
01391                              "grestore\n"
01392                              "%%%%EndPage\n");
01393 
01394     return status;
01395 }