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

            
37
/* The script surface is one that records all operations performed on
38
 * it in the form of a procedural script, similar in fashion to
39
 * PostScript but using Cairo's imaging model. In essence, this is
40
 * equivalent to the recording-surface, but as there is no impedance mismatch
41
 * between Cairo and CairoScript, we can generate output immediately
42
 * without having to copy and hold the data in memory.
43
 */
44

            
45
/**
46
 * SECTION:cairo-script
47
 * @Title: Script Surfaces
48
 * @Short_Description: Rendering to replayable scripts
49
 * @See_Also: #cairo_surface_t
50
 *
51
 * The script surface provides the ability to render to a native
52
 * script that matches the cairo drawing model. The scripts can
53
 * be replayed using tools under the util/cairo-script directory,
54
 * or with cairo-perf-trace.
55
 **/
56

            
57
/**
58
 * CAIRO_HAS_SCRIPT_SURFACE:
59
 *
60
 * Defined if the script surface backend is available.
61
 * The script surface backend is always built in since 1.12.
62
 *
63
 * Since: 1.12
64
 **/
65

            
66

            
67
#include "cairoint.h"
68

            
69
#include "cairo-script.h"
70
#include "cairo-script-private.h"
71

            
72
#include "cairo-analysis-surface-private.h"
73
#include "cairo-default-context-private.h"
74
#include "cairo-device-private.h"
75
#include "cairo-error-private.h"
76
#include "cairo-list-inline.h"
77
#include "cairo-image-surface-private.h"
78
#include "cairo-output-stream-private.h"
79
#include "cairo-pattern-private.h"
80
#include "cairo-recording-surface-inline.h"
81
#include "cairo-scaled-font-private.h"
82
#include "cairo-surface-clipper-private.h"
83
#include "cairo-surface-snapshot-inline.h"
84
#include "cairo-surface-subsurface-private.h"
85
#include "cairo-surface-wrapper-private.h"
86

            
87
#if CAIRO_HAS_FT_FONT
88
#include "cairo-ft-private.h"
89
#endif
90

            
91
#include <ctype.h>
92

            
93
#ifdef WORDS_BIGENDIAN
94
#define to_be32(x) x
95
#else
96
#define to_be32(x) bswap_32(x)
97
#endif
98

            
99
#define _cairo_output_stream_puts(S, STR) \
100
    _cairo_output_stream_write ((S), (STR), strlen (STR))
101

            
102
#define static cairo_warn static
103

            
104
typedef struct _cairo_script_context cairo_script_context_t;
105
typedef struct _cairo_script_surface cairo_script_surface_t;
106
typedef struct _cairo_script_implicit_context cairo_script_implicit_context_t;
107
typedef struct _cairo_script_font cairo_script_font_t;
108

            
109
typedef struct _operand {
110
    enum {
111
	SURFACE,
112
	DEFERRED,
113
    } type;
114
    cairo_list_t link;
115
} operand_t;
116

            
117

            
118
struct deferred_finish {
119
    cairo_list_t link;
120
    operand_t operand;
121
};
122

            
123
struct _cairo_script_context {
124
    cairo_device_t base;
125

            
126
    int active;
127
    int attach_snapshots;
128

            
129
    cairo_bool_t owns_stream;
130
    cairo_output_stream_t *stream;
131
    cairo_script_mode_t mode;
132

            
133
    struct _bitmap {
134
	unsigned long min;
135
	unsigned long count;
136
	unsigned int map[64];
137
	struct _bitmap *next;
138
    } surface_id, font_id;
139

            
140
    cairo_list_t operands;
141
    cairo_list_t deferred;
142

            
143
    cairo_list_t fonts;
144
    cairo_list_t defines;
145
};
146

            
147
struct _cairo_script_font {
148
    cairo_scaled_font_private_t base;
149

            
150
    cairo_bool_t has_sfnt;
151
    unsigned long id;
152
    unsigned long subset_glyph_index;
153
    cairo_list_t link;
154
    cairo_scaled_font_t *parent;
155
};
156

            
157
struct _cairo_script_implicit_context {
158
    cairo_operator_t current_operator;
159
    cairo_fill_rule_t current_fill_rule;
160
    double current_tolerance;
161
    cairo_antialias_t current_antialias;
162
    cairo_stroke_style_t current_style;
163
    cairo_pattern_union_t current_source;
164
    cairo_matrix_t current_ctm;
165
    cairo_matrix_t current_stroke_matrix;
166
    cairo_matrix_t current_font_matrix;
167
    cairo_font_options_t current_font_options;
168
    cairo_scaled_font_t *current_scaled_font;
169
    cairo_path_fixed_t current_path;
170
    cairo_bool_t has_clip;
171
};
172

            
173
struct _cairo_script_surface {
174
    cairo_surface_t base;
175

            
176
    cairo_surface_wrapper_t wrapper;
177

            
178
    cairo_surface_clipper_t clipper;
179

            
180
    operand_t operand;
181
    cairo_bool_t emitted;
182
    cairo_bool_t defined;
183
    cairo_bool_t active;
184

            
185
    double width, height;
186

            
187
    /* implicit flattened context */
188
    cairo_script_implicit_context_t cr;
189
};
190

            
191
static const cairo_surface_backend_t _cairo_script_surface_backend;
192

            
193
static cairo_script_surface_t *
194
_cairo_script_surface_create_internal (cairo_script_context_t *ctx,
195
				       cairo_content_t content,
196
				       cairo_rectangle_t *extents,
197
				       cairo_surface_t *passthrough);
198

            
199
static void
200
_cairo_script_scaled_font_fini (cairo_scaled_font_private_t *abstract_private,
201
				cairo_scaled_font_t *scaled_font);
202

            
203
static void
204
_cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr);
205

            
206
static void
207
_cairo_script_implicit_context_reset (cairo_script_implicit_context_t *cr);
208

            
209
static void
210
_bitmap_release_id (struct _bitmap *b, unsigned long token)
211
{
212
    struct _bitmap **prev = NULL;
213

            
214
    do {
215
	if (token < b->min + sizeof (b->map) * CHAR_BIT) {
216
	    unsigned int bit, elem;
217

            
218
	    token -= b->min;
219
	    elem = token / (sizeof (b->map[0]) * CHAR_BIT);
220
	    bit  = token % (sizeof (b->map[0]) * CHAR_BIT);
221
	    b->map[elem] &= ~(1 << bit);
222
	    if (! --b->count && prev) {
223
		*prev = b->next;
224
		free (b);
225
	    }
226
	    return;
227
	}
228
	prev = &b->next;
229
	b = b->next;
230
    } while (b != NULL);
231
}
232

            
233
static cairo_status_t
234
_bitmap_next_id (struct _bitmap *b,
235
		 unsigned long *id)
236
{
237
    struct _bitmap *bb, **prev = NULL;
238
    unsigned long min = 0;
239

            
240
    do {
241
	if (b->min != min)
242
	    break;
243

            
244
	if (b->count < sizeof (b->map) * CHAR_BIT) {
245
	    unsigned int n, m, bit;
246
	    for (n = 0; n < ARRAY_LENGTH (b->map); n++) {
247
		if (b->map[n] == (unsigned int) -1)
248
		    continue;
249

            
250
		for (m=0, bit=1; m<sizeof (b->map[0])*CHAR_BIT; m++, bit<<=1) {
251
		    if ((b->map[n] & bit) == 0) {
252
			b->map[n] |= bit;
253
			b->count++;
254
			*id = n * sizeof (b->map[0])*CHAR_BIT + m + b->min;
255
			return CAIRO_STATUS_SUCCESS;
256
		    }
257
		}
258
	    }
259
	}
260
	min += sizeof (b->map) * CHAR_BIT;
261

            
262
	prev = &b->next;
263
	b = b->next;
264
    } while (b != NULL);
265
    assert (prev != NULL);
266

            
267
    bb = _cairo_calloc (sizeof (struct _bitmap));
268
    if (unlikely (bb == NULL))
269
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
270

            
271
    *prev = bb;
272
    bb->next = b;
273
    bb->min = min;
274
    bb->count = 1;
275
    bb->map[0] = 0x1;
276
    memset (bb->map + 1, 0, sizeof (bb->map) - sizeof (bb->map[0]));
277
    *id = min;
278

            
279
    return CAIRO_STATUS_SUCCESS;
280
}
281

            
282
static void
283
2
_bitmap_fini (struct _bitmap *b)
284
{
285
2
    while (b != NULL) {
286
	struct _bitmap *next = b->next;
287
	free (b);
288
	b = next;
289
    }
290
2
}
291

            
292
static const char *
293
_direction_to_string (cairo_bool_t backward)
294
{
295
    static const char *names[] = {
296
	"FORWARD",
297
	"BACKWARD"
298
    };
299
    assert (backward < ARRAY_LENGTH (names));
300
    return names[backward];
301
}
302

            
303
static const char *
304
_operator_to_string (cairo_operator_t op)
305
{
306
    static const char *names[] = {
307
	"CLEAR",	/* CAIRO_OPERATOR_CLEAR */
308

            
309
	"SOURCE",	/* CAIRO_OPERATOR_SOURCE */
310
	"OVER",		/* CAIRO_OPERATOR_OVER */
311
	"IN",		/* CAIRO_OPERATOR_IN */
312
	"OUT",		/* CAIRO_OPERATOR_OUT */
313
	"ATOP",		/* CAIRO_OPERATOR_ATOP */
314

            
315
	"DEST",		/* CAIRO_OPERATOR_DEST */
316
	"DEST_OVER",	/* CAIRO_OPERATOR_DEST_OVER */
317
	"DEST_IN",	/* CAIRO_OPERATOR_DEST_IN */
318
	"DEST_OUT",	/* CAIRO_OPERATOR_DEST_OUT */
319
	"DEST_ATOP",	/* CAIRO_OPERATOR_DEST_ATOP */
320

            
321
	"XOR",		/* CAIRO_OPERATOR_XOR */
322
	"ADD",		/* CAIRO_OPERATOR_ADD */
323
	"SATURATE",	/* CAIRO_OPERATOR_SATURATE */
324

            
325
	"MULTIPLY",	/* CAIRO_OPERATOR_MULTIPLY */
326
	"SCREEN",	/* CAIRO_OPERATOR_SCREEN */
327
	"OVERLAY",	/* CAIRO_OPERATOR_OVERLAY */
328
	"DARKEN",	/* CAIRO_OPERATOR_DARKEN */
329
	"LIGHTEN",	/* CAIRO_OPERATOR_LIGHTEN */
330
	"DODGE",	/* CAIRO_OPERATOR_COLOR_DODGE */
331
	"BURN",		/* CAIRO_OPERATOR_COLOR_BURN */
332
	"HARD_LIGHT",	/* CAIRO_OPERATOR_HARD_LIGHT */
333
	"SOFT_LIGHT",	/* CAIRO_OPERATOR_SOFT_LIGHT */
334
	"DIFFERENCE",	/* CAIRO_OPERATOR_DIFFERENCE */
335
	"EXCLUSION",	/* CAIRO_OPERATOR_EXCLUSION */
336
	"HSL_HUE",	/* CAIRO_OPERATOR_HSL_HUE */
337
	"HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */
338
	"HSL_COLOR",	/* CAIRO_OPERATOR_HSL_COLOR */
339
	"HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */
340
    };
341
    assert (op < ARRAY_LENGTH (names));
342
    return names[op];
343
}
344

            
345
static const char *
346
_extend_to_string (cairo_extend_t extend)
347
{
348
    static const char *names[] = {
349
	"EXTEND_NONE",		/* CAIRO_EXTEND_NONE */
350
	"EXTEND_REPEAT",	/* CAIRO_EXTEND_REPEAT */
351
	"EXTEND_REFLECT",	/* CAIRO_EXTEND_REFLECT */
352
	"EXTEND_PAD"		/* CAIRO_EXTEND_PAD */
353
    };
354
    assert (extend < ARRAY_LENGTH (names));
355
    return names[extend];
356
}
357

            
358
static const char *
359
_filter_to_string (cairo_filter_t filter)
360
{
361
    static const char *names[] = {
362
	"FILTER_FAST",		/* CAIRO_FILTER_FAST */
363
	"FILTER_GOOD",		/* CAIRO_FILTER_GOOD */
364
	"FILTER_BEST",		/* CAIRO_FILTER_BEST */
365
	"FILTER_NEAREST",	/* CAIRO_FILTER_NEAREST */
366
	"FILTER_BILINEAR",	/* CAIRO_FILTER_BILINEAR */
367
	"FILTER_GAUSSIAN",	/* CAIRO_FILTER_GAUSSIAN */
368
    };
369
    assert (filter < ARRAY_LENGTH (names));
370
    return names[filter];
371
}
372

            
373
static const char *
374
_dither_to_string (cairo_dither_t dither)
375
{
376
    static const char *names[] = {
377
	"DITHER_DEFAULT",	/* CAIRO_FILTER_FAST */
378
	"DITHER_NONE",		/* CAIRO_FILTER_GOOD */
379
	"DITHER_FAST",		/* CAIRO_FILTER_BEST */
380
	"DITHER_GOOD",		/* CAIRO_FILTER_NEAREST */
381
	"DITHER_BEST",		/* CAIRO_FILTER_BILINEAR */
382
    };
383
    assert (dither < ARRAY_LENGTH (names));
384
    return names[dither];
385
}
386

            
387

            
388
static const char *
389
_fill_rule_to_string (cairo_fill_rule_t rule)
390
{
391
    static const char *names[] = {
392
	"WINDING",	/* CAIRO_FILL_RULE_WINDING */
393
	"EVEN_ODD"	/* CAIRO_FILL_RILE_EVEN_ODD */
394
    };
395
    assert (rule < ARRAY_LENGTH (names));
396
    return names[rule];
397
}
398

            
399
static const char *
400
_antialias_to_string (cairo_antialias_t antialias)
401
{
402
    static const char *names[] = {
403
	"ANTIALIAS_DEFAULT",	/* CAIRO_ANTIALIAS_DEFAULT */
404
	"ANTIALIAS_NONE",	/* CAIRO_ANTIALIAS_NONE */
405
	"ANTIALIAS_GRAY",	/* CAIRO_ANTIALIAS_GRAY */
406
	"ANTIALIAS_SUBPIXEL",	/* CAIRO_ANTIALIAS_SUBPIXEL */
407
	"ANTIALIAS_FAST",	/* CAIRO_ANTIALIAS_FAST */
408
	"ANTIALIAS_GOOD",	/* CAIRO_ANTIALIAS_GOOD */
409
	"ANTIALIAS_BEST"	/* CAIRO_ANTIALIAS_BEST */
410
    };
411
    assert (antialias < ARRAY_LENGTH (names));
412
    return names[antialias];
413
}
414

            
415
static const char *
416
_line_cap_to_string (cairo_line_cap_t line_cap)
417
{
418
    static const char *names[] = {
419
	"LINE_CAP_BUTT",	/* CAIRO_LINE_CAP_BUTT */
420
	"LINE_CAP_ROUND",	/* CAIRO_LINE_CAP_ROUND */
421
	"LINE_CAP_SQUARE"	/* CAIRO_LINE_CAP_SQUARE */
422
    };
423
    assert (line_cap < ARRAY_LENGTH (names));
424
    return names[line_cap];
425
}
426

            
427
static const char *
428
_line_join_to_string (cairo_line_join_t line_join)
429
{
430
    static const char *names[] = {
431
	"LINE_JOIN_MITER",	/* CAIRO_LINE_JOIN_MITER */
432
	"LINE_JOIN_ROUND",	/* CAIRO_LINE_JOIN_ROUND */
433
	"LINE_JOIN_BEVEL",	/* CAIRO_LINE_JOIN_BEVEL */
434
    };
435
    assert (line_join < ARRAY_LENGTH (names));
436
    return names[line_join];
437
}
438

            
439
static inline cairo_script_context_t *
440
45
to_context (cairo_script_surface_t *surface)
441
{
442
45
    return (cairo_script_context_t *) surface->base.device;
443
}
444

            
445
static cairo_bool_t
446
23
target_is_active (cairo_script_surface_t *surface)
447
{
448
46
    return cairo_list_is_first (&surface->operand.link,
449
23
				&to_context (surface)->operands);
450
}
451

            
452
static void
453
2
target_push (cairo_script_surface_t *surface)
454
{
455
2
    cairo_list_move (&surface->operand.link, &to_context (surface)->operands);
456
2
}
457

            
458
static int
459
target_depth (cairo_script_surface_t *surface)
460
{
461
    cairo_list_t *link;
462
    int depth = 0;
463

            
464
    cairo_list_foreach (link, &to_context (surface)->operands) {
465
	if (link == &surface->operand.link)
466
	    break;
467
	depth++;
468
    }
469

            
470
    return depth;
471
}
472

            
473
static void
474
_get_target (cairo_script_surface_t *surface)
475
{
476
    cairo_script_context_t *ctx = to_context (surface);
477

            
478
    if (target_is_active (surface)) {
479
	_cairo_output_stream_puts (ctx->stream, "dup ");
480
	return;
481
    }
482

            
483
    if (surface->defined) {
484
	_cairo_output_stream_printf (ctx->stream, "s%u ",
485
				     surface->base.unique_id);
486
    } else {
487
	int depth = target_depth (surface);
488

            
489
	assert (! cairo_list_is_empty (&surface->operand.link));
490
	assert (! target_is_active (surface));
491

            
492
	if (ctx->active) {
493
	    _cairo_output_stream_printf (ctx->stream, "%d index ", depth);
494
	    _cairo_output_stream_puts (ctx->stream, "/target get exch pop ");
495
	} else {
496
	    if (depth == 1) {
497
		_cairo_output_stream_puts (ctx->stream, "exch ");
498
	    } else {
499
		_cairo_output_stream_printf (ctx->stream,
500
					     "%d -1 roll ", depth);
501
	    }
502
	    target_push (surface);
503
	    _cairo_output_stream_puts (ctx->stream, "dup ");
504
	}
505
    }
506
}
507

            
508
static const char *
509
2
_content_to_string (cairo_content_t content)
510
{
511
2
    switch (content) {
512
    case CAIRO_CONTENT_ALPHA: return "ALPHA";
513
2
    case CAIRO_CONTENT_COLOR: return "COLOR";
514
    default:
515
    case CAIRO_CONTENT_COLOR_ALPHA: return "COLOR_ALPHA";
516
    }
517
}
518

            
519
static cairo_status_t
520
1
_emit_surface (cairo_script_surface_t *surface)
521
{
522
1
    cairo_script_context_t *ctx = to_context (surface);
523

            
524
1
    _cairo_output_stream_printf (ctx->stream,
525
				 "<< /content //%s",
526
				 _content_to_string (surface->base.content));
527
1
    if (surface->width != -1 && surface->height != -1) {
528
1
	_cairo_output_stream_printf (ctx->stream,
529
				     " /width %f /height %f",
530
				     surface->width,
531
				     surface->height);
532
    }
533

            
534
1
    if (surface->base.x_fallback_resolution !=
535
1
	CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT ||
536
1
	surface->base.y_fallback_resolution !=
537
	CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT)
538
    {
539
	_cairo_output_stream_printf (ctx->stream,
540
				     " /fallback-resolution [%f %f]",
541
				     surface->base.x_fallback_resolution,
542
				     surface->base.y_fallback_resolution);
543
    }
544

            
545
1
    if (surface->base.device_transform.x0 != 0. ||
546
1
	surface->base.device_transform.y0 != 0.)
547
    {
548
	/* XXX device offset is encoded into the pattern matrices etc. */
549
	if (0) {
550
	_cairo_output_stream_printf (ctx->stream,
551
				     " /device-offset [%f %f]",
552
				     surface->base.device_transform.x0,
553
				     surface->base.device_transform.y0);
554
	}
555
    }
556

            
557
1
    _cairo_output_stream_puts (ctx->stream, " >> surface context\n");
558
1
    surface->emitted = TRUE;
559
1
    return CAIRO_STATUS_SUCCESS;
560
}
561

            
562
static cairo_status_t
563
2
_emit_context (cairo_script_surface_t *surface)
564
{
565
2
    cairo_script_context_t *ctx = to_context (surface);
566

            
567
2
    if (target_is_active (surface))
568
1
	return CAIRO_STATUS_SUCCESS;
569

            
570
1
    while (! cairo_list_is_empty (&ctx->operands)) {
571
	operand_t *op;
572
	cairo_script_surface_t *old;
573

            
574
	op = cairo_list_first_entry (&ctx->operands,
575
				     operand_t,
576
				     link);
577
	if (op->type == DEFERRED)
578
	    break;
579

            
580
	old = cairo_container_of (op, cairo_script_surface_t, operand);
581
	if (old == surface)
582
	    break;
583
	if (old->active)
584
	    break;
585

            
586
	if (! old->defined) {
587
	    assert (old->emitted);
588
	    _cairo_output_stream_printf (ctx->stream,
589
					 "/target get /s%u exch def pop\n",
590
					 old->base.unique_id);
591
	    old->defined = TRUE;
592
	} else {
593
	    _cairo_output_stream_puts (ctx->stream, "pop\n");
594
	}
595

            
596
	cairo_list_del (&old->operand.link);
597
    }
598

            
599
1
    if (target_is_active (surface))
600
	return CAIRO_STATUS_SUCCESS;
601

            
602
1
    if (! surface->emitted) {
603
	cairo_status_t status;
604

            
605
1
	status = _emit_surface (surface);
606
1
	if (unlikely (status))
607
	    return status;
608
    } else if (cairo_list_is_empty (&surface->operand.link)) {
609
	assert (surface->defined);
610
	_cairo_output_stream_printf (ctx->stream,
611
				     "s%u context\n",
612
				     surface->base.unique_id);
613
	_cairo_script_implicit_context_reset (&surface->cr);
614
	_cairo_surface_clipper_reset (&surface->clipper);
615
    } else {
616
	int depth = target_depth (surface);
617
	if (depth == 1) {
618
	    _cairo_output_stream_puts (ctx->stream, "exch\n");
619
	} else {
620
	    _cairo_output_stream_printf (ctx->stream,
621
					 "%d -1 roll\n",
622
					 depth);
623
	}
624
    }
625
1
    target_push (surface);
626

            
627
1
    return CAIRO_STATUS_SUCCESS;
628
}
629

            
630
static cairo_status_t
631
2
_emit_operator (cairo_script_surface_t *surface,
632
		cairo_operator_t op)
633
{
634
2
    assert (target_is_active (surface));
635

            
636
2
    if (surface->cr.current_operator == op)
637
2
	return CAIRO_STATUS_SUCCESS;
638

            
639
    surface->cr.current_operator = op;
640

            
641
    _cairo_output_stream_printf (to_context (surface)->stream,
642
				 "//%s set-operator\n",
643
				 _operator_to_string (op));
644
    return CAIRO_STATUS_SUCCESS;
645
}
646

            
647
static cairo_status_t
648
_emit_fill_rule (cairo_script_surface_t *surface,
649
		 cairo_fill_rule_t fill_rule)
650
{
651
    assert (target_is_active (surface));
652

            
653
    if (surface->cr.current_fill_rule == fill_rule)
654
	return CAIRO_STATUS_SUCCESS;
655

            
656
    surface->cr.current_fill_rule = fill_rule;
657

            
658
    _cairo_output_stream_printf (to_context (surface)->stream,
659
				 "//%s set-fill-rule\n",
660
				 _fill_rule_to_string (fill_rule));
661
    return CAIRO_STATUS_SUCCESS;
662
}
663

            
664
static cairo_status_t
665
1
_emit_tolerance (cairo_script_surface_t *surface,
666
		 double tolerance,
667
		 cairo_bool_t force)
668
{
669
1
    assert (target_is_active (surface));
670

            
671
1
    if ((! force ||
672
	 fabs (tolerance - CAIRO_GSTATE_TOLERANCE_DEFAULT) < 1e-5) &&
673
1
	surface->cr.current_tolerance == tolerance)
674
    {
675
1
	return CAIRO_STATUS_SUCCESS;
676
    }
677

            
678
    surface->cr.current_tolerance = tolerance;
679

            
680
    _cairo_output_stream_printf (to_context (surface)->stream,
681
				 "%f set-tolerance\n",
682
				 tolerance);
683
    return CAIRO_STATUS_SUCCESS;
684
}
685

            
686
static cairo_status_t
687
1
_emit_antialias (cairo_script_surface_t *surface,
688
		 cairo_antialias_t antialias)
689
{
690
1
    assert (target_is_active (surface));
691

            
692
1
    if (surface->cr.current_antialias == antialias)
693
1
	return CAIRO_STATUS_SUCCESS;
694

            
695
    surface->cr.current_antialias = antialias;
696

            
697
    _cairo_output_stream_printf (to_context (surface)->stream,
698
				 "//%s set-antialias\n",
699
				 _antialias_to_string (antialias));
700

            
701
    return CAIRO_STATUS_SUCCESS;
702
}
703

            
704
static cairo_status_t
705
1
_emit_line_width (cairo_script_surface_t *surface,
706
		 double line_width,
707
		 cairo_bool_t force)
708
{
709
1
    assert (target_is_active (surface));
710

            
711
1
    if ((! force ||
712
	 fabs (line_width - CAIRO_GSTATE_LINE_WIDTH_DEFAULT) < 1e-5) &&
713
1
	surface->cr.current_style.line_width == line_width)
714
    {
715
1
	return CAIRO_STATUS_SUCCESS;
716
    }
717

            
718
    surface->cr.current_style.line_width = line_width;
719

            
720
    _cairo_output_stream_printf (to_context (surface)->stream,
721
				 "%f set-line-width\n",
722
				 line_width);
723
    return CAIRO_STATUS_SUCCESS;
724
}
725

            
726
static cairo_status_t
727
1
_emit_hairline (cairo_script_surface_t *surface, cairo_bool_t set_hairline)
728
{
729
1
    assert (target_is_active (surface));
730

            
731
1
    if (surface->cr.current_style.is_hairline == set_hairline)
732
    {
733
1
	return CAIRO_STATUS_SUCCESS;
734
    }
735

            
736
    surface->cr.current_style.is_hairline = set_hairline;
737

            
738
    _cairo_output_stream_printf (to_context (surface)->stream, 
739
					"%d set-hairline\n",
740
					set_hairline);
741
    return CAIRO_STATUS_SUCCESS;
742
}
743

            
744
static cairo_status_t
745
1
_emit_line_cap (cairo_script_surface_t *surface,
746
		cairo_line_cap_t line_cap)
747
{
748
1
    assert (target_is_active (surface));
749

            
750
1
    if (surface->cr.current_style.line_cap == line_cap)
751
1
	return CAIRO_STATUS_SUCCESS;
752

            
753
    surface->cr.current_style.line_cap = line_cap;
754

            
755
    _cairo_output_stream_printf (to_context (surface)->stream,
756
				 "//%s set-line-cap\n",
757
				 _line_cap_to_string (line_cap));
758
    return CAIRO_STATUS_SUCCESS;
759
}
760

            
761
static cairo_status_t
762
1
_emit_line_join (cairo_script_surface_t *surface,
763
		 cairo_line_join_t line_join)
764
{
765
1
    assert (target_is_active (surface));
766

            
767
1
    if (surface->cr.current_style.line_join == line_join)
768
1
	return CAIRO_STATUS_SUCCESS;
769

            
770
    surface->cr.current_style.line_join = line_join;
771

            
772
    _cairo_output_stream_printf (to_context (surface)->stream,
773
				 "//%s set-line-join\n",
774
				 _line_join_to_string (line_join));
775
    return CAIRO_STATUS_SUCCESS;
776
}
777

            
778
static cairo_status_t
779
1
_emit_miter_limit (cairo_script_surface_t *surface,
780
		   double miter_limit,
781
		   cairo_bool_t force)
782
{
783
1
    assert (target_is_active (surface));
784

            
785
1
    if ((! force ||
786
	 fabs (miter_limit - CAIRO_GSTATE_MITER_LIMIT_DEFAULT) < 1e-5) &&
787
1
	surface->cr.current_style.miter_limit == miter_limit)
788
    {
789
1
	return CAIRO_STATUS_SUCCESS;
790
    }
791

            
792
    surface->cr.current_style.miter_limit = miter_limit;
793

            
794
    _cairo_output_stream_printf (to_context (surface)->stream,
795
				 "%f set-miter-limit\n",
796
				 miter_limit);
797
    return CAIRO_STATUS_SUCCESS;
798
}
799

            
800
static cairo_bool_t
801
_dashes_equal (const double *a, const double *b, int num_dashes)
802
{
803
    while (num_dashes--) {
804
	if (fabs (*a - *b) > 1e-5)
805
	    return FALSE;
806
	a++, b++;
807
    }
808

            
809
    return TRUE;
810
}
811

            
812
static cairo_status_t
813
1
_emit_dash (cairo_script_surface_t *surface,
814
	    const double *dash,
815
	    unsigned int num_dashes,
816
	    double offset,
817
	    cairo_bool_t force)
818
{
819
    unsigned int n;
820

            
821
1
    assert (target_is_active (surface));
822

            
823
1
    if (force &&
824
	num_dashes == 0 &&
825
	surface->cr.current_style.num_dashes == 0)
826
    {
827
	return CAIRO_STATUS_SUCCESS;
828
    }
829

            
830
1
    if (! force &&
831
1
	(surface->cr.current_style.num_dashes == num_dashes &&
832
	 (num_dashes == 0 ||
833
	  (fabs (surface->cr.current_style.dash_offset - offset) < 1e-5 &&
834
	   _dashes_equal (surface->cr.current_style.dash, dash, num_dashes)))))
835
    {
836
1
	return CAIRO_STATUS_SUCCESS;
837
    }
838

            
839

            
840
    if (num_dashes) {
841
	surface->cr.current_style.dash = _cairo_realloc_ab
842
	    (surface->cr.current_style.dash, num_dashes, sizeof (double));
843
	if (unlikely (surface->cr.current_style.dash == NULL))
844
	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
845

            
846
	memcpy (surface->cr.current_style.dash, dash,
847
		sizeof (double) * num_dashes);
848
    } else {
849
	free (surface->cr.current_style.dash);
850
	surface->cr.current_style.dash = NULL;
851
    }
852

            
853
    surface->cr.current_style.num_dashes = num_dashes;
854
    surface->cr.current_style.dash_offset = offset;
855

            
856
    _cairo_output_stream_puts (to_context (surface)->stream, "[");
857
    for (n = 0; n < num_dashes; n++) {
858
	_cairo_output_stream_printf (to_context (surface)->stream, "%f", dash[n]);
859
	if (n < num_dashes-1)
860
	    _cairo_output_stream_puts (to_context (surface)->stream, " ");
861
    }
862
    _cairo_output_stream_printf (to_context (surface)->stream,
863
				 "] %f set-dash\n",
864
				 offset);
865

            
866
    return CAIRO_STATUS_SUCCESS;
867
}
868

            
869
static cairo_status_t
870
1
_emit_stroke_style (cairo_script_surface_t *surface,
871
		    const cairo_stroke_style_t *style,
872
		    cairo_bool_t force)
873
{
874
    cairo_status_t status;
875

            
876
1
    assert (target_is_active (surface));
877

            
878
1
    status = _emit_line_width (surface, style->line_width, force);
879
1
    if (unlikely (status))
880
	return status;
881

            
882
1
    status = _emit_line_cap (surface, style->line_cap);
883
1
    if (unlikely (status))
884
	return status;
885

            
886
1
    status = _emit_line_join (surface, style->line_join);
887
1
    if (unlikely (status))
888
	return status;
889

            
890
1
    status = _emit_miter_limit (surface, style->miter_limit, force);
891
1
    if (unlikely (status))
892
	return status;
893

            
894
1
    status = _emit_hairline (surface, style->is_hairline);
895
1
    if (unlikely (status))
896
	return status;
897

            
898
1
    status = _emit_dash (surface,
899
1
			 style->dash, style->num_dashes, style->dash_offset,
900
			 force);
901
1
    if (unlikely (status))
902
	return status;
903

            
904
1
    return CAIRO_STATUS_SUCCESS;
905
}
906

            
907
static const char *
908
_format_to_string (cairo_format_t format)
909
{
910
    switch (format) {
911
    case CAIRO_FORMAT_RGBA128F: return "RGBA128F";
912
    case CAIRO_FORMAT_RGB96F: return "RGB96F";
913
    case CAIRO_FORMAT_ARGB32:  return "ARGB32";
914
    case CAIRO_FORMAT_RGB30:   return "RGB30";
915
    case CAIRO_FORMAT_RGB24:   return "RGB24";
916
    case CAIRO_FORMAT_RGB16_565: return "RGB16_565";
917
    case CAIRO_FORMAT_A8:      return "A8";
918
    case CAIRO_FORMAT_A1:      return "A1";
919
    case CAIRO_FORMAT_INVALID: return "INVALID";
920
    }
921
    ASSERT_NOT_REACHED;
922
    return "INVALID";
923
}
924

            
925
static cairo_status_t
926
_emit_solid_pattern (cairo_script_surface_t *surface,
927
		     const cairo_pattern_t *pattern)
928
{
929
    cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
930
    cairo_script_context_t *ctx = to_context (surface);
931

            
932
    if (! CAIRO_COLOR_IS_OPAQUE (&solid->color))
933
    {
934
	if (! (surface->base.content & CAIRO_CONTENT_COLOR) ||
935
	    ((solid->color.red_short   == 0 || solid->color.red_short   == 0xffff) &&
936
	     (solid->color.green_short == 0 || solid->color.green_short == 0xffff) &&
937
	     (solid->color.blue_short  == 0 || solid->color.blue_short  == 0xffff) ))
938
	{
939
	    _cairo_output_stream_printf (ctx->stream,
940
					 "%f a",
941
					 solid->color.alpha);
942
	}
943
	else
944
	{
945
	    _cairo_output_stream_printf (ctx->stream,
946
					 "%f %f %f %f rgba",
947
					 solid->color.red,
948
					 solid->color.green,
949
					 solid->color.blue,
950
					 solid->color.alpha);
951
	}
952
    }
953
    else
954
    {
955
	if (solid->color.red_short == solid->color.green_short &&
956
	    solid->color.red_short == solid->color.blue_short)
957
	{
958
	    _cairo_output_stream_printf (ctx->stream,
959
					 "%f g",
960
					 solid->color.red);
961
	}
962
	else
963
	{
964
	    _cairo_output_stream_printf (ctx->stream,
965
					 "%f %f %f rgb",
966
					 solid->color.red,
967
					 solid->color.green,
968
					 solid->color.blue);
969
	}
970
    }
971

            
972
    return CAIRO_STATUS_SUCCESS;
973
}
974

            
975

            
976
static cairo_status_t
977
_emit_gradient_color_stops (cairo_gradient_pattern_t *gradient,
978
			    cairo_output_stream_t *output)
979
{
980
    unsigned int n;
981

            
982
    for (n = 0; n < gradient->n_stops; n++) {
983
	_cairo_output_stream_printf (output,
984
				     "\n  %f %f %f %f %f add-color-stop",
985
				     gradient->stops[n].offset,
986
				     gradient->stops[n].color.red,
987
				     gradient->stops[n].color.green,
988
				     gradient->stops[n].color.blue,
989
				     gradient->stops[n].color.alpha);
990
    }
991

            
992
    return CAIRO_STATUS_SUCCESS;
993
}
994

            
995
static cairo_status_t
996
_emit_linear_pattern (cairo_script_surface_t *surface,
997
		      const cairo_pattern_t *pattern)
998
{
999
    cairo_script_context_t *ctx = to_context (surface);
    cairo_linear_pattern_t *linear;
    linear = (cairo_linear_pattern_t *) pattern;
    _cairo_output_stream_printf (ctx->stream,
				 "%f %f %f %f linear",
				 linear->pd1.x, linear->pd1.y,
				 linear->pd2.x, linear->pd2.y);
    return _emit_gradient_color_stops (&linear->base, ctx->stream);
}
static cairo_status_t
_emit_radial_pattern (cairo_script_surface_t *surface,
		      const cairo_pattern_t *pattern)
{
    cairo_script_context_t *ctx = to_context (surface);
    cairo_radial_pattern_t *radial;
    radial = (cairo_radial_pattern_t *) pattern;
    _cairo_output_stream_printf (ctx->stream,
				 "%f %f %f %f %f %f radial",
				 radial->cd1.center.x,
				 radial->cd1.center.y,
				 radial->cd1.radius,
				 radial->cd2.center.x,
				 radial->cd2.center.y,
				 radial->cd2.radius);
    return _emit_gradient_color_stops (&radial->base, ctx->stream);
}
static cairo_status_t
_emit_mesh_pattern (cairo_script_surface_t *surface,
		    const cairo_pattern_t *pattern)
{
    cairo_script_context_t *ctx = to_context (surface);
    cairo_pattern_t *mesh;
    cairo_status_t status;
    unsigned int i, n;
    mesh = (cairo_pattern_t *) pattern;
    status = cairo_mesh_pattern_get_patch_count (mesh, &n);
    if (unlikely (status))
	return status;
    _cairo_output_stream_printf (ctx->stream, "mesh");
    for (i = 0; i < n; i++) {
	cairo_path_t *path;
	cairo_path_data_t *data;
	int j;
	_cairo_output_stream_printf (ctx->stream, "\n  begin-patch");
	path = cairo_mesh_pattern_get_path (mesh, i);
	if (unlikely (path->status))
	    return path->status;
	for (j = 0; j < path->num_data; j+=data[0].header.length) {
	    data = &path->data[j];
	    switch (data->header.type) {
	    case CAIRO_PATH_MOVE_TO:
		_cairo_output_stream_printf (ctx->stream,
					     "\n  %f %f m",
					     data[1].point.x, data[1].point.y);
		break;
	    case CAIRO_PATH_LINE_TO:
		_cairo_output_stream_printf (ctx->stream,
					     "\n  %f %f l",
					     data[1].point.x, data[1].point.y);
		break;
	    case CAIRO_PATH_CURVE_TO:
		_cairo_output_stream_printf (ctx->stream,
					     "\n  %f %f %f %f %f %f c",
					     data[1].point.x, data[1].point.y,
					     data[2].point.x, data[2].point.y,
					     data[3].point.x, data[3].point.y);
		break;
	    case CAIRO_PATH_CLOSE_PATH:
		break;
	    }
	}
	cairo_path_destroy (path);
	for (j = 0; j < 4; j++) {
	    double x, y;
	    status = cairo_mesh_pattern_get_control_point (mesh, i, j, &x, &y);
	    if (unlikely (status))
		return status;
	    _cairo_output_stream_printf (ctx->stream,
					 "\n  %d %f %f set-control-point",
					 j, x, y);
	}
	for (j = 0; j < 4; j++) {
	    double r, g, b, a;
	    status = cairo_mesh_pattern_get_corner_color_rgba (mesh, i, j, &r, &g, &b, &a);
	    if (unlikely (status))
		return status;
	    _cairo_output_stream_printf (ctx->stream,
					 "\n  %d %f %f %f %f set-corner-color",
					 j, r, g, b, a);
	}
	_cairo_output_stream_printf (ctx->stream, "\n  end-patch");
    }
    return CAIRO_STATUS_SUCCESS;
}
struct script_snapshot {
    cairo_surface_t base;
};
static cairo_status_t
1
script_snapshot_finish (void *abstract_surface)
{
1
    return CAIRO_STATUS_SUCCESS;
}
static const cairo_surface_backend_t script_snapshot_backend = {
    CAIRO_SURFACE_TYPE_SCRIPT,
    script_snapshot_finish,
};
static void
1
detach_snapshot (cairo_surface_t *abstract_surface)
{
1
    cairo_script_surface_t *surface = (cairo_script_surface_t *)abstract_surface;
1
    cairo_script_context_t *ctx = to_context (surface);
1
    _cairo_output_stream_printf (ctx->stream,
				 "/s%d undef\n",
				 surface->base.unique_id);
1
}
static void
1
attach_snapshot (cairo_script_context_t *ctx,
		 cairo_surface_t *source)
{
    struct script_snapshot *surface;
1
    if (! ctx->attach_snapshots)
	return;
1
    surface = _cairo_calloc (sizeof (*surface));
1
    if (unlikely (surface == NULL))
	return;
1
    _cairo_surface_init (&surface->base,
			 &script_snapshot_backend,
			 &ctx->base,
			 source->content,
1
			 source->is_vector);
1
    _cairo_output_stream_printf (ctx->stream,
				 "dup /s%d exch def ",
				 surface->base.unique_id);
1
    _cairo_surface_attach_snapshot (source, &surface->base, detach_snapshot);
1
    cairo_surface_destroy (&surface->base);
}
static cairo_status_t
1
_emit_recording_surface_pattern (cairo_script_surface_t *surface,
				 cairo_recording_surface_t *source)
{
    cairo_script_implicit_context_t old_cr;
1
    cairo_script_context_t *ctx = to_context (surface);
    cairo_script_surface_t *similar;
    cairo_surface_t *snapshot;
    cairo_rectangle_t r, *extents;
    cairo_status_t status;
1
    snapshot = _cairo_surface_has_snapshot (&source->base, &script_snapshot_backend);
1
    if (snapshot) {
	_cairo_output_stream_printf (ctx->stream, "s%d", snapshot->unique_id);
	return CAIRO_INT_STATUS_SUCCESS;
    }
1
    extents = NULL;
1
    if (_cairo_recording_surface_get_bounds (&source->base, &r))
	extents = &r;
1
    similar = _cairo_script_surface_create_internal (ctx,
						     source->base.content,
						     extents,
						     NULL);
1
    if (unlikely (similar->base.status))
	return similar->base.status;
1
    similar->base.is_clear = TRUE;
1
    _cairo_output_stream_printf (ctx->stream, "//%s ",
				 _content_to_string (source->base.content));
1
    if (extents) {
	_cairo_output_stream_printf (ctx->stream, "[%f %f %f %f]",
				     extents->x, extents->y,
				     extents->width, extents->height);
    } else
1
	_cairo_output_stream_puts (ctx->stream, "[]");
1
    _cairo_output_stream_puts (ctx->stream, " record\n");
1
    attach_snapshot (ctx, &source->base);
1
    _cairo_output_stream_puts (ctx->stream, "dup context\n");
1
    target_push (similar);
1
    similar->emitted = TRUE;
1
    old_cr = surface->cr;
1
    _cairo_script_implicit_context_init (&surface->cr);
1
    status = _cairo_recording_surface_replay (&source->base, &similar->base);
1
    surface->cr = old_cr;
1
    if (unlikely (status)) {
	cairo_surface_destroy (&similar->base);
	return status;
    }
1
    cairo_list_del (&similar->operand.link);
1
    assert (target_is_active (surface));
1
    _cairo_output_stream_puts (ctx->stream, "pop ");
1
    cairo_surface_destroy (&similar->base);
1
    return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_script_surface_pattern (cairo_script_surface_t *surface,
			      cairo_script_surface_t *source)
{
    _get_target (source);
    return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_write_image_surface (cairo_output_stream_t *output,
		      const cairo_image_surface_t *image)
{
    int row, width;
    ptrdiff_t stride;
    uint8_t row_stack[CAIRO_STACK_BUFFER_SIZE];
    uint8_t *rowdata;
    uint8_t *data;
    stride = image->stride;
    width = image->width;
    data = image->data;
#if WORDS_BIGENDIAN
    switch (image->format) {
    case CAIRO_FORMAT_A1:
	for (row = image->height; row--; ) {
	    _cairo_output_stream_write (output, data, (width+7)/8);
	    data += stride;
	}
	break;
    case CAIRO_FORMAT_A8:
	for (row = image->height; row--; ) {
	    _cairo_output_stream_write (output, data, width);
	    data += stride;
	}
	break;
    case CAIRO_FORMAT_RGB16_565:
	for (row = image->height; row--; ) {
	    _cairo_output_stream_write (output, data, 2*width);
	    data += stride;
	}
	break;
    case CAIRO_FORMAT_RGB24:
	for (row = image->height; row--; ) {
	    int col;
	    rowdata = data;
	    for (col = width; col--; ) {
		_cairo_output_stream_write (output, rowdata, 3);
		rowdata+=4;
	    }
	    data += stride;
	}
	break;
    case CAIRO_FORMAT_ARGB32:
	for (row = image->height; row--; ) {
	    _cairo_output_stream_write (output, data, 4*width);
	    data += stride;
	}
	break;
    case CAIRO_FORMAT_INVALID:
    default:
	ASSERT_NOT_REACHED;
	break;
    }
#else
    if (stride > ARRAY_LENGTH (row_stack)) {
	rowdata = _cairo_malloc (stride);
	if (unlikely (rowdata == NULL))
	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    } else
	rowdata = row_stack;
    switch (image->format) {
    case CAIRO_FORMAT_A1:
	for (row = image->height; row--; ) {
	    int col;
	    for (col = 0; col < (width + 7)/8; col++)
		rowdata[col] = CAIRO_BITSWAP8 (data[col]);
	    _cairo_output_stream_write (output, rowdata, (width+7)/8);
	    data += stride;
	}
	break;
    case CAIRO_FORMAT_A8:
	for (row = image->height; row--; ) {
	    _cairo_output_stream_write (output, data, width);
	    data += stride;
	}
	break;
    case CAIRO_FORMAT_RGB16_565:
	for (row = image->height; row--; ) {
	    uint16_t *src = (uint16_t *) data;
	    uint16_t *dst = (uint16_t *) rowdata;
	    int col;
	    for (col = 0; col < width; col++)
		dst[col] = bswap_16 (src[col]);
	    _cairo_output_stream_write (output, rowdata, 2*width);
	    data += stride;
	}
	break;
    case CAIRO_FORMAT_RGB24:
	for (row = image->height; row--; ) {
	    uint8_t *src = data;
	    int col;
	    for (col = 0; col < width; col++) {
		rowdata[3*col+2] = *src++;
		rowdata[3*col+1] = *src++;
		rowdata[3*col+0] = *src++;
		src++;
	    }
	    _cairo_output_stream_write (output, rowdata, 3*width);
	    data += stride;
	}
	break;
    case CAIRO_FORMAT_RGB30:
    case CAIRO_FORMAT_ARGB32:
	for (row = image->height; row--; ) {
	    uint32_t *src = (uint32_t *) data;
	    uint32_t *dst = (uint32_t *) rowdata;
	    int col;
	    for (col = 0; col < width; col++)
		dst[col] = bswap_32 (src[col]);
	    _cairo_output_stream_write (output, rowdata, 4*width);
	    data += stride;
	}
	break;
    case CAIRO_FORMAT_RGB96F:
	for (row = image->height; row--; ) {
	    _cairo_output_stream_write (output, data, 12*width);
	    data += stride;
	}
	break;
    case CAIRO_FORMAT_RGBA128F:
	for (row = image->height; row--; ) {
	    _cairo_output_stream_write (output, data, 16*width);
	    data += stride;
	}
	break;
    case CAIRO_FORMAT_INVALID:
    default:
	ASSERT_NOT_REACHED;
	break;
    }
    if (rowdata != row_stack)
	free (rowdata);
#endif
    return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
_emit_png_surface (cairo_script_surface_t *surface,
		   cairo_image_surface_t *image)
{
    cairo_script_context_t *ctx = to_context (surface);
    cairo_output_stream_t *base85_stream;
    cairo_status_t status;
    const uint8_t *mime_data;
    unsigned long mime_data_length;
    cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_PNG,
				 &mime_data, &mime_data_length);
    if (mime_data == NULL)
	return CAIRO_INT_STATUS_UNSUPPORTED;
    _cairo_output_stream_printf (ctx->stream,
				 "<< "
				 "/width %d "
				 "/height %d "
				 "/format //%s "
				 "/mime-type (image/png) "
				 "/source <~",
				 image->width, image->height,
				 _format_to_string (image->format));
    base85_stream = _cairo_base85_stream_create (ctx->stream);
    _cairo_output_stream_write (base85_stream, mime_data, mime_data_length);
    status = _cairo_output_stream_destroy (base85_stream);
    if (unlikely (status))
	return status;
    _cairo_output_stream_puts (ctx->stream, "~> >> image ");
    return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
_emit_image_surface (cairo_script_surface_t *surface,
		     cairo_image_surface_t *image)
{
    cairo_script_context_t *ctx = to_context (surface);
    cairo_output_stream_t *base85_stream;
    cairo_output_stream_t *zlib_stream;
    cairo_int_status_t status, status2;
    cairo_surface_t *snapshot;
    const uint8_t *mime_data;
    unsigned long mime_data_length;
    snapshot = _cairo_surface_has_snapshot (&image->base,
					    &script_snapshot_backend);
    if (snapshot) {
	_cairo_output_stream_printf (ctx->stream, "s%u ", snapshot->unique_id);
	return CAIRO_INT_STATUS_SUCCESS;
    }
    status = _emit_png_surface (surface, image);
    if (_cairo_int_status_is_error (status)) {
	return status;
    } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
	cairo_image_surface_t *clone;
	uint32_t len;
	if (image->format == CAIRO_FORMAT_INVALID) {
	    clone = _cairo_image_surface_coerce (image);
	} else {
	    clone = (cairo_image_surface_t *)
		cairo_surface_reference (&image->base);
	}
	_cairo_output_stream_printf (ctx->stream,
				     "<< "
				     "/width %d "
				     "/height %d "
				     "/format //%s "
				     "/source ",
				     clone->width, clone->height,
				     _format_to_string (clone->format));
	switch (clone->format) {
	case CAIRO_FORMAT_A1:
	    len = (clone->width + 7)/8;
	    break;
	case CAIRO_FORMAT_A8:
	    len = clone->width;
	    break;
	case CAIRO_FORMAT_RGB16_565:
	    len = clone->width * 2;
	    break;
	case CAIRO_FORMAT_RGB24:
	    len = clone->width * 3;
	    break;
	case CAIRO_FORMAT_RGB30:
	case CAIRO_FORMAT_ARGB32:
	    len = clone->width * 4;
	    break;
	case CAIRO_FORMAT_RGB96F:
	    len = clone->width * 12;
	    break;
	case CAIRO_FORMAT_RGBA128F:
	    len = clone->width * 16;
	    break;
	case CAIRO_FORMAT_INVALID:
	default:
	    ASSERT_NOT_REACHED;
	    len = 0;
	    break;
	}
	len *= clone->height;
	if (len > 24) {
	    _cairo_output_stream_puts (ctx->stream, "<|");
	    base85_stream = _cairo_base85_stream_create (ctx->stream);
	    len = to_be32 (len);
	    _cairo_output_stream_write (base85_stream, &len, sizeof (len));
	    zlib_stream = _cairo_deflate_stream_create (base85_stream);
	    status = _write_image_surface (zlib_stream, clone);
	    status2 = _cairo_output_stream_destroy (zlib_stream);
	    if (status == CAIRO_INT_STATUS_SUCCESS)
		status = status2;
	    status2 = _cairo_output_stream_destroy (base85_stream);
	    if (status == CAIRO_INT_STATUS_SUCCESS)
		status = status2;
	    if (unlikely (status))
		return status;
	} else {
	    _cairo_output_stream_puts (ctx->stream, "<~");
	    base85_stream = _cairo_base85_stream_create (ctx->stream);
	    status = _write_image_surface (base85_stream, clone);
	    status2 = _cairo_output_stream_destroy (base85_stream);
	    if (status == CAIRO_INT_STATUS_SUCCESS)
		status = status2;
	    if (unlikely (status))
		return status;
	}
	_cairo_output_stream_puts (ctx->stream, "~> >> image ");
	cairo_surface_destroy (&clone->base);
    }
    cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JPEG,
				 &mime_data, &mime_data_length);
    if (mime_data != NULL) {
	_cairo_output_stream_printf (ctx->stream,
				     "\n  (%s) <~",
				     CAIRO_MIME_TYPE_JPEG);
	base85_stream = _cairo_base85_stream_create (ctx->stream);
	_cairo_output_stream_write (base85_stream, mime_data, mime_data_length);
	status = _cairo_output_stream_destroy (base85_stream);
	if (unlikely (status))
	    return status;
	_cairo_output_stream_puts (ctx->stream, "~> set-mime-data\n");
    }
    cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JP2,
				 &mime_data, &mime_data_length);
    if (mime_data != NULL) {
	_cairo_output_stream_printf (ctx->stream,
				     "\n  (%s) <~",
				     CAIRO_MIME_TYPE_JP2);
	base85_stream = _cairo_base85_stream_create (ctx->stream);
	_cairo_output_stream_write (base85_stream, mime_data, mime_data_length);
	status = _cairo_output_stream_destroy (base85_stream);
	if (unlikely (status))
	    return status;
	_cairo_output_stream_puts (ctx->stream, "~> set-mime-data\n");
    }
    return CAIRO_INT_STATUS_SUCCESS;
}
static cairo_int_status_t
_emit_image_surface_pattern (cairo_script_surface_t *surface,
			     cairo_surface_t *source)
{
    cairo_image_surface_t *image;
    cairo_status_t status;
    void *extra;
    status = _cairo_surface_acquire_source_image (source, &image, &extra);
    if (likely (status == CAIRO_STATUS_SUCCESS)) {
	status = _emit_image_surface (surface, image);
	_cairo_surface_release_source_image (source, image, extra);
    }
    return status;
}
static cairo_int_status_t
_emit_subsurface_pattern (cairo_script_surface_t *surface,
			  cairo_surface_subsurface_t *sub)
{
    cairo_surface_t *source = sub->target;
    cairo_int_status_t status;
    switch ((int) source->backend->type) {
    case CAIRO_SURFACE_TYPE_RECORDING:
	status = _emit_recording_surface_pattern (surface, (cairo_recording_surface_t *) source);
	break;
    case CAIRO_SURFACE_TYPE_SCRIPT:
	status = _emit_script_surface_pattern (surface, (cairo_script_surface_t *) source);
	break;
    default:
	status = _emit_image_surface_pattern (surface, source);
	break;
    }
    if (unlikely (status))
	return status;
    _cairo_output_stream_printf (to_context (surface)->stream,
				 "%d %d %d %d subsurface ",
				 sub->extents.x,
				 sub->extents.y,
				 sub->extents.width,
				 sub->extents.height);
    return CAIRO_INT_STATUS_SUCCESS;
}
static cairo_int_status_t
1
_emit_surface_pattern (cairo_script_surface_t *surface,
		       const cairo_pattern_t *pattern)
{
1
    cairo_script_context_t *ctx = to_context (surface);
    cairo_surface_pattern_t *surface_pattern;
1
    cairo_surface_t *source, *snapshot, *free_me = NULL;
1
    cairo_surface_t *take_snapshot = NULL;
    cairo_int_status_t status;
1
    surface_pattern = (cairo_surface_pattern_t *) pattern;
1
    source = surface_pattern->surface;
1
    if (_cairo_surface_is_snapshot (source)) {
	snapshot = _cairo_surface_has_snapshot (source, &script_snapshot_backend);
	if (snapshot) {
	    _cairo_output_stream_printf (ctx->stream,
					 "s%d pattern ",
					 snapshot->unique_id);
	    return CAIRO_INT_STATUS_SUCCESS;
	}
	if (_cairo_surface_snapshot_is_reused (source))
	    take_snapshot = source;
	free_me = source = _cairo_surface_snapshot_get_target (source);
    }
1
    switch ((int) source->backend->type) {
1
    case CAIRO_SURFACE_TYPE_RECORDING:
1
	status = _emit_recording_surface_pattern (surface, (cairo_recording_surface_t *) source);
1
	break;
    case CAIRO_SURFACE_TYPE_SCRIPT:
	status = _emit_script_surface_pattern (surface, (cairo_script_surface_t *) source);
	break;
    case CAIRO_SURFACE_TYPE_SUBSURFACE:
	status = _emit_subsurface_pattern (surface, (cairo_surface_subsurface_t *) source);
	break;
    default:
	status = _emit_image_surface_pattern (surface, source);
	break;
    }
1
    cairo_surface_destroy (free_me);
1
    if (unlikely (status))
	return status;
1
    if (take_snapshot)
	attach_snapshot (ctx, take_snapshot);
1
    _cairo_output_stream_puts (ctx->stream, "pattern");
1
    return CAIRO_INT_STATUS_SUCCESS;
}
static cairo_int_status_t
_emit_raster_pattern (cairo_script_surface_t *surface,
		      const cairo_pattern_t *pattern)
{
    cairo_surface_t *source;
    cairo_int_status_t status;
    source = _cairo_raster_source_pattern_acquire (pattern, &surface->base, NULL);
    if (unlikely (source == NULL)) {
	ASSERT_NOT_REACHED;
	return CAIRO_INT_STATUS_UNSUPPORTED;
    }
    if (unlikely (source->status))
	return source->status;
    status = _emit_image_surface_pattern (surface, source);
    _cairo_raster_source_pattern_release (pattern, source);
    if (unlikely (status))
	return status;
    _cairo_output_stream_puts (to_context(surface)->stream, "pattern");
    return CAIRO_INT_STATUS_SUCCESS;
}
static cairo_int_status_t
1
_emit_pattern (cairo_script_surface_t *surface,
	       const cairo_pattern_t *pattern)
{
1
    cairo_script_context_t *ctx = to_context (surface);
    cairo_int_status_t status;
    cairo_bool_t is_default_extend;
1
    cairo_bool_t need_newline = TRUE;
1
    switch (pattern->type) {
    case CAIRO_PATTERN_TYPE_SOLID:
	/* solid colors do not need filter/extend/matrix */
	return _emit_solid_pattern (surface, pattern);
    case CAIRO_PATTERN_TYPE_LINEAR:
	status = _emit_linear_pattern (surface, pattern);
	is_default_extend = pattern->extend == CAIRO_EXTEND_GRADIENT_DEFAULT;
	break;
    case CAIRO_PATTERN_TYPE_RADIAL:
	status = _emit_radial_pattern (surface, pattern);
	is_default_extend = pattern->extend == CAIRO_EXTEND_GRADIENT_DEFAULT;
	break;
    case CAIRO_PATTERN_TYPE_MESH:
	status = _emit_mesh_pattern (surface, pattern);
	is_default_extend = TRUE;
	break;
1
    case CAIRO_PATTERN_TYPE_SURFACE:
1
	status = _emit_surface_pattern (surface, pattern);
1
	is_default_extend = pattern->extend == CAIRO_EXTEND_SURFACE_DEFAULT;
1
	break;
    case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
	status = _emit_raster_pattern (surface, pattern);
	is_default_extend = pattern->extend == CAIRO_EXTEND_SURFACE_DEFAULT;
	break;
    default:
	ASSERT_NOT_REACHED;
	status = CAIRO_INT_STATUS_UNSUPPORTED;
    }
1
    if (unlikely (status))
	return status;
1
    if (! _cairo_matrix_is_identity (&pattern->matrix)) {
	if (need_newline) {
	    _cairo_output_stream_puts (ctx->stream, "\n ");
	    need_newline = FALSE;
	}
	_cairo_output_stream_printf (ctx->stream,
				     " [%f %f %f %f %f %f] set-matrix\n ",
				     pattern->matrix.xx, pattern->matrix.yx,
				     pattern->matrix.xy, pattern->matrix.yy,
				     pattern->matrix.x0, pattern->matrix.y0);
    }
    /* XXX need to discriminate the user explicitly setting the default */
1
    if (pattern->filter != CAIRO_FILTER_DEFAULT) {
	if (need_newline) {
	    _cairo_output_stream_puts (ctx->stream, "\n ");
	    need_newline = FALSE;
	}
	_cairo_output_stream_printf (ctx->stream,
				     " //%s set-filter\n ",
				     _filter_to_string (pattern->filter));
    }
    /* XXX need to discriminate the user explicitly setting the default */
1
    if (pattern->dither != CAIRO_DITHER_DEFAULT) {
	if (need_newline) {
	    _cairo_output_stream_puts (ctx->stream, "\n ");
	    need_newline = FALSE;
	}
	_cairo_output_stream_printf (ctx->stream,
				     " //%s set-dither\n ",
				     _dither_to_string (pattern->dither));
    }
1
    if (! is_default_extend ){
	if (need_newline) {
	    _cairo_output_stream_puts (ctx->stream, "\n ");
	    need_newline = FALSE;
	}
	_cairo_output_stream_printf (ctx->stream,
				     " //%s set-extend\n ",
				     _extend_to_string (pattern->extend));
    }
1
    if (need_newline)
1
	_cairo_output_stream_puts (ctx->stream, "\n ");
1
    return CAIRO_INT_STATUS_SUCCESS;
}
static cairo_int_status_t
2
_emit_identity (cairo_script_surface_t *surface,
		cairo_bool_t *matrix_updated)
{
2
    assert (target_is_active (surface));
2
    if (_cairo_matrix_is_identity (&surface->cr.current_ctm))
2
	return CAIRO_INT_STATUS_SUCCESS;
    _cairo_output_stream_puts (to_context (surface)->stream,
			       "identity set-matrix\n");
    *matrix_updated = TRUE;
    cairo_matrix_init_identity (&surface->cr.current_ctm);
    return CAIRO_INT_STATUS_SUCCESS;
}
static cairo_int_status_t
2
_emit_source (cairo_script_surface_t *surface,
	      cairo_operator_t op,
	      const cairo_pattern_t *source)
{
2
    cairo_bool_t matrix_updated = FALSE;
    cairo_int_status_t status;
2
    assert (target_is_active (surface));
2
    if (op == CAIRO_OPERATOR_CLEAR) {
	/* the source is ignored, so don't change it */
	return CAIRO_INT_STATUS_SUCCESS;
    }
2
    if (_cairo_pattern_equal (&surface->cr.current_source.base, source))
1
	return CAIRO_INT_STATUS_SUCCESS;
1
    _cairo_pattern_fini (&surface->cr.current_source.base);
1
    status = _cairo_pattern_init_copy (&surface->cr.current_source.base,
				       source);
1
    if (unlikely (status))
	return status;
1
    status = _emit_identity (surface, &matrix_updated);
1
    if (unlikely (status))
	return status;
1
    status = _emit_pattern (surface, source);
1
    if (unlikely (status))
	return status;
1
    assert (target_is_active (surface));
1
    _cairo_output_stream_puts (to_context (surface)->stream,
			       " set-source\n");
1
    return CAIRO_INT_STATUS_SUCCESS;
}
static cairo_status_t
1
_path_move_to (void *closure,
	       const cairo_point_t *point)
{
1
    _cairo_output_stream_printf (closure,
				 " %f %f m",
1
				 _cairo_fixed_to_double (point->x),
1
				 _cairo_fixed_to_double (point->y));
1
    return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
1
_path_line_to (void *closure,
	       const cairo_point_t *point)
{
1
    _cairo_output_stream_printf (closure,
				 " %f %f l",
1
				 _cairo_fixed_to_double (point->x),
1
				 _cairo_fixed_to_double (point->y));
1
    return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_path_curve_to (void *closure,
		const cairo_point_t *p1,
		const cairo_point_t *p2,
		const cairo_point_t *p3)
{
    _cairo_output_stream_printf (closure,
				 " %f %f %f %f %f %f c",
				 _cairo_fixed_to_double (p1->x),
				 _cairo_fixed_to_double (p1->y),
				 _cairo_fixed_to_double (p2->x),
				 _cairo_fixed_to_double (p2->y),
				 _cairo_fixed_to_double (p3->x),
				 _cairo_fixed_to_double (p3->y));
    return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_path_close (void *closure)
{
    _cairo_output_stream_printf (closure,
				 " h");
    return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_path_boxes (cairo_script_surface_t *surface,
		  const cairo_path_fixed_t *path)
{
    cairo_script_context_t *ctx = to_context (surface);
    cairo_path_fixed_iter_t iter;
    cairo_status_t status = CAIRO_STATUS_SUCCESS;
    struct _cairo_boxes_chunk *chunk;
    cairo_boxes_t boxes;
    cairo_box_t box;
    int i;
    _cairo_boxes_init (&boxes);
    _cairo_path_fixed_iter_init (&iter, path);
    while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) {
	if (box.p1.y == box.p2.y || box.p1.x == box.p2.x)
	    continue;
	status = _cairo_boxes_add (&boxes, CAIRO_ANTIALIAS_DEFAULT, &box);
	if (unlikely (status)) {
	    _cairo_boxes_fini (&boxes);
	    return status;
	}
    }
    if (! _cairo_path_fixed_iter_at_end (&iter)) {
	_cairo_boxes_fini (&boxes);
	return CAIRO_INT_STATUS_UNSUPPORTED;
    }
    for (chunk = &boxes.chunks; chunk; chunk = chunk->next) {
	for (i = 0; i < chunk->count; i++) {
	    const cairo_box_t *b = &chunk->base[i];
	    double x1 = _cairo_fixed_to_double (b->p1.x);
	    double y1 = _cairo_fixed_to_double (b->p1.y);
	    double x2 = _cairo_fixed_to_double (b->p2.x);
	    double y2 = _cairo_fixed_to_double (b->p2.y);
	    _cairo_output_stream_printf (ctx->stream,
					 "\n  %f %f %f %f rectangle",
					 x1, y1, x2 - x1, y2 - y1);
	}
    }
    _cairo_boxes_fini (&boxes);
    return status;
}
static cairo_status_t
1
_emit_path (cairo_script_surface_t *surface,
	    const cairo_path_fixed_t *path,
	    cairo_bool_t is_fill)
{
1
    cairo_script_context_t *ctx = to_context (surface);
    cairo_box_t box;
    cairo_int_status_t status;
1
    assert (target_is_active (surface));
1
    assert (_cairo_matrix_is_identity (&surface->cr.current_ctm));
1
    if (_cairo_path_fixed_equal (&surface->cr.current_path, path))
	return CAIRO_STATUS_SUCCESS;
1
    _cairo_path_fixed_fini (&surface->cr.current_path);
1
    _cairo_output_stream_puts (ctx->stream, "n");
1
    if (path == NULL) {
	_cairo_path_fixed_init (&surface->cr.current_path);
	_cairo_output_stream_puts (ctx->stream, "\n");
	return CAIRO_STATUS_SUCCESS;
    }
1
    status = _cairo_path_fixed_init_copy (&surface->cr.current_path, path);
1
    if (unlikely (status))
	return status;
1
    status = CAIRO_INT_STATUS_UNSUPPORTED;
1
    if (_cairo_path_fixed_is_rectangle (path, &box)) {
	double x1 = _cairo_fixed_to_double (box.p1.x);
	double y1 = _cairo_fixed_to_double (box.p1.y);
	double x2 = _cairo_fixed_to_double (box.p2.x);
	double y2 = _cairo_fixed_to_double (box.p2.y);
	assert (x1 > -9999);
	_cairo_output_stream_printf (ctx->stream,
				     " %f %f %f %f rectangle",
				     x1, y1, x2 - x1, y2 - y1);
	status = CAIRO_INT_STATUS_SUCCESS;
1
    } else if (is_fill && _cairo_path_fixed_fill_is_rectilinear (path)) {
	status = _emit_path_boxes (surface, path);
    }
1
    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
1
	status = _cairo_path_fixed_interpret (path,
					      _path_move_to,
					      _path_line_to,
					      _path_curve_to,
					      _path_close,
1
					      ctx->stream);
    }
1
    _cairo_output_stream_puts (ctx->stream, "\n");
1
    return status;
}
static cairo_bool_t
2
_scaling_matrix_equal (const cairo_matrix_t *a,
		       const cairo_matrix_t *b)
{
4
    return fabs (a->xx - b->xx) < 1e-5 &&
2
	   fabs (a->xy - b->xy) < 1e-5 &&
6
	   fabs (a->yx - b->yx) < 1e-5 &&
2
	   fabs (a->yy - b->yy) < 1e-5;
}
static cairo_status_t
1
_emit_scaling_matrix (cairo_script_surface_t *surface,
		      const cairo_matrix_t *ctm,
		      cairo_bool_t *matrix_updated)
{
1
    cairo_script_context_t *ctx = to_context (surface);
    cairo_bool_t was_identity;
1
    assert (target_is_active (surface));
1
    if (_scaling_matrix_equal (&surface->cr.current_ctm, ctm))
1
	return CAIRO_STATUS_SUCCESS;
    was_identity = _cairo_matrix_is_identity (&surface->cr.current_ctm);
    *matrix_updated = TRUE;
    surface->cr.current_ctm = *ctm;
    surface->cr.current_ctm.x0 = 0.;
    surface->cr.current_ctm.y0 = 0.;
    if (_cairo_matrix_is_identity (&surface->cr.current_ctm)) {
	_cairo_output_stream_puts (ctx->stream,
				   "identity set-matrix\n");
    } else if (was_identity && fabs (ctm->yx) < 1e-5 && fabs (ctm->xy) < 1e-5) {
	_cairo_output_stream_printf (ctx->stream,
				     "%f %f scale\n",
				     ctm->xx, ctm->yy);
    } else {
	_cairo_output_stream_printf (ctx->stream,
				     "[%f %f %f %f 0 0] set-matrix\n",
				     ctm->xx, ctm->yx,
				     ctm->xy, ctm->yy);
    }
    return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_font_matrix (cairo_script_surface_t *surface,
		   const cairo_matrix_t *font_matrix)
{
    cairo_script_context_t *ctx = to_context (surface);
    assert (target_is_active (surface));
    if (memcmp (&surface->cr.current_font_matrix,
		font_matrix,
		sizeof (cairo_matrix_t)) == 0)
    {
	return CAIRO_STATUS_SUCCESS;
    }
    surface->cr.current_font_matrix = *font_matrix;
    if (_cairo_matrix_is_identity (font_matrix)) {
	_cairo_output_stream_puts (ctx->stream,
				   "identity set-font-matrix\n");
    } else {
	_cairo_output_stream_printf (ctx->stream,
				     "[%f %f %f %f %f %f] set-font-matrix\n",
				     font_matrix->xx, font_matrix->yx,
				     font_matrix->xy, font_matrix->yy,
				     font_matrix->x0, font_matrix->y0);
    }
    return CAIRO_STATUS_SUCCESS;
}
static cairo_surface_t *
_cairo_script_surface_create_similar (void	       *abstract_surface,
				      cairo_content_t	content,
				      int		width,
				      int		height)
{
    cairo_script_surface_t *surface, *other = abstract_surface;
    cairo_surface_t *passthrough = NULL;
    cairo_script_context_t *ctx;
    cairo_rectangle_t extents;
    cairo_status_t status;
    ctx = to_context (other);
    status = cairo_device_acquire (&ctx->base);
    if (unlikely (status))
	return _cairo_surface_create_in_error (status);
    if (! other->emitted) {
	status = _emit_surface (other);
	if (unlikely (status)) {
	    cairo_device_release (&ctx->base);
	    return _cairo_surface_create_in_error (status);
	}
	target_push (other);
    }
    if (_cairo_surface_wrapper_is_active (&other->wrapper)) {
	passthrough =
	    _cairo_surface_wrapper_create_similar (&other->wrapper,
						   content, width, height);
	if (unlikely (passthrough->status)) {
	    cairo_device_release (&ctx->base);
	    return passthrough;
	}
    }
    extents.x = extents.y = 0;
    extents.width = width;
    extents.height = height;
    surface = _cairo_script_surface_create_internal (ctx, content,
						     &extents, passthrough);
    cairo_surface_destroy (passthrough);
    if (unlikely (surface->base.status)) {
	cairo_device_release (&ctx->base);
	return &surface->base;
    }
    _get_target (other);
    _cairo_output_stream_printf (ctx->stream,
				 "%u %u //%s similar dup /s%u exch def context\n",
				 width, height,
				 _content_to_string (content),
				 surface->base.unique_id);
    surface->emitted = TRUE;
    surface->defined = TRUE;
    surface->base.is_clear = TRUE;
    target_push (surface);
    cairo_device_release (&ctx->base);
    return &surface->base;
}
static cairo_status_t
1
_device_flush (void *abstract_device)
{
1
    cairo_script_context_t *ctx = abstract_device;
1
    return _cairo_output_stream_flush (ctx->stream);
}
static void
1
_device_finish (void *abstract_device)
{
1
    cairo_script_context_t *ctx = abstract_device;
1
    cairo_status_t status = _cairo_output_stream_close (ctx->stream);
1
    status = _cairo_device_set_error (&ctx->base, status);
    (void) status;
1
}
static void
1
_device_destroy (void *abstract_device)
{
1
    cairo_script_context_t *ctx = abstract_device;
    cairo_status_t status;
1
    while (! cairo_list_is_empty (&ctx->fonts)) {
	cairo_script_font_t *font;
	font = cairo_list_first_entry (&ctx->fonts, cairo_script_font_t, link);
	cairo_list_del (&font->base.link);
	cairo_list_del (&font->link);
	free (font);
    }
1
    _bitmap_fini (ctx->surface_id.next);
1
    _bitmap_fini (ctx->font_id.next);
1
    if (ctx->owns_stream)
1
	status = _cairo_output_stream_destroy (ctx->stream);
1
    free (ctx);
1
}
static cairo_surface_t *
_cairo_script_surface_source (void                    *abstract_surface,
			      cairo_rectangle_int_t	*extents)
{
    cairo_script_surface_t *surface = abstract_surface;
    if (extents) {
	extents->x = extents->y = 0;
	extents->width  = surface->width;
	extents->height = surface->height;
    }
    return &surface->base;
}
static cairo_status_t
_cairo_script_surface_acquire_source_image (void                    *abstract_surface,
					    cairo_image_surface_t  **image_out,
					    void                   **image_extra)
{
    cairo_script_surface_t *surface = abstract_surface;
    if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
	return _cairo_surface_wrapper_acquire_source_image (&surface->wrapper,
							    image_out,
							    image_extra);
    }
    return CAIRO_INT_STATUS_UNSUPPORTED;
}
static void
_cairo_script_surface_release_source_image (void                   *abstract_surface,
					   cairo_image_surface_t  *image,
					   void                   *image_extra)
{
    cairo_script_surface_t *surface = abstract_surface;
    assert (_cairo_surface_wrapper_is_active (&surface->wrapper));
    _cairo_surface_wrapper_release_source_image (&surface->wrapper,
						 image,
						 image_extra);
}
static cairo_status_t
2
_cairo_script_surface_finish (void *abstract_surface)
{
2
    cairo_script_surface_t *surface = abstract_surface;
2
    cairo_script_context_t *ctx = to_context (surface);
2
    cairo_status_t status = CAIRO_STATUS_SUCCESS, status2;
2
    _cairo_surface_wrapper_fini (&surface->wrapper);
2
    free (surface->cr.current_style.dash);
2
    surface->cr.current_style.dash = NULL;
2
    _cairo_pattern_fini (&surface->cr.current_source.base);
2
    _cairo_path_fixed_fini (&surface->cr.current_path);
2
    _cairo_font_options_fini (&surface->cr.current_font_options);
2
    _cairo_surface_clipper_reset (&surface->clipper);
2
    status = cairo_device_acquire (&ctx->base);
2
    if (unlikely (status))
	return status;
2
    if (surface->emitted) {
2
	assert (! surface->active);
2
	if (! cairo_list_is_empty (&surface->operand.link)) {
1
	    if (! ctx->active) {
1
		if (target_is_active (surface)) {
1
		    _cairo_output_stream_printf (ctx->stream,
						 "pop\n");
		} else {
		    int depth = target_depth (surface);
		    if (depth == 1) {
			_cairo_output_stream_printf (ctx->stream,
						     "exch pop\n");
		    } else {
			_cairo_output_stream_printf (ctx->stream,
						     "%d -1 roll pop\n",
						     depth);
		    }
		}
1
		cairo_list_del (&surface->operand.link);
	    } else {
		struct deferred_finish *link = _cairo_calloc (sizeof (*link));
		if (link == NULL) {
		    status2 = _cairo_error (CAIRO_STATUS_NO_MEMORY);
		    if (status == CAIRO_STATUS_SUCCESS)
			status = status2;
		    cairo_list_del (&surface->operand.link);
		} else {
		    link->operand.type = DEFERRED;
		    cairo_list_move_list (&surface->operand.link, &link->operand.link);
		    cairo_list_add (&link->link, &ctx->deferred);
		}
	    }
	}
2
	if (surface->defined) {
	    _cairo_output_stream_printf (ctx->stream,
					 "/s%u undef\n",
					 surface->base.unique_id);
	}
    }
2
    if (status == CAIRO_STATUS_SUCCESS)
2
	status = _cairo_output_stream_flush (to_context (surface)->stream);
2
    cairo_device_release (&ctx->base);
2
    return status;
}
static cairo_int_status_t
_cairo_script_surface_copy_page (void *abstract_surface)
{
    cairo_script_surface_t *surface = abstract_surface;
    cairo_status_t status;
    status = cairo_device_acquire (surface->base.device);
    if (unlikely (status))
	return status;
    status = _emit_context (surface);
    if (unlikely (status))
	goto BAIL;
    _cairo_output_stream_puts (to_context (surface)->stream, "copy-page\n");
BAIL:
    cairo_device_release (surface->base.device);
    return status;
}
static cairo_int_status_t
_cairo_script_surface_show_page (void *abstract_surface)
{
    cairo_script_surface_t *surface = abstract_surface;
    cairo_status_t status;
    status = cairo_device_acquire (surface->base.device);
    if (unlikely (status))
	return status;
    status = _emit_context (surface);
    if (unlikely (status))
	goto BAIL;
    _cairo_output_stream_puts (to_context (surface)->stream, "show-page\n");
BAIL:
    cairo_device_release (surface->base.device);
    return status;
}
static cairo_status_t
_cairo_script_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
						   cairo_path_fixed_t	*path,
						   cairo_fill_rule_t	 fill_rule,
						   double		 tolerance,
						   cairo_antialias_t	 antialias)
{
    cairo_script_surface_t *surface = cairo_container_of (clipper,
							  cairo_script_surface_t,
							  clipper);
    cairo_script_context_t *ctx = to_context (surface);
    cairo_bool_t matrix_updated = FALSE;
    cairo_status_t status;
    cairo_box_t box;
    status = _emit_context (surface);
    if (unlikely (status))
	return status;
    if (path == NULL) {
	if (surface->cr.has_clip) {
	    _cairo_output_stream_puts (ctx->stream, "reset-clip\n");
	    surface->cr.has_clip = FALSE;
	}
	return CAIRO_STATUS_SUCCESS;
    }
    /* skip the trivial clip covering the surface extents */
    if (surface->width >= 0 && surface->height >= 0 &&
	_cairo_path_fixed_is_box (path, &box))
    {
	if (box.p1.x <= 0 && box.p1.y <= 0 &&
	    box.p2.x >= _cairo_fixed_from_double (surface->width) &&
	    box.p2.y >= _cairo_fixed_from_double (surface->height))
	{
	    return CAIRO_STATUS_SUCCESS;
	}
    }
    status = _emit_identity (surface, &matrix_updated);
    if (unlikely (status))
	return status;
    status = _emit_fill_rule (surface, fill_rule);
    if (unlikely (status))
	return status;
    if (path->has_curve_to) {
	status = _emit_tolerance (surface, tolerance, matrix_updated);
	if (unlikely (status))
	    return status;
    }
    if (! _cairo_path_fixed_fill_maybe_region (path)) {
	status = _emit_antialias (surface, antialias);
	if (unlikely (status))
	    return status;
    }
    status = _emit_path (surface, path, TRUE);
    if (unlikely (status))
	return status;
    _cairo_output_stream_puts (ctx->stream, "clip+\n");
    surface->cr.has_clip = TRUE;
    return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
2
active (cairo_script_surface_t *surface)
{
    cairo_status_t status;
2
    status = cairo_device_acquire (surface->base.device);
2
    if (unlikely (status))
	return status;
2
    if (surface->active++ == 0)
2
	to_context (surface)->active++;
2
    return CAIRO_STATUS_SUCCESS;
}
static void
2
inactive (cairo_script_surface_t *surface)
{
2
    cairo_script_context_t *ctx = to_context (surface);
    cairo_list_t sorted;
2
    assert (surface->active > 0);
2
    if (--surface->active)
	goto DONE;
2
    assert (ctx->active > 0);
2
    if (--ctx->active)
1
	goto DONE;
1
    cairo_list_init (&sorted);
1
    while (! cairo_list_is_empty (&ctx->deferred)) {
	struct deferred_finish *df;
	cairo_list_t *operand;
	int depth;
	df = cairo_list_first_entry (&ctx->deferred,
				     struct deferred_finish,
				     link);
	depth = 0;
	cairo_list_foreach (operand, &ctx->operands) {
	    if (operand == &df->operand.link)
		break;
	    depth++;
	}
	df->operand.type = depth;
	if (cairo_list_is_empty (&sorted)) {
	    cairo_list_move (&df->link, &sorted);
	} else {
	    struct deferred_finish *pos;
	    cairo_list_foreach_entry (pos, struct deferred_finish,
				      &sorted,
				      link)
	    {
		if (df->operand.type < pos->operand.type)
		    break;
	    }
	    cairo_list_move_tail (&df->link, &pos->link);
	}
    }
1
    while (! cairo_list_is_empty (&sorted)) {
	struct deferred_finish *df;
	cairo_list_t *operand;
	int depth;
	df = cairo_list_first_entry (&sorted,
				     struct deferred_finish,
				     link);
	depth = 0;
	cairo_list_foreach (operand, &ctx->operands) {
	    if (operand == &df->operand.link)
		break;
	    depth++;
	}
	if (depth == 0) {
	    _cairo_output_stream_printf (ctx->stream,
					 "pop\n");
	} else if (depth == 1) {
	    _cairo_output_stream_printf (ctx->stream,
					 "exch pop\n");
	} else {
	    _cairo_output_stream_printf (ctx->stream,
					 "%d -1 roll pop\n",
					 depth);
	}
	cairo_list_del (&df->operand.link);
	cairo_list_del (&df->link);
	free (df);
    }
1
DONE:
2
    cairo_device_release (surface->base.device);
2
}
static cairo_int_status_t
1
_cairo_script_surface_paint (void			*abstract_surface,
			     cairo_operator_t		 op,
			     const cairo_pattern_t	*source,
			     const cairo_clip_t		*clip)
{
1
    cairo_script_surface_t *surface = abstract_surface;
    cairo_status_t status;
1
    status = active (surface);
1
    if (unlikely (status))
	return status;
1
    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
1
    if (unlikely (status))
	goto BAIL;
1
    status = _emit_context (surface);
1
    if (unlikely (status))
	goto BAIL;
1
    status = _emit_source (surface, op, source);
1
    if (unlikely (status))
	goto BAIL;
1
    status = _emit_operator (surface, op);
1
    if (unlikely (status))
	goto BAIL;
1
    _cairo_output_stream_puts (to_context (surface)->stream,
			       "paint\n");
1
    inactive (surface);
1
    if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
	return _cairo_surface_wrapper_paint (&surface->wrapper,
					     op, source, 0, clip);
    }
1
    return CAIRO_STATUS_SUCCESS;
BAIL:
    inactive (surface);
    return status;
}
static cairo_int_status_t
_cairo_script_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_script_surface_t *surface = abstract_surface;
    cairo_status_t status;
    status = active (surface);
    if (unlikely (status))
	return status;
    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
    if (unlikely (status))
	goto BAIL;
    status = _emit_context (surface);
    if (unlikely (status))
	goto BAIL;
    status = _emit_source (surface, op, source);
    if (unlikely (status))
	goto BAIL;
    status = _emit_operator (surface, op);
    if (unlikely (status))
	goto BAIL;
    if (_cairo_pattern_equal (source, mask)) {
	_cairo_output_stream_puts (to_context (surface)->stream, "/source get");
    } else {
	status = _emit_pattern (surface, mask);
	if (unlikely (status))
	    goto BAIL;
    }
    assert (surface->cr.current_operator == op);
    _cairo_output_stream_puts (to_context (surface)->stream,
			       " mask\n");
    inactive (surface);
    if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
	return _cairo_surface_wrapper_mask (&surface->wrapper,
					    op, source, 0, mask, 0, clip);
    }
    return CAIRO_STATUS_SUCCESS;
BAIL:
    inactive (surface);
    return status;
}
static cairo_int_status_t
1
_cairo_script_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)
{
1
    cairo_script_surface_t *surface = abstract_surface;
1
    cairo_bool_t matrix_updated = FALSE;
    cairo_status_t status;
1
    status = active (surface);
1
    if (unlikely (status))
	return status;
1
    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
1
    if (unlikely (status))
	goto BAIL;
1
    status = _emit_context (surface);
1
    if (unlikely (status))
	goto BAIL;
1
    status = _emit_identity (surface, &matrix_updated);
1
    if (unlikely (status))
	goto BAIL;
1
    status = _emit_path (surface, path, FALSE);
1
    if (unlikely (status))
	goto BAIL;
1
    status = _emit_source (surface, op, source);
1
    if (unlikely (status))
	goto BAIL;
1
    status = _emit_scaling_matrix (surface, ctm, &matrix_updated);
1
    if (unlikely (status))
	goto BAIL;
1
    status = _emit_operator (surface, op);
1
    if (unlikely (status))
	goto BAIL;
1
    if (_scaling_matrix_equal (&surface->cr.current_ctm,
1
			       &surface->cr.current_stroke_matrix))
    {
1
	matrix_updated = FALSE;
    }
    else
    {
	matrix_updated = TRUE;
	surface->cr.current_stroke_matrix = surface->cr.current_ctm;
    }
1
    status = _emit_stroke_style (surface, style, matrix_updated);
1
    if (unlikely (status))
	goto BAIL;
1
    status = _emit_tolerance (surface, tolerance, matrix_updated);
1
    if (unlikely (status))
	goto BAIL;
1
    status = _emit_antialias (surface, antialias);
1
    if (unlikely (status))
	goto BAIL;
1
    _cairo_output_stream_puts (to_context (surface)->stream, "stroke+\n");
1
    inactive (surface);
1
    if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
	return _cairo_surface_wrapper_stroke (&surface->wrapper,
					      op, source, 0, path,
					      style,
					      ctm, ctm_inverse,
					      tolerance, antialias,
					      clip);
    }
1
    return CAIRO_STATUS_SUCCESS;
BAIL:
    inactive (surface);
    return status;
}
static cairo_int_status_t
_cairo_script_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)
{
    cairo_script_surface_t *surface = abstract_surface;
    cairo_bool_t matrix_updated = FALSE;
    cairo_status_t status;
    cairo_box_t box;
    status = active (surface);
    if (unlikely (status))
	return status;
    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
    if (unlikely (status))
	goto BAIL;
    status = _emit_context (surface);
    if (unlikely (status))
	goto BAIL;
    status = _emit_identity (surface, &matrix_updated);
    if (unlikely (status))
	goto BAIL;
    status = _emit_source (surface, op, source);
    if (unlikely (status))
	goto BAIL;
    if (! _cairo_path_fixed_is_box (path, &box)) {
	status = _emit_fill_rule (surface, fill_rule);
	if (unlikely (status))
	    goto BAIL;
    }
    if (path->has_curve_to) {
	status = _emit_tolerance (surface, tolerance, matrix_updated);
	if (unlikely (status))
	    goto BAIL;
    }
    if (! _cairo_path_fixed_fill_maybe_region (path)) {
	status = _emit_antialias (surface, antialias);
	if (unlikely (status))
	    goto BAIL;
    }
    status = _emit_path (surface, path, TRUE);
    if (unlikely (status))
	goto BAIL;
    status = _emit_operator (surface, op);
    if (unlikely (status))
	goto BAIL;
    _cairo_output_stream_puts (to_context (surface)->stream, "fill+\n");
    inactive (surface);
    if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
	return _cairo_surface_wrapper_fill (&surface->wrapper,
					    op, source, 0, path,
					    fill_rule,
					    tolerance,
					    antialias,
					    clip);
    }
    return CAIRO_STATUS_SUCCESS;
BAIL:
    inactive (surface);
    return status;
}
static cairo_surface_t *
_cairo_script_surface_snapshot (void *abstract_surface)
{
    cairo_script_surface_t *surface = abstract_surface;
    if (_cairo_surface_wrapper_is_active (&surface->wrapper))
	return _cairo_surface_wrapper_snapshot (&surface->wrapper);
    return NULL;
}
static cairo_bool_t
_cairo_script_surface_has_show_text_glyphs (void *abstract_surface)
{
    return TRUE;
}
static const char *
_subpixel_order_to_string (cairo_subpixel_order_t subpixel_order)
{
    static const char *names[] = {
	"SUBPIXEL_ORDER_DEFAULT",	/* CAIRO_SUBPIXEL_ORDER_DEFAULT */
	"SUBPIXEL_ORDER_RGB",		/* CAIRO_SUBPIXEL_ORDER_RGB */
	"SUBPIXEL_ORDER_BGR",		/* CAIRO_SUBPIXEL_ORDER_BGR */
	"SUBPIXEL_ORDER_VRGB",		/* CAIRO_SUBPIXEL_ORDER_VRGB */
	"SUBPIXEL_ORDER_VBGR"		/* CAIRO_SUBPIXEL_ORDER_VBGR */
    };
    return names[subpixel_order];
}
static const char *
_hint_style_to_string (cairo_hint_style_t hint_style)
{
    static const char *names[] = {
	"HINT_STYLE_DEFAULT",	/* CAIRO_HINT_STYLE_DEFAULT */
	"HINT_STYLE_NONE",	/* CAIRO_HINT_STYLE_NONE */
	"HINT_STYLE_SLIGHT",	/* CAIRO_HINT_STYLE_SLIGHT */
	"HINT_STYLE_MEDIUM",	/* CAIRO_HINT_STYLE_MEDIUM */
	"HINT_STYLE_FULL"	/* CAIRO_HINT_STYLE_FULL */
    };
    return names[hint_style];
}
static const char *
_hint_metrics_to_string (cairo_hint_metrics_t hint_metrics)
{
    static const char *names[] = {
	 "HINT_METRICS_DEFAULT",	/* CAIRO_HINT_METRICS_DEFAULT */
	 "HINT_METRICS_OFF",		/* CAIRO_HINT_METRICS_OFF */
	 "HINT_METRICS_ON"		/* CAIRO_HINT_METRICS_ON */
    };
    return names[hint_metrics];
}
static cairo_status_t
_emit_font_options (cairo_script_surface_t *surface,
		    cairo_font_options_t *font_options)
{
    cairo_script_context_t *ctx = to_context (surface);
    if (cairo_font_options_equal (&surface->cr.current_font_options,
				  font_options))
    {
	return CAIRO_STATUS_SUCCESS;
    }
    _cairo_output_stream_printf (ctx->stream, "<<");
    if (font_options->antialias != surface->cr.current_font_options.antialias) {
	_cairo_output_stream_printf (ctx->stream,
				     " /antialias //%s",
				     _antialias_to_string (font_options->antialias));
    }
    if (font_options->subpixel_order !=
	surface->cr.current_font_options.subpixel_order)
    {
	_cairo_output_stream_printf (ctx->stream,
				     " /subpixel-order //%s",
				     _subpixel_order_to_string (font_options->subpixel_order));
    }
    if (font_options->hint_style !=
	surface->cr.current_font_options.hint_style)
    {
	_cairo_output_stream_printf (ctx->stream,
				     " /hint-style //%s",
				     _hint_style_to_string (font_options->hint_style));
    }
    if (font_options->hint_metrics !=
	surface->cr.current_font_options.hint_metrics)
    {
	_cairo_output_stream_printf (ctx->stream,
				     " /hint-metrics //%s",
				     _hint_metrics_to_string (font_options->hint_metrics));
    }
    _cairo_output_stream_printf (ctx->stream,
				 " >> set-font-options\n");
    surface->cr.current_font_options = *font_options;
    return CAIRO_STATUS_SUCCESS;
}
static void
_cairo_script_scaled_font_fini (cairo_scaled_font_private_t *abstract_private,
				cairo_scaled_font_t *scaled_font)
{
    cairo_script_font_t *priv = (cairo_script_font_t *)abstract_private;
    cairo_script_context_t *ctx = (cairo_script_context_t *)abstract_private->key;
    cairo_status_t status;
    status = cairo_device_acquire (&ctx->base);
    if (likely (status == CAIRO_STATUS_SUCCESS)) {
	_cairo_output_stream_printf (ctx->stream,
				     "/f%lu undef /sf%lu undef\n",
				     priv->id,
				     priv->id);
	_bitmap_release_id (&ctx->font_id, priv->id);
	cairo_device_release (&ctx->base);
    }
    cairo_list_del (&priv->link);
    cairo_list_del (&priv->base.link);
    free (priv);
}
static cairo_script_font_t *
_cairo_script_font_get (cairo_script_context_t *ctx, cairo_scaled_font_t *font)
{
    return (cairo_script_font_t *) _cairo_scaled_font_find_private (font, ctx);
}
static long unsigned
_cairo_script_font_id (cairo_script_context_t *ctx, cairo_scaled_font_t *font)
{
    return _cairo_script_font_get (ctx, font)->id;
}
static cairo_status_t
_emit_type42_font (cairo_script_surface_t *surface,
		   cairo_scaled_font_t *scaled_font)
{
    cairo_script_context_t *ctx = to_context (surface);
    const cairo_scaled_font_backend_t *backend;
    cairo_output_stream_t *base85_stream;
    cairo_output_stream_t *zlib_stream;
    cairo_status_t status, status2;
    unsigned long size;
    unsigned int load_flags;
    uint32_t len;
    uint8_t *buf;
    backend = scaled_font->backend;
    if (backend->load_truetype_table == NULL)
	return CAIRO_INT_STATUS_UNSUPPORTED;
    size = 0;
    status = backend->load_truetype_table (scaled_font, 0, 0, NULL, &size);
    if (unlikely (status))
	return status;
    buf = _cairo_malloc (size);
    if (unlikely (buf == NULL))
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    status = backend->load_truetype_table (scaled_font, 0, 0, buf, &size);
    if (unlikely (status)) {
	free (buf);
	return status;
    }
#if CAIRO_HAS_FT_FONT
    load_flags = _cairo_ft_scaled_font_get_load_flags (scaled_font);
#else
    load_flags = 0;
#endif
    _cairo_output_stream_printf (ctx->stream,
				 "<< "
				 "/type 42 "
				 "/index 0 "
				 "/flags %d "
				 "/source <|",
				 load_flags);
    base85_stream = _cairo_base85_stream_create (ctx->stream);
    len = to_be32 (size);
    _cairo_output_stream_write (base85_stream, &len, sizeof (len));
    zlib_stream = _cairo_deflate_stream_create (base85_stream);
    _cairo_output_stream_write (zlib_stream, buf, size);
    free (buf);
    status2 = _cairo_output_stream_destroy (zlib_stream);
    if (status == CAIRO_STATUS_SUCCESS)
	status = status2;
    status2 = _cairo_output_stream_destroy (base85_stream);
    if (status == CAIRO_STATUS_SUCCESS)
	status = status2;
    _cairo_output_stream_printf (ctx->stream,
				 "~> >> font dup /f%lu exch def set-font-face",
				 _cairo_script_font_id (ctx, scaled_font));
    return status;
}
static cairo_status_t
_emit_scaled_font_init (cairo_script_surface_t *surface,
			cairo_scaled_font_t *scaled_font,
			cairo_script_font_t **font_out)
{
    cairo_script_context_t *ctx = to_context (surface);
    cairo_script_font_t *font_private;
    cairo_int_status_t status;
    font_private = _cairo_calloc (sizeof (cairo_script_font_t));
    if (unlikely (font_private == NULL))
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    _cairo_scaled_font_attach_private (scaled_font, &font_private->base, ctx,
				       _cairo_script_scaled_font_fini);
    font_private->parent = scaled_font;
    font_private->subset_glyph_index = 0;
    font_private->has_sfnt = TRUE;
    cairo_list_add (&font_private->link, &ctx->fonts);
    status = _bitmap_next_id (&ctx->font_id,
			      &font_private->id);
    if (unlikely (status)) {
	free (font_private);
	return status;
    }
    status = _emit_context (surface);
    if (unlikely (status)) {
	free (font_private);
	return status;
    }
    status = _emit_type42_font (surface, scaled_font);
    if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
	*font_out = font_private;
	return status;
    }
    font_private->has_sfnt = FALSE;
    _cairo_output_stream_printf (ctx->stream,
				 "dict\n"
				 "  /type 3 set\n"
				 "  /metrics [%f %f %f %f %f] set\n"
				 "  /glyphs array set\n"
				 "  font dup /f%lu exch def set-font-face",
				 scaled_font->fs_extents.ascent,
				 scaled_font->fs_extents.descent,
				 scaled_font->fs_extents.height,
				 scaled_font->fs_extents.max_x_advance,
				 scaled_font->fs_extents.max_y_advance,
				 font_private->id);
    *font_out = font_private;
    return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_scaled_font (cairo_script_surface_t *surface,
		   cairo_scaled_font_t *scaled_font)
{
    cairo_script_context_t *ctx = to_context (surface);
    cairo_matrix_t matrix;
    cairo_font_options_t options;
    cairo_bool_t matrix_updated = FALSE;
    cairo_status_t status;
    cairo_script_font_t *font_private;
    cairo_scaled_font_get_ctm (scaled_font, &matrix);
    status = _emit_scaling_matrix (surface, &matrix, &matrix_updated);
    if (unlikely (status))
	return status;
    if (! matrix_updated && surface->cr.current_scaled_font == scaled_font)
	return CAIRO_STATUS_SUCCESS;
    surface->cr.current_scaled_font = scaled_font;
    font_private = _cairo_script_font_get (ctx, scaled_font);
    if (font_private == NULL) {
	cairo_scaled_font_get_font_matrix (scaled_font, &matrix);
	status = _emit_font_matrix (surface, &matrix);
	if (unlikely (status))
	    return status;
	_cairo_font_options_init_default (&options);
	cairo_scaled_font_get_font_options (scaled_font, &options);
	status = _emit_font_options (surface, &options);
	if (unlikely (status))
	    return status;
	status = _emit_scaled_font_init (surface, scaled_font, &font_private);
	if (unlikely (status))
	    return status;
	assert (target_is_active (surface));
	_cairo_output_stream_printf (ctx->stream,
				     " /scaled-font get /sf%lu exch def\n",
				     font_private->id);
    } else {
	_cairo_output_stream_printf (ctx->stream,
				     "sf%lu set-scaled-font\n",
				     font_private->id);
    }
    return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_scaled_glyph_vector (cairo_script_surface_t *surface,
			   cairo_scaled_font_t *scaled_font,
			   cairo_script_font_t *font_private,
			   cairo_scaled_glyph_t *scaled_glyph)
{
    cairo_script_context_t *ctx = to_context (surface);
    cairo_script_implicit_context_t old_cr;
    cairo_status_t status;
    unsigned long index;
    index = ++font_private->subset_glyph_index;
    scaled_glyph->dev_private_key = ctx;
    scaled_glyph->dev_private = (void *)(uintptr_t)index;
    _cairo_output_stream_printf (ctx->stream,
				 "%lu <<\n"
				 "  /metrics [%f %f %f %f %f %f]\n"
				 "  /render {\n",
				 index,
				 scaled_glyph->fs_metrics.x_bearing,
				 scaled_glyph->fs_metrics.y_bearing,
				 scaled_glyph->fs_metrics.width,
				 scaled_glyph->fs_metrics.height,
				 scaled_glyph->fs_metrics.x_advance,
				 scaled_glyph->fs_metrics.y_advance);
    if (! _cairo_matrix_is_identity (&scaled_font->scale_inverse)) {
	_cairo_output_stream_printf (ctx->stream,
				     "[%f %f %f %f %f %f] transform\n",
				     scaled_font->scale_inverse.xx,
				     scaled_font->scale_inverse.yx,
				     scaled_font->scale_inverse.xy,
				     scaled_font->scale_inverse.yy,
				     scaled_font->scale_inverse.x0,
				     scaled_font->scale_inverse.y0);
    }
    old_cr = surface->cr;
    _cairo_script_implicit_context_init (&surface->cr);
    status = _cairo_recording_surface_replay (scaled_glyph->recording_surface,
					      &surface->base);
    surface->cr = old_cr;
    _cairo_output_stream_puts (ctx->stream, "} >> set\n");
    return status;
}
static cairo_status_t
_emit_scaled_glyph_bitmap (cairo_script_surface_t *surface,
			   cairo_scaled_font_t *scaled_font,
			   cairo_script_font_t *font_private,
			   cairo_scaled_glyph_t *scaled_glyph)
{
    cairo_script_context_t *ctx = to_context (surface);
    cairo_status_t status;
    unsigned long index;
    index = ++font_private->subset_glyph_index;
    scaled_glyph->dev_private_key = ctx;
    scaled_glyph->dev_private = (void *)(uintptr_t)index;
    _cairo_output_stream_printf (ctx->stream,
				 "%lu <<\n"
				 "  /metrics [%f %f %f %f %f %f]\n"
				 "  /render {\n"
				 "%f %f translate\n",
				 index,
				 scaled_glyph->fs_metrics.x_bearing,
				 scaled_glyph->fs_metrics.y_bearing,
				 scaled_glyph->fs_metrics.width,
				 scaled_glyph->fs_metrics.height,
				 scaled_glyph->fs_metrics.x_advance,
				 scaled_glyph->fs_metrics.y_advance,
				 scaled_glyph->fs_metrics.x_bearing,
				 scaled_glyph->fs_metrics.y_bearing);
    status = _emit_image_surface (surface, scaled_glyph->surface);
    if (unlikely (status))
	return status;
    _cairo_output_stream_puts (ctx->stream, "pattern ");
    if (! _cairo_matrix_is_identity (&scaled_font->font_matrix)) {
	_cairo_output_stream_printf (ctx->stream,
				     "\n  [%f %f %f %f %f %f] set-matrix\n",
				     scaled_font->font_matrix.xx,
				     scaled_font->font_matrix.yx,
				     scaled_font->font_matrix.xy,
				     scaled_font->font_matrix.yy,
				     scaled_font->font_matrix.x0,
				     scaled_font->font_matrix.y0);
    }
    _cairo_output_stream_puts (ctx->stream,
				 "mask\n} >> set\n");
    return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_scaled_glyph_prologue (cairo_script_surface_t *surface,
			     cairo_scaled_font_t *scaled_font)
{
    cairo_script_context_t *ctx = to_context (surface);
    _cairo_output_stream_printf (ctx->stream, "f%lu /glyphs get\n",
				 _cairo_script_font_id (ctx, scaled_font));
    return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_scaled_glyphs (cairo_script_surface_t *surface,
		     cairo_scaled_font_t *scaled_font,
		     cairo_glyph_t *glyphs,
		     unsigned int num_glyphs)
{
    cairo_script_context_t *ctx = to_context (surface);
    cairo_script_font_t *font_private;
    cairo_status_t status;
    unsigned int n;
    cairo_bool_t have_glyph_prologue = FALSE;
    if (num_glyphs == 0)
	return CAIRO_STATUS_SUCCESS;
    font_private = _cairo_script_font_get (ctx, scaled_font);
    if (font_private->has_sfnt)
	return CAIRO_STATUS_SUCCESS;
    _cairo_scaled_font_freeze_cache (scaled_font);
    for (n = 0; n < num_glyphs; n++) {
	cairo_scaled_glyph_t *scaled_glyph;
	status = _cairo_scaled_glyph_lookup (scaled_font,
					     glyphs[n].index,
					     CAIRO_SCALED_GLYPH_INFO_METRICS,
                                             NULL, /* foreground color */
					     &scaled_glyph);
	if (unlikely (status))
	    break;
	if (scaled_glyph->dev_private_key == ctx)
	    continue;
	status = _cairo_scaled_glyph_lookup (scaled_font,
					     glyphs[n].index,
					     CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE,
                                             NULL, /* foreground color */
					     &scaled_glyph);
	if (_cairo_status_is_error (status))
	    break;
	if (status == CAIRO_STATUS_SUCCESS) {
	    if (! have_glyph_prologue) {
		status = _emit_scaled_glyph_prologue (surface, scaled_font);
		if (unlikely (status))
		    break;
		have_glyph_prologue = TRUE;
	    }
	    status = _emit_scaled_glyph_vector (surface,
						scaled_font, font_private,
						scaled_glyph);
	    if (unlikely (status))
		break;
	    continue;
	}
	status = _cairo_scaled_glyph_lookup (scaled_font,
					     glyphs[n].index,
					     CAIRO_SCALED_GLYPH_INFO_SURFACE,
                                             NULL, /* foreground color */
					     &scaled_glyph);
	if (_cairo_status_is_error (status))
	    break;
	if (status == CAIRO_STATUS_SUCCESS) {
	    if (! have_glyph_prologue) {
		status = _emit_scaled_glyph_prologue (surface, scaled_font);
		if (unlikely (status))
		    break;
		have_glyph_prologue = TRUE;
	    }
	    status = _emit_scaled_glyph_bitmap (surface,
						scaled_font,
						font_private,
						scaled_glyph);
	    if (unlikely (status))
		break;
	    continue;
	}
    }
    _cairo_scaled_font_thaw_cache (scaled_font);
    if (have_glyph_prologue) {
	_cairo_output_stream_puts (to_context (surface)->stream, "pop pop\n");
    }
    return status;
}
static void
to_octal (int value, char *buf, size_t size)
{
    do {
	buf[--size] = '0' + (value & 7);
	value >>= 3;
    } while (size);
}
static void
_emit_string_literal (cairo_script_surface_t *surface,
		      const char *utf8, int len)
{
    cairo_script_context_t *ctx = to_context (surface);
    char c;
    const char *end;
    _cairo_output_stream_puts (ctx->stream, "(");
    if (utf8 == NULL) {
	end = utf8;
    } else {
	if (len < 0)
	    len = strlen (utf8);
	end = utf8 + len;
    }
    while (utf8 < end) {
	switch ((c = *utf8++)) {
	case '\n':
	    c = 'n';
	    goto ESCAPED_CHAR;
	case '\r':
	    c = 'r';
	    goto ESCAPED_CHAR;
	case '\t':
	    c = 't';
	    goto ESCAPED_CHAR;
	case '\b':
	    c = 'b';
	    goto ESCAPED_CHAR;
	case '\f':
	    c = 'f';
	    goto ESCAPED_CHAR;
	case '\\':
	case '(':
	case ')':
ESCAPED_CHAR:
	    _cairo_output_stream_printf (ctx->stream, "\\%c", c);
	    break;
	default:
	    if (_cairo_isprint(c)) {
		_cairo_output_stream_printf (ctx->stream, "%c", c);
	    } else {
		char buf[4] = { '\\' };
		to_octal (c, buf+1, 3);
		_cairo_output_stream_write (ctx->stream, buf, 4);
	    }
	    break;
	}
    }
    _cairo_output_stream_puts (ctx->stream, ")");
}
static cairo_int_status_t
_cairo_script_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   backward,
					cairo_scaled_font_t	    *scaled_font,
					const cairo_clip_t	    *clip)
{
    cairo_script_surface_t *surface = abstract_surface;
    cairo_script_context_t *ctx = to_context (surface);
    cairo_script_font_t *font_private;
    cairo_scaled_glyph_t *scaled_glyph;
    cairo_matrix_t matrix;
    cairo_status_t status;
    double x, y, ix, iy;
    int n;
    cairo_output_stream_t *base85_stream = NULL;
    status = active (surface);
    if (unlikely (status))
	return status;
    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
    if (unlikely (status))
	goto BAIL;
    status = _emit_context (surface);
    if (unlikely (status))
	goto BAIL;
    status = _emit_source (surface, op, source);
    if (unlikely (status))
	goto BAIL;
    status = _emit_scaled_font (surface, scaled_font);
    if (unlikely (status))
	goto BAIL;
    status = _emit_operator (surface, op);
    if (unlikely (status))
	goto BAIL;
    status = _emit_scaled_glyphs (surface, scaled_font, glyphs, num_glyphs);
    if (unlikely (status))
	goto BAIL;
    /* (utf8) [cx cy [glyphs]] [clusters] backward show_text_glyphs */
    /* [cx cy [glyphs]] show_glyphs */
    if (utf8 != NULL && clusters != NULL) {
	_emit_string_literal (surface, utf8, utf8_len);
	_cairo_output_stream_puts (ctx->stream, " ");
    }
    matrix = surface->cr.current_ctm;
    status = cairo_matrix_invert (&matrix);
    assert (status == CAIRO_STATUS_SUCCESS);
    ix = x = glyphs[0].x;
    iy = y = glyphs[0].y;
    cairo_matrix_transform_point (&matrix, &ix, &iy);
    ix -= scaled_font->font_matrix.x0;
    iy -= scaled_font->font_matrix.y0;
    _cairo_scaled_font_freeze_cache (scaled_font);
    font_private = _cairo_script_font_get (ctx, scaled_font);
    _cairo_output_stream_printf (ctx->stream,
				 "[%f %f ",
				 ix, iy);
    for (n = 0; n < num_glyphs; n++) {
	if (font_private->has_sfnt) {
	    if (glyphs[n].index > 256)
		break;
	} else {
	    status = _cairo_scaled_glyph_lookup (scaled_font,
						 glyphs[n].index,
						 CAIRO_SCALED_GLYPH_INFO_METRICS,
						 NULL, /* foreground color */
						 &scaled_glyph);
	    if (unlikely (status)) {
		_cairo_scaled_font_thaw_cache (scaled_font);
		goto BAIL;
	    }
	    if ((uintptr_t)scaled_glyph->dev_private > 256)
		break;
	}
    }
    if (n == num_glyphs) {
	_cairo_output_stream_puts (ctx->stream, "<~");
	base85_stream = _cairo_base85_stream_create (ctx->stream);
    } else
	_cairo_output_stream_puts (ctx->stream, "[");
    for (n = 0; n < num_glyphs; n++) {
	double dx, dy;
	status = _cairo_scaled_glyph_lookup (scaled_font,
					     glyphs[n].index,
					     CAIRO_SCALED_GLYPH_INFO_METRICS,
                                             NULL, /* foreground color */
					     &scaled_glyph);
	if (unlikely (status)) {
	    _cairo_scaled_font_thaw_cache (scaled_font);
	    goto BAIL;
	}
	if (fabs (glyphs[n].x - x) > 1e-5 || fabs (glyphs[n].y - y) > 1e-5) {
	    if (fabs (glyphs[n].y - y) < 1e-5) {
		if (base85_stream != NULL) {
		    status = _cairo_output_stream_destroy (base85_stream);
		    if (unlikely (status)) {
			base85_stream = NULL;
			break;
		    }
		    _cairo_output_stream_printf (ctx->stream,
						 "~> %f <~", glyphs[n].x - x);
		    base85_stream = _cairo_base85_stream_create (ctx->stream);
		} else {
		    _cairo_output_stream_printf (ctx->stream,
						 " ] %f [ ", glyphs[n].x - x);
		}
		x = glyphs[n].x;
	    } else {
		ix = x = glyphs[n].x;
		iy = y = glyphs[n].y;
		cairo_matrix_transform_point (&matrix, &ix, &iy);
		ix -= scaled_font->font_matrix.x0;
		iy -= scaled_font->font_matrix.y0;
		if (base85_stream != NULL) {
		    status = _cairo_output_stream_destroy (base85_stream);
		    if (unlikely (status)) {
			base85_stream = NULL;
			break;
		    }
		    _cairo_output_stream_printf (ctx->stream,
						 "~> %f %f <~",
						 ix, iy);
		    base85_stream = _cairo_base85_stream_create (ctx->stream);
		} else {
		    _cairo_output_stream_printf (ctx->stream,
						 " ] %f %f [ ",
						 ix, iy);
		}
	    }
	}
	if (base85_stream != NULL) {
	    uint8_t c;
	    if (font_private->has_sfnt)
		c = glyphs[n].index;
	    else
		c = (uint8_t) (uintptr_t) scaled_glyph->dev_private;
	    _cairo_output_stream_write (base85_stream, &c, 1);
	} else {
	    if (font_private->has_sfnt)
		_cairo_output_stream_printf (ctx->stream, " %lu",
					     glyphs[n].index);
	    else
		_cairo_output_stream_printf (ctx->stream, " %lu",
					     (long unsigned) (uintptr_t)scaled_glyph->dev_private);
	}
        dx = scaled_glyph->metrics.x_advance;
        dy = scaled_glyph->metrics.y_advance;
	cairo_matrix_transform_distance (&scaled_font->ctm, &dx, &dy);
	x += dx;
	y += dy;
    }
    _cairo_scaled_font_thaw_cache (scaled_font);
    if (base85_stream != NULL) {
	cairo_status_t status2;
	status2 = _cairo_output_stream_destroy (base85_stream);
	if (status == CAIRO_STATUS_SUCCESS)
	    status = status2;
	_cairo_output_stream_printf (ctx->stream, "~>");
    } else {
	_cairo_output_stream_puts (ctx->stream, " ]");
    }
    if (unlikely (status))
	return status;
    if (utf8 != NULL && clusters != NULL) {
	for (n = 0; n < num_clusters; n++) {
	    if (clusters[n].num_bytes > UCHAR_MAX ||
		clusters[n].num_glyphs > UCHAR_MAX)
	    {
		break;
	    }
	}
	if (n < num_clusters) {
	    _cairo_output_stream_puts (ctx->stream, "] [ ");
	    for (n = 0; n < num_clusters; n++) {
		_cairo_output_stream_printf (ctx->stream,
					     "%d %d ",
					     clusters[n].num_bytes,
					     clusters[n].num_glyphs);
	    }
	    _cairo_output_stream_puts (ctx->stream, "]");
	}
	else
	{
	    _cairo_output_stream_puts (ctx->stream, "] <~");
	    base85_stream = _cairo_base85_stream_create (ctx->stream);
	    for (n = 0; n < num_clusters; n++) {
		uint8_t c[2];
		c[0] = clusters[n].num_bytes;
		c[1] = clusters[n].num_glyphs;
		_cairo_output_stream_write (base85_stream, c, 2);
	    }
	    status = _cairo_output_stream_destroy (base85_stream);
	    if (unlikely (status))
		goto BAIL;
	    _cairo_output_stream_puts (ctx->stream, "~>");
	}
	_cairo_output_stream_printf (ctx->stream,
				     " //%s show-text-glyphs\n",
				     _direction_to_string (backward));
    } else {
	_cairo_output_stream_puts (ctx->stream,
				   "] show-glyphs\n");
    }
    inactive (surface);
    if (_cairo_surface_wrapper_is_active (&surface->wrapper)){
	return _cairo_surface_wrapper_show_text_glyphs (&surface->wrapper,
							op, source, 0,
							utf8, utf8_len,
							glyphs, num_glyphs,
							clusters, num_clusters,
							backward,
							scaled_font,
							clip);
    }
    return CAIRO_STATUS_SUCCESS;
BAIL:
    inactive (surface);
    return status;
}
static cairo_bool_t
1
_cairo_script_surface_get_extents (void *abstract_surface,
				   cairo_rectangle_int_t *rectangle)
{
1
    cairo_script_surface_t *surface = abstract_surface;
1
    if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
	return _cairo_surface_wrapper_get_extents (&surface->wrapper,
						   rectangle);
    }
1
    if (surface->width < 0 || surface->height < 0)
1
	return FALSE;
    rectangle->x = 0;
    rectangle->y = 0;
    rectangle->width = surface->width;
    rectangle->height = surface->height;
    return TRUE;
}
static const cairo_surface_backend_t
_cairo_script_surface_backend = {
    CAIRO_SURFACE_TYPE_SCRIPT,
    _cairo_script_surface_finish,
    _cairo_default_context_create,
    _cairo_script_surface_create_similar,
    NULL, /* create similar image */
    NULL, /* map to image */
    NULL, /* unmap image */
    _cairo_script_surface_source,
    _cairo_script_surface_acquire_source_image,
    _cairo_script_surface_release_source_image,
    _cairo_script_surface_snapshot,
    _cairo_script_surface_copy_page,
    _cairo_script_surface_show_page,
    _cairo_script_surface_get_extents,
    NULL, /* get_font_options */
    NULL, /* flush */
    NULL, /* mark_dirty_rectangle */
    _cairo_script_surface_paint,
    _cairo_script_surface_mask,
    _cairo_script_surface_stroke,
    _cairo_script_surface_fill,
    NULL, /* fill/stroke */
    NULL, /* glyphs */
    _cairo_script_surface_has_show_text_glyphs,
    _cairo_script_surface_show_text_glyphs
};
static void
3
_cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr)
{
3
    cr->current_operator = CAIRO_GSTATE_OPERATOR_DEFAULT;
3
    cr->current_fill_rule = CAIRO_GSTATE_FILL_RULE_DEFAULT;
3
    cr->current_tolerance = CAIRO_GSTATE_TOLERANCE_DEFAULT;
3
    cr->current_antialias = CAIRO_ANTIALIAS_DEFAULT;
3
    _cairo_stroke_style_init (&cr->current_style);
3
    _cairo_pattern_init_solid (&cr->current_source.solid,
			       CAIRO_COLOR_BLACK);
3
    _cairo_path_fixed_init (&cr->current_path);
3
    cairo_matrix_init_identity (&cr->current_ctm);
3
    cairo_matrix_init_identity (&cr->current_stroke_matrix);
3
    cairo_matrix_init_identity (&cr->current_font_matrix);
3
    _cairo_font_options_init_default (&cr->current_font_options);
3
    cr->current_scaled_font = NULL;
3
    cr->has_clip = FALSE;
3
}
static void
_cairo_script_implicit_context_reset (cairo_script_implicit_context_t *cr)
{
    free (cr->current_style.dash);
    cr->current_style.dash = NULL;
    _cairo_pattern_fini (&cr->current_source.base);
    _cairo_path_fixed_fini (&cr->current_path);
    _cairo_script_implicit_context_init (cr);
}
static cairo_script_surface_t *
2
_cairo_script_surface_create_internal (cairo_script_context_t *ctx,
				       cairo_content_t content,
				       cairo_rectangle_t *extents,
				       cairo_surface_t *passthrough)
{
    cairo_script_surface_t *surface;
2
    if (unlikely (ctx == NULL))
	return (cairo_script_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER));
2
    surface = _cairo_calloc (sizeof (cairo_script_surface_t));
2
    if (unlikely (surface == NULL))
	return (cairo_script_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2
    _cairo_surface_init (&surface->base,
			 &_cairo_script_surface_backend,
			 &ctx->base,
			 content,
			 TRUE); /* is_vector */
2
    _cairo_surface_wrapper_init (&surface->wrapper, passthrough);
2
    _cairo_surface_clipper_init (&surface->clipper,
				 _cairo_script_surface_clipper_intersect_clip_path);
2
    surface->width = surface->height = -1;
2
    if (extents) {
1
	surface->width = extents->width;
1
	surface->height = extents->height;
1
	cairo_surface_set_device_offset (&surface->base,
1
					 -extents->x, -extents->y);
    }
2
    surface->emitted = FALSE;
2
    surface->defined = FALSE;
2
    surface->active = FALSE;
2
    surface->operand.type = SURFACE;
2
    cairo_list_init (&surface->operand.link);
2
    _cairo_script_implicit_context_init (&surface->cr);
2
    return surface;
}
static const cairo_device_backend_t _cairo_script_device_backend = {
    CAIRO_DEVICE_TYPE_SCRIPT,
    NULL, NULL, /* lock, unlock */
    _device_flush,  /* flush */
    _device_finish,  /* finish */
    _device_destroy
};
cairo_device_t *
1
_cairo_script_context_create_internal (cairo_output_stream_t *stream)
{
    cairo_script_context_t *ctx;
1
    ctx = _cairo_calloc (sizeof (cairo_script_context_t));
1
    if (unlikely (ctx == NULL))
	return _cairo_device_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1
    memset (ctx, 0, sizeof (cairo_script_context_t));
1
    _cairo_device_init (&ctx->base, &_cairo_script_device_backend);
1
    cairo_list_init (&ctx->operands);
1
    cairo_list_init (&ctx->deferred);
1
    ctx->stream = stream;
1
    ctx->mode = CAIRO_SCRIPT_MODE_ASCII;
1
    cairo_list_init (&ctx->fonts);
1
    cairo_list_init (&ctx->defines);
1
    ctx->attach_snapshots = TRUE;
1
    return &ctx->base;
}
void
_cairo_script_context_attach_snapshots (cairo_device_t *device,
					cairo_bool_t enable)
{
    cairo_script_context_t *ctx;
    ctx = (cairo_script_context_t *) device;
    ctx->attach_snapshots = enable;
}
static cairo_device_t *
1
_cairo_script_context_create (cairo_output_stream_t *stream)
{
    cairo_script_context_t *ctx;
    ctx = (cairo_script_context_t *)
1
	_cairo_script_context_create_internal (stream);
1
    if (unlikely (ctx->base.status))
	return &ctx->base;
1
    ctx->owns_stream = TRUE;
1
    _cairo_output_stream_puts (ctx->stream, "%!CairoScript\n");
1
    return &ctx->base;
}
/**
 * cairo_script_create:
 * @filename: the name (path) of the file to write the script to
 *
 * Creates a output device for emitting the script, used when
 * creating the individual surfaces.
 *
 * Return value: a pointer to the newly created device. The caller
 * owns the surface and should call cairo_device_destroy() when done
 * with it.
 *
 * This function always returns a valid pointer, but it will return a
 * pointer to a "nil" device if an error such as out of memory
 * occurs. You can use cairo_device_status() to check for this.
 *
 * Since: 1.12
 **/
cairo_device_t *
cairo_script_create (const char *filename)
{
    cairo_output_stream_t *stream;
    cairo_status_t status;
    stream = _cairo_output_stream_create_for_filename (filename);
    if ((status = _cairo_output_stream_get_status (stream)))
	return _cairo_device_create_in_error (status);
    return _cairo_script_context_create (stream);
}
/**
 * cairo_script_create_for_stream:
 * @write_func: callback function passed the bytes written to the script
 * @closure: user data to be passed to the callback
 *
 * Creates a output device for emitting the script, used when
 * creating the individual surfaces.
 *
 * Return value: a pointer to the newly created device. The caller
 * owns the surface and should call cairo_device_destroy() when done
 * with it.
 *
 * This function always returns a valid pointer, but it will return a
 * pointer to a "nil" device if an error such as out of memory
 * occurs. You can use cairo_device_status() to check for this.
 *
 * Since: 1.12
 **/
cairo_device_t *
1
cairo_script_create_for_stream (cairo_write_func_t	 write_func,
				void			*closure)
{
    cairo_output_stream_t *stream;
    cairo_status_t status;
1
    stream = _cairo_output_stream_create (write_func, NULL, closure);
1
    if ((status = _cairo_output_stream_get_status (stream)))
	return _cairo_device_create_in_error (status);
1
    return _cairo_script_context_create (stream);
}
/**
 * cairo_script_write_comment:
 * @script: the script (output device)
 * @comment: the string to emit
 * @len:the length of the string to write, or -1 to use strlen()
 *
 * Emit a string verbatim into the script.
 *
 * Since: 1.12
 **/
void
cairo_script_write_comment (cairo_device_t *script,
			    const char *comment,
			    int len)
{
    cairo_script_context_t *context = (cairo_script_context_t *) script;
    if (len < 0)
	len = strlen (comment);
    _cairo_output_stream_puts (context->stream, "% ");
    _cairo_output_stream_write (context->stream, comment, len);
    _cairo_output_stream_puts (context->stream, "\n");
}
/**
 * cairo_script_set_mode:
 * @script: The script (output device)
 * @mode: the new mode
 *
 * Change the output mode of the script
 *
 * Since: 1.12
 **/
void
cairo_script_set_mode (cairo_device_t *script,
		       cairo_script_mode_t mode)
{
    cairo_script_context_t *context = (cairo_script_context_t *) script;
    context->mode = mode;
}
/**
 * cairo_script_get_mode:
 * @script: The script (output device) to query
 *
 * Queries the script for its current output mode.
 *
 * Return value: the current output mode of the script
 *
 * Since: 1.12
 **/
cairo_script_mode_t
cairo_script_get_mode (cairo_device_t *script)
{
    cairo_script_context_t *context = (cairo_script_context_t *) script;
    return context->mode;
}
/**
 * cairo_script_surface_create:
 * @script: the script (output device)
 * @content: the content of the surface
 * @width: width in pixels
 * @height: height in pixels
 *
 * Create a new surface that will emit its rendering through @script
 *
 * Return value: a pointer to the newly created surface. The caller
 * owns the surface and should call cairo_surface_destroy() when done
 * with it.
 *
 * This function always returns a valid pointer, but it will return a
 * pointer to a "nil" surface if an error such as out of memory
 * occurs. You can use cairo_surface_status() to check for this.
 *
 * Since: 1.12
 **/
cairo_surface_t *
1
cairo_script_surface_create (cairo_device_t *script,
			     cairo_content_t content,
			     double width,
			     double height)
{
    cairo_rectangle_t *extents, r;
1
    if (unlikely (script->backend->type != CAIRO_DEVICE_TYPE_SCRIPT))
	return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
1
    if (unlikely (script->status))
	return _cairo_surface_create_in_error (script->status);
1
    extents = NULL;
1
    if (width > 0 && height > 0) {
1
	r.x = r.y = 0;
1
	r.width  = width;
1
	r.height = height;
1
	extents = &r;
    }
1
    return &_cairo_script_surface_create_internal ((cairo_script_context_t *) script,
						   content, extents,
						   NULL)->base;
}
/**
 * cairo_script_surface_create_for_target:
 * @script: the script (output device)
 * @target: a target surface to wrap
 *
 * Create a pxoy surface that will render to @target and record
 * the operations to @device.
 *
 * Return value: a pointer to the newly created surface. The caller
 * owns the surface and should call cairo_surface_destroy() when done
 * with it.
 *
 * This function always returns a valid pointer, but it will return a
 * pointer to a "nil" surface if an error such as out of memory
 * occurs. You can use cairo_surface_status() to check for this.
 *
 * Since: 1.12
 **/
cairo_surface_t *
cairo_script_surface_create_for_target (cairo_device_t *script,
					cairo_surface_t *target)
{
    cairo_rectangle_int_t extents;
    cairo_rectangle_t rect, *r;
    if (unlikely (script->backend->type != CAIRO_DEVICE_TYPE_SCRIPT))
	return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
    if (unlikely (script->status))
	return _cairo_surface_create_in_error (script->status);
    if (unlikely (target->status))
	return _cairo_surface_create_in_error (target->status);
    r = NULL;
    if (_cairo_surface_get_extents (target, &extents)) {
	rect.x = rect.y = 0;
	rect.width = extents.width;
	rect.height = extents.height;
	r= &rect;
    }
    return &_cairo_script_surface_create_internal ((cairo_script_context_t *) script,
						   target->content, r,
						   target)->base;
}
/**
 * cairo_script_from_recording_surface:
 * @script: the script (output device)
 * @recording_surface: the recording surface to replay
 *
 * Converts the record operations in @recording_surface into a script.
 *
 * Return value: #CAIRO_STATUS_SUCCESS on successful completion or an error code.
 *
 * Since: 1.12
 **/
cairo_status_t
cairo_script_from_recording_surface (cairo_device_t *script,
				     cairo_surface_t *recording_surface)
{
    cairo_rectangle_t r, *extents;
    cairo_surface_t *surface;
    cairo_status_t status;
    if (unlikely (script->backend->type != CAIRO_DEVICE_TYPE_SCRIPT))
	return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
    if (unlikely (script->status))
	return _cairo_error (script->status);
    if (unlikely (recording_surface->status))
	return recording_surface->status;
    if (unlikely (! _cairo_surface_is_recording (recording_surface)))
	return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
    extents = NULL;
    if (_cairo_recording_surface_get_bounds (recording_surface, &r))
	extents = &r;
    surface = &_cairo_script_surface_create_internal ((cairo_script_context_t *) script,
						      recording_surface->content,
						      extents,
						      NULL)->base;
    if (unlikely (surface->status))
	return surface->status;
    status = _cairo_recording_surface_replay (recording_surface, surface);
    cairo_surface_destroy (surface);
    return status;
}