Tizen 2.0 Release
[framework/graphics/cairo.git] / util / cairo-script / cairo-script-operators.c
1 /*
2  * Copyright © 2008 Chris Wilson <chris@chris-wilson.co.uk>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it either under the terms of the GNU Lesser General Public
6  * License version 2.1 as published by the Free Software Foundation
7  * (the "LGPL") or, at your option, under the terms of the Mozilla
8  * Public License Version 1.1 (the "MPL"). If you do not alter this
9  * notice, a recipient may use your version of this file under either
10  * the MPL or the LGPL.
11  *
12  * You should have received a copy of the LGPL along with this library
13  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
15  * You should have received a copy of the MPL along with this library
16  * in the file COPYING-MPL-1.1
17  *
18  * The contents of this file are subject to the Mozilla Public License
19  * Version 1.1 (the "License"); you may not use this file except in
20  * compliance with the License. You may obtain a copy of the License at
21  * http://www.mozilla.org/MPL/
22  *
23  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
24  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
25  * the specific language governing rights and limitations.
26  *
27  * The Original Code is the cairo graphics library.
28  *
29  * The Initial Developer of the Original Code is Chris Wilson.
30  *
31  * Contributor(s):
32  *      Chris Wilson <chris@chris-wilson.co.uk>
33  */
34
35 /* TODO real path type */
36
37 #include "cairo-script-private.h"
38
39 #if CAIRO_HAS_SCRIPT_SURFACE
40 #include "cairo-script.h"
41 #endif
42
43 #include <stdio.h> /* snprintf */
44 #include <stdlib.h> /* mkstemp */
45 #include <string.h>
46
47 #ifdef _MSC_VER
48 #define _USE_MATH_DEFINES /* for M_LN2, M_PI and M_SQRT2 on win32 */
49 #define snprintf _snprintf
50 #endif
51
52 #include <math.h>
53 #include <limits.h> /* INT_MAX */
54 #include <assert.h>
55 #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 retry:
1926     resolved = pattern;
1927     if (cairo_version () < CAIRO_VERSION_ENCODE (1, 9, 0)) {
1928         /* prior to 1.9, you needed to pass a resolved pattern */
1929         resolved = FcFontMatch (NULL, pattern, NULL);
1930         if (_csi_unlikely (resolved == NULL)) {
1931             FcPatternDestroy (pattern);
1932             return _csi_error (CSI_STATUS_NO_MEMORY);
1933         }
1934     }
1935
1936     font_face = cairo_ft_font_face_create_for_pattern (resolved);
1937     if (resolved != pattern)
1938         FcPatternDestroy (resolved);
1939
1940     if (cairo_font_face_status (font_face)) {
1941         char *filename = NULL;
1942
1943         /* Try a manual fallback process by eliminating specific requests */
1944
1945         if (FcPatternGetString (pattern,
1946                                 FC_FILE, 0,
1947                                 (FcChar8 **) &filename) == FcResultMatch) {
1948             FcPatternDel (pattern, FC_FILE);
1949             goto retry;
1950         }
1951     }
1952
1953     FcPatternDestroy (pattern);
1954
1955     data = _csi_slab_alloc (ctx, sizeof (*data));
1956     ctx->_faces = _csi_list_prepend (ctx->_faces, &data->blob.list);
1957     data->ctx = cairo_script_interpreter_reference (ctx);
1958     data->blob.hash = tmpl.hash;
1959     data->blob.len = tmpl.len;
1960     data->bytes = NULL;
1961     data->face = NULL;
1962 #ifdef HAVE_MMAP
1963     vec.bytes = tmpl.bytes;
1964     vec.num_bytes = tmpl.len;
1965     data->blob.bytes = _mmap_bytes (&vec, 1);
1966     if (data->blob.bytes != MAP_FAILED) {
1967         data->source = NULL;
1968         if (--string->base.ref == 0)
1969             csi_string_free (ctx, string);
1970     } else {
1971         data->blob.bytes = tmpl.bytes;
1972         data->source = string;
1973     }
1974 #else
1975     data->blob.bytes = tmpl.bytes;
1976     data->source = string;
1977 #endif
1978
1979     status = cairo_font_face_set_user_data (font_face,
1980                                             &_csi_blob_key,
1981                                             data, _ft_done_face);
1982     if (_csi_unlikely (status)) {
1983         _ft_done_face (data);
1984         cairo_font_face_destroy (font_face);
1985         return status;
1986     }
1987
1988     data->font_face = font_face;
1989     *font_face_out = font_face;
1990     return CSI_STATUS_SUCCESS;
1991 #else
1992     if (--string->base.ref == 0)
1993         csi_string_free (ctx, string);
1994     return CSI_INT_STATUS_UNSUPPORTED;
1995 #endif
1996 }
1997
1998 static csi_status_t
1999 _ft_type42_create (csi_t *ctx,
2000                    csi_dictionary_t *font,
2001                    cairo_font_face_t **font_face_out)
2002 {
2003     csi_object_t key;
2004     csi_status_t status;
2005
2006     /* two basic sub-types, either an FcPattern or embedded font */
2007     status = csi_name_new_static (ctx, &key, "pattern");
2008     if (_csi_unlikely (status))
2009         return status;
2010
2011     if (csi_dictionary_has (font, key.datum.name)) {
2012         csi_object_t obj;
2013         int type;
2014
2015         status = csi_dictionary_get (ctx, font, key.datum.name, &obj);
2016         if (_csi_unlikely (status))
2017             return status;
2018
2019         type = csi_object_get_type (&obj);
2020         switch (type) {
2021         case CSI_OBJECT_TYPE_FILE:
2022             status = _csi_file_as_string (ctx, obj.datum.file, &obj);
2023             if (_csi_unlikely (status))
2024                 return status;
2025             break;
2026         case CSI_OBJECT_TYPE_STRING:
2027             obj.datum.object->ref++;
2028             break;
2029         default:
2030             return  _csi_error (CSI_STATUS_INVALID_SCRIPT);
2031         }
2032
2033         return _ft_create_for_pattern (ctx,
2034                                        obj.datum.string,
2035                                        font_face_out);
2036     }
2037
2038     status = csi_name_new_static (ctx, &key, "source");
2039     if (_csi_unlikely (status))
2040         return status;
2041
2042     if (csi_dictionary_has (font, key.datum.name)) {
2043         csi_object_t obj;
2044         long index, flags;
2045         int type;
2046
2047         index = 0;
2048         status = _csi_dictionary_get_integer (ctx, font, "index", TRUE, &index);
2049         if (_csi_unlikely (status))
2050             return status;
2051
2052         flags = 0;
2053         status = _csi_dictionary_get_integer (ctx, font, "flags", TRUE, &flags);
2054         if (_csi_unlikely (status))
2055             return status;
2056
2057         status = csi_name_new_static (ctx, &key, "source");
2058         if (_csi_unlikely (status))
2059             return status;
2060         status = csi_dictionary_get (ctx, font, key.datum.name, &obj);
2061         if (_csi_unlikely (status))
2062             return status;
2063         type = csi_object_get_type (&obj);
2064         switch (type) {
2065         case CSI_OBJECT_TYPE_FILE:
2066             status = _csi_file_as_string (ctx, obj.datum.file, &obj);
2067             if (_csi_unlikely (status))
2068                 return status;
2069             break;
2070         case CSI_OBJECT_TYPE_STRING:
2071             obj.datum.object->ref++;
2072             break;
2073         default:
2074             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
2075         }
2076
2077         return _ft_create_for_source (ctx, obj.datum.string,
2078                                       index, flags,
2079                                       font_face_out);
2080     }
2081
2082     return _csi_error (CSI_STATUS_INVALID_SCRIPT);
2083 }
2084 #else
2085 #define _ft_type42_create(ctx, font, face_out) CSI_INT_STATUS_UNSUPPORTED
2086 #endif
2087
2088 static char *
2089 _fc_strcpy (csi_t *ctx, const char *str)
2090 {
2091     char *ret;
2092     int len;
2093
2094     ret = strchr (str, ':');
2095     if (ret != NULL)
2096         len = ret - str;
2097     else
2098         len = strlen (str);
2099
2100     ret = _csi_alloc (ctx, len+1);
2101     if (_csi_unlikely (ret == NULL))
2102         return NULL;
2103
2104     memcpy (ret, str, len);
2105     ret[len] = '\0';
2106
2107     return ret;
2108 }
2109
2110 static cairo_font_face_t *
2111 _select_font (const char *name)
2112 {
2113     cairo_surface_t *surface;
2114     cairo_font_face_t *face;
2115     cairo_t *cr;
2116
2117     /* create a dummy context to choose a font */
2118     surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
2119     cr = cairo_create (surface);
2120     cairo_surface_destroy (surface);
2121
2122     cairo_select_font_face (cr, name,
2123                             CAIRO_FONT_SLANT_NORMAL,
2124                             CAIRO_FONT_WEIGHT_NORMAL);
2125     face = cairo_font_face_reference (cairo_get_font_face (cr));
2126     cairo_destroy (cr);
2127
2128     return face;
2129 }
2130
2131 static csi_status_t
2132 _ft_fallback_create_for_pattern (csi_t *ctx,
2133                                  csi_string_t *string,
2134                                  cairo_font_face_t **font_face_out)
2135 {
2136     char *str, *name;
2137
2138     str = string->string;
2139 #if 0
2140     name = strstr (str, "fullname=");
2141     if (name != NULL)
2142         str = name + 9;
2143 #endif
2144
2145     name = _fc_strcpy (ctx, str);
2146     if (_csi_unlikely (name == NULL))
2147         return _csi_error (CSI_STATUS_NO_MEMORY);
2148
2149     *font_face_out = _select_font (name);
2150     _csi_free (ctx, name);
2151
2152     return CSI_STATUS_SUCCESS;
2153 }
2154
2155 static csi_status_t
2156 _ft_type42_fallback_create (csi_t *ctx,
2157                             csi_dictionary_t *font,
2158                             cairo_font_face_t **font_face_out)
2159 {
2160     csi_object_t key;
2161     csi_status_t status;
2162
2163     /* attempt to select a similar font */
2164
2165     /* two basic sub-types, either an FcPattern or embedded font */
2166     status = csi_name_new_static (ctx, &key, "pattern");
2167     if (_csi_unlikely (status))
2168         return status;
2169
2170     if (csi_dictionary_has (font, key.datum.name)) {
2171         csi_object_t obj;
2172         int type;
2173
2174         status = csi_dictionary_get (ctx, font, key.datum.name, &obj);
2175         if (_csi_unlikely (status))
2176             return status;
2177
2178         type = csi_object_get_type (&obj);
2179         switch (type) {
2180         case CSI_OBJECT_TYPE_FILE:
2181             status = _csi_file_as_string (ctx, obj.datum.file, &obj);
2182             if (_csi_unlikely (status))
2183                 return status;
2184             break;
2185         case CSI_OBJECT_TYPE_STRING:
2186             obj.datum.object->ref++;
2187             break;
2188         default:
2189             return  _csi_error (CSI_STATUS_INVALID_SCRIPT);
2190         }
2191
2192         return _ft_fallback_create_for_pattern (ctx,
2193                                                 obj.datum.string,
2194                                                 font_face_out);
2195     }
2196
2197     /* XXX: enable the trace to run */
2198     *font_face_out = _select_font ("Sans");
2199     return CSI_STATUS_SUCCESS;
2200 }
2201
2202 static csi_status_t
2203 _font_type42 (csi_t *ctx, csi_dictionary_t *font, cairo_font_face_t **font_face)
2204 {
2205     csi_status_t status;
2206
2207     status = _ft_type42_create (ctx, font, font_face);
2208     if (_csi_likely (status != CSI_INT_STATUS_UNSUPPORTED))
2209         return status;
2210
2211     return _ft_type42_fallback_create (ctx, font, font_face);
2212 }
2213
2214 static csi_status_t
2215 _font (csi_t *ctx)
2216 {
2217     csi_dictionary_t *font;
2218     csi_status_t status;
2219     cairo_font_face_t *font_face = NULL; /* silence the compiler */
2220     csi_proxy_t *proxy;
2221     csi_object_t obj;
2222     long type;
2223
2224     check (1);
2225
2226     status = _csi_ostack_get_dictionary (ctx, 0, &font);
2227     if (_csi_unlikely (status))
2228         return status;
2229
2230     status = _csi_dictionary_get_integer (ctx, font, "type", FALSE, &type);
2231     if (_csi_unlikely (status))
2232         return status;
2233
2234     switch (type) {
2235     case 3:
2236         status = _font_type3 (ctx, font, &font_face);
2237         break;
2238     case 42:
2239         status = _font_type42 (ctx, font, &font_face);
2240         break;
2241     default:
2242         status = _csi_error (CSI_STATUS_INVALID_SCRIPT);
2243         break;
2244     }
2245
2246     if (_csi_unlikely (status))
2247         return status;
2248
2249     /* transfer ownership of dictionary to cairo_font_face_t */
2250     proxy = _csi_proxy_create (ctx, font_face, font, NULL, NULL);
2251     if (_csi_unlikely (proxy == NULL)) {
2252         cairo_font_face_destroy (font_face);
2253         return _csi_error (CSI_STATUS_NO_MEMORY);
2254     }
2255
2256     status = cairo_font_face_set_user_data (font_face,
2257                                             &_csi_proxy_key,
2258                                             proxy, _csi_proxy_destroy);
2259     if (_csi_unlikely (status)) {
2260         _csi_proxy_destroy (proxy);
2261         cairo_font_face_destroy (font_face);
2262         return status;
2263     }
2264
2265     obj.type = CSI_OBJECT_TYPE_FONT;
2266     obj.datum.font_face = font_face;
2267
2268     pop (1);
2269     status = push (&obj);
2270     if (_csi_unlikely (status)) {
2271         cairo_font_face_destroy (font_face);
2272         return status;
2273     }
2274
2275     return CSI_STATUS_SUCCESS;
2276 }
2277
2278 static csi_status_t
2279 _for (csi_t *ctx)
2280 {
2281     csi_array_t *proc;
2282     csi_status_t status;
2283     long i, inc, limit;
2284
2285     check (4);
2286
2287     status = _csi_ostack_get_procedure (ctx, 0, &proc);
2288     if (_csi_unlikely (status))
2289         return status;
2290     status = _csi_ostack_get_integer (ctx, 1, &limit);
2291     if (_csi_unlikely (status))
2292         return status;
2293     status = _csi_ostack_get_integer (ctx, 2, &inc);
2294     if (_csi_unlikely (status))
2295         return status;
2296     status = _csi_ostack_get_integer (ctx, 3, &i);
2297     if (_csi_unlikely (status))
2298         return status;
2299
2300     proc->base.ref++;
2301     pop (4);
2302
2303     for (; i <= limit; i += inc) {
2304         status = _csi_push_ostack_integer (ctx, i);
2305         if (_csi_unlikely (status))
2306             break;
2307
2308         status = _csi_array_execute (ctx, proc);
2309         if (_csi_unlikely (status))
2310             break;
2311     }
2312
2313     if (--proc->base.ref == 0)
2314         csi_array_free (ctx, proc);
2315     return status;
2316 }
2317
2318 static csi_status_t
2319 _ge (csi_t *ctx)
2320 {
2321     csi_status_t status;
2322     csi_object_t *a, *b;
2323     int cmp;
2324
2325     check (2);
2326
2327     b = _csi_peek_ostack (ctx, 0);
2328     a = _csi_peek_ostack (ctx, 1);
2329
2330     status = csi_object_compare (a, b, &cmp);
2331     if (_csi_unlikely (status))
2332         return status;
2333
2334     pop (2);
2335     return _csi_push_ostack_boolean (ctx, cmp >= 0);
2336 }
2337
2338 static csi_status_t
2339 _proxy_get (csi_proxy_t *proxy,
2340             csi_name_t key)
2341 {
2342     csi_object_t obj;
2343     csi_status_t status;
2344
2345     if (_csi_unlikely (proxy == NULL || proxy->dictionary == NULL))
2346         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
2347
2348     status = csi_dictionary_get (proxy->ctx, proxy->dictionary, key, &obj);
2349     if (_csi_unlikely (status))
2350         return status;
2351
2352     return _csi_push_ostack_copy (proxy->ctx, &obj);
2353 }
2354
2355 static csi_status_t
2356 _context_get (csi_t *ctx,
2357               cairo_t *cr,
2358               csi_name_t key)
2359 {
2360     csi_status_t status;
2361     csi_object_t obj;
2362
2363     if (strcmp ((char *) key, "current-point") == 0) {
2364         double x, y;
2365
2366         cairo_get_current_point (cr, &x, &y);
2367
2368         status = _csi_push_ostack_real (ctx, x);
2369         if (_csi_unlikely (status))
2370             return status;
2371         status = _csi_push_ostack_real (ctx, y);
2372         if (_csi_unlikely (status))
2373             return status;
2374
2375         return CSI_STATUS_SUCCESS;
2376     } else if (strcmp ((char *) key, "source") == 0) {
2377         obj.type = CSI_OBJECT_TYPE_PATTERN;
2378         obj.datum.pattern = cairo_pattern_reference (cairo_get_source (cr));
2379     } else if (strcmp ((char *) key, "target") == 0) {
2380         obj.type = CSI_OBJECT_TYPE_SURFACE;
2381         obj.datum.surface = cairo_surface_reference (cairo_get_target (cr));
2382     } else if (strcmp ((char *) key, "group-target") == 0) {
2383         obj.type = CSI_OBJECT_TYPE_SURFACE;
2384         obj.datum.surface = cairo_surface_reference (cairo_get_group_target (cr));
2385     } else if (strcmp ((char *) key, "scaled-font") == 0) {
2386         obj.type = CSI_OBJECT_TYPE_SCALED_FONT;
2387         obj.datum.scaled_font = cairo_scaled_font_reference (cairo_get_scaled_font (cr));
2388     } else if (strcmp ((char *) key, "font-face") == 0) {
2389         obj.type = CSI_OBJECT_TYPE_FONT;
2390         obj.datum.font_face = cairo_font_face_reference (cairo_get_font_face (cr));
2391     } else
2392         return _proxy_get (cairo_get_user_data (cr, &_csi_proxy_key), key);
2393
2394     return push (&obj);
2395 }
2396
2397 static csi_status_t
2398 _font_get (csi_t *ctx,
2399            cairo_font_face_t *font_face,
2400            csi_name_t key)
2401 {
2402     return _proxy_get (cairo_font_face_get_user_data (font_face,
2403                                                       &_csi_proxy_key),
2404                        key);
2405 }
2406
2407 static csi_status_t
2408 _pattern_get (csi_t *ctx,
2409               cairo_pattern_t *pattern,
2410               csi_name_t key)
2411 {
2412     csi_status_t status;
2413
2414     if (strcmp ((char *) key, "type") == 0)
2415         return _csi_push_ostack_integer (ctx, cairo_pattern_get_type (pattern));
2416
2417     if (strcmp ((char *) key, "filter") == 0)
2418         return _csi_push_ostack_integer (ctx, cairo_pattern_get_filter (pattern));
2419
2420     if (strcmp ((char *) key, "extend") == 0)
2421         return _csi_push_ostack_integer (ctx, cairo_pattern_get_extend (pattern));
2422
2423     if (strcmp ((char *) key, "matrix") == 0) {
2424         csi_object_t obj;
2425         cairo_matrix_t m;
2426
2427         cairo_pattern_get_matrix (pattern, &m);
2428         status = csi_matrix_new_from_matrix (ctx, &obj, &m);
2429         if (_csi_unlikely (status))
2430             return status;
2431
2432         return push (&obj);
2433     }
2434
2435     return _proxy_get (cairo_pattern_get_user_data (pattern, &_csi_proxy_key),
2436                        key);
2437 }
2438
2439 static csi_status_t
2440 _scaled_font_get (csi_t *ctx,
2441                   cairo_scaled_font_t *font,
2442                   csi_name_t key)
2443 {
2444     return _proxy_get (cairo_scaled_font_get_user_data (font, &_csi_proxy_key),
2445                        key);
2446 }
2447
2448 static csi_status_t
2449 _surface_get (csi_t *ctx,
2450               cairo_surface_t *surface,
2451               csi_name_t key)
2452 {
2453     if (strcmp ((char *) key, "type") == 0) {
2454         return _csi_push_ostack_integer (ctx, cairo_surface_get_type (surface));
2455     }
2456
2457     if (strcmp ((char *) key, "content") == 0) {
2458         return _csi_push_ostack_integer (ctx,
2459                                          cairo_surface_get_content (surface));
2460     }
2461
2462     return _proxy_get (cairo_surface_get_user_data (surface, &_csi_proxy_key),
2463                        key);
2464 }
2465
2466 static csi_status_t
2467 _get (csi_t *ctx)
2468 {
2469     csi_object_t *key, *src, obj;
2470     csi_status_t status;
2471     int type;
2472
2473     check (2);
2474
2475     key = _csi_peek_ostack (ctx, 0);
2476     src = _csi_peek_ostack (ctx, 1);
2477     pop (1);
2478     type = csi_object_get_type (src);
2479     switch (type) {
2480     case CSI_OBJECT_TYPE_DICTIONARY:
2481         if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_NAME))
2482             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
2483
2484         status = csi_dictionary_get (ctx,
2485                                      src->datum.dictionary,
2486                                      key->datum.name,
2487                                      &obj);
2488         break;
2489     case CSI_OBJECT_TYPE_ARRAY:
2490         if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_INTEGER))
2491             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
2492
2493         status = csi_array_get (ctx,
2494                                 src->datum.array,
2495                                 key->datum.integer,
2496                                 &obj);
2497         break;
2498 #if 0
2499     case CSI_OBJECT_TYPE_STRING:
2500         status = csi_string_get (src, key, &obj);
2501         break;
2502 #endif
2503
2504     case CSI_OBJECT_TYPE_CONTEXT:
2505         if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_NAME))
2506             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
2507         return _context_get (ctx, src->datum.cr, key->datum.name);
2508
2509     case CSI_OBJECT_TYPE_FONT:
2510         if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_NAME))
2511             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
2512         return _font_get (ctx, src->datum.font_face, key->datum.name);
2513
2514     case CSI_OBJECT_TYPE_PATTERN:
2515         if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_NAME))
2516             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
2517         return _pattern_get (ctx, src->datum.pattern, key->datum.name);
2518
2519     case CSI_OBJECT_TYPE_SCALED_FONT:
2520         if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_NAME))
2521             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
2522         return _scaled_font_get (ctx, src->datum.scaled_font, key->datum.name);
2523
2524     case CSI_OBJECT_TYPE_SURFACE:
2525         if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_NAME))
2526             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
2527         return _surface_get (ctx, src->datum.surface, key->datum.name);
2528
2529     default:
2530         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
2531     }
2532
2533     if (_csi_unlikely (status))
2534         return status;
2535
2536     return _csi_push_ostack_copy (ctx, &obj);
2537 }
2538
2539 struct glyph_advance_cache {
2540     csi_t *ctx;
2541     double glyph_advance[256][2];
2542     unsigned long have_glyph_advance[256];
2543 };
2544
2545 static void
2546 glyph_advance_cache_destroy (void *closure)
2547 {
2548     struct glyph_advance_cache *cache = closure;
2549     _csi_free (cache->ctx, cache);
2550 }
2551
2552 static int
2553 _glyph_string (csi_t *ctx,
2554                csi_array_t *array,
2555                cairo_scaled_font_t *scaled_font,
2556                cairo_glyph_t *glyphs)
2557 {
2558     struct glyph_advance_cache uncached;
2559     struct glyph_advance_cache *cache;
2560     csi_integer_t nglyphs, i, j;
2561     double x, y, dx;
2562     cairo_status_t status;
2563
2564     if (cairo_scaled_font_status (scaled_font))
2565         return 0;
2566
2567     cache = cairo_scaled_font_get_user_data (scaled_font,
2568                                              (cairo_user_data_key_t *) ctx);
2569     if (cache == NULL) {
2570         cache = _csi_alloc (ctx, sizeof (*cache));
2571         if (_csi_likely (cache != NULL)) {
2572             cache->ctx = ctx;
2573             memset (cache->have_glyph_advance, 0xff,
2574                     sizeof (cache->have_glyph_advance));
2575
2576             status = cairo_scaled_font_set_user_data (scaled_font,
2577                                                       (cairo_user_data_key_t *) ctx,
2578                                                       cache,
2579                                                       glyph_advance_cache_destroy);
2580             if (_csi_unlikely (status)) {
2581                 _csi_free (ctx, cache);
2582                 cache = NULL;
2583             }
2584         }
2585     }
2586
2587     if (_csi_unlikely (cache == NULL)) {
2588         cache = &uncached;
2589
2590         cache->ctx = ctx;
2591         memset (cache->have_glyph_advance, 0xff,
2592                 sizeof (cache->have_glyph_advance));
2593     }
2594
2595     nglyphs = 0;
2596     x = y = 0;
2597     for (i = 0; i < array->stack.len; i++) {
2598         const csi_object_t *obj = &array->stack.objects[i];
2599         int type = csi_object_get_type (obj);
2600
2601         switch (type) {
2602         case CSI_OBJECT_TYPE_ARRAY: {
2603             const csi_array_t *glyph_array = obj->datum.array;
2604             for (j = 0; j < glyph_array->stack.len; j++) {
2605                 unsigned long g;
2606                 int gi;
2607
2608                 obj = &glyph_array->stack.objects[j];
2609                 if (csi_object_get_type (obj) != CSI_OBJECT_TYPE_INTEGER)
2610                     break;
2611                 g = obj->datum.integer;
2612
2613                 glyphs[nglyphs].index = g;
2614                 glyphs[nglyphs].x = x;
2615                 glyphs[nglyphs].y = y;
2616
2617                 gi = g % ARRAY_LENGTH (cache->have_glyph_advance);
2618                 if (cache->have_glyph_advance[gi] != g) {
2619                     cairo_text_extents_t extents;
2620
2621                     cairo_scaled_font_glyph_extents (scaled_font,
2622                                                      &glyphs[nglyphs], 1,
2623                                                      &extents);
2624
2625                     cache->glyph_advance[gi][0] = extents.x_advance;
2626                     cache->glyph_advance[gi][1] = extents.y_advance;
2627                     cache->have_glyph_advance[gi] = g;
2628                 }
2629
2630                 x += cache->glyph_advance[gi][0];
2631                 y += cache->glyph_advance[gi][1];
2632                 nglyphs++;
2633             }
2634             break;
2635         }
2636
2637         case CSI_OBJECT_TYPE_STRING: {
2638             const csi_string_t *glyph_string = obj->datum.string;
2639             for (j = 0; j < glyph_string->len; j++) {
2640                 uint8_t g;
2641
2642                 g = glyph_string->string[j];
2643                 glyphs[nglyphs].index = g;
2644                 glyphs[nglyphs].x = x;
2645                 glyphs[nglyphs].y = y;
2646
2647                 if (cache->have_glyph_advance[g] != g) {
2648                     cairo_text_extents_t extents;
2649
2650                     cairo_scaled_font_glyph_extents (scaled_font,
2651                                                      &glyphs[nglyphs], 1,
2652                                                      &extents);
2653
2654                     cache->glyph_advance[g][0] = extents.x_advance;
2655                     cache->glyph_advance[g][1] = extents.y_advance;
2656                     cache->have_glyph_advance[g] = g;
2657                 }
2658
2659                 x += cache->glyph_advance[g][0];
2660                 y += cache->glyph_advance[g][1];
2661                 nglyphs++;
2662             }
2663             break;
2664         }
2665
2666         case CSI_OBJECT_TYPE_INTEGER:
2667         case CSI_OBJECT_TYPE_REAL: /* dx or x*/
2668             dx = csi_number_get_value (obj);
2669             if (i+1 == array->stack.len)
2670                 break;
2671
2672             type = csi_object_get_type (&array->stack.objects[i+1]);
2673             switch (type) {
2674             case CSI_OBJECT_TYPE_INTEGER:
2675             case CSI_OBJECT_TYPE_REAL: /* y */
2676                 y = csi_number_get_value (&array->stack.objects[i+1]);
2677                 x = dx;
2678                 i++;
2679                 break;
2680
2681             default:
2682                 x += dx;
2683             }
2684         }
2685     }
2686
2687     return nglyphs;
2688 }
2689
2690 static csi_status_t
2691 _glyph_path (csi_t *ctx)
2692 {
2693     csi_array_t *array;
2694     csi_status_t status;
2695     cairo_t *cr;
2696     cairo_glyph_t stack_glyphs[256], *glyphs;
2697     csi_integer_t nglyphs, i;
2698
2699     check (2);
2700
2701     status = _csi_ostack_get_array (ctx, 0, &array);
2702     if (_csi_unlikely (status))
2703         return status;
2704     status = _csi_ostack_get_context (ctx, 1, &cr);
2705     if (_csi_unlikely (status))
2706         return status;
2707
2708     /* count glyphs */
2709     nglyphs = 0;
2710     for (i = 0; i < array->stack.len; i++) {
2711         csi_object_t *obj = &array->stack.objects[i];
2712         int type = csi_object_get_type (obj);
2713         switch (type) {
2714         case CSI_OBJECT_TYPE_ARRAY:
2715             nglyphs += obj->datum.array->stack.len;
2716             break;
2717         case CSI_OBJECT_TYPE_STRING:
2718             nglyphs += obj->datum.string->len;
2719             break;
2720         }
2721     }
2722     if (nglyphs == 0) {
2723         pop (1);
2724         return CSI_STATUS_SUCCESS;
2725     }
2726
2727     if (nglyphs > ARRAY_LENGTH (stack_glyphs)) {
2728         if (_csi_unlikely ((unsigned) nglyphs >= INT_MAX / sizeof (cairo_glyph_t)))
2729             return _csi_error (CSI_STATUS_NO_MEMORY);
2730
2731         glyphs = _csi_alloc (ctx, sizeof (cairo_glyph_t) * nglyphs);
2732         if (_csi_unlikely (glyphs == NULL))
2733             return _csi_error (CSI_STATUS_NO_MEMORY);
2734     } else
2735         glyphs = stack_glyphs;
2736
2737     nglyphs = _glyph_string (ctx, array, cairo_get_scaled_font (cr), glyphs);
2738     cairo_glyph_path (cr, glyphs, nglyphs);
2739
2740     if (glyphs != stack_glyphs)
2741         _csi_free (ctx, glyphs);
2742
2743     pop (1);
2744     return CSI_STATUS_SUCCESS;
2745 }
2746
2747 static csi_status_t
2748 _gray (csi_t *ctx)
2749 {
2750     csi_object_t obj;
2751     csi_status_t status;
2752     double g;
2753
2754     check (1);
2755
2756     status = _csi_ostack_get_number (ctx, 0, &g);
2757     if (_csi_unlikely (status))
2758         return status;
2759
2760     pop (1);
2761
2762     obj.type = CSI_OBJECT_TYPE_PATTERN;
2763     obj.datum.pattern = cairo_pattern_create_rgba (g, g, g, 1);
2764     return push (&obj);
2765 }
2766
2767 static csi_status_t
2768 _gt (csi_t *ctx)
2769 {
2770     csi_status_t status;
2771     csi_object_t *a, *b;
2772     int cmp;
2773
2774     check (2);
2775
2776     b = _csi_peek_ostack (ctx, 0);
2777     a = _csi_peek_ostack (ctx, 1);
2778
2779     status = csi_object_compare (a, b, &cmp);
2780     if (_csi_unlikely (status))
2781         return status;
2782
2783     pop (2);
2784     return _csi_push_ostack_boolean (ctx, cmp > 0);
2785 }
2786
2787 static csi_status_t
2788 _identity (csi_t *ctx)
2789 {
2790     csi_object_t obj;
2791     csi_status_t status;
2792
2793     status = csi_matrix_new (ctx, &obj);
2794     if (_csi_unlikely (status))
2795         return status;
2796
2797     return push (&obj);
2798 }
2799
2800 static csi_status_t
2801 _if (csi_t *ctx)
2802 {
2803     csi_array_t *proc;
2804     csi_boolean_t predicate = FALSE; /* silence the compiler */
2805     csi_status_t status;
2806
2807     check (2);
2808
2809     status = _csi_ostack_get_procedure (ctx, 0, &proc);
2810     if (_csi_unlikely (status))
2811         return status;
2812
2813     status = _csi_ostack_get_boolean (ctx, 1, &predicate);
2814     if (_csi_unlikely (status))
2815         return status;
2816
2817     proc->base.ref++;
2818     pop (2);
2819
2820     if (predicate)
2821         status = _csi_array_execute (ctx, proc);
2822
2823     if (--proc->base.ref == 0)
2824         csi_array_free (ctx, proc);
2825
2826     return status;
2827 }
2828
2829 static csi_status_t
2830 _ifelse (csi_t *ctx)
2831 {
2832     csi_array_t *true_proc, *false_proc;
2833     csi_boolean_t predicate = FALSE; /* silence the compiler */
2834     csi_status_t status;
2835
2836     check (3);
2837
2838     status = _csi_ostack_get_procedure (ctx, 0, &false_proc);
2839     if (_csi_unlikely (status))
2840         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
2841
2842     status = _csi_ostack_get_procedure (ctx, 1, &true_proc);
2843     if (_csi_unlikely (status))
2844         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
2845
2846     status = _csi_ostack_get_boolean (ctx, 2, &predicate);
2847     if (_csi_unlikely (status))
2848         return status;
2849
2850     true_proc->base.ref++;
2851     false_proc->base.ref++;
2852     pop (3);
2853
2854     if (predicate)
2855         status = _csi_array_execute (ctx, true_proc);
2856     else
2857         status = _csi_array_execute (ctx, false_proc);
2858
2859     if (--true_proc->base.ref == 0)
2860         csi_array_free (ctx, true_proc);
2861     if (--false_proc->base.ref == 0)
2862         csi_array_free (ctx, false_proc);
2863
2864     return status;
2865 }
2866
2867 static csi_status_t
2868 _image_read_raw (csi_file_t *src,
2869                  cairo_format_t format,
2870                  int width, int height,
2871                  cairo_surface_t **image_out)
2872 {
2873     cairo_surface_t *image;
2874     uint8_t *bp, *data;
2875     int rem, len, ret, x, rowlen, instride, stride;
2876     cairo_status_t status;
2877
2878     stride = cairo_format_stride_for_width (format, width);
2879     data = malloc (stride * height);
2880     if (data == NULL)
2881         return CAIRO_STATUS_NO_MEMORY;
2882
2883     image = cairo_image_surface_create_for_data (data, format,
2884                                                  width, height, stride);
2885     status = cairo_surface_set_user_data (image,
2886                                           (const cairo_user_data_key_t *) image,
2887                                           data, free);
2888     if (status) {
2889         cairo_surface_destroy (image);
2890         free (image);
2891         return status;
2892     }
2893
2894     switch (format) {
2895     case CAIRO_FORMAT_A1:
2896         instride = rowlen = (width+7)/8;
2897         break;
2898     case CAIRO_FORMAT_A8:
2899         instride = rowlen = width;
2900         break;
2901     case CAIRO_FORMAT_RGB16_565:
2902         instride = rowlen = 2 * width;
2903         break;
2904     case CAIRO_FORMAT_RGB24:
2905         rowlen = 3 * width;
2906         instride = 4 *width;
2907         break;
2908     default:
2909     case CAIRO_FORMAT_RGB30:
2910     case CAIRO_FORMAT_INVALID:
2911     case CAIRO_FORMAT_ARGB32:
2912         instride = rowlen = 4 * width;
2913         break;
2914     }
2915     len = rowlen * height;
2916
2917     bp = data;
2918     rem = len;
2919     while (rem) {
2920         ret = csi_file_read (src, bp, rem);
2921         if (_csi_unlikely (ret == 0)) {
2922             cairo_surface_destroy (image);
2923             return _csi_error (CSI_STATUS_READ_ERROR);
2924         }
2925         rem -= ret;
2926         bp += ret;
2927     }
2928
2929     if (len != height * stride) {
2930         while (--height) {
2931             uint8_t *row = data + height * stride;
2932
2933             /* XXX pixel conversion */
2934             switch (format) {
2935             case CAIRO_FORMAT_A1:
2936                 for (x = rowlen; x--; ) {
2937                     uint8_t byte = *--bp;
2938                     row[x] = CSI_BITSWAP8_IF_LITTLE_ENDIAN (byte);
2939                 }
2940                 break;
2941             case CAIRO_FORMAT_A8:
2942                 for (x = width; x--; )
2943                     row[x] = *--bp;
2944                 break;
2945             case CAIRO_FORMAT_RGB16_565:
2946                 for (x = width; x--; ) {
2947 #ifdef WORDS_BIGENDIAN
2948                     row[2*x + 1] = *--bp;
2949                     row[2*x + 0] = *--bp;
2950 #else
2951                     row[2*x + 0] = *--bp;
2952                     row[2*x + 1] = *--bp;
2953 #endif
2954                 }
2955                 break;
2956             case CAIRO_FORMAT_RGB24:
2957                 for (x = width; x--; ) {
2958 #ifdef WORDS_BIGENDIAN
2959                     row[4*x + 3] = *--bp;
2960                     row[4*x + 2] = *--bp;
2961                     row[4*x + 1] = *--bp;
2962                     row[4*x + 0] = 0xff;
2963 #else
2964                     row[4*x + 0] = *--bp;
2965                     row[4*x + 1] = *--bp;
2966                     row[4*x + 2] = *--bp;
2967                     row[4*x + 3] = 0xff;
2968 #endif
2969                 }
2970                 break;
2971             case CAIRO_FORMAT_RGB30:
2972             case CAIRO_FORMAT_INVALID:
2973             case CAIRO_FORMAT_ARGB32:
2974                 /* stride == width */
2975                 break;
2976             }
2977
2978             memset (row + instride, 0, stride - instride);
2979         }
2980
2981         /* need to treat last row carefully */
2982         switch (format) {
2983         case CAIRO_FORMAT_A1:
2984             for (x = rowlen; x--; ) {
2985                 uint8_t byte = *--bp;
2986                 data[x] = CSI_BITSWAP8_IF_LITTLE_ENDIAN (byte);
2987             }
2988             break;
2989         case CAIRO_FORMAT_A8:
2990             for (x = width; x--; )
2991                 data[x] = *--bp;
2992             break;
2993         case CAIRO_FORMAT_RGB16_565:
2994             for (x = width; x--; ) {
2995 #ifdef WORDS_BIGENDIAN
2996                 data[2*x + 1] = *--bp;
2997                 data[2*x + 0] = *--bp;
2998 #else
2999                 data[2*x + 0] = *--bp;
3000                 data[2*x + 1] = *--bp;
3001 #endif
3002             }
3003             break;
3004         case CAIRO_FORMAT_RGB24:
3005             for (x = width; --x>1; ) {
3006 #ifdef WORDS_BIGENDIAN
3007                 data[4*x + 3] = *--bp;
3008                 data[4*x + 2] = *--bp;
3009                 data[4*x + 1] = *--bp;
3010                 data[4*x + 0] = 0xff;
3011 #else
3012                 data[4*x + 0] = *--bp;
3013                 data[4*x + 1] = *--bp;
3014                 data[4*x + 2] = *--bp;
3015                 data[4*x + 3] = 0xff;
3016 #endif
3017             }
3018             if (width > 1) {
3019                 uint8_t rgb[2][3];
3020                 /* shuffle the last couple of overlapping pixels */
3021                 rgb[1][0] = data[5];
3022                 rgb[1][1] = data[4];
3023                 rgb[1][2] = data[3];
3024                 rgb[0][0] = data[2];
3025                 rgb[0][1] = data[1];
3026                 rgb[0][2] = data[0];
3027 #ifdef WORDS_BIGENDIAN
3028                 data[4] = 0xff;
3029                 data[5] = rgb[1][2];
3030                 data[6] = rgb[1][1];
3031                 data[7] = rgb[1][0];
3032                 data[0] = 0xff;
3033                 data[1] = rgb[0][2];
3034                 data[2] = rgb[0][1];
3035                 data[3] = rgb[0][0];
3036 #else
3037                 data[7] = 0xff;
3038                 data[6] = rgb[1][2];
3039                 data[5] = rgb[1][1];
3040                 data[4] = rgb[1][0];
3041                 data[3] = 0xff;
3042                 data[2] = rgb[0][2];
3043                 data[1] = rgb[0][1];
3044                 data[0] = rgb[0][0];
3045 #endif
3046             } else {
3047 #ifdef WORDS_BIGENDIAN
3048                 data[0] = 0xff;
3049                 data[1] = data[0];
3050                 data[2] = data[1];
3051                 data[3] = data[2];
3052 #else
3053                 data[3] = data[0];
3054                 data[0] = data[2];
3055                 data[2] = data[3];
3056                 data[3] = 0xff;
3057 #endif
3058             }
3059             break;
3060         case CAIRO_FORMAT_RGB30:
3061         case CAIRO_FORMAT_INVALID:
3062         case CAIRO_FORMAT_ARGB32:
3063             /* stride == width */
3064             break;
3065         }
3066         memset (data + instride, 0, stride - instride);
3067     } else {
3068 #ifndef WORDS_BIGENDIAN
3069         switch (format) {
3070         case CAIRO_FORMAT_A1:
3071             for (x = 0; x < len; x++) {
3072                 uint8_t byte = data[x];
3073                 data[x] = CSI_BITSWAP8_IF_LITTLE_ENDIAN (byte);
3074             }
3075             break;
3076         case CAIRO_FORMAT_RGB16_565:
3077             {
3078                 uint32_t *rgba = (uint32_t *) data;
3079                 for (x = len/2; x--; rgba++) {
3080                     *rgba = bswap_16 (*rgba);
3081                 }
3082             }
3083             break;
3084         case CAIRO_FORMAT_ARGB32:
3085             {
3086                 uint32_t *rgba = (uint32_t *) data;
3087                 for (x = len/4; x--; rgba++) {
3088                     *rgba = bswap_32 (*rgba);
3089                 }
3090             }
3091             break;
3092
3093         case CAIRO_FORMAT_A8:
3094             break;
3095
3096         case CAIRO_FORMAT_RGB30:
3097         case CAIRO_FORMAT_RGB24:
3098         case CAIRO_FORMAT_INVALID:
3099         default:
3100             break;
3101         }
3102 #endif
3103     }
3104
3105     cairo_surface_mark_dirty (image);
3106     *image_out = image;
3107     return CSI_STATUS_SUCCESS;
3108 }
3109
3110 static cairo_status_t
3111 png_read_func (void *closure, uint8_t *data, unsigned int len)
3112 {
3113     int ret;
3114
3115     ret = csi_file_read (closure, data, len);
3116     if ((unsigned int) ret != len)
3117         return CAIRO_STATUS_READ_ERROR;
3118
3119     return CAIRO_STATUS_SUCCESS;
3120 }
3121
3122 static csi_status_t
3123 _image_read_png (csi_file_t *src, cairo_surface_t **out)
3124 {
3125 #if CAIRO_HAS_PNG_FUNCTIONS
3126     *out = cairo_image_surface_create_from_png_stream (png_read_func, src);
3127     return cairo_surface_status (*out);
3128 #else
3129     return CAIRO_STATUS_READ_ERROR;
3130 #endif
3131 }
3132
3133 struct _image_tag {
3134     csi_t *ctx;
3135     csi_blob_t blob;
3136     cairo_surface_t *surface;
3137 };
3138
3139 static void
3140 _image_tag_done (void *closure)
3141 {
3142     struct _image_tag *tag = closure;
3143     csi_t *ctx = tag->ctx;
3144
3145     ctx->_images = _csi_list_unlink (ctx->_images, &tag->blob.list);
3146     _csi_slab_free (ctx, tag, sizeof (*tag));
3147     cairo_script_interpreter_destroy (ctx);
3148 }
3149
3150 static void
3151 _image_hash (csi_blob_t *blob,
3152              cairo_surface_t *surface)
3153 {
3154     uint32_t  value;
3155
3156     value = cairo_image_surface_get_width (surface);
3157     _csi_blob_hash (blob, &value, 1);
3158
3159     value = cairo_image_surface_get_height (surface);
3160     _csi_blob_hash (blob, &value, 1);
3161
3162     value = cairo_image_surface_get_format (surface);
3163     _csi_blob_hash (blob, &value, 1);
3164 }
3165
3166 static cairo_surface_t *
3167 _image_cached (csi_t *ctx, cairo_surface_t *surface)
3168 {
3169     csi_blob_t tmpl;
3170     csi_list_t *link;
3171     uint8_t *data;
3172     int stride, height;
3173     struct _image_tag *tag;
3174
3175     /* check for an existing image  */
3176
3177     data = cairo_image_surface_get_data (surface);
3178     stride = cairo_image_surface_get_stride (surface);
3179     height = cairo_image_surface_get_height (surface);
3180     _csi_blob_init (&tmpl, data, stride * height);
3181     _image_hash (&tmpl, surface);
3182     link = _csi_list_find (ctx->_images, _csi_blob_equal, &tmpl);
3183     if (link) {
3184         cairo_surface_destroy (surface);
3185         tag = csi_container_of (link, struct _image_tag, blob.list);
3186         return cairo_surface_reference (tag->surface);
3187     }
3188
3189     /* none found, insert a tag for this one */
3190
3191     tag = _csi_slab_alloc (ctx, sizeof (struct _image_tag));
3192     if (tag == NULL)
3193         return surface;
3194
3195     ctx->_images = _csi_list_prepend (ctx->_images, &tag->blob.list);
3196     tag->ctx = cairo_script_interpreter_reference (ctx);
3197     tag->blob.hash = tmpl.hash;
3198     tag->blob.bytes = tmpl.bytes;
3199     tag->blob.len = tmpl.len;
3200     tag->surface = surface;
3201
3202     if (cairo_surface_set_user_data (surface, &_csi_blob_key,
3203                                      tag, _image_tag_done))
3204     {
3205         _image_tag_done (tag);
3206     }
3207
3208     return surface;
3209 }
3210
3211 static csi_status_t
3212 _image_load_from_dictionary (csi_t *ctx,
3213                              csi_dictionary_t *dict,
3214                              cairo_surface_t **image_out)
3215 {
3216     csi_object_t obj, key;
3217     long width;
3218     long height;
3219     long format;
3220     cairo_surface_t *image = NULL; /* silence the compiler */
3221     csi_status_t status;
3222
3223     /* check for "status? */
3224
3225     status = _csi_dictionary_get_integer (ctx, dict, "width", FALSE, &width);
3226     if (_csi_unlikely (status))
3227         return status;
3228     status = _csi_dictionary_get_integer (ctx, dict, "height", FALSE, &height);
3229     if (_csi_unlikely (status))
3230         return status;
3231
3232     format = CAIRO_FORMAT_ARGB32;
3233     status = _csi_dictionary_get_integer (ctx, dict, "format", TRUE, &format);
3234     if (_csi_unlikely (status))
3235         return status;
3236
3237     status = csi_name_new_static (ctx, &key, "source");
3238     if (_csi_unlikely (status))
3239         return status;
3240
3241     if (csi_dictionary_has (dict, key.datum.name)) {
3242         enum mime_type mime_type;
3243         csi_object_t file;
3244
3245         status = csi_dictionary_get (ctx, dict, key.datum.name, &obj);
3246         if (_csi_unlikely (status))
3247             return status;
3248
3249         status = csi_name_new_static (ctx, &key, "mime-type");
3250         if (_csi_unlikely (status))
3251             return status;
3252
3253         mime_type = MIME_TYPE_NONE;
3254         if (csi_dictionary_has (dict, key.datum.name)) {
3255             csi_object_t type_obj;
3256             const char *type_str;
3257             int type;
3258
3259             status = csi_dictionary_get (ctx, dict, key.datum.name, &type_obj);
3260             if (_csi_unlikely (status))
3261                 return status;
3262
3263             type = csi_object_get_type (&type_obj);
3264             switch (type) {
3265             case CSI_OBJECT_TYPE_STRING:
3266                 type_str = type_obj.datum.string->string;
3267                 break;
3268             case CSI_OBJECT_TYPE_NAME:
3269                 type_str = (char *) type_obj.datum.name;
3270                 break;
3271             default:
3272                 return _csi_error (CSI_STATUS_INVALID_SCRIPT);
3273             }
3274
3275             if (strcmp (type_str, CAIRO_MIME_TYPE_PNG) == 0)
3276                 mime_type = MIME_TYPE_PNG;
3277         }
3278
3279         status = csi_object_as_file (ctx, &obj, &file);
3280         if (_csi_unlikely (status))
3281             return status;
3282
3283         /* XXX hook for general mime-type decoder */
3284
3285         switch (mime_type) {
3286         case MIME_TYPE_NONE:
3287             status = _image_read_raw (file.datum.file,
3288                                       format, width, height, &image);
3289             break;
3290         case MIME_TYPE_PNG:
3291             status = _image_read_png (file.datum.file, &image);
3292             break;
3293         }
3294         csi_object_free (ctx, &file);
3295         if (_csi_unlikely (status))
3296             return status;
3297
3298         image = _image_cached (ctx, image);
3299     } else
3300         image = cairo_image_surface_create (format, width, height);
3301
3302     *image_out = image;
3303     return CSI_STATUS_SUCCESS;
3304 }
3305
3306 static csi_status_t
3307 _image (csi_t *ctx)
3308 {
3309     csi_dictionary_t *dict;
3310     cairo_surface_t *image;
3311     csi_status_t status;
3312     csi_object_t obj;
3313
3314     check (1);
3315
3316     status = _csi_ostack_get_dictionary (ctx, 0, &dict);
3317     if (_csi_unlikely (status))
3318         return status;
3319
3320     status = _image_load_from_dictionary (ctx, dict, &image);
3321     if (_csi_unlikely (status))
3322         return status;
3323
3324     pop (1);
3325     obj.type = CSI_OBJECT_TYPE_SURFACE;
3326     obj.datum.surface = image;
3327     return push (&obj);
3328 }
3329
3330 static csi_status_t
3331 _index (csi_t *ctx)
3332 {
3333     csi_status_t status;
3334     long n;
3335
3336     check (1);
3337
3338     status = _csi_ostack_get_integer (ctx, 0,  &n);
3339     if (_csi_unlikely (status))
3340         return status;
3341
3342     pop (1);
3343
3344     check (n);
3345     return _csi_push_ostack_copy (ctx, _csi_peek_ostack (ctx, n));
3346 }
3347
3348 static csi_status_t
3349 _integer (csi_t *ctx)
3350 {
3351     csi_object_t *obj;
3352     int type;
3353
3354     check (1);
3355
3356     obj = _csi_peek_ostack (ctx, 0);
3357     type = csi_object_get_type (obj);
3358     switch (type) {
3359     case CSI_OBJECT_TYPE_INTEGER:
3360         break;
3361     case CSI_OBJECT_TYPE_REAL:
3362         obj->datum.integer = obj->datum.real;
3363         break;
3364     default:
3365         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
3366     }
3367     obj->type = CSI_OBJECT_TYPE_INTEGER;
3368
3369     return CSI_STATUS_SUCCESS;
3370 }
3371
3372 static csi_status_t
3373 _invert (csi_t *ctx)
3374 {
3375     csi_object_t obj;
3376     csi_status_t status;
3377     cairo_matrix_t m;
3378
3379     check (1);
3380
3381     status = _csi_ostack_get_matrix (ctx, 0, &m);
3382     if (_csi_unlikely (status))
3383         return status;
3384
3385     cairo_matrix_invert (&m);
3386
3387     status = csi_matrix_new_from_matrix (ctx, &obj, &m);
3388     if (_csi_unlikely (status))
3389         return status;
3390
3391     pop (1);
3392
3393     return push (&obj);
3394 }
3395
3396 static csi_status_t
3397 _le (csi_t *ctx)
3398 {
3399     csi_status_t status;
3400     csi_object_t *a, *b;
3401     int cmp;
3402
3403     check (2);
3404
3405     b = _csi_peek_ostack (ctx, 0);
3406     a = _csi_peek_ostack (ctx, 1);
3407
3408     status = csi_object_compare (a, b, &cmp);
3409     if (_csi_unlikely (status))
3410         return status;
3411
3412     pop (2);
3413     return _csi_push_ostack_boolean (ctx, cmp <= 0);
3414 }
3415
3416 static csi_status_t
3417 _linear (csi_t *ctx)
3418 {
3419     csi_object_t obj;
3420     csi_status_t status;
3421     double x1, y1, x2, y2;
3422
3423     check (4);
3424
3425     status = _csi_ostack_get_number (ctx, 0, &y2);
3426     if (_csi_unlikely (status))
3427         return status;
3428     status = _csi_ostack_get_number (ctx, 1, &x2);
3429     if (_csi_unlikely (status))
3430         return status;
3431     status = _csi_ostack_get_number (ctx, 2, &y1);
3432     if (_csi_unlikely (status))
3433         return status;
3434     status = _csi_ostack_get_number (ctx, 3, &x1);
3435     if (_csi_unlikely (status))
3436         return status;
3437
3438     pop (4);
3439
3440     obj.type = CSI_OBJECT_TYPE_PATTERN;
3441     obj.datum.pattern = cairo_pattern_create_linear (x1, y1, x2, y2);
3442     return push (&obj);
3443 }
3444
3445 static csi_status_t
3446 _line_to (csi_t *ctx)
3447 {
3448     csi_status_t status;
3449     csi_object_t *obj;
3450     int type;
3451     double x, y;
3452
3453     check (3);
3454
3455     status = _csi_ostack_get_number (ctx, 0, &y);
3456     if (_csi_unlikely (status))
3457         return status;
3458     status = _csi_ostack_get_number (ctx, 1, &x);
3459     if (_csi_unlikely (status))
3460         return status;
3461
3462     /* XXX path object */
3463
3464     obj = _csi_peek_ostack (ctx, 2);
3465     type = csi_object_get_type (obj);
3466     switch (type) {
3467     case CSI_OBJECT_TYPE_CONTEXT:
3468         cairo_line_to (obj->datum.cr, x, y);
3469         break;
3470     case CSI_OBJECT_TYPE_PATTERN:
3471         cairo_mesh_pattern_line_to (obj->datum.pattern, x, y);
3472         break;
3473     }
3474
3475     pop (2);
3476     return CSI_STATUS_SUCCESS;
3477 }
3478
3479 static csi_status_t
3480 _lt (csi_t *ctx)
3481 {
3482     csi_status_t status;
3483     csi_object_t *a, *b;
3484     int cmp;
3485
3486     check (2);
3487
3488     b = _csi_peek_ostack (ctx, 0);
3489     a = _csi_peek_ostack (ctx, 1);
3490
3491     status = csi_object_compare (a, b, &cmp);
3492     if (_csi_unlikely (status))
3493         return status;
3494
3495     pop (2);
3496     return _csi_push_ostack_boolean (ctx, cmp < 0);
3497 }
3498
3499 static csi_status_t
3500 _mark (csi_t *ctx)
3501 {
3502     return _csi_push_ostack_mark (ctx);
3503 }
3504
3505 static csi_status_t
3506 _ne (csi_t *ctx)
3507 {
3508     csi_object_t *a, *b;
3509     csi_boolean_t v;
3510
3511     check (2);
3512
3513     b = _csi_peek_ostack (ctx, 0);
3514     a = _csi_peek_ostack (ctx, 1);
3515
3516     v = ! csi_object_eq (a, b);
3517
3518     pop (2);
3519     return _csi_push_ostack_boolean (ctx, v);
3520 }
3521
3522 static csi_status_t
3523 _neg (csi_t *ctx)
3524 {
3525     csi_object_t *obj;
3526     int type;
3527
3528     check (1);
3529
3530     obj = _csi_peek_ostack (ctx, 0);
3531     type = csi_object_get_type (obj);
3532     switch (type) {
3533     case CSI_OBJECT_TYPE_INTEGER:
3534         obj->datum.integer = -obj->datum.integer;
3535         break;
3536     case CSI_OBJECT_TYPE_REAL:
3537         obj->datum.real = -obj->datum.real;
3538         break;
3539     default:
3540         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
3541     }
3542
3543     return CSI_STATUS_SUCCESS;
3544 }
3545
3546 static csi_status_t
3547 _not (csi_t *ctx)
3548 {
3549     csi_object_t *obj;
3550     int type;
3551
3552     check (1);
3553
3554     obj = _csi_peek_ostack (ctx, 0);
3555     type = csi_object_get_type (obj);
3556     switch (type) {
3557     case CSI_OBJECT_TYPE_BOOLEAN:
3558         obj->datum.boolean = ! obj->datum.boolean;
3559         break;
3560     case CSI_OBJECT_TYPE_INTEGER:
3561         obj->type = CSI_OBJECT_TYPE_BOOLEAN;
3562         obj->datum.boolean = ! obj->datum.integer;
3563         break;
3564     case CSI_OBJECT_TYPE_REAL:
3565         obj->type = CSI_OBJECT_TYPE_BOOLEAN;
3566         obj->datum.boolean = obj->datum.real == 0.0;
3567         break;
3568     default:
3569         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
3570     }
3571
3572     return CSI_STATUS_SUCCESS;
3573 }
3574
3575 static csi_status_t
3576 _new_path (csi_t *ctx)
3577 {
3578     /* XXX handle path object */
3579     return _do_cairo_op (ctx, cairo_new_path);
3580 }
3581
3582 static csi_status_t
3583 _new_sub_path (csi_t *ctx)
3584 {
3585     /* XXX handle path object */
3586     return _do_cairo_op (ctx, cairo_new_sub_path);
3587 }
3588
3589 static csi_status_t
3590 _null (csi_t *ctx)
3591 {
3592     return _csi_push_ostack_null (ctx);
3593 }
3594
3595 static csi_status_t
3596 _mask (csi_t *ctx)
3597 {
3598     cairo_t *cr;
3599     cairo_pattern_t *pattern = NULL; /* silence the compiler */
3600     csi_status_t status;
3601
3602     check (2);
3603
3604     status = _csi_ostack_get_pattern (ctx, 0, &pattern);
3605     if (_csi_unlikely (status))
3606         return status;
3607     status = _csi_ostack_get_context (ctx, 1, &cr);
3608     if (_csi_unlikely (status))
3609         return status;
3610
3611     cairo_mask (cr, pattern);
3612     pop (1);
3613
3614     return CSI_STATUS_SUCCESS;
3615 }
3616
3617 static csi_status_t
3618 _matrix (csi_t *ctx)
3619 {
3620     csi_object_t *obj, matrix;
3621     double v[6];
3622     csi_status_t status;
3623     int n;
3624
3625     check (1);
3626
3627     obj = _csi_peek_ostack (ctx, 0);
3628     if (csi_object_is_number (obj)) {
3629         check (6);
3630
3631         for (n = 6; n--; ) {
3632             status = _csi_ostack_get_number (ctx, 5-n, &v[n]);
3633             if (_csi_unlikely (status))
3634                 return status;
3635         }
3636         status = csi_matrix_new_from_values (ctx, &matrix, v);
3637         if (_csi_unlikely (status))
3638             return status;
3639
3640         pop (6);
3641     } else {
3642         csi_array_t *array;
3643
3644         status = _csi_ostack_get_array (ctx, 0, &array);
3645         if (_csi_unlikely (status))
3646             return status;
3647
3648         status = csi_matrix_new_from_array (ctx, &matrix, array);
3649         if (_csi_unlikely (status))
3650             return status;
3651
3652         pop (1);
3653     }
3654
3655     return push (&matrix);
3656 }
3657
3658 static csi_status_t
3659 _map_to_image (csi_t *ctx)
3660 {
3661     csi_object_t obj;
3662     csi_array_t *array;
3663     csi_status_t status;
3664     cairo_rectangle_int_t extents, *r;
3665     cairo_surface_t *surface;
3666
3667     check (2);
3668
3669     status = _csi_ostack_get_array (ctx, 0, &array);
3670     if (_csi_unlikely (status))
3671         return status;
3672
3673     status = _csi_ostack_get_surface (ctx, 1, &surface);
3674     if (_csi_unlikely (status))
3675         return status;
3676
3677     switch (array->stack.len) {
3678     case 0:
3679         r = NULL;
3680         break;
3681     case 4:
3682         extents.x = floor (_csi_object_as_real (&array->stack.objects[0]));
3683         extents.y = floor (_csi_object_as_real (&array->stack.objects[1]));
3684         extents.width = ceil (_csi_object_as_real (&array->stack.objects[2]));
3685         extents.height = ceil (_csi_object_as_real (&array->stack.objects[3]));
3686         r = &extents;
3687         break;
3688     default:
3689         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
3690     }
3691
3692     obj.type = CSI_OBJECT_TYPE_SURFACE;
3693     obj.datum.surface = cairo_surface_reference (cairo_surface_map_to_image (surface, r));
3694     pop (1);
3695     return push (&obj);
3696 }
3697
3698 static csi_status_t
3699 _unmap_image (csi_t *ctx)
3700 {
3701     cairo_surface_t *surface, *image;
3702     csi_status_t status;
3703
3704     check (2);
3705
3706     status = _csi_ostack_get_surface (ctx, 0, &image);
3707     if (_csi_unlikely (status))
3708         return status;
3709     status = _csi_ostack_get_surface (ctx, 1, &surface);
3710     if (_csi_unlikely (status))
3711         return status;
3712
3713     cairo_surface_unmap_image (surface, image);
3714
3715     pop (1);
3716     return CSI_STATUS_SUCCESS;
3717 }
3718
3719 static csi_status_t
3720 _mesh (csi_t *ctx)
3721 {
3722     csi_object_t obj;
3723
3724     obj.type = CSI_OBJECT_TYPE_PATTERN;
3725     obj.datum.pattern = cairo_pattern_create_mesh ();
3726     return push (&obj);
3727 }
3728
3729 static csi_status_t
3730 _mesh_begin_patch (csi_t *ctx)
3731 {
3732     csi_status_t status;
3733     cairo_pattern_t *pattern = NULL; /* silence the compiler */
3734
3735     check (1);
3736
3737     status = _csi_ostack_get_pattern (ctx, 0, &pattern);
3738     if (_csi_unlikely (status))
3739         return status;
3740
3741     cairo_mesh_pattern_begin_patch (pattern);
3742     return CSI_STATUS_SUCCESS;
3743 }
3744
3745 static csi_status_t
3746 _mesh_end_patch (csi_t *ctx)
3747 {
3748     csi_status_t status;
3749     cairo_pattern_t *pattern = NULL; /* silence the compiler */
3750
3751     check (1);
3752
3753     status = _csi_ostack_get_pattern (ctx, 0, &pattern);
3754     if (_csi_unlikely (status))
3755         return status;
3756
3757     cairo_mesh_pattern_end_patch (pattern);
3758     return CSI_STATUS_SUCCESS;
3759 }
3760
3761 static csi_status_t
3762 _mesh_set_control_point (csi_t *ctx)
3763 {
3764     csi_status_t status;
3765     double x, y;
3766     csi_integer_t point;
3767     cairo_pattern_t *pattern = NULL; /* silence the compiler */
3768
3769     check (4);
3770
3771     status = _csi_ostack_get_number (ctx, 0, &y);
3772     if (_csi_unlikely (status))
3773         return status;
3774     status = _csi_ostack_get_number (ctx, 1, &x);
3775     if (_csi_unlikely (status))
3776         return status;
3777     status = _csi_ostack_get_integer (ctx, 2, &point);
3778     if (_csi_unlikely (status))
3779         return status;
3780     status = _csi_ostack_get_pattern (ctx, 3, &pattern);
3781     if (_csi_unlikely (status))
3782         return status;
3783
3784     cairo_mesh_pattern_set_control_point (pattern, point, x, y);
3785
3786     pop (3);
3787     return CSI_STATUS_SUCCESS;
3788 }
3789
3790 static csi_status_t
3791 _mesh_set_corner_color (csi_t *ctx)
3792 {
3793     csi_status_t status;
3794     double r, g, b, a;
3795     csi_integer_t corner;
3796     cairo_pattern_t *pattern = NULL; /* silence the compiler */
3797
3798     check (6);
3799
3800     status = _csi_ostack_get_number (ctx, 0, &a);
3801     if (_csi_unlikely (status))
3802         return status;
3803     status = _csi_ostack_get_number (ctx, 1, &b);
3804     if (_csi_unlikely (status))
3805         return status;
3806     status = _csi_ostack_get_number (ctx, 2, &g);
3807     if (_csi_unlikely (status))
3808         return status;
3809     status = _csi_ostack_get_number (ctx, 3, &r);
3810     if (_csi_unlikely (status))
3811         return status;
3812     status = _csi_ostack_get_integer (ctx, 4, &corner);
3813     if (_csi_unlikely (status))
3814         return status;
3815     status = _csi_ostack_get_pattern (ctx, 5, &pattern);
3816     if (_csi_unlikely (status))
3817         return status;
3818
3819     cairo_mesh_pattern_set_corner_color_rgba (pattern, corner, r, g, b, a);
3820
3821     pop (5);
3822     return CSI_STATUS_SUCCESS;
3823 }
3824
3825 static csi_status_t
3826 _mod (csi_t *ctx)
3827 {
3828     csi_integer_t x, y;
3829     csi_status_t status;
3830
3831     check (2);
3832
3833     status = _csi_ostack_get_integer (ctx, 0, &y);
3834     if (_csi_unlikely (status))
3835         return status;
3836     status = _csi_ostack_get_integer (ctx, 1, &x);
3837     if (_csi_unlikely (status))
3838         return status;
3839
3840     pop (2);
3841     return _csi_push_ostack_integer (ctx, x % y);
3842 }
3843
3844 static csi_status_t
3845 _move_to (csi_t *ctx)
3846 {
3847     csi_status_t status;
3848     csi_object_t *obj;
3849     int type;
3850     double x, y;
3851
3852     check (3);
3853
3854     status = _csi_ostack_get_number (ctx, 0, &y);
3855     if (_csi_unlikely (status))
3856         return status;
3857     status = _csi_ostack_get_number (ctx, 1, &x);
3858     if (_csi_unlikely (status))
3859         return status;
3860
3861     obj = _csi_peek_ostack (ctx, 2);
3862     type = csi_object_get_type (obj);
3863     switch (type) {
3864     case CSI_OBJECT_TYPE_CONTEXT:
3865         cairo_move_to (obj->datum.cr, x, y);
3866         break;
3867     case CSI_OBJECT_TYPE_PATTERN:
3868         cairo_mesh_pattern_move_to (obj->datum.pattern, x, y);
3869         break;
3870
3871         /* XXX path object */
3872     }
3873
3874     pop (2);
3875     return CSI_STATUS_SUCCESS;
3876 }
3877
3878 static csi_status_t
3879 _mul (csi_t *ctx)
3880 {
3881     csi_object_t *A;
3882     csi_object_t *B;
3883     csi_object_type_t type_a, type_b;
3884
3885     check (2);
3886
3887     B = _csi_peek_ostack (ctx, 0);
3888     A = _csi_peek_ostack (ctx, 1);
3889
3890     type_a = csi_object_get_type (A);
3891     if (_csi_unlikely (! (type_a == CSI_OBJECT_TYPE_INTEGER ||
3892                             type_a == CSI_OBJECT_TYPE_REAL)))
3893     {
3894         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
3895     }
3896     type_b = csi_object_get_type (B);
3897     if (_csi_unlikely (! (type_b == CSI_OBJECT_TYPE_INTEGER ||
3898                             type_b == CSI_OBJECT_TYPE_REAL)))
3899     {
3900         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
3901     }
3902
3903     pop (2);
3904
3905     if (type_a == CSI_OBJECT_TYPE_REAL &&
3906         type_b == CSI_OBJECT_TYPE_REAL)
3907     {
3908         return _csi_push_ostack_real (ctx, A->datum.real * B->datum.real);
3909
3910     }
3911     else if (type_a == CSI_OBJECT_TYPE_INTEGER &&
3912              type_b == CSI_OBJECT_TYPE_INTEGER)
3913     {
3914         return _csi_push_ostack_integer (ctx,
3915                                          A->datum.integer * B->datum.integer);
3916     }
3917     else
3918     {
3919         double v;
3920
3921         if (type_a == CSI_OBJECT_TYPE_REAL)
3922             v = A->datum.real;
3923         else
3924             v = A->datum.integer;
3925
3926         if (type_b == CSI_OBJECT_TYPE_REAL)
3927             v *= B->datum.real;
3928         else
3929             v *= B->datum.integer;
3930
3931         return _csi_push_ostack_real (ctx, v);
3932     }
3933 }
3934
3935 static csi_status_t
3936 _or (csi_t *ctx)
3937 {
3938     csi_object_t *a, *b;
3939     int type;
3940
3941     check (2);
3942
3943     a = _csi_peek_ostack (ctx, 0);
3944     b = _csi_peek_ostack (ctx, 1);
3945     if (_csi_unlikely (csi_object_get_type (a) != csi_object_get_type (b)))
3946         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
3947
3948     pop (2);
3949     type = csi_object_get_type (a);
3950     switch (type) {
3951     case CSI_OBJECT_TYPE_INTEGER:
3952         return _csi_push_ostack_integer (ctx,
3953                                          a->datum.integer | b->datum.integer);
3954     case CSI_OBJECT_TYPE_BOOLEAN:
3955         return _csi_push_ostack_boolean (ctx,
3956                                          a->datum.boolean | b->datum.boolean);
3957     default:
3958         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
3959     }
3960 }
3961
3962 static csi_status_t
3963 _paint (csi_t *ctx)
3964 {
3965     return _do_cairo_op (ctx, cairo_paint);
3966 }
3967
3968 static csi_status_t
3969 _paint_with_alpha (csi_t *ctx)
3970 {
3971     cairo_t *cr;
3972     csi_status_t status;
3973     double alpha;
3974
3975     check (2);
3976
3977     status = _csi_ostack_get_number (ctx, 0, &alpha);
3978     if (_csi_unlikely (status))
3979         return status;
3980
3981     status = _csi_ostack_get_context (ctx, 1, &cr);
3982     if (_csi_unlikely (status))
3983         return status;
3984
3985     cairo_paint_with_alpha (cr, alpha);
3986     pop (1);
3987     return CSI_STATUS_SUCCESS;
3988 }
3989
3990 static csi_status_t
3991 _pattern (csi_t *ctx)
3992 {
3993     csi_object_t obj;
3994     csi_status_t status;
3995     cairo_surface_t *surface;
3996
3997     check (1);
3998
3999     status = _csi_ostack_get_surface (ctx, 0, &surface);
4000     if (_csi_unlikely (status))
4001         return status;
4002
4003     obj.type = CSI_OBJECT_TYPE_PATTERN;
4004     obj.datum.pattern = cairo_pattern_create_for_surface (surface);
4005
4006     pop (1);
4007     return push (&obj);
4008 }
4009
4010 static csi_status_t
4011 _pop (csi_t *ctx)
4012 {
4013     check (1);
4014     pop (1);
4015
4016     return CSI_STATUS_SUCCESS;
4017 }
4018
4019 static csi_status_t
4020 _pop_group (csi_t *ctx)
4021 {
4022     csi_object_t obj;
4023     csi_status_t status;
4024     cairo_t *cr;
4025
4026     check (1);
4027
4028     status = _csi_ostack_get_context (ctx, 0, &cr);
4029     if (_csi_unlikely (status))
4030         return status;
4031
4032     obj.type = CSI_OBJECT_TYPE_PATTERN;
4033     obj.datum.pattern = cairo_pop_group (cr);
4034
4035     return push (&obj);
4036 }
4037
4038 static csi_status_t
4039 _push_group (csi_t *ctx)
4040 {
4041     csi_status_t status;
4042     cairo_t *cr;
4043     long content;
4044
4045     check (2);
4046
4047     status = _csi_ostack_get_integer (ctx, 0, &content);
4048     if (_csi_unlikely (status))
4049         return status;
4050
4051     status = _csi_ostack_get_context (ctx, 1, &cr);
4052     if (_csi_unlikely (status))
4053         return status;
4054
4055     cairo_push_group_with_content (cr, content);
4056     pop (1);
4057     return CSI_STATUS_SUCCESS;
4058 }
4059
4060 static csi_status_t
4061 _radial (csi_t *ctx)
4062 {
4063     csi_object_t obj;
4064     csi_status_t status;
4065     double x1, y1, r1, x2, y2, r2;
4066
4067     check (6);
4068
4069     status = _csi_ostack_get_number (ctx, 0, &r2);
4070     if (_csi_unlikely (status))
4071         return status;
4072     status = _csi_ostack_get_number (ctx, 1, &y2);
4073     if (_csi_unlikely (status))
4074         return status;
4075     status = _csi_ostack_get_number (ctx, 2, &x2);
4076     if (_csi_unlikely (status))
4077         return status;
4078     status = _csi_ostack_get_number (ctx, 3, &r1);
4079     if (_csi_unlikely (status))
4080         return status;
4081     status = _csi_ostack_get_number (ctx, 4, &y1);
4082     if (_csi_unlikely (status))
4083         return status;
4084     status = _csi_ostack_get_number (ctx, 5, &x1);
4085     if (_csi_unlikely (status))
4086         return status;
4087
4088     obj.type = CSI_OBJECT_TYPE_PATTERN;
4089     obj.datum.pattern = cairo_pattern_create_radial (x1, y1, r1, x2, y2, r2);
4090     pop (6);
4091     return push (&obj);
4092 }
4093
4094 static csi_status_t
4095 _rectangle (csi_t *ctx)
4096 {
4097     csi_status_t status;
4098     double x, y;
4099     double w, h;
4100     cairo_t *cr;
4101
4102     check (5);
4103
4104     status = _csi_ostack_get_number (ctx, 0, &h);
4105     if (_csi_unlikely (status))
4106         return status;
4107     status = _csi_ostack_get_number (ctx, 1, &w);
4108     if (_csi_unlikely (status))
4109         return status;
4110     status = _csi_ostack_get_number (ctx, 2, &y);
4111     if (_csi_unlikely (status))
4112         return status;
4113     status = _csi_ostack_get_number (ctx, 3, &x);
4114     if (_csi_unlikely (status))
4115         return status;
4116     status = _csi_ostack_get_context (ctx, 4, &cr);
4117     if (_csi_unlikely (status))
4118         return status;
4119
4120     /* XXX path object */
4121
4122     cairo_rectangle (cr, x, y, w, h);
4123     pop(4);
4124     return CSI_STATUS_SUCCESS;
4125 }
4126
4127 static csi_status_t
4128 _rel_curve_to (csi_t *ctx)
4129 {
4130     csi_status_t status;
4131     double x1, y1;
4132     double x2, y2;
4133     double x3, y3;
4134     cairo_t *cr;
4135
4136     check (7);
4137
4138     status = _csi_ostack_get_number (ctx, 0, &y3);
4139     if (_csi_unlikely (status))
4140         return status;
4141     status = _csi_ostack_get_number (ctx, 1, &x3);
4142     if (_csi_unlikely (status))
4143         return status;
4144     status = _csi_ostack_get_number (ctx, 2, &y2);
4145     if (_csi_unlikely (status))
4146         return status;
4147     status = _csi_ostack_get_number (ctx, 3, &x2);
4148     if (_csi_unlikely (status))
4149         return status;
4150     status = _csi_ostack_get_number (ctx, 4, &y1);
4151     if (_csi_unlikely (status))
4152         return status;
4153     status = _csi_ostack_get_number (ctx, 5, &x1);
4154     if (_csi_unlikely (status))
4155         return status;
4156     status = _csi_ostack_get_context (ctx, 6, &cr);
4157     if (_csi_unlikely (status))
4158         return status;
4159
4160     /* XXX path object */
4161
4162     cairo_rel_curve_to (cr, x1, y1, x2, y2, x3, y3);
4163     pop (6);
4164     return CSI_STATUS_SUCCESS;
4165 }
4166
4167 static csi_status_t
4168 _rel_line_to (csi_t *ctx)
4169 {
4170     csi_status_t status;
4171     double x, y;
4172     cairo_t *cr;
4173
4174     check (3);
4175
4176     status = _csi_ostack_get_number (ctx, 0, &y);
4177     if (_csi_unlikely (status))
4178         return status;
4179     status = _csi_ostack_get_number (ctx, 1, &x);
4180     if (_csi_unlikely (status))
4181         return status;
4182     status = _csi_ostack_get_context (ctx, 2, &cr);
4183     if (_csi_unlikely (status))
4184         return status;
4185
4186     /* XXX path object */
4187
4188     cairo_rel_line_to (cr, x, y);
4189     pop (2);
4190     return CSI_STATUS_SUCCESS;
4191 }
4192
4193 static csi_status_t
4194 _rel_move_to (csi_t *ctx)
4195 {
4196     csi_status_t status;
4197     double x, y;
4198     cairo_t *cr;
4199
4200     check (3);
4201
4202     status = _csi_ostack_get_number (ctx, 0, &y);
4203     if (_csi_unlikely (status))
4204         return status;
4205     status = _csi_ostack_get_number (ctx, 1, &x);
4206     if (_csi_unlikely (status))
4207         return status;
4208     status = _csi_ostack_get_context (ctx, 2, &cr);
4209     if (_csi_unlikely (status))
4210         return status;
4211
4212     /* XXX path object */
4213     cairo_rel_move_to (cr, x, y);
4214     pop (2);
4215     return CSI_STATUS_SUCCESS;
4216 }
4217
4218 static csi_status_t
4219 _repeat (csi_t *ctx)
4220 {
4221     csi_array_t *proc;
4222     csi_integer_t count;
4223     csi_status_t status;
4224
4225     check (2);
4226
4227     status = _csi_ostack_get_procedure (ctx, 0, &proc);
4228     if (_csi_unlikely (status))
4229         return status;
4230
4231     status = _csi_ostack_get_integer (ctx, 1, &count);
4232     if (_csi_unlikely (status))
4233         return status;
4234
4235     if (_csi_unlikely (count < 0))
4236         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4237
4238     proc->base.ref++;
4239     pop (2);
4240
4241     while (count--) {
4242         status = _csi_array_execute (ctx, proc);
4243         if (_csi_unlikely (status))
4244             break;
4245     }
4246
4247     if (--proc->base.ref == 0)
4248         csi_array_free (ctx, proc);
4249
4250     return status;
4251 }
4252
4253 static csi_status_t
4254 _reset_clip (csi_t *ctx)
4255 {
4256     return _do_cairo_op (ctx, cairo_reset_clip);
4257 }
4258
4259 static csi_status_t
4260 _restore (csi_t *ctx)
4261 {
4262     return _do_cairo_op (ctx, cairo_restore);
4263 }
4264
4265 static csi_status_t
4266 _rgb (csi_t *ctx)
4267 {
4268     csi_object_t obj;
4269     csi_status_t status;
4270     double r,g,b;
4271
4272     check (3);
4273
4274     status = _csi_ostack_get_number (ctx, 0, &b);
4275     if (_csi_unlikely (status))
4276         return status;
4277     status = _csi_ostack_get_number (ctx, 1, &g);
4278     if (_csi_unlikely (status))
4279         return status;
4280     status = _csi_ostack_get_number (ctx, 2, &r);
4281     if (_csi_unlikely (status))
4282         return status;
4283
4284     obj.type = CSI_OBJECT_TYPE_PATTERN;
4285     obj.datum.pattern = cairo_pattern_create_rgb (r, g, b);
4286     pop (3);
4287     return push (&obj);
4288 }
4289
4290 static csi_status_t
4291 _rgba (csi_t *ctx)
4292 {
4293     csi_object_t obj;
4294     csi_status_t status;
4295     double r,g,b,a;
4296
4297     check (4);
4298
4299     status = _csi_ostack_get_number (ctx, 0, &a);
4300     if (_csi_unlikely (status))
4301         return status;
4302     status = _csi_ostack_get_number (ctx, 1, &b);
4303     if (_csi_unlikely (status))
4304         return status;
4305     status = _csi_ostack_get_number (ctx, 2, &g);
4306     if (_csi_unlikely (status))
4307         return status;
4308     status = _csi_ostack_get_number (ctx, 3, &r);
4309     if (_csi_unlikely (status))
4310         return status;
4311
4312     obj.type = CSI_OBJECT_TYPE_PATTERN;
4313     obj.datum.pattern = cairo_pattern_create_rgba (r, g, b, a);
4314     pop (4);
4315     return push (&obj);
4316 }
4317
4318 static csi_status_t
4319 _roll (csi_t *ctx)
4320 {
4321     csi_status_t status;
4322     long j, n;
4323
4324     check (2);
4325
4326     status = _csi_ostack_get_integer (ctx, 0, &j);
4327     if (_csi_unlikely (status))
4328         return status;
4329     status = _csi_ostack_get_integer (ctx, 1, &n);
4330     if (_csi_unlikely (status))
4331         return status;
4332
4333     pop (2);
4334     check (n);
4335     return _csi_stack_roll (ctx, &ctx->ostack, j, n);
4336 }
4337
4338 static csi_status_t
4339 _rotate (csi_t *ctx)
4340 {
4341     csi_object_t *obj;
4342     csi_status_t status;
4343     double theta;
4344     int type;
4345
4346     check (2);
4347
4348     status = _csi_ostack_get_number (ctx, 0, &theta);
4349     if (_csi_unlikely (status))
4350         return status;
4351
4352     obj = _csi_peek_ostack (ctx, 1);
4353     type = csi_object_get_type (obj);
4354     switch (type) {
4355     case CSI_OBJECT_TYPE_CONTEXT:
4356         cairo_rotate (obj->datum.cr, theta);
4357         break;
4358
4359     case CSI_OBJECT_TYPE_PATTERN:
4360         {
4361             cairo_matrix_t ctm;
4362             cairo_pattern_get_matrix (obj->datum.pattern, &ctm);
4363             cairo_matrix_rotate (&ctm, theta);
4364             cairo_pattern_set_matrix (obj->datum.pattern, &ctm);
4365         }
4366         break;
4367
4368
4369     case CSI_OBJECT_TYPE_MATRIX:
4370         cairo_matrix_rotate (&obj->datum.matrix->matrix, theta);
4371         break;
4372
4373     default:
4374         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4375     }
4376
4377     pop (1);
4378     return CSI_STATUS_SUCCESS;
4379 }
4380
4381 static csi_status_t
4382 _save (csi_t *ctx)
4383 {
4384     return _do_cairo_op (ctx, cairo_save);
4385 }
4386
4387 static csi_status_t
4388 _scale (csi_t *ctx)
4389 {
4390     csi_object_t *obj;
4391     csi_status_t status;
4392     double x, y;
4393     int type;
4394
4395     check (3);
4396
4397     status = _csi_ostack_get_number (ctx, 0, &y);
4398     if (_csi_unlikely (status))
4399         return status;
4400     status = _csi_ostack_get_number (ctx, 1, &x);
4401     if (_csi_unlikely (status))
4402         return status;
4403
4404     obj = _csi_peek_ostack (ctx, 2);
4405     type = csi_object_get_type (obj);
4406     switch (type) {
4407     case CSI_OBJECT_TYPE_CONTEXT:
4408         cairo_scale (obj->datum.cr, x, y);
4409         break;
4410
4411     case CSI_OBJECT_TYPE_PATTERN:
4412         {
4413             cairo_matrix_t ctm;
4414             cairo_pattern_get_matrix (obj->datum.pattern, &ctm);
4415             cairo_matrix_scale (&ctm, x, y);
4416             cairo_pattern_set_matrix (obj->datum.pattern, &ctm);
4417         }
4418         break;
4419
4420
4421     case CSI_OBJECT_TYPE_MATRIX:
4422         cairo_matrix_scale (&obj->datum.matrix->matrix, x, y);
4423         break;
4424
4425     default:
4426         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4427     }
4428
4429     pop (2);
4430     return CSI_STATUS_SUCCESS;
4431 }
4432
4433 static csi_status_t
4434 _font_options_load_from_dictionary (csi_t *ctx,
4435                                     csi_dictionary_t *dict,
4436                                     cairo_font_options_t *options)
4437 {
4438     const struct {
4439         const char *key;
4440         void (*setter) (cairo_font_options_t *, int val);
4441     } properties[] = {
4442         { "antialias",
4443             (void (*)(cairo_font_options_t *, int val))
4444                 cairo_font_options_set_antialias },
4445         { "subpixel-order",
4446             (void (*)(cairo_font_options_t *, int val))
4447                 cairo_font_options_set_subpixel_order },
4448         { "hint-style",
4449             (void (*)(cairo_font_options_t *, int val))
4450                 cairo_font_options_set_hint_style },
4451         { "hint-metrics",
4452             (void (*)(cairo_font_options_t *, int val))
4453                 cairo_font_options_set_hint_metrics },
4454         { NULL, NULL },
4455     }, *prop = properties;
4456
4457     while (prop->key != NULL) {
4458         csi_object_t key, value;
4459         csi_status_t status;
4460
4461         status = csi_name_new_static (ctx, &key, prop->key);
4462         if (_csi_unlikely (status))
4463             return status;
4464
4465         if (csi_dictionary_has (dict, key.datum.name)) {
4466             status = csi_dictionary_get (ctx, dict, key.datum.name, &value);
4467             if (_csi_unlikely (status))
4468                 return status;
4469
4470             if (_csi_unlikely (csi_object_get_type (&value) !=
4471                                  CSI_OBJECT_TYPE_INTEGER))
4472             {
4473                 csi_object_free (ctx, &value);
4474                 return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4475             }
4476
4477             prop->setter (options, value.datum.integer);
4478         }
4479
4480         prop++;
4481     }
4482
4483     return CSI_STATUS_SUCCESS;
4484 }
4485
4486 static csi_status_t
4487 _scaled_font (csi_t *ctx)
4488 {
4489     csi_object_t obj;
4490     csi_dictionary_t *dict;
4491     cairo_font_face_t *font_face = NULL; /* silence the compiler */
4492     cairo_matrix_t font_matrix, ctm;
4493     cairo_font_options_t *options;
4494     csi_status_t status;
4495
4496     check (4);
4497
4498     status = _csi_ostack_get_dictionary (ctx, 0, &dict);
4499     if (_csi_unlikely (status))
4500         return status;
4501     options = cairo_font_options_create ();
4502     status = _font_options_load_from_dictionary (ctx, dict, options);
4503     if (_csi_unlikely (status)) {
4504         cairo_font_options_destroy (options);
4505         return status;
4506     }
4507
4508     status = _csi_ostack_get_matrix (ctx, 1, &ctm);
4509     if (_csi_unlikely (status)) {
4510         cairo_font_options_destroy (options);
4511         return status;
4512     }
4513
4514     status = _csi_ostack_get_matrix (ctx, 2, &font_matrix);
4515     if (_csi_unlikely (status)) {
4516         cairo_font_options_destroy (options);
4517         return status;
4518     }
4519
4520     status = _csi_ostack_get_font_face (ctx, 3, &font_face);
4521     if (_csi_unlikely (status)) {
4522         cairo_font_options_destroy (options);
4523         return status;
4524     }
4525
4526     obj.type = CSI_OBJECT_TYPE_SCALED_FONT;
4527     obj.datum.scaled_font = cairo_scaled_font_create (font_face,
4528                                                       &font_matrix,
4529                                                       &ctm,
4530                                                       options);
4531     cairo_font_options_destroy (options);
4532     pop (4);
4533     return push (&obj);
4534 }
4535
4536 static csi_status_t
4537 _select_font_face (csi_t *ctx)
4538 {
4539     cairo_t *cr;
4540     long weight;
4541     long slant;
4542     csi_string_t *family;
4543     csi_status_t status;
4544
4545     check (4);
4546
4547     status = _csi_ostack_get_integer (ctx, 0,  &weight);
4548     if (_csi_unlikely (status))
4549         return status;
4550     status = _csi_ostack_get_integer (ctx, 1, &slant);
4551     if (_csi_unlikely (status))
4552         return status;
4553     status = _csi_ostack_get_string (ctx, 2, &family);
4554     if (_csi_unlikely (status))
4555         return status;
4556     status = _csi_ostack_get_context (ctx, 3, &cr);
4557     if (_csi_unlikely (status))
4558         return status;
4559
4560     cairo_select_font_face (cr, family->string, slant, weight);
4561     pop (3);
4562     return CSI_STATUS_SUCCESS;
4563 }
4564
4565 static csi_status_t
4566 _context_set (csi_t *ctx,
4567               cairo_t *cr,
4568               csi_name_t key,
4569               csi_object_t *obj)
4570 {
4571     if (strcmp ((char *) key, "source") == 0) {
4572         if (_csi_unlikely (csi_object_get_type (obj) !=
4573                              CSI_OBJECT_TYPE_PATTERN))
4574             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4575
4576         cairo_set_source (cr, obj->datum.pattern);
4577         return CSI_STATUS_SUCCESS;
4578     }
4579
4580     if (strcmp ((char *) key, "scaled-font") == 0) {
4581         if (_csi_unlikely (csi_object_get_type (obj) !=
4582                              CSI_OBJECT_TYPE_SCALED_FONT))
4583             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4584
4585         cairo_set_scaled_font (cr, obj->datum.scaled_font);
4586         return CSI_STATUS_SUCCESS;
4587     }
4588
4589     if (strcmp ((char *) key, "font-face") == 0) {
4590         if (_csi_unlikely (csi_object_get_type (obj) !=
4591                              CSI_OBJECT_TYPE_FONT))
4592             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4593
4594         cairo_set_font_face (cr, obj->datum.font_face);
4595         return CSI_STATUS_SUCCESS;
4596     }
4597
4598     /* return _proxy_set()? */
4599     return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4600 }
4601
4602 static csi_status_t
4603 _set (csi_t *ctx)
4604 {
4605     csi_object_t *key, *value, *dst;
4606     csi_status_t status;
4607     int type;
4608
4609     check (3);
4610
4611     value = _csi_peek_ostack (ctx, 0);
4612     key = _csi_peek_ostack (ctx, 1);
4613     dst = _csi_peek_ostack (ctx, 2);
4614
4615     type = csi_object_get_type (dst);
4616     switch (type) {
4617     case CSI_OBJECT_TYPE_DICTIONARY:
4618         if (_csi_unlikely (csi_object_get_type (key) !=
4619                              CSI_OBJECT_TYPE_NAME))
4620             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4621
4622         status = csi_dictionary_put (ctx,
4623                                      dst->datum.dictionary,
4624                                      key->datum.name,
4625                                      value);
4626         break;
4627     case CSI_OBJECT_TYPE_ARRAY:
4628         if (_csi_unlikely (csi_object_get_type (key) !=
4629                              CSI_OBJECT_TYPE_INTEGER))
4630             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4631
4632         status = csi_array_put (ctx,
4633                                 dst->datum.array,
4634                                 key->datum.integer,
4635                                 value);
4636         break;
4637
4638     case CSI_OBJECT_TYPE_CONTEXT:
4639         if (_csi_unlikely (csi_object_get_type (key) !=
4640                              CSI_OBJECT_TYPE_NAME))
4641             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4642
4643         status = _context_set (ctx,
4644                                dst->datum.cr,
4645                                key->datum.name,
4646                                value);
4647         break;
4648
4649     case CSI_OBJECT_TYPE_STRING:
4650 #if 0
4651         status = csi_string_put (dst, key, value);
4652         break;
4653 #endif
4654     default:
4655         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4656     }
4657
4658     pop (2);
4659     return status;
4660 }
4661
4662 static csi_status_t
4663 _set_antialias (csi_t *ctx)
4664 {
4665     csi_status_t status;
4666     cairo_t *cr;
4667     long antialias;
4668
4669     check (2);
4670
4671     status = _csi_ostack_get_integer (ctx, 0, &antialias);
4672     if (_csi_unlikely (status))
4673         return status;
4674     status = _csi_ostack_get_context (ctx, 1, &cr);
4675     if (_csi_unlikely (status))
4676         return status;
4677
4678     cairo_set_antialias (cr, antialias);
4679     pop (1);
4680     return CSI_STATUS_SUCCESS;
4681 }
4682
4683 static csi_status_t
4684 _set_dash (csi_t *ctx)
4685 {
4686     csi_array_t *array;
4687     csi_status_t status;
4688     cairo_t *cr;
4689     double offset;
4690
4691     check (3);
4692
4693     status = _csi_ostack_get_number (ctx, 0, &offset);
4694     if (_csi_unlikely (status))
4695         return status;
4696     status = _csi_ostack_get_array (ctx, 1, &array);
4697     if (_csi_unlikely (status))
4698         return status;
4699     status = _csi_ostack_get_context (ctx, 2, &cr);
4700     if (_csi_unlikely (status))
4701         return status;
4702
4703     if (array->stack.len == 0) {
4704         cairo_set_dash (cr, NULL, 0., 0.);
4705     } else {
4706         double stack_dashes[8];
4707         double *dashes;
4708         csi_integer_t n;
4709
4710         if (_csi_likely (array->stack.len < ARRAY_LENGTH (stack_dashes))) {
4711             dashes = stack_dashes;
4712         } else {
4713         if (_csi_unlikely ((unsigned) array->stack.len >= INT_MAX / sizeof (double)))
4714             return _csi_error (CSI_STATUS_NO_MEMORY);
4715             dashes = _csi_alloc (ctx, sizeof (double) * array->stack.len);
4716             if (_csi_unlikely (dashes == NULL))
4717                 return _csi_error (CSI_STATUS_NO_MEMORY);
4718         }
4719
4720         for (n = 0; n < array->stack.len; n++) {
4721             if (_csi_unlikely (! csi_object_is_number
4722                                  (&array->stack.objects[n])))
4723             {
4724                 if (dashes != stack_dashes)
4725                     _csi_free (ctx, dashes);
4726                 return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4727             }
4728
4729             dashes[n] = csi_number_get_value (&array->stack.objects[n]);
4730         }
4731
4732         cairo_set_dash (cr, dashes, n, offset);
4733
4734         if (dashes != stack_dashes)
4735             _csi_free (ctx, dashes);
4736     }
4737
4738     pop (2);
4739     return CSI_STATUS_SUCCESS;
4740 }
4741
4742 static csi_status_t
4743 _set_device_offset (csi_t *ctx)
4744 {
4745     csi_status_t status;
4746     cairo_surface_t *surface;
4747     double x, y;
4748
4749     check (3);
4750
4751     status = _csi_ostack_get_number (ctx, 0,  &y);
4752     if (_csi_unlikely (status))
4753         return status;
4754     status = _csi_ostack_get_number (ctx, 1, &x);
4755     if (_csi_unlikely (status))
4756         return status;
4757     status = _csi_ostack_get_surface (ctx, 2, &surface);
4758     if (_csi_unlikely (status))
4759         return status;
4760
4761     cairo_surface_set_device_offset (surface, x, y);
4762     pop (2);
4763     return CSI_STATUS_SUCCESS;
4764 }
4765
4766 static csi_status_t
4767 _set_extend (csi_t *ctx)
4768 {
4769     csi_status_t status;
4770     csi_object_t *obj;
4771     long extend;
4772     int type;
4773
4774     check (2);
4775
4776     status = _csi_ostack_get_integer (ctx, 0, &extend);
4777     if (_csi_unlikely (status))
4778         return status;
4779
4780     obj = _csi_peek_ostack (ctx, 1);
4781     type = csi_object_get_type (obj);
4782     switch (type) {
4783     case CSI_OBJECT_TYPE_CONTEXT:
4784         cairo_pattern_set_extend (cairo_get_source (obj->datum.cr),
4785                                   extend);
4786         break;
4787     case CSI_OBJECT_TYPE_PATTERN:
4788         cairo_pattern_set_extend (obj->datum.pattern, extend);
4789         break;
4790     default:
4791         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4792     }
4793
4794     pop (1);
4795     return CSI_STATUS_SUCCESS;
4796 }
4797
4798 static csi_status_t
4799 _set_fallback_resolution (csi_t *ctx)
4800 {
4801     csi_status_t status;
4802     cairo_surface_t *surface;
4803     double dpi_x, dpi_y;
4804
4805     check (3);
4806
4807     status = _csi_ostack_get_number (ctx, 0, &dpi_y);
4808     if (_csi_unlikely (status))
4809         return status;
4810     status = _csi_ostack_get_number (ctx, 1, &dpi_x);
4811     if (_csi_unlikely (status))
4812         return status;
4813     status = _csi_ostack_get_surface (ctx, 2, &surface);
4814     if (_csi_unlikely (status))
4815         return status;
4816
4817     cairo_surface_set_fallback_resolution (surface, dpi_x, dpi_y);
4818     pop (2);
4819     return CSI_STATUS_SUCCESS;
4820 }
4821
4822 static csi_status_t
4823 _set_fill_rule (csi_t *ctx)
4824 {
4825     csi_status_t status;
4826     cairo_t *cr;
4827     long fill_rule;
4828
4829     check (2);
4830
4831     status = _csi_ostack_get_integer (ctx, 0, &fill_rule);
4832     if (_csi_unlikely (status))
4833         return status;
4834     status = _csi_ostack_get_context (ctx, 1, &cr);
4835     if (_csi_unlikely (status))
4836         return status;
4837
4838     cairo_set_fill_rule (cr, fill_rule);
4839     pop (1);
4840     return CSI_STATUS_SUCCESS;
4841 }
4842
4843 static csi_status_t
4844 _set_filter (csi_t *ctx)
4845 {
4846     csi_status_t status;
4847     csi_object_t *obj;
4848     long filter;
4849     int type;
4850
4851     check (2);
4852
4853     status = _csi_ostack_get_integer (ctx, 0, &filter);
4854     if (_csi_unlikely (status))
4855         return status;
4856
4857     obj = _csi_peek_ostack (ctx, 1);
4858     type = csi_object_get_type (obj);
4859     switch (type) {
4860     case CSI_OBJECT_TYPE_CONTEXT:
4861         cairo_pattern_set_filter (cairo_get_source (obj->datum.cr),
4862                                   filter);
4863         break;
4864     case CSI_OBJECT_TYPE_PATTERN:
4865         cairo_pattern_set_filter (obj->datum.pattern, filter);
4866         break;
4867     default:
4868         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
4869     }
4870
4871     pop (1);
4872     return CSI_STATUS_SUCCESS;
4873 }
4874
4875 static csi_status_t
4876 _set_font_face (csi_t *ctx)
4877 {
4878     cairo_t *cr;
4879     cairo_font_face_t *font = NULL; /* silence the compiler */
4880     csi_status_t status;
4881
4882     check (2);
4883
4884     status = _csi_ostack_get_font_face (ctx, 0, &font);
4885     if (_csi_unlikely (status))
4886         return status;
4887     status = _csi_ostack_get_context (ctx, 1, &cr);
4888     if (_csi_unlikely (status))
4889         return status;
4890
4891     cairo_set_font_face (cr, font);
4892     pop (1);
4893     return CSI_STATUS_SUCCESS;
4894 }
4895
4896 static csi_status_t
4897 _set_font_options (csi_t *ctx)
4898 {
4899     csi_status_t status;
4900     cairo_t *cr;
4901     csi_dictionary_t *dict;
4902     cairo_font_options_t *options;
4903
4904     check (2);
4905
4906     status = _csi_ostack_get_dictionary (ctx, 0, &dict);
4907     if (_csi_unlikely (status))
4908         return status;
4909     status = _csi_ostack_get_context (ctx, 1, &cr);
4910     if (_csi_unlikely (status))
4911         return status;
4912
4913     options = cairo_font_options_create ();
4914     status = _font_options_load_from_dictionary (ctx, dict, options);
4915     if (_csi_unlikely (status))
4916         return status;
4917
4918     cairo_set_font_options (cr, options);
4919     cairo_font_options_destroy (options);
4920     pop (1);
4921     return CSI_STATUS_SUCCESS;
4922 }
4923
4924 static csi_status_t
4925 _set_font_matrix (csi_t *ctx)
4926 {
4927     csi_status_t status;
4928     cairo_t *cr;
4929     cairo_matrix_t m;
4930
4931     check (2);
4932
4933     status = _csi_ostack_get_matrix (ctx, 0, &m);
4934     if (_csi_unlikely (status))
4935         return status;
4936     status = _csi_ostack_get_context (ctx, 1, &cr);
4937     if (_csi_unlikely (status))
4938         return status;
4939
4940     cairo_set_font_matrix (cr, &m);
4941     pop(1);
4942     return CSI_STATUS_SUCCESS;
4943 }
4944
4945 static csi_status_t
4946 _set_font_size (csi_t *ctx)
4947 {
4948     csi_status_t status;
4949     cairo_t *cr;
4950     double size;
4951
4952     check (2);
4953
4954     status = _csi_ostack_get_number (ctx, 0, &size);
4955     if (_csi_unlikely (status))
4956         return status;
4957     status = _csi_ostack_get_context (ctx, 1, &cr);
4958     if (_csi_unlikely (status))
4959         return status;
4960
4961     cairo_set_font_size (cr, size);
4962     pop (1);
4963     return CSI_STATUS_SUCCESS;
4964 }
4965
4966 static csi_status_t
4967 _set_line_cap (csi_t *ctx)
4968 {
4969     csi_status_t status;
4970     cairo_t *cr;
4971     long line_cap;
4972
4973     check (2);
4974
4975     status = _csi_ostack_get_integer (ctx, 0, &line_cap);
4976     if (_csi_unlikely (status))
4977         return status;
4978     status = _csi_ostack_get_context (ctx, 1, &cr);
4979     if (_csi_unlikely (status))
4980         return status;
4981
4982     cairo_set_line_cap (cr, line_cap);
4983     pop (1);
4984     return CSI_STATUS_SUCCESS;
4985 }
4986
4987 static csi_status_t
4988 _set_line_join (csi_t *ctx)
4989 {
4990     csi_status_t status;
4991     cairo_t *cr;
4992     long line_join;
4993
4994     status = _csi_ostack_get_integer (ctx, 0, &line_join);
4995     if (_csi_unlikely (status))
4996         return status;
4997     status = _csi_ostack_get_context (ctx, 1, &cr);
4998     if (_csi_unlikely (status))
4999         return status;
5000
5001     cairo_set_line_join (cr, line_join);
5002     pop (1);
5003     return CSI_STATUS_SUCCESS;
5004 }
5005
5006 static csi_status_t
5007 _set_line_width (csi_t *ctx)
5008 {
5009     csi_status_t status;
5010     cairo_t *cr;
5011     double line_width;
5012
5013     check (2);
5014
5015     status = _csi_ostack_get_number (ctx, 0, &line_width);
5016     if (_csi_unlikely (status))
5017         return status;
5018     status = _csi_ostack_get_context (ctx, 1, &cr);
5019     if (_csi_unlikely (status))
5020         return status;
5021
5022     cairo_set_line_width (cr, line_width);
5023     pop (1);
5024     return CSI_STATUS_SUCCESS;
5025 }
5026
5027 static csi_status_t
5028 _set_matrix (csi_t *ctx)
5029 {
5030     csi_object_t *obj;
5031     csi_status_t status;
5032     cairo_matrix_t m;
5033     int type;
5034
5035     check (2);
5036
5037     status = _csi_ostack_get_matrix (ctx, 0, &m);
5038     if (_csi_unlikely (status))
5039         return status;
5040
5041     obj = _csi_peek_ostack (ctx, 1);
5042     type = csi_object_get_type (obj);
5043     switch (type) {
5044     case CSI_OBJECT_TYPE_CONTEXT:
5045         cairo_set_matrix (obj->datum.cr, &m);
5046         break;
5047     case CSI_OBJECT_TYPE_PATTERN:
5048         cairo_pattern_set_matrix (obj->datum.pattern, &m);
5049         break;
5050     case CSI_OBJECT_TYPE_MATRIX:
5051         obj->datum.matrix->matrix = m;
5052         break;
5053     default:
5054         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
5055     }
5056
5057     pop (1);
5058     return CSI_STATUS_SUCCESS;
5059 }
5060
5061 struct _mime_tag {
5062     csi_t *ctx;
5063     csi_string_t *source;
5064 };
5065 static void
5066 _mime_tag_destroy (void *closure)
5067 {
5068     struct _mime_tag *tag = closure;
5069
5070     if (--tag->source->base.ref)
5071         csi_string_free (tag->ctx, tag->source);
5072
5073     _csi_slab_free (tag->ctx, tag, sizeof (struct _mime_tag));
5074 }
5075
5076 static csi_status_t
5077 _set_mime_data (csi_t *ctx)
5078 {
5079     csi_status_t status;
5080     csi_object_t *obj;
5081     const char *mime = NULL; /* silence the compiler */
5082     csi_object_t source;
5083     cairo_surface_t *surface;
5084     struct _mime_tag *tag;
5085     int type;
5086
5087     check (3);
5088
5089     obj = _csi_peek_ostack (ctx, 0);
5090     type = csi_object_get_type (obj);
5091     switch (type) {
5092     case CSI_OBJECT_TYPE_FILE:
5093         status = _csi_file_as_string (ctx, obj->datum.file, &source);
5094         if (_csi_unlikely (status))
5095             return status;
5096
5097         break;
5098
5099     case CSI_OBJECT_TYPE_STRING:
5100         source = *csi_object_reference (obj);
5101         break;
5102
5103     default:
5104         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
5105     }
5106
5107     status = _csi_ostack_get_string_constant (ctx, 1, &mime);
5108     if (_csi_unlikely (status))
5109         return status;
5110
5111     status = _csi_ostack_get_surface (ctx, 2, &surface);
5112     if (_csi_unlikely (status))
5113         return status;
5114
5115
5116     /* XXX free source */
5117     tag = _csi_slab_alloc (ctx, sizeof (struct _mime_tag));
5118     if (_csi_unlikely (tag == NULL))
5119         return _csi_error (CSI_STATUS_NO_MEMORY);
5120     tag->ctx = cairo_script_interpreter_reference (ctx);
5121     tag->source = source.datum.string;
5122     tag->source->base.ref++;
5123
5124     status = cairo_surface_set_mime_data (surface,
5125                                           mime,
5126                                           (uint8_t *)
5127                                           source.datum.string->string,
5128                                           source.datum.string->len,
5129                                           _mime_tag_destroy, tag);
5130     if (_csi_unlikely (status)) {
5131         _mime_tag_destroy (tag);
5132         return status;
5133     }
5134
5135     pop (2);
5136     return CSI_STATUS_SUCCESS;
5137 }
5138
5139 static csi_status_t
5140 _set_miter_limit (csi_t *ctx)
5141 {
5142     csi_status_t status;
5143     cairo_t *cr;
5144     double miter_limit;
5145
5146     check (2);
5147
5148     status = _csi_ostack_get_number (ctx, 0, &miter_limit);
5149     if (_csi_unlikely (status))
5150         return status;
5151     status = _csi_ostack_get_context (ctx, 1, &cr);
5152     if (_csi_unlikely (status))
5153         return status;
5154
5155     cairo_set_miter_limit (cr, miter_limit);
5156     pop (1);
5157     return CSI_STATUS_SUCCESS;
5158 }
5159
5160 static csi_status_t
5161 _set_operator (csi_t *ctx)
5162 {
5163     cairo_t *cr;
5164     long val;
5165     csi_status_t status;
5166
5167     check (2);
5168
5169     status = _csi_ostack_get_integer (ctx, 0, &val);
5170     if (_csi_unlikely (status))
5171         return status;
5172     status = _csi_ostack_get_context (ctx, 1, &cr);
5173     if (_csi_unlikely (status))
5174         return status;
5175
5176     cairo_set_operator (cr, val);
5177     pop (1);
5178     return CSI_STATUS_SUCCESS;
5179 }
5180
5181 static csi_status_t
5182 _set_scaled_font (csi_t *ctx)
5183 {
5184     cairo_t *cr;
5185     cairo_scaled_font_t *font = NULL; /* silence the compiler */
5186     csi_status_t status;
5187
5188     check (2);
5189
5190     status = _csi_ostack_get_scaled_font (ctx, 0, &font);
5191     if (_csi_unlikely (status))
5192         return status;
5193     status = _csi_ostack_get_context (ctx, 1, &cr);
5194     if (_csi_unlikely (status))
5195         return status;
5196
5197     cairo_set_scaled_font (cr, font);
5198     pop (1);
5199     return CSI_STATUS_SUCCESS;
5200 }
5201
5202 static csi_status_t
5203 _set_source (csi_t *ctx)
5204 {
5205     cairo_t *cr;
5206     cairo_pattern_t *pattern = NULL; /* silence the compiler */
5207     csi_status_t status;
5208
5209     check (2);
5210
5211     status = _csi_ostack_get_pattern (ctx, 0, &pattern);
5212     if (_csi_unlikely (status))
5213         return status;
5214     status = _csi_ostack_get_context (ctx, 1, &cr);
5215     if (_csi_unlikely (status))
5216         return status;
5217
5218     cairo_set_source (cr, pattern);
5219     pop (1);
5220     return CSI_STATUS_SUCCESS;
5221 }
5222
5223 static csi_boolean_t
5224 _matching_images (cairo_surface_t *a, cairo_surface_t *b)
5225 {
5226     cairo_format_t format_a, format_b;
5227
5228     if (cairo_surface_get_type (a) != CAIRO_SURFACE_TYPE_IMAGE)
5229         return FALSE;
5230     if (cairo_surface_get_type (b) != CAIRO_SURFACE_TYPE_IMAGE)
5231         return FALSE;
5232
5233     if (cairo_image_surface_get_height (a) != cairo_image_surface_get_height (b))
5234         return FALSE;
5235
5236     if (cairo_image_surface_get_width (a) != cairo_image_surface_get_width (b))
5237         return FALSE;
5238
5239     format_a = cairo_image_surface_get_format (a);
5240     if (format_a == CAIRO_FORMAT_RGB24)
5241         format_a = CAIRO_FORMAT_ARGB32;
5242
5243     format_b = cairo_image_surface_get_format (b);
5244     if (format_b == CAIRO_FORMAT_RGB24)
5245         format_b = CAIRO_FORMAT_ARGB32;
5246
5247     if (format_a != format_b)
5248         return FALSE;
5249
5250     return TRUE;
5251 }
5252
5253 static csi_status_t
5254 _set_source_image (csi_t *ctx)
5255 {
5256     csi_status_t status;
5257     cairo_surface_t *surface;
5258     cairo_surface_t *source;
5259
5260     check (2);
5261
5262     status = _csi_ostack_get_surface (ctx, 0, &source);
5263     if (_csi_unlikely (status))
5264         return status;
5265     status = _csi_ostack_get_surface (ctx, 1, &surface);
5266     if (_csi_unlikely (status))
5267         return status;
5268
5269     /* Catch the most frequent use of simply uploading pixel data,
5270      * principally to remove the pixman ops from the profiles.
5271      */
5272     if (_csi_likely (_matching_images (surface, source))) {
5273         cairo_surface_flush (surface);
5274         memcpy (cairo_image_surface_get_data (surface),
5275                 cairo_image_surface_get_data (source),
5276                 cairo_image_surface_get_height (source) * cairo_image_surface_get_stride (source));
5277         cairo_surface_mark_dirty (surface);
5278     } else {
5279         cairo_t *cr;
5280
5281         cr = cairo_create (surface);
5282         cairo_set_source_surface (cr, source, 0, 0);
5283         cairo_paint (cr);
5284         cairo_destroy (cr);
5285     }
5286
5287     pop (1);
5288     return CSI_STATUS_SUCCESS;
5289 }
5290
5291 static csi_status_t
5292 _set_source_rgb (csi_t *ctx)
5293 {
5294     csi_status_t status;
5295     double r,g,b;
5296     cairo_t *cr;
5297
5298     check (4);
5299
5300     status = _csi_ostack_get_number (ctx, 0, &b);
5301     if (_csi_unlikely (status))
5302         return status;
5303     status = _csi_ostack_get_number (ctx, 1, &g);
5304     if (_csi_unlikely (status))
5305         return status;
5306     status = _csi_ostack_get_number (ctx, 2, &r);
5307     if (_csi_unlikely (status))
5308         return status;
5309     status = _csi_ostack_get_context (ctx, 3, &cr);
5310     if (_csi_unlikely (status))
5311         return status;
5312
5313     cairo_set_source_rgb (cr, r, g, b);
5314     pop (3);
5315     return CSI_STATUS_SUCCESS;
5316 }
5317
5318 static csi_status_t
5319 _set_source_rgba (csi_t *ctx)
5320 {
5321     csi_status_t status;
5322     double r,g,b,a;
5323     cairo_t *cr;
5324
5325     check (5);
5326
5327     status = _csi_ostack_get_number (ctx, 0, &a);
5328     if (_csi_unlikely (status))
5329         return status;
5330     status = _csi_ostack_get_number (ctx, 1, &b);
5331     if (_csi_unlikely (status))
5332         return status;
5333     status = _csi_ostack_get_number (ctx, 2, &g);
5334     if (_csi_unlikely (status))
5335         return status;
5336     status = _csi_ostack_get_number (ctx, 3, &r);
5337     if (_csi_unlikely (status))
5338         return status;
5339     status = _csi_ostack_get_context (ctx, 4, &cr);
5340     if (_csi_unlikely (status))
5341         return status;
5342
5343     cairo_set_source_rgba (cr, r, g, b, a);
5344     pop (4);
5345     return CSI_STATUS_SUCCESS;
5346 }
5347
5348 static csi_status_t
5349 _set_tolerance (csi_t *ctx)
5350 {
5351     csi_status_t status;
5352     cairo_t *cr;
5353     double tolerance;
5354
5355     check (2);
5356
5357     status = _csi_ostack_get_number (ctx, 0, &tolerance);
5358     if (_csi_unlikely (status))
5359         return status;
5360     status = _csi_ostack_get_context (ctx, 1, &cr);
5361     if (_csi_unlikely (status))
5362         return status;
5363
5364     cairo_set_tolerance (cr, tolerance);
5365     pop (1);
5366     return CSI_STATUS_SUCCESS;
5367 }
5368
5369 static csi_status_t
5370 _transform (csi_t *ctx)
5371 {
5372     csi_object_t *obj;
5373     csi_status_t status;
5374     cairo_matrix_t m;
5375     int type;
5376
5377     check (2);
5378
5379     status = _csi_ostack_get_matrix (ctx, 0, &m);
5380     if (_csi_unlikely (status))
5381         return status;
5382
5383     obj = _csi_peek_ostack (ctx, 1);
5384     type = csi_object_get_type (obj);
5385     switch (type) {
5386     case CSI_OBJECT_TYPE_CONTEXT:
5387         cairo_transform (obj->datum.cr, &m);
5388         break;
5389     case CSI_OBJECT_TYPE_PATTERN:
5390         {
5391             cairo_matrix_t ctm;
5392             cairo_pattern_get_matrix (obj->datum.pattern, &ctm);
5393             cairo_matrix_multiply (&ctm, &m, &ctm);
5394             cairo_pattern_set_matrix (obj->datum.pattern, &ctm);
5395         }
5396         break;
5397     case CSI_OBJECT_TYPE_MATRIX:
5398             cairo_matrix_multiply (&obj->datum.matrix->matrix,
5399                                    &m,
5400                                    &obj->datum.matrix->matrix);
5401         break;
5402     default:
5403         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
5404     }
5405
5406     pop (1);
5407     return CSI_STATUS_SUCCESS;
5408 }
5409
5410 static csi_status_t
5411 _translate (csi_t *ctx)
5412 {
5413     csi_object_t *obj;
5414     csi_status_t status;
5415     double x, y;
5416     int type;
5417
5418     check (3);
5419
5420     status = _csi_ostack_get_number (ctx, 0, &y);
5421     if (_csi_unlikely (status))
5422         return status;
5423     status = _csi_ostack_get_number (ctx, 1, &x);
5424     if (_csi_unlikely (status))
5425         return status;
5426
5427     obj = _csi_peek_ostack (ctx, 2);
5428     type = csi_object_get_type (obj);
5429     switch (type) {
5430     case CSI_OBJECT_TYPE_CONTEXT:
5431         cairo_translate (obj->datum.cr, x, y);
5432         break;
5433
5434     case CSI_OBJECT_TYPE_PATTERN:
5435         {
5436             cairo_matrix_t ctm;
5437             cairo_pattern_get_matrix (obj->datum.pattern, &ctm);
5438             cairo_matrix_translate (&ctm, x, y);
5439             cairo_pattern_set_matrix (obj->datum.pattern, &ctm);
5440         }
5441         break;
5442
5443
5444     case CSI_OBJECT_TYPE_MATRIX:
5445         cairo_matrix_translate (&obj->datum.matrix->matrix, x, y);
5446         break;
5447
5448     default:
5449         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
5450     }
5451
5452     pop (2);
5453     return CSI_STATUS_SUCCESS;
5454 }
5455
5456 static csi_status_t
5457 _true (csi_t *ctx)
5458 {
5459     return _csi_push_ostack_boolean (ctx, TRUE);
5460 }
5461
5462 static csi_status_t
5463 _show_page (csi_t *ctx)
5464 {
5465     csi_object_t *obj;
5466     int type;
5467
5468     check (1);
5469
5470     obj = _csi_peek_ostack (ctx, 0);
5471     type = csi_object_get_type (obj);
5472     switch (type) {
5473     case CSI_OBJECT_TYPE_CONTEXT:
5474         cairo_show_page (obj->datum.cr);
5475         if (ctx->hooks.copy_page != NULL)
5476             ctx->hooks.copy_page (ctx->hooks.closure, obj->datum.cr);
5477         break;
5478     case CSI_OBJECT_TYPE_SURFACE:
5479         cairo_surface_show_page (obj->datum.surface);
5480         /* XXX hook? */
5481         break;
5482     default:
5483         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
5484     }
5485
5486     return CSI_STATUS_SUCCESS;
5487 }
5488
5489 static csi_status_t
5490 _similar (csi_t *ctx)
5491 {
5492     csi_object_t obj;
5493     long content;
5494     double width, height;
5495     cairo_surface_t *other;
5496     csi_status_t status;
5497
5498     check (4);
5499
5500     status = _csi_ostack_get_integer (ctx, 0, &content);
5501     if (_csi_unlikely (status))
5502         return status;
5503     status = _csi_ostack_get_number (ctx, 1, &height);
5504     if (_csi_unlikely (status))
5505         return status;
5506     status = _csi_ostack_get_number (ctx, 2, &width);
5507     if (_csi_unlikely (status))
5508         return status;
5509     status = _csi_ostack_get_surface (ctx, 3, &other);
5510     if (_csi_unlikely (status))
5511         return status;
5512
5513     /* silently fix-up a common bug when writing CS */
5514     if ((content & CAIRO_CONTENT_COLOR_ALPHA) == 0) {
5515         if (_csi_unlikely (content & ~CAIRO_CONTENT_COLOR_ALPHA))
5516             return _csi_error (CSI_STATUS_INVALID_SCRIPT);
5517
5518         switch ((int) content) {
5519         default:
5520         case CAIRO_FORMAT_ARGB32:
5521             content = CAIRO_CONTENT_COLOR_ALPHA;
5522             break;
5523         case CAIRO_FORMAT_RGB16_565:
5524         case CAIRO_FORMAT_RGB24:
5525             content = CAIRO_CONTENT_COLOR;
5526             break;
5527         case CAIRO_FORMAT_A8:
5528         case CAIRO_FORMAT_A1:
5529             content = CAIRO_CONTENT_ALPHA;
5530             break;
5531         }
5532     }
5533
5534     obj.type = CSI_OBJECT_TYPE_SURFACE;
5535     obj.datum.surface = cairo_surface_create_similar (other,
5536                                                       content, width, height);
5537     pop (4);
5538     return push (&obj);
5539 }
5540
5541 static csi_status_t
5542 _similar_image (csi_t *ctx)
5543 {
5544     csi_object_t obj;
5545     long format;
5546     double width, height;
5547     cairo_surface_t *other;
5548     csi_status_t status;
5549
5550     check (4);
5551
5552     status = _csi_ostack_get_number (ctx, 0, &height);
5553     if (_csi_unlikely (status))
5554         return status;
5555     status = _csi_ostack_get_number (ctx, 1, &width);
5556     if (_csi_unlikely (status))
5557         return status;
5558     status = _csi_ostack_get_integer (ctx, 2, &format);
5559     if (_csi_unlikely (status))
5560         return status;
5561     status = _csi_ostack_get_surface (ctx, 3, &other);
5562     if (_csi_unlikely (status))
5563         return status;
5564
5565     obj.type = CSI_OBJECT_TYPE_SURFACE;
5566     obj.datum.surface = cairo_surface_create_similar_image (other,
5567                                                             format,
5568                                                             width, height);
5569     pop (4);
5570     return push (&obj);
5571 }
5572
5573 static csi_status_t
5574 _subsurface (csi_t *ctx)
5575 {
5576     csi_object_t obj;
5577     double x, y, width, height;
5578     cairo_surface_t *target;
5579     csi_status_t status;
5580
5581     check (5);
5582
5583     status = _csi_ostack_get_number (ctx, 0, &height);
5584     if (_csi_unlikely (status))
5585         return status;
5586     status = _csi_ostack_get_number (ctx, 1, &width);
5587     if (_csi_unlikely (status))
5588         return status;
5589     status = _csi_ostack_get_number (ctx, 2, &y);
5590     if (_csi_unlikely (status))
5591         return status;
5592     status = _csi_ostack_get_number (ctx, 3, &x);
5593     if (_csi_unlikely (status))
5594         return status;
5595     status = _csi_ostack_get_surface (ctx, 4, &target);
5596     if (_csi_unlikely (status))
5597         return status;
5598
5599     obj.type = CSI_OBJECT_TYPE_SURFACE;
5600     obj.datum.surface = cairo_surface_create_for_rectangle (target, x, y, width, height);
5601     pop (5);
5602     return push (&obj);
5603 }
5604
5605 static csi_status_t
5606 _show_text (csi_t *ctx)
5607 {
5608     csi_status_t status;
5609     csi_string_t *text;
5610     cairo_t *cr;
5611
5612     check (2);
5613
5614     status = _csi_ostack_get_string (ctx, 0, &text);
5615     if (_csi_unlikely (status))
5616         return status;
5617     status = _csi_ostack_get_context (ctx, 1, &cr);
5618     if (_csi_unlikely (status))
5619         return status;
5620
5621     cairo_show_text (cr, text->string);
5622     pop (1);
5623     return CSI_STATUS_SUCCESS;
5624 }
5625
5626 static csi_status_t
5627 _show_glyphs (csi_t *ctx)
5628 {
5629     csi_array_t *array;
5630     csi_status_t status;
5631     cairo_t *cr;
5632     cairo_glyph_t stack_glyphs[256], *glyphs;
5633     csi_integer_t nglyphs, i;
5634
5635     check (2);
5636
5637     status = _csi_ostack_get_array (ctx, 0, &array);
5638     if (_csi_unlikely (status))
5639         return status;
5640     status = _csi_ostack_get_context (ctx, 1, &cr);
5641     if (_csi_unlikely (status))
5642         return status;
5643
5644     /* count glyphs */
5645     nglyphs = 0;
5646     for (i = 0; i < array->stack.len; i++) {
5647         csi_object_t *obj = &array->stack.objects[i];
5648         int type = csi_object_get_type (obj);
5649         switch (type) {
5650         case CSI_OBJECT_TYPE_ARRAY:
5651             nglyphs += obj->datum.array->stack.len;
5652             break;
5653         case CSI_OBJECT_TYPE_STRING:
5654             nglyphs += obj->datum.string->len;
5655             break;
5656         }
5657     }
5658     if (nglyphs == 0) {
5659         pop (1);
5660         return CSI_STATUS_SUCCESS;
5661     }
5662
5663     if (nglyphs > ARRAY_LENGTH (stack_glyphs)) {
5664         if (_csi_unlikely ((unsigned) nglyphs >= INT_MAX / sizeof (cairo_glyph_t)))
5665             return _csi_error (CSI_STATUS_NO_MEMORY);
5666
5667         glyphs = _csi_alloc (ctx, sizeof (cairo_glyph_t) * nglyphs);
5668         if (_csi_unlikely (glyphs == NULL))
5669             return _csi_error (CSI_STATUS_NO_MEMORY);
5670     } else
5671         glyphs = stack_glyphs;
5672
5673     nglyphs = _glyph_string (ctx, array, cairo_get_scaled_font (cr), glyphs);
5674     cairo_show_glyphs (cr, glyphs, nglyphs);
5675
5676     if (glyphs != stack_glyphs)
5677         _csi_free (ctx, glyphs);
5678
5679     pop (1);
5680     return CSI_STATUS_SUCCESS;
5681 }
5682
5683 static csi_status_t
5684 _show_text_glyphs (csi_t *ctx)
5685 {
5686     csi_object_t *obj;
5687     csi_array_t *array;
5688     csi_string_t *string;
5689     csi_string_t *utf8_string;
5690     csi_status_t status;
5691     cairo_t *cr;
5692     cairo_text_cluster_t stack_clusters[256], *clusters;
5693     cairo_glyph_t stack_glyphs[256], *glyphs;
5694     csi_integer_t nglyphs, nclusters, i;
5695     long direction;
5696     int type;
5697
5698     check (5);
5699
5700     status = _csi_ostack_get_integer (ctx, 0, &direction);
5701     if (_csi_unlikely (status))
5702         return status;
5703
5704     obj = _csi_peek_ostack (ctx, 1);
5705     type = csi_object_get_type (obj);
5706     switch (type) {
5707     case CSI_OBJECT_TYPE_ARRAY:
5708         array = obj->datum.array;
5709         nclusters = array->stack.len / 2;
5710         if (nclusters > ARRAY_LENGTH (stack_clusters)) {
5711             if (_csi_unlikely ((unsigned) nclusters >= INT_MAX / sizeof (cairo_text_cluster_t)))
5712                 return _csi_error (CSI_STATUS_NO_MEMORY);
5713             clusters = _csi_alloc (ctx, sizeof (cairo_text_cluster_t) * nclusters);
5714             if (_csi_unlikely (clusters == NULL))
5715                 return _csi_error (CSI_STATUS_NO_MEMORY);
5716         } else
5717             clusters = stack_clusters;
5718
5719         for (i = 0; i < nclusters; i++) {
5720             clusters[i].num_bytes = csi_number_get_value (&array->stack.objects[2*i+0]);
5721             clusters[i].num_glyphs = csi_number_get_value (&array->stack.objects[2*i+1]);
5722         }
5723         break;
5724
5725     case CSI_OBJECT_TYPE_STRING:
5726         string = obj->datum.string;
5727         nclusters = string->len / 2;
5728         if (nclusters > ARRAY_LENGTH (stack_clusters)) {
5729             if (_csi_unlikely ((unsigned) nclusters >= INT_MAX / sizeof (cairo_text_cluster_t)))
5730                 return _csi_error (CSI_STATUS_NO_MEMORY);
5731             clusters = _csi_alloc (ctx, sizeof (cairo_text_cluster_t) * nclusters);
5732             if (_csi_unlikely (clusters == NULL))
5733                 return _csi_error (CSI_STATUS_NO_MEMORY);
5734         } else
5735             clusters = stack_clusters;
5736
5737         for (i = 0; i < nclusters; i++) {
5738             clusters[i].num_bytes = string->string[2*i+0];
5739             clusters[i].num_glyphs = string->string[2*i+1];
5740         }
5741         break;
5742
5743     default:
5744         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
5745     }
5746
5747     status = _csi_ostack_get_array (ctx, 2, &array);
5748     if (_csi_unlikely (status))
5749         return status;
5750     status = _csi_ostack_get_string (ctx, 3, &utf8_string);
5751     if (_csi_unlikely (status))
5752         return status;
5753     status = _csi_ostack_get_context (ctx, 4, &cr);
5754     if (_csi_unlikely (status))
5755         return status;
5756
5757     /* count glyphs */
5758     nglyphs = 0;
5759     for (i = 0; i < array->stack.len; i++) {
5760         obj = &array->stack.objects[i];
5761         type = csi_object_get_type (obj);
5762         switch (type) {
5763         case CSI_OBJECT_TYPE_ARRAY:
5764             nglyphs += obj->datum.array->stack.len;
5765             break;
5766         case CSI_OBJECT_TYPE_STRING:
5767             nglyphs += obj->datum.string->len;
5768             break;
5769         }
5770     }
5771     if (nglyphs == 0) {
5772         pop (4);
5773         return CSI_STATUS_SUCCESS;
5774     }
5775
5776     if (nglyphs > ARRAY_LENGTH (stack_glyphs)) {
5777         if (_csi_unlikely ((unsigned) nglyphs >= INT_MAX / sizeof (cairo_glyph_t)))
5778             return _csi_error (CSI_STATUS_NO_MEMORY);
5779
5780         glyphs = _csi_alloc (ctx, sizeof (cairo_glyph_t) * nglyphs);
5781         if (_csi_unlikely (glyphs == NULL))
5782             return _csi_error (CSI_STATUS_NO_MEMORY);
5783     } else
5784         glyphs = stack_glyphs;
5785
5786     nglyphs = _glyph_string (ctx, array, cairo_get_scaled_font (cr), glyphs);
5787     cairo_show_text_glyphs (cr,
5788                             utf8_string->string, utf8_string->len,
5789                             glyphs, nglyphs,
5790                             clusters, nclusters,
5791                             direction);
5792
5793     if (clusters != stack_clusters)
5794         _csi_free (ctx, clusters);
5795     if (glyphs != stack_glyphs)
5796         _csi_free (ctx, glyphs);
5797
5798     pop (4);
5799     return CSI_STATUS_SUCCESS;
5800 }
5801
5802 static csi_status_t
5803 _stroke (csi_t *ctx)
5804 {
5805     return _do_cairo_op (ctx, cairo_stroke);
5806 }
5807
5808 static csi_status_t
5809 _stroke_preserve (csi_t *ctx)
5810 {
5811     return _do_cairo_op (ctx, cairo_stroke_preserve);
5812 }
5813
5814 static csi_status_t
5815 _sub (csi_t *ctx)
5816 {
5817     csi_object_t *A;
5818     csi_object_t *B;
5819     csi_object_type_t type_a, type_b;
5820
5821     check (2);
5822
5823     B = _csi_peek_ostack (ctx, 0);
5824     A = _csi_peek_ostack (ctx, 1);
5825
5826     type_a = csi_object_get_type (A);
5827     if (_csi_unlikely (! (type_a == CSI_OBJECT_TYPE_INTEGER ||
5828                             type_a == CSI_OBJECT_TYPE_REAL)))
5829     {
5830         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
5831     }
5832
5833     type_b = csi_object_get_type (B);
5834     if (_csi_unlikely (! (type_b == CSI_OBJECT_TYPE_INTEGER ||
5835                             type_b == CSI_OBJECT_TYPE_REAL)))
5836     {
5837         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
5838     }
5839
5840     pop (2);
5841
5842     if (type_a == CSI_OBJECT_TYPE_REAL &&
5843         type_b == CSI_OBJECT_TYPE_REAL)
5844     {
5845         return _csi_push_ostack_real (ctx, A->datum.real - B->datum.real);
5846
5847     }
5848     else if (type_a == CSI_OBJECT_TYPE_INTEGER &&
5849              type_b == CSI_OBJECT_TYPE_INTEGER)
5850     {
5851         return _csi_push_ostack_integer (ctx,
5852                                          A->datum.integer - B->datum.integer);
5853     }
5854     else
5855     {
5856         double v;
5857
5858         if (type_a == CSI_OBJECT_TYPE_REAL)
5859             v = A->datum.real;
5860         else
5861             v = A->datum.integer;
5862
5863         if (type_b == CSI_OBJECT_TYPE_REAL)
5864             v -= B->datum.real;
5865         else
5866             v -= B->datum.integer;
5867
5868         return _csi_push_ostack_real (ctx, v);
5869     }
5870 }
5871
5872 static csi_status_t
5873 _surface (csi_t *ctx)
5874 {
5875     csi_object_t obj;
5876     csi_dictionary_t *dict;
5877     csi_proxy_t *proxy;
5878     csi_object_t key;
5879     double width, height;
5880     csi_surface_create_func_t hook;
5881     long content;
5882     cairo_surface_t *surface;
5883     long uid;
5884     csi_status_t status;
5885
5886     check (1);
5887
5888     status = _csi_ostack_get_dictionary (ctx, 0, &dict);
5889     if (_csi_unlikely (status))
5890         return status;
5891
5892     status = _csi_dictionary_get_number (ctx, dict, "width", FALSE, &width);
5893     if (_csi_unlikely (status))
5894         return status;
5895     status = _csi_dictionary_get_number (ctx, dict, "height", FALSE, &height);
5896     if (_csi_unlikely (status))
5897         return status;
5898
5899     content = CAIRO_CONTENT_COLOR_ALPHA;
5900     status = _csi_dictionary_get_integer (ctx, dict, "content", TRUE, &content);
5901     if (_csi_unlikely (status))
5902         return status;
5903
5904     uid = 0;
5905     status = _csi_dictionary_get_integer (ctx, dict, "uid", TRUE, &uid);
5906     if (_csi_unlikely (status))
5907         return status;
5908     if (uid == 0) {
5909         status = _csi_dictionary_get_integer (ctx, dict, "drawable", TRUE, &uid);
5910         if (_csi_unlikely (status))
5911             return status;
5912     }
5913
5914     hook = ctx->hooks.surface_create;
5915     assert (hook != NULL);
5916
5917     surface = hook (ctx->hooks.closure, content, width, height, uid);
5918     if (_csi_unlikely (surface == NULL)) {
5919         return _csi_error (CSI_STATUS_NULL_POINTER);
5920     }
5921
5922     proxy = _csi_proxy_create (ctx, surface, dict,
5923                                ctx->hooks.surface_destroy,
5924                                ctx->hooks.closure);
5925     if (_csi_unlikely (proxy == NULL)) {
5926         cairo_surface_destroy (surface);
5927         return _csi_error (CSI_STATUS_NO_MEMORY);
5928     }
5929
5930     status = cairo_surface_set_user_data (surface,
5931                                           &_csi_proxy_key,
5932                                           proxy, _csi_proxy_destroy);
5933     if (_csi_unlikely (status)) {
5934         _csi_proxy_destroy (proxy);
5935         cairo_surface_destroy (surface);
5936         return status;
5937     }
5938
5939     status = csi_name_new_static (ctx, &key, "fallback-resolution");
5940     if (_csi_unlikely (status)) {
5941         cairo_surface_destroy (surface);
5942         return status;
5943     }
5944     if (csi_dictionary_has (dict, key.datum.name)) {
5945         status = csi_dictionary_get (ctx, dict, key.datum.name, &obj);
5946         if (_csi_unlikely (status)) {
5947             cairo_surface_destroy (surface);
5948             return status;
5949         }
5950         if (csi_object_get_type (&obj) == CSI_OBJECT_TYPE_ARRAY) {
5951             csi_array_t *array = obj.datum.array;
5952             if (array->stack.len == 2) {
5953                 cairo_surface_set_fallback_resolution (surface,
5954                                                        csi_number_get_value
5955                                                        (&array->stack.objects[0]),
5956                                                        csi_number_get_value
5957                                                        (&array->stack.objects[1]));
5958             }
5959         }
5960     }
5961     /* initialise surface to source */
5962     status = csi_name_new_static (ctx, &key, "source");
5963     if (_csi_unlikely (status)) {
5964         cairo_surface_destroy (surface);
5965         return status;
5966     }
5967     if (csi_dictionary_has (dict, key.datum.name)) {
5968         cairo_surface_t *image;
5969         cairo_t *cr;
5970
5971         status = _image_load_from_dictionary (ctx, dict, &image);
5972         if (_csi_unlikely (status)) {
5973             cairo_surface_destroy (surface);
5974             return status;
5975         }
5976
5977         cr = cairo_create (surface);
5978         cairo_set_source_surface (cr, image, 0, 0);
5979         cairo_surface_destroy (image);
5980         cairo_paint (cr);
5981         status = cairo_status (cr);
5982         cairo_destroy (cr);
5983
5984         if (_csi_unlikely (status))
5985             return status;
5986     }
5987
5988     status = csi_name_new_static (ctx, &key, "device-offset");
5989     if (_csi_unlikely (status)) {
5990         cairo_surface_destroy (surface);
5991         return status;
5992     }
5993     if (csi_dictionary_has (dict, key.datum.name)) {
5994         status = csi_dictionary_get (ctx, dict, key.datum.name, &obj);
5995         if (_csi_unlikely (status))
5996             return status;
5997
5998         if (csi_object_get_type (&obj) == CSI_OBJECT_TYPE_ARRAY) {
5999             csi_array_t *array = obj.datum.array;
6000
6001             if (array->stack.len == 2) {
6002                 cairo_surface_set_device_offset (surface,
6003                                                  csi_number_get_value
6004                                                  (&array->stack.objects[0]),
6005                                                  csi_number_get_value
6006                                                  (&array->stack.objects[1]));
6007             }
6008         }
6009     }
6010
6011     obj.type = CSI_OBJECT_TYPE_SURFACE;
6012     obj.datum.surface = surface;
6013     pop (1);
6014     return push (&obj);
6015 }
6016
6017 static csi_status_t
6018 _record (csi_t *ctx)
6019 {
6020     csi_object_t obj;
6021     long content;
6022     csi_array_t *array;
6023     csi_status_t status;
6024     cairo_rectangle_t extents;
6025     cairo_rectangle_t *r;
6026
6027     check (2);
6028
6029     status = _csi_ostack_get_array (ctx, 0, &array);
6030     if (_csi_unlikely (status))
6031         return status;
6032
6033     status = _csi_ostack_get_integer (ctx, 1, &content);
6034     if (_csi_unlikely (status))
6035         return status;
6036
6037     switch (array->stack.len) {
6038     case 0:
6039         r = NULL;
6040         break;
6041     case 2:
6042         extents.x = extents.y = 0;
6043         extents.width = _csi_object_as_real (&array->stack.objects[0]);
6044         extents.height = _csi_object_as_real (&array->stack.objects[1]);
6045         r = &extents;
6046         break;
6047     case 4:
6048         extents.x = _csi_object_as_real (&array->stack.objects[0]);
6049         extents.y = _csi_object_as_real (&array->stack.objects[1]);
6050         extents.width = _csi_object_as_real (&array->stack.objects[2]);
6051         extents.height = _csi_object_as_real (&array->stack.objects[3]);
6052         r = &extents;
6053         break;
6054     default:
6055         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
6056     }
6057
6058     obj.type = CSI_OBJECT_TYPE_SURFACE;
6059     obj.datum.surface = cairo_recording_surface_create (content, r);
6060     pop (2);
6061     return push (&obj);
6062 }
6063
6064 static csi_status_t
6065 _text_path (csi_t *ctx)
6066 {
6067     csi_status_t status;
6068     csi_string_t *text;
6069     cairo_t *cr;
6070
6071     check (2);
6072
6073     status = _csi_ostack_get_string (ctx, 0, &text);
6074     if (_csi_unlikely (status))
6075         return status;
6076     status = _csi_ostack_get_context (ctx, 1, &cr);
6077     if (_csi_unlikely (status))
6078         return status;
6079
6080     cairo_text_path (cr, text->string);
6081     pop (1);
6082     return CSI_STATUS_SUCCESS;
6083 }
6084
6085 static csi_status_t
6086 _undef (csi_t *ctx)
6087 {
6088     csi_name_t name = 0; /* silence the compiler */
6089     csi_status_t status;
6090
6091     check (1);
6092
6093     status = _csi_ostack_get_name (ctx, 0, &name);
6094     if (_csi_unlikely (status))
6095         return status;
6096
6097     status = _csi_name_undefine (ctx, name);
6098     if (_csi_unlikely (status))
6099         return status;
6100
6101     pop (1);
6102     return CSI_STATUS_SUCCESS;
6103 }
6104
6105 static csi_status_t
6106 _unset (csi_t *ctx)
6107 {
6108     csi_object_t *dst;
6109     csi_name_t name = 0; /* silence the compiler */
6110     csi_status_t status;
6111     int type;
6112
6113     check (2);
6114
6115     status = _csi_ostack_get_name (ctx, 0, &name);
6116     if (_csi_unlikely (status))
6117         return status;
6118
6119     dst = _csi_peek_ostack (ctx, 1);
6120     type = csi_object_get_type (dst);
6121     switch (type) {
6122     case CSI_OBJECT_TYPE_DICTIONARY:
6123         csi_dictionary_remove (ctx, dst->datum.dictionary, name);
6124         break;
6125     case CSI_OBJECT_TYPE_STRING:
6126     case CSI_OBJECT_TYPE_ARRAY:
6127     default:
6128         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
6129     }
6130
6131     pop (1);
6132     return CSI_STATUS_SUCCESS;
6133 }
6134
6135 static csi_status_t
6136 _write_to_png (csi_t *ctx)
6137 {
6138     csi_status_t status;
6139     csi_string_t *filename;
6140     cairo_surface_t *surface;
6141
6142     check (2);
6143
6144     status = _csi_ostack_get_string (ctx, 0, &filename);
6145     if (_csi_unlikely (status))
6146         return status;
6147     status = _csi_ostack_get_surface (ctx, 1, &surface);
6148     if (_csi_unlikely (status))
6149         return status;
6150
6151 #if CAIRO_HAS_PNG_FUNCTIONS
6152     status = cairo_surface_write_to_png (surface, filename->string);
6153     if (_csi_unlikely (status))
6154         return status;
6155 #else
6156     return CAIRO_STATUS_WRITE_ERROR;
6157 #endif
6158
6159     pop (1);
6160     return CSI_STATUS_SUCCESS;
6161 }
6162
6163 static csi_status_t
6164 _write_to_script (csi_t *ctx)
6165 {
6166     csi_status_t status;
6167     csi_string_t *filename;
6168     cairo_surface_t *record;
6169
6170     check (2);
6171
6172     status = _csi_ostack_get_string (ctx, 0, &filename);
6173     if (_csi_unlikely (status))
6174         return status;
6175     status = _csi_ostack_get_surface (ctx, 1, &record);
6176     if (_csi_unlikely (status))
6177         return status;
6178
6179     if (cairo_surface_get_type (record) != CAIRO_SURFACE_TYPE_RECORDING)
6180         return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
6181
6182 #if CAIRO_HAS_SCRIPT_SURFACE
6183     {
6184         cairo_device_t *script;
6185
6186         script = cairo_script_create (filename->string);
6187         status = cairo_script_from_recording_surface (script, record);
6188         cairo_device_destroy (script);
6189         if (_csi_unlikely (status))
6190             return status;
6191     }
6192 #else
6193     return CAIRO_STATUS_WRITE_ERROR;
6194 #endif
6195
6196     pop (1);
6197     return CSI_STATUS_SUCCESS;
6198 }
6199
6200 static csi_status_t
6201 _xor (csi_t *ctx)
6202 {
6203     csi_object_t *a, *b;
6204     int type;
6205
6206     check (2);
6207
6208     a = _csi_peek_ostack (ctx, 0);
6209     b = _csi_peek_ostack (ctx, 1);
6210     if (_csi_unlikely (csi_object_get_type (a) != csi_object_get_type (b)))
6211         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
6212
6213     pop (2);
6214     type = csi_object_get_type (a);
6215     switch (type) {
6216     case CSI_OBJECT_TYPE_INTEGER:
6217         return _csi_push_ostack_integer (ctx,
6218                                          a->datum.integer ^ b->datum.integer);
6219     case CSI_OBJECT_TYPE_BOOLEAN:
6220         return _csi_push_ostack_boolean (ctx,
6221                                          a->datum.boolean ^ b->datum.boolean);
6222     default:
6223         return _csi_error (CSI_STATUS_INVALID_SCRIPT);
6224     }
6225 }
6226
6227 static csi_status_t
6228 _debug_print (csi_t *ctx)
6229 {
6230     csi_object_t *obj;
6231
6232     check (1);
6233     obj = _csi_peek_ostack (ctx, 0);
6234     switch (csi_object_get_type (obj)) {
6235     case CSI_OBJECT_TYPE_NULL:
6236         fprintf (stderr, "NULL\n");
6237         break;
6238
6239         /* atomics */
6240     case CSI_OBJECT_TYPE_BOOLEAN:
6241         fprintf (stderr, "boolean: %s\n",
6242                  obj->datum.boolean ? "true" : "false");
6243         break;
6244     case CSI_OBJECT_TYPE_INTEGER:
6245         fprintf (stderr, "integer: %ld\n", obj->datum.integer);
6246         break;
6247     case CSI_OBJECT_TYPE_MARK:
6248         fprintf (stderr, "mark\n");
6249         break;
6250     case CSI_OBJECT_TYPE_NAME:
6251         fprintf (stderr, "name: %s\n", (char *) obj->datum.name);
6252         break;
6253     case CSI_OBJECT_TYPE_OPERATOR:
6254         fprintf (stderr, "operator: %p\n", obj->datum.ptr);
6255         break;
6256     case CSI_OBJECT_TYPE_REAL:
6257         fprintf (stderr, "real: %g\n", obj->datum.real);
6258         break;
6259
6260         /* compound */
6261     case CSI_OBJECT_TYPE_ARRAY:
6262         fprintf (stderr, "array\n");
6263         break;
6264     case CSI_OBJECT_TYPE_DICTIONARY:
6265         fprintf (stderr, "dictionary\n");
6266         break;
6267     case CSI_OBJECT_TYPE_FILE:
6268         fprintf (stderr, "file\n");
6269         break;
6270     case CSI_OBJECT_TYPE_MATRIX:
6271         fprintf (stderr, "matrix: [%g %g %g %g %g %g]\n",
6272                  obj->datum.matrix->matrix.xx,
6273                  obj->datum.matrix->matrix.yx,
6274                  obj->datum.matrix->matrix.xy,
6275                  obj->datum.matrix->matrix.yy,
6276                  obj->datum.matrix->matrix.x0,
6277                  obj->datum.matrix->matrix.y0);
6278         break;
6279     case CSI_OBJECT_TYPE_STRING:
6280         fprintf (stderr, "string: %s\n", obj->datum.string->string);
6281         break;
6282
6283         /* cairo */
6284     case CSI_OBJECT_TYPE_CONTEXT:
6285         fprintf (stderr, "context\n");
6286         break;
6287     case CSI_OBJECT_TYPE_FONT:
6288         fprintf (stderr, "font\n");
6289         break;
6290     case CSI_OBJECT_TYPE_PATTERN:
6291         fprintf (stderr, "pattern\n");
6292         break;
6293     case CSI_OBJECT_TYPE_SCALED_FONT:
6294         fprintf (stderr, "scaled-font\n");
6295         break;
6296     case CSI_OBJECT_TYPE_SURFACE:
6297         fprintf (stderr, "surface\n");
6298         break;
6299     }
6300     pop (1);
6301     return CSI_STATUS_SUCCESS;
6302 }
6303
6304 static const csi_operator_def_t
6305 _defs[] = {
6306     { "<<", _mark },
6307     { ">>", end_dict_construction },
6308     { "[", _mark },
6309     { "]", end_array_construction },
6310     { "a", _alpha },
6311     { "abs", NULL },
6312     { "add", _add },
6313     { "add-color-stop", _add_color_stop },
6314     { "and", _and },
6315     { "arc", _arc },
6316     { "arc-negative", _arc_negative },
6317     { "arc-", _arc_negative },
6318     { "arc-to", NULL },
6319     { "array", _array },
6320     { "astore", NULL },
6321     { "atan", NULL },
6322     { "bind", _bind },
6323     { "bitshift", _bitshift },
6324     { "c", _curve_to },
6325     { "C", _rel_curve_to },
6326     { "ceiling", NULL },
6327     { "clear", NULL },
6328     { "clear-to-mark", NULL },
6329     { "clip", _clip },
6330     { "clip-extents", NULL },
6331     { "clip-preserve", _clip_preserve },
6332     { "clip+", _clip_preserve },
6333     { "close-path", _close_path },
6334     { "context", _context },
6335     { "copy", _copy },
6336     { "copy-page", _copy_page },
6337     { "cos", NULL },
6338     { "count", NULL },
6339     { "count-to-mark", NULL },
6340     { "curve-to", _curve_to },
6341     { "cvi", _cvi },
6342     { "cvr", _cvr },
6343     { "def", _def },
6344     { "device-to-user", NULL },
6345     { "device-to-user-distance", NULL },
6346     { "dict", _dict },
6347     { "div", _div },
6348     { "dup", _duplicate },
6349     { "eq", _eq },
6350     { "exch", _exch },
6351     { "exec", NULL },
6352     { "exp", NULL },
6353     { "false", _false },
6354     { "fill", _fill },
6355     { "fill-extents", NULL },
6356     { "fill-preserve", _fill_preserve },
6357     { "fill+", _fill_preserve },
6358     { "filter", _filter },
6359     { "floor", NULL },
6360     { "font", _font },
6361     { "for", _for },
6362     { "forall", NULL },
6363     { "g", _gray },
6364     { "ge", _ge },
6365     { "get", _get },
6366     { "glyph-path", _glyph_path },
6367     { "gt", _gt },
6368     { "h", _close_path },
6369     { "identity", _identity },
6370     { "if", _if },
6371     { "ifelse", _ifelse },
6372     { "image", _image },
6373     { "index", _index },
6374     { "integer", _integer },
6375     { "invert", _invert },
6376     { "in-stroke", NULL },
6377     { "in-fill", NULL },
6378     { "known", NULL },
6379     { "l", _line_to },
6380     { "L", _rel_line_to },
6381     { "languagelevel", NULL },
6382     { "le", _le },
6383     { "length", NULL },
6384     { "linear", _linear },
6385     { "line-to", _line_to },
6386     { "ln", NULL },
6387     { "load", NULL },
6388     { "log", NULL },
6389     { "loop", NULL },
6390     { "lt", _lt },
6391     { "m", _move_to },
6392     { "M", _rel_move_to },
6393     { "map-to-image", _map_to_image },
6394     { "mark", _mark },
6395     { "mask", _mask },
6396     { "matrix", _matrix },
6397
6398     { "mesh", _mesh },
6399     { "begin-patch", _mesh_begin_patch },
6400     { "end-patch", _mesh_end_patch },
6401     { "set-control-point", _mesh_set_control_point },
6402     { "set-corner-color", _mesh_set_corner_color },
6403
6404     { "mod", _mod },
6405     { "move-to", _move_to },
6406     { "mul", _mul },
6407     { "multiply", NULL },
6408     { "n", _new_path },
6409     { "N", _new_sub_path },
6410     { "ne", _ne },
6411     { "neg", _neg },
6412     { "new-path", _new_path },
6413     { "new-sub-path", _new_sub_path },
6414     { "not", _not },
6415     { "null", _null },
6416     { "or", _or },
6417     { "paint", _paint },
6418     { "paint-with-alpha", _paint_with_alpha },
6419     { "pattern", _pattern },
6420     { "pop", _pop },
6421     { "pop-group", _pop_group },
6422     { "push-group", _push_group },
6423     { "radial", _radial },
6424     { "rand", NULL },
6425     { "record", _record },
6426     { "rectangle", _rectangle },
6427     { "repeat", _repeat },
6428     { "restore", _restore },
6429     { "rel-curve-to", _rel_curve_to },
6430     { "rel-line-to", _rel_line_to },
6431     { "rel-move-to", _rel_move_to },
6432     { "reset-clip", _reset_clip },
6433     { "rgb", _rgb },
6434     { "rgba", _rgba },
6435     { "roll", _roll },
6436     { "rotate", _rotate },
6437     { "round", NULL },
6438     { "run", NULL },
6439     { "save", _save },
6440     { "scale", _scale },
6441     { "scaled-font", _scaled_font },
6442     { "select-font-face", _select_font_face },
6443     { "set", _set },
6444     { "set-antialias", _set_antialias },
6445     { "set-dash", _set_dash },
6446     { "set-device-offset", _set_device_offset },
6447     { "set-extend", _set_extend },
6448     { "set-fallback-resolution", _set_fallback_resolution },
6449     { "set-fill-rule", _set_fill_rule },
6450     { "set-filter", _set_filter },
6451     { "set-font-face", _set_font_face },
6452     { "set-font-options", _set_font_options },
6453     { "set-font-matrix", _set_font_matrix },
6454     { "set-font-size", _set_font_size },
6455     { "set-line-cap", _set_line_cap },
6456     { "set-line-join", _set_line_join },
6457     { "set-line-width", _set_line_width },
6458     { "set-matrix", _set_matrix },
6459     { "set-miter-limit", _set_miter_limit },
6460     { "set-mime-data", _set_mime_data },
6461     { "set-operator", _set_operator },
6462     { "set-scaled-font", _set_scaled_font },
6463     { "set-source", _set_source },
6464     { "set-source-image", _set_source_image },
6465     { "set-source-rgb", _set_source_rgb },
6466     { "set-source-rgba", _set_source_rgba },
6467     { "set-tolerance", _set_tolerance },
6468     { "show-glyphs", _show_glyphs },
6469     { "show-text", _show_text },
6470     { "show-text-glyphs", _show_text_glyphs },
6471     { "show-page", _show_page },
6472     { "similar", _similar },
6473     { "similar-image", _similar_image },
6474     { "sin", NULL },
6475     { "sqrt", NULL },
6476     { "sub", _sub },
6477     { "subsurface", _subsurface },
6478     { "surface", _surface },
6479     { "string", NULL },
6480     { "stroke", _stroke },
6481     { "stroke-extents", NULL },
6482     { "stroke-preserve", _stroke_preserve },
6483     { "stroke+", _stroke_preserve },
6484     { "text-path", _text_path },
6485     { "transform", _transform },
6486     { "transform-distance", NULL },
6487     { "transform-point", NULL },
6488     { "translate", _translate },
6489     { "true", _true },
6490     { "type", NULL },
6491     { "undef", _undef },
6492     { "unmap-image", _unmap_image },
6493     { "unset", _unset },
6494     { "user-to-device", NULL },
6495     { "user-to-device-distance", NULL },
6496     { "where", NULL },
6497     { "write-to-png", _write_to_png },
6498     { "write-to-script", _write_to_script },
6499     { "xor", _xor },
6500
6501     { "=", _debug_print },
6502
6503     { NULL, NULL },
6504 };
6505
6506 const csi_operator_def_t *
6507 _csi_operators (void)
6508 {
6509     return _defs;
6510 }
6511
6512 static const csi_integer_constant_def_t
6513 _integer_constants[] = {
6514     { "CLEAR",          CAIRO_OPERATOR_CLEAR },
6515     { "SOURCE",         CAIRO_OPERATOR_SOURCE },
6516     { "OVER",           CAIRO_OPERATOR_OVER },
6517     { "IN",             CAIRO_OPERATOR_IN },
6518     { "OUT",            CAIRO_OPERATOR_OUT },
6519     { "ATOP",           CAIRO_OPERATOR_ATOP },
6520     { "DEST",           CAIRO_OPERATOR_DEST },
6521     { "DEST_OVER",      CAIRO_OPERATOR_DEST_OVER },
6522     { "DEST_IN",        CAIRO_OPERATOR_DEST_IN },
6523     { "DEST_OUT",       CAIRO_OPERATOR_DEST_OUT },
6524     { "DEST_ATOP",      CAIRO_OPERATOR_DEST_ATOP },
6525     { "XOR",            CAIRO_OPERATOR_XOR },
6526     { "ADD",            CAIRO_OPERATOR_ADD },
6527     { "SATURATE",       CAIRO_OPERATOR_SATURATE },
6528     { "MULTIPLY",       CAIRO_OPERATOR_MULTIPLY },
6529     { "SCREEN",         CAIRO_OPERATOR_SCREEN },
6530     { "OVERLAY",        CAIRO_OPERATOR_OVERLAY },
6531     { "DARKEN",         CAIRO_OPERATOR_DARKEN },
6532     { "LIGHTEN",        CAIRO_OPERATOR_LIGHTEN },
6533     { "DODGE",          CAIRO_OPERATOR_COLOR_DODGE },
6534     { "BURN",           CAIRO_OPERATOR_COLOR_BURN },
6535     { "HARD_LIGHT",     CAIRO_OPERATOR_HARD_LIGHT },
6536     { "SOFT_LIGHT",     CAIRO_OPERATOR_SOFT_LIGHT },
6537     { "DIFFERENCE",     CAIRO_OPERATOR_DIFFERENCE },
6538     { "EXCLUSION",      CAIRO_OPERATOR_EXCLUSION },
6539     { "HSL_HUE",        CAIRO_OPERATOR_HSL_HUE },
6540     { "HSL_SATURATION", CAIRO_OPERATOR_HSL_SATURATION },
6541     { "HSL_COLOR",      CAIRO_OPERATOR_HSL_COLOR },
6542     { "HSL_LUMINOSITY", CAIRO_OPERATOR_HSL_LUMINOSITY },
6543
6544     { "WINDING",        CAIRO_FILL_RULE_WINDING },
6545     { "EVEN_ODD",       CAIRO_FILL_RULE_EVEN_ODD },
6546
6547     { "ANTIALIAS_DEFAULT",      CAIRO_ANTIALIAS_DEFAULT },
6548     { "ANTIALIAS_NONE",         CAIRO_ANTIALIAS_NONE },
6549     { "ANTIALIAS_GRAY",         CAIRO_ANTIALIAS_GRAY },
6550     { "ANTIALIAS_SUBPIXEL",     CAIRO_ANTIALIAS_SUBPIXEL },
6551     { "ANTIALIAS_FAST",         CAIRO_ANTIALIAS_FAST },
6552     { "ANTIALIAS_GOOD",         CAIRO_ANTIALIAS_GOOD },
6553     { "ANTIALIAS_BEST",         CAIRO_ANTIALIAS_BEST },
6554
6555     { "LINE_CAP_BUTT",          CAIRO_LINE_CAP_BUTT },
6556     { "LINE_CAP_ROUND",         CAIRO_LINE_CAP_ROUND },
6557     { "LINE_CAP_SQUARE",        CAIRO_LINE_CAP_SQUARE },
6558
6559     { "LINE_JOIN_MITER",        CAIRO_LINE_JOIN_MITER },
6560     { "LINE_JOIN_ROUND",        CAIRO_LINE_JOIN_ROUND },
6561     { "LINE_JOIN_BEVEL",        CAIRO_LINE_JOIN_BEVEL },
6562
6563     { "EXTEND_NONE",            CAIRO_EXTEND_NONE },
6564     { "EXTEND_REPEAT",          CAIRO_EXTEND_REPEAT },
6565     { "EXTEND_REFLECT",         CAIRO_EXTEND_REFLECT },
6566     { "EXTEND_PAD",             CAIRO_EXTEND_PAD },
6567
6568     { "FILTER_FAST",            CAIRO_FILTER_FAST },
6569     { "FILTER_GOOD",            CAIRO_FILTER_GOOD },
6570     { "FILTER_BEST",            CAIRO_FILTER_BEST },
6571     { "FILTER_BILINEAR",        CAIRO_FILTER_BILINEAR },
6572     { "FILTER_NEAREST",         CAIRO_FILTER_NEAREST },
6573     { "FILTER_GAUSSIAN",        CAIRO_FILTER_GAUSSIAN },
6574
6575     { "SLANT_NORMAL",           CAIRO_FONT_SLANT_NORMAL },
6576     { "SLANT_ITALIC",           CAIRO_FONT_SLANT_ITALIC },
6577     { "SLANT_OBLIQUE",          CAIRO_FONT_SLANT_OBLIQUE },
6578
6579     { "WEIGHT_NORMAL",          CAIRO_FONT_WEIGHT_NORMAL },
6580     { "WEIGHT_BOLD",            CAIRO_FONT_WEIGHT_BOLD },
6581
6582     { "SUBPIXEL_ORDER_DEFAULT", CAIRO_SUBPIXEL_ORDER_DEFAULT },
6583     { "SUBPIXEL_ORDER_RGB",     CAIRO_SUBPIXEL_ORDER_RGB },
6584     { "SUBPIXEL_ORDER_BGR",     CAIRO_SUBPIXEL_ORDER_BGR },
6585     { "SUBPIXEL_ORDER_VRGB",    CAIRO_SUBPIXEL_ORDER_VRGB },
6586     { "SUBPIXEL_ORDER_VBGR",    CAIRO_SUBPIXEL_ORDER_VBGR },
6587
6588     { "HINT_STYLE_DEFAULT",     CAIRO_HINT_STYLE_DEFAULT },
6589     { "HINT_STYLE_NONE",        CAIRO_HINT_STYLE_NONE },
6590     { "HINT_STYLE_SLIGHT",      CAIRO_HINT_STYLE_SLIGHT },
6591     { "HINT_STYLE_MEDIUM",      CAIRO_HINT_STYLE_MEDIUM },
6592     { "HINT_STYLE_FULL",        CAIRO_HINT_STYLE_FULL },
6593
6594     { "HINT_METRICS_DEFAULT",   CAIRO_HINT_METRICS_DEFAULT },
6595     { "HINT_METRICS_OFF",       CAIRO_HINT_METRICS_OFF },
6596     { "HINT_METRICS_ON",        CAIRO_HINT_METRICS_ON },
6597
6598     { "FORWARD",                0 },
6599     { "BACKWARD",               1 },
6600
6601     { "COLOR",                  CAIRO_CONTENT_COLOR },
6602     { "ALPHA",                  CAIRO_CONTENT_ALPHA },
6603     { "COLOR_ALPHA",            CAIRO_CONTENT_COLOR_ALPHA },
6604
6605     { "A1",                     CAIRO_FORMAT_A1 },
6606     { "A8",                     CAIRO_FORMAT_A8 },
6607     { "RGB16_565",              CAIRO_FORMAT_RGB16_565 },
6608     { "RGB24",                  CAIRO_FORMAT_RGB24 },
6609     { "ARGB32",                 CAIRO_FORMAT_ARGB32 },
6610     { "INVALID",                CAIRO_FORMAT_INVALID },
6611
6612     { NULL, 0 }
6613 };
6614
6615
6616 const csi_integer_constant_def_t *
6617 _csi_integer_constants (void)
6618 {
6619     return _integer_constants;
6620 }
6621
6622 static const csi_real_constant_def_t
6623 _real_constants[] = {
6624     { "math.pi",                M_PI },
6625     { "math.2pi",               2 * M_PI },
6626     { "math.sqrt2",             M_SQRT2 },
6627     { "math.ln2",               M_LN2 },
6628
6629     { NULL, 0 }
6630 };
6631
6632 const csi_real_constant_def_t *
6633 _csi_real_constants (void)
6634 {
6635     return _real_constants;
6636 }