Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/gfx/cairo/cairo/src/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 167 kB image not shown  

Quelle  cairo-ps-surface.c   Sprache: C

 
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
/* cairo - a vector graphics library with display and print output
 *
 * Copyright © 2003 University of Southern California
 * Copyright © 2005 Red Hat, Inc
 * Copyright © 2007,2008 Adrian Johnson
 *
 * 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 University of Southern
 * California.
 *
 * Contributor(s):
 * Carl D. Worth <cworth@cworth.org>
 * Kristian Høgsberg <krh@redhat.com>
 * Keith Packard <keithp@keithp.com>
 * Adrian Johnson <ajohnson@redneon.com>
 */



/*
 * Design of the PS output:
 *
 * The PS output is harmonised with the PDF operations using PS procedures
 * to emulate the PDF operators.
 *
 * This has a number of advantages:
 *   1. A large chunk of code is shared between the PDF and PS backends.
 *      See cairo-pdf-operators.
 *   2. Using gs to do PS -> PDF and PDF -> PS will always work well.
 */


#define _DEFAULT_SOURCE /* for ctime_r(), snprintf(), strdup() */
#include "cairoint.h"

#include "cairo-ps.h"
#include "cairo-ps-surface-private.h"

#include "cairo-pdf-operators-private.h"
#include "cairo-pdf-shading-private.h"

#include "cairo-array-private.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-image-info-private.h"
#include "cairo-image-surface-inline.h"
#include "cairo-list-inline.h"
#include "cairo-output-stream-private.h"
#include "cairo-paginated-private.h"
#include "cairo-recording-surface-inline.h"
#include "cairo-recording-surface-private.h"
#include "cairo-scaled-font-subsets-private.h"
#include "cairo-surface-clipper-private.h"
#include "cairo-surface-snapshot-inline.h"
#include "cairo-surface-subsurface-private.h"
#include "cairo-tag-attributes-private.h"
#include "cairo-type3-glyph-surface-private.h"

#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include <zlib.h>
#include <errno.h>

/* Forms are emitted at the start and stored in memory so we limit the
 * total size of all forms to prevent running out of memory. If this
 * limit is exceeded, surfaces that would be stored in forms are
 * emitted each time the surface is used. */

#define MAX_L2_FORM_DATA (256*1024)
#define MAX_L3_FORM_DATA (2*1024*1024) /* Assume Level 3 printers have more memory */

/* #define DEBUG_PS 1 */

#if DEBUG_PS
#define DEBUG_FALLBACK(s) \
    fprintf (stderr, "%s::%d -- %s\n", __FUNCTION__, __LINE__, (s))
#else
#define DEBUG_FALLBACK(s)
#endif

#ifndef HAVE_CTIME_R
static char *ctime_r(const time_t *timep, char *buf)
{
    (void)buf;
    return ctime(timep);
}
#endif

/**
 * SECTION:cairo-ps
 * @Title: PostScript Surfaces
 * @Short_Description: Rendering PostScript documents
 * @See_Also: #cairo_surface_t
 *
 * The PostScript surface is used to render cairo graphics to Adobe
 * PostScript files and is a multi-page vector surface backend.
 *
 * The following mime types are supported on source patterns:
 * %CAIRO_MIME_TYPE_JPEG, %CAIRO_MIME_TYPE_UNIQUE_ID,
 * %CAIRO_MIME_TYPE_CCITT_FAX, %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS,
 * %CAIRO_MIME_TYPE_EPS, %CAIRO_MIME_TYPE_EPS_PARAMS.
 *
 * Source surfaces used by the PostScript surface that have a
 * %CAIRO_MIME_TYPE_UNIQUE_ID mime type will be stored in PostScript
 * printer memory for the duration of the print
 * job. %CAIRO_MIME_TYPE_UNIQUE_ID should only be used for small
 * frequently used sources.
 *
 * The %CAIRO_MIME_TYPE_CCITT_FAX and %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS mime types
 * are documented in [CCITT Fax Images][ccitt].
 *
 * # Embedding EPS files # {#eps}
 *
 * Encapsulated PostScript files can be embedded in the PS output by
 * setting the CAIRO_MIME_TYPE_EPS mime data on a surface to the EPS
 * data and painting the surface.  The EPS will be scaled and
 * translated to the extents of the surface the EPS data is attached
 * to.
 *
 * The %CAIRO_MIME_TYPE_EPS mime type requires the
 * %CAIRO_MIME_TYPE_EPS_PARAMS mime data to also be provided in order
 * to specify the embeddding parameters.  %CAIRO_MIME_TYPE_EPS_PARAMS
 * mime data must contain a string of the form "bbox=[llx lly urx
 * ury]" that specifies the bounding box (in PS coordinates) of the
 * EPS graphics. The parameters are: lower left x, lower left y, upper
 * right x, upper right y. Normally the bbox data is identical to the
 * \%\%\%BoundingBox data in the EPS file.
 *
 **/


/**
 * CAIRO_HAS_PS_SURFACE:
 *
 * Defined if the PostScript surface backend is available.
 * This macro can be used to conditionally compile backend-specific code.
 *
 * Since: 1.2
 **/


typedef enum {
    CAIRO_PS_COMPRESS_NONE,
    CAIRO_PS_COMPRESS_LZW,
    CAIRO_PS_COMPRESS_DEFLATE
} cairo_ps_compress_t;

typedef enum {
    CAIRO_EMIT_SURFACE_ANALYZE,
    CAIRO_EMIT_SURFACE_EMIT,
    CAIRO_EMIT_SURFACE_EMIT_FORM
} cairo_emit_surface_mode_t;

typedef struct  {
    /* input params */
    cairo_surface_t *src_surface;
    unsigned int regions_id;
    cairo_operator_t op;
    const cairo_rectangle_int_t *src_surface_extents;
    cairo_bool_t src_surface_bounded;
    const cairo_rectangle_int_t *src_op_extents; /* operation extents in src space */
    cairo_filter_t filter;
    cairo_bool_t stencil_mask; /* TRUE if source is to be used as a mask */

    /* output params */
    cairo_bool_t is_image; /* returns TRUE if PS image will be emitted */
                           /*         FALSE if recording will be emitted */
    long approx_size;
    int eod_count;
} cairo_emit_surface_params_t;

static const cairo_surface_backend_t cairo_ps_surface_backend;
static const cairo_paginated_surface_backend_t cairo_ps_surface_paginated_backend;

static cairo_bool_t
_cairo_ps_surface_get_extents (void         *abstract_surface,
          cairo_rectangle_int_t   *rectangle);

static void
_cairo_ps_form_emit (void *entry, void *closure);

static const cairo_ps_level_t _cairo_ps_levels[] =
{
    CAIRO_PS_LEVEL_2,
    CAIRO_PS_LEVEL_3
};

#define CAIRO_PS_LEVEL_LAST ARRAY_LENGTH (_cairo_ps_levels)

static const char * _cairo_ps_level_strings[CAIRO_PS_LEVEL_LAST] =
{
    "PS Level 2",
    "PS Level 3"
};

static const char *_cairo_ps_supported_mime_types[] =
{
    CAIRO_MIME_TYPE_JPEG,
    CAIRO_MIME_TYPE_UNIQUE_ID,
    CAIRO_MIME_TYPE_CCITT_FAX,
    CAIRO_MIME_TYPE_CCITT_FAX_PARAMS,
    CAIRO_MIME_TYPE_EPS,
    CAIRO_MIME_TYPE_EPS_PARAMS,
    NULL
};

typedef struct _cairo_page_standard_media {
    const char *name;
    int width;
    int height;
} cairo_page_standard_media_t;

static const cairo_page_standard_media_t _cairo_page_standard_media[] =
{
    { "A0",       2384, 3371 },
    { "A1",       1685, 2384 },
    { "A2",       1190, 1684 },
    { "A3",        842, 1190 },
    { "A4",        595,  842 },
    { "A5",        420,  595 },
    { "B4",        729, 1032 },
    { "B5",        516,  729 },
    { "Letter",    612,  792 },
    { "Tabloid",   792, 1224 },
    { "Ledger",   1224,  792 },
    { "Legal",     612, 1008 },
    { "Statement", 396,  612 },
    { "Executive", 540,  720 },
    { "Folio",     612,  936 },
    { "Quarto",    610,  780 },
    { "10x14",     720, 1008 },
};

typedef struct _cairo_page_media {
    char *name;
    int width;
    int height;
    cairo_list_t link;
} cairo_page_media_t;

static void
_cairo_ps_form_init_key (cairo_ps_form_t *key)
{
    key->base.hash = _cairo_hash_bytes (_CAIRO_HASH_INIT_VALUE,
     key->unique_id, key->unique_id_length);
}

static cairo_bool_t
_cairo_ps_form_equal (const void *key_a, const void *key_b)
{
    const cairo_ps_form_t *a = key_a;
    const cairo_ps_form_t *b = key_b;

    if (a->filter != b->filter)
 return FALSE;

    if (a->unique_id_length != b->unique_id_length)
 return FALSE;

    return memcmp (a->unique_id, b->unique_id, a->unique_id_length) == 0;
}

static void
_cairo_ps_form_pluck (void *entry, void *closure)
{
    cairo_ps_form_t *surface_entry = entry;
    cairo_hash_table_t *patterns = closure;

    _cairo_hash_table_remove (patterns, &surface_entry->base);
    free (surface_entry->unique_id);
    if (_cairo_surface_is_recording (surface_entry->src_surface) && surface_entry->regions_id != 0)
 _cairo_recording_surface_region_array_remove (surface_entry->src_surface, surface_entry->regions_id);
    cairo_surface_destroy (surface_entry->src_surface);
    free (surface_entry);
}

static void
_cairo_ps_surface_emit_header (cairo_ps_surface_t *surface)
{
    char ctime_buf[26];
    time_t now;
    char **comments;
    int i, num_comments;
    int level;
    const char *eps_header = "";
    cairo_bool_t has_bbox;

    if (surface->has_creation_date)
 now = surface->creation_date;
    else
 now = time (NULL);

    if (surface->ps_level_used == CAIRO_PS_LEVEL_2)
 level = 2;
    else
 level = 3;

    if (surface->eps)
 eps_header = " EPSF-3.0";

    _cairo_output_stream_printf (surface->final_stream,
     "%%!PS-Adobe-3.0%s\n"
     "%%%%Creator: cairo %s (https://cairographics.org)\n",
     eps_header,
     cairo_version_string ());

    if (!getenv ("CAIRO_DEBUG_PS_NO_DATE")) {
 _cairo_output_stream_printf (surface->final_stream,
         "%%%%CreationDate: %s",
         ctime_r (&now, ctime_buf));
    }

    _cairo_output_stream_printf (surface->final_stream,
     "%%%%Pages: %d\n"
     "%%%%DocumentData: Clean7Bit\n"
     "%%%%LanguageLevel: %d\n",
     surface->num_pages,
     level);

    if (!cairo_list_is_empty (&surface->document_media)) {
 cairo_page_media_t *page;
 cairo_bool_t first = TRUE;

 cairo_list_foreach_entry (page, cairo_page_media_t, &surface->document_media, link) {
     if (first) {
  _cairo_output_stream_printf (surface->final_stream,
          "%%%%DocumentMedia: ");
  first = FALSE;
     } else {
  _cairo_output_stream_printf (surface->final_stream,
          "%%%%+ ");
     }
     _cairo_output_stream_printf (surface->final_stream,
      "%s %d %d 0 () ()\n",
      page->name,
      page->width,
      page->height);
 }
    }

    has_bbox = FALSE;
    num_comments = _cairo_array_num_elements (&surface->dsc_header_comments);
    comments = _cairo_array_index (&surface->dsc_header_comments, 0);
    for (i = 0; i < num_comments; i++) {
 _cairo_output_stream_printf (surface->final_stream,
         "%s\n", comments[i]);
 if (strncmp (comments[i], "%%BoundingBox:", 14) == 0)
     has_bbox = TRUE;

 free (comments[i]);
 comments[i] = NULL;
    }

    if (!has_bbox) {
 _cairo_output_stream_printf (surface->final_stream,
         "%%%%BoundingBox: %d %d %d %d\n",
         surface->document_bbox_p1.x,
         surface->document_bbox_p1.y,
         surface->document_bbox_p2.x,
         surface->document_bbox_p2.y);
    }

    _cairo_output_stream_printf (surface->final_stream,
     "%%%%EndComments\n");

    _cairo_output_stream_printf (surface->final_stream,
     "%%%%BeginProlog\n");

    if (surface->eps) {
 _cairo_output_stream_printf (surface->final_stream,
         "50 dict begin\n");
    } else {
 _cairo_output_stream_printf (surface->final_stream,
         "/languagelevel where\n"
         "{ pop languagelevel } { 1 } ifelse\n"
         "%d lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto\n"
         " (This print job requires a PostScript Language Level %d printer.) show\n"
         " showpage quit } if\n",
         level,
         level);
    }

    _cairo_output_stream_printf (surface->final_stream,
     "/q { gsave } bind def\n"
     "/Q { grestore } bind def\n"
     "/cm { 6 array astore concat } bind def\n"
     "/w { setlinewidth } bind def\n"
     "/J { setlinecap } bind def\n"
     "/j { setlinejoin } bind def\n"
     "/M { setmiterlimit } bind def\n"
     "/d { setdash } bind def\n"
     "/m { moveto } bind def\n"
     "/l { lineto } bind def\n"
     "/c { curveto } bind def\n"
     "/h { closepath } bind def\n"
     "/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto\n"
     " 0 exch rlineto 0 rlineto closepath } bind def\n"
     "/S { stroke } bind def\n"
     "/f { fill } bind def\n"
     "/f* { eofill } bind def\n"
     "/n { newpath } bind def\n"
     "/W { clip } bind def\n"
     "/W* { eoclip } bind def\n"
     "/BT { } bind def\n"
     "/ET { } bind def\n"
     "/BDC { mark 3 1 roll /BDC pdfmark } bind def\n"
     "/EMC { mark /EMC pdfmark } bind def\n"
     "/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def\n"
     "/Tj { show currentpoint cairo_store_point } bind def\n"
     "/TJ {\n"
     " {\n"
     " dup\n"
     " type /stringtype eq\n"
     " { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse\n"
     " } forall\n"
     " currentpoint cairo_store_point\n"
     "} bind def\n"
     "/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore\n"
     " cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def\n"
     "/Tf { pop /cairo_font exch def /cairo_font_matrix where\n"
     " { pop cairo_selectfont } if } bind def\n"
     "/Td { matrix translate cairo_font_matrix matrix concatmatrix dup\n"
     " /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point\n"
     " /cairo_font where { pop cairo_selectfont } if } bind def\n"
     "/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def\n"
     " cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def\n"
     "/g { setgray } bind def\n"
     "/rg { setrgbcolor } bind def\n"
     "/d1 { setcachedevice } bind def\n"
     "/cairo_data_source {\n"
     " CairoDataIndex CairoData length lt\n"
     " { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def }\n"
     " { () } ifelse\n"
     "} def\n"
     "/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def\n"
     "/cairo_image { image cairo_flush_ascii85_file } def\n"
     "/cairo_imagemask { imagemask cairo_flush_ascii85_file } def\n");

    if (!surface->eps) {
 _cairo_output_stream_printf (surface->final_stream,
         "/cairo_set_page_size {\n"
         " %% Change paper size, but only if different from previous paper size otherwise\n"
         " %% duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size\n"
         " %% so we use the same when checking if the size changes.\n"
         " /setpagedevice where {\n"
         " pop currentpagedevice\n"
         " /PageSize known {\n"
         " 2 copy\n"
         " currentpagedevice /PageSize get aload pop\n"
         " exch 4 1 roll\n"
         " sub abs 5 gt\n"
         " 3 1 roll\n"
         " sub abs 5 gt\n"
         " or\n"
         " } {\n"
         " true\n"
         " } ifelse\n"
         " {\n"
         " 2 array astore\n"
         " 2 dict begin\n"
         " /PageSize exch def\n"
         " /ImagingBBox null def\n"
         " currentdict end\n"
         " setpagedevice\n"
         " } {\n"
         " pop pop\n"
         " } ifelse\n"
         " } {\n"
         " pop\n"
         " } ifelse\n"
         "} def\n");
    }
    if (surface->contains_eps) {
 _cairo_output_stream_printf (surface->final_stream,
         "/cairo_eps_begin {\n"
         " /cairo_save_state save def\n"
         " /dict_count countdictstack def\n"
         " /op_count count 1 sub def\n"
         " userdict begin\n"
         " /showpage { } def\n"
         " 0 g 0 J 1 w 0 j 10 M [ ] 0 d n\n"
         "} bind def\n"
         "/cairo_eps_end {\n"
         " count op_count sub { pop } repeat\n"
         " countdictstack dict_count sub { end } repeat\n"
         " cairo_save_state restore\n"
         "} bind def\n");
    }

    _cairo_output_stream_printf (surface->final_stream,
     "%%%%EndProlog\n");
}

static cairo_status_t
_cairo_ps_surface_emit_type1_font_subset (cairo_ps_surface_t  *surface,
       cairo_scaled_font_subset_t *font_subset)


{
    cairo_type1_subset_t subset;
    cairo_status_t status;
    int length;
    char name[64];

    snprintf (name, sizeof name, "f-%d-%d",
       font_subset->font_id, font_subset->subset_id);
    status = _cairo_type1_subset_init (&subset, name, font_subset, TRUE);
    if (unlikely (status))
 return status;

    /* FIXME: Figure out document structure convention for fonts */

#if DEBUG_PS
    _cairo_output_stream_printf (surface->final_stream,
     "%% _cairo_ps_surface_emit_type1_font_subset\n");
#endif

    _cairo_output_stream_printf (surface->final_stream,
     "%%%%BeginResource: font %s\n",
     subset.base_font);
    length = subset.header_length + subset.data_length + subset.trailer_length;
    _cairo_output_stream_write (surface->final_stream, subset.data, length);
    _cairo_output_stream_printf (surface->final_stream,
     "%%%%EndResource\n");

    _cairo_type1_subset_fini (&subset);

    return CAIRO_STATUS_SUCCESS;
}


static cairo_status_t
_cairo_ps_surface_emit_type1_font_fallback (cairo_ps_surface_t  *surface,
                                            cairo_scaled_font_subset_t *font_subset)
{
    cairo_type1_subset_t subset;
    cairo_status_t status;
    int length;
    char name[64];

    snprintf (name, sizeof name, "f-%d-%d",
       font_subset->font_id, font_subset->subset_id);
    status = _cairo_type1_fallback_init_hex (&subset, name, font_subset);
    if (unlikely (status))
 return status;

#if DEBUG_PS
    _cairo_output_stream_printf (surface->final_stream,
     "%% _cairo_ps_surface_emit_type1_font_fallback\n");
#endif

    _cairo_output_stream_printf (surface->final_stream,
     "%%%%BeginResource: font %s\n",
     subset.base_font);
    length = subset.header_length + subset.data_length + subset.trailer_length;
    _cairo_output_stream_write (surface->final_stream, subset.data, length);
    _cairo_output_stream_printf (surface->final_stream,
     "%%%%EndResource\n");

    _cairo_type1_fallback_fini (&subset);

    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_ps_surface_emit_truetype_font_subset (cairo_ps_surface_t  *surface,
          cairo_scaled_font_subset_t *font_subset)


{
    cairo_truetype_subset_t subset;
    cairo_status_t status;
    unsigned int i, begin, end;

    status = _cairo_truetype_subset_init_ps (&subset, font_subset);
    if (unlikely (status))
 return status;

    /* FIXME: Figure out document structure convention for fonts */

#if DEBUG_PS
    _cairo_output_stream_printf (surface->final_stream,
     "%% _cairo_ps_surface_emit_truetype_font_subset\n");
#endif

    _cairo_output_stream_printf (surface->final_stream,
     "%%%%BeginResource: font %s\n",
     subset.ps_name);
    _cairo_output_stream_printf (surface->final_stream,
     "11 dict begin\n"
     "/FontType 42 def\n"
     "/FontName /%s def\n"
     "/PaintType 0 def\n"
     "/FontMatrix [ 1 0 0 1 0 0 ] def\n"
     "/FontBBox [ 0 0 0 0 ] def\n"
     "/Encoding 256 array def\n"
     "0 1 255 { Encoding exch /.notdef put } for\n",
     subset.ps_name);

    /* FIXME: Figure out how subset->x_max etc maps to the /FontBBox */

    if (font_subset->is_latin) {
 for (i = 1; i < 256; i++) {
     if (font_subset->latin_to_subset_glyph_index[i] > 0) {
  if (font_subset->glyph_names != NULL) {
      _cairo_output_stream_printf (surface->final_stream,
       "Encoding %d /%s put\n",
       i, font_subset->glyph_names[font_subset->latin_to_subset_glyph_index[i]]);
  } else {
      _cairo_output_stream_printf (surface->final_stream,
       "Encoding %d /g%ld put\n", i, font_subset->latin_to_subset_glyph_index[i]);
  }
     }
 }
    } else {
 for (i = 1; i < font_subset->num_glyphs; i++) {
     if (font_subset->glyph_names != NULL) {
  _cairo_output_stream_printf (surface->final_stream,
          "Encoding %d /%s put\n",
          i, font_subset->glyph_names[i]);
     } else {
  _cairo_output_stream_printf (surface->final_stream,
          "Encoding %d /g%d put\n", i, i);
     }
 }
    }

    _cairo_output_stream_printf (surface->final_stream,
     "/CharStrings %d dict dup begin\n"
     "/.notdef 0 def\n",
     font_subset->num_glyphs);

    for (i = 1; i < font_subset->num_glyphs; i++) {
 if (font_subset->glyph_names != NULL) {
     _cairo_output_stream_printf (surface->final_stream,
      "/%s %d def\n",
      font_subset->glyph_names[i], i);
 } else {
     _cairo_output_stream_printf (surface->final_stream,
      "/g%d %d def\n", i, i);
 }
    }

    _cairo_output_stream_printf (surface->final_stream,
     "end readonly def\n");

    _cairo_output_stream_printf (surface->final_stream,
     "/sfnts [\n");
    begin = 0;
    end = 0;
    for (i = 0; i < subset.num_string_offsets; i++) {
        end = subset.string_offsets[i];
        _cairo_output_stream_printf (surface->final_stream,"<");
        _cairo_output_stream_write_hex_string (surface->final_stream,
                                               subset.data + begin, end - begin);
        _cairo_output_stream_printf (surface->final_stream,"00>\n");
        begin = end;
    }
    if (subset.data_length > end) {
        _cairo_output_stream_printf (surface->final_stream,"<");
        _cairo_output_stream_write_hex_string (surface->final_stream,
                                               subset.data + end, subset.data_length - end);
        _cairo_output_stream_printf (surface->final_stream,"00>\n");
    }

    _cairo_output_stream_printf (surface->final_stream,
     "] def\n"
     "/f-%d-%d currentdict end definefont pop\n",
     font_subset->font_id,
     font_subset->subset_id);
    _cairo_output_stream_printf (surface->final_stream,
     "%%%%EndResource\n");
    _cairo_truetype_subset_fini (&subset);


    return CAIRO_STATUS_SUCCESS;
}

static cairo_int_status_t
_cairo_ps_emit_imagemask (cairo_image_surface_t *image,
     cairo_output_stream_t *stream)
{
    uint8_t *row, *byte;
    int rows, cols;

    /* The only image type supported by Type 3 fonts are 1-bit image
     * masks */

    assert (image->format == CAIRO_FORMAT_A1);

    _cairo_output_stream_printf (stream,
     "<<\n"
     " /ImageType 1\n"
     " /Width %d\n"
     " /Height %d\n"
     " /ImageMatrix [%d 0 0 %d 0 %d]\n"
     " /Decode [1 0]\n"
     " /BitsPerComponent 1\n",
     image->width,
     image->height,
     image->width,
     -image->height,
     image->height);

    _cairo_output_stream_printf (stream,
     " /DataSource {<\n ");
    for (row = image->data, rows = image->height; rows; row += image->stride, rows--) {
 for (byte = row, cols = (image->width + 7) / 8; cols; byte++, cols--) {
     uint8_t output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte);
     _cairo_output_stream_printf (stream, "%02x ", output_byte);
 }
 _cairo_output_stream_printf (stream, "\n ");
    }
    _cairo_output_stream_printf (stream, ">}\n>>\n");

    _cairo_output_stream_printf (stream,
     "imagemask\n");

    return _cairo_output_stream_get_status (stream);
}

static cairo_status_t
_cairo_ps_surface_emit_type3_font_subset (cairo_ps_surface_t  *surface,
       cairo_scaled_font_subset_t *font_subset)


{
    cairo_status_t status;
    unsigned int i;
    cairo_box_t font_bbox = {{0,0},{0,0}};
    cairo_box_t bbox = {{0,0},{0,0}};
    cairo_surface_t *type3_surface;
    double width;

    if (font_subset->num_glyphs == 0)
 return CAIRO_STATUS_SUCCESS;

#if DEBUG_PS
    _cairo_output_stream_printf (surface->final_stream,
     "%% _cairo_ps_surface_emit_type3_font_subset\n");
#endif

    _cairo_output_stream_printf (surface->final_stream,
     "%%%%BeginResource: font\n");
    _cairo_output_stream_printf (surface->final_stream,
     "8 dict begin\n"
     "/FontType 3 def\n"
     "/FontMatrix [1 0 0 -1 0 0] def\n"
     "/Encoding 256 array def\n"
     "0 1 255 { Encoding exch /.notdef put } for\n");

    type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font,
             NULL,
             _cairo_ps_emit_imagemask,
             surface->font_subsets,
             TRUE);
    status = type3_surface->status;
    if (unlikely (status))
 return status;

    for (i = 0; i < font_subset->num_glyphs; i++) {
 if (font_subset->glyph_names != NULL) {
     _cairo_output_stream_printf (surface->final_stream,
      "Encoding %d /%s put\n",
      i, font_subset->glyph_names[i]);
 } else {
     _cairo_output_stream_printf (surface->final_stream,
      "Encoding %d /g%d put\n", i, i);
 }
    }

    _cairo_output_stream_printf (surface->final_stream,
     "/Glyphs [\n");

    for (i = 0; i < font_subset->num_glyphs; i++) {
 _cairo_output_stream_printf (surface->final_stream,
         " { %% %d\n", i);
 status = _cairo_type3_glyph_surface_emit_glyph (type3_surface,
       surface->final_stream,
       font_subset->glyphs[i],
       &bbox,
       &width);
 if (unlikely (status))
     break;

 _cairo_output_stream_printf (surface->final_stream,
         " }\n");
        if (i == 0) {
            font_bbox.p1.x = bbox.p1.x;
            font_bbox.p1.y = bbox.p1.y;
            font_bbox.p2.x = bbox.p2.x;
            font_bbox.p2.y = bbox.p2.y;
        } else {
            if (bbox.p1.x < font_bbox.p1.x)
                font_bbox.p1.x = bbox.p1.x;
            if (bbox.p1.y < font_bbox.p1.y)
                font_bbox.p1.y = bbox.p1.y;
            if (bbox.p2.x > font_bbox.p2.x)
                font_bbox.p2.x = bbox.p2.x;
            if (bbox.p2.y > font_bbox.p2.y)
                font_bbox.p2.y = bbox.p2.y;
        }
    }
    cairo_surface_finish (type3_surface);
    cairo_surface_destroy (type3_surface);
    if (unlikely (status))
 return status;

    _cairo_output_stream_printf (surface->final_stream,
     "] def\n"
     "/FontBBox [%f %f %f %f] def\n"
     "/BuildChar {\n"
     " exch /Glyphs get\n"
     " exch get\n"
     " 10 dict begin exec end\n"
     "} bind def\n"
     "currentdict\n"
     "end\n"
     "/f-%d-%d exch definefont pop\n",
     _cairo_fixed_to_double (font_bbox.p1.x),
     - _cairo_fixed_to_double (font_bbox.p2.y),
     _cairo_fixed_to_double (font_bbox.p2.x),
     - _cairo_fixed_to_double (font_bbox.p1.y),
     font_subset->font_id,
     font_subset->subset_id);
    _cairo_output_stream_printf (surface->final_stream,
     "%%%%EndResource\n");

    return CAIRO_STATUS_SUCCESS;
}

static cairo_int_status_t
_cairo_ps_surface_emit_unscaled_font_subset (cairo_scaled_font_subset_t *font_subset,
                void   *closure)
{
    cairo_ps_surface_t *surface = closure;
    cairo_int_status_t status;

    status = _cairo_scaled_font_subset_create_glyph_names (font_subset);
    if (_cairo_int_status_is_error (status))
 return status;

    status = _cairo_ps_surface_emit_type1_font_subset (surface, font_subset);
    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
 return status;

    status = _cairo_ps_surface_emit_truetype_font_subset (surface, font_subset);
    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
 return status;

    status = _cairo_ps_surface_emit_type1_font_fallback (surface, font_subset);
    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
 return status;

    ASSERT_NOT_REACHED;
    return CAIRO_STATUS_SUCCESS;
}

static cairo_int_status_t
_cairo_ps_surface_emit_scaled_font_subset (cairo_scaled_font_subset_t *font_subset,
                                           void         *closure)
{
    cairo_ps_surface_t *surface = closure;
    cairo_int_status_t status;

    status = _cairo_scaled_font_subset_create_glyph_names (font_subset);
    if (_cairo_int_status_is_error (status))
 return status;

    status = _cairo_ps_surface_emit_type3_font_subset (surface, font_subset);
    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
 return status;

    ASSERT_NOT_REACHED;
    return CAIRO_INT_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_ps_surface_emit_font_subsets (cairo_ps_surface_t *surface)
{
    cairo_status_t status;

#if DEBUG_PS
    _cairo_output_stream_printf (surface->final_stream,
     "%% _cairo_ps_surface_emit_font_subsets\n");
#endif

    status = _cairo_scaled_font_subsets_foreach_unscaled (surface->font_subsets,
                                                          _cairo_ps_surface_emit_unscaled_font_subset,
                                                          surface);
    if (unlikely (status))
 return status;

    return _cairo_scaled_font_subsets_foreach_scaled (surface->font_subsets,
            _cairo_ps_surface_emit_scaled_font_subset,
            surface);
}

static cairo_int_status_t
_cairo_ps_surface_emit_forms (cairo_ps_surface_t *surface)
{
    _cairo_hash_table_foreach (surface->forms,
          _cairo_ps_form_emit,
          surface);
    return surface->base.status;
}

static cairo_status_t
_cairo_ps_surface_emit_body (cairo_ps_surface_t *surface)
{
    char    buf[4096];
    int     n;

    if (ferror (surface->tmpfile) != 0)
 return _cairo_error (CAIRO_STATUS_TEMP_FILE_ERROR);

    rewind (surface->tmpfile);
    while ((n = fread (buf, 1, sizeof (buf), surface->tmpfile)) > 0)
 _cairo_output_stream_write (surface->final_stream, buf, n);

    if (ferror (surface->tmpfile) != 0)
 return _cairo_error (CAIRO_STATUS_TEMP_FILE_ERROR);

    return CAIRO_STATUS_SUCCESS;
}

static void
_cairo_ps_surface_emit_footer (cairo_ps_surface_t *surface)
{
    _cairo_output_stream_printf (surface->final_stream,
     "%%%%Trailer\n");

    if (surface->eps) {
 _cairo_output_stream_printf (surface->final_stream,
         "end\n");
    }

    _cairo_output_stream_printf (surface->final_stream,
     "%%%%EOF\n");
}

static cairo_bool_t
_path_covers_bbox (cairo_ps_surface_t *surface,
     cairo_path_fixed_t *path)
{
    cairo_box_t box;

    if (_cairo_path_fixed_is_box (path, &box)) {
 cairo_rectangle_int_t rect;

 _cairo_box_round_to_rectangle (&box, &rect);

 /* skip trivial whole-page clips */
 if (_cairo_rectangle_intersect (&rect, &surface->surface_extents)) {
     if (rect.x == surface->surface_extents.x &&
  rect.width == surface->surface_extents.width &&
  rect.y == surface->surface_extents.y &&
  rect.height == surface->surface_extents.height)
     {
  return TRUE;
     }
 }
    }

    return FALSE;
}

static cairo_status_t
_cairo_ps_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
            cairo_path_fixed_t *path,
            cairo_fill_rule_t   fill_rule,
            double      tolerance,
            cairo_antialias_t   antialias)
{
    cairo_ps_surface_t *surface = cairo_container_of (clipper,
            cairo_ps_surface_t,
            clipper);
    cairo_output_stream_t *stream = surface->stream;
    cairo_status_t status;

    assert (surface->paginated_mode != CAIRO_PAGINATED_MODE_ANALYZE);

#if DEBUG_PS
    _cairo_output_stream_printf (stream,
     "%% _cairo_ps_surface_intersect_clip_path\n");
#endif

    if (path == NULL) {
 status = _cairo_pdf_operators_flush (&surface->pdf_operators);
 if (unlikely (status))
     return status;

 _cairo_output_stream_printf (stream, "Q q\n");

 surface->current_pattern_is_solid_color = FALSE;
 _cairo_pdf_operators_reset (&surface->pdf_operators);

 return CAIRO_STATUS_SUCCESS;
    }

    if (_path_covers_bbox (surface, path))
 return CAIRO_STATUS_SUCCESS;

    return _cairo_pdf_operators_clip (&surface->pdf_operators,
          path,
          fill_rule);
}

/* PLRM specifies a tolerance of 5 points when matching page sizes */
static cairo_bool_t
_ps_page_dimension_equal (int a, int b)
{
    return (abs (a - b) < 5);
}

static const char *
_cairo_ps_surface_get_page_media (cairo_ps_surface_t     *surface)
{
    int width, height, i;
    char buf[50];
    cairo_page_media_t *page;
    const char *page_name;

    width = _cairo_lround (surface->width);
    height = _cairo_lround (surface->height);

    /* search previously used page sizes */
    cairo_list_foreach_entry (page, cairo_page_media_t, &surface->document_media, link) {
 if (_ps_page_dimension_equal (width, page->width) &&
     _ps_page_dimension_equal (height, page->height))
     return page->name;
    }

    /* search list of standard page sizes */
    page_name = NULL;
    for (i = 0; i < ARRAY_LENGTH (_cairo_page_standard_media); i++) {
 if (_ps_page_dimension_equal (width, _cairo_page_standard_media[i].width) &&
     _ps_page_dimension_equal (height, _cairo_page_standard_media[i].height))
 {
     page_name = _cairo_page_standard_media[i].name;
     width = _cairo_page_standard_media[i].width;
     height = _cairo_page_standard_media[i].height;
     break;
 }
    }

    page = _cairo_malloc (sizeof (cairo_page_media_t));
    if (unlikely (page == NULL)) {
 _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
 return NULL;
    }

    if (page_name) {
 page->name = strdup (page_name);
    } else {
 snprintf (buf, sizeof (buf), "%dx%dmm",
    (int) _cairo_lround (surface->width * 25.4/72),
    (int) _cairo_lround (surface->height * 25.4/72));
 page->name = strdup (buf);
    }

    if (unlikely (page->name == NULL)) {
 free (page);
 _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
 return NULL;
    }

    page->width = width;
    page->height = height;
    cairo_list_add_tail (&page->link, &surface->document_media);

    return page->name;
}

static cairo_surface_t *
_cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream,
           double       width,
           double       height)
{
    cairo_status_t status, status_ignored;
    cairo_ps_surface_t *surface;

    surface = _cairo_malloc (sizeof (cairo_ps_surface_t));
    if (unlikely (surface == NULL)) {
 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 goto CLEANUP;
    }

    _cairo_surface_init (&surface->base,
    &cairo_ps_surface_backend,
    NULL, /* device */
    CAIRO_CONTENT_COLOR_ALPHA,
    TRUE); /* is_vector */

    surface->final_stream = stream;

    surface->tmpfile = tmpfile ();
    if (surface->tmpfile == NULL) {
 switch (errno) {
 case ENOMEM:
     status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
     break;
 default:
     status = _cairo_error (CAIRO_STATUS_TEMP_FILE_ERROR);
     break;
 }
 goto CLEANUP_SURFACE;
    }

    surface->stream = _cairo_output_stream_create_for_file (surface->tmpfile);
    status = _cairo_output_stream_get_status (surface->stream);
    if (unlikely (status))
 goto CLEANUP_OUTPUT_STREAM;

    surface->font_subsets = _cairo_scaled_font_subsets_create_simple ();
    if (unlikely (surface->font_subsets == NULL)) {
 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 goto CLEANUP_OUTPUT_STREAM;
    }

    _cairo_scaled_font_subsets_enable_latin_subset (surface->font_subsets, TRUE);
    surface->has_creation_date = FALSE;
    surface->eps = FALSE;
    surface->ps_level = CAIRO_PS_LEVEL_3;
    surface->ps_level_used = CAIRO_PS_LEVEL_2;
    surface->width  = width;
    surface->height = height;
    cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, 1, 0, 0);
    surface->surface_extents.x = 0;
    surface->surface_extents.y = 0;
    surface->surface_extents.width  = ceil (surface->width);
    surface->surface_extents.height = ceil (surface->height);
    surface->surface_bounded = TRUE;
    surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE;
    surface->force_fallbacks = FALSE;
    surface->content = CAIRO_CONTENT_COLOR_ALPHA;
    surface->current_pattern_is_solid_color = FALSE;
    surface->document_bbox_p1.x = 0;
    surface->document_bbox_p1.y = 0;
    surface->document_bbox_p2.x = 0;
    surface->document_bbox_p2.y = 0;
    surface->total_form_size = 0;
    surface->contains_eps = FALSE;
    surface->paint_proc = FALSE;

    _cairo_surface_clipper_init (&surface->clipper,
     _cairo_ps_surface_clipper_intersect_clip_path);

    _cairo_pdf_operators_init (&surface->pdf_operators,
          surface->stream,
          &surface->cairo_to_ps,
          surface->font_subsets,
          TRUE);
    surface->num_pages = 0;

    cairo_list_init (&surface->document_media);
    _cairo_array_init (&surface->dsc_header_comments, sizeof (char *));
    _cairo_array_init (&surface->dsc_setup_comments, sizeof (char *));
    _cairo_array_init (&surface->dsc_page_setup_comments, sizeof (char *));
    _cairo_array_init (&surface->recording_surf_stack, sizeof (unsigned int));

    surface->num_forms = 0;
    surface->forms = _cairo_hash_table_create (_cairo_ps_form_equal);
    if (unlikely (surface->forms == NULL)) {
 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 goto CLEANUP_FONT_SUBSETS;
    }

    surface->dsc_comment_target = &surface->dsc_header_comments;

    surface->paginated_surface = _cairo_paginated_surface_create (
                                    &surface->base,
        CAIRO_CONTENT_COLOR_ALPHA,
        &cairo_ps_surface_paginated_backend);
    status = surface->paginated_surface->status;
    if (status == CAIRO_STATUS_SUCCESS) {
 /* paginated keeps the only reference to surface now, drop ours */
 cairo_surface_destroy (&surface->base);
 return surface->paginated_surface;
    }

 CLEANUP_FONT_SUBSETS:
    _cairo_scaled_font_subsets_destroy (surface->font_subsets);
 CLEANUP_OUTPUT_STREAM:
    status_ignored = _cairo_output_stream_destroy (surface->stream);
    fclose (surface->tmpfile);
 CLEANUP_SURFACE:
    free (surface);
 CLEANUP:
    /* destroy stream on behalf of caller */
    status_ignored = _cairo_output_stream_destroy (stream);

    return _cairo_surface_create_in_error (status);
}

/**
 * cairo_ps_surface_create:
 * @filename: a filename for the PS output (must be writable), %NULL may be
 *            used to specify no output. This will generate a PS surface that
 *            may be queried and used as a source, without generating a
 *            temporary file.
 * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
 * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
 *
 * Creates a PostScript surface of the specified size in points to be
 * written to @filename. See cairo_ps_surface_create_for_stream() for
 * a more flexible mechanism for handling the PostScript output than
 * simply writing it to a named file.
 *
 * Note that the size of individual pages of the PostScript output can
 * vary. See cairo_ps_surface_set_size().
 *
 * Return value: a pointer to the newly created surface. The caller
 * owns the surface and should call cairo_surface_destroy() when done
 * with it.
 *
 * This function always returns a valid pointer, but it will return a
 * pointer to a "nil" surface if an error such as out of memory
 * occurs. You can use cairo_surface_status() to check for this.
 *
 * Since: 1.2
 **/

cairo_surface_t *
cairo_ps_surface_create (const char  *filename,
    double    width_in_points,
    double    height_in_points)
{
    cairo_output_stream_t *stream;

    stream = _cairo_output_stream_create_for_filename (filename);
    if (_cairo_output_stream_get_status (stream))
 return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));

    return _cairo_ps_surface_create_for_stream_internal (stream,
        width_in_points,
        height_in_points);
}

/**
 * cairo_ps_surface_create_for_stream:
 * @write_func: a #cairo_write_func_t to accept the output data, may be %NULL
 *              to indicate a no-op @write_func. With a no-op @write_func,
 *              the surface may be queried or used as a source without
 *              generating any temporary files.
 * @closure: the closure argument for @write_func
 * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
 * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
 *
 * Creates a PostScript surface of the specified size in points to be
 * written incrementally to the stream represented by @write_func and
 * @closure. See cairo_ps_surface_create() for a more convenient way
 * to simply direct the PostScript output to a named file.
 *
 * Note that the size of individual pages of the PostScript
 * output can vary. See cairo_ps_surface_set_size().
 *
 * Return value: a pointer to the newly created surface. The caller
 * owns the surface and should call cairo_surface_destroy() when done
 * with it.
 *
 * This function always returns a valid pointer, but it will return a
 * pointer to a "nil" surface if an error such as out of memory
 * occurs. You can use cairo_surface_status() to check for this.
 *
 * Since: 1.2
 **/

cairo_surface_t *
cairo_ps_surface_create_for_stream (cairo_write_func_t write_func,
        void        *closure,
        double  width_in_points,
        double  height_in_points)
{
    cairo_output_stream_t *stream;

    stream = _cairo_output_stream_create (write_func, NULL, closure);
    if (_cairo_output_stream_get_status (stream))
 return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));

    return _cairo_ps_surface_create_for_stream_internal (stream,
        width_in_points,
        height_in_points);
}

static cairo_bool_t
_cairo_surface_is_ps (cairo_surface_t *surface)
{
    return surface->backend == &cairo_ps_surface_backend;
}

/* If the abstract_surface is a paginated surface, and that paginated
 * surface's target is a ps_surface, then set ps_surface to that
 * target. Otherwise return FALSE.
 */

static cairo_bool_t
_extract_ps_surface (cairo_surface_t  *surface,
                     cairo_bool_t         set_error_on_failure,
       cairo_ps_surface_t **ps_surface)
{
    cairo_surface_t *target;

    if (surface->status)
 return FALSE;
    if (surface->finished) {
        if (set_error_on_failure)
     _cairo_surface_set_error (surface,
          _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
 return FALSE;
    }

    if (! _cairo_surface_is_paginated (surface)) {
        if (set_error_on_failure)
     _cairo_surface_set_error (surface,
          _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
 return FALSE;
    }

    target = _cairo_paginated_surface_get_target (surface);
    if (target->status) {
        if (set_error_on_failure)
     _cairo_surface_set_error (surface, target->status);
 return FALSE;
    }
    if (target->finished) {
        if (set_error_on_failure)
     _cairo_surface_set_error (surface,
          _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
 return FALSE;
    }

    if (! _cairo_surface_is_ps (target)) {
        if (set_error_on_failure)
     _cairo_surface_set_error (surface,
          _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
 return FALSE;
    }

    *ps_surface = (cairo_ps_surface_t *) target;
    return TRUE;
}

/**
 * cairo_ps_surface_restrict_to_level:
 * @surface: a PostScript #cairo_surface_t
 * @level: PostScript level
 *
 * Restricts the generated PostSript file to @level. See
 * cairo_ps_get_levels() for a list of available level values that
 * can be used here.
 *
 * This function should only be called before any drawing operations
 * have been performed on the given surface. The simplest way to do
 * this is to call this function immediately after creating the
 * surface.
 *
 * Since: 1.6
 **/

void
cairo_ps_surface_restrict_to_level (cairo_surface_t  *surface,
                                    cairo_ps_level_t  level)
{
    cairo_ps_surface_t *ps_surface = NULL;

    if (! _extract_ps_surface (surface, TRUE, &ps_surface))
 return;

    if (level < CAIRO_PS_LEVEL_LAST)
 ps_surface->ps_level = level;
}

/**
 * cairo_ps_get_levels:
 * @levels: supported level list
 * @num_levels: list length
 *
 * Used to retrieve the list of supported levels. See
 * cairo_ps_surface_restrict_to_level().
 *
 * Since: 1.6
 **/

void
cairo_ps_get_levels (cairo_ps_level_t const **levels,
                     int                       *num_levels)
{
    if (levels != NULL)
 *levels = _cairo_ps_levels;

    if (num_levels != NULL)
 *num_levels = CAIRO_PS_LEVEL_LAST;
}

/**
 * cairo_ps_level_to_string:
 * @level: a level id
 *
 * Get the string representation of the given @level id. This function
 * will return %NULL if @level id isn't valid. See cairo_ps_get_levels()
 * for a way to get the list of valid level ids.
 *
 * Return value: the string associated to given level.
 *
 * Since: 1.6
 **/

const char *
cairo_ps_level_to_string (cairo_ps_level_t level)
{
    if (level >= CAIRO_PS_LEVEL_LAST)
 return NULL;

    return _cairo_ps_level_strings[level];
}

/**
 * cairo_ps_surface_set_eps:
 * @surface: a PostScript #cairo_surface_t
 * @eps: %TRUE to output EPS format PostScript
 *
 * If @eps is %TRUE, the PostScript surface will output Encapsulated
 * PostScript.
 *
 * This function should only be called before any drawing operations
 * have been performed on the current page. The simplest way to do
 * this is to call this function immediately after creating the
 * surface. An Encapsulated PostScript file should never contain more
 * than one page.
 *
 * Since: 1.6
 **/

void
cairo_ps_surface_set_eps (cairo_surface_t *surface,
     cairo_bool_t           eps)
{
    cairo_ps_surface_t *ps_surface = NULL;

    if (! _extract_ps_surface (surface, TRUE, &ps_surface))
 return;

    ps_surface->eps = eps;
}

/**
 * cairo_ps_surface_get_eps:
 * @surface: a PostScript #cairo_surface_t
 *
 * Check whether the PostScript surface will output Encapsulated PostScript.
 *
 * Return value: %TRUE if the surface will output Encapsulated PostScript.
 *
 * Since: 1.6
 **/

cairo_public cairo_bool_t
cairo_ps_surface_get_eps (cairo_surface_t *surface)
{
    cairo_ps_surface_t *ps_surface = NULL;

    if (! _extract_ps_surface (surface, FALSE, &ps_surface))
 return FALSE;

    return ps_surface->eps;
}

/**
 * cairo_ps_surface_set_size:
 * @surface: a PostScript #cairo_surface_t
 * @width_in_points: new surface width, in points (1 point == 1/72.0 inch)
 * @height_in_points: new surface height, in points (1 point == 1/72.0 inch)
 *
 * Changes the size of a PostScript surface for the current (and
 * subsequent) pages.
 *
 * This function should only be called before any drawing operations
 * have been performed on the current page. The simplest way to do
 * this is to call this function immediately after creating the
 * surface or immediately after completing a page with either
 * cairo_show_page() or cairo_copy_page().
 *
 * Since: 1.2
 **/

void
cairo_ps_surface_set_size (cairo_surface_t *surface,
      double   width_in_points,
      double   height_in_points)
{
    cairo_ps_surface_t *ps_surface = NULL;
    cairo_status_t status;

    if (! _extract_ps_surface (surface, TRUE, &ps_surface))
 return;

    ps_surface->width = width_in_points;
    ps_surface->height = height_in_points;
    cairo_matrix_init (&ps_surface->cairo_to_ps, 1, 0, 0, 1, 0, 0);
    ps_surface->surface_extents.x = 0;
    ps_surface->surface_extents.y = 0;
    ps_surface->surface_extents.width  = ceil (ps_surface->width);
    ps_surface->surface_extents.height = ceil (ps_surface->height);
    _cairo_pdf_operators_set_cairo_to_pdf_matrix (&ps_surface->pdf_operators,
        &ps_surface->cairo_to_ps);
    status = _cairo_paginated_surface_set_size (ps_surface->paginated_surface,
      width_in_points,
      height_in_points);
    if (status)
 status = _cairo_surface_set_error (surface, status);
}

/**
 * cairo_ps_surface_dsc_comment:
 * @surface: a PostScript #cairo_surface_t
 * @comment: a comment string to be emitted into the PostScript output
 *
 * Emit a comment into the PostScript output for the given surface.
 *
 * The comment is expected to conform to the PostScript Language
 * Document Structuring Conventions (DSC). Please see that manual for
 * details on the available comments and their meanings. In
 * particular, the \%\%IncludeFeature comment allows a
 * device-independent means of controlling printer device features. So
 * the PostScript Printer Description Files Specification will also be
 * a useful reference.
 *
 * The comment string must begin with a percent character (\%) and the
 * total length of the string (including any initial percent
 * characters) must not exceed 255 characters. Violating either of
 * these conditions will place @surface into an error state. But
 * beyond these two conditions, this function will not enforce
 * conformance of the comment with any particular specification.
 *
 * The comment string must not contain any newline characters.
 *
 * The DSC specifies different sections in which particular comments
 * can appear. This function provides for comments to be emitted
 * within three sections: the header, the Setup section, and the
 * PageSetup section.  Comments appearing in the first two sections
 * apply to the entire document while comments in the BeginPageSetup
 * section apply only to a single page.
 *
 * For comments to appear in the header section, this function should
 * be called after the surface is created, but before a call to
 * cairo_ps_surface_dsc_begin_setup().
 *
 * For comments to appear in the Setup section, this function should
 * be called after a call to cairo_ps_surface_dsc_begin_setup() but
 * before a call to cairo_ps_surface_dsc_begin_page_setup().
 *
 * For comments to appear in the PageSetup section, this function
 * should be called after a call to
 * cairo_ps_surface_dsc_begin_page_setup().
 *
 * Note that it is only necessary to call
 * cairo_ps_surface_dsc_begin_page_setup() for the first page of any
 * surface. After a call to cairo_show_page() or cairo_copy_page()
 * comments are unambiguously directed to the PageSetup section of the
 * current page. But it doesn't hurt to call this function at the
 * beginning of every page as that consistency may make the calling
 * code simpler.
 *
 * As a final note, cairo automatically generates several comments on
 * its own. As such, applications must not manually generate any of
 * the following comments:
 *
 * Header section: \%!PS-Adobe-3.0, \%\%Creator, \%\%CreationDate, \%\%Pages,
 * \%\%BoundingBox, \%\%DocumentData, \%\%LanguageLevel, \%\%EndComments.
 *
 * Setup section: \%\%BeginSetup, \%\%EndSetup
 *
 * PageSetup section: \%\%BeginPageSetup, \%\%PageBoundingBox, \%\%EndPageSetup.
 *
 * Other sections: \%\%BeginProlog, \%\%EndProlog, \%\%Page, \%\%Trailer, \%\%EOF
 *
 * Here is an example sequence showing how this function might be used:
 *
 * <informalexample><programlisting>
 * cairo_surface_t *surface = cairo_ps_surface_create (filename, width, height);
 * ...
 * cairo_ps_surface_dsc_comment (surface, "%%Title: My excellent document");
 * cairo_ps_surface_dsc_comment (surface, "%%Copyright: Copyright (C) 2006 Cairo Lover")
 * ...
 * cairo_ps_surface_dsc_begin_setup (surface);
 * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *MediaColor White");
 * ...
 * cairo_ps_surface_dsc_begin_page_setup (surface);
 * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *PageSize A3");
 * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *InputSlot LargeCapacity");
 * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *MediaType Glossy");
 * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *MediaColor Blue");
 * ... draw to first page here ..
 * cairo_show_page (cr);
 * ...
 * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *PageSize A5");
 * ...
 * </programlisting></informalexample>
 *
 * Since: 1.2
 **/

void
cairo_ps_surface_dsc_comment (cairo_surface_t *surface,
         const char *comment)
{
    cairo_ps_surface_t *ps_surface = NULL;
    cairo_status_t status;
    char *comment_copy;

    if (! _extract_ps_surface (surface, TRUE, &ps_surface))
 return;

    /* A couple of sanity checks on the comment value. */
    if (comment == NULL) {
 status = _cairo_surface_set_error (surface, CAIRO_STATUS_NULL_POINTER);
 return;
    }

    if (comment[0] != '%' || strlen (comment) > 255) {
 status = _cairo_surface_set_error (surface, CAIRO_STATUS_INVALID_DSC_COMMENT);
 return;
    }

    /* Then, copy the comment and store it in the appropriate array. */
    comment_copy = strdup (comment);
    if (unlikely (comment_copy == NULL)) {
 status = _cairo_surface_set_error (surface, CAIRO_STATUS_NO_MEMORY);
 return;
    }

    status = _cairo_array_append (ps_surface->dsc_comment_target, &comment_copy);
    if (unlikely (status)) {
 free (comment_copy);
 status = _cairo_surface_set_error (surface, status);
 return;
    }
}

/**
 * cairo_ps_surface_dsc_begin_setup:
 * @surface: a PostScript #cairo_surface_t
 *
 * This function indicates that subsequent calls to
 * cairo_ps_surface_dsc_comment() should direct comments to the Setup
 * section of the PostScript output.
 *
 * This function should be called at most once per surface, and must
 * be called before any call to cairo_ps_surface_dsc_begin_page_setup()
 * and before any drawing is performed to the surface.
 *
 * See cairo_ps_surface_dsc_comment() for more details.
 *
 * Since: 1.2
 **/

void
cairo_ps_surface_dsc_begin_setup (cairo_surface_t *surface)
{
    cairo_ps_surface_t *ps_surface = NULL;

    if (! _extract_ps_surface (surface, TRUE, &ps_surface))
 return;

    if (ps_surface->dsc_comment_target == &ps_surface->dsc_header_comments)
 ps_surface->dsc_comment_target = &ps_surface->dsc_setup_comments;
}

/**
 * cairo_ps_surface_dsc_begin_page_setup:
 * @surface: a PostScript #cairo_surface_t
 *
 * This function indicates that subsequent calls to
 * cairo_ps_surface_dsc_comment() should direct comments to the
 * PageSetup section of the PostScript output.
 *
 * This function call is only needed for the first page of a
 * surface. It should be called after any call to
 * cairo_ps_surface_dsc_begin_setup() and before any drawing is
 * performed to the surface.
 *
 * See cairo_ps_surface_dsc_comment() for more details.
 *
 * Since: 1.2
 **/

void
cairo_ps_surface_dsc_begin_page_setup (cairo_surface_t *surface)
{
    cairo_ps_surface_t *ps_surface = NULL;

    if (! _extract_ps_surface (surface, TRUE, &ps_surface))
 return;

    if (ps_surface->dsc_comment_target == &ps_surface->dsc_header_comments ||
 ps_surface->dsc_comment_target == &ps_surface->dsc_setup_comments)
    {
 ps_surface->dsc_comment_target = &ps_surface->dsc_page_setup_comments;
    }
}

static cairo_status_t
_cairo_ps_surface_finish (void *abstract_surface)
{
    cairo_status_t status, status2;
    cairo_ps_surface_t *surface = abstract_surface;
    int i, num_comments;
    char **comments;

    status = surface->base.status;
    if (unlikely (status))
 goto CLEANUP;

    _cairo_ps_surface_emit_header (surface);

    _cairo_output_stream_printf (surface->final_stream,
     "%%%%BeginSetup\n");

    num_comments = _cairo_array_num_elements (&surface->dsc_setup_comments);
    if (num_comments) {
 comments = _cairo_array_index (&surface->dsc_setup_comments, 0);
 for (i = 0; i < num_comments; i++) {
     _cairo_output_stream_printf (surface->final_stream,
      "%s\n", comments[i]);
     free (comments[i]);
     comments[i] = NULL;
 }
    }

    status = _cairo_ps_surface_emit_font_subsets (surface);
    if (unlikely (status))
 goto CLEANUP;

    status = _cairo_ps_surface_emit_forms (surface);
    if (unlikely (status))
 goto CLEANUP;

    _cairo_output_stream_printf (surface->final_stream,
     "%%%%EndSetup\n");

    status = _cairo_ps_surface_emit_body (surface);
    if (unlikely (status))
 goto CLEANUP;

    _cairo_ps_surface_emit_footer (surface);

CLEANUP:
    _cairo_hash_table_foreach (surface->forms,
          _cairo_ps_form_pluck,
          surface->forms);
    _cairo_hash_table_destroy (surface->forms);
    _cairo_scaled_font_subsets_destroy (surface->font_subsets);

    status2 = _cairo_output_stream_destroy (surface->stream);
    if (status == CAIRO_STATUS_SUCCESS)
 status = status2;

    fclose (surface->tmpfile);

    status2 = _cairo_output_stream_destroy (surface->final_stream);
    if (status == CAIRO_STATUS_SUCCESS)
 status = status2;

    while (! cairo_list_is_empty (&surface->document_media)) {
        cairo_page_media_t *page;

        page = cairo_list_first_entry (&surface->document_media,
                                       cairo_page_media_t,
                                       link);
        cairo_list_del (&page->link);
 free (page->name);
 free (page);
    }

    num_comments = _cairo_array_num_elements (&surface->dsc_header_comments);
    comments = _cairo_array_index (&surface->dsc_header_comments, 0);
    for (i = 0; i < num_comments; i++)
 free (comments[i]);
    _cairo_array_fini (&surface->dsc_header_comments);

    num_comments = _cairo_array_num_elements (&surface->dsc_setup_comments);
    comments = _cairo_array_index (&surface->dsc_setup_comments, 0);
    for (i = 0; i < num_comments; i++)
 free (comments[i]);
    _cairo_array_fini (&surface->dsc_setup_comments);

    num_comments = _cairo_array_num_elements (&surface->dsc_page_setup_comments);
    comments = _cairo_array_index (&surface->dsc_page_setup_comments, 0);
    for (i = 0; i < num_comments; i++)
 free (comments[i]);
    _cairo_array_fini (&surface->dsc_page_setup_comments);

    _cairo_array_fini (&surface->recording_surf_stack);

    _cairo_surface_clipper_reset (&surface->clipper);

    return status;
}

static cairo_int_status_t
_cairo_ps_surface_start_page (void *abstract_surface)
{
    cairo_ps_surface_t *surface = abstract_surface;

    /* Increment before print so page numbers start at 1. */
    surface->num_pages++;

    return CAIRO_STATUS_SUCCESS;
}

static cairo_int_status_t
_cairo_ps_surface_show_page (void *abstract_surface)
{
    cairo_ps_surface_t *surface = abstract_surface;
    cairo_int_status_t status;

    if (surface->clipper.clip != NULL)
 _cairo_surface_clipper_reset (&surface->clipper);

    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
    if (unlikely (status))
 return status;

    _cairo_output_stream_printf (surface->stream,
     "Q Q\n"
     "showpage\n");

    return CAIRO_STATUS_SUCCESS;
}

static cairo_bool_t
color_is_gray (double red, double green, double blue)
{
    const double epsilon = 0.00001;

    return (fabs (red - green) < epsilon &&
     fabs (red - blue) < epsilon);
}

/**
 * _cairo_ps_surface_acquire_source_surface_from_pattern:
 * @surface: [in] the ps surface
 * @pattern: [in] A #cairo_pattern_t of type SURFACE or RASTER_SOURCE to use
 *                as the source
 * @extents: [in] extents of the operation that is using this source
 * @src_surface_extents: [out] return source surface extents
 * @src_surface_bounded: [out] return TRUE if source surface is bounded
 * @src_op_extents: [out] return operation extents in source space
 * @source_surface: [out] returns surface of type image surface or recording surface
 * @x_offset: [out] return x offset of surface
 * @y_offset: [out] return y offset of surface
 *
 * Acquire source surface or raster source pattern.
 **/

static cairo_status_t
_cairo_ps_surface_acquire_source_surface_from_pattern (
    cairo_ps_surface_t           *surface,
    const cairo_pattern_t        *pattern,
    const cairo_rectangle_int_t  *extents,
    cairo_rectangle_int_t        *src_surface_extents,
    cairo_bool_t                 *src_surface_bounded,
    cairo_rectangle_int_t        *src_op_extents,
    cairo_surface_t             **source_surface,
    double                       *x_offset,
    double                       *y_offset)
{
    cairo_status_t status;
    cairo_box_t bbox;

    *x_offset = 0;
    *y_offset = 0;

    /* get the operation extents in pattern space */
    _cairo_box_from_rectangle (&bbox, extents);
    _cairo_matrix_transform_bounding_box_fixed (&pattern->matrix, &bbox, NULL);
    _cairo_box_round_to_rectangle (&bbox, src_op_extents);

    if (pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) {
 cairo_surface_t *surf;

 surf = _cairo_raster_source_pattern_acquire (pattern, &surface->base, src_op_extents);
 if (!surf)
     return CAIRO_INT_STATUS_UNSUPPORTED;

 *src_surface_bounded = _cairo_surface_get_extents (surf, src_surface_extents);
 cairo_surface_get_device_offset (surf, x_offset, y_offset);
 *source_surface = surf;
    } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
 cairo_surface_t *surf = NULL;

 *source_surface = ((cairo_surface_pattern_t *) pattern)->surface;
 surf = *source_surface;
 *src_surface_bounded = _cairo_surface_get_extents (surf, src_surface_extents);
 if (surf->type == CAIRO_SURFACE_TYPE_RECORDING) {
     if (_cairo_surface_is_snapshot (surf))
  surf = _cairo_surface_snapshot_get_target (surf);

     if (surf->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
  cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) surf;

  *src_surface_extents = sub->extents;
  *src_surface_bounded = TRUE;
  *x_offset = -sub->extents.x;
  *y_offset = -sub->extents.y;
     }

     cairo_surface_destroy (surf);
 } else if (surf->type != CAIRO_SURFACE_TYPE_IMAGE) {
     cairo_image_surface_t *image;
     void *image_extra;

     status = _cairo_surface_acquire_source_image (surf, &image, &image_extra);
     if (unlikely (status))
  return status;

     *src_surface_bounded = _cairo_surface_get_extents (&image->base, src_surface_extents);
     _cairo_surface_release_source_image (surf, image, image_extra);
 }
    } else {
 ASSERT_NOT_REACHED;
    }

    return CAIRO_STATUS_SUCCESS;
}

static void
_cairo_ps_surface_release_source_surface_from_pattern (cairo_ps_surface_t           *surface,
             const cairo_pattern_t        *pattern,
             cairo_surface_t              *source_surface)
{
    if  (pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE)
 _cairo_raster_source_pattern_release (pattern, source_surface);
}

/**
 * _cairo_ps_surface_create_padded_image_from_image:
 * @surface: the ps surface
 * @source: The source image
 * @extents: extents of the operation that is using this source
 * @image: returns the padded image or NULL if padding not required to fill @extents
 * @image_extents: returns extents of padded image. These extents in are in source image space.
 *
 * Creates a padded image if the source image does not fill the extents.
 **/

static cairo_status_t
_cairo_ps_surface_create_padded_image_from_image (cairo_ps_surface_t           *surface,
        cairo_image_surface_t        *source,
        const cairo_matrix_t         *source_matrix,
        const cairo_rectangle_int_t  *extents,
        cairo_image_surface_t       **image,
        cairo_rectangle_int_t        *image_extents)
{
    cairo_box_t box;
    cairo_rectangle_int_t rect;
    cairo_surface_t    *pad_image;
    cairo_surface_pattern_t pad_pattern;
    int w, h;
    cairo_int_status_t      status;

    /* get the operation extents in pattern space */
    _cairo_box_from_rectangle (&box, extents);
    _cairo_matrix_transform_bounding_box_fixed (source_matrix, &box, NULL);
    _cairo_box_round_to_rectangle (&box, &rect);

    /* Check if image needs padding to fill extents. */
    w = source->width;
    h = source->height;
    if (_cairo_fixed_integer_ceil(box.p1.x) < 0 ||
 _cairo_fixed_integer_ceil(box.p1.y) < 0 ||
 _cairo_fixed_integer_floor(box.p2.y) > w ||
 _cairo_fixed_integer_floor(box.p2.y) > h)
    {
 pad_image = _cairo_image_surface_create_with_content (source->base.content,
             rect.width,
             rect.height);
 if (pad_image->status)
     return pad_image->status;

 _cairo_pattern_init_for_surface (&pad_pattern, &source->base);
 cairo_matrix_init_translate (&pad_pattern.base.matrix, rect.x, rect.y);
 pad_pattern.base.extend = CAIRO_EXTEND_PAD;
 status = _cairo_surface_paint (pad_image,
           CAIRO_OPERATOR_SOURCE,
           &pad_pattern.base,
           NULL);
 _cairo_pattern_fini (&pad_pattern.base);
 *image = (cairo_image_surface_t *) pad_image;
 image_extents->x = rect.x;
 image_extents->y = rect.y;
 image_extents->width = rect.width;
 image_extents->height = rect.height;
    } else {
 *image = NULL;
 status = CAIRO_STATUS_SUCCESS;
    }

    return status;
}

static cairo_int_status_t
_cairo_ps_surface_analyze_surface_pattern_transparency (cairo_ps_surface_t            *surface,
       const cairo_pattern_t         *pattern,
       const cairo_rectangle_int_t   *extents)
{
    cairo_rectangle_int_t src_surface_extents;
    cairo_bool_t src_surface_bounded;
    cairo_rectangle_int_t src_op_extents;
    cairo_surface_t *source_surface;
    double x_offset, y_offset;
    cairo_image_surface_t *image;
    void *image_extra;
    cairo_int_status_t status;
    cairo_image_transparency_t transparency;

    status = _cairo_ps_surface_acquire_source_surface_from_pattern (surface,
            pattern,
            extents,
            &src_surface_extents,
            &src_surface_bounded,
            &src_op_extents,
            &source_surface,
            &x_offset,
            &y_offset);
    if (unlikely (status))
 return status;

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=98 H=90 G=94

¤ Dauer der Verarbeitung: 0.27 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.