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