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 © 2004 Red Hat, Inc
5
 * Copyright © 2006 Red Hat, Inc
6
 * Copyright © 2007, 2008 Adrian Johnson
7
 *
8
 * This library is free software; you can redistribute it and/or
9
 * modify it either under the terms of the GNU Lesser General Public
10
 * License version 2.1 as published by the Free Software Foundation
11
 * (the "LGPL") or, at your option, under the terms of the Mozilla
12
 * Public License Version 1.1 (the "MPL"). If you do not alter this
13
 * notice, a recipient may use your version of this file under either
14
 * the MPL or the LGPL.
15
 *
16
 * You should have received a copy of the LGPL along with this library
17
 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
19
 * You should have received a copy of the MPL along with this library
20
 * in the file COPYING-MPL-1.1
21
 *
22
 * The contents of this file are subject to the Mozilla Public License
23
 * Version 1.1 (the "License"); you may not use this file except in
24
 * compliance with the License. You may obtain a copy of the License at
25
 * http://www.mozilla.org/MPL/
26
 *
27
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
28
 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
29
 * the specific language governing rights and limitations.
30
 *
31
 * The Original Code is the cairo graphics library.
32
 *
33
 * The Initial Developer of the Original Code is University of Southern
34
 * California.
35
 *
36
 * Contributor(s):
37
 *	Kristian Høgsberg <krh@redhat.com>
38
 *	Carl Worth <cworth@cworth.org>
39
 *	Adrian Johnson <ajohnson@redneon.com>
40
 */
41

            
42
#define _DEFAULT_SOURCE /* for snprintf() */
43
#include "cairoint.h"
44

            
45
#include "cairo-pdf.h"
46
#include "cairo-pdf-surface-private.h"
47
#include "cairo-pdf-operators-private.h"
48
#include "cairo-pdf-shading-private.h"
49

            
50
#include "cairo-array-private.h"
51
#include "cairo-analysis-surface-private.h"
52
#include "cairo-composite-rectangles-private.h"
53
#include "cairo-default-context-private.h"
54
#include "cairo-error-private.h"
55
#include "cairo-user-font-private.h"
56
#include "cairo-image-surface-inline.h"
57
#include "cairo-image-info-private.h"
58
#include "cairo-recording-surface-inline.h"
59
#include "cairo-recording-surface-private.h"
60
#include "cairo-output-stream-private.h"
61
#include "cairo-paginated-private.h"
62
#include "cairo-scaled-font-subsets-private.h"
63
#include "cairo-surface-clipper-private.h"
64
#include "cairo-surface-snapshot-inline.h"
65
#include "cairo-surface-subsurface-private.h"
66
#include "cairo-type3-glyph-surface-private.h"
67

            
68
#include <zlib.h>
69

            
70
/*
71
 * Page Structure of the Generated PDF:
72
 *
73
 * Each page requiring fallbacks images contains a knockout group at
74
 * the top level. The first operation of the knockout group paints a
75
 * group containing all the supported drawing operations. Fallback
76
 * images (if any) are painted in the knockout group. This ensures
77
 * that fallback images do not composite with any content under the
78
 * fallback images.
79
 *
80
 * Streams:
81
 *
82
 * This PDF surface has three types of streams:
83
 *  - PDF Stream
84
 *  - Content Stream
85
 *  - Group Stream
86
 *  - Object stream
87
 *
88
 * Calling _cairo_output_stream_printf (surface->output, ...) will
89
 * write to the currently open stream.
90
 *
91
 * PDF Stream:
92
 *   A PDF Stream may be opened and closed with the following functions:
93
 *     _cairo_pdf_surface_open stream ()
94
 *     _cairo_pdf_surface_close_stream ()
95
 *
96
 *   PDF Streams are written directly to the PDF file. They are used for
97
 *   fonts, images and patterns.
98
 *
99
 * Content Stream:
100
 *   The Content Stream is opened and closed with the following functions:
101
 *     _cairo_pdf_surface_open_content_stream ()
102
 *     _cairo_pdf_surface_close_content_stream ()
103
 *
104
 *   The Content Stream contains the text and graphics operators.
105
 *
106
 * Group Stream:
107
 *   A Group Stream may be opened and closed with the following functions:
108
 *     _cairo_pdf_surface_open_group ()
109
 *     _cairo_pdf_surface_close_group ()
110
 *
111
 *   A Group Stream is a Form XObject. It is used for short sequences
112
 *   of operators. As the content is very short the group is stored in
113
 *   memory until it is closed. This allows some optimization such as
114
 *   including the Resource dictionary and stream length inside the
115
 *   XObject instead of using an indirect object.
116
 *
117
 * Object Stream (PDF 1.5)
118
 *   An Object Stream may be opened and closed with the following functions:
119
 *     _cairo_pdf_surface_open_object_stream ()
120
 *     _cairo_pdf_surface_close_object_stream ()
121
 *
122
 *  An Object Stream contains one or more objects compressed into a stream.
123
 *  Only non stream objects are permitted. When emitting objects intended for
124
 *  the Object Stream, enclose the emit object operation with
125
 *  _cairo_pdf_surface_object_begin()/_cairo_pdf_surface_object_end().
126
 */
127

            
128
/**
129
 * SECTION:cairo-pdf
130
 * @Title: PDF Surfaces
131
 * @Short_Description: Rendering PDF documents
132
 * @See_Also: #cairo_surface_t
133
 *
134
 * The PDF surface is used to render cairo graphics to Adobe
135
 * PDF files and is a multi-page vector surface backend.
136
 *
137
 * The following mime types are supported on source patterns:
138
 * %CAIRO_MIME_TYPE_JPEG, %CAIRO_MIME_TYPE_JP2,
139
 * %CAIRO_MIME_TYPE_UNIQUE_ID, %CAIRO_MIME_TYPE_JBIG2,
140
 * %CAIRO_MIME_TYPE_JBIG2_GLOBAL, %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID,
141
 * %CAIRO_MIME_TYPE_CCITT_FAX, %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS.
142
 *
143
 * # JBIG2 Images #
144
 * JBIG2 data in PDF must be in the embedded format as described in
145
 * ISO/IEC 11544. Image specific JBIG2 data must be in
146
 * %CAIRO_MIME_TYPE_JBIG2.  Any global segments in the JBIG2 data
147
 * (segments with page association field set to 0) must be in
148
 * %CAIRO_MIME_TYPE_JBIG2_GLOBAL. The global data may be shared by
149
 * multiple images. All images sharing the same global data must set
150
 * %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID to a unique identifier. At least
151
 * one of the images must provide the global data using
152
 * %CAIRO_MIME_TYPE_JBIG2_GLOBAL. The global data will only be
153
 * embedded once and shared by all JBIG2 images with the same
154
 * %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID.
155
 *
156
 * # CCITT Fax Images # {#ccitt}
157
 * The %CAIRO_MIME_TYPE_CCITT_FAX mime data requires a number of decoding
158
 * parameters These parameters are specified using %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS.
159
 *
160
 * %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS mime data must contain a string of the form
161
 * "param1=value1 param2=value2 ...".
162
 *
163
 * @Columns: [required] An integer specifying the width of the image in pixels.
164
 *
165
 * @Rows: [required] An integer specifying the height of the image in scan lines.
166
 *
167
 * @K: [optional] An integer identifying the encoding scheme used. < 0
168
 * is 2 dimensional Group 4, = 0 is Group3 1 dimensional, > 0 is mixed 1
169
 * and 2 dimensional encoding. Default is 0.
170
 *
171
 * @EndOfLine: [optional] If true end-of-line bit patterns are present. Default is false.
172
 *
173
 * @EncodedByteAlign: [optional] If true the end of line is padded
174
 * with 0 bits so the next line begins on a byte boundary. Default is false.
175
 *
176
 * @EndOfBlock: [optional] If true the data contains an end-of-block pattern. Default is true.
177
 *
178
 * @BlackIs1: [optional] If true 1 bits are black pixels. Default is false.
179
 *
180
 * @DamagedRowsBeforeError: [optional] An integer specifying the
181
 * number of damages rows tolerated before an error occurs. Default is 0.
182
 *
183
 * Boolean values may be "true" or "false", or 1 or 0.
184
 *
185
 * These parameters are the same as the CCITTFaxDecode parameters in the
186
 * [PostScript Language Reference](https://www.adobe.com/products/postscript/pdfs/PLRM.pdf)
187
 * and [Portable Document Format (PDF)](https://www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf).
188
 * Refer to these documents for further details.
189
 *
190
 * An example %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS string is:
191
 *
192
 * <programlisting>
193
 * "Columns=10230 Rows=40000 K=1 EndOfLine=true EncodedByteAlign=1 BlackIs1=false"
194
 * </programlisting>
195
 *
196
 **/
197

            
198
static cairo_bool_t
199
_cairo_pdf_surface_get_extents (void		        *abstract_surface,
200
				cairo_rectangle_int_t   *rectangle);
201

            
202
/**
203
 * CAIRO_HAS_PDF_SURFACE:
204
 *
205
 * Defined if the PDF surface backend is available.
206
 * This macro can be used to conditionally compile backend-specific code.
207
 *
208
 * Since: 1.2
209
 **/
210

            
211
static const cairo_pdf_version_t _cairo_pdf_versions[] =
212
{
213
    CAIRO_PDF_VERSION_1_4,
214
    CAIRO_PDF_VERSION_1_5,
215
    CAIRO_PDF_VERSION_1_6,
216
    CAIRO_PDF_VERSION_1_7
217
};
218

            
219
#define CAIRO_PDF_VERSION_LAST ARRAY_LENGTH (_cairo_pdf_versions)
220

            
221
static const char * _cairo_pdf_version_strings[CAIRO_PDF_VERSION_LAST] =
222
{
223
    "PDF 1.4",
224
    "PDF 1.5",
225
    "PDF 1.6",
226
    "PDF 1.7"
227
};
228

            
229
static const char *_cairo_pdf_supported_mime_types[] =
230
{
231
    CAIRO_MIME_TYPE_JPEG,
232
    CAIRO_MIME_TYPE_JP2,
233
    CAIRO_MIME_TYPE_UNIQUE_ID,
234
    CAIRO_MIME_TYPE_JBIG2,
235
    CAIRO_MIME_TYPE_JBIG2_GLOBAL,
236
    CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID,
237
    CAIRO_MIME_TYPE_CCITT_FAX,
238
    CAIRO_MIME_TYPE_CCITT_FAX_PARAMS,
239
    NULL
240
};
241

            
242
/* PDF cross-reference stream types */
243
typedef enum {
244
    PDF_OBJECT_FREE = 0,
245
    PDF_OBJECT_UNCOMPRESSED = 1,
246
    PDF_OBJECT_COMPRESSED = 2,
247
} cairo_pdf_object_type_t;
248

            
249
typedef struct _cairo_pdf_object {
250
    cairo_pdf_object_type_t type;
251
    union {
252
	long long offset; /* type == PDF_OBJECT_UNCOMPRESSED */
253
	struct compressed_obj {  /* type == PDF_OBJECT_COMPRESSED */
254
	    cairo_pdf_resource_t xref_stream;
255
	    int index;
256
	} compressed_obj;
257
    } u;
258
} cairo_pdf_object_t;
259

            
260
typedef struct _cairo_xref_stream_object {
261
    cairo_pdf_resource_t resource;
262
    long long offset;
263
} cairo_xref_stream_object_t;
264

            
265
typedef struct _cairo_pdf_font {
266
    unsigned int font_id;
267
    unsigned int subset_id;
268
    cairo_pdf_resource_t subset_resource;
269
} cairo_pdf_font_t;
270

            
271
typedef struct _cairo_pdf_rgb_linear_function {
272
    cairo_pdf_resource_t resource;
273
    double               color1[3];
274
    double               color2[3];
275
} cairo_pdf_rgb_linear_function_t;
276

            
277
typedef struct _cairo_pdf_alpha_linear_function {
278
    cairo_pdf_resource_t resource;
279
    double               alpha1;
280
    double               alpha2;
281
} cairo_pdf_alpha_linear_function_t;
282

            
283
static void
284
_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface,
285
			  cairo_bool_t         clear_doc_surfaces);
286

            
287
static void
288
_cairo_pdf_smask_group_destroy (cairo_pdf_smask_group_t *group);
289

            
290
static cairo_int_status_t
291
_cairo_pdf_surface_add_font (unsigned int        font_id,
292
			     unsigned int        subset_id,
293
			     void		*closure);
294

            
295
static void
296
_cairo_pdf_group_resources_init (cairo_pdf_group_resources_t *res);
297

            
298
static cairo_int_status_t
299
_cairo_pdf_surface_open_stream (cairo_pdf_surface_t	*surface,
300
				cairo_pdf_resource_t    *resource,
301
                                cairo_bool_t             compressed,
302
				const char		*fmt,
303
				...) CAIRO_PRINTF_FORMAT(4, 5);
304
static cairo_int_status_t
305
_cairo_pdf_surface_close_stream (cairo_pdf_surface_t	*surface);
306

            
307
static cairo_int_status_t
308
_cairo_pdf_surface_emit_surface (cairo_pdf_surface_t        *surface,
309
				 cairo_pdf_source_surface_t *source,
310
				 cairo_bool_t                test,
311
				 cairo_bool_t               *is_image);
312

            
313
static cairo_int_status_t
314
_cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface);
315

            
316
static cairo_int_status_t
317
_cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface);
318

            
319
static cairo_int_status_t
320
_cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface,
321
				  cairo_pdf_resource_t catalog);
322

            
323
static long long
324
_cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface);
325

            
326
static cairo_int_status_t
327
_cairo_pdf_surface_write_xref_stream (cairo_pdf_surface_t  *surface,
328
				      cairo_pdf_resource_t  xref_res,
329
				      cairo_pdf_resource_t  root_res,
330
				      cairo_pdf_resource_t  info_res,
331
				      long long            *xref_offset);
332

            
333
static cairo_int_status_t
334
_cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface,
335
						    cairo_bool_t         finish);
336

            
337
static cairo_int_status_t
338
_cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface);
339

            
340
static cairo_bool_t
341
_cairo_pdf_source_surface_equal (const void *key_a, const void *key_b);
342

            
343
static cairo_bool_t
344
_cairo_pdf_color_glyph_equal (const void *key_a, const void *key_b);
345

            
346
static const cairo_surface_backend_t cairo_pdf_surface_backend;
347
static const cairo_paginated_surface_backend_t cairo_pdf_surface_paginated_backend;
348

            
349
cairo_pdf_resource_t
350
142
_cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface)
351
{
352
    cairo_pdf_resource_t resource;
353
    cairo_int_status_t status;
354
    cairo_pdf_object_t object;
355

            
356
    /* Default to Uncompressed. If this object is used with
357
     * _cairo_pdf_surface_object_begin() and Object Streams are
358
     * enabled it will be changed to Compressed. */
359
142
    object.type = PDF_OBJECT_UNCOMPRESSED;
360
142
    object.u.offset = _cairo_output_stream_get_position (surface->output);
361

            
362
142
    status = _cairo_array_append (&surface->objects, &object);
363
142
    if (unlikely (status)) {
364
	resource.id = 0;
365
	return resource;
366
    }
367

            
368
142
    resource = surface->next_available_resource;
369
142
    surface->next_available_resource.id++;
370

            
371
142
    return resource;
372
}
373

            
374
void
375
101
_cairo_pdf_surface_update_object (cairo_pdf_surface_t	*surface,
376
				  cairo_pdf_resource_t	 resource)
377
{
378
    cairo_pdf_object_t *object;
379

            
380
101
    object = _cairo_array_index (&surface->objects, resource.id - 1);
381
101
    object->u.offset = _cairo_output_stream_get_position (surface->output);
382
101
}
383

            
384
static void
385
_cairo_pdf_surface_set_size_internal (cairo_pdf_surface_t *surface,
386
				      double		  width,
387
				      double		  height)
388
{
389
    surface->width = width;
390
    surface->height = height;
391
    surface->surface_extents.x = 0;
392
    surface->surface_extents.y = 0;
393
    surface->surface_extents.width  = ceil (surface->width);
394
    surface->surface_extents.height = ceil (surface->height);
395
}
396

            
397
static cairo_bool_t
398
19
_path_covers_bbox (cairo_pdf_surface_t *surface,
399
		   cairo_path_fixed_t *path)
400
{
401
    cairo_box_t box;
402

            
403
19
    return _cairo_path_fixed_is_box (path, &box) &&
404
7
	   box.p1.x <= 0 &&
405
	   box.p1.y <= 0 &&
406
26
	   box.p2.x >= _cairo_fixed_from_double (surface->width) &&
407
	   box.p2.y >= _cairo_fixed_from_double (surface->height);
408
}
409

            
410
static cairo_status_t
411
38
_cairo_pdf_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
412
						cairo_path_fixed_t	*path,
413
						cairo_fill_rule_t	fill_rule,
414
						double			tolerance,
415
						cairo_antialias_t	antialias)
416
{
417
38
    cairo_pdf_surface_t *surface = cairo_container_of (clipper,
418
						       cairo_pdf_surface_t,
419
						       clipper);
420
    cairo_int_status_t status;
421

            
422
38
    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
423
38
    if (unlikely (status))
424
	return status;
425

            
426
38
    if (path == NULL) {
427
19
	_cairo_output_stream_printf (surface->output, "Q q\n");
428

            
429
19
	surface->current_pattern_is_solid_color = FALSE;
430
19
	surface->current_operator = CAIRO_OPERATOR_OVER;
431
19
	_cairo_pdf_operators_reset (&surface->pdf_operators);
432

            
433
19
	return CAIRO_STATUS_SUCCESS;
434
    }
435

            
436
19
    if (_path_covers_bbox (surface, path))
437
	return CAIRO_STATUS_SUCCESS;
438

            
439
19
    return _cairo_pdf_operators_clip (&surface->pdf_operators, path, fill_rule);
440
}
441

            
442
static cairo_surface_t *
443
5
_cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t	*output,
444
					       double			 width,
445
					       double			 height)
446
{
447
    cairo_pdf_surface_t *surface;
448
    cairo_status_t status, status_ignored;
449

            
450
5
    surface = _cairo_calloc (sizeof (cairo_pdf_surface_t));
451
5
    if (unlikely (surface == NULL)) {
452
	/* destroy stream on behalf of caller */
453
	status = _cairo_output_stream_destroy (output);
454
	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
455
    }
456

            
457
5
    _cairo_surface_init (&surface->base,
458
			 &cairo_pdf_surface_backend,
459
			 NULL, /* device */
460
			 CAIRO_CONTENT_COLOR_ALPHA,
461
			 TRUE); /* is_vector */
462

            
463
5
    surface->output = output;
464
5
    surface->width = width;
465
5
    surface->height = height;
466
5
    cairo_matrix_init (&surface->cairo_to_pdf, 1, 0, 0, 1, 0, 0);
467
5
    surface->in_xobject = FALSE;
468
5
    surface->surface_extents.x = 0;
469
5
    surface->surface_extents.y = 0;
470
5
    surface->surface_extents.width  = ceil (surface->width);
471
5
    surface->surface_extents.height = ceil (surface->height);
472
5
    surface->surface_bounded = TRUE;
473

            
474
5
    _cairo_array_init (&surface->objects, sizeof (cairo_pdf_object_t));
475
5
    _cairo_array_init (&surface->pages, sizeof (cairo_pdf_page_info_t));
476
5
    _cairo_array_init (&surface->rgb_linear_functions, sizeof (cairo_pdf_rgb_linear_function_t));
477
5
    _cairo_array_init (&surface->alpha_linear_functions, sizeof (cairo_pdf_alpha_linear_function_t));
478
5
    _cairo_array_init (&surface->fonts, sizeof (cairo_pdf_font_t));
479
5
    _cairo_array_init (&surface->smask_groups, sizeof (cairo_pdf_smask_group_t *));
480
5
    _cairo_array_init (&surface->knockout_group, sizeof (cairo_pdf_resource_t));
481

            
482
5
    _cairo_array_init (&surface->page_patterns, sizeof (cairo_pdf_pattern_t));
483
5
    _cairo_array_init (&surface->page_surfaces, sizeof (cairo_pdf_source_surface_t));
484
5
    _cairo_array_init (&surface->doc_surfaces, sizeof (cairo_pdf_source_surface_t));
485
5
    _cairo_array_init (&surface->jbig2_global, sizeof (cairo_pdf_jbig2_global_t));
486
5
    surface->all_surfaces = _cairo_hash_table_create (_cairo_pdf_source_surface_equal);
487
5
    if (unlikely (surface->all_surfaces == NULL)) {
488
	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
489
	goto BAIL0;
490
    }
491

            
492
5
    surface->color_glyphs = _cairo_hash_table_create (_cairo_pdf_color_glyph_equal);
493
5
    if (unlikely (surface->color_glyphs == NULL)) {
494
	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
495
	goto BAIL1;
496
    }
497

            
498
5
    surface->duplicate_surface_number = 0;
499

            
500
5
    _cairo_pdf_group_resources_init (&surface->resources);
501

            
502
5
    surface->font_subsets = _cairo_scaled_font_subsets_create_composite ();
503
5
    if (! surface->font_subsets) {
504
	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
505
	goto BAIL2;
506
    }
507

            
508
5
    _cairo_scaled_font_subsets_enable_latin_subset (surface->font_subsets, TRUE);
509

            
510
5
    surface->next_available_resource.id = 1;
511
5
    surface->pages_resource = _cairo_pdf_surface_new_object (surface);
512
5
    if (surface->pages_resource.id == 0) {
513
	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
514
        goto BAIL3;
515
    }
516

            
517
5
    surface->struct_tree_root.id = 0;
518
5
    surface->pdf_version = CAIRO_PDF_VERSION_1_7;
519
5
    surface->compress_streams = TRUE;
520
5
    surface->pdf_stream.active = FALSE;
521
5
    surface->pdf_stream.old_output = NULL;
522
5
    surface->group_stream.active = FALSE;
523
5
    surface->group_stream.stream = NULL;
524
5
    surface->group_stream.mem_stream = NULL;
525
5
    surface->object_stream.active = FALSE;
526
5
    surface->object_stream.stream = NULL;
527
5
    _cairo_array_init (&surface->object_stream.objects, sizeof (cairo_xref_stream_object_t));
528

            
529
5
    surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE;
530
5
    surface->type3_replay = FALSE;
531

            
532
5
    surface->force_fallbacks = FALSE;
533
5
    surface->select_pattern_gstate_saved = FALSE;
534
5
    surface->current_pattern_is_solid_color = FALSE;
535
5
    surface->current_operator = CAIRO_OPERATOR_OVER;
536
5
    surface->reset_gs_required = FALSE;
537
5
    surface->header_emitted = FALSE;
538

            
539
5
    _cairo_surface_clipper_init (&surface->clipper,
540
				 _cairo_pdf_surface_clipper_intersect_clip_path);
541

            
542
5
    _cairo_pdf_operators_init (&surface->pdf_operators,
543
			       surface->output,
544
			       &surface->cairo_to_pdf,
545
			       surface->font_subsets,
546
			       FALSE);
547
5
    _cairo_pdf_operators_set_font_subsets_callback (&surface->pdf_operators,
548
						    _cairo_pdf_surface_add_font,
549
						    surface);
550
5
    _cairo_pdf_operators_enable_actual_text(&surface->pdf_operators, TRUE);
551

            
552
5
    status = _cairo_pdf_interchange_init (surface);
553
5
    if (unlikely (status))
554
	goto BAIL3;
555

            
556
5
    surface->page_parent_tree = -1;
557
5
    _cairo_array_init (&surface->page_annots, sizeof (cairo_pdf_resource_t));
558
5
    surface->tagged = FALSE;
559
5
    surface->current_page_label = NULL;
560
5
    _cairo_array_init (&surface->page_labels, sizeof (char *));
561
5
    surface->outlines_dict_res.id = 0;
562
5
    surface->names_dict_res.id = 0;
563
5
    surface->docinfo_res.id = 0;
564
5
    surface->page_labels_res.id = 0;
565
5
    surface->thumbnail_width = 0;
566
5
    surface->thumbnail_height = 0;
567
5
    surface->thumbnail_image = NULL;
568

            
569
5
    surface->debug = FALSE;
570
5
    if (getenv ("CAIRO_DEBUG_PDF") != NULL) {
571
	surface->debug = TRUE;
572
	surface->compress_streams = FALSE;
573
    }
574

            
575
5
    surface->paginated_surface =  _cairo_paginated_surface_create (
576
	                                  &surface->base,
577
					  CAIRO_CONTENT_COLOR_ALPHA,
578
					  &cairo_pdf_surface_paginated_backend);
579

            
580
5
    status = surface->paginated_surface->status;
581
5
    if (status == CAIRO_STATUS_SUCCESS) {
582
	/* paginated keeps the only reference to surface now, drop ours */
583
5
	cairo_surface_destroy (&surface->base);
584
5
	return surface->paginated_surface;
585
    }
586

            
587
BAIL3:
588
    _cairo_scaled_font_subsets_destroy (surface->font_subsets);
589
BAIL2:
590
    _cairo_hash_table_destroy (surface->color_glyphs);
591
BAIL1:
592
    _cairo_hash_table_destroy (surface->all_surfaces);
593
BAIL0:
594
    _cairo_array_fini (&surface->objects);
595
    free (surface);
596

            
597
    /* destroy stream on behalf of caller */
598
    status_ignored = _cairo_output_stream_destroy (output);
599

            
600
    return _cairo_surface_create_in_error (status);
601
}
602

            
603
/**
604
 * cairo_pdf_surface_create_for_stream:
605
 * @write_func: a #cairo_write_func_t to accept the output data, may be %NULL
606
 *              to indicate a no-op @write_func. With a no-op @write_func,
607
 *              the surface may be queried or used as a source without
608
 *              generating any temporary files.
609
 * @closure: the closure argument for @write_func
610
 * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
611
 * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
612
 *
613
 * Creates a PDF surface of the specified size in points to be written
614
 * incrementally to the stream represented by @write_func and @closure.
615
 *
616
 * Return value: a pointer to the newly created surface. The caller
617
 * owns the surface and should call cairo_surface_destroy() when done
618
 * with it.
619
 *
620
 * This function always returns a valid pointer, but it will return a
621
 * pointer to a "nil" surface if an error such as out of memory
622
 * occurs. You can use cairo_surface_status() to check for this.
623
 *
624
 * Since: 1.2
625
 **/
626
cairo_surface_t *
627
1
cairo_pdf_surface_create_for_stream (cairo_write_func_t		 write_func,
628
				     void			*closure,
629
				     double			 width_in_points,
630
				     double			 height_in_points)
631
{
632
    cairo_output_stream_t *output;
633

            
634
1
    output = _cairo_output_stream_create (write_func, NULL, closure);
635
1
    if (_cairo_output_stream_get_status (output))
636
	return _cairo_surface_create_in_error (_cairo_output_stream_destroy (output));
637

            
638
1
    return _cairo_pdf_surface_create_for_stream_internal (output,
639
							  width_in_points,
640
							  height_in_points);
641
}
642

            
643
/**
644
 * cairo_pdf_surface_create:
645
 * @filename: a filename for the PDF output (must be writable), %NULL may be
646
 *            used to specify no output. This will generate a PDF surface that
647
 *            may be queried and used as a source, without generating a
648
 *            temporary file.
649
 * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
650
 * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
651
 *
652
 * Creates a PDF surface of the specified size in points to be written
653
 * to @filename.
654
 *
655
 * Return value: a pointer to the newly created surface. The caller
656
 * owns the surface and should call cairo_surface_destroy() when done
657
 * with it.
658
 *
659
 * This function always returns a valid pointer, but it will return a
660
 * pointer to a "nil" surface if an error such as out of memory
661
 * occurs. You can use cairo_surface_status() to check for this.
662
 *
663
 * Since: 1.2
664
 **/
665
cairo_surface_t *
666
4
cairo_pdf_surface_create (const char		*filename,
667
			  double		 width_in_points,
668
			  double		 height_in_points)
669
{
670
    cairo_output_stream_t *output;
671

            
672
4
    output = _cairo_output_stream_create_for_filename (filename);
673
4
    if (_cairo_output_stream_get_status (output))
674
	return _cairo_surface_create_in_error (_cairo_output_stream_destroy (output));
675

            
676
4
    return _cairo_pdf_surface_create_for_stream_internal (output,
677
							  width_in_points,
678
							  height_in_points);
679
}
680

            
681
static cairo_bool_t
682
_cairo_surface_is_pdf (cairo_surface_t *surface)
683
{
684
    return surface->backend == &cairo_pdf_surface_backend;
685
}
686

            
687
/* If the abstract_surface is a paginated surface, and that paginated
688
 * surface's target is a pdf_surface, then set pdf_surface to that
689
 * target. Otherwise return FALSE.
690
 */
691
static cairo_bool_t
692
16
_extract_pdf_surface (cairo_surface_t		 *surface,
693
		      cairo_pdf_surface_t	**pdf_surface)
694
{
695
    cairo_surface_t *target;
696
    cairo_status_t status_ignored;
697

            
698
16
    if (surface->status)
699
4
	return FALSE;
700
12
    if (surface->finished) {
701
6
	status_ignored = _cairo_surface_set_error (surface,
702
6
						   _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
703
6
        return FALSE;
704
    }
705

            
706
6
    if (! _cairo_surface_is_paginated (surface)) {
707
6
	status_ignored = _cairo_surface_set_error (surface,
708
6
						   _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
709
6
	return FALSE;
710
    }
711

            
712
    target = _cairo_paginated_surface_get_target (surface);
713
    if (target->status) {
714
	status_ignored = _cairo_surface_set_error (surface,
715
						   target->status);
716
	return FALSE;
717
    }
718
    if (target->finished) {
719
	status_ignored = _cairo_surface_set_error (surface,
720
						   _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
721
	return FALSE;
722
    }
723

            
724
    if (! _cairo_surface_is_pdf (target)) {
725
	status_ignored = _cairo_surface_set_error (surface,
726
						   _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
727
	return FALSE;
728
    }
729

            
730
    *pdf_surface = (cairo_pdf_surface_t *) target;
731
    return TRUE;
732
}
733

            
734
/**
735
 * cairo_pdf_surface_restrict_to_version:
736
 * @surface: a PDF #cairo_surface_t
737
 * @version: PDF version
738
 *
739
 * Restricts the generated PDF file to @version. See cairo_pdf_get_versions()
740
 * for a list of available version values that can be used here.
741
 *
742
 * This function should only be called before any drawing operations
743
 * have been performed on the given surface. The simplest way to do
744
 * this is to call this function immediately after creating the
745
 * surface.
746
 *
747
 * Since: 1.10
748
 **/
749
void
750
8
cairo_pdf_surface_restrict_to_version (cairo_surface_t 		*abstract_surface,
751
				       cairo_pdf_version_t  	 version)
752
{
753
8
    cairo_pdf_surface_t *surface = NULL; /* hide compiler warning */
754

            
755
8
    if (! _extract_pdf_surface (abstract_surface, &surface))
756
8
	return;
757

            
758
    if (version < CAIRO_PDF_VERSION_LAST)
759
	surface->pdf_version = version;
760

            
761
    _cairo_pdf_operators_enable_actual_text(&surface->pdf_operators,
762
					    version >= CAIRO_PDF_VERSION_1_5);
763
}
764

            
765
/**
766
 * cairo_pdf_get_versions:
767
 * @versions: supported version list
768
 * @num_versions: list length
769
 *
770
 * Used to retrieve the list of supported versions. See
771
 * cairo_pdf_surface_restrict_to_version().
772
 *
773
 * Since: 1.10
774
 **/
775
void
776
cairo_pdf_get_versions (cairo_pdf_version_t const	**versions,
777
                        int                     	 *num_versions)
778
{
779
    if (versions != NULL)
780
	*versions = _cairo_pdf_versions;
781

            
782
    if (num_versions != NULL)
783
	*num_versions = CAIRO_PDF_VERSION_LAST;
784
}
785

            
786
/**
787
 * cairo_pdf_version_to_string:
788
 * @version: a version id
789
 *
790
 * Get the string representation of the given @version id. This function
791
 * will return %NULL if @version isn't valid. See cairo_pdf_get_versions()
792
 * for a way to get the list of valid version ids.
793
 *
794
 * Return value: the string associated to given version.
795
 *
796
 * Since: 1.10
797
 **/
798
const char *
799
cairo_pdf_version_to_string (cairo_pdf_version_t version)
800
{
801
    if (version < 0 || version >= CAIRO_PDF_VERSION_LAST)
802
	return NULL;
803

            
804
    return _cairo_pdf_version_strings[version];
805
}
806

            
807
/**
808
 * cairo_pdf_surface_set_size:
809
 * @surface: a PDF #cairo_surface_t
810
 * @width_in_points: new surface width, in points (1 point == 1/72.0 inch)
811
 * @height_in_points: new surface height, in points (1 point == 1/72.0 inch)
812
 *
813
 * Changes the size of a PDF surface for the current (and
814
 * subsequent) pages.
815
 *
816
 * This function should only be called before any drawing operations
817
 * have been performed on the current page. The simplest way to do
818
 * this is to call this function immediately after creating the
819
 * surface or immediately after completing a page with either
820
 * cairo_show_page() or cairo_copy_page().
821
 *
822
 * Since: 1.2
823
 **/
824
void
825
8
cairo_pdf_surface_set_size (cairo_surface_t	*surface,
826
			    double		 width_in_points,
827
			    double		 height_in_points)
828
{
829
8
    cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */
830
    cairo_status_t status;
831

            
832
8
    if (! _extract_pdf_surface (surface, &pdf_surface))
833
8
	return;
834

            
835
    _cairo_pdf_surface_set_size_internal (pdf_surface,
836
					  width_in_points,
837
					  height_in_points);
838
    status = _cairo_paginated_surface_set_size (pdf_surface->paginated_surface,
839
						width_in_points,
840
						height_in_points);
841
    if (status)
842
	status = _cairo_surface_set_error (surface, status);
843
}
844

            
845
/**
846
 * CAIRO_PDF_OUTLINE_ROOT:
847
 *
848
 * The root outline item in cairo_pdf_surface_add_outline().
849
 *
850
 * Since: 1.16
851
 **/
852

            
853
/**
854
 * cairo_pdf_surface_add_outline:
855
 * @surface: a PDF #cairo_surface_t
856
 * @parent_id: the id of the parent item or %CAIRO_PDF_OUTLINE_ROOT if this is a top level item.
857
 * @utf8: the name of the outline
858
 * @link_attribs: the link attributes specifying where this outline links to
859
 * @flags: outline item flags
860
 *
861
 * Add an item to the document outline hierarchy with the name @utf8
862
 * that links to the location specified by @link_attribs. Link
863
 * attributes have the same keys and values as the [Link Tag][link],
864
 * excluding the "rect" attribute. The item will be a child of the
865
 * item with id @parent_id. Use %CAIRO_PDF_OUTLINE_ROOT as the parent
866
 * id of top level items.
867
 *
868
 * Return value: the id for the added item.
869
 *
870
 * Since: 1.16
871
 **/
872
int
873
cairo_pdf_surface_add_outline (cairo_surface_t	         *surface,
874
			       int                        parent_id,
875
			       const char                *utf8,
876
			       const char                *link_attribs,
877
			       cairo_pdf_outline_flags_t  flags)
878
{
879
    cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */
880
    cairo_status_t status;
881
    int id = 0;
882

            
883
    if (! _extract_pdf_surface (surface, &pdf_surface))
884
	return 0;
885

            
886
    status = _cairo_pdf_interchange_add_outline (pdf_surface,
887
						 parent_id,
888
						 utf8,
889
						 link_attribs,
890
						 flags,
891
						 &id);
892
    if (status)
893
	status = _cairo_surface_set_error (surface, status);
894

            
895
    return id;
896
}
897

            
898
/**
899
 * cairo_pdf_surface_set_metadata:
900
 * @surface: a PDF #cairo_surface_t
901
 * @metadata: The metadata item to set.
902
 * @utf8: metadata value
903
 *
904
 * Set document metadata. The %CAIRO_PDF_METADATA_CREATE_DATE and
905
 * %CAIRO_PDF_METADATA_MOD_DATE values must be in ISO-8601 format:
906
 * YYYY-MM-DDThh:mm:ss. An optional timezone of the form "[+/-]hh:mm"
907
 * or "Z" for UTC time can be appended. All other metadata values can be any UTF-8
908
 * string.
909
 *
910
 * For example:
911
 * <informalexample><programlisting>
912
 * cairo_pdf_surface_set_metadata (surface, CAIRO_PDF_METADATA_TITLE, "My Document");
913
 * cairo_pdf_surface_set_metadata (surface, CAIRO_PDF_METADATA_CREATE_DATE, "2015-12-31T23:59+02:00");
914
 * </programlisting></informalexample>
915
 *
916
 * Since: 1.16
917
 **/
918
void
919
cairo_pdf_surface_set_metadata (cairo_surface_t      *surface,
920
				cairo_pdf_metadata_t  metadata,
921
                                const char           *utf8)
922
{
923
    cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */
924
    cairo_status_t status;
925

            
926
    if (! _extract_pdf_surface (surface, &pdf_surface))
927
	return;
928

            
929
    status = _cairo_pdf_interchange_set_metadata (pdf_surface, metadata, utf8);
930
    if (status)
931
	status = _cairo_surface_set_error (surface, status);
932
}
933

            
934
/**
935
 * cairo_pdf_surface_set_custom_metadata:
936
 * @surface: a PDF #cairo_surface_t
937
 * @name: The name of the custom metadata item to set (utf8).
938
 * @value: The value of the metadata (utf8).
939
 *
940
 * Set custom document metadata. @name may be any string except for
941
 * the following names reserved by PDF: "Title", "Author", "Subject",
942
 * "Keywords", "Creator", "Producer", "CreationDate", "ModDate",
943
 * "Trapped".
944
 *
945
 * If @value is NULL or an empty string, the @name metadata will not be set.
946
 *
947
 * For example:
948
 * <informalexample><programlisting>
949
 * cairo_pdf_surface_set_custom_metadata (surface, "ISBN", "978-0123456789");
950
 * </programlisting></informalexample>
951
 *
952
 * Since: 1.18
953
 **/
954
void
955
cairo_pdf_surface_set_custom_metadata (cairo_surface_t	    *surface,
956
                                       const char           *name,
957
                                       const char           *value)
958
{
959
    cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */
960
    cairo_status_t status;
961

            
962
    if (! _extract_pdf_surface (surface, &pdf_surface))
963
	return;
964

            
965
    status = _cairo_pdf_interchange_set_custom_metadata (pdf_surface, name, value);
966
    if (status)
967
	status = _cairo_surface_set_error (surface, status);
968
}
969

            
970
/**
971
 * cairo_pdf_surface_set_page_label:
972
 * @surface: a PDF #cairo_surface_t
973
 * @utf8: The page label.
974
 *
975
 * Set page label for the current page.
976
 *
977
 * Since: 1.16
978
 **/
979
void
980
cairo_pdf_surface_set_page_label (cairo_surface_t *surface,
981
                                  const char      *utf8)
982
{
983
    cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */
984

            
985
    if (! _extract_pdf_surface (surface, &pdf_surface))
986
	return;
987

            
988
    free (pdf_surface->current_page_label);
989
    pdf_surface->current_page_label = utf8 ? strdup (utf8) : NULL;
990
}
991

            
992
/**
993
 * cairo_pdf_surface_set_thumbnail_size:
994
 * @surface: a PDF #cairo_surface_t
995
 * @width: Thumbnail width.
996
 * @height: Thumbnail height
997
 *
998
 * Set the thumbnail image size for the current and all subsequent
999
 * pages. Setting a width or height of 0 disables thumbnails for the
 * current and subsequent pages.
 *
 * Since: 1.16
 **/
void
cairo_pdf_surface_set_thumbnail_size (cairo_surface_t *surface,
				      int              width,
				      int              height)
{
    cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */
    if (! _extract_pdf_surface (surface, &pdf_surface))
	return;
    pdf_surface->thumbnail_width = width;
    pdf_surface->thumbnail_height = height;
}
static void
20
_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface,
			  cairo_bool_t         clear_doc_surfaces)
{
    int i, size;
    cairo_pdf_pattern_t *pattern;
    cairo_pdf_source_surface_t *src_surface;
    cairo_pdf_smask_group_t *group;
    cairo_pdf_source_surface_t doc_surface;
20
    size = _cairo_array_num_elements (&surface->page_patterns);
20
    for (i = 0; i < size; i++) {
	pattern = (cairo_pdf_pattern_t *) _cairo_array_index (&surface->page_patterns, i);
	cairo_pattern_destroy (pattern->pattern);
    }
20
    _cairo_array_truncate (&surface->page_patterns, 0);
20
    size = _cairo_array_num_elements (&surface->page_surfaces);
27
    for (i = 0; i < size; i++) {
7
	src_surface = (cairo_pdf_source_surface_t *) _cairo_array_index (&surface->page_surfaces, i);
7
	if (src_surface->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) {
	    cairo_pattern_destroy (src_surface->raster_pattern);
	} else {
7
	    if (_cairo_surface_is_recording (src_surface->surface) && src_surface->region_id != 0)
		_cairo_recording_surface_region_array_remove (src_surface->surface, src_surface->region_id);
7
	    cairo_surface_destroy (src_surface->surface);
	}
    }
20
    _cairo_array_truncate (&surface->page_surfaces, 0);
20
    size = _cairo_array_num_elements (&surface->smask_groups);
20
    for (i = 0; i < size; i++) {
	_cairo_array_copy_element (&surface->smask_groups, i, &group);
	_cairo_pdf_smask_group_destroy (group);
    }
20
    _cairo_array_truncate (&surface->smask_groups, 0);
20
    _cairo_array_truncate (&surface->knockout_group, 0);
20
    _cairo_array_truncate (&surface->page_annots, 0);
20
    if (surface->thumbnail_image)
	cairo_surface_destroy (&surface->thumbnail_image->base);
20
    surface->thumbnail_image = NULL;
20
    if (clear_doc_surfaces) {
10
	size = _cairo_array_num_elements (&surface->doc_surfaces);
14
	for (i = 0; i < size; i++) {
4
	    _cairo_array_copy_element (&surface->doc_surfaces, i, &doc_surface);
4
	    if (doc_surface.type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) {
		cairo_pattern_destroy (doc_surface.raster_pattern);
	    } else {
4
		if (_cairo_surface_is_recording (doc_surface.surface) && doc_surface.region_id != 0)
		    _cairo_recording_surface_region_array_remove (doc_surface.surface, doc_surface.region_id);
4
		cairo_surface_destroy (doc_surface.surface);
	    }
	}
10
	_cairo_array_truncate (&surface->doc_surfaces, 0);
    }
20
}
static void
5
_cairo_pdf_group_resources_init (cairo_pdf_group_resources_t *res)
{
    int i;
150
    for (i = 0; i < CAIRO_NUM_OPERATORS; i++)
145
	res->operators[i] = FALSE;
5
    _cairo_array_init (&res->alphas, sizeof (double));
5
    _cairo_array_init (&res->smasks, sizeof (cairo_pdf_resource_t));
5
    _cairo_array_init (&res->patterns, sizeof (cairo_pdf_resource_t));
5
    _cairo_array_init (&res->shadings, sizeof (cairo_pdf_resource_t));
5
    _cairo_array_init (&res->xobjects, sizeof (cairo_pdf_resource_t));
5
    _cairo_array_init (&res->fonts, sizeof (cairo_pdf_font_t));
5
}
static void
5
_cairo_pdf_group_resources_fini (cairo_pdf_group_resources_t *res)
{
5
    _cairo_array_fini (&res->alphas);
5
    _cairo_array_fini (&res->smasks);
5
    _cairo_array_fini (&res->patterns);
5
    _cairo_array_fini (&res->shadings);
5
    _cairo_array_fini (&res->xobjects);
5
    _cairo_array_fini (&res->fonts);
5
}
static void
29
_cairo_pdf_group_resources_clear (cairo_pdf_group_resources_t *res)
{
    int i;
870
    for (i = 0; i < CAIRO_NUM_OPERATORS; i++)
841
	res->operators[i] = FALSE;
29
    _cairo_array_truncate (&res->alphas, 0);
29
    _cairo_array_truncate (&res->smasks, 0);
29
    _cairo_array_truncate (&res->patterns, 0);
29
    _cairo_array_truncate (&res->shadings, 0);
29
    _cairo_array_truncate (&res->xobjects, 0);
29
    _cairo_array_truncate (&res->fonts, 0);
29
}
static void
20
_cairo_pdf_surface_add_operator (cairo_pdf_surface_t *surface,
				 cairo_operator_t     op)
{
20
    cairo_pdf_group_resources_t *res = &surface->resources;
20
    res->operators[op] = TRUE;
20
}
static cairo_int_status_t
27
_cairo_pdf_surface_add_alpha (cairo_pdf_surface_t *surface,
			      double               alpha,
			      int                 *index)
{
    int num_alphas, i;
    double other;
    cairo_int_status_t status;
27
    cairo_pdf_group_resources_t *res = &surface->resources;
27
    num_alphas = _cairo_array_num_elements (&res->alphas);
27
    for (i = 0; i < num_alphas; i++) {
12
	_cairo_array_copy_element (&res->alphas, i, &other);
12
	if (alpha == other) {
12
	    *index = i;
12
	    return CAIRO_STATUS_SUCCESS;
	}
    }
15
    status = _cairo_array_append (&res->alphas, &alpha);
15
    if (unlikely (status))
	return status;
15
    *index = _cairo_array_num_elements (&res->alphas) - 1;
15
    return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
_cairo_pdf_surface_add_smask (cairo_pdf_surface_t  *surface,
			      cairo_pdf_resource_t  smask)
{
    return _cairo_array_append (&(surface->resources.smasks), &smask);
}
static cairo_int_status_t
_cairo_pdf_surface_add_pattern (cairo_pdf_surface_t  *surface,
				cairo_pdf_resource_t  pattern)
{
    return _cairo_array_append (&(surface->resources.patterns), &pattern);
}
static cairo_int_status_t
_cairo_pdf_surface_add_shading (cairo_pdf_surface_t  *surface,
				cairo_pdf_resource_t  shading)
{
    return _cairo_array_append (&(surface->resources.shadings), &shading);
}
static cairo_int_status_t
28
_cairo_pdf_surface_add_xobject (cairo_pdf_surface_t  *surface,
				cairo_pdf_resource_t  xobject)
{
28
    return _cairo_array_append (&(surface->resources.xobjects), &xobject);
}
static cairo_int_status_t
_cairo_pdf_surface_add_font (unsigned int        font_id,
			     unsigned int        subset_id,
			     void		*closure)
{
    cairo_pdf_surface_t *surface = closure;
    cairo_pdf_font_t font;
    int num_fonts, i;
    cairo_int_status_t status;
    cairo_pdf_group_resources_t *res = &surface->resources;
    num_fonts = _cairo_array_num_elements (&res->fonts);
    for (i = 0; i < num_fonts; i++) {
	_cairo_array_copy_element (&res->fonts, i, &font);
	if (font.font_id == font_id &&
	    font.subset_id == subset_id)
	    return CAIRO_STATUS_SUCCESS;
    }
    num_fonts = _cairo_array_num_elements (&surface->fonts);
    for (i = 0; i < num_fonts; i++) {
	_cairo_array_copy_element (&surface->fonts, i, &font);
	if (font.font_id == font_id &&
	    font.subset_id == subset_id)
	    return _cairo_array_append (&res->fonts, &font);
    }
    font.font_id = font_id;
    font.subset_id = subset_id;
    font.subset_resource = _cairo_pdf_surface_new_object (surface);
    if (font.subset_resource.id == 0)
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    status = _cairo_array_append (&surface->fonts, &font);
    if (unlikely (status))
	return status;
    return _cairo_array_append (&res->fonts, &font);
}
static cairo_pdf_resource_t
_cairo_pdf_surface_get_font_resource (cairo_pdf_surface_t *surface,
				      unsigned int         font_id,
				      unsigned int         subset_id)
{
    cairo_pdf_font_t font;
    int num_fonts, i;
    num_fonts = _cairo_array_num_elements (&surface->fonts);
    for (i = 0; i < num_fonts; i++) {
	_cairo_array_copy_element (&surface->fonts, i, &font);
	if (font.font_id == font_id && font.subset_id == subset_id)
	    return font.subset_resource;
    }
    font.subset_resource.id = 0;
    return font.subset_resource;
}
static const char *
11
_cairo_operator_to_pdf_blend_mode (cairo_operator_t op)
{
11
    switch (op) {
    /* The extend blend mode operators */
    case CAIRO_OPERATOR_MULTIPLY:       return "Multiply";
    case CAIRO_OPERATOR_SCREEN:         return "Screen";
    case CAIRO_OPERATOR_OVERLAY:        return "Overlay";
    case CAIRO_OPERATOR_DARKEN:         return "Darken";
    case CAIRO_OPERATOR_LIGHTEN:        return "Lighten";
    case CAIRO_OPERATOR_COLOR_DODGE:    return "ColorDodge";
    case CAIRO_OPERATOR_COLOR_BURN:     return "ColorBurn";
    case CAIRO_OPERATOR_HARD_LIGHT:     return "HardLight";
    case CAIRO_OPERATOR_SOFT_LIGHT:     return "SoftLight";
1
    case CAIRO_OPERATOR_DIFFERENCE:     return "Difference";
    case CAIRO_OPERATOR_EXCLUSION:      return "Exclusion";
    case CAIRO_OPERATOR_HSL_HUE:        return "Hue";
    case CAIRO_OPERATOR_HSL_SATURATION: return "Saturation";
    case CAIRO_OPERATOR_HSL_COLOR:      return "Color";
    case CAIRO_OPERATOR_HSL_LUMINOSITY: return "Luminosity";
10
    default:
    /* The original Porter-Duff set */
    case CAIRO_OPERATOR_CLEAR:
    case CAIRO_OPERATOR_SOURCE:
    case CAIRO_OPERATOR_OVER:
    case CAIRO_OPERATOR_IN:
    case CAIRO_OPERATOR_OUT:
    case CAIRO_OPERATOR_ATOP:
    case CAIRO_OPERATOR_DEST:
    case CAIRO_OPERATOR_DEST_OVER:
    case CAIRO_OPERATOR_DEST_IN:
    case CAIRO_OPERATOR_DEST_OUT:
    case CAIRO_OPERATOR_DEST_ATOP:
    case CAIRO_OPERATOR_XOR:
    case CAIRO_OPERATOR_ADD:
    case CAIRO_OPERATOR_SATURATE:
10
	return "Normal";
    }
}
static void
24
_cairo_pdf_surface_emit_group_resources (cairo_pdf_surface_t         *surface,
					 cairo_pdf_group_resources_t *res,
					 cairo_bool_t                 gs0)
{
    int num_alphas, num_smasks, num_resources, i;
    double alpha;
    cairo_pdf_resource_t *smask, *pattern, *shading, *xobject;
    cairo_pdf_font_t *font;
24
    _cairo_output_stream_printf (surface->output, "<<\n");
24
    num_alphas = _cairo_array_num_elements (&res->alphas);
24
    num_smasks = _cairo_array_num_elements (&res->smasks);
24
    if (num_alphas > 0 || num_smasks > 0) {
15
	_cairo_output_stream_printf (surface->output,
				     "   /ExtGState <<\n");
15
	if (gs0) {
4
	    _cairo_output_stream_printf (surface->output,
					 "      /gs0 << /BM /Normal /SMask /None /CA 1.0 /ca 1.0 >>\n");
	}
450
	for (i = 0; i < CAIRO_NUM_OPERATORS; i++) {
435
	    if (res->operators[i]) {
11
		_cairo_output_stream_printf (surface->output,
					     "      /b%d << /BM /%s >>\n",
					     i, _cairo_operator_to_pdf_blend_mode(i));
	    }
	}
30
	for (i = 0; i < num_alphas; i++) {
15
	    _cairo_array_copy_element (&res->alphas, i, &alpha);
15
	    _cairo_output_stream_printf (surface->output,
					 "      /a%d << /CA %f /ca %f >>\n",
					 i, alpha, alpha);
	}
15
	for (i = 0; i < num_smasks; i++) {
	    smask = _cairo_array_index (&res->smasks, i);
	    _cairo_output_stream_printf (surface->output,
					 "      /s%d %d 0 R\n",
					 smask->id, smask->id);
	}
15
	_cairo_output_stream_printf (surface->output,
				     "   >>\n");
    }
24
    num_resources = _cairo_array_num_elements (&res->patterns);
24
    if (num_resources > 0) {
	_cairo_output_stream_printf (surface->output,
				     "   /Pattern <<");
	for (i = 0; i < num_resources; i++) {
	    pattern = _cairo_array_index (&res->patterns, i);
	    _cairo_output_stream_printf (surface->output,
					 " /p%d %d 0 R",
					 pattern->id, pattern->id);
	}
	_cairo_output_stream_printf (surface->output,
				     " >>\n");
    }
24
    num_resources = _cairo_array_num_elements (&res->shadings);
24
    if (num_resources > 0) {
	_cairo_output_stream_printf (surface->output,
				     "   /Shading <<");
	for (i = 0; i < num_resources; i++) {
	    shading = _cairo_array_index (&res->shadings, i);
	    _cairo_output_stream_printf (surface->output,
					 " /sh%d %d 0 R",
					 shading->id, shading->id);
	}
	_cairo_output_stream_printf (surface->output,
				     " >>\n");
    }
24
    num_resources = _cairo_array_num_elements (&res->xobjects);
24
    if (num_resources > 0) {
18
	_cairo_output_stream_printf (surface->output,
				     "   /XObject <<");
46
	for (i = 0; i < num_resources; i++) {
28
	    xobject = _cairo_array_index (&res->xobjects, i);
28
	    _cairo_output_stream_printf (surface->output,
					 " /x%d %d 0 R",
					 xobject->id, xobject->id);
	}
18
	_cairo_output_stream_printf (surface->output,
				     " >>\n");
    }
24
    num_resources = _cairo_array_num_elements (&res->fonts);
24
    if (num_resources > 0) {
	_cairo_output_stream_printf (surface->output,"   /Font <<\n");
	for (i = 0; i < num_resources; i++) {
	    font = _cairo_array_index (&res->fonts, i);
	    _cairo_output_stream_printf (surface->output,
					 "      /f-%d-%d %d 0 R\n",
					 font->font_id,
					 font->subset_id,
					 font->subset_resource.id);
	}
	_cairo_output_stream_printf (surface->output, "   >>\n");
    }
24
    _cairo_output_stream_printf (surface->output,
				 ">>\n");
24
}
static cairo_pdf_smask_group_t *
_cairo_pdf_surface_create_smask_group (cairo_pdf_surface_t	    *surface,
				       const cairo_rectangle_int_t  *extents)
{
    cairo_pdf_smask_group_t	*group;
    group = _cairo_calloc (sizeof (cairo_pdf_smask_group_t));
    if (unlikely (group == NULL)) {
	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
	return NULL;
    }
    group->group_res = _cairo_pdf_surface_new_object (surface);
    if (group->group_res.id == 0) {
	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
	free (group);
	return NULL;
    }
    group->width = surface->width;
    group->height = surface->height;
    if (extents != NULL) {
	group->extents = *extents;
    } else {
	group->extents.x = 0;
	group->extents.y = 0;
	group->extents.width = surface->width;
	group->extents.height = surface->height;
    }
    return group;
}
static void
_cairo_pdf_smask_group_destroy (cairo_pdf_smask_group_t *group)
{
    if (group->operation == PDF_FILL ||	group->operation == PDF_STROKE)
	_cairo_path_fixed_fini (&group->path);
    if (group->source)
	cairo_pattern_destroy (group->source);
    if (group->mask)
	cairo_pattern_destroy (group->mask);
    free (group->utf8);
    free (group->glyphs);
    free (group->clusters);
    if (group->scaled_font)
	cairo_scaled_font_destroy (group->scaled_font);
    free (group);
}
static cairo_int_status_t
_cairo_pdf_surface_add_smask_group (cairo_pdf_surface_t     *surface,
				    cairo_pdf_smask_group_t *group)
{
    return _cairo_array_append (&surface->smask_groups, &group);
}
static cairo_bool_t
2
_cairo_pdf_source_surface_equal (const void *key_a, const void *key_b)
{
2
    const cairo_pdf_source_surface_entry_t *a = key_a;
2
    const cairo_pdf_source_surface_entry_t *b = key_b;
2
    if (a->interpolate != b->interpolate)
	return FALSE;
2
    if (a->region_id != b->region_id)
	return FALSE;
2
    if (a->unique_id && b->unique_id && a->unique_id_length == b->unique_id_length)
	return (memcmp (a->unique_id, b->unique_id, a->unique_id_length) == 0);
2
    return (a->id == b->id);
}
static void
24
_cairo_pdf_source_surface_init_key (cairo_pdf_source_surface_entry_t *key)
{
24
    if (key->unique_id && key->unique_id_length > 0) {
	key->base.hash = _cairo_hash_bytes (_CAIRO_HASH_INIT_VALUE,
					    key->unique_id, key->unique_id_length);
    } else {
24
	key->base.hash = key->id;
    }
48
    key->base.hash = _cairo_hash_bytes (key->base.hash,
24
					&key->region_id,
					sizeof(key->region_id));
24
}
static cairo_bool_t
_cairo_pdf_color_glyph_equal (const void *key_a, const void *key_b)
{
    const cairo_pdf_color_glyph_t *a = key_a;
    const cairo_pdf_color_glyph_t *b = key_b;
    if (a->scaled_font != b->scaled_font)
	return FALSE;
    return (a->glyph_index == b->glyph_index);
}
static void
_cairo_pdf_color_glyph_init_key (cairo_pdf_color_glyph_t *key)
{
    key->base.hash = _cairo_hash_uintptr (_CAIRO_HASH_INIT_VALUE, (uintptr_t)key->scaled_font);
    key->base.hash = _cairo_hash_uintptr (key->base.hash, key->glyph_index);
}
static cairo_int_status_t
_cairo_pdf_surface_acquire_source_image_from_pattern (cairo_pdf_surface_t          *surface,
						      const cairo_pattern_t        *pattern,
						      cairo_image_surface_t       **image,
						      void                        **image_extra)
{
    switch (pattern->type) {
    case CAIRO_PATTERN_TYPE_SURFACE: {
	cairo_surface_pattern_t *surf_pat = (cairo_surface_pattern_t *) pattern;
	return _cairo_surface_acquire_source_image (surf_pat->surface, image, image_extra);
    } break;
    case CAIRO_PATTERN_TYPE_RASTER_SOURCE: {
	cairo_surface_t *surf;
	surf = _cairo_raster_source_pattern_acquire (pattern, &surface->base, NULL);
	if (!surf)
	    return CAIRO_INT_STATUS_UNSUPPORTED;
	assert (_cairo_surface_is_image (surf));
	*image = (cairo_image_surface_t *) surf;
    } break;
    case CAIRO_PATTERN_TYPE_SOLID:
    case CAIRO_PATTERN_TYPE_LINEAR:
    case CAIRO_PATTERN_TYPE_RADIAL:
    case CAIRO_PATTERN_TYPE_MESH:
    default:
	ASSERT_NOT_REACHED;
	break;
    }
    return CAIRO_STATUS_SUCCESS;
}
static void
_cairo_pdf_surface_release_source_image_from_pattern (cairo_pdf_surface_t          *surface,
						      const cairo_pattern_t        *pattern,
						      cairo_image_surface_t        *image,
						      void                         *image_extra)
{
    switch (pattern->type) {
    case CAIRO_PATTERN_TYPE_SURFACE: {
	cairo_surface_pattern_t *surf_pat = (cairo_surface_pattern_t *) pattern;
	_cairo_surface_release_source_image (surf_pat->surface, image, image_extra);
    } break;
    case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
	_cairo_raster_source_pattern_release (pattern, &image->base);
	break;
    case CAIRO_PATTERN_TYPE_SOLID:
    case CAIRO_PATTERN_TYPE_LINEAR:
    case CAIRO_PATTERN_TYPE_RADIAL:
    case CAIRO_PATTERN_TYPE_MESH:
    default:
	ASSERT_NOT_REACHED;
	break;
    }
}
static cairo_int_status_t
11
_get_source_surface_extents (cairo_surface_t         *source,
			     cairo_rectangle_int_t   *extents,
			     cairo_bool_t            *bounded,
			     cairo_bool_t            *subsurface)
{
    cairo_int_status_t status;
11
    *bounded = TRUE;
11
    *subsurface = FALSE;
11
    if (source->type == CAIRO_SURFACE_TYPE_RECORDING) {
4
	cairo_surface_t *free_me = NULL;
4
	if (_cairo_surface_is_snapshot (source))
4
	    free_me = source = _cairo_surface_snapshot_get_target (source);
4
	if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
	    cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source;
	    *extents = sub->extents;
	    *subsurface = TRUE;
	} else {
	    cairo_box_t box;
4
	    *bounded = _cairo_surface_get_extents (source, extents);
4
	    if (! *bounded) {
4
		status = _cairo_recording_surface_get_ink_bbox ((cairo_recording_surface_t *)source,
								&box, NULL);
4
		if (unlikely (status)) {
		    cairo_surface_destroy (free_me);
		    return status;
		}
4
		_cairo_box_round_to_rectangle (&box, extents);
	    }
	}
4
	cairo_surface_destroy (free_me);
    } else {
7
	*bounded =  _cairo_surface_get_extents (source, extents);
    }
11
    return CAIRO_STATUS_SUCCESS;
}
/**
 * _cairo_pdf_surface_add_source_surface:
 * @surface: [in] the pdf surface
 * @source_surface: [in] A #cairo_surface_t to use as the source surface
 * @source_pattern: [in] A #cairo_pattern_t of type SURFACE or RASTER_SOURCE to use as the source
 * @region_id: [in] Surfaces containing tags set this to the recording
 * surface region id. When tags are used in a XObject, PDF requires a
 * separate object for each use (section 14.7.4.2) @region_id is used
 * as a key to ensure a separate object is emitted for each use. Set
 * to 0 for surfaces without tags.
 * @op: [in] the operator used to composite this source
 * @filter: [in] filter type of the source pattern
 * @stencil_mask: [in] if true, the surface will be written to the PDF as an /ImageMask
 * @smask: [in] if true, only the alpha channel will be written (images only)
 * @need_transp_group: [in] if true and an XObject is used, make it a Transparency group
 * @extents: [in] extents of the operation that is using this source
 * @smask_res: [in] if not NULL, the image written will specify this resource as the smask for
 * the image (images only)
 * @pdf_source: [out] return pdf_source_surface entry in hash table
 * @x_offset: [out] if not NULL return x offset of surface
 * @y_offset: [out] if not NULL return y offset of surface
 * @source_extents: [out] if not NULL return operation extents in source space
 *
 * Add surface or raster_source pattern to list of surfaces to be
 * written to the PDF file when the current page is finished. Returns
 * a PDF resource to reference the surface. A hash table of all
 * surfaces in the PDF file (keyed by CAIRO_MIME_TYPE_UNIQUE_ID or
 * surface unique_id) is used to ensure surfaces with the same id are
 * only written once to the PDF file.
 *
 * Only one of @source_pattern or @source_surface is to be
 * specified. Set the other to NULL.
 **/
static cairo_int_status_t
13
_cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t	         *surface,
				       cairo_surface_t	                 *source_surface,
				       const cairo_pattern_t	         *source_pattern,
				       int                                region_id,
				       cairo_operator_t                   op,
				       cairo_filter_t		          filter,
				       cairo_bool_t                       stencil_mask,
				       cairo_bool_t                       smask,
				       cairo_bool_t                       need_transp_group,
				       const cairo_rectangle_int_t       *extents,
				       cairo_pdf_resource_t	         *smask_res,
				       cairo_pdf_source_surface_entry_t **pdf_source,
				       double                            *x_offset,
				       double                            *y_offset,
				       cairo_rectangle_int_t             *source_extents)
{
    cairo_pdf_source_surface_t src_surface;
    cairo_pdf_source_surface_entry_t surface_key;
    cairo_pdf_source_surface_entry_t *surface_entry;
13
    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
    cairo_bool_t interpolate;
13
    unsigned char *unique_id = NULL;
13
    unsigned long unique_id_length = 0;
    cairo_image_surface_t *image;
    void *image_extra;
    cairo_box_t box;
    cairo_rectangle_int_t op_extents;
    double x, y;
    cairo_bool_t subsurface;
    cairo_bool_t emit_image;
13
    switch (filter) {
6
    default:
    case CAIRO_FILTER_GOOD:
    case CAIRO_FILTER_BEST:
    case CAIRO_FILTER_BILINEAR:
6
	interpolate = TRUE;
6
	break;
7
    case CAIRO_FILTER_FAST:
    case CAIRO_FILTER_NEAREST:
    case CAIRO_FILTER_GAUSSIAN:
7
	interpolate = FALSE;
7
	break;
    }
13
    x = 0;
13
    y = 0;
13
    if (source_pattern) {
13
	if (source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) {
	    status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, source_pattern,
									   &image, &image_extra);
	    if (unlikely (status))
		return status;
	    source_surface = &image->base;
	    cairo_surface_get_device_offset (source_surface, &x, &y);
	} else {
13
	    cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) source_pattern;
13
	    source_surface = surface_pattern->surface;
	}
    }
13
    if (x_offset)
13
	*x_offset = x;
13
    if (y_offset)
13
	*y_offset = y;
    /* transform operation extents to pattern space */
13
    op_extents = *extents;
13
    if (source_pattern)
    {
13
	_cairo_box_from_rectangle (&box, extents);
13
	_cairo_matrix_transform_bounding_box_fixed (&source_pattern->matrix, &box, NULL);
13
	_cairo_box_round_to_rectangle (&box, &op_extents);
    }
13
    if (source_extents)
	*source_extents = op_extents;
13
    surface_key.id  = source_surface->unique_id;
    /* Recording surfaces do not use interpolate. Ensure it is always
     * false for recording surfaces. This is because pdf-interchange
     * needs to lookup recording surfaces in the hash table using
     * interpolate = FALSE in the key since it does not know the
     * interpolate value passed to this function.
     */
13
    emit_image = source_surface->type != CAIRO_SURFACE_TYPE_RECORDING;
13
    surface_key.interpolate = emit_image ? interpolate : FALSE;
13
    cairo_surface_get_mime_data (source_surface, CAIRO_MIME_TYPE_UNIQUE_ID,
				 (const unsigned char **) &surface_key.unique_id,
				 &surface_key.unique_id_length);
13
    surface_key.region_id = region_id;
13
    _cairo_pdf_source_surface_init_key (&surface_key);
13
    surface_entry = _cairo_hash_table_lookup (surface->all_surfaces, &surface_key.base);
13
    if (surface_entry) {
2
	if (pdf_source)
2
	    *pdf_source = surface_entry;
2
	if (source_pattern && source_pattern->extend != CAIRO_EXTEND_NONE)
	    _cairo_unbounded_rectangle_init (&op_extents);
2
	_cairo_rectangle_intersect (&op_extents, &surface_entry->extents);
2
	_cairo_rectangle_union (&surface_entry->required_extents, &op_extents);
2
	status = CAIRO_STATUS_SUCCESS;
    }
13
    if (status || surface_entry) {
2
	if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE)
	    _cairo_pdf_surface_release_source_image_from_pattern (surface, source_pattern, image, image_extra);
2
	return status;
    }
11
    if (surface_key.unique_id && surface_key.unique_id_length > 0) {
	unique_id = _cairo_malloc (surface_key.unique_id_length);
	if (unique_id == NULL) {
	    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
	    goto fail1;
	}
	unique_id_length = surface_key.unique_id_length;
	    memcpy (unique_id, surface_key.unique_id, unique_id_length);
    } else {
11
	unique_id = NULL;
11
	unique_id_length = 0;
    }
11
    surface_entry = _cairo_calloc (sizeof (cairo_pdf_source_surface_entry_t));
11
    if (surface_entry == NULL) {
	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
	goto fail1;
    }
11
    if (pdf_source)
11
	*pdf_source = surface_entry;
11
    surface_entry->id = surface_key.id;
11
    surface_entry->region_id = region_id;
11
    surface_entry->operator = op;
11
    surface_entry->interpolate = emit_image ? interpolate : FALSE;
11
    surface_entry->emit_image = emit_image;
11
    surface_entry->stencil_mask = stencil_mask;
11
    surface_entry->smask = smask;
11
    surface_entry->need_transp_group = need_transp_group;
11
    surface_entry->unique_id_length = unique_id_length;
11
    surface_entry->unique_id = unique_id;
11
    if (smask_res)
	surface_entry->smask_res = *smask_res;
    else
11
	surface_entry->smask_res.id = 0;
11
    status = _get_source_surface_extents (source_surface,
					  &surface_entry->extents,
					  &surface_entry->bounded,
					  &subsurface);
11
    if (unlikely (status))
	goto fail2;
11
    if (subsurface) {
	*x_offset = -surface_entry->extents.x;
	*y_offset = -surface_entry->extents.y;
    }
11
    if (source_pattern && source_pattern->extend != CAIRO_EXTEND_NONE)
	_cairo_unbounded_rectangle_init (&op_extents);
11
    _cairo_rectangle_intersect (&op_extents, &surface_entry->extents);
11
    surface_entry->required_extents = op_extents;
11
    _cairo_pdf_source_surface_init_key (surface_entry);
11
    src_surface.hash_entry = surface_entry;
11
    src_surface.region_id = 0;
11
    if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) {
	src_surface.type = CAIRO_PATTERN_TYPE_RASTER_SOURCE;
	src_surface.surface = NULL;
	status = _cairo_pattern_create_copy (&src_surface.raster_pattern, source_pattern);
	if (unlikely (status))
	    goto fail2;
    } else {
11
	src_surface.type = CAIRO_PATTERN_TYPE_SURFACE;
11
	src_surface.surface = cairo_surface_reference (source_surface);
11
	src_surface.raster_pattern = NULL;
11
	if (source_pattern) {
11
	    cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) source_pattern;
11
	    src_surface.region_id = surface_pattern->region_array_id;
11
	    if (_cairo_surface_is_recording (surface_pattern->surface) &&
		surface_pattern->region_array_id != 0)
	    {
		_cairo_recording_surface_region_array_reference (surface_pattern->surface,
								 surface_pattern->region_array_id);
	    }
	}
    }
11
    surface_entry->surface_res = _cairo_pdf_surface_new_object (surface);
11
    if (surface_entry->surface_res.id == 0) {
	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
	goto fail3;
    }
11
    if (surface_entry->bounded) {
7
	status = _cairo_array_append (&surface->page_surfaces, &src_surface);
7
	if (unlikely (status))
	    goto fail3;
    } else {
4
	status = _cairo_array_append (&surface->doc_surfaces, &src_surface);
4
	if (unlikely (status))
	    goto fail3;
    }
11
    status = _cairo_hash_table_insert (surface->all_surfaces,
				       &surface_entry->base);
11
    if (unlikely(status))
	goto fail3;
11
    if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE)
	_cairo_pdf_surface_release_source_image_from_pattern (surface, source_pattern, image, image_extra);
11
    return CAIRO_STATUS_SUCCESS;
fail3:
    if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE)
	cairo_pattern_destroy (src_surface.raster_pattern);
    else
	cairo_surface_destroy (src_surface.surface);
fail2:
    free (surface_entry);
fail1:
    if (unique_id)
	free (unique_id);
    if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE)
	_cairo_pdf_surface_release_source_image_from_pattern (surface, source_pattern, image, image_extra);
    return status;
}
static cairo_int_status_t
16
_cairo_pdf_surface_add_pdf_pattern_or_shading (cairo_pdf_surface_t	   *surface,
					       const cairo_pattern_t	   *pattern,
					       cairo_operator_t	            op,
					       cairo_analysis_source_t      source_type,
					       const cairo_rectangle_int_t *extents,
					       cairo_bool_t                 is_shading,
					       cairo_pdf_resource_t	   *pattern_res,
					       cairo_pdf_resource_t	   *gstate_res)
{
    cairo_pdf_pattern_t pdf_pattern;
    cairo_int_status_t status;
16
    int region_id = 0;
16
    pdf_pattern.is_shading = is_shading;
16
    pdf_pattern.operator = op;
    /* Solid colors are emitted into the content stream */
16
    if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
16
	pattern_res->id = 0;
16
	gstate_res->id = 0;
16
	return CAIRO_INT_STATUS_SUCCESS;
    }
    status = _cairo_pattern_create_copy (&pdf_pattern.pattern, pattern);
    if (unlikely (status))
	return status;
    pdf_pattern.pattern_res = _cairo_pdf_surface_new_object (surface);
    if (pdf_pattern.pattern_res.id == 0) {
	cairo_pattern_destroy (pdf_pattern.pattern);
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    }
    pdf_pattern.gstate_res.id = 0;
    /* gradient patterns require an smask object to implement transparency */
    if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR ||
	pattern->type == CAIRO_PATTERN_TYPE_RADIAL ||
	pattern->type == CAIRO_PATTERN_TYPE_MESH)
    {
	double min_alpha;
	_cairo_pattern_alpha_range (pattern, &min_alpha, NULL);
	if (! CAIRO_ALPHA_IS_OPAQUE (min_alpha)) {
            pdf_pattern.gstate_res = _cairo_pdf_surface_new_object (surface);
	    if (pdf_pattern.gstate_res.id == 0) {
		cairo_pattern_destroy (pdf_pattern.pattern);
		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
	    }
        }
    }
    pdf_pattern.width  = surface->width;
    pdf_pattern.height = surface->height;
    if (extents != NULL) {
	pdf_pattern.extents = *extents;
    } else {
	pdf_pattern.extents.x = 0;
	pdf_pattern.extents.y = 0;
	pdf_pattern.extents.width  = surface->width;
	pdf_pattern.extents.height = surface->height;
    }
    *pattern_res = pdf_pattern.pattern_res;
    *gstate_res = pdf_pattern.gstate_res;
    /* If the pattern requires a gstate it will be drawn from within
     * an XObject. The initial space of each XObject has an inverted
     * Y-axis. */
    pdf_pattern.inverted_y_axis = pdf_pattern.gstate_res.id ? TRUE : surface->in_xobject;
    if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
	cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
	if (_cairo_pdf_interchange_struct_tree_requires_recording_surface (surface,
									   surface_pattern,
									   source_type))
	{
	    region_id = surface_pattern->region_array_id;
	}
    }
    pdf_pattern.region_id = region_id;
    status = _cairo_array_append (&surface->page_patterns, &pdf_pattern);
    if (unlikely (status)) {
	cairo_pattern_destroy (pdf_pattern.pattern);
	return status;
    }
    return CAIRO_INT_STATUS_SUCCESS;
}
/* Get BBox from extents */
static void
8
_get_bbox_from_extents (const cairo_rectangle_int_t  *extents,
			cairo_box_double_t           *bbox)
{
8
    bbox->p1.x = extents->x;
8
    bbox->p1.y = extents->y;
8
    bbox->p2.x = extents->x + extents->width;
8
    bbox->p2.y = extents->y + extents->height;
8
}
static cairo_int_status_t
_cairo_pdf_surface_add_pdf_shading (cairo_pdf_surface_t		*surface,
				    const cairo_pattern_t	*pattern,
				    cairo_operator_t	         op,
				    const cairo_rectangle_int_t	*extents,
				    cairo_pdf_resource_t	*shading_res,
				    cairo_pdf_resource_t	*gstate_res)
{
    return _cairo_pdf_surface_add_pdf_pattern_or_shading (surface,
							  pattern,
							  op,
							  CAIRO_ANALYSIS_SOURCE_NONE,
							  extents,
							  TRUE,
							  shading_res,
							  gstate_res);
}
static cairo_int_status_t
16
_cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t		*surface,
				    const cairo_pattern_t	*pattern,
				    cairo_operator_t	         op,
				    cairo_analysis_source_t      source_type,
				    const cairo_rectangle_int_t	*extents,
				    cairo_pdf_resource_t	*pattern_res,
				    cairo_pdf_resource_t	*gstate_res)
{
16
    return _cairo_pdf_surface_add_pdf_pattern_or_shading (surface,
							  pattern,
							  op,
							  source_type,
							  extents,
							  FALSE,
							  pattern_res,
							  gstate_res);
}
static cairo_int_status_t
34
_cairo_pdf_surface_open_stream (cairo_pdf_surface_t	*surface,
				cairo_pdf_resource_t    *resource,
				cairo_bool_t             compressed,
				const char		*fmt,
				...)
{
    va_list ap;
    cairo_pdf_resource_t self, length;
34
    cairo_output_stream_t *output = NULL;
34
    if (resource) {
18
	self = *resource;
18
	_cairo_pdf_surface_update_object (surface, self);
    } else {
16
	self = _cairo_pdf_surface_new_object (surface);
16
	if (self.id == 0)
	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    }
34
    length = _cairo_pdf_surface_new_object (surface);
34
    if (length.id == 0)
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
34
    if (compressed) {
34
	output = _cairo_deflate_stream_create (surface->output);
34
	if (_cairo_output_stream_get_status (output))
	    return _cairo_output_stream_destroy (output);
    }
34
    surface->pdf_stream.active = TRUE;
34
    surface->pdf_stream.self = self;
34
    surface->pdf_stream.length = length;
34
    surface->pdf_stream.compressed = compressed;
34
    surface->current_pattern_is_solid_color = FALSE;
34
    surface->current_operator = CAIRO_OPERATOR_OVER;
34
    _cairo_pdf_operators_reset (&surface->pdf_operators);
34
    _cairo_output_stream_printf (surface->output,
				 "%d 0 obj\n"
				 "<< /Length %d 0 R\n",
				 surface->pdf_stream.self.id,
				 surface->pdf_stream.length.id);
34
    if (compressed)
34
	_cairo_output_stream_printf (surface->output,
				     "   /Filter /FlateDecode\n");
34
    if (fmt != NULL) {
29
	va_start (ap, fmt);
29
	_cairo_output_stream_vprintf (surface->output, fmt, ap);
29
	va_end (ap);
    }
34
    _cairo_output_stream_printf (surface->output,
				 ">>\n"
				 "stream\n");
34
    surface->pdf_stream.start_offset = _cairo_output_stream_get_position (surface->output);
34
    if (compressed) {
34
	assert (surface->pdf_stream.old_output == NULL);
34
        surface->pdf_stream.old_output = surface->output;
34
        surface->output = output;
34
	_cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output);
    }
34
    _cairo_pdf_operators_reset (&surface->pdf_operators);
34
    return _cairo_output_stream_get_status (surface->output);
}
static cairo_int_status_t
39
_cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface)
{
    cairo_int_status_t status;
    long long length;
39
    if (! surface->pdf_stream.active)
5
	return CAIRO_INT_STATUS_SUCCESS;
34
    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
34
    if (surface->pdf_stream.compressed) {
	cairo_int_status_t status2;
34
	status2 = _cairo_output_stream_destroy (surface->output);
34
	if (likely (status == CAIRO_INT_STATUS_SUCCESS))
34
	    status = status2;
34
	surface->output = surface->pdf_stream.old_output;
34
	_cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output);
34
	surface->pdf_stream.old_output = NULL;
    }
34
    length = _cairo_output_stream_get_position (surface->output) -
34
	surface->pdf_stream.start_offset;
34
    _cairo_output_stream_printf (surface->output,
				 "\n"
				 "endstream\n"
				 "endobj\n");
34
    _cairo_pdf_surface_update_object (surface,
				      surface->pdf_stream.length);
34
    _cairo_output_stream_printf (surface->output,
				 "%d 0 obj\n"
				 "   %lld\n"
				 "endobj\n",
				 surface->pdf_stream.length.id,
				 length);
34
    surface->pdf_stream.active = FALSE;
34
    if (likely (status == CAIRO_INT_STATUS_SUCCESS))
34
	status = _cairo_output_stream_get_status (surface->output);
34
    return status;
}
static void
4
_cairo_pdf_surface_write_memory_stream (cairo_pdf_surface_t         *surface,
					cairo_output_stream_t       *mem_stream,
					cairo_pdf_resource_t         resource,
					cairo_pdf_group_resources_t *resources,
					cairo_bool_t                 is_knockout_group,
					const cairo_box_double_t    *bbox)
{
4
    _cairo_pdf_surface_update_object (surface, resource);
4
    _cairo_output_stream_printf (surface->output,
				 "%d 0 obj\n"
				 "<< /Type /XObject\n"
				 "   /Length %d\n",
				 resource.id,
				 _cairo_memory_stream_length (mem_stream));
4
    if (surface->compress_streams) {
4
	_cairo_output_stream_printf (surface->output,
				     "   /Filter /FlateDecode\n");
    }
4
    _cairo_output_stream_printf (surface->output,
				 "   /Subtype /Form\n"
				 "   /BBox [ %f %f %f %f ]\n"
				 "   /Group <<\n"
				 "      /Type /Group\n"
				 "      /S /Transparency\n"
				 "      /I true\n"
				 "      /CS /DeviceRGB\n",
4
				 bbox->p1.x, bbox->p1.y, bbox->p2.x, bbox->p2.y);
4
    if (is_knockout_group)
4
        _cairo_output_stream_printf (surface->output,
                                     "      /K true\n");
4
    _cairo_output_stream_printf (surface->output,
				 "   >>\n"
				 "   /Resources\n");
4
    _cairo_pdf_surface_emit_group_resources (surface, resources, TRUE);
4
    _cairo_output_stream_printf (surface->output,
				 ">>\n"
				 "stream\n");
4
    _cairo_memory_stream_copy (mem_stream, surface->output);
4
    _cairo_output_stream_printf (surface->output,
				 "endstream\n"
				 "endobj\n");
4
}
static cairo_int_status_t
4
_cairo_pdf_surface_open_group (cairo_pdf_surface_t         *surface,
			       const cairo_box_double_t    *bbox,
			       cairo_pdf_resource_t        *resource)
{
    cairo_int_status_t status;
4
    assert (surface->pdf_stream.active == FALSE);
4
    assert (surface->group_stream.active == FALSE);
4
    surface->group_stream.active = TRUE;
4
    surface->group_stream.mem_stream = _cairo_memory_stream_create ();
4
    if (surface->compress_streams) {
4
	surface->group_stream.stream =
4
	    _cairo_deflate_stream_create (surface->group_stream.mem_stream);
    } else {
	surface->group_stream.stream = surface->group_stream.mem_stream;
    }
4
    status = _cairo_output_stream_get_status (surface->group_stream.stream);
4
    surface->group_stream.old_output = surface->output;
4
    surface->output = surface->group_stream.stream;
4
    _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output);
4
    _cairo_pdf_group_resources_clear (&surface->resources);
4
    if (resource) {
	surface->group_stream.resource = *resource;
    } else {
4
	surface->group_stream.resource = _cairo_pdf_surface_new_object (surface);
4
	if (surface->group_stream.resource.id == 0)
	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    }
4
    surface->group_stream.is_knockout = FALSE;
4
    surface->group_stream.bbox = *bbox;
    /* Reset gstate */
4
    surface->reset_gs_required = TRUE;
4
    surface->current_pattern_is_solid_color = FALSE;
4
    surface->current_operator = CAIRO_OPERATOR_OVER;
4
    _cairo_pdf_operators_reset (&surface->pdf_operators);
4
    return status;
}
static cairo_int_status_t
4
_cairo_pdf_surface_open_knockout_group (cairo_pdf_surface_t         *surface,
					const cairo_box_double_t    *bbox)
{
    cairo_int_status_t status;
4
    status = _cairo_pdf_surface_open_group (surface, bbox, NULL);
4
    if (unlikely (status))
	return status;
4
    surface->group_stream.is_knockout = TRUE;
4
    return CAIRO_INT_STATUS_SUCCESS;
}
static cairo_int_status_t
4
_cairo_pdf_surface_close_group (cairo_pdf_surface_t *surface,
				cairo_pdf_resource_t *group)
{
4
    cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS, status2;
4
    assert (surface->pdf_stream.active == FALSE);
4
    assert (surface->group_stream.active == TRUE);
4
    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
4
    if (unlikely (status))
	return status;
4
    if (surface->compress_streams) {
4
	status = _cairo_output_stream_destroy (surface->group_stream.stream);
4
	surface->group_stream.stream = NULL;
4
	_cairo_output_stream_printf (surface->group_stream.mem_stream,
				     "\n");
    }
4
    surface->output = surface->group_stream.old_output;
4
    _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output);
4
    surface->group_stream.active = FALSE;
4
    _cairo_pdf_surface_write_memory_stream (surface,
					    surface->group_stream.mem_stream,
					    surface->group_stream.resource,
					    &surface->resources,
					    surface->group_stream.is_knockout,
4
					    &surface->group_stream.bbox);
4
    if (group)
4
	*group = surface->group_stream.resource;
4
    status2 = _cairo_output_stream_destroy (surface->group_stream.mem_stream);
4
    if (status == CAIRO_INT_STATUS_SUCCESS)
4
	status = status2;
4
    surface->group_stream.mem_stream = NULL;
4
    surface->group_stream.stream = NULL;
4
    surface->reset_gs_required = FALSE;
4
    return status;
}
static cairo_int_status_t
10
_cairo_pdf_surface_open_object_stream (cairo_pdf_surface_t *surface)
{
10
    if (surface->debug || surface->pdf_version < CAIRO_PDF_VERSION_1_5) {
	/* Object streams not supported. All objects will be written
	 * directly to the file. */
	assert (surface->pdf_stream.active == FALSE);
	assert (surface->group_stream.active == FALSE);
	surface->object_stream.stream = surface->output;
    } else {
10
	surface->object_stream.resource = _cairo_pdf_surface_new_object (surface);
10
	if (surface->object_stream.resource.id == 0)
	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
10
	_cairo_array_truncate (&surface->object_stream.objects, 0);
10
	surface->object_stream.stream = _cairo_memory_stream_create ();
10
	surface->object_stream.active = TRUE;
    }
10
    return _cairo_output_stream_get_status (surface->object_stream.stream);
}
cairo_int_status_t
25
_cairo_pdf_surface_object_begin (cairo_pdf_surface_t *surface,
				 cairo_pdf_resource_t resource)
{
    cairo_xref_stream_object_t xref_obj;
    cairo_pdf_object_t *object;
    cairo_int_status_t status;
25
    if (surface->object_stream.active) {
25
	xref_obj.resource = resource;
25
	xref_obj.offset = _cairo_output_stream_get_position (surface->object_stream.stream);
25
	status = _cairo_array_append (&surface->object_stream.objects, &xref_obj);
25
	if (unlikely (status))
	    return status;
25
	object = _cairo_array_index (&surface->objects, resource.id - 1);
25
	object->type = PDF_OBJECT_COMPRESSED;
25
	object->u.compressed_obj.xref_stream = surface->object_stream.resource;
25
	object->u.compressed_obj.index = _cairo_array_num_elements (&surface->object_stream.objects) - 1;
    } else {
	_cairo_pdf_surface_update_object (surface, resource);
	_cairo_output_stream_printf (surface->output,
				     "%d 0 obj\n",
				     resource.id);
    }
25
    return CAIRO_INT_STATUS_SUCCESS;
}
void
25
_cairo_pdf_surface_object_end (cairo_pdf_surface_t *surface)
{
25
    if (!surface->object_stream.active) {
	_cairo_output_stream_printf (surface->output,
				     "endobj\n");
    }
25
}
static int
20
_cairo_xref_stream_object_compare (const void *a,
				   const void *b)
{
20
    const cairo_xref_stream_object_t *a_obj = a;
20
    const cairo_xref_stream_object_t *b_obj = b;
20
    if (a_obj->offset < b_obj->offset)
20
	return -1;
    else if (a_obj->offset > b_obj->offset)
	return 1;
    else
	return 0;
}
static cairo_int_status_t
10
_cairo_pdf_surface_close_object_stream (cairo_pdf_surface_t *surface)
{
    int i, num_objects;
    cairo_xref_stream_object_t *xref_obj;
    long long start_pos, length;
    cairo_output_stream_t *index_stream;
    cairo_output_stream_t *deflate_stream;
    cairo_pdf_resource_t length_res;
    cairo_int_status_t status;
    cairo_pdf_object_t *object;
10
    if (!surface->object_stream.active) {
	surface->object_stream.stream = NULL;
	return CAIRO_INT_STATUS_SUCCESS;
    }
10
    num_objects = _cairo_array_num_elements (&surface->object_stream.objects);
10
    if (num_objects == 0) {
	object = _cairo_array_index (&surface->objects, surface->object_stream.resource.id - 1);
	object->type = PDF_OBJECT_FREE;
	return CAIRO_INT_STATUS_SUCCESS;
    }
10
    index_stream = _cairo_memory_stream_create ();
    /* PDF requires the object id/offset pairs to be sorted by offset. */
10
    _cairo_array_sort (&surface->object_stream.objects, _cairo_xref_stream_object_compare);
35
    for (i = 0; i < num_objects; i++) {
25
	xref_obj = _cairo_array_index (&surface->object_stream.objects, i);
25
	_cairo_output_stream_printf (index_stream,
				     "%d %lld\n",
				     xref_obj->resource.id,
				     xref_obj->offset);
    }
10
    length_res = _cairo_pdf_surface_new_object (surface);
10
    if (length_res.id == 0)
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
10
    _cairo_pdf_surface_update_object (surface, surface->object_stream.resource);
10
    _cairo_output_stream_printf (surface->output,
				 "%d 0 obj\n"
				 "<< /Type /ObjStm\n"
                                 "   /Length %d 0 R\n"
                                 "   /N %d\n"
                                 "   /First %d\n",
				 surface->object_stream.resource.id,
				 length_res.id,
				 num_objects,
				 _cairo_memory_stream_length (index_stream));
10
    if (surface->compress_streams) {
10
	_cairo_output_stream_printf (surface->output,
				     "   /Filter /FlateDecode\n");
    }
10
    _cairo_output_stream_printf (surface->output,
				 ">>\n"
				 "stream\n");
10
    start_pos = _cairo_output_stream_get_position (surface->output);
10
    if (surface->compress_streams) {
10
	deflate_stream = _cairo_deflate_stream_create (surface->output);
10
	_cairo_memory_stream_copy (index_stream, deflate_stream);
10
	_cairo_memory_stream_copy (surface->object_stream.stream, deflate_stream);
10
	status = _cairo_output_stream_destroy (deflate_stream);
10
	if (unlikely (status))
	    return status;
10
	length = _cairo_output_stream_get_position (surface->output) - start_pos;
    } else {
	_cairo_memory_stream_copy (index_stream, surface->output);
	_cairo_memory_stream_copy (surface->object_stream.stream, surface->output);
	length = _cairo_output_stream_get_position (surface->output) - start_pos;
    }
10
    _cairo_output_stream_printf (surface->output,
				 "\n"
				 "endstream\n"
				 "endobj\n");
10
    _cairo_pdf_surface_update_object (surface,
				      length_res);
10
    _cairo_output_stream_printf (surface->output,
				 "%d 0 obj\n"
				 "   %lld\n"
				 "endobj\n",
				 length_res.id,
				 length);
10
    status = _cairo_output_stream_destroy (index_stream);
10
    if (unlikely (status))
	return status;
10
    status = _cairo_output_stream_destroy (surface->object_stream.stream);
10
    if (unlikely (status))
	return status;
10
    surface->object_stream.stream = NULL;
10
    surface->object_stream.active = FALSE;
10
    return _cairo_output_stream_get_status (surface->output);
}
static cairo_int_status_t
20
_cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t       *surface,
					const cairo_box_double_t  *bbox,
					cairo_pdf_resource_t      *resource,
					cairo_bool_t               is_form,
					cairo_bool_t               is_group,
					int                        struct_parents)
{
    cairo_int_status_t status;
20
    assert (surface->pdf_stream.active == FALSE);
20
    assert (surface->group_stream.active == FALSE);
20
    surface->content_resources = _cairo_pdf_surface_new_object (surface);
20
    if (surface->content_resources.id == 0)
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
20
    if (is_form) {
15
	assert (bbox != NULL);
15
	cairo_output_stream_t *mem_stream = _cairo_memory_stream_create ();
15
	if (is_group) {
11
	    _cairo_output_stream_printf (mem_stream,
		     "   /Type /XObject\n"
		     "   /Subtype /Form\n"
		     "   /BBox [ %f %f %f %f ]\n"
		     "   /Group <<\n"
		     "      /Type /Group\n"
		     "      /S /Transparency\n"
		     "      /I true\n"
		     "      /CS /DeviceRGB\n"
		     "   >>\n"
		     "   /Resources %d 0 R\n",
11
		     bbox->p1.x,
11
		     bbox->p1.y,
11
		     bbox->p2.x,
11
		     bbox->p2.y,
		     surface->content_resources.id);
	} else {
4
	    _cairo_output_stream_printf (mem_stream,
		     "   /Type /XObject\n"
		     "   /Subtype /Form\n"
		     "   /BBox [ %f %f %f %f ]\n"
		     "   /Resources %d 0 R\n",
4
		     bbox->p1.x,
4
		     bbox->p1.y,
4
		     bbox->p2.x,
4
		     bbox->p2.y,
		     surface->content_resources.id);
	}
15
	if (struct_parents >= 0) {
	    _cairo_output_stream_printf (mem_stream,
		"   /StructParents %d\n", struct_parents);
	}
	unsigned char *data;
	unsigned long length;
15
	status = _cairo_memory_stream_destroy (mem_stream, &data, &length);
15
	if (unlikely (status))
	    return status;
15
	char *str = _cairo_strndup ((const char*)data, length); /* Add NULL terminator */
	status =
15
	    _cairo_pdf_surface_open_stream (surface,
					    resource,
					    surface->compress_streams,
					    "%s",
					    str);
15
	free (str);
15
	free (data);
    } else {
	status =
5
	    _cairo_pdf_surface_open_stream (surface,
					    resource,
					    surface->compress_streams,
					    NULL);
5
	_cairo_output_stream_printf (surface->output,
				     "1 0 0 -1 0 %f cm\n",
				     surface->height);
    }
20
    if (unlikely (status))
	return status;
20
    surface->content = surface->pdf_stream.self;
20
    _cairo_output_stream_printf (surface->output, "q\n");
20
    _cairo_pdf_operators_reset (&surface->pdf_operators);
20
    return _cairo_output_stream_get_status (surface->output);
}
static cairo_int_status_t
20
_cairo_pdf_surface_close_content_stream (cairo_pdf_surface_t *surface,
					 cairo_bool_t         is_form)
{
    cairo_int_status_t status;
20
    assert (surface->pdf_stream.active == TRUE);
20
    assert (surface->group_stream.active == FALSE);
20
    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
20
    if (unlikely (status))
	return status;
20
    _cairo_output_stream_printf (surface->output, "Q\n");
20
    status = _cairo_pdf_surface_close_stream (surface);
20
    if (unlikely (status))
	return status;
20
    _cairo_pdf_surface_update_object (surface, surface->content_resources);
20
    _cairo_output_stream_printf (surface->output,
				 "%d 0 obj\n",
				 surface->content_resources.id);
20
    _cairo_pdf_surface_emit_group_resources (surface, &surface->resources, is_form);
20
    _cairo_output_stream_printf (surface->output,
				 "endobj\n");
20
    return _cairo_output_stream_get_status (surface->output);
}
static void
11
_cairo_pdf_source_surface_entry_pluck (void *entry, void *closure)
{
11
    cairo_pdf_source_surface_entry_t *surface_entry = entry;
11
    cairo_hash_table_t *patterns = closure;
11
    _cairo_hash_table_remove (patterns, &surface_entry->base);
11
    free (surface_entry->unique_id);
11
    free (surface_entry);
11
}
static void
_cairo_pdf_color_glyph_pluck (void *entry, void *closure)
{
    cairo_pdf_color_glyph_t *glyph_entry = entry;
    cairo_hash_table_t *patterns = closure;
    _cairo_hash_table_remove (patterns, &glyph_entry->base);
    cairo_scaled_font_destroy (glyph_entry->scaled_font);
    free (glyph_entry);
}
static cairo_int_status_t
5
_cairo_pdf_surface_write_page_dicts (cairo_pdf_surface_t *surface)
{
    cairo_int_status_t status;
    cairo_pdf_page_info_t *page_info;
    int num_annots;
    cairo_pdf_resource_t res;
10
    for (unsigned i = 0; i < _cairo_array_num_elements (&surface->pages); i++) {
5
	page_info = _cairo_array_index (&surface->pages, i);
5
	status = _cairo_pdf_surface_object_begin (surface, page_info->page_res);
5
	if (unlikely (status))
	    return status;
5
	_cairo_output_stream_printf (surface->object_stream.stream,
				     "<< /Type /Page %% %d\n"
				     "   /Parent %d 0 R\n"
				     "   /MediaBox [ 0 0 %f %f ]\n"
				     "   /Contents %d 0 R\n"
				     "   /Group <<\n"
				     "      /Type /Group\n"
				     "      /S /Transparency\n"
				     "      /I true\n"
				     "      /CS /DeviceRGB\n"
				     "   >>\n"
				     "   /Resources %d 0 R\n",
				     i + 1,
				     surface->pages_resource.id,
				     page_info->width,
				     page_info->height,
				     page_info->content.id,
				     page_info->resources.id);
5
	if (page_info->struct_parents >= 0) {
5
	    _cairo_output_stream_printf (surface->object_stream.stream,
					 "   /StructParents %d\n",
					 page_info->struct_parents);
	}
5
	num_annots = _cairo_array_num_elements (&page_info->annots);
5
	if (num_annots > 0) {
	    _cairo_output_stream_printf (surface->object_stream.stream,
					 "   /Annots [ ");
	    for (int j = 0; j < num_annots; j++) {
		_cairo_array_copy_element (&page_info->annots, j, &res);
		_cairo_output_stream_printf (surface->object_stream.stream,
					     "%d 0 R ",
					     res.id);
	    }
	    _cairo_output_stream_printf (surface->object_stream.stream, "]\n");
	}
5
	if (page_info->thumbnail.id) {
	    _cairo_output_stream_printf (surface->object_stream.stream,
					 "   /Thumb %d 0 R\n",
					 page_info->thumbnail.id);
	}
5
	_cairo_output_stream_printf (surface->object_stream.stream,
				     ">>\n");
5
	_cairo_pdf_surface_object_end (surface);
    }
5
    return status;
}
static cairo_status_t
5
_cairo_pdf_surface_finish (void *abstract_surface)
{
5
    cairo_pdf_surface_t *surface = abstract_surface;
    long long offset;
    cairo_pdf_resource_t catalog;
5
    cairo_status_t status = CAIRO_STATUS_SUCCESS, status2;
    int size, i;
    cairo_pdf_jbig2_global_t *global;
    char *label;
    cairo_pdf_resource_t xref_res;
    /* Some of the data may be in an inconistent state if there is an error status. */
5
    if (surface->base.status != CAIRO_STATUS_SUCCESS)
	goto CLEANUP;
5
    _cairo_pdf_surface_clear (surface, FALSE);
5
    status = _cairo_pdf_surface_open_object_stream (surface);
5
    if (unlikely (status))
	goto CLEANUP;
    /* Emit unbounded surfaces */
5
    status = _cairo_pdf_surface_write_patterns_and_smask_groups (surface, TRUE);
5
    if (unlikely (status))
	goto CLEANUP;
5
    _cairo_pdf_surface_clear (surface, TRUE);
5
    status = _cairo_pdf_surface_emit_font_subsets (surface);
5
    if (unlikely (status))
	goto CLEANUP;
    /* Emit any new patterns or surfaces created by the Type 3 font subset. */
5
    _cairo_pdf_surface_write_patterns_and_smask_groups (surface, TRUE);
5
    _cairo_pdf_surface_clear (surface, TRUE);
5
    status = _cairo_pdf_surface_write_pages (surface);
5
    if (unlikely (status))
	goto CLEANUP;
5
    status = _cairo_pdf_interchange_write_document_objects (surface);
5
    if (unlikely (status))
	goto CLEANUP;
5
    status = _cairo_pdf_surface_write_page_dicts (surface);
5
    if (unlikely (status))
	goto CLEANUP;
5
    catalog = _cairo_pdf_surface_new_object (surface);
5
    if (catalog.id == 0) {
	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
	goto CLEANUP;
    }
5
    status = _cairo_pdf_surface_write_catalog (surface, catalog);
5
    if (unlikely (status))
	goto CLEANUP;
5
    status = _cairo_pdf_surface_close_object_stream (surface);
5
    if (unlikely (status))
	goto CLEANUP;
5
    if (!surface->debug && surface->pdf_version >= CAIRO_PDF_VERSION_1_5)
    {
5
	xref_res = _cairo_pdf_surface_new_object (surface);
5
	status = _cairo_pdf_surface_write_xref_stream (surface,
						       xref_res,
						       catalog,
						       surface->docinfo_res,
						       &offset);
    } else {
	offset = _cairo_pdf_surface_write_xref (surface);
	_cairo_output_stream_printf (surface->output,
				     "trailer\n"
				     "<< /Size %d\n"
				     "   /Root %d 0 R\n"
				     "   /Info %d 0 R\n"
				     ">>\n",
				     surface->next_available_resource.id,
				     catalog.id,
				     surface->docinfo_res.id);
    }
5
    _cairo_output_stream_printf (surface->output,
				 "startxref\n"
				 "%lld\n"
				 "%%%%EOF\n",
				 offset);
5
  CLEANUP:
    /* pdf_operators has already been flushed when the last stream was
     * closed so we should never be writing anything here - however,
     * the stream may itself be in an error state. */
5
    status2 = _cairo_pdf_operators_fini (&surface->pdf_operators);
5
    if (status == CAIRO_STATUS_SUCCESS)
5
	status = status2;
    /* close any active streams still open due to fatal errors */
5
    status2 = _cairo_pdf_surface_close_stream (surface);
5
    if (status == CAIRO_STATUS_SUCCESS)
5
	status = status2;
5
    if (surface->group_stream.stream != NULL) {
	status2 = _cairo_output_stream_destroy (surface->group_stream.stream);
	if (status == CAIRO_STATUS_SUCCESS)
	    status = status2;
    }
5
    if (surface->group_stream.mem_stream != NULL) {
	status2 = _cairo_output_stream_destroy (surface->group_stream.mem_stream);
	if (status == CAIRO_STATUS_SUCCESS)
	    status = status2;
    }
5
    if (surface->pdf_stream.active)
	surface->output = surface->pdf_stream.old_output;
5
    if (surface->group_stream.active)
	surface->output = surface->group_stream.old_output;
    /* and finish the pdf surface */
5
    status2 = _cairo_output_stream_destroy (surface->output);
5
    if (status == CAIRO_STATUS_SUCCESS)
5
	status = status2;
5
    _cairo_pdf_group_resources_fini (&surface->resources);
5
    _cairo_array_fini (&surface->objects);
5
    size = _cairo_array_num_elements (&surface->pages);
10
    for (i = 0; i < size; i++) {
5
	cairo_pdf_page_info_t *page_info = _cairo_array_index (&surface->pages, i);
5
	_cairo_array_fini (&page_info->annots);
    }
5
    _cairo_array_fini (&surface->pages);
5
    _cairo_array_fini (&surface->rgb_linear_functions);
5
    _cairo_array_fini (&surface->alpha_linear_functions);
5
    _cairo_array_fini (&surface->page_patterns);
5
    _cairo_array_fini (&surface->page_surfaces);
5
    _cairo_array_fini (&surface->object_stream.objects);
5
    _cairo_array_fini (&surface->doc_surfaces);
5
    _cairo_hash_table_foreach (surface->all_surfaces,
			       _cairo_pdf_source_surface_entry_pluck,
5
			       surface->all_surfaces);
5
    _cairo_hash_table_destroy (surface->all_surfaces);
5
    _cairo_array_fini (&surface->smask_groups);
5
    _cairo_array_fini (&surface->fonts);
5
    _cairo_array_fini (&surface->knockout_group);
5
    _cairo_array_fini (&surface->page_annots);
5
     _cairo_hash_table_foreach (surface->color_glyphs,
				_cairo_pdf_color_glyph_pluck,
5
				surface->color_glyphs);
5
     _cairo_hash_table_destroy (surface->color_glyphs);
5
    if (surface->font_subsets) {
	_cairo_scaled_font_subsets_destroy (surface->font_subsets);
	surface->font_subsets = NULL;
    }
5
    size = _cairo_array_num_elements (&surface->jbig2_global);
5
    for (i = 0; i < size; i++) {
	global = (cairo_pdf_jbig2_global_t *) _cairo_array_index (&surface->jbig2_global, i);
	free(global->id);
	if (!global->emitted)
	    return _cairo_error (CAIRO_STATUS_JBIG2_GLOBAL_MISSING);
    }
5
    _cairo_array_fini (&surface->jbig2_global);
5
    size = _cairo_array_num_elements (&surface->page_labels);
10
    for (i = 0; i < size; i++) {
5
	_cairo_array_copy_element (&surface->page_labels, i, &label);
5
	free (label);
    }
5
    _cairo_array_fini (&surface->page_labels);
5
    _cairo_surface_clipper_reset (&surface->clipper);
5
    _cairo_pdf_interchange_fini (surface);
5
    return status;
}
static cairo_int_status_t
5
_cairo_pdf_surface_start_page (void *abstract_surface)
{
5
    cairo_pdf_surface_t *surface = abstract_surface;
    cairo_pdf_page_info_t page_info;
    cairo_int_status_t status;
    /* Document header */
5
    if (! surface->header_emitted) {
	const char *version;
5
	switch (surface->pdf_version) {
	case CAIRO_PDF_VERSION_1_4:
	    version = "1.4";
	    break;
	case CAIRO_PDF_VERSION_1_5:
	    version = "1.5";
	    break;
	case CAIRO_PDF_VERSION_1_6:
	    version = "1.6";
	    break;
5
	default:
	case CAIRO_PDF_VERSION_1_7:
5
	    version = "1.7";
5
	    break;
	}
5
	_cairo_output_stream_printf (surface->output,
				     "%%PDF-%s\n", version);
5
	_cairo_output_stream_printf (surface->output,
				     "%%%c%c%c%c\n", 181, 237, 174, 251);
5
	surface->header_emitted = TRUE;
    }
5
    _cairo_pdf_group_resources_clear (&surface->resources);
5
    surface->in_xobject = FALSE;
5
    page_info.page_res = _cairo_pdf_surface_new_object (surface);
5
    if (page_info.page_res.id == 0)
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
5
    page_info.width = surface->width;
5
    page_info.height = surface->height;
5
    page_info.content.id = 0;
5
    page_info.resources.id = 0;
5
    page_info.thumbnail.id = 0;
5
    page_info.struct_parents = -1;
5
    _cairo_array_init (&page_info.annots, sizeof (cairo_pdf_resource_t));
5
    status = _cairo_array_append (&surface->pages, &page_info);
5
    if (unlikely (status))
	return status;
5
    return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
5
_cairo_pdf_surface_has_fallback_images (void		*abstract_surface,
					cairo_bool_t	 has_fallbacks)
{
    cairo_int_status_t status;
5
    cairo_pdf_surface_t *surface = abstract_surface;
    cairo_box_double_t bbox;
5
    status = _cairo_pdf_interchange_end_page_content (surface);
5
    if (unlikely (status))
	return status;
5
    surface->has_fallback_images = has_fallbacks;
5
    surface->in_xobject = has_fallbacks;
5
    bbox.p1.x = 0;
5
    bbox.p1.y = 0;
5
    bbox.p2.x = surface->width;
5
    bbox.p2.y = surface->height;
5
    status = _cairo_pdf_surface_open_content_stream (surface, &bbox, NULL, has_fallbacks, has_fallbacks, -1);
5
    if (unlikely (status))
	return status;
5
    return CAIRO_STATUS_SUCCESS;
}
static cairo_bool_t
5
_cairo_pdf_surface_supports_fine_grained_fallbacks (void *abstract_surface)
{
5
    return TRUE;
}
static cairo_bool_t
5
_cairo_pdf_surface_requires_thumbnail_image (void *abstract_surface,
					     int  *width,
					     int  *height)
{
5
    cairo_pdf_surface_t *surface = abstract_surface;
5
    if (surface->thumbnail_width > 0 && surface->thumbnail_height > 0) {
	*width = surface->thumbnail_width;
	*height = surface->thumbnail_height;
	return TRUE;
    }
5
    return FALSE;
}
static cairo_int_status_t
_cairo_pdf_surface_set_thumbnail_image (void                  *abstract_surface,
					cairo_image_surface_t *image)
{
    cairo_pdf_surface_t *surface = abstract_surface;
    surface->thumbnail_image = 	(cairo_image_surface_t *)cairo_surface_reference(&image->base);
    return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
_cairo_pdf_surface_add_padded_image_surface (cairo_pdf_surface_t          *surface,
					     const cairo_pattern_t        *source,
					     const cairo_rectangle_int_t  *extents,
					     cairo_pdf_source_surface_entry_t **pdf_source,
					     double                       *x_offset,
					     double                       *y_offset,
					     cairo_rectangle_int_t        *source_extents)
{
    cairo_image_surface_t *image;
    cairo_surface_t *pad_image;
    void *image_extra;
    cairo_int_status_t status;
    int w, h;
    cairo_box_t box;
    cairo_rectangle_int_t rect;
    cairo_surface_pattern_t pad_pattern;
    status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, source,
								   &image, &image_extra);
    if (unlikely (status))
        return status;
    pad_image = &image->base;
    /* 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 = image->width;
    h = image->height;
    if (_cairo_fixed_integer_ceil(box.p1.x) < 0 ||
	_cairo_fixed_integer_ceil(box.p1.y) < 0 ||
	_cairo_fixed_integer_floor(box.p2.x) > w ||
	_cairo_fixed_integer_floor(box.p2.y) > h)
    {
	pad_image = _cairo_image_surface_create_with_content (image->base.content,
							      rect.width,
							      rect.height);
	if (pad_image->status) {
	    status = pad_image->status;
	    goto BAIL;
	}
	_cairo_pattern_init_for_surface (&pad_pattern, &image->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);
        if (unlikely (status))
            goto BAIL;
    }
    status = _cairo_pdf_surface_add_source_surface (surface,
						    pad_image,
						    NULL,
						    -1 , /* node_surface_index */
						    CAIRO_OPERATOR_OVER, /* not used for images */
						    source->filter,
						    FALSE, /* stencil mask */
						    FALSE, /* smask */
						    FALSE, /* need_transp_group */
						    extents,
						    NULL, /* smask_res */
						    pdf_source,
						    x_offset,
						    y_offset,
						    source_extents);
    if (unlikely (status))
        goto BAIL;
    if (pad_image != &image->base) {
	/* If using a padded image, replace _add_source_surface
	 * x/y_offset with padded image offset. Note:
	 * _add_source_surface only sets a non zero x/y_offset for
	 * RASTER_SOURCE patterns. _add_source_surface will always set
	 * x/y_offset to 0 for surfaces so we can ignore the returned
	 * offset and replace it with the offset required for the
	 * padded image */
	*x_offset = rect.x;
	*y_offset = rect.y;
    }
BAIL:
    if (pad_image != &image->base)
        cairo_surface_destroy (pad_image);
    _cairo_pdf_surface_release_source_image_from_pattern (surface, source, image, image_extra);
    return status;
}
/* Emit alpha channel from the image into stream_res.
 */
static cairo_int_status_t
7
_cairo_pdf_surface_emit_smask (cairo_pdf_surface_t	*surface,
			       cairo_image_surface_t	*image,
			       cairo_bool_t              stencil_mask,
			       cairo_bool_t              interpolate,
			       cairo_pdf_resource_t	*stream_res)
{
7
    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
    char *alpha;
    unsigned long alpha_size;
    uint32_t *pixel32;
    uint8_t *pixel8;
    int i, x, y, bit, a;
    cairo_image_transparency_t transparency;
    /* This is the only image format we support, which simplifies things. */
7
    assert (image->format == CAIRO_FORMAT_ARGB32 ||
	    image->format == CAIRO_FORMAT_RGB24 ||
	    image->format == CAIRO_FORMAT_A8 ||
	    image->format == CAIRO_FORMAT_A1 );
7
    transparency = _cairo_image_analyze_transparency (image);
7
    if (stencil_mask) {
	assert (transparency == CAIRO_IMAGE_IS_OPAQUE ||
		transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA);
    } else {
7
	assert (transparency != CAIRO_IMAGE_IS_OPAQUE);
    }
7
    if (transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA || transparency == CAIRO_IMAGE_IS_OPAQUE) {
	alpha_size = (image->width + 7) / 8 * image->height;
	alpha = _cairo_malloc_ab ((image->width+7) / 8, image->height);
    } else {
7
	alpha_size = image->height * image->width;
14
	alpha = _cairo_malloc_ab (image->height, image->width);
    }
7
    if (unlikely (alpha == NULL)) {
	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
	goto CLEANUP;
    }
7
    i = 0;
519
    for (y = 0; y < image->height; y++) {
512
	if (transparency == CAIRO_IMAGE_IS_OPAQUE) {
	    for (x = 0; x < (image->width + 7) / 8; x++)
		alpha[i++] = 0xff;
512
	} else if (image->format == CAIRO_FORMAT_A1) {
	    pixel8 = (uint8_t *) (image->data + y * image->stride);
	    for (x = 0; x < (image->width + 7) / 8; x++, pixel8++) {
		a = *pixel8;
		a = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (a);
		alpha[i++] = a;
	    }
	} else {
512
	    pixel8 = (uint8_t *) (image->data + y * image->stride);
512
	    pixel32 = (uint32_t *) (image->data + y * image->stride);
512
	    bit = 7;
71824
	    for (x = 0; x < image->width; x++) {
71312
		if (image->format == CAIRO_FORMAT_ARGB32) {
71312
		    a = (*pixel32 & 0xff000000) >> 24;
71312
		    pixel32++;
		} else {
		    a = *pixel8;
		    pixel8++;
		}
71312
		if (transparency == CAIRO_IMAGE_HAS_ALPHA) {
71312
		    alpha[i++] = a;
		} else { /* transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA or CAIRO_IMAGE_IS_OPAQUE */
		    if (bit == 7)
			alpha[i] = 0;
		    if (a != 0)
			alpha[i] |= (1 << bit);
		    bit--;
		    if (bit < 0) {
			bit = 7;
			i++;
		    }
		}
	    }
512
	    if (bit != 7)
		i++;
	}
    }
7
    if (stencil_mask) {
	status = _cairo_pdf_surface_open_stream (surface,
						 stream_res,
						 TRUE,
						 "   /Type /XObject\n"
						 "   /Subtype /Image\n"
						 "   /ImageMask true\n"
						 "   /Width %d\n"
						 "   /Height %d\n"
						 "   /Interpolate %s\n"
						 "   /BitsPerComponent 1\n"
						 "   /Decode [1 0]\n",
						 image->width, image->height,
						 interpolate ? "true" : "false");
    } else {
7
	status = _cairo_pdf_surface_open_stream (surface,
						 stream_res,
						 TRUE,
						 "   /Type /XObject\n"
						 "   /Subtype /Image\n"
						 "   /Width %d\n"
						 "   /Height %d\n"
						 "   /ColorSpace /DeviceGray\n"
						 "   /Interpolate %s\n"
						 "   /BitsPerComponent %d\n",
						 image->width, image->height,
						 interpolate ? "true" : "false",
						 transparency == CAIRO_IMAGE_HAS_ALPHA ? 8 : 1);
    }
7
    if (unlikely (status))
	goto CLEANUP_ALPHA;
7
    _cairo_output_stream_write (surface->output, alpha, alpha_size);
7
    status = _cairo_pdf_surface_close_stream (surface);
7
 CLEANUP_ALPHA:
7
    free (alpha);
7
 CLEANUP:
7
    return status;
}
/**
 * _cairo_pdf_surface_emit_image:
 * @surface: the pdf surface
 * @image_surf: The image to write
 * @surface_entry: Contains image resource, smask resource, interpolate and stencil mask parameters.
 *
 * Emit an image stream using the @image_res resource and write out
 * the image data from @image_surf. If @smask_res is not null, @smask_res will
 * be specified as the smask for the image. Otherwise emit the an smask if
 * the image is requires one.
 **/
static cairo_int_status_t
7
_cairo_pdf_surface_emit_image (cairo_pdf_surface_t              *surface,
                               cairo_image_surface_t            *image_surf,
			       cairo_pdf_source_surface_entry_t *surface_entry)
{
7
    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
    char *data;
    unsigned long data_size;
    uint32_t *pixel;
    int i, x, y, bit;
7
    cairo_pdf_resource_t smask = {0}; /* squelch bogus compiler warning */
    cairo_bool_t need_smask;
    cairo_image_color_t color;
    cairo_image_surface_t *image;
    cairo_image_transparency_t transparency;
    char smask_buf[30];
7
    image  = image_surf;
7
    if (image->format != CAIRO_FORMAT_RGB24 &&
7
	image->format != CAIRO_FORMAT_ARGB32 &&
	image->format != CAIRO_FORMAT_A8 &&
	image->format != CAIRO_FORMAT_A1)
    {
	cairo_surface_t *surf;
	cairo_surface_pattern_t pattern;
	surf = _cairo_image_surface_create_with_content (image_surf->base.content,
							 image_surf->width,
							 image_surf->height);
	image = (cairo_image_surface_t *) surf;
	if (surf->status) {
	    status = surf->status;
	    goto CLEANUP;
	}
	_cairo_pattern_init_for_surface (&pattern, &image_surf->base);
	status = _cairo_surface_paint (surf,
				       CAIRO_OPERATOR_SOURCE, &pattern.base,
				       NULL);
        _cairo_pattern_fini (&pattern.base);
        if (unlikely (status))
            goto CLEANUP;
    }
7
    if (surface_entry->smask || surface_entry->stencil_mask) {
	return _cairo_pdf_surface_emit_smask (surface, image,
					      surface_entry->stencil_mask,
					      surface_entry->interpolate,
					      &surface_entry->surface_res);
    }
7
    color = _cairo_image_analyze_color (image);
7
    switch (color) {
        default:
	case CAIRO_IMAGE_UNKNOWN_COLOR:
	    ASSERT_NOT_REACHED;
	case CAIRO_IMAGE_IS_COLOR:
7
	    data_size = image->height * image->width * 3;
7
	    data = _cairo_malloc_abc (image->width, image->height, 3);
7
	    break;
	case CAIRO_IMAGE_IS_GRAYSCALE:
	    data_size = image->height * image->width;
	    data = _cairo_malloc_ab (image->width, image->height);
	    break;
	case CAIRO_IMAGE_IS_MONOCHROME:
	    data_size = (image->width + 7) / 8 * image->height;
	    data = _cairo_malloc_ab ((image->width+7) / 8, image->height);
	    break;
    }
7
    if (unlikely (data == NULL)) {
	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
	goto CLEANUP;
    }
7
    i = 0;
519
    for (y = 0; y < image->height; y++) {
512
	pixel = (uint32_t *) (image->data + y * image->stride);
512
	bit = 7;
71824
	for (x = 0; x < image->width; x++, pixel++) {
	    int r, g, b;
	    /* XXX: We're un-premultiplying alpha here. My reading of the PDF
	     * specification suggests that we should be able to avoid having
	     * to do this by filling in the SMask's Matte dictionary
	     * appropriately, but my attempts to do that so far have
	     * failed. */
71312
	    if (image->format == CAIRO_FORMAT_ARGB32) {
		uint8_t a;
71312
		a = (*pixel & 0xff000000) >> 24;
71312
		if (a == 0) {
28563
		    r = g = b = 0;
		} else {
42749
		    r = (((*pixel & 0xff0000) >> 16) * 255 + a / 2) / a;
42749
		    g = (((*pixel & 0x00ff00) >>  8) * 255 + a / 2) / a;
42749
		    b = (((*pixel & 0x0000ff) >>  0) * 255 + a / 2) / a;
		}
	    } else if (image->format == CAIRO_FORMAT_RGB24) {
		r = (*pixel & 0x00ff0000) >> 16;
		g = (*pixel & 0x0000ff00) >>  8;
		b = (*pixel & 0x000000ff) >>  0;
	    } else {
		r = g = b = 0;
	    }
71312
	    switch (color) {
71312
		case CAIRO_IMAGE_IS_COLOR:
		case CAIRO_IMAGE_UNKNOWN_COLOR:
71312
		    data[i++] = r;
71312
		    data[i++] = g;
71312
		    data[i++] = b;
71312
		    break;
		case CAIRO_IMAGE_IS_GRAYSCALE:
		    data[i++] = r;
		    break;
		case CAIRO_IMAGE_IS_MONOCHROME:
		    if (bit == 7)
			data[i] = 0;
		    if (r != 0)
			data[i] |= (1 << bit);
		    bit--;
		    if (bit < 0) {
			bit = 7;
			i++;
		    }
		    break;
	    }
	}
512
	if (bit != 7)
	    i++;
    }
7
    if (surface_entry->smask_res.id != 0) {
	need_smask = TRUE;
	smask = surface_entry->smask_res;
    } else {
7
	need_smask = FALSE;
7
	if (image->format == CAIRO_FORMAT_ARGB32 ||
	    image->format == CAIRO_FORMAT_A8 ||
	    image->format == CAIRO_FORMAT_A1)
	{
7
	    transparency = _cairo_image_analyze_transparency (image);
7
	    if (transparency != CAIRO_IMAGE_IS_OPAQUE) {
7
		need_smask = TRUE;
7
		smask = _cairo_pdf_surface_new_object (surface);
7
		if (smask.id == 0) {
		    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
		    goto CLEANUP_RGB;
		}
7
		status = _cairo_pdf_surface_emit_smask (surface, image, FALSE, surface_entry->interpolate, &smask);
7
		if (unlikely (status))
		    goto CLEANUP_RGB;
	    }
	}
    }
7
    if (need_smask)
7
	snprintf(smask_buf, sizeof(smask_buf), "   /SMask %d 0 R\n", smask.id);
    else
	smask_buf[0] = 0;
14
    status = _cairo_pdf_surface_open_stream (surface,
					     &surface_entry->surface_res,
					     TRUE,
					     "   /Type /XObject\n"
					     "   /Subtype /Image\n"
					     "   /Width %d\n"
					     "   /Height %d\n"
					     "   /ColorSpace %s\n"
					     "   /Interpolate %s\n"
					     "   /BitsPerComponent %d\n"
					     "%s",
					     image->width,
					     image->height,
					     color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray",
7
					     surface_entry->interpolate ? "true" : "false",
					     color == CAIRO_IMAGE_IS_MONOCHROME? 1 : 8,
					     smask_buf);
7
    if (unlikely (status))
	goto CLEANUP_RGB;
#undef IMAGE_DICTIONARY
7
    _cairo_output_stream_write (surface->output, data, data_size);
7
    status = _cairo_pdf_surface_close_stream (surface);
7
CLEANUP_RGB:
7
    free (data);
7
CLEANUP:
7
    if (image != image_surf)
	cairo_surface_destroy (&image->base);
7
    return status;
}
static cairo_int_status_t
_cairo_pdf_surface_lookup_jbig2_global (cairo_pdf_surface_t       *surface,
					const unsigned char       *global_id,
					unsigned long              global_id_length,
					cairo_pdf_jbig2_global_t **entry)
{
    cairo_pdf_jbig2_global_t global;
    int size, i;
    cairo_int_status_t status;
    size = _cairo_array_num_elements (&surface->jbig2_global);
    for (i = 0; i < size; i++) {
	*entry = (cairo_pdf_jbig2_global_t *) _cairo_array_index (&surface->jbig2_global, i);
	if ((*entry)->id && global_id && (*entry)->id_length == global_id_length
	    && memcmp((*entry)->id, global_id, global_id_length) == 0) {
	    return CAIRO_STATUS_SUCCESS;
	}
    }
    global.id = _cairo_malloc (global_id_length);
    if (unlikely (global.id == NULL)) {
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    }
    memcpy (global.id, global_id, global_id_length);
    global.id_length = global_id_length;
    global.res = _cairo_pdf_surface_new_object (surface);
    if (global.res.id == 0) {
	free(global.id);
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    }
    global.emitted = FALSE;
    status = _cairo_array_append (&surface->jbig2_global, &global);
    if (unlikely(status))
	return status;
    size = _cairo_array_num_elements (&surface->jbig2_global);
    *entry = (cairo_pdf_jbig2_global_t *) _cairo_array_index (&surface->jbig2_global, size - 1);
    return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
11
_cairo_pdf_surface_emit_jbig2_image (cairo_pdf_surface_t              *surface,
				     cairo_surface_t	              *source,
				     cairo_pdf_source_surface_entry_t *surface_entry,
				     cairo_bool_t                      test)
{
    cairo_int_status_t status;
    const unsigned char *mime_data;
    unsigned long mime_data_length;
    cairo_image_info_t info;
    const unsigned char *global_id;
    unsigned long global_id_length;
    const unsigned char *global_data;
    unsigned long global_data_length;
11
    cairo_pdf_jbig2_global_t *global_entry = NULL; /* hide compiler warning */
    char smask_buf[30];
    char decode_parms_buf[100];
11
    cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JBIG2,
				 &mime_data, &mime_data_length);
11
    if (mime_data == NULL)
11
	return CAIRO_INT_STATUS_UNSUPPORTED;
    status = _cairo_image_info_get_jbig2_info (&info, mime_data, mime_data_length);
    if (status)
	return status;
    /* At this point we know emitting jbig2 will succeed. */
    if (test)
	return CAIRO_STATUS_SUCCESS;
    cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID,
				 &global_id, &global_id_length);
    if (global_id && global_id_length > 0) {
	status = _cairo_pdf_surface_lookup_jbig2_global (surface, global_id, global_id_length, &global_entry);
	if (unlikely(status))
	    return status;
	if (!global_entry->emitted) {
	    cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JBIG2_GLOBAL,
					 &global_data, &global_data_length);
	    if (global_data) {
		status = _cairo_pdf_surface_open_stream (surface, &global_entry->res, FALSE, NULL);
		if (unlikely(status))
		    return status;
		_cairo_output_stream_write (surface->output, global_data, global_data_length);
		status = _cairo_pdf_surface_close_stream (surface);
		if (unlikely(status))
		    return status;
		global_entry->emitted = TRUE;
	    }
	}
	snprintf(decode_parms_buf, sizeof(decode_parms_buf),
		 "   /DecodeParms << /JBIG2Globals %d 0 R >>\n", global_entry->res.id);
    } else {
	decode_parms_buf[0] = 0;
    }
    if (surface_entry->smask_res.id)
	snprintf(smask_buf, sizeof(smask_buf), "   /SMask %d 0 R\n", surface_entry->smask_res.id);
    else
	smask_buf[0] = 0;
    if (surface_entry->stencil_mask) {
	status = _cairo_pdf_surface_open_stream (surface,
						 &surface_entry->surface_res,
						 FALSE,
						 "   /Type /XObject\n"
						 "   /Subtype /Image\n"
						 "   /ImageMask true\n"
						 "   /Width %d\n"
						 "   /Height %d\n"
						 "   /Interpolate %s\n"
						 "   /BitsPerComponent 1\n"
						 "   /Decode [1 0]\n"
						 "   /Filter /JPXDecode\n"
						 "%s",
						 info.width,
						 info.height,
						 surface_entry->interpolate ? "true" : "false",
						 decode_parms_buf);
    } else {
	status = _cairo_pdf_surface_open_stream (surface,
						 &surface_entry->surface_res,
						 FALSE,
						 "   /Type /XObject\n"
						 "   /Subtype /Image\n"
						 "   /Width %d\n"
						 "   /Height %d\n"
						 "   /ColorSpace /DeviceGray\n"
						 "   /BitsPerComponent 1\n"
						 "   /Interpolate %s\n"
						 "%s"
						 "   /Filter /JBIG2Decode\n"
						 "%s",
						 info.width,
						 info.height,
						 surface_entry->interpolate ? "true" : "false",
						 smask_buf,
						 decode_parms_buf);
    }
    if (unlikely(status))
	return status;
    _cairo_output_stream_write (surface->output, mime_data, mime_data_length);
    status = _cairo_pdf_surface_close_stream (surface);
    return status;
}
static cairo_int_status_t
11
_cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t              *surface,
				   cairo_surface_t	            *source,
				   cairo_pdf_source_surface_entry_t *surface_entry,
				   cairo_bool_t                      test)
{
    cairo_int_status_t status;
    const unsigned char *mime_data;
    unsigned long mime_data_length;
    cairo_image_info_t info;
    char smask_buf[30];
11
    if (surface->pdf_version < CAIRO_PDF_VERSION_1_5)
	return CAIRO_INT_STATUS_UNSUPPORTED;
11
    cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JP2,
				 &mime_data, &mime_data_length);
11
    if (mime_data == NULL)
11
	return CAIRO_INT_STATUS_UNSUPPORTED;
    status = _cairo_image_info_get_jpx_info (&info, mime_data, mime_data_length);
    if (status)
	return status;
    if ((surface_entry->smask || surface_entry->stencil_mask) && info.num_components != 1)
	return CAIRO_INT_STATUS_UNSUPPORTED;
    if ((surface_entry->stencil_mask) && info.bits_per_component != 1)
	return CAIRO_INT_STATUS_UNSUPPORTED;
    if (surface_entry->smask_res.id)
	snprintf(smask_buf, sizeof(smask_buf), "   /SMask %d 0 R\n", surface_entry->smask_res.id);
    else
	smask_buf[0] = 0;
    /* At this point we know emitting jpx will succeed. */
    if (test)
	return CAIRO_STATUS_SUCCESS;
    if (surface_entry->stencil_mask) {
	status = _cairo_pdf_surface_open_stream (surface,
						 &surface_entry->surface_res,
						 FALSE,
						 "   /Type /XObject\n"
						 "   /Subtype /Image\n"
						 "   /ImageMask true\n"
						 "   /Width %d\n"
						 "   /Height %d\n"
						 "   /Interpolate %s\n"
						 "   /BitsPerComponent 1\n"
						 "   /Decode [1 0]\n"
						 "   /Filter /JPXDecode\n",
						 info.width,
						 info.height,
						 surface_entry->interpolate ? "true" : "false");
    } else {
	status = _cairo_pdf_surface_open_stream (surface,
						 &surface_entry->surface_res,
						 FALSE,
						 "   /Type /XObject\n"
						 "   /Subtype /Image\n"
						 "   /Width %d\n"
						 "   /Height %d\n"
						 "   /Interpolate %s\n"
						 "%s"
						 "   /Filter /JPXDecode\n",
						 info.width,
						 info.height,
						 surface_entry->interpolate ? "true" : "false",
						 smask_buf);
    }
    if (status)
	return status;
    _cairo_output_stream_write (surface->output, mime_data, mime_data_length);
    status = _cairo_pdf_surface_close_stream (surface);
    return status;
}
static cairo_int_status_t
11
_cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t              *surface,
				    cairo_surface_t	             *source,
				    cairo_pdf_source_surface_entry_t *surface_entry,
				    cairo_bool_t                      test)
{
    cairo_int_status_t status;
    const unsigned char *mime_data;
    unsigned long mime_data_length;
    cairo_image_info_t info;
    const char *colorspace;
    char smask_buf[30];
11
    cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG,
				 &mime_data, &mime_data_length);
11
    if (unlikely (source->status))
	return source->status;
11
    if (mime_data == NULL)
11
	return CAIRO_INT_STATUS_UNSUPPORTED;
    status = _cairo_image_info_get_jpeg_info (&info, mime_data, mime_data_length);
    if (unlikely (status))
	return status;
    if ((surface_entry->smask || surface_entry->stencil_mask) && info.num_components != 1)
	return CAIRO_INT_STATUS_UNSUPPORTED;
    if ((surface_entry->stencil_mask) && info.bits_per_component != 1)
	return CAIRO_INT_STATUS_UNSUPPORTED;
    switch (info.num_components) {
	case 1:
	    colorspace = "/DeviceGray";
	    break;
	case 3:
	    colorspace = "/DeviceRGB";
	    break;
	case 4:
	    colorspace = "/DeviceCMYK";
	    break;
	default:
	    return CAIRO_INT_STATUS_UNSUPPORTED;
    }
    /* At this point we know emitting jpeg will succeed. */
    if (test)
	return CAIRO_STATUS_SUCCESS;
    if (surface_entry->smask_res.id)
	snprintf(smask_buf, sizeof(smask_buf), "   /SMask %d 0 R\n", surface_entry->smask_res.id);
    else
	smask_buf[0] = 0;
    if (surface_entry->stencil_mask) {
	status = _cairo_pdf_surface_open_stream (surface,
						 &surface_entry->surface_res,
						 FALSE,
						 "   /Type /XObject\n"
						 "   /Subtype /Image\n"
						 "   /ImageMask true\n"
						 "   /Width %d\n"
						 "   /Height %d\n"
						 "   /Interpolate %s\n"
						 "   /BitsPerComponent 1\n"
						 "   /Decode [1 0]\n"
						 "   /Filter /DCTDecode\n",
						 info.width,
						 info.height,
						 surface_entry->interpolate ? "true" : "false");
    } else {
	status = _cairo_pdf_surface_open_stream (surface,
						 &surface_entry->surface_res,
						 FALSE,
						 "   /Type /XObject\n"
						 "   /Subtype /Image\n"
						 "   /Width %d\n"
						 "   /Height %d\n"
						 "   /ColorSpace %s\n"
						 "   /Interpolate %s\n"
						 "   /BitsPerComponent %d\n"
						 "%s"
						 "   /Filter /DCTDecode\n",
						 info.width,
						 info.height,
						 colorspace,
						 surface_entry->interpolate ? "true" : "false",
						 info.bits_per_component,
						 smask_buf);
    }
    if (unlikely (status))
	return status;
    _cairo_output_stream_write (surface->output, mime_data, mime_data_length);
    status = _cairo_pdf_surface_close_stream (surface);
    return status;
}
static cairo_int_status_t
11
_cairo_pdf_surface_emit_ccitt_image (cairo_pdf_surface_t              *surface,
				     cairo_surface_t	             *source,
				     cairo_pdf_source_surface_entry_t *surface_entry,
				     cairo_bool_t                      test)
{
    cairo_status_t status;
    const unsigned char *ccitt_data;
    unsigned long ccitt_data_len;
    const unsigned char *ccitt_params_string;
    unsigned long ccitt_params_string_len;
    char *params, *p, *end;
    cairo_ccitt_params_t ccitt_params;
    char buf[300];
11
    cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_CCITT_FAX,
				 &ccitt_data, &ccitt_data_len);
11
    if (unlikely (source->status))
	return source->status;
11
    if (ccitt_data == NULL)
11
	return CAIRO_INT_STATUS_UNSUPPORTED;
    cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_CCITT_FAX_PARAMS,
				 &ccitt_params_string, &ccitt_params_string_len);
    if (unlikely (source->status))
	return source->status;
    if (ccitt_params_string == NULL)
	return CAIRO_INT_STATUS_UNSUPPORTED;
    /* ensure params_string is null terminated */
    params = _cairo_strndup ((const char *)ccitt_params_string, ccitt_params_string_len);
    if (unlikely (params == NULL))
	return _cairo_surface_set_error (&surface->base, CAIRO_STATUS_NO_MEMORY);
    status = _cairo_tag_parse_ccitt_params (params, &ccitt_params);
    if (unlikely(status))
	return source->status;
    free (params);
    /* At this point we know emitting jbig2 will succeed. */
    if (test)
	return CAIRO_STATUS_SUCCESS;
    p = buf;
    *p = 0;
    end = buf + sizeof(buf) - 1;
    p += snprintf (p, end - p, "/Columns %d /Rows %d /K %d",
				 ccitt_params.columns,
				 ccitt_params.rows,
				 ccitt_params.k);
    if (ccitt_params.end_of_line)
	p += snprintf (p, end - p, " /EndOfLine true");
    if (ccitt_params.encoded_byte_align)
	p += snprintf (p, end - p, " /EncodedByteAlign true");
    if (!ccitt_params.end_of_block)
	p += snprintf (p, end - p, " /EndOfBlock false");
    if (ccitt_params.black_is_1)
	p += snprintf (p, end - p, " /BlackIs1 true");
    if (ccitt_params.damaged_rows_before_error > 0) {
	p += snprintf (p, end - p, " /DamagedRowsBeforeError %d",
		       ccitt_params.damaged_rows_before_error);
    }
    if (surface_entry->stencil_mask) {
	status = _cairo_pdf_surface_open_stream (surface,
						 &surface_entry->surface_res,
						 FALSE,
						 "   /Type /XObject\n"
						 "   /Subtype /Image\n"
						 "   /ImageMask true\n"
						 "   /Width %d\n"
						 "   /Height %d\n"
						 "   /Interpolate %s\n"
						 "   /BitsPerComponent 1\n"
						 "   /Decode [1 0]\n"
						 "   /Filter /CCITTFaxDecode\n"
						 "   /DecodeParms << %s >> ",
						 ccitt_params.columns,
						 ccitt_params.rows,
						 surface_entry->interpolate ? "true" : "false",
						 buf);
    } else {
	status = _cairo_pdf_surface_open_stream (surface,
						 &surface_entry->surface_res,
						 FALSE,
						 "   /Type /XObject\n"
						 "   /Subtype /Image\n"
						 "   /Width %d\n"
						 "   /Height %d\n"
						 "   /ColorSpace /DeviceGray\n"
						 "   /BitsPerComponent 1\n"
						 "   /Interpolate %s\n"
						 "   /Filter /CCITTFaxDecode\n"
						 "   /DecodeParms << %s >> ",
						 ccitt_params.columns,
						 ccitt_params.rows,
						 surface_entry->interpolate ? "true" : "false",
						 buf);
    }
    if (unlikely (status))
	return status;
    _cairo_output_stream_write (surface->output, ccitt_data, ccitt_data_len);
    status = _cairo_pdf_surface_close_stream (surface);
    return status;
}
static cairo_int_status_t
4
_cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t        *surface,
					   cairo_pdf_source_surface_t *pdf_source)
{
    cairo_rectangle_int_t old_surface_extents;
    cairo_bool_t old_surface_bounded;
    cairo_paginated_mode_t old_paginated_mode;
    cairo_surface_clipper_t old_clipper;
    cairo_bool_t old_in_xobject;
    cairo_box_double_t bbox;
    cairo_int_status_t status;
4
    int alpha = 0;
4
    cairo_surface_t *free_me = NULL;
    cairo_surface_t *source;
    const cairo_rectangle_int_t *extents;
    cairo_bool_t is_subsurface;
    cairo_bool_t transparency_group;
    cairo_recording_surface_t *recording;
4
    int struct_parents = -1;
4
    assert (pdf_source->type == CAIRO_PATTERN_TYPE_SURFACE);
4
    if (pdf_source->hash_entry->bounded) {
	extents = &pdf_source->hash_entry->extents;
    } else {
4
	extents = &pdf_source->hash_entry->required_extents;
    }
4
    is_subsurface = FALSE;
4
    source = pdf_source->surface;
4
    if (_cairo_surface_is_snapshot (source))
4
	free_me = source = _cairo_surface_snapshot_get_target (source);
4
    if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
	cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source;
	source = sub->target;
	extents = &sub->extents;
	is_subsurface = TRUE;
    }
4
    assert (source->type == CAIRO_SURFACE_TYPE_RECORDING);
4
    recording = (cairo_recording_surface_t *) source;
4
    old_in_xobject = surface->in_xobject;
4
    old_surface_extents = surface->surface_extents;
4
    old_surface_bounded = surface->surface_bounded;
4
    old_paginated_mode = surface->paginated_mode;
4
    old_clipper = surface->clipper;
4
    surface->surface_extents = *extents;
4
    _cairo_surface_clipper_init (&surface->clipper,
				 _cairo_pdf_surface_clipper_intersect_clip_path);
4
    _cairo_pdf_operators_reset (&surface->pdf_operators);
4
    surface->in_xobject = TRUE;
4
    surface->surface_extents = *extents;
4
    surface->surface_bounded = TRUE;
    /* Patterns are emitted after fallback images. The paginated mode
     * needs to be set to _RENDER while the recording surface is replayed
     * back to this surface.
     */
4
    surface->paginated_mode = CAIRO_PAGINATED_MODE_RENDER;
4
    _cairo_pdf_group_resources_clear (&surface->resources);
4
    _get_bbox_from_extents (extents, &bbox);
    /* We can optimize away the transparency group allowing the viewer
     * to replay the group in place when:
     *  - ca/CA when painting this groups is 1.0 (need_transp_group is FALSE),
     *  - all operators are OVER, and
     *  - the recording contains only opaque and/or clear alpha.
     */
8
    transparency_group = pdf_source->hash_entry->need_transp_group ||
12
	!(pdf_source->hash_entry->operator == CAIRO_OPERATOR_OVER &&
4
			   _cairo_recording_surface_has_only_bilevel_alpha (recording) &&
4
			   _cairo_recording_surface_has_only_op_over (recording));
4
    status = _cairo_pdf_interchange_emit_recording_surface_begin (surface,
								  pdf_source->surface,
4
								  pdf_source->hash_entry->region_id,
4
								  pdf_source->hash_entry->surface_res,
								  &struct_parents);
4
    if (unlikely (status))
	goto err;
4
    status = _cairo_pdf_surface_open_content_stream (surface,
						     &bbox,
4
						     &pdf_source->hash_entry->surface_res,
						     TRUE,
						     transparency_group,
						     struct_parents);
4
    if (unlikely (status))
	goto err;
    /* Reset gstate */
4
    surface->reset_gs_required = TRUE;
4
    if (source->content == CAIRO_CONTENT_COLOR) {
	status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha);
	if (unlikely (status))
	    goto err;
	_cairo_output_stream_printf (surface->output,
				     "q /a%d gs 0 0 0 rg %d %d %d %d re f Q\n",
				     alpha,
				     extents->x,
				     extents->y,
				     extents->width,
				     extents->height);
    }
4
    status = _cairo_recording_surface_replay_region (source,
						     pdf_source->region_id,
						     is_subsurface ? extents : NULL,
						     &surface->base,
						     CAIRO_RECORDING_REGION_NATIVE);
4
    assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
4
    if (unlikely (status))
	goto err;
4
    status = _cairo_pdf_surface_close_content_stream (surface, TRUE);
4
    _cairo_surface_clipper_reset (&surface->clipper);
4
    surface->clipper = old_clipper;
4
    _cairo_pdf_operators_reset (&surface->pdf_operators);
4
    surface->in_xobject = old_in_xobject;
4
    surface->paginated_mode = old_paginated_mode;
4
    surface->surface_extents = old_surface_extents;
4
    surface->surface_bounded = old_surface_bounded;
4
    surface->reset_gs_required = FALSE;
4
    if (pdf_source->hash_entry->region_id > 0)
	status = _cairo_pdf_interchange_emit_recording_surface_end (surface, pdf_source->surface);
4
err:
4
    cairo_surface_destroy (free_me);
4
    return status;
}
/**
 * _cairo_pdf_surface_emit_surface:
 * @surface: [in] the pdf surface
 * @source: [in] #cairo_pdf_source_surface_t containing the surface to write
 * @test: [in] if true, test what type of surface will be emitted.
 * @is_image: [out] if @test is true, returns TRUE if the surface will be emitted
 *  as an Image XObject.
 *
 * If @test is FALSE, emit @src_surface as an XObject.
 * If @test is TRUE, don't emit anything. Set @is_image based on the output that would be emitted.
 **/
static cairo_int_status_t
11
_cairo_pdf_surface_emit_surface (cairo_pdf_surface_t        *surface,
				 cairo_pdf_source_surface_t *source,
				 cairo_bool_t                test,
				 cairo_bool_t               *is_image)
{
    cairo_image_surface_t *image;
    void *image_extra;
    cairo_int_status_t status;
    /* Try all the supported mime types and recording type, falling through
     * each option if unsupported */
11
    if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
11
	status = _cairo_pdf_surface_emit_jbig2_image (surface,
						      source->surface,
						      source->hash_entry,
						      test);
11
	if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
	    *is_image = TRUE;
	    return status;
	}
11
	status = _cairo_pdf_surface_emit_jpx_image (surface,
						    source->surface,
						    source->hash_entry,
						    test);
11
	if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
	    *is_image = TRUE;
	    return status;
	}
11
	status = _cairo_pdf_surface_emit_jpeg_image (surface,
						     source->surface,
						     source->hash_entry,
						     test);
11
	if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
	    *is_image = TRUE;
	    return status;
	}
11
	status = _cairo_pdf_surface_emit_ccitt_image (surface,
						      source->surface,
						      source->hash_entry,
						      test);
11
	if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
	    *is_image = TRUE;
	    return status;
	}
11
	if (source->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
4
	    if (test) {
		*is_image = FALSE;
		return CAIRO_INT_STATUS_SUCCESS;
	    } else {
4
		return _cairo_pdf_surface_emit_recording_surface (surface, source);
	    }
	}
    }
    /* The only option left is to emit as an image surface */
7
    if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
7
	status = _cairo_surface_acquire_source_image (source->surface, &image, &image_extra);
    } else {
	status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface,
								       source->raster_pattern,
								       &image,
								       &image_extra);
    }
7
    if (unlikely (status))
	return status;
7
    if (test) {
	*is_image = TRUE;
    } else {
7
	status = _cairo_pdf_surface_emit_image (surface,
						image,
						source->hash_entry);
    }
7
    if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
7
	_cairo_surface_release_source_image (source->surface, image, image_extra);
    } else {
	_cairo_pdf_surface_release_source_image_from_pattern (surface,
							      source->raster_pattern,
							      image,
							      image_extra);
    }
7
    return status;
}
static cairo_int_status_t
_cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t	*surface,
					 cairo_pdf_pattern_t	*pdf_pattern)
{
    cairo_pattern_t *pattern = pdf_pattern->pattern;
    cairo_int_status_t status;
    cairo_matrix_t cairo_p2d, pdf_p2d;
    cairo_extend_t extend = cairo_pattern_get_extend (pattern);
    double xstep, ystep;
    cairo_rectangle_int_t pattern_extents;
    double x_offset;
    double y_offset;
    char draw_surface[50];
    char draw_surface2[200];
    cairo_box_double_t bbox;
    cairo_matrix_t mat;
    cairo_pdf_source_surface_entry_t *pdf_source;
    cairo_rectangle_int_t op_extents;
    assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE);
    if (pattern->extend == CAIRO_EXTEND_PAD) {
	status = _cairo_pdf_surface_add_padded_image_surface (surface,
							      pattern,
							      &pdf_pattern->extents,
							      &pdf_source,
							      &x_offset,
							      &y_offset,
							      &op_extents);
    } else {
	status = _cairo_pdf_surface_add_source_surface (surface,
							NULL,
							pattern,
							pdf_pattern->region_id,
							pdf_pattern->operator,
							pattern->filter,
							FALSE, /* stencil mask */
							FALSE, /* smask */
							FALSE, /* need_transp_group */
							&pdf_pattern->extents,
							NULL, /* smask_res */
							&pdf_source,
							&x_offset,
							&y_offset,
							&op_extents);
    }
    if (unlikely (status))
	return status;
    pattern_extents = pdf_source->extents;
    if (!pdf_source->bounded)
    {
	extend = CAIRO_EXTEND_NONE;
	_cairo_rectangle_intersect (&pattern_extents, &op_extents);
    }
    switch (extend) {
    case CAIRO_EXTEND_PAD:
    case CAIRO_EXTEND_NONE:
    {
	/* In PS/PDF, (as far as I can tell), all patterns are
	 * repeating. So we support cairo's EXTEND_NONE semantics
	 * by setting the repeat step size to a size large enough
	 * to guarantee that no more than a single occurrence will
	 * be visible.
	 *
	 * First, map the surface extents into pattern space (since
	 * xstep and ystep are in pattern space).  Then use an upper
	 * bound on the length of the diagonal of the pattern image
	 * and the surface as repeat size.  This guarantees to never
	 * repeat visibly.
	 */
	double x1 = 0.0, y1 = 0.0;
	double x2 = surface->surface_extents.width;
	double y2 = surface->surface_extents.height;
	_cairo_matrix_transform_bounding_box (&pattern->matrix,
					      &x1, &y1, &x2, &y2,
					      NULL);
	/* Rather than computing precise bounds of the union, just
	 * add the surface extents unconditionally. We only
	 * required an answer that's large enough, we don't really
	 * care if it's not as tight as possible.*/
	xstep = ystep = ceil ((x2 - x1) + (y2 - y1) +
			      pattern_extents.width + pattern_extents.height);
    }
    break;
    case CAIRO_EXTEND_REPEAT:
	xstep = pattern_extents.width;
	ystep = pattern_extents.height;
	break;
    case CAIRO_EXTEND_REFLECT:
	pattern_extents.width *= 2;
	pattern_extents.height *= 2;
	xstep = pattern_extents.width;
	ystep = pattern_extents.height;
	break;
    /* All the rest (if any) should have been analyzed away, so this
     * case should be unreachable. */
    default:
	ASSERT_NOT_REACHED;
	xstep = 0;
	ystep = 0;
    }
    /* At this point, (that is, within the surface backend interface),
     * the pattern's matrix maps from cairo's device space to cairo's
     * pattern space, (both with their origin at the upper-left, and
     * cairo's pattern space of size width,height).
     *
     * Then, we must emit a PDF pattern object that maps from its own
     * pattern space, (which has a size that we establish in the BBox
     * dictionary entry), to the PDF page's *initial* space, (which
     * does not benefit from the Y-axis flipping matrix that we emit
     * on each page). So the PDF patterns matrix maps from a
     * (width,height) pattern space to a device space with the origin
     * in the lower-left corner.
     *
     * So to handle all of that, we start with an identity matrix for
     * the PDF pattern to device matrix. We translate it up by the
     * image height then flip it in the Y direction, (moving us from
     * the PDF origin to cairo's origin). We then multiply in the
     * inverse of the cairo pattern matrix, (since it maps from device
     * to pattern, while we're setting up pattern to device). Finally,
     * we translate back down by the image height and flip again to
     * end up at the lower-left origin that PDF expects.
     *
     * Additionally, within the stream that paints the pattern itself,
     * we are using a PDF image object that has a size of (1,1) so we
     * have to scale it up by the image width and height to fill our
     * pattern cell.
     */
    cairo_p2d = pattern->matrix;
    status = cairo_matrix_invert (&cairo_p2d);
    /* cairo_pattern_set_matrix ensures the matrix is invertible */
    assert (status == CAIRO_INT_STATUS_SUCCESS);
    if (pdf_pattern->inverted_y_axis)
	cairo_matrix_init (&mat, 1, 0, 0, 1, 0, 0);
    else
	cairo_matrix_init (&mat, 1, 0, 0, -1, 0, surface->height);
    cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &mat);
    cairo_matrix_translate (&pdf_p2d, x_offset, y_offset);
    if (pdf_source->emit_image) {
	cairo_matrix_translate (&pdf_p2d, 0.0, pdf_source->extents.height);
	cairo_matrix_scale (&pdf_p2d, 1.0, -1.0);
    }
    _get_bbox_from_extents (&pattern_extents, &bbox);
    _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res);
    status = _cairo_pdf_surface_open_stream (surface,
				             &pdf_pattern->pattern_res,
					     FALSE,
					     "   /PatternType 1\n"
					     "   /BBox [ %f %f %f %f ]\n"
					     "   /XStep %f\n"
					     "   /YStep %f\n"
					     "   /TilingType 1\n"
					     "   /PaintType 1\n"
					     "   /Matrix [ %f %f %f %f %f %f ]\n"
					     "   /Resources << /XObject << /x%d %d 0 R >> >>\n",
					     bbox.p1.x, bbox.p1.y, bbox.p2.x, bbox.p2.y,
					     xstep, ystep,
					     pdf_p2d.xx, pdf_p2d.yx,
					     pdf_p2d.xy, pdf_p2d.yy,
					     pdf_p2d.x0, pdf_p2d.y0,
					     pdf_source->surface_res.id,
					     pdf_source->surface_res.id);
    if (unlikely (status))
	return status;
    if (pdf_source->emit_image) {
	snprintf(draw_surface,
		 sizeof (draw_surface),
		 "q %d 0 0 %d 0 0 cm /x%d Do Q",
		 pdf_source->extents.width,
		 pdf_source->extents.height,
		 pdf_source->surface_res.id);
    } else {
	snprintf(draw_surface,
		 sizeof (draw_surface),
		 "/x%d Do",
		 pdf_source->surface_res.id);
    }
    if (extend == CAIRO_EXTEND_REFLECT) {
	cairo_rectangle_int_t p_extents = pdf_source->extents;
	snprintf(draw_surface2,
		 sizeof (draw_surface2),
		 "%d %d %d %d re W n %s",
		 p_extents.x, p_extents.y,
		 p_extents.width, p_extents.height,
		 draw_surface);
	_cairo_output_stream_printf (surface->output, "q %s Q\n", draw_surface2);
	cairo_matrix_init_translate (&mat, p_extents.x, p_extents.y);
	cairo_matrix_scale (&mat, -1, 1);
	cairo_matrix_translate (&mat, -2*p_extents.width, 0);
	cairo_matrix_translate (&mat, -p_extents.x, -p_extents.y);
	_cairo_output_stream_printf (surface->output, "q ");
	_cairo_output_stream_print_matrix (surface->output, &mat);
	_cairo_output_stream_printf (surface->output, " cm %s Q\n", draw_surface2);
	cairo_matrix_init_translate (&mat, p_extents.x, p_extents.y);
	cairo_matrix_scale (&mat, 1, -1);
	cairo_matrix_translate (&mat, 0, -2*p_extents.height);
	cairo_matrix_translate (&mat, -p_extents.x, -p_extents.y);
	_cairo_output_stream_printf (surface->output, "q ");
	_cairo_output_stream_print_matrix (surface->output, &mat);
	_cairo_output_stream_printf (surface->output, " cm %s Q\n", draw_surface2);
	cairo_matrix_init_translate (&mat, p_extents.x, p_extents.y);
	cairo_matrix_scale (&mat, -1, -1);
	cairo_matrix_translate (&mat, -2*p_extents.width, -2*p_extents.height);
	cairo_matrix_translate (&mat, -p_extents.x, -p_extents.y);
	_cairo_output_stream_printf (surface->output, "q ");
	_cairo_output_stream_print_matrix (surface->output, &mat);
	_cairo_output_stream_printf (surface->output, " cm %s Q\n", draw_surface2);
    } else {
	_cairo_output_stream_printf (surface->output,
				     " %s \n",
				     draw_surface);
    }
    status = _cairo_pdf_surface_close_stream (surface);
    if (unlikely (status))
	return status;
    return _cairo_output_stream_get_status (surface->output);
}
typedef struct _cairo_pdf_color_stop {
    double offset;
    double color[4];
    cairo_pdf_resource_t resource;
} cairo_pdf_color_stop_t;
static cairo_int_status_t
cairo_pdf_surface_emit_rgb_linear_function (cairo_pdf_surface_t    *surface,
                                            cairo_pdf_color_stop_t *stop1,
                                            cairo_pdf_color_stop_t *stop2,
                                            cairo_pdf_resource_t   *function)
{
    int num_elems, i;
    cairo_pdf_rgb_linear_function_t elem;
    cairo_pdf_resource_t res;
    cairo_int_status_t status;
    num_elems = _cairo_array_num_elements (&surface->rgb_linear_functions);
    for (i = 0; i < num_elems; i++) {
	_cairo_array_copy_element (&surface->rgb_linear_functions, i, &elem);
        if (memcmp (&elem.color1[0], &stop1->color[0], sizeof (double)*3) != 0)
            continue;
        if (memcmp (&elem.color2[0], &stop2->color[0], sizeof (double)*3) != 0)
            continue;
        *function =  elem.resource;
        return CAIRO_STATUS_SUCCESS;
    }
    res = _cairo_pdf_surface_new_object (surface);
    if (res.id == 0)
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    _cairo_output_stream_printf (surface->output,
				 "%d 0 obj\n"
				 "<< /FunctionType 2\n"
				 "   /Domain [ 0 1 ]\n"
				 "   /C0 [ %f %f %f ]\n"
				 "   /C1 [ %f %f %f ]\n"
				 "   /N 1\n"
				 ">>\n"
				 "endobj\n",
				 res.id,
                                 stop1->color[0],
                                 stop1->color[1],
                                 stop1->color[2],
                                 stop2->color[0],
                                 stop2->color[1],
                                 stop2->color[2]);
    elem.resource = res;
    memcpy (&elem.color1[0], &stop1->color[0], sizeof (double)*3);
    memcpy (&elem.color2[0], &stop2->color[0], sizeof (double)*3);
    status = _cairo_array_append (&surface->rgb_linear_functions, &elem);
    *function = res;
    return status;
}
static cairo_int_status_t
cairo_pdf_surface_emit_alpha_linear_function (cairo_pdf_surface_t    *surface,
                                              cairo_pdf_color_stop_t *stop1,
                                              cairo_pdf_color_stop_t *stop2,
                                              cairo_pdf_resource_t   *function)
{
    int num_elems, i;
    cairo_pdf_alpha_linear_function_t elem;
    cairo_pdf_resource_t res;
    cairo_int_status_t status;
    num_elems = _cairo_array_num_elements (&surface->alpha_linear_functions);
    for (i = 0; i < num_elems; i++) {
	_cairo_array_copy_element (&surface->alpha_linear_functions, i, &elem);
        if (elem.alpha1 != stop1->color[3])
            continue;
        if (elem.alpha2 != stop2->color[3])
            continue;
        *function =  elem.resource;
        return CAIRO_STATUS_SUCCESS;
    }
    res = _cairo_pdf_surface_new_object (surface);
    if (res.id == 0)
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    _cairo_output_stream_printf (surface->output,
				 "%d 0 obj\n"
				 "<< /FunctionType 2\n"
				 "   /Domain [ 0 1 ]\n"
				 "   /C0 [ %f ]\n"
				 "   /C1 [ %f ]\n"
				 "   /N 1\n"
				 ">>\n"
				 "endobj\n",
				 res.id,
                                 stop1->color[3],
                                 stop2->color[3]);
    elem.resource = res;
    elem.alpha1 = stop1->color[3];
    elem.alpha2 = stop2->color[3];
    status = _cairo_array_append (&surface->alpha_linear_functions, &elem);
    *function = res;
    return status;
}
static cairo_int_status_t
_cairo_pdf_surface_emit_stitched_colorgradient (cairo_pdf_surface_t    *surface,
                                                unsigned int	        n_stops,
                                                cairo_pdf_color_stop_t *stops,
                                                cairo_bool_t	        is_alpha,
                                                cairo_pdf_resource_t   *function)
{
    cairo_pdf_resource_t res;
    unsigned int i;
    cairo_int_status_t status;
    /* emit linear gradients between pairs of subsequent stops... */
    for (i = 0; i < n_stops-1; i++) {
        if (is_alpha) {
            status = cairo_pdf_surface_emit_alpha_linear_function (surface,
                                                                   &stops[i],
                                                                   &stops[i+1],
                                                                   &stops[i].resource);
            if (unlikely (status))
                return status;
        } else {
            status = cairo_pdf_surface_emit_rgb_linear_function (surface,
                                                                 &stops[i],
                                                                 &stops[i+1],
                                                                 &stops[i].resource);
            if (unlikely (status))
                return status;
        }
    }
    /* ... and stitch them together */
    res = _cairo_pdf_surface_new_object (surface);
    if (res.id == 0)
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    _cairo_output_stream_printf (surface->output,
				 "%d 0 obj\n"
				 "<< /FunctionType 3\n"
				 "   /Domain [ %f %f ]\n",
				 res.id,
                                 stops[0].offset,
                                 stops[n_stops - 1].offset);
    _cairo_output_stream_printf (surface->output,
				 "   /Functions [ ");
    for (i = 0; i < n_stops-1; i++)
        _cairo_output_stream_printf (surface->output,
                                     "%d 0 R ", stops[i].resource.id);
    _cairo_output_stream_printf (surface->output,
				 "]\n");
    _cairo_output_stream_printf (surface->output,
				 "   /Bounds [ ");
    for (i = 1; i < n_stops-1; i++)
        _cairo_output_stream_printf (surface->output,
				     "%f ", stops[i].offset);
    _cairo_output_stream_printf (surface->output,
				 "]\n");
    _cairo_output_stream_printf (surface->output,
				 "   /Encode [ ");
    for (i = 1; i < n_stops; i++)
        _cairo_output_stream_printf (surface->output,
				     "0 1 ");
    _cairo_output_stream_printf (surface->output,
				 "]\n");
    _cairo_output_stream_printf (surface->output,
				 ">>\n"
				 "endobj\n");
    *function = res;
    return _cairo_output_stream_get_status (surface->output);
}
static void
calc_gradient_color (cairo_pdf_color_stop_t *new_stop,
		     cairo_pdf_color_stop_t *stop1,
		     cairo_pdf_color_stop_t *stop2)
{
    int i;
    double offset = stop1->offset / (stop1->offset + 1.0 - stop2->offset);
    for (i = 0; i < 4; i++)
	new_stop->color[i] = stop1->color[i] + offset*(stop2->color[i] - stop1->color[i]);
}
#define COLOR_STOP_EPSILON 1e-6
static cairo_int_status_t
_cairo_pdf_surface_emit_pattern_stops (cairo_pdf_surface_t      *surface,
                                       cairo_gradient_pattern_t *pattern,
                                       cairo_pdf_resource_t     *color_function,
                                       cairo_pdf_resource_t     *alpha_function)
{
    cairo_pdf_color_stop_t *allstops, *stops;
    unsigned int n_stops;
    unsigned int i;
    cairo_bool_t emit_alpha = FALSE;
    cairo_int_status_t status;
    color_function->id = 0;
    alpha_function->id = 0;
    allstops = _cairo_malloc_ab ((pattern->n_stops + 2), sizeof (cairo_pdf_color_stop_t));
    if (unlikely (allstops == NULL))
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    stops = &allstops[1];
    n_stops = pattern->n_stops;
    for (i = 0; i < n_stops; i++) {
	stops[i].color[0] = pattern->stops[i].color.red;
	stops[i].color[1] = pattern->stops[i].color.green;
	stops[i].color[2] = pattern->stops[i].color.blue;
	stops[i].color[3] = pattern->stops[i].color.alpha;
        if (!CAIRO_ALPHA_IS_OPAQUE (stops[i].color[3]))
            emit_alpha = TRUE;
	stops[i].offset = pattern->stops[i].offset;
    }
    if (pattern->base.extend == CAIRO_EXTEND_REPEAT ||
	pattern->base.extend == CAIRO_EXTEND_REFLECT) {
	if (stops[0].offset > COLOR_STOP_EPSILON) {
	    if (pattern->base.extend == CAIRO_EXTEND_REFLECT)
		memcpy (allstops, stops, sizeof (cairo_pdf_color_stop_t));
	    else
		calc_gradient_color (&allstops[0], &stops[0], &stops[n_stops-1]);
	    stops = allstops;
	    n_stops++;
	}
	stops[0].offset = 0.0;
	if (stops[n_stops-1].offset < 1.0 - COLOR_STOP_EPSILON) {
	    if (pattern->base.extend == CAIRO_EXTEND_REFLECT) {
		memcpy (&stops[n_stops],
			&stops[n_stops - 1],
			sizeof (cairo_pdf_color_stop_t));
	    } else {
		calc_gradient_color (&stops[n_stops], &stops[0], &stops[n_stops-1]);
	    }
	    n_stops++;
	}
	stops[n_stops-1].offset = 1.0;
    }
    if (stops[0].offset == stops[n_stops - 1].offset) {
	/*
	 * The first and the last stops have the same offset, but we
	 * don't want a function with an empty domain, because that
	 * would provoke underdefined behaviour from rasterisers.
	 * This can only happen with EXTEND_PAD, because EXTEND_NONE
	 * is optimised into a clear pattern in cairo-gstate, and
	 * REFLECT/REPEAT are always transformed to have the first
	 * stop at t=0 and the last stop at t=1.  Thus we want a step
	 * function going from the first color to the last one.
	 *
	 * This can be accomplished by stitching three functions:
	 *  - a constant first color function,
	 *  - a step from the first color to the last color (with empty domain)
	 *  - a constant last color function
	 */
	cairo_pdf_color_stop_t pad_stops[4];
	assert (pattern->base.extend == CAIRO_EXTEND_PAD);
	pad_stops[0] = pad_stops[1] = stops[0];
	pad_stops[2] = pad_stops[3] = stops[n_stops - 1];
	pad_stops[0].offset = 0;
	pad_stops[3].offset = 1;
        status = _cairo_pdf_surface_emit_stitched_colorgradient (surface,
                                                                 4,
                                                                 pad_stops,
                                                                 FALSE,
                                                                 color_function);
        if (unlikely (status))
            goto BAIL;
        if (emit_alpha) {
            status = _cairo_pdf_surface_emit_stitched_colorgradient (surface,
                                                                     4,
                                                                     pad_stops,
                                                                     TRUE,
                                                                     alpha_function);
            if (unlikely (status))
                goto BAIL;
        }
    } else if (n_stops == 2) {
        /* no need for stitched function */
        status = cairo_pdf_surface_emit_rgb_linear_function (surface,
                                                             &stops[0],
                                                             &stops[n_stops - 1],
                                                             color_function);
        if (unlikely (status))
            goto BAIL;
        if (emit_alpha) {
            status = cairo_pdf_surface_emit_alpha_linear_function (surface,
                                                                   &stops[0],
                                                                   &stops[n_stops - 1],
                                                                   alpha_function);
            if (unlikely (status))
                goto BAIL;
        }
    } else {
        /* multiple stops: stitch. XXX possible optimization: regularly spaced
         * stops do not require stitching. XXX */
        status = _cairo_pdf_surface_emit_stitched_colorgradient (surface,
                                                                 n_stops,
                                                                 stops,
                                                                 FALSE,
                                                                 color_function);
        if (unlikely (status))
            goto BAIL;
        if (emit_alpha) {
            status = _cairo_pdf_surface_emit_stitched_colorgradient (surface,
                                                                     n_stops,
                                                                     stops,
                                                                     TRUE,
                                                                     alpha_function);
            if (unlikely (status))
                goto BAIL;
        }
    }
BAIL:
    free (allstops);
    return status;
}
static cairo_int_status_t
_cairo_pdf_surface_emit_repeating_function (cairo_pdf_surface_t      *surface,
					    cairo_gradient_pattern_t *pattern,
					    cairo_pdf_resource_t     *function,
					    int                       begin,
					    int                       end)
{
    cairo_pdf_resource_t res;
    int i;
    res = _cairo_pdf_surface_new_object (surface);
    if (res.id == 0)
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    _cairo_output_stream_printf (surface->output,
				 "%d 0 obj\n"
				 "<< /FunctionType 3\n"
				 "   /Domain [ %d %d ]\n",
				 res.id,
                                 begin,
                                 end);
    _cairo_output_stream_printf (surface->output,
				 "   /Functions [ ");
    for (i = begin; i < end; i++)
        _cairo_output_stream_printf (surface->output,
                                     "%d 0 R ", function->id);
    _cairo_output_stream_printf (surface->output,
				 "]\n");
    _cairo_output_stream_printf (surface->output,
				 "   /Bounds [ ");
    for (i = begin + 1; i < end; i++)
        _cairo_output_stream_printf (surface->output,
				     "%d ", i);
    _cairo_output_stream_printf (surface->output,
				 "]\n");
    _cairo_output_stream_printf (surface->output,
				 "   /Encode [ ");
    for (i = begin; i < end; i++) {
	if ((i % 2) && pattern->base.extend == CAIRO_EXTEND_REFLECT) {
	    _cairo_output_stream_printf (surface->output,
					 "1 0 ");
	} else {
	    _cairo_output_stream_printf (surface->output,
					 "0 1 ");
	}
    }
    _cairo_output_stream_printf (surface->output,
				 "]\n");
    _cairo_output_stream_printf (surface->output,
				 ">>\n"
				 "endobj\n");
    *function = res;
    return _cairo_output_stream_get_status (surface->output);
}
static cairo_int_status_t
cairo_pdf_surface_emit_transparency_group (cairo_pdf_surface_t  *surface,
					   cairo_pdf_pattern_t  *pdf_pattern,
					   cairo_pdf_resource_t  gstate_resource,
					   cairo_pdf_resource_t  gradient_mask)
{
    cairo_pdf_resource_t smask_resource;
    cairo_int_status_t status;
    char buf[100];
    double x1, y1, x2, y2;
    if (pdf_pattern->is_shading) {
	snprintf(buf, sizeof(buf),
		 "         /Shading\n"
		 "            << /sh%d %d 0 R >>\n",
		 gradient_mask.id,
		 gradient_mask.id);
    } else {
	snprintf(buf, sizeof(buf),
		 "         /Pattern\n"
		 "            << /p%d %d 0 R >>\n",
		 gradient_mask.id,
		 gradient_mask.id);
    }
    if (pdf_pattern->is_shading) {
	cairo_box_t box;
	/* When emitting a shading operator we are in cairo pattern
	 * coordinates. _cairo_pdf_surface_paint_gradient has set the
	 * ctm to the pattern matrix (including the conversion from
	 * pdf to cairo coordinates) */
	_cairo_box_from_rectangle (&box, &pdf_pattern->extents);
	_cairo_box_to_doubles (&box, &x1, &y1, &x2, &y2);
	_cairo_matrix_transform_bounding_box (&pdf_pattern->pattern->matrix, &x1, &y1, &x2, &y2, NULL);
    } else {
	cairo_box_double_t box;
	/* When emitting a shading pattern we are in pdf page
	 * coordinates. The color and alpha shading patterns painted
	 * in the XObject below contain the cairo pattern to pdf page
	 * matrix in the /Matrix entry of the pattern. */
	_get_bbox_from_extents (&pdf_pattern->extents, &box);
	x1 = box.p1.x;
	y1 = box.p1.y;
	x2 = box.p2.x;
	y2 = box.p2.y;
    }
    status = _cairo_pdf_surface_open_stream (surface,
					     NULL,
					     surface->compress_streams,
					     "   /Type /XObject\n"
					     "   /Subtype /Form\n"
					     "   /FormType 1\n"
					     "   /BBox [ %f %f %f %f ]\n"
					     "   /Resources\n"
					     "      << /ExtGState\n"
					     "            << /a0 << /ca 1 /CA 1 >>"
					     "      >>\n"
					     "%s"
					     "      >>\n"
					     "   /Group\n"
					     "      << /Type /Group\n"
					     "         /S /Transparency\n"
					     "         /I true\n"
					     "         /CS /DeviceGray\n"
					     "      >>\n",
					     x1,y1,x2,y2,
					     buf);
    if (unlikely (status))
	return status;
    if (pdf_pattern->is_shading) {
	_cairo_output_stream_printf (surface->output,
				     "/a0 gs /sh%d sh\n",
				     gradient_mask.id);
    } else {
	_cairo_output_stream_printf (surface->output,
				     "q\n"
				     "/a0 gs\n"
				     "/Pattern cs /p%d scn\n"
				     "0 0 %f %f re\n"
				     "f\n"
				     "Q\n",
				     gradient_mask.id,
				     surface->width,
				     surface->height);
    }
    status = _cairo_pdf_surface_close_stream (surface);
    if (unlikely (status))
	return status;
    smask_resource = _cairo_pdf_surface_new_object (surface);
    if (smask_resource.id == 0)
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    _cairo_output_stream_printf (surface->output,
                                 "%d 0 obj\n"
                                 "<< /Type /Mask\n"
                                 "   /S /Luminosity\n"
                                 "   /G %d 0 R\n"
                                 ">>\n"
                                 "endobj\n",
                                 smask_resource.id,
                                 surface->pdf_stream.self.id);
    /* Create GState which uses the transparency group as an SMask. */
    _cairo_pdf_surface_update_object (surface, gstate_resource);
    _cairo_output_stream_printf (surface->output,
                                 "%d 0 obj\n"
                                 "<< /Type /ExtGState\n"
                                 "   /SMask %d 0 R\n"
                                 "   /ca 1\n"
                                 "   /CA 1\n"
                                 "   /AIS false\n"
                                 ">>\n"
                                 "endobj\n",
                                 gstate_resource.id,
                                 smask_resource.id);
    return _cairo_output_stream_get_status (surface->output);
}
static void
_cairo_pdf_surface_output_gradient (cairo_pdf_surface_t        *surface,
				    const cairo_pdf_pattern_t  *pdf_pattern,
				    cairo_pdf_resource_t        pattern_resource,
				    const cairo_matrix_t       *pat_to_pdf,
				    const cairo_circle_double_t*start,
				    const cairo_circle_double_t*end,
				    const double               *domain,
				    const char                 *colorspace,
				    cairo_pdf_resource_t        color_function)
{
    _cairo_output_stream_printf (surface->output,
                                 "%d 0 obj\n",
				 pattern_resource.id);
    if (!pdf_pattern->is_shading) {
	_cairo_output_stream_printf (surface->output,
				     "<< /Type /Pattern\n"
				     "   /PatternType 2\n"
				     "   /Matrix [ ");
	_cairo_output_stream_print_matrix (surface->output, pat_to_pdf);
	_cairo_output_stream_printf (surface->output,
				     " ]\n"
				     "   /Shading\n");
    }
    if (pdf_pattern->pattern->type == CAIRO_PATTERN_TYPE_LINEAR) {
	_cairo_output_stream_printf (surface->output,
				     "      << /ShadingType 2\n"
				     "         /ColorSpace %s\n"
				     "         /Coords [ %f %f %f %f ]\n",
				     colorspace,
				     start->center.x, start->center.y,
				     end->center.x, end->center.y);
    } else {
	_cairo_output_stream_printf (surface->output,
				     "      << /ShadingType 3\n"
				     "         /ColorSpace %s\n"
				     "         /Coords [ %f %f %f %f %f %f ]\n",
				     colorspace,
				     start->center.x, start->center.y,
				     MAX (start->radius, 0),
				     end->center.x, end->center.y,
				     MAX (end->radius, 0));
    }
    _cairo_output_stream_printf (surface->output,
				 "         /Domain [ %f %f ]\n",
				 domain[0], domain[1]);
    if (pdf_pattern->pattern->extend != CAIRO_EXTEND_NONE) {
        _cairo_output_stream_printf (surface->output,
                                     "         /Extend [ true true ]\n");
    } else {
        _cairo_output_stream_printf (surface->output,
                                     "         /Extend [ false false ]\n");
    }
    _cairo_output_stream_printf (surface->output,
				 "         /Function %d 0 R\n"
                                 "      >>\n",
				 color_function.id);
    if (!pdf_pattern->is_shading) {
	_cairo_output_stream_printf (surface->output,
				     ">>\n");
    }
    _cairo_output_stream_printf (surface->output,
				     "endobj\n");
}
static cairo_int_status_t
_cairo_pdf_surface_emit_gradient (cairo_pdf_surface_t    *surface,
				  cairo_pdf_pattern_t    *pdf_pattern)
{
    cairo_gradient_pattern_t *pattern = (cairo_gradient_pattern_t *) pdf_pattern->pattern;
    cairo_pdf_resource_t color_function, alpha_function;
    cairo_matrix_t pat_to_pdf;
    cairo_circle_double_t start, end;
    double domain[2];
    cairo_int_status_t status;
    cairo_matrix_t mat;
    assert (pattern->n_stops != 0);
    status = _cairo_pdf_surface_emit_pattern_stops (surface,
                                                    pattern,
                                                    &color_function,
                                                    &alpha_function);
    if (unlikely (status))
	return status;
    pat_to_pdf = pattern->base.matrix;
    status = cairo_matrix_invert (&pat_to_pdf);
    /* cairo_pattern_set_matrix ensures the matrix is invertible */
    assert (status == CAIRO_INT_STATUS_SUCCESS);
    if (pdf_pattern->inverted_y_axis)
       cairo_matrix_init (&mat, 1, 0, 0, 1, 0, 0);
    else
       cairo_matrix_init (&mat, 1, 0, 0, -1, 0, surface->height);
    cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &mat);
    if (pattern->base.extend == CAIRO_EXTEND_REPEAT ||
	pattern->base.extend == CAIRO_EXTEND_REFLECT)
    {
	double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
	double x_scale, y_scale, tolerance;
	/* TODO: use tighter extents */
	bounds_x1 = 0;
	bounds_y1 = 0;
	bounds_x2 = surface->width;
	bounds_y2 = surface->height;
	_cairo_matrix_transform_bounding_box (&pattern->base.matrix,
					      &bounds_x1, &bounds_y1,
					      &bounds_x2, &bounds_y2,
					      NULL);
	x_scale = surface->base.x_resolution / surface->base.x_fallback_resolution;
	y_scale = surface->base.y_resolution / surface->base.y_fallback_resolution;
	tolerance = fabs (_cairo_matrix_compute_determinant (&pattern->base.matrix));
	tolerance /= _cairo_matrix_transformed_circle_major_axis (&pattern->base.matrix, 1);
	tolerance *= MIN (x_scale, y_scale);
	_cairo_gradient_pattern_box_to_parameter (pattern,
						  bounds_x1, bounds_y1,
						  bounds_x2, bounds_y2,
						  tolerance, domain);
    } else if (pattern->stops[0].offset == pattern->stops[pattern->n_stops - 1].offset) {
	/*
	 * If the first and the last stop offset are the same, then
	 * the color function is a step function.
	 * _cairo_ps_surface_emit_pattern_stops emits it as a stitched
	 * function no matter how many stops the pattern has.  The
	 * domain of the stitched function will be [0 1] in this case.
	 *
	 * This is done to avoid emitting degenerate gradients for
	 * EXTEND_PAD patterns having a step color function.
	 */
	domain[0] = 0.0;
	domain[1] = 1.0;
	assert (pattern->base.extend == CAIRO_EXTEND_PAD);
    } else {
	domain[0] = pattern->stops[0].offset;
	domain[1] = pattern->stops[pattern->n_stops - 1].offset;
    }
    /* PDF requires the first and last stop to be the same as the
     * extreme coordinates. For repeating patterns this moves the
     * extreme coordinates out to the begin/end of the repeating
     * function. For non repeating patterns this may move the extreme
     * coordinates in if there are not stops at offset 0 and 1. */
    _cairo_gradient_pattern_interpolate (pattern, domain[0], &start);
    _cairo_gradient_pattern_interpolate (pattern, domain[1], &end);
    if (pattern->base.extend == CAIRO_EXTEND_REPEAT ||
	pattern->base.extend == CAIRO_EXTEND_REFLECT)
    {
	int repeat_begin, repeat_end;
	repeat_begin = floor (domain[0]);
	repeat_end = ceil (domain[1]);
	status = _cairo_pdf_surface_emit_repeating_function (surface,
							     pattern,
							     &color_function,
							     repeat_begin,
							     repeat_end);
	if (unlikely (status))
	    return status;
	if (alpha_function.id != 0) {
	    status = _cairo_pdf_surface_emit_repeating_function (surface,
								 pattern,
								 &alpha_function,
								 repeat_begin,
								 repeat_end);
	    if (unlikely (status))
		return status;
	}
    } else if (pattern->n_stops <= 2) {
	/* For EXTEND_NONE and EXTEND_PAD if there are only two stops a
	 * Type 2 function is used by itself without a stitching
	 * function. Type 2 functions always have the domain [0 1] */
	domain[0] = 0.0;
	domain[1] = 1.0;
    }
    _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res);
    _cairo_pdf_surface_output_gradient (surface, pdf_pattern,
					pdf_pattern->pattern_res,
					&pat_to_pdf, &start, &end, domain,
					"/DeviceRGB", color_function);
    if (alpha_function.id != 0) {
	cairo_pdf_resource_t mask_resource;
	assert (pdf_pattern->gstate_res.id != 0);
        /* Create pattern for SMask. */
        mask_resource = _cairo_pdf_surface_new_object (surface);
	if (mask_resource.id == 0)
	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
	_cairo_pdf_surface_output_gradient (surface, pdf_pattern,
					    mask_resource,
					    &pat_to_pdf, &start, &end, domain,
					    "/DeviceGray", alpha_function);
	status = cairo_pdf_surface_emit_transparency_group (surface,
							    pdf_pattern,
						            pdf_pattern->gstate_res,
							    mask_resource);
	if (unlikely (status))
	    return status;
    }
    return _cairo_output_stream_get_status (surface->output);
}
static cairo_int_status_t
_cairo_pdf_surface_emit_mesh_pattern (cairo_pdf_surface_t    *surface,
				      cairo_pdf_pattern_t    *pdf_pattern)
{
    cairo_matrix_t pat_to_pdf;
    cairo_int_status_t status;
    cairo_pattern_t *pattern = pdf_pattern->pattern;
    cairo_pdf_shading_t shading;
    int i;
    cairo_pdf_resource_t res;
    cairo_matrix_t mat;
    pat_to_pdf = pattern->matrix;
    status = cairo_matrix_invert (&pat_to_pdf);
    /* cairo_pattern_set_matrix ensures the matrix is invertible */
    assert (status == CAIRO_INT_STATUS_SUCCESS);
    if (pdf_pattern->inverted_y_axis)
	cairo_matrix_init (&mat, 1, 0, 0, 1, 0, 0);
    else
	cairo_matrix_init (&mat, 1, 0, 0, -1, 0, surface->height);
    cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &mat);
    status = _cairo_pdf_shading_init_color (&shading, (cairo_mesh_pattern_t *) pattern);
    if (unlikely (status))
	return status;
    res = _cairo_pdf_surface_new_object (surface);
    if (unlikely (res.id == 0))
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    _cairo_output_stream_printf (surface->output,
				 "%d 0 obj\n"
                                 "<< /ShadingType %d\n"
                                 "   /ColorSpace /DeviceRGB\n"
				 "   /BitsPerCoordinate %d\n"
				 "   /BitsPerComponent %d\n"
				 "   /BitsPerFlag %d\n"
				 "   /Decode [",
				 res.id,
				 shading.shading_type,
				 shading.bits_per_coordinate,
				 shading.bits_per_component,
				 shading.bits_per_flag);
    for (i = 0; i < shading.decode_array_length; i++)
	_cairo_output_stream_printf (surface->output, "%f ", shading.decode_array[i]);
    _cairo_output_stream_printf (surface->output,
				 "]\n"
				 "   /Length %ld\n"
				 ">>\n"
				 "stream\n",
				 shading.data_length);
    _cairo_output_stream_write (surface->output, shading.data, shading.data_length);
    _cairo_output_stream_printf (surface->output,
				 "\nendstream\n"
				 "endobj\n");
    _cairo_pdf_shading_fini (&shading);
    _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res);
    _cairo_output_stream_printf (surface->output,
                                 "%d 0 obj\n"
                                 "<< /Type /Pattern\n"
                                 "   /PatternType 2\n"
                                 "   /Matrix [ ",
				 pdf_pattern->pattern_res.id);
    _cairo_output_stream_print_matrix (surface->output, &pat_to_pdf);
    _cairo_output_stream_printf (surface->output,
                                 " ]\n"
                                 "   /Shading %d 0 R\n"
				 ">>\n"
				 "endobj\n",
				 res.id);
    if (pdf_pattern->gstate_res.id != 0) {
	cairo_pdf_resource_t mask_resource;
	/* Create pattern for SMask. */
        res = _cairo_pdf_surface_new_object (surface);
	if (unlikely (res.id == 0))
	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
	status = _cairo_pdf_shading_init_alpha (&shading, (cairo_mesh_pattern_t *) pattern);
	if (unlikely (status))
	    return status;
	_cairo_output_stream_printf (surface->output,
				 "%d 0 obj\n"
                                 "<< /ShadingType %d\n"
                                 "   /ColorSpace /DeviceGray\n"
				 "   /BitsPerCoordinate %d\n"
				 "   /BitsPerComponent %d\n"
				 "   /BitsPerFlag %d\n"
				 "   /Decode [",
				 res.id,
				 shading.shading_type,
				 shading.bits_per_coordinate,
				 shading.bits_per_component,
				 shading.bits_per_flag);
	for (i = 0; i < shading.decode_array_length; i++)
	    _cairo_output_stream_printf (surface->output, "%f ", shading.decode_array[i]);
	_cairo_output_stream_printf (surface->output,
				     "]\n"
				     "   /Length %ld\n"
				     ">>\n"
				     "stream\n",
				     shading.data_length);
	_cairo_output_stream_write (surface->output, shading.data, shading.data_length);
	_cairo_output_stream_printf (surface->output,
				     "\nendstream\n"
				     "endobj\n");
	_cairo_pdf_shading_fini (&shading);
        mask_resource = _cairo_pdf_surface_new_object (surface);
	if (unlikely (mask_resource.id == 0))
	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
	_cairo_output_stream_printf (surface->output,
				     "%d 0 obj\n"
				     "<< /Type /Pattern\n"
				     "   /PatternType 2\n"
				     "   /Matrix [ ",
				     mask_resource.id);
	_cairo_output_stream_print_matrix (surface->output, &pat_to_pdf);
	_cairo_output_stream_printf (surface->output,
				     " ]\n"
				     "   /Shading %d 0 R\n"
				     ">>\n"
				     "endobj\n",
				     res.id);
	status = cairo_pdf_surface_emit_transparency_group (surface,
							    pdf_pattern,
						            pdf_pattern->gstate_res,
							    mask_resource);
	if (unlikely (status))
	    return status;
    }
    return _cairo_output_stream_get_status (surface->output);
}
static cairo_int_status_t
_cairo_pdf_surface_emit_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern_t *pdf_pattern)
{
    cairo_int_status_t status;
    switch (pdf_pattern->pattern->type) {
    case CAIRO_PATTERN_TYPE_SOLID:
	ASSERT_NOT_REACHED;
	status = _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
	break;
    case CAIRO_PATTERN_TYPE_SURFACE:
    case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
	status = _cairo_pdf_surface_emit_surface_pattern (surface, pdf_pattern);
	break;
    case CAIRO_PATTERN_TYPE_LINEAR:
    case CAIRO_PATTERN_TYPE_RADIAL:
	status = _cairo_pdf_surface_emit_gradient (surface, pdf_pattern);
	break;
    case CAIRO_PATTERN_TYPE_MESH:
	status = _cairo_pdf_surface_emit_mesh_pattern (surface, pdf_pattern);
	break;
    default:
	ASSERT_NOT_REACHED;
	status = _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
	break;
    }
    return status;
}
static cairo_int_status_t
13
_cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t          *surface,
					  cairo_operator_t              op,
					  const cairo_pattern_t        *source,
					  cairo_analysis_source_t       source_type,
					  const cairo_rectangle_int_t  *extents,
					  double                        alpha,
					  cairo_pdf_resource_t	       *smask_res,
					  cairo_bool_t                  stencil_mask)
{
    cairo_matrix_t cairo_p2d, pdf_p2d;
    cairo_int_status_t status;
    int alpha_id;
    double x_offset;
    double y_offset;
    cairo_pdf_source_surface_entry_t *pdf_source;
13
    int region_id = 0;
13
    if (source->extend == CAIRO_EXTEND_PAD &&
	!(source->type == CAIRO_PATTERN_TYPE_SURFACE &&
	  ((cairo_surface_pattern_t *)source)->surface->type == CAIRO_SURFACE_TYPE_RECORDING))
    {
	status = _cairo_pdf_surface_add_padded_image_surface (surface,
							      source,
							      extents,
							      &pdf_source,
							      &x_offset,
							      &y_offset,
							      NULL);
    } else {
13
	if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
13
	    cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) source;
13
	    if (_cairo_pdf_interchange_struct_tree_requires_recording_surface (surface,
									       surface_pattern,
									       source_type))
	    {
		region_id = surface_pattern->region_array_id;
	    }
	}
13
	status = _cairo_pdf_surface_add_source_surface (surface,
							NULL,
							source,
							region_id,
							op,
13
							source->filter,
							stencil_mask,
							FALSE, /* smask */
							alpha != 1.0, /* need_transp_group */
							extents,
							smask_res,
							&pdf_source,
							&x_offset,
							&y_offset,
							NULL);
    }
13
    if (unlikely (status))
	return status;
13
    cairo_p2d = source->matrix;
13
    status = cairo_matrix_invert (&cairo_p2d);
    /* cairo_pattern_set_matrix ensures the matrix is invertible */
13
    assert (status == CAIRO_INT_STATUS_SUCCESS);
13
    pdf_p2d = surface->cairo_to_pdf;
13
    cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &pdf_p2d);
13
    cairo_matrix_translate (&pdf_p2d, x_offset, y_offset);
13
    if (pdf_source->emit_image) {
	int width, height;
7
	if (pdf_source->bounded) {
7
	    width = pdf_source->extents.width;
7
	    height = pdf_source->extents.height;
	} else {
	    /* We can't scale an image to an unbounded surface size so just set the size to 1 */
	    width = 1;
	    height = 1;
	}
7
	cairo_matrix_translate (&pdf_p2d, 0.0, height);
7
	cairo_matrix_scale (&pdf_p2d, 1.0, -1.0);
7
	cairo_matrix_scale (&pdf_p2d, width, height);
    }
13
    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
13
    if (unlikely (status))
	return status;
13
    if (! _cairo_matrix_is_identity (&pdf_p2d)) {
11
	_cairo_output_stream_print_matrix (surface->output, &pdf_p2d);
11
	_cairo_output_stream_printf (surface->output, " cm\n");
    }
13
    status = _cairo_pdf_surface_add_alpha (surface, alpha, &alpha_id);
13
    if (unlikely (status))
	return status;
13
    if (stencil_mask) {
	_cairo_output_stream_printf (surface->output,
				     "/x%d Do\n",
				     pdf_source->surface_res.id);
    } else {
13
	_cairo_output_stream_printf (surface->output,
				     "/a%d gs /x%d Do\n",
				     alpha_id,
13
				     pdf_source->surface_res.id);
    }
13
    return _cairo_pdf_surface_add_xobject (surface, pdf_source->surface_res);
}
static cairo_int_status_t
_cairo_pdf_surface_paint_gradient (cairo_pdf_surface_t         *surface,
				   cairo_operator_t             op,
				   const cairo_pattern_t       *source,
				   const cairo_rectangle_int_t *extents,
				   double                       alpha)
{
    cairo_pdf_resource_t shading_res, gstate_res;
    cairo_matrix_t pat_to_pdf;
    cairo_int_status_t status;
    int alpha_id;
    status = _cairo_pdf_surface_add_pdf_shading (surface, source,
						 op, extents,
						 &shading_res, &gstate_res);
    if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
	return CAIRO_INT_STATUS_SUCCESS;
    if (unlikely (status))
	return status;
    pat_to_pdf = source->matrix;
    status = cairo_matrix_invert (&pat_to_pdf);
    /* cairo_pattern_set_matrix ensures the matrix is invertible */
    assert (status == CAIRO_INT_STATUS_SUCCESS);
    cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf);
    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
    if (unlikely (status))
	return status;
    if (! _cairo_matrix_is_identity (&pat_to_pdf)) {
	_cairo_output_stream_print_matrix (surface->output, &pat_to_pdf);
	_cairo_output_stream_printf (surface->output, " cm\n");
    }
    status = _cairo_pdf_surface_add_shading (surface, shading_res);
    if (unlikely (status))
	return status;
    if (gstate_res.id != 0) {
	status = _cairo_pdf_surface_add_smask (surface, gstate_res);
	if (unlikely (status))
	    return status;
	_cairo_output_stream_printf (surface->output,
				     "/s%d gs /sh%d sh\n",
				     gstate_res.id,
				     shading_res.id);
    } else {
	status = _cairo_pdf_surface_add_alpha (surface, alpha, &alpha_id);
	if (unlikely (status))
	    return status;
	_cairo_output_stream_printf (surface->output,
				     "/a%d gs /sh%d sh\n",
				     alpha_id,
				     shading_res.id);
    }
    return status;
}
static cairo_int_status_t
13
_cairo_pdf_surface_paint_pattern (cairo_pdf_surface_t          *surface,
				  cairo_operator_t              op,
				  const cairo_pattern_t        *source,
				  cairo_analysis_source_t       source_type,
				  const cairo_rectangle_int_t  *extents,
				  double                        alpha,
				  cairo_bool_t                  mask)
{
13
    switch (source->type) {
13
    case CAIRO_PATTERN_TYPE_SURFACE:
    case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
13
	return _cairo_pdf_surface_paint_surface_pattern (surface,
							 op,
							 source,
							 source_type,
							 extents,
							 alpha,
							 NULL,
							 mask);
    case CAIRO_PATTERN_TYPE_LINEAR:
    case CAIRO_PATTERN_TYPE_RADIAL:
    case CAIRO_PATTERN_TYPE_MESH:
	return _cairo_pdf_surface_paint_gradient (surface,
						  op,
						  source,
						  extents,
						  alpha);
    case CAIRO_PATTERN_TYPE_SOLID:
    default:
	ASSERT_NOT_REACHED;
	return CAIRO_STATUS_SUCCESS;
    }
}
static cairo_bool_t
29
_can_paint_pattern (const cairo_pattern_t *pattern)
{
29
    switch (pattern->type) {
16
    case CAIRO_PATTERN_TYPE_SOLID:
16
	return FALSE;
13
    case CAIRO_PATTERN_TYPE_SURFACE:
    case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
13
	return (pattern->extend == CAIRO_EXTEND_NONE ||
		pattern->extend == CAIRO_EXTEND_PAD);
    case CAIRO_PATTERN_TYPE_LINEAR:
    case CAIRO_PATTERN_TYPE_RADIAL:
	return TRUE;
    case CAIRO_PATTERN_TYPE_MESH:
	return FALSE;
    default:
	ASSERT_NOT_REACHED;
	return FALSE;
    }
}
static cairo_int_status_t
29
_cairo_pdf_surface_select_operator (cairo_pdf_surface_t *surface,
				    cairo_operator_t     op)
{
    cairo_int_status_t status;
29
    if (surface->reset_gs_required) {
4
	_cairo_output_stream_printf (surface->output, "/gs0 gs\n");
4
	surface->reset_gs_required = FALSE;
    }
29
    if (op == surface->current_operator)
9
	return CAIRO_STATUS_SUCCESS;
20
    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
20
    if (unlikely (status))
	return status;
20
    _cairo_output_stream_printf (surface->output,
				 "/b%d gs\n", op);
20
    surface->current_operator = op;
20
    _cairo_pdf_surface_add_operator (surface, op);
20
    return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
16
_cairo_pdf_surface_select_pattern (cairo_pdf_surface_t *surface,
				   const cairo_pattern_t     *pattern,
				   cairo_pdf_resource_t pattern_res,
				   cairo_bool_t         is_stroke)
{
    cairo_int_status_t status;
    int alpha;
16
    const cairo_color_t *solid_color = NULL;
16
    if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
16
	const cairo_solid_pattern_t *solid = (const cairo_solid_pattern_t *) pattern;
16
	solid_color = &solid->color;
    }
16
    if (solid_color != NULL) {
16
	if (surface->current_pattern_is_solid_color == FALSE ||
2
	    surface->current_color_red != solid_color->red ||
	    surface->current_color_green != solid_color->green ||
	    surface->current_color_blue != solid_color->blue ||
	    surface->current_color_is_stroke != is_stroke)
	{
16
	    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
16
	    if (unlikely (status))
		return status;
16
	    _cairo_output_stream_printf (surface->output,
					 "%f %f %f ",
16
					 solid_color->red,
16
					 solid_color->green,
16
					 solid_color->blue);
16
	    if (is_stroke)
		_cairo_output_stream_printf (surface->output, "RG ");
	    else
16
		_cairo_output_stream_printf (surface->output, "rg ");
16
	    surface->current_color_red = solid_color->red;
16
	    surface->current_color_green = solid_color->green;
16
	    surface->current_color_blue = solid_color->blue;
16
	    surface->current_color_is_stroke = is_stroke;
	}
16
	if (surface->current_pattern_is_solid_color == FALSE ||
2
	    surface->current_color_alpha != solid_color->alpha)
	{
14
	    status = _cairo_pdf_surface_add_alpha (surface, solid_color->alpha, &alpha);
14
	    if (unlikely (status))
		return status;
14
	    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
14
	    if (unlikely (status))
		return status;
14
	    _cairo_output_stream_printf (surface->output,
					 "/a%d gs\n",
					 alpha);
14
	    surface->current_color_alpha = solid_color->alpha;
	}
16
	surface->current_pattern_is_solid_color = TRUE;
    } else {
	status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha);
	if (unlikely (status))
	    return status;
	status = _cairo_pdf_surface_add_pattern (surface, pattern_res);
	if (unlikely (status))
	    return status;
	status = _cairo_pdf_operators_flush (&surface->pdf_operators);
	if (unlikely (status))
	    return status;
	/* fill-stroke calls select_pattern twice. Don't save if the
	 * gstate is already saved. */
	if (!surface->select_pattern_gstate_saved)
	    _cairo_output_stream_printf (surface->output, "q ");
	if (is_stroke) {
	    _cairo_output_stream_printf (surface->output,
					 "/Pattern CS /p%d SCN ",
					 pattern_res.id);
	} else {
	    _cairo_output_stream_printf (surface->output,
					 "/Pattern cs /p%d scn ",
					 pattern_res.id);
	}
	_cairo_output_stream_printf (surface->output,
				     "/a%d gs\n",
				     alpha);
	surface->select_pattern_gstate_saved = TRUE;
	surface->current_pattern_is_solid_color = FALSE;
    }
16
    return _cairo_output_stream_get_status (surface->output);
}
static cairo_int_status_t
16
_cairo_pdf_surface_unselect_pattern (cairo_pdf_surface_t *surface)
{
    cairo_int_status_t status;
16
    if (surface->select_pattern_gstate_saved) {
	status = _cairo_pdf_operators_flush (&surface->pdf_operators);
	if (unlikely (status))
	    return status;
	_cairo_output_stream_printf (surface->output, "Q\n");
	_cairo_pdf_operators_reset (&surface->pdf_operators);
	surface->current_pattern_is_solid_color = FALSE;
    }
16
    surface->select_pattern_gstate_saved = FALSE;
16
    return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
5
_cairo_pdf_surface_show_page (void *abstract_surface)
{
5
    cairo_pdf_surface_t *surface = abstract_surface;
    cairo_int_status_t status;
5
    status = _cairo_array_append (&surface->page_labels, &surface->current_page_label);
5
    if (unlikely (status))
	return status;
5
    surface->current_page_label = NULL;
5
    status = _cairo_pdf_interchange_end_page_content (surface);
5
    if (unlikely (status))
	return status;
5
    status = _cairo_pdf_surface_close_content_stream (surface, FALSE);
5
    if (unlikely (status))
	return status;
5
    _cairo_surface_clipper_reset (&surface->clipper);
5
    status = _cairo_pdf_surface_write_page (surface);
5
    if (unlikely (status))
	return status;
5
    _cairo_pdf_surface_clear (surface, FALSE);
5
    return CAIRO_STATUS_SUCCESS;
}
static cairo_bool_t
199
_cairo_pdf_surface_get_extents (void		        *abstract_surface,
				cairo_rectangle_int_t   *rectangle)
{
199
    cairo_pdf_surface_t *surface = abstract_surface;
199
    if (surface->surface_bounded)
199
	*rectangle = surface->surface_extents;
199
    return surface->surface_bounded;
}
static void
4
_cairo_pdf_surface_get_font_options (void                  *abstract_surface,
				     cairo_font_options_t  *options)
{
4
    _cairo_font_options_init_default (options);
4
    cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
4
    cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
4
    cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
4
    _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF);
4
}
static cairo_int_status_t
5
_cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface)
{
    cairo_pdf_page_info_t *page_info;
    int num_pages, i;
    cairo_int_status_t status;
5
    status = _cairo_pdf_surface_object_begin (surface, surface->pages_resource);
5
    if (unlikely (status))
	return status;
5
    _cairo_output_stream_printf (surface->object_stream.stream,
				 "<< /Type /Pages\n"
				 "   /Kids [ ");
5
    num_pages = _cairo_array_num_elements (&surface->pages);
10
    for (i = 0; i < num_pages; i++) {
5
	page_info = _cairo_array_index (&surface->pages, i);
5
	_cairo_output_stream_printf (surface->object_stream.stream, "%d 0 R ", page_info->page_res.id);
    }
5
    _cairo_output_stream_printf (surface->object_stream.stream, "]\n");
5
    _cairo_output_stream_printf (surface->object_stream.stream, "   /Count %d\n", num_pages);
    /* TODO: Figure out which other defaults to be inherited by /Page
     * objects. */
5
    _cairo_output_stream_printf (surface->object_stream.stream,
				 ">>\n");
5
    _cairo_pdf_surface_object_end (surface);
5
    return CAIRO_INT_STATUS_SUCCESS;
}
cairo_int_status_t
_cairo_utf8_to_pdf_string (const char *utf8, char **str_out)
{
    int i;
    int len;
    unsigned char *p;
    cairo_bool_t ascii;
    char *str;
    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
    ascii = TRUE;
    p = (unsigned char *)utf8;
    len = 0;
    while (*p) {
	if (*p < 32 || *p > 126) {
	    ascii = FALSE;
	    break;
	}
	if (*p == '(' || *p == ')' || *p == '\\')
	    len += 2;
	else
	    len++;
	p++;
    }
    if (ascii) {
	str = _cairo_malloc (len + 3);
	if (str == NULL)
	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
	str[0] = '(';
	p = (unsigned char *)utf8;
	i = 1;
	while (*p) {
	    if (*p == '(' || *p == ')' || *p == '\\')
		str[i++] = '\\';
	    str[i++] = *p;
	    p++;
	}
	str[i++] = ')';
	str[i++] = 0;
    } else {
	uint16_t *utf16 = NULL;
	int utf16_len = 0;
	status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len);
	if (unlikely (status))
	    return status;
	str = _cairo_malloc (utf16_len*4 + 7);
	if (str == NULL) {
	    free (utf16);
	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
	}
	strcpy (str, "<FEFF");
	for (i = 0; i < utf16_len; i++)
	    snprintf (str + 4*i + 5, 5, "%04X", utf16[i]);
	strcat (str, ">");
	free (utf16);
    }
    *str_out = str;
    return status;
}
static cairo_int_status_t
_cairo_pdf_surface_emit_unicode_for_glyph (cairo_pdf_surface_t	*surface,
					   const char 		*utf8)
{
    uint16_t *utf16 = NULL;
    int utf16_len = 0;
    cairo_int_status_t status;
    int i;
    if (utf8 && *utf8) {
	status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len);
	if (unlikely (status == CAIRO_INT_STATUS_INVALID_STRING)) {
	    utf16 = NULL;
	    utf16_len = 0;
	} else if (unlikely (status)) {
	    return status;
	}
    }
    _cairo_output_stream_printf (surface->output, "<");
    if (utf16 == NULL || utf16_len == 0) {
	/* According to the "ToUnicode Mapping File Tutorial"
	 * http://www.adobe.com/devnet/acrobat/pdfs/5411.ToUnicode.pdf
	 *
	 * Glyphs that do not map to a Unicode code point must be
	 * mapped to 0xfffd "REPLACEMENT CHARACTER".
	 */
	_cairo_output_stream_printf (surface->output,
				     "fffd");
    } else {
	for (i = 0; i < utf16_len; i++)
	    _cairo_output_stream_printf (surface->output,
					 "%04x", (int) (utf16[i]));
    }
    _cairo_output_stream_printf (surface->output, ">");
    free (utf16);
    return CAIRO_STATUS_SUCCESS;
}
/* Bob Jenkins hash
 *
 * Public domain code from:
 *   http://burtleburtle.net/bob/hash/doobs.html
 */
#define HASH_MIX(a,b,c) 		\
{ 					\
    a -= b; a -= c; a ^= (c>>13);	\
    b -= c; b -= a; b ^= (a<<8);	\
    c -= a; c -= b; c ^= (b>>13);	\
    a -= b; a -= c; a ^= (c>>12);	\
    b -= c; b -= a; b ^= (a<<16);	\
    c -= a; c -= b; c ^= (b>>5);	\
    a -= b; a -= c; a ^= (c>>3);	\
    b -= c; b -= a; b ^= (a<<10);	\
    c -= a; c -= b; c ^= (b>>15);	\
}
static uint32_t
_hash_data (const unsigned char *data, int length, uint32_t initval)
{
    uint32_t a, b, c, len;
    len = length;
    a = b = 0x9e3779b9;  /* the golden ratio; an arbitrary value */
    c = initval;         /* the previous hash value */
    while (len >= 12) {
	a += (data[0] + ((uint32_t)data[1]<<8) + ((uint32_t)data[2]<<16) + ((uint32_t)data[3]<<24));
	b += (data[4] + ((uint32_t)data[5]<<8) + ((uint32_t)data[6]<<16) + ((uint32_t)data[7]<<24));
	c += (data[8] + ((uint32_t)data[9]<<8) + ((uint32_t)data[10]<<16)+ ((uint32_t)data[11]<<24));
	HASH_MIX (a,b,c);
	data += 12;
	len -= 12;
    }
    c += length;
    switch(len) {
    case 11: c+= ((uint32_t) data[10] << 24); /* fall through */
    case 10: c+= ((uint32_t) data[9] << 16);  /* fall through */
    case 9 : c+= ((uint32_t) data[8] << 8);   /* fall through */
    case 8 : b+= ((uint32_t) data[7] << 24);  /* fall through */
    case 7 : b+= ((uint32_t) data[6] << 16);  /* fall through */
    case 6 : b+= ((uint32_t) data[5] << 8);   /* fall through */
    case 5 : b+= data[4];		      /* fall through */
    case 4 : a+= ((uint32_t) data[3] << 24);  /* fall through */
    case 3 : a+= ((uint32_t) data[2] << 16);  /* fall through */
    case 2 : a+= ((uint32_t) data[1] << 8);   /* fall through */
    case 1 : a+= data[0];
    }
    HASH_MIX (a,b,c);
    return c;
}
static void
_create_font_subset_tag (cairo_scaled_font_subset_t	*font_subset,
			 const char 			*font_name,
			 char				*tag)
{
    uint32_t hash;
    int i;
    hash = _hash_data ((unsigned char *) font_name, strlen(font_name), 0);
    hash = _hash_data ((unsigned char *) (font_subset->glyphs),
		       font_subset->num_glyphs * sizeof(unsigned long), hash);
    for (i = 0; i < 6; i++) {
	tag[i] = 'A' + (hash % 26);
	hash /= 26;
    }
    tag[i] = 0;
}
static cairo_int_status_t
_cairo_pdf_surface_emit_to_unicode_stream (cairo_pdf_surface_t		*surface,
					   cairo_scaled_font_subset_t	*font_subset,
					   cairo_pdf_resource_t         *stream)
{
    unsigned int i, num_bfchar;
    cairo_int_status_t status;
    stream->id = 0;
    status = _cairo_pdf_surface_open_stream (surface,
					      NULL,
					      surface->compress_streams,
					      NULL);
    if (unlikely (status))
	return status;
    _cairo_output_stream_printf (surface->output,
                                 "/CIDInit /ProcSet findresource begin\n"
                                 "12 dict begin\n"
                                 "begincmap\n"
                                 "/CIDSystemInfo\n"
                                 "<< /Registry (Adobe)\n"
                                 "   /Ordering (UCS)\n"
                                 "   /Supplement 0\n"
                                 ">> def\n"
                                 "/CMapName /Adobe-Identity-UCS def\n"
                                 "/CMapType 2 def\n"
                                 "1 begincodespacerange\n");
    if (font_subset->is_composite && !font_subset->is_latin) {
        _cairo_output_stream_printf (surface->output,
                                     "<0000> <ffff>\n");
    } else {
        _cairo_output_stream_printf (surface->output,
                                     "<00> <ff>\n");
    }
    _cairo_output_stream_printf (surface->output,
                                  "endcodespacerange\n");
    if (font_subset->is_scaled) {
	/* Type 3 fonts include glyph 0 in the subset */
	num_bfchar = font_subset->num_glyphs;
	/* The CMap specification has a limit of 100 characters per beginbfchar operator */
	_cairo_output_stream_printf (surface->output,
				     "%d beginbfchar\n",
				     num_bfchar > 100 ? 100 : num_bfchar);
	for (i = 0; i < num_bfchar; i++) {
	    if (i != 0 && i % 100 == 0) {
		_cairo_output_stream_printf (surface->output,
					     "endbfchar\n"
					     "%d beginbfchar\n",
					     num_bfchar - i > 100 ? 100 : num_bfchar - i);
	    }
	    _cairo_output_stream_printf (surface->output, "<%02x> ", i);
	    status = _cairo_pdf_surface_emit_unicode_for_glyph (surface,
								font_subset->utf8[i]);
	    if (unlikely (status))
		return status;
	    _cairo_output_stream_printf (surface->output,
					 "\n");
	}
    } else {
	/* Other fonts reserve glyph 0 for .notdef. Omit glyph 0 from the /ToUnicode map */
	num_bfchar = font_subset->num_glyphs - 1;
	/* The CMap specification has a limit of 100 characters per beginbfchar operator */
	_cairo_output_stream_printf (surface->output,
				     "%d beginbfchar\n",
				     num_bfchar > 100 ? 100 : num_bfchar);
	for (i = 0; i < num_bfchar; i++) {
	    if (i != 0 && i % 100 == 0) {
		_cairo_output_stream_printf (surface->output,
					     "endbfchar\n"
					     "%d beginbfchar\n",
					     num_bfchar - i > 100 ? 100 : num_bfchar - i);
	    }
	    if (font_subset->is_latin)
		_cairo_output_stream_printf (surface->output, "<%02x> ", font_subset->to_latin_char[i + 1]);
	    else if (font_subset->is_composite)
		_cairo_output_stream_printf (surface->output, "<%04x> ", i + 1);
	    else
		_cairo_output_stream_printf (surface->output, "<%02x> ", i + 1);
	    status = _cairo_pdf_surface_emit_unicode_for_glyph (surface,
								font_subset->utf8[i + 1]);
	    if (unlikely (status))
		return status;
	    _cairo_output_stream_printf (surface->output,
					 "\n");
	}
    }
    _cairo_output_stream_printf (surface->output,
                                 "endbfchar\n");
    _cairo_output_stream_printf (surface->output,
                                 "endcmap\n"
                                 "CMapName currentdict /CMap defineresource pop\n"
                                 "end\n"
                                 "end\n");
    *stream = surface->pdf_stream.self;
    return _cairo_pdf_surface_close_stream (surface);
}
#define PDF_UNITS_PER_EM 1000
static cairo_int_status_t
_cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t		*surface,
                                  cairo_scaled_font_subset_t	*font_subset,
                                  cairo_cff_subset_t            *subset)
{
    cairo_pdf_resource_t stream, descriptor, cidfont_dict;
    cairo_pdf_resource_t subset_resource, to_unicode_stream;
    cairo_pdf_font_t font;
    unsigned int i, last_glyph;
    cairo_int_status_t status;
    char tag[10];
    _create_font_subset_tag (font_subset, subset->ps_name, tag);
    subset_resource = _cairo_pdf_surface_get_font_resource (surface,
							    font_subset->font_id,
							    font_subset->subset_id);
    if (subset_resource.id == 0)
	return CAIRO_STATUS_SUCCESS;
    status = _cairo_pdf_surface_open_stream (surface,
					     NULL,
					     TRUE,
					     font_subset->is_latin ?
					     "   /Subtype /Type1C\n" :
					     "   /Subtype /CIDFontType0C\n");
    if (unlikely (status))
	return status;
    stream = surface->pdf_stream.self;
    _cairo_output_stream_write (surface->output,
				subset->data, subset->data_length);
    status = _cairo_pdf_surface_close_stream (surface);
    if (unlikely (status))
	return status;
    status = _cairo_pdf_surface_emit_to_unicode_stream (surface,
	                                                font_subset,
							&to_unicode_stream);
    if (_cairo_int_status_is_error (status))
	return status;
    descriptor = _cairo_pdf_surface_new_object (surface);
    if (descriptor.id == 0)
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    _cairo_output_stream_printf (surface->output,
				 "%d 0 obj\n"
				 "<< /Type /FontDescriptor\n"
				 "   /FontName /%s+%s\n",
				 descriptor.id,
				 tag,
				 subset->ps_name);
    if (subset->family_name_utf8) {
	char *pdf_str;
	status = _cairo_utf8_to_pdf_string (subset->family_name_utf8, &pdf_str);
	if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
	    _cairo_output_stream_printf (surface->output,
					 "   /FontFamily %s\n",
					 pdf_str);
	    free (pdf_str);
	} else if (status != CAIRO_INT_STATUS_INVALID_STRING) {
	    return status;
	}
    }
    _cairo_output_stream_printf (surface->output,
				 "   /Flags 4\n"
				 "   /FontBBox [ %ld %ld %ld %ld ]\n"
				 "   /ItalicAngle 0\n"
				 "   /Ascent %ld\n"
				 "   /Descent %ld\n"
				 "   /CapHeight %ld\n"
				 "   /StemV 80\n"
				 "   /StemH 80\n"
				 "   /FontFile3 %u 0 R\n"
				 ">>\n"
				 "endobj\n",
				 (long)(subset->x_min*PDF_UNITS_PER_EM),
				 (long)(subset->y_min*PDF_UNITS_PER_EM),
				 (long)(subset->x_max*PDF_UNITS_PER_EM),
				 (long)(subset->y_max*PDF_UNITS_PER_EM),
				 (long)(subset->ascent*PDF_UNITS_PER_EM),
				 (long)(subset->descent*PDF_UNITS_PER_EM),
				 (long)(subset->y_max*PDF_UNITS_PER_EM),
				 stream.id);
    if (font_subset->is_latin) {
	/* find last glyph used */
	for (i = 255; i >= 32; i--)
	    if (font_subset->latin_to_subset_glyph_index[i] > 0)
		break;
	last_glyph = i;
	_cairo_pdf_surface_update_object (surface, subset_resource);
	_cairo_output_stream_printf (surface->output,
				     "%d 0 obj\n"
				     "<< /Type /Font\n"
				     "   /Subtype /Type1\n"
				     "   /BaseFont /%s+%s\n"
				     "   /FirstChar 32\n"
				     "   /LastChar %d\n"
				     "   /FontDescriptor %d 0 R\n"
				     "   /Encoding /WinAnsiEncoding\n"
				     "   /Widths [",
				     subset_resource.id,
				     tag,
				     subset->ps_name,
				     last_glyph,
				     descriptor.id);
	for (i = 32; i < last_glyph + 1; i++) {
	    int glyph = font_subset->latin_to_subset_glyph_index[i];
	    if (glyph > 0) {
		_cairo_output_stream_printf (surface->output,
					     " %f",
					     (subset->widths[glyph]*PDF_UNITS_PER_EM));
	    } else {
		_cairo_output_stream_printf (surface->output, " 0");
	    }
	}
	_cairo_output_stream_printf (surface->output,
				     " ]\n");
	if (to_unicode_stream.id != 0)
	    _cairo_output_stream_printf (surface->output,
					 "    /ToUnicode %d 0 R\n",
					 to_unicode_stream.id);
	_cairo_output_stream_printf (surface->output,
				     ">>\n"
				     "endobj\n");
    } else {
	cidfont_dict = _cairo_pdf_surface_new_object (surface);
	if (cidfont_dict.id == 0)
	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
	_cairo_output_stream_printf (surface->output,
				     "%d 0 obj\n"
				     "<< /Type /Font\n"
				     "   /Subtype /CIDFontType0\n"
				     "   /BaseFont /%s+%s\n"
				     "   /CIDSystemInfo\n"
				     "   << /Registry (Adobe)\n"
				     "      /Ordering (Identity)\n"
				     "      /Supplement 0\n"
				     "   >>\n"
				     "   /FontDescriptor %d 0 R\n"
				     "   /W [0 [",
				     cidfont_dict.id,
				     tag,
				     subset->ps_name,
				     descriptor.id);
	for (i = 0; i < font_subset->num_glyphs; i++)
	    _cairo_output_stream_printf (surface->output,
					 " %f",
					 (subset->widths[i]*PDF_UNITS_PER_EM));
	_cairo_output_stream_printf (surface->output,
				     " ]]\n"
				     ">>\n"
				     "endobj\n");
	_cairo_pdf_surface_update_object (surface, subset_resource);
	_cairo_output_stream_printf (surface->output,
				     "%d 0 obj\n"
				     "<< /Type /Font\n"
				     "   /Subtype /Type0\n"
				     "   /BaseFont /%s+%s\n"
				     "   /Encoding /Identity-H\n"
				     "   /DescendantFonts [ %d 0 R]\n",
				     subset_resource.id,
				     tag,
				     subset->ps_name,
				     cidfont_dict.id);
	if (to_unicode_stream.id != 0)
	    _cairo_output_stream_printf (surface->output,
					 "   /ToUnicode %d 0 R\n",
					 to_unicode_stream.id);
	_cairo_output_stream_printf (surface->output,
				     ">>\n"
				     "endobj\n");
    }
    font.font_id = font_subset->font_id;
    font.subset_id = font_subset->subset_id;
    font.subset_resource = subset_resource;
    status = _cairo_array_append (&surface->fonts, &font);
    return status;
}
static cairo_int_status_t
_cairo_pdf_surface_emit_cff_font_subset (cairo_pdf_surface_t	     *surface,
                                         cairo_scaled_font_subset_t  *font_subset)
{
    cairo_int_status_t status;
    cairo_cff_subset_t subset;
    char name[64];
    snprintf (name, sizeof name, "CairoFont-%d-%d",
              font_subset->font_id, font_subset->subset_id);
    status = _cairo_cff_subset_init (&subset, name, font_subset);
    if (unlikely (status))
        return status;
    status = _cairo_pdf_surface_emit_cff_font (surface, font_subset, &subset);
    _cairo_cff_subset_fini (&subset);
    return status;
}
static cairo_int_status_t
_cairo_pdf_surface_emit_cff_fallback_font (cairo_pdf_surface_t	       *surface,
                                           cairo_scaled_font_subset_t  *font_subset)
{
    cairo_int_status_t status;
    cairo_cff_subset_t subset;
    char name[64];
    /* CFF fallback subsetting does not work with 8-bit glyphs unless
     * they are a latin subset */
    if (!font_subset->is_composite && !font_subset->is_latin)
	return CAIRO_INT_STATUS_UNSUPPORTED;
    snprintf (name, sizeof name, "CairoFont-%d-%d",
              font_subset->font_id, font_subset->subset_id);
    status = _cairo_cff_fallback_init (&subset, name, font_subset);
    if (unlikely (status))
        return status;
    status = _cairo_pdf_surface_emit_cff_font (surface, font_subset, &subset);
    _cairo_cff_fallback_fini (&subset);
    return status;
}
static cairo_int_status_t
_cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t		*surface,
                                    cairo_scaled_font_subset_t	*font_subset,
                                    cairo_type1_subset_t        *subset)
{
    cairo_pdf_resource_t stream, descriptor, subset_resource, to_unicode_stream;
    cairo_pdf_font_t font;
    cairo_int_status_t status;
    unsigned long length;
    unsigned int i, last_glyph;
    char tag[10];
    _create_font_subset_tag (font_subset, subset->base_font, tag);
    subset_resource = _cairo_pdf_surface_get_font_resource (surface,
							    font_subset->font_id,
							    font_subset->subset_id);
    if (subset_resource.id == 0)
	return CAIRO_STATUS_SUCCESS;
    length = subset->header_length + subset->data_length + subset->trailer_length;
    status = _cairo_pdf_surface_open_stream (surface,
					     NULL,
					     TRUE,
					     "   /Length1 %lu\n"
					     "   /Length2 %lu\n"
					     "   /Length3 %lu\n",
					     subset->header_length,
					     subset->data_length,
					     subset->trailer_length);
    if (unlikely (status))
	return status;
    stream = surface->pdf_stream.self;
    _cairo_output_stream_write (surface->output, subset->data, length);
    status = _cairo_pdf_surface_close_stream (surface);
    if (unlikely (status))
	return status;
    status = _cairo_pdf_surface_emit_to_unicode_stream (surface,
	                                                font_subset,
							&to_unicode_stream);
    if (_cairo_int_status_is_error (status))
	return status;
    last_glyph = font_subset->num_glyphs - 1;
    if (font_subset->is_latin) {
	/* find last glyph used */
	for (i = 255; i >= 32; i--)
	    if (font_subset->latin_to_subset_glyph_index[i] > 0)
		break;
	last_glyph = i;
    }
    descriptor = _cairo_pdf_surface_new_object (surface);
    if (descriptor.id == 0)
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    _cairo_output_stream_printf (surface->output,
				 "%d 0 obj\n"
				 "<< /Type /FontDescriptor\n"
				 "   /FontName /%s+%s\n"
				 "   /Flags 4\n"
				 "   /FontBBox [ %ld %ld %ld %ld ]\n"
				 "   /ItalicAngle 0\n"
				 "   /Ascent %ld\n"
				 "   /Descent %ld\n"
				 "   /CapHeight %ld\n"
				 "   /StemV 80\n"
				 "   /StemH 80\n"
				 "   /FontFile %u 0 R\n"
				 ">>\n"
				 "endobj\n",
				 descriptor.id,
				 tag,
				 subset->base_font,
				 (long)(subset->x_min*PDF_UNITS_PER_EM),
				 (long)(subset->y_min*PDF_UNITS_PER_EM),
				 (long)(subset->x_max*PDF_UNITS_PER_EM),
				 (long)(subset->y_max*PDF_UNITS_PER_EM),
				 (long)(subset->ascent*PDF_UNITS_PER_EM),
				 (long)(subset->descent*PDF_UNITS_PER_EM),
				 (long)(subset->y_max*PDF_UNITS_PER_EM),
				 stream.id);
    _cairo_pdf_surface_update_object (surface, subset_resource);
    _cairo_output_stream_printf (surface->output,
				 "%d 0 obj\n"
				 "<< /Type /Font\n"
				 "   /Subtype /Type1\n"
				 "   /BaseFont /%s+%s\n"
				 "   /FirstChar %d\n"
				 "   /LastChar %d\n"
				 "   /FontDescriptor %d 0 R\n",
				 subset_resource.id,
				 tag,
				 subset->base_font,
				 font_subset->is_latin ? 32 : 0,
				 last_glyph,
				 descriptor.id);
    if (font_subset->is_latin)
	_cairo_output_stream_printf (surface->output, "   /Encoding /WinAnsiEncoding\n");
    _cairo_output_stream_printf (surface->output, "   /Widths [");
    if (font_subset->is_latin) {
	for (i = 32; i < last_glyph + 1; i++) {
	    int glyph = font_subset->latin_to_subset_glyph_index[i];
	    if (glyph > 0) {
		_cairo_output_stream_printf (surface->output,
					     " %f",
					     (subset->widths[glyph]*PDF_UNITS_PER_EM));
	    } else {
		_cairo_output_stream_printf (surface->output, " 0");
	    }
	}
    } else {
	for (i = 0; i < font_subset->num_glyphs; i++)
	    _cairo_output_stream_printf (surface->output,
					 " %f",
					 (subset->widths[i]*PDF_UNITS_PER_EM));
    }
    _cairo_output_stream_printf (surface->output,
				 " ]\n");
    if (to_unicode_stream.id != 0)
        _cairo_output_stream_printf (surface->output,
                                     "    /ToUnicode %d 0 R\n",
                                     to_unicode_stream.id);
    _cairo_output_stream_printf (surface->output,
				 ">>\n"
				 "endobj\n");
    font.font_id = font_subset->font_id;
    font.subset_id = font_subset->subset_id;
    font.subset_resource = subset_resource;
    return _cairo_array_append (&surface->fonts, &font);
}
static cairo_int_status_t
_cairo_pdf_surface_emit_type1_font_subset (cairo_pdf_surface_t		*surface,
					   cairo_scaled_font_subset_t	*font_subset)
{
    cairo_int_status_t status;
    cairo_type1_subset_t subset;
    char name[64];
    /* 16-bit glyphs not compatible with Type 1 fonts */
    if (font_subset->is_composite && !font_subset->is_latin)
	return CAIRO_INT_STATUS_UNSUPPORTED;
    snprintf (name, sizeof name, "CairoFont-%d-%d",
	      font_subset->font_id, font_subset->subset_id);
    status = _cairo_type1_subset_init (&subset, name, font_subset, FALSE);
    if (unlikely (status))
	return status;
    status = _cairo_pdf_surface_emit_type1_font (surface, font_subset, &subset);
    _cairo_type1_subset_fini (&subset);
    return status;
}
static cairo_int_status_t
_cairo_pdf_surface_emit_type1_fallback_font (cairo_pdf_surface_t	*surface,
                                             cairo_scaled_font_subset_t	*font_subset)
{
    cairo_int_status_t status;
    cairo_type1_subset_t subset;
    char name[64];
    /* 16-bit glyphs not compatible with Type 1 fonts */
    if (font_subset->is_composite && !font_subset->is_latin)
	return CAIRO_INT_STATUS_UNSUPPORTED;
    snprintf (name, sizeof name, "CairoFont-%d-%d",
	      font_subset->font_id, font_subset->subset_id);
    status = _cairo_type1_fallback_init_binary (&subset, name, font_subset);
    if (unlikely (status))
	return status;
    status = _cairo_pdf_surface_emit_type1_font (surface, font_subset, &subset);
    _cairo_type1_fallback_fini (&subset);
    return status;
}
static cairo_int_status_t
_cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t		*surface,
					      cairo_scaled_font_subset_t	*font_subset)
{
    cairo_pdf_resource_t stream, descriptor, cidfont_dict;
    cairo_pdf_resource_t subset_resource, to_unicode_stream;
    cairo_int_status_t status;
    cairo_pdf_font_t font;
    cairo_truetype_subset_t subset;
    unsigned int i, last_glyph;
    char tag[10];
    subset_resource = _cairo_pdf_surface_get_font_resource (surface,
							    font_subset->font_id,
							    font_subset->subset_id);
    if (subset_resource.id == 0)
	return CAIRO_STATUS_SUCCESS;
    status = _cairo_truetype_subset_init_pdf (&subset, font_subset);
    if (unlikely (status))
	return status;
    _create_font_subset_tag (font_subset, subset.ps_name, tag);
    status = _cairo_pdf_surface_open_stream (surface,
					     NULL,
					     TRUE,
					     "   /Length1 %lu\n",
					     subset.data_length);
    if (unlikely (status)) {
	_cairo_truetype_subset_fini (&subset);
	return status;
    }
    stream = surface->pdf_stream.self;
    _cairo_output_stream_write (surface->output,
				subset.data, subset.data_length);
    status = _cairo_pdf_surface_close_stream (surface);
    if (unlikely (status)) {
	_cairo_truetype_subset_fini (&subset);
	return status;
    }
    status = _cairo_pdf_surface_emit_to_unicode_stream (surface,
	                                                font_subset,
							&to_unicode_stream);
    if (_cairo_int_status_is_error (status)) {
	_cairo_truetype_subset_fini (&subset);
	return status;
    }
    descriptor = _cairo_pdf_surface_new_object (surface);
    if (descriptor.id == 0) {
	_cairo_truetype_subset_fini (&subset);
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    }
    _cairo_output_stream_printf (surface->output,
				 "%d 0 obj\n"
				 "<< /Type /FontDescriptor\n"
				 "   /FontName /%s+%s\n",
				 descriptor.id,
				 tag,
				 subset.ps_name);
    if (subset.family_name_utf8) {
	char *pdf_str;
	status = _cairo_utf8_to_pdf_string (subset.family_name_utf8, &pdf_str);
	if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
	    _cairo_output_stream_printf (surface->output,
					 "   /FontFamily %s\n",
					 pdf_str);
	    free (pdf_str);
	} else if (status != CAIRO_INT_STATUS_INVALID_STRING) {
	    return status;
	}
    }
    _cairo_output_stream_printf (surface->output,
				 "   /Flags %d\n"
				 "   /FontBBox [ %ld %ld %ld %ld ]\n"
				 "   /ItalicAngle 0\n"
				 "   /Ascent %ld\n"
				 "   /Descent %ld\n"
				 "   /CapHeight %ld\n"
				 "   /StemV 80\n"
				 "   /StemH 80\n"
				 "   /FontFile2 %u 0 R\n"
				 ">>\n"
				 "endobj\n",
				 font_subset->is_latin ? 32 : 4,
				 (long)(subset.x_min*PDF_UNITS_PER_EM),
				 (long)(subset.y_min*PDF_UNITS_PER_EM),
                                 (long)(subset.x_max*PDF_UNITS_PER_EM),
				 (long)(subset.y_max*PDF_UNITS_PER_EM),
				 (long)(subset.ascent*PDF_UNITS_PER_EM),
				 (long)(subset.descent*PDF_UNITS_PER_EM),
				 (long)(subset.y_max*PDF_UNITS_PER_EM),
				 stream.id);
    if (font_subset->is_latin) {
	/* find last glyph used */
	for (i = 255; i >= 32; i--)
	    if (font_subset->latin_to_subset_glyph_index[i] > 0)
		break;
	last_glyph = i;
	_cairo_pdf_surface_update_object (surface, subset_resource);
	_cairo_output_stream_printf (surface->output,
				     "%d 0 obj\n"
				     "<< /Type /Font\n"
				     "   /Subtype /TrueType\n"
				     "   /BaseFont /%s+%s\n"
				     "   /FirstChar 32\n"
				     "   /LastChar %d\n"
				     "   /FontDescriptor %d 0 R\n"
				     "   /Encoding /WinAnsiEncoding\n"
				     "   /Widths [",
				     subset_resource.id,
				     tag,
				     subset.ps_name,
				     last_glyph,
				     descriptor.id);
	for (i = 32; i < last_glyph + 1; i++) {
	    int glyph = font_subset->latin_to_subset_glyph_index[i];
	    if (glyph > 0) {
		_cairo_output_stream_printf (surface->output,
					     " %f",
					     (subset.widths[glyph]*PDF_UNITS_PER_EM));
	    } else {
		_cairo_output_stream_printf (surface->output, " 0");
	    }
	}
	_cairo_output_stream_printf (surface->output,
				     " ]\n");
	if (to_unicode_stream.id != 0)
	    _cairo_output_stream_printf (surface->output,
					 "    /ToUnicode %d 0 R\n",
					 to_unicode_stream.id);
	_cairo_output_stream_printf (surface->output,
				     ">>\n"
				     "endobj\n");
    } else {
	cidfont_dict = _cairo_pdf_surface_new_object (surface);
	if (cidfont_dict.id == 0) {
	    _cairo_truetype_subset_fini (&subset);
	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
	}
	_cairo_output_stream_printf (surface->output,
				     "%d 0 obj\n"
				     "<< /Type /Font\n"
				     "   /Subtype /CIDFontType2\n"
				     "   /BaseFont /%s+%s\n"
				     "   /CIDSystemInfo\n"
				     "   << /Registry (Adobe)\n"
				     "      /Ordering (Identity)\n"
				     "      /Supplement 0\n"
				     "   >>\n"
				     "   /FontDescriptor %d 0 R\n"
				     "   /W [0 [",
				     cidfont_dict.id,
				     tag,
				     subset.ps_name,
				     descriptor.id);
	for (i = 0; i < font_subset->num_glyphs; i++)
	    _cairo_output_stream_printf (surface->output,
					 " %f",
					 (subset.widths[i]*PDF_UNITS_PER_EM));
	_cairo_output_stream_printf (surface->output,
				     " ]]\n"
				     ">>\n"
				     "endobj\n");
	_cairo_pdf_surface_update_object (surface, subset_resource);
	_cairo_output_stream_printf (surface->output,
				     "%d 0 obj\n"
				     "<< /Type /Font\n"
				     "   /Subtype /Type0\n"
				     "   /BaseFont /%s+%s\n"
				     "   /Encoding /Identity-H\n"
				     "   /DescendantFonts [ %d 0 R]\n",
				     subset_resource.id,
				     tag,
				     subset.ps_name,
				     cidfont_dict.id);
	if (to_unicode_stream.id != 0)
	    _cairo_output_stream_printf (surface->output,
					 "   /ToUnicode %d 0 R\n",
					 to_unicode_stream.id);
	_cairo_output_stream_printf (surface->output,
				     ">>\n"
				     "endobj\n");
    }
    font.font_id = font_subset->font_id;
    font.subset_id = font_subset->subset_id;
    font.subset_resource = subset_resource;
    status = _cairo_array_append (&surface->fonts, &font);
    _cairo_truetype_subset_fini (&subset);
    return status;
}
static cairo_int_status_t
_cairo_pdf_emit_imagemask (cairo_image_surface_t *image,
			     cairo_output_stream_t *stream)
{
    uint8_t *byte, output_byte;
    int row, col, num_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,
				 "BI\n"
				 "/IM true\n"
				 "/W %d\n"
				 "/H %d\n"
				 "/BPC 1\n"
				 "/D [1 0]\n",
				 image->width,
				 image->height);
    _cairo_output_stream_printf (stream,
				 "ID ");
    num_cols = (image->width + 7) / 8;
    for (row = 0; row < image->height; row++) {
	byte = image->data + row * image->stride;
	for (col = 0; col < num_cols; col++) {
	    output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte);
	    _cairo_output_stream_write (stream, &output_byte, 1);
	    byte++;
	}
    }
    _cairo_output_stream_printf (stream,
				 "\nEI\n");
    return _cairo_output_stream_get_status (stream);
}
static cairo_int_status_t
cairo_pdf_surface_emit_color_glyph (cairo_pdf_surface_t *surface,
				    cairo_scaled_font_t *scaled_font,
				    unsigned long	 glyph_index,
				    cairo_box_t         *bbox,
				    double              *width)
{
    cairo_rectangle_int_t extents;
    cairo_scaled_glyph_t *scaled_glyph;
    cairo_matrix_t mat;
    cairo_int_status_t status;
    double x_advance, y_advance;
    cairo_matrix_t font_matrix_inverse;
    cairo_surface_t *analysis;
    cairo_rectangle_int_t old_surface_extents;
    cairo_bool_t old_surface_bounded;
    cairo_paginated_mode_t old_paginated_mode;
    cairo_surface_t *glyph_surface = NULL;
    unsigned int regions_id = 0;
    cairo_surface_pattern_t surface_pattern;
    _cairo_scaled_font_freeze_cache (scaled_font);
    status = _cairo_scaled_glyph_lookup (scaled_font,
					 glyph_index,
					 CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE,
					 NULL, /* foreground color */
					 &scaled_glyph);
    if (status == CAIRO_INT_STATUS_SUCCESS)
	glyph_surface = cairo_surface_reference (scaled_glyph->recording_surface);
    _cairo_scaled_font_thaw_cache (scaled_font);
    if (unlikely (status))
	return status;
    analysis = _cairo_analysis_surface_create (&surface->base, TRUE);
    if (unlikely (analysis->status)) {
	status = _cairo_surface_set_error (&surface->base, analysis->status);
	goto cleanup;
    }
    extents.x = _cairo_fixed_to_double (scaled_glyph->bbox.p1.x);
    extents.y = _cairo_fixed_to_double (scaled_glyph->bbox.p1.y);
    extents.width = _cairo_fixed_to_double (scaled_glyph->bbox.p2.x) - extents.x;
    extents.height = _cairo_fixed_to_double (scaled_glyph->bbox.p2.y) - extents.y;
    old_surface_extents = surface->surface_extents;
    old_surface_bounded = surface->surface_bounded;
    old_paginated_mode = surface->paginated_mode;
    surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE;
    surface->type3_replay = TRUE;
    surface->surface_extents = extents;
    surface->surface_bounded = TRUE;
    status = _cairo_recording_surface_region_array_attach (glyph_surface, &regions_id);
    if (status)
	goto cleanup;
    status = _cairo_recording_surface_replay_and_create_regions (glyph_surface, regions_id,
                                                                 NULL, analysis, TRUE, FALSE);
    if (status)
	goto cleanup;
    surface->surface_extents = old_surface_extents;
    surface->surface_bounded = old_surface_bounded;
    surface->paginated_mode = old_paginated_mode;
    surface->type3_replay = FALSE;
    if (status ==  CAIRO_INT_STATUS_SUCCESS &&
	_cairo_analysis_surface_has_unsupported (analysis))
    {
	status = CAIRO_INT_STATUS_UNSUPPORTED;
    }
    cairo_surface_destroy (analysis);
    if (status)
	goto cleanup;
    _cairo_pattern_init_for_surface (&surface_pattern, glyph_surface);
    surface_pattern.region_array_id = regions_id;
    cairo_matrix_init_identity (&mat);
    cairo_matrix_multiply (&mat, &mat, &scaled_font->scale_inverse);
    /* transform glyph extents to operation space */
    cairo_box_t box;
    _cairo_box_from_rectangle (&box, &extents);
    _cairo_matrix_transform_bounding_box_fixed (&mat, &box, NULL);
    _cairo_box_round_to_rectangle (&box, &extents);
    status = cairo_matrix_invert (&mat);
    if (status) {
	status = CAIRO_INT_STATUS_UNSUPPORTED;
	goto cleanup;
    }
    cairo_pattern_set_matrix (&surface_pattern.base, &mat);
    x_advance = scaled_glyph->metrics.x_advance;
    y_advance = scaled_glyph->metrics.y_advance;
    font_matrix_inverse = scaled_font->font_matrix;
    status = cairo_matrix_invert (&font_matrix_inverse);
    if (status) {
	status = CAIRO_INT_STATUS_UNSUPPORTED;
	goto cleanup;
    }
    cairo_matrix_transform_distance (&font_matrix_inverse, &x_advance, &y_advance);
    *width = x_advance;
    *bbox = scaled_glyph->bbox;
    _cairo_matrix_transform_bounding_box_fixed (&scaled_font->scale_inverse,
						bbox, NULL);
    _cairo_output_stream_printf (surface->output,
				 "%f 0 d0\n",
				 x_advance);
    _cairo_pdf_surface_paint_surface_pattern (surface,
					      CAIRO_OPERATOR_OVER,
					      &surface_pattern.base,
					      CAIRO_ANALYSIS_SOURCE_NONE,
					      &extents,
					      1.0, /* alpha */
					      NULL, /* smask_res */
					      FALSE); /* mask */
  cleanup:
    cairo_surface_destroy (glyph_surface);
    return status;
}
static cairo_int_status_t
cairo_pdf_surface_emit_color_glyph_image (cairo_pdf_surface_t *surface,
					  cairo_scaled_font_t *scaled_font,
					  unsigned long	       glyph_index,
					  cairo_box_t         *bbox,
					  double              *width)
{
    cairo_rectangle_int_t extents;
    cairo_pattern_t *image_pattern;
    cairo_scaled_glyph_t *scaled_glyph;
    cairo_matrix_t mat;
    cairo_int_status_t status, status2;
    double x_advance, y_advance;
    cairo_matrix_t font_matrix_inverse;
    _cairo_scaled_font_freeze_cache (scaled_font);
    status = _cairo_scaled_glyph_lookup (scaled_font,
					 glyph_index,
					 CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE,
					 NULL, /* foreground color */
					 &scaled_glyph);
    if (unlikely (status))
	goto FAIL;
    extents.x = _cairo_fixed_to_double (scaled_glyph->bbox.p1.x);
    extents.y = _cairo_fixed_to_double (scaled_glyph->bbox.p1.y);
    extents.width = _cairo_fixed_to_double (scaled_glyph->bbox.p2.x) - extents.x;
    extents.height = _cairo_fixed_to_double (scaled_glyph->bbox.p2.y) - extents.y;
    image_pattern = cairo_pattern_create_for_surface (&scaled_glyph->color_surface->base);
    cairo_matrix_init_translate (&mat, extents.x, extents.y);
    cairo_matrix_multiply (&mat, &mat, &scaled_font->scale_inverse);
    status2 = cairo_matrix_invert (&mat);
    cairo_pattern_set_matrix (image_pattern, &mat);
    x_advance = scaled_glyph->metrics.x_advance;
    y_advance = scaled_glyph->metrics.y_advance;
    font_matrix_inverse = scaled_font->font_matrix;
    status2 = cairo_matrix_invert (&font_matrix_inverse);
    /* The invertability of font_matrix is tested in
     * pdf_operators_show_glyphs before any glyphs are mapped to the
     * subset. */
    assert (status2 == CAIRO_INT_STATUS_SUCCESS);
    cairo_matrix_transform_distance (&font_matrix_inverse, &x_advance, &y_advance);
    *width = x_advance;
    *bbox = scaled_glyph->bbox;
    _cairo_matrix_transform_bounding_box_fixed (&scaled_font->scale_inverse,
						bbox, NULL);
    _cairo_output_stream_printf (surface->output,
				 "%f 0 d0\n",
				 x_advance);
    _cairo_pdf_surface_paint_surface_pattern (surface,
					      CAIRO_OPERATOR_OVER,
					      image_pattern,
					      CAIRO_ANALYSIS_SOURCE_NONE,
					      &extents,
					      1.0, /* alpha */
					      NULL, /* smask_res */
					      FALSE); /* mask */
    cairo_pattern_destroy (image_pattern);
  FAIL:
    _cairo_scaled_font_thaw_cache (scaled_font);
    return status;
}
static cairo_int_status_t
_cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t		*surface,
					   cairo_scaled_font_subset_t	*font_subset)
{
    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
    cairo_pdf_resource_t *glyphs, encoding, char_procs, subset_resource, to_unicode_stream;
    cairo_pdf_font_t font;
    double *widths;
    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;
    if (font_subset->num_glyphs == 0)
	return CAIRO_STATUS_SUCCESS;
    subset_resource = _cairo_pdf_surface_get_font_resource (surface,
							    font_subset->font_id,
							    font_subset->subset_id);
    if (subset_resource.id == 0)
	return CAIRO_STATUS_SUCCESS;
    glyphs = _cairo_malloc_ab (font_subset->num_glyphs, sizeof (cairo_pdf_resource_t));
    if (unlikely (glyphs == NULL))
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    widths = _cairo_malloc_ab (font_subset->num_glyphs, sizeof (double));
    if (unlikely (widths == NULL)) {
        free (glyphs);
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    }
    _cairo_pdf_group_resources_clear (&surface->resources);
    type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font,
						       NULL,
						       _cairo_pdf_emit_imagemask,
						       surface->font_subsets,
						       FALSE);
    if (unlikely (type3_surface->status)) {
        free (glyphs);
        free (widths);
	return type3_surface->status;
    }
    _cairo_type3_glyph_surface_set_font_subsets_callback (type3_surface,
							  _cairo_pdf_surface_add_font,
							  surface);
    for (i = 0; i < font_subset->num_glyphs; i++) {
	status = _cairo_pdf_surface_open_stream (surface,
						 NULL,
						 surface->compress_streams,
						 NULL);
	if (unlikely (status))
	    break;
	glyphs[i] = surface->pdf_stream.self;
	status = _cairo_type3_glyph_surface_emit_glyph (type3_surface,
							surface->output,
							font_subset->glyphs[i],
							&bbox,
							&widths[i]);
	if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
	    status = cairo_pdf_surface_emit_color_glyph (surface,
							 font_subset->scaled_font,
							 font_subset->glyphs[i],
							 &bbox,
							 &widths[i]);
	    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
		status = cairo_pdf_surface_emit_color_glyph_image (surface,
								   font_subset->scaled_font,
								   font_subset->glyphs[i],
								   &bbox,
								   &widths[i]);
	    }
	}
	if (unlikely (status))
	    break;
	status = _cairo_pdf_surface_close_stream (surface);
	if (unlikely (status))
	    break;
        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_destroy (type3_surface);
    if (unlikely (status)) {
	free (glyphs);
	free (widths);
	return status;
    }
    encoding = _cairo_pdf_surface_new_object (surface);
    if (encoding.id == 0) {
	free (glyphs);
	free (widths);
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    }
    _cairo_output_stream_printf (surface->output,
				 "%d 0 obj\n"
				 "<< /Type /Encoding\n"
				 "   /Differences [0", encoding.id);
    for (i = 0; i < font_subset->num_glyphs; i++)
	_cairo_output_stream_printf (surface->output,
				     " /%d", i);
    _cairo_output_stream_printf (surface->output,
				 "]\n"
				 ">>\n"
				 "endobj\n");
    char_procs = _cairo_pdf_surface_new_object (surface);
    if (char_procs.id == 0) {
	free (glyphs);
	free (widths);
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    }
    _cairo_output_stream_printf (surface->output,
				 "%d 0 obj\n"
				 "<<\n", char_procs.id);
    for (i = 0; i < font_subset->num_glyphs; i++)
	_cairo_output_stream_printf (surface->output,
				     " /%d %d 0 R\n",
				     i, glyphs[i].id);
    _cairo_output_stream_printf (surface->output,
				 ">>\n"
				 "endobj\n");
    free (glyphs);
    status = _cairo_pdf_surface_emit_to_unicode_stream (surface,
	                                                font_subset,
							&to_unicode_stream);
    if (_cairo_int_status_is_error (status)) {
	free (widths);
	return status;
    }
    _cairo_pdf_surface_update_object (surface, subset_resource);
    _cairo_output_stream_printf (surface->output,
				 "%d 0 obj\n"
				 "<< /Type /Font\n"
				 "   /Subtype /Type3\n"
				 "   /FontBBox [%f %f %f %f]\n"
				 "   /FontMatrix [ 1 0 0 -1 0 0 ]\n"
				 "   /Encoding %d 0 R\n"
				 "   /CharProcs %d 0 R\n"
				 "   /FirstChar 0\n"
				 "   /LastChar %d\n",
				 subset_resource.id,
				 _cairo_fixed_to_double (font_bbox.p1.x),
				 _cairo_fixed_to_double (font_bbox.p1.y),
				 _cairo_fixed_to_double (font_bbox.p2.x),
				 _cairo_fixed_to_double (font_bbox.p2.y),
				 encoding.id,
				 char_procs.id,
				 font_subset->num_glyphs - 1);
    _cairo_output_stream_printf (surface->output,
				 "   /Widths [");
    for (i = 0; i < font_subset->num_glyphs; i++)
	_cairo_output_stream_printf (surface->output, " %f", widths[i]);
    _cairo_output_stream_printf (surface->output,
				 "]\n");
    free (widths);
    _cairo_output_stream_printf (surface->output,
				 "   /Resources\n");
    _cairo_pdf_surface_emit_group_resources (surface, &surface->resources, FALSE);
    if (to_unicode_stream.id != 0)
        _cairo_output_stream_printf (surface->output,
                                     "    /ToUnicode %d 0 R\n",
                                     to_unicode_stream.id);
    _cairo_output_stream_printf (surface->output,
				 ">>\n"
				 "endobj\n");
    font.font_id = font_subset->font_id;
    font.subset_id = font_subset->subset_id;
    font.subset_resource = subset_resource;
    return _cairo_array_append (&surface->fonts, &font);
}
static cairo_int_status_t
_cairo_pdf_surface_emit_unscaled_font_subset (cairo_scaled_font_subset_t *font_subset,
                                              void			 *closure)
{
    cairo_pdf_surface_t *surface = closure;
    cairo_int_status_t status;
    status = _cairo_pdf_surface_emit_cff_font_subset (surface, font_subset);
    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
	return status;
    status = _cairo_pdf_surface_emit_truetype_font_subset (surface, font_subset);
    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
	return status;
    status = _cairo_pdf_surface_emit_type1_font_subset (surface, font_subset);
    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
	return status;
    status = _cairo_pdf_surface_emit_cff_fallback_font (surface, font_subset);
    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
	return status;
    status = _cairo_pdf_surface_emit_type1_fallback_font (surface, font_subset);
    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
	return status;
    ASSERT_NOT_REACHED;
    return CAIRO_INT_STATUS_SUCCESS;
}
static cairo_int_status_t
_cairo_pdf_surface_emit_scaled_font_subset (cairo_scaled_font_subset_t *font_subset,
                                            void		       *closure)
{
    cairo_pdf_surface_t *surface = closure;
    cairo_int_status_t status;
    status = _cairo_pdf_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_int_status_t
5
_cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface)
{
    cairo_int_status_t status;
5
    status = _cairo_scaled_font_subsets_foreach_unscaled (surface->font_subsets,
                                                          _cairo_pdf_surface_emit_unscaled_font_subset,
                                                          surface);
5
    if (unlikely (status))
	goto BAIL;
5
    status = _cairo_scaled_font_subsets_foreach_scaled (surface->font_subsets,
                                                        _cairo_pdf_surface_emit_scaled_font_subset,
                                                        surface);
5
BAIL:
5
    _cairo_scaled_font_subsets_destroy (surface->font_subsets);
5
    surface->font_subsets = NULL;
5
    return status;
}
static cairo_int_status_t
5
_cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface,
				  cairo_pdf_resource_t catalog)
{
    cairo_status_t status;
5
    status = _cairo_pdf_surface_object_begin (surface, catalog);
5
    if (unlikely (status))
	return status;
5
    _cairo_output_stream_printf (surface->object_stream.stream,
				 "<< /Type /Catalog\n"
				 "   /Pages %d 0 R\n",
				 surface->pages_resource.id);
5
    if (surface->struct_tree_root.id != 0) {
	_cairo_output_stream_printf (surface->object_stream.stream,
				     "   /StructTreeRoot %d 0 R\n",
				     surface->struct_tree_root.id);
	if (surface->tagged) {
	    _cairo_output_stream_printf (surface->object_stream.stream,
					 "   /MarkInfo << /Marked true >>\n");
	}
    }
5
    if (surface->outlines_dict_res.id != 0) {
	_cairo_output_stream_printf (surface->object_stream.stream,
				     "   /Outlines %d 0 R\n",
				     surface->outlines_dict_res.id);
    }
5
    if (surface->page_labels_res.id != 0) {
	_cairo_output_stream_printf (surface->object_stream.stream,
				     "   /PageLabels %d 0 R\n",
				     surface->page_labels_res.id);
    }
5
    if (surface->names_dict_res.id != 0) {
	_cairo_output_stream_printf (surface->object_stream.stream,
				     "   /Names %d 0 R\n",
				     surface->names_dict_res.id);
    }
5
    _cairo_output_stream_printf (surface->object_stream.stream,
				 ">>\n");
5
    _cairo_pdf_surface_object_end (surface);
5
    return status;
}
static long long
_cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface)
{
    cairo_pdf_object_t *object;
    int num_objects, i;
    long long offset;
    num_objects = _cairo_array_num_elements (&surface->objects);
    offset = _cairo_output_stream_get_position (surface->output);
    _cairo_output_stream_printf (surface->output,
				 "xref\n"
				 "%d %d\n",
				 0, num_objects + 1);
    _cairo_output_stream_printf (surface->output,
				 "0000000000 65535 f \n");
    for (i = 0; i < num_objects; i++) {
	object = _cairo_array_index (&surface->objects, i);
	_cairo_output_stream_printf (surface->output,
				     "%010lld 00000 n \n", object->u.offset);
    }
    return offset;
}
static void
147
_cairo_write_xref_stream_entry (cairo_output_stream_t *stream,
				int                    id,
				int                    type,
				int                    field2_size,
				long long              field2,
				int                    field3,
				cairo_bool_t           write_as_comments)
{
    char buf[20];
    int i;
147
    if (write_as_comments) {
	_cairo_output_stream_printf (stream, "%% %5d %2d %10lld  %d\n", id, type, field2, field3);
    } else {
	/* Each field is big endian */
147
	buf[0] = type; /* field 1 */
441
	for (i = field2_size - 1; i >= 0; i--) {
294
	    buf[i + 1] = field2 & 0xff;
294
	    field2 >>= 8;
	}
147
	buf[field2_size + 1] = field3 >> 8;
147
	buf[field2_size + 2] = field3 & 0xff;
147
	_cairo_output_stream_write (stream, buf, field2_size + 3);
    }
147
}
static void
5
_cairo_write_xref_stream_entries (cairo_pdf_surface_t   *surface,
				  cairo_output_stream_t *stream,
				  int                    field2_size,
				  cairo_bool_t           write_as_comments)
{
    cairo_pdf_object_t *object;
    int num_objects, i;
    /* PDF requires this to be first entry */
5
    _cairo_write_xref_stream_entry (stream,
				    0,
				    PDF_OBJECT_FREE,
				    field2_size,
				    0, /* next free object number */
				    0xffff, /* next generation number */
				    write_as_comments);
5
    num_objects = _cairo_array_num_elements (&surface->objects);
147
    for (i = 0; i < num_objects; i++) {
142
	object = _cairo_array_index (&surface->objects, i);
142
	if (object->type == PDF_OBJECT_UNCOMPRESSED) {
117
	    _cairo_write_xref_stream_entry (stream,
					    i + 1,
117
					    object->type,
					    field2_size,
					    object->u.offset,
					    0, /* generation number */
					    write_as_comments);
25
	} else if (object->type == PDF_OBJECT_COMPRESSED) {
25
	    _cairo_write_xref_stream_entry (stream,
					    i + 1,
25
					    object->type,
					    field2_size,
25
					    object->u.compressed_obj.xref_stream.id,
					    object->u.compressed_obj.index,
					    write_as_comments);
	} else {
	    _cairo_write_xref_stream_entry (stream,
					    i + 1,
					    PDF_OBJECT_FREE,
					    field2_size,
					    0,
					    0xffff,
					    write_as_comments);
	}
    }
5
}
static cairo_int_status_t
5
_cairo_pdf_surface_write_xref_stream (cairo_pdf_surface_t  *surface,
				      cairo_pdf_resource_t  xref_res,
				      cairo_pdf_resource_t  root_res,
				      cairo_pdf_resource_t  info_res,
				      long long            *xref_offset)
{
    cairo_output_stream_t *mem_stream;
    cairo_output_stream_t *xref_stream;
    long long offset;
    int offset_bytes;
    cairo_status_t status;
5
    *xref_offset = _cairo_output_stream_get_position (surface->output);
    /* Find the minimum number of bytes required to represent offsets in the generated file (up to this point). */
5
    offset_bytes = 0;
5
    offset = *xref_offset;
15
    while (offset > 0) {
10
	offset >>= 8;
10
	offset_bytes++;
    }
5
    mem_stream = _cairo_memory_stream_create ();
5
    xref_stream = _cairo_deflate_stream_create (mem_stream);
5
    _cairo_write_xref_stream_entries (surface, xref_stream, offset_bytes, FALSE);
5
    status = _cairo_output_stream_destroy (xref_stream);
5
    if (unlikely (status))
	return status;
5
    _cairo_pdf_surface_update_object (surface, xref_res);
5
    _cairo_output_stream_printf (surface->output,
				 "%d 0 obj\n"
				 "<< /Type /XRef\n"
				 "   /Length %d\n"
				 "   /Filter /FlateDecode\n"
				 "   /Size %d\n"
				 "   /W [1 %d 2]\n"
				 "   /Root %d 0 R\n"
				 "   /Info %d 0 R\n"
				 ">>\n",
				 xref_res.id,
				 _cairo_memory_stream_length (mem_stream),
				 surface->next_available_resource.id,
				 offset_bytes,
				 root_res.id,
				 info_res.id);
5
    if (!surface->compress_streams) {
	/* Adobe Reader requires xref streams to be flate encoded (PDF
	 * Reference 1.7, implementation note 20). This means
	 * compression must always be enabled on this stream. To
	 * facilitate debugging when compress_stream is disabled, emit
	 * a human readable format of the xref stream as PDF comments.
	 */
	_cairo_output_stream_printf (surface->output,
				     "%%   id   type  offset/obj  gen/index\n");
	_cairo_write_xref_stream_entries (surface, surface->output, offset_bytes, TRUE);
    }
5
    _cairo_output_stream_printf (surface->output,
				 "stream\n");
5
    _cairo_memory_stream_copy (mem_stream, surface->output);
5
    status = _cairo_output_stream_destroy (mem_stream);
5
    if (unlikely (status))
	return status;
5
    _cairo_output_stream_printf (surface->output,
				 "\n"
				 "endstream\n"
				 "endobj\n");
5
    return _cairo_output_stream_get_status (surface->output);
}
static cairo_int_status_t
_cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t	*surface,
				     cairo_pdf_smask_group_t	*group)
{
    cairo_pdf_resource_t mask_group;
    cairo_pdf_resource_t smask;
    cairo_pdf_smask_group_t *smask_group;
    cairo_pdf_resource_t pattern_res, gstate_res;
    cairo_int_status_t status;
    cairo_box_double_t bbox;
    /* Create mask group */
    _get_bbox_from_extents (&group->extents, &bbox);
    status = _cairo_pdf_surface_open_group (surface, &bbox, NULL);
    if (unlikely (status))
	return status;
    if (_can_paint_pattern (group->mask)) {
	_cairo_output_stream_printf (surface->output, "q\n");
	status = _cairo_pdf_surface_paint_pattern (surface,
						   CAIRO_OPERATOR_OVER,
						   group->mask,
						   CAIRO_ANALYSIS_MASK_MASK,
						   &group->extents,
						   1.0, /* alpha */
						   FALSE); /* mask */
	if (unlikely (status))
	    return status;
	_cairo_output_stream_printf (surface->output, "Q\n");
    } else {
	pattern_res.id = 0;
	gstate_res.id = 0;
	status = _cairo_pdf_surface_add_pdf_pattern (surface, group->mask,
						     CAIRO_OPERATOR_OVER,
						     CAIRO_ANALYSIS_MASK_MASK,
						     NULL,
						     &pattern_res, &gstate_res);
	if (unlikely (status))
	    return status;
	if (gstate_res.id != 0) {
	    smask_group = _cairo_pdf_surface_create_smask_group (surface, &group->extents);
	    if (unlikely (smask_group == NULL))
		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
	    smask_group->width = group->width;
	    smask_group->height = group->height;
	    smask_group->operation = PDF_PAINT;
	    smask_group->source = cairo_pattern_reference (group->mask);
	    smask_group->source_res = pattern_res;
	    status = _cairo_pdf_surface_add_smask_group (surface, smask_group);
	    if (unlikely (status)) {
		_cairo_pdf_smask_group_destroy (smask_group);
		return status;
	    }
	    status = _cairo_pdf_surface_add_smask (surface, gstate_res);
	    if (unlikely (status))
		return status;
	    status = _cairo_pdf_surface_add_xobject (surface, smask_group->group_res);
	    if (unlikely (status))
		return status;
	    _cairo_output_stream_printf (surface->output,
					 "q /s%d gs /x%d Do Q\n",
					 gstate_res.id,
					 smask_group->group_res.id);
	} else {
	    status = _cairo_pdf_surface_select_pattern (surface, group->mask, pattern_res, FALSE);
	    if (unlikely (status))
		return status;
	    _cairo_output_stream_printf (surface->output,
					 "%f %f %f %f re f\n",
					 bbox.p1.x,
					 bbox.p1.y,
					 bbox.p2.x - bbox.p1.x,
					 bbox.p2.y - bbox.p1.y);
	    status = _cairo_pdf_surface_unselect_pattern (surface);
	    if (unlikely (status))
		return status;
	}
    }
    status = _cairo_pdf_surface_close_group (surface, &mask_group);
    if (unlikely (status))
	return status;
    /* Create source group */
    status = _cairo_pdf_surface_open_group (surface, &bbox, &group->source_res);
    if (unlikely (status))
	return status;
    if (_can_paint_pattern (group->source)) {
	_cairo_output_stream_printf (surface->output, "q\n");
	status = _cairo_pdf_surface_paint_pattern (surface,
						   CAIRO_OPERATOR_OVER,
						   group->source,
						   CAIRO_ANALYSIS_MASK_MASK,
						   &group->extents,
						   1.0, /* alpha */
						   FALSE); /* mask */
	if (unlikely (status))
	    return status;
	_cairo_output_stream_printf (surface->output, "Q\n");
    } else {
	pattern_res.id = 0;
	gstate_res.id = 0;
	status = _cairo_pdf_surface_add_pdf_pattern (surface, group->source,
						     CAIRO_OPERATOR_OVER,
						     CAIRO_ANALYSIS_MASK_MASK,
						     NULL,
						     &pattern_res, &gstate_res);
	if (unlikely (status))
	    return status;
	if (gstate_res.id != 0) {
	    smask_group = _cairo_pdf_surface_create_smask_group (surface, &group->extents);
	    if (unlikely (smask_group == NULL))
		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
	    smask_group->operation = PDF_PAINT;
	    smask_group->source = cairo_pattern_reference (group->source);
	    smask_group->source_res = pattern_res;
	    status = _cairo_pdf_surface_add_smask_group (surface, smask_group);
	    if (unlikely (status)) {
		_cairo_pdf_smask_group_destroy (smask_group);
		return status;
	    }
	    status = _cairo_pdf_surface_add_smask (surface, gstate_res);
	    if (unlikely (status))
		return status;
	    status = _cairo_pdf_surface_add_xobject (surface, smask_group->group_res);
	    if (unlikely (status))
		return status;
	    _cairo_output_stream_printf (surface->output,
					 "q /s%d gs /x%d Do Q\n",
					 gstate_res.id,
					 smask_group->group_res.id);
	} else {
	    status = _cairo_pdf_surface_select_pattern (surface, group->source, pattern_res, FALSE);
	    if (unlikely (status))
		return status;
	    _cairo_output_stream_printf (surface->output,
					 "%f %f %f %f re f\n",
					 bbox.p1.x,
					 bbox.p1.y,
					 bbox.p2.x - bbox.p1.x,
					 bbox.p2.y - bbox.p1.y);
	    status = _cairo_pdf_surface_unselect_pattern (surface);
	    if (unlikely (status))
		return status;
	}
    }
    status = _cairo_pdf_surface_close_group (surface, NULL);
    if (unlikely (status))
	return status;
    /* Create an smask based on the alpha component of mask_group */
    smask = _cairo_pdf_surface_new_object (surface);
    if (smask.id == 0)
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    _cairo_output_stream_printf (surface->output,
				 "%d 0 obj\n"
				 "<< /Type /Mask\n"
				 "   /S /Alpha\n"
				 "   /G %d 0 R\n"
				 ">>\n"
				 "endobj\n",
				 smask.id,
				 mask_group.id);
    /* Create a GState that uses the smask */
    _cairo_pdf_surface_update_object (surface, group->group_res);
    _cairo_output_stream_printf (surface->output,
				 "%d 0 obj\n"
				 "<< /Type /ExtGState\n"
				 "   /SMask %d 0 R\n"
				 "   /ca 1\n"
				 "   /CA 1\n"
				 "   /AIS false\n"
				 ">>\n"
				 "endobj\n",
				 group->group_res.id,
				 smask.id);
    return _cairo_output_stream_get_status (surface->output);
}
static cairo_int_status_t
_cairo_pdf_surface_write_smask_group (cairo_pdf_surface_t     *surface,
				      cairo_pdf_smask_group_t *group)
{
    double old_width, old_height;
    cairo_bool_t old_in_xobject;
    cairo_int_status_t status;
    cairo_box_double_t bbox;
    cairo_rectangle_int_t old_surface_extents;
    old_width = surface->width;
    old_height = surface->height;
    old_surface_extents = surface->surface_extents;
    old_in_xobject = surface->in_xobject;
    surface->in_xobject = TRUE;
    _cairo_pdf_surface_set_size_internal (surface,
					  group->width,
					  group->height);
    _cairo_pdf_operators_reset (&surface->pdf_operators);
    /* _mask is a special case that requires two groups - source
     * and mask as well as a smask and gstate dictionary */
    if (group->operation == PDF_MASK) {
	status = _cairo_pdf_surface_write_mask_group (surface, group);
	goto RESTORE_SIZE;
    }
    _get_bbox_from_extents (&group->extents, &bbox);
    status = _cairo_pdf_surface_open_group (surface, &bbox, &group->group_res);
    if (unlikely (status))
	return status;
    status = _cairo_pdf_surface_select_pattern (surface,
						group->source,
						group->source_res,
						group->operation == PDF_STROKE);
    if (unlikely (status))
	return status;
    switch (group->operation) {
    case PDF_PAINT:
	_cairo_output_stream_printf (surface->output,
				     "0 0 %f %f re f\n",
				     surface->width, surface->height);
	break;
    case PDF_MASK:
	ASSERT_NOT_REACHED;
	break;
    case PDF_FILL:
	status = _cairo_pdf_operators_fill (&surface->pdf_operators,
					    &group->path,
					    group->fill_rule);
	break;
    case PDF_STROKE:
	status = _cairo_pdf_operators_stroke (&surface->pdf_operators,
					      &group->path,
					      &group->style,
					      &group->ctm,
					      &group->ctm_inverse);
	break;
    case PDF_SHOW_GLYPHS:
	status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators,
							group->utf8, group->utf8_len,
							group->glyphs, group->num_glyphs,
							group->clusters, group->num_clusters,
							group->cluster_flags,
							group->scaled_font);
	break;
    }
    if (unlikely (status))
	return status;
    status = _cairo_pdf_surface_unselect_pattern (surface);
    if (unlikely (status))
	return status;
    status = _cairo_pdf_surface_close_group (surface, NULL);
RESTORE_SIZE:
    surface->in_xobject = old_in_xobject;
    _cairo_pdf_surface_set_size_internal (surface,
					  old_width,
					  old_height);
    surface->surface_extents = old_surface_extents;
    _cairo_pdf_operators_reset (&surface->pdf_operators);
    return status;
}
static cairo_int_status_t
15
_cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface,
						    cairo_bool_t         finish)
{
    cairo_pdf_pattern_t pattern;
    cairo_pdf_smask_group_t *group;
    cairo_pdf_source_surface_t src_surface;
    unsigned int pattern_index, group_index, surface_index, doc_surface_index;
    cairo_int_status_t status;
    cairo_bool_t is_image;
    /* Writing out PDF_MASK groups will cause additional smask groups
     * to be appended to surface->smask_groups. Additional patterns
     * may also be appended to surface->patterns.
     *
     * Writing recording surface patterns will cause additional patterns
     * and groups to be appended.
     */
15
    pattern_index = 0;
15
    group_index = 0;
15
    surface_index = 0;
15
    doc_surface_index = 0;
40
    while ((pattern_index < _cairo_array_num_elements (&surface->page_patterns)) ||
40
	   (group_index < _cairo_array_num_elements (&surface->smask_groups)) ||
56
	   (surface_index < _cairo_array_num_elements (&surface->page_surfaces)) ||
11
	   (finish && (doc_surface_index < _cairo_array_num_elements (&surface->doc_surfaces))))
    {
5
	for (; group_index < _cairo_array_num_elements (&surface->smask_groups); group_index++) {
	    _cairo_array_copy_element (&surface->smask_groups, group_index, &group);
	    status = _cairo_pdf_surface_write_smask_group (surface, group);
	    if (unlikely (status))
		return status;
	}
5
	for (; pattern_index < _cairo_array_num_elements (&surface->page_patterns); pattern_index++) {
	    _cairo_array_copy_element (&surface->page_patterns, pattern_index, &pattern);
	    status = _cairo_pdf_surface_emit_pattern (surface, &pattern);
	    if (unlikely (status))
		return status;
	}
12
	for (; surface_index < _cairo_array_num_elements (&surface->page_surfaces); surface_index++) {
7
	    _cairo_array_copy_element (&surface->page_surfaces, surface_index, &src_surface);
7
	    status = _cairo_pdf_surface_emit_surface (surface, &src_surface, FALSE, &is_image);
7
	    if (unlikely (status))
		return status;
	}
5
	if (finish) {
5
	    for (; doc_surface_index < _cairo_array_num_elements (&surface->doc_surfaces); doc_surface_index++) {
4
		_cairo_array_copy_element (&surface->doc_surfaces, doc_surface_index, &src_surface);
4
		status = _cairo_pdf_surface_emit_surface (surface, &src_surface, FALSE, &is_image);
4
		if (unlikely (status))
		    return status;
	    }
	}
    }
15
    return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
5
_cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface)
{
    cairo_pdf_resource_t knockout, res;
    cairo_int_status_t status;
    unsigned int i, len;
    cairo_pdf_page_info_t *page_info;
5
    page_info = _cairo_array_last_element (&surface->pages);
5
    status = _cairo_pdf_surface_open_object_stream (surface);
5
    if (unlikely (status))
	return status;
5
    status = _cairo_pdf_interchange_write_page_objects (surface);
5
    if (unlikely (status))
	return status;
5
    _cairo_pdf_group_resources_clear (&surface->resources);
5
    if (surface->has_fallback_images) {
	cairo_rectangle_int_t extents;
	cairo_box_double_t    bbox;
4
	extents.x = 0;
4
	extents.y = 0;
4
	extents.width = ceil (surface->width);
4
	extents.height = ceil (surface->height);
4
	_get_bbox_from_extents (&extents, &bbox);
4
	status = _cairo_pdf_surface_open_knockout_group (surface, &bbox);
4
	if (unlikely (status))
	    return status;
4
	len = _cairo_array_num_elements (&surface->knockout_group);
11
	for (i = 0; i < len; i++) {
7
	    _cairo_array_copy_element (&surface->knockout_group, i, &res);
7
	    _cairo_output_stream_printf (surface->output,
					 "/x%d Do\n",
					 res.id);
7
	    status = _cairo_pdf_surface_add_xobject (surface, res);
7
	    if (unlikely (status))
		return status;
	}
4
	_cairo_output_stream_printf (surface->output,
				     "/x%d Do\n",
				     surface->content.id);
4
	status = _cairo_pdf_surface_add_xobject (surface, surface->content);
4
	if (unlikely (status))
	    return status;
4
	status = _cairo_pdf_surface_close_group (surface, &knockout);
4
	if (unlikely (status))
	    return status;
4
	_cairo_pdf_group_resources_clear (&surface->resources);
4
	status = _cairo_pdf_surface_open_content_stream (surface, NULL, NULL, FALSE, FALSE, -1);
4
	if (unlikely (status))
	    return status;
4
	_cairo_output_stream_printf (surface->output,
				     "/x%d Do\n",
				     knockout.id);
4
	status = _cairo_pdf_surface_add_xobject (surface, knockout);
4
	if (unlikely (status))
	    return status;
4
	status = _cairo_pdf_surface_close_content_stream (surface, FALSE);
4
	if (unlikely (status))
	    return status;
    }
5
    if (surface->thumbnail_image) {
	cairo_pdf_source_surface_entry_t entry;
	memset (&entry, 0, sizeof (entry));
	page_info->thumbnail = _cairo_pdf_surface_new_object (surface);
	entry.surface_res = page_info->thumbnail;
	_cairo_pdf_surface_emit_image (surface, surface->thumbnail_image, &entry);
    }
5
    page_info->content = surface->content;
5
    page_info->resources = surface->content_resources;
5
    page_info->struct_parents = surface->page_parent_tree;
5
    status = _cairo_pdf_surface_write_patterns_and_smask_groups (surface, FALSE);
5
    if (unlikely (status))
	return status;
5
    return _cairo_pdf_surface_close_object_stream (surface);
}
static cairo_int_status_t
7
_cairo_pdf_surface_analyze_surface_pattern_transparency (cairo_pdf_surface_t      *surface,
							 cairo_surface_pattern_t  *pattern)
{
    cairo_image_surface_t  *image;
    void		   *image_extra;
    cairo_int_status_t      status;
    cairo_image_transparency_t transparency;
7
    status = _cairo_surface_acquire_source_image (pattern->surface,
						  &image,
						  &image_extra);
7
    if (unlikely (status))
	return status;
7
    if (image->base.status)
	return image->base.status;
7
    transparency = _cairo_image_analyze_transparency (image);
7
    if (transparency == CAIRO_IMAGE_IS_OPAQUE)
	status = CAIRO_STATUS_SUCCESS;
    else
7
	status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
7
    _cairo_surface_release_source_image (pattern->surface, image, image_extra);
7
    return status;
}
static cairo_bool_t
22
_surface_pattern_supported (cairo_surface_pattern_t *pattern)
{
    cairo_extend_t extend;
22
    if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
15
	return TRUE;
7
    if (pattern->surface->backend->acquire_source_image == NULL)
	return FALSE;
    /* Does an ALPHA-only source surface even make sense? Maybe, but I
     * don't think it's worth the extra code to support it. */
/* XXX: Need to write this function here...
    if (pattern->surface->content == CAIRO_CONTENT_ALPHA)
	return FALSE;
*/
7
    extend = cairo_pattern_get_extend (&pattern->base);
7
    switch (extend) {
7
    case CAIRO_EXTEND_NONE:
    case CAIRO_EXTEND_REPEAT:
    case CAIRO_EXTEND_REFLECT:
    /* There's no point returning FALSE for EXTEND_PAD, as the image
     * surface does not currently implement it either */
    case CAIRO_EXTEND_PAD:
7
	return TRUE;
    }
    ASSERT_NOT_REACHED;
    return FALSE;
}
static cairo_bool_t
74
_pattern_supported (const cairo_pattern_t *pattern)
{
74
    switch (pattern->type) {
52
    case CAIRO_PATTERN_TYPE_SOLID:
    case CAIRO_PATTERN_TYPE_LINEAR:
    case CAIRO_PATTERN_TYPE_RADIAL:
    case CAIRO_PATTERN_TYPE_MESH:
    case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
52
	return TRUE;
22
    case CAIRO_PATTERN_TYPE_SURFACE:
22
	return _surface_pattern_supported ((cairo_surface_pattern_t *) pattern);
    default:
	ASSERT_NOT_REACHED;
	return FALSE;
    }
}
static cairo_bool_t
74
_pdf_operator_supported (cairo_operator_t op)
{
74
    switch (op) {
29
    case CAIRO_OPERATOR_OVER:
    case CAIRO_OPERATOR_MULTIPLY:
    case CAIRO_OPERATOR_SCREEN:
    case CAIRO_OPERATOR_OVERLAY:
    case CAIRO_OPERATOR_DARKEN:
    case CAIRO_OPERATOR_LIGHTEN:
    case CAIRO_OPERATOR_COLOR_DODGE:
    case CAIRO_OPERATOR_COLOR_BURN:
    case CAIRO_OPERATOR_HARD_LIGHT:
    case CAIRO_OPERATOR_SOFT_LIGHT:
    case CAIRO_OPERATOR_DIFFERENCE:
    case CAIRO_OPERATOR_EXCLUSION:
    case CAIRO_OPERATOR_HSL_HUE:
    case CAIRO_OPERATOR_HSL_SATURATION:
    case CAIRO_OPERATOR_HSL_COLOR:
    case CAIRO_OPERATOR_HSL_LUMINOSITY:
29
	return TRUE;
45
    default:
    case CAIRO_OPERATOR_CLEAR:
    case CAIRO_OPERATOR_SOURCE:
    case CAIRO_OPERATOR_IN:
    case CAIRO_OPERATOR_OUT:
    case CAIRO_OPERATOR_ATOP:
    case CAIRO_OPERATOR_DEST:
    case CAIRO_OPERATOR_DEST_OVER:
    case CAIRO_OPERATOR_DEST_IN:
    case CAIRO_OPERATOR_DEST_OUT:
    case CAIRO_OPERATOR_DEST_ATOP:
    case CAIRO_OPERATOR_XOR:
    case CAIRO_OPERATOR_ADD:
    case CAIRO_OPERATOR_SATURATE:
45
	return FALSE;
    }
}
static cairo_int_status_t
74
_cairo_pdf_surface_analyze_operation (cairo_pdf_surface_t  *surface,
				      cairo_operator_t      op,
				      const cairo_pattern_t      *pattern,
				      const cairo_rectangle_int_t	 *extents)
{
74
    if (surface->force_fallbacks &&
	surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
    {
	return CAIRO_INT_STATUS_UNSUPPORTED;
    }
74
    if (! _pattern_supported (pattern))
	return CAIRO_INT_STATUS_UNSUPPORTED;
74
    if (_pdf_operator_supported (op)) {
29
	if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
15
	    cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
15
	    if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
15
		if (pattern->extend == CAIRO_EXTEND_PAD) {
		    cairo_box_t box;
		    cairo_rectangle_int_t rect;
		    cairo_rectangle_int_t rec_extents;
		    /* get the operation extents in pattern space */
		    _cairo_box_from_rectangle (&box, extents);
		    _cairo_matrix_transform_bounding_box_fixed (&pattern->matrix, &box, NULL);
		    _cairo_box_round_to_rectangle (&box, &rect);
		    /* Check if surface needs padding to fill extents */
		    if (_cairo_surface_get_extents (surface_pattern->surface, &rec_extents)) {
			if (_cairo_fixed_integer_ceil(box.p1.x) < rec_extents.x ||
			    _cairo_fixed_integer_ceil(box.p1.y) < rec_extents.y ||
			    _cairo_fixed_integer_floor(box.p2.x) > rec_extents.x + rec_extents.width ||
			    _cairo_fixed_integer_floor(box.p2.y) > rec_extents.y + rec_extents.height)
			{
			    return CAIRO_INT_STATUS_UNSUPPORTED;
			}
		    }
		}
15
		return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
	    }
	}
14
	return CAIRO_STATUS_SUCCESS;
    }
    /* The SOURCE operator is supported if the pattern is opaque or if
     * there is nothing painted underneath. */
45
    if (op == CAIRO_OPERATOR_SOURCE) {
43
	if (surface->type3_replay)
	    return CAIRO_INT_STATUS_UNSUPPORTED;
43
	if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
7
	    cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
7
	    if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
		if (_cairo_pattern_is_opaque (pattern, extents)) {
		    return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
		} else {
		    /* FIXME: The analysis surface does not yet have
		     * the capability to analyze a non opaque recording
		     * surface and mark it supported if there is
		     * nothing underneath. For now recording surfaces of
		     * type CONTENT_COLOR_ALPHA painted with
		     * OPERATOR_SOURCE will result in a fallback
		     * image. */
		    return CAIRO_INT_STATUS_UNSUPPORTED;
		}
	    } else {
7
		return _cairo_pdf_surface_analyze_surface_pattern_transparency (surface,
										surface_pattern);
	    }
	}
36
	if (_cairo_pattern_is_opaque (pattern, extents))
24
	    return CAIRO_STATUS_SUCCESS;
	else
12
	    return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
    }
2
    return CAIRO_INT_STATUS_UNSUPPORTED;
}
static cairo_bool_t
29
_cairo_pdf_surface_operation_supported (cairo_pdf_surface_t  *surface,
					cairo_operator_t      op,
					const cairo_pattern_t      *pattern,
					const cairo_rectangle_int_t *extents)
{
29
    return _cairo_pdf_surface_analyze_operation (surface, op, pattern, extents) != CAIRO_INT_STATUS_UNSUPPORTED;
}
static cairo_int_status_t
7
_cairo_pdf_surface_start_fallback (cairo_pdf_surface_t *surface)
{
    cairo_box_double_t bbox;
    cairo_int_status_t status;
7
    status = _cairo_pdf_surface_close_content_stream (surface, FALSE);
7
    if (unlikely (status))
	return status;
7
    status = _cairo_array_append (&surface->knockout_group, &surface->content);
7
    if (unlikely (status))
	return status;
7
    _cairo_pdf_group_resources_clear (&surface->resources);
7
    bbox.p1.x = 0;
7
    bbox.p1.y = 0;
7
    bbox.p2.x = surface->width;
7
    bbox.p2.y = surface->height;
7
    status = _cairo_pdf_surface_open_content_stream (surface, &bbox, NULL, TRUE, TRUE, -1);
7
    if (unlikely (status))
	return status;
7
    return _cairo_pdf_interchange_begin_page_content (surface);
}
/* If source is an opaque image and mask is an image and both images
 * have the same bounding box we can emit them as a image/smask pair.
 */
static cairo_int_status_t
_cairo_pdf_surface_emit_combined_smask (cairo_pdf_surface_t         *surface,
					cairo_operator_t	     op,
					const cairo_pattern_t       *source,
					const cairo_pattern_t       *mask,
					const cairo_rectangle_int_t *extents)
{
    cairo_int_status_t status;
    cairo_image_surface_t  *image;
    void		   *image_extra;
    cairo_image_transparency_t transparency;
    int src_width, src_height;
    int mask_width, mask_height;
    double src_x_offset, src_y_offset;
    double mask_x_offset, mask_y_offset;
    double src_x1, src_y1, src_x2, src_y2;
    double mask_x1, mask_y1, mask_x2, mask_y2;
    cairo_matrix_t p2u;
    double src_radius, mask_radius, e;
    cairo_bool_t need_smask;
    cairo_pdf_source_surface_entry_t *pdf_source;
    /* Check that source and mask are images */
    if (!((source->type == CAIRO_PATTERN_TYPE_SURFACE || source->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) &&
	  (mask->type == CAIRO_PATTERN_TYPE_SURFACE || mask->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE)))
	return CAIRO_INT_STATUS_UNSUPPORTED;
    if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
	((cairo_surface_pattern_t *) source)->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
    {
	return CAIRO_INT_STATUS_UNSUPPORTED;
    }
    if (mask->type == CAIRO_PATTERN_TYPE_SURFACE &&
	((cairo_surface_pattern_t *) mask)->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
    {
	return CAIRO_INT_STATUS_UNSUPPORTED;
    }
    if (source->extend != CAIRO_EXTEND_NONE || mask->extend != CAIRO_EXTEND_NONE)
	return CAIRO_INT_STATUS_UNSUPPORTED;
    /* Check that source is opaque and get image sizes */
    status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, source,
								   &image, &image_extra);
    if (unlikely (status))
	return status;
    if (image->base.status)
	return image->base.status;
    src_width = image->width;
    src_height = image->height;
    if (source->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) {
	cairo_surface_get_device_offset (&image->base, &src_x_offset, &src_y_offset);
    } else {
	src_x_offset = 0;
	src_y_offset = 0;
    }
    transparency = _cairo_image_analyze_transparency (image);
    _cairo_pdf_surface_release_source_image_from_pattern (surface, source, image, image_extra);
    if (transparency != CAIRO_IMAGE_IS_OPAQUE)
	return CAIRO_INT_STATUS_UNSUPPORTED;
    status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, mask,
								   &image, &image_extra);
    if (unlikely (status))
	return status;
    if (image->base.status)
	return image->base.status;
    mask_width = image->width;
    mask_height = image->height;
    if (mask->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) {
	cairo_surface_get_device_offset (&image->base, &mask_x_offset, &mask_y_offset);
    } else {
	mask_x_offset = 0;
	mask_y_offset = 0;
    }
    transparency = _cairo_image_analyze_transparency (image);
    need_smask = transparency != CAIRO_IMAGE_IS_OPAQUE;
    _cairo_pdf_surface_release_source_image_from_pattern (surface, mask, image, image_extra);
    /* Check that both images have the same extents with a tolerance
     * of half the smallest source pixel. */
    p2u = source->matrix;
    status = cairo_matrix_invert (&p2u);
    /* cairo_pattern_set_matrix ensures the matrix is invertible */
    assert (status == CAIRO_INT_STATUS_SUCCESS);
    src_x1 = 0;
    src_y1 = 0;
    src_x2 = src_width;
    src_y2 = src_height;
    cairo_matrix_transform_point (&p2u, &src_x1, &src_y1);
    cairo_matrix_transform_point (&p2u, &src_x2, &src_y2);
    src_radius = _cairo_matrix_transformed_circle_major_axis (&p2u, 0.5);
    p2u = mask->matrix;
    status = cairo_matrix_invert (&p2u);
    /* cairo_pattern_set_matrix ensures the matrix is invertible */
    assert (status == CAIRO_INT_STATUS_SUCCESS);
    mask_x1 = 0;
    mask_y1 = 0;
    mask_x2 = mask_width;
    mask_y2 = mask_height;
    cairo_matrix_transform_point (&p2u, &mask_x1, &mask_y1);
    cairo_matrix_transform_point (&p2u, &mask_x2, &mask_y2);
    mask_radius = _cairo_matrix_transformed_circle_major_axis (&p2u, 0.5);
    if (src_radius < mask_radius)
	e = src_radius;
    else
	e = mask_radius;
    if (fabs(src_x1 - mask_x1) > e ||
	fabs(src_x2 - mask_x2) > e ||
	fabs(src_y1 - mask_y1) > e ||
	fabs(src_y2 - mask_y2) > e)
	return CAIRO_INT_STATUS_UNSUPPORTED;
    /* Check both images have same device offset */
    if (fabs(src_x_offset - mask_x_offset) > e ||
	fabs(src_y_offset - mask_y_offset) > e)
	return CAIRO_INT_STATUS_UNSUPPORTED;
    if (need_smask) {
	status = _cairo_pdf_surface_add_source_surface (surface,
							NULL,
							mask,
							-1, /* node_surface_index */
							op,
							source->filter,
							FALSE, /* stencil mask */
							TRUE, /* smask */
							FALSE, /* need_transp_group */
							extents,
							NULL, /* smask_res */
							&pdf_source,
							NULL,
							NULL,
							NULL);
	if (unlikely (status))
	    return status;
    }
    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
    if (unlikely (status))
	return status;
    _cairo_output_stream_printf (surface->output, "q\n");
    status = _cairo_pdf_surface_paint_surface_pattern (surface, op, source,
						       CAIRO_ANALYSIS_SOURCE_NONE,
						       extents,
						       1.0, /* alpha */
						       need_smask ? &pdf_source->surface_res : NULL,
						       FALSE);
    if (unlikely (status))
	return status;
    _cairo_output_stream_printf (surface->output, "Q\n");
    status = _cairo_output_stream_get_status (surface->output);
    return status;
}
/* A PDF stencil mask is an A1 mask used with the current color */
static cairo_int_status_t
_cairo_pdf_surface_emit_stencil_mask (cairo_pdf_surface_t         *surface,
				      cairo_operator_t	           op,
				      const cairo_pattern_t       *source,
				      const cairo_pattern_t       *mask,
				      const cairo_rectangle_int_t *extents)
{
    cairo_int_status_t status;
    cairo_image_surface_t  *image;
    void		   *image_extra;
    cairo_image_transparency_t transparency;
    cairo_pdf_resource_t pattern_res = {0};
    if (! (source->type == CAIRO_PATTERN_TYPE_SOLID &&
	   (mask->type == CAIRO_PATTERN_TYPE_SURFACE || mask->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE)))
	return CAIRO_INT_STATUS_UNSUPPORTED;
    if (mask->type == CAIRO_PATTERN_TYPE_SURFACE &&
	((cairo_surface_pattern_t *) mask)->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
    {
	return CAIRO_INT_STATUS_UNSUPPORTED;
    }
    status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, mask,
								   &image, &image_extra);
    if (unlikely (status))
	return status;
    if (image->base.status)
	return image->base.status;
    transparency = _cairo_image_analyze_transparency (image);
    if (transparency != CAIRO_IMAGE_IS_OPAQUE &&
	transparency != CAIRO_IMAGE_HAS_BILEVEL_ALPHA)
    {
	status = CAIRO_INT_STATUS_UNSUPPORTED;
	goto cleanup;
    }
    status = _cairo_pdf_surface_select_pattern (surface, source,
						pattern_res, FALSE);
    if (unlikely (status))
	return status;
    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
    if (unlikely (status))
	return status;
    _cairo_output_stream_printf (surface->output, "q\n");
    status = _cairo_pdf_surface_paint_surface_pattern (surface, op, mask,
						       CAIRO_ANALYSIS_SOURCE_NONE,
						       extents, 1.0, NULL, TRUE);
    if (unlikely (status))
	return status;
    _cairo_output_stream_printf (surface->output, "Q\n");
    status = _cairo_output_stream_get_status (surface->output);
cleanup:
    _cairo_pdf_surface_release_source_image_from_pattern (surface, mask, image, image_extra);
    return status;
}
static cairo_int_status_t
29
_cairo_pdf_surface_set_clip (cairo_pdf_surface_t *surface,
			     cairo_composite_rectangles_t *composite)
{
29
    cairo_clip_t *clip = composite->clip;
29
    if (_cairo_composite_rectangles_can_reduce_clip (composite, clip))
10
	clip = NULL;
29
    if (clip == NULL) {
10
	if (_cairo_composite_rectangles_can_reduce_clip (composite,
							 surface->clipper.clip))
10
	    return CAIRO_STATUS_SUCCESS;
    }
19
    return _cairo_surface_clipper_set_clip (&surface->clipper, clip);
}
static cairo_int_status_t
26
_cairo_pdf_surface_paint (void			*abstract_surface,
			  cairo_operator_t	 op,
			  const cairo_pattern_t	*source,
			  const cairo_clip_t	*clip)
{
26
    cairo_pdf_surface_t *surface = abstract_surface;
    cairo_pdf_smask_group_t *group;
    cairo_pdf_resource_t pattern_res, gstate_res;
    cairo_composite_rectangles_t extents;
    cairo_int_status_t status;
26
    status = _cairo_composite_rectangles_init_for_paint (&extents,
							 &surface->base,
							 op, source, clip);
26
    if (unlikely (status))
4
	return status;
22
    status = _cairo_pdf_interchange_add_operation_extents (surface, &extents.bounded);
22
    if (unlikely (status))
	return status;
22
    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
9
	status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded);
9
	goto cleanup;
13
    } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) {
7
	status = _cairo_pdf_surface_start_fallback  (surface);
7
	if (unlikely (status))
	    goto cleanup;
    }
13
    assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded));
13
    status = _cairo_pdf_surface_set_clip (surface, &extents);
13
    if (unlikely (status))
	goto cleanup;
13
    status = _cairo_pdf_surface_select_operator (surface, op);
13
    if (unlikely (status))
	goto cleanup;
13
    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
13
    if (unlikely (status))
	goto cleanup;
13
    if (_can_paint_pattern (source)) {
13
	_cairo_output_stream_printf (surface->output, "q\n");
13
	status = _cairo_pdf_surface_paint_pattern (surface,
						   op,
						   source,
						   CAIRO_ANALYSIS_SOURCE_PAINT,
						   &extents.bounded,
						   1.0, /* alpha */
						   FALSE); /* mask */
13
	if (unlikely (status))
	    goto cleanup;
13
	_cairo_output_stream_printf (surface->output, "Q\n");
13
	_cairo_composite_rectangles_fini (&extents);
13
	return _cairo_output_stream_get_status (surface->output);
    }
    pattern_res.id = 0;
    gstate_res.id = 0;
    status = _cairo_pdf_surface_add_pdf_pattern (surface, source, op,
						 CAIRO_ANALYSIS_SOURCE_PAINT,
						 &extents.bounded,
						 &pattern_res, &gstate_res);
    if (unlikely (status))
	goto cleanup;
    if (gstate_res.id != 0) {
	group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded);
	if (unlikely (group == NULL)) {
	    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
	    goto cleanup;
	}
	group->operation = PDF_PAINT;
	status = _cairo_pattern_create_copy (&group->source, source);
	if (unlikely (status)) {
	    _cairo_pdf_smask_group_destroy (group);
	    goto cleanup;
	}
	group->source_res = pattern_res;
	status = _cairo_pdf_surface_add_smask_group (surface, group);
	if (unlikely (status)) {
	    _cairo_pdf_smask_group_destroy (group);
	    goto cleanup;
	}
	status = _cairo_pdf_surface_add_smask (surface, gstate_res);
	if (unlikely (status))
	    goto cleanup;
	status = _cairo_pdf_surface_add_xobject (surface, group->group_res);
	if (unlikely (status))
	    goto cleanup;
	_cairo_output_stream_printf (surface->output,
				     "q /s%d gs /x%d Do Q\n",
				     gstate_res.id,
				     group->group_res.id);
    } else {
	status = _cairo_pdf_surface_select_pattern (surface, source,
						    pattern_res, FALSE);
	if (unlikely (status))
	    goto cleanup;
	_cairo_output_stream_printf (surface->output,
				     "%d %d %d %d re f\n",
				     surface->surface_extents.x,
				     surface->surface_extents.y,
				     surface->surface_extents.width,
				     surface->surface_extents.height);
	status = _cairo_pdf_surface_unselect_pattern (surface);
	if (unlikely (status))
	    goto cleanup;
    }
    _cairo_composite_rectangles_fini (&extents);
    return _cairo_output_stream_get_status (surface->output);
9
cleanup:
9
    _cairo_composite_rectangles_fini (&extents);
9
    return status;
}
static cairo_int_status_t
_cairo_pdf_surface_mask (void			*abstract_surface,
			 cairo_operator_t	 op,
			 const cairo_pattern_t	*source,
			 const cairo_pattern_t	*mask,
			 const cairo_clip_t	*clip)
{
    cairo_pdf_surface_t *surface = abstract_surface;
    cairo_pdf_smask_group_t *group;
    cairo_composite_rectangles_t extents;
    cairo_int_status_t status;
    cairo_rectangle_int_t r;
    cairo_box_t box;
    double alpha;
    status = _cairo_composite_rectangles_init_for_mask (&extents,
							&surface->base,
							op, source, mask, clip);
    if (unlikely (status))
	return status;
    status = _cairo_pdf_interchange_add_operation_extents (surface, &extents.bounded);
    if (unlikely (status))
	return status;
    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
	cairo_int_status_t source_status, mask_status;
	status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded);
	if (_cairo_int_status_is_error (status))
	    goto cleanup;
	source_status = status;
	if (mask->has_component_alpha) {
	    status = CAIRO_INT_STATUS_UNSUPPORTED;
	} else {
	    status = _cairo_pdf_surface_analyze_operation (surface, op, mask, &extents.bounded);
	    if (_cairo_int_status_is_error (status))
		goto cleanup;
	}
	mask_status = status;
	_cairo_composite_rectangles_fini (&extents);
	return _cairo_analysis_surface_merge_status (source_status,
						     mask_status);
    } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) {
	status = _cairo_pdf_surface_start_fallback (surface);
	if (unlikely (status))
	    goto cleanup;
    }
    assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded));
    assert (_cairo_pdf_surface_operation_supported (surface, op, mask, &extents.bounded));
    /* get the accurate extents */
    status = _cairo_pattern_get_ink_extents (source, &r);
    if (unlikely (status))
	goto cleanup;
    /* XXX slight impedance mismatch */
    _cairo_box_from_rectangle (&box, &r);
    status = _cairo_composite_rectangles_intersect_source_extents (&extents,
								   &box);
    if (unlikely (status))
	goto cleanup;
    status = _cairo_pattern_get_ink_extents (mask, &r);
    if (unlikely (status))
	goto cleanup;
    _cairo_box_from_rectangle (&box, &r);
    status = _cairo_composite_rectangles_intersect_mask_extents (&extents,
								 &box);
    if (unlikely (status))
	goto cleanup;
    status = _cairo_pdf_surface_set_clip (surface, &extents);
    if (unlikely (status))
	goto cleanup;
    status = _cairo_pdf_surface_select_operator (surface, op);
    if (unlikely (status))
	goto cleanup;
    /* Check if we can combine source and mask into a smask image */
    status = _cairo_pdf_surface_emit_combined_smask (surface, op, source, mask, &extents.bounded);
    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
	goto cleanup;
    /* Check if we can use a stencil mask */
    status = _cairo_pdf_surface_emit_stencil_mask (surface, op, source, mask, &extents.bounded);
    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
	goto cleanup;
    /* Check if we can set ca/CA instead of an smask. We could handle
     * other source patterns as well but for now this is the easiest,
     * and most common, case to handle. */
    if (_cairo_pattern_is_constant_alpha (mask, &extents.bounded, &alpha) &&
	_can_paint_pattern (source)) {
	status = _cairo_pdf_operators_flush (&surface->pdf_operators);
	if (unlikely (status))
	    goto cleanup;
	_cairo_output_stream_printf (surface->output, "q\n");
	status = _cairo_pdf_surface_paint_pattern (surface,
						   op,
						   source,
						   CAIRO_ANALYSIS_SOURCE_MASK,
						   &extents.bounded,
						   alpha,
						   FALSE); /* mask */
	if (unlikely (status))
	    goto cleanup;
	_cairo_output_stream_printf (surface->output, "Q\n");
	_cairo_composite_rectangles_fini (&extents);
	return _cairo_output_stream_get_status (surface->output);
    }
    group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded);
    if (unlikely (group == NULL)) {
	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
	goto cleanup;
    }
    group->operation = PDF_MASK;
    status = _cairo_pattern_create_copy (&group->source, source);
    if (unlikely (status)) {
	_cairo_pdf_smask_group_destroy (group);
	goto cleanup;
    }
    status = _cairo_pattern_create_copy (&group->mask, mask);
    if (unlikely (status)) {
	_cairo_pdf_smask_group_destroy (group);
	goto cleanup;
    }
    group->source_res = _cairo_pdf_surface_new_object (surface);
    if (group->source_res.id == 0) {
	_cairo_pdf_smask_group_destroy (group);
	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
	goto cleanup;
    }
    status = _cairo_pdf_surface_add_smask_group (surface, group);
    if (unlikely (status)) {
	_cairo_pdf_smask_group_destroy (group);
	goto cleanup;
    }
    status = _cairo_pdf_surface_add_smask (surface, group->group_res);
    if (unlikely (status))
	goto cleanup;
    status = _cairo_pdf_surface_add_xobject (surface, group->source_res);
    if (unlikely (status))
	goto cleanup;
    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
    if (unlikely (status))
	goto cleanup;
    _cairo_output_stream_printf (surface->output,
				 "q /s%d gs /x%d Do Q\n",
				 group->group_res.id,
				 group->source_res.id);
    _cairo_composite_rectangles_fini (&extents);
    return _cairo_output_stream_get_status (surface->output);
cleanup:
    _cairo_composite_rectangles_fini (&extents);
    return status;
}
static cairo_int_status_t
_cairo_pdf_surface_stroke (void			*abstract_surface,
			   cairo_operator_t	 op,
			   const cairo_pattern_t *source,
			   const cairo_path_fixed_t	*path,
			   const cairo_stroke_style_t	*style,
			   const cairo_matrix_t	*ctm,
			   const cairo_matrix_t	*ctm_inverse,
			   double		 tolerance,
			   cairo_antialias_t	 antialias,
			   const cairo_clip_t	*clip)
{
    cairo_pdf_surface_t *surface = abstract_surface;
    cairo_pdf_smask_group_t *group;
    cairo_pdf_resource_t pattern_res, gstate_res;
    cairo_composite_rectangles_t extents;
    cairo_int_status_t status;
    status = _cairo_composite_rectangles_init_for_stroke (&extents,
							  &surface->base,
							  op, source,
							  path, style, ctm,
							  clip);
    if (unlikely (status))
	return status;
    /* use the more accurate extents */
    if (extents.is_bounded) {
	cairo_rectangle_int_t mask;
	cairo_box_t box;
	status = _cairo_path_fixed_stroke_extents (path, style,
						   ctm, ctm_inverse,
						   tolerance,
						   &mask);
	if (unlikely (status))
	    goto cleanup;
	_cairo_box_from_rectangle (&box, &mask);
	status = _cairo_composite_rectangles_intersect_mask_extents (&extents,
								     &box);
	if (unlikely (status))
	    goto cleanup;
    }
    status = _cairo_pdf_interchange_add_operation_extents (surface, &extents.bounded);
    if (unlikely (status))
	goto cleanup;
    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
	status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded);
	goto cleanup;
    }
    assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded));
    status = _cairo_pdf_surface_set_clip (surface, &extents);
    if (unlikely (status))
	goto cleanup;
    pattern_res.id = 0;
    gstate_res.id = 0;
    status = _cairo_pdf_surface_add_pdf_pattern (surface, source, op,
						 CAIRO_ANALYSIS_SOURCE_STROKE,
						 &extents.bounded,
						 &pattern_res, &gstate_res);
    if (unlikely (status))
	goto cleanup;
    status = _cairo_pdf_surface_select_operator (surface, op);
    if (unlikely (status))
	goto cleanup;
    if (gstate_res.id != 0) {
	group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded);
	if (unlikely (group == NULL)) {
	    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
	    goto cleanup;
	}
	group->operation = PDF_STROKE;
	status = _cairo_pattern_create_copy (&group->source, source);
	if (unlikely (status)) {
	    _cairo_pdf_smask_group_destroy (group);
	    goto cleanup;
	}
	group->source_res = pattern_res;
	status = _cairo_path_fixed_init_copy (&group->path, path);
	if (unlikely (status)) {
	    _cairo_pdf_smask_group_destroy (group);
	    goto cleanup;
	}
	group->style = *style;
	group->ctm = *ctm;
	group->ctm_inverse = *ctm_inverse;
	status = _cairo_pdf_surface_add_smask_group (surface, group);
	if (unlikely (status)) {
	    _cairo_pdf_smask_group_destroy (group);
	    goto cleanup;
	}
	status = _cairo_pdf_surface_add_smask (surface, gstate_res);
	if (unlikely (status))
	    goto cleanup;
	status = _cairo_pdf_surface_add_xobject (surface, group->group_res);
	if (unlikely (status))
	    goto cleanup;
	status = _cairo_pdf_operators_flush (&surface->pdf_operators);
	if (unlikely (status))
	    goto cleanup;
	_cairo_output_stream_printf (surface->output,
				     "q /s%d gs /x%d Do Q\n",
				     gstate_res.id,
				     group->group_res.id);
    } else {
	status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, TRUE);
	if (unlikely (status))
	    goto cleanup;
	status = _cairo_pdf_operators_stroke (&surface->pdf_operators,
					      path,
					      style,
					      ctm,
					      ctm_inverse);
	if (unlikely (status))
	    goto cleanup;
	status = _cairo_pdf_surface_unselect_pattern (surface);
	if (unlikely (status))
	    goto cleanup;
    }
    _cairo_composite_rectangles_fini (&extents);
    return _cairo_output_stream_get_status (surface->output);
cleanup:
    _cairo_composite_rectangles_fini (&extents);
    return status;
}
static cairo_int_status_t
52
_cairo_pdf_surface_fill (void			*abstract_surface,
			 cairo_operator_t	 op,
			 const cairo_pattern_t	*source,
			 const cairo_path_fixed_t*path,
			 cairo_fill_rule_t	 fill_rule,
			 double			 tolerance,
			 cairo_antialias_t	 antialias,
			 const cairo_clip_t	*clip)
{
52
    cairo_pdf_surface_t *surface = abstract_surface;
    cairo_int_status_t status;
    cairo_pdf_smask_group_t *group;
    cairo_pdf_resource_t pattern_res, gstate_res;
    cairo_composite_rectangles_t extents;
52
    status = _cairo_composite_rectangles_init_for_fill (&extents,
							&surface->base,
							op, source, path,
							clip);
52
    if (unlikely (status))
	return status;
    /* use the more accurate extents */
52
    if (extents.is_bounded) {
	cairo_rectangle_int_t mask;
	cairo_box_t box;
52
	_cairo_path_fixed_fill_extents (path,
					fill_rule,
					tolerance,
					&mask);
52
	_cairo_box_from_rectangle (&box, &mask);
52
	status = _cairo_composite_rectangles_intersect_mask_extents (&extents,
								     &box);
52
	if (unlikely (status))
	    goto cleanup;
    }
52
    status = _cairo_pdf_interchange_add_operation_extents (surface, &extents.bounded);
52
    if (unlikely (status))
	goto cleanup;
52
    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
36
	status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded);
36
	goto cleanup;
16
    } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) {
	status = _cairo_pdf_surface_start_fallback (surface);
	if (unlikely (status))
	    goto cleanup;
    }
16
    assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded));
16
    status = _cairo_pdf_surface_set_clip (surface, &extents);
16
    if (unlikely (status))
	goto cleanup;
16
    status = _cairo_pdf_surface_select_operator (surface, op);
16
    if (unlikely (status))
	goto cleanup;
16
    if (_can_paint_pattern (source)) {
	status = _cairo_pdf_operators_flush (&surface->pdf_operators);
	if (unlikely (status))
	    goto cleanup;
	_cairo_output_stream_printf (surface->output, "q\n");
	status =  _cairo_pdf_operators_clip (&surface->pdf_operators,
					     path,
					     fill_rule);
	if (unlikely (status))
	    goto cleanup;
	status = _cairo_pdf_surface_paint_pattern (surface,
						   op,
						   source,
						   CAIRO_ANALYSIS_SOURCE_FILL,
						   &extents.bounded,
						   1.0, /* alpha */
						   FALSE); /* mask */
	if (unlikely (status))
	    goto cleanup;
	_cairo_output_stream_printf (surface->output, "Q\n");
	status = _cairo_output_stream_get_status (surface->output);
	goto cleanup;
    }
16
    pattern_res.id = 0;
16
    gstate_res.id = 0;
16
    status = _cairo_pdf_surface_add_pdf_pattern (surface, source, op,
						 CAIRO_ANALYSIS_SOURCE_FILL,
						 &extents.bounded,
						 &pattern_res, &gstate_res);
16
    if (unlikely (status))
	goto cleanup;
16
    if (gstate_res.id != 0) {
	group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded);
	if (unlikely (group == NULL)) {
	    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
	    goto cleanup;
	}
	group->operation = PDF_FILL;
	status = _cairo_pattern_create_copy (&group->source, source);
	if (unlikely (status)) {
	    _cairo_pdf_smask_group_destroy (group);
	    goto cleanup;
	}
	group->source_res = pattern_res;
	status = _cairo_path_fixed_init_copy (&group->path, path);
	if (unlikely (status)) {
	    _cairo_pdf_smask_group_destroy (group);
	    goto cleanup;
	}
	group->fill_rule = fill_rule;
	status = _cairo_pdf_surface_add_smask_group (surface, group);
	if (unlikely (status)) {
	    _cairo_pdf_smask_group_destroy (group);
	    goto cleanup;
	}
	status = _cairo_pdf_surface_add_smask (surface, gstate_res);
	if (unlikely (status))
	    goto cleanup;
	status = _cairo_pdf_surface_add_xobject (surface, group->group_res);
	if (unlikely (status))
	    goto cleanup;
	status = _cairo_pdf_operators_flush (&surface->pdf_operators);
	if (unlikely (status))
	    goto cleanup;
	_cairo_output_stream_printf (surface->output,
				     "q /s%d gs /x%d Do Q\n",
				     gstate_res.id,
				     group->group_res.id);
    } else {
16
	status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE);
16
	if (unlikely (status))
	    goto cleanup;
16
	status = _cairo_pdf_operators_fill (&surface->pdf_operators,
					    path,
					    fill_rule);
16
	if (unlikely (status))
	    goto cleanup;
16
	status = _cairo_pdf_surface_unselect_pattern (surface);
16
	if (unlikely (status))
	    goto cleanup;
    }
16
    _cairo_composite_rectangles_fini (&extents);
16
    return _cairo_output_stream_get_status (surface->output);
36
cleanup:
36
    _cairo_composite_rectangles_fini (&extents);
36
    return status;
}
static cairo_int_status_t
_cairo_pdf_surface_fill_stroke (void			*abstract_surface,
				cairo_operator_t	 fill_op,
				const cairo_pattern_t	*fill_source,
				cairo_fill_rule_t	 fill_rule,
				double			 fill_tolerance,
				cairo_antialias_t	 fill_antialias,
				const cairo_path_fixed_t*path,
				cairo_operator_t	 stroke_op,
				const cairo_pattern_t	*stroke_source,
				const cairo_stroke_style_t *stroke_style,
				const cairo_matrix_t	*stroke_ctm,
				const cairo_matrix_t	*stroke_ctm_inverse,
				double			 stroke_tolerance,
				cairo_antialias_t	 stroke_antialias,
				const cairo_clip_t	*clip)
{
    cairo_pdf_surface_t *surface = abstract_surface;
    cairo_int_status_t status;
    cairo_pdf_resource_t fill_pattern_res, stroke_pattern_res, gstate_res;
    cairo_composite_rectangles_t extents;
    /* During analysis we return unsupported and let the _fill and
     * _stroke functions that are on the fallback path do the analysis
     * for us. During render we may still encounter unsupported
     * combinations of fill/stroke patterns. However we can return
     * unsupported anytime to let the _fill and _stroke functions take
     * over.
     */
    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
	return CAIRO_INT_STATUS_UNSUPPORTED;
    /* PDF rendering of fill-stroke is not the same as cairo when
     * either the fill or stroke is not opaque.
     */
    if ( !_cairo_pattern_is_opaque (fill_source, NULL) ||
	 !_cairo_pattern_is_opaque (stroke_source, NULL))
    {
	return CAIRO_INT_STATUS_UNSUPPORTED;
    }
    if (fill_op != stroke_op)
	return CAIRO_INT_STATUS_UNSUPPORTED;
    /* Compute the operation extents using the stroke which will naturally
     * be larger than the fill extents.
     */
    status = _cairo_composite_rectangles_init_for_stroke (&extents,
							  &surface->base,
							  stroke_op, stroke_source,
							  path, stroke_style, stroke_ctm,
							  clip);
    if (unlikely (status))
	return status;
    /* use the more accurate extents */
    if (extents.is_bounded) {
	cairo_rectangle_int_t mask;
	cairo_box_t box;
	status = _cairo_path_fixed_stroke_extents (path, stroke_style,
						   stroke_ctm, stroke_ctm_inverse,
						   stroke_tolerance,
						   &mask);
	if (unlikely (status))
	    goto cleanup;
	_cairo_box_from_rectangle (&box, &mask);
	status = _cairo_composite_rectangles_intersect_mask_extents (&extents,
								     &box);
	if (unlikely (status))
	    goto cleanup;
    }
    status = _cairo_pdf_surface_set_clip (surface, &extents);
    if (unlikely (status))
	goto cleanup;
    status = _cairo_pdf_surface_select_operator (surface, fill_op);
    if (unlikely (status))
	goto cleanup;
    /* use the more accurate extents */
    if (extents.is_bounded) {
	cairo_rectangle_int_t mask;
	cairo_box_t box;
	_cairo_path_fixed_fill_extents (path,
					fill_rule,
					fill_tolerance,
					&mask);
	_cairo_box_from_rectangle (&box, &mask);
	status = _cairo_composite_rectangles_intersect_mask_extents (&extents,
								     &box);
	if (unlikely (status))
	    goto cleanup;
    }
    status = _cairo_pdf_interchange_add_operation_extents (surface, &extents.bounded);
    if (unlikely (status))
	goto cleanup;
    fill_pattern_res.id = 0;
    gstate_res.id = 0;
    status = _cairo_pdf_surface_add_pdf_pattern (surface, fill_source,
						 fill_op,
						 CAIRO_ANALYSIS_SOURCE_FILL,
						 &extents.bounded,
						 &fill_pattern_res,
						 &gstate_res);
    if (unlikely (status))
	goto cleanup;
    assert (gstate_res.id == 0);
    stroke_pattern_res.id = 0;
    gstate_res.id = 0;
    status = _cairo_pdf_surface_add_pdf_pattern (surface,
						 stroke_source,
						 stroke_op,
						 CAIRO_ANALYSIS_SOURCE_STROKE,
						 &extents.bounded,
						 &stroke_pattern_res,
						 &gstate_res);
    if (unlikely (status))
	goto cleanup;
    assert (gstate_res.id == 0);
    /* As PDF has separate graphics state for fill and stroke we can
     * select both at the same time */
    status = _cairo_pdf_surface_select_pattern (surface, fill_source,
						fill_pattern_res, FALSE);
    if (unlikely (status))
	goto cleanup;
    status = _cairo_pdf_surface_select_pattern (surface, stroke_source,
						stroke_pattern_res, TRUE);
    if (unlikely (status))
	goto cleanup;
    status = _cairo_pdf_operators_fill_stroke (&surface->pdf_operators,
					       path,
					       fill_rule,
					       stroke_style,
					       stroke_ctm,
					       stroke_ctm_inverse);
    if (unlikely (status))
	goto cleanup;
    status = _cairo_pdf_surface_unselect_pattern (surface);
    if (unlikely (status))
	goto cleanup;
    _cairo_composite_rectangles_fini (&extents);
    return _cairo_output_stream_get_status (surface->output);
cleanup:
    _cairo_composite_rectangles_fini (&extents);
    return status;
}
static cairo_bool_t
_cairo_pdf_surface_has_show_text_glyphs	(void			*abstract_surface)
{
    return TRUE;
}
static cairo_int_status_t
_cairo_pdf_surface_show_text_glyphs (void			*abstract_surface,
				     cairo_operator_t		 op,
				     const cairo_pattern_t	*source,
				     const char                 *utf8,
				     int                         utf8_len,
				     cairo_glyph_t		*glyphs,
				     int			 num_glyphs,
				     const cairo_text_cluster_t *clusters,
				     int                         num_clusters,
				     cairo_text_cluster_flags_t  cluster_flags,
				     cairo_scaled_font_t	*scaled_font,
				     const cairo_clip_t		*clip)
{
    cairo_pdf_surface_t *surface = abstract_surface;
    cairo_pdf_smask_group_t *group;
    cairo_pdf_resource_t pattern_res, gstate_res;
    cairo_composite_rectangles_t extents;
    cairo_bool_t overlap;
    cairo_int_status_t status;
    status = _cairo_composite_rectangles_init_for_glyphs (&extents,
							  &surface->base,
							  op, source,
							  scaled_font,
							  glyphs, num_glyphs,
							  clip,
							  &overlap);
    if (unlikely (status))
	return status;
    status = _cairo_pdf_interchange_add_content (surface);
    if (unlikely (status))
	return status;
    status = _cairo_pdf_interchange_add_operation_extents (surface, &extents.bounded);
    if (unlikely (status))
	return status;
    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
	/* Enabling text in Type 3 fonts currently crashes cairo. Most
	 * PDF viewers don't seem to suport text in Type 3 so we let
	 * this go to image fallback.
	 */
	if (surface->type3_replay)
	    return CAIRO_INT_STATUS_UNSUPPORTED;
	status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded);
	goto cleanup;
    }
    assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded));
    status = _cairo_pdf_surface_set_clip (surface, &extents);
    if (unlikely (status))
	goto cleanup;
    pattern_res.id = 0;
    gstate_res.id = 0;
    status = _cairo_pdf_surface_add_pdf_pattern (surface, source, op,
						 CAIRO_ANALYSIS_SOURCE_SHOW_GLYPHS,
						 &extents.bounded,
						 &pattern_res, &gstate_res);
    if (unlikely (status))
	goto cleanup;
    status = _cairo_pdf_surface_select_operator (surface, op);
    if (unlikely (status))
	goto cleanup;
    if (gstate_res.id != 0) {
	group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded);
	if (unlikely (group == NULL)) {
	    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
	    goto cleanup;
	}
	group->operation = PDF_SHOW_GLYPHS;
	status = _cairo_pattern_create_copy (&group->source, source);
	if (unlikely (status)) {
	    _cairo_pdf_smask_group_destroy (group);
	    goto cleanup;
	}
	group->source_res = pattern_res;
	if (utf8_len) {
	    group->utf8 = _cairo_malloc (utf8_len);
	    if (unlikely (group->utf8 == NULL)) {
		_cairo_pdf_smask_group_destroy (group);
		status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
		goto cleanup;
	    }
	    memcpy (group->utf8, utf8, utf8_len);
	}
	group->utf8_len = utf8_len;
	if (num_glyphs) {
	    group->glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
	    if (unlikely (group->glyphs == NULL)) {
		_cairo_pdf_smask_group_destroy (group);
		status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
		goto cleanup;
	    }
	    memcpy (group->glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
	}
	group->num_glyphs = num_glyphs;
	if (num_clusters) {
	    group->clusters = _cairo_malloc_ab (num_clusters, sizeof (cairo_text_cluster_t));
	    if (unlikely (group->clusters == NULL)) {
		_cairo_pdf_smask_group_destroy (group);
		status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
		goto cleanup;
	    }
	    memcpy (group->clusters, clusters, sizeof (cairo_text_cluster_t) * num_clusters);
	}
	group->num_clusters = num_clusters;
	group->scaled_font = cairo_scaled_font_reference (scaled_font);
	status = _cairo_pdf_surface_add_smask_group (surface, group);
	if (unlikely (status)) {
	    _cairo_pdf_smask_group_destroy (group);
	    goto cleanup;
	}
	status = _cairo_pdf_surface_add_smask (surface, gstate_res);
	if (unlikely (status))
	    goto cleanup;
	status = _cairo_pdf_surface_add_xobject (surface, group->group_res);
	if (unlikely (status))
	    goto cleanup;
	status = _cairo_pdf_operators_flush (&surface->pdf_operators);
	if (unlikely (status))
	    goto cleanup;
	_cairo_output_stream_printf (surface->output,
				     "q /s%d gs /x%d Do Q\n",
				     gstate_res.id,
				     group->group_res.id);
    } else {
	status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE);
	if (unlikely (status))
	    goto cleanup;
	/* User-fonts can use strokes; reset the stroke pattern as well. */
	if (_cairo_font_face_is_user(scaled_font->font_face)) {
	    status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, TRUE);
	    if (unlikely (status))
		goto cleanup;
	}
	/* Each call to show_glyphs() with a transclucent pattern must
	 * be in a separate text object otherwise overlapping text
	 * from separate calls to show_glyphs will not composite with
	 * each other. */
	if (! _cairo_pattern_is_opaque (source, &extents.bounded)) {
	    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
	    if (unlikely (status))
		goto cleanup;
	}
	status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators,
							utf8, utf8_len,
							glyphs, num_glyphs,
							clusters, num_clusters,
							cluster_flags,
							scaled_font);
	if (unlikely (status))
	    goto cleanup;
	status = _cairo_pdf_surface_unselect_pattern (surface);
	if (unlikely (status))
	    goto cleanup;
    }
    _cairo_composite_rectangles_fini (&extents);
    return _cairo_output_stream_get_status (surface->output);
cleanup:
    _cairo_composite_rectangles_fini (&extents);
    return status;
}
static const char **
_cairo_pdf_surface_get_supported_mime_types (void		 *abstract_surface)
{
    return _cairo_pdf_supported_mime_types;
}
static cairo_int_status_t
_cairo_pdf_surface_tag (void			   *abstract_surface,
			cairo_bool_t                begin,
			const char                 *tag_name,
			const char                 *attributes)
{
    cairo_pdf_surface_t *surface = abstract_surface;
    cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
    if (begin)
	status = _cairo_pdf_interchange_tag_begin (surface, tag_name, attributes);
    else
	status = _cairo_pdf_interchange_tag_end (surface, tag_name);
    return status;
}
static cairo_int_status_t
67
_cairo_pdf_surface_command_id (void                 *abstract_surface,
			       unsigned int          recording_id,
			       unsigned int          command_id)
{
67
    cairo_pdf_surface_t *surface = abstract_surface;
67
    return _cairo_pdf_interchange_command_id (surface, recording_id, command_id);
}
/* The Type 3 font subset support will the embed the
 * CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE image if vector operations
 * are not supported. The only case we don't currently handle is if a
 * foreground color is used.
 */
static cairo_bool_t
_cairo_pdf_surface_supports_color_glyph (void                  *abstract_surface,
					 cairo_scaled_font_t   *scaled_font,
					 unsigned long          glyph_index)
{
    cairo_pdf_surface_t *surface = abstract_surface;
    cairo_pdf_color_glyph_t glyph_key;
    cairo_pdf_color_glyph_t *glyph_entry;
    cairo_scaled_glyph_t *scaled_glyph;
    cairo_status_t status;
    glyph_key.scaled_font = scaled_font;
    glyph_key.glyph_index = glyph_index;
    _cairo_pdf_color_glyph_init_key (&glyph_key);
    glyph_entry = _cairo_hash_table_lookup (surface->color_glyphs, &glyph_key.base);
    if (glyph_entry)
	return glyph_entry->supported;
    glyph_entry = _cairo_calloc (sizeof (cairo_pdf_color_glyph_t));
    if (glyph_entry == NULL) {
	status = _cairo_surface_set_error (&surface->base,
					   _cairo_error (CAIRO_STATUS_NO_MEMORY));
        return FALSE;
    }
    glyph_entry->scaled_font = cairo_scaled_font_reference (scaled_font);
    glyph_entry->glyph_index = glyph_index;
    _cairo_pdf_color_glyph_init_key (glyph_entry);
    glyph_entry->supported = FALSE;
    _cairo_scaled_font_freeze_cache (scaled_font);
    status = _cairo_scaled_glyph_lookup (scaled_font,
					 glyph_index,
					 CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE,
					 NULL, /* foreground color */
					 &scaled_glyph);
    if (unlikely (status))
	goto done;
    glyph_entry->supported = !(scaled_glyph->recording_uses_foreground_color ||
			       scaled_glyph->recording_uses_foreground_marker);
  done:
    _cairo_scaled_font_thaw_cache (scaled_font);
    status = _cairo_hash_table_insert (surface->color_glyphs,
				       &glyph_entry->base);
    if (unlikely(status)) {
	status = _cairo_surface_set_error (&surface->base,
					   _cairo_error (CAIRO_STATUS_NO_MEMORY));
        return FALSE;
    }
    return glyph_entry->supported;
}
static cairo_int_status_t
18
_cairo_pdf_surface_analyze_recording_surface(void		           *abstract_surface,
					     const cairo_surface_pattern_t *recording_surface_pattern,
					     unsigned int                   region_id,
					     cairo_analysis_source_t        source_type,
					     cairo_bool_t                   begin)
{
18
    cairo_pdf_surface_t *surface = abstract_surface;
18
    cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
18
    if (begin) {
9
	status = _cairo_pdf_interchange_recording_source_surface_begin (
	    surface,
	    recording_surface_pattern,
	    region_id,
	    source_type);
    } else {
9
	status = _cairo_pdf_interchange_recording_source_surface_end (
	    surface,
	    recording_surface_pattern,
	    region_id,
	    source_type);
    }
18
    return status;
}
static cairo_int_status_t
14
_cairo_pdf_surface_set_paginated_mode (void			*abstract_surface,
				       cairo_paginated_mode_t	 paginated_mode)
{
14
    cairo_pdf_surface_t *surface = abstract_surface;
    cairo_int_status_t status;
14
    surface->paginated_mode = paginated_mode;
14
    status = _cairo_pdf_interchange_begin_page_content (surface);
14
    if (unlikely (status))
	return status;
14
    if (paginated_mode == CAIRO_PAGINATED_MODE_RENDER) {
5
	surface->surface_extents.x = 0;
5
	surface->surface_extents.y = 0;
5
	surface->surface_extents.width  = ceil (surface->width);
5
	surface->surface_extents.height = ceil (surface->height);
    }
14
    return CAIRO_INT_STATUS_SUCCESS;
}
static const cairo_surface_backend_t cairo_pdf_surface_backend = {
    CAIRO_SURFACE_TYPE_PDF,
    _cairo_pdf_surface_finish,
    _cairo_default_context_create,
    NULL, /* create similar: handled by wrapper */
    NULL, /* create similar image */
    NULL, /* map to image */
    NULL, /* unmap image */
    _cairo_surface_default_source,
    NULL, /* acquire_source_image */
    NULL, /* release_source_image */
    NULL, /* snapshot */
    NULL,  /* _cairo_pdf_surface_copy_page */
    _cairo_pdf_surface_show_page,
    _cairo_pdf_surface_get_extents,
    _cairo_pdf_surface_get_font_options,
    NULL, /* flush */
    NULL, /* mark_dirty_rectangle */
    /* Here are the drawing functions */
    _cairo_pdf_surface_paint,
    _cairo_pdf_surface_mask,
    _cairo_pdf_surface_stroke,
    _cairo_pdf_surface_fill,
    _cairo_pdf_surface_fill_stroke,
    NULL, /* show_glyphs */
    _cairo_pdf_surface_has_show_text_glyphs,
    _cairo_pdf_surface_show_text_glyphs,
    _cairo_pdf_surface_get_supported_mime_types,
    _cairo_pdf_surface_tag,
    _cairo_pdf_surface_supports_color_glyph,
    _cairo_pdf_surface_analyze_recording_surface,
    _cairo_pdf_surface_command_id,
};
static const cairo_paginated_surface_backend_t
cairo_pdf_surface_paginated_backend = {
    _cairo_pdf_surface_start_page,
    _cairo_pdf_surface_set_paginated_mode,
    NULL, /* set_bounding_box */
    _cairo_pdf_surface_has_fallback_images,
    _cairo_pdf_surface_supports_fine_grained_fallbacks,
    _cairo_pdf_surface_requires_thumbnail_image,
    _cairo_pdf_surface_set_thumbnail_image,
};