cairo-script-operators.c [plain text]
#include "cairo-script-private.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <limits.h>
#include <assert.h>
#include <zlib.h>
#ifdef HAVE_MMAP
# ifdef HAVE_UNISTD_H
# include <sys/mman.h>
# include <unistd.h>
# else
# undef HAVE_MMAP
# endif
#endif
typedef struct _csi_proxy {
csi_t *ctx;
void *ptr;
csi_dictionary_t *dictionary;
csi_destroy_func_t destroy_func;
void *destroy_data;
} csi_proxy_t;
typedef struct _csi_blob {
csi_list_t list;
unsigned long hash;
uint8_t *bytes;
unsigned int len;
} csi_blob_t;
static const cairo_user_data_key_t _csi_proxy_key;
static const cairo_user_data_key_t _csi_blob_key;
enum mime_type {
MIME_TYPE_NONE = 0,
MIME_TYPE_PNG
};
#define check(CNT) do {\
if (_csi_unlikely (! _csi_check_ostack (ctx, (CNT)))) \
return _csi_error (CSI_STATUS_INVALID_SCRIPT); \
} while (0)
#define pop(CNT) _csi_pop_ostack (ctx, (CNT))
#define push(OBJ) _csi_push_ostack (ctx, (OBJ))
static csi_proxy_t *
_csi_proxy_create (csi_t *ctx,
void *ptr,
csi_dictionary_t *dictionary,
csi_destroy_func_t destroy_func,
void *destroy_data)
{
csi_proxy_t *proxy;
proxy = _csi_slab_alloc (ctx, sizeof (csi_proxy_t));
if (proxy == NULL)
return NULL;
proxy->ctx = cairo_script_interpreter_reference (ctx);
proxy->ptr = ptr;
proxy->destroy_func = destroy_func;
proxy->destroy_data = destroy_data;
proxy->dictionary = dictionary;
if (dictionary != NULL)
dictionary->base.ref++;
return proxy;
}
static void
_csi_proxy_destroy (void *closure)
{
csi_proxy_t *proxy = closure;
csi_t *ctx = proxy->ctx;
if (proxy->destroy_func != NULL)
proxy->destroy_func (proxy->destroy_data, proxy->ptr);
if (proxy->dictionary != NULL && --proxy->dictionary->base.ref == 0)
csi_dictionary_free (ctx, proxy->dictionary);
_csi_slab_free (ctx, proxy, sizeof (csi_proxy_t));
cairo_script_interpreter_destroy (ctx);
}
static void
_csi_blob_hash (csi_blob_t *blob, const uint32_t *data, int len)
{
unsigned long hash = blob->hash;
while (len--) {
unsigned long c = *data++;
hash *= 33;
hash ^= c;
}
blob->hash = hash;
}
static csi_boolean_t
_csi_blob_equal (const csi_list_t *link, void *data)
{
csi_blob_t *A, *B;
A = csi_container_of (link, csi_blob_t, list);
B = data;
if (A->len != B->len)
return FALSE;
if (A->hash != B->hash)
return FALSE;
return memcmp (A->bytes, B->bytes, A->len) == 0;
}
static void
_csi_blob_init (csi_blob_t *blob, uint8_t *bytes, int len)
{
blob->hash = 5381;
blob->len = len;
blob->bytes = bytes;
}
static csi_list_t *
_csi_list_unlink (csi_list_t *head, csi_list_t *link)
{
if (link->next != NULL)
link->next->prev = link->prev;
if (link->prev != NULL)
link->prev->next = link->next;
else
head = link->next;
return head;
}
static csi_list_t *
_csi_list_prepend (csi_list_t *head, csi_list_t *link)
{
if (head != NULL)
head->prev = link;
link->next = head;
link->prev = NULL;
return link;
}
static csi_list_t *
_csi_list_find (csi_list_t *head,
csi_boolean_t (*predicate) (const csi_list_t *link, void *data),
void *data)
{
while (head != NULL) {
if (predicate (head, data))
return head;
head = head->next;
}
return NULL;
}
static csi_status_t
_csi_ostack_get_boolean (csi_t *ctx, unsigned int i, csi_boolean_t *out)
{
csi_object_t *obj;
int type;
obj = _csi_peek_ostack (ctx, i);
type = csi_object_get_type (obj);
switch (type) {
case CSI_OBJECT_TYPE_BOOLEAN:
*out = obj->datum.boolean;
break;
case CSI_OBJECT_TYPE_INTEGER:
*out = !! obj->datum.integer;
break;
case CSI_OBJECT_TYPE_REAL:
*out = obj->datum.real != 0.;
break;
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_csi_ostack_get_integer (csi_t *ctx, unsigned int i, csi_integer_t *out)
{
csi_object_t *obj;
int type;
obj = _csi_peek_ostack (ctx, i);
type = csi_object_get_type (obj);
switch (type) {
case CSI_OBJECT_TYPE_BOOLEAN:
*out = obj->datum.boolean;
break;
case CSI_OBJECT_TYPE_INTEGER:
*out = obj->datum.integer;
break;
case CSI_OBJECT_TYPE_REAL:
*out = obj->datum.real;
break;
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_csi_ostack_get_number (csi_t *ctx, unsigned int i, double *out)
{
csi_object_t *obj;
int type;
obj = _csi_peek_ostack (ctx, i);
type = csi_object_get_type (obj);
switch (type) {
case CSI_OBJECT_TYPE_BOOLEAN:
*out = obj->datum.boolean;
break;
case CSI_OBJECT_TYPE_INTEGER:
*out = obj->datum.integer;
break;
case CSI_OBJECT_TYPE_REAL:
*out = obj->datum.real;
break;
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_csi_ostack_get_name (csi_t *ctx, unsigned int i, csi_name_t *out)
{
csi_object_t *obj;
obj = _csi_peek_ostack (ctx, i);
if (_csi_unlikely (csi_object_get_type (obj) != CSI_OBJECT_TYPE_NAME))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
*out = obj->datum.name;
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_csi_ostack_get_context (csi_t *ctx, unsigned int i, cairo_t **out)
{
csi_object_t *obj;
obj = _csi_peek_ostack (ctx, i);
if (_csi_unlikely (csi_object_get_type (obj) != CSI_OBJECT_TYPE_CONTEXT))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
*out = obj->datum.cr;
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_csi_ostack_get_font_face (csi_t *ctx, unsigned int i, cairo_font_face_t **out)
{
csi_object_t *obj;
obj = _csi_peek_ostack (ctx, i);
if (_csi_unlikely (csi_object_get_type (obj) != CSI_OBJECT_TYPE_FONT))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
*out = obj->datum.font_face;
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_csi_ostack_get_pattern (csi_t *ctx, unsigned int i, cairo_pattern_t **out)
{
csi_object_t *obj;
obj = _csi_peek_ostack (ctx, i);
if (_csi_unlikely (csi_object_get_type (obj) != CSI_OBJECT_TYPE_PATTERN))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
*out = obj->datum.pattern;
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_csi_ostack_get_scaled_font (csi_t *ctx, unsigned int i,
cairo_scaled_font_t **out)
{
csi_object_t *obj;
obj = _csi_peek_ostack (ctx, i);
if (_csi_unlikely
(csi_object_get_type (obj) != CSI_OBJECT_TYPE_SCALED_FONT))
{
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
*out = obj->datum.scaled_font;
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_csi_ostack_get_surface (csi_t *ctx, unsigned int i, cairo_surface_t **out)
{
csi_object_t *obj;
int type;
obj = _csi_peek_ostack (ctx, i);
type = csi_object_get_type (obj);
switch (type) {
case CSI_OBJECT_TYPE_CONTEXT:
*out = cairo_get_target (obj->datum.cr);
break;
case CSI_OBJECT_TYPE_SURFACE:
*out = obj->datum.surface;
break;
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_csi_ostack_get_array (csi_t *ctx, unsigned int i, csi_array_t **out)
{
csi_object_t *obj;
obj = _csi_peek_ostack (ctx, i);
if (_csi_unlikely (csi_object_get_type (obj) != CSI_OBJECT_TYPE_ARRAY))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
*out = obj->datum.array;
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_csi_ostack_get_procedure (csi_t *ctx, unsigned int i, csi_array_t **out)
{
csi_object_t *obj;
obj = _csi_peek_ostack (ctx, i);
if (_csi_unlikely (! csi_object_is_procedure (obj)))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
*out = obj->datum.array;
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_csi_ostack_get_dictionary (csi_t *ctx, unsigned int i, csi_dictionary_t **out)
{
csi_object_t *obj;
obj = _csi_peek_ostack (ctx, i);
if (_csi_unlikely
(csi_object_get_type (obj) != CSI_OBJECT_TYPE_DICTIONARY))
{
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
*out = obj->datum.dictionary;
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_csi_ostack_get_matrix (csi_t *ctx, unsigned int i, cairo_matrix_t *out)
{
csi_object_t *obj;
int type;
obj = _csi_peek_ostack (ctx, i);
type = csi_object_get_type (obj);
switch (type) {
case CSI_OBJECT_TYPE_MATRIX:
*out = obj->datum.matrix->matrix;
return CSI_STATUS_SUCCESS;
case CSI_OBJECT_TYPE_ARRAY:
if (obj->datum.array->stack.len == 6) {
cairo_matrix_init (out,
csi_number_get_value (&obj->datum.array->stack.objects[0]),
csi_number_get_value (&obj->datum.array->stack.objects[1]),
csi_number_get_value (&obj->datum.array->stack.objects[2]),
csi_number_get_value (&obj->datum.array->stack.objects[3]),
csi_number_get_value (&obj->datum.array->stack.objects[4]),
csi_number_get_value (&obj->datum.array->stack.objects[5]));
return CSI_STATUS_SUCCESS;
}
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
}
static csi_status_t
_csi_dictionary_get_integer (csi_t *ctx,
csi_dictionary_t *dict,
const char *name,
csi_boolean_t optional,
long *value)
{
csi_status_t status;
csi_object_t key, obj;
int type;
status = csi_name_new_static (ctx, &key, name);
if (_csi_unlikely (status))
return status;
if (optional && ! csi_dictionary_has (dict, key.datum.name))
return CSI_STATUS_SUCCESS;
status = csi_dictionary_get (ctx, dict, key.datum.name, &obj);
if (_csi_unlikely (status))
return status;
type = csi_object_get_type (&obj);
switch (type) {
case CSI_OBJECT_TYPE_BOOLEAN:
*value = obj.datum.boolean;
break;
case CSI_OBJECT_TYPE_INTEGER:
*value = obj.datum.integer;
break;
case CSI_OBJECT_TYPE_REAL:
*value = obj.datum.real;
break;
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_csi_dictionary_get_number (csi_t *ctx,
csi_dictionary_t *dict,
const char *name,
csi_boolean_t optional,
double *value)
{
csi_status_t status;
csi_object_t key, obj;
status = csi_name_new_static (ctx, &key, name);
if (_csi_unlikely (status))
return status;
if (optional && ! csi_dictionary_has (dict, key.datum.name))
return CSI_STATUS_SUCCESS;
status = csi_dictionary_get (ctx, dict, key.datum.name, &obj);
if (_csi_unlikely (status))
return status;
*value = csi_number_get_value (&obj);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_csi_ostack_get_string (csi_t *ctx, unsigned int i, csi_string_t **out)
{
csi_object_t *obj;
obj = _csi_peek_ostack (ctx, i);
if (_csi_unlikely (csi_object_get_type (obj) != CSI_OBJECT_TYPE_STRING))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
*out = obj->datum.string;
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_csi_ostack_get_string_constant (csi_t *ctx, unsigned int i, const char **out)
{
csi_object_t *obj;
int type;
obj = _csi_peek_ostack (ctx, i);
type = csi_object_get_type (obj);
switch (type) {
case CSI_OBJECT_TYPE_NAME:
*out = (const char *) obj->datum.name;
break;
case CSI_OBJECT_TYPE_STRING:
*out = obj->datum.string->string;
break;
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_do_cairo_op (csi_t *ctx, void (*op) (cairo_t *))
{
cairo_t *cr;
csi_status_t status;
check (1);
status = _csi_ostack_get_context (ctx, 0, &cr);
if (_csi_unlikely (status))
return status;
op (cr);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
end_dict_construction (csi_t *ctx)
{
csi_object_t obj;
csi_dictionary_t *dict;
csi_status_t status;
status = csi_dictionary_new (ctx, &obj);
if (_csi_unlikely (status))
return status;
dict = obj.datum.dictionary;
do {
csi_object_t *name, *value;
check (1);
value = _csi_peek_ostack (ctx, 0);
if (csi_object_get_type (value) == CSI_OBJECT_TYPE_MARK) {
pop (1);
break;
}
check (2);
name = _csi_peek_ostack (ctx, 1);
if (_csi_unlikely
(csi_object_get_type (name) != CSI_OBJECT_TYPE_NAME))
{
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
status = csi_dictionary_put (ctx, dict, name->datum.name, value);
if (_csi_unlikely (status))
return status;
pop (2);
} while (TRUE);
return push (&obj);
}
static csi_status_t
end_array_construction (csi_t *ctx)
{
csi_object_t obj;
csi_status_t status;
int len = 0;
do {
check (len + 1);
if (csi_object_get_type (_csi_peek_ostack (ctx, len)) ==
CSI_OBJECT_TYPE_MARK)
{
break;
}
len++;
} while (TRUE);
status = csi_array_new (ctx, len, &obj);
if (_csi_unlikely (status))
return status;
if (len != 0) {
csi_array_t *array;
array = obj.datum.array;
memcpy (array->stack.objects,
_csi_peek_ostack (ctx, len - 1),
sizeof (csi_object_t) * len);
array->stack.len = len;
}
ctx->ostack.len -= len + 1;
return push (&obj);
}
static csi_status_t
_alpha (csi_t *ctx)
{
csi_object_t obj;
csi_status_t status;
double a;
check (1);
status = _csi_ostack_get_number (ctx, 0, &a);
if (_csi_unlikely (status))
return status;
pop (1);
obj.type = CSI_OBJECT_TYPE_PATTERN;
obj.datum.pattern = cairo_pattern_create_rgba (0, 0, 0, a);
return push (&obj);
}
static csi_status_t
_add (csi_t *ctx)
{
csi_object_t *A;
csi_object_t *B;
csi_object_type_t type_a, type_b;
check (2);
B = _csi_peek_ostack (ctx, 0);
A = _csi_peek_ostack (ctx, 1);
type_a = csi_object_get_type (A);
if (_csi_unlikely (! (type_a == CSI_OBJECT_TYPE_INTEGER ||
type_a == CSI_OBJECT_TYPE_REAL)))
{
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
type_b = csi_object_get_type (B);
if (_csi_unlikely (! (type_b == CSI_OBJECT_TYPE_INTEGER ||
type_b == CSI_OBJECT_TYPE_REAL)))
{
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
pop (2);
if (type_a == CSI_OBJECT_TYPE_REAL &&
type_b == CSI_OBJECT_TYPE_REAL)
{
return _csi_push_ostack_real (ctx, A->datum.real + B->datum.real);
}
else if (type_a == CSI_OBJECT_TYPE_INTEGER &&
type_b == CSI_OBJECT_TYPE_INTEGER)
{
return _csi_push_ostack_integer (ctx,
A->datum.integer + B->datum.integer);
}
else
{
double v;
if (type_a == CSI_OBJECT_TYPE_REAL)
v = A->datum.real;
else
v = A->datum.integer;
if (type_b == CSI_OBJECT_TYPE_REAL)
v += B->datum.real;
else
v += B->datum.integer;
return _csi_push_ostack_real (ctx, v);
}
}
static csi_status_t
_add_color_stop (csi_t *ctx)
{
csi_status_t status;
double offset, r, g, b, a;
cairo_pattern_t *pattern = NULL;
check (6);
status = _csi_ostack_get_number (ctx, 0, &a);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 1, &b);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 2, &g);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 3, &r);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 4, &offset);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_pattern (ctx, 5, &pattern);
if (_csi_unlikely (status))
return status;
cairo_pattern_add_color_stop_rgba (pattern, offset, r, g, b, a);
pop (5);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_and (csi_t *ctx)
{
csi_object_t *a, *b;
int type;
check (2);
a = _csi_peek_ostack (ctx, 0);
b = _csi_peek_ostack (ctx, 1);
if (_csi_unlikely (csi_object_get_type (a) != csi_object_get_type (b)))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
pop (2);
type = csi_object_get_type (a);
switch (type) {
case CSI_OBJECT_TYPE_INTEGER:
return _csi_push_ostack_integer (ctx,
a->datum.integer & b->datum.integer);
case CSI_OBJECT_TYPE_BOOLEAN:
return _csi_push_ostack_boolean (ctx,
a->datum.boolean & b->datum.boolean);
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
}
static csi_status_t
_arc (csi_t *ctx)
{
csi_status_t status;
double x, y, r;
double theta1, theta2;
cairo_t *cr;
check (6);
status = _csi_ostack_get_number (ctx, 0, &theta2);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 1, &theta1);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 2, &r);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 3, &y);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 4, &x);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 5, &cr);
if (_csi_unlikely (status))
return status;
cairo_arc (cr, x, y, r, theta1, theta2);
pop (5);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_arc_negative (csi_t *ctx)
{
csi_status_t status;
double x, y, r;
double theta1, theta2;
cairo_t *cr;
check (6);
status = _csi_ostack_get_number (ctx, 0, &theta2);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 1, &theta1);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 2, &r);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 3, &y);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 4, &x);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 5, &cr);
if (_csi_unlikely (status))
return status;
cairo_arc_negative (cr, x, y, r, theta1, theta2);
pop (5);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_array (csi_t *ctx)
{
csi_object_t obj;
csi_status_t status;
status = csi_array_new (ctx, 0, &obj);
if (_csi_unlikely (status))
return status;
return push (&obj);
}
static csi_status_t
_bind_substitute (csi_t *ctx, csi_array_t *array)
{
csi_status_t status;
csi_integer_t i, n;
csi_dictionary_t *dict;
dict = ctx->dstack.objects[0].datum.dictionary;
n = array->stack.len;
for (i = 0; i < n; i++) {
csi_object_t *obj = &array->stack.objects[i];
if (obj->type == (CSI_OBJECT_TYPE_NAME | CSI_OBJECT_ATTR_EXECUTABLE)) {
csi_dictionary_entry_t *entry;
entry = _csi_hash_table_lookup (&dict->hash_table,
(csi_hash_entry_t *)
&obj->datum.name);
if (entry != NULL)
*obj = entry->value;
} else if (csi_object_is_procedure (obj)) {
status = _bind_substitute (ctx, obj->datum.array);
if (_csi_unlikely (status))
return status;
}
}
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_idiom_substitute (csi_t *ctx, csi_array_t *array)
{
#if 0
csi_status_t status;
csi_integer_t i, j;
j = array->stack.len;
for (i = 0; i < j; i++) {
csi_object_t *obj = &array->stack.objects[i];
if (csi_object_is_procedure (obj)) {
status = _idiom_substitute (ctx, obj->datum.array);
if (_csi_unlikely (_cairo_is_error (status))
return status;
}
}
#endif
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_bind (csi_t *ctx)
{
csi_array_t *array;
csi_status_t status;
check (1);
status = _csi_ostack_get_procedure (ctx, 0, &array);
if (_csi_unlikely (status))
return status;
status = _bind_substitute (ctx, array);
if (_csi_unlikely (status))
return status;
status = _idiom_substitute (ctx, array);
if (_csi_unlikely (status))
return status;
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_bitshift (csi_t *ctx)
{
long v, shift;
csi_status_t status;
check (2);
status = _csi_ostack_get_integer (ctx, 0, &shift);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_integer (ctx, 1, &v);
if (_csi_unlikely (status))
return status;
if (shift < 0) {
shift = -shift;
v >>= shift;
} else
v <<= shift;
pop (1);
_csi_peek_ostack (ctx, 0)->datum.integer = v;
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_clip (csi_t *ctx)
{
return _do_cairo_op (ctx, cairo_clip);
}
static csi_status_t
_clip_preserve (csi_t *ctx)
{
return _do_cairo_op (ctx, cairo_clip_preserve);
}
static csi_status_t
_close_path (csi_t *ctx)
{
return _do_cairo_op (ctx, cairo_close_path);
}
static csi_status_t
_context (csi_t *ctx)
{
csi_object_t obj;
csi_status_t status;
cairo_surface_t *surface;
cairo_t *cr;
csi_context_create_func_t hook;
csi_proxy_t *proxy;
check (1);
status = _csi_ostack_get_surface (ctx, 0, &surface);
if (_csi_unlikely (status))
return status;
hook = ctx->hooks.context_create;
if (hook != NULL)
cr = hook (ctx->hooks.closure, surface);
else
cr = cairo_create (surface);
proxy = _csi_proxy_create (ctx, cr, NULL,
ctx->hooks.context_destroy,
ctx->hooks.closure);
if (_csi_unlikely (proxy == NULL)) {
cairo_destroy (cr);
return _csi_error (CSI_STATUS_NO_MEMORY);
}
status = cairo_set_user_data (cr, &_csi_proxy_key,
proxy, _csi_proxy_destroy);
if (_csi_unlikely (status)) {
_csi_proxy_destroy (proxy);
cairo_destroy (cr);
return status;
}
pop (1);
obj.type = CSI_OBJECT_TYPE_CONTEXT;
obj.datum.cr = cr;
return push (&obj);
}
static csi_status_t
_copy (csi_t *ctx)
{
csi_object_t *obj;
int type;
check (1);
obj = csi_object_reference (_csi_peek_ostack (ctx, 0));
pop (1);
type = csi_object_get_type (obj);
switch (type) {
case CSI_OBJECT_TYPE_INTEGER:
{
long i, n;
n = obj->datum.integer;
if (_csi_unlikely (n < 0))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
check (n);
for (i = n; i--; ) {
csi_status_t status;
status = _csi_push_ostack_copy (ctx,
_csi_peek_ostack (ctx, n-1));
if (_csi_unlikely (status))
return status;
}
break;
}
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_copy_page (csi_t *ctx)
{
csi_object_t *obj;
int type;
check (1);
obj = _csi_peek_ostack (ctx, 0);
type = csi_object_get_type (obj);
switch (type) {
case CSI_OBJECT_TYPE_CONTEXT:
cairo_copy_page (obj->datum.cr);
if (ctx->hooks.copy_page != NULL)
ctx->hooks.copy_page (ctx->hooks.closure, obj->datum.cr);
break;
case CSI_OBJECT_TYPE_SURFACE:
cairo_surface_copy_page (obj->datum.surface);
break;
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_curve_to (csi_t *ctx)
{
csi_status_t status;
double x1, y1;
double x2, y2;
double x3, y3;
cairo_t *cr;
check (7);
status = _csi_ostack_get_number (ctx, 0, &y3);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 1, &x3);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 2, &y2);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 3, &x2);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 4, &y1);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 5, &x1);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 6, &cr);
if (_csi_unlikely (status))
return status;
cairo_curve_to (cr, x1, y1, x2, y2, x3, y3);
pop (6);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_cvi (csi_t *ctx)
{
csi_object_t *val, obj;
int type;
check (1);
val = _csi_peek_ostack (ctx, 0);
type = csi_object_get_type (val);
switch (type) {
case CSI_OBJECT_TYPE_INTEGER:
return CSI_STATUS_SUCCESS;
case CSI_OBJECT_TYPE_REAL:
pop (1);
return _csi_push_ostack_integer (ctx, val->datum.real);
case CSI_OBJECT_TYPE_STRING:
if (! _csi_parse_number (&obj,
val->datum.string->string,
val->datum.string->len))
{
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
pop (1);
if (csi_object_get_type (&obj) == CSI_OBJECT_TYPE_INTEGER)
return push (&obj);
else
return _csi_push_ostack_integer (ctx, obj.datum.real);
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
}
static csi_status_t
_cvr (csi_t *ctx)
{
csi_object_t *val, obj;
int type;
check (1);
val = _csi_peek_ostack (ctx, 0);
type = csi_object_get_type (val);
switch (type) {
case CSI_OBJECT_TYPE_REAL:
return CSI_STATUS_SUCCESS;
case CSI_OBJECT_TYPE_INTEGER:
pop (1);
return _csi_push_ostack_real (ctx, val->datum.integer);
case CSI_OBJECT_TYPE_STRING:
if (! _csi_parse_number (&obj,
val->datum.string->string,
val->datum.string->len))
{
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
pop (1);
if (csi_object_get_type (&obj) == CSI_OBJECT_TYPE_REAL)
return push (&obj);
else
return _csi_push_ostack_real (ctx, obj.datum.integer);
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
}
static csi_status_t
_def (csi_t *ctx)
{
csi_name_t name = 0;
csi_status_t status;
check (2);
status = _csi_ostack_get_name (ctx, 1, &name);
if (_csi_unlikely (status))
return status;
status = _csi_name_define (ctx, name, _csi_peek_ostack (ctx, 0));
if (_csi_unlikely (status))
return status;
pop (2);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_dict (csi_t *ctx)
{
csi_object_t obj;
csi_status_t status;
status = csi_dictionary_new (ctx, &obj);
if (_csi_unlikely (status))
return status;
return push (&obj);
}
static csi_status_t
_div (csi_t *ctx)
{
csi_object_t *A;
csi_object_t *B;
csi_object_type_t type_a, type_b;
check (2);
B = _csi_peek_ostack (ctx, 0);
A = _csi_peek_ostack (ctx, 1);
type_a = csi_object_get_type (A);
if (_csi_unlikely (! (type_a == CSI_OBJECT_TYPE_INTEGER ||
type_a == CSI_OBJECT_TYPE_REAL)))
{
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
type_b = csi_object_get_type (B);
if (_csi_unlikely (! (type_b == CSI_OBJECT_TYPE_INTEGER ||
type_b == CSI_OBJECT_TYPE_REAL)))
{
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
pop (2);
if (type_a == CSI_OBJECT_TYPE_REAL &&
type_b == CSI_OBJECT_TYPE_REAL)
{
return _csi_push_ostack_real (ctx, A->datum.real / B->datum.real);
}
else if (type_a == CSI_OBJECT_TYPE_INTEGER &&
type_b == CSI_OBJECT_TYPE_INTEGER)
{
return _csi_push_ostack_integer (ctx,
A->datum.integer / B->datum.integer);
}
else
{
double v;
if (type_a == CSI_OBJECT_TYPE_REAL)
v = A->datum.real;
else
v = A->datum.integer;
if (type_b == CSI_OBJECT_TYPE_REAL)
v /= B->datum.real;
else
v /= B->datum.integer;
return _csi_push_ostack_real (ctx, v);
}
}
static csi_status_t
_duplicate (csi_t *ctx)
{
check (1);
return _csi_push_ostack_copy (ctx, _csi_peek_ostack (ctx, 0));
}
static csi_status_t
_eq (csi_t *ctx)
{
csi_object_t *a, *b;
csi_boolean_t v;
check (2);
b = _csi_peek_ostack (ctx, 0);
a = _csi_peek_ostack (ctx, 1);
v = csi_object_eq (a, b);
pop (2);
return _csi_push_ostack_boolean (ctx, v);
}
static csi_status_t
_exch (csi_t *ctx)
{
return _csi_stack_exch (&ctx->ostack);
}
static csi_status_t
_false (csi_t *ctx)
{
return _csi_push_ostack_boolean (ctx, FALSE);
}
static csi_status_t
_fill (csi_t *ctx)
{
return _do_cairo_op (ctx, cairo_fill);
}
static csi_status_t
_fill_preserve (csi_t *ctx)
{
return _do_cairo_op (ctx, cairo_fill_preserve);
}
static csi_status_t
_filter (csi_t *ctx)
{
csi_object_t *src;
csi_dictionary_t *dict = NULL;
csi_status_t status;
const char *name = NULL;
const struct filters {
const char *name;
csi_status_t (*constructor) (csi_t *t,
csi_object_t *,
csi_dictionary_t *,
csi_object_t *);
} filters[] = {
{ "ascii85", csi_file_new_ascii85_decode },
#if HAVE_ZLIB
{ "deflate", csi_file_new_deflate_decode },
#endif
#if 0
{ "lzw", csi_file_new_lzw_decode },
#endif
{ NULL, NULL }
}, *filter;
int cnt;
check (2);
status = _csi_ostack_get_string_constant (ctx, 0, &name);
if (_csi_unlikely (status))
return status;
src = _csi_peek_ostack (ctx, 1);
cnt = 2;
if (csi_object_get_type (src) == CSI_OBJECT_TYPE_DICTIONARY) {
dict = src->datum.dictionary;
check (3);
src = _csi_peek_ostack (ctx, 2);
cnt = 3;
}
for (filter = filters; filter->name != NULL; filter++) {
if (strcmp (name, filter->name) == 0) {
csi_object_t file;
status = filter->constructor (ctx, &file, dict, src);
if (_csi_unlikely (status))
return status;
pop (cnt);
return push (&file);
}
}
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
static cairo_status_t
_type3_init (cairo_scaled_font_t *scaled_font,
cairo_t *cr,
cairo_font_extents_t *metrics)
{
cairo_font_face_t *face;
csi_proxy_t *proxy;
csi_t *ctx;
csi_dictionary_t *font;
csi_object_t key;
csi_object_t obj;
csi_array_t *array;
csi_status_t status;
face = cairo_scaled_font_get_font_face (scaled_font);
proxy = cairo_font_face_get_user_data (face, &_csi_proxy_key);
if (_csi_unlikely (proxy == NULL))
return CAIRO_STATUS_NO_MEMORY;
ctx = proxy->ctx;
font = proxy->dictionary;
status = csi_name_new_static (ctx, &key, "metrics");
if (_csi_unlikely (status))
return CAIRO_STATUS_NO_MEMORY;
if (! csi_dictionary_has (font, key.datum.name))
return CAIRO_STATUS_SUCCESS;
status = csi_dictionary_get (ctx, font, key.datum.name, &obj);
if (_csi_unlikely (status))
return status;
if (csi_object_get_type (&obj) != CSI_OBJECT_TYPE_ARRAY)
return CAIRO_STATUS_USER_FONT_ERROR;
array = obj.datum.array;
if (array->stack.len != 5)
return CAIRO_STATUS_USER_FONT_ERROR;
metrics->ascent = csi_number_get_value (&array->stack.objects[0]);
metrics->descent = csi_number_get_value (&array->stack.objects[1]);
metrics->height = csi_number_get_value (&array->stack.objects[2]);
metrics->max_x_advance = csi_number_get_value (&array->stack.objects[3]);
metrics->max_y_advance = csi_number_get_value (&array->stack.objects[4]);
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_type3_lookup (cairo_scaled_font_t *scaled_font,
unsigned long unicode,
unsigned long *glyph)
{
cairo_font_face_t *face;
csi_proxy_t *proxy;
csi_t *ctx;
csi_dictionary_t *font;
csi_object_t obj, key;
csi_array_t *array;
char buf[12];
csi_integer_t i;
cairo_status_t status;
face = cairo_scaled_font_get_font_face (scaled_font);
proxy = cairo_font_face_get_user_data (face, &_csi_proxy_key);
if (_csi_unlikely (proxy == NULL))
return CAIRO_STATUS_USER_FONT_ERROR;
ctx = proxy->ctx;
font = proxy->dictionary;
status = csi_name_new_static (ctx, &key, "encoding");
if (_csi_unlikely (status))
return CAIRO_STATUS_USER_FONT_ERROR;
if (! csi_dictionary_has (font, key.datum.name)) {
*glyph = unicode;
return CAIRO_STATUS_SUCCESS;
}
status = csi_dictionary_get (ctx, font, key.datum.name, &obj);
if (_csi_unlikely (status))
return CAIRO_STATUS_USER_FONT_ERROR;
if (_csi_unlikely (csi_object_get_type (&obj) != CSI_OBJECT_TYPE_ARRAY))
return CAIRO_STATUS_USER_FONT_ERROR;
snprintf (buf, sizeof (buf), "uni%04lu", unicode);
array = obj.datum.array;
for (i = 0; i < array->stack.len; i++) {
csi_object_t *name;
name = &array->stack.objects[i];
if (csi_object_get_type (name) != CSI_OBJECT_TYPE_NAME)
continue;
if (strcmp ((char *) name->datum.name, buf) == 0) {
*glyph = i;
return CAIRO_STATUS_SUCCESS;
}
}
return CAIRO_STATUS_USER_FONT_ERROR;
}
static cairo_status_t
_type3_render (cairo_scaled_font_t *scaled_font,
unsigned long glyph_index,
cairo_t *cr,
cairo_text_extents_t *metrics)
{
cairo_font_face_t *face;
csi_proxy_t *proxy;
csi_t *ctx;
csi_dictionary_t *font;
csi_array_t *glyphs;
csi_object_t *glyph;
csi_object_t key;
csi_object_t obj;
csi_object_t render;
csi_status_t status;
face = cairo_scaled_font_get_font_face (scaled_font);
proxy = cairo_font_face_get_user_data (face, &_csi_proxy_key);
if (_csi_unlikely (proxy == NULL))
return CAIRO_STATUS_USER_FONT_ERROR;
ctx = proxy->ctx;
font = proxy->dictionary;
status = csi_name_new_static (ctx, &key, "glyphs");
if (_csi_unlikely (status))
return CAIRO_STATUS_USER_FONT_ERROR;
status = csi_dictionary_get (ctx, font, key.datum.name, &obj);
if (_csi_unlikely (status))
return CAIRO_STATUS_USER_FONT_ERROR;
if (_csi_unlikely (csi_object_get_type (&obj) != CSI_OBJECT_TYPE_ARRAY))
return CAIRO_STATUS_USER_FONT_ERROR;
glyphs = obj.datum.array;
glyph = &glyphs->stack.objects[glyph_index];
if (csi_object_get_type (glyph) == CSI_OBJECT_TYPE_NULL)
return CAIRO_STATUS_SUCCESS;
if (_csi_unlikely (csi_object_get_type (glyph) != CSI_OBJECT_TYPE_DICTIONARY))
return CAIRO_STATUS_USER_FONT_ERROR;
status = csi_name_new_static (ctx, &key, "metrics");
if (_csi_unlikely (status))
return CAIRO_STATUS_USER_FONT_ERROR;
font = glyph->datum.dictionary;
if (csi_dictionary_has (font, key.datum.name)) {
csi_array_t *array;
status = csi_dictionary_get (ctx, font, key.datum.name, &obj);
if (_csi_unlikely (status))
return CAIRO_STATUS_USER_FONT_ERROR;
if (_csi_unlikely (csi_object_get_type (&obj) !=
CSI_OBJECT_TYPE_ARRAY))
return CAIRO_STATUS_USER_FONT_ERROR;
array = obj.datum.array;
if (_csi_unlikely (array->stack.len != 6))
return CAIRO_STATUS_USER_FONT_ERROR;
metrics->x_bearing = csi_number_get_value (&array->stack.objects[0]);
metrics->y_bearing = csi_number_get_value (&array->stack.objects[1]);
metrics->width = csi_number_get_value (&array->stack.objects[2]);
metrics->height = csi_number_get_value (&array->stack.objects[3]);
metrics->x_advance = csi_number_get_value (&array->stack.objects[4]);
metrics->y_advance = csi_number_get_value (&array->stack.objects[5]);
}
status = csi_name_new_static (ctx, &key, "render");
if (_csi_unlikely (status))
return CAIRO_STATUS_USER_FONT_ERROR;
status = csi_dictionary_get (ctx, font, key.datum.name, &render);
if (_csi_unlikely (status))
return CAIRO_STATUS_USER_FONT_ERROR;
if (_csi_unlikely (! csi_object_is_procedure (&render)))
return CAIRO_STATUS_USER_FONT_ERROR;
obj.type = CSI_OBJECT_TYPE_CONTEXT;
obj.datum.cr = cairo_reference (cr);
status = push (&obj);
if (_csi_unlikely (status)) {
cairo_destroy (cr);
return CAIRO_STATUS_USER_FONT_ERROR;
}
status = csi_object_execute (ctx, &render);
pop (1);
return status ? CAIRO_STATUS_USER_FONT_ERROR : CAIRO_STATUS_SUCCESS;
}
static csi_status_t
_font_type3 (csi_t *ctx,
csi_dictionary_t *font,
cairo_font_face_t **font_face_out)
{
cairo_font_face_t *font_face;
font_face = cairo_user_font_face_create ();
cairo_user_font_face_set_init_func (font_face, _type3_init);
cairo_user_font_face_set_unicode_to_glyph_func (font_face, _type3_lookup);
cairo_user_font_face_set_render_glyph_func (font_face, _type3_render);
*font_face_out = font_face;
return CSI_STATUS_SUCCESS;
}
#if CAIRO_HAS_FT_FONT
#include <cairo-ft.h>
#include <ft2build.h>
#include FT_FREETYPE_H
static FT_Library _ft_lib;
struct _ft_face_data {
csi_t *ctx;
csi_blob_t blob;
FT_Face face;
csi_string_t *source;
void *bytes;
cairo_font_face_t *font_face;
};
static void
_ft_done_face (void *closure)
{
struct _ft_face_data *data = closure;
csi_t *ctx;
ctx = data->ctx;
if (data->face != NULL)
FT_Done_Face (data->face);
ctx->_faces = _csi_list_unlink (ctx->_faces, &data->blob.list);
if (data->source != NULL) {
if (--data->source->base.ref == 0)
csi_string_free (ctx, data->source);
} else {
#ifdef HAVE_MMAP
munmap (data->blob.bytes, data->blob.len);
#endif
}
if (data->bytes != NULL)
_csi_free (ctx, data->bytes);
_csi_slab_free (ctx, data, sizeof (*data));
cairo_script_interpreter_destroy (ctx);
}
struct mmap_vec {
const uint8_t *bytes;
size_t num_bytes;
};
#ifdef HAVE_MMAP
static void *
_mmap_bytes (const struct mmap_vec *vec, int count)
{
char template[] = "/tmp/csi-font.XXXXXX";
void *ptr;
int fd;
int num_bytes;
fd = mkstemp (template);
if (fd == -1)
return MAP_FAILED;
unlink (template);
num_bytes = 0;
while (count--) {
const uint8_t *bytes = vec->bytes;
size_t len = vec->num_bytes;
while (len) {
int ret = write (fd, bytes, len);
if (ret < 0) {
close (fd);
return MAP_FAILED;
}
len -= ret;
bytes += ret;
}
num_bytes += vec->num_bytes;
vec++;
}
ptr = mmap (NULL, num_bytes, PROT_READ, MAP_SHARED, fd, 0);
close (fd);
return ptr;
}
#endif
static void *
inflate_string (csi_t *ctx, csi_string_t *src)
{
uLongf len;
uint8_t *bytes;
len = src->deflate;
bytes = _csi_alloc (ctx, len + 1);
if (bytes == NULL)
return NULL;
if (uncompress ((Bytef *) bytes, &len,
(Bytef *) src->string, src->len) != Z_OK)
{
_csi_free (ctx, bytes);
bytes = NULL;
}
else
{
bytes[len] = '\0';
}
return bytes;
}
static csi_status_t
_ft_create_for_source (csi_t *ctx,
csi_string_t *source,
int index, int load_flags,
cairo_font_face_t **font_face_out)
{
csi_blob_t tmpl;
struct _ft_face_data *data;
csi_list_t *link;
FT_Error err;
cairo_font_face_t *font_face;
csi_status_t status;
struct mmap_vec vec[2];
int vec_count;
void *bytes;
int len;
_csi_blob_init (&tmpl, (uint8_t *) source->string, source->len);
_csi_blob_hash (&tmpl, (uint32_t *) source->string, source->len / sizeof (uint32_t));
link = _csi_list_find (ctx->_faces, _csi_blob_equal, &tmpl);
if (link) {
if (--source->base.ref == 0)
csi_string_free (ctx, source);
data = csi_container_of (link, struct _ft_face_data, blob.list);
*font_face_out = cairo_font_face_reference (data->font_face);
return CSI_STATUS_SUCCESS;
}
if (_ft_lib == NULL) {
err = FT_Init_FreeType (&_ft_lib);
if (_csi_unlikely (err != FT_Err_Ok))
return _csi_error (CSI_STATUS_NO_MEMORY);
}
data = _csi_slab_alloc (ctx, sizeof (*data));
data->bytes = NULL;
data->source = source;
vec[0].bytes = tmpl.bytes;
vec[0].num_bytes = tmpl.len;
if (source->deflate) {
len = source->deflate;
bytes = inflate_string (ctx, source);
if (_csi_unlikely (bytes == NULL))
return _csi_error (CSI_STATUS_NO_MEMORY);
vec[1].bytes = bytes;
vec[1].num_bytes = len;
data->bytes = bytes;
vec_count = 2;
} else {
bytes = tmpl.bytes;
len = tmpl.len;
vec_count = 1;
}
data->face = NULL;
ctx->_faces = _csi_list_prepend (ctx->_faces, &data->blob.list);
data->ctx = cairo_script_interpreter_reference (ctx);
data->blob.hash = tmpl.hash;
data->blob.len = tmpl.len;
#ifdef HAVE_MMAP
data->blob.bytes = _mmap_bytes (vec, vec_count);
if (data->blob.bytes != MAP_FAILED) {
if (--source->base.ref == 0)
csi_string_free (ctx, source);
if (source->deflate) {
_csi_free (ctx, bytes);
bytes = data->blob.bytes + vec[0].num_bytes;
} else
bytes = data->blob.bytes;
data->source = NULL;
data->bytes = NULL;
} else {
data->blob.bytes = tmpl.bytes;
}
#else
data->blob.bytes = tmpl.bytes;
#endif
err = FT_New_Memory_Face (_ft_lib,
bytes, len,
index,
&data->face);
if (_csi_unlikely (err != FT_Err_Ok)) {
_ft_done_face (data);
if (err == FT_Err_Out_Of_Memory)
return _csi_error (CSI_STATUS_NO_MEMORY);
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
font_face = cairo_ft_font_face_create_for_ft_face (data->face, load_flags);
status = cairo_font_face_set_user_data (font_face,
&_csi_blob_key,
data, _ft_done_face);
if (_csi_unlikely (status)) {
_ft_done_face (data);
cairo_font_face_destroy (font_face);
return status;
}
data->font_face = font_face;
*font_face_out = font_face;
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_ft_create_for_pattern (csi_t *ctx,
csi_string_t *string,
cairo_font_face_t **font_face_out)
{
#if CAIRO_HAS_FC_FONT
csi_blob_t tmpl;
struct _ft_face_data *data;
csi_list_t *link;
cairo_font_face_t *font_face;
FcPattern *pattern, *resolved;
csi_status_t status;
struct mmap_vec vec;
void *bytes;
_csi_blob_init (&tmpl, (uint8_t *) string->string, string->len);
_csi_blob_hash (&tmpl, (uint32_t *) string->string, string->len / sizeof (uint32_t));
link = _csi_list_find (ctx->_faces, _csi_blob_equal, &tmpl);
if (link) {
if (--string->base.ref == 0)
csi_string_free (ctx, string);
data = csi_container_of (link, struct _ft_face_data, blob.list);
*font_face_out = cairo_font_face_reference (data->font_face);
return CSI_STATUS_SUCCESS;
}
if (string->deflate) {
bytes = inflate_string (ctx, string);
if (_csi_unlikely (bytes == NULL))
return _csi_error (CSI_STATUS_NO_MEMORY);
} else {
bytes = tmpl.bytes;
}
pattern = FcNameParse (bytes);
if (bytes != tmpl.bytes)
_csi_free (ctx, bytes);
resolved = pattern;
if (cairo_version () < CAIRO_VERSION_ENCODE (1, 9, 0)) {
resolved = FcFontMatch (NULL, pattern, NULL);
if (_csi_unlikely (resolved == NULL)) {
FcPatternDestroy (pattern);
return _csi_error (CSI_STATUS_NO_MEMORY);
}
}
font_face = cairo_ft_font_face_create_for_pattern (resolved);
FcPatternDestroy (resolved);
if (resolved != pattern)
FcPatternDestroy (pattern);
data = _csi_slab_alloc (ctx, sizeof (*data));
ctx->_faces = _csi_list_prepend (ctx->_faces, &data->blob.list);
data->ctx = cairo_script_interpreter_reference (ctx);
data->blob.hash = tmpl.hash;
data->blob.len = tmpl.len;
data->bytes = NULL;
data->face = NULL;
#ifdef HAVE_MMAP
vec.bytes = tmpl.bytes;
vec.num_bytes = tmpl.len;
data->blob.bytes = _mmap_bytes (&vec, 1);
if (data->blob.bytes != MAP_FAILED) {
data->source = NULL;
if (--string->base.ref == 0)
csi_string_free (ctx, string);
} else {
data->blob.bytes = tmpl.bytes;
data->source = string;
}
#else
data->blob.bytes = tmpl.bytes;
data->source = string;
#endif
status = cairo_font_face_set_user_data (font_face,
&_csi_blob_key,
data, _ft_done_face);
if (_csi_unlikely (status)) {
_ft_done_face (data);
cairo_font_face_destroy (font_face);
return status;
}
data->font_face = font_face;
*font_face_out = font_face;
return CSI_STATUS_SUCCESS;
#else
if (--string->base.ref == 0)
csi_string_free (ctx, string);
return CSI_INT_STATUS_UNSUPPORTED;
#endif
}
static csi_status_t
_ft_type42_create (csi_t *ctx,
csi_dictionary_t *font,
cairo_font_face_t **font_face_out)
{
csi_object_t key;
csi_status_t status;
status = csi_name_new_static (ctx, &key, "pattern");
if (_csi_unlikely (status))
return status;
if (csi_dictionary_has (font, key.datum.name)) {
csi_object_t obj;
int type;
status = csi_dictionary_get (ctx, font, key.datum.name, &obj);
if (_csi_unlikely (status))
return status;
type = csi_object_get_type (&obj);
switch (type) {
case CSI_OBJECT_TYPE_FILE:
status = _csi_file_as_string (ctx, obj.datum.file, &obj);
if (_csi_unlikely (status))
return status;
break;
case CSI_OBJECT_TYPE_STRING:
obj.datum.object->ref++;
break;
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
return _ft_create_for_pattern (ctx,
obj.datum.string,
font_face_out);
}
status = csi_name_new_static (ctx, &key, "source");
if (_csi_unlikely (status))
return status;
if (csi_dictionary_has (font, key.datum.name)) {
csi_object_t obj;
long index, flags;
int type;
index = 0;
status = _csi_dictionary_get_integer (ctx, font, "index", TRUE, &index);
if (_csi_unlikely (status))
return status;
flags = 0;
status = _csi_dictionary_get_integer (ctx, font, "flags", TRUE, &flags);
if (_csi_unlikely (status))
return status;
status = csi_name_new_static (ctx, &key, "source");
if (_csi_unlikely (status))
return status;
status = csi_dictionary_get (ctx, font, key.datum.name, &obj);
if (_csi_unlikely (status))
return status;
type = csi_object_get_type (&obj);
switch (type) {
case CSI_OBJECT_TYPE_FILE:
status = _csi_file_as_string (ctx, obj.datum.file, &obj);
if (_csi_unlikely (status))
return status;
break;
case CSI_OBJECT_TYPE_STRING:
obj.datum.object->ref++;
break;
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
return _ft_create_for_source (ctx, obj.datum.string,
index, flags,
font_face_out);
}
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
#else
#define _ft_type42_create(ctx, font, face_out) CSI_INT_STATUS_UNSUPPORTED
#endif
static char *
_fc_strcpy (csi_t *ctx, const char *str)
{
char *ret;
int len;
ret = strchr (str, ':');
if (ret != NULL)
len = ret - str;
else
len = strlen (str);
ret = _csi_alloc (ctx, len+1);
if (_csi_unlikely (ret == NULL))
return NULL;
memcpy (ret, str, len);
ret[len] = '\0';
return ret;
}
static cairo_font_face_t *
_select_font (const char *name)
{
cairo_surface_t *surface;
cairo_font_face_t *face;
cairo_t *cr;
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
cr = cairo_create (surface);
cairo_surface_destroy (surface);
cairo_select_font_face (cr, name,
CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
face = cairo_font_face_reference (cairo_get_font_face (cr));
cairo_destroy (cr);
return face;
}
static csi_status_t
_ft_fallback_create_for_pattern (csi_t *ctx,
csi_string_t *string,
cairo_font_face_t **font_face_out)
{
char *str, *name;
str = string->string;
#if 0
name = strstr (str, "fullname=");
if (name != NULL)
str = name + 9;
#endif
name = _fc_strcpy (ctx, str);
if (_csi_unlikely (name == NULL))
return _csi_error (CSI_STATUS_NO_MEMORY);
*font_face_out = _select_font (name);
_csi_free (ctx, name);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_ft_type42_fallback_create (csi_t *ctx,
csi_dictionary_t *font,
cairo_font_face_t **font_face_out)
{
csi_object_t key;
csi_status_t status;
status = csi_name_new_static (ctx, &key, "pattern");
if (_csi_unlikely (status))
return status;
if (csi_dictionary_has (font, key.datum.name)) {
csi_object_t obj;
int type;
status = csi_dictionary_get (ctx, font, key.datum.name, &obj);
if (_csi_unlikely (status))
return status;
type = csi_object_get_type (&obj);
switch (type) {
case CSI_OBJECT_TYPE_FILE:
status = _csi_file_as_string (ctx, obj.datum.file, &obj);
if (_csi_unlikely (status))
return status;
break;
case CSI_OBJECT_TYPE_STRING:
obj.datum.object->ref++;
break;
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
return _ft_fallback_create_for_pattern (ctx,
obj.datum.string,
font_face_out);
}
*font_face_out = _select_font ("Sans");
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_font_type42 (csi_t *ctx, csi_dictionary_t *font, cairo_font_face_t **font_face)
{
csi_status_t status;
status = _ft_type42_create (ctx, font, font_face);
if (_csi_likely (status != CSI_INT_STATUS_UNSUPPORTED))
return status;
return _ft_type42_fallback_create (ctx, font, font_face);
}
static csi_status_t
_font (csi_t *ctx)
{
csi_dictionary_t *font;
csi_status_t status;
cairo_font_face_t *font_face = NULL;
csi_proxy_t *proxy;
csi_object_t obj;
long type;
check (1);
status = _csi_ostack_get_dictionary (ctx, 0, &font);
if (_csi_unlikely (status))
return status;
status = _csi_dictionary_get_integer (ctx, font, "type", FALSE, &type);
if (_csi_unlikely (status))
return status;
switch (type) {
case 3:
status = _font_type3 (ctx, font, &font_face);
break;
case 42:
status = _font_type42 (ctx, font, &font_face);
break;
default:
status = _csi_error (CSI_STATUS_INVALID_SCRIPT);
break;
}
if (_csi_unlikely (status))
return status;
proxy = _csi_proxy_create (ctx, font_face, font, NULL, NULL);
if (_csi_unlikely (proxy == NULL)) {
cairo_font_face_destroy (font_face);
return _csi_error (CSI_STATUS_NO_MEMORY);
}
status = cairo_font_face_set_user_data (font_face,
&_csi_proxy_key,
proxy, _csi_proxy_destroy);
if (_csi_unlikely (status)) {
_csi_proxy_destroy (proxy);
cairo_font_face_destroy (font_face);
return status;
}
obj.type = CSI_OBJECT_TYPE_FONT;
obj.datum.font_face = font_face;
pop (1);
status = push (&obj);
if (_csi_unlikely (status)) {
cairo_font_face_destroy (font_face);
return status;
}
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_for (csi_t *ctx)
{
csi_array_t *proc;
csi_status_t status;
long i, inc, limit;
check (4);
status = _csi_ostack_get_procedure (ctx, 0, &proc);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_integer (ctx, 1, &limit);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_integer (ctx, 2, &inc);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_integer (ctx, 3, &i);
if (_csi_unlikely (status))
return status;
proc->base.ref++;
pop (4);
for (; i <= limit; i += inc) {
status = _csi_push_ostack_integer (ctx, i);
if (_csi_unlikely (status))
break;
status = _csi_array_execute (ctx, proc);
if (_csi_unlikely (status))
break;
}
if (--proc->base.ref == 0)
csi_array_free (ctx, proc);
return status;
}
static csi_status_t
_ge (csi_t *ctx)
{
csi_status_t status;
csi_object_t *a, *b;
int cmp;
check (2);
b = _csi_peek_ostack (ctx, 0);
a = _csi_peek_ostack (ctx, 1);
status = csi_object_compare (a, b, &cmp);
if (_csi_unlikely (status))
return status;
pop (2);
return _csi_push_ostack_boolean (ctx, cmp >= 0);
}
static csi_status_t
_proxy_get (csi_proxy_t *proxy,
csi_name_t key)
{
csi_object_t obj;
csi_status_t status;
if (_csi_unlikely (proxy == NULL || proxy->dictionary == NULL))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
status = csi_dictionary_get (proxy->ctx, proxy->dictionary, key, &obj);
if (_csi_unlikely (status))
return status;
return _csi_push_ostack_copy (proxy->ctx, &obj);
}
static csi_status_t
_context_get (csi_t *ctx,
cairo_t *cr,
csi_name_t key)
{
csi_status_t status;
csi_object_t obj;
if (strcmp ((char *) key, "current-point") == 0) {
double x, y;
cairo_get_current_point (cr, &x, &y);
status = _csi_push_ostack_real (ctx, x);
if (_csi_unlikely (status))
return status;
status = _csi_push_ostack_real (ctx, y);
if (_csi_unlikely (status))
return status;
return CSI_STATUS_SUCCESS;
} else if (strcmp ((char *) key, "source") == 0) {
obj.type = CSI_OBJECT_TYPE_PATTERN;
obj.datum.pattern = cairo_pattern_reference (cairo_get_source (cr));
} else if (strcmp ((char *) key, "target") == 0) {
obj.type = CSI_OBJECT_TYPE_SURFACE;
obj.datum.surface = cairo_surface_reference (cairo_get_target (cr));
} else if (strcmp ((char *) key, "group-target") == 0) {
obj.type = CSI_OBJECT_TYPE_SURFACE;
obj.datum.surface = cairo_surface_reference (cairo_get_group_target (cr));
} else if (strcmp ((char *) key, "scaled-font") == 0) {
obj.type = CSI_OBJECT_TYPE_SCALED_FONT;
obj.datum.scaled_font = cairo_scaled_font_reference (cairo_get_scaled_font (cr));
} else if (strcmp ((char *) key, "font-face") == 0) {
obj.type = CSI_OBJECT_TYPE_FONT;
obj.datum.font_face = cairo_font_face_reference (cairo_get_font_face (cr));
} else
return _proxy_get (cairo_get_user_data (cr, &_csi_proxy_key), key);
return push (&obj);
}
static csi_status_t
_font_get (csi_t *ctx,
cairo_font_face_t *font_face,
csi_name_t key)
{
return _proxy_get (cairo_font_face_get_user_data (font_face,
&_csi_proxy_key),
key);
}
static csi_status_t
_pattern_get (csi_t *ctx,
cairo_pattern_t *pattern,
csi_name_t key)
{
csi_status_t status;
if (strcmp ((char *) key, "type") == 0)
return _csi_push_ostack_integer (ctx, cairo_pattern_get_type (pattern));
if (strcmp ((char *) key, "filter") == 0)
return _csi_push_ostack_integer (ctx, cairo_pattern_get_filter (pattern));
if (strcmp ((char *) key, "extend") == 0)
return _csi_push_ostack_integer (ctx, cairo_pattern_get_extend (pattern));
if (strcmp ((char *) key, "matrix") == 0) {
csi_object_t obj;
cairo_matrix_t m;
cairo_pattern_get_matrix (pattern, &m);
status = csi_matrix_new_from_matrix (ctx, &obj, &m);
if (_csi_unlikely (status))
return status;
return push (&obj);
}
return _proxy_get (cairo_pattern_get_user_data (pattern, &_csi_proxy_key),
key);
}
static csi_status_t
_scaled_font_get (csi_t *ctx,
cairo_scaled_font_t *font,
csi_name_t key)
{
return _proxy_get (cairo_scaled_font_get_user_data (font, &_csi_proxy_key),
key);
}
static csi_status_t
_surface_get (csi_t *ctx,
cairo_surface_t *surface,
csi_name_t key)
{
if (strcmp ((char *) key, "type") == 0) {
return _csi_push_ostack_integer (ctx, cairo_surface_get_type (surface));
}
if (strcmp ((char *) key, "content") == 0) {
return _csi_push_ostack_integer (ctx,
cairo_surface_get_content (surface));
}
return _proxy_get (cairo_surface_get_user_data (surface, &_csi_proxy_key),
key);
}
static csi_status_t
_get (csi_t *ctx)
{
csi_object_t *key, *src, obj;
csi_status_t status;
int type;
check (2);
key = _csi_peek_ostack (ctx, 0);
src = _csi_peek_ostack (ctx, 1);
pop (1);
type = csi_object_get_type (src);
switch (type) {
case CSI_OBJECT_TYPE_DICTIONARY:
if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_NAME))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
status = csi_dictionary_get (ctx,
src->datum.dictionary,
key->datum.name,
&obj);
break;
case CSI_OBJECT_TYPE_ARRAY:
if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_INTEGER))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
status = csi_array_get (ctx,
src->datum.array,
key->datum.integer,
&obj);
break;
#if 0
case CSI_OBJECT_TYPE_STRING:
status = csi_string_get (src, key, &obj);
break;
#endif
case CSI_OBJECT_TYPE_CONTEXT:
if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_NAME))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
return _context_get (ctx, src->datum.cr, key->datum.name);
case CSI_OBJECT_TYPE_FONT:
if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_NAME))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
return _font_get (ctx, src->datum.font_face, key->datum.name);
case CSI_OBJECT_TYPE_PATTERN:
if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_NAME))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
return _pattern_get (ctx, src->datum.pattern, key->datum.name);
case CSI_OBJECT_TYPE_SCALED_FONT:
if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_NAME))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
return _scaled_font_get (ctx, src->datum.scaled_font, key->datum.name);
case CSI_OBJECT_TYPE_SURFACE:
if (_csi_unlikely (csi_object_get_type (key) != CSI_OBJECT_TYPE_NAME))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
return _surface_get (ctx, src->datum.surface, key->datum.name);
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
if (_csi_unlikely (status))
return status;
return _csi_push_ostack_copy (ctx, &obj);
}
struct glyph_advance_cache {
csi_t *ctx;
double glyph_advance[256][2];
unsigned long have_glyph_advance[256];
};
static void
glyph_advance_cache_destroy (void *closure)
{
struct glyph_advance_cache *cache = closure;
_csi_free (cache->ctx, cache);
}
static int
_glyph_string (csi_t *ctx,
csi_array_t *array,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs)
{
struct glyph_advance_cache uncached;
struct glyph_advance_cache *cache;
csi_integer_t nglyphs, i, j;
double x, y, dx;
cairo_status_t status;
if (cairo_scaled_font_status (scaled_font))
return 0;
cache = cairo_scaled_font_get_user_data (scaled_font,
(cairo_user_data_key_t *) ctx);
if (cache == NULL) {
cache = _csi_alloc (ctx, sizeof (*cache));
if (_csi_likely (cache != NULL)) {
cache->ctx = ctx;
memset (cache->have_glyph_advance, 0xff,
sizeof (cache->have_glyph_advance));
status = cairo_scaled_font_set_user_data (scaled_font,
(cairo_user_data_key_t *) ctx,
cache,
glyph_advance_cache_destroy);
if (_csi_unlikely (status)) {
_csi_free (ctx, cache);
cache = NULL;
}
}
}
if (_csi_unlikely (cache == NULL)) {
cache = &uncached;
cache->ctx = ctx;
memset (cache->have_glyph_advance, 0xff,
sizeof (cache->have_glyph_advance));
}
nglyphs = 0;
x = y = 0;
for (i = 0; i < array->stack.len; i++) {
const csi_object_t *obj = &array->stack.objects[i];
int type = csi_object_get_type (obj);
switch (type) {
case CSI_OBJECT_TYPE_ARRAY: {
const csi_array_t *glyph_array = obj->datum.array;
for (j = 0; j < glyph_array->stack.len; j++) {
unsigned long g;
int gi;
obj = &glyph_array->stack.objects[j];
if (csi_object_get_type (obj) != CSI_OBJECT_TYPE_INTEGER)
break;
g = obj->datum.integer;
glyphs[nglyphs].index = g;
glyphs[nglyphs].x = x;
glyphs[nglyphs].y = y;
gi = g % ARRAY_LENGTH (cache->have_glyph_advance);
if (cache->have_glyph_advance[gi] != g) {
cairo_text_extents_t extents;
cairo_scaled_font_glyph_extents (scaled_font,
&glyphs[nglyphs], 1,
&extents);
cache->glyph_advance[gi][0] = extents.x_advance;
cache->glyph_advance[gi][1] = extents.y_advance;
cache->have_glyph_advance[gi] = g;
}
x += cache->glyph_advance[gi][0];
y += cache->glyph_advance[gi][1];
nglyphs++;
}
break;
}
case CSI_OBJECT_TYPE_STRING: {
const csi_string_t *glyph_string = obj->datum.string;
for (j = 0; j < glyph_string->len; j++) {
uint8_t g;
g = glyph_string->string[j];
glyphs[nglyphs].index = g;
glyphs[nglyphs].x = x;
glyphs[nglyphs].y = y;
if (cache->have_glyph_advance[g] != g) {
cairo_text_extents_t extents;
cairo_scaled_font_glyph_extents (scaled_font,
&glyphs[nglyphs], 1,
&extents);
cache->glyph_advance[g][0] = extents.x_advance;
cache->glyph_advance[g][1] = extents.y_advance;
cache->have_glyph_advance[g] = g;
}
x += cache->glyph_advance[g][0];
y += cache->glyph_advance[g][1];
nglyphs++;
}
break;
}
case CSI_OBJECT_TYPE_INTEGER:
case CSI_OBJECT_TYPE_REAL:
dx = csi_number_get_value (obj);
if (i+1 == array->stack.len)
break;
type = csi_object_get_type (&array->stack.objects[i+1]);
switch (type) {
case CSI_OBJECT_TYPE_INTEGER:
case CSI_OBJECT_TYPE_REAL:
y = csi_number_get_value (&array->stack.objects[i+1]);
x = dx;
i++;
break;
default:
x += dx;
}
}
}
return nglyphs;
}
static csi_status_t
_glyph_path (csi_t *ctx)
{
csi_array_t *array;
csi_status_t status;
cairo_t *cr;
cairo_glyph_t stack_glyphs[256], *glyphs;
csi_integer_t nglyphs, i;
check (2);
status = _csi_ostack_get_array (ctx, 0, &array);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 1, &cr);
if (_csi_unlikely (status))
return status;
nglyphs = 0;
for (i = 0; i < array->stack.len; i++) {
csi_object_t *obj = &array->stack.objects[i];
int type = csi_object_get_type (obj);
switch (type) {
case CSI_OBJECT_TYPE_ARRAY:
nglyphs += obj->datum.array->stack.len;
break;
case CSI_OBJECT_TYPE_STRING:
nglyphs += obj->datum.string->len;
break;
}
}
if (nglyphs == 0) {
pop (1);
return CSI_STATUS_SUCCESS;
}
if (nglyphs > ARRAY_LENGTH (stack_glyphs)) {
if (_csi_unlikely ((unsigned) nglyphs >= INT_MAX / sizeof (cairo_glyph_t)))
return _csi_error (CSI_STATUS_NO_MEMORY);
glyphs = _csi_alloc (ctx, sizeof (cairo_glyph_t) * nglyphs);
if (_csi_unlikely (glyphs == NULL))
return _csi_error (CSI_STATUS_NO_MEMORY);
} else
glyphs = stack_glyphs;
nglyphs = _glyph_string (ctx, array, cairo_get_scaled_font (cr), glyphs);
cairo_glyph_path (cr, glyphs, nglyphs);
if (glyphs != stack_glyphs)
_csi_free (ctx, glyphs);
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_gray (csi_t *ctx)
{
csi_object_t obj;
csi_status_t status;
double g;
check (1);
status = _csi_ostack_get_number (ctx, 0, &g);
if (_csi_unlikely (status))
return status;
pop (1);
obj.type = CSI_OBJECT_TYPE_PATTERN;
obj.datum.pattern = cairo_pattern_create_rgba (g, g, g, 1);
return push (&obj);
}
static csi_status_t
_gt (csi_t *ctx)
{
csi_status_t status;
csi_object_t *a, *b;
int cmp;
check (2);
b = _csi_peek_ostack (ctx, 0);
a = _csi_peek_ostack (ctx, 1);
status = csi_object_compare (a, b, &cmp);
if (_csi_unlikely (status))
return status;
pop (2);
return _csi_push_ostack_boolean (ctx, cmp > 0);
}
static csi_status_t
_identity (csi_t *ctx)
{
csi_object_t obj;
csi_status_t status;
status = csi_matrix_new (ctx, &obj);
if (_csi_unlikely (status))
return status;
return push (&obj);
}
static csi_status_t
_if (csi_t *ctx)
{
csi_array_t *proc;
csi_boolean_t predicate = FALSE;
csi_status_t status;
check (2);
status = _csi_ostack_get_procedure (ctx, 0, &proc);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_boolean (ctx, 1, &predicate);
if (_csi_unlikely (status))
return status;
proc->base.ref++;
pop (2);
if (predicate)
status = _csi_array_execute (ctx, proc);
if (--proc->base.ref == 0)
csi_array_free (ctx, proc);
return status;
}
static csi_status_t
_ifelse (csi_t *ctx)
{
csi_array_t *true_proc, *false_proc;
csi_boolean_t predicate = FALSE;
csi_status_t status;
check (3);
status = _csi_ostack_get_procedure (ctx, 0, &false_proc);
if (_csi_unlikely (status))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
status = _csi_ostack_get_procedure (ctx, 1, &true_proc);
if (_csi_unlikely (status))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
status = _csi_ostack_get_boolean (ctx, 2, &predicate);
if (_csi_unlikely (status))
return status;
true_proc->base.ref++;
false_proc->base.ref++;
pop (3);
if (predicate)
status = _csi_array_execute (ctx, true_proc);
else
status = _csi_array_execute (ctx, false_proc);
if (--true_proc->base.ref == 0)
csi_array_free (ctx, true_proc);
if (--false_proc->base.ref == 0)
csi_array_free (ctx, false_proc);
return status;
}
static csi_status_t
_image_read_raw (csi_file_t *src,
cairo_format_t format,
int width, int height,
cairo_surface_t **image_out)
{
cairo_surface_t *image;
uint8_t *bp, *data;
int rem, len, ret, x, rowlen, stride;
cairo_status_t status;
stride = cairo_format_stride_for_width (format, width);
data = malloc (stride * height);
if (data == NULL)
return CAIRO_STATUS_NO_MEMORY;
image = cairo_image_surface_create_for_data (data, format,
width, height, stride);
status = cairo_surface_set_user_data (image,
(const cairo_user_data_key_t *) image,
data, free);
if (status) {
cairo_surface_destroy (image);
free (image);
return status;
}
switch (format) {
case CAIRO_FORMAT_A1:
rowlen = (width+7)/8;
break;
case CAIRO_FORMAT_A8:
rowlen = width;
break;
case CAIRO_FORMAT_RGB16_565:
rowlen = 2 * width;
break;
case CAIRO_FORMAT_RGB24:
rowlen = 3 * width;
break;
default:
case CAIRO_FORMAT_INVALID:
case CAIRO_FORMAT_ARGB32:
rowlen = 4 * width;
break;
}
len = rowlen * height;
bp = data;
rem = len;
while (rem) {
ret = csi_file_read (src, bp, rem);
if (_csi_unlikely (ret == 0)) {
cairo_surface_destroy (image);
return _csi_error (CSI_STATUS_READ_ERROR);
}
rem -= ret;
bp += ret;
}
if (len != height * stride) {
while (--height) {
uint8_t *row = data + height * stride;
switch (format) {
case CAIRO_FORMAT_A1:
for (x = rowlen; x--; ) {
uint8_t byte = *--bp;
row[x] = CSI_BITSWAP8_IF_LITTLE_ENDIAN (byte);
}
break;
case CAIRO_FORMAT_A8:
for (x = width; x--; )
row[x] = *--bp;
break;
case CAIRO_FORMAT_RGB16_565:
for (x = width; x--; ) {
#ifdef WORDS_BIGENDIAN
row[2*x + 1] = *--bp;
row[2*x + 0] = *--bp;
#else
row[2*x + 0] = *--bp;
row[2*x + 1] = *--bp;
#endif
}
break;
case CAIRO_FORMAT_RGB24:
for (x = width; x--; ) {
#ifdef WORDS_BIGENDIAN
row[4*x + 3] = *--bp;
row[4*x + 2] = *--bp;
row[4*x + 1] = *--bp;
row[4*x + 0] = 0xff;
#else
row[4*x + 0] = *--bp;
row[4*x + 1] = *--bp;
row[4*x + 2] = *--bp;
row[4*x + 3] = 0xff;
#endif
}
break;
case CAIRO_FORMAT_INVALID:
case CAIRO_FORMAT_ARGB32:
break;
}
memset (row + rowlen, 0, stride - rowlen);
}
switch (format) {
case CAIRO_FORMAT_A1:
for (x = rowlen; x--; ) {
uint8_t byte = *--bp;
data[x] = CSI_BITSWAP8_IF_LITTLE_ENDIAN (byte);
}
break;
case CAIRO_FORMAT_A8:
for (x = width; x--; )
data[x] = *--bp;
break;
case CAIRO_FORMAT_RGB16_565:
for (x = width; x--; ) {
#ifdef WORDS_BIGENDIAN
data[2*x + 1] = *--bp;
data[2*x + 0] = *--bp;
#else
data[2*x + 0] = *--bp;
data[2*x + 1] = *--bp;
#endif
}
break;
case CAIRO_FORMAT_RGB24:
for (x = width; --x>1; ) {
#ifdef WORDS_BIGENDIAN
data[4*x + 3] = *--bp;
data[4*x + 2] = *--bp;
data[4*x + 1] = *--bp;
data[4*x + 0] = 0xff;
#else
data[4*x + 0] = *--bp;
data[4*x + 1] = *--bp;
data[4*x + 2] = *--bp;
data[4*x + 3] = 0xff;
#endif
}
if (width > 1) {
uint8_t rgb[2][3];
rgb[1][0] = data[5];
rgb[1][1] = data[4];
rgb[1][2] = data[3];
rgb[0][0] = data[2];
rgb[0][1] = data[1];
rgb[0][2] = data[0];
#ifdef WORDS_BIGENDIAN
data[4] = 0xff;
data[5] = rgb[1][2];
data[6] = rgb[1][1];
data[7] = rgb[1][0];
data[0] = 0xff;
data[1] = rgb[0][2];
data[2] = rgb[0][1];
data[3] = rgb[0][0];
#else
data[7] = 0xff;
data[6] = rgb[1][2];
data[5] = rgb[1][1];
data[4] = rgb[1][0];
data[3] = 0xff;
data[2] = rgb[0][2];
data[1] = rgb[0][1];
data[0] = rgb[0][0];
#endif
} else {
#ifdef WORDS_BIGENDIAN
data[0] = 0xff;
data[1] = data[0];
data[2] = data[1];
data[3] = data[2];
#else
data[3] = data[0];
data[0] = data[2];
data[2] = data[3];
data[3] = 0xff;
#endif
}
break;
case CAIRO_FORMAT_INVALID:
case CAIRO_FORMAT_ARGB32:
break;
}
memset (data + rowlen, 0, stride - rowlen);
} else {
#ifndef WORDS_BIGENDIAN
switch (format) {
case CAIRO_FORMAT_A1:
for (x = 0; x < len; x++) {
uint8_t byte = data[x];
data[x] = CSI_BITSWAP8_IF_LITTLE_ENDIAN (byte);
}
break;
case CAIRO_FORMAT_RGB16_565:
{
uint32_t *rgba = (uint32_t *) data;
for (x = len/2; x--; rgba++) {
*rgba = bswap_16 (*rgba);
}
}
break;
case CAIRO_FORMAT_ARGB32:
{
uint32_t *rgba = (uint32_t *) data;
for (x = len/4; x--; rgba++) {
*rgba = bswap_32 (*rgba);
}
}
break;
case CAIRO_FORMAT_A8:
break;
case CAIRO_FORMAT_RGB24:
case CAIRO_FORMAT_INVALID:
default:
break;
}
#endif
}
cairo_surface_mark_dirty (image);
*image_out = image;
return CSI_STATUS_SUCCESS;
}
static cairo_status_t
png_read_func (void *closure, uint8_t *data, unsigned int len)
{
int ret;
ret = csi_file_read (closure, data, len);
if ((unsigned int) ret != len)
return CAIRO_STATUS_READ_ERROR;
return CAIRO_STATUS_SUCCESS;
}
static csi_status_t
_image_read_png (csi_file_t *src, cairo_surface_t **out)
{
#if CAIRO_HAS_PNG_FUNCTIONS
*out = cairo_image_surface_create_from_png_stream (png_read_func, src);
return cairo_surface_status (*out);
#else
return CAIRO_STATUS_READ_ERROR;
#endif
}
struct _image_tag {
csi_t *ctx;
csi_blob_t blob;
cairo_surface_t *surface;
};
static void
_image_tag_done (void *closure)
{
struct _image_tag *tag = closure;
csi_t *ctx = tag->ctx;
ctx->_images = _csi_list_unlink (ctx->_images, &tag->blob.list);
_csi_slab_free (ctx, tag, sizeof (*tag));
cairo_script_interpreter_destroy (ctx);
}
static void
_image_hash (csi_blob_t *blob,
cairo_surface_t *surface)
{
uint32_t value;
value = cairo_image_surface_get_width (surface);
_csi_blob_hash (blob, &value, 1);
value = cairo_image_surface_get_height (surface);
_csi_blob_hash (blob, &value, 1);
value = cairo_image_surface_get_format (surface);
_csi_blob_hash (blob, &value, 1);
}
static cairo_surface_t *
_image_cached (csi_t *ctx, cairo_surface_t *surface)
{
csi_blob_t tmpl;
csi_list_t *link;
uint8_t *data;
int stride, height;
struct _image_tag *tag;
data = cairo_image_surface_get_data (surface);
stride = cairo_image_surface_get_stride (surface);
height = cairo_image_surface_get_height (surface);
_csi_blob_init (&tmpl, data, stride * height);
_image_hash (&tmpl, surface);
link = _csi_list_find (ctx->_images, _csi_blob_equal, &tmpl);
if (link) {
cairo_surface_destroy (surface);
tag = csi_container_of (link, struct _image_tag, blob.list);
return cairo_surface_reference (tag->surface);
}
tag = _csi_slab_alloc (ctx, sizeof (struct _image_tag));
if (tag == NULL)
return surface;
ctx->_images = _csi_list_prepend (ctx->_images, &tag->blob.list);
tag->ctx = cairo_script_interpreter_reference (ctx);
tag->blob.hash = tmpl.hash;
tag->blob.bytes = tmpl.bytes;
tag->blob.len = tmpl.len;
tag->surface = surface;
if (cairo_surface_set_user_data (surface, &_csi_blob_key,
tag, _image_tag_done))
{
_image_tag_done (tag);
}
return surface;
}
static csi_status_t
_image_load_from_dictionary (csi_t *ctx,
csi_dictionary_t *dict,
cairo_surface_t **image_out)
{
csi_object_t obj, key;
long width;
long height;
long format;
cairo_surface_t *image = NULL;
csi_status_t status;
status = _csi_dictionary_get_integer (ctx, dict, "width", FALSE, &width);
if (_csi_unlikely (status))
return status;
status = _csi_dictionary_get_integer (ctx, dict, "height", FALSE, &height);
if (_csi_unlikely (status))
return status;
format = CAIRO_FORMAT_ARGB32;
status = _csi_dictionary_get_integer (ctx, dict, "format", TRUE, &format);
if (_csi_unlikely (status))
return status;
status = csi_name_new_static (ctx, &key, "source");
if (_csi_unlikely (status))
return status;
if (csi_dictionary_has (dict, key.datum.name)) {
enum mime_type mime_type;
csi_object_t file;
status = csi_dictionary_get (ctx, dict, key.datum.name, &obj);
if (_csi_unlikely (status))
return status;
status = csi_name_new_static (ctx, &key, "mime-type");
if (_csi_unlikely (status))
return status;
mime_type = MIME_TYPE_NONE;
if (csi_dictionary_has (dict, key.datum.name)) {
csi_object_t type_obj;
const char *type_str;
int type;
status = csi_dictionary_get (ctx, dict, key.datum.name, &type_obj);
if (_csi_unlikely (status))
return status;
type = csi_object_get_type (&type_obj);
switch (type) {
case CSI_OBJECT_TYPE_STRING:
type_str = type_obj.datum.string->string;
break;
case CSI_OBJECT_TYPE_NAME:
type_str = (char *) type_obj.datum.name;
break;
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
if (strcmp (type_str, CAIRO_MIME_TYPE_PNG) == 0)
mime_type = MIME_TYPE_PNG;
}
status = csi_object_as_file (ctx, &obj, &file);
if (_csi_unlikely (status))
return status;
switch (mime_type) {
case MIME_TYPE_NONE:
status = _image_read_raw (file.datum.file,
format, width, height, &image);
break;
case MIME_TYPE_PNG:
status = _image_read_png (file.datum.file, &image);
break;
}
csi_object_free (ctx, &file);
if (_csi_unlikely (status))
return status;
image = _image_cached (ctx, image);
} else
image = cairo_image_surface_create (format, width, height);
*image_out = image;
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_image (csi_t *ctx)
{
csi_dictionary_t *dict;
cairo_surface_t *image;
csi_status_t status;
csi_object_t obj;
check (1);
status = _csi_ostack_get_dictionary (ctx, 0, &dict);
if (_csi_unlikely (status))
return status;
status = _image_load_from_dictionary (ctx, dict, &image);
if (_csi_unlikely (status))
return status;
pop (1);
obj.type = CSI_OBJECT_TYPE_SURFACE;
obj.datum.surface = image;
return push (&obj);
}
static csi_status_t
_index (csi_t *ctx)
{
csi_status_t status;
long n;
check (1);
status = _csi_ostack_get_integer (ctx, 0, &n);
if (_csi_unlikely (status))
return status;
pop (1);
check (n);
return _csi_push_ostack_copy (ctx, _csi_peek_ostack (ctx, n));
}
static csi_status_t
_integer (csi_t *ctx)
{
csi_object_t *obj;
int type;
check (1);
obj = _csi_peek_ostack (ctx, 0);
type = csi_object_get_type (obj);
switch (type) {
case CSI_OBJECT_TYPE_INTEGER:
break;
case CSI_OBJECT_TYPE_REAL:
obj->datum.integer = obj->datum.real;
break;
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
obj->type = CSI_OBJECT_TYPE_INTEGER;
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_invert (csi_t *ctx)
{
csi_object_t obj;
csi_status_t status;
cairo_matrix_t m;
check (1);
status = _csi_ostack_get_matrix (ctx, 0, &m);
if (_csi_unlikely (status))
return status;
cairo_matrix_invert (&m);
status = csi_matrix_new_from_matrix (ctx, &obj, &m);
if (_csi_unlikely (status))
return status;
pop (1);
return push (&obj);
}
static csi_status_t
_le (csi_t *ctx)
{
csi_status_t status;
csi_object_t *a, *b;
int cmp;
check (2);
b = _csi_peek_ostack (ctx, 0);
a = _csi_peek_ostack (ctx, 1);
status = csi_object_compare (a, b, &cmp);
if (_csi_unlikely (status))
return status;
pop (2);
return _csi_push_ostack_boolean (ctx, cmp <= 0);
}
static csi_status_t
_linear (csi_t *ctx)
{
csi_object_t obj;
csi_status_t status;
double x1, y1, x2, y2;
check (4);
status = _csi_ostack_get_number (ctx, 0, &y2);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 1, &x2);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 2, &y1);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 3, &x1);
if (_csi_unlikely (status))
return status;
pop (4);
obj.type = CSI_OBJECT_TYPE_PATTERN;
obj.datum.pattern = cairo_pattern_create_linear (x1, y1, x2, y2);
return push (&obj);
}
static csi_status_t
_line_to (csi_t *ctx)
{
csi_status_t status;
double x, y;
cairo_t *cr;
check (3);
status = _csi_ostack_get_number (ctx, 0, &y);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 1, &x);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 2, &cr);
if (_csi_unlikely (status))
return status;
pop (2);
cairo_line_to (cr, x, y);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_lt (csi_t *ctx)
{
csi_status_t status;
csi_object_t *a, *b;
int cmp;
check (2);
b = _csi_peek_ostack (ctx, 0);
a = _csi_peek_ostack (ctx, 1);
status = csi_object_compare (a, b, &cmp);
if (_csi_unlikely (status))
return status;
pop (2);
return _csi_push_ostack_boolean (ctx, cmp < 0);
}
static csi_status_t
_mark (csi_t *ctx)
{
return _csi_push_ostack_mark (ctx);
}
static csi_status_t
_ne (csi_t *ctx)
{
csi_object_t *a, *b;
csi_boolean_t v;
check (2);
b = _csi_peek_ostack (ctx, 0);
a = _csi_peek_ostack (ctx, 1);
v = ! csi_object_eq (a, b);
pop (2);
return _csi_push_ostack_boolean (ctx, v);
}
static csi_status_t
_neg (csi_t *ctx)
{
csi_object_t *obj;
int type;
check (1);
obj = _csi_peek_ostack (ctx, 0);
type = csi_object_get_type (obj);
switch (type) {
case CSI_OBJECT_TYPE_INTEGER:
obj->datum.integer = -obj->datum.integer;
break;
case CSI_OBJECT_TYPE_REAL:
obj->datum.real = -obj->datum.real;
break;
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_not (csi_t *ctx)
{
csi_object_t *obj;
int type;
check (1);
obj = _csi_peek_ostack (ctx, 0);
type = csi_object_get_type (obj);
switch (type) {
case CSI_OBJECT_TYPE_BOOLEAN:
obj->datum.boolean = ! obj->datum.boolean;
break;
case CSI_OBJECT_TYPE_INTEGER:
obj->type = CSI_OBJECT_TYPE_BOOLEAN;
obj->datum.boolean = ! obj->datum.integer;
break;
case CSI_OBJECT_TYPE_REAL:
obj->type = CSI_OBJECT_TYPE_BOOLEAN;
obj->datum.boolean = obj->datum.real == 0.0;
break;
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_new_path (csi_t *ctx)
{
return _do_cairo_op (ctx, cairo_new_path);
}
static csi_status_t
_new_sub_path (csi_t *ctx)
{
return _do_cairo_op (ctx, cairo_new_sub_path);
}
static csi_status_t
_null (csi_t *ctx)
{
return _csi_push_ostack_null (ctx);
}
static csi_status_t
_mask (csi_t *ctx)
{
cairo_t *cr;
cairo_pattern_t *pattern = NULL;
csi_status_t status;
check (2);
status = _csi_ostack_get_pattern (ctx, 0, &pattern);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 1, &cr);
if (_csi_unlikely (status))
return status;
cairo_mask (cr, pattern);
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_matrix (csi_t *ctx)
{
csi_object_t *obj, matrix;
double v[6];
csi_status_t status;
int n;
check (1);
obj = _csi_peek_ostack (ctx, 0);
if (csi_object_is_number (obj)) {
check (6);
for (n = 6; n--; ) {
status = _csi_ostack_get_number (ctx, 5-n, &v[n]);
if (_csi_unlikely (status))
return status;
}
status = csi_matrix_new_from_values (ctx, &matrix, v);
if (_csi_unlikely (status))
return status;
pop (6);
} else {
csi_array_t *array;
status = _csi_ostack_get_array (ctx, 0, &array);
if (_csi_unlikely (status))
return status;
status = csi_matrix_new_from_array (ctx, &matrix, array);
if (_csi_unlikely (status))
return status;
pop (1);
}
return push (&matrix);
}
static csi_status_t
_mod (csi_t *ctx)
{
csi_integer_t x, y;
csi_status_t status;
check (2);
status = _csi_ostack_get_integer (ctx, 0, &y);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_integer (ctx, 1, &x);
if (_csi_unlikely (status))
return status;
pop (2);
return _csi_push_ostack_integer (ctx, x % y);
}
static csi_status_t
_move_to (csi_t *ctx)
{
csi_status_t status;
double x, y;
cairo_t *cr;
check (3);
status = _csi_ostack_get_number (ctx, 0, &y);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 1, &x);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 2, &cr);
if (_csi_unlikely (status))
return status;
pop (2);
cairo_move_to (cr, x, y);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_mul (csi_t *ctx)
{
csi_object_t *A;
csi_object_t *B;
csi_object_type_t type_a, type_b;
check (2);
B = _csi_peek_ostack (ctx, 0);
A = _csi_peek_ostack (ctx, 1);
type_a = csi_object_get_type (A);
if (_csi_unlikely (! (type_a == CSI_OBJECT_TYPE_INTEGER ||
type_a == CSI_OBJECT_TYPE_REAL)))
{
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
type_b = csi_object_get_type (B);
if (_csi_unlikely (! (type_b == CSI_OBJECT_TYPE_INTEGER ||
type_b == CSI_OBJECT_TYPE_REAL)))
{
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
pop (2);
if (type_a == CSI_OBJECT_TYPE_REAL &&
type_b == CSI_OBJECT_TYPE_REAL)
{
return _csi_push_ostack_real (ctx, A->datum.real * B->datum.real);
}
else if (type_a == CSI_OBJECT_TYPE_INTEGER &&
type_b == CSI_OBJECT_TYPE_INTEGER)
{
return _csi_push_ostack_integer (ctx,
A->datum.integer * B->datum.integer);
}
else
{
double v;
if (type_a == CSI_OBJECT_TYPE_REAL)
v = A->datum.real;
else
v = A->datum.integer;
if (type_b == CSI_OBJECT_TYPE_REAL)
v *= B->datum.real;
else
v *= B->datum.integer;
return _csi_push_ostack_real (ctx, v);
}
}
static csi_status_t
_or (csi_t *ctx)
{
csi_object_t *a, *b;
int type;
check (2);
a = _csi_peek_ostack (ctx, 0);
b = _csi_peek_ostack (ctx, 1);
if (_csi_unlikely (csi_object_get_type (a) != csi_object_get_type (b)))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
pop (2);
type = csi_object_get_type (a);
switch (type) {
case CSI_OBJECT_TYPE_INTEGER:
return _csi_push_ostack_integer (ctx,
a->datum.integer | b->datum.integer);
case CSI_OBJECT_TYPE_BOOLEAN:
return _csi_push_ostack_boolean (ctx,
a->datum.boolean | b->datum.boolean);
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
}
static csi_status_t
_paint (csi_t *ctx)
{
return _do_cairo_op (ctx, cairo_paint);
}
static csi_status_t
_paint_with_alpha (csi_t *ctx)
{
cairo_t *cr;
csi_status_t status;
double alpha;
check (2);
status = _csi_ostack_get_number (ctx, 0, &alpha);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 1, &cr);
if (_csi_unlikely (status))
return status;
cairo_paint_with_alpha (cr, alpha);
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_pattern (csi_t *ctx)
{
csi_object_t obj;
csi_status_t status;
cairo_surface_t *surface;
check (1);
status = _csi_ostack_get_surface (ctx, 0, &surface);
if (_csi_unlikely (status))
return status;
obj.type = CSI_OBJECT_TYPE_PATTERN;
obj.datum.pattern = cairo_pattern_create_for_surface (surface);
pop (1);
return push (&obj);
}
static csi_status_t
_pop (csi_t *ctx)
{
check (1);
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_pop_group (csi_t *ctx)
{
csi_object_t obj;
csi_status_t status;
cairo_t *cr;
check (1);
status = _csi_ostack_get_context (ctx, 0, &cr);
if (_csi_unlikely (status))
return status;
obj.type = CSI_OBJECT_TYPE_PATTERN;
obj.datum.pattern = cairo_pop_group (cr);
return push (&obj);
}
static csi_status_t
_push_group (csi_t *ctx)
{
csi_status_t status;
cairo_t *cr;
long content;
check (2);
status = _csi_ostack_get_integer (ctx, 0, &content);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 1, &cr);
if (_csi_unlikely (status))
return status;
cairo_push_group_with_content (cr, content);
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_radial (csi_t *ctx)
{
csi_object_t obj;
csi_status_t status;
double x1, y1, r1, x2, y2, r2;
check (6);
status = _csi_ostack_get_number (ctx, 0, &r2);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 1, &y2);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 2, &x2);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 3, &r1);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 4, &y1);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 5, &x1);
if (_csi_unlikely (status))
return status;
obj.type = CSI_OBJECT_TYPE_PATTERN;
obj.datum.pattern = cairo_pattern_create_radial (x1, y1, r1, x2, y2, r2);
pop (6);
return push (&obj);
}
static csi_status_t
_rectangle (csi_t *ctx)
{
csi_status_t status;
double x, y;
double w, h;
cairo_t *cr;
check (5);
status = _csi_ostack_get_number (ctx, 0, &h);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 1, &w);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 2, &y);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 3, &x);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 4, &cr);
if (_csi_unlikely (status))
return status;
cairo_rectangle (cr, x, y, w, h);
pop(4);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_rel_curve_to (csi_t *ctx)
{
csi_status_t status;
double x1, y1;
double x2, y2;
double x3, y3;
cairo_t *cr;
check (7);
status = _csi_ostack_get_number (ctx, 0, &y3);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 1, &x3);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 2, &y2);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 3, &x2);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 4, &y1);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 5, &x1);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 6, &cr);
if (_csi_unlikely (status))
return status;
cairo_rel_curve_to (cr, x1, y1, x2, y2, x3, y3);
pop (6);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_rel_line_to (csi_t *ctx)
{
csi_status_t status;
double x, y;
cairo_t *cr;
check (3);
status = _csi_ostack_get_number (ctx, 0, &y);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 1, &x);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 2, &cr);
if (_csi_unlikely (status))
return status;
cairo_rel_line_to (cr, x, y);
pop (2);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_rel_move_to (csi_t *ctx)
{
csi_status_t status;
double x, y;
cairo_t *cr;
check (3);
status = _csi_ostack_get_number (ctx, 0, &y);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 1, &x);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 2, &cr);
if (_csi_unlikely (status))
return status;
cairo_rel_move_to (cr, x, y);
pop (2);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_repeat (csi_t *ctx)
{
csi_array_t *proc;
csi_integer_t count;
csi_status_t status;
check (2);
status = _csi_ostack_get_procedure (ctx, 0, &proc);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_integer (ctx, 1, &count);
if (_csi_unlikely (status))
return status;
if (_csi_unlikely (count < 0))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
proc->base.ref++;
pop (2);
while (count--) {
status = _csi_array_execute (ctx, proc);
if (_csi_unlikely (status))
break;
}
if (--proc->base.ref == 0)
csi_array_free (ctx, proc);
return status;
}
static csi_status_t
_reset_clip (csi_t *ctx)
{
return _do_cairo_op (ctx, cairo_reset_clip);
}
static csi_status_t
_restore (csi_t *ctx)
{
return _do_cairo_op (ctx, cairo_restore);
}
static csi_status_t
_rgb (csi_t *ctx)
{
csi_object_t obj;
csi_status_t status;
double r,g,b;
check (3);
status = _csi_ostack_get_number (ctx, 0, &b);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 1, &g);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 2, &r);
if (_csi_unlikely (status))
return status;
obj.type = CSI_OBJECT_TYPE_PATTERN;
obj.datum.pattern = cairo_pattern_create_rgb (r, g, b);
pop (3);
return push (&obj);
}
static csi_status_t
_rgba (csi_t *ctx)
{
csi_object_t obj;
csi_status_t status;
double r,g,b,a;
check (4);
status = _csi_ostack_get_number (ctx, 0, &a);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 1, &b);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 2, &g);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 3, &r);
if (_csi_unlikely (status))
return status;
obj.type = CSI_OBJECT_TYPE_PATTERN;
obj.datum.pattern = cairo_pattern_create_rgba (r, g, b, a);
pop (4);
return push (&obj);
}
static csi_status_t
_roll (csi_t *ctx)
{
csi_status_t status;
long j, n;
check (2);
status = _csi_ostack_get_integer (ctx, 0, &j);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_integer (ctx, 1, &n);
if (_csi_unlikely (status))
return status;
pop (2);
check (n);
return _csi_stack_roll (ctx, &ctx->ostack, j, n);
}
static csi_status_t
_rotate (csi_t *ctx)
{
csi_object_t *obj;
csi_status_t status;
double theta;
int type;
check (2);
status = _csi_ostack_get_number (ctx, 0, &theta);
if (_csi_unlikely (status))
return status;
obj = _csi_peek_ostack (ctx, 1);
type = csi_object_get_type (obj);
switch (type) {
case CSI_OBJECT_TYPE_CONTEXT:
cairo_rotate (obj->datum.cr, theta);
break;
case CSI_OBJECT_TYPE_PATTERN:
{
cairo_matrix_t ctm;
cairo_pattern_get_matrix (obj->datum.pattern, &ctm);
cairo_matrix_rotate (&ctm, theta);
cairo_pattern_set_matrix (obj->datum.pattern, &ctm);
}
break;
case CSI_OBJECT_TYPE_MATRIX:
cairo_matrix_rotate (&obj->datum.matrix->matrix, theta);
break;
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_save (csi_t *ctx)
{
return _do_cairo_op (ctx, cairo_save);
}
static csi_status_t
_scale (csi_t *ctx)
{
csi_object_t *obj;
csi_status_t status;
double x, y;
int type;
check (3);
status = _csi_ostack_get_number (ctx, 0, &y);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 1, &x);
if (_csi_unlikely (status))
return status;
obj = _csi_peek_ostack (ctx, 2);
type = csi_object_get_type (obj);
switch (type) {
case CSI_OBJECT_TYPE_CONTEXT:
cairo_scale (obj->datum.cr, x, y);
break;
case CSI_OBJECT_TYPE_PATTERN:
{
cairo_matrix_t ctm;
cairo_pattern_get_matrix (obj->datum.pattern, &ctm);
cairo_matrix_scale (&ctm, x, y);
cairo_pattern_set_matrix (obj->datum.pattern, &ctm);
}
break;
case CSI_OBJECT_TYPE_MATRIX:
cairo_matrix_scale (&obj->datum.matrix->matrix, x, y);
break;
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
pop (2);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_font_options_load_from_dictionary (csi_t *ctx,
csi_dictionary_t *dict,
cairo_font_options_t *options)
{
const struct {
const char *key;
void (*setter) (cairo_font_options_t *, int val);
} properties[] = {
{ "antialias",
(void (*)(cairo_font_options_t *, int val))
cairo_font_options_set_antialias },
{ "subpixel-order",
(void (*)(cairo_font_options_t *, int val))
cairo_font_options_set_subpixel_order },
{ "hint-style",
(void (*)(cairo_font_options_t *, int val))
cairo_font_options_set_hint_style },
{ "hint-metrics",
(void (*)(cairo_font_options_t *, int val))
cairo_font_options_set_hint_metrics },
{ NULL, NULL },
}, *prop = properties;
while (prop->key != NULL) {
csi_object_t key, value;
csi_status_t status;
status = csi_name_new_static (ctx, &key, prop->key);
if (_csi_unlikely (status))
return status;
if (csi_dictionary_has (dict, key.datum.name)) {
status = csi_dictionary_get (ctx, dict, key.datum.name, &value);
if (_csi_unlikely (status))
return status;
if (_csi_unlikely (csi_object_get_type (&value) !=
CSI_OBJECT_TYPE_INTEGER))
{
csi_object_free (ctx, &value);
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
prop->setter (options, value.datum.integer);
}
prop++;
}
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_scaled_font (csi_t *ctx)
{
csi_object_t obj;
csi_dictionary_t *dict;
cairo_font_face_t *font_face = NULL;
cairo_matrix_t font_matrix, ctm;
cairo_font_options_t *options;
csi_status_t status;
check (4);
status = _csi_ostack_get_dictionary (ctx, 0, &dict);
if (_csi_unlikely (status))
return status;
options = cairo_font_options_create ();
status = _font_options_load_from_dictionary (ctx, dict, options);
if (_csi_unlikely (status)) {
cairo_font_options_destroy (options);
return status;
}
status = _csi_ostack_get_matrix (ctx, 1, &ctm);
if (_csi_unlikely (status)) {
cairo_font_options_destroy (options);
return status;
}
status = _csi_ostack_get_matrix (ctx, 2, &font_matrix);
if (_csi_unlikely (status)) {
cairo_font_options_destroy (options);
return status;
}
status = _csi_ostack_get_font_face (ctx, 3, &font_face);
if (_csi_unlikely (status)) {
cairo_font_options_destroy (options);
return status;
}
obj.type = CSI_OBJECT_TYPE_SCALED_FONT;
obj.datum.scaled_font = cairo_scaled_font_create (font_face,
&font_matrix,
&ctm,
options);
cairo_font_options_destroy (options);
pop (4);
return push (&obj);
}
static csi_status_t
_select_font_face (csi_t *ctx)
{
cairo_t *cr;
long weight;
long slant;
csi_string_t *family;
csi_status_t status;
check (4);
status = _csi_ostack_get_integer (ctx, 0, &weight);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_integer (ctx, 1, &slant);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_string (ctx, 2, &family);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 3, &cr);
if (_csi_unlikely (status))
return status;
cairo_select_font_face (cr, family->string, slant, weight);
pop (3);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_context_set (csi_t *ctx,
cairo_t *cr,
csi_name_t key,
csi_object_t *obj)
{
if (strcmp ((char *) key, "source") == 0) {
if (_csi_unlikely (csi_object_get_type (obj) !=
CSI_OBJECT_TYPE_PATTERN))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
cairo_set_source (cr, obj->datum.pattern);
return CSI_STATUS_SUCCESS;
}
if (strcmp ((char *) key, "scaled-font") == 0) {
if (_csi_unlikely (csi_object_get_type (obj) !=
CSI_OBJECT_TYPE_SCALED_FONT))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
cairo_set_scaled_font (cr, obj->datum.scaled_font);
return CSI_STATUS_SUCCESS;
}
if (strcmp ((char *) key, "font-face") == 0) {
if (_csi_unlikely (csi_object_get_type (obj) !=
CSI_OBJECT_TYPE_FONT))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
cairo_set_font_face (cr, obj->datum.font_face);
return CSI_STATUS_SUCCESS;
}
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
static csi_status_t
_set (csi_t *ctx)
{
csi_object_t *key, *value, *dst;
csi_status_t status;
int type;
check (3);
value = _csi_peek_ostack (ctx, 0);
key = _csi_peek_ostack (ctx, 1);
dst = _csi_peek_ostack (ctx, 2);
type = csi_object_get_type (dst);
switch (type) {
case CSI_OBJECT_TYPE_DICTIONARY:
if (_csi_unlikely (csi_object_get_type (key) !=
CSI_OBJECT_TYPE_NAME))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
status = csi_dictionary_put (ctx,
dst->datum.dictionary,
key->datum.name,
value);
break;
case CSI_OBJECT_TYPE_ARRAY:
if (_csi_unlikely (csi_object_get_type (key) !=
CSI_OBJECT_TYPE_INTEGER))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
status = csi_array_put (ctx,
dst->datum.array,
key->datum.integer,
value);
break;
case CSI_OBJECT_TYPE_CONTEXT:
if (_csi_unlikely (csi_object_get_type (key) !=
CSI_OBJECT_TYPE_NAME))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
status = _context_set (ctx,
dst->datum.cr,
key->datum.name,
value);
break;
case CSI_OBJECT_TYPE_STRING:
#if 0
status = csi_string_put (dst, key, value);
break;
#endif
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
pop (2);
return status;
}
static csi_status_t
_set_antialias (csi_t *ctx)
{
csi_status_t status;
cairo_t *cr;
long antialias;
check (2);
status = _csi_ostack_get_integer (ctx, 0, &antialias);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 1, &cr);
if (_csi_unlikely (status))
return status;
cairo_set_antialias (cr, antialias);
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_set_dash (csi_t *ctx)
{
csi_array_t *array;
csi_status_t status;
cairo_t *cr;
double offset;
check (3);
status = _csi_ostack_get_number (ctx, 0, &offset);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_array (ctx, 1, &array);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 2, &cr);
if (_csi_unlikely (status))
return status;
if (array->stack.len == 0) {
cairo_set_dash (cr, NULL, 0., 0.);
} else {
double stack_dashes[8];
double *dashes;
csi_integer_t n;
if (_csi_likely (array->stack.len < ARRAY_LENGTH (stack_dashes))) {
dashes = stack_dashes;
} else {
if (_csi_unlikely ((unsigned) array->stack.len >= INT_MAX / sizeof (double)))
return _csi_error (CSI_STATUS_NO_MEMORY);
dashes = _csi_alloc (ctx, sizeof (double) * array->stack.len);
if (_csi_unlikely (dashes == NULL))
return _csi_error (CSI_STATUS_NO_MEMORY);
}
for (n = 0; n < array->stack.len; n++) {
if (_csi_unlikely (! csi_object_is_number
(&array->stack.objects[n])))
{
if (dashes != stack_dashes)
_csi_free (ctx, dashes);
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
dashes[n] = csi_number_get_value (&array->stack.objects[n]);
}
cairo_set_dash (cr, dashes, n, offset);
if (dashes != stack_dashes)
_csi_free (ctx, dashes);
}
pop (2);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_set_device_offset (csi_t *ctx)
{
csi_status_t status;
cairo_surface_t *surface;
double x, y;
check (3);
status = _csi_ostack_get_number (ctx, 0, &y);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 1, &x);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_surface (ctx, 2, &surface);
if (_csi_unlikely (status))
return status;
cairo_surface_set_device_offset (surface, x, y);
pop (2);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_set_extend (csi_t *ctx)
{
csi_status_t status;
csi_object_t *obj;
long extend;
int type;
check (2);
status = _csi_ostack_get_integer (ctx, 0, &extend);
if (_csi_unlikely (status))
return status;
obj = _csi_peek_ostack (ctx, 1);
type = csi_object_get_type (obj);
switch (type) {
case CSI_OBJECT_TYPE_CONTEXT:
cairo_pattern_set_extend (cairo_get_source (obj->datum.cr),
extend);
break;
case CSI_OBJECT_TYPE_PATTERN:
cairo_pattern_set_extend (obj->datum.pattern, extend);
break;
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_set_fallback_resolution (csi_t *ctx)
{
csi_status_t status;
cairo_surface_t *surface;
double dpi_x, dpi_y;
check (3);
status = _csi_ostack_get_number (ctx, 0, &dpi_y);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 1, &dpi_x);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_surface (ctx, 2, &surface);
if (_csi_unlikely (status))
return status;
cairo_surface_set_fallback_resolution (surface, dpi_x, dpi_y);
pop (2);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_set_fill_rule (csi_t *ctx)
{
csi_status_t status;
cairo_t *cr;
long fill_rule;
check (2);
status = _csi_ostack_get_integer (ctx, 0, &fill_rule);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 1, &cr);
if (_csi_unlikely (status))
return status;
cairo_set_fill_rule (cr, fill_rule);
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_set_filter (csi_t *ctx)
{
csi_status_t status;
csi_object_t *obj;
long filter;
int type;
check (2);
status = _csi_ostack_get_integer (ctx, 0, &filter);
if (_csi_unlikely (status))
return status;
obj = _csi_peek_ostack (ctx, 1);
type = csi_object_get_type (obj);
switch (type) {
case CSI_OBJECT_TYPE_CONTEXT:
cairo_pattern_set_filter (cairo_get_source (obj->datum.cr),
filter);
break;
case CSI_OBJECT_TYPE_PATTERN:
cairo_pattern_set_filter (obj->datum.pattern, filter);
break;
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_set_font_face (csi_t *ctx)
{
cairo_t *cr;
cairo_font_face_t *font = NULL;
csi_status_t status;
check (2);
status = _csi_ostack_get_font_face (ctx, 0, &font);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 1, &cr);
if (_csi_unlikely (status))
return status;
cairo_set_font_face (cr, font);
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_set_font_options (csi_t *ctx)
{
csi_status_t status;
cairo_t *cr;
csi_dictionary_t *dict;
cairo_font_options_t *options;
check (2);
status = _csi_ostack_get_dictionary (ctx, 0, &dict);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 1, &cr);
if (_csi_unlikely (status))
return status;
options = cairo_font_options_create ();
status = _font_options_load_from_dictionary (ctx, dict, options);
if (_csi_unlikely (status))
return status;
cairo_set_font_options (cr, options);
cairo_font_options_destroy (options);
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_set_font_matrix (csi_t *ctx)
{
csi_status_t status;
cairo_t *cr;
cairo_matrix_t m;
check (2);
status = _csi_ostack_get_matrix (ctx, 0, &m);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 1, &cr);
if (_csi_unlikely (status))
return status;
cairo_set_font_matrix (cr, &m);
pop(1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_set_font_size (csi_t *ctx)
{
csi_status_t status;
cairo_t *cr;
double size;
check (2);
status = _csi_ostack_get_number (ctx, 0, &size);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 1, &cr);
if (_csi_unlikely (status))
return status;
cairo_set_font_size (cr, size);
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_set_line_cap (csi_t *ctx)
{
csi_status_t status;
cairo_t *cr;
long line_cap;
check (2);
status = _csi_ostack_get_integer (ctx, 0, &line_cap);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 1, &cr);
if (_csi_unlikely (status))
return status;
cairo_set_line_cap (cr, line_cap);
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_set_line_join (csi_t *ctx)
{
csi_status_t status;
cairo_t *cr;
long line_join;
status = _csi_ostack_get_integer (ctx, 0, &line_join);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 1, &cr);
if (_csi_unlikely (status))
return status;
cairo_set_line_join (cr, line_join);
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_set_line_width (csi_t *ctx)
{
csi_status_t status;
cairo_t *cr;
double line_width;
check (2);
status = _csi_ostack_get_number (ctx, 0, &line_width);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 1, &cr);
if (_csi_unlikely (status))
return status;
cairo_set_line_width (cr, line_width);
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_set_matrix (csi_t *ctx)
{
csi_object_t *obj;
csi_status_t status;
cairo_matrix_t m;
int type;
check (2);
status = _csi_ostack_get_matrix (ctx, 0, &m);
if (_csi_unlikely (status))
return status;
obj = _csi_peek_ostack (ctx, 1);
type = csi_object_get_type (obj);
switch (type) {
case CSI_OBJECT_TYPE_CONTEXT:
cairo_set_matrix (obj->datum.cr, &m);
break;
case CSI_OBJECT_TYPE_PATTERN:
cairo_pattern_set_matrix (obj->datum.pattern, &m);
break;
case CSI_OBJECT_TYPE_MATRIX:
obj->datum.matrix->matrix = m;
break;
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
pop (1);
return CSI_STATUS_SUCCESS;
}
struct _mime_tag {
csi_t *ctx;
csi_string_t *source;
};
static void
_mime_tag_destroy (void *closure)
{
struct _mime_tag *tag = closure;
if (--tag->source->base.ref)
csi_string_free (tag->ctx, tag->source);
_csi_slab_free (tag->ctx, tag, sizeof (struct _mime_tag));
}
static csi_status_t
_set_mime_data (csi_t *ctx)
{
csi_status_t status;
csi_object_t *obj;
const char *mime = NULL;
csi_object_t source;
cairo_surface_t *surface;
struct _mime_tag *tag;
int type;
check (3);
obj = _csi_peek_ostack (ctx, 0);
type = csi_object_get_type (obj);
switch (type) {
case CSI_OBJECT_TYPE_FILE:
status = _csi_file_as_string (ctx, obj->datum.file, &source);
if (_csi_unlikely (status))
return status;
break;
case CSI_OBJECT_TYPE_STRING:
source = *csi_object_reference (obj);
break;
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
status = _csi_ostack_get_string_constant (ctx, 1, &mime);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_surface (ctx, 2, &surface);
if (_csi_unlikely (status))
return status;
tag = _csi_slab_alloc (ctx, sizeof (struct _mime_tag));
if (_csi_unlikely (tag == NULL))
return _csi_error (CSI_STATUS_NO_MEMORY);
tag->ctx = cairo_script_interpreter_reference (ctx);
tag->source = source.datum.string;
tag->source->base.ref++;
status = cairo_surface_set_mime_data (surface,
mime,
(uint8_t *)
source.datum.string->string,
source.datum.string->len,
_mime_tag_destroy, tag);
if (_csi_unlikely (status)) {
_mime_tag_destroy (tag);
return status;
}
pop (2);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_set_miter_limit (csi_t *ctx)
{
csi_status_t status;
cairo_t *cr;
double miter_limit;
check (2);
status = _csi_ostack_get_number (ctx, 0, &miter_limit);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 1, &cr);
if (_csi_unlikely (status))
return status;
cairo_set_miter_limit (cr, miter_limit);
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_set_operator (csi_t *ctx)
{
cairo_t *cr;
long val;
csi_status_t status;
check (2);
status = _csi_ostack_get_integer (ctx, 0, &val);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 1, &cr);
if (_csi_unlikely (status))
return status;
cairo_set_operator (cr, val);
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_set_scaled_font (csi_t *ctx)
{
cairo_t *cr;
cairo_scaled_font_t *font = NULL;
csi_status_t status;
check (2);
status = _csi_ostack_get_scaled_font (ctx, 0, &font);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 1, &cr);
if (_csi_unlikely (status))
return status;
cairo_set_scaled_font (cr, font);
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_set_source (csi_t *ctx)
{
cairo_t *cr;
cairo_pattern_t *pattern = NULL;
csi_status_t status;
check (2);
status = _csi_ostack_get_pattern (ctx, 0, &pattern);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 1, &cr);
if (_csi_unlikely (status))
return status;
cairo_set_source (cr, pattern);
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_boolean_t
_matching_images (cairo_surface_t *a, cairo_surface_t *b)
{
cairo_format_t format_a, format_b;
if (cairo_surface_get_type (a) != CAIRO_SURFACE_TYPE_IMAGE)
return FALSE;
if (cairo_surface_get_type (b) != CAIRO_SURFACE_TYPE_IMAGE)
return FALSE;
if (cairo_image_surface_get_height (a) != cairo_image_surface_get_height (b))
return FALSE;
if (cairo_image_surface_get_width (a) != cairo_image_surface_get_width (b))
return FALSE;
format_a = cairo_image_surface_get_format (a);
if (format_a == CAIRO_FORMAT_RGB24)
format_a = CAIRO_FORMAT_ARGB32;
format_b = cairo_image_surface_get_format (b);
if (format_b == CAIRO_FORMAT_RGB24)
format_b = CAIRO_FORMAT_ARGB32;
if (format_a != format_b)
return FALSE;
return TRUE;
}
static csi_status_t
_set_source_image (csi_t *ctx)
{
csi_status_t status;
cairo_surface_t *surface;
cairo_surface_t *source;
check (2);
status = _csi_ostack_get_surface (ctx, 0, &source);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_surface (ctx, 1, &surface);
if (_csi_unlikely (status))
return status;
if (_csi_likely (_matching_images (surface, source))) {
cairo_surface_flush (surface);
memcpy (cairo_image_surface_get_data (surface),
cairo_image_surface_get_data (source),
cairo_image_surface_get_height (source) * cairo_image_surface_get_stride (source));
cairo_surface_mark_dirty (surface);
} else {
cairo_t *cr;
cr = cairo_create (surface);
cairo_set_source_surface (cr, source, 0, 0);
cairo_paint (cr);
cairo_destroy (cr);
}
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_set_source_rgb (csi_t *ctx)
{
csi_status_t status;
double r,g,b;
cairo_t *cr;
check (4);
status = _csi_ostack_get_number (ctx, 0, &b);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 1, &g);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 2, &r);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 3, &cr);
if (_csi_unlikely (status))
return status;
cairo_set_source_rgb (cr, r, g, b);
pop (3);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_set_source_rgba (csi_t *ctx)
{
csi_status_t status;
double r,g,b,a;
cairo_t *cr;
check (5);
status = _csi_ostack_get_number (ctx, 0, &a);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 1, &b);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 2, &g);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 3, &r);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 4, &cr);
if (_csi_unlikely (status))
return status;
cairo_set_source_rgba (cr, r, g, b, a);
pop (4);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_set_tolerance (csi_t *ctx)
{
csi_status_t status;
cairo_t *cr;
double tolerance;
check (2);
status = _csi_ostack_get_number (ctx, 0, &tolerance);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 1, &cr);
if (_csi_unlikely (status))
return status;
cairo_set_tolerance (cr, tolerance);
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_transform (csi_t *ctx)
{
csi_object_t *obj;
csi_status_t status;
cairo_matrix_t m;
int type;
check (2);
status = _csi_ostack_get_matrix (ctx, 0, &m);
if (_csi_unlikely (status))
return status;
obj = _csi_peek_ostack (ctx, 1);
type = csi_object_get_type (obj);
switch (type) {
case CSI_OBJECT_TYPE_CONTEXT:
cairo_transform (obj->datum.cr, &m);
break;
case CSI_OBJECT_TYPE_PATTERN:
{
cairo_matrix_t ctm;
cairo_pattern_get_matrix (obj->datum.pattern, &ctm);
cairo_matrix_multiply (&ctm, &m, &ctm);
cairo_pattern_set_matrix (obj->datum.pattern, &ctm);
}
break;
case CSI_OBJECT_TYPE_MATRIX:
cairo_matrix_multiply (&obj->datum.matrix->matrix,
&m,
&obj->datum.matrix->matrix);
break;
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_translate (csi_t *ctx)
{
csi_object_t *obj;
csi_status_t status;
double x, y;
int type;
check (3);
status = _csi_ostack_get_number (ctx, 0, &y);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 1, &x);
if (_csi_unlikely (status))
return status;
obj = _csi_peek_ostack (ctx, 2);
type = csi_object_get_type (obj);
switch (type) {
case CSI_OBJECT_TYPE_CONTEXT:
cairo_translate (obj->datum.cr, x, y);
break;
case CSI_OBJECT_TYPE_PATTERN:
{
cairo_matrix_t ctm;
cairo_pattern_get_matrix (obj->datum.pattern, &ctm);
cairo_matrix_translate (&ctm, x, y);
cairo_pattern_set_matrix (obj->datum.pattern, &ctm);
}
break;
case CSI_OBJECT_TYPE_MATRIX:
cairo_matrix_translate (&obj->datum.matrix->matrix, x, y);
break;
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
pop (2);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_true (csi_t *ctx)
{
return _csi_push_ostack_boolean (ctx, TRUE);
}
static csi_status_t
_show_page (csi_t *ctx)
{
csi_object_t *obj;
int type;
check (1);
obj = _csi_peek_ostack (ctx, 0);
type = csi_object_get_type (obj);
switch (type) {
case CSI_OBJECT_TYPE_CONTEXT:
cairo_show_page (obj->datum.cr);
if (ctx->hooks.copy_page != NULL)
ctx->hooks.copy_page (ctx->hooks.closure, obj->datum.cr);
break;
case CSI_OBJECT_TYPE_SURFACE:
cairo_surface_show_page (obj->datum.surface);
break;
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_similar (csi_t *ctx)
{
csi_object_t obj;
long content;
double width, height;
cairo_surface_t *other;
csi_status_t status;
check (4);
status = _csi_ostack_get_integer (ctx, 0, &content);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 1, &height);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 2, &width);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_surface (ctx, 3, &other);
if (_csi_unlikely (status))
return status;
if ((content & CAIRO_CONTENT_COLOR_ALPHA) == 0) {
if (_csi_unlikely (content & ~CAIRO_CONTENT_COLOR_ALPHA))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
switch ((int) content) {
default:
case CAIRO_FORMAT_ARGB32:
content = CAIRO_CONTENT_COLOR_ALPHA;
break;
case CAIRO_FORMAT_RGB16_565:
case CAIRO_FORMAT_RGB24:
content = CAIRO_CONTENT_COLOR;
break;
case CAIRO_FORMAT_A8:
case CAIRO_FORMAT_A1:
content = CAIRO_CONTENT_ALPHA;
break;
}
}
obj.type = CSI_OBJECT_TYPE_SURFACE;
obj.datum.surface = cairo_surface_create_similar (other,
content, width, height);
pop (4);
return push (&obj);
}
static csi_status_t
_subsurface (csi_t *ctx)
{
csi_object_t obj;
double x, y, width, height;
cairo_surface_t *target;
csi_status_t status;
check (5);
status = _csi_ostack_get_number (ctx, 0, &height);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 1, &width);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 2, &y);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_number (ctx, 3, &x);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_surface (ctx, 4, &target);
if (_csi_unlikely (status))
return status;
obj.type = CSI_OBJECT_TYPE_SURFACE;
obj.datum.surface = cairo_surface_create_for_rectangle (target, x, y, width, height);
pop (5);
return push (&obj);
}
static csi_status_t
_show_text (csi_t *ctx)
{
csi_status_t status;
csi_string_t *text;
cairo_t *cr;
check (2);
status = _csi_ostack_get_string (ctx, 0, &text);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 1, &cr);
if (_csi_unlikely (status))
return status;
cairo_show_text (cr, text->string);
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_show_glyphs (csi_t *ctx)
{
csi_array_t *array;
csi_status_t status;
cairo_t *cr;
cairo_glyph_t stack_glyphs[256], *glyphs;
csi_integer_t nglyphs, i;
check (2);
status = _csi_ostack_get_array (ctx, 0, &array);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 1, &cr);
if (_csi_unlikely (status))
return status;
nglyphs = 0;
for (i = 0; i < array->stack.len; i++) {
csi_object_t *obj = &array->stack.objects[i];
int type = csi_object_get_type (obj);
switch (type) {
case CSI_OBJECT_TYPE_ARRAY:
nglyphs += obj->datum.array->stack.len;
break;
case CSI_OBJECT_TYPE_STRING:
nglyphs += obj->datum.string->len;
break;
}
}
if (nglyphs == 0) {
pop (1);
return CSI_STATUS_SUCCESS;
}
if (nglyphs > ARRAY_LENGTH (stack_glyphs)) {
if (_csi_unlikely ((unsigned) nglyphs >= INT_MAX / sizeof (cairo_glyph_t)))
return _csi_error (CSI_STATUS_NO_MEMORY);
glyphs = _csi_alloc (ctx, sizeof (cairo_glyph_t) * nglyphs);
if (_csi_unlikely (glyphs == NULL))
return _csi_error (CSI_STATUS_NO_MEMORY);
} else
glyphs = stack_glyphs;
nglyphs = _glyph_string (ctx, array, cairo_get_scaled_font (cr), glyphs);
cairo_show_glyphs (cr, glyphs, nglyphs);
if (glyphs != stack_glyphs)
_csi_free (ctx, glyphs);
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_show_text_glyphs (csi_t *ctx)
{
csi_object_t *obj;
csi_array_t *array;
csi_string_t *string;
csi_string_t *utf8_string;
csi_status_t status;
cairo_t *cr;
cairo_text_cluster_t stack_clusters[256], *clusters;
cairo_glyph_t stack_glyphs[256], *glyphs;
csi_integer_t nglyphs, nclusters, i;
long direction;
int type;
check (5);
status = _csi_ostack_get_integer (ctx, 0, &direction);
if (_csi_unlikely (status))
return status;
obj = _csi_peek_ostack (ctx, 1);
type = csi_object_get_type (obj);
switch (type) {
case CSI_OBJECT_TYPE_ARRAY:
array = obj->datum.array;
nclusters = array->stack.len / 2;
if (nclusters > ARRAY_LENGTH (stack_clusters)) {
if (_csi_unlikely ((unsigned) nclusters >= INT_MAX / sizeof (cairo_text_cluster_t)))
return _csi_error (CSI_STATUS_NO_MEMORY);
clusters = _csi_alloc (ctx, sizeof (cairo_text_cluster_t) * nclusters);
if (_csi_unlikely (clusters == NULL))
return _csi_error (CSI_STATUS_NO_MEMORY);
} else
clusters = stack_clusters;
for (i = 0; i < nclusters; i++) {
clusters[i].num_bytes = csi_number_get_value (&array->stack.objects[2*i+0]);
clusters[i].num_glyphs = csi_number_get_value (&array->stack.objects[2*i+1]);
}
break;
case CSI_OBJECT_TYPE_STRING:
string = obj->datum.string;
nclusters = string->len / 2;
if (nclusters > ARRAY_LENGTH (stack_clusters)) {
if (_csi_unlikely ((unsigned) nclusters >= INT_MAX / sizeof (cairo_text_cluster_t)))
return _csi_error (CSI_STATUS_NO_MEMORY);
clusters = _csi_alloc (ctx, sizeof (cairo_text_cluster_t) * nclusters);
if (_csi_unlikely (clusters == NULL))
return _csi_error (CSI_STATUS_NO_MEMORY);
} else
clusters = stack_clusters;
for (i = 0; i < nclusters; i++) {
clusters[i].num_bytes = string->string[2*i+0];
clusters[i].num_glyphs = string->string[2*i+1];
}
break;
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
status = _csi_ostack_get_array (ctx, 2, &array);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_string (ctx, 3, &utf8_string);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 4, &cr);
if (_csi_unlikely (status))
return status;
nglyphs = 0;
for (i = 0; i < array->stack.len; i++) {
obj = &array->stack.objects[i];
type = csi_object_get_type (obj);
switch (type) {
case CSI_OBJECT_TYPE_ARRAY:
nglyphs += obj->datum.array->stack.len;
break;
case CSI_OBJECT_TYPE_STRING:
nglyphs += obj->datum.string->len;
break;
}
}
if (nglyphs == 0) {
pop (4);
return CSI_STATUS_SUCCESS;
}
if (nglyphs > ARRAY_LENGTH (stack_glyphs)) {
if (_csi_unlikely ((unsigned) nglyphs >= INT_MAX / sizeof (cairo_glyph_t)))
return _csi_error (CSI_STATUS_NO_MEMORY);
glyphs = _csi_alloc (ctx, sizeof (cairo_glyph_t) * nglyphs);
if (_csi_unlikely (glyphs == NULL))
return _csi_error (CSI_STATUS_NO_MEMORY);
} else
glyphs = stack_glyphs;
nglyphs = _glyph_string (ctx, array, cairo_get_scaled_font (cr), glyphs);
cairo_show_text_glyphs (cr,
utf8_string->string, utf8_string->len,
glyphs, nglyphs,
clusters, nclusters,
direction);
if (clusters != stack_clusters)
_csi_free (ctx, clusters);
if (glyphs != stack_glyphs)
_csi_free (ctx, glyphs);
pop (4);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_stroke (csi_t *ctx)
{
return _do_cairo_op (ctx, cairo_stroke);
}
static csi_status_t
_stroke_preserve (csi_t *ctx)
{
return _do_cairo_op (ctx, cairo_stroke_preserve);
}
static csi_status_t
_sub (csi_t *ctx)
{
csi_object_t *A;
csi_object_t *B;
csi_object_type_t type_a, type_b;
check (2);
B = _csi_peek_ostack (ctx, 0);
A = _csi_peek_ostack (ctx, 1);
type_a = csi_object_get_type (A);
if (_csi_unlikely (! (type_a == CSI_OBJECT_TYPE_INTEGER ||
type_a == CSI_OBJECT_TYPE_REAL)))
{
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
type_b = csi_object_get_type (B);
if (_csi_unlikely (! (type_b == CSI_OBJECT_TYPE_INTEGER ||
type_b == CSI_OBJECT_TYPE_REAL)))
{
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
pop (2);
if (type_a == CSI_OBJECT_TYPE_REAL &&
type_b == CSI_OBJECT_TYPE_REAL)
{
return _csi_push_ostack_real (ctx, A->datum.real - B->datum.real);
}
else if (type_a == CSI_OBJECT_TYPE_INTEGER &&
type_b == CSI_OBJECT_TYPE_INTEGER)
{
return _csi_push_ostack_integer (ctx,
A->datum.integer - B->datum.integer);
}
else
{
double v;
if (type_a == CSI_OBJECT_TYPE_REAL)
v = A->datum.real;
else
v = A->datum.integer;
if (type_b == CSI_OBJECT_TYPE_REAL)
v -= B->datum.real;
else
v -= B->datum.integer;
return _csi_push_ostack_real (ctx, v);
}
}
static csi_status_t
_surface (csi_t *ctx)
{
csi_object_t obj;
csi_dictionary_t *dict;
csi_proxy_t *proxy;
csi_object_t key;
double width, height;
csi_surface_create_func_t hook;
long content;
cairo_surface_t *surface;
long uid;
csi_status_t status;
check (1);
status = _csi_ostack_get_dictionary (ctx, 0, &dict);
if (_csi_unlikely (status))
return status;
status = _csi_dictionary_get_number (ctx, dict, "width", FALSE, &width);
if (_csi_unlikely (status))
return status;
status = _csi_dictionary_get_number (ctx, dict, "height", FALSE, &height);
if (_csi_unlikely (status))
return status;
content = CAIRO_CONTENT_COLOR_ALPHA;
status = _csi_dictionary_get_integer (ctx, dict, "content", TRUE, &content);
if (_csi_unlikely (status))
return status;
uid = 0;
status = _csi_dictionary_get_integer (ctx, dict, "uid", TRUE, &uid);
if (_csi_unlikely (status))
return status;
if (uid == 0) {
status = _csi_dictionary_get_integer (ctx, dict, "drawable", TRUE, &uid);
if (_csi_unlikely (status))
return status;
}
hook = ctx->hooks.surface_create;
assert (hook != NULL);
surface = hook (ctx->hooks.closure, content, width, height, uid);
if (_csi_unlikely (surface == NULL)) {
return _csi_error (CSI_STATUS_NULL_POINTER);
}
proxy = _csi_proxy_create (ctx, surface, dict,
ctx->hooks.surface_destroy,
ctx->hooks.closure);
if (_csi_unlikely (proxy == NULL)) {
cairo_surface_destroy (surface);
return _csi_error (CSI_STATUS_NO_MEMORY);
}
status = cairo_surface_set_user_data (surface,
&_csi_proxy_key,
proxy, _csi_proxy_destroy);
if (_csi_unlikely (status)) {
_csi_proxy_destroy (proxy);
cairo_surface_destroy (surface);
return status;
}
status = csi_name_new_static (ctx, &key, "fallback-resolution");
if (_csi_unlikely (status)) {
cairo_surface_destroy (surface);
return status;
}
if (csi_dictionary_has (dict, key.datum.name)) {
status = csi_dictionary_get (ctx, dict, key.datum.name, &obj);
if (_csi_unlikely (status)) {
cairo_surface_destroy (surface);
return status;
}
if (csi_object_get_type (&obj) == CSI_OBJECT_TYPE_ARRAY) {
csi_array_t *array = obj.datum.array;
if (array->stack.len == 2) {
cairo_surface_set_fallback_resolution (surface,
csi_number_get_value
(&array->stack.objects[0]),
csi_number_get_value
(&array->stack.objects[1]));
}
}
}
status = csi_name_new_static (ctx, &key, "source");
if (_csi_unlikely (status)) {
cairo_surface_destroy (surface);
return status;
}
if (csi_dictionary_has (dict, key.datum.name)) {
cairo_surface_t *image;
cairo_t *cr;
status = _image_load_from_dictionary (ctx, dict, &image);
if (_csi_unlikely (status)) {
cairo_surface_destroy (surface);
return status;
}
cr = cairo_create (surface);
cairo_set_source_surface (cr, image, 0, 0);
cairo_surface_destroy (image);
cairo_paint (cr);
status = cairo_status (cr);
cairo_destroy (cr);
if (_csi_unlikely (status))
return status;
}
status = csi_name_new_static (ctx, &key, "device-offset");
if (_csi_unlikely (status)) {
cairo_surface_destroy (surface);
return status;
}
if (csi_dictionary_has (dict, key.datum.name)) {
status = csi_dictionary_get (ctx, dict, key.datum.name, &obj);
if (_csi_unlikely (status))
return status;
if (csi_object_get_type (&obj) == CSI_OBJECT_TYPE_ARRAY) {
csi_array_t *array = obj.datum.array;
if (array->stack.len == 2) {
cairo_surface_set_device_offset (surface,
csi_number_get_value
(&array->stack.objects[0]),
csi_number_get_value
(&array->stack.objects[1]));
}
}
}
obj.type = CSI_OBJECT_TYPE_SURFACE;
obj.datum.surface = surface;
pop (1);
return push (&obj);
}
static csi_status_t
_text_path (csi_t *ctx)
{
csi_status_t status;
csi_string_t *text;
cairo_t *cr;
check (2);
status = _csi_ostack_get_string (ctx, 0, &text);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_context (ctx, 1, &cr);
if (_csi_unlikely (status))
return status;
cairo_text_path (cr, text->string);
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_undef (csi_t *ctx)
{
csi_name_t name = 0;
csi_status_t status;
check (1);
status = _csi_ostack_get_name (ctx, 0, &name);
if (_csi_unlikely (status))
return status;
status = _csi_name_undefine (ctx, name);
if (_csi_unlikely (status))
return status;
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_unset (csi_t *ctx)
{
csi_object_t *dst;
csi_name_t name = 0;
csi_status_t status;
int type;
check (2);
status = _csi_ostack_get_name (ctx, 0, &name);
if (_csi_unlikely (status))
return status;
dst = _csi_peek_ostack (ctx, 1);
type = csi_object_get_type (dst);
switch (type) {
case CSI_OBJECT_TYPE_DICTIONARY:
csi_dictionary_remove (ctx, dst->datum.dictionary, name);
break;
case CSI_OBJECT_TYPE_STRING:
case CSI_OBJECT_TYPE_ARRAY:
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_write_to_png (csi_t *ctx)
{
csi_status_t status;
csi_string_t *filename;
cairo_surface_t *surface;
check (2);
status = _csi_ostack_get_string (ctx, 0, &filename);
if (_csi_unlikely (status))
return status;
status = _csi_ostack_get_surface (ctx, 1, &surface);
if (_csi_unlikely (status))
return status;
#if CAIRO_HAS_PNG_FUNCTIONS
status = cairo_surface_write_to_png (surface, filename->string);
if (_csi_unlikely (status))
return status;
#else
return CAIRO_STATUS_WRITE_ERROR;
#endif
pop (1);
return CSI_STATUS_SUCCESS;
}
static csi_status_t
_xor (csi_t *ctx)
{
csi_object_t *a, *b;
int type;
check (2);
a = _csi_peek_ostack (ctx, 0);
b = _csi_peek_ostack (ctx, 1);
if (_csi_unlikely (csi_object_get_type (a) != csi_object_get_type (b)))
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
pop (2);
type = csi_object_get_type (a);
switch (type) {
case CSI_OBJECT_TYPE_INTEGER:
return _csi_push_ostack_integer (ctx,
a->datum.integer ^ b->datum.integer);
case CSI_OBJECT_TYPE_BOOLEAN:
return _csi_push_ostack_boolean (ctx,
a->datum.boolean ^ b->datum.boolean);
default:
return _csi_error (CSI_STATUS_INVALID_SCRIPT);
}
}
static csi_status_t
_debug_print (csi_t *ctx)
{
csi_object_t *obj;
check (1);
obj = _csi_peek_ostack (ctx, 0);
switch (csi_object_get_type (obj)) {
case CSI_OBJECT_TYPE_NULL:
fprintf (stderr, "NULL\n");
break;
case CSI_OBJECT_TYPE_BOOLEAN:
fprintf (stderr, "boolean: %s\n",
obj->datum.boolean ? "true" : "false");
break;
case CSI_OBJECT_TYPE_INTEGER:
fprintf (stderr, "integer: %ld\n", obj->datum.integer);
break;
case CSI_OBJECT_TYPE_MARK:
fprintf (stderr, "mark\n");
break;
case CSI_OBJECT_TYPE_NAME:
fprintf (stderr, "name: %s\n", (char *) obj->datum.name);
break;
case CSI_OBJECT_TYPE_OPERATOR:
fprintf (stderr, "operator: %p\n", obj->datum.ptr);
break;
case CSI_OBJECT_TYPE_REAL:
fprintf (stderr, "real: %g\n", obj->datum.real);
break;
case CSI_OBJECT_TYPE_ARRAY:
fprintf (stderr, "array\n");
break;
case CSI_OBJECT_TYPE_DICTIONARY:
fprintf (stderr, "dictionary\n");
break;
case CSI_OBJECT_TYPE_FILE:
fprintf (stderr, "file\n");
break;
case CSI_OBJECT_TYPE_MATRIX:
fprintf (stderr, "matrix: [%g %g %g %g %g %g]\n",
obj->datum.matrix->matrix.xx,
obj->datum.matrix->matrix.yx,
obj->datum.matrix->matrix.xy,
obj->datum.matrix->matrix.yy,
obj->datum.matrix->matrix.x0,
obj->datum.matrix->matrix.y0);
break;
case CSI_OBJECT_TYPE_STRING:
fprintf (stderr, "string: %s\n", obj->datum.string->string);
break;
case CSI_OBJECT_TYPE_CONTEXT:
fprintf (stderr, "context\n");
break;
case CSI_OBJECT_TYPE_FONT:
fprintf (stderr, "font\n");
break;
case CSI_OBJECT_TYPE_PATTERN:
fprintf (stderr, "pattern\n");
break;
case CSI_OBJECT_TYPE_SCALED_FONT:
fprintf (stderr, "scaled-font\n");
break;
case CSI_OBJECT_TYPE_SURFACE:
fprintf (stderr, "surface\n");
break;
}
pop (1);
return CSI_STATUS_SUCCESS;
}
static const csi_operator_def_t
_defs[] = {
{ "<<", _mark },
{ ">>", end_dict_construction },
{ "[", _mark },
{ "]", end_array_construction },
{ "a", _alpha },
{ "abs", NULL },
{ "add", _add },
{ "add-color-stop", _add_color_stop },
{ "and", _and },
{ "arc", _arc },
{ "arc-negative", _arc_negative },
{ "arc-", _arc_negative },
{ "arc-to", NULL },
{ "array", _array },
{ "astore", NULL },
{ "atan", NULL },
{ "bind", _bind },
{ "bitshift", _bitshift },
{ "c", _curve_to },
{ "C", _rel_curve_to },
{ "ceiling", NULL },
{ "clear", NULL },
{ "clear-to-mark", NULL },
{ "clip", _clip },
{ "clip-extents", NULL },
{ "clip-preserve", _clip_preserve },
{ "clip+", _clip_preserve },
{ "close-path", _close_path },
{ "context", _context },
{ "copy", _copy },
{ "copy-page", _copy_page },
{ "cos", NULL },
{ "count", NULL },
{ "count-to-mark", NULL },
{ "curve-to", _curve_to },
{ "cvi", _cvi },
{ "cvr", _cvr },
{ "def", _def },
{ "device-to-user", NULL },
{ "device-to-user-distance", NULL },
{ "dict", _dict },
{ "div", _div },
{ "dup", _duplicate },
{ "eq", _eq },
{ "exch", _exch },
{ "exec", NULL },
{ "exp", NULL },
{ "false", _false },
{ "fill", _fill },
{ "fill-extents", NULL },
{ "fill-preserve", _fill_preserve },
{ "fill+", _fill_preserve },
{ "filter", _filter },
{ "floor", NULL },
{ "font", _font },
{ "for", _for },
{ "forall", NULL },
{ "g", _gray },
{ "ge", _ge },
{ "get", _get },
{ "glyph-path", _glyph_path },
{ "gt", _gt },
{ "h", _close_path },
{ "identity", _identity },
{ "if", _if },
{ "ifelse", _ifelse },
{ "image", _image },
{ "index", _index },
{ "integer", _integer },
{ "invert", _invert },
{ "in-stroke", NULL },
{ "in-fill", NULL },
{ "known", NULL },
{ "l", _line_to },
{ "L", _rel_line_to },
{ "languagelevel", NULL },
{ "le", _le },
{ "length", NULL },
{ "linear", _linear },
{ "line-to", _line_to },
{ "ln", NULL },
{ "load", NULL },
{ "log", NULL },
{ "loop", NULL },
{ "lt", _lt },
{ "m", _move_to },
{ "M", _rel_move_to },
{ "mark", _mark },
{ "mask", _mask },
{ "matrix", _matrix },
{ "mod", _mod },
{ "move-to", _move_to },
{ "mul", _mul },
{ "multiply", NULL },
{ "n", _new_path },
{ "N", _new_sub_path },
{ "ne", _ne },
{ "neg", _neg },
{ "new-path", _new_path },
{ "new-sub-path", _new_sub_path },
{ "not", _not },
{ "null", _null },
{ "or", _or },
{ "paint", _paint },
{ "paint-with-alpha", _paint_with_alpha },
{ "pattern", _pattern },
{ "pop", _pop },
{ "pop-group", _pop_group },
{ "push-group", _push_group },
{ "radial", _radial },
{ "rand", NULL },
{ "rectangle", _rectangle },
{ "repeat", _repeat },
{ "restore", _restore },
{ "rel-curve-to", _rel_curve_to },
{ "rel-line-to", _rel_line_to },
{ "rel-move-to", _rel_move_to },
{ "reset-clip", _reset_clip },
{ "rgb", _rgb },
{ "rgba", _rgba },
{ "roll", _roll },
{ "rotate", _rotate },
{ "round", NULL },
{ "run", NULL },
{ "save", _save },
{ "scale", _scale },
{ "scaled-font", _scaled_font },
{ "select-font-face", _select_font_face },
{ "set", _set },
{ "set-antialias", _set_antialias },
{ "set-dash", _set_dash },
{ "set-device-offset", _set_device_offset },
{ "set-extend", _set_extend },
{ "set-fallback-resolution", _set_fallback_resolution },
{ "set-fill-rule", _set_fill_rule },
{ "set-filter", _set_filter },
{ "set-font-face", _set_font_face },
{ "set-font-options", _set_font_options },
{ "set-font-matrix", _set_font_matrix },
{ "set-font-size", _set_font_size },
{ "set-line-cap", _set_line_cap },
{ "set-line-join", _set_line_join },
{ "set-line-width", _set_line_width },
{ "set-matrix", _set_matrix },
{ "set-miter-limit", _set_miter_limit },
{ "set-mime-data", _set_mime_data },
{ "set-operator", _set_operator },
{ "set-scaled-font", _set_scaled_font },
{ "set-source", _set_source },
{ "set-source-image", _set_source_image },
{ "set-source-rgb", _set_source_rgb },
{ "set-source-rgba", _set_source_rgba },
{ "set-tolerance", _set_tolerance },
{ "show-glyphs", _show_glyphs },
{ "show-text", _show_text },
{ "show-text-glyphs", _show_text_glyphs },
{ "show-page", _show_page },
{ "similar", _similar },
{ "sin", NULL },
{ "sqrt", NULL },
{ "sub", _sub },
{ "subsurface", _subsurface },
{ "surface", _surface },
{ "string", NULL },
{ "stroke", _stroke },
{ "stroke-extents", NULL },
{ "stroke-preserve", _stroke_preserve },
{ "stroke+", _stroke_preserve },
{ "text-path", _text_path },
{ "transform", _transform },
{ "transform-distance", NULL },
{ "transform-point", NULL },
{ "translate", _translate },
{ "true", _true },
{ "type", NULL },
{ "undef", _undef },
{ "unset", _unset },
{ "user-to-device", NULL },
{ "user-to-device-distance", NULL },
{ "where", NULL },
{ "write-to-png", _write_to_png },
{ "xor", _xor },
{ "=", _debug_print },
{ NULL, NULL },
};
const csi_operator_def_t *
_csi_operators (void)
{
return _defs;
}
static const csi_integer_constant_def_t
_integer_constants[] = {
{ "CLEAR", CAIRO_OPERATOR_CLEAR },
{ "SOURCE", CAIRO_OPERATOR_SOURCE },
{ "OVER", CAIRO_OPERATOR_OVER },
{ "IN", CAIRO_OPERATOR_IN },
{ "OUT", CAIRO_OPERATOR_OUT },
{ "ATOP", CAIRO_OPERATOR_ATOP },
{ "DEST", CAIRO_OPERATOR_DEST },
{ "DEST_OVER", CAIRO_OPERATOR_DEST_OVER },
{ "DEST_IN", CAIRO_OPERATOR_DEST_IN },
{ "DEST_OUT", CAIRO_OPERATOR_DEST_OUT },
{ "DEST_ATOP", CAIRO_OPERATOR_DEST_ATOP },
{ "XOR", CAIRO_OPERATOR_XOR },
{ "ADD", CAIRO_OPERATOR_ADD },
{ "SATURATE", CAIRO_OPERATOR_SATURATE },
{ "MULTIPLY", CAIRO_OPERATOR_MULTIPLY },
{ "SCREEN", CAIRO_OPERATOR_SCREEN },
{ "OVERLAY", CAIRO_OPERATOR_OVERLAY },
{ "DARKEN", CAIRO_OPERATOR_DARKEN },
{ "LIGHTEN", CAIRO_OPERATOR_LIGHTEN },
{ "DODGE", CAIRO_OPERATOR_COLOR_DODGE },
{ "BURN", CAIRO_OPERATOR_COLOR_BURN },
{ "HARD_LIGHT", CAIRO_OPERATOR_HARD_LIGHT },
{ "SOFT_LIGHT", CAIRO_OPERATOR_SOFT_LIGHT },
{ "DIFFERENCE", CAIRO_OPERATOR_DIFFERENCE },
{ "EXCLUSION", CAIRO_OPERATOR_EXCLUSION },
{ "HSL_HUE", CAIRO_OPERATOR_HSL_HUE },
{ "HSL_SATURATION", CAIRO_OPERATOR_HSL_SATURATION },
{ "HSL_COLOR", CAIRO_OPERATOR_HSL_COLOR },
{ "HSL_LUMINOSITY", CAIRO_OPERATOR_HSL_LUMINOSITY },
{ "WINDING", CAIRO_FILL_RULE_WINDING },
{ "EVEN_ODD", CAIRO_FILL_RULE_EVEN_ODD },
{ "ANTIALIAS_DEFAULT", CAIRO_ANTIALIAS_DEFAULT },
{ "ANTIALIAS_NONE", CAIRO_ANTIALIAS_NONE },
{ "ANTIALIAS_GRAY", CAIRO_ANTIALIAS_GRAY },
{ "ANTIALIAS_SUBPIXEL", CAIRO_ANTIALIAS_SUBPIXEL },
{ "LINE_CAP_BUTT", CAIRO_LINE_CAP_BUTT },
{ "LINE_CAP_ROUND", CAIRO_LINE_CAP_ROUND },
{ "LINE_CAP_SQUARE", CAIRO_LINE_CAP_SQUARE },
{ "LINE_JOIN_MITER", CAIRO_LINE_JOIN_MITER },
{ "LINE_JOIN_ROUND", CAIRO_LINE_JOIN_ROUND },
{ "LINE_JOIN_BEVEL", CAIRO_LINE_JOIN_BEVEL },
{ "EXTEND_NONE", CAIRO_EXTEND_NONE },
{ "EXTEND_REPEAT", CAIRO_EXTEND_REPEAT },
{ "EXTEND_REFLECT", CAIRO_EXTEND_REFLECT },
{ "EXTEND_PAD", CAIRO_EXTEND_PAD },
{ "FILTER_FAST", CAIRO_FILTER_FAST },
{ "FILTER_GOOD", CAIRO_FILTER_GOOD },
{ "FILTER_BEST", CAIRO_FILTER_BEST },
{ "FILTER_BILINEAR", CAIRO_FILTER_BILINEAR },
{ "FILTER_NEAREST", CAIRO_FILTER_NEAREST },
{ "FILTER_GAUSSIAN", CAIRO_FILTER_GAUSSIAN },
{ "SLANT_NORMAL", CAIRO_FONT_SLANT_NORMAL },
{ "SLANT_ITALIC", CAIRO_FONT_SLANT_ITALIC },
{ "SLANT_OBLIQUE", CAIRO_FONT_SLANT_OBLIQUE },
{ "WEIGHT_NORMAL", CAIRO_FONT_WEIGHT_NORMAL },
{ "WEIGHT_BOLD", CAIRO_FONT_WEIGHT_BOLD },
{ "SUBPIXEL_ORDER_DEFAULT", CAIRO_SUBPIXEL_ORDER_DEFAULT },
{ "SUBPIXEL_ORDER_RGB", CAIRO_SUBPIXEL_ORDER_RGB },
{ "SUBPIXEL_ORDER_BGR", CAIRO_SUBPIXEL_ORDER_BGR },
{ "SUBPIXEL_ORDER_VRGB", CAIRO_SUBPIXEL_ORDER_VRGB },
{ "SUBPIXEL_ORDER_VBGR", CAIRO_SUBPIXEL_ORDER_VBGR },
{ "HINT_STYLE_DEFAULT", CAIRO_HINT_STYLE_DEFAULT },
{ "HINT_STYLE_NONE", CAIRO_HINT_STYLE_NONE },
{ "HINT_STYLE_SLIGHT", CAIRO_HINT_STYLE_SLIGHT },
{ "HINT_STYLE_MEDIUM", CAIRO_HINT_STYLE_MEDIUM },
{ "HINT_STYLE_FULL", CAIRO_HINT_STYLE_FULL },
{ "HINT_METRICS_DEFAULT", CAIRO_HINT_METRICS_DEFAULT },
{ "HINT_METRICS_OFF", CAIRO_HINT_METRICS_OFF },
{ "HINT_METRICS_ON", CAIRO_HINT_METRICS_ON },
{ "FORWARD", 0 },
{ "BACKWARD", 1 },
{ "COLOR", CAIRO_CONTENT_COLOR },
{ "ALPHA", CAIRO_CONTENT_ALPHA },
{ "COLOR_ALPHA", CAIRO_CONTENT_COLOR_ALPHA },
{ "A1", CAIRO_FORMAT_A1 },
{ "A8", CAIRO_FORMAT_A8 },
{ "RGB16_565", CAIRO_FORMAT_RGB16_565 },
{ "RGB24", CAIRO_FORMAT_RGB24 },
{ "ARGB32", CAIRO_FORMAT_ARGB32 },
{ "INVALID", CAIRO_FORMAT_INVALID },
{ NULL, 0 }
};
const csi_integer_constant_def_t *
_csi_integer_constants (void)
{
return _integer_constants;
}
static const csi_real_constant_def_t
_real_constants[] = {
{ "math.pi", M_PI },
{ "math.2pi", 2 * M_PI },
{ "math.sqrt2", M_SQRT2 },
{ "math.ln2", M_LN2 },
{ NULL, 0 }
};
const csi_real_constant_def_t *
_csi_real_constants (void)
{
return _real_constants;
}