tizen 2.3.1 release
[framework/graphics/cairo.git] / util / cairo-script / cairo-script-operators.c
1 /*
2  * Copyright © 2008 Chris Wilson <chris@chris-wilson.co.uk>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it either under the terms of the GNU Lesser General Public
6  * License version 2.1 as published by the Free Software Foundation
7  * (the "LGPL") or, at your option, under the terms of the Mozilla
8  * Public License Version 1.1 (the "MPL"). If you do not alter this
9  * notice, a recipient may use your version of this file under either
10  * the MPL or the LGPL.
11  *
12  * You should have received a copy of the LGPL along with this library
13  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
15  * You should have received a copy of the MPL along with this library
16  * in the file COPYING-MPL-1.1
17  *
18  * The contents of this file are subject to the Mozilla Public License
19  * Version 1.1 (the "License"); you may not use this file except in
20  * compliance with the License. You may obtain a copy of the License at
21  * http://www.mozilla.org/MPL/
22  *
23  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
24  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
25  * the specific language governing rights and limitations.
26  *
27  * The Original Code is the cairo graphics library.
28  *
29  * The Initial Developer of the Original Code is Chris Wilson.
30  *
31  * Contributor(s):
32  *      Chris Wilson <chris@chris-wilson.co.uk>
33  */
34
35 /* TODO real path type */
36
37 #include "cairo-script-private.h"
38
39 #if CAIRO_HAS_SCRIPT_SURFACE
40 #include "cairo-script.h"
41 #endif
42
43 #include <stdio.h> /* snprintf */
44 #include <stdlib.h> /* mkstemp */
45 #include <string.h>
46
47 #ifdef _MSC_VER
48 #define _USE_MATH_DEFINES /* for M_LN2, M_PI and M_SQRT2 on win32 */
49 #define snprintf _snprintf
50 #endif
51
52 #include <math.h>
53 #include <limits.h> /* INT_MAX */
54 #include <assert.h>
55
56 #if HAVE_ZLIB
57 #include <zlib.h>
58 #endif
59
60 #if HAVE_LZO
61 #include <lzo/lzo2a.h>
62 #endif
63
64 #ifdef HAVE_MMAP
65 # ifdef HAVE_UNISTD_H
66 #  include <sys/mman.h>
67 #  include <unistd.h>
68 # else
69 #  undef HAVE_MMAP
70 # endif
71 #endif
72
73 typedef struct _csi_proxy {
74     csi_t *ctx;
75     void *ptr;
76     csi_dictionary_t *dictionary;
77     csi_destroy_func_t destroy_func;
78     void *destroy_data;
79 } csi_proxy_t;
80
81 typedef struct _csi_blob {
82     csi_list_t list;
83     unsigned long hash;
84     uint8_t *bytes;
85     unsigned int len;
86 } csi_blob_t;
87
88 static const cairo_user_data_key_t _csi_proxy_key;
89 static const cairo_user_data_key_t _csi_blob_key;
90
91 enum mime_type {
92     MIME_TYPE_NONE = 0,
93     MIME_TYPE_PNG
94 };
95
96 #define check(CNT) do {\
97     if (_csi_unlikely (! _csi_check_ostack (ctx, (CNT)))) \
98         return _csi_error (CSI_STATUS_INVALID_SCRIPT); \
99 } while (0)
100 #define pop(CNT) _csi_pop_ostack (ctx, (CNT))
101 #define push(OBJ) _csi_push_ostack (ctx, (OBJ))
102
103 static csi_proxy_t *
104 _csi_proxy_create (csi_t *ctx,
105                    void *ptr,
106                    csi_dictionary_t *dictionary,
107                    csi_destroy_func_t destroy_func,
108                    void *destroy_data)
109 {
110     csi_proxy_t *proxy;
111
112     proxy = _csi_slab_alloc (ctx, sizeof (csi_proxy_t));
113     if (proxy == NULL)
114         return NULL;
115
116     proxy->ctx = cairo_script_interpreter_reference (ctx);
117     proxy->ptr = ptr;
118     proxy->destroy_func = destroy_func;
119     proxy->destroy_data = destroy_data;
120     proxy->dictionary = dictionary;
121     if (dictionary != NULL)
122         dictionary->base.ref++;
123
124     return proxy;
125 }
126
127 static void
128 _csi_proxy_destroy (void *closure)
129 {
130     csi_proxy_t *proxy = closure;
131     csi_t *ctx = proxy->ctx;
132
133     /* XXX this doesn't work because user_data_destroy is called too late.
134      * Considering another hook into the (cairo internal) object system.
135      */
136     if (proxy->destroy_func != NULL)
137         proxy->destroy_func (proxy->destroy_data, proxy->ptr);
138
139     if (proxy->dictionary != NULL && --proxy->dictionary->base.ref == 0)
140         csi_dictionary_free (ctx, proxy->dictionary);
141
142     _csi_slab_free (ctx, proxy, sizeof (csi_proxy_t));
143     cairo_script_interpreter_destroy (ctx);
144 }
145
146 static void
147 _csi_blob_hash (csi_blob_t *blob, const uint32_t *data, int len)
148 {
149     unsigned long hash = blob->hash;
150     /* very simple! */
151     while (len--) {
152         unsigned long c = *data++;
153         hash *= 33;
154         hash ^= c;
155     }
156     blob->hash = hash;
157 }
158
159 static csi_boolean_t
160 _csi_blob_equal (const csi_list_t *link, void *data)
161 {
162     csi_blob_t *A, *B;
163
164     A = csi_container_of (link, csi_blob_t, list);
165     B = data;
166
167     if (A->len != B->len)
168         return FALSE;
169
170     if (A->hash != B->hash)
171         return FALSE;
172
173     return memcmp (A->bytes, B->bytes, A->len) == 0;
174 }
175
176 static void
177 _csi_blob_init (csi_blob_t *blob, uint8_t *bytes, int len)
178 {
179     blob->hash = 5381;
180     blob->len = len;
181     blob->bytes = bytes;
182 }
183
184 static csi_list_t *
185 _csi_list_unlink (csi_list_t *head, csi_list_t *link)
186 {
187     if (link->next != NULL)
188         link->next->prev = link->prev;
189     if (link->prev != NULL)
190         link->prev->next = link->next;
191     else
192         head = link->next;
193     return head;
194 }
195
196 static csi_list_t *
197 _csi_list_prepend (csi_list_t *head, csi_list_t *link)
198 {
199     if (head != NULL)
200         head->prev = link;
201     link->next = head;
202     link->prev = NULL;
203     return link;
204 }
205
206 static csi_list_t *
207 _csi_list_find (csi_list_t *head,
208                 csi_boolean_t (*predicate) (const csi_list_t *link, void *data),
209                 void *data)
210 {
211     while (head != NULL) {
212         if (predicate (head, data))
213             return head;
214         head = head->next;
215     }
216
217     return NULL;
218 }
219
220 static csi_status_t
221 _csi_ostack_get_boolean (csi_t *ctx, unsigned int i, csi_boolean_t *out)
222 {
223     csi_object_t *obj;
224     int type;
225
226     obj = _csi_peek_ostack (ctx, i);
227     type = csi_object_get_type (obj);
228     switch (type) {
229     case CSI_OBJECT_TYPE_BOOLEAN:
230         *out = obj->datum.boolean;
231         break;
232     case CSI_OBJECT_TYPE_INTEGER:
233         *out = !! obj->datum.integer;
234         break;
235     case CSI_OBJECT_TYPE_REAL:
236         *out = obj->datum.real != 0.;
237         break;
238     default:
239         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
240     }
241     return CSI_STATUS_SUCCESS;
242 }
243
244 static csi_status_t
245 _csi_ostack_get_integer (csi_t *ctx, unsigned int i, csi_integer_t *out)
246 {
247     csi_object_t *obj;
248     int type;
249
250     obj = _csi_peek_ostack (ctx, i);
251     type = csi_object_get_type (obj);
252     switch (type) {
253     case CSI_OBJECT_TYPE_BOOLEAN:
254         *out = obj->datum.boolean;
255         break;
256     case CSI_OBJECT_TYPE_INTEGER:
257         *out = obj->datum.integer;
258         break;
259     case CSI_OBJECT_TYPE_REAL:
260         *out = obj->datum.real;
261         break;
262     default:
263         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
264     }
265     return CSI_STATUS_SUCCESS;
266 }
267
268 static csi_status_t
269 _csi_ostack_get_number (csi_t *ctx, unsigned int i, double *out)
270 {
271     csi_object_t *obj;
272     int type;
273
274     obj = _csi_peek_ostack (ctx, i);
275     type = csi_object_get_type (obj);
276     switch (type) {
277     case CSI_OBJECT_TYPE_BOOLEAN:
278         *out = obj->datum.boolean;
279         break;
280     case CSI_OBJECT_TYPE_INTEGER:
281         *out = obj->datum.integer;
282         break;
283     case CSI_OBJECT_TYPE_REAL:
284         *out = obj->datum.real;
285         break;
286     default:
287         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
288     }
289     return CSI_STATUS_SUCCESS;
290 }
291
292 static double
293 _csi_object_as_real (csi_object_t *obj)
294 {
295     int type;
296
297     type = csi_object_get_type (obj);
298     switch (type) {
299     case CSI_OBJECT_TYPE_BOOLEAN:
300         return obj->datum.boolean;
301     case CSI_OBJECT_TYPE_INTEGER:
302         return obj->datum.integer;
303     case CSI_OBJECT_TYPE_REAL:
304         return obj->datum.real;
305     default:
306         return 0;
307     }
308 }
309
310 static csi_status_t
311 _csi_ostack_get_name (csi_t *ctx, unsigned int i, csi_name_t *out)
312 {
313     csi_object_t *obj;
314
315     obj = _csi_peek_ostack (ctx, i);
316     if (_csi_unlikely (csi_object_get_type (obj) != CSI_OBJECT_TYPE_NAME))
317         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
318
319     *out = obj->datum.name;
320     return CSI_STATUS_SUCCESS;
321 }
322
323 static csi_status_t
324 _csi_ostack_get_context (csi_t *ctx, unsigned int i, cairo_t **out)
325 {
326     csi_object_t *obj;
327
328     obj = _csi_peek_ostack (ctx, i);
329     if (_csi_unlikely (csi_object_get_type (obj) != CSI_OBJECT_TYPE_CONTEXT))
330         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
331
332     *out = obj->datum.cr;
333     return CSI_STATUS_SUCCESS;
334 }
335
336 static csi_status_t
337 _csi_ostack_get_font_face (csi_t *ctx, unsigned int i, cairo_font_face_t **out)
338 {
339     csi_object_t *obj;
340
341     obj = _csi_peek_ostack (ctx, i);
342     if (_csi_unlikely (csi_object_get_type (obj) != CSI_OBJECT_TYPE_FONT))
343         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
344
345     *out = obj->datum.font_face;
346     return CSI_STATUS_SUCCESS;
347 }
348
349 static csi_status_t
350 _csi_ostack_get_pattern (csi_t *ctx, unsigned int i, cairo_pattern_t **out)
351 {
352     csi_object_t *obj;
353
354     obj = _csi_peek_ostack (ctx, i);
355     if (_csi_unlikely (csi_object_get_type (obj) != CSI_OBJECT_TYPE_PATTERN))
356         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
357
358     *out = obj->datum.pattern;
359     return CSI_STATUS_SUCCESS;
360 }
361
362 static csi_status_t
363 _csi_ostack_get_scaled_font (csi_t *ctx, unsigned int i,
364                              cairo_scaled_font_t **out)
365 {
366     csi_object_t *obj;
367
368     obj = _csi_peek_ostack (ctx, i);
369     if (_csi_unlikely
370         (csi_object_get_type (obj) != CSI_OBJECT_TYPE_SCALED_FONT))
371     {
372         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
373     }
374
375     *out = obj->datum.scaled_font;
376     return CSI_STATUS_SUCCESS;
377 }
378
379 static csi_status_t
380 _csi_ostack_get_surface (csi_t *ctx, unsigned int i, cairo_surface_t **out)
381 {
382     csi_object_t *obj;
383     int type;
384
385     obj = _csi_peek_ostack (ctx, i);
386     type = csi_object_get_type (obj);
387     switch (type) {
388     case CSI_OBJECT_TYPE_CONTEXT:
389         *out = cairo_get_target (obj->datum.cr);
390         break;
391     case CSI_OBJECT_TYPE_SURFACE:
392         *out = obj->datum.surface;
393         break;
394     default:
395         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
396     }
397     return CSI_STATUS_SUCCESS;
398 }
399
400 static csi_status_t
401 _csi_ostack_get_array (csi_t *ctx, unsigned int i, csi_array_t **out)
402 {
403     csi_object_t *obj;
404
405     obj = _csi_peek_ostack (ctx, i);
406     if (_csi_unlikely (csi_object_get_type (obj) != CSI_OBJECT_TYPE_ARRAY))
407         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
408
409     *out = obj->datum.array;
410     return CSI_STATUS_SUCCESS;
411 }
412
413 static csi_status_t
414 _csi_ostack_get_procedure (csi_t *ctx, unsigned int i, csi_array_t **out)
415 {
416     csi_object_t *obj;
417
418     obj = _csi_peek_ostack (ctx, i);
419     if (_csi_unlikely (! csi_object_is_procedure (obj)))
420         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
421
422     *out = obj->datum.array;
423     return CSI_STATUS_SUCCESS;
424 }
425
426 static csi_status_t
427 _csi_ostack_get_dictionary (csi_t *ctx, unsigned int i, csi_dictionary_t **out)
428 {
429     csi_object_t *obj;
430
431     obj = _csi_peek_ostack (ctx, i);
432     if (_csi_unlikely
433         (csi_object_get_type (obj) != CSI_OBJECT_TYPE_DICTIONARY))
434     {
435         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
436     }
437
438     *out = obj->datum.dictionary;
439     return CSI_STATUS_SUCCESS;
440 }
441
442 static csi_status_t
443 _csi_ostack_get_matrix (csi_t *ctx, unsigned int i, cairo_matrix_t *out)
444 {
445     csi_object_t *obj;
446     int type;
447
448     obj = _csi_peek_ostack (ctx, i);
449     type = csi_object_get_type (obj);
450     switch (type) {
451     case CSI_OBJECT_TYPE_MATRIX:
452         *out = obj->datum.matrix->matrix;
453         return CSI_STATUS_SUCCESS;
454
455     case CSI_OBJECT_TYPE_ARRAY:
456         if (obj->datum.array->stack.len == 6) {
457             cairo_matrix_init (out,
458                                csi_number_get_value (&obj->datum.array->stack.objects[0]),
459                                csi_number_get_value (&obj->datum.array->stack.objects[1]),
460                                csi_number_get_value (&obj->datum.array->stack.objects[2]),
461                                csi_number_get_value (&obj->datum.array->stack.objects[3]),
462                                csi_number_get_value (&obj->datum.array->stack.objects[4]),
463                                csi_number_get_value (&obj->datum.array->stack.objects[5]));
464             return CSI_STATUS_SUCCESS;
465         }
466     default:
467         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
468     }
469 }
470
471 static csi_status_t
472 _csi_dictionary_get_integer (csi_t *ctx,
473                              csi_dictionary_t *dict,
474                              const char *name,
475                              csi_boolean_t optional,
476                              long *value)
477 {
478     csi_status_t status;
479     csi_object_t key, obj;
480     int type;
481
482     status = csi_name_new_static (ctx, &key, name);
483     if (_csi_unlikely (status))
484         return status;
485
486     if (optional && ! csi_dictionary_has (dict, key.datum.name))
487         return CSI_STATUS_SUCCESS;
488
489     status = csi_dictionary_get (ctx, dict, key.datum.name, &obj);
490     if (_csi_unlikely (status))
491         return status;
492
493     type = csi_object_get_type (&obj);
494     switch (type) {
495     case CSI_OBJECT_TYPE_BOOLEAN:
496         *value = obj.datum.boolean;
497         break;
498     case CSI_OBJECT_TYPE_INTEGER:
499         *value = obj.datum.integer;
500         break;
501     case CSI_OBJECT_TYPE_REAL:
502         *value = obj.datum.real;
503         break;
504     default:
505         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
506     }
507     return CSI_STATUS_SUCCESS;
508 }
509
510 static csi_status_t
511 _csi_dictionary_get_number (csi_t *ctx,
512                             csi_dictionary_t *dict,
513                             const char *name,
514                             csi_boolean_t optional,
515                             double *value)
516 {
517     csi_status_t status;
518     csi_object_t key, obj;
519
520     status = csi_name_new_static (ctx, &key, name);
521     if (_csi_unlikely (status))
522         return status;
523
524     if (optional && ! csi_dictionary_has (dict, key.datum.name))
525         return CSI_STATUS_SUCCESS;
526
527     status = csi_dictionary_get (ctx, dict, key.datum.name, &obj);
528     if (_csi_unlikely (status))
529         return status;
530
531     *value = csi_number_get_value (&obj);
532     return CSI_STATUS_SUCCESS;
533 }
534
535 static csi_status_t
536 _csi_ostack_get_string (csi_t *ctx, unsigned int i, csi_string_t **out)
537 {
538     csi_object_t *obj;
539
540     obj = _csi_peek_ostack (ctx, i);
541     if (_csi_unlikely (csi_object_get_type (obj) != CSI_OBJECT_TYPE_STRING))
542         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
543
544     *out = obj->datum.string;
545     return CSI_STATUS_SUCCESS;
546 }
547
548 static csi_status_t
549 _csi_ostack_get_string_constant (csi_t *ctx, unsigned int i, const char **out)
550 {
551     csi_object_t *obj;
552     int type;
553
554     obj = _csi_peek_ostack (ctx, i);
555     type = csi_object_get_type (obj);
556     switch (type) {
557     case CSI_OBJECT_TYPE_NAME:
558         *out = (const char *) obj->datum.name;
559         break;
560     case CSI_OBJECT_TYPE_STRING:
561         *out = obj->datum.string->string;
562         break;
563     default:
564         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
565     }
566     return CSI_STATUS_SUCCESS;
567 }
568
569 static csi_status_t
570 _do_cairo_op (csi_t *ctx, void (*op) (cairo_t *))
571 {
572     cairo_t *cr;
573     csi_status_t status;
574
575     check (1);
576
577     status = _csi_ostack_get_context (ctx, 0, &cr);
578     if (_csi_unlikely (status))
579         return status;
580
581     op (cr);
582     return CSI_STATUS_SUCCESS;
583 }
584
585 static csi_status_t
586 end_dict_construction (csi_t *ctx)
587 {
588     csi_object_t obj;
589     csi_dictionary_t *dict;
590     csi_status_t status;
591
592     status = csi_dictionary_new (ctx, &obj);
593     if (_csi_unlikely (status))
594         return status;
595
596     dict = obj.datum.dictionary;
597     do {
598         csi_object_t *name, *value;
599
600         check (1);
601
602         value = _csi_peek_ostack (ctx, 0);
603         if (csi_object_get_type (value) == CSI_OBJECT_TYPE_MARK) {
604             pop (1);
605             break;
606         }
607
608         check (2);
609
610         name = _csi_peek_ostack (ctx, 1);
611         if (_csi_unlikely
612             (csi_object_get_type (name) != CSI_OBJECT_TYPE_NAME))
613         {
614             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
615         }
616
617         status = csi_dictionary_put (ctx, dict, name->datum.name, value);
618         if (_csi_unlikely (status))
619             return status;
620
621         pop (2);
622     } while (TRUE);
623
624     return push (&obj);
625 }
626
627 static csi_status_t
628 end_array_construction (csi_t *ctx)
629 {
630     csi_object_t obj;
631     csi_status_t status;
632     int len = 0;
633
634     do {
635         check (len + 1);
636
637         if (csi_object_get_type (_csi_peek_ostack (ctx, len)) ==
638             CSI_OBJECT_TYPE_MARK)
639         {
640             break;
641         }
642
643         len++;
644     } while (TRUE);
645
646     status = csi_array_new (ctx, len, &obj);
647     if (_csi_unlikely (status))
648         return status;
649
650     if (len != 0) {
651         csi_array_t *array;
652
653         array = obj.datum.array;
654         memcpy (array->stack.objects,
655                 _csi_peek_ostack (ctx, len - 1),
656                 sizeof (csi_object_t) * len);
657         array->stack.len = len;
658     }
659     ctx->ostack.len -= len + 1;
660
661     return push (&obj);
662 }
663
664 static csi_status_t
665 _alpha (csi_t *ctx)
666 {
667     csi_object_t obj;
668     csi_status_t status;
669     double a;
670
671     check (1);
672
673     status = _csi_ostack_get_number (ctx, 0, &a);
674     if (_csi_unlikely (status))
675         return status;
676
677     pop (1);
678
679     obj.type = CSI_OBJECT_TYPE_PATTERN;
680     obj.datum.pattern = cairo_pattern_create_rgba (0, 0, 0, a);
681     return push (&obj);
682 }
683
684 static csi_status_t
685 _add (csi_t *ctx)
686 {
687     csi_object_t *A;
688     csi_object_t *B;
689     csi_object_type_t type_a, type_b;
690
691     check (2);
692
693     B = _csi_peek_ostack (ctx, 0);
694     A = _csi_peek_ostack (ctx, 1);
695
696     type_a = csi_object_get_type (A);
697     if (_csi_unlikely (! (type_a == CSI_OBJECT_TYPE_INTEGER ||
698                             type_a == CSI_OBJECT_TYPE_REAL)))
699     {
700         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
701     }
702
703     type_b = csi_object_get_type (B);
704     if (_csi_unlikely (! (type_b == CSI_OBJECT_TYPE_INTEGER ||
705                             type_b == CSI_OBJECT_TYPE_REAL)))
706     {
707         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
708     }
709
710     pop (2);
711
712     if (type_a == CSI_OBJECT_TYPE_REAL &&
713         type_b == CSI_OBJECT_TYPE_REAL)
714     {
715         return _csi_push_ostack_real (ctx, A->datum.real + B->datum.real);
716
717     }
718     else if (type_a == CSI_OBJECT_TYPE_INTEGER &&
719              type_b == CSI_OBJECT_TYPE_INTEGER)
720     {
721         return _csi_push_ostack_integer (ctx,
722                                          A->datum.integer + B->datum.integer);
723     }
724     else
725     {
726         double v;
727
728         if (type_a == CSI_OBJECT_TYPE_REAL)
729             v = A->datum.real;
730         else
731             v = A->datum.integer;
732
733         if (type_b == CSI_OBJECT_TYPE_REAL)
734             v += B->datum.real;
735         else
736             v += B->datum.integer;
737
738         return _csi_push_ostack_real (ctx, v);
739     }
740 }
741
742 static csi_status_t
743 _add_color_stop (csi_t *ctx)
744 {
745     csi_status_t status;
746     double offset, r, g, b, a;
747     cairo_pattern_t *pattern = NULL; /* silence the compiler */
748
749     check (6);
750
751     status = _csi_ostack_get_number (ctx, 0, &a);
752     if (_csi_unlikely (status))
753         return status;
754     status = _csi_ostack_get_number (ctx, 1, &b);
755     if (_csi_unlikely (status))
756         return status;
757     status = _csi_ostack_get_number (ctx, 2, &g);
758     if (_csi_unlikely (status))
759         return status;
760     status = _csi_ostack_get_number (ctx, 3, &r);
761     if (_csi_unlikely (status))
762         return status;
763     status = _csi_ostack_get_number (ctx, 4, &offset);
764     if (_csi_unlikely (status))
765         return status;
766     status = _csi_ostack_get_pattern (ctx, 5, &pattern);
767     if (_csi_unlikely (status))
768         return status;
769
770     cairo_pattern_add_color_stop_rgba (pattern, offset, r, g, b, a);
771
772     pop (5);
773     return CSI_STATUS_SUCCESS;
774 }
775
776 static csi_status_t
777 _and (csi_t *ctx)
778 {
779     csi_object_t *a, *b;
780     int type;
781
782     check (2);
783
784     a = _csi_peek_ostack (ctx, 0);
785     b = _csi_peek_ostack (ctx, 1);
786     if (_csi_unlikely (csi_object_get_type (a) != csi_object_get_type (b)))
787         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
788
789     pop (2);
790     type = csi_object_get_type (a);
791     switch (type) {
792     case CSI_OBJECT_TYPE_INTEGER:
793         return _csi_push_ostack_integer (ctx,
794                                          a->datum.integer & b->datum.integer);
795     case CSI_OBJECT_TYPE_BOOLEAN:
796         return _csi_push_ostack_boolean (ctx,
797                                          a->datum.boolean & b->datum.boolean);
798     default:
799         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
800     }
801 }
802
803 static csi_status_t
804 _arc (csi_t *ctx)
805 {
806     csi_status_t status;
807     double x, y, r;
808     double theta1, theta2;
809     cairo_t *cr;
810
811     check (6);
812
813     status = _csi_ostack_get_number (ctx, 0, &theta2);
814     if (_csi_unlikely (status))
815         return status;
816     status = _csi_ostack_get_number (ctx, 1, &theta1);
817     if (_csi_unlikely (status))
818         return status;
819     status = _csi_ostack_get_number (ctx, 2, &r);
820     if (_csi_unlikely (status))
821         return status;
822     status = _csi_ostack_get_number (ctx, 3, &y);
823     if (_csi_unlikely (status))
824         return status;
825     status = _csi_ostack_get_number (ctx, 4, &x);
826     if (_csi_unlikely (status))
827         return status;
828     status = _csi_ostack_get_context (ctx, 5, &cr);
829     if (_csi_unlikely (status))
830         return status;
831
832     /* XXX handle path object */
833
834     cairo_arc (cr, x, y, r, theta1, theta2);
835     pop (5);
836     return CSI_STATUS_SUCCESS;
837 }
838
839 static csi_status_t
840 _arc_negative (csi_t *ctx)
841 {
842     csi_status_t status;
843     double x, y, r;
844     double theta1, theta2;
845     cairo_t *cr;
846
847     check (6);
848
849     status = _csi_ostack_get_number (ctx, 0, &theta2);
850     if (_csi_unlikely (status))
851         return status;
852     status = _csi_ostack_get_number (ctx, 1, &theta1);
853     if (_csi_unlikely (status))
854         return status;
855     status = _csi_ostack_get_number (ctx, 2, &r);
856     if (_csi_unlikely (status))
857         return status;
858     status = _csi_ostack_get_number (ctx, 3, &y);
859     if (_csi_unlikely (status))
860         return status;
861     status = _csi_ostack_get_number (ctx, 4, &x);
862     if (_csi_unlikely (status))
863         return status;
864     status = _csi_ostack_get_context (ctx, 5, &cr);
865     if (_csi_unlikely (status))
866         return status;
867
868     /* XXX handle path object */
869
870     cairo_arc_negative (cr, x, y, r, theta1, theta2);
871     pop (5);
872     return CSI_STATUS_SUCCESS;
873 }
874
875 static csi_status_t
876 _array (csi_t *ctx)
877 {
878     csi_object_t obj;
879     csi_status_t status;
880
881     status = csi_array_new (ctx, 0, &obj);
882     if (_csi_unlikely (status))
883         return status;
884
885     return push (&obj);
886 }
887
888 static csi_status_t
889 _bind_substitute (csi_t *ctx, csi_array_t *array)
890 {
891     csi_status_t status;
892     csi_integer_t i, n;
893     csi_dictionary_t *dict;
894
895     /* perform operator substitution on the executable array (procedure) */
896     dict = ctx->dstack.objects[0].datum.dictionary;
897     n = array->stack.len;
898     for (i = 0; i < n; i++) {
899         csi_object_t *obj = &array->stack.objects[i];
900
901         if (obj->type == (CSI_OBJECT_TYPE_NAME | CSI_OBJECT_ATTR_EXECUTABLE)) {
902             csi_dictionary_entry_t *entry;
903
904             entry = _csi_hash_table_lookup (&dict->hash_table,
905                                             (csi_hash_entry_t *)
906                                             &obj->datum.name);
907             if (entry != NULL)
908                 *obj = entry->value;
909         } else if (csi_object_is_procedure (obj)) {
910             status = _bind_substitute (ctx, obj->datum.array);
911             if (_csi_unlikely (status))
912                 return status;
913         }
914     }
915
916     return CSI_STATUS_SUCCESS;
917 }
918
919 static csi_status_t
920 _idiom_substitute (csi_t *ctx, csi_array_t *array)
921 {
922 #if 0
923     csi_status_t status;
924     csi_integer_t i, j;
925
926     /* XXX substring search, build array once then search for
927      * longest matching idiom, repeat. */
928
929     /* scan the top-most array for sequences we can pre-compile */
930
931     /* now recurse for subroutines */
932     j = array->stack.len;
933     for (i = 0; i < j; i++) {
934         csi_object_t *obj = &array->stack.objects[i];
935
936         if (csi_object_is_procedure (obj)) {
937             status = _idiom_substitute (ctx, obj->datum.array);
938             if (_csi_unlikely (_cairo_is_error (status))
939                 return status;
940         }
941     }
942 #endif
943
944     return CSI_STATUS_SUCCESS;
945 }
946
947 static csi_status_t
948 _bind (csi_t *ctx)
949 {
950     csi_array_t *array;
951     csi_status_t status;
952
953     check (1);
954
955     status = _csi_ostack_get_procedure (ctx, 0, &array);
956     if (_csi_unlikely (status))
957         return status;
958
959     status = _bind_substitute (ctx, array);
960     if (_csi_unlikely (status))
961         return status;
962
963     status = _idiom_substitute (ctx, array);
964     if (_csi_unlikely (status))
965         return status;
966
967     return CSI_STATUS_SUCCESS;
968 }
969
970 static csi_status_t
971 _bitshift (csi_t *ctx)
972 {
973     long v, shift;
974     csi_status_t status;
975
976     check (2);
977
978     status = _csi_ostack_get_integer (ctx, 0, &shift);
979     if (_csi_unlikely (status))
980         return status;
981     status = _csi_ostack_get_integer (ctx, 1, &v);
982     if (_csi_unlikely (status))
983         return status;
984
985     if (shift < 0) {
986         shift = -shift;
987         v >>= shift;
988     } else
989         v <<= shift;
990
991     pop (1);
992     _csi_peek_ostack (ctx, 0)->datum.integer = v;
993
994     return CSI_STATUS_SUCCESS;
995 }
996
997 static csi_status_t
998 _clip (csi_t *ctx)
999 {
1000     return _do_cairo_op (ctx, cairo_clip);
1001 }
1002
1003 static csi_status_t
1004 _clip_preserve (csi_t *ctx)
1005 {
1006     return _do_cairo_op (ctx, cairo_clip_preserve);
1007 }
1008
1009 static csi_status_t
1010 _close_path (csi_t *ctx)
1011 {
1012     return _do_cairo_op (ctx, cairo_close_path);
1013 }
1014
1015 static csi_status_t
1016 _context (csi_t *ctx)
1017 {
1018     csi_object_t obj;
1019     csi_status_t status;
1020     cairo_surface_t *surface;
1021     cairo_t *cr;
1022     csi_context_create_func_t hook;
1023     csi_proxy_t *proxy;
1024
1025     check (1);
1026
1027     status = _csi_ostack_get_surface (ctx, 0, &surface);
1028     if (_csi_unlikely (status))
1029         return status;
1030
1031     hook = ctx->hooks.context_create;
1032     if (hook != NULL)
1033         cr = hook (ctx->hooks.closure, surface);
1034     else
1035         cr = cairo_create (surface);
1036
1037     proxy = _csi_proxy_create (ctx, cr, NULL,
1038                                ctx->hooks.context_destroy,
1039                                ctx->hooks.closure);
1040     if (_csi_unlikely (proxy == NULL)) {
1041         cairo_destroy (cr);
1042         return _csi_error (CSI_STATUS_NO_MEMORY);
1043     }
1044
1045     status = cairo_set_user_data (cr, &_csi_proxy_key,
1046                                   proxy, _csi_proxy_destroy);
1047     if (_csi_unlikely (status)) {
1048         _csi_proxy_destroy (proxy);
1049         cairo_destroy (cr);
1050         return status;
1051     }
1052
1053     pop (1);
1054     obj.type = CSI_OBJECT_TYPE_CONTEXT;
1055     obj.datum.cr = cr;
1056     return push (&obj);
1057 }
1058
1059 static csi_status_t
1060 _copy (csi_t *ctx)
1061 {
1062     csi_object_t *obj;
1063     int type;
1064
1065     check (1);
1066
1067     obj = csi_object_reference (_csi_peek_ostack (ctx, 0));
1068     pop (1);
1069
1070     type = csi_object_get_type (obj);
1071     switch (type) {
1072         /*XXX array, string, dictionary, etc */
1073     case CSI_OBJECT_TYPE_INTEGER:
1074         {
1075             long i, n;
1076
1077             n = obj->datum.integer;
1078             if (_csi_unlikely (n < 0))
1079                 return _csi_error (CSI_STATUS_INVALID_SCRIPT);
1080             check (n);
1081
1082             for (i = n; i--; ) {
1083                 csi_status_t status;
1084
1085                 status = _csi_push_ostack_copy (ctx,
1086                                                 _csi_peek_ostack (ctx, n-1));
1087                 if (_csi_unlikely (status))
1088                     return status;
1089             }
1090             break;
1091         }
1092     default:
1093         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
1094     }
1095
1096     return CSI_STATUS_SUCCESS;
1097 }
1098
1099 static csi_status_t
1100 _copy_page (csi_t *ctx)
1101 {
1102     csi_object_t *obj;
1103     int type;
1104
1105     check (1);
1106
1107     obj = _csi_peek_ostack (ctx, 0);
1108     type = csi_object_get_type (obj);
1109     switch (type) {
1110     case CSI_OBJECT_TYPE_CONTEXT:
1111         cairo_copy_page (obj->datum.cr);
1112         if (ctx->hooks.copy_page != NULL)
1113             ctx->hooks.copy_page (ctx->hooks.closure, obj->datum.cr);
1114         break;
1115     case CSI_OBJECT_TYPE_SURFACE:
1116         cairo_surface_copy_page (obj->datum.surface);
1117         /* XXX hook? */
1118         break;
1119     default:
1120         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
1121     }
1122
1123     return CSI_STATUS_SUCCESS;
1124 }
1125
1126 static csi_status_t
1127 _curve_to (csi_t *ctx)
1128 {
1129     csi_status_t status;
1130     csi_object_t *obj;
1131     int type;
1132     double x1, y1;
1133     double x2, y2;
1134     double x3, y3;
1135
1136     check (7);
1137
1138     status = _csi_ostack_get_number (ctx, 0, &y3);
1139     if (_csi_unlikely (status))
1140         return status;
1141     status = _csi_ostack_get_number (ctx, 1, &x3);
1142     if (_csi_unlikely (status))
1143         return status;
1144     status = _csi_ostack_get_number (ctx, 2, &y2);
1145     if (_csi_unlikely (status))
1146         return status;
1147     status = _csi_ostack_get_number (ctx, 3, &x2);
1148     if (_csi_unlikely (status))
1149         return status;
1150     status = _csi_ostack_get_number (ctx, 4, &y1);
1151     if (_csi_unlikely (status))
1152         return status;
1153     status = _csi_ostack_get_number (ctx, 5, &x1);
1154     if (_csi_unlikely (status))
1155         return status;
1156
1157     obj = _csi_peek_ostack (ctx, 6);
1158     type = csi_object_get_type (obj);
1159     switch (type) {
1160     case CSI_OBJECT_TYPE_CONTEXT:
1161         cairo_curve_to (obj->datum.cr, x1, y1, x2, y2, x3, y3);
1162         break;
1163     case CSI_OBJECT_TYPE_PATTERN:
1164         cairo_mesh_pattern_curve_to (obj->datum.pattern,
1165                                      x1, y1, x2, y2, x3, y3);
1166         break;
1167         /* XXX handle path object */
1168     }
1169
1170     pop (6);
1171     return CSI_STATUS_SUCCESS;
1172 }
1173
1174 static csi_status_t
1175 _cvi (csi_t *ctx)
1176 {
1177     csi_object_t *val, obj;
1178     int type;
1179
1180     check (1);
1181
1182     val = _csi_peek_ostack (ctx, 0);
1183     type = csi_object_get_type (val);
1184     switch (type) {
1185     case CSI_OBJECT_TYPE_INTEGER:
1186         return CSI_STATUS_SUCCESS;
1187
1188     case CSI_OBJECT_TYPE_REAL:
1189         pop (1);
1190         return _csi_push_ostack_integer (ctx, val->datum.real);
1191
1192     case CSI_OBJECT_TYPE_STRING:
1193         if (! _csi_parse_number (&obj,
1194                                  val->datum.string->string,
1195                                  val->datum.string->len))
1196         {
1197             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
1198         }
1199
1200         pop (1);
1201         if (csi_object_get_type (&obj) == CSI_OBJECT_TYPE_INTEGER)
1202             return push (&obj);
1203         else
1204             return _csi_push_ostack_integer (ctx, obj.datum.real);
1205
1206     default:
1207         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
1208     }
1209 }
1210
1211 static csi_status_t
1212 _cvr (csi_t *ctx)
1213 {
1214     csi_object_t *val, obj;
1215     int type;
1216
1217     check (1);
1218
1219     val = _csi_peek_ostack (ctx, 0);
1220     type = csi_object_get_type (val);
1221     switch (type) {
1222     case CSI_OBJECT_TYPE_REAL:
1223         return CSI_STATUS_SUCCESS;
1224
1225     case CSI_OBJECT_TYPE_INTEGER:
1226         pop (1);
1227         return _csi_push_ostack_real (ctx, val->datum.integer);
1228
1229     case CSI_OBJECT_TYPE_STRING:
1230         if (! _csi_parse_number (&obj,
1231                                  val->datum.string->string,
1232                                  val->datum.string->len))
1233         {
1234             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
1235         }
1236
1237         pop (1);
1238         if (csi_object_get_type (&obj) == CSI_OBJECT_TYPE_REAL)
1239             return push (&obj);
1240         else
1241             return _csi_push_ostack_real (ctx, obj.datum.integer);
1242
1243     default:
1244         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
1245     }
1246 }
1247
1248 static csi_status_t
1249 _def (csi_t *ctx)
1250 {
1251     csi_name_t name = 0; /* silence the compiler */
1252     csi_status_t status;
1253
1254     check (2);
1255
1256     status = _csi_ostack_get_name (ctx, 1, &name);
1257     if (_csi_unlikely (status))
1258         return status;
1259
1260     status = _csi_name_define (ctx, name, _csi_peek_ostack (ctx, 0));
1261     if (_csi_unlikely (status))
1262         return status;
1263
1264     pop (2);
1265     return CSI_STATUS_SUCCESS;
1266 }
1267
1268 static csi_status_t
1269 _dict (csi_t *ctx)
1270 {
1271     csi_object_t obj;
1272     csi_status_t status;
1273
1274     status = csi_dictionary_new (ctx, &obj);
1275     if (_csi_unlikely (status))
1276         return status;
1277
1278     return push (&obj);
1279 }
1280
1281 static csi_status_t
1282 _div (csi_t *ctx)
1283 {
1284     csi_object_t *A;
1285     csi_object_t *B;
1286     csi_object_type_t type_a, type_b;
1287
1288     check (2);
1289
1290     B = _csi_peek_ostack (ctx, 0);
1291     A = _csi_peek_ostack (ctx, 1);
1292
1293     type_a = csi_object_get_type (A);
1294     if (_csi_unlikely (! (type_a == CSI_OBJECT_TYPE_INTEGER ||
1295                             type_a == CSI_OBJECT_TYPE_REAL)))
1296     {
1297         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
1298     }
1299
1300     type_b = csi_object_get_type (B);
1301     if (_csi_unlikely (! (type_b == CSI_OBJECT_TYPE_INTEGER ||
1302                             type_b == CSI_OBJECT_TYPE_REAL)))
1303     {
1304         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
1305     }
1306
1307     pop (2);
1308
1309     if (type_a == CSI_OBJECT_TYPE_REAL &&
1310         type_b == CSI_OBJECT_TYPE_REAL)
1311     {
1312         return _csi_push_ostack_real (ctx, A->datum.real / B->datum.real);
1313
1314     }
1315     else if (type_a == CSI_OBJECT_TYPE_INTEGER &&
1316              type_b == CSI_OBJECT_TYPE_INTEGER)
1317     {
1318         return _csi_push_ostack_integer (ctx,
1319                                          A->datum.integer / B->datum.integer);
1320     }
1321     else
1322     {
1323         double v;
1324
1325         if (type_a == CSI_OBJECT_TYPE_REAL)
1326             v = A->datum.real;
1327         else
1328             v = A->datum.integer;
1329
1330         if (type_b == CSI_OBJECT_TYPE_REAL)
1331             v /= B->datum.real;
1332         else
1333             v /= B->datum.integer;
1334
1335         return _csi_push_ostack_real (ctx, v);
1336     }
1337 }
1338
1339 static csi_status_t
1340 _duplicate (csi_t *ctx)
1341 {
1342     check (1);
1343
1344     return _csi_push_ostack_copy (ctx, _csi_peek_ostack (ctx, 0));
1345 }
1346
1347 static csi_status_t
1348 _eq (csi_t *ctx)
1349 {
1350     csi_object_t *a, *b;
1351     csi_boolean_t v;
1352
1353     check (2);
1354
1355     b = _csi_peek_ostack (ctx, 0);
1356     a = _csi_peek_ostack (ctx, 1);
1357
1358     v = csi_object_eq (a, b);
1359
1360     pop (2);
1361     return _csi_push_ostack_boolean (ctx, v);
1362 }
1363
1364 static csi_status_t
1365 _exch (csi_t *ctx)
1366 {
1367     return _csi_stack_exch (&ctx->ostack);
1368 }
1369
1370 static csi_status_t
1371 _false (csi_t *ctx)
1372 {
1373     return _csi_push_ostack_boolean (ctx, FALSE);
1374 }
1375
1376 static csi_status_t
1377 _fill (csi_t *ctx)
1378 {
1379     return _do_cairo_op (ctx, cairo_fill);
1380 }
1381
1382 static csi_status_t
1383 _fill_preserve (csi_t *ctx)
1384 {
1385     return _do_cairo_op (ctx, cairo_fill_preserve);
1386 }
1387
1388 static csi_status_t
1389 _filter (csi_t *ctx)
1390 {
1391     csi_object_t *src;
1392     csi_dictionary_t *dict = NULL;
1393     csi_status_t status;
1394     const char *name = NULL; /* silence the compiler */
1395     const struct filters {
1396         const char *name;
1397         csi_status_t (*constructor) (csi_t *t,
1398                                        csi_object_t *,
1399                                        csi_dictionary_t *,
1400                                        csi_object_t *);
1401     } filters[] = {
1402         { "ascii85", csi_file_new_ascii85_decode },
1403 #if HAVE_ZLIB
1404         { "deflate", csi_file_new_deflate_decode },
1405 #endif
1406 #if 0
1407         { "lzw", csi_file_new_lzw_decode },
1408 #endif
1409         { NULL, NULL }
1410     }, *filter;
1411     int cnt;
1412
1413     check (2);
1414
1415     status = _csi_ostack_get_string_constant (ctx, 0, &name);
1416     if (_csi_unlikely (status))
1417         return status;
1418
1419     src = _csi_peek_ostack (ctx, 1);
1420     cnt = 2;
1421     if (csi_object_get_type (src) == CSI_OBJECT_TYPE_DICTIONARY) {
1422         dict = src->datum.dictionary;
1423
1424         check (3);
1425
1426         src = _csi_peek_ostack (ctx, 2);
1427         cnt = 3;
1428     }
1429
1430     for (filter = filters; filter->name != NULL; filter++) {
1431         if (strcmp (name, filter->name) == 0) {
1432             csi_object_t file;
1433
1434             status = filter->constructor (ctx, &file, dict, src);
1435             if (_csi_unlikely (status))
1436                 return status;
1437
1438             pop (cnt);
1439             return push (&file);
1440         }
1441     }
1442
1443     return _csi_error (CSI_STATUS_INVALID_SCRIPT);
1444 }
1445
1446 static cairo_status_t
1447 _type3_init (cairo_scaled_font_t *scaled_font,
1448              cairo_t *cr,
1449              cairo_font_extents_t *metrics)
1450 {
1451     cairo_font_face_t *face;
1452     csi_proxy_t *proxy;
1453     csi_t *ctx;
1454     csi_dictionary_t *font;
1455     csi_object_t key;
1456     csi_object_t obj;
1457     csi_array_t *array;
1458     csi_status_t status;
1459
1460     face = cairo_scaled_font_get_font_face (scaled_font);
1461     proxy = cairo_font_face_get_user_data (face, &_csi_proxy_key);
1462     if (_csi_unlikely (proxy == NULL))
1463         return CAIRO_STATUS_NO_MEMORY;
1464
1465     ctx = proxy->ctx;
1466     font = proxy->dictionary;
1467
1468     status = csi_name_new_static (ctx, &key, "metrics");
1469     if (_csi_unlikely (status))
1470         return CAIRO_STATUS_NO_MEMORY;
1471
1472     if (! csi_dictionary_has (font, key.datum.name))
1473         return CAIRO_STATUS_SUCCESS;
1474
1475     status = csi_dictionary_get (ctx, font, key.datum.name, &obj);
1476     if (_csi_unlikely (status))
1477         return status;
1478
1479     if (csi_object_get_type (&obj) != CSI_OBJECT_TYPE_ARRAY)
1480         return CAIRO_STATUS_USER_FONT_ERROR;
1481
1482     array = obj.datum.array;
1483     if (array->stack.len != 5)
1484         return CAIRO_STATUS_USER_FONT_ERROR;
1485
1486     metrics->ascent  = csi_number_get_value (&array->stack.objects[0]);
1487     metrics->descent = csi_number_get_value (&array->stack.objects[1]);
1488     metrics->height  = csi_number_get_value (&array->stack.objects[2]);
1489     metrics->max_x_advance = csi_number_get_value (&array->stack.objects[3]);
1490     metrics->max_y_advance = csi_number_get_value (&array->stack.objects[4]);
1491
1492     return CAIRO_STATUS_SUCCESS;
1493 }
1494
1495 static cairo_status_t
1496 _type3_lookup (cairo_scaled_font_t *scaled_font,
1497                unsigned long unicode,
1498                unsigned long *glyph)
1499 {
1500     cairo_font_face_t *face;
1501     csi_proxy_t *proxy;
1502     csi_t *ctx;
1503     csi_dictionary_t *font;
1504     csi_object_t obj, key;
1505     csi_array_t *array;
1506     char buf[12];
1507     csi_integer_t i;
1508     cairo_status_t status;
1509
1510     face = cairo_scaled_font_get_font_face (scaled_font);
1511     proxy = cairo_font_face_get_user_data (face, &_csi_proxy_key);
1512     if (_csi_unlikely (proxy == NULL))
1513         return CAIRO_STATUS_USER_FONT_ERROR;
1514
1515     ctx = proxy->ctx;
1516     font = proxy->dictionary;
1517
1518     status = csi_name_new_static (ctx, &key, "encoding");
1519     if (_csi_unlikely (status))
1520         return CAIRO_STATUS_USER_FONT_ERROR;
1521
1522     if (! csi_dictionary_has (font, key.datum.name)) {
1523         *glyph = unicode;
1524         return CAIRO_STATUS_SUCCESS;
1525     }
1526
1527     status = csi_dictionary_get (ctx, font, key.datum.name, &obj);
1528     if (_csi_unlikely (status))
1529         return CAIRO_STATUS_USER_FONT_ERROR;
1530
1531     if (_csi_unlikely (csi_object_get_type (&obj) != CSI_OBJECT_TYPE_ARRAY))
1532         return CAIRO_STATUS_USER_FONT_ERROR;
1533
1534     snprintf (buf, sizeof (buf), "uni%04lu", unicode);
1535     array = obj.datum.array;
1536     for (i = 0; i < array->stack.len; i++) {
1537         csi_object_t *name;
1538
1539         name = &array->stack.objects[i];
1540         if (csi_object_get_type (name) != CSI_OBJECT_TYPE_NAME)
1541             continue;
1542
1543         if (strcmp ((char *) name->datum.name, buf) == 0) {
1544             *glyph = i;
1545             return CAIRO_STATUS_SUCCESS;
1546         }
1547     }
1548
1549     return CAIRO_STATUS_USER_FONT_ERROR;
1550 }
1551
1552 static cairo_status_t
1553 _type3_render (cairo_scaled_font_t *scaled_font,
1554                unsigned long glyph_index,
1555                cairo_t *cr,
1556                cairo_text_extents_t *metrics)
1557 {
1558     cairo_font_face_t *face;
1559     csi_proxy_t *proxy;
1560     csi_t *ctx;
1561     csi_dictionary_t *font;
1562     csi_array_t *glyphs;
1563     csi_object_t *glyph;
1564     csi_object_t key;
1565     csi_object_t obj;
1566     csi_object_t render;
1567     csi_status_t status;
1568
1569     face = cairo_scaled_font_get_font_face (scaled_font);
1570     proxy = cairo_font_face_get_user_data (face, &_csi_proxy_key);
1571     if (_csi_unlikely (proxy == NULL))
1572         return CAIRO_STATUS_USER_FONT_ERROR;
1573
1574     ctx = proxy->ctx;
1575     font = proxy->dictionary;
1576
1577     status = csi_name_new_static (ctx, &key, "glyphs");
1578     if (_csi_unlikely (status))
1579         return CAIRO_STATUS_USER_FONT_ERROR;
1580
1581     status = csi_dictionary_get (ctx, font, key.datum.name, &obj);
1582     if (_csi_unlikely (status))
1583         return CAIRO_STATUS_USER_FONT_ERROR;
1584
1585     if (_csi_unlikely (csi_object_get_type (&obj) != CSI_OBJECT_TYPE_ARRAY))
1586         return CAIRO_STATUS_USER_FONT_ERROR;
1587
1588     glyphs = obj.datum.array;
1589     glyph = &glyphs->stack.objects[glyph_index];
1590     if (csi_object_get_type (glyph) == CSI_OBJECT_TYPE_NULL)
1591         return CAIRO_STATUS_SUCCESS; /* .notdef */
1592
1593     if (_csi_unlikely (csi_object_get_type (glyph) != CSI_OBJECT_TYPE_DICTIONARY))
1594         return CAIRO_STATUS_USER_FONT_ERROR;
1595
1596     status = csi_name_new_static (ctx, &key, "metrics");
1597     if (_csi_unlikely (status))
1598         return CAIRO_STATUS_USER_FONT_ERROR;
1599
1600     font = glyph->datum.dictionary;
1601     if (csi_dictionary_has (font, key.datum.name)) {
1602         csi_array_t *array;
1603
1604         status = csi_dictionary_get (ctx, font, key.datum.name, &obj);
1605         if (_csi_unlikely (status))
1606             return CAIRO_STATUS_USER_FONT_ERROR;
1607
1608         if (_csi_unlikely (csi_object_get_type (&obj) !=
1609                              CSI_OBJECT_TYPE_ARRAY))
1610             return CAIRO_STATUS_USER_FONT_ERROR;
1611
1612         array = obj.datum.array;
1613         if (_csi_unlikely (array->stack.len != 6))
1614             return CAIRO_STATUS_USER_FONT_ERROR;
1615
1616         metrics->x_bearing = csi_number_get_value (&array->stack.objects[0]);
1617         metrics->y_bearing = csi_number_get_value (&array->stack.objects[1]);
1618         metrics->width = csi_number_get_value (&array->stack.objects[2]);
1619         metrics->height = csi_number_get_value (&array->stack.objects[3]);
1620         metrics->x_advance = csi_number_get_value (&array->stack.objects[4]);
1621         metrics->y_advance = csi_number_get_value (&array->stack.objects[5]);
1622     }
1623
1624     status = csi_name_new_static (ctx, &key, "render");
1625     if (_csi_unlikely (status))
1626         return CAIRO_STATUS_USER_FONT_ERROR;
1627
1628     status = csi_dictionary_get (ctx, font, key.datum.name, &render);
1629     if (_csi_unlikely (status))
1630         return CAIRO_STATUS_USER_FONT_ERROR;
1631
1632     if (_csi_unlikely (! csi_object_is_procedure (&render)))
1633         return CAIRO_STATUS_USER_FONT_ERROR;
1634
1635     obj.type = CSI_OBJECT_TYPE_CONTEXT;
1636     obj.datum.cr = cairo_reference (cr);
1637     status = push (&obj);
1638     if (_csi_unlikely (status)) {
1639         cairo_destroy (cr);
1640         return CAIRO_STATUS_USER_FONT_ERROR;
1641     }
1642
1643     status = csi_object_execute (ctx, &render);
1644     pop (1);
1645     return status ? CAIRO_STATUS_USER_FONT_ERROR : CAIRO_STATUS_SUCCESS;
1646 }
1647
1648 static csi_status_t
1649 _font_type3 (csi_t *ctx,
1650              csi_dictionary_t *font,
1651              cairo_font_face_t **font_face_out)
1652 {
1653     cairo_font_face_t *font_face;
1654
1655     font_face = cairo_user_font_face_create ();
1656     cairo_user_font_face_set_init_func (font_face, _type3_init);
1657     cairo_user_font_face_set_unicode_to_glyph_func (font_face, _type3_lookup);
1658     cairo_user_font_face_set_render_glyph_func (font_face, _type3_render);
1659
1660     *font_face_out = font_face;
1661     return CSI_STATUS_SUCCESS;
1662 }
1663
1664 #if CAIRO_HAS_FT_FONT
1665 #include <cairo-ft.h>
1666 #include <ft2build.h>
1667 #include FT_FREETYPE_H
1668
1669 static FT_Library _ft_lib;
1670
1671 struct _ft_face_data {
1672     csi_t *ctx;
1673     csi_blob_t blob;
1674     FT_Face face;
1675     csi_string_t *source;
1676     void *bytes;
1677     cairo_font_face_t *font_face;
1678 };
1679
1680 static void
1681 _ft_done_face (void *closure)
1682 {
1683     struct _ft_face_data *data = closure;
1684     csi_t *ctx;
1685
1686     ctx = data->ctx;
1687
1688     if (data->face != NULL)
1689         FT_Done_Face (data->face);
1690
1691     ctx->_faces = _csi_list_unlink (ctx->_faces, &data->blob.list);
1692
1693     if (data->source != NULL) {
1694         if (--data->source->base.ref == 0)
1695             csi_string_free (ctx, data->source);
1696     } else {
1697 #ifdef HAVE_MMAP
1698         munmap (data->blob.bytes, data->blob.len);
1699 #endif
1700     }
1701
1702     if (data->bytes != NULL)
1703         _csi_free (ctx, data->bytes);
1704
1705     _csi_slab_free (ctx, data, sizeof (*data));
1706
1707     cairo_script_interpreter_destroy (ctx);
1708 }
1709
1710 struct mmap_vec {
1711     const uint8_t *bytes;
1712     size_t num_bytes;
1713 };
1714
1715 #ifdef HAVE_MMAP
1716 /* manual form of swapping for swapless systems like tiny */
1717 static void *
1718 _mmap_bytes (const struct mmap_vec *vec, int count)
1719 {
1720     char template[] = "/tmp/csi-font.XXXXXX";
1721     void *ptr;
1722     int fd;
1723     int num_bytes;
1724
1725     fd = mkstemp (template);
1726     if (fd == -1)
1727         return MAP_FAILED;
1728
1729     unlink (template);
1730     num_bytes = 0;
1731     while (count--) {
1732         const uint8_t *bytes = vec->bytes;
1733         size_t len = vec->num_bytes;
1734         while (len) {
1735             int ret = write (fd, bytes, len);
1736             if (ret < 0) {
1737                 close (fd);
1738                 return MAP_FAILED;
1739             }
1740             len -= ret;
1741             bytes += ret;
1742         }
1743
1744         num_bytes += vec->num_bytes;
1745         vec++;
1746     }
1747
1748     ptr = mmap (NULL, num_bytes, PROT_READ, MAP_SHARED, fd, 0);
1749     close (fd);
1750
1751     return ptr;
1752 }
1753 #endif
1754
1755 static void *
1756 inflate_string (csi_t *ctx, csi_string_t *src)
1757 {
1758     uLongf len;
1759     uint8_t *bytes;
1760
1761     len = src->deflate;
1762     bytes = _csi_alloc (ctx, len + 1);
1763     if (bytes == NULL)
1764         return NULL;
1765
1766     switch (src->method) {
1767     default:
1768     case NONE:
1769         free (bytes);
1770         return NULL;
1771
1772 #if HAVE_ZLIB
1773     case ZLIB:
1774         if (uncompress ((Bytef *) bytes, &len,
1775                         (Bytef *) src->string, src->len) != Z_OK)
1776         {
1777             _csi_free (ctx, bytes);
1778             return NULL;
1779         }
1780         break;
1781 #endif
1782
1783 #if HAVE_LZO
1784     case LZO:
1785         if (lzo2a_decompress ((Bytef *) src->string, src->len,
1786                               (Bytef *) bytes, &len,
1787                               NULL))
1788         {
1789             _csi_free (ctx, bytes);
1790             return NULL;
1791         }
1792         break;
1793 #endif
1794     }
1795
1796     bytes[len] = '\0';
1797     return bytes;
1798 }
1799
1800 static csi_status_t
1801 _ft_create_for_source (csi_t *ctx,
1802                        csi_string_t *source,
1803                        int index, int load_flags,
1804                        cairo_font_face_t **font_face_out)
1805 {
1806     csi_blob_t tmpl;
1807     struct _ft_face_data *data;
1808     csi_list_t *link;
1809     FT_Error err;
1810     cairo_font_face_t *font_face;
1811     csi_status_t status;
1812     struct mmap_vec vec[2];
1813     int vec_count;
1814     void *bytes;
1815     int len;
1816
1817     /* check for an existing FT_Face (kept alive by the font cache) */
1818     /* XXX index/flags */
1819     _csi_blob_init (&tmpl, (uint8_t *) source->string, source->len);
1820     _csi_blob_hash (&tmpl, (uint32_t *) source->string, source->len / sizeof (uint32_t));
1821     link = _csi_list_find (ctx->_faces, _csi_blob_equal, &tmpl);
1822     if (link) {
1823         if (--source->base.ref == 0)
1824             csi_string_free (ctx, source);
1825         data = csi_container_of (link, struct _ft_face_data, blob.list);
1826         *font_face_out = cairo_font_face_reference (data->font_face);
1827         return CSI_STATUS_SUCCESS;
1828     }
1829
1830     /* no existing font_face, create new FT_Face */
1831     if (_ft_lib == NULL) {
1832         err = FT_Init_FreeType (&_ft_lib);
1833         if (_csi_unlikely (err != FT_Err_Ok))
1834             return _csi_error (CSI_STATUS_NO_MEMORY);
1835     }
1836
1837     data = _csi_slab_alloc (ctx, sizeof (*data));
1838     if (data == NULL)
1839         return _csi_error (CSI_STATUS_NO_MEMORY);
1840
1841     data->bytes = NULL;
1842     data->source = source;
1843
1844     vec[0].bytes = tmpl.bytes;
1845     vec[0].num_bytes = tmpl.len;
1846
1847     if (source->deflate) {
1848         len = source->deflate;
1849         bytes = inflate_string (ctx, source);
1850         if (_csi_unlikely (bytes == NULL))
1851             return _csi_error (CSI_STATUS_NO_MEMORY);
1852
1853         vec[1].bytes = bytes;
1854         vec[1].num_bytes = len;
1855         data->bytes = bytes;
1856         vec_count = 2;
1857     } else {
1858         bytes = tmpl.bytes;
1859         len = tmpl.len;
1860         vec_count = 1;
1861     }
1862
1863     data->face = NULL;
1864     ctx->_faces = _csi_list_prepend (ctx->_faces, &data->blob.list);
1865     data->ctx = cairo_script_interpreter_reference (ctx);
1866     data->blob.hash = tmpl.hash;
1867     data->blob.len = tmpl.len;
1868 #ifdef HAVE_MMAP
1869     data->blob.bytes = _mmap_bytes (vec, vec_count);
1870     if (data->blob.bytes != MAP_FAILED) {
1871         if (--source->base.ref == 0)
1872             csi_string_free (ctx, source);
1873
1874         if (source->deflate) {
1875             _csi_free (ctx, bytes);
1876             bytes = data->blob.bytes + vec[0].num_bytes;
1877         } else
1878             bytes = data->blob.bytes;
1879
1880         data->source = NULL;
1881         data->bytes = NULL;
1882     } else {
1883         data->blob.bytes = tmpl.bytes;
1884     }
1885 #else
1886     data->blob.bytes = tmpl.bytes;
1887 #endif
1888
1889     err = FT_New_Memory_Face (_ft_lib,
1890                               bytes, len,
1891                               index,
1892                               &data->face);
1893     if (_csi_unlikely (err != FT_Err_Ok)) {
1894         _ft_done_face (data);
1895
1896         if (err == FT_Err_Out_Of_Memory)
1897             return _csi_error (CSI_STATUS_NO_MEMORY);
1898
1899         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
1900     }
1901
1902     font_face = cairo_ft_font_face_create_for_ft_face (data->face, load_flags);
1903     status = cairo_font_face_set_user_data (font_face,
1904                                             &_csi_blob_key,
1905                                             data, _ft_done_face);
1906     if (_csi_unlikely (status)) {
1907         _ft_done_face (data);
1908         cairo_font_face_destroy (font_face);
1909         return status;
1910     }
1911
1912     data->font_face = font_face;
1913     *font_face_out = font_face;
1914     return CSI_STATUS_SUCCESS;
1915 }
1916
1917 static csi_status_t
1918 _ft_create_for_pattern (csi_t *ctx,
1919                         csi_string_t *string,
1920                         cairo_font_face_t **font_face_out)
1921 {
1922 #if CAIRO_HAS_FC_FONT
1923     csi_blob_t tmpl;
1924     struct _ft_face_data *data;
1925     csi_list_t *link;
1926     cairo_font_face_t *font_face;
1927     FcPattern *pattern, *resolved;
1928     csi_status_t status;
1929     struct mmap_vec vec;
1930     void *bytes;
1931
1932     _csi_blob_init (&tmpl, (uint8_t *) string->string, string->len);
1933     _csi_blob_hash (&tmpl, (uint32_t *) string->string, string->len / sizeof (uint32_t));
1934     link = _csi_list_find (ctx->_faces, _csi_blob_equal, &tmpl);
1935     if (link) {
1936         if (--string->base.ref == 0)
1937             csi_string_free (ctx, string);
1938         data = csi_container_of (link, struct _ft_face_data, blob.list);
1939         *font_face_out = cairo_font_face_reference (data->font_face);
1940         return CSI_STATUS_SUCCESS;
1941     }
1942
1943     if (string->deflate) {
1944         bytes = inflate_string (ctx, string);
1945         if (_csi_unlikely (bytes == NULL))
1946             return _csi_error (CSI_STATUS_NO_MEMORY);
1947     } else {
1948         bytes = tmpl.bytes;
1949     }
1950
1951     pattern = FcNameParse (bytes);
1952     if (bytes != tmpl.bytes)
1953         _csi_free (ctx, bytes);
1954
1955 retry:
1956     resolved = pattern;
1957     if (cairo_version () < CAIRO_VERSION_ENCODE (1, 9, 0)) {
1958         /* prior to 1.9, you needed to pass a resolved pattern */
1959         resolved = FcFontMatch (NULL, pattern, NULL);
1960         if (_csi_unlikely (resolved == NULL)) {
1961             FcPatternDestroy (pattern);
1962             return _csi_error (CSI_STATUS_NO_MEMORY);
1963         }
1964     }
1965
1966     font_face = cairo_ft_font_face_create_for_pattern (resolved);
1967     if (resolved != pattern)
1968         FcPatternDestroy (resolved);
1969
1970     if (cairo_font_face_status (font_face)) {
1971         char *filename = NULL;
1972
1973         /* Try a manual fallback process by eliminating specific requests */
1974
1975         if (FcPatternGetString (pattern,
1976                                 FC_FILE, 0,
1977                                 (FcChar8 **) &filename) == FcResultMatch) {
1978             FcPatternDel (pattern, FC_FILE);
1979             if(font_face)
1980                 cairo_font_face_destroy (font_face);
1981             goto retry;
1982         }
1983     }
1984
1985     FcPatternDestroy (pattern);
1986
1987     data = _csi_slab_alloc (ctx, sizeof (*data));
1988     if (_csi_unlikely (data == NULL)) {
1989         if(font_face)
1990             cairo_font_face_destroy (font_face);
1991         return _csi_error (CSI_STATUS_NO_MEMORY);
1992     }
1993
1994     ctx->_faces = _csi_list_prepend (ctx->_faces, &data->blob.list);
1995     data->ctx = cairo_script_interpreter_reference (ctx);
1996     data->blob.hash = tmpl.hash;
1997     data->blob.len = tmpl.len;
1998     data->bytes = NULL;
1999     data->face = NULL;
2000 #ifdef HAVE_MMAP
2001     vec.bytes = tmpl.bytes;
2002     vec.num_bytes = tmpl.len;
2003     data->blob.bytes = _mmap_bytes (&vec, 1);
2004     if (data->blob.bytes != MAP_FAILED) {
2005         data->source = NULL;
2006         if (--string->base.ref == 0)
2007             csi_string_free (ctx, string);
2008     } else {
2009         data->blob.bytes = tmpl.bytes;
2010         data->source = string;
2011     }
2012 #else
2013     data->blob.bytes = tmpl.bytes;
2014     data->source = string;
2015 #endif
2016
2017     status = cairo_font_face_set_user_data (font_face,
2018                                             &_csi_blob_key,
2019                                             data, _ft_done_face);
2020     if (_csi_unlikely (status)) {
2021         _ft_done_face (data);
2022         cairo_font_face_destroy (font_face);
2023         return status;
2024     }
2025
2026     data->font_face = font_face;
2027     *font_face_out = font_face;
2028     return CSI_STATUS_SUCCESS;
2029 #else
2030     if (--string->base.ref == 0)
2031         csi_string_free (ctx, string);
2032     return CSI_INT_STATUS_UNSUPPORTED;
2033 #endif
2034 }
2035
2036 static csi_status_t
2037 _ft_type42_create (csi_t *ctx,
2038                    csi_dictionary_t *font,
2039                    cairo_font_face_t **font_face_out)
2040 {
2041     csi_object_t key;
2042     csi_status_t status;
2043
2044     /* two basic sub-types, either an FcPattern or embedded font */
2045     status = csi_name_new_static (ctx, &key, "pattern");
2046     if (_csi_unlikely (status))
2047         return status;
2048
2049     if (csi_dictionary_has (font, key.datum.name)) {
2050         csi_object_t obj;
2051         int type;
2052
2053         status = csi_dictionary_get (ctx, font, key.datum.name, &obj);
2054         if (_csi_unlikely (status))
2055             return status;
2056
2057         type = csi_object_get_type (&obj);
2058         switch (type) {
2059         case CSI_OBJECT_TYPE_FILE:
2060             status = _csi_file_as_string (ctx, obj.datum.file, &obj);
2061             if (_csi_unlikely (status))
2062                 return status;
2063             break;
2064         case CSI_OBJECT_TYPE_STRING:
2065             obj.datum.object->ref++;
2066             break;
2067         default:
2068             return  _csi_error (CSI_STATUS_INVALID_SCRIPT);
2069         }
2070
2071         return _ft_create_for_pattern (ctx,
2072                                        obj.datum.string,
2073                                        font_face_out);
2074     }
2075
2076     status = csi_name_new_static (ctx, &key, "source");
2077     if (_csi_unlikely (status))
2078         return status;
2079
2080     if (csi_dictionary_has (font, key.datum.name)) {
2081         csi_object_t obj;
2082         long index, flags;
2083         int type;
2084
2085         index = 0;
2086         status = _csi_dictionary_get_integer (ctx, font, "index", TRUE, &index);
2087         if (_csi_unlikely (status))
2088             return status;
2089
2090         flags = 0;
2091         status = _csi_dictionary_get_integer (ctx, font, "flags", TRUE, &flags);
2092         if (_csi_unlikely (status))
2093             return status;
2094
2095         status = csi_name_new_static (ctx, &key, "source");
2096         if (_csi_unlikely (status))
2097             return status;
2098         status = csi_dictionary_get (ctx, font, key.datum.name, &obj);
2099         if (_csi_unlikely (status))
2100             return status;
2101         type = csi_object_get_type (&obj);
2102         switch (type) {
2103         case CSI_OBJECT_TYPE_FILE:
2104             status = _csi_file_as_string (ctx, obj.datum.file, &obj);
2105             if (_csi_unlikely (status))
2106                 return status;
2107             break;
2108         case CSI_OBJECT_TYPE_STRING:
2109             obj.datum.object->ref++;
2110             break;
2111         default:
2112             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
2113         }
2114
2115         return _ft_create_for_source (ctx, obj.datum.string,
2116                                       index, flags,
2117                                       font_face_out);
2118     }
2119
2120     return _csi_error (CSI_STATUS_INVALID_SCRIPT);
2121 }
2122 #else
2123 #define _ft_type42_create(ctx, font, face_out) CSI_INT_STATUS_UNSUPPORTED
2124 #endif
2125
2126 static char *
2127 _fc_strcpy (csi_t *ctx, const char *str)
2128 {
2129     char *ret;
2130     int len;
2131
2132     ret = strchr (str, ':');
2133     if (ret != NULL)
2134         len = ret - str;
2135     else
2136         len = strlen (str);
2137
2138     ret = _csi_alloc (ctx, len+1);
2139     if (_csi_unlikely (ret == NULL))
2140         return NULL;
2141
2142     memcpy (ret, str, len);
2143     ret[len] = '\0';
2144
2145     return ret;
2146 }
2147
2148 static cairo_font_face_t *
2149 _select_font (const char *name)
2150 {
2151     cairo_surface_t *surface;
2152     cairo_font_face_t *face;
2153     cairo_t *cr;
2154
2155     /* create a dummy context to choose a font */
2156     surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
2157     cr = cairo_create (surface);
2158     cairo_surface_destroy (surface);
2159
2160     cairo_select_font_face (cr, name,
2161                             CAIRO_FONT_SLANT_NORMAL,
2162                             CAIRO_FONT_WEIGHT_NORMAL);
2163     face = cairo_font_face_reference (cairo_get_font_face (cr));
2164     cairo_destroy (cr);
2165
2166     return face;
2167 }
2168
2169 static csi_status_t
2170 _ft_fallback_create_for_pattern (csi_t *ctx,
2171                                  csi_string_t *string,
2172                                  cairo_font_face_t **font_face_out)
2173 {
2174     char *str, *name;
2175
2176     str = string->string;
2177 #if 0
2178     name = strstr (str, "fullname=");
2179     if (name != NULL)
2180         str = name + 9;
2181 #endif
2182
2183     name = _fc_strcpy (ctx, str);
2184     if (_csi_unlikely (name == NULL))
2185         return _csi_error (CSI_STATUS_NO_MEMORY);
2186
2187     *font_face_out = _select_font (name);
2188     _csi_free (ctx, name);
2189
2190     return CSI_STATUS_SUCCESS;
2191 }
2192
2193 static csi_status_t
2194 _ft_type42_fallback_create (csi_t *ctx,
2195                             csi_dictionary_t *font,
2196                             cairo_font_face_t **font_face_out)
2197 {
2198     csi_object_t key;
2199     csi_status_t status;
2200
2201     /* attempt to select a similar font */
2202
2203     /* two basic sub-types, either an FcPattern or embedded font */
2204     status = csi_name_new_static (ctx, &key, "pattern");
2205     if (_csi_unlikely (status))
2206         return status;
2207
2208     if (csi_dictionary_has (font, key.datum.name)) {
2209         csi_object_t obj;
2210         int type;
2211
2212         status = csi_dictionary_get (ctx, font, key.datum.name, &obj);
2213         if (_csi_unlikely (status))
2214             return status;
2215
2216         type = csi_object_get_type (&obj);
2217         switch (type) {
2218         case CSI_OBJECT_TYPE_FILE:
2219             status = _csi_file_as_string (ctx, obj.datum.file, &obj);
2220             if (_csi_unlikely (status))
2221                 return status;
2222             break;
2223         case CSI_OBJECT_TYPE_STRING:
2224             obj.datum.object->ref++;
2225             break;
2226         default:
2227             return  _csi_error (CSI_STATUS_INVALID_SCRIPT);
2228         }
2229
2230         return _ft_fallback_create_for_pattern (ctx,
2231                                                 obj.datum.string,
2232                                                 font_face_out);
2233     }
2234
2235     /* XXX: enable the trace to run */
2236     *font_face_out = _select_font ("Sans");
2237     return CSI_STATUS_SUCCESS;
2238 }
2239
2240 static csi_status_t
2241 _font_type42 (csi_t *ctx, csi_dictionary_t *font, cairo_font_face_t **font_face)
2242 {
2243     csi_status_t status;
2244
2245     status = _ft_type42_create (ctx, font, font_face);
2246     if (_csi_likely (status != CSI_INT_STATUS_UNSUPPORTED))
2247         return status;
2248
2249     return _ft_type42_fallback_create (ctx, font, font_face);
2250 }
2251
2252 static csi_status_t
2253 _font (csi_t *ctx)
2254 {
2255     csi_dictionary_t *font;
2256     csi_status_t status;
2257     cairo_font_face_t *font_face = NULL; /* silence the compiler */
2258     csi_proxy_t *proxy;
2259     csi_object_t obj;
2260     long type;
2261
2262     check (1);
2263
2264     status = _csi_ostack_get_dictionary (ctx, 0, &font);
2265     if (_csi_unlikely (status))
2266         return status;
2267
2268     status = _csi_dictionary_get_integer (ctx, font, "type", FALSE, &type);
2269     if (_csi_unlikely (status))
2270         return status;
2271
2272     switch (type) {
2273     case 3:
2274         status = _font_type3 (ctx, font, &font_face);
2275         break;
2276     case 42:
2277         status = _font_type42 (ctx, font, &font_face);
2278         break;
2279     default:
2280         status = _csi_error (CSI_STATUS_INVALID_SCRIPT);
2281         break;
2282     }
2283
2284     if (_csi_unlikely (status))
2285         return status;
2286
2287     /* transfer ownership of dictionary to cairo_font_face_t */
2288     proxy = _csi_proxy_create (ctx, font_face, font, NULL, NULL);
2289     if (_csi_unlikely (proxy == NULL)) {
2290         cairo_font_face_destroy (font_face);
2291         return _csi_error (CSI_STATUS_NO_MEMORY);
2292     }
2293
2294     status = cairo_font_face_set_user_data (font_face,
2295                                             &_csi_proxy_key,
2296                                             proxy, _csi_proxy_destroy);
2297     if (_csi_unlikely (status)) {
2298         _csi_proxy_destroy (proxy);
2299         cairo_font_face_destroy (font_face);
2300         return status;
2301     }
2302
2303     obj.type = CSI_OBJECT_TYPE_FONT;
2304     obj.datum.font_face = font_face;
2305
2306     pop (1);
2307     status = push (&obj);
2308     if (_csi_unlikely (status)) {
2309         cairo_font_face_destroy (font_face);
2310         return status;
2311     }
2312
2313     return CSI_STATUS_SUCCESS;
2314 }
2315
2316 static csi_status_t
2317 _for (csi_t *ctx)
2318 {
2319     csi_array_t *proc;
2320     csi_status_t status;
2321     long i, inc, limit;
2322
2323     check (4);
2324
2325     status = _csi_ostack_get_procedure (ctx, 0, &proc);
2326     if (_csi_unlikely (status))
2327         return status;
2328     status = _csi_ostack_get_integer (ctx, 1, &limit);
2329     if (_csi_unlikely (status))
2330         return status;
2331     status = _csi_ostack_get_integer (ctx, 2, &inc);
2332     if (_csi_unlikely (status))
2333         return status;
2334     status = _csi_ostack_get_integer (ctx, 3, &i);
2335     if (_csi_unlikely (status))
2336         return status;
2337
2338     proc->base.ref++;
2339     pop (4);
2340
2341     for (; i <= limit; i += inc) {
2342         status = _csi_push_ostack_integer (ctx, i);
2343         if (_csi_unlikely (status))
2344             break;
2345
2346         status = _csi_array_execute (ctx, proc);
2347         if (_csi_unlikely (status))
2348             break;
2349     }
2350
2351     if (--proc->base.ref == 0)
2352         csi_array_free (ctx, proc);
2353     return status;
2354 }
2355
2356 static csi_status_t
2357 _ge (csi_t *ctx)
2358 {
2359     csi_status_t status;
2360     csi_object_t *a, *b;
2361     int cmp;
2362
2363     check (2);
2364
2365     b = _csi_peek_ostack (ctx, 0);
2366     a = _csi_peek_ostack (ctx, 1);
2367
2368     status = csi_object_compare (a, b, &cmp);
2369     if (_csi_unlikely (status))
2370         return status;
2371
2372     pop (2);
2373     return _csi_push_ostack_boolean (ctx, cmp >= 0);
2374 }
2375
2376 static csi_status_t
2377 _proxy_get (csi_proxy_t *proxy,
2378             csi_name_t key)
2379 {
2380     csi_object_t obj;
2381     csi_status_t status;
2382
2383     if (_csi_unlikely (proxy == NULL || proxy->dictionary == NULL))
2384         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
2385
2386     status = csi_dictionary_get (proxy->ctx, proxy->dictionary, key, &obj);
2387     if (_csi_unlikely (status))
2388         return status;
2389
2390     return _csi_push_ostack_copy (proxy->ctx, &obj);
2391 }
2392
2393 static csi_status_t
2394 _context_get (csi_t *ctx,
2395               cairo_t *cr,
2396               csi_name_t key)
2397 {
2398     csi_status_t status;
2399     csi_object_t obj;
2400
2401     if (strcmp ((char *) key, "current-point") == 0) {
2402         double x, y;
2403
2404         cairo_get_current_point (cr, &x, &y);
2405
2406         status = _csi_push_ostack_real (ctx, x);
2407         if (_csi_unlikely (status))
2408             return status;
2409         status = _csi_push_ostack_real (ctx, y);
2410         if (_csi_unlikely (status))
2411             return status;
2412
2413         return CSI_STATUS_SUCCESS;
2414     } else if (strcmp ((char *) key, "source") == 0) {
2415         obj.type = CSI_OBJECT_TYPE_PATTERN;
2416         obj.datum.pattern = cairo_pattern_reference (cairo_get_source (cr));
2417     } else if (strcmp ((char *) key, "target") == 0) {
2418         obj.type = CSI_OBJECT_TYPE_SURFACE;
2419         obj.datum.surface = cairo_surface_reference (cairo_get_target (cr));
2420     } else if (strcmp ((char *) key, "group-target") == 0) {
2421         obj.type = CSI_OBJECT_TYPE_SURFACE;
2422         obj.datum.surface = cairo_surface_reference (cairo_get_group_target (cr));
2423     } else if (strcmp ((char *) key, "scaled-font") == 0) {
2424         obj.type = CSI_OBJECT_TYPE_SCALED_FONT;
2425         obj.datum.scaled_font = cairo_scaled_font_reference (cairo_get_scaled_font (cr));
2426     } else if (strcmp ((char *) key, "font-face") == 0) {
2427         obj.type = CSI_OBJECT_TYPE_FONT;
2428         obj.datum.font_face = cairo_font_face_reference (cairo_get_font_face (cr));
2429     } else
2430         return _proxy_get (cairo_get_user_data (cr, &_csi_proxy_key), key);
2431
2432     return push (&obj);
2433 }
2434
2435 static csi_status_t
2436 _font_get (csi_t *ctx,
2437            cairo_font_face_t *font_face,
2438            csi_name_t key)
2439 {
2440     return _proxy_get (cairo_font_face_get_user_data (font_face,
2441                                                       &_csi_proxy_key),
2442                        key);
2443 }
2444
2445 static csi_status_t
2446 _pattern_get (csi_t *ctx,
2447               cairo_pattern_t *pattern,
2448               csi_name_t key)
2449 {
2450     csi_status_t status;
2451
2452     if (strcmp ((char *) key, "type") == 0)
2453         return _csi_push_ostack_integer (ctx, cairo_pattern_get_type (pattern));
2454
2455     if (strcmp ((char *) key, "filter") == 0)
2456         return _csi_push_ostack_integer (ctx, cairo_pattern_get_filter (pattern));
2457
2458     if (strcmp ((char *) key, "extend") == 0)
2459         return _csi_push_ostack_integer (ctx, cairo_pattern_get_extend (pattern));
2460
2461     if (strcmp ((char *) key, "matrix") == 0) {
2462         csi_object_t obj;
2463         cairo_matrix_t m;
2464
2465         cairo_pattern_get_matrix (pattern, &m);
2466         status = csi_matrix_new_from_matrix (ctx, &obj, &m);
2467         if (_csi_unlikely (status))
2468             return status;
2469
2470         return push (&obj);
2471     }
2472
2473     return _proxy_get (cairo_pattern_get_user_data (pattern, &_csi_proxy_key),
2474                        key);
2475 }
2476
2477 static csi_status_t
2478 _scaled_font_get (csi_t *ctx,
2479                   cairo_scaled_font_t *font,
2480                   csi_name_t key)
2481 {
2482     return _proxy_get (cairo_scaled_font_get_user_data (font, &_csi_proxy_key),
2483                        key);
2484 }
2485
2486 static csi_status_t
2487 _surface_get (csi_t *ctx,
2488               cairo_surface_t *surface,
2489               csi_name_t key)
2490 {
2491     if (strcmp ((char *) key, "type") == 0) {
2492         return _csi_push_ostack_integer (ctx, cairo_surface_get_type (surface));
2493     }
2494
2495     if (strcmp ((char *) key, "content") == 0) {
2496         return _csi_push_ostack_integer (ctx,
2497                                          cairo_surface_get_content (surface));
2498     }
2499
2500     return _proxy_get (cairo_surface_get_user_data (surface, &_csi_proxy_key),
2501                        key);
2502 }
2503
2504 static csi_status_t
2505 _get (csi_t *ctx)
2506 {
2507     csi_object_t *key, *src, obj;
2508     csi_status_t status;
2509     int type;
2510
2511     check (2);
2512
2513     key = _csi_peek_ostack (ctx, 0);
2514     src = _csi_peek_ostack (ctx, 1);
2515     pop (1);
2516     type = csi_object_get_type (src);
2517     switch (type) {
2518     case CSI_OBJECT_TYPE_DICTIONARY:
2519         if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_NAME))
2520             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
2521
2522         status = csi_dictionary_get (ctx,
2523                                      src->datum.dictionary,
2524                                      key->datum.name,
2525                                      &obj);
2526         break;
2527     case CSI_OBJECT_TYPE_ARRAY:
2528         if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_INTEGER))
2529             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
2530
2531         status = csi_array_get (ctx,
2532                                 src->datum.array,
2533                                 key->datum.integer,
2534                                 &obj);
2535         break;
2536 #if 0
2537     case CSI_OBJECT_TYPE_STRING:
2538         status = csi_string_get (src, key, &obj);
2539         break;
2540 #endif
2541
2542     case CSI_OBJECT_TYPE_CONTEXT:
2543         if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_NAME))
2544             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
2545         return _context_get (ctx, src->datum.cr, key->datum.name);
2546
2547     case CSI_OBJECT_TYPE_FONT:
2548         if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_NAME))
2549             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
2550         return _font_get (ctx, src->datum.font_face, key->datum.name);
2551
2552     case CSI_OBJECT_TYPE_PATTERN:
2553         if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_NAME))
2554             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
2555         return _pattern_get (ctx, src->datum.pattern, key->datum.name);
2556
2557     case CSI_OBJECT_TYPE_SCALED_FONT:
2558         if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_NAME))
2559             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
2560         return _scaled_font_get (ctx, src->datum.scaled_font, key->datum.name);
2561
2562     case CSI_OBJECT_TYPE_SURFACE:
2563         if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_NAME))
2564             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
2565         return _surface_get (ctx, src->datum.surface, key->datum.name);
2566
2567     default:
2568         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
2569     }
2570
2571     if (_csi_unlikely (status))
2572         return status;
2573
2574     return _csi_push_ostack_copy (ctx, &obj);
2575 }
2576
2577 struct glyph_advance_cache {
2578     csi_t *ctx;
2579     double glyph_advance[256][2];
2580     unsigned long have_glyph_advance[256];
2581 };
2582
2583 static void
2584 glyph_advance_cache_destroy (void *closure)
2585 {
2586     struct glyph_advance_cache *cache = closure;
2587     _csi_free (cache->ctx, cache);
2588 }
2589
2590 static int
2591 _glyph_string (csi_t *ctx,
2592                csi_array_t *array,
2593                cairo_scaled_font_t *scaled_font,
2594                cairo_glyph_t *glyphs)
2595 {
2596     struct glyph_advance_cache uncached;
2597     struct glyph_advance_cache *cache;
2598     csi_integer_t nglyphs, i, j;
2599     double x, y, dx;
2600     cairo_status_t status;
2601
2602     if (cairo_scaled_font_status (scaled_font))
2603         return 0;
2604
2605     cache = cairo_scaled_font_get_user_data (scaled_font,
2606                                              (cairo_user_data_key_t *) ctx);
2607     if (cache == NULL) {
2608         cache = _csi_alloc (ctx, sizeof (*cache));
2609         if (_csi_likely (cache != NULL)) {
2610             cache->ctx = ctx;
2611             memset (cache->have_glyph_advance, 0xff,
2612                     sizeof (cache->have_glyph_advance));
2613
2614             status = cairo_scaled_font_set_user_data (scaled_font,
2615                                                       (cairo_user_data_key_t *) ctx,
2616                                                       cache,
2617                                                       glyph_advance_cache_destroy);
2618             if (_csi_unlikely (status)) {
2619                 _csi_free (ctx, cache);
2620                 cache = NULL;
2621             }
2622         }
2623     }
2624
2625     if (_csi_unlikely (cache == NULL)) {
2626         cache = &uncached;
2627
2628         cache->ctx = ctx;
2629         memset (cache->have_glyph_advance, 0xff,
2630                 sizeof (cache->have_glyph_advance));
2631     }
2632
2633     nglyphs = 0;
2634     x = y = 0;
2635     for (i = 0; i < array->stack.len; i++) {
2636         const csi_object_t *obj = &array->stack.objects[i];
2637         int type = csi_object_get_type (obj);
2638
2639         switch (type) {
2640         case CSI_OBJECT_TYPE_ARRAY: {
2641             const csi_array_t *glyph_array = obj->datum.array;
2642             for (j = 0; j < glyph_array->stack.len; j++) {
2643                 unsigned long g;
2644                 int gi;
2645
2646                 obj = &glyph_array->stack.objects[j];
2647                 if (csi_object_get_type (obj) != CSI_OBJECT_TYPE_INTEGER)
2648                     break;
2649                 g = obj->datum.integer;
2650
2651                 glyphs[nglyphs].index = g;
2652                 glyphs[nglyphs].x = x;
2653                 glyphs[nglyphs].y = y;
2654
2655                 gi = g % ARRAY_LENGTH (cache->have_glyph_advance);
2656                 if (cache->have_glyph_advance[gi] != g) {
2657                     cairo_text_extents_t extents;
2658
2659                     cairo_scaled_font_glyph_extents (scaled_font,
2660                                                      &glyphs[nglyphs], 1,
2661                                                      &extents);
2662
2663                     cache->glyph_advance[gi][0] = extents.x_advance;
2664                     cache->glyph_advance[gi][1] = extents.y_advance;
2665                     cache->have_glyph_advance[gi] = g;
2666                 }
2667
2668                 x += cache->glyph_advance[gi][0];
2669                 y += cache->glyph_advance[gi][1];
2670                 nglyphs++;
2671             }
2672             break;
2673         }
2674
2675         case CSI_OBJECT_TYPE_STRING: {
2676             const csi_string_t *glyph_string = obj->datum.string;
2677             for (j = 0; j < glyph_string->len; j++) {
2678                 uint8_t g;
2679
2680                 g = glyph_string->string[j];
2681                 glyphs[nglyphs].index = g;
2682                 glyphs[nglyphs].x = x;
2683                 glyphs[nglyphs].y = y;
2684
2685                 if (cache->have_glyph_advance[g] != g) {
2686                     cairo_text_extents_t extents;
2687
2688                     cairo_scaled_font_glyph_extents (scaled_font,
2689                                                      &glyphs[nglyphs], 1,
2690                                                      &extents);
2691
2692                     cache->glyph_advance[g][0] = extents.x_advance;
2693                     cache->glyph_advance[g][1] = extents.y_advance;
2694                     cache->have_glyph_advance[g] = g;
2695                 }
2696
2697                 x += cache->glyph_advance[g][0];
2698                 y += cache->glyph_advance[g][1];
2699                 nglyphs++;
2700             }
2701             break;
2702         }
2703
2704         case CSI_OBJECT_TYPE_INTEGER:
2705         case CSI_OBJECT_TYPE_REAL: /* dx or x*/
2706             dx = csi_number_get_value (obj);
2707             if (i+1 == array->stack.len)
2708                 break;
2709
2710             type = csi_object_get_type (&array->stack.objects[i+1]);
2711             switch (type) {
2712             case CSI_OBJECT_TYPE_INTEGER:
2713             case CSI_OBJECT_TYPE_REAL: /* y */
2714                 y = csi_number_get_value (&array->stack.objects[i+1]);
2715                 x = dx;
2716                 i++;
2717                 break;
2718
2719             default:
2720                 x += dx;
2721             }
2722         }
2723     }
2724
2725     return nglyphs;
2726 }
2727
2728 static csi_status_t
2729 _glyph_path (csi_t *ctx)
2730 {
2731     csi_array_t *array;
2732     csi_status_t status;
2733     cairo_t *cr;
2734     cairo_glyph_t stack_glyphs[256], *glyphs;
2735     csi_integer_t nglyphs, i;
2736
2737     check (2);
2738
2739     status = _csi_ostack_get_array (ctx, 0, &array);
2740     if (_csi_unlikely (status))
2741         return status;
2742     status = _csi_ostack_get_context (ctx, 1, &cr);
2743     if (_csi_unlikely (status))
2744         return status;
2745
2746     /* count glyphs */
2747     nglyphs = 0;
2748     for (i = 0; i < array->stack.len; i++) {
2749         csi_object_t *obj = &array->stack.objects[i];
2750         int type = csi_object_get_type (obj);
2751         switch (type) {
2752         case CSI_OBJECT_TYPE_ARRAY:
2753             nglyphs += obj->datum.array->stack.len;
2754             break;
2755         case CSI_OBJECT_TYPE_STRING:
2756             nglyphs += obj->datum.string->len;
2757             break;
2758         }
2759     }
2760     if (nglyphs == 0) {
2761         pop (1);
2762         return CSI_STATUS_SUCCESS;
2763     }
2764
2765     if (nglyphs > ARRAY_LENGTH (stack_glyphs)) {
2766         if (_csi_unlikely ((unsigned) nglyphs >= INT_MAX / sizeof (cairo_glyph_t)))
2767             return _csi_error (CSI_STATUS_NO_MEMORY);
2768
2769         glyphs = _csi_alloc (ctx, sizeof (cairo_glyph_t) * nglyphs);
2770         if (_csi_unlikely (glyphs == NULL))
2771             return _csi_error (CSI_STATUS_NO_MEMORY);
2772     } else
2773         glyphs = stack_glyphs;
2774
2775     nglyphs = _glyph_string (ctx, array, cairo_get_scaled_font (cr), glyphs);
2776     cairo_glyph_path (cr, glyphs, nglyphs);
2777
2778     if (glyphs != stack_glyphs)
2779         _csi_free (ctx, glyphs);
2780
2781     pop (1);
2782     return CSI_STATUS_SUCCESS;
2783 }
2784
2785 static csi_status_t
2786 _gray (csi_t *ctx)
2787 {
2788     csi_object_t obj;
2789     csi_status_t status;
2790     double g;
2791
2792     check (1);
2793
2794     status = _csi_ostack_get_number (ctx, 0, &g);
2795     if (_csi_unlikely (status))
2796         return status;
2797
2798     pop (1);
2799
2800     obj.type = CSI_OBJECT_TYPE_PATTERN;
2801     obj.datum.pattern = cairo_pattern_create_rgba (g, g, g, 1);
2802     return push (&obj);
2803 }
2804
2805 static csi_status_t
2806 _gt (csi_t *ctx)
2807 {
2808     csi_status_t status;
2809     csi_object_t *a, *b;
2810     int cmp;
2811
2812     check (2);
2813
2814     b = _csi_peek_ostack (ctx, 0);
2815     a = _csi_peek_ostack (ctx, 1);
2816
2817     status = csi_object_compare (a, b, &cmp);
2818     if (_csi_unlikely (status))
2819         return status;
2820
2821     pop (2);
2822     return _csi_push_ostack_boolean (ctx, cmp > 0);
2823 }
2824
2825 static csi_status_t
2826 _identity (csi_t *ctx)
2827 {
2828     csi_object_t obj;
2829     csi_status_t status;
2830
2831     status = csi_matrix_new (ctx, &obj);
2832     if (_csi_unlikely (status))
2833         return status;
2834
2835     return push (&obj);
2836 }
2837
2838 static csi_status_t
2839 _if (csi_t *ctx)
2840 {
2841     csi_array_t *proc;
2842     csi_boolean_t predicate = FALSE; /* silence the compiler */
2843     csi_status_t status;
2844
2845     check (2);
2846
2847     status = _csi_ostack_get_procedure (ctx, 0, &proc);
2848     if (_csi_unlikely (status))
2849         return status;
2850
2851     status = _csi_ostack_get_boolean (ctx, 1, &predicate);
2852     if (_csi_unlikely (status))
2853         return status;
2854
2855     proc->base.ref++;
2856     pop (2);
2857
2858     if (predicate)
2859         status = _csi_array_execute (ctx, proc);
2860
2861     if (--proc->base.ref == 0)
2862         csi_array_free (ctx, proc);
2863
2864     return status;
2865 }
2866
2867 static csi_status_t
2868 _ifelse (csi_t *ctx)
2869 {
2870     csi_array_t *true_proc, *false_proc;
2871     csi_boolean_t predicate = FALSE; /* silence the compiler */
2872     csi_status_t status;
2873
2874     check (3);
2875
2876     status = _csi_ostack_get_procedure (ctx, 0, &false_proc);
2877     if (_csi_unlikely (status))
2878         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
2879
2880     status = _csi_ostack_get_procedure (ctx, 1, &true_proc);
2881     if (_csi_unlikely (status))
2882         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
2883
2884     status = _csi_ostack_get_boolean (ctx, 2, &predicate);
2885     if (_csi_unlikely (status))
2886         return status;
2887
2888     true_proc->base.ref++;
2889     false_proc->base.ref++;
2890     pop (3);
2891
2892     if (predicate)
2893         status = _csi_array_execute (ctx, true_proc);
2894     else
2895         status = _csi_array_execute (ctx, false_proc);
2896
2897     if (--true_proc->base.ref == 0)
2898         csi_array_free (ctx, true_proc);
2899     if (--false_proc->base.ref == 0)
2900         csi_array_free (ctx, false_proc);
2901
2902     return status;
2903 }
2904
2905 static csi_status_t
2906 _image_read_raw (csi_t *ctx,
2907                  csi_object_t *src,
2908                  cairo_format_t format,
2909                  int width, int height,
2910                  cairo_surface_t **image_out)
2911 {
2912     cairo_surface_t *image;
2913     uint8_t *bp, *data;
2914     int rem, len, ret, x, rowlen, instride, stride;
2915     cairo_status_t status;
2916
2917     if (width == 0 || height == 0) {
2918         *image_out = cairo_image_surface_create (format, 0, 0);
2919         return CSI_STATUS_SUCCESS;
2920     }
2921
2922     if (ctx->hooks.create_source_image != NULL) {
2923         image = ctx->hooks.create_source_image (ctx->hooks.closure,
2924                                                 format, width, height,
2925                                                 0);
2926
2927         stride = cairo_image_surface_get_stride (image);
2928         data = cairo_image_surface_get_data (image);
2929         if (data == NULL)
2930             return CAIRO_STATUS_NULL_POINTER;
2931         } else {
2932         stride = cairo_format_stride_for_width (format, width);
2933         data = malloc (stride * height);
2934         if (data == NULL)
2935             return CAIRO_STATUS_NO_MEMORY;
2936
2937         image = cairo_image_surface_create_for_data (data, format,
2938                                                      width, height, stride);
2939         status = cairo_surface_set_user_data (image,
2940                                               (const cairo_user_data_key_t *) image,
2941                                               data, free);
2942         if (status) {
2943             cairo_surface_destroy (image);
2944             free (image);
2945             return status;
2946         }
2947     }
2948
2949     switch (format) {
2950     case CAIRO_FORMAT_A1:
2951         instride = rowlen = (width+7)/8;
2952         break;
2953     case CAIRO_FORMAT_A8:
2954         instride = rowlen = width;
2955         break;
2956     case CAIRO_FORMAT_RGB16_565:
2957         instride = rowlen = 2 * width;
2958         break;
2959     case CAIRO_FORMAT_RGB24:
2960         rowlen = 3 * width;
2961         instride = 4 *width;
2962         break;
2963     default:
2964     case CAIRO_FORMAT_RGB30:
2965     case CAIRO_FORMAT_INVALID:
2966     case CAIRO_FORMAT_ARGB32:
2967         instride = rowlen = 4 * width;
2968         break;
2969     }
2970     len = rowlen * height;
2971
2972     if (rowlen == instride &&
2973         src->type == CSI_OBJECT_TYPE_STRING &&
2974         len == src->datum.string->deflate)
2975     {
2976         csi_string_t *s = src->datum.string;
2977         unsigned long out = s->deflate;
2978
2979         switch (s->method) {
2980         default:
2981         case NONE:
2982 err_decompress:
2983             cairo_surface_destroy (image);
2984             return _csi_error (CSI_STATUS_READ_ERROR);
2985
2986 #if HAVE_ZLIB
2987         case ZLIB:
2988             if (uncompress ((Bytef *) data, &out,
2989                             (Bytef *) s->string, s->len) != Z_OK)
2990                 goto err_decompress;
2991             break;
2992 #endif
2993
2994 #if HAVE_LZO
2995         case LZO:
2996             if (lzo2a_decompress ((Bytef *) s->string, s->len,
2997                                   (Bytef *) data, &out,
2998                                   NULL))
2999                 goto err_decompress;
3000             break;
3001 #endif
3002         }
3003     }
3004     else
3005     {
3006         csi_object_t file;
3007
3008         status = csi_object_as_file (ctx, src, &file);
3009         if (_csi_unlikely (status)) {
3010             cairo_surface_destroy (image);
3011             return status;
3012         }
3013
3014         bp = data;
3015         rem = len;
3016         while (rem) {
3017             ret = csi_file_read (file.datum.file, bp, rem);
3018             if (_csi_unlikely (ret == 0)) {
3019                 cairo_surface_destroy (image);
3020                 return _csi_error (CSI_STATUS_READ_ERROR);
3021             }
3022             rem -= ret;
3023             bp += ret;
3024         }
3025
3026         if (len != height * stride) {
3027             while (--height) {
3028                 uint8_t *row = data + height * stride;
3029
3030                 /* XXX pixel conversion */
3031                 switch (format) {
3032                 case CAIRO_FORMAT_A1:
3033                     for (x = rowlen; x--; ) {
3034                         uint8_t byte = *--bp;
3035                         row[x] = CSI_BITSWAP8_IF_LITTLE_ENDIAN (byte);
3036                     }
3037                     break;
3038                 case CAIRO_FORMAT_A8:
3039                     for (x = width; x--; )
3040                         row[x] = *--bp;
3041                     break;
3042                 case CAIRO_FORMAT_RGB16_565:
3043                     for (x = width; x--; ) {
3044 #ifdef WORDS_BIGENDIAN
3045                         row[2*x + 1] = *--bp;
3046                         row[2*x + 0] = *--bp;
3047 #else
3048                         row[2*x + 0] = *--bp;
3049                         row[2*x + 1] = *--bp;
3050 #endif
3051                     }
3052                     break;
3053                 case CAIRO_FORMAT_RGB24:
3054                     for (x = width; x--; ) {
3055 #ifdef WORDS_BIGENDIAN
3056                         row[4*x + 3] = *--bp;
3057                         row[4*x + 2] = *--bp;
3058                         row[4*x + 1] = *--bp;
3059                         row[4*x + 0] = 0xff;
3060 #else
3061                         row[4*x + 0] = *--bp;
3062                         row[4*x + 1] = *--bp;
3063                         row[4*x + 2] = *--bp;
3064                         row[4*x + 3] = 0xff;
3065 #endif
3066                     }
3067                     break;
3068                 case CAIRO_FORMAT_RGB30:
3069                 case CAIRO_FORMAT_INVALID:
3070                 case CAIRO_FORMAT_ARGB32:
3071                     /* stride == width */
3072                     break;
3073                 }
3074
3075                 memset (row + instride, 0, stride - instride);
3076             }
3077
3078             /* need to treat last row carefully */
3079             switch (format) {
3080             case CAIRO_FORMAT_A1:
3081                 for (x = rowlen; x--; ) {
3082                     uint8_t byte = *--bp;
3083                     data[x] = CSI_BITSWAP8_IF_LITTLE_ENDIAN (byte);
3084                 }
3085                 break;
3086             case CAIRO_FORMAT_A8:
3087                 for (x = width; x--; )
3088                     data[x] = *--bp;
3089                 break;
3090             case CAIRO_FORMAT_RGB16_565:
3091                 for (x = width; x--; ) {
3092 #ifdef WORDS_BIGENDIAN
3093                     data[2*x + 1] = *--bp;
3094                     data[2*x + 0] = *--bp;
3095 #else
3096                     data[2*x + 0] = *--bp;
3097                     data[2*x + 1] = *--bp;
3098 #endif
3099                 }
3100                 break;
3101             case CAIRO_FORMAT_RGB24:
3102                 for (x = width; --x>1; ) {
3103 #ifdef WORDS_BIGENDIAN
3104                     data[4*x + 3] = *--bp;
3105                     data[4*x + 2] = *--bp;
3106                     data[4*x + 1] = *--bp;
3107                     data[4*x + 0] = 0xff;
3108 #else
3109                     data[4*x + 0] = *--bp;
3110                     data[4*x + 1] = *--bp;
3111                     data[4*x + 2] = *--bp;
3112                     data[4*x + 3] = 0xff;
3113 #endif
3114                 }
3115                 if (width > 1) {
3116                     uint8_t rgb[2][3];
3117                     /* shuffle the last couple of overlapping pixels */
3118                     rgb[1][0] = data[5];
3119                     rgb[1][1] = data[4];
3120                     rgb[1][2] = data[3];
3121                     rgb[0][0] = data[2];
3122                     rgb[0][1] = data[1];
3123                     rgb[0][2] = data[0];
3124 #ifdef WORDS_BIGENDIAN
3125                     data[4] = 0xff;
3126                     data[5] = rgb[1][2];
3127                     data[6] = rgb[1][1];
3128                     data[7] = rgb[1][0];
3129                     data[0] = 0xff;
3130                     data[1] = rgb[0][2];
3131                     data[2] = rgb[0][1];
3132                     data[3] = rgb[0][0];
3133 #else
3134                     data[7] = 0xff;
3135                     data[6] = rgb[1][2];
3136                     data[5] = rgb[1][1];
3137                     data[4] = rgb[1][0];
3138                     data[3] = 0xff;
3139                     data[2] = rgb[0][2];
3140                     data[1] = rgb[0][1];
3141                     data[0] = rgb[0][0];
3142 #endif
3143                 } else {
3144 #ifdef WORDS_BIGENDIAN
3145                     data[0] = 0xff;
3146                     data[1] = data[0];
3147                     data[2] = data[1];
3148                     data[3] = data[2];
3149 #else
3150                     data[3] = data[0];
3151                     data[0] = data[2];
3152                     data[2] = data[3];
3153                     data[3] = 0xff;
3154 #endif
3155                 }
3156                 break;
3157             case CAIRO_FORMAT_RGB30:
3158             case CAIRO_FORMAT_INVALID:
3159             case CAIRO_FORMAT_ARGB32:
3160                 /* stride == width */
3161                 break;
3162             }
3163             memset (data + instride, 0, stride - instride);
3164         } else {
3165 #ifndef WORDS_BIGENDIAN
3166             switch (format) {
3167             case CAIRO_FORMAT_A1:
3168                 for (x = 0; x < len; x++) {
3169                     uint8_t byte = data[x];
3170                     data[x] = CSI_BITSWAP8_IF_LITTLE_ENDIAN (byte);
3171                 }
3172                 break;
3173             case CAIRO_FORMAT_RGB16_565:
3174                 {
3175                     uint32_t *rgba = (uint32_t *) data;
3176                     for (x = len/2; x--; rgba++) {
3177                         *rgba = bswap_16 (*rgba);
3178                     }
3179                 }
3180                 break;
3181             case CAIRO_FORMAT_ARGB32:
3182                 {
3183                     uint32_t *rgba = (uint32_t *) data;
3184                     for (x = len/4; x--; rgba++) {
3185                         *rgba = bswap_32 (*rgba);
3186                     }
3187                 }
3188                 break;
3189
3190             case CAIRO_FORMAT_A8:
3191                 break;
3192
3193             case CAIRO_FORMAT_RGB30:
3194             case CAIRO_FORMAT_RGB24:
3195             case CAIRO_FORMAT_INVALID:
3196             default:
3197                 break;
3198             }
3199 #endif
3200         }
3201         csi_object_free (ctx, &file);
3202     }
3203
3204     cairo_surface_mark_dirty (image);
3205     *image_out = image;
3206     return CSI_STATUS_SUCCESS;
3207 }
3208
3209 static cairo_status_t
3210 png_read_func (void *closure, uint8_t *data, unsigned int len)
3211 {
3212     int ret;
3213
3214     ret = csi_file_read (closure, data, len);
3215     if ((unsigned int) ret != len)
3216         return CAIRO_STATUS_READ_ERROR;
3217
3218     return CAIRO_STATUS_SUCCESS;
3219 }
3220
3221 static csi_status_t
3222 _image_read_png (csi_file_t *src, cairo_surface_t **out)
3223 {
3224 #if CAIRO_HAS_PNG_FUNCTIONS
3225     *out = cairo_image_surface_create_from_png_stream (png_read_func, src);
3226     return cairo_surface_status (*out);
3227 #else
3228     return CAIRO_STATUS_READ_ERROR;
3229 #endif
3230 }
3231
3232 struct _image_tag {
3233     csi_t *ctx;
3234     csi_blob_t blob;
3235     cairo_surface_t *surface;
3236 };
3237
3238 static void
3239 _image_tag_done (void *closure)
3240 {
3241     struct _image_tag *tag = closure;
3242     csi_t *ctx = tag->ctx;
3243
3244     ctx->_images = _csi_list_unlink (ctx->_images, &tag->blob.list);
3245     _csi_slab_free (ctx, tag, sizeof (*tag));
3246     cairo_script_interpreter_destroy (ctx);
3247 }
3248
3249 static void
3250 _image_hash (csi_blob_t *blob,
3251              cairo_surface_t *surface)
3252 {
3253     uint32_t  value;
3254
3255     value = cairo_image_surface_get_width (surface);
3256     _csi_blob_hash (blob, &value, 1);
3257
3258     value = cairo_image_surface_get_height (surface);
3259     _csi_blob_hash (blob, &value, 1);
3260
3261     value = cairo_image_surface_get_format (surface);
3262     _csi_blob_hash (blob, &value, 1);
3263 }
3264
3265 static cairo_surface_t *
3266 _image_cached (csi_t *ctx, cairo_surface_t *surface)
3267 {
3268     csi_blob_t tmpl;
3269     csi_list_t *link;
3270     uint8_t *data;
3271     int stride, height;
3272     struct _image_tag *tag;
3273
3274     /* check for an existing image  */
3275
3276     data = cairo_image_surface_get_data (surface);
3277     stride = cairo_image_surface_get_stride (surface);
3278     height = cairo_image_surface_get_height (surface);
3279     _csi_blob_init (&tmpl, data, stride * height);
3280     _image_hash (&tmpl, surface);
3281     link = _csi_list_find (ctx->_images, _csi_blob_equal, &tmpl);
3282     if (link) {
3283         cairo_surface_destroy (surface);
3284         tag = csi_container_of (link, struct _image_tag, blob.list);
3285         return cairo_surface_reference (tag->surface);
3286     }
3287
3288     /* none found, insert a tag for this one */
3289
3290     tag = _csi_slab_alloc (ctx, sizeof (struct _image_tag));
3291     if (tag == NULL)
3292         return surface;
3293
3294     ctx->_images = _csi_list_prepend (ctx->_images, &tag->blob.list);
3295     tag->ctx = cairo_script_interpreter_reference (ctx);
3296     tag->blob.hash = tmpl.hash;
3297     tag->blob.bytes = tmpl.bytes;
3298     tag->blob.len = tmpl.len;
3299     tag->surface = surface;
3300
3301     if (cairo_surface_set_user_data (surface, &_csi_blob_key,
3302                                      tag, _image_tag_done))
3303     {
3304         _image_tag_done (tag);
3305     }
3306
3307     return surface;
3308 }
3309
3310 static csi_status_t
3311 _image_load_from_dictionary (csi_t *ctx,
3312                              csi_dictionary_t *dict,
3313                              cairo_surface_t **image_out)
3314 {
3315     csi_object_t obj, key;
3316     long width;
3317     long height;
3318     long format;
3319     cairo_surface_t *image = NULL; /* silence the compiler */
3320     csi_status_t status;
3321
3322     /* check for "status? */
3323
3324     status = _csi_dictionary_get_integer (ctx, dict, "width", FALSE, &width);
3325     if (_csi_unlikely (status))
3326         return status;
3327     status = _csi_dictionary_get_integer (ctx, dict, "height", FALSE, &height);
3328     if (_csi_unlikely (status))
3329         return status;
3330
3331     format = CAIRO_FORMAT_ARGB32;
3332     status = _csi_dictionary_get_integer (ctx, dict, "format", TRUE, &format);
3333     if (_csi_unlikely (status))
3334         return status;
3335
3336     status = csi_name_new_static (ctx, &key, "source");
3337     if (_csi_unlikely (status))
3338         return status;
3339
3340     if (csi_dictionary_has (dict, key.datum.name)) {
3341         enum mime_type mime_type;
3342         csi_object_t file;
3343
3344         status = csi_dictionary_get (ctx, dict, key.datum.name, &obj);
3345         if (_csi_unlikely (status))
3346             return status;
3347
3348         status = csi_name_new_static (ctx, &key, "mime-type");
3349         if (_csi_unlikely (status))
3350             return status;
3351
3352         mime_type = MIME_TYPE_NONE;
3353         if (csi_dictionary_has (dict, key.datum.name)) {
3354             csi_object_t type_obj;
3355             const char *type_str;
3356             int type;
3357
3358             status = csi_dictionary_get (ctx, dict, key.datum.name, &type_obj);
3359             if (_csi_unlikely (status))
3360                 return status;
3361
3362             type = csi_object_get_type (&type_obj);
3363             switch (type) {
3364             case CSI_OBJECT_TYPE_STRING:
3365                 type_str = type_obj.datum.string->string;
3366                 break;
3367             case CSI_OBJECT_TYPE_NAME:
3368                 type_str = (char *) type_obj.datum.name;
3369                 break;
3370             default:
3371                 return _csi_error (CSI_STATUS_INVALID_SCRIPT);
3372             }
3373
3374             if (strcmp (type_str, CAIRO_MIME_TYPE_PNG) == 0)
3375                 mime_type = MIME_TYPE_PNG;
3376         }
3377
3378
3379         /* XXX hook for general mime-type decoder */
3380
3381         switch (mime_type) {
3382         case MIME_TYPE_NONE:
3383             status = _image_read_raw (ctx, &obj, format, width, height, &image);
3384             break;
3385         case MIME_TYPE_PNG:
3386             status = csi_object_as_file (ctx, &obj, &file);
3387             if (_csi_unlikely (status))
3388                 return status;
3389
3390             status = _image_read_png (file.datum.file, &image);
3391             csi_object_free (ctx, &file);
3392             break;
3393         }
3394         if (_csi_unlikely (status)) {
3395             cairo_surface_destroy (image);
3396             return status;
3397         }
3398         image = _image_cached (ctx, image);
3399     } else
3400         image = cairo_image_surface_create (format, width, height);
3401
3402     *image_out = image;
3403     return CSI_STATUS_SUCCESS;
3404 }
3405
3406 static csi_status_t
3407 _image (csi_t *ctx)
3408 {
3409     csi_dictionary_t *dict;
3410     cairo_surface_t *image;
3411     csi_status_t status;
3412     csi_object_t obj;
3413
3414     check (1);
3415
3416     status = _csi_ostack_get_dictionary (ctx, 0, &dict);
3417     if (_csi_unlikely (status))
3418         return status;
3419
3420     status = _image_load_from_dictionary (ctx, dict, &image);
3421     if (_csi_unlikely (status)) {
3422         cairo_surface_destroy (image);
3423         return status;
3424     }
3425
3426     pop (1);
3427     obj.type = CSI_OBJECT_TYPE_SURFACE;
3428     obj.datum.surface = image;
3429     return push (&obj);
3430 }
3431
3432 static csi_status_t
3433 _index (csi_t *ctx)
3434 {
3435     csi_status_t status;
3436     long n;
3437
3438     check (1);
3439
3440     status = _csi_ostack_get_integer (ctx, 0,  &n);
3441     if (_csi_unlikely (status))
3442         return status;
3443
3444     pop (1);
3445
3446     check (n);
3447     return _csi_push_ostack_copy (ctx, _csi_peek_ostack (ctx, n));
3448 }
3449
3450 static csi_status_t
3451 _integer (csi_t *ctx)
3452 {
3453     csi_object_t *obj;
3454     int type;
3455
3456     check (1);
3457
3458     obj = _csi_peek_ostack (ctx, 0);
3459     type = csi_object_get_type (obj);
3460     switch (type) {
3461     case CSI_OBJECT_TYPE_INTEGER:
3462         break;
3463     case CSI_OBJECT_TYPE_REAL:
3464         obj->datum.integer = obj->datum.real;
3465         break;
3466     default:
3467         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
3468     }
3469     obj->type = CSI_OBJECT_TYPE_INTEGER;
3470
3471     return CSI_STATUS_SUCCESS;
3472 }
3473
3474 static csi_status_t
3475 _invert (csi_t *ctx)
3476 {
3477     csi_object_t obj;
3478     csi_status_t status;
3479     cairo_matrix_t m;
3480
3481     check (1);
3482
3483     status = _csi_ostack_get_matrix (ctx, 0, &m);
3484     if (_csi_unlikely (status))
3485         return status;
3486
3487     cairo_matrix_invert (&m);
3488
3489     status = csi_matrix_new_from_matrix (ctx, &obj, &m);
3490     if (_csi_unlikely (status))
3491         return status;
3492
3493     pop (1);
3494
3495     return push (&obj);
3496 }
3497
3498 static csi_status_t
3499 _le (csi_t *ctx)
3500 {
3501     csi_status_t status;
3502     csi_object_t *a, *b;
3503     int cmp;
3504
3505     check (2);
3506
3507     b = _csi_peek_ostack (ctx, 0);
3508     a = _csi_peek_ostack (ctx, 1);
3509
3510     status = csi_object_compare (a, b, &cmp);
3511     if (_csi_unlikely (status))
3512         return status;
3513
3514     pop (2);
3515     return _csi_push_ostack_boolean (ctx, cmp <= 0);
3516 }
3517
3518 static csi_status_t
3519 _linear (csi_t *ctx)
3520 {
3521     csi_object_t obj;
3522     csi_status_t status;
3523     double x1, y1, x2, y2;
3524
3525     check (4);
3526
3527     status = _csi_ostack_get_number (ctx, 0, &y2);
3528     if (_csi_unlikely (status))
3529         return status;
3530     status = _csi_ostack_get_number (ctx, 1, &x2);
3531     if (_csi_unlikely (status))
3532         return status;
3533     status = _csi_ostack_get_number (ctx, 2, &y1);
3534     if (_csi_unlikely (status))
3535         return status;
3536     status = _csi_ostack_get_number (ctx, 3, &x1);
3537     if (_csi_unlikely (status))
3538         return status;
3539
3540     pop (4);
3541
3542     obj.type = CSI_OBJECT_TYPE_PATTERN;
3543     obj.datum.pattern = cairo_pattern_create_linear (x1, y1, x2, y2);
3544     return push (&obj);
3545 }
3546
3547 static csi_status_t
3548 _line_to (csi_t *ctx)
3549 {
3550     csi_status_t status;
3551     csi_object_t *obj;
3552     int type;
3553     double x, y;
3554
3555     check (3);
3556
3557     status = _csi_ostack_get_number (ctx, 0, &y);
3558     if (_csi_unlikely (status))
3559         return status;
3560     status = _csi_ostack_get_number (ctx, 1, &x);
3561     if (_csi_unlikely (status))
3562         return status;
3563
3564     /* XXX path object */
3565
3566     obj = _csi_peek_ostack (ctx, 2);
3567     type = csi_object_get_type (obj);
3568     switch (type) {
3569     case CSI_OBJECT_TYPE_CONTEXT:
3570         cairo_line_to (obj->datum.cr, x, y);
3571         break;
3572     case CSI_OBJECT_TYPE_PATTERN:
3573         cairo_mesh_pattern_line_to (obj->datum.pattern, x, y);
3574         break;
3575     }
3576
3577     pop (2);
3578     return CSI_STATUS_SUCCESS;
3579 }
3580
3581 static csi_status_t
3582 _lt (csi_t *ctx)
3583 {
3584     csi_status_t status;
3585     csi_object_t *a, *b;
3586     int cmp;
3587
3588     check (2);
3589
3590     b = _csi_peek_ostack (ctx, 0);
3591     a = _csi_peek_ostack (ctx, 1);
3592
3593     status = csi_object_compare (a, b, &cmp);
3594     if (_csi_unlikely (status))
3595         return status;
3596
3597     pop (2);
3598     return _csi_push_ostack_boolean (ctx, cmp < 0);
3599 }
3600
3601 static csi_status_t
3602 _mark (csi_t *ctx)
3603 {
3604     return _csi_push_ostack_mark (ctx);
3605 }
3606
3607 static csi_status_t
3608 _ne (csi_t *ctx)
3609 {
3610     csi_object_t *a, *b;
3611     csi_boolean_t v;
3612
3613     check (2);
3614
3615     b = _csi_peek_ostack (ctx, 0);
3616     a = _csi_peek_ostack (ctx, 1);
3617
3618     v = ! csi_object_eq (a, b);
3619
3620     pop (2);
3621     return _csi_push_ostack_boolean (ctx, v);
3622 }
3623
3624 static csi_status_t
3625 _neg (csi_t *ctx)
3626 {
3627     csi_object_t *obj;
3628     int type;
3629
3630     check (1);
3631
3632     obj = _csi_peek_ostack (ctx, 0);
3633     type = csi_object_get_type (obj);
3634     switch (type) {
3635     case CSI_OBJECT_TYPE_INTEGER:
3636         obj->datum.integer = -obj->datum.integer;
3637         break;
3638     case CSI_OBJECT_TYPE_REAL:
3639         obj->datum.real = -obj->datum.real;
3640         break;
3641     default:
3642         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
3643     }
3644
3645     return CSI_STATUS_SUCCESS;
3646 }
3647
3648 static csi_status_t
3649 _not (csi_t *ctx)
3650 {
3651     csi_object_t *obj;
3652     int type;
3653
3654     check (1);
3655
3656     obj = _csi_peek_ostack (ctx, 0);
3657     type = csi_object_get_type (obj);
3658     switch (type) {
3659     case CSI_OBJECT_TYPE_BOOLEAN:
3660         obj->datum.boolean = ! obj->datum.boolean;
3661         break;
3662     case CSI_OBJECT_TYPE_INTEGER:
3663         obj->type = CSI_OBJECT_TYPE_BOOLEAN;
3664         obj->datum.boolean = ! obj->datum.integer;
3665         break;
3666     case CSI_OBJECT_TYPE_REAL:
3667         obj->type = CSI_OBJECT_TYPE_BOOLEAN;
3668         obj->datum.boolean = obj->datum.real == 0.0;
3669         break;
3670     default:
3671         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
3672     }
3673
3674     return CSI_STATUS_SUCCESS;
3675 }
3676
3677 static csi_status_t
3678 _new_path (csi_t *ctx)
3679 {
3680     /* XXX handle path object */
3681     return _do_cairo_op (ctx, cairo_new_path);
3682 }
3683
3684 static csi_status_t
3685 _new_sub_path (csi_t *ctx)
3686 {
3687     /* XXX handle path object */
3688     return _do_cairo_op (ctx, cairo_new_sub_path);
3689 }
3690
3691 static csi_status_t
3692 _null (csi_t *ctx)
3693 {
3694     return _csi_push_ostack_null (ctx);
3695 }
3696
3697 static csi_status_t
3698 _mask (csi_t *ctx)
3699 {
3700     cairo_t *cr;
3701     cairo_pattern_t *pattern = NULL; /* silence the compiler */
3702     csi_status_t status;
3703
3704     check (2);
3705
3706     status = _csi_ostack_get_pattern (ctx, 0, &pattern);
3707     if (_csi_unlikely (status))
3708         return status;
3709     status = _csi_ostack_get_context (ctx, 1, &cr);
3710     if (_csi_unlikely (status))
3711         return status;
3712
3713     cairo_mask (cr, pattern);
3714     pop (1);
3715
3716     return CSI_STATUS_SUCCESS;
3717 }
3718
3719 static csi_status_t
3720 _matrix (csi_t *ctx)
3721 {
3722     csi_object_t *obj, matrix;
3723     double v[6];
3724     csi_status_t status;
3725     int n;
3726
3727     check (1);
3728
3729     obj = _csi_peek_ostack (ctx, 0);
3730     if (csi_object_is_number (obj)) {
3731         check (6);
3732
3733         for (n = 6; n--; ) {
3734             status = _csi_ostack_get_number (ctx, 5-n, &v[n]);
3735             if (_csi_unlikely (status))
3736                 return status;
3737         }
3738         status = csi_matrix_new_from_values (ctx, &matrix, v);
3739         if (_csi_unlikely (status))
3740             return status;
3741
3742         pop (6);
3743     } else {
3744         csi_array_t *array;
3745
3746         status = _csi_ostack_get_array (ctx, 0, &array);
3747         if (_csi_unlikely (status))
3748             return status;
3749
3750         status = csi_matrix_new_from_array (ctx, &matrix, array);
3751         if (_csi_unlikely (status))
3752             return status;
3753
3754         pop (1);
3755     }
3756
3757     return push (&matrix);
3758 }
3759
3760 static csi_status_t
3761 _map_to_image (csi_t *ctx)
3762 {
3763     csi_object_t obj;
3764     csi_array_t *array;
3765     csi_status_t status;
3766     cairo_rectangle_int_t extents, *r;
3767     cairo_surface_t *surface;
3768
3769     check (2);
3770
3771     status = _csi_ostack_get_array (ctx, 0, &array);
3772     if (_csi_unlikely (status))
3773         return status;
3774
3775     status = _csi_ostack_get_surface (ctx, 1, &surface);
3776     if (_csi_unlikely (status))
3777         return status;
3778
3779     switch (array->stack.len) {
3780     case 0:
3781         r = NULL;
3782         break;
3783     case 4:
3784         extents.x = floor (_csi_object_as_real (&array->stack.objects[0]));
3785         extents.y = floor (_csi_object_as_real (&array->stack.objects[1]));
3786         extents.width = ceil (_csi_object_as_real (&array->stack.objects[2]));
3787         extents.height = ceil (_csi_object_as_real (&array->stack.objects[3]));
3788         r = &extents;
3789         break;
3790     default:
3791         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
3792     }
3793
3794     obj.type = CSI_OBJECT_TYPE_SURFACE;
3795     obj.datum.surface = cairo_surface_reference (cairo_surface_map_to_image (surface, r));
3796     pop (1);
3797     return push (&obj);
3798 }
3799
3800 static csi_status_t
3801 _unmap_image (csi_t *ctx)
3802 {
3803     cairo_surface_t *surface, *image;
3804     csi_status_t status;
3805
3806     check (2);
3807
3808     status = _csi_ostack_get_surface (ctx, 0, &image);
3809     if (_csi_unlikely (status))
3810         return status;
3811     status = _csi_ostack_get_surface (ctx, 1, &surface);
3812     if (_csi_unlikely (status))
3813         return status;
3814
3815     cairo_surface_unmap_image (surface, image);
3816
3817     pop (1);
3818     return CSI_STATUS_SUCCESS;
3819 }
3820
3821 static csi_status_t
3822 _mesh (csi_t *ctx)
3823 {
3824     csi_object_t obj;
3825
3826     obj.type = CSI_OBJECT_TYPE_PATTERN;
3827     obj.datum.pattern = cairo_pattern_create_mesh ();
3828     return push (&obj);
3829 }
3830
3831 static csi_status_t
3832 _mesh_begin_patch (csi_t *ctx)
3833 {
3834     csi_status_t status;
3835     cairo_pattern_t *pattern = NULL; /* silence the compiler */
3836
3837     check (1);
3838
3839     status = _csi_ostack_get_pattern (ctx, 0, &pattern);
3840     if (_csi_unlikely (status))
3841         return status;
3842
3843     cairo_mesh_pattern_begin_patch (pattern);
3844     return CSI_STATUS_SUCCESS;
3845 }
3846
3847 static csi_status_t
3848 _mesh_end_patch (csi_t *ctx)
3849 {
3850     csi_status_t status;
3851     cairo_pattern_t *pattern = NULL; /* silence the compiler */
3852
3853     check (1);
3854
3855     status = _csi_ostack_get_pattern (ctx, 0, &pattern);
3856     if (_csi_unlikely (status))
3857         return status;
3858
3859     cairo_mesh_pattern_end_patch (pattern);
3860     return CSI_STATUS_SUCCESS;
3861 }
3862
3863 static csi_status_t
3864 _mesh_set_control_point (csi_t *ctx)
3865 {
3866     csi_status_t status;
3867     double x, y;
3868     csi_integer_t point;
3869     cairo_pattern_t *pattern = NULL; /* silence the compiler */
3870
3871     check (4);
3872
3873     status = _csi_ostack_get_number (ctx, 0, &y);
3874     if (_csi_unlikely (status))
3875         return status;
3876     status = _csi_ostack_get_number (ctx, 1, &x);
3877     if (_csi_unlikely (status))
3878         return status;
3879     status = _csi_ostack_get_integer (ctx, 2, &point);
3880     if (_csi_unlikely (status))
3881         return status;
3882     status = _csi_ostack_get_pattern (ctx, 3, &pattern);
3883     if (_csi_unlikely (status))
3884         return status;
3885
3886     cairo_mesh_pattern_set_control_point (pattern, point, x, y);
3887
3888     pop (3);
3889     return CSI_STATUS_SUCCESS;
3890 }
3891
3892 static csi_status_t
3893 _mesh_set_corner_color (csi_t *ctx)
3894 {
3895     csi_status_t status;
3896     double r, g, b, a;
3897     csi_integer_t corner;
3898     cairo_pattern_t *pattern = NULL; /* silence the compiler */
3899
3900     check (6);
3901
3902     status = _csi_ostack_get_number (ctx, 0, &a);
3903     if (_csi_unlikely (status))
3904         return status;
3905     status = _csi_ostack_get_number (ctx, 1, &b);
3906     if (_csi_unlikely (status))
3907         return status;
3908     status = _csi_ostack_get_number (ctx, 2, &g);
3909     if (_csi_unlikely (status))
3910         return status;
3911     status = _csi_ostack_get_number (ctx, 3, &r);
3912     if (_csi_unlikely (status))
3913         return status;
3914     status = _csi_ostack_get_integer (ctx, 4, &corner);
3915     if (_csi_unlikely (status))
3916         return status;
3917     status = _csi_ostack_get_pattern (ctx, 5, &pattern);
3918     if (_csi_unlikely (status))
3919         return status;
3920
3921     cairo_mesh_pattern_set_corner_color_rgba (pattern, corner, r, g, b, a);
3922
3923     pop (5);
3924     return CSI_STATUS_SUCCESS;
3925 }
3926
3927 static csi_status_t
3928 _mod (csi_t *ctx)
3929 {
3930     csi_integer_t x, y;
3931     csi_status_t status;
3932
3933     check (2);
3934
3935     status = _csi_ostack_get_integer (ctx, 0, &y);
3936     if (_csi_unlikely (status))
3937         return status;
3938     status = _csi_ostack_get_integer (ctx, 1, &x);
3939     if (_csi_unlikely (status))
3940         return status;
3941
3942     pop (2);
3943     return _csi_push_ostack_integer (ctx, x % y);
3944 }
3945
3946 static csi_status_t
3947 _move_to (csi_t *ctx)
3948 {
3949     csi_status_t status;
3950     csi_object_t *obj;
3951     int type;
3952     double x, y;
3953
3954     check (3);
3955
3956     status = _csi_ostack_get_number (ctx, 0, &y);
3957     if (_csi_unlikely (status))
3958         return status;
3959     status = _csi_ostack_get_number (ctx, 1, &x);
3960     if (_csi_unlikely (status))
3961         return status;
3962
3963     obj = _csi_peek_ostack (ctx, 2);
3964     type = csi_object_get_type (obj);
3965     switch (type) {
3966     case CSI_OBJECT_TYPE_CONTEXT:
3967         cairo_move_to (obj->datum.cr, x, y);
3968         break;
3969     case CSI_OBJECT_TYPE_PATTERN:
3970         cairo_mesh_pattern_move_to (obj->datum.pattern, x, y);
3971         break;
3972
3973         /* XXX path object */
3974     }
3975
3976     pop (2);
3977     return CSI_STATUS_SUCCESS;
3978 }
3979
3980 static csi_status_t
3981 _mul (csi_t *ctx)
3982 {
3983     csi_object_t *A;
3984     csi_object_t *B;
3985     csi_object_type_t type_a, type_b;
3986
3987     check (2);
3988
3989     B = _csi_peek_ostack (ctx, 0);
3990     A = _csi_peek_ostack (ctx, 1);
3991
3992     type_a = csi_object_get_type (A);
3993     if (_csi_unlikely (! (type_a == CSI_OBJECT_TYPE_INTEGER ||
3994                             type_a == CSI_OBJECT_TYPE_REAL)))
3995     {
3996         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
3997     }
3998     type_b = csi_object_get_type (B);
3999     if (_csi_unlikely (! (type_b == CSI_OBJECT_TYPE_INTEGER ||
4000                             type_b == CSI_OBJECT_TYPE_REAL)))
4001     {
4002         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4003     }
4004
4005     pop (2);
4006
4007     if (type_a == CSI_OBJECT_TYPE_REAL &&
4008         type_b == CSI_OBJECT_TYPE_REAL)
4009     {
4010         return _csi_push_ostack_real (ctx, A->datum.real * B->datum.real);
4011
4012     }
4013     else if (type_a == CSI_OBJECT_TYPE_INTEGER &&
4014              type_b == CSI_OBJECT_TYPE_INTEGER)
4015     {
4016         return _csi_push_ostack_integer (ctx,
4017                                          A->datum.integer * B->datum.integer);
4018     }
4019     else
4020     {
4021         double v;
4022
4023         if (type_a == CSI_OBJECT_TYPE_REAL)
4024             v = A->datum.real;
4025         else
4026             v = A->datum.integer;
4027
4028         if (type_b == CSI_OBJECT_TYPE_REAL)
4029             v *= B->datum.real;
4030         else
4031             v *= B->datum.integer;
4032
4033         return _csi_push_ostack_real (ctx, v);
4034     }
4035 }
4036
4037 static csi_status_t
4038 _or (csi_t *ctx)
4039 {
4040     csi_object_t *a, *b;
4041     int type;
4042
4043     check (2);
4044
4045     a = _csi_peek_ostack (ctx, 0);
4046     b = _csi_peek_ostack (ctx, 1);
4047     if (_csi_unlikely (csi_object_get_type (a) != csi_object_get_type (b)))
4048         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4049
4050     pop (2);
4051     type = csi_object_get_type (a);
4052     switch (type) {
4053     case CSI_OBJECT_TYPE_INTEGER:
4054         return _csi_push_ostack_integer (ctx,
4055                                          a->datum.integer | b->datum.integer);
4056     case CSI_OBJECT_TYPE_BOOLEAN:
4057         return _csi_push_ostack_boolean (ctx,
4058                                          a->datum.boolean | b->datum.boolean);
4059     default:
4060         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4061     }
4062 }
4063
4064 static csi_status_t
4065 _paint (csi_t *ctx)
4066 {
4067     return _do_cairo_op (ctx, cairo_paint);
4068 }
4069
4070 static csi_status_t
4071 _paint_with_alpha (csi_t *ctx)
4072 {
4073     cairo_t *cr;
4074     csi_status_t status;
4075     double alpha;
4076
4077     check (2);
4078
4079     status = _csi_ostack_get_number (ctx, 0, &alpha);
4080     if (_csi_unlikely (status))
4081         return status;
4082
4083     status = _csi_ostack_get_context (ctx, 1, &cr);
4084     if (_csi_unlikely (status))
4085         return status;
4086
4087     cairo_paint_with_alpha (cr, alpha);
4088     pop (1);
4089     return CSI_STATUS_SUCCESS;
4090 }
4091
4092 static csi_status_t
4093 _pattern (csi_t *ctx)
4094 {
4095     csi_object_t obj;
4096     csi_status_t status;
4097     cairo_surface_t *surface;
4098
4099     check (1);
4100
4101     status = _csi_ostack_get_surface (ctx, 0, &surface);
4102     if (_csi_unlikely (status))
4103         return status;
4104
4105     obj.type = CSI_OBJECT_TYPE_PATTERN;
4106     obj.datum.pattern = cairo_pattern_create_for_surface (surface);
4107
4108     pop (1);
4109     return push (&obj);
4110 }
4111
4112 static csi_status_t
4113 _pop (csi_t *ctx)
4114 {
4115     check (1);
4116     pop (1);
4117
4118     return CSI_STATUS_SUCCESS;
4119 }
4120
4121 static csi_status_t
4122 _pop_group (csi_t *ctx)
4123 {
4124     csi_object_t obj;
4125     csi_status_t status;
4126     cairo_t *cr;
4127
4128     check (1);
4129
4130     status = _csi_ostack_get_context (ctx, 0, &cr);
4131     if (_csi_unlikely (status))
4132         return status;
4133
4134     obj.type = CSI_OBJECT_TYPE_PATTERN;
4135     obj.datum.pattern = cairo_pop_group (cr);
4136
4137     return push (&obj);
4138 }
4139
4140 static csi_status_t
4141 _push_group (csi_t *ctx)
4142 {
4143     csi_status_t status;
4144     cairo_t *cr;
4145     long content;
4146
4147     check (2);
4148
4149     status = _csi_ostack_get_integer (ctx, 0, &content);
4150     if (_csi_unlikely (status))
4151         return status;
4152
4153     status = _csi_ostack_get_context (ctx, 1, &cr);
4154     if (_csi_unlikely (status))
4155         return status;
4156
4157     cairo_push_group_with_content (cr, content);
4158     pop (1);
4159     return CSI_STATUS_SUCCESS;
4160 }
4161
4162 static csi_status_t
4163 _radial (csi_t *ctx)
4164 {
4165     csi_object_t obj;
4166     csi_status_t status;
4167     double x1, y1, r1, x2, y2, r2;
4168
4169     check (6);
4170
4171     status = _csi_ostack_get_number (ctx, 0, &r2);
4172     if (_csi_unlikely (status))
4173         return status;
4174     status = _csi_ostack_get_number (ctx, 1, &y2);
4175     if (_csi_unlikely (status))
4176         return status;
4177     status = _csi_ostack_get_number (ctx, 2, &x2);
4178     if (_csi_unlikely (status))
4179         return status;
4180     status = _csi_ostack_get_number (ctx, 3, &r1);
4181     if (_csi_unlikely (status))
4182         return status;
4183     status = _csi_ostack_get_number (ctx, 4, &y1);
4184     if (_csi_unlikely (status))
4185         return status;
4186     status = _csi_ostack_get_number (ctx, 5, &x1);
4187     if (_csi_unlikely (status))
4188         return status;
4189
4190     obj.type = CSI_OBJECT_TYPE_PATTERN;
4191     obj.datum.pattern = cairo_pattern_create_radial (x1, y1, r1, x2, y2, r2);
4192     pop (6);
4193     return push (&obj);
4194 }
4195
4196 static csi_status_t
4197 _rectangle (csi_t *ctx)
4198 {
4199     csi_status_t status;
4200     double x, y;
4201     double w, h;
4202     cairo_t *cr;
4203
4204     check (5);
4205
4206     status = _csi_ostack_get_number (ctx, 0, &h);
4207     if (_csi_unlikely (status))
4208         return status;
4209     status = _csi_ostack_get_number (ctx, 1, &w);
4210     if (_csi_unlikely (status))
4211         return status;
4212     status = _csi_ostack_get_number (ctx, 2, &y);
4213     if (_csi_unlikely (status))
4214         return status;
4215     status = _csi_ostack_get_number (ctx, 3, &x);
4216     if (_csi_unlikely (status))
4217         return status;
4218     status = _csi_ostack_get_context (ctx, 4, &cr);
4219     if (_csi_unlikely (status))
4220         return status;
4221
4222     /* XXX path object */
4223
4224     cairo_rectangle (cr, x, y, w, h);
4225     pop(4);
4226     return CSI_STATUS_SUCCESS;
4227 }
4228
4229 static csi_status_t
4230 _rel_curve_to (csi_t *ctx)
4231 {
4232     csi_status_t status;
4233     double x1, y1;
4234     double x2, y2;
4235     double x3, y3;
4236     cairo_t *cr;
4237
4238     check (7);
4239
4240     status = _csi_ostack_get_number (ctx, 0, &y3);
4241     if (_csi_unlikely (status))
4242         return status;
4243     status = _csi_ostack_get_number (ctx, 1, &x3);
4244     if (_csi_unlikely (status))
4245         return status;
4246     status = _csi_ostack_get_number (ctx, 2, &y2);
4247     if (_csi_unlikely (status))
4248         return status;
4249     status = _csi_ostack_get_number (ctx, 3, &x2);
4250     if (_csi_unlikely (status))
4251         return status;
4252     status = _csi_ostack_get_number (ctx, 4, &y1);
4253     if (_csi_unlikely (status))
4254         return status;
4255     status = _csi_ostack_get_number (ctx, 5, &x1);
4256     if (_csi_unlikely (status))
4257         return status;
4258     status = _csi_ostack_get_context (ctx, 6, &cr);
4259     if (_csi_unlikely (status))
4260         return status;
4261
4262     /* XXX path object */
4263
4264     cairo_rel_curve_to (cr, x1, y1, x2, y2, x3, y3);
4265     pop (6);
4266     return CSI_STATUS_SUCCESS;
4267 }
4268
4269 static csi_status_t
4270 _rel_line_to (csi_t *ctx)
4271 {
4272     csi_status_t status;
4273     double x, y;
4274     cairo_t *cr;
4275
4276     check (3);
4277
4278     status = _csi_ostack_get_number (ctx, 0, &y);
4279     if (_csi_unlikely (status))
4280         return status;
4281     status = _csi_ostack_get_number (ctx, 1, &x);
4282     if (_csi_unlikely (status))
4283         return status;
4284     status = _csi_ostack_get_context (ctx, 2, &cr);
4285     if (_csi_unlikely (status))
4286         return status;
4287
4288     /* XXX path object */
4289
4290     cairo_rel_line_to (cr, x, y);
4291     pop (2);
4292     return CSI_STATUS_SUCCESS;
4293 }
4294
4295 static csi_status_t
4296 _rel_move_to (csi_t *ctx)
4297 {
4298     csi_status_t status;
4299     double x, y;
4300     cairo_t *cr;
4301
4302     check (3);
4303
4304     status = _csi_ostack_get_number (ctx, 0, &y);
4305     if (_csi_unlikely (status))
4306         return status;
4307     status = _csi_ostack_get_number (ctx, 1, &x);
4308     if (_csi_unlikely (status))
4309         return status;
4310     status = _csi_ostack_get_context (ctx, 2, &cr);
4311     if (_csi_unlikely (status))
4312         return status;
4313
4314     /* XXX path object */
4315     cairo_rel_move_to (cr, x, y);
4316     pop (2);
4317     return CSI_STATUS_SUCCESS;
4318 }
4319
4320 static csi_status_t
4321 _repeat (csi_t *ctx)
4322 {
4323     csi_array_t *proc;
4324     csi_integer_t count;
4325     csi_status_t status;
4326
4327     check (2);
4328
4329     status = _csi_ostack_get_procedure (ctx, 0, &proc);
4330     if (_csi_unlikely (status))
4331         return status;
4332
4333     status = _csi_ostack_get_integer (ctx, 1, &count);
4334     if (_csi_unlikely (status))
4335         return status;
4336
4337     if (_csi_unlikely (count < 0))
4338         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4339
4340     proc->base.ref++;
4341     pop (2);
4342
4343     while (count--) {
4344         status = _csi_array_execute (ctx, proc);
4345         if (_csi_unlikely (status))
4346             break;
4347     }
4348
4349     if (--proc->base.ref == 0)
4350         csi_array_free (ctx, proc);
4351
4352     return status;
4353 }
4354
4355 static csi_status_t
4356 _reset_clip (csi_t *ctx)
4357 {
4358     return _do_cairo_op (ctx, cairo_reset_clip);
4359 }
4360
4361 static csi_status_t
4362 _restore (csi_t *ctx)
4363 {
4364     return _do_cairo_op (ctx, cairo_restore);
4365 }
4366
4367 static csi_status_t
4368 _rgb (csi_t *ctx)
4369 {
4370     csi_object_t obj;
4371     csi_status_t status;
4372     double r,g,b;
4373
4374     check (3);
4375
4376     status = _csi_ostack_get_number (ctx, 0, &b);
4377     if (_csi_unlikely (status))
4378         return status;
4379     status = _csi_ostack_get_number (ctx, 1, &g);
4380     if (_csi_unlikely (status))
4381         return status;
4382     status = _csi_ostack_get_number (ctx, 2, &r);
4383     if (_csi_unlikely (status))
4384         return status;
4385
4386     obj.type = CSI_OBJECT_TYPE_PATTERN;
4387     obj.datum.pattern = cairo_pattern_create_rgb (r, g, b);
4388     pop (3);
4389     return push (&obj);
4390 }
4391
4392 static csi_status_t
4393 _rgba (csi_t *ctx)
4394 {
4395     csi_object_t obj;
4396     csi_status_t status;
4397     double r,g,b,a;
4398
4399     check (4);
4400
4401     status = _csi_ostack_get_number (ctx, 0, &a);
4402     if (_csi_unlikely (status))
4403         return status;
4404     status = _csi_ostack_get_number (ctx, 1, &b);
4405     if (_csi_unlikely (status))
4406         return status;
4407     status = _csi_ostack_get_number (ctx, 2, &g);
4408     if (_csi_unlikely (status))
4409         return status;
4410     status = _csi_ostack_get_number (ctx, 3, &r);
4411     if (_csi_unlikely (status))
4412         return status;
4413
4414     obj.type = CSI_OBJECT_TYPE_PATTERN;
4415     obj.datum.pattern = cairo_pattern_create_rgba (r, g, b, a);
4416     pop (4);
4417     return push (&obj);
4418 }
4419
4420 static csi_status_t
4421 _roll (csi_t *ctx)
4422 {
4423     csi_status_t status;
4424     long j, n;
4425
4426     check (2);
4427
4428     status = _csi_ostack_get_integer (ctx, 0, &j);
4429     if (_csi_unlikely (status))
4430         return status;
4431     status = _csi_ostack_get_integer (ctx, 1, &n);
4432     if (_csi_unlikely (status))
4433         return status;
4434
4435     pop (2);
4436     check (n);
4437     return _csi_stack_roll (ctx, &ctx->ostack, j, n);
4438 }
4439
4440 static csi_status_t
4441 _rotate (csi_t *ctx)
4442 {
4443     csi_object_t *obj;
4444     csi_status_t status;
4445     double theta;
4446     int type;
4447
4448     check (2);
4449
4450     status = _csi_ostack_get_number (ctx, 0, &theta);
4451     if (_csi_unlikely (status))
4452         return status;
4453
4454     obj = _csi_peek_ostack (ctx, 1);
4455     type = csi_object_get_type (obj);
4456     switch (type) {
4457     case CSI_OBJECT_TYPE_CONTEXT:
4458         cairo_rotate (obj->datum.cr, theta);
4459         break;
4460
4461     case CSI_OBJECT_TYPE_PATTERN:
4462         {
4463             cairo_matrix_t ctm;
4464             cairo_pattern_get_matrix (obj->datum.pattern, &ctm);
4465             cairo_matrix_rotate (&ctm, theta);
4466             cairo_pattern_set_matrix (obj->datum.pattern, &ctm);
4467         }
4468         break;
4469
4470
4471     case CSI_OBJECT_TYPE_MATRIX:
4472         cairo_matrix_rotate (&obj->datum.matrix->matrix, theta);
4473         break;
4474
4475     default:
4476         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4477     }
4478
4479     pop (1);
4480     return CSI_STATUS_SUCCESS;
4481 }
4482
4483 static csi_status_t
4484 _save (csi_t *ctx)
4485 {
4486     return _do_cairo_op (ctx, cairo_save);
4487 }
4488
4489 static csi_status_t
4490 _scale (csi_t *ctx)
4491 {
4492     csi_object_t *obj;
4493     csi_status_t status;
4494     double x, y;
4495     int type;
4496
4497     check (3);
4498
4499     status = _csi_ostack_get_number (ctx, 0, &y);
4500     if (_csi_unlikely (status))
4501         return status;
4502     status = _csi_ostack_get_number (ctx, 1, &x);
4503     if (_csi_unlikely (status))
4504         return status;
4505
4506     obj = _csi_peek_ostack (ctx, 2);
4507     type = csi_object_get_type (obj);
4508     switch (type) {
4509     case CSI_OBJECT_TYPE_CONTEXT:
4510         cairo_scale (obj->datum.cr, x, y);
4511         break;
4512
4513     case CSI_OBJECT_TYPE_PATTERN:
4514         {
4515             cairo_matrix_t ctm;
4516             cairo_pattern_get_matrix (obj->datum.pattern, &ctm);
4517             cairo_matrix_scale (&ctm, x, y);
4518             cairo_pattern_set_matrix (obj->datum.pattern, &ctm);
4519         }
4520         break;
4521
4522
4523     case CSI_OBJECT_TYPE_MATRIX:
4524         cairo_matrix_scale (&obj->datum.matrix->matrix, x, y);
4525         break;
4526
4527     default:
4528         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4529     }
4530
4531     pop (2);
4532     return CSI_STATUS_SUCCESS;
4533 }
4534
4535 static csi_status_t
4536 _font_options_load_from_dictionary (csi_t *ctx,
4537                                     csi_dictionary_t *dict,
4538                                     cairo_font_options_t *options)
4539 {
4540     const struct {
4541         const char *key;
4542         void (*setter) (cairo_font_options_t *, int val);
4543     } properties[] = {
4544         { "antialias",
4545             (void (*)(cairo_font_options_t *, int val))
4546                 cairo_font_options_set_antialias },
4547         { "subpixel-order",
4548             (void (*)(cairo_font_options_t *, int val))
4549                 cairo_font_options_set_subpixel_order },
4550         { "hint-style",
4551             (void (*)(cairo_font_options_t *, int val))
4552                 cairo_font_options_set_hint_style },
4553         { "hint-metrics",
4554             (void (*)(cairo_font_options_t *, int val))
4555                 cairo_font_options_set_hint_metrics },
4556         { NULL, NULL },
4557     }, *prop = properties;
4558
4559     while (prop->key != NULL) {
4560         csi_object_t key, value;
4561         csi_status_t status;
4562
4563         status = csi_name_new_static (ctx, &key, prop->key);
4564         if (_csi_unlikely (status))
4565             return status;
4566
4567         if (csi_dictionary_has (dict, key.datum.name)) {
4568             status = csi_dictionary_get (ctx, dict, key.datum.name, &value);
4569             if (_csi_unlikely (status))
4570                 return status;
4571
4572             if (_csi_unlikely (csi_object_get_type (&value) !=
4573                                  CSI_OBJECT_TYPE_INTEGER))
4574             {
4575                 csi_object_free (ctx, &value);
4576                 return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4577             }
4578
4579             prop->setter (options, value.datum.integer);
4580         }
4581
4582         prop++;
4583     }
4584
4585     return CSI_STATUS_SUCCESS;
4586 }
4587
4588 static csi_status_t
4589 _scaled_font (csi_t *ctx)
4590 {
4591     csi_object_t obj;
4592     csi_dictionary_t *dict;
4593     cairo_font_face_t *font_face = NULL; /* silence the compiler */
4594     cairo_matrix_t font_matrix, ctm;
4595     cairo_font_options_t *options;
4596     csi_status_t status;
4597
4598     check (4);
4599
4600     status = _csi_ostack_get_dictionary (ctx, 0, &dict);
4601     if (_csi_unlikely (status))
4602         return status;
4603     options = cairo_font_options_create ();
4604     status = _font_options_load_from_dictionary (ctx, dict, options);
4605     if (_csi_unlikely (status)) {
4606         cairo_font_options_destroy (options);
4607         return status;
4608     }
4609
4610     status = _csi_ostack_get_matrix (ctx, 1, &ctm);
4611     if (_csi_unlikely (status)) {
4612         cairo_font_options_destroy (options);
4613         return status;
4614     }
4615
4616     status = _csi_ostack_get_matrix (ctx, 2, &font_matrix);
4617     if (_csi_unlikely (status)) {
4618         cairo_font_options_destroy (options);
4619         return status;
4620     }
4621
4622     status = _csi_ostack_get_font_face (ctx, 3, &font_face);
4623     if (_csi_unlikely (status)) {
4624         cairo_font_options_destroy (options);
4625         return status;
4626     }
4627
4628     obj.type = CSI_OBJECT_TYPE_SCALED_FONT;
4629     obj.datum.scaled_font = cairo_scaled_font_create (font_face,
4630                                                       &font_matrix,
4631                                                       &ctm,
4632                                                       options);
4633     cairo_font_options_destroy (options);
4634     pop (4);
4635     return push (&obj);
4636 }
4637
4638 static csi_status_t
4639 _select_font_face (csi_t *ctx)
4640 {
4641     cairo_t *cr;
4642     long weight;
4643     long slant;
4644     csi_string_t *family;
4645     csi_status_t status;
4646
4647     check (4);
4648
4649     status = _csi_ostack_get_integer (ctx, 0,  &weight);
4650     if (_csi_unlikely (status))
4651         return status;
4652     status = _csi_ostack_get_integer (ctx, 1, &slant);
4653     if (_csi_unlikely (status))
4654         return status;
4655     status = _csi_ostack_get_string (ctx, 2, &family);
4656     if (_csi_unlikely (status))
4657         return status;
4658     status = _csi_ostack_get_context (ctx, 3, &cr);
4659     if (_csi_unlikely (status))
4660         return status;
4661
4662     cairo_select_font_face (cr, family->string, slant, weight);
4663     pop (3);
4664     return CSI_STATUS_SUCCESS;
4665 }
4666
4667 static csi_status_t
4668 _context_set (csi_t *ctx,
4669               cairo_t *cr,
4670               csi_name_t key,
4671               csi_object_t *obj)
4672 {
4673     if (strcmp ((char *) key, "source") == 0) {
4674         if (_csi_unlikely (csi_object_get_type (obj) !=
4675                              CSI_OBJECT_TYPE_PATTERN))
4676             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4677
4678         cairo_set_source (cr, obj->datum.pattern);
4679         return CSI_STATUS_SUCCESS;
4680     }
4681
4682     if (strcmp ((char *) key, "scaled-font") == 0) {
4683         if (_csi_unlikely (csi_object_get_type (obj) !=
4684                              CSI_OBJECT_TYPE_SCALED_FONT))
4685             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4686
4687         cairo_set_scaled_font (cr, obj->datum.scaled_font);
4688         return CSI_STATUS_SUCCESS;
4689     }
4690
4691     if (strcmp ((char *) key, "font-face") == 0) {
4692         if (_csi_unlikely (csi_object_get_type (obj) !=
4693                              CSI_OBJECT_TYPE_FONT))
4694             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4695
4696         cairo_set_font_face (cr, obj->datum.font_face);
4697         return CSI_STATUS_SUCCESS;
4698     }
4699
4700     /* return _proxy_set()? */
4701     return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4702 }
4703
4704 static csi_status_t
4705 _set (csi_t *ctx)
4706 {
4707     csi_object_t *key, *value, *dst;
4708     csi_status_t status;
4709     int type;
4710
4711     check (3);
4712
4713     value = _csi_peek_ostack (ctx, 0);
4714     key = _csi_peek_ostack (ctx, 1);
4715     dst = _csi_peek_ostack (ctx, 2);
4716
4717     type = csi_object_get_type (dst);
4718     switch (type) {
4719     case CSI_OBJECT_TYPE_DICTIONARY:
4720         if (_csi_unlikely (csi_object_get_type (key) !=
4721                              CSI_OBJECT_TYPE_NAME))
4722             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4723
4724         status = csi_dictionary_put (ctx,
4725                                      dst->datum.dictionary,
4726                                      key->datum.name,
4727                                      value);
4728         break;
4729     case CSI_OBJECT_TYPE_ARRAY:
4730         if (_csi_unlikely (csi_object_get_type (key) !=
4731                              CSI_OBJECT_TYPE_INTEGER))
4732             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4733
4734         status = csi_array_put (ctx,
4735                                 dst->datum.array,
4736                                 key->datum.integer,
4737                                 value);
4738         break;
4739
4740     case CSI_OBJECT_TYPE_CONTEXT:
4741         if (_csi_unlikely (csi_object_get_type (key) !=
4742                              CSI_OBJECT_TYPE_NAME))
4743             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4744
4745         status = _context_set (ctx,
4746                                dst->datum.cr,
4747                                key->datum.name,
4748                                value);
4749         break;
4750
4751     case CSI_OBJECT_TYPE_STRING:
4752 #if 0
4753         status = csi_string_put (dst, key, value);
4754         break;
4755 #endif
4756     default:
4757         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4758     }
4759
4760     pop (2);
4761     return status;
4762 }
4763
4764 static csi_status_t
4765 _set_antialias (csi_t *ctx)
4766 {
4767     csi_status_t status;
4768     cairo_t *cr;
4769     long antialias;
4770
4771     check (2);
4772
4773     status = _csi_ostack_get_integer (ctx, 0, &antialias);
4774     if (_csi_unlikely (status))
4775         return status;
4776     status = _csi_ostack_get_context (ctx, 1, &cr);
4777     if (_csi_unlikely (status))
4778         return status;
4779
4780     cairo_set_antialias (cr, antialias);
4781     pop (1);
4782     return CSI_STATUS_SUCCESS;
4783 }
4784
4785 static csi_status_t
4786 _set_dash (csi_t *ctx)
4787 {
4788     csi_array_t *array;
4789     csi_status_t status;
4790     cairo_t *cr;
4791     double offset;
4792
4793     check (3);
4794
4795     status = _csi_ostack_get_number (ctx, 0, &offset);
4796     if (_csi_unlikely (status))
4797         return status;
4798     status = _csi_ostack_get_array (ctx, 1, &array);
4799     if (_csi_unlikely (status))
4800         return status;
4801     status = _csi_ostack_get_context (ctx, 2, &cr);
4802     if (_csi_unlikely (status))
4803         return status;
4804
4805     if (array->stack.len == 0) {
4806         cairo_set_dash (cr, NULL, 0., 0.);
4807     } else {
4808         double stack_dashes[8];
4809         double *dashes;
4810         csi_integer_t n;
4811
4812         if (_csi_likely (array->stack.len < ARRAY_LENGTH (stack_dashes))) {
4813             dashes = stack_dashes;
4814         } else {
4815         if (_csi_unlikely ((unsigned) array->stack.len >= INT_MAX / sizeof (double)))
4816             return _csi_error (CSI_STATUS_NO_MEMORY);
4817             dashes = _csi_alloc (ctx, sizeof (double) * array->stack.len);
4818             if (_csi_unlikely (dashes == NULL))
4819                 return _csi_error (CSI_STATUS_NO_MEMORY);
4820         }
4821
4822         for (n = 0; n < array->stack.len; n++) {
4823             if (_csi_unlikely (! csi_object_is_number
4824                                  (&array->stack.objects[n])))
4825             {
4826                 if (dashes != stack_dashes)
4827                     _csi_free (ctx, dashes);
4828                 return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4829             }
4830
4831             dashes[n] = csi_number_get_value (&array->stack.objects[n]);
4832         }
4833
4834         cairo_set_dash (cr, dashes, n, offset);
4835
4836         if (dashes != stack_dashes)
4837             _csi_free (ctx, dashes);
4838     }
4839
4840     pop (2);
4841     return CSI_STATUS_SUCCESS;
4842 }
4843
4844 static csi_status_t
4845 _set_device_offset (csi_t *ctx)
4846 {
4847     csi_status_t status;
4848     cairo_surface_t *surface;
4849     double x, y;
4850
4851     check (3);
4852
4853     status = _csi_ostack_get_number (ctx, 0,  &y);
4854     if (_csi_unlikely (status))
4855         return status;
4856     status = _csi_ostack_get_number (ctx, 1, &x);
4857     if (_csi_unlikely (status))
4858         return status;
4859     status = _csi_ostack_get_surface (ctx, 2, &surface);
4860     if (_csi_unlikely (status))
4861         return status;
4862
4863     cairo_surface_set_device_offset (surface, x, y);
4864     pop (2);
4865     return CSI_STATUS_SUCCESS;
4866 }
4867
4868 static csi_status_t
4869 _set_extend (csi_t *ctx)
4870 {
4871     csi_status_t status;
4872     csi_object_t *obj;
4873     long extend;
4874     int type;
4875
4876     check (2);
4877
4878     status = _csi_ostack_get_integer (ctx, 0, &extend);
4879     if (_csi_unlikely (status))
4880         return status;
4881
4882     obj = _csi_peek_ostack (ctx, 1);
4883     type = csi_object_get_type (obj);
4884     switch (type) {
4885     case CSI_OBJECT_TYPE_CONTEXT:
4886         cairo_pattern_set_extend (cairo_get_source (obj->datum.cr),
4887                                   extend);
4888         break;
4889     case CSI_OBJECT_TYPE_PATTERN:
4890         cairo_pattern_set_extend (obj->datum.pattern, extend);
4891         break;
4892     default:
4893         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4894     }
4895
4896     pop (1);
4897     return CSI_STATUS_SUCCESS;
4898 }
4899
4900 static csi_status_t
4901 _set_fallback_resolution (csi_t *ctx)
4902 {
4903     csi_status_t status;
4904     cairo_surface_t *surface;
4905     double dpi_x, dpi_y;
4906
4907     check (3);
4908
4909     status = _csi_ostack_get_number (ctx, 0, &dpi_y);
4910     if (_csi_unlikely (status))
4911         return status;
4912     status = _csi_ostack_get_number (ctx, 1, &dpi_x);
4913     if (_csi_unlikely (status))
4914         return status;
4915     status = _csi_ostack_get_surface (ctx, 2, &surface);
4916     if (_csi_unlikely (status))
4917         return status;
4918
4919     cairo_surface_set_fallback_resolution (surface, dpi_x, dpi_y);
4920     pop (2);
4921     return CSI_STATUS_SUCCESS;
4922 }
4923
4924 static csi_status_t
4925 _set_fill_rule (csi_t *ctx)
4926 {
4927     csi_status_t status;
4928     cairo_t *cr;
4929     long fill_rule;
4930
4931     check (2);
4932
4933     status = _csi_ostack_get_integer (ctx, 0, &fill_rule);
4934     if (_csi_unlikely (status))
4935         return status;
4936     status = _csi_ostack_get_context (ctx, 1, &cr);
4937     if (_csi_unlikely (status))
4938         return status;
4939
4940     cairo_set_fill_rule (cr, fill_rule);
4941     pop (1);
4942     return CSI_STATUS_SUCCESS;
4943 }
4944
4945 static csi_status_t
4946 _set_filter (csi_t *ctx)
4947 {
4948     csi_status_t status;
4949     csi_object_t *obj;
4950     long filter;
4951     int type;
4952
4953     check (2);
4954
4955     status = _csi_ostack_get_integer (ctx, 0, &filter);
4956     if (_csi_unlikely (status))
4957         return status;
4958
4959     obj = _csi_peek_ostack (ctx, 1);
4960     type = csi_object_get_type (obj);
4961     switch (type) {
4962     case CSI_OBJECT_TYPE_CONTEXT:
4963         cairo_pattern_set_filter (cairo_get_source (obj->datum.cr),
4964                                   filter);
4965         break;
4966     case CSI_OBJECT_TYPE_PATTERN:
4967         cairo_pattern_set_filter (obj->datum.pattern, filter);
4968         break;
4969     default:
4970         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4971     }
4972
4973     pop (1);
4974     return CSI_STATUS_SUCCESS;
4975 }
4976
4977 static csi_status_t
4978 _set_font_face (csi_t *ctx)
4979 {
4980     cairo_t *cr;
4981     cairo_font_face_t *font = NULL; /* silence the compiler */
4982     csi_status_t status;
4983
4984     check (2);
4985
4986     status = _csi_ostack_get_font_face (ctx, 0, &font);
4987     if (_csi_unlikely (status))
4988         return status;
4989     status = _csi_ostack_get_context (ctx, 1, &cr);
4990     if (_csi_unlikely (status))
4991         return status;
4992
4993     cairo_set_font_face (cr, font);
4994     pop (1);
4995     return CSI_STATUS_SUCCESS;
4996 }
4997
4998 static csi_status_t
4999 _set_font_options (csi_t *ctx)
5000 {
5001     csi_status_t status;
5002     cairo_t *cr;
5003     csi_dictionary_t *dict;
5004     cairo_font_options_t *options;
5005
5006     check (2);
5007
5008     status = _csi_ostack_get_dictionary (ctx, 0, &dict);
5009     if (_csi_unlikely (status))
5010         return status;
5011     status = _csi_ostack_get_context (ctx, 1, &cr);
5012     if (_csi_unlikely (status))
5013         return status;
5014
5015     options = cairo_font_options_create ();
5016     status = _font_options_load_from_dictionary (ctx, dict, options);
5017     if (_csi_unlikely (status))
5018         return status;
5019
5020     cairo_set_font_options (cr, options);
5021     cairo_font_options_destroy (options);
5022     pop (1);
5023     return CSI_STATUS_SUCCESS;
5024 }
5025
5026 static csi_status_t
5027 _set_font_matrix (csi_t *ctx)
5028 {
5029     csi_status_t status;
5030     cairo_t *cr;
5031     cairo_matrix_t m;
5032
5033     check (2);
5034
5035     status = _csi_ostack_get_matrix (ctx, 0, &m);
5036     if (_csi_unlikely (status))
5037         return status;
5038     status = _csi_ostack_get_context (ctx, 1, &cr);
5039     if (_csi_unlikely (status))
5040         return status;
5041
5042     cairo_set_font_matrix (cr, &m);
5043     pop(1);
5044     return CSI_STATUS_SUCCESS;
5045 }
5046
5047 static csi_status_t
5048 _set_font_size (csi_t *ctx)
5049 {
5050     csi_status_t status;
5051     cairo_t *cr;
5052     double size;
5053
5054     check (2);
5055
5056     status = _csi_ostack_get_number (ctx, 0, &size);
5057     if (_csi_unlikely (status))
5058         return status;
5059     status = _csi_ostack_get_context (ctx, 1, &cr);
5060     if (_csi_unlikely (status))
5061         return status;
5062
5063     cairo_set_font_size (cr, size);
5064     pop (1);
5065     return CSI_STATUS_SUCCESS;
5066 }
5067
5068 static csi_status_t
5069 _set_line_cap (csi_t *ctx)
5070 {
5071     csi_status_t status;
5072     cairo_t *cr;
5073     long line_cap;
5074
5075     check (2);
5076
5077     status = _csi_ostack_get_integer (ctx, 0, &line_cap);
5078     if (_csi_unlikely (status))
5079         return status;
5080     status = _csi_ostack_get_context (ctx, 1, &cr);
5081     if (_csi_unlikely (status))
5082         return status;
5083
5084     cairo_set_line_cap (cr, line_cap);
5085     pop (1);
5086     return CSI_STATUS_SUCCESS;
5087 }
5088
5089 static csi_status_t
5090 _set_line_join (csi_t *ctx)
5091 {
5092     csi_status_t status;
5093     cairo_t *cr;
5094     long line_join;
5095
5096     status = _csi_ostack_get_integer (ctx, 0, &line_join);
5097     if (_csi_unlikely (status))
5098         return status;
5099     status = _csi_ostack_get_context (ctx, 1, &cr);
5100     if (_csi_unlikely (status))
5101         return status;
5102
5103     cairo_set_line_join (cr, line_join);
5104     pop (1);
5105     return CSI_STATUS_SUCCESS;
5106 }
5107
5108 static csi_status_t
5109 _set_line_width (csi_t *ctx)
5110 {
5111     csi_status_t status;
5112     cairo_t *cr;
5113     double line_width;
5114
5115     check (2);
5116
5117     status = _csi_ostack_get_number (ctx, 0, &line_width);
5118     if (_csi_unlikely (status))
5119         return status;
5120     status = _csi_ostack_get_context (ctx, 1, &cr);
5121     if (_csi_unlikely (status))
5122         return status;
5123
5124     cairo_set_line_width (cr, line_width);
5125     pop (1);
5126     return CSI_STATUS_SUCCESS;
5127 }
5128
5129 static csi_status_t
5130 _set_matrix (csi_t *ctx)
5131 {
5132     csi_object_t *obj;
5133     csi_status_t status;
5134     cairo_matrix_t m;
5135     int type;
5136
5137     check (2);
5138
5139     status = _csi_ostack_get_matrix (ctx, 0, &m);
5140     if (_csi_unlikely (status))
5141         return status;
5142
5143     obj = _csi_peek_ostack (ctx, 1);
5144     type = csi_object_get_type (obj);
5145     switch (type) {
5146     case CSI_OBJECT_TYPE_CONTEXT:
5147         cairo_set_matrix (obj->datum.cr, &m);
5148         break;
5149     case CSI_OBJECT_TYPE_PATTERN:
5150         cairo_pattern_set_matrix (obj->datum.pattern, &m);
5151         break;
5152     case CSI_OBJECT_TYPE_MATRIX:
5153         obj->datum.matrix->matrix = m;
5154         break;
5155     default:
5156         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
5157     }
5158
5159     pop (1);
5160     return CSI_STATUS_SUCCESS;
5161 }
5162
5163 struct _mime_tag {
5164     csi_t *ctx;
5165     csi_string_t *source;
5166 };
5167 static void
5168 _mime_tag_destroy (void *closure)
5169 {
5170     struct _mime_tag *tag = closure;
5171
5172     if (--tag->source->base.ref)
5173         csi_string_free (tag->ctx, tag->source);
5174
5175     _csi_slab_free (tag->ctx, tag, sizeof (struct _mime_tag));
5176 }
5177
5178 static csi_status_t
5179 _set_mime_data (csi_t *ctx)
5180 {
5181     csi_status_t status;
5182     csi_object_t *obj;
5183     const char *mime = NULL; /* silence the compiler */
5184     csi_object_t source;
5185     cairo_surface_t *surface;
5186     struct _mime_tag *tag;
5187     int type;
5188
5189     check (3);
5190
5191     obj = _csi_peek_ostack (ctx, 0);
5192     type = csi_object_get_type (obj);
5193     switch (type) {
5194     case CSI_OBJECT_TYPE_FILE:
5195         status = _csi_file_as_string (ctx, obj->datum.file, &source);
5196         if (_csi_unlikely (status))
5197             return status;
5198
5199         break;
5200
5201     case CSI_OBJECT_TYPE_STRING:
5202         source = *csi_object_reference (obj);
5203         break;
5204
5205     default:
5206         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
5207     }
5208
5209     status = _csi_ostack_get_string_constant (ctx, 1, &mime);
5210     if (_csi_unlikely (status))
5211         return status;
5212
5213     status = _csi_ostack_get_surface (ctx, 2, &surface);
5214     if (_csi_unlikely (status))
5215         return status;
5216
5217
5218     /* XXX free source */
5219     tag = _csi_slab_alloc (ctx, sizeof (struct _mime_tag));
5220     if (_csi_unlikely (tag == NULL))
5221         return _csi_error (CSI_STATUS_NO_MEMORY);
5222     tag->ctx = cairo_script_interpreter_reference (ctx);
5223     tag->source = source.datum.string;
5224     tag->source->base.ref++;
5225
5226     status = cairo_surface_set_mime_data (surface,
5227                                           mime,
5228                                           (uint8_t *)
5229                                           source.datum.string->string,
5230                                           source.datum.string->len,
5231                                           _mime_tag_destroy, tag);
5232     if (_csi_unlikely (status)) {
5233         _mime_tag_destroy (tag);
5234         return status;
5235     }
5236
5237     pop (2);
5238     return CSI_STATUS_SUCCESS;
5239 }
5240
5241 static csi_status_t
5242 _set_miter_limit (csi_t *ctx)
5243 {
5244     csi_status_t status;
5245     cairo_t *cr;
5246     double miter_limit;
5247
5248     check (2);
5249
5250     status = _csi_ostack_get_number (ctx, 0, &miter_limit);
5251     if (_csi_unlikely (status))
5252         return status;
5253     status = _csi_ostack_get_context (ctx, 1, &cr);
5254     if (_csi_unlikely (status))
5255         return status;
5256
5257     cairo_set_miter_limit (cr, miter_limit);
5258     pop (1);
5259     return CSI_STATUS_SUCCESS;
5260 }
5261
5262 static csi_status_t
5263 _set_operator (csi_t *ctx)
5264 {
5265     cairo_t *cr;
5266     long val;
5267     csi_status_t status;
5268
5269     check (2);
5270
5271     status = _csi_ostack_get_integer (ctx, 0, &val);
5272     if (_csi_unlikely (status))
5273         return status;
5274     status = _csi_ostack_get_context (ctx, 1, &cr);
5275     if (_csi_unlikely (status))
5276         return status;
5277
5278     cairo_set_operator (cr, val);
5279     pop (1);
5280     return CSI_STATUS_SUCCESS;
5281 }
5282
5283 static csi_status_t
5284 _set_scaled_font (csi_t *ctx)
5285 {
5286     cairo_t *cr;
5287     cairo_scaled_font_t *font = NULL; /* silence the compiler */
5288     csi_status_t status;
5289
5290     check (2);
5291
5292     status = _csi_ostack_get_scaled_font (ctx, 0, &font);
5293     if (_csi_unlikely (status))
5294         return status;
5295     status = _csi_ostack_get_context (ctx, 1, &cr);
5296     if (_csi_unlikely (status))
5297         return status;
5298
5299     cairo_set_scaled_font (cr, font);
5300     pop (1);
5301     return CSI_STATUS_SUCCESS;
5302 }
5303
5304 static csi_status_t
5305 _set_source (csi_t *ctx)
5306 {
5307     cairo_t *cr;
5308     cairo_pattern_t *pattern = NULL; /* silence the compiler */
5309     csi_status_t status;
5310
5311     check (2);
5312
5313     status = _csi_ostack_get_pattern (ctx, 0, &pattern);
5314     if (_csi_unlikely (status))
5315         return status;
5316     status = _csi_ostack_get_context (ctx, 1, &cr);
5317     if (_csi_unlikely (status))
5318         return status;
5319
5320     cairo_set_source (cr, pattern);
5321     pop (1);
5322     return CSI_STATUS_SUCCESS;
5323 }
5324
5325 static csi_boolean_t
5326 _matching_images (cairo_surface_t *a, cairo_surface_t *b)
5327 {
5328     cairo_format_t format_a, format_b;
5329
5330     if (cairo_surface_get_type (a) != CAIRO_SURFACE_TYPE_IMAGE)
5331         return FALSE;
5332     if (cairo_surface_get_type (b) != CAIRO_SURFACE_TYPE_IMAGE)
5333         return FALSE;
5334
5335     if (cairo_image_surface_get_height (a) != cairo_image_surface_get_height (b))
5336         return FALSE;
5337
5338     if (cairo_image_surface_get_width (a) != cairo_image_surface_get_width (b))
5339         return FALSE;
5340
5341     format_a = cairo_image_surface_get_format (a);
5342     if (format_a == CAIRO_FORMAT_RGB24)
5343         format_a = CAIRO_FORMAT_ARGB32;
5344
5345     format_b = cairo_image_surface_get_format (b);
5346     if (format_b == CAIRO_FORMAT_RGB24)
5347         format_b = CAIRO_FORMAT_ARGB32;
5348
5349     if (format_a != format_b)
5350         return FALSE;
5351
5352     return TRUE;
5353 }
5354
5355 static csi_status_t
5356 _set_source_image (csi_t *ctx)
5357 {
5358     csi_status_t status;
5359     cairo_surface_t *surface;
5360     cairo_surface_t *source;
5361     unsigned char *image_surface_data;
5362     unsigned char *source_data;
5363     check (2);
5364
5365     status = _csi_ostack_get_surface (ctx, 0, &source);
5366     if (_csi_unlikely (status))
5367         return status;
5368     status = _csi_ostack_get_surface (ctx, 1, &surface);
5369     if (_csi_unlikely (status))
5370         return status;
5371
5372     /* Catch the most frequent use of simply uploading pixel data,
5373      * principally to remove the pixman ops from the profiles.
5374      */
5375     if (_csi_likely (_matching_images (surface, source))) {
5376         if (cairo_surface_get_reference_count (surface) == 1 &&
5377             cairo_surface_get_reference_count (source) == 1)
5378         {
5379             _csi_peek_ostack (ctx, 0)->datum.surface = surface;
5380             _csi_peek_ostack (ctx, 1)->datum.surface = source;
5381         }
5382         else
5383         {
5384             cairo_surface_flush (surface);
5385             image_surface_data = cairo_image_surface_get_data (surface);
5386             if (image_surface_data == NULL)
5387                 return _csi_error (CSI_STATUS_NULL_POINTER);
5388
5389             source_data = cairo_image_surface_get_data (source);
5390             if (source_data == NULL)
5391                 return _csi_error (CSI_STATUS_NULL_POINTER);
5392
5393             memcpy (image_surface_data,
5394                     source_data,
5395                     cairo_image_surface_get_height (source) * cairo_image_surface_get_stride (source));
5396             cairo_surface_mark_dirty (surface);
5397         }
5398     } else {
5399         cairo_t *cr;
5400
5401         cr = cairo_create (surface);
5402         cairo_set_source_surface (cr, source, 0, 0);
5403         cairo_paint (cr);
5404         cairo_destroy (cr);
5405     }
5406
5407     pop (1);
5408     return CSI_STATUS_SUCCESS;
5409 }
5410
5411 static csi_status_t
5412 _set_source_rgb (csi_t *ctx)
5413 {
5414     csi_status_t status;
5415     double r,g,b;
5416     cairo_t *cr;
5417
5418     check (4);
5419
5420     status = _csi_ostack_get_number (ctx, 0, &b);
5421     if (_csi_unlikely (status))
5422         return status;
5423     status = _csi_ostack_get_number (ctx, 1, &g);
5424     if (_csi_unlikely (status))
5425         return status;
5426     status = _csi_ostack_get_number (ctx, 2, &r);
5427     if (_csi_unlikely (status))
5428         return status;
5429     status = _csi_ostack_get_context (ctx, 3, &cr);
5430     if (_csi_unlikely (status))
5431         return status;
5432
5433     cairo_set_source_rgb (cr, r, g, b);
5434     pop (3);
5435     return CSI_STATUS_SUCCESS;
5436 }
5437
5438 static csi_status_t
5439 _set_source_rgba (csi_t *ctx)
5440 {
5441     csi_status_t status;
5442     double r,g,b,a;
5443     cairo_t *cr;
5444
5445     check (5);
5446
5447     status = _csi_ostack_get_number (ctx, 0, &a);
5448     if (_csi_unlikely (status))
5449         return status;
5450     status = _csi_ostack_get_number (ctx, 1, &b);
5451     if (_csi_unlikely (status))
5452         return status;
5453     status = _csi_ostack_get_number (ctx, 2, &g);
5454     if (_csi_unlikely (status))
5455         return status;
5456     status = _csi_ostack_get_number (ctx, 3, &r);
5457     if (_csi_unlikely (status))
5458         return status;
5459     status = _csi_ostack_get_context (ctx, 4, &cr);
5460     if (_csi_unlikely (status))
5461         return status;
5462
5463     cairo_set_source_rgba (cr, r, g, b, a);
5464     pop (4);
5465     return CSI_STATUS_SUCCESS;
5466 }
5467
5468 static csi_status_t
5469 _set_tolerance (csi_t *ctx)
5470 {
5471     csi_status_t status;
5472     cairo_t *cr;
5473     double tolerance;
5474
5475     check (2);
5476
5477     status = _csi_ostack_get_number (ctx, 0, &tolerance);
5478     if (_csi_unlikely (status))
5479         return status;
5480     status = _csi_ostack_get_context (ctx, 1, &cr);
5481     if (_csi_unlikely (status))
5482         return status;
5483
5484     cairo_set_tolerance (cr, tolerance);
5485     pop (1);
5486     return CSI_STATUS_SUCCESS;
5487 }
5488
5489 static csi_status_t
5490 _transform (csi_t *ctx)
5491 {
5492     csi_object_t *obj;
5493     csi_status_t status;
5494     cairo_matrix_t m;
5495     int type;
5496
5497     check (2);
5498
5499     status = _csi_ostack_get_matrix (ctx, 0, &m);
5500     if (_csi_unlikely (status))
5501         return status;
5502
5503     obj = _csi_peek_ostack (ctx, 1);
5504     type = csi_object_get_type (obj);
5505     switch (type) {
5506     case CSI_OBJECT_TYPE_CONTEXT:
5507         cairo_transform (obj->datum.cr, &m);
5508         break;
5509     case CSI_OBJECT_TYPE_PATTERN:
5510         {
5511             cairo_matrix_t ctm;
5512             cairo_pattern_get_matrix (obj->datum.pattern, &ctm);
5513             cairo_matrix_multiply (&ctm, &m, &ctm);
5514             cairo_pattern_set_matrix (obj->datum.pattern, &ctm);
5515         }
5516         break;
5517     case CSI_OBJECT_TYPE_MATRIX:
5518             cairo_matrix_multiply (&obj->datum.matrix->matrix,
5519                                    &m,
5520                                    &obj->datum.matrix->matrix);
5521         break;
5522     default:
5523         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
5524     }
5525
5526     pop (1);
5527     return CSI_STATUS_SUCCESS;
5528 }
5529
5530 static csi_status_t
5531 _translate (csi_t *ctx)
5532 {
5533     csi_object_t *obj;
5534     csi_status_t status;
5535     double x, y;
5536     int type;
5537
5538     check (3);
5539
5540     status = _csi_ostack_get_number (ctx, 0, &y);
5541     if (_csi_unlikely (status))
5542         return status;
5543     status = _csi_ostack_get_number (ctx, 1, &x);
5544     if (_csi_unlikely (status))
5545         return status;
5546
5547     obj = _csi_peek_ostack (ctx, 2);
5548     type = csi_object_get_type (obj);
5549     switch (type) {
5550     case CSI_OBJECT_TYPE_CONTEXT:
5551         cairo_translate (obj->datum.cr, x, y);
5552         break;
5553
5554     case CSI_OBJECT_TYPE_PATTERN:
5555         {
5556             cairo_matrix_t ctm;
5557             cairo_pattern_get_matrix (obj->datum.pattern, &ctm);
5558             cairo_matrix_translate (&ctm, x, y);
5559             cairo_pattern_set_matrix (obj->datum.pattern, &ctm);
5560         }
5561         break;
5562
5563
5564     case CSI_OBJECT_TYPE_MATRIX:
5565         cairo_matrix_translate (&obj->datum.matrix->matrix, x, y);
5566         break;
5567
5568     default:
5569         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
5570     }
5571
5572     pop (2);
5573     return CSI_STATUS_SUCCESS;
5574 }
5575
5576 static csi_status_t
5577 _true (csi_t *ctx)
5578 {
5579     return _csi_push_ostack_boolean (ctx, TRUE);
5580 }
5581
5582 static csi_status_t
5583 _show_page (csi_t *ctx)
5584 {
5585     csi_object_t *obj;
5586     int type;
5587
5588     check (1);
5589
5590     obj = _csi_peek_ostack (ctx, 0);
5591     type = csi_object_get_type (obj);
5592     switch (type) {
5593     case CSI_OBJECT_TYPE_CONTEXT:
5594         cairo_show_page (obj->datum.cr);
5595         if (ctx->hooks.copy_page != NULL)
5596             ctx->hooks.copy_page (ctx->hooks.closure, obj->datum.cr);
5597         break;
5598     case CSI_OBJECT_TYPE_SURFACE:
5599         cairo_surface_show_page (obj->datum.surface);
5600         /* XXX hook? */
5601         break;
5602     default:
5603         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
5604     }
5605
5606     return CSI_STATUS_SUCCESS;
5607 }
5608
5609 static csi_status_t
5610 _similar (csi_t *ctx)
5611 {
5612     csi_object_t obj;
5613     long content;
5614     double width, height;
5615     cairo_surface_t *other;
5616     csi_status_t status;
5617
5618     check (4);
5619
5620     status = _csi_ostack_get_integer (ctx, 0, &content);
5621     if (_csi_unlikely (status))
5622         return status;
5623     status = _csi_ostack_get_number (ctx, 1, &height);
5624     if (_csi_unlikely (status))
5625         return status;
5626     status = _csi_ostack_get_number (ctx, 2, &width);
5627     if (_csi_unlikely (status))
5628         return status;
5629     status = _csi_ostack_get_surface (ctx, 3, &other);
5630     if (_csi_unlikely (status))
5631         return status;
5632
5633     /* silently fix-up a common bug when writing CS */
5634     if ((content & CAIRO_CONTENT_COLOR_ALPHA) == 0) {
5635         if (_csi_unlikely (content & ~CAIRO_CONTENT_COLOR_ALPHA))
5636             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
5637
5638         switch ((int) content) {
5639         default:
5640         case CAIRO_FORMAT_ARGB32:
5641             content = CAIRO_CONTENT_COLOR_ALPHA;
5642             break;
5643         case CAIRO_FORMAT_RGB16_565:
5644         case CAIRO_FORMAT_RGB24:
5645             content = CAIRO_CONTENT_COLOR;
5646             break;
5647         case CAIRO_FORMAT_A8:
5648         case CAIRO_FORMAT_A1:
5649             content = CAIRO_CONTENT_ALPHA;
5650             break;
5651         }
5652     }
5653
5654     obj.type = CSI_OBJECT_TYPE_SURFACE;
5655     obj.datum.surface = cairo_surface_create_similar (other,
5656                                                       content, width, height);
5657     pop (4);
5658     return push (&obj);
5659 }
5660
5661 static csi_status_t
5662 _similar_image (csi_t *ctx)
5663 {
5664     csi_object_t obj;
5665     long format;
5666     double width, height;
5667     cairo_surface_t *other;
5668     csi_status_t status;
5669
5670     check (4);
5671
5672     status = _csi_ostack_get_number (ctx, 0, &height);
5673     if (_csi_unlikely (status))
5674         return status;
5675     status = _csi_ostack_get_number (ctx, 1, &width);
5676     if (_csi_unlikely (status))
5677         return status;
5678     status = _csi_ostack_get_integer (ctx, 2, &format);
5679     if (_csi_unlikely (status))
5680         return status;
5681     status = _csi_ostack_get_surface (ctx, 3, &other);
5682     if (_csi_unlikely (status))
5683         return status;
5684
5685     obj.type = CSI_OBJECT_TYPE_SURFACE;
5686     obj.datum.surface = cairo_surface_create_similar_image (other,
5687                                                             format,
5688                                                             width, height);
5689     pop (4);
5690     return push (&obj);
5691 }
5692
5693 static csi_status_t
5694 _subsurface (csi_t *ctx)
5695 {
5696     csi_object_t obj;
5697     double x, y, width, height;
5698     cairo_surface_t *target;
5699     csi_status_t status;
5700
5701     check (5);
5702
5703     status = _csi_ostack_get_number (ctx, 0, &height);
5704     if (_csi_unlikely (status))
5705         return status;
5706     status = _csi_ostack_get_number (ctx, 1, &width);
5707     if (_csi_unlikely (status))
5708         return status;
5709     status = _csi_ostack_get_number (ctx, 2, &y);
5710     if (_csi_unlikely (status))
5711         return status;
5712     status = _csi_ostack_get_number (ctx, 3, &x);
5713     if (_csi_unlikely (status))
5714         return status;
5715     status = _csi_ostack_get_surface (ctx, 4, &target);
5716     if (_csi_unlikely (status))
5717         return status;
5718
5719     obj.type = CSI_OBJECT_TYPE_SURFACE;
5720     obj.datum.surface = cairo_surface_create_for_rectangle (target, x, y, width, height);
5721     pop (5);
5722     return push (&obj);
5723 }
5724
5725 static csi_status_t
5726 _show_text (csi_t *ctx)
5727 {
5728     csi_status_t status;
5729     csi_string_t *text;
5730     cairo_t *cr;
5731
5732     check (2);
5733
5734     status = _csi_ostack_get_string (ctx, 0, &text);
5735     if (_csi_unlikely (status))
5736         return status;
5737     status = _csi_ostack_get_context (ctx, 1, &cr);
5738     if (_csi_unlikely (status))
5739         return status;
5740
5741     cairo_show_text (cr, text->string);
5742     pop (1);
5743     return CSI_STATUS_SUCCESS;
5744 }
5745
5746 static csi_status_t
5747 _show_glyphs (csi_t *ctx)
5748 {
5749     csi_array_t *array;
5750     csi_status_t status;
5751     cairo_t *cr;
5752     cairo_glyph_t stack_glyphs[256], *glyphs;
5753     csi_integer_t nglyphs, i;
5754
5755     check (2);
5756
5757     status = _csi_ostack_get_array (ctx, 0, &array);
5758     if (_csi_unlikely (status))
5759         return status;
5760     status = _csi_ostack_get_context (ctx, 1, &cr);
5761     if (_csi_unlikely (status))
5762         return status;
5763
5764     /* count glyphs */
5765     nglyphs = 0;
5766     for (i = 0; i < array->stack.len; i++) {
5767         csi_object_t *obj = &array->stack.objects[i];
5768         int type = csi_object_get_type (obj);
5769         switch (type) {
5770         case CSI_OBJECT_TYPE_ARRAY:
5771             nglyphs += obj->datum.array->stack.len;
5772             break;
5773         case CSI_OBJECT_TYPE_STRING:
5774             nglyphs += obj->datum.string->len;
5775             break;
5776         }
5777     }
5778     if (nglyphs == 0) {
5779         pop (1);
5780         return CSI_STATUS_SUCCESS;
5781     }
5782
5783     if (nglyphs > ARRAY_LENGTH (stack_glyphs)) {
5784         if (_csi_unlikely ((unsigned) nglyphs >= INT_MAX / sizeof (cairo_glyph_t)))
5785             return _csi_error (CSI_STATUS_NO_MEMORY);
5786
5787         glyphs = _csi_alloc (ctx, sizeof (cairo_glyph_t) * nglyphs);
5788         if (_csi_unlikely (glyphs == NULL))
5789             return _csi_error (CSI_STATUS_NO_MEMORY);
5790     } else
5791         glyphs = stack_glyphs;
5792
5793     nglyphs = _glyph_string (ctx, array, cairo_get_scaled_font (cr), glyphs);
5794     cairo_show_glyphs (cr, glyphs, nglyphs);
5795
5796     if (glyphs != stack_glyphs)
5797         _csi_free (ctx, glyphs);
5798
5799     pop (1);
5800     return CSI_STATUS_SUCCESS;
5801 }
5802
5803 static csi_status_t
5804 _show_text_glyphs (csi_t *ctx)
5805 {
5806     csi_object_t *obj;
5807     csi_array_t *array;
5808     csi_string_t *string;
5809     csi_string_t *utf8_string;
5810     csi_status_t status;
5811     cairo_t *cr;
5812     cairo_text_cluster_t stack_clusters[256], *clusters;
5813     cairo_glyph_t stack_glyphs[256], *glyphs;
5814     csi_integer_t nglyphs, nclusters, i;
5815     long direction;
5816     int type;
5817
5818     check (5);
5819
5820     status = _csi_ostack_get_integer (ctx, 0, &direction);
5821     if (_csi_unlikely (status))
5822         return status;
5823
5824     obj = _csi_peek_ostack (ctx, 1);
5825     type = csi_object_get_type (obj);
5826     switch (type) {
5827     case CSI_OBJECT_TYPE_ARRAY:
5828         array = obj->datum.array;
5829         nclusters = array->stack.len / 2;
5830         if (nclusters > ARRAY_LENGTH (stack_clusters)) {
5831             if (_csi_unlikely ((unsigned) nclusters >= INT_MAX / sizeof (cairo_text_cluster_t)))
5832                 return _csi_error (CSI_STATUS_NO_MEMORY);
5833             clusters = _csi_alloc (ctx, sizeof (cairo_text_cluster_t) * nclusters);
5834             if (_csi_unlikely (clusters == NULL))
5835                 return _csi_error (CSI_STATUS_NO_MEMORY);
5836         } else
5837             clusters = stack_clusters;
5838
5839         for (i = 0; i < nclusters; i++) {
5840             clusters[i].num_bytes = csi_number_get_value (&array->stack.objects[2*i+0]);
5841             clusters[i].num_glyphs = csi_number_get_value (&array->stack.objects[2*i+1]);
5842         }
5843         break;
5844
5845     case CSI_OBJECT_TYPE_STRING:
5846         string = obj->datum.string;
5847         nclusters = string->len / 2;
5848         if (nclusters > ARRAY_LENGTH (stack_clusters)) {
5849             if (_csi_unlikely ((unsigned) nclusters >= INT_MAX / sizeof (cairo_text_cluster_t)))
5850                 return _csi_error (CSI_STATUS_NO_MEMORY);
5851             clusters = _csi_alloc (ctx, sizeof (cairo_text_cluster_t) * nclusters);
5852             if (_csi_unlikely (clusters == NULL))
5853                 return _csi_error (CSI_STATUS_NO_MEMORY);
5854         } else
5855             clusters = stack_clusters;
5856
5857         for (i = 0; i < nclusters; i++) {
5858             clusters[i].num_bytes = string->string[2*i+0];
5859             clusters[i].num_glyphs = string->string[2*i+1];
5860         }
5861         break;
5862
5863     default:
5864         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
5865     }
5866
5867     status = _csi_ostack_get_array (ctx, 2, &array);
5868     if (_csi_unlikely (status)) {
5869         if (clusters != stack_clusters)
5870             _csi_free (ctx, clusters);
5871         return status;
5872     }
5873     status = _csi_ostack_get_string (ctx, 3, &utf8_string);
5874     if (_csi_unlikely (status)) {
5875         if (clusters != stack_clusters)
5876             _csi_free (ctx, clusters);
5877         return status;
5878     }
5879     status = _csi_ostack_get_context (ctx, 4, &cr);
5880     if (_csi_unlikely (status)) {
5881         if (clusters != stack_clusters)
5882             _csi_free (ctx, clusters);
5883         return status;
5884     }
5885
5886     /* count glyphs */
5887     nglyphs = 0;
5888     for (i = 0; i < array->stack.len; i++) {
5889         obj = &array->stack.objects[i];
5890         type = csi_object_get_type (obj);
5891         switch (type) {
5892         case CSI_OBJECT_TYPE_ARRAY:
5893             nglyphs += obj->datum.array->stack.len;
5894             break;
5895         case CSI_OBJECT_TYPE_STRING:
5896             nglyphs += obj->datum.string->len;
5897             break;
5898         }
5899     }
5900     if (nglyphs == 0) {
5901         pop (4);
5902         if (clusters != stack_clusters)
5903             _csi_free (ctx, clusters);
5904         return CSI_STATUS_SUCCESS;
5905     }
5906
5907     if (nglyphs > ARRAY_LENGTH (stack_glyphs)) {
5908         if (_csi_unlikely ((unsigned) nglyphs >= INT_MAX / sizeof (cairo_glyph_t))) {
5909             if (clusters != stack_clusters)
5910                 _csi_free (ctx, clusters);
5911             return _csi_error (CSI_STATUS_NO_MEMORY);
5912         }
5913
5914         glyphs = _csi_alloc (ctx, sizeof (cairo_glyph_t) * nglyphs);
5915         if (_csi_unlikely (glyphs == NULL)) {
5916             if (clusters != stack_clusters)
5917                 _csi_free (ctx, clusters);
5918             return _csi_error (CSI_STATUS_NO_MEMORY);
5919         }
5920     } else
5921         glyphs = stack_glyphs;
5922
5923     nglyphs = _glyph_string (ctx, array, cairo_get_scaled_font (cr), glyphs);
5924     cairo_show_text_glyphs (cr,
5925                             utf8_string->string, utf8_string->len,
5926                             glyphs, nglyphs,
5927                             clusters, nclusters,
5928                             direction);
5929
5930     if (clusters != stack_clusters)
5931         _csi_free (ctx, clusters);
5932     if (glyphs != stack_glyphs)
5933         _csi_free (ctx, glyphs);
5934
5935     pop (4);
5936     return CSI_STATUS_SUCCESS;
5937 }
5938
5939 static csi_status_t
5940 _stroke (csi_t *ctx)
5941 {
5942     return _do_cairo_op (ctx, cairo_stroke);
5943 }
5944
5945 static csi_status_t
5946 _stroke_preserve (csi_t *ctx)
5947 {
5948     return _do_cairo_op (ctx, cairo_stroke_preserve);
5949 }
5950
5951 static csi_status_t
5952 _sub (csi_t *ctx)
5953 {
5954     csi_object_t *A;
5955     csi_object_t *B;
5956     csi_object_type_t type_a, type_b;
5957
5958     check (2);
5959
5960     B = _csi_peek_ostack (ctx, 0);
5961     A = _csi_peek_ostack (ctx, 1);
5962
5963     type_a = csi_object_get_type (A);
5964     if (_csi_unlikely (! (type_a == CSI_OBJECT_TYPE_INTEGER ||
5965                             type_a == CSI_OBJECT_TYPE_REAL)))
5966     {
5967         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
5968     }
5969
5970     type_b = csi_object_get_type (B);
5971     if (_csi_unlikely (! (type_b == CSI_OBJECT_TYPE_INTEGER ||
5972                             type_b == CSI_OBJECT_TYPE_REAL)))
5973     {
5974         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
5975     }
5976
5977     pop (2);
5978
5979     if (type_a == CSI_OBJECT_TYPE_REAL &&
5980         type_b == CSI_OBJECT_TYPE_REAL)
5981     {
5982         return _csi_push_ostack_real (ctx, A->datum.real - B->datum.real);
5983
5984     }
5985     else if (type_a == CSI_OBJECT_TYPE_INTEGER &&
5986              type_b == CSI_OBJECT_TYPE_INTEGER)
5987     {
5988         return _csi_push_ostack_integer (ctx,
5989                                          A->datum.integer - B->datum.integer);
5990     }
5991     else
5992     {
5993         double v;
5994
5995         if (type_a == CSI_OBJECT_TYPE_REAL)
5996             v = A->datum.real;
5997         else
5998             v = A->datum.integer;
5999
6000         if (type_b == CSI_OBJECT_TYPE_REAL)
6001             v -= B->datum.real;
6002         else
6003             v -= B->datum.integer;
6004
6005         return _csi_push_ostack_real (ctx, v);
6006     }
6007 }
6008
6009 static csi_status_t
6010 _surface (csi_t *ctx)
6011 {
6012     csi_object_t obj;
6013     csi_dictionary_t *dict;
6014     csi_proxy_t *proxy;
6015     csi_object_t key;
6016     double width, height;
6017     csi_surface_create_func_t hook;
6018     long content;
6019     cairo_surface_t *surface;
6020     long uid;
6021     csi_status_t status;
6022
6023     check (1);
6024
6025     status = _csi_ostack_get_dictionary (ctx, 0, &dict);
6026     if (_csi_unlikely (status))
6027         return status;
6028
6029     status = _csi_dictionary_get_number (ctx, dict, "width", FALSE, &width);
6030     if (_csi_unlikely (status))
6031         return status;
6032     status = _csi_dictionary_get_number (ctx, dict, "height", FALSE, &height);
6033     if (_csi_unlikely (status))
6034         return status;
6035
6036     content = CAIRO_CONTENT_COLOR_ALPHA;
6037     status = _csi_dictionary_get_integer (ctx, dict, "content", TRUE, &content);
6038     if (_csi_unlikely (status))
6039         return status;
6040
6041     uid = 0;
6042     status = _csi_dictionary_get_integer (ctx, dict, "uid", TRUE, &uid);
6043     if (_csi_unlikely (status))
6044         return status;
6045     if (uid == 0) {
6046         status = _csi_dictionary_get_integer (ctx, dict, "drawable", TRUE, &uid);
6047         if (_csi_unlikely (status))
6048             return status;
6049     }
6050
6051     hook = ctx->hooks.surface_create;
6052     assert (hook != NULL);
6053
6054     surface = hook (ctx->hooks.closure, content, width, height, uid);
6055     if (_csi_unlikely (surface == NULL)) {
6056         return _csi_error (CSI_STATUS_NULL_POINTER);
6057     }
6058
6059     proxy = _csi_proxy_create (ctx, surface, dict,
6060                                ctx->hooks.surface_destroy,
6061                                ctx->hooks.closure);
6062     if (_csi_unlikely (proxy == NULL)) {
6063         cairo_surface_destroy (surface);
6064         return _csi_error (CSI_STATUS_NO_MEMORY);
6065     }
6066
6067     status = cairo_surface_set_user_data (surface,
6068                                           &_csi_proxy_key,
6069                                           proxy, _csi_proxy_destroy);
6070     if (_csi_unlikely (status)) {
6071         _csi_proxy_destroy (proxy);
6072         cairo_surface_destroy (surface);
6073         return status;
6074     }
6075
6076     status = csi_name_new_static (ctx, &key, "fallback-resolution");
6077     if (_csi_unlikely (status)) {
6078         cairo_surface_destroy (surface);
6079         return status;
6080     }
6081     if (csi_dictionary_has (dict, key.datum.name)) {
6082         status = csi_dictionary_get (ctx, dict, key.datum.name, &obj);
6083         if (_csi_unlikely (status)) {
6084             cairo_surface_destroy (surface);
6085             return status;
6086         }
6087         if (csi_object_get_type (&obj) == CSI_OBJECT_TYPE_ARRAY) {
6088             csi_array_t *array = obj.datum.array;
6089             if (array->stack.len == 2) {
6090                 cairo_surface_set_fallback_resolution (surface,
6091                                                        csi_number_get_value
6092                                                        (&array->stack.objects[0]),
6093                                                        csi_number_get_value
6094                                                        (&array->stack.objects[1]));
6095             }
6096         }
6097     }
6098     /* initialise surface to source */
6099     status = csi_name_new_static (ctx, &key, "source");
6100     if (_csi_unlikely (status)) {
6101         cairo_surface_destroy (surface);
6102         return status;
6103     }
6104     if (csi_dictionary_has (dict, key.datum.name)) {
6105         cairo_surface_t *image;
6106         cairo_t *cr;
6107
6108         status = _image_load_from_dictionary (ctx, dict, &image);
6109         if (_csi_unlikely (status)) {
6110             cairo_surface_destroy (surface);
6111             cairo_surface_destroy (image);
6112             return status;
6113         }
6114
6115         cr = cairo_create (surface);
6116         cairo_set_source_surface (cr, image, 0, 0);
6117         cairo_surface_destroy (image);
6118         cairo_paint (cr);
6119         status = cairo_status (cr);
6120         cairo_destroy (cr);
6121
6122         if (_csi_unlikely (status))
6123             return status;
6124     }
6125
6126     status = csi_name_new_static (ctx, &key, "device-offset");
6127     if (_csi_unlikely (status)) {
6128         cairo_surface_destroy (surface);
6129         return status;
6130     }
6131     if (csi_dictionary_has (dict, key.datum.name)) {
6132         status = csi_dictionary_get (ctx, dict, key.datum.name, &obj);
6133         if (_csi_unlikely (status))
6134             return status;
6135
6136         if (csi_object_get_type (&obj) == CSI_OBJECT_TYPE_ARRAY) {
6137             csi_array_t *array = obj.datum.array;
6138
6139             if (array->stack.len == 2) {
6140                 cairo_surface_set_device_offset (surface,
6141                                                  csi_number_get_value
6142                                                  (&array->stack.objects[0]),
6143                                                  csi_number_get_value
6144                                                  (&array->stack.objects[1]));
6145             }
6146         }
6147     }
6148
6149     obj.type = CSI_OBJECT_TYPE_SURFACE;
6150     obj.datum.surface = surface;
6151     pop (1);
6152     return push (&obj);
6153 }
6154
6155 static csi_status_t
6156 _record (csi_t *ctx)
6157 {
6158     csi_object_t obj;
6159     long content;
6160     csi_array_t *array;
6161     csi_status_t status;
6162     cairo_rectangle_t extents;
6163     cairo_rectangle_t *r;
6164
6165     check (2);
6166
6167     status = _csi_ostack_get_array (ctx, 0, &array);
6168     if (_csi_unlikely (status))
6169         return status;
6170
6171     status = _csi_ostack_get_integer (ctx, 1, &content);
6172     if (_csi_unlikely (status))
6173         return status;
6174
6175     switch (array->stack.len) {
6176     case 0:
6177         r = NULL;
6178         break;
6179     case 2:
6180         extents.x = extents.y = 0;
6181         extents.width = _csi_object_as_real (&array->stack.objects[0]);
6182         extents.height = _csi_object_as_real (&array->stack.objects[1]);
6183         r = &extents;
6184         break;
6185     case 4:
6186         extents.x = _csi_object_as_real (&array->stack.objects[0]);
6187         extents.y = _csi_object_as_real (&array->stack.objects[1]);
6188         extents.width = _csi_object_as_real (&array->stack.objects[2]);
6189         extents.height = _csi_object_as_real (&array->stack.objects[3]);
6190         r = &extents;
6191         break;
6192     default:
6193         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
6194     }
6195
6196     obj.type = CSI_OBJECT_TYPE_SURFACE;
6197     obj.datum.surface = cairo_recording_surface_create (content, r);
6198     pop (2);
6199     return push (&obj);
6200 }
6201
6202 static csi_status_t
6203 _text_path (csi_t *ctx)
6204 {
6205     csi_status_t status;
6206     csi_string_t *text;
6207     cairo_t *cr;
6208
6209     check (2);
6210
6211     status = _csi_ostack_get_string (ctx, 0, &text);
6212     if (_csi_unlikely (status))
6213         return status;
6214     status = _csi_ostack_get_context (ctx, 1, &cr);
6215     if (_csi_unlikely (status))
6216         return status;
6217
6218     cairo_text_path (cr, text->string);
6219     pop (1);
6220     return CSI_STATUS_SUCCESS;
6221 }
6222
6223 static csi_status_t
6224 _undef (csi_t *ctx)
6225 {
6226     csi_name_t name = 0; /* silence the compiler */
6227     csi_status_t status;
6228
6229     check (1);
6230
6231     status = _csi_ostack_get_name (ctx, 0, &name);
6232     if (_csi_unlikely (status))
6233         return status;
6234
6235     status = _csi_name_undefine (ctx, name);
6236     if (_csi_unlikely (status))
6237         return status;
6238
6239     pop (1);
6240     return CSI_STATUS_SUCCESS;
6241 }
6242
6243 static csi_status_t
6244 _unset (csi_t *ctx)
6245 {
6246     csi_object_t *dst;
6247     csi_name_t name = 0; /* silence the compiler */
6248     csi_status_t status;
6249     int type;
6250
6251     check (2);
6252
6253     status = _csi_ostack_get_name (ctx, 0, &name);
6254     if (_csi_unlikely (status))
6255         return status;
6256
6257     dst = _csi_peek_ostack (ctx, 1);
6258     type = csi_object_get_type (dst);
6259     switch (type) {
6260     case CSI_OBJECT_TYPE_DICTIONARY:
6261         csi_dictionary_remove (ctx, dst->datum.dictionary, name);
6262         break;
6263     case CSI_OBJECT_TYPE_STRING:
6264     case CSI_OBJECT_TYPE_ARRAY:
6265     default:
6266         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
6267     }
6268
6269     pop (1);
6270     return CSI_STATUS_SUCCESS;
6271 }
6272
6273 static csi_status_t
6274 _write_to_png (csi_t *ctx)
6275 {
6276     csi_status_t status;
6277     csi_string_t *filename;
6278     cairo_surface_t *surface;
6279
6280     check (2);
6281
6282     status = _csi_ostack_get_string (ctx, 0, &filename);
6283     if (_csi_unlikely (status))
6284         return status;
6285     status = _csi_ostack_get_surface (ctx, 1, &surface);
6286     if (_csi_unlikely (status))
6287         return status;
6288
6289 #if CAIRO_HAS_PNG_FUNCTIONS
6290     status = cairo_surface_write_to_png (surface, filename->string);
6291     if (_csi_unlikely (status))
6292         return status;
6293 #else
6294     return CAIRO_STATUS_WRITE_ERROR;
6295 #endif
6296
6297     pop (1);
6298     return CSI_STATUS_SUCCESS;
6299 }
6300
6301 static csi_status_t
6302 _write_to_script (csi_t *ctx)
6303 {
6304     csi_status_t status;
6305     csi_string_t *filename;
6306     cairo_surface_t *record;
6307
6308     check (2);
6309
6310     status = _csi_ostack_get_string (ctx, 0, &filename);
6311     if (_csi_unlikely (status))
6312         return status;
6313     status = _csi_ostack_get_surface (ctx, 1, &record);
6314     if (_csi_unlikely (status))
6315         return status;
6316
6317     if (cairo_surface_get_type (record) != CAIRO_SURFACE_TYPE_RECORDING)
6318         return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
6319
6320 #if CAIRO_HAS_SCRIPT_SURFACE
6321     {
6322         cairo_device_t *script;
6323
6324         script = cairo_script_create (filename->string);
6325         status = cairo_script_from_recording_surface (script, record);
6326         cairo_device_destroy (script);
6327         if (_csi_unlikely (status))
6328             return status;
6329     }
6330 #else
6331     return CAIRO_STATUS_WRITE_ERROR;
6332 #endif
6333
6334     pop (1);
6335     return CSI_STATUS_SUCCESS;
6336 }
6337
6338 static csi_status_t
6339 _xor (csi_t *ctx)
6340 {
6341     csi_object_t *a, *b;
6342     int type;
6343
6344     check (2);
6345
6346     a = _csi_peek_ostack (ctx, 0);
6347     b = _csi_peek_ostack (ctx, 1);
6348     if (_csi_unlikely (csi_object_get_type (a) != csi_object_get_type (b)))
6349         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
6350
6351     pop (2);
6352     type = csi_object_get_type (a);
6353     switch (type) {
6354     case CSI_OBJECT_TYPE_INTEGER:
6355         return _csi_push_ostack_integer (ctx,
6356                                          a->datum.integer ^ b->datum.integer);
6357     case CSI_OBJECT_TYPE_BOOLEAN:
6358         return _csi_push_ostack_boolean (ctx,
6359                                          a->datum.boolean ^ b->datum.boolean);
6360     default:
6361         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
6362     }
6363 }
6364
6365 static csi_status_t
6366 _debug_print (csi_t *ctx)
6367 {
6368     csi_object_t *obj;
6369
6370     check (1);
6371     obj = _csi_peek_ostack (ctx, 0);
6372     switch (csi_object_get_type (obj)) {
6373     case CSI_OBJECT_TYPE_NULL:
6374         fprintf (stderr, "NULL\n");
6375         break;
6376
6377         /* atomics */
6378     case CSI_OBJECT_TYPE_BOOLEAN:
6379         fprintf (stderr, "boolean: %s\n",
6380                  obj->datum.boolean ? "true" : "false");
6381         break;
6382     case CSI_OBJECT_TYPE_INTEGER:
6383         fprintf (stderr, "integer: %ld\n", obj->datum.integer);
6384         break;
6385     case CSI_OBJECT_TYPE_MARK:
6386         fprintf (stderr, "mark\n");
6387         break;
6388     case CSI_OBJECT_TYPE_NAME:
6389         fprintf (stderr, "name: %s\n", (char *) obj->datum.name);
6390         break;
6391     case CSI_OBJECT_TYPE_OPERATOR:
6392         fprintf (stderr, "operator: %p\n", obj->datum.ptr);
6393         break;
6394     case CSI_OBJECT_TYPE_REAL:
6395         fprintf (stderr, "real: %g\n", obj->datum.real);
6396         break;
6397
6398         /* compound */
6399     case CSI_OBJECT_TYPE_ARRAY:
6400         fprintf (stderr, "array\n");
6401         break;
6402     case CSI_OBJECT_TYPE_DICTIONARY:
6403         fprintf (stderr, "dictionary\n");
6404         break;
6405     case CSI_OBJECT_TYPE_FILE:
6406         fprintf (stderr, "file\n");
6407         break;
6408     case CSI_OBJECT_TYPE_MATRIX:
6409         fprintf (stderr, "matrix: [%g %g %g %g %g %g]\n",
6410                  obj->datum.matrix->matrix.xx,
6411                  obj->datum.matrix->matrix.yx,
6412                  obj->datum.matrix->matrix.xy,
6413                  obj->datum.matrix->matrix.yy,
6414                  obj->datum.matrix->matrix.x0,
6415                  obj->datum.matrix->matrix.y0);
6416         break;
6417     case CSI_OBJECT_TYPE_STRING:
6418         fprintf (stderr, "string: %s\n", obj->datum.string->string);
6419         break;
6420
6421         /* cairo */
6422     case CSI_OBJECT_TYPE_CONTEXT:
6423         fprintf (stderr, "context\n");
6424         break;
6425     case CSI_OBJECT_TYPE_FONT:
6426         fprintf (stderr, "font\n");
6427         break;
6428     case CSI_OBJECT_TYPE_PATTERN:
6429         fprintf (stderr, "pattern\n");
6430         break;
6431     case CSI_OBJECT_TYPE_SCALED_FONT:
6432         fprintf (stderr, "scaled-font\n");
6433         break;
6434     case CSI_OBJECT_TYPE_SURFACE:
6435         fprintf (stderr, "surface\n");
6436         break;
6437     }
6438     pop (1);
6439     return CSI_STATUS_SUCCESS;
6440 }
6441
6442 static const csi_operator_def_t
6443 _defs[] = {
6444     { "<<", _mark },
6445     { ">>", end_dict_construction },
6446     { "[", _mark },
6447     { "]", end_array_construction },
6448     { "a", _alpha },
6449     { "abs", NULL },
6450     { "add", _add },
6451     { "add-color-stop", _add_color_stop },
6452     { "and", _and },
6453     { "arc", _arc },
6454     { "arc-negative", _arc_negative },
6455     { "arc-", _arc_negative },
6456     { "arc-to", NULL },
6457     { "array", _array },
6458     { "astore", NULL },
6459     { "atan", NULL },
6460     { "bind", _bind },
6461     { "bitshift", _bitshift },
6462     { "c", _curve_to },
6463     { "C", _rel_curve_to },
6464     { "ceiling", NULL },
6465     { "clear", NULL },
6466     { "clear-to-mark", NULL },
6467     { "clip", _clip },
6468     { "clip-extents", NULL },
6469     { "clip-preserve", _clip_preserve },
6470     { "clip+", _clip_preserve },
6471     { "close-path", _close_path },
6472     { "context", _context },
6473     { "copy", _copy },
6474     { "copy-page", _copy_page },
6475     { "cos", NULL },
6476     { "count", NULL },
6477     { "count-to-mark", NULL },
6478     { "curve-to", _curve_to },
6479     { "cvi", _cvi },
6480     { "cvr", _cvr },
6481     { "def", _def },
6482     { "device-to-user", NULL },
6483     { "device-to-user-distance", NULL },
6484     { "dict", _dict },
6485     { "div", _div },
6486     { "dup", _duplicate },
6487     { "eq", _eq },
6488     { "exch", _exch },
6489     { "exec", NULL },
6490     { "exp", NULL },
6491     { "false", _false },
6492     { "fill", _fill },
6493     { "fill-extents", NULL },
6494     { "fill-preserve", _fill_preserve },
6495     { "fill+", _fill_preserve },
6496     { "filter", _filter },
6497     { "floor", NULL },
6498     { "font", _font },
6499     { "for", _for },
6500     { "forall", NULL },
6501     { "g", _gray },
6502     { "ge", _ge },
6503     { "get", _get },
6504     { "glyph-path", _glyph_path },
6505     { "gt", _gt },
6506     { "h", _close_path },
6507     { "identity", _identity },
6508     { "if", _if },
6509     { "ifelse", _ifelse },
6510     { "image", _image },
6511     { "index", _index },
6512     { "integer", _integer },
6513     { "invert", _invert },
6514     { "in-stroke", NULL },
6515     { "in-fill", NULL },
6516     { "known", NULL },
6517     { "l", _line_to },
6518     { "L", _rel_line_to },
6519     { "languagelevel", NULL },
6520     { "le", _le },
6521     { "length", NULL },
6522     { "linear", _linear },
6523     { "line-to", _line_to },
6524     { "ln", NULL },
6525     { "load", NULL },
6526     { "log", NULL },
6527     { "loop", NULL },
6528     { "lt", _lt },
6529     { "m", _move_to },
6530     { "M", _rel_move_to },
6531     { "map-to-image", _map_to_image },
6532     { "mark", _mark },
6533     { "mask", _mask },
6534     { "matrix", _matrix },
6535
6536     { "mesh", _mesh },
6537     { "begin-patch", _mesh_begin_patch },
6538     { "end-patch", _mesh_end_patch },
6539     { "set-control-point", _mesh_set_control_point },
6540     { "set-corner-color", _mesh_set_corner_color },
6541
6542     { "mod", _mod },
6543     { "move-to", _move_to },
6544     { "mul", _mul },
6545     { "multiply", NULL },
6546     { "n", _new_path },
6547     { "N", _new_sub_path },
6548     { "ne", _ne },
6549     { "neg", _neg },
6550     { "new-path", _new_path },
6551     { "new-sub-path", _new_sub_path },
6552     { "not", _not },
6553     { "null", _null },
6554     { "or", _or },
6555     { "paint", _paint },
6556     { "paint-with-alpha", _paint_with_alpha },
6557     { "pattern", _pattern },
6558     { "pop", _pop },
6559     { "pop-group", _pop_group },
6560     { "push-group", _push_group },
6561     { "radial", _radial },
6562     { "rand", NULL },
6563     { "record", _record },
6564     { "rectangle", _rectangle },
6565     { "repeat", _repeat },
6566     { "restore", _restore },
6567     { "rel-curve-to", _rel_curve_to },
6568     { "rel-line-to", _rel_line_to },
6569     { "rel-move-to", _rel_move_to },
6570     { "reset-clip", _reset_clip },
6571     { "rgb", _rgb },
6572     { "rgba", _rgba },
6573     { "roll", _roll },
6574     { "rotate", _rotate },
6575     { "round", NULL },
6576     { "run", NULL },
6577     { "save", _save },
6578     { "scale", _scale },
6579     { "scaled-font", _scaled_font },
6580     { "select-font-face", _select_font_face },
6581     { "set", _set },
6582     { "set-antialias", _set_antialias },
6583     { "set-dash", _set_dash },
6584     { "set-device-offset", _set_device_offset },
6585     { "set-extend", _set_extend },
6586     { "set-fallback-resolution", _set_fallback_resolution },
6587     { "set-fill-rule", _set_fill_rule },
6588     { "set-filter", _set_filter },
6589     { "set-font-face", _set_font_face },
6590     { "set-font-options", _set_font_options },
6591     { "set-font-matrix", _set_font_matrix },
6592     { "set-font-size", _set_font_size },
6593     { "set-line-cap", _set_line_cap },
6594     { "set-line-join", _set_line_join },
6595     { "set-line-width", _set_line_width },
6596     { "set-matrix", _set_matrix },
6597     { "set-miter-limit", _set_miter_limit },
6598     { "set-mime-data", _set_mime_data },
6599     { "set-operator", _set_operator },
6600     { "set-scaled-font", _set_scaled_font },
6601     { "set-source", _set_source },
6602     { "set-source-image", _set_source_image },
6603     { "set-source-rgb", _set_source_rgb },
6604     { "set-source-rgba", _set_source_rgba },
6605     { "set-tolerance", _set_tolerance },
6606     { "show-glyphs", _show_glyphs },
6607     { "show-text", _show_text },
6608     { "show-text-glyphs", _show_text_glyphs },
6609     { "show-page", _show_page },
6610     { "similar", _similar },
6611     { "similar-image", _similar_image },
6612     { "sin", NULL },
6613     { "sqrt", NULL },
6614     { "sub", _sub },
6615     { "subsurface", _subsurface },
6616     { "surface", _surface },
6617     { "string", NULL },
6618     { "stroke", _stroke },
6619     { "stroke-extents", NULL },
6620     { "stroke-preserve", _stroke_preserve },
6621     { "stroke+", _stroke_preserve },
6622     { "text-path", _text_path },
6623     { "transform", _transform },
6624     { "transform-distance", NULL },
6625     { "transform-point", NULL },
6626     { "translate", _translate },
6627     { "true", _true },
6628     { "type", NULL },
6629     { "undef", _undef },
6630     { "unmap-image", _unmap_image },
6631     { "unset", _unset },
6632     { "user-to-device", NULL },
6633     { "user-to-device-distance", NULL },
6634     { "where", NULL },
6635     { "write-to-png", _write_to_png },
6636     { "write-to-script", _write_to_script },
6637     { "xor", _xor },
6638
6639     { "=", _debug_print },
6640
6641     { NULL, NULL },
6642 };
6643
6644 const csi_operator_def_t *
6645 _csi_operators (void)
6646 {
6647     return _defs;
6648 }
6649
6650 static const csi_integer_constant_def_t
6651 _integer_constants[] = {
6652     { "CLEAR",          CAIRO_OPERATOR_CLEAR },
6653     { "SOURCE",         CAIRO_OPERATOR_SOURCE },
6654     { "OVER",           CAIRO_OPERATOR_OVER },
6655     { "IN",             CAIRO_OPERATOR_IN },
6656     { "OUT",            CAIRO_OPERATOR_OUT },
6657     { "ATOP",           CAIRO_OPERATOR_ATOP },
6658     { "DEST",           CAIRO_OPERATOR_DEST },
6659     { "DEST_OVER",      CAIRO_OPERATOR_DEST_OVER },
6660     { "DEST_IN",        CAIRO_OPERATOR_DEST_IN },
6661     { "DEST_OUT",       CAIRO_OPERATOR_DEST_OUT },
6662     { "DEST_ATOP",      CAIRO_OPERATOR_DEST_ATOP },
6663     { "XOR",            CAIRO_OPERATOR_XOR },
6664     { "ADD",            CAIRO_OPERATOR_ADD },
6665     { "SATURATE",       CAIRO_OPERATOR_SATURATE },
6666     { "MULTIPLY",       CAIRO_OPERATOR_MULTIPLY },
6667     { "SCREEN",         CAIRO_OPERATOR_SCREEN },
6668     { "OVERLAY",        CAIRO_OPERATOR_OVERLAY },
6669     { "DARKEN",         CAIRO_OPERATOR_DARKEN },
6670     { "LIGHTEN",        CAIRO_OPERATOR_LIGHTEN },
6671     { "DODGE",          CAIRO_OPERATOR_COLOR_DODGE },
6672     { "BURN",           CAIRO_OPERATOR_COLOR_BURN },
6673     { "HARD_LIGHT",     CAIRO_OPERATOR_HARD_LIGHT },
6674     { "SOFT_LIGHT",     CAIRO_OPERATOR_SOFT_LIGHT },
6675     { "DIFFERENCE",     CAIRO_OPERATOR_DIFFERENCE },
6676     { "EXCLUSION",      CAIRO_OPERATOR_EXCLUSION },
6677     { "HSL_HUE",        CAIRO_OPERATOR_HSL_HUE },
6678     { "HSL_SATURATION", CAIRO_OPERATOR_HSL_SATURATION },
6679     { "HSL_COLOR",      CAIRO_OPERATOR_HSL_COLOR },
6680     { "HSL_LUMINOSITY", CAIRO_OPERATOR_HSL_LUMINOSITY },
6681
6682     { "WINDING",        CAIRO_FILL_RULE_WINDING },
6683     { "EVEN_ODD",       CAIRO_FILL_RULE_EVEN_ODD },
6684
6685     { "ANTIALIAS_DEFAULT",      CAIRO_ANTIALIAS_DEFAULT },
6686     { "ANTIALIAS_NONE",         CAIRO_ANTIALIAS_NONE },
6687     { "ANTIALIAS_GRAY",         CAIRO_ANTIALIAS_GRAY },
6688     { "ANTIALIAS_SUBPIXEL",     CAIRO_ANTIALIAS_SUBPIXEL },
6689     { "ANTIALIAS_FAST",         CAIRO_ANTIALIAS_FAST },
6690     { "ANTIALIAS_GOOD",         CAIRO_ANTIALIAS_GOOD },
6691     { "ANTIALIAS_BEST",         CAIRO_ANTIALIAS_BEST },
6692
6693     { "LINE_CAP_BUTT",          CAIRO_LINE_CAP_BUTT },
6694     { "LINE_CAP_ROUND",         CAIRO_LINE_CAP_ROUND },
6695     { "LINE_CAP_SQUARE",        CAIRO_LINE_CAP_SQUARE },
6696
6697     { "LINE_JOIN_MITER",        CAIRO_LINE_JOIN_MITER },
6698     { "LINE_JOIN_ROUND",        CAIRO_LINE_JOIN_ROUND },
6699     { "LINE_JOIN_BEVEL",        CAIRO_LINE_JOIN_BEVEL },
6700
6701     { "EXTEND_NONE",            CAIRO_EXTEND_NONE },
6702     { "EXTEND_REPEAT",          CAIRO_EXTEND_REPEAT },
6703     { "EXTEND_REFLECT",         CAIRO_EXTEND_REFLECT },
6704     { "EXTEND_PAD",             CAIRO_EXTEND_PAD },
6705
6706     { "FILTER_FAST",            CAIRO_FILTER_FAST },
6707     { "FILTER_GOOD",            CAIRO_FILTER_GOOD },
6708     { "FILTER_BEST",            CAIRO_FILTER_BEST },
6709     { "FILTER_BILINEAR",        CAIRO_FILTER_BILINEAR },
6710     { "FILTER_NEAREST",         CAIRO_FILTER_NEAREST },
6711     { "FILTER_GAUSSIAN",        CAIRO_FILTER_GAUSSIAN },
6712
6713     { "SLANT_NORMAL",           CAIRO_FONT_SLANT_NORMAL },
6714     { "SLANT_ITALIC",           CAIRO_FONT_SLANT_ITALIC },
6715     { "SLANT_OBLIQUE",          CAIRO_FONT_SLANT_OBLIQUE },
6716
6717     { "WEIGHT_NORMAL",          CAIRO_FONT_WEIGHT_NORMAL },
6718     { "WEIGHT_BOLD",            CAIRO_FONT_WEIGHT_BOLD },
6719
6720     { "SUBPIXEL_ORDER_DEFAULT", CAIRO_SUBPIXEL_ORDER_DEFAULT },
6721     { "SUBPIXEL_ORDER_RGB",     CAIRO_SUBPIXEL_ORDER_RGB },
6722     { "SUBPIXEL_ORDER_BGR",     CAIRO_SUBPIXEL_ORDER_BGR },
6723     { "SUBPIXEL_ORDER_VRGB",    CAIRO_SUBPIXEL_ORDER_VRGB },
6724     { "SUBPIXEL_ORDER_VBGR",    CAIRO_SUBPIXEL_ORDER_VBGR },
6725
6726     { "HINT_STYLE_DEFAULT",     CAIRO_HINT_STYLE_DEFAULT },
6727     { "HINT_STYLE_NONE",        CAIRO_HINT_STYLE_NONE },
6728     { "HINT_STYLE_SLIGHT",      CAIRO_HINT_STYLE_SLIGHT },
6729     { "HINT_STYLE_MEDIUM",      CAIRO_HINT_STYLE_MEDIUM },
6730     { "HINT_STYLE_FULL",        CAIRO_HINT_STYLE_FULL },
6731
6732     { "HINT_METRICS_DEFAULT",   CAIRO_HINT_METRICS_DEFAULT },
6733     { "HINT_METRICS_OFF",       CAIRO_HINT_METRICS_OFF },
6734     { "HINT_METRICS_ON",        CAIRO_HINT_METRICS_ON },
6735
6736     { "FORWARD",                0 },
6737     { "BACKWARD",               1 },
6738
6739     { "COLOR",                  CAIRO_CONTENT_COLOR },
6740     { "ALPHA",                  CAIRO_CONTENT_ALPHA },
6741     { "COLOR_ALPHA",            CAIRO_CONTENT_COLOR_ALPHA },
6742
6743     { "A1",                     CAIRO_FORMAT_A1 },
6744     { "A8",                     CAIRO_FORMAT_A8 },
6745     { "RGB16_565",              CAIRO_FORMAT_RGB16_565 },
6746     { "RGB24",                  CAIRO_FORMAT_RGB24 },
6747     { "ARGB32",                 CAIRO_FORMAT_ARGB32 },
6748     { "INVALID",                CAIRO_FORMAT_INVALID },
6749
6750     { NULL, 0 }
6751 };
6752
6753
6754 const csi_integer_constant_def_t *
6755 _csi_integer_constants (void)
6756 {
6757     return _integer_constants;
6758 }
6759
6760 static const csi_real_constant_def_t
6761 _real_constants[] = {
6762     { "math.pi",                M_PI },
6763     { "math.2pi",               2 * M_PI },
6764     { "math.sqrt2",             M_SQRT2 },
6765     { "math.ln2",               M_LN2 },
6766
6767     { NULL, 0 }
6768 };
6769
6770 const csi_real_constant_def_t *
6771 _csi_real_constants (void)
6772 {
6773     return _real_constants;
6774 }