1
/* cairo-trace - a utility to record and replay calls to the Cairo library.
2
 *
3
 * Copyright © 2008 Chris Wilson
4
 *
5
 * This program is free software: you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation, either version 3 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
 */
18

            
19
#include "config.h"
20

            
21
/* The autoconf on OpenBSD 4.5 produces the malformed constant name
22
 * SIZEOF_VOID__ rather than SIZEOF_VOID_P.  Work around that here. */
23
#if !defined(SIZEOF_VOID_P) && defined(SIZEOF_VOID__)
24
# define SIZEOF_VOID_P SIZEOF_VOID__
25
#endif
26

            
27
#include <dlfcn.h>
28
#include <stdint.h>
29
#include <stdbool.h>
30
#include <stdio.h>
31
#include <string.h>
32
#include <sys/types.h>
33
#include <unistd.h>
34
#include <errno.h>
35
#include <pthread.h>
36
#include <zlib.h>
37
#include <math.h>
38
#include <locale.h> /* for locale independent %f printing */
39
#include <ctype.h>
40
#include <assert.h>
41
#include <stdlib.h>
42
#include <limits.h>
43
#include <stdarg.h>
44

            
45
#include <cairo.h>
46
#if CAIRO_HAS_FT_FONT
47
# include <cairo-ft.h>
48
#endif
49

            
50
#include "cairo-ctype-inline.h"
51

            
52
#ifndef TRUE
53
#define TRUE 1
54
#define FALSE 0
55
#endif
56

            
57
#ifndef CAIRO_TRACE_OUTDIR
58
#define CAIRO_TRACE_OUTDIR "."
59
#endif
60

            
61
#define DEBUG_STACK 0
62

            
63
#if HAVE_BYTESWAP_H
64
# include <byteswap.h>
65
#endif
66
#ifndef bswap_16
67
# define bswap_16(p) \
68
	(((((uint16_t)(p)) & 0x00ff) << 8) | \
69
	  (((uint16_t)(p))           >> 8))
70
#endif
71
#ifndef bswap_32
72
# define bswap_32(p) \
73
         (((((uint32_t)(p)) & 0x000000ff) << 24) | \
74
	  ((((uint32_t)(p)) & 0x0000ff00) << 8)  | \
75
	  ((((uint32_t)(p)) & 0x00ff0000) >> 8)  | \
76
	  ((((uint32_t)(p)))              >> 24))
77
#endif
78

            
79
#if WORDS_BIGENDIAN
80
#define le16(x) bswap_16 (x)
81
#define le32(x) bswap_32 (x)
82
#define be16(x) x
83
#define be32(x) x
84
#define to_be32(x) x
85
#else
86
#define le16(x) x
87
#define le32(x) x
88
#define be16(x) bswap_16 (x)
89
#define be32(x) bswap_32 (x)
90
#define to_be32(x) bswap_32 (x)
91
#endif
92

            
93
#if CAIRO_HAS_SYMBOL_LOOKUP
94
#include "lookup-symbol.h"
95
#endif
96

            
97
/* Reverse the bits in a byte with 7 operations (no 64-bit):
98
 * Devised by Sean Anderson, July 13, 2001.
99
 * Source: http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits
100
 */
101
#define CAIRO_BITSWAP8(c) ((((c) * 0x0802LU & 0x22110LU) | ((c) * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16)
102

            
103
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
104
#ifdef __MINGW32__
105
#define CAIRO_PRINTF_FORMAT(fmt_index, va_index)                        \
106
	__attribute__((__format__(__MINGW_PRINTF_FORMAT, fmt_index, va_index)))
107
#else
108
#define CAIRO_PRINTF_FORMAT(fmt_index, va_index)                        \
109
	__attribute__((__format__(__printf__, fmt_index, va_index)))
110
#endif
111
#else
112
#define CAIRO_PRINTF_FORMAT(fmt_index, va_index)
113
#endif
114

            
115
/* XXX implement manual vprintf so that the user can control precision of
116
 * printed numbers.
117
 */
118

            
119
static void *_dlhandle = RTLD_NEXT;
120
#define DLCALL(name, args...) ({ \
121
    static typeof (&name) name##_real; \
122
    if (name##_real == NULL) { \
123
	name##_real = (typeof (&name))(dlsym (_dlhandle, #name));	\
124
	if (name##_real == NULL && _dlhandle == RTLD_NEXT) { \
125
	    _dlhandle = dlopen ("libcairo." SHARED_LIB_EXT, RTLD_LAZY); \
126
	    name##_real = (typeof (&name))(dlsym (_dlhandle, #name));	\
127
	    assert (name##_real != NULL); \
128
	} \
129
    } \
130
    (*name##_real) (args);  \
131
})
132

            
133
#ifndef ARRAY_LENGTH
134
#define ARRAY_LENGTH(__array) ((int) (sizeof (__array) / sizeof (__array[0])))
135
#endif
136

            
137
#if SIZEOF_VOID_P == 4
138
#define PTR_SHIFT 2
139
#elif SIZEOF_VOID_P == 8
140
#define PTR_SHIFT 3
141
#else
142
#error Unexpected pointer size
143
#endif
144
#define BUCKET(b, ptr) (((uintptr_t) (ptr) >> PTR_SHIFT) % ARRAY_LENGTH (b))
145

            
146
#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__)
147
#define _BOOLEAN_EXPR(expr)                   \
148
 __extension__ ({                             \
149
   int _boolean_var_;                         \
150
   if (expr)                                  \
151
      _boolean_var_ = 1;                      \
152
   else                                       \
153
      _boolean_var_ = 0;                      \
154
   _boolean_var_;                             \
155
})
156
#define LIKELY(expr) (__builtin_expect (_BOOLEAN_EXPR(expr), 1))
157
#define UNLIKELY(expr) (__builtin_expect (_BOOLEAN_EXPR(expr), 0))
158
#else
159
#define LIKELY(expr) (expr)
160
#define UNLIKELY(expr) (expr)
161
#endif
162

            
163
typedef struct _object Object;
164
typedef struct _type Type;
165

            
166
struct _object {
167
    const void *addr;
168
    Type *type;
169
    unsigned long int token;
170
    int width, height;
171
    cairo_bool_t foreign;
172
    cairo_bool_t defined;
173
    cairo_bool_t unknown;
174
    int operand;
175
    void *data;
176
    void (*destroy)(void *);
177
    Object *next, *prev;
178
};
179

            
180
struct _type {
181
    const char *name;
182

            
183
    enum operand_type {
184
	NONE,
185
	SURFACE,
186
	CONTEXT,
187
	FONT_FACE,
188
	PATTERN,
189
	SCALED_FONT,
190
	_N_OP_TYPES
191
    } op_type;
192
    const char *op_code;
193

            
194
    pthread_mutex_t mutex;
195
    struct _bitmap {
196
	unsigned long int min;
197
	unsigned long int count;
198
	unsigned int map[64];
199
	struct _bitmap *next;
200
    } map;
201
    Object *objects[607];
202
    Type *next;
203
};
204

            
205
static struct _type_table {
206
    pthread_mutex_t mutex;
207
    Type *op_types[_N_OP_TYPES];
208
} Types;
209

            
210
static FILE *logfile;
211
static cairo_bool_t _flush;
212
static cairo_bool_t _error;
213
static cairo_bool_t _line_info;
214
static cairo_bool_t _mark_dirty;
215
static const cairo_user_data_key_t destroy_key;
216
static pthread_once_t once_control = PTHREAD_ONCE_INIT;
217
static pthread_key_t counter_key;
218

            
219
static void _init_trace (void);
220

            
221
#define INIT_TRACE_ONCE() pthread_once (&once_control, _init_trace)
222

            
223
#if __GNUC__ >= 3 && defined(__ELF__) && !defined(__sun)
224
# define _enter_trace() INIT_TRACE_ONCE ()
225
# define _exit_trace()  do { } while (0)
226
# define _should_trace() 1
227
# define USE_ENTER_EXIT 0
228
#else
229
static void _enter_trace (void);
230
static void _exit_trace (void);
231
static cairo_bool_t _should_trace (void);
232
# define USE_ENTER_EXIT 1
233
#endif
234

            
235
#if HAVE_BUILTIN_RETURN_ADDRESS && CAIRO_HAS_SYMBOL_LOOKUP
236
#define _emit_line_info() do { \
237
    if (_line_info && _write_lock ()) {	\
238
	void *addr = __builtin_return_address(0); \
239
	char caller[1024]; \
240
	_trace_printf ("%% %s() called by %s\n", __FUNCTION__, \
241
		       lookup_symbol (caller, sizeof (caller), addr)); \
242
	_write_unlock (); \
243
    } \
244
} while (0)
245
#else
246
#define _emit_line_info()
247
#endif
248

            
249
static void
250
_type_release_token (Type *t, unsigned long int token)
251
{
252
    struct _bitmap *b, **prev = NULL;
253

            
254
    b = &t->map;
255
    while (b != NULL) {
256
	if (token < b->min + sizeof (b->map) * CHAR_BIT) {
257
	    unsigned int bit, elem;
258

            
259
	    token -= b->min;
260
	    elem = token / (sizeof (b->map[0]) * CHAR_BIT);
261
	    bit  = token % (sizeof (b->map[0]) * CHAR_BIT);
262
	    b->map[elem] &= ~(1 << bit);
263
	    if (! --b->count && prev) {
264
		*prev = b->next;
265
		free (b);
266
	    }
267
	    return;
268
	}
269
	prev = &b->next;
270
	b = b->next;
271
    }
272
}
273

            
274
static unsigned long int
275
_type_next_token (Type *t)
276
{
277
    struct _bitmap *b, *bb, **prev = NULL;
278
    unsigned long int min = 0;
279

            
280
    b = &t->map;
281
    while (b != NULL) {
282
	if (b->min != min)
283
	    break;
284

            
285
	if (b->count < sizeof (b->map) * CHAR_BIT) {
286
	    unsigned int n, m, bit;
287
	    for (n = 0; n < ARRAY_LENGTH (b->map); n++) {
288
		if (b->map[n] == (unsigned int) -1)
289
		    continue;
290

            
291
		for (m=0, bit=1; m<sizeof (b->map[0])*CHAR_BIT; m++, bit<<=1) {
292
		    if ((b->map[n] & bit) == 0) {
293
			b->map[n] |= bit;
294
			b->count++;
295
			return n * sizeof (b->map[0])*CHAR_BIT + m + b->min;
296
		    }
297
		}
298
	    }
299
	}
300
	min += sizeof (b->map) * CHAR_BIT;
301

            
302
	prev = &b->next;
303
	b = b->next;
304
    }
305
    assert (prev != NULL);
306

            
307
    bb = malloc (sizeof (struct _bitmap));
308

            
309
    *prev = bb;
310
    bb->next = b;
311
    bb->min = min;
312
    bb->count = 1;
313
    bb->map[0] = 0x1;
314
    memset (bb->map + 1, 0, sizeof (bb->map) - sizeof (bb->map[0]));
315

            
316
    return min;
317
}
318

            
319
static void
320
_object_destroy (Object *obj)
321
{
322
    int bucket;
323

            
324
    pthread_mutex_lock (&obj->type->mutex);
325
    bucket = BUCKET (obj->type->objects, obj->addr);
326
    _type_release_token (obj->type, obj->token);
327

            
328
    if (obj->prev != NULL)
329
	obj->prev->next = obj->next;
330
    else
331
	obj->type->objects[bucket] = obj->next;
332

            
333
    if (obj->next != NULL)
334
	obj->next->prev = obj->prev;
335
    pthread_mutex_unlock (&obj->type->mutex);
336

            
337
    if (obj->data != NULL && obj->destroy != NULL)
338
	obj->destroy (obj->data);
339

            
340
    free (obj);
341
}
342

            
343
static void
344
_type_create (const char *typename,
345
	      enum operand_type op_type,
346
	      const char *op_code)
347
{
348
    Type *t;
349

            
350
    pthread_mutex_lock (&Types.mutex);
351

            
352
    t = malloc (sizeof (Type));
353
    t->name = typename;
354
    t->op_type = op_type;
355
    t->op_code = op_code;
356

            
357
    pthread_mutex_init (&t->mutex, NULL);
358

            
359
    t->map.min = 0;
360
    t->map.count = 0;
361
    memset (t->map.map, 0, sizeof (t->map.map));
362
    t->map.next = NULL;
363

            
364
    memset (t->objects, 0, sizeof (t->objects));
365

            
366
    t->next = NULL;
367

            
368
    Types.op_types[op_type] = t;
369
    pthread_mutex_unlock (&Types.mutex);
370
}
371

            
372
static Type *
373
_get_type (enum operand_type type)
374
{
375
    return Types.op_types[type];
376
}
377

            
378
static void
379
_type_destroy (Type *t)
380
{
381
    int n;
382
    struct _bitmap *b;
383

            
384
    for (n = 0; n < ARRAY_LENGTH (t->objects); n++) {
385
	Object *obj = t->objects[n];
386
	while (obj != NULL) {
387
	    Object *next = obj->next;
388
	    _object_destroy (obj);
389
	    obj = next;
390
	}
391
    }
392

            
393
    b = t->map.next;
394
    while (b != NULL) {
395
	struct _bitmap *next = b->next;
396
	free (b);
397
	b = next;
398
    }
399

            
400
    pthread_mutex_destroy (&t->mutex);
401
    free (t);
402
}
403

            
404
static Object *
405
_type_get_object (Type *type, const void *ptr)
406
{
407
    Object *obj;
408
    int bucket = BUCKET (type->objects, ptr);
409

            
410
    for (obj = type->objects[bucket]; obj != NULL; obj = obj->next) {
411
	if (obj->addr == ptr) {
412
	    if (obj->prev != NULL) { /* mru */
413
		obj->prev->next = obj->next;
414
		if (obj->next != NULL)
415
		    obj->next->prev = obj->prev;
416
		obj->prev = NULL;
417
		type->objects[bucket]->prev = obj;
418
		obj->next = type->objects[bucket];
419
		type->objects[bucket] = obj;
420
	    }
421
	    return obj;
422
	}
423
    }
424

            
425
    return NULL;
426
}
427

            
428
static Object *
429
_object_create (Type *type, const void *ptr)
430
{
431
    Object *obj;
432
    int bucket = BUCKET (type->objects, ptr);
433

            
434
    obj = malloc (sizeof (Object));
435
    obj->unknown = TRUE;
436
    obj->defined = FALSE;
437
    obj->foreign = FALSE;
438
    obj->operand = -1;
439
    obj->type = type;
440
    obj->addr = ptr;
441
    obj->token = _type_next_token (type);
442
    obj->data = NULL;
443
    obj->destroy = NULL;
444
    obj->prev = NULL;
445
    obj->next = type->objects[bucket];
446
    if (type->objects[bucket] != NULL)
447
	type->objects[bucket]->prev = obj;
448
    type->objects[bucket] = obj;
449

            
450
    return obj;
451
}
452

            
453
#if USE_ENTER_EXIT
454
static int *
455
_get_counter (void)
456
{
457
    int *counter = pthread_getspecific (counter_key);
458
    if (counter == NULL) {
459
	counter = calloc(1, sizeof(int));
460
	pthread_setspecific (counter_key, counter);
461
    }
462
    return counter;
463
}
464

            
465
static void
466
_enter_trace (void)
467
{
468
    INIT_TRACE_ONCE ();
469
    _get_counter ()[0]++;
470
}
471

            
472
static void
473
_exit_trace (void)
474
{
475
    _get_counter ()[0]--;
476
}
477

            
478
static cairo_bool_t
479
_should_trace (void)
480
{
481
    return _get_counter ()[0] <= 1;
482
}
483
#endif /* USE_ENTER_EXIT */
484

            
485
static void
486
_init_trace (void)
487
{
488
    pthread_mutex_init (&Types.mutex, NULL);
489
    pthread_key_create (&counter_key, free);
490

            
491
    _type_create ("unclassed", NONE, "");
492
    _type_create ("cairo_t", CONTEXT, "c");
493
    _type_create ("cairo_font_face_t", FONT_FACE, "f");
494
    _type_create ("cairo_pattern_t", PATTERN, "p");
495
    _type_create ("cairo_scaled_font_t", SCALED_FONT, "sf");
496
    _type_create ("cairo_surface_t", SURFACE, "s");
497
}
498

            
499
static void
500
_close_trace (void)
501
{
502
    if (logfile != NULL) {
503
	fclose (logfile);
504
	logfile = NULL;
505
    }
506
}
507

            
508
static void __attribute__ ((destructor))
509
_fini_trace (void)
510
{
511
    int n;
512

            
513
    _close_trace ();
514

            
515
    for (n = 0; n < ARRAY_LENGTH (Types.op_types); n++) {
516
	if (Types.op_types[n]) {
517
	    _type_destroy (Types.op_types[n]);
518
	    Types.op_types[n] = NULL;
519
	}
520
    }
521

            
522
    pthread_mutex_destroy (&Types.mutex);
523
}
524

            
525
/* Format a double in a locale independent way and trim trailing
526
 * zeros.  Based on code from Alex Larson <alexl@redhat.com>.
527
 * https://mail.gnome.org/archives/gtk-devel-list/2001-October/msg00087.html
528
 *
529
 * The code in the patch is copyright Red Hat, Inc under the LGPL.
530
 */
531
#define SIGNIFICANT_DIGITS_AFTER_DECIMAL 6
532
static void
533
_trace_dtostr (char *buffer, size_t size, double d)
534
{
535
    struct lconv *locale_data;
536
    const char *decimal_point;
537
    int decimal_point_len;
538
    char *p;
539
    int decimal_len;
540
    int num_zeros, decimal_digits;
541

            
542
    /* Omit the minus sign from negative zero. */
543
    if (d == 0.0)
544
	d = 0.0;
545

            
546
    locale_data = localeconv ();
547
    decimal_point = locale_data->decimal_point;
548
    decimal_point_len = strlen (decimal_point);
549

            
550
    /* Using "%f" to print numbers less than 0.1 will result in
551
     * reduced precision due to the default 6 digits after the
552
     * decimal point.
553
     *
554
     * For numbers is < 0.1, we print with maximum precision and count
555
     * the number of zeros between the decimal point and the first
556
     * significant digit. We then print the number again with the
557
     * number of decimal places that gives us the required number of
558
     * significant digits. This ensures the number is correctly
559
     * rounded.
560
     */
561
    if (fabs (d) >= 0.1) {
562
	snprintf (buffer, size, "%f", d);
563
    } else {
564
	snprintf (buffer, size, "%.18f", d);
565
	p = buffer;
566

            
567
	if (*p == '+' || *p == '-')
568
	    p++;
569

            
570
	while (_cairo_isdigit (*p))
571
	    p++;
572

            
573
	if (strncmp (p, decimal_point, decimal_point_len) == 0)
574
	    p += decimal_point_len;
575

            
576
	num_zeros = 0;
577
	while (*p++ == '0')
578
	    num_zeros++;
579

            
580
	decimal_digits = num_zeros + SIGNIFICANT_DIGITS_AFTER_DECIMAL;
581

            
582
	if (decimal_digits < 18)
583
	    snprintf (buffer, size, "%.*f", decimal_digits, d);
584
    }
585
    p = buffer;
586

            
587
    if (*p == '+' || *p == '-')
588
	p++;
589

            
590
    while (_cairo_isdigit (*p))
591
	p++;
592

            
593
    if (strncmp (p, decimal_point, decimal_point_len) == 0) {
594
	*p = '.';
595
	decimal_len = strlen (p + decimal_point_len);
596
	memmove (p + 1, p + decimal_point_len, decimal_len);
597
	p[1 + decimal_len] = 0;
598

            
599
	/* Remove trailing zeros and decimal point if possible. */
600
	for (p = p + decimal_len; *p == '0'; p--)
601
	    *p = 0;
602

            
603
	if (*p == '.') {
604
	    *p = 0;
605
	    p--;
606
	}
607
    }
608
}
609

            
610
enum {
611
    LENGTH_MODIFIER_LONG = 0x100
612
};
613

            
614
/* Here's a limited reimplementation of printf.  The reason for doing
615
 * this is primarily to special case handling of doubles.  We want
616
 * locale independent formatting of doubles and we want to trim
617
 * trailing zeros.  This is handled by dtostr() above, and the code
618
 * below handles everything else by calling snprintf() to do the
619
 * formatting.  This functionality is only for internal use and we
620
 * only implement the formats we actually use.
621
 */
622
static void CAIRO_PRINTF_FORMAT(1, 0)
623
_trace_vprintf (const char *fmt, va_list ap)
624
{
625
#define SINGLE_FMT_BUFFER_SIZE 32
626
    char buffer[512], single_fmt[SINGLE_FMT_BUFFER_SIZE];
627
    int single_fmt_length;
628
    char *p;
629
    const char *f, *start;
630
    int length_modifier, width;
631
    cairo_bool_t var_width;
632
    int ret_ignored;
633

            
634
    assert (_should_trace ());
635

            
636
    f = fmt;
637
    p = buffer;
638
    while (*f != '\0') {
639
	if (*f != '%') {
640
	    *p++ = *f++;
641
	    continue;
642
	}
643

            
644
	start = f;
645
	f++;
646

            
647
	if (*f == '0')
648
	    f++;
649

            
650
        var_width = 0;
651
        if (*f == '*') {
652
            var_width = 1;
653
	    f++;
654
        }
655

            
656
	while (_cairo_isdigit (*f))
657
	    f++;
658

            
659
	length_modifier = 0;
660
	if (*f == 'l') {
661
	    length_modifier = LENGTH_MODIFIER_LONG;
662
	    f++;
663
	}
664

            
665
	/* The only format strings exist in the cairo implementation
666
	 * itself. So there's an internal consistency problem if any
667
	 * of them is larger than our format buffer size. */
668
	single_fmt_length = f - start + 1;
669

            
670
	/* Reuse the format string for this conversion. */
671
	memcpy (single_fmt, start, single_fmt_length);
672
	single_fmt[single_fmt_length] = '\0';
673

            
674
	/* Flush contents of buffer before snprintf()'ing into it. */
675
	ret_ignored = fwrite (buffer, 1, p-buffer, logfile);
676

            
677
	/* We group signed and unsigned together in this switch, the
678
	 * only thing that matters here is the size of the arguments,
679
	 * since we're just passing the data through to sprintf(). */
680
	switch (*f | length_modifier) {
681
	case '%':
682
	    buffer[0] = *f;
683
	    buffer[1] = 0;
684
	    break;
685
	case 'd':
686
	case 'u':
687
	case 'o':
688
	case 'x':
689
	case 'X':
690
            if (var_width) {
691
                width = va_arg (ap, int);
692
                snprintf (buffer, sizeof buffer,
693
                          single_fmt, width, va_arg (ap, int));
694
            } else {
695
                snprintf (buffer, sizeof buffer, single_fmt, va_arg (ap, int));
696
            }
697
	    break;
698
	case 'd' | LENGTH_MODIFIER_LONG:
699
	case 'u' | LENGTH_MODIFIER_LONG:
700
	case 'o' | LENGTH_MODIFIER_LONG:
701
	case 'x' | LENGTH_MODIFIER_LONG:
702
	case 'X' | LENGTH_MODIFIER_LONG:
703
            if (var_width) {
704
                width = va_arg (ap, int);
705
                snprintf (buffer, sizeof buffer,
706
                          single_fmt, width, va_arg (ap, long int));
707
            } else {
708
                snprintf (buffer, sizeof buffer,
709
                          single_fmt, va_arg (ap, long int));
710
            }
711
	    break;
712
	case 's':
713
	    snprintf (buffer, sizeof buffer,
714
		      single_fmt, va_arg (ap, const char *));
715
	    break;
716
	case 'f':
717
	case 'g':
718
	    _trace_dtostr (buffer, sizeof buffer, va_arg (ap, double));
719
	    break;
720
	case 'c':
721
	    buffer[0] = va_arg (ap, int);
722
	    buffer[1] = 0;
723
	    break;
724
	default:
725
	    break;
726
	}
727
	p = buffer + strlen (buffer);
728
	f++;
729
    }
730

            
731
    ret_ignored = fwrite (buffer, 1, p-buffer, logfile);
732
    (void)ret_ignored;
733
}
734

            
735
static void CAIRO_PRINTF_FORMAT(1, 2)
736
_trace_printf (const char *fmt, ...)
737
{
738
    va_list ap;
739

            
740
    va_start (ap, fmt);
741
    _trace_vprintf (fmt, ap);
742
    va_end (ap);
743
}
744

            
745
static void
746
get_prog_name (char *buf, int length)
747
{
748
    char *slash;
749
    FILE *file;
750

            
751
    memset (buf, 0, length);
752
    if (length == 0)
753
	return;
754

            
755
    file = fopen ("/proc/self/cmdline", "rb");
756
    if (file != NULL) {
757
	slash = fgets (buf, length, file);
758
	fclose (file);
759

            
760
	if (slash == NULL)
761
	    return;
762
    } else {
763
	char const *name = getenv ("CAIRO_TRACE_PROG_NAME");
764
	if (name != NULL) {
765
	    strncpy (buf, name, length-1);
766
	}
767
    }
768

            
769
    slash = strrchr (buf, '/');
770
    if (slash != NULL) {
771
	size_t len = strlen (slash+1);
772
	memmove (buf, slash+1, len+1);
773
    }
774
}
775

            
776
static void
777
_emit_header (void)
778
{
779
    char name[4096] = "";
780

            
781
    get_prog_name (name, sizeof (name));
782

            
783
    _trace_printf ("%%!CairoScript - %s\n", name);
784
}
785

            
786
static cairo_bool_t
787
_init_logfile (void)
788
{
789
    static cairo_bool_t initialized;
790
    char buf[4096];
791
    const char *filename;
792
    const char *env;
793

            
794
    if (initialized)
795
	return logfile != NULL;
796

            
797
    initialized = TRUE;
798

            
799
    env = getenv ("CAIRO_TRACE_FLUSH");
800
    if (env != NULL)
801
	_flush = atoi (env);
802

            
803
    _line_info = TRUE;
804
    env = getenv ("CAIRO_TRACE_LINE_INFO");
805
    if (env != NULL)
806
	_line_info = atoi (env);
807

            
808
    _mark_dirty = TRUE;
809
    env = getenv ("CAIRO_TRACE_MARK_DIRTY");
810
    if (env != NULL)
811
	_mark_dirty = atoi (env);
812

            
813
    filename = getenv ("CAIRO_TRACE_FD");
814
    if (filename != NULL) {
815
	int fd = atoi (filename);
816
	if (fd == -1)
817
	    return FALSE;
818

            
819
	logfile = fdopen (fd, "w");
820
	if (logfile == NULL) {
821
	    fprintf (stderr, "Failed to open trace file descriptor '%s': %s\n",
822
		       filename, strerror (errno));
823
	    return FALSE;
824
	}
825

            
826
	setenv ("CAIRO_TRACE_FD", "-1", 1);
827
	goto done;
828
    }
829

            
830
    filename = getenv ("CAIRO_TRACE_OUTFILE_EXACT");
831
    if (filename == NULL) {
832
	char name[4096] = "";
833

            
834
	filename = getenv ("CAIRO_TRACE_OUTDIR");
835
	if (filename == NULL)
836
	    filename = CAIRO_TRACE_OUTDIR;
837

            
838
	get_prog_name (name, sizeof (name));
839
	if (*name == '\0')
840
	    strcpy (name, "cairo-trace.dat");
841

            
842
	if (snprintf (buf, sizeof (buf), "%s/%s.%d.trace",
843
		      filename, name, getpid()) >= (int) sizeof (buf))
844
	{
845
	    fprintf (stderr, "cairo-trace: Trace file name too long\n");
846
	    return FALSE;
847
	}
848

            
849
	filename = buf;
850
    } else {
851
	setenv ("CAIRO_TRACE_FD", "-1", 1);
852
    }
853

            
854
    logfile = fopen (filename, "wb");
855
    if (logfile == NULL) {
856
	fprintf (stderr, "Failed to open trace file '%s': %s\n",
857
		   filename, strerror (errno));
858
	return FALSE;
859
    }
860

            
861
    fprintf (stderr, "cairo-trace: Recording cairo trace data to %s\n",
862
	     filename);
863

            
864
done:
865
    atexit (_close_trace);
866
    _emit_header ();
867
    return TRUE;
868
}
869

            
870
static cairo_bool_t
871
_write_lock (void)
872
{
873
    if (_error)
874
	return FALSE;
875

            
876
    if (! _should_trace ())
877
	return FALSE;
878

            
879
    if (! _init_logfile ())
880
	return FALSE;
881

            
882
#if HAVE_FLOCKFILE && HAVE_FUNLOCKFILE
883
    flockfile (logfile);
884
#endif
885
    return TRUE;
886
}
887

            
888
static void
889
_write_unlock (void)
890
{
891
    if (logfile == NULL)
892
	return;
893

            
894
#if HAVE_FLOCKFILE && HAVE_FUNLOCKFILE
895
    funlockfile (logfile);
896
#endif
897

            
898
    if (_flush)
899
	fflush (logfile);
900
}
901

            
902

            
903
static Object *
904
_type_object_create (enum operand_type op_type, const void *ptr)
905
{
906
    Type *type;
907
    Object *obj;
908

            
909
    type = _get_type (op_type);
910

            
911
    pthread_mutex_lock (&type->mutex);
912
    obj = _object_create (type, ptr);
913
    pthread_mutex_unlock (&type->mutex);
914

            
915
    return obj;
916
}
917

            
918
static Object *
919
_get_object (enum operand_type op_type, const void *ptr)
920
{
921
    Type *type;
922
    Object *obj;
923

            
924
    type = _get_type (op_type);
925
    pthread_mutex_lock (&type->mutex);
926
    obj = _type_get_object (type, ptr);
927
    pthread_mutex_unlock (&type->mutex);
928

            
929
    return obj;
930
}
931

            
932
static Object *current_object[2048]; /* XXX limit operand stack */
933
static int current_stack_depth;
934

            
935
static void
936
dump_stack(const char *func)
937
{
938
#if DEBUG_STACK
939
	int n;
940

            
941
	_trace_printf ("%% %s: stack[%d] = [", func, current_stack_depth);
942
	fflush (logfile);
943
	for (n = 0; n < current_stack_depth; n++) {
944
		Object *obj = current_object[n];
945
		assert(obj && obj->type);
946
		_trace_printf (" %s%s%ld",
947
			       obj->defined ? "" : "*",
948
			       obj->type->op_code, obj->token);
949
	fflush (logfile);
950
	}
951
	_trace_printf (" ]\n");
952
	fflush (logfile);
953
#endif
954
}
955

            
956
static void
957
ensure_operands (int num_operands)
958
{
959
    if (current_stack_depth < num_operands) {
960
	int n;
961

            
962
	fprintf (stderr, "Operand stack underflow!\n");
963
	for (n = 0; n < current_stack_depth; n++) {
964
	    Object *obj = current_object[n];
965

            
966
	    fprintf (stderr, "  [%3d] = %s%ld\n",
967
		     n, obj->type->op_code, obj->token);
968
	}
969

            
970
	abort ();
971
    }
972
}
973

            
974
static void
975
_consume_operand (bool discard)
976
{
977
    Object *obj;
978

            
979
    ensure_operands (1);
980
    obj = current_object[--current_stack_depth];
981
    if (!discard && ! obj->defined) {
982
	_trace_printf ("dup /%s%ld exch def\n",
983
		       obj->type->op_code,
984
		       obj->token);
985
	obj->defined = TRUE;
986
    }
987
    obj->operand = -1;
988
}
989

            
990
static void
991
_exch_operands (void)
992
{
993
    Object *tmp;
994

            
995
    ensure_operands (2);
996
    tmp = current_object[current_stack_depth-1];
997
    tmp->operand--;
998
    current_object[current_stack_depth-1] = current_object[current_stack_depth-2];
999
    current_object[current_stack_depth-2] = tmp;
    tmp = current_object[current_stack_depth-1];
    tmp->operand++;
}
static cairo_bool_t
_pop_operands_to_depth (int depth)
{
    if (depth < 0)
	return FALSE;
    assert(current_stack_depth >= depth);
    if (current_stack_depth == depth)
	return TRUE;
    while (current_stack_depth > depth + 1) {
	Object *c_obj;
	ensure_operands (1);
	c_obj = current_object[--current_stack_depth];
	assert(c_obj);
	assert(c_obj->type);
	if (! c_obj->defined) {
	    current_stack_depth++;
	    return FALSE;
	}
	_trace_printf ("pop %% %s%ld\n",
		       c_obj->type->op_code, c_obj->token);
	c_obj->operand = -1;
    }
    _exch_operands ();
    _trace_printf ("exch\n");
    dump_stack(__func__);
    return TRUE;
}
static cairo_bool_t
_pop_operands_to_object (Object *obj)
{
    if (!obj)
	return FALSE;
    if (obj->operand == -1)
	return FALSE;
    if (obj->operand == current_stack_depth - 1)
	return TRUE;
    if (obj->operand == current_stack_depth - 2) {
	_exch_operands ();
	_trace_printf ("exch ");
	return TRUE;
    }
    return _pop_operands_to_depth (obj->operand + 1);
}
static cairo_bool_t
_pop_operands_to (enum operand_type t, const void *ptr)
{
    return _pop_operands_to_object (_get_object (t, ptr));
}
static cairo_bool_t
_is_current_object (Object *obj, int depth)
{
    if (current_stack_depth <= depth)
	return FALSE;
    return current_object[current_stack_depth-depth-1] == obj;
}
static cairo_bool_t
_is_current (enum operand_type type, const void *ptr, int depth)
{
    return _is_current_object (_get_object (type, ptr), depth);
}
static void
_push_object(Object *obj)
{
    assert(obj->operand == -1);
    if (current_stack_depth == ARRAY_LENGTH (current_object)) {
	int n;
	fprintf (stderr, "Operand stack overflow!\n");
	for (n = 0; n < current_stack_depth; n++) {
	    obj = current_object[n];
	    fprintf (stderr, "  [%3d] = %s%ld\n",
		     n, obj->type->op_code, obj->token);
	}
	abort ();
    }
    obj->operand = current_stack_depth;
    current_object[current_stack_depth++] = obj;
}
static void
_push_operand (enum operand_type t, const void *ptr)
{
    _push_object(_get_object(t, ptr));
}
static void
_object_remove (Object *obj)
{
    if (obj->operand != -1) {
	ensure_operands (1);
	if (obj->operand == current_stack_depth - 1) {
	    _trace_printf ("pop %% %s%ld destroyed\n",
			   obj->type->op_code, obj->token);
	} else if (obj->operand == current_stack_depth - 2) {
	    _exch_operands ();
	    _trace_printf ("exch pop %% %s%ld destroyed\n",
			   obj->type->op_code, obj->token);
	} else {
	    int n;
	    _trace_printf ("%d -1 roll pop %% %s%ld destroyed\n",
			   current_stack_depth - obj->operand,
			   obj->type->op_code, obj->token);
	    for (n = obj->operand; n < current_stack_depth - 1; n++) {
		current_object[n] = current_object[n+1];
		current_object[n]->operand = n;
	    }
	}
	obj->operand = -1;
	current_stack_depth--;
	dump_stack(__func__);
    }
}
static void
_object_undef (void *ptr)
{
    Object *obj = ptr;
    if (_write_lock ()) {
	_object_remove (obj);
	if (obj->defined) {
	    _trace_printf ("/%s%ld undef\n",
			   obj->type->op_code, obj->token);
	}
	_write_unlock ();
    }
    _object_destroy (obj);
}
static long
_create_context_id (cairo_t *cr)
{
    Object *obj;
    obj = _get_object (CONTEXT, cr);
    if (obj == NULL) {
	obj = _type_object_create (CONTEXT, cr);
	DLCALL (cairo_set_user_data,
		cr, &destroy_key, obj, _object_undef);
    }
    return obj->token;
}
static long
_get_id (enum operand_type op_type, const void *ptr)
{
    Object *obj;
    obj = _get_object (op_type, ptr);
    if (obj == NULL) {
	if (logfile != NULL) {
	    _trace_printf ("%% Unknown object of type %s, trace is incomplete.",
			   _get_type (op_type)->name);
	}
	_error = TRUE;
	return -1;
    }
    return obj->token;
}
static cairo_bool_t
_has_id (enum operand_type op_type, const void *ptr)
{
    return _get_object (op_type, ptr) != NULL;
}
static long
_create_font_face_id (cairo_font_face_t *font_face)
{
    Object *obj;
    obj = _get_object (FONT_FACE, font_face);
    if (obj == NULL) {
	obj = _type_object_create (FONT_FACE, font_face);
	DLCALL (cairo_font_face_set_user_data,
		font_face, &destroy_key, obj, _object_undef);
    }
    return obj->token;
}
static long
_get_font_face_id (cairo_font_face_t *font_face)
{
    return _get_id (FONT_FACE, font_face);
}
static void
_emit_font_face_id (cairo_font_face_t *font_face)
{
    Object *obj = _get_object (FONT_FACE, font_face);
    if (obj == NULL) {
	_trace_printf ("null ");
    } else {
	if (obj->defined) {
	    _trace_printf ("f%ld ", obj->token);
	} else {
	    _trace_printf ("%d index ", current_stack_depth - obj->operand - 1);
	}
    }
}
static cairo_bool_t
_has_pattern_id (cairo_pattern_t *pattern)
{
    return _has_id (PATTERN, pattern);
}
static long
_create_pattern_id (cairo_pattern_t *pattern)
{
    Object *obj;
    obj = _get_object (PATTERN, pattern);
    if (obj == NULL) {
	obj = _type_object_create (PATTERN, pattern);
	DLCALL (cairo_pattern_set_user_data,
		pattern, &destroy_key, obj, _object_undef);
    }
    return obj->token;
}
static void
_emit_pattern_id (cairo_pattern_t *pattern)
{
    Object *obj = _get_object (PATTERN, pattern);
    if (obj == NULL) {
	_trace_printf ("null ");
    } else {
	if (obj->defined) {
	    _trace_printf ("p%ld ", obj->token);
	} else {
	    _trace_printf ("%d index ",
			   current_stack_depth - obj->operand - 1);
	}
    }
}
static void
_emit_scaled_font_id (const cairo_scaled_font_t *scaled_font)
{
    Object *obj = _get_object (SCALED_FONT, scaled_font);
    if (obj == NULL) {
	_trace_printf ("null ");
    } else {
	if (obj->defined) {
	    _trace_printf ("sf%ld ", obj->token);
	} else {
	    _trace_printf ("%d index ",
		     current_stack_depth - obj->operand - 1);
	}
    }
}
static long
_create_scaled_font_id (cairo_scaled_font_t *font)
{
    Object *obj;
    assert(_get_object (SCALED_FONT, font) == NULL);
    obj = _type_object_create (SCALED_FONT, font);
    DLCALL (cairo_scaled_font_set_user_data,
	    font, &destroy_key, obj, _object_undef);
    return obj->token;
}
static cairo_bool_t
_has_scaled_font_id (const cairo_scaled_font_t *font)
{
    return _has_id (SCALED_FONT, font);
}
static Object *
_create_surface (cairo_surface_t *surface)
{
    Object *obj;
    obj = _get_object (SURFACE, surface);
    if (obj == NULL) {
	obj = _type_object_create (SURFACE, surface);
	DLCALL (cairo_surface_set_user_data,
		surface, &destroy_key, obj, _object_undef);
    }
    return obj;
}
static long
_get_surface_id (cairo_surface_t *surface)
{
    return _get_id (SURFACE, surface);
}
static cairo_bool_t
_matrix_is_identity (const cairo_matrix_t *m)
{
    return m->xx == 1. && m->yx == 0. &&
	   m->xy == 0. && m->yy == 1. &&
	   m->x0 == 0. && m->y0 == 0.;
}
#define BUFFER_SIZE 16384
struct _data_stream {
    z_stream zlib_stream;
    unsigned char zin_buf[BUFFER_SIZE];
    unsigned char zout_buf[BUFFER_SIZE];
    unsigned char four_tuple[4];
    int base85_pending;
};
static void
_write_zlib_data_start (struct _data_stream *stream)
{
    stream->zlib_stream.zalloc = Z_NULL;
    stream->zlib_stream.zfree  = Z_NULL;
    stream->zlib_stream.opaque  = Z_NULL;
    deflateInit (&stream->zlib_stream, Z_DEFAULT_COMPRESSION);
    stream->zlib_stream.next_in = stream->zin_buf;
    stream->zlib_stream.avail_in = 0;
    stream->zlib_stream.next_out = stream->zout_buf;
    stream->zlib_stream.avail_out = BUFFER_SIZE;
}
static void
_write_base85_data_start (struct _data_stream *stream)
{
    stream->base85_pending = 0;
}
static cairo_bool_t
_expand_four_tuple_to_five (unsigned char four_tuple[4],
			    unsigned char five_tuple[5])
{
    uint32_t value;
    int digit, i;
    cairo_bool_t all_zero = TRUE;
    value = four_tuple[0] << 24 |
	    four_tuple[1] << 16 |
	    four_tuple[2] << 8  |
	    four_tuple[3] << 0;
    for (i = 0; i < 5; i++) {
	digit = value % 85;
	if (digit != 0 && all_zero)
	    all_zero = FALSE;
	five_tuple[4-i] = digit + 33;
	value = value / 85;
    }
    return all_zero;
}
static void
_write_base85_data (struct _data_stream *stream,
		    const unsigned char	  *data,
		    unsigned long	   length)
{
    unsigned char five_tuple[5];
    int ret;
    assert (_should_trace ());
    while (length--) {
	stream->four_tuple[stream->base85_pending++] = *data++;
	if (stream->base85_pending == 4) {
	    if (_expand_four_tuple_to_five (stream->four_tuple, five_tuple))
		ret = fwrite ("z", 1, 1, logfile);
	    else
		ret = fwrite (five_tuple, 5, 1, logfile);
	    (void)ret;
	    stream->base85_pending = 0;
	}
    }
}
static void
_write_zlib_data (struct _data_stream *stream, cairo_bool_t flush)
{
    cairo_bool_t finished;
    do {
	int ret = deflate (&stream->zlib_stream, flush ? Z_FINISH : Z_NO_FLUSH);
	if (flush || stream->zlib_stream.avail_out == 0) {
	    _write_base85_data (stream,
				stream->zout_buf,
				BUFFER_SIZE - stream->zlib_stream.avail_out);
	    stream->zlib_stream.next_out = stream->zout_buf;
	    stream->zlib_stream.avail_out = BUFFER_SIZE;
	}
	finished = TRUE;
	if (stream->zlib_stream.avail_in != 0)
	    finished = FALSE;
	if (flush && ret != Z_STREAM_END)
	    finished = FALSE;
    } while (! finished);
    stream->zlib_stream.next_in = stream->zin_buf;
}
static void
_write_data_start (struct _data_stream *stream, uint32_t len)
{
    _write_zlib_data_start (stream);
    _write_base85_data_start (stream);
    _trace_printf ("<|");
    len = to_be32 (len);
    _write_base85_data (stream, (unsigned char *) &len, sizeof (len));
}
static void
_write_data (struct _data_stream *stream,
	     const void *data,
	     unsigned int length)
{
    unsigned int count;
    const unsigned char *p = data;
    while (length) {
	count = length;
	if (count > BUFFER_SIZE - stream->zlib_stream.avail_in)
	    count = BUFFER_SIZE - stream->zlib_stream.avail_in;
	memcpy (stream->zin_buf + stream->zlib_stream.avail_in, p, count);
	p += count;
	stream->zlib_stream.avail_in += count;
	length -= count;
	if (stream->zlib_stream.avail_in == BUFFER_SIZE)
	    _write_zlib_data (stream, FALSE);
    }
}
static void
_write_zlib_data_end (struct _data_stream *stream)
{
    _write_zlib_data (stream, TRUE);
    deflateEnd (&stream->zlib_stream);
}
static void
_write_base85_data_end (struct _data_stream *stream)
{
    unsigned char five_tuple[5];
    int ret;
    assert (_should_trace ());
    if (stream->base85_pending) {
	memset (stream->four_tuple + stream->base85_pending,
		0, 4 - stream->base85_pending);
	_expand_four_tuple_to_five (stream->four_tuple, five_tuple);
	ret = fwrite (five_tuple, stream->base85_pending+1, 1, logfile);
	(void) ret;
    }
}
static void
_write_data_end (struct _data_stream *stream)
{
    _write_zlib_data_end (stream);
    _write_base85_data_end (stream);
    _trace_printf ("~>");
}
static void
_emit_data (const void *data, unsigned int length)
{
    struct _data_stream stream;
    _write_data_start (&stream, length);
    _write_data (&stream, data, length);
    _write_data_end (&stream);
}
static const char *
_format_to_string (cairo_format_t format)
{
#define f(name) case CAIRO_FORMAT_ ## name: return #name
    switch (format) {
	f(INVALID);
	f(RGBA128F);
	f(RGB96F);
	f(ARGB32);
	f(RGB30);
	f(RGB24);
	f(RGB16_565);
	f(A8);
	f(A1);
    }
#undef f
    return "UNKNOWN_FORMAT";
}
static const char *
_format_to_content_string (cairo_format_t format)
{
    switch (format) {
    case CAIRO_FORMAT_INVALID:
	return "INVALID";
    case CAIRO_FORMAT_RGBA128F:
    case CAIRO_FORMAT_ARGB32:
	return "COLOR_ALPHA";
    case CAIRO_FORMAT_RGB96F:
    case CAIRO_FORMAT_RGB30:
    case CAIRO_FORMAT_RGB24:
    case CAIRO_FORMAT_RGB16_565:
	return "COLOR";
    case CAIRO_FORMAT_A8:
    case CAIRO_FORMAT_A1:
	return "ALPHA";
    }
    return "UNKNOWN";
}
static const char *
_status_to_string (cairo_status_t status)
{
#define f(name) case CAIRO_STATUS_ ## name: return "STATUS_" #name
    switch (status) {
	f(SUCCESS);
	f(NO_MEMORY);
	f(INVALID_RESTORE);
	f(INVALID_POP_GROUP);
	f(NO_CURRENT_POINT);
	f(INVALID_MATRIX);
	f(INVALID_STATUS);
	f(NULL_POINTER);
	f(INVALID_STRING);
	f(INVALID_PATH_DATA);
	f(READ_ERROR);
	f(WRITE_ERROR);
	f(SURFACE_FINISHED);
	f(SURFACE_TYPE_MISMATCH);
	f(PATTERN_TYPE_MISMATCH);
	f(INVALID_CONTENT);
	f(INVALID_FORMAT);
	f(INVALID_VISUAL);
	f(FILE_NOT_FOUND);
	f(INVALID_DASH);
	f(INVALID_DSC_COMMENT);
	f(INVALID_INDEX);
	f(CLIP_NOT_REPRESENTABLE);
	f(TEMP_FILE_ERROR);
	f(INVALID_STRIDE);
	f(FONT_TYPE_MISMATCH);
	f(USER_FONT_IMMUTABLE);
	f(USER_FONT_ERROR);
	f(NEGATIVE_COUNT);
	f(INVALID_CLUSTERS);
	f(INVALID_SLANT);
	f(INVALID_WEIGHT);
	f(INVALID_SIZE);
	f(USER_FONT_NOT_IMPLEMENTED);
	f(DEVICE_TYPE_MISMATCH);
	f(DEVICE_ERROR);
	f(INVALID_MESH_CONSTRUCTION);
	f(DEVICE_FINISHED);
	f(JBIG2_GLOBAL_MISSING);
	f(PNG_ERROR);
	f(FREETYPE_ERROR);
	f(WIN32_GDI_ERROR);
	f(TAG_ERROR);
	f(DWRITE_ERROR);
	f(SVG_FONT_ERROR);
    case CAIRO_STATUS_LAST_STATUS:
	break;
    }
    return "UNKNOWN_STATUS";
#undef f
}
static void CAIRO_PRINTF_FORMAT(2, 3)
_emit_image (cairo_surface_t *image,
	     const char *info,
	     ...)
{
    int stride, row, width, height;
    uint32_t len;
    cairo_format_t format;
    uint8_t row_stack[BUFFER_SIZE];
    uint8_t *rowdata;
    uint8_t *data;
    struct _data_stream stream;
    cairo_status_t status;
    status = DLCALL (cairo_surface_status, image);
    if (status) {
	_trace_printf ("<< /status //%s >> image",
		       _status_to_string (status));
	return;
    }
    width = DLCALL (cairo_image_surface_get_width, image);
    height = DLCALL (cairo_image_surface_get_height, image);
    stride = DLCALL (cairo_image_surface_get_stride, image);
    format = DLCALL (cairo_image_surface_get_format, image);
    data = DLCALL (cairo_image_surface_get_data, image);
    _trace_printf ("dict\n"
		   "  /width %d set\n"
		   "  /height %d set\n"
		   "  /format //%s set\n",
		   width, height,
		   _format_to_string (format));
    if (info != NULL) {
	va_list ap;
	va_start (ap, info);
	_trace_vprintf (info, ap);
	va_end (ap);
    }
    if (DLCALL (cairo_version) >= CAIRO_VERSION_ENCODE (1, 9, 0)) {
	const char *mime_types[] = {
	    CAIRO_MIME_TYPE_JPEG,
	    CAIRO_MIME_TYPE_JP2,
	    CAIRO_MIME_TYPE_PNG,
	    NULL
	}, **mime_type;
	for (mime_type = mime_types; *mime_type; mime_type++) {
	    const unsigned char *mime_data;
	    unsigned long mime_length;
	    DLCALL (cairo_surface_get_mime_data,
		    image, *mime_type, &mime_data, &mime_length);
	    if (mime_data != NULL) {
		_trace_printf ("  /mime-type (%s) set\n"
			       "  /source <~",
			       *mime_type);
		_write_base85_data_start (&stream);
		_write_base85_data (&stream, mime_data, mime_length);
		_write_base85_data_end (&stream);
		_trace_printf ("~> set\n"
			       "  image");
		return;
	    }
	}
    }
    switch (format) {
    case CAIRO_FORMAT_A1:        len = (width + 7)/8; break;
    case CAIRO_FORMAT_A8:        len =  width; break;
    case CAIRO_FORMAT_RGB16_565: len = 2*width; break;
    case CAIRO_FORMAT_RGB24:     len = 3*width; break;
    default:
    case CAIRO_FORMAT_RGB30:
    case CAIRO_FORMAT_INVALID:
    case CAIRO_FORMAT_ARGB32: len = 4*width; break;
    case CAIRO_FORMAT_RGB96F: len = 12*width; break;
    case CAIRO_FORMAT_RGBA128F: len = 16*width; break;
    }
    _trace_printf ("  /source ");
    _write_data_start (&stream, len * height);
#ifdef WORDS_BIGENDIAN
    switch (format) {
    case CAIRO_FORMAT_RGB24:
	for (row = height; row--; ) {
	    int col;
	    rowdata = data;
	    for (col = width; col--; ) {
		_write_data (&stream, rowdata, 3);
		rowdata+=4;
	    }
	    data += stride;
	}
	break;
    case CAIRO_FORMAT_A1:
    case CAIRO_FORMAT_A8:
    case CAIRO_FORMAT_RGB16_565:
    case CAIRO_FORMAT_RGB30:
    case CAIRO_FORMAT_ARGB32:
    case CAIRO_FORMAT_RGB96F:
    case CAIRO_FORMAT_RGBA128F:
	for (row = height; row--; ) {
	    _write_data (&stream, data, len);
	    data += stride;
	}
	break;
    case CAIRO_FORMAT_INVALID:
    default:
	break;
    }
#else
    if (stride > ARRAY_LENGTH (row_stack)) {
	rowdata = malloc (stride);
	if (rowdata == NULL)
	    goto BAIL;
    } else
	rowdata = row_stack;
    switch (format) {
    case CAIRO_FORMAT_A1:
	for (row = height; row--; ) {
	    int col;
	    for (col = 0; col < (width + 7)/8; col++)
		rowdata[col] = CAIRO_BITSWAP8 (data[col]);
	    _write_data (&stream, rowdata, (width+7)/8);
	    data += stride;
	}
	break;
    case CAIRO_FORMAT_A8:
	for (row = height; row--; ) {
	    _write_data (&stream, data, width);
	    data += stride;
	}
	break;
    case CAIRO_FORMAT_RGB16_565: /* XXX endianness */
	for (row = 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]);
	    _write_data (&stream, rowdata, 2*width);
	    data += stride;
	}
	break;
    case CAIRO_FORMAT_RGB24:
	for (row = 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++;
	    }
	    _write_data (&stream, rowdata, 3*width);
	    data += stride;
	}
	break;
    case CAIRO_FORMAT_RGB96F:
    case CAIRO_FORMAT_RGBA128F:
    case CAIRO_FORMAT_RGB30:
    case CAIRO_FORMAT_ARGB32:
	for (row = 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]);
	    _write_data (&stream, rowdata, len);
	    data += stride;
	}
	break;
   case CAIRO_FORMAT_INVALID:
    default:
	break;
    }
    if (rowdata != row_stack)
	free (rowdata);
BAIL:
    _write_data_end (&stream);
#endif
    _trace_printf (" set\n  image");
}
static void
_encode_string_literal (char *out, int max,
			const char *utf8, int len)
{
    char c;
    const char *end;
    *out++ = '(';
    max--;
    if (utf8 == NULL)
	goto DONE;
    if (len < 0)
	len = strlen (utf8);
    end = utf8 + len;
    while (utf8 < end) {
	if (max < 5)
	    break;
	switch ((c = *utf8++)) {
	case '\n':
	    *out++ = '\\';
	    *out++ = 'n';
	    max -= 2;
	    break;
	case '\r':
	    *out++ = '\\';
	    *out++ = 'r';
	    max -= 2;
	    break;
	case '\t':
	    *out++ = '\\';
	    *out++ = 't';
	    max -= 2;
	    break;
	case '\b':
	    *out++ = '\\';
	    *out++ = 'b';
	    max -= 2;
	    break;
	case '\f':
	    *out++ = '\\';
	    *out++ = 'f';
	    max -= 2;
	    break;
	case '\\':
	case '(':
	case ')':
	    *out++ = '\\';
	    *out++ = c;
	    max -= 2;
	    break;
	default:
	    if (_cairo_isprint (c)) {
		*out++ = c;
	    } else {
		int octal = 0;
		while (c) {
		    octal *= 10;
		    octal += c&7;
		    c /= 8;
		}
		octal = snprintf (out, max, "\\%03d", octal);
		out += octal;
		max -= octal;
	    }
	    break;
	}
    }
DONE:
    *out++ = ')';
    *out = '\0';
}
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 (const char *utf8, int len)
{
    char c;
    const char *end;
    if (utf8 == NULL) {
	_trace_printf ("()");
	return;
    }
    if (len < 0)
	len = strlen (utf8);
    end = utf8 + len;
    _trace_printf ("(");
    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:
	    _trace_printf ("\\%c", c);
	    break;
	default:
	    if (_cairo_isprint (c)) {
		_trace_printf ("%c", c);
	    } else {
		char buf[4] = { '\\' };
		int ret_ignored;
		to_octal (c, buf+1, 3);
		ret_ignored = fwrite (buf, 4, 1, logfile);
		(void)ret_ignored;
	    }
	    break;
	}
    }
    _trace_printf (")");
}
static void
_emit_current (Object *obj)
{
    if (obj != NULL && ! _pop_operands_to_object (obj)) {
	if (obj->operand != -1) {
	    int n;
	    _trace_printf ("%d -1 roll %% %s%ld\n",
			   current_stack_depth - obj->operand,
			   obj->type->op_code, obj->token);
	    for (n = obj->operand; n < current_stack_depth - 1; n++) {
		current_object[n] = current_object[n+1];
		current_object[n]->operand = n;
	    }
	    obj->operand = -1;
	    current_stack_depth--;
	} else {
	    assert(obj->defined);
	    _trace_printf ("%s%ld\n", obj->type->op_code, obj->token);
	}
	_push_object (obj);
	dump_stack(__func__);
    }
}
static void
_emit_context (cairo_t *cr)
{
    _emit_current (_get_object (CONTEXT, cr));
}
static void
_emit_pattern (cairo_pattern_t *pattern)
{
    _emit_current (_get_object (PATTERN, pattern));
}
static void
_emit_surface (cairo_surface_t *surface)
{
    _emit_current (_get_object (SURFACE, surface));
}
static void CAIRO_PRINTF_FORMAT(2, 3)
_emit_cairo_op (cairo_t *cr, const char *fmt, ...)
{
    va_list ap;
    if (cr == NULL || ! _write_lock ())
	return;
    _emit_context (cr);
    va_start (ap, fmt);
    _trace_vprintf ( fmt, ap);
    va_end (ap);
    _write_unlock ();
}
cairo_t *
cairo_create (cairo_surface_t *target)
{
    cairo_t *ret;
    long surface_id;
    long context_id;
    _enter_trace ();
    ret = DLCALL (cairo_create, target);
    context_id = _create_context_id (ret);
    _emit_line_info ();
    if (target != NULL && _write_lock ()) {
	surface_id = _get_surface_id (target);
	if (surface_id != -1) {
	    _get_object (SURFACE, target)->foreign = FALSE;
	    /* we presume that we will continue to use the context */
	    if (_pop_operands_to (SURFACE, target)){
		_consume_operand (false);
	    } else {
		_trace_printf ("s%ld ", surface_id);
	    }
	    _trace_printf ("context %% c%ld\n", context_id);
	    _push_operand (CONTEXT, ret);
	    dump_stack(__func__);
	}
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
void
cairo_save (cairo_t *cr)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "save\n");
    DLCALL (cairo_save, cr);
    _exit_trace ();
}
void
cairo_restore (cairo_t *cr)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "restore\n");
    DLCALL (cairo_restore, cr);
    _exit_trace ();
}
void
cairo_push_group (cairo_t *cr)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "//COLOR_ALPHA push-group\n");
    DLCALL (cairo_push_group, cr);
    _exit_trace ();
}
static const char *
_content_to_string (cairo_content_t content)
{
    switch (content) {
    case CAIRO_CONTENT_ALPHA: return "ALPHA";
    case CAIRO_CONTENT_COLOR: return "COLOR";
    default:
    case CAIRO_CONTENT_COLOR_ALPHA: return "COLOR_ALPHA";
    }
}
void
cairo_push_group_with_content (cairo_t *cr, cairo_content_t content)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "//%s push-group\n", _content_to_string (content));
    DLCALL (cairo_push_group_with_content, cr, content);
    _exit_trace ();
}
cairo_pattern_t *
cairo_pop_group (cairo_t *cr)
{
    cairo_pattern_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_pop_group, cr);
    _emit_line_info ();
    _emit_cairo_op (cr, "pop-group %% p%ld\n", _create_pattern_id (ret));
    _push_operand (PATTERN, ret);
    dump_stack(__func__);
    _exit_trace ();
    return ret;
}
void
cairo_pop_group_to_source (cairo_t *cr)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "pop-group set-source\n");
    DLCALL (cairo_pop_group_to_source, cr);
    _exit_trace ();
}
static const char *
_operator_to_string (cairo_operator_t op)
{
#define f(name) case CAIRO_OPERATOR_ ## name: return #name
    switch (op) {
	f(OVER);
	f(SOURCE);
	f(CLEAR);
	f(IN);
	f(OUT);
	f(ATOP);
	f(DEST);
	f(DEST_OVER);
	f(DEST_IN);
	f(DEST_OUT);
	f(DEST_ATOP);
	f(XOR);
	f(ADD);
	f(SATURATE);
	f(MULTIPLY);
	f(SCREEN);
	f(OVERLAY);
	f(DARKEN);
	f(LIGHTEN);
        case CAIRO_OPERATOR_COLOR_DODGE: return "DODGE";
        case CAIRO_OPERATOR_COLOR_BURN: return "BURN";
	f(HARD_LIGHT);
	f(SOFT_LIGHT);
	f(DIFFERENCE);
	f(EXCLUSION);
	f(HSL_HUE);
	f(HSL_SATURATION);
	f(HSL_COLOR);
	f(HSL_LUMINOSITY);
    }
#undef f
    return "UNKNOWN_OPERATOR";
}
void
cairo_set_operator (cairo_t *cr, cairo_operator_t op)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "//%s set-operator\n", _operator_to_string (op));
    DLCALL (cairo_set_operator, cr, op);
    _exit_trace ();
}
void
cairo_set_source_rgb (cairo_t *cr, double red, double green, double blue)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "%g %g %g set-source-rgb\n", red, green, blue);
    DLCALL (cairo_set_source_rgb, cr, red, green, blue);
    _exit_trace ();
}
void
cairo_set_source_rgba (cairo_t *cr, double red, double green, double blue, double alpha)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "%g %g %g %g set-source-rgba\n",
		    red, green, blue, alpha);
    DLCALL (cairo_set_source_rgba, cr, red, green, blue, alpha);
    _exit_trace ();
}
static void
_emit_source_image (cairo_surface_t *surface)
{
    Object *obj;
    cairo_surface_t *image;
    cairo_t *cr;
    obj = _get_object (SURFACE, surface);
    if (obj == NULL)
	return;
    image = DLCALL (cairo_image_surface_create,
		    CAIRO_FORMAT_ARGB32,
		    obj->width,
		    obj->height);
    cr = DLCALL (cairo_create, image);
    DLCALL (cairo_set_source_surface, cr, surface, 0, 0);
    DLCALL (cairo_paint, cr);
    DLCALL (cairo_destroy, cr);
    _emit_image (image, NULL);
    _trace_printf (" set-source-image ");
    DLCALL (cairo_surface_destroy, image);
    obj->foreign = FALSE;
}
static void
_emit_source_image_rectangle (cairo_surface_t *surface,
			      int x, int y,
			      int width, int height)
{
    Object *obj;
    cairo_surface_t *image;
    cairo_t *cr;
    obj = _get_object (SURFACE, surface);
    if (obj == NULL)
	return;
    if (obj->foreign) {
	_emit_source_image (surface);
	return;
    }
    image = DLCALL (cairo_image_surface_create,
		    CAIRO_FORMAT_ARGB32,
		    width,
		    height);
    cr = DLCALL (cairo_create, image);
    DLCALL (cairo_set_source_surface, cr, surface, x, y);
    DLCALL (cairo_paint, cr);
    DLCALL (cairo_destroy, cr);
    _emit_image (image, NULL);
    _trace_printf (" %d %d set-device-offset set-source-image ",
	     x, y);
    DLCALL (cairo_surface_destroy, image);
}
void
cairo_set_source_surface (cairo_t *cr, cairo_surface_t *surface, double x, double y)
{
    _enter_trace ();
    _emit_line_info ();
    if (cr != NULL && surface != NULL && _write_lock ()) {
	Object *obj = _get_object (SURFACE, surface);
	if (_is_current (SURFACE, surface, 0) &&
	    _is_current (CONTEXT, cr, 1))
	{
	    _consume_operand (false);
	}
	else if (_is_current (SURFACE, surface, 1) &&
		 _is_current (CONTEXT, cr, 0) &&
		 obj->defined)
	{
	    _trace_printf ("exch ");
	    _exch_operands ();
	    _consume_operand (false);
	} else if (obj->defined) {
	    _emit_context (cr);
	    _trace_printf ("s%ld ", obj->token);
	} else {
	    _emit_context (cr);
	    _trace_printf ("%d index ",
			   current_stack_depth - obj->operand - 1);
	}
	if (obj->foreign)
	    _emit_source_image (surface);
	_trace_printf ("pattern");
	if (x != 0. || y != 0.)
	    _trace_printf (" %g %g translate", -x, -y);
	_trace_printf (" set-source\n");
	_write_unlock ();
    }
    DLCALL (cairo_set_source_surface, cr, surface, x, y);
    _exit_trace ();
}
void
cairo_set_source (cairo_t *cr, cairo_pattern_t *source)
{
    _enter_trace ();
    _emit_line_info ();
    if (cr != NULL && source != NULL && _write_lock ()) {
	Object *obj = _get_object (PATTERN, source);
	cairo_bool_t need_context_and_pattern = TRUE;
	if (_is_current (PATTERN, source, 0) &&
	    _is_current (CONTEXT, cr, 1))
	{
	    if (obj->defined) {
		_consume_operand (false);
	    } else {
		_trace_printf ("exch 1 index ");
		_exch_operands ();
	    }
	    need_context_and_pattern = FALSE;
	}
	else if (_is_current (PATTERN, source, 1) &&
		 _is_current (CONTEXT, cr, 0))
	{
	    if (obj->defined) {
		_trace_printf ("exch ");
		_exch_operands ();
		_consume_operand (false);
		need_context_and_pattern = FALSE;
	    }
	}
	if (need_context_and_pattern) {
	    _emit_context (cr);
	    _emit_pattern_id (source);
	}
	_trace_printf ("set-source %% p%ld\n", obj->token);
	_write_unlock ();
    }
    DLCALL (cairo_set_source, cr, source);
    _exit_trace ();
}
cairo_pattern_t *
cairo_get_source (cairo_t *cr)
{
    cairo_pattern_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_get_source, cr);
    if (! _has_pattern_id (ret)) {
	_emit_cairo_op (cr, "/source get /p%ld exch def\n",
			_create_pattern_id (ret));
	_get_object (PATTERN, ret)->defined = TRUE;
    }
    _exit_trace ();
    return ret;
}
void
cairo_set_tolerance (cairo_t *cr, double tolerance)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "%g set-tolerance\n", tolerance);
    DLCALL (cairo_set_tolerance, cr, tolerance);
    _exit_trace ();
}
static const char *
_antialias_to_string (cairo_antialias_t antialias)
{
#define f(name) case CAIRO_ANTIALIAS_ ## name: return "ANTIALIAS_" #name
    switch (antialias) {
	f(DEFAULT);
	f(NONE);
	f(GRAY);
	f(SUBPIXEL);
	f(FAST);
	f(GOOD);
	f(BEST);
    };
#undef f
    return "UNKNOWN_ANTIALIAS";
}
void
cairo_set_antialias (cairo_t *cr, cairo_antialias_t antialias)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr,
		    "//%s set-antialias\n", _antialias_to_string (antialias));
    DLCALL (cairo_set_antialias, cr, antialias);
    _exit_trace ();
}
static const char *
_fill_rule_to_string (cairo_fill_rule_t rule)
{
#define f(name) case CAIRO_FILL_RULE_ ## name: return #name
    switch (rule) {
	f(WINDING);
	f(EVEN_ODD);
    };
#undef f
    return "UNKNOWN_FILL_RULE";
}
void
cairo_set_fill_rule (cairo_t *cr, cairo_fill_rule_t fill_rule)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr,
		    "//%s set-fill-rule\n", _fill_rule_to_string (fill_rule));
    DLCALL (cairo_set_fill_rule, cr, fill_rule);
    _exit_trace ();
}
void
cairo_set_line_width (cairo_t *cr, double width)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "%g set-line-width\n", width);
    DLCALL (cairo_set_line_width, cr, width);
    _exit_trace ();
}
static const char *
_line_cap_to_string (cairo_line_cap_t line_cap)
{
#define f(name) case CAIRO_LINE_CAP_ ## name: return "LINE_CAP_" #name
    switch (line_cap) {
	f(BUTT);
	f(ROUND);
	f(SQUARE);
    };
#undef f
    return "UNKNOWN_LINE_CAP";
}
void
cairo_set_line_cap (cairo_t *cr, cairo_line_cap_t line_cap)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "//%s set-line-cap\n", _line_cap_to_string (line_cap));
    DLCALL (cairo_set_line_cap, cr, line_cap);
    _exit_trace ();
}
static const char *
_line_join_to_string (cairo_line_join_t line_join)
{
#define f(name) case CAIRO_LINE_JOIN_ ## name: return "LINE_JOIN_" #name
    switch (line_join) {
	f(MITER);
	f(ROUND);
	f(BEVEL);
    };
#undef f
    return "UNKNOWN_LINE_JOIN";
}
void
cairo_set_line_join (cairo_t *cr, cairo_line_join_t line_join)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr,
		    "//%s set-line-join\n", _line_join_to_string (line_join));
    DLCALL (cairo_set_line_join, cr, line_join);
    _exit_trace ();
}
void
cairo_set_dash (cairo_t *cr, const double *dashes, int num_dashes, double offset)
{
    _enter_trace ();
    _emit_line_info ();
    if (cr != NULL && _write_lock ()) {
	int n;
	_emit_context (cr);
	_trace_printf ("[");
	for (n = 0; n <  num_dashes; n++) {
	    if (n != 0)
		_trace_printf (" ");
	    _trace_printf ("%g", dashes[n]);
	}
	_trace_printf ("] %g set-dash\n", offset);
	_write_unlock ();
    }
    DLCALL (cairo_set_dash, cr, dashes, num_dashes, offset);
    _exit_trace ();
}
void
cairo_set_miter_limit (cairo_t *cr, double limit)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "%g set-miter-limit\n", limit);
    DLCALL (cairo_set_miter_limit, cr, limit);
    _exit_trace ();
}
void
cairo_translate (cairo_t *cr, double tx, double ty)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "%g %g translate\n", tx, ty);
    DLCALL (cairo_translate, cr, tx, ty);
    _exit_trace ();
}
void
cairo_scale (cairo_t *cr, double sx, double sy)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "%g %g scale\n", sx, sy);
    DLCALL (cairo_scale, cr, sx, sy);
    _exit_trace ();
}
void
cairo_rotate (cairo_t *cr, double angle)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "%g rotate\n", angle);
    DLCALL (cairo_rotate, cr, angle);
    _exit_trace ();
}
void
cairo_transform (cairo_t *cr, const cairo_matrix_t *matrix)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "%g %g %g %g %g %g matrix transform\n",
		    matrix->xx, matrix->yx,
		    matrix->xy, matrix->yy,
		    matrix->x0, matrix->y0);
    DLCALL (cairo_transform, cr, matrix);
    _exit_trace ();
}
void
cairo_set_matrix (cairo_t *cr, const cairo_matrix_t *matrix)
{
    _enter_trace ();
    _emit_line_info ();
    if (_matrix_is_identity (matrix)) {
	_emit_cairo_op (cr, "identity set-matrix\n");
    } else {
	_emit_cairo_op (cr, "%g %g %g %g %g %g matrix set-matrix\n",
			matrix->xx, matrix->yx,
			matrix->xy, matrix->yy,
			matrix->x0, matrix->y0);
    }
    DLCALL (cairo_set_matrix, cr, matrix);
    _exit_trace ();
}
cairo_surface_t *
cairo_get_target (cairo_t *cr)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_get_target, cr);
    if (cr != NULL) {
	Object *obj = _create_surface (ret);
	if (! obj->defined) {
	    _emit_cairo_op (cr,
			    "/target get /s%ld exch def\n",
			    obj->token);
	    obj->defined = TRUE;
	}
    }
    _exit_trace ();
    return ret;
}
cairo_surface_t *
cairo_get_group_target (cairo_t *cr)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_get_group_target, cr);
    if (cr != NULL) {
	Object *obj = _create_surface (ret);
	if (! obj->defined) {
	    _emit_cairo_op (cr,
			    "/group-target get /s%ld exch def\n",
			    obj->token);
	    obj->defined = TRUE;
	}
    }
    _exit_trace ();
    return ret;
}
void
cairo_identity_matrix (cairo_t *cr)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "identity set-matrix\n");
    DLCALL (cairo_identity_matrix, cr);
    _exit_trace ();
}
void
cairo_new_path (cairo_t *cr)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "n ");
    DLCALL (cairo_new_path, cr);
    _exit_trace ();
}
void
cairo_move_to (cairo_t *cr, double x, double y)
{
    _enter_trace ();
    _emit_cairo_op (cr, "%g %g m ", x, y);
    DLCALL (cairo_move_to, cr, x, y);
    _exit_trace ();
}
void
cairo_new_sub_path (cairo_t *cr)
{
    _enter_trace ();
    _emit_cairo_op (cr, "N ");
    DLCALL (cairo_new_sub_path, cr);
    _exit_trace ();
}
void
cairo_line_to (cairo_t *cr, double x, double y)
{
    _enter_trace ();
    _emit_cairo_op (cr, "%g %g l ", x, y);
    DLCALL (cairo_line_to, cr, x, y);
    _exit_trace ();
}
void
cairo_curve_to (cairo_t *cr, double x1, double y1, double x2, double y2, double x3, double y3)
{
    _enter_trace ();
    _emit_cairo_op (cr, "%g %g %g %g %g %g c ", x1, y1, x2, y2, x3, y3);
    DLCALL (cairo_curve_to, cr, x1, y1, x2, y2, x3, y3);
    _exit_trace ();
}
void
cairo_arc (cairo_t *cr, double xc, double yc, double radius, double angle1, double angle2)
{
    _enter_trace ();
    _emit_cairo_op (cr, "%g %g %g %g %g arc\n", xc, yc, radius, angle1, angle2);
    DLCALL (cairo_arc, cr, xc, yc, radius, angle1, angle2);
    _exit_trace ();
}
void
cairo_arc_negative (cairo_t *cr, double xc, double yc, double radius, double angle1, double angle2)
{
    _enter_trace ();
    _emit_cairo_op (cr, "%g %g %g %g %g arc-\n",
		    xc, yc, radius, angle1, angle2);
    DLCALL (cairo_arc_negative, cr, xc, yc, radius, angle1, angle2);
    _exit_trace ();
}
void
cairo_rel_move_to (cairo_t *cr, double dx, double dy)
{
    _enter_trace ();
    _emit_cairo_op (cr, "%g %g M ", dx, dy);
    DLCALL (cairo_rel_move_to, cr, dx, dy);
    _exit_trace ();
}
void
cairo_rel_line_to (cairo_t *cr, double dx, double dy)
{
    _enter_trace ();
    _emit_cairo_op (cr, "%g %g L ", dx, dy);
    DLCALL (cairo_rel_line_to, cr, dx, dy);
    _exit_trace ();
}
void
cairo_rel_curve_to (cairo_t *cr, double dx1, double dy1, double dx2, double dy2, double dx3, double dy3)
{
    _enter_trace ();
    _emit_cairo_op (cr, "%g %g %g %g %g %g C ",
		    dx1, dy1, dx2, dy2, dx3, dy3);
    DLCALL (cairo_rel_curve_to, cr, dx1, dy1, dx2, dy2, dx3, dy3);
    _exit_trace ();
}
void
cairo_rectangle (cairo_t *cr, double x, double y, double width, double height)
{
    _enter_trace ();
    _emit_cairo_op (cr, "%g %g %g %g rectangle\n", x, y, width, height);
    DLCALL (cairo_rectangle, cr, x, y, width, height);
    _exit_trace ();
}
void
cairo_close_path (cairo_t *cr)
{
    _enter_trace ();
    _emit_cairo_op (cr, "h\n");
    DLCALL (cairo_close_path, cr);
    _exit_trace ();
}
void
cairo_paint (cairo_t *cr)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "paint\n");
    DLCALL (cairo_paint, cr);
    _exit_trace ();
}
void
cairo_paint_with_alpha (cairo_t *cr, double alpha)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "%g paint-with-alpha\n", alpha);
    DLCALL (cairo_paint_with_alpha, cr, alpha);
    _exit_trace ();
}
void
cairo_mask (cairo_t *cr, cairo_pattern_t *pattern)
{
    _enter_trace ();
    _emit_line_info ();
    if (cr != NULL && pattern != NULL && _write_lock ()) {
	Object *obj = _get_object (PATTERN, pattern);
	cairo_bool_t need_context_and_pattern = TRUE;
	if (_is_current (PATTERN, pattern, 0) &&
	    _is_current (CONTEXT, cr, 1))
	{
	    if (obj->defined) {
		_consume_operand (false);
		need_context_and_pattern = FALSE;
	    }
	}
	else if (_is_current (PATTERN, pattern, 1) &&
		 _is_current (CONTEXT, cr, 0))
	{
	    if (obj->defined) {
		_trace_printf ("exch ");
		_exch_operands ();
		_consume_operand (false);
		need_context_and_pattern = FALSE;
	    }
	}
	if (need_context_and_pattern) {
	    _emit_context (cr);
	    _emit_pattern_id (pattern);
	}
	_trace_printf (" mask\n");
	_write_unlock ();
    }
    DLCALL (cairo_mask, cr, pattern);
    _exit_trace ();
}
void
cairo_mask_surface (cairo_t *cr, cairo_surface_t *surface, double x, double y)
{
    _enter_trace ();
    _emit_line_info ();
    if (cr != NULL && surface != NULL && _write_lock ()) {
	Object *obj = _get_object (SURFACE, surface);
	if (_is_current (SURFACE, surface, 0) &&
	    _is_current (CONTEXT, cr, 1))
	{
	    _consume_operand (false);
	}
	else if (_is_current (SURFACE, surface, 1) &&
		 _is_current (CONTEXT, cr, 0))
	{
	    _trace_printf ("exch ");
	    _exch_operands ();
	    _consume_operand (false);
	} else if (obj->defined){
	    _emit_context (cr);
	    _trace_printf ("s%ld ", obj->token);
	} else {
	    _emit_context (cr);
	    _trace_printf ("%d index ",
			   current_stack_depth - obj->operand - 1);
	}
	_trace_printf ("pattern");
	if (x != 0. || y != 0.)
	    _trace_printf (" %g %g translate", -x, -y);
	_trace_printf (" mask\n");
	_write_unlock ();
    }
    DLCALL (cairo_mask_surface, cr, surface, x, y);
    _exit_trace ();
}
void
cairo_stroke (cairo_t *cr)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "stroke\n");
    DLCALL (cairo_stroke, cr);
    _exit_trace ();
}
void
cairo_stroke_preserve (cairo_t *cr)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "stroke+\n");
    DLCALL (cairo_stroke_preserve, cr);
    _exit_trace ();
}
void
cairo_fill (cairo_t *cr)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "fill\n");
    DLCALL (cairo_fill, cr);
    _exit_trace ();
}
void
cairo_fill_preserve (cairo_t *cr)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "fill+\n");
    DLCALL (cairo_fill_preserve, cr);
    _exit_trace ();
}
void
cairo_copy_page (cairo_t *cr)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "copy-page\n");
    DLCALL (cairo_copy_page, cr);
    _exit_trace ();
}
void
cairo_show_page (cairo_t *cr)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "show-page\n");
    DLCALL (cairo_show_page, cr);
    _exit_trace ();
}
void
cairo_clip (cairo_t *cr)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "clip\n");
    DLCALL (cairo_clip, cr);
    _exit_trace ();
}
void
cairo_clip_preserve (cairo_t *cr)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "clip+\n");
    DLCALL (cairo_clip_preserve, cr);
    _exit_trace ();
}
void
cairo_reset_clip (cairo_t *cr)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "reset-clip\n");
    DLCALL (cairo_reset_clip, cr);
    _exit_trace ();
}
static const char *
_slant_to_string (cairo_font_slant_t font_slant)
{
#define f(name) case CAIRO_FONT_SLANT_ ## name: return "SLANT_" #name
    switch (font_slant) {
	f(NORMAL);
	f(ITALIC);
	f(OBLIQUE);
    };
#undef f
    return "UNKNOWN_SLANT";
}
static const char *
_weight_to_string (cairo_font_weight_t font_weight)
{
#define f(name) case CAIRO_FONT_WEIGHT_ ## name: return "WEIGHT_" #name
    switch (font_weight) {
	f(NORMAL);
	f(BOLD);
    };
#undef f
    return "UNKNOWN_WEIGHT";
}
void
cairo_select_font_face (cairo_t *cr, const char *family, cairo_font_slant_t slant, cairo_font_weight_t weight)
{
    _enter_trace ();
    _emit_line_info ();
    if (cr != NULL && _write_lock ()) {
	_emit_context (cr);
	_emit_string_literal (family, -1);
	_trace_printf (" //%s //%s select-font-face\n",
		       _slant_to_string (slant),
		       _weight_to_string (weight));
	_write_unlock ();
    }
    DLCALL (cairo_select_font_face, cr, family, slant, weight);
    _exit_trace ();
}
cairo_font_face_t *
cairo_get_font_face (cairo_t *cr)
{
    cairo_font_face_t *ret;
    long font_face_id;
    _enter_trace ();
    ret = DLCALL (cairo_get_font_face, cr);
    font_face_id = _create_font_face_id (ret);
    _emit_cairo_op (cr, "/font-face get %% f%ld\n", font_face_id);
    _push_operand (FONT_FACE, ret);
    dump_stack(__func__);
    _exit_trace ();
    return ret;
}
void
cairo_set_font_face (cairo_t *cr, cairo_font_face_t *font_face)
{
    _enter_trace ();
    _emit_line_info ();
    if (cr != NULL && font_face != NULL && _write_lock ()) {
	if (_is_current (FONT_FACE, font_face, 0) &&
	    _is_current (CONTEXT, cr, 1))
	{
	    _consume_operand (false);
	}
	else if (_is_current (FONT_FACE, font_face, 1) &&
		 _is_current (CONTEXT, cr, 0))
	{
	    _trace_printf ("exch ");
	    _exch_operands ();
	    _consume_operand (false);
	}
	else
	{
	    _emit_context (cr);
	    _emit_font_face_id (font_face);
	}
	_trace_printf ("set-font-face\n");
	_write_unlock ();
    }
    DLCALL (cairo_set_font_face, cr, font_face);
    _exit_trace ();
}
void
cairo_set_font_size (cairo_t *cr, double size)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "%g set-font-size\n", size);
    DLCALL (cairo_set_font_size, cr, size);
    _exit_trace ();
}
void
cairo_set_font_matrix (cairo_t *cr, const cairo_matrix_t *matrix)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_cairo_op (cr, "%g %g %g %g %g %g matrix set-font-matrix\n",
		    matrix->xx, matrix->yx,
		    matrix->xy, matrix->yy,
		    matrix->x0, matrix->y0);
    DLCALL (cairo_set_font_matrix, cr, matrix);
    _exit_trace ();
}
static const char *
_subpixel_order_to_string (cairo_subpixel_order_t subpixel_order)
{
#define f(name) case CAIRO_SUBPIXEL_ORDER_ ## name: return "SUBPIXEL_ORDER_" #name
    switch (subpixel_order) {
	f(DEFAULT);
	f(RGB);
	f(BGR);
	f(VRGB);
	f(VBGR);
    };
#undef f
    return "UNKNOWN_SUBPIXEL_ORDER";
}
static const char *
_hint_style_to_string (cairo_hint_style_t hint_style)
{
#define f(name) case CAIRO_HINT_STYLE_ ## name: return "HINT_STYLE_" #name
    switch (hint_style) {
	f(DEFAULT);
	f(NONE);
	f(SLIGHT);
	f(MEDIUM);
	f(FULL);
    };
#undef f
    return "UNKNOWN_HINT_STYLE";
}
static const char *
_hint_metrics_to_string (cairo_hint_metrics_t hint_metrics)
{
#define f(name) case CAIRO_HINT_METRICS_ ## name: return "HINT_METRICS_" #name
    switch (hint_metrics) {
	f(DEFAULT);
	f(OFF);
	f(ON);
    };
#undef f
    return "UNKNOWN_HINT_METRICS";
}
static void
_emit_font_options (const cairo_font_options_t *options)
{
    cairo_antialias_t antialias;
    cairo_subpixel_order_t subpixel_order;
    cairo_hint_style_t hint_style;
    cairo_hint_metrics_t hint_metrics;
    _trace_printf ("<<");
    antialias = DLCALL (cairo_font_options_get_antialias, options);
    if (antialias != CAIRO_ANTIALIAS_DEFAULT) {
	_trace_printf (" /antialias //%s",
		       _antialias_to_string (antialias));
    }
    subpixel_order = DLCALL (cairo_font_options_get_subpixel_order, options);
    if (subpixel_order != CAIRO_SUBPIXEL_ORDER_DEFAULT) {
	_trace_printf (" /subpixel-order //%s",
		       _subpixel_order_to_string (subpixel_order));
    }
    hint_style = DLCALL (cairo_font_options_get_hint_style, options);
    if (hint_style != CAIRO_HINT_STYLE_DEFAULT) {
	_trace_printf (" /hint-style //%s",
		       _hint_style_to_string (hint_style));
    }
    hint_metrics = DLCALL (cairo_font_options_get_hint_metrics, options);
    if (hint_metrics != CAIRO_HINT_METRICS_DEFAULT) {
	_trace_printf (" /hint-metrics //%s",
		       _hint_metrics_to_string (hint_metrics));
    }
    _trace_printf (" >>");
}
void
cairo_set_font_options (cairo_t *cr, const cairo_font_options_t *options)
{
    _enter_trace ();
    _emit_line_info ();
    if (cr != NULL && options != NULL && _write_lock ()) {
	_emit_context (cr);
	_emit_font_options (options);
	_trace_printf (" set-font-options\n");
	_write_unlock ();
    }
    DLCALL (cairo_set_font_options, cr, options);
    _exit_trace ();
}
cairo_scaled_font_t *
cairo_get_scaled_font (cairo_t *cr)
{
    cairo_scaled_font_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_get_scaled_font, cr);
    if (cr != NULL && ! _has_scaled_font_id (ret)) {
	_emit_cairo_op (cr, "/scaled-font get /sf%ld exch def\n",
			_create_scaled_font_id (ret));
	_get_object (SCALED_FONT, ret)->defined = TRUE;
    }
    _exit_trace ();
    return ret;
}
void
cairo_set_scaled_font (cairo_t *cr, const cairo_scaled_font_t *scaled_font)
{
    _enter_trace ();
    _emit_line_info ();
    if (cr != NULL && scaled_font != NULL && _write_lock ()) {
	Object *obj = _get_object (SCALED_FONT, scaled_font);
	cairo_bool_t need_context_and_font = TRUE;
	if (_is_current (SCALED_FONT, scaled_font, 0) &&
	    _is_current (CONTEXT, cr, 1))
	{
	    if (obj->defined) {
		_consume_operand (false);
	    } else {
		_trace_printf ("exch 1 index ");
		_exch_operands ();
	    }
	    need_context_and_font = FALSE;
	}
	else if (_is_current (SCALED_FONT, scaled_font, 1) &&
		 _is_current (CONTEXT, cr, 0))
	{
	    if (obj->defined) {
		_trace_printf ("exch ");
		_exch_operands ();
		_consume_operand (false);
		need_context_and_font = FALSE;
	    }
	}
	if (need_context_and_font) {
	    _emit_context (cr);
	    _emit_scaled_font_id (scaled_font);
	}
	_trace_printf ("set-scaled-font\n");
	_write_unlock ();
    }
    DLCALL (cairo_set_scaled_font, cr, scaled_font);
    _exit_trace ();
}
static void
_emit_matrix (const cairo_matrix_t *m)
{
    if (_matrix_is_identity(m))
    {
	_trace_printf ("identity");
    }
    else
    {
	_trace_printf ("%g %g %g %g %g %g matrix",
		       m->xx, m->yx,
		       m->xy, m->yy,
		       m->x0, m->y0);
    }
}
cairo_scaled_font_t *
cairo_scaled_font_create (cairo_font_face_t *font_face,
			  const cairo_matrix_t *font_matrix,
			  const cairo_matrix_t *ctm,
			  const cairo_font_options_t *options)
{
    cairo_scaled_font_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_scaled_font_create, font_face, font_matrix, ctm, options);
    if (_has_scaled_font_id (ret))
	    goto out;
    _emit_line_info ();
    if (font_face != NULL &&
	font_matrix != NULL &&
	ctm != NULL &&
	options != NULL
	&& _write_lock ())
    {
	Object *obj;
	obj = _type_object_create (SCALED_FONT, ret);
	DLCALL (cairo_scaled_font_set_user_data,
		ret, &destroy_key, obj, _object_undef);
	if (_pop_operands_to (FONT_FACE, font_face))
	    _consume_operand (false);
	else
	    _trace_printf ("f%ld ", _get_font_face_id (font_face));
	_emit_matrix (font_matrix);
	_trace_printf (" ");
	_emit_matrix (ctm);
	_trace_printf (" ");
	_emit_font_options (options);
	_trace_printf (" scaled-font /sf%ld exch def\n",
		       obj->token);
	obj->defined = TRUE;
	_write_unlock ();
    }
out:
    _exit_trace ();
    return ret;
}
void
cairo_show_text (cairo_t *cr, const char *utf8)
{
    _enter_trace ();
    _emit_line_info ();
    if (cr != NULL && _write_lock ()) {
	_emit_context (cr);
	_emit_string_literal (utf8, -1);
	_trace_printf (" show-text\n");
	_write_unlock ();
    }
    DLCALL (cairo_show_text, cr, utf8);
    _exit_trace ();
}
static void
_glyph_advance (cairo_scaled_font_t *font,
		const cairo_glyph_t *glyph,
		double *x, double *y)
{
    cairo_text_extents_t extents;
    DLCALL (cairo_scaled_font_glyph_extents, font, glyph, 1, &extents);
    *x += extents.x_advance;
    *y += extents.y_advance;
}
#define TOLERANCE 1e-5
static void
_emit_glyphs (cairo_scaled_font_t *font,
	      const cairo_glyph_t *glyphs,
	      int num_glyphs)
{
    double x,y;
    int n;
    if (num_glyphs == 0) {
	_trace_printf ("[]");
	return;
    }
    for (n = 0; n < num_glyphs; n++) {
	if (glyphs[n].index > 255)
	    break;
    }
    x = glyphs->x;
    y = glyphs->y;
    if (n < num_glyphs) { /* need full glyph range */
	cairo_bool_t first;
	_trace_printf ("[%g %g [", x, y);
	first = TRUE;
	while (num_glyphs--) {
	    if (fabs (glyphs->x - x) > TOLERANCE ||
		fabs (glyphs->y - y) > TOLERANCE)
	    {
		x = glyphs->x;
		y = glyphs->y;
		_trace_printf ("] %g %g [", x, y);
		first = TRUE;
	    }
	    if (! first)
		_trace_printf (" ");
	    _trace_printf ("%lu", glyphs->index);
	    first = FALSE;
	    _glyph_advance (font, glyphs, &x, &y);
	    glyphs++;
	}
	_trace_printf ("]]");
    } else {
	struct _data_stream stream;
	if (num_glyphs == 1) {
	    _trace_printf ("[%g %g <%02lx>]", x, y,  glyphs->index);
	} else {
	    _trace_printf ("[%g %g <~", x, y);
	    _write_base85_data_start (&stream);
	    while (num_glyphs--) {
		unsigned char c;
		if (fabs (glyphs->x - x) > TOLERANCE ||
		    fabs (glyphs->y - y) > TOLERANCE)
		{
		    x = glyphs->x;
		    y = glyphs->y;
		    _write_base85_data_end (&stream);
		    _trace_printf ("~> %g %g <~", x, y);
		    _write_base85_data_start (&stream);
		}
		c = glyphs->index;
		_write_base85_data (&stream, &c, 1);
		_glyph_advance (font, glyphs, &x, &y);
		glyphs++;
	    }
	    _write_base85_data_end (&stream);
	    _trace_printf ("~>]");
	}
    }
}
void
cairo_show_glyphs (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs)
{
    _enter_trace ();
    _emit_line_info ();
    if (cr != NULL && glyphs != NULL && _write_lock ()) {
	cairo_scaled_font_t *font;
	_emit_context (cr);
	font = DLCALL (cairo_get_scaled_font, cr);
	_emit_glyphs (font, glyphs, num_glyphs);
	_trace_printf (" show-glyphs\n");
	_write_unlock ();
    }
    DLCALL (cairo_show_glyphs, cr, glyphs, num_glyphs);
    _exit_trace ();
}
static const char *
_direction_to_string (cairo_bool_t backward)
{
    const char *names[] = {
	"FORWARD",
	"BACKWARD"
    };
    return names[!!backward];
}
void
cairo_show_text_glyphs (cairo_t			   *cr,
			const char		   *utf8,
			int			    utf8_len,
			const 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 *font;
    _enter_trace ();
    font = DLCALL (cairo_get_scaled_font, cr);
    _emit_line_info ();
    if (cr != NULL && glyphs != NULL && clusters != NULL && _write_lock ()) {
	int n;
	_emit_context (cr);
	_emit_string_literal (utf8, utf8_len);
	_emit_glyphs (font, glyphs, num_glyphs);
	_trace_printf ("  [");
	for (n = 0; n < num_clusters; n++) {
	    _trace_printf (" %d %d",
			   clusters[n].num_bytes,
			   clusters[n].num_glyphs);
	}
	_trace_printf (" ] //%s show-text-glyphs\n",
		       _direction_to_string (backward));
	_write_unlock ();
    }
    DLCALL (cairo_show_text_glyphs, cr,
	                            utf8, utf8_len,
				    glyphs, num_glyphs,
				    clusters, num_clusters,
				    backward);
    _exit_trace ();
}
void
cairo_text_path (cairo_t *cr, const char *utf8)
{
    _enter_trace ();
    _emit_line_info ();
    if (cr != NULL && _write_lock ()) {
	_emit_context (cr);
	_emit_string_literal (utf8, -1);
	_trace_printf (" text-path\n");
	_write_unlock ();
    }
    DLCALL (cairo_text_path, cr, utf8);
    _exit_trace ();
}
void
cairo_glyph_path (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs)
{
    cairo_scaled_font_t *font;
    _enter_trace ();
    font = DLCALL (cairo_get_scaled_font, cr);
    _emit_line_info ();
    if (cr != NULL && glyphs != NULL && _write_lock ()) {
	_emit_context (cr);
	_emit_glyphs (font, glyphs, num_glyphs);
	_trace_printf (" glyph-path\n");
	_write_unlock ();
    }
    DLCALL (cairo_glyph_path, cr, glyphs, num_glyphs);
    _exit_trace ();
}
void
cairo_append_path (cairo_t *cr, const cairo_path_t *path)
{
    /* XXX no support for named paths, so manually reconstruct */
    int i;
    cairo_path_data_t *p;
    _enter_trace ();
    _emit_line_info ();
    if (cr == NULL || path == NULL) {
	DLCALL (cairo_append_path, cr, path);
	_exit_trace ();
	return;
    }
    for (i=0; i < path->num_data; i += path->data[i].header.length) {
	p = &path->data[i];
	switch (p->header.type) {
	case CAIRO_PATH_MOVE_TO:
	    if (p->header.length >= 2)
		cairo_move_to (cr, p[1].point.x, p[1].point.y);
	    break;
	case CAIRO_PATH_LINE_TO:
	    if (p->header.length >= 2)
		cairo_line_to (cr, p[1].point.x, p[1].point.y);
	    break;
	case CAIRO_PATH_CURVE_TO:
	    if (p->header.length >= 4)
		cairo_curve_to (cr,
				p[1].point.x, p[1].point.y,
				p[2].point.x, p[2].point.y,
				p[3].point.x, p[3].point.y);
	    break;
	case CAIRO_PATH_CLOSE_PATH:
	    if (p->header.length >= 1)
		cairo_close_path (cr);
	    break;
	default:
	    break;
	}
    }
    _exit_trace ();
}
cairo_surface_t *
cairo_image_surface_create (cairo_format_t format, int width, int height)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_image_surface_create, format, width, height);
    _emit_line_info ();
    if (_write_lock ()) {
	Object *obj = _create_surface (ret);
	const char *format_str = _format_to_string (format);
	const char *content_str = _format_to_content_string (format);
	_trace_printf ("dict\n"
		       "  /width %d set\n"
		       "  /height %d set\n"
		       "  /format //%s set\n"
		       "  /content //%s set\n"
		       "  image dup /s%ld exch def\n",
		       width, height, format_str, content_str, obj->token);
	obj->width = width;
	obj->height = height;
	obj->defined = TRUE;
	_push_object (obj);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
cairo_surface_t *
cairo_image_surface_create_for_data (unsigned char *data, cairo_format_t format, int width, int height, int stride)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_image_surface_create_for_data, data, format, width, height, stride);
    _emit_line_info ();
    if (_write_lock ()) {
	Object *obj = _create_surface (ret);
	/* cairo_image_surface_create_for_data() is both used to supply
	 * foreign pixel data to cairo and in order to read pixels back.
	 * Defer grabbing the pixel contents until we have to, but only for
	 * "large" images, for small images the overhead of embedding pixels
	 * is negligible.
	 *
	 * Choose 32x32 as that captures most icons which thanks to GdkPixbuf
	 * are frequently reloaded.
	 */
	if (width * height < 32*32) {
	    _emit_image (ret, NULL);
	    _trace_printf (" dup /s%ld exch def\n",
			   obj->token);
	} else {
	    _trace_printf ("dict\n"
			   "  /width %d set\n"
			   "  /height %d set\n"
			   "  /format //%s set\n"
			   "  image dup /s%ld exch def\n",
			   width, height,
			   _format_to_string (format),
			   obj->token);
	    obj->foreign = TRUE;
	}
	obj->width  = width;
	obj->height = height;
	obj->defined = TRUE;
	_push_object (obj);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
unsigned char *
cairo_image_surface_get_data (cairo_surface_t *surface)
{
    unsigned char *ptr;
    /* Just leave some breadcrumbs */
    _enter_trace ();
    _emit_line_info ();
    if (surface != NULL && _write_lock ()) {
	_trace_printf ("%% s%ld get-data\n", _get_surface_id (surface));
	_write_unlock ();
    }
    ptr = DLCALL (cairo_image_surface_get_data, surface);
    _exit_trace ();
    return ptr;
}
cairo_pattern_t *
cairo_pattern_create_raster_source (void *data, cairo_content_t content, int width, int height)
{
    cairo_pattern_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_pattern_create_raster_source, data, content, width, height);
    _emit_line_info ();
    if (_write_lock ()) {
	long pattern_id = _create_pattern_id (ret);
	cairo_format_t format;
	cairo_surface_t *image;
	cairo_t *cr;
	/* Impossible to accurately record the interaction with this custom
	 * pattern so just suck all the data into an image upfront */
	switch (content) {
	case CAIRO_CONTENT_ALPHA: format = CAIRO_FORMAT_A8; break;
	case CAIRO_CONTENT_COLOR: format = CAIRO_FORMAT_RGB24; break;
	default:
	case CAIRO_CONTENT_COLOR_ALPHA: format = CAIRO_FORMAT_ARGB32; break;
	}
	_trace_printf ("%% raster-source\n");
	image = DLCALL (cairo_image_surface_create, format, width, height);
	cr = DLCALL (cairo_create, image);
	DLCALL (cairo_set_source, cr, ret);
	DLCALL (cairo_paint, cr);
	DLCALL (cairo_destroy, cr);
	_emit_image (image, NULL);
	DLCALL (cairo_surface_destroy, image);
	_trace_printf (" pattern dup /s%ld exch def\n",
		       pattern_id);
	_push_operand (PATTERN, ret);
	_get_object (PATTERN, ret)->defined = TRUE;
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
cairo_surface_t *
cairo_surface_create_similar (cairo_surface_t *other,
			      cairo_content_t content,
			      int width, int height)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_surface_create_similar, other, content, width, height);
    _emit_line_info ();
    if (other != NULL && _write_lock ()) {
	Object *other_obj = _get_object(SURFACE, other);
	Object *new_obj = _create_surface (ret);
	if (other_obj->operand != -1) {
	    if (current_stack_depth == other_obj->operand + 1)
		_trace_printf ("dup ");
	    else
		_trace_printf ("%d index ",
			       current_stack_depth - other_obj->operand - 1);
	} else {
	    assert(other_obj->defined);
	    _trace_printf ("s%ld ", other_obj->token);
	}
	_trace_printf ("%d %d //%s similar dup /s%ld exch def\n",
		       width, height,
		       _content_to_string (content),
		       new_obj->token);
	new_obj->width = width;
	new_obj->height = height;
	new_obj->defined = TRUE;
	_push_object (new_obj);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
cairo_surface_t *
cairo_surface_create_similar_image (cairo_surface_t *other,
				    cairo_format_t format,
				    int width, int height)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_surface_create_similar_image,
		  other, format, width, height);
    _emit_line_info ();
    if (other != NULL && _write_lock ()) {
	Object *other_obj = _get_object(SURFACE, other);
	Object *new_obj = _create_surface (ret);
	if (other_obj->defined)
	    _trace_printf ("s%ld ", other_obj->token);
	else if (current_stack_depth == other_obj->operand + 1)
	    _trace_printf ("dup ");
	else
	    _trace_printf ("%d index ",
			   current_stack_depth - other_obj->operand - 1);
	_trace_printf ("//%s %d %d similar-image %% s%ld\n",
		       _format_to_string (format),
		       width, height,
		       new_obj->token);
	new_obj->width = width;
	new_obj->height = height;
	_push_object (new_obj);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
cairo_surface_t *
cairo_surface_map_to_image (cairo_surface_t *surface,
			    const cairo_rectangle_int_t *extents)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_surface_map_to_image, surface, extents);
    _emit_line_info ();
    if (_write_lock ()) {
	Object *obj = _create_surface (ret);
	_emit_surface (surface);
	if (extents) {
	    _trace_printf ("[%d %d %d %d] map-to-image %% s%ld\n",
			   extents->x, extents->y,
			   extents->width, extents->height,
			   obj->token);
	    obj->width  = extents->width;
	    obj->height = extents->height;
	} else {
	    _trace_printf ("[ ] map-to-image %% s%ld\n", obj->token);
	}
	_push_object (obj);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
void
cairo_surface_unmap_image (cairo_surface_t *surface,
			   cairo_surface_t *image)
{
    _enter_trace ();
    _emit_line_info ();
    if (_write_lock ()) {
	Object *s = _get_object (SURFACE, surface);
	Object *i = _get_object (SURFACE, image);
	if (!(s->operand == current_stack_depth - 2 &&
	      i->operand == current_stack_depth - 1)) {
	    if (i->operand != s->operand + 1 || ! _pop_operands_to_depth (i->operand + 1)) {
		_emit_surface (surface);
		_emit_surface (image);
	    }
	}
	_trace_printf ("unmap-image\n");
	_consume_operand (true);
	_write_unlock ();
    }
    DLCALL (cairo_surface_unmap_image, surface, image);
    _exit_trace ();
}
cairo_surface_t *
cairo_surface_create_for_rectangle (cairo_surface_t *target,
                                    double x, double y,
                                    double width, double height)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_surface_create_for_rectangle, target, x, y, width, height);
    _emit_line_info ();
    if (target != NULL && _write_lock ()) {
	Object *target_obj = _get_object (SURFACE, target);
	Object *child_obj = _create_surface (ret);
	if (target_obj->defined)
	    _trace_printf ("s%ld ", target_obj->token);
	else if (current_stack_depth == target_obj->operand + 1)
	    _trace_printf ("dup ");
	else
	    _trace_printf ("%d index ", current_stack_depth - target_obj->operand - 1);
	_trace_printf ("%f %f %f %f subsurface %% s%ld\n",
		       x, y, width, height,
		       child_obj->token);
	_push_object (child_obj);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
static void CAIRO_PRINTF_FORMAT(2, 3)
_emit_surface_op (cairo_surface_t *surface, const char *fmt, ...)
{
    va_list ap;
    if (surface == NULL || ! _write_lock ())
	return;
    _emit_surface (surface);
    va_start (ap, fmt);
    _trace_vprintf ( fmt, ap);
    va_end (ap);
    _write_unlock ();
}
void
cairo_surface_finish (cairo_surface_t *surface)
{
    _enter_trace ();
    _emit_line_info ();
    DLCALL (cairo_surface_finish, surface);
    _exit_trace ();
}
void
cairo_surface_flush (cairo_surface_t *surface)
{
    _enter_trace ();
    _emit_line_info ();
    if (surface != NULL && _write_lock ()) {
	_trace_printf ("%% s%ld flush\n", _get_surface_id (surface));
	_write_unlock ();
    }
    DLCALL (cairo_surface_flush, surface);
    _exit_trace ();
}
void
cairo_surface_mark_dirty (cairo_surface_t *surface)
{
    _enter_trace ();
    _emit_line_info ();
    /* Call cairo before emitting the trace since _emit_surface() might cause
     * snapshots to be creates while mark_dirty assert()s that there are none.
     */
    DLCALL (cairo_surface_mark_dirty, surface);
    if (surface != NULL && _write_lock ()) {
	if (_mark_dirty) {
	    _emit_surface (surface);
	    _trace_printf ("%% mark-dirty\n");
	    _emit_source_image (surface);
	} else
	    _trace_printf ("%% s%ld mark-dirty\n", _get_surface_id (surface));
	_write_unlock ();
    }
    _exit_trace ();
}
void
cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface,
				    int x, int y, int width, int height)
{
    _enter_trace ();
    /* Call cairo before emitting the trace since _emit_surface() might cause
     * snapshots to be creates while mark_dirty assert()s that there are none.
     */
    DLCALL (cairo_surface_mark_dirty_rectangle, surface, x, y, width, height);
    _emit_line_info ();
    if (surface != NULL && _write_lock ()) {
	if (_mark_dirty) {
	    _emit_surface (surface);
	    _trace_printf ("%% %d %d %d %d mark-dirty-rectangle\n",
		            x, y, width, height);
	    _emit_source_image_rectangle (surface, x,y, width, height);
	} else
	    _trace_printf ("%% s%ld %d %d %d %d mark-dirty-rectangle\n",
		           _get_surface_id (surface), x, y, width, height);
	_write_unlock ();
    }
    _exit_trace ();
}
void
cairo_surface_set_device_offset (cairo_surface_t *surface, double x_offset, double y_offset)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_surface_op (surface, "%g %g set-device-offset\n",
		      x_offset, y_offset);
    DLCALL (cairo_surface_set_device_offset, surface, x_offset, y_offset);
    _exit_trace ();
}
void
cairo_surface_set_device_scale (cairo_surface_t *surface, double x_offset, double y_offset)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_surface_op (surface, "%g %g set-device-scale\n",
		      x_offset, y_offset);
    DLCALL (cairo_surface_set_device_scale, surface, x_offset, y_offset);
    _exit_trace ();
}
void
cairo_surface_set_fallback_resolution (cairo_surface_t *surface, double x_pixels_per_inch, double y_pixels_per_inch)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_surface_op (surface, "%g %g set-fallback-resolution\n",
		      x_pixels_per_inch, y_pixels_per_inch);
    DLCALL (cairo_surface_set_fallback_resolution, surface, x_pixels_per_inch, y_pixels_per_inch);
    _exit_trace ();
}
void
cairo_surface_copy_page (cairo_surface_t *surface)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_surface_op (surface, "copy-page\n");
    DLCALL (cairo_surface_copy_page, surface);
    _exit_trace ();
}
void
cairo_surface_show_page (cairo_surface_t *surface)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_surface_op (surface, "show-page\n");
    DLCALL (cairo_surface_show_page, surface);
    _exit_trace ();
}
cairo_status_t
cairo_surface_set_mime_data (cairo_surface_t		*surface,
                             const char			*mime_type,
                             const unsigned char	*data,
                             unsigned long		 length,
			     cairo_destroy_func_t	 destroy,
			     void			*closure)
{
    cairo_status_t ret;
    _enter_trace ();
    _emit_line_info ();
    if (surface != NULL && _write_lock ()) {
	_emit_surface (surface);
	_emit_string_literal (mime_type, -1);
	_trace_printf (" ");
	_emit_data (data, length);
	_trace_printf (" /deflate filter set-mime-data\n");
	_write_unlock ();
    }
    ret = DLCALL (cairo_surface_set_mime_data,
		  surface,
		  mime_type,
		  data, length,
		  destroy,
		  closure);
    _exit_trace ();
    return ret;
}
#if CAIRO_HAS_PNG_FUNCTIONS
cairo_status_t
cairo_surface_write_to_png (cairo_surface_t *surface, const char *filename)
{
    cairo_status_t ret;
    _enter_trace ();
    _emit_line_info ();
    if (surface != NULL && _write_lock ()) {
	_trace_printf ("%% s%ld ", _get_surface_id (surface));
	_emit_string_literal (filename, -1);
	_trace_printf (" write-to-png pop\n");
	_write_unlock ();
    }
    ret = DLCALL (cairo_surface_write_to_png, surface, filename);
    _exit_trace ();
    return ret;
}
cairo_status_t
cairo_surface_write_to_png_stream (cairo_surface_t *surface,
				   cairo_write_func_t write_func,
				   void *data)
{
    cairo_status_t ret;
    _enter_trace ();
    _emit_line_info ();
    if (surface != NULL && _write_lock ()) {
	char symbol[1024];
	_trace_printf ("%% s%ld ", _get_surface_id (surface));
#if CAIRO_HAS_SYMBOL_LOOKUP
	lookup_symbol (symbol, sizeof (symbol), write_func);
#else
	symbol[0] = '\0';
#endif
	_emit_string_literal (symbol, -1);
	_trace_printf (" write-to-png-stream pop\n");
	_write_unlock ();
    }
    ret = DLCALL (cairo_surface_write_to_png_stream,
		  surface, write_func, data);
    _exit_trace ();
    return ret;
}
#endif
static void CAIRO_PRINTF_FORMAT(2, 3)
_emit_pattern_op (cairo_pattern_t *pattern, const char *fmt, ...)
{
    va_list ap;
    if (pattern == NULL || ! _write_lock ())
	return;
    _emit_pattern (pattern);
    va_start (ap, fmt);
    _trace_vprintf (fmt, ap);
    va_end (ap);
    _write_unlock ();
}
cairo_pattern_t *
cairo_pattern_create_rgb (double red, double green, double blue)
{
    cairo_pattern_t *ret;
    long pattern_id;
    _enter_trace ();
    ret = DLCALL (cairo_pattern_create_rgb, red, green, blue);
    pattern_id = _create_pattern_id (ret);
    _emit_line_info ();
    if (_write_lock ()) {
	_trace_printf ("/p%ld %g %g %g rgb def\n",
		       pattern_id, red, green, blue);
	_get_object (PATTERN, ret)->defined = TRUE;
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
cairo_pattern_t *
cairo_pattern_create_rgba (double red, double green, double blue, double alpha)
{
    cairo_pattern_t *ret;
    long pattern_id;
    _enter_trace ();
    ret = DLCALL (cairo_pattern_create_rgba, red, green, blue, alpha);
    pattern_id = _create_pattern_id (ret);
    _emit_line_info ();
    if (_write_lock ()) {
	_trace_printf ("/p%ld %g %g %g %g rgba def\n",
		       pattern_id, red, green, blue, alpha);
	_get_object (PATTERN, ret)->defined = TRUE;
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
cairo_pattern_t *
cairo_pattern_create_for_surface (cairo_surface_t *surface)
{
    cairo_pattern_t *ret;
    long pattern_id;
    long surface_id;
    _enter_trace ();
    ret = DLCALL (cairo_pattern_create_for_surface, surface);
    pattern_id = _create_pattern_id (ret);
    _emit_line_info ();
    if (surface != NULL && _write_lock ()) {
	surface_id = _get_surface_id (surface);
	if (_pop_operands_to (SURFACE, surface)) {
	    _consume_operand (false);
	} else {
	    _trace_printf ("s%ld ", surface_id);
	}
	if (_get_object (SURFACE, surface)->foreign)
	    _emit_source_image (surface);
	_trace_printf ("pattern %% p%ld\n", pattern_id);
	_push_operand (PATTERN, ret);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
cairo_pattern_t *
cairo_pattern_create_linear (double x0, double y0, double x1, double y1)
{
    cairo_pattern_t *ret;
    long pattern_id;
    _enter_trace ();
    ret = DLCALL (cairo_pattern_create_linear, x0, y0, x1, y1);
    pattern_id = _create_pattern_id (ret);
    _emit_line_info ();
    if (_write_lock ()) {
	_trace_printf ("%g %g %g %g linear %% p%ld\n",
		       x0, y0, x1, y1, pattern_id);
	_push_operand (PATTERN, ret);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
cairo_pattern_t *
cairo_pattern_create_radial (double cx0, double cy0, double radius0, double cx1, double cy1, double radius1)
{
    cairo_pattern_t *ret;
    long pattern_id;
    _enter_trace ();
    ret = DLCALL (cairo_pattern_create_radial,
		  cx0, cy0, radius0,
		  cx1, cy1, radius1);
    pattern_id = _create_pattern_id (ret);
    _emit_line_info ();
    if (_write_lock ()) {
	_trace_printf ("%g %g %g %g %g %g radial %% p%ld\n",
		       cx0, cy0, radius0, cx1, cy1, radius1,
		       pattern_id);
	_push_operand (PATTERN, ret);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
void
cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern, double offset, double red, double green, double blue)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_pattern_op (pattern,
		      "%g %g %g %g 1 add-color-stop\n",
		      offset, red, green, blue);
    DLCALL (cairo_pattern_add_color_stop_rgb, pattern, offset, red, green, blue);
    _exit_trace ();
}
void
cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern, double offset, double red, double green, double blue, double alpha)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_pattern_op (pattern,
		      "%g %g %g %g %g add-color-stop\n",
		      offset, red, green, blue, alpha);
    DLCALL (cairo_pattern_add_color_stop_rgba, pattern, offset, red, green, blue, alpha);
    _exit_trace ();
}
void
cairo_pattern_set_matrix (cairo_pattern_t *pattern, const cairo_matrix_t *matrix)
{
    _enter_trace ();
    _emit_line_info ();
    if (_matrix_is_identity (matrix)) {
	_emit_pattern_op (pattern, "identity set-matrix\n");
    } else {
	_emit_pattern_op (pattern,
			  "%g %g %g %g %g %g matrix set-matrix\n",
			  matrix->xx, matrix->yx,
			  matrix->xy, matrix->yy,
			  matrix->x0, matrix->y0);
    }
    DLCALL (cairo_pattern_set_matrix, pattern, matrix);
    _exit_trace ();
}
static const char *
_filter_to_string (cairo_filter_t filter)
{
#define f(name) case CAIRO_FILTER_ ## name: return "FILTER_" #name
    switch (filter) {
	f(FAST);
	f(GOOD);
	f(BEST);
	f(NEAREST);
	f(BILINEAR);
	f(GAUSSIAN);
    };
#undef f
    return "UNKNOWN_FILTER";
}
void
cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_pattern_op (pattern, "//%s set-filter\n", _filter_to_string (filter));
    DLCALL (cairo_pattern_set_filter, pattern, filter);
    _exit_trace ();
}
static const char *
_extend_to_string (cairo_extend_t extend)
{
#define f(name) case CAIRO_EXTEND_ ## name: return "EXTEND_" #name
    switch (extend) {
	f(NONE);
	f(REPEAT);
	f(REFLECT);
	f(PAD);
    };
#undef f
    return "UNKNOWN_EXTEND";
}
void
cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend)
{
    _enter_trace ();
    _emit_line_info ();
    _emit_pattern_op (pattern, "//%s set-extend\n", _extend_to_string (extend));
    DLCALL (cairo_pattern_set_extend, pattern, extend);
    _exit_trace ();
}
#if CAIRO_HAS_FT_FONT
#if CAIRO_HAS_FC_FONT
cairo_font_face_t *
cairo_ft_font_face_create_for_pattern (FcPattern *pattern)
{
    cairo_font_face_t *ret;
    long font_face_id;
    _enter_trace ();
    ret = DLCALL (cairo_ft_font_face_create_for_pattern, pattern);
    font_face_id = _create_font_face_id (ret);
    _emit_line_info ();
    if (pattern != NULL && _write_lock ()) {
	Object *obj;
	obj = _get_object (FONT_FACE, ret);
	if (obj->unknown) {
		FcPattern *copy;
		FcChar8 *unparsed;
		copy = DLCALL (FcPatternDuplicate, pattern);
		if (copy)
		{
			DLCALL (FcPatternDel, copy, FC_LANG);
			DLCALL (FcPatternDel, copy, FC_CHARSET);
			DLCALL (FcPatternDel, copy, FC_CAPABILITY);
		}
		else
			copy = pattern;
		unparsed = DLCALL (FcNameUnparse, copy);
		_trace_printf ("dict\n"
			       "  /type 42 set\n"
			       "  /pattern ");
		_emit_string_literal ((char *) unparsed, -1);
		_trace_printf (" set\n"
			       "  font %% f%ld\n",
			       font_face_id);
		obj->unknown = FALSE;
		_push_operand (FONT_FACE, ret);
		dump_stack(__func__);
		if (copy != pattern)
			DLCALL (FcPatternDestroy, copy);
		free (unparsed);
	}
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
#endif /* CAIRO_HAS_FC_FONT*/
typedef struct _ft_face_data {
    unsigned long index;
    unsigned long size;
    void *data;
} FtFaceData;
static void
_ft_face_data_destroy (void *arg)
{
    FtFaceData *data = arg;
    free (data->data);
    free (data);
}
cairo_font_face_t *
cairo_ft_font_face_create_for_ft_face (FT_Face face, int load_flags)
{
    cairo_font_face_t *ret;
    Object *obj;
    FtFaceData *data;
    long font_face_id;
    _enter_trace ();
    ret = DLCALL (cairo_ft_font_face_create_for_ft_face, face, load_flags);
    font_face_id = _create_font_face_id (ret);
    if (face == NULL) {
	_exit_trace ();
	return ret;
    }
    obj = _get_object (NONE, face);
    data = obj->data;
    if (data == NULL) {
	_exit_trace ();
	return ret;
    }
    _emit_line_info ();
    if (_write_lock ()) {
	obj = _get_object (FONT_FACE, ret);
	if (obj->operand != -1)
	    _object_remove (obj);
	_trace_printf ("<< /type 42 /source ");
	_emit_data (data->data, data->size);
	_trace_printf (" /index %lu /flags %d >> font %% f%ld\n",
		       data->index, load_flags, font_face_id);
	_push_operand (FONT_FACE, ret);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
static cairo_bool_t
_ft_read_file (FtFaceData *data, const char *path)
{
    char buf[8192];
    FILE *file;
    file = fopen (path, "rb");
    if (file != NULL) {
	size_t ret;
	unsigned long int allocated = sizeof (buf);
	data->data = malloc (allocated);
	do {
	    ret = fread (buf, 1, sizeof (buf), file);
	    if (ret == 0)
		break;
	    memcpy ((char *) data->data + data->size, buf, ret);
	    data->size += ret;
	    if (ret != sizeof (buf))
		break;
	    if (data->size == allocated) {
		allocated *= 2;
		data->data = realloc (data->data, allocated);
	    }
	} while (TRUE);
	fclose (file);
    }
    return file != NULL;
}
FT_Error
FT_New_Face (FT_Library library, const char *pathname, FT_Long index, FT_Face *face)
{
    FT_Error ret;
    _enter_trace ();
    ret = DLCALL (FT_New_Face, library, pathname, index, face);
    if (ret == 0) {
	Object *obj = _type_object_create (NONE, *face);
	FtFaceData *data = malloc (sizeof (FtFaceData));
	data->index = index;
	data->size = 0;
	data->data = NULL;
	_ft_read_file (data, pathname);
	obj->data = data;
	obj->destroy = _ft_face_data_destroy;
    }
    _exit_trace ();
    return ret;
}
FT_Error
FT_New_Memory_Face (FT_Library library, const FT_Byte *mem, FT_Long size, FT_Long index, FT_Face *face)
{
    FT_Error ret;
    _enter_trace ();
    ret = DLCALL (FT_New_Memory_Face, library, mem, size, index, face);
    if (ret == 0) {
	Object *obj = _type_object_create (NONE, *face);
	FtFaceData *data = malloc (sizeof (FtFaceData));
	data->index = index;
	data->size = size;
	data->data = malloc (size);
	memcpy (data->data, mem, size);
	obj->data = data;
	obj->destroy = _ft_face_data_destroy;
    }
    _exit_trace ();
    return ret;
}
/* XXX
 * FT_New_Memory_Face() and FT_New_Face() appear to wrap FT_Open_Face() so we
 * get a redundant call to FT_Open_Face() from those paths (no PLT hiding
 * within FT, naughty library!) but we do not intercept a direct call to
 * FT_Open_Face(). So far this has not caused any issues, but it will one
 * day...
 */
FT_Error
FT_Open_Face (FT_Library library, const FT_Open_Args *args, FT_Long index, FT_Face *face)
{
    FT_Error ret;
    _enter_trace ();
    ret = DLCALL (FT_Open_Face, library, args, index, face);
    if (ret == 0) {
	Object *obj = _get_object (NONE, *face);
	if (obj == NULL) {
	    FtFaceData *data;
	    data = malloc (sizeof (FtFaceData));
	    data->index = index;
	    if (args->flags & FT_OPEN_MEMORY) {
		data->size = args->memory_size;
		data->data = malloc (args->memory_size);
		memcpy (data->data, args->memory_base, args->memory_size);
	    } else if (args->flags & FT_OPEN_STREAM) {
		fprintf (stderr, "FT_Open_Face (stream, %ld) = %p\n",
			 index, *face);
		abort ();
	    } else if (args->flags & FT_OPEN_PATHNAME) {
		data->size = 0;
		data->data = NULL;
		_ft_read_file (data, args->pathname);
	    }
	    obj = _type_object_create (NONE, *face);
	    obj->data = data;
	    obj->destroy = _ft_face_data_destroy;
	}
    }
    _exit_trace ();
    return ret;
}
FT_Error
FT_Done_Face (FT_Face face)
{
    FT_Error ret;
    _enter_trace ();
    _object_destroy (_get_object (NONE, face));
    ret = DLCALL (FT_Done_Face, face);
    _exit_trace ();
    return ret;
}
#endif
static void
_surface_object_set_size (cairo_surface_t *surface, int width, int height)
{
    Object *obj;
    obj = _get_object (SURFACE, surface);
    obj->width = width;
    obj->height = height;
}
static void
_surface_object_set_size_from_surface (cairo_surface_t *surface)
{
    _surface_object_set_size (surface,
			      DLCALL (cairo_image_surface_get_width, surface),
			      DLCALL (cairo_image_surface_get_height, surface));
}
#if CAIRO_HAS_PS_SURFACE
#include<cairo-ps.h>
cairo_surface_t *
cairo_ps_surface_create (const char *filename, double width_in_points, double height_in_points)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_ps_surface_create, filename, width_in_points, height_in_points);
    _emit_line_info ();
    if (_write_lock ()) {
	Object *obj = _create_surface (ret);
	_trace_printf ("dict\n"
		       "  /type /PS set\n"
		       "  /filename ");
	_emit_string_literal (filename, -1);
	_trace_printf (" set\n"
		       "  /width %g set\n"
		       "  /height %g set\n"
		       "  surface %% s%ld\n",
		       width_in_points,
		       height_in_points,
		       obj->token);
	obj->width = width_in_points;
	obj->height = height_in_points;
	_push_object (obj);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
cairo_surface_t *
cairo_ps_surface_create_for_stream (cairo_write_func_t write_func, void *closure, double width_in_points, double height_in_points)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_ps_surface_create_for_stream, write_func, closure, width_in_points, height_in_points);
    _emit_line_info ();
    if (_write_lock ()) {
	Object *obj = _create_surface (ret);
	_trace_printf ("dict\n"
		       "  /type /PS set\n"
		       "  /width %g set\n"
		       "  /height %g set\n"
		       "  surface %% s%ld\n",
		       width_in_points,
		       height_in_points,
		       obj->token);
	obj->width = width_in_points;
	obj->height = height_in_points;
	_push_object (obj);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
void
cairo_ps_surface_set_size (cairo_surface_t *surface, double width_in_points, double height_in_points)
{
    _enter_trace ();
    _emit_line_info ();
    DLCALL (cairo_ps_surface_set_size, surface, width_in_points, height_in_points);
    _exit_trace ();
}
#endif
#if CAIRO_HAS_PDF_SURFACE
#include <cairo-pdf.h>
cairo_surface_t *
cairo_pdf_surface_create (const char *filename, double width_in_points, double height_in_points)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_pdf_surface_create, filename, width_in_points, height_in_points);
    _emit_line_info ();
    if (_write_lock ()) {
	Object *obj = _create_surface (ret);
	_trace_printf ("dict\n"
		       "  /type /PDF set\n"
		       "  /filename ");
	_emit_string_literal (filename, -1);
	_trace_printf (" set\n"
		       "  /width %g set\n"
		       "  /height %g set\n"
		       "  surface %% s%ld\n",
		       width_in_points,
		       height_in_points,
		       obj->token);
	obj->width = width_in_points;
	obj->height = height_in_points;
	_push_object (obj);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
cairo_surface_t *
cairo_pdf_surface_create_for_stream (cairo_write_func_t write_func, void *closure, double width_in_points, double height_in_points)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_pdf_surface_create_for_stream, write_func, closure, width_in_points, height_in_points);
    _emit_line_info ();
    if (_write_lock ()) {
	Object *obj = _create_surface (ret);
	_trace_printf ("dict\n"
		       "  /type /PDF set\n"
		       "  /width %g set\n"
		       "  /height %g set\n"
		       "  surface %% s%ld\n",
		       width_in_points,
		       height_in_points,
		       obj->token);
	obj->width = width_in_points;
	obj->height = height_in_points;
	_push_object (obj);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
void
cairo_pdf_surface_set_size (cairo_surface_t *surface, double width_in_points, double height_in_points)
{
    _enter_trace ();
    _emit_line_info ();
    DLCALL (cairo_pdf_surface_set_size, surface, width_in_points, height_in_points);
    _exit_trace ();
}
#endif
#if CAIRO_HAS_SVG_SURFACE
#include <cairo-svg.h>
cairo_surface_t *
cairo_svg_surface_create (const char *filename, double width, double height)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_svg_surface_create, filename, width, height);
    _emit_line_info ();
    if (_write_lock ()) {
	Object *obj = _create_surface (ret);
	_trace_printf ("dict\n"
		       "  /type /SVG set\n"
		       "  /filename ");
	_emit_string_literal (filename, -1);
	_trace_printf (" set\n"
		       "  /width %g set\n"
		       "  /height %g set\n"
		       "  surface %% s%ld\n",
		       width,
		       height,
		       obj->token);
	obj->width = width;
	obj->height = height;
	_push_object (obj);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
cairo_surface_t *
cairo_svg_surface_create_for_stream (cairo_write_func_t write_func, void *closure, double width, double height)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_svg_surface_create_for_stream, write_func, closure, width, height);
    _emit_line_info ();
    if (_write_lock ()) {
	Object *obj = _create_surface (ret);
	_trace_printf ("dict\n"
		       "  /type /SVG set\n"
		       "  /width %g set\n"
		       "  /height %g set\n"
		       "  surface %% s%ld\n",
		       width,
		       height,
		       obj->token);
	obj->width = width;
	obj->height = height;
	_push_object (obj);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
#endif
#if CAIRO_HAS_PNG_FUNCTIONS
cairo_surface_t *
cairo_image_surface_create_from_png (const char *filename)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_image_surface_create_from_png, filename);
    _emit_line_info ();
    if (_write_lock ()) {
	Object *obj = _create_surface (ret);
	char filename_string[4096];
	_encode_string_literal (filename_string, sizeof (filename_string),
				filename, -1);
	_emit_image (ret, "  /filename %s set\n", filename_string);
	_trace_printf (" dup /s%ld exch def\n", obj->token);
	_surface_object_set_size_from_surface (ret);
	obj->defined = TRUE;
	_push_object (obj);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
cairo_surface_t *
cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func, void *closure)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_image_surface_create_from_png_stream, read_func, closure);
    _emit_line_info ();
    if (_write_lock ()) {
	Object *obj = _create_surface (ret);
	_emit_image (ret, NULL);
	_trace_printf (" dup /s%ld exch def\n",
		       obj->token);
	_surface_object_set_size_from_surface (ret);
	obj->defined = TRUE;
	_push_object (obj);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
#endif
#if CAIRO_HAS_TEE_SURFACE
#include <cairo-tee.h>
cairo_surface_t *
cairo_tee_surface_create (cairo_surface_t *master)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_tee_surface_create, master);
    _emit_line_info ();
    if (_write_lock ()) {
	Object *obj = _create_surface (ret);
	_trace_printf ("dict\n"
		       "  /type /tee set\n"
		       "  /master s%ld set\n"
		       "  surface dup /s%ld exch def\n",
		       _get_object (SURFACE, master)->token,
		       obj->token);
	obj->defined = TRUE;
	_push_object (obj);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
#endif
#if CAIRO_HAS_XLIB_SURFACE
#include <cairo-xlib.h>
static const char *
_content_from_surface (cairo_surface_t *surface)
{
    return _content_to_string (DLCALL (cairo_surface_get_content, surface));
}
cairo_surface_t *
cairo_xlib_surface_create (Display *dpy,
			   Drawable drawable,
			   Visual *visual,
			   int width, int height)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_xlib_surface_create,
	          dpy, drawable, visual, width, height);
    _emit_line_info ();
    if (_write_lock ()) {
	Object *obj = _create_surface (ret);
	_trace_printf ("dict\n"
		       "  /type /xlib set\n"
		       "  /drawable 16!%lx set\n"
		       "  /content //%s set\n"
		       "  /width %d set\n"
		       "  /height %d set\n"
		       "  surface dup /s%ld exch def\n",
		       drawable,
		       _content_from_surface (ret),
		       width, height,
		       obj->token);
	obj->defined = TRUE;
	obj->width = width;
	obj->height = height;
	obj->foreign = TRUE;
	_push_object (obj);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
cairo_surface_t *
cairo_xlib_surface_create_for_bitmap (Display *dpy,
				      Pixmap bitmap,
				      Screen *screen,
				      int width, int height)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_xlib_surface_create_for_bitmap,
	          dpy, bitmap, screen, width, height);
    _emit_line_info ();
    if (_write_lock ()) {
	Object *obj = _create_surface (ret);
	_trace_printf ("dict\n"
		       "  /type /xlib set\n"
		       "  /drawable 16!%lx set\n"
		       "  /content //%s set\n"
		       "  /width %d set\n"
		       "  /height %d set\n"
		       "  /depth 1 set\n"
		       "  surface dup /s%ld exch def\n",
		       bitmap,
		       _content_from_surface (ret),
		       width, height,
		       obj->token);
	obj->defined = TRUE;
	obj->width = width;
	obj->height = height;
	obj->foreign = TRUE;
	_push_object (obj);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
#if CAIRO_HAS_XLIB_XRENDER_SURFACE
#include <cairo-xlib-xrender.h>
cairo_surface_t *
cairo_xlib_surface_create_with_xrender_format (Display *dpy,
					       Drawable drawable,
					       Screen *screen,
					       XRenderPictFormat *format,
					       int width, int height)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_xlib_surface_create_with_xrender_format,
	          dpy, drawable, screen, format, width, height);
    _emit_line_info ();
    if (_write_lock ()) {
	Object *obj = _create_surface (ret);
	_trace_printf ("dict\n"
		       "  /type /xrender set\n"
		       "  /drawable 16!%lx set\n"
		       "  /content //%s set\n"
		       "  /width %d set\n"
		       "  /height %d set\n"
		       "  /depth %d set\n"
		       "  surface dup /s%ld exch def\n",
		       drawable,
		       _content_from_surface (ret),
		       width, height,
		       format->depth,
		       obj->token);
	obj->defined = TRUE;
	obj->width = width;
	obj->height = height;
	obj->foreign = TRUE;
	_push_object (obj);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
#endif
#endif
#if CAIRO_HAS_SCRIPT_SURFACE
#include <cairo-script.h>
cairo_surface_t *
cairo_script_surface_create (cairo_device_t *device,
			     cairo_content_t content,
			     double width,
			     double height)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_script_surface_create, device, content, width, height);
    _emit_line_info ();
    if (_write_lock ()) {
	Object *obj = _create_surface (ret);
	_trace_printf ("dict\n"
		       "  /type /script set\n"
		       "  /content %s set\n"
		       "  /width %g set\n"
		       "  /height %g set\n"
		       "  surface dup /s%ld exch def\n",
		       _content_to_string (content),
		       width, height,
		       obj->token);
	obj->width = width;
	obj->height = height;
	obj->defined = TRUE;
	_push_object (obj);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
cairo_surface_t *
cairo_script_surface_create_for_target (cairo_device_t *device,
					cairo_surface_t *target)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_script_surface_create_for_target, device, target);
    _emit_line_info ();
    if (_write_lock ()) {
	Object *obj = _create_surface (ret);
	_trace_printf ("dict\n"
		       "  /type /script set\n"
		       "  surface dup /s%ld exch def\n",
		       obj->token);
	obj->defined = TRUE;
	_push_object (obj);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
#endif
#if CAIRO_HAS_TEST_SURFACES
#include <test-paginated-surface.h>
cairo_surface_t *
_cairo_test_paginated_surface_create (cairo_surface_t *surface)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (_cairo_test_paginated_surface_create, surface);
    _emit_line_info ();
    if (_write_lock ()) {
	Object *obj = _create_surface (ret);
	/* XXX store initial data? */
	_trace_printf ("dict\n"
		       "  /type /test-paginated set\n"
		       "  /target s%ld set\n"
		       "  surface dup /s%ld exch def\n",
		       _get_surface_id (surface),
		       obj->token);
	_push_object (obj);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
#include <test-compositor-surface.h>
cairo_surface_t *
_cairo_test_fallback_compositor_surface_create (cairo_content_t content, int width, int height)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (_cairo_test_fallback_compositor_surface_create, content, width, height);
    _emit_line_info ();
    if (_write_lock ()) {
	Object *obj = _create_surface (ret);
	_trace_printf ("dict\n"
		       "  /type /test-fallback-compositor set\n"
		       "  /content //%s set\n"
		       "  /width %d set\n"
		       "  /height %d set\n"
		       "  surface dup /s%ld exch def\n",
		       _content_to_string (content),
		       width, height,
		       obj->token);
	obj->defined = TRUE;
	_push_object (obj);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
cairo_surface_t *
_cairo_test_mask_compositor_surface_create (cairo_content_t content, int width, int height)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (_cairo_test_mask_compositor_surface_create, content, width, height);
    _emit_line_info ();
    if (_write_lock ()) {
	Object *obj = _create_surface (ret);
	_trace_printf ("dict\n"
		       "  /type /test-mask-compositor set\n"
		       "  /content //%s set\n"
		       "  /width %d set\n"
		       "  /height %d set\n"
		       "  surface dup /s%ld exch def\n",
		       _content_to_string (content),
		       width, height,
		       obj->token);
	obj->defined = TRUE;
	_push_object (obj);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
cairo_surface_t *
_cairo_test_spans_compositor_surface_create (cairo_content_t content, int width, int height)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (_cairo_test_spans_compositor_surface_create, content, width, height);
    _emit_line_info ();
    if (_write_lock ()) {
	Object *obj = _create_surface (ret);
	_trace_printf ("dict\n"
		       "  /type /test-spans-compositor set\n"
		       "  /content //%s set\n"
		       "  /width %d set\n"
		       "  /height %d set\n"
		       "  surface dup /s%ld exch def\n",
		       _content_to_string (content),
		       width, height,
		       obj->token);
	obj->defined = TRUE;
	_push_object (obj);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
cairo_surface_t *
_cairo_test_traps_compositor_surface_create (cairo_content_t content, int width, int height)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (_cairo_test_traps_compositor_surface_create, content, width, height);
    _emit_line_info ();
    if (_write_lock ()) {
	Object *obj = _create_surface (ret);
	_trace_printf ("dict\n"
		       "  /type /test-traps-compositor set\n"
		       "  /content //%s set\n"
		       "  /width %d set\n"
		       "  /height %d set\n"
		       "  surface dup /s%ld exch def\n",
		       _content_to_string (content),
		       width, height,
		       obj->token);
	obj->defined = TRUE;
	_push_object (obj);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}
#endif
cairo_surface_t *
cairo_recording_surface_create (cairo_content_t content,
				const cairo_rectangle_t *extents)
{
    cairo_surface_t *ret;
    _enter_trace ();
    ret = DLCALL (cairo_recording_surface_create, content, extents);
    _emit_line_info ();
    if (_write_lock ()) {
	Object *obj = _create_surface (ret);
	if (extents) {
	    _trace_printf ("//%s [ %f %f %f %f ] record dup /s%ld exch def\n",
			   _content_to_string (content),
			   extents->x, extents->y,
			   extents->width, extents->height,
			   obj->token);
	    obj->width = extents->width;
	    obj->height = extents->height;
	} else {
	    _trace_printf ("//%s [ ] record dup /s%ld exch def\n",
			   _content_to_string (content),
			   obj->token);
	}
	obj->defined = TRUE;
	_push_object (obj);
	dump_stack(__func__);
	_write_unlock ();
    }
    _exit_trace ();
    return ret;
}