Ticket #87: 0005-Add-cairo-os2-printing-surface.c-patterned-after-cai.patch

File 0005-Add-cairo-os2-printing-surface.c-patterned-after-cai.patch, 60.1 KB (added by dmik, 8 years ago)
  • src/Makefile.sources

    From c6b1437877a9400d5561f10a1f1c8cbc18fbfb60 Mon Sep 17 00:00:00 2001
    From: Dave Yeo <dave.r.yeo@gmail.com>
    Date: Wed, 31 Aug 2011 21:40:37 -0700
    Subject: [PATCH 5/5] Add cairo-os2-printing-surface.c patterned after
     cairo-win32-printing-surface.c.
    
    Signed-off-by: Dave Yeo <dave.r.yeo@gmail.com>
    ---
     src/Makefile.sources             |    2 +-
     src/cairo-os2-printing-surface.c | 1593 ++++++++++++++++++++++++++++++++++++++
     src/cairo-os2-private.h          |   13 +-
     src/cairo-os2-surface.c          |    6 +-
     src/cairo-os2.h                  |    5 +
     src/cairo.h                      |    4 +-
     6 files changed, 1618 insertions(+), 5 deletions(-)
     create mode 100644 src/cairo-os2-printing-surface.c
    
    diff --git a/src/Makefile.sources b/src/Makefile.sources
    index 8f2d09e..ddf06df 100644
    a b cairo_skia_cxx_sources = \ 
    322322
    323323cairo_os2_headers = cairo-os2.h
    324324cairo_os2_private = cairo-os2-private.h
    325 cairo_os2_sources = cairo-os2-surface.c
     325cairo_os2_sources = cairo-os2-surface.c cairo-os2-printing-surface.c
    326326
    327327# automake is stupid enough to always use c++ linker if we enable the
    328328# following lines, even if beos surface is not enabled.  Disable it for now.
  • new file src/cairo-os2-printing-surface.c

    diff --git a/src/cairo-os2-printing-surface.c b/src/cairo-os2-printing-surface.c
    new file mode 100644
    index 0000000..cb4b177
    - +  
     1/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
     2/* Cairo - a vector graphics library with display and print output
     3 *
     4 * Copyright © 2007, 2008 Adrian Johnson
     5 *
     6 * This library is free software; you can redistribute it and/or
     7 * modify it either under the terms of the GNU Lesser General Public
     8 * License version 2.1 as published by the Free Software Foundation
     9 * (the "LGPL") or, at your option, under the terms of the Mozilla
     10 * Public License Version 1.1 (the "MPL"). If you do not alter this
     11 * notice, a recipient may use your version of this file under either
     12 * the MPL or the LGPL.
     13 *
     14 * You should have received a copy of the LGPL along with this library
     15 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
     16 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
     17 * You should have received a copy of the MPL along with this library
     18 * in the file COPYING-MPL-1.1
     19 *
     20 * The contents of this file are subject to the Mozilla Public License
     21 * Version 1.1 (the "License"); you may not use this file except in
     22 * compliance with the License. You may obtain a copy of the License at
     23 * http://www.mozilla.org/MPL/
     24 *
     25 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
     26 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
     27 * the specific language governing rights and limitations.
     28 *
     29 * The Original Code is the cairo graphics library.
     30 *
     31 * The Initial Developer of the Original Code is Adrian Johnson.
     32 *
     33 * Contributor(s):
     34 *      Adrian Johnson <ajohnson@redneon.com>
     35 *      Vladimir Vukicevic <vladimir@pobox.com>
     36 *      Rich Walsh <rich@e-vertise.com>
     37 */
     38
     39
     40/**
     41 * cairo_os2_printing_surface:
     42 *
     43 * #cairo_os2_printing_surface is a port of #cairo_win32_printing_surface.
     44 * Changes have been made to accommodate differences in the platforms'
     45 * APIs and capabilities.  The source code has also been rearranged to
     46 * provide the file with more structure.
     47 **/
     48
     49
     50#define INCL_WINERRORS
     51#define INCL_GPI
     52#define INCL_DEV
     53#include <os2.h>
     54#include "cairo-os2-private.h"
     55#include "cairo-default-context-private.h"
     56#include "cairo-error-private.h"
     57#include "cairo-paginated-private.h"
     58#include "cairo-clip-private.h"
     59#include "cairo-image-surface-private.h"
     60#include "cairo-recording-surface-private.h"
     61#include "cairo-scaled-font-subsets-private.h"
     62#include "cairo-image-info-private.h"
     63
     64typedef union _RGB2LONG {
     65    RGB2  r;
     66    LONG  l;
     67} RGB2LONG;
     68
     69static cairo_status_t
     70_cairo_os2_printing_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
     71                                                   cairo_path_fixed_t *path,
     72                                                   cairo_fill_rule_t   fill_rule,
     73                                                   double              tolerance,
     74                                                   cairo_antialias_t   antialias);
     75
     76static const cairo_surface_backend_t cairo_os2_printing_surface_backend;
     77static const cairo_paginated_surface_backend_t cairo_os2_paginated_surface_backend;
     78
     79
     80/*
     81 * public function
     82 */
     83
     84/**
     85 * cairo_os2_printing_surface_create:
     86 * @hps: presentation space handle associated with the printer's
     87 *   device context
     88 * @width: width of the surface in pixels
     89 * @height: height of the surface in pixels
     90 *
     91 * Creates a cairo surface that targets the given HPS.  GPI will be
     92 * used as much as possible to draw to the surface.
     93 *
     94 * The returned surface will be wrapped using the paginated surface to
     95 * provide correct complex rendering behaviour; show_page() and
     96 * associated methods must be used for correct output.
     97 *
     98 * Return value: the newly created surface
     99 *
     100 * Since: 1.12
     101 **/
     102cairo_surface_t *
     103cairo_os2_printing_surface_create (HPS  hps,
     104                                   int  width,
     105                                   int  height)
     106{
     107    cairo_os2_surface_t *surface;
     108
     109    if (width < 0 || height < 0)
     110        return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
     111
     112    if (!hps)
     113        return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
     114
     115    WinGetLastError (0);
     116
     117    surface = malloc (sizeof (cairo_os2_surface_t));
     118    if (surface == NULL)
     119        return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
     120
     121    surface->subtype = CAIRO_OS2_SUBTYPE_PRINT;
     122    surface->hps     = hps;
     123    surface->width   = width;
     124    surface->height  = height;
     125    surface->format  = CAIRO_FORMAT_RGB24;
     126    surface->content = CAIRO_CONTENT_COLOR_ALPHA;
     127
     128    _cairo_surface_clipper_init (&surface->clipper,
     129                                 _cairo_os2_printing_surface_clipper_intersect_clip_path);
     130
     131    _cairo_surface_init (&surface->base,
     132                         &cairo_os2_printing_surface_backend,
     133                         NULL,
     134                         CAIRO_CONTENT_COLOR_ALPHA);
     135
     136    surface->paginated_surf = _cairo_paginated_surface_create (&surface->base,
     137                                                 CAIRO_CONTENT_COLOR_ALPHA,
     138                                                 &cairo_os2_paginated_surface_backend);
     139    /* paginated keeps the only reference to surface now, drop ours */
     140    cairo_surface_destroy (&surface->base);
     141
     142    return surface->paginated_surf;
     143}
     144
     145
     146/*
     147 * analysis functions
     148 */
     149
     150static cairo_int_status_t
     151analyze_surface_pattern_transparency (cairo_surface_pattern_t *pattern)
     152{
     153    cairo_image_surface_t     *image;
     154    void                      *image_extra;
     155    cairo_int_status_t         status;
     156    cairo_image_transparency_t transparency;
     157
     158    status = _cairo_surface_acquire_source_image (pattern->surface,
     159                                                  &image,
     160                                                  &image_extra);
     161    if (status)
     162        return status;
     163
     164    transparency = _cairo_image_analyze_transparency (image);
     165    switch (transparency) {
     166    case CAIRO_IMAGE_UNKNOWN:
     167        ASSERT_NOT_REACHED;
     168    case CAIRO_IMAGE_IS_OPAQUE:
     169        status = CAIRO_STATUS_SUCCESS;
     170        break;
     171
     172    case CAIRO_IMAGE_HAS_BILEVEL_ALPHA:
     173    case CAIRO_IMAGE_HAS_ALPHA:
     174        status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
     175        break;
     176    }
     177
     178    _cairo_surface_release_source_image (pattern->surface, image, image_extra);
     179
     180    return status;
     181}
     182
     183
     184static cairo_bool_t
     185surface_pattern_supported (const cairo_surface_pattern_t *pattern)
     186{
     187    if (_cairo_surface_is_recording (pattern->surface))
     188        return TRUE;
     189
     190    if (cairo_surface_get_type (pattern->surface) != CAIRO_SURFACE_TYPE_OS2_PRINTING &&
     191        pattern->surface->backend->acquire_source_image == NULL)
     192    {
     193        return FALSE;
     194    }
     195
     196    return TRUE;
     197}
     198
     199
     200static cairo_bool_t
     201pattern_supported (cairo_os2_surface_t *surface, const cairo_pattern_t *pattern)
     202{
     203    if (pattern->type == CAIRO_PATTERN_TYPE_SOLID)
     204        return TRUE;
     205
     206    if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE)
     207        return surface_pattern_supported ((const cairo_surface_pattern_t *) pattern);
     208
     209    return FALSE;
     210}
     211
     212
     213static cairo_int_status_t
     214_cairo_os2_printing_surface_analyze_operation (cairo_os2_surface_t   *surface,
     215                                               cairo_operator_t       op,
     216                                               const cairo_pattern_t *pattern)
     217{
     218    if (! pattern_supported (surface, pattern))
     219        return CAIRO_INT_STATUS_UNSUPPORTED;
     220
     221    if (!(op == CAIRO_OPERATOR_SOURCE ||
     222          op == CAIRO_OPERATOR_OVER ||
     223          op == CAIRO_OPERATOR_CLEAR))
     224        return CAIRO_INT_STATUS_UNSUPPORTED;
     225
     226    if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
     227        cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
     228
     229        if ( _cairo_surface_is_recording (surface_pattern->surface))
     230            return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
     231    }
     232
     233    if (op == CAIRO_OPERATOR_SOURCE ||
     234        op == CAIRO_OPERATOR_CLEAR)
     235        return CAIRO_STATUS_SUCCESS;
     236
     237    /* CAIRO_OPERATOR_OVER is only supported for opaque patterns. If
     238     * the pattern contains transparency, we return
     239     * CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY to the analysis
     240     * surface. If the analysis surface determines that there is
     241     * anything drawn under this operation, a fallback image will be
     242     * used. Otherwise the operation will be replayed during the
     243     * render stage and we blend the transarency into the white
     244     * background to convert the pattern to opaque.
     245     */
     246
     247    if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
     248        cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
     249
     250        return analyze_surface_pattern_transparency (surface_pattern);
     251    }
     252
     253    if (_cairo_pattern_is_opaque (pattern, NULL))
     254        return CAIRO_STATUS_SUCCESS;
     255    else
     256        return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
     257}
     258
     259
     260static cairo_bool_t
     261_cairo_os2_printing_surface_operation_supported (cairo_os2_surface_t   *surface,
     262                                                 cairo_operator_t       op,
     263                                                 const cairo_pattern_t *pattern)
     264{
     265    if (_cairo_os2_printing_surface_analyze_operation (surface, op, pattern)
     266        != CAIRO_INT_STATUS_UNSUPPORTED)
     267        return TRUE;
     268
     269    return FALSE;
     270}
     271
     272
     273/*
     274 * utility functions
     275 */
     276
     277static cairo_status_t
     278_gpi_error (const char *context)
     279{
     280
     281    ULONG errid = WinGetLastError (0);
     282
     283    fprintf (stderr, "_cairo_os2_printing_surface_%s - err= 0x%04lx\n",
     284             context, errid);
     285    fflush (stderr);
     286    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
     287}
     288
     289
     290static void
     291_cairo_os2_printing_surface_init_clear_color (cairo_os2_surface_t   *surface,
     292                                              cairo_solid_pattern_t *color)
     293{
     294    if (surface->content == CAIRO_CONTENT_COLOR_ALPHA)
     295        _cairo_pattern_init_solid (color, CAIRO_COLOR_WHITE);
     296    else
     297        _cairo_pattern_init_solid (color, CAIRO_COLOR_BLACK);
     298}
     299
     300
     301static cairo_status_t
     302_cairo_os2_printing_surface_set_area_color (cairo_os2_surface_t   *surface,
     303                                            const cairo_pattern_t *source)
     304{
     305    cairo_solid_pattern_t *pattern = (cairo_solid_pattern_t *) source;
     306    AREABUNDLE bundle;
     307    RGB2LONG   c;
     308
     309    c.r.bBlue  = pattern->color.blue_short  >> 8;
     310    c.r.bGreen = pattern->color.green_short >> 8;
     311    c.r.bRed   = pattern->color.red_short   >> 8;
     312    c.r.fcOptions = 0;
     313
     314    if (!CAIRO_COLOR_IS_OPAQUE(&pattern->color) &&
     315        surface->content == CAIRO_CONTENT_COLOR_ALPHA) {
     316        /* Blend into white */
     317        BYTE one_minus_alpha = 255 - (pattern->color.alpha_short >> 8);
     318
     319        c.r.bBlue  += one_minus_alpha;
     320        c.r.bGreen += one_minus_alpha;
     321        c.r.bRed   += one_minus_alpha;
     322    }
     323
     324    bundle.lColor = c.l;
     325    if (!GpiSetAttrs (surface->hps, PRIM_AREA, ABB_COLOR, 0, (PBUNDLE)&bundle))
     326        return _gpi_error ("set_area_color - GpiSetAttrs");
     327
     328    return CAIRO_STATUS_SUCCESS;
     329}
     330
     331
     332static inline void
     333_cairo_os2_matrixlf_to_matrix (const MATRIXLF *mlf,
     334                               cairo_matrix_t *m)
     335{
     336    m->xx = ((double) mlf->fxM11) / ((double) (1 << 16));
     337    m->yx = ((double) mlf->fxM12) / ((double) (1 << 16));
     338
     339    m->xy = ((double) mlf->fxM21) / ((double) (1 << 16));
     340    m->yy = ((double) mlf->fxM22) / ((double) (1 << 16));
     341
     342    m->x0 = (double) mlf->lM31;
     343    m->y0 = (double) mlf->lM32;
     344}
     345
     346
     347static inline void
     348_cairo_matrix_to_os2_matrixlf (const cairo_matrix_t *m,
     349                               MATRIXLF             *mlf)
     350{
     351    mlf->fxM11 = _cairo_fixed_16_16_from_double (m->xx);
     352    mlf->fxM12 = _cairo_fixed_16_16_from_double (m->yx);
     353    mlf->lM13  = 0;
     354
     355    mlf->fxM21 = _cairo_fixed_16_16_from_double (m->xy);
     356    mlf->fxM22 = _cairo_fixed_16_16_from_double (m->yy);
     357    mlf->lM23  = 0;
     358
     359    mlf->lM31  = (LONG) (m->x0 + 0.5);
     360    mlf->lM32  = (LONG) (m->y0 + 0.5);
     361    mlf->lM33  = 1;
     362}
     363
     364
     365static void
     366_cairo_matrix_factor_out_scale (cairo_matrix_t *m, double *scale)
     367{
     368    double s;
     369
     370    s = fabs (m->xx);
     371    if (fabs (m->xy) > s)
     372        s = fabs (m->xy);
     373    if (fabs (m->yx) > s)
     374        s = fabs (m->yx);
     375    if (fabs (m->yy) > s)
     376        s = fabs (m->yy);
     377    *scale = s;
     378    s = 1.0/s;
     379    cairo_matrix_scale (m, s, s);
     380}
     381
     382
     383static int
     384_cairo_os2_printing_surface_line_cap (cairo_line_cap_t cap)
     385{
     386    switch (cap) {
     387    case CAIRO_LINE_CAP_BUTT:
     388        return LINEEND_FLAT;
     389    case CAIRO_LINE_CAP_ROUND:
     390        return LINEEND_ROUND;
     391    case CAIRO_LINE_CAP_SQUARE:
     392        return LINEEND_SQUARE;
     393    default:
     394        ASSERT_NOT_REACHED;
     395        return 0;
     396    }
     397}
     398
     399
     400static int
     401_cairo_os2_printing_surface_line_join (cairo_line_join_t join)
     402{
     403    switch (join) {
     404    case CAIRO_LINE_JOIN_MITER:
     405        return LINEJOIN_MITRE;
     406    case CAIRO_LINE_JOIN_ROUND:
     407        return LINEJOIN_ROUND;
     408    case CAIRO_LINE_JOIN_BEVEL:
     409        return LINEJOIN_BEVEL;
     410    default:
     411        ASSERT_NOT_REACHED;
     412        return 0;
     413    }
     414}
     415
     416
     417/*
     418 * path functions
     419 */
     420
     421typedef struct _os2_print_path_info {
     422    cairo_os2_surface_t *surface;
     423} os2_path_info_t;
     424
     425
     426static cairo_status_t
     427_cairo_os2_printing_surface_path_move_to (void                *closure,
     428                                          const cairo_point_t *point)
     429{
     430    POINTL ptl;
     431    os2_path_info_t *path_info = closure;
     432
     433    if (path_info->surface->has_ctm) {
     434        double x, y;
     435
     436        x = _cairo_fixed_to_double (point->x);
     437        y = _cairo_fixed_to_double (point->y);
     438        cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y);
     439        ptl.x = (LONG) x;
     440        ptl.y = (LONG) y;
     441    } else {
     442        ptl.x = _cairo_fixed_integer_part (point->x);
     443        ptl.y = _cairo_fixed_integer_part (point->y);
     444    }
     445    if (!GpiMove (path_info->surface->hps, &ptl))
     446        return _gpi_error ("path_move_to - GpiMove");
     447
     448    return CAIRO_STATUS_SUCCESS;
     449}
     450
     451
     452static cairo_status_t
     453_cairo_os2_printing_surface_path_line_to (void                *closure,
     454                                          const cairo_point_t *point)
     455{
     456    POINTL ptl;
     457    os2_path_info_t *path_info = closure;
     458
     459    path_info->surface->path_empty = FALSE;
     460    if (path_info->surface->has_ctm) {
     461        double x, y;
     462
     463        x = _cairo_fixed_to_double (point->x);
     464        y = _cairo_fixed_to_double (point->y);
     465        cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y);
     466        ptl.x = (LONG) x;
     467        ptl.y = (LONG) y;
     468    } else {
     469        ptl.x = _cairo_fixed_integer_part (point->x);
     470        ptl.y = _cairo_fixed_integer_part (point->y);
     471    }
     472    if (GpiLine (path_info->surface->hps, &ptl) == GPI_ERROR)
     473        return _gpi_error ("path_line_to - GpiLine");
     474
     475    return CAIRO_STATUS_SUCCESS;
     476}
     477
     478
     479static cairo_status_t
     480_cairo_os2_printing_surface_path_curve_to (void                *closure,
     481                                           const cairo_point_t *b,
     482                                           const cairo_point_t *c,
     483                                           const cairo_point_t *d)
     484{
     485    os2_path_info_t *path_info = closure;
     486    POINTL points[3];
     487
     488    path_info->surface->path_empty = FALSE;
     489    if (path_info->surface->has_ctm) {
     490        double x, y;
     491
     492        x = _cairo_fixed_to_double (b->x);
     493        y = _cairo_fixed_to_double (b->y);
     494        cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y);
     495        points[0].x = (LONG) x;
     496        points[0].y = (LONG) y;
     497
     498        x = _cairo_fixed_to_double (c->x);
     499        y = _cairo_fixed_to_double (c->y);
     500        cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y);
     501        points[1].x = (LONG) x;
     502        points[1].y = (LONG) y;
     503
     504        x = _cairo_fixed_to_double (d->x);
     505        y = _cairo_fixed_to_double (d->y);
     506        cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y);
     507        points[2].x = (LONG) x;
     508        points[2].y = (LONG) y;
     509    } else {
     510        points[0].x = _cairo_fixed_integer_part (b->x);
     511        points[0].y = _cairo_fixed_integer_part (b->y);
     512        points[1].x = _cairo_fixed_integer_part (c->x);
     513        points[1].y = _cairo_fixed_integer_part (c->y);
     514        points[2].x = _cairo_fixed_integer_part (d->x);
     515        points[2].y = _cairo_fixed_integer_part (d->y);
     516    }
     517    if (GpiPolySpline (path_info->surface->hps, 3, points) == GPI_ERROR)
     518        return _gpi_error ("path_curve_to - GpiPolySpline");
     519
     520    return CAIRO_STATUS_SUCCESS;
     521}
     522
     523
     524static cairo_status_t
     525_cairo_os2_printing_surface_path_close_path (void *closure)
     526{
     527    os2_path_info_t *path_info = closure;
     528
     529    if (!GpiCloseFigure (path_info->surface->hps))
     530        return _gpi_error ("path_close_path - GpiCloseFigure");
     531
     532    return CAIRO_STATUS_SUCCESS;
     533}
     534
     535
     536static cairo_status_t
     537_cairo_os2_printing_surface_emit_path (cairo_os2_surface_t      *surface,
     538                                       const cairo_path_fixed_t *path)
     539{
     540    os2_path_info_t path_info;
     541
     542    path_info.surface = surface;
     543    return _cairo_path_fixed_interpret (path,
     544                                        _cairo_os2_printing_surface_path_move_to,
     545                                        _cairo_os2_printing_surface_path_line_to,
     546                                        _cairo_os2_printing_surface_path_curve_to,
     547                                        _cairo_os2_printing_surface_path_close_path,
     548                                        &path_info);
     549}
     550
     551
     552/*
     553 * clip functions
     554 */
     555
     556static cairo_status_t
     557_cairo_os2_printing_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
     558                                                   cairo_path_fixed_t *path,
     559                                                   cairo_fill_rule_t   fill_rule,
     560                                                   double              tolerance,
     561                                                   cairo_antialias_t   antialias)
     562{
     563    cairo_os2_surface_t *surface = cairo_container_of (clipper,
     564                                                       cairo_os2_surface_t,
     565                                                       clipper);
     566    cairo_status_t status;
     567    LONG options;
     568
     569    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
     570        return CAIRO_STATUS_SUCCESS;
     571
     572    if (path == NULL) {
     573        if (!GpiRestorePS (surface->hps, -1))
     574            return _gpi_error ("clipper_intersect_clip_path - GpiRestorePS");
     575        if (GpiSavePS (surface->hps) == GPI_ERROR)
     576            return _gpi_error ("clipper_intersect_clip_path - GpiSavePS");
     577
     578        return CAIRO_STATUS_SUCCESS;
     579    }
     580
     581    if (!GpiBeginPath (surface->hps, 1))
     582        return _gpi_error ("clipper_intersect_clip_path - GpiBeginPath");
     583    status = _cairo_os2_printing_surface_emit_path (surface, path);
     584    if (status)
     585        return status;
     586    if (!GpiEndPath (surface->hps))
     587        return _gpi_error ("clipper_intersect_clip_path - GpiEndPath");
     588
     589    options = SCP_AND | SCP_INCL;
     590
     591    switch (fill_rule) {
     592    case CAIRO_FILL_RULE_WINDING:
     593        options |= SCP_WINDING;
     594        break;
     595    case CAIRO_FILL_RULE_EVEN_ODD:
     596        options |= SCP_ALTERNATE;
     597        break;
     598    default:
     599        ASSERT_NOT_REACHED;
     600    }
     601
     602    if (!GpiSetClipPath (surface->hps, 1, options))
     603        return _gpi_error ("clipper_intersect_clip_path - GpiSetClipPath - and");
     604
     605    return CAIRO_STATUS_SUCCESS;
     606}
     607
     608
     609static cairo_status_t
     610_cairo_os2_printing_surface_get_ctm_clip_box (cairo_os2_surface_t *surface,
     611                                              RECTL               *clip)
     612{
     613    MATRIXLF mlf;
     614
     615    _cairo_matrix_to_os2_matrixlf (&surface->ctm, &mlf);
     616
     617    if (!GpiSetModelTransformMatrix (surface->hps, 9, &mlf, TRANSFORM_PREEMPT))
     618        return _gpi_error ("get_ctm_clip_box - GpiSetModelTransformMatrix (preempt)");
     619    if (GpiQueryClipBox (surface->hps, clip) == RGN_ERROR)
     620        return _gpi_error ("get_ctm_clip_box - GpiQueryClipBox");
     621
     622    _cairo_matrix_to_os2_matrixlf (&surface->gpi_ctm, &mlf);
     623    if (!GpiSetModelTransformMatrix (surface->hps, 9, &mlf, TRANSFORM_REPLACE))
     624        return _gpi_error ("get_ctm_clip_box - GpiSetModelTransformMatrix (replace)");
     625
     626    return CAIRO_STATUS_SUCCESS;
     627}
     628
     629
     630/*
     631 * paint
     632 */
     633
     634static cairo_status_t
     635_cairo_os2_printing_surface_paint_solid_pattern (cairo_os2_surface_t   *surface,
     636                                                 const cairo_pattern_t *pattern)
     637{
     638    RECTL   clip;
     639    POINTL  ptl;
     640    cairo_status_t status;
     641
     642    if (GpiQueryClipBox (surface->hps, &clip) == RGN_ERROR)
     643        return _gpi_error ("paint_solid_pattern - GpiQueryClipBox");
     644
     645    status = _cairo_os2_printing_surface_set_area_color (surface, pattern);
     646    if (status)
     647        return status;
     648
     649    if (!GpiQueryCurrentPosition (surface->hps, &ptl) ||
     650        !GpiMove (surface->hps, (POINTL*)&clip.xLeft) ||
     651        GpiBox (surface->hps, DRO_FILL, (POINTL*)&clip.xRight, 0, 0) == GPI_ERROR ||
     652        !GpiMove (surface->hps, &ptl))
     653        return _gpi_error ("paint_solid_pattern - GpiBox et al");
     654
     655    return CAIRO_STATUS_SUCCESS;
     656}
     657
     658
     659static cairo_status_t
     660_cairo_os2_printing_surface_paint_recording_pattern (cairo_os2_surface_t     *surface,
     661                                                     cairo_surface_pattern_t *pattern)
     662{
     663    cairo_content_t old_content;
     664    cairo_matrix_t old_ctm;
     665    cairo_bool_t old_has_ctm;
     666    cairo_rectangle_int_t recording_extents;
     667    cairo_status_t status;
     668    cairo_extend_t extend;
     669    cairo_matrix_t p2d;
     670    MATRIXLF mlf;
     671    int x_tile, y_tile, left, right, top, bottom;
     672    RECTL clip;
     673    cairo_recording_surface_t *recording_surface =
     674                              (cairo_recording_surface_t *) pattern->surface;
     675    cairo_box_t bbox;
     676
     677    extend = cairo_pattern_get_extend (&pattern->base);
     678
     679    p2d = pattern->base.matrix;
     680    status = cairo_matrix_invert (&p2d);
     681    /* _cairo_pattern_set_matrix guarantees invertibility */
     682    assert (status == CAIRO_STATUS_SUCCESS);
     683
     684    old_ctm = surface->ctm;
     685    old_has_ctm = surface->has_ctm;
     686    cairo_matrix_multiply (&p2d, &p2d, &surface->ctm);
     687    surface->ctm = p2d;
     688    if (GpiSavePS (surface->hps) == GPI_ERROR)
     689        return _gpi_error ("paint_recording_pattern - GpiSavePS 1");
     690
     691    _cairo_matrix_to_os2_matrixlf (&p2d, &mlf);
     692
     693    status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL);
     694    if (status)
     695        return status;
     696
     697    _cairo_box_round_to_rectangle (&bbox, &recording_extents);
     698
     699    status = _cairo_os2_printing_surface_get_ctm_clip_box (surface, &clip);
     700    if (status)
     701        return status;
     702
     703    if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) {
     704        left = floor (clip.xLeft / _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x));
     705        right = ceil (clip.xRight / _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x));
     706        top = floor (clip.yTop / _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y));
     707        bottom = ceil (clip.yBottom / _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y));
     708    } else {
     709        left = 0;
     710        right = 1;
     711        top = 0;
     712        bottom = 1;
     713    }
     714
     715    old_content = surface->content;
     716    if (recording_surface->base.content == CAIRO_CONTENT_COLOR) {
     717        surface->content = CAIRO_CONTENT_COLOR;
     718        status = _cairo_os2_printing_surface_paint_solid_pattern (surface,
     719                                                                  &_cairo_pattern_black.base);
     720        if (status)
     721            return status;
     722    }
     723
     724    for (y_tile = top; y_tile < bottom; y_tile++) {
     725        for (x_tile = left; x_tile < right; x_tile++) {
     726            cairo_matrix_t m;
     727            double x, y;
     728            POINTL ptl;
     729
     730            if (GpiSavePS (surface->hps) == GPI_ERROR)
     731                return _gpi_error ("paint_recording_pattern - GpiSavePS");
     732            m = p2d;
     733            cairo_matrix_translate (&m,
     734                                    x_tile*recording_extents.width,
     735                                    y_tile*recording_extents.height);
     736            if (extend == CAIRO_EXTEND_REFLECT) {
     737                if (x_tile % 2) {
     738                    cairo_matrix_translate (&m, recording_extents.width, 0);
     739                    cairo_matrix_scale (&m, -1, 1);
     740                }
     741                if (y_tile % 2) {
     742                    cairo_matrix_translate (&m, 0, recording_extents.height);
     743                    cairo_matrix_scale (&m, 1, -1);
     744                }
     745            }
     746            surface->ctm = m;
     747            surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm);
     748
     749            /* Set clip path around bbox of the pattern. */
     750            if (!GpiBeginPath (surface->hps, 1))
     751                return _gpi_error ("paint_recording_pattern - GpiBeginPath");
     752
     753            x = 0;
     754            y = 0;
     755            cairo_matrix_transform_point (&surface->ctm, &x, &y);
     756            ptl.x = (LONG) x;
     757            ptl.y = (LONG) y;
     758            if (!GpiMove (surface->hps, &ptl))
     759                return _gpi_error ("paint_recording_pattern - GpiMove");
     760
     761            x = recording_extents.width;
     762            y = 0;
     763            cairo_matrix_transform_point (&surface->ctm, &x, &y);
     764            ptl.x = (LONG) x;
     765            ptl.y = (LONG) y;
     766            if (GpiLine (surface->hps, &ptl) == GPI_ERROR)
     767                return _gpi_error ("paint_recording_pattern - GpiLine 1");
     768
     769            x = recording_extents.width;
     770            y = recording_extents.height;
     771            cairo_matrix_transform_point (&surface->ctm, &x, &y);
     772            ptl.x = (LONG) x;
     773            ptl.y = (LONG) y;
     774            if (GpiLine (surface->hps, &ptl) == GPI_ERROR)
     775                return _gpi_error ("paint_recording_pattern - GpiLine 2");
     776
     777            x = 0;
     778            y = recording_extents.height;
     779            cairo_matrix_transform_point (&surface->ctm, &x, &y);
     780            ptl.x = (LONG) x;
     781            ptl.y = (LONG) y;
     782            if (GpiLine (surface->hps, &ptl) == GPI_ERROR)
     783                return _gpi_error ("paint_recording_pattern - GpiLine 3");
     784
     785            if (!GpiCloseFigure (surface->hps))
     786                return _gpi_error ("paint_recording_pattern - GpiCloseFigure");
     787            if (!GpiEndPath (surface->hps))
     788                return _gpi_error ("paint_recording_pattern - GpiEndPath");
     789            if (!GpiSetClipPath (surface->hps, 1, SCP_AND))
     790                return _gpi_error ("paint_recording_pattern - GpiSetClipPath");
     791
     792            /* Allow clip path to be reset during replay */
     793            if (GpiSavePS (surface->hps) == GPI_ERROR)
     794                return _gpi_error ("paint_recording_pattern - GpiSavePS 2");
     795            status = _cairo_recording_surface_replay_region (&recording_surface->base,
     796                                                             NULL, &surface->base,
     797                                                             CAIRO_RECORDING_REGION_NATIVE);
     798            assert (status != (cairo_status_t) CAIRO_INT_STATUS_UNSUPPORTED);
     799
     800            /* Restore both the clip save and our earlier path SaveDC */
     801            if (!GpiRestorePS (surface->hps, -2))
     802                return _gpi_error ("paint_recording_pattern - GpiRestorePS 1");
     803
     804            if (status)
     805                return status;
     806        }
     807    }
     808
     809    surface->content = old_content;
     810    surface->ctm = old_ctm;
     811    surface->has_ctm = old_has_ctm;
     812    if (!GpiRestorePS (surface->hps, -1))
     813        return _gpi_error ("paint_recording_pattern - GpiRestorePS 2");
     814
     815    return status;
     816}
     817
     818
     819static cairo_status_t
     820_cairo_os2_printing_surface_paint_image_pattern (cairo_os2_surface_t     *surface,
     821                                                 cairo_surface_pattern_t *pattern)
     822{
     823    cairo_status_t status;
     824    cairo_extend_t extend;
     825    cairo_image_surface_t *image;
     826    void *image_extra;
     827    cairo_matrix_t m;
     828    MATRIXLF    mlf;
     829    BITMAPINFO2 bi;
     830    RECTL       clip;
     831    POINTL      aptl[4];
     832    int         x_tile, y_tile, left, right, top, bottom;
     833    int         bufsize, tgt_stride, tgt_pad, x_ctr, y_ctr;
     834    char       *pchBuffer = 0;
     835    char       *pchTarget;
     836    int        *pulSource;
     837
     838    extend = cairo_pattern_get_extend (&pattern->base);
     839    status = _cairo_surface_acquire_source_image (pattern->surface,
     840                                                  &image, &image_extra);
     841    if (status)
     842        return status;
     843
     844    if (image->base.status) {
     845        status = image->base.status;
     846        goto CLEANUP_IMAGE;
     847    }
     848
     849    if (image->width == 0 || image->height == 0) {
     850        status = CAIRO_STATUS_SUCCESS;
     851        goto CLEANUP_IMAGE;
     852    }
     853
     854    m = pattern->base.matrix;
     855    status = cairo_matrix_invert (&m);
     856    /* _cairo_pattern_set_matrix guarantees invertibility */
     857    assert (status == CAIRO_STATUS_SUCCESS);
     858
     859    cairo_matrix_multiply (&m, &m, &surface->gpi_ctm);
     860    if (GpiSavePS (surface->hps) == GPI_ERROR) {
     861        status = _gpi_error ("paint_image_pattern - GpiSavePS 1");
     862        goto CLEANUP_IMAGE;
     863    }
     864    _cairo_matrix_to_os2_matrixlf (&m, &mlf);
     865
     866    if (!GpiSetModelTransformMatrix (surface->hps, 9, &mlf, TRANSFORM_REPLACE)) {
     867        status = _gpi_error ("paint_image_pattern - GpiSetModelTransformMatrix");
     868        goto CLEANUP_IMAGE;
     869    }
     870
     871    /* OS/2 printer drivers typically don't support 32-bit bitmaps, so the
     872     * image data has to be converted to 24-bit using a temporary buffer.
     873     * This is done by copying 4 bytes from the source but advancing the
     874     * target pointer by 3 bytes so the high-order byte gets overwritten
     875     * on the next copy.  Because the start of each row has to be DWORD
     876     * aligned, padding bytes may be needed at the end of the row.  If it
     877     * happens that no padding is needed, then the temp buffer has to be
     878     * one byte longer than the bitmap or else the high-order byte from
     879     * the last source row will end up in unallocated memory.
     880     */
     881
     882    memset (&bi, 0, sizeof (bi));
     883    bi.cbFix = sizeof (BITMAPINFO2) - sizeof (bi.argbColor[0]);
     884    bi.cx = image->width;
     885    bi.cy = image->height;
     886    bi.cPlanes = 1;
     887    bi.cBitCount = 24;
     888
     889    tgt_stride = (((bi.cx * bi.cBitCount) + 31) / 32) * 4;
     890    tgt_pad = tgt_stride - bi.cx * 3;
     891    bufsize = tgt_stride * bi.cy + (tgt_pad ? 0 : 1);
     892
     893#ifdef OS2_USE_PLATFORM_ALLOC
     894# ifdef OS2_HIGH_MEMORY
     895    if (DosAllocMem ((void**) &pchBuffer, bufsize,
     896                     OBJ_ANY | PAG_READ | PAG_WRITE | PAG_COMMIT))
     897# endif
     898        if (DosAllocMem ((void**) &pchBuffer, bufsize,
     899                         PAG_READ | PAG_WRITE | PAG_COMMIT))
     900            status = CAIRO_STATUS_NO_MEMORY;
     901#else
     902    pchBuffer = (char*) calloc (1, bufsize);
     903    if (!pchBuffer)
     904        status = CAIRO_STATUS_NO_MEMORY;
     905#endif
     906
     907    if (status == CAIRO_STATUS_NO_MEMORY)
     908        goto CLEANUP_IMAGE;
     909
     910    pulSource = (int*)image->data;
     911    pchTarget = pchBuffer;
     912    for (y_ctr = bi.cy; y_ctr; y_ctr--) {
     913        for (x_ctr = bi.cx; x_ctr; x_ctr--) {
     914            *((int*)pchTarget) = *pulSource++;
     915            pchTarget += 3;
     916        }
     917        pchTarget += tgt_pad;
     918    }
     919
     920    if (GpiQueryClipBox (surface->hps, &clip) == RGN_ERROR) {
     921        status = _gpi_error ("paint_image_pattern - GpiQueryClipBox");
     922        goto CLEANUP_BUFFER;
     923    }
     924
     925    if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) {
     926        left = floor ( clip.xLeft / (double) image->width);
     927        right = ceil (clip.xRight / (double) image->width);
     928        top = floor (clip.yTop / (double) image->height);
     929        bottom = ceil (clip.yBottom / (double) image->height);
     930    } else {
     931        left = 0;
     932        right = 1;
     933        top = 0;
     934        bottom = 1;
     935    }
     936
     937    /* src coordinates */
     938    aptl[2].x = 0;
     939    aptl[2].y = 0;
     940    aptl[3].x = image->width;
     941    aptl[3].y = image->height;
     942
     943    for (y_tile = top; y_tile < bottom; y_tile++) {
     944        for (x_tile = left; x_tile < right; x_tile++) {
     945
     946            /* dst coordinates (made non-inclusive) */
     947            aptl[0].x = x_tile*image->width;
     948            aptl[0].y = y_tile*image->height;
     949            aptl[1].x = aptl[0].x + image->width - 1;
     950            aptl[1].y = aptl[0].y + image->height - 1;
     951
     952            if (GpiDrawBits (surface->hps, pchBuffer, &bi,
     953                             4, aptl, ROP_SRCCOPY, BBO_IGNORE) == GPI_ERROR)
     954            {
     955                status = _gpi_error ("paint_image_pattern - GpiDrawBits");
     956                goto CLEANUP_BUFFER;
     957            }
     958        }
     959    }
     960    if (!GpiRestorePS (surface->hps, -1)) {
     961        status = _gpi_error ("paint_image_pattern - GpiRestorePS");
     962        goto CLEANUP_BUFFER;
     963    }
     964
     965CLEANUP_BUFFER:
     966    /* Free the temp buffer */
     967    if (pchBuffer)
     968#ifdef OS2_USE_PLATFORM_ALLOC
     969        DosFreeMem (pchBuffer);
     970#else
     971        free (pchBuffer);
     972#endif
     973
     974CLEANUP_IMAGE:
     975    _cairo_surface_release_source_image (pattern->surface, image, image_extra);
     976
     977    return status;
     978}
     979
     980
     981static cairo_int_status_t
     982_cairo_os2_printing_surface_paint_pattern (cairo_os2_surface_t   *surface,
     983                                           const cairo_pattern_t *pattern)
     984{
     985    switch (pattern->type) {
     986        case CAIRO_PATTERN_TYPE_SOLID:
     987            return _cairo_os2_printing_surface_paint_solid_pattern (surface, pattern);
     988
     989        case CAIRO_PATTERN_TYPE_SURFACE: {
     990            cairo_surface_pattern_t * surf_pattern = (cairo_surface_pattern_t *) pattern;
     991
     992            if (_cairo_surface_is_recording (surf_pattern->surface))
     993                return _cairo_os2_printing_surface_paint_recording_pattern (surface,
     994                                                                            surf_pattern);
     995
     996            return _cairo_os2_printing_surface_paint_image_pattern (surface,
     997                                                                    surf_pattern);
     998        }
     999
     1000        case CAIRO_PATTERN_TYPE_LINEAR:
     1001        case CAIRO_PATTERN_TYPE_RADIAL:
     1002        case CAIRO_PATTERN_TYPE_MESH:
     1003            break;
     1004    }
     1005
     1006    return CAIRO_INT_STATUS_UNSUPPORTED;
     1007}
     1008
     1009
     1010static cairo_int_status_t
     1011_cairo_os2_printing_surface_paint (void                  *abstract_surface,
     1012                                   cairo_operator_t       op,
     1013                                   const cairo_pattern_t *source,
     1014                                   const cairo_clip_t    *clip)
     1015{
     1016    cairo_os2_surface_t *surface = abstract_surface;
     1017    cairo_solid_pattern_t clear;
     1018    cairo_status_t status;
     1019
     1020    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
     1021    if (status)
     1022        return status;
     1023
     1024    if (op == CAIRO_OPERATOR_CLEAR) {
     1025        _cairo_os2_printing_surface_init_clear_color (surface, &clear);
     1026        source = (cairo_pattern_t*) &clear;
     1027        op = CAIRO_OPERATOR_SOURCE;
     1028    }
     1029
     1030    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
     1031        return _cairo_os2_printing_surface_analyze_operation (surface, op, source);
     1032
     1033    assert (_cairo_os2_printing_surface_operation_supported (surface, op, source));
     1034
     1035    return _cairo_os2_printing_surface_paint_pattern (surface, source);
     1036}
     1037
     1038
     1039/*
     1040 * stroke
     1041 */
     1042
     1043static cairo_int_status_t
     1044_cairo_os2_printing_surface_stroke (void                        *abstract_surface,
     1045                                    cairo_operator_t             op,
     1046                                    const cairo_pattern_t       *source,
     1047                                    const cairo_path_fixed_t    *path,
     1048                                    const cairo_stroke_style_t  *style,
     1049                                    const cairo_matrix_t        *stroke_ctm,
     1050                                    const cairo_matrix_t        *stroke_ctm_inverse,
     1051                                    double                       tolerance,
     1052                                    cairo_antialias_t            antialias,
     1053                                    const cairo_clip_t          *clip)
     1054{
     1055    cairo_os2_surface_t *surface = abstract_surface;
     1056    cairo_int_status_t status;
     1057    MATRIXLF mlf;
     1058    cairo_solid_pattern_t clear;
     1059    cairo_matrix_t mat;
     1060    double scale;
     1061    LINEBUNDLE lb;
     1062
     1063    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
     1064    if (status)
     1065        return status;
     1066
     1067    if (op == CAIRO_OPERATOR_CLEAR) {
     1068        _cairo_os2_printing_surface_init_clear_color (surface, &clear);
     1069        source = (cairo_pattern_t*) &clear;
     1070        op = CAIRO_OPERATOR_SOURCE;
     1071    }
     1072
     1073    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
     1074        /* OS/2 doesn't support user-styled dashed lines, and trying
     1075         * to map cairo's user styling to its few built-in line types
     1076         * produces unsatisfactory results, so use fallback images.
     1077         */
     1078        if (style->num_dashes)
     1079            return CAIRO_INT_STATUS_UNSUPPORTED;
     1080
     1081        return _cairo_os2_printing_surface_analyze_operation (surface, op, source);
     1082    }
     1083
     1084    assert (_cairo_os2_printing_surface_operation_supported (surface, op, source));
     1085    assert (style->num_dashes == 0);
     1086
     1087    cairo_matrix_multiply (&mat, stroke_ctm, &surface->ctm);
     1088    _cairo_matrix_factor_out_scale (&mat, &scale);
     1089
     1090    /* For this function, the only area attribute that needs to be
     1091     * changed from the default is foreground color, and then only
     1092     * if we're using GpiStrokePath()
     1093     */
     1094    if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
     1095        status = _cairo_os2_printing_surface_set_area_color (surface, source);
     1096        if (status)
     1097            return status;
     1098    }
     1099
     1100    lb.lGeomWidth = (LONG) (scale * style->line_width + 0.5);
     1101    lb.usType = LINETYPE_SOLID;
     1102    lb.usEnd = _cairo_os2_printing_surface_line_cap (style->line_cap);
     1103    lb.usJoin = _cairo_os2_printing_surface_line_join (style->line_join);
     1104
     1105    if (!GpiSetAttrs (surface->hps,
     1106                      PRIM_LINE,
     1107                      LBB_GEOM_WIDTH | LBB_TYPE | LBB_END | LBB_JOIN,
     1108                      0,
     1109                      &lb))
     1110        return _gpi_error ("stroke - GpiSetAttrs 2");
     1111
     1112    if (!GpiBeginPath (surface->hps, 1))
     1113        return _gpi_error ("stroke - GpiBeginPath");
     1114    status = _cairo_os2_printing_surface_emit_path (surface, path);
     1115    if (!GpiEndPath (surface->hps))
     1116        return _gpi_error ("stroke - GpiEndPath");
     1117    if (status)
     1118        return status;
     1119
     1120    /*
     1121     * Switch to user space to set line parameters
     1122     */
     1123    if (GpiSavePS (surface->hps) == GPI_ERROR)
     1124        return _gpi_error ("stroke - GpiSavePS");
     1125
     1126    _cairo_matrix_to_os2_matrixlf (&mat, &mlf);
     1127    mlf.lM31 = 0;
     1128    mlf.lM32 = 0;
     1129
     1130    if (!GpiSetModelTransformMatrix (surface->hps, 9, &mlf, TRANSFORM_PREEMPT))
     1131        return _gpi_error ("stroke - GpiSetModelTransformMatrix (preempt)");
     1132
     1133    if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
     1134        if (GpiStrokePath (surface->hps, 1, 0) == GPI_ERROR)
     1135            return _gpi_error ("stroke - GpiStrokePath");
     1136    } else {
     1137        if (!GpiModifyPath (surface->hps, 1, MPATH_STROKE))
     1138            return _gpi_error ("stroke - GpiModifyPath");
     1139        if (!GpiSetClipPath (surface->hps, 1, SCP_WINDING | SCP_AND | SCP_INCL))
     1140            return _gpi_error ("stroke - GpiSetClipPath");
     1141
     1142        /* Return to device space to paint the pattern */
     1143        _cairo_matrix_to_os2_matrixlf (&surface->gpi_ctm, &mlf);
     1144        if (!GpiSetModelTransformMatrix (surface->hps, 9, &mlf, TRANSFORM_REPLACE))
     1145            return _gpi_error ("stroke - GpiSetModelTransformMatrix (replace)");
     1146        status = _cairo_os2_printing_surface_paint_pattern (surface, source);
     1147    }
     1148    if (!GpiRestorePS (surface->hps, -1))
     1149        return _gpi_error ("stroke - GpiRestorePS");
     1150
     1151    return status;
     1152}
     1153
     1154
     1155/*
     1156 * fill
     1157 */
     1158
     1159static cairo_int_status_t
     1160_cairo_os2_printing_surface_fill (void                     *abstract_surface,
     1161                                  cairo_operator_t          op,
     1162                                  const cairo_pattern_t    *source,
     1163                                  const cairo_path_fixed_t *path,
     1164                                  cairo_fill_rule_t         fill_rule,
     1165                                  double                    tolerance,
     1166                                  cairo_antialias_t         antialias,
     1167                                  const cairo_clip_t       *clip)
     1168{
     1169    cairo_os2_surface_t *surface = abstract_surface;
     1170    cairo_int_status_t status;
     1171    cairo_solid_pattern_t clear;
     1172    LONG fill_mode;
     1173
     1174    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
     1175    if (status)
     1176        return status;
     1177
     1178    if (op == CAIRO_OPERATOR_CLEAR) {
     1179        _cairo_os2_printing_surface_init_clear_color (surface, &clear);
     1180        source = (cairo_pattern_t*) &clear;
     1181        op = CAIRO_OPERATOR_SOURCE;
     1182    }
     1183
     1184    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
     1185        return _cairo_os2_printing_surface_analyze_operation (surface, op, source);
     1186
     1187    assert (_cairo_os2_printing_surface_operation_supported (surface, op, source));
     1188
     1189    surface->path_empty = TRUE;
     1190
     1191    if (!GpiBeginPath (surface->hps, 1))
     1192        return _gpi_error ("fill - GpiBeginPath");
     1193    status = _cairo_os2_printing_surface_emit_path (surface, path);
     1194    if (!GpiEndPath (surface->hps))
     1195        return _gpi_error ("fill - GpiEndPath");
     1196
     1197    /* note:  FPATH_* == SCP_* */
     1198    switch (fill_rule) {
     1199    case CAIRO_FILL_RULE_WINDING:
     1200        fill_mode = FPATH_WINDING;
     1201        break;
     1202    case CAIRO_FILL_RULE_EVEN_ODD:
     1203        fill_mode = FPATH_ALTERNATE;
     1204        break;
     1205    default:
     1206        fill_mode = FPATH_WINDING;
     1207        ASSERT_NOT_REACHED;
     1208    }
     1209
     1210    if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
     1211        status = _cairo_os2_printing_surface_set_area_color (surface, source);
     1212        if (status)
     1213            return status;
     1214        if (GpiFillPath (surface->hps, 1, fill_mode) == GPI_ERROR)
     1215            return _gpi_error ("fill - GpiFillPath");
     1216    } else if (surface->path_empty == FALSE) {
     1217        if (GpiSavePS (surface->hps) == GPI_ERROR)
     1218            return _gpi_error ("fill - GpiSavePS");
     1219        if (!GpiSetClipPath (surface->hps, 1, fill_mode | SCP_AND | SCP_INCL))
     1220            return _gpi_error ("fill - GpiSetClipPath");
     1221        status = _cairo_os2_printing_surface_paint_pattern (surface, source);
     1222        if (!GpiRestorePS (surface->hps, -1))
     1223            return _gpi_error ("fill - GpiRestorePS");
     1224    }
     1225
     1226    return status;
     1227}
     1228
     1229
     1230/*
     1231 * glyphs
     1232 */
     1233
     1234static cairo_int_status_t
     1235_cairo_os2_printing_surface_show_glyphs (void                   *abstract_surface,
     1236                                         cairo_operator_t        op,
     1237                                         const cairo_pattern_t  *source,
     1238                                         cairo_glyph_t          *glyphs,
     1239                                         int                     num_glyphs,
     1240                                         cairo_scaled_font_t    *scaled_font,
     1241                                         const cairo_clip_t     *clip,
     1242                                         int                    *remaining_glyphs)
     1243{
     1244    cairo_os2_surface_t *surface = abstract_surface;
     1245    cairo_status_t status = CAIRO_STATUS_SUCCESS;
     1246    cairo_scaled_glyph_t *scaled_glyph;
     1247    int i;
     1248    cairo_matrix_t old_ctm;
     1249    cairo_bool_t old_has_ctm;
     1250    cairo_solid_pattern_t clear;
     1251
     1252    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
     1253    if (status)
     1254        return status;
     1255
     1256    if (op == CAIRO_OPERATOR_CLEAR) {
     1257        _cairo_os2_printing_surface_init_clear_color (surface, &clear);
     1258        source = (cairo_pattern_t*) &clear;
     1259        op = CAIRO_OPERATOR_SOURCE;
     1260    }
     1261
     1262    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
     1263
     1264        /* Check that each glyph has a path available. If a path is
     1265         * not available, _cairo_scaled_glyph_lookup() will return
     1266         * CAIRO_INT_STATUS_UNSUPPORTED and a fallback image will be
     1267         * used.
     1268         */
     1269        for (i = 0; i < num_glyphs; i++) {
     1270            status = _cairo_scaled_glyph_lookup (scaled_font,
     1271                                                 glyphs[i].index,
     1272                                                 CAIRO_SCALED_GLYPH_INFO_PATH,
     1273                                                 &scaled_glyph);
     1274            if (status)
     1275                return status;
     1276        }
     1277
     1278        return _cairo_os2_printing_surface_analyze_operation (surface, op, source);
     1279    }
     1280
     1281    if (GpiSavePS (surface->hps) == GPI_ERROR)
     1282        return _gpi_error ("show_glyphs - GpiSavePS");
     1283    old_ctm = surface->ctm;
     1284    old_has_ctm = surface->has_ctm;
     1285    surface->has_ctm = TRUE;
     1286    surface->path_empty = TRUE;
     1287    if (!GpiBeginPath (surface->hps, 1))
     1288        return _gpi_error ("show_glyphs - GpiBeginPath");
     1289    for (i = 0; i < num_glyphs; i++) {
     1290        status = _cairo_scaled_glyph_lookup (scaled_font,
     1291                                             glyphs[i].index,
     1292                                             CAIRO_SCALED_GLYPH_INFO_PATH,
     1293                                             &scaled_glyph);
     1294        if (status)
     1295            break;
     1296        surface->ctm = old_ctm;
     1297        cairo_matrix_translate (&surface->ctm, glyphs[i].x, glyphs[i].y);
     1298        status = _cairo_os2_printing_surface_emit_path (surface, scaled_glyph->path);
     1299    }
     1300    if (!GpiEndPath (surface->hps))
     1301        return _gpi_error ("show_glyphs - GpiEndPath");
     1302    surface->ctm = old_ctm;
     1303    surface->has_ctm = old_has_ctm;
     1304    if (status == CAIRO_STATUS_SUCCESS && surface->path_empty == FALSE) {
     1305        if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
     1306            status = _cairo_os2_printing_surface_set_area_color (surface, source);
     1307            if (status)
     1308                return status;
     1309            if (GpiFillPath (surface->hps, 1, FPATH_WINDING) == GPI_ERROR)
     1310                return _gpi_error ("show_glyphs - GpiFillPath");
     1311
     1312        } else {
     1313            if (!GpiSetClipPath (surface->hps, 1, SCP_WINDING | SCP_AND | SCP_INCL))
     1314                return _gpi_error ("show_glyphs - GpiSetClipPath");
     1315            status = _cairo_os2_printing_surface_paint_pattern (surface, source);
     1316        }
     1317    }
     1318    if (!GpiRestorePS (surface->hps, -1))
     1319        return _gpi_error ("show_glyphs - GpiRestorePS");
     1320
     1321    return status;
     1322}
     1323
     1324
     1325/*
     1326 * additional backend functions
     1327 */
     1328
     1329static cairo_surface_t *
     1330_cairo_os2_printing_surface_create_similar (void            *abstract_surface,
     1331                                            cairo_content_t  content,
     1332                                            int              width,
     1333                                            int              height)
     1334{
     1335    cairo_rectangle_t extents;
     1336
     1337    extents.x = extents.y = 0;
     1338    extents.width  = width;
     1339    extents.height = height;
     1340    return cairo_recording_surface_create (content, &extents);
     1341}
     1342
     1343
     1344static cairo_status_t
     1345_cairo_os2_printing_surface_finish (void *abstract_surface)
     1346{
     1347    return CAIRO_STATUS_SUCCESS;
     1348}
     1349
     1350
     1351static cairo_int_status_t
     1352_cairo_os2_printing_surface_show_page (void *abstract_surface)
     1353{
     1354    cairo_os2_surface_t *surface = abstract_surface;
     1355
     1356    /* Undo both GpiSavePS's that we did in start_page */
     1357    if (!GpiRestorePS (surface->hps, -2))
     1358        return _gpi_error ("show_page - GpiRestorePS");
     1359
     1360    return CAIRO_STATUS_SUCCESS;
     1361}
     1362
     1363
     1364static cairo_bool_t
     1365_cairo_os2_printing_surface_get_extents (void                  *abstract_surface,
     1366                                         cairo_rectangle_int_t *rectangle)
     1367{
     1368    cairo_os2_surface_t *surface = abstract_surface;
     1369
     1370    rectangle->x = 0;
     1371    rectangle->y = 0;
     1372    rectangle->width  = surface->width;
     1373    rectangle->height = surface->height;
     1374
     1375    return TRUE;
     1376}
     1377
     1378
     1379static void
     1380_cairo_os2_printing_surface_get_font_options (void                 *abstract_surface,
     1381                                              cairo_font_options_t *options)
     1382{
     1383    _cairo_font_options_init_default (options);
     1384
     1385    cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
     1386    cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
     1387    cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
     1388    _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON);
     1389}
     1390
     1391
     1392/*
     1393 * printing surface backend
     1394 */
     1395
     1396static const cairo_surface_backend_t cairo_os2_printing_surface_backend = {
     1397    CAIRO_SURFACE_TYPE_OS2_PRINTING,
     1398    _cairo_os2_printing_surface_finish,
     1399    _cairo_default_context_create,
     1400    _cairo_os2_printing_surface_create_similar,
     1401    NULL, /* create_similar_image */
     1402    NULL, /* map_to_image */
     1403    NULL, /* unmap_image */
     1404    NULL, /* acquire_source_image */
     1405    NULL, /* release_source_image */
     1406    NULL, /* acquire_dest_image */
     1407    NULL, /* release_dest_image */
     1408    NULL, /* clone_similar */
     1409    NULL, /* composite */
     1410    NULL, /* fill_rectangles */
     1411    NULL, /* composite_trapezoids */
     1412    NULL, /* create_span_renderer */
     1413    NULL, /* check_span_renderer */
     1414    NULL, /* copy_page */
     1415    _cairo_os2_printing_surface_show_page,
     1416    _cairo_os2_printing_surface_get_extents,
     1417    NULL, /* old_show_glyphs */
     1418    _cairo_os2_printing_surface_get_font_options,
     1419    NULL, /* flush */
     1420    NULL, /* mark_dirty_rectangle */
     1421    NULL, /* scaled_font_fini */
     1422    NULL, /* scaled_glyph_fini */
     1423    _cairo_os2_printing_surface_paint,
     1424    NULL, /* mask */
     1425    _cairo_os2_printing_surface_stroke,
     1426    _cairo_os2_printing_surface_fill,
     1427    _cairo_os2_printing_surface_show_glyphs,
     1428    NULL, /* snapshot */
     1429    NULL, /* is_similar */
     1430    NULL, /* fill_stroke */
     1431    NULL, /* create_solid_pattern_surface */
     1432    NULL, /* can_repaint_solid_pattern_surface */
     1433    NULL, /* has_show_text_glyphs */
     1434    NULL  /* show_text_glyphs */
     1435};
     1436
     1437
     1438/*
     1439 * paginated surface backend functions
     1440 */
     1441
     1442static cairo_int_status_t
     1443_cairo_os2_paginated_surface_start_page (void *abstract_surface)
     1444{
     1445    cairo_os2_surface_t *surface = abstract_surface;
     1446    MATRIXLF mlf;
     1447    HDC hdc;
     1448    LONG xy_res[2];
     1449    double x_res, y_res;
     1450    cairo_matrix_t inverse_ctm;
     1451    cairo_status_t status;
     1452
     1453    /* Rather than trying to translate every coordinate for every
     1454     * call from cairo to PM coordinate space, the entire image is
     1455     * generated upside-down using cairo coordinates.  This viewing
     1456     * transform then flips and repositions the entire image to map
     1457     * it into PM coordinate space.  Note:  GpiEnableYInversion()
     1458     * should accomplish the same result - but it doesn't.
     1459     */
     1460    memset (&mlf, 0, sizeof (mlf));
     1461    mlf.fxM11 = (1 << 16);
     1462    mlf.fxM22 = (-1 << 16);
     1463    mlf.lM32  = surface->height - 1;
     1464    mlf.lM33  = 1;
     1465    if (!GpiSetDefaultViewMatrix (surface->hps, 9, &mlf, TRANSFORM_REPLACE))
     1466        return _gpi_error ("start_page - GpiSetDefaultViewMatrix");
     1467
     1468    /* Set full-color (32-bit) mode */
     1469    if (!GpiCreateLogColorTable (surface->hps, 0, LCOLF_RGB, 0, 0, 0))
     1470        return _gpi_error ("start_page - GpiCreateLogColorTable");
     1471
     1472    /* Save application context first */
     1473    if (GpiSavePS (surface->hps) == GPI_ERROR)
     1474        return _gpi_error ("start_page - GpiSavePS 1");
     1475
     1476    /* As the logical coordinates used by GDI functions (eg LineTo)
     1477     * are integers we need to do some additional work to prevent
     1478     * rounding errors. For example the obvious way to paint a recording
     1479     * pattern is to:
     1480     *
     1481     *   SaveDC()
     1482     *   transform the device context DC by the pattern to device matrix
     1483     *   replay the recording surface
     1484     *   RestoreDC()
     1485     *
     1486     * The problem here is that if the pattern to device matrix is
     1487     * [100 0 0 100 0 0], coordinates in the recording pattern such as
     1488     * (1.56, 2.23) which correspond to (156, 223) in device space
     1489     * will be rounded to (100, 200) due to (1.56, 2.23) being
     1490     * truncated to integers.
     1491     *
     1492     * This is solved by saving the current GDI CTM in surface->ctm,
     1493     * switch the GDI CTM to identity, and transforming all
     1494     * coordinates by surface->ctm before passing them to GDI. When
     1495     * painting a recording pattern, surface->ctm is transformed by the
     1496     * pattern to device matrix.
     1497     *
     1498     * For printing device contexts where 1 unit is 1 dpi, switching
     1499     * the GDI CTM to identity maximises the possible resolution of
     1500     * coordinates.
     1501     *
     1502     * If the device context is an EMF file, using an identity
     1503     * transform often provides insufficent resolution. The workaround
     1504     * is to set the GDI CTM to a scale < 1 eg [1.0/16 0 0 1/0/16 0 0]
     1505     * and scale the cairo CTM by [16 0 0 16 0 0]. The
     1506     * SetWorldTransform function call to scale the GDI CTM by 1.0/16
     1507     * will be recorded in the EMF followed by all the graphics
     1508     * functions by their coordinateds multiplied by 16.
     1509     *
     1510     * To support allowing the user to set a GDI CTM with scale < 1,
     1511     * we avoid switching to an identity CTM if the CTM xx and yy is < 1.
     1512     */
     1513
     1514    if (!GpiQueryModelTransformMatrix (surface->hps, 9, &mlf))
     1515        return _gpi_error ("start_page - GpiQueryModelTransformMatrix");
     1516
     1517    if (mlf.fxM11 < (1 << 16) && mlf.fxM22 < (1 << 16)) {
     1518        cairo_matrix_init_identity (&surface->ctm);
     1519        _cairo_os2_matrixlf_to_matrix (&mlf, &surface->gpi_ctm);
     1520    } else {
     1521        _cairo_os2_matrixlf_to_matrix (&mlf, &surface->ctm);
     1522        cairo_matrix_init_identity (&surface->gpi_ctm);
     1523
     1524        memset (&mlf, 0, sizeof (mlf));
     1525        mlf.fxM11 = (1 << 16);
     1526        mlf.fxM22 = (1 << 16);
     1527        mlf.lM33  = 1;
     1528        if (!GpiSetModelTransformMatrix (surface->hps, 9, &mlf, TRANSFORM_REPLACE))
     1529            return _gpi_error ("start_page - GpiSetModelTransformMatrix");
     1530    }
     1531
     1532    surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm);
     1533    surface->has_gpi_ctm = !_cairo_matrix_is_identity (&surface->gpi_ctm);
     1534    inverse_ctm = surface->ctm;
     1535    status = cairo_matrix_invert (&inverse_ctm);
     1536    if (status)
     1537        return status;
     1538
     1539    hdc = GpiQueryDevice (surface->hps);
     1540    if (!hdc)
     1541        return _gpi_error ("start_page - GpiQueryDevice");
     1542
     1543    /* For printers, CAPS_*_FONT_RES == actual printer resolution in DPI */
     1544    if (!DevQueryCaps (hdc, CAPS_HORIZONTAL_FONT_RES, 2, (PLONG)&xy_res))
     1545        return _gpi_error ("start_page - DevQueryCaps");
     1546    x_res = xy_res[0];
     1547    y_res = xy_res[1];
     1548    cairo_matrix_transform_distance (&inverse_ctm, &x_res, &y_res);
     1549
     1550    _cairo_surface_set_resolution (&surface->base, x_res, y_res);
     1551    _cairo_surface_set_resolution (surface->paginated_surf, x_res, y_res);
     1552
     1553    /* ensure fallback images are at the same res as the primary surfaces */
     1554    cairo_surface_set_fallback_resolution (&surface->base, x_res, y_res);
     1555    cairo_surface_set_fallback_resolution (surface->paginated_surf, x_res, y_res);
     1556
     1557    /* Save Cairo's known-good clip state, so the clip path can be reset */
     1558    if (GpiSavePS (surface->hps) == GPI_ERROR)
     1559        return _gpi_error ("start_page - GpiSavePS 2");
     1560
     1561    return CAIRO_STATUS_SUCCESS;
     1562}
     1563
     1564
     1565static void
     1566_cairo_os2_paginated_surface_set_paginated_mode (void                   *abstract_surface,
     1567                                                cairo_paginated_mode_t  paginated_mode)
     1568{
     1569    cairo_os2_surface_t *surface = abstract_surface;
     1570
     1571    surface->paginated_mode = paginated_mode;
     1572}
     1573
     1574
     1575static cairo_bool_t
     1576_cairo_os2_paginated_surface_supports_fine_grained_fallbacks (void *abstract_surface)
     1577{
     1578    return TRUE;
     1579}
     1580
     1581
     1582/*
     1583 * paginated surface backend
     1584 */
     1585
     1586static const cairo_paginated_surface_backend_t cairo_os2_paginated_surface_backend = {
     1587    _cairo_os2_paginated_surface_start_page,
     1588    _cairo_os2_paginated_surface_set_paginated_mode,
     1589    NULL, /* set_bounding_box */
     1590    NULL, /* set_fallback_images_required */
     1591    _cairo_os2_paginated_surface_supports_fine_grained_fallbacks
     1592};
     1593
  • src/cairo-os2-private.h

    diff --git a/src/cairo-os2-private.h b/src/cairo-os2-private.h
    index bc02db9..dffce6c 100644
    a b  
    4242
    4343#include "cairoint.h"
    4444#include "cairo-os2.h"
     45#include "cairo-surface-clipper-private.h"
    4546
    4647/**
    4748 * Unpublished API:
    typedef ULONG (APIENTRY *DiveDeacquire_t)(ULONG); 
    8788
    8889typedef enum _cairo_os2_subtype {
    8990    CAIRO_OS2_SUBTYPE_NULL = 0,
    90     CAIRO_OS2_SUBTYPE_IMAGE
     91    CAIRO_OS2_SUBTYPE_IMAGE,
     92    CAIRO_OS2_SUBTYPE_PRINT
    9193} cairo_os2_subtype_t;
    9294
    9395/**
    typedef struct _cairo_os2_surface { 
    140142    unsigned char          *data;
    141143    cairo_surface_t        *image;
    142144
     145    /* printing surface data */
     146    cairo_bool_t            path_empty;
     147    cairo_bool_t            has_ctm;
     148    cairo_bool_t            has_gpi_ctm;
     149    cairo_matrix_t          ctm;
     150    cairo_matrix_t          gpi_ctm;
     151    cairo_surface_clipper_t clipper;
     152    cairo_paginated_mode_t  paginated_mode;
     153    cairo_surface_t *       paginated_surf;
    143154} cairo_os2_surface_t;
    144155
    145156#endif /* CAIRO_OS2_PRIVATE_H */
  • src/cairo-os2-surface.c

    diff --git a/src/cairo-os2-surface.c b/src/cairo-os2-surface.c
    index 109eb44..9f5488c 100644
    a b cairo_os2_surface_get_hps (cairo_surface_t *surface, 
    506506    if (unlikely (!surface || !hps))
    507507        return _cairo_error (CAIRO_STATUS_NULL_POINTER);
    508508
    509     if (cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_OS2)
     509    if (cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_OS2 &&
     510        cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_OS2_PRINTING)
    510511        return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
    511512
    512513    *hps = os2surf->hps;
    cairo_os2_surface_set_hps (cairo_surface_t *surface, 
    539540    if (unlikely (!surface))
    540541        return _cairo_error (CAIRO_STATUS_NULL_POINTER);
    541542
    542     if (cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_OS2)
     543    if (cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_OS2 &&
     544        cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_OS2_PRINTING)
    543545        return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
    544546
    545547    os2surf->hps = hps;
  • src/cairo-os2.h

    diff --git a/src/cairo-os2.h b/src/cairo-os2.h
    index 4b84a60..6d80a99 100644
    a b cairo_public cairo_bool_t 
    104104cairo_os2_surface_enable_dive (cairo_bool_t enable,
    105105                               cairo_bool_t hide_pointer);
    106106
     107cairo_surface_t *
     108cairo_os2_printing_surface_create (HPS  hps,
     109                                   int  width,
     110                                   int  height);
     111
    107112#else  /* CAIRO_HAS_OS2_SURFACE */
    108113# error Cairo was not compiled with support for the OS/2 backend
    109114#endif /* CAIRO_HAS_OS2_SURFACE */
  • src/cairo.h

    diff --git a/src/cairo.h b/src/cairo.h
    index 53065a1..7490ee4 100644
    a b cairo_surface_status (cairo_surface_t *surface); 
    21772177 * @CAIRO_SURFACE_TYPE_SUBSURFACE: The surface is a subsurface created with
    21782178 *   cairo_surface_create_for_rectangle(), since 1.10
    21792179 * @CAIRO_SURFACE_TYPE_MIME: The surface is a callback (mime) surface, since 1.12
     2180 * @CAIRO_SURFACE_TYPE_OS2_PRINTING: The surface is an os2 printing surface, since 1.12
    21802181 *
    21812182 * #cairo_surface_type_t is used to describe the type of a given
    21822183 * surface. The surface types are also known as "backends" or "surface
    typedef enum _cairo_surface_type { 
    22272228    CAIRO_SURFACE_TYPE_XML,
    22282229    CAIRO_SURFACE_TYPE_SKIA,
    22292230    CAIRO_SURFACE_TYPE_SUBSURFACE,
    2230     CAIRO_SURFACE_TYPE_MIME
     2231    CAIRO_SURFACE_TYPE_MIME,
     2232    CAIRO_SURFACE_TYPE_OS2_PRINTING
    22312233} cairo_surface_type_t;
    22322234
    22332235cairo_public cairo_surface_type_t