Coverage Report

Created: 2026-10-17 07:00

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