| 1 |
#include <string.h> |
| 2 |
#include <stdlib.h> |
| 3 |
#include <cairo.h> |
| 4 |
#include <ccss-cairo/ccss-cairo.h> |
| 5 |
#include <gtk/gtk.h> |
| 6 |
|
| 7 |
/* ================================================================ */ |
| 8 |
/* Our little hierarchy */ |
| 9 |
typedef enum _CopperClasses { |
| 10 |
CC_FRAME, |
| 11 |
CC_CONTENT, CC_TITLEBAR, |
| 12 |
CC_MENU, CC_TITLE, CC_MINIMIZE, CC_MAXIMIZE, CC_CLOSE, |
| 13 |
CC_FILLER, |
| 14 |
CC_LAST |
| 15 |
} CopperClasses; |
| 16 |
|
| 17 |
char *names[] = |
| 18 |
{ |
| 19 |
"frame", |
| 20 |
"area", "area", |
| 21 |
"button", "title", "button", "button", "button", |
| 22 |
"area", |
| 23 |
"last" |
| 24 |
}; |
| 25 |
|
| 26 |
CopperClasses parents[] = |
| 27 |
{ |
| 28 |
CC_LAST, |
| 29 |
CC_FRAME, CC_FRAME, |
| 30 |
CC_TITLEBAR, CC_TITLEBAR, CC_TITLEBAR, CC_TITLEBAR, CC_TITLEBAR, |
| 31 |
CC_TITLEBAR, |
| 32 |
CC_LAST |
| 33 |
}; |
| 34 |
|
| 35 |
char *copper_classnames[] = |
| 36 |
{ |
| 37 |
NULL, |
| 38 |
"content", "titlebar", |
| 39 |
"menu", NULL, "minimize", "maximize", "close", |
| 40 |
"filler", |
| 41 |
NULL |
| 42 |
}; |
| 43 |
|
| 44 |
typedef struct |
| 45 |
{ |
| 46 |
ccss_node_t basic; |
| 47 |
CopperClasses copper_class; |
| 48 |
} CopperNode; |
| 49 |
|
| 50 |
CopperNode copper_nodes[CC_LAST]; |
| 51 |
|
| 52 |
static char const* |
| 53 |
get_type (ccss_node_t const *self) |
| 54 |
{ |
| 55 |
g_warning ("It's %s", self->type_name); |
| 56 |
return self->type_name; |
| 57 |
} |
| 58 |
|
| 59 |
static ptrdiff_t |
| 60 |
get_instance (ccss_node_t const *self) |
| 61 |
{ |
| 62 |
return self->instance; |
| 63 |
} |
| 64 |
|
| 65 |
static char const* |
| 66 |
get_style (ccss_node_t const *self) { |
| 67 |
return self->inline_style; |
| 68 |
} |
| 69 |
|
| 70 |
static ccss_node_t* |
| 71 |
get_container (ccss_node_t const *self) |
| 72 |
{ |
| 73 |
CopperClasses candidate = parents[((CopperNode*)self)->copper_class]; |
| 74 |
|
| 75 |
if (candidate==CC_LAST) |
| 76 |
return NULL; |
| 77 |
else |
| 78 |
/* or should we allocate a new one? */ |
| 79 |
return (ccss_node_t*) &(copper_nodes[candidate]); |
| 80 |
} |
| 81 |
|
| 82 |
static const char* |
| 83 |
get_class (ccss_node_t const *self) |
| 84 |
{ |
| 85 |
return copper_classnames[((CopperNode*)self)->copper_class]; |
| 86 |
} |
| 87 |
|
| 88 |
static ccss_node_class_t copper_node_class = { |
| 89 |
.get_type = (ccss_node_get_type_f) get_type, |
| 90 |
.get_instance = (ccss_node_get_instance_f) get_instance, |
| 91 |
.get_style = (ccss_node_get_style_f) get_style, |
| 92 |
.get_container = (ccss_node_get_container_f) get_container, |
| 93 |
.get_class = (ccss_node_get_class_f) get_class |
| 94 |
}; |
| 95 |
|
| 96 |
static void |
| 97 |
initialise_classes (void) |
| 98 |
{ |
| 99 |
int i; |
| 100 |
|
| 101 |
for (i=0; i<CC_LAST; i++) |
| 102 |
{ |
| 103 |
ccss_node_init ((ccss_node_t*) &copper_nodes[i], &copper_node_class); |
| 104 |
copper_nodes[i].basic.type_name = names[i]; |
| 105 |
copper_nodes[i].basic.instance = 0; |
| 106 |
copper_nodes[i].basic.inline_style = NULL; |
| 107 |
copper_nodes[i].copper_class = i; |
| 108 |
} |
| 109 |
} |
| 110 |
|
| 111 |
/* ================================================================ */ |
| 112 |
|
| 113 |
static gboolean |
| 114 |
get_number_from_style (ccss_style_t *style, |
| 115 |
char *element, |
| 116 |
int *dummy, |
| 117 |
int *original_value) |
| 118 |
{ |
| 119 |
double d = 0.0; |
| 120 |
gboolean result; |
| 121 |
|
| 122 |
result = ccss_style_get_double (style, element, &d); |
| 123 |
|
| 124 |
if (original_value) |
| 125 |
*original_value = d; |
| 126 |
|
| 127 |
return result; |
| 128 |
} |
| 129 |
|
| 130 |
static void |
| 131 |
reduce_by_padding_borders_and_margins (ccss_stylesheet_t *stylesheet, |
| 132 |
CopperClasses style_id, |
| 133 |
int *x, int *y, int *w, int *h, |
| 134 |
gboolean honour_margins, |
| 135 |
gboolean reverse) |
| 136 |
{ |
| 137 |
ccss_style_t *style = ccss_stylesheet_query (stylesheet, |
| 138 |
(ccss_node_t*) &copper_nodes[style_id]); |
| 139 |
int bt=0, br=0, bb=0, bl=0, pt=0, pr=0, pb=0, pl=0; |
| 140 |
|
| 141 |
if (!style) return; |
| 142 |
|
| 143 |
/* FIXME this is silly; libccss should do this for us */ |
| 144 |
/* FIXME maybe it does if we get the property rather than the number */ |
| 145 |
if (get_number_from_style (style, "border-width", NULL, &bl)) |
| 146 |
{ |
| 147 |
bt = br = bb = bl; |
| 148 |
} |
| 149 |
else |
| 150 |
{ |
| 151 |
get_number_from_style (style, "border-top-width", NULL, &bt); |
| 152 |
get_number_from_style (style, "border-right-width", NULL, &br); |
| 153 |
get_number_from_style (style, "border-bottom-width", NULL, &bb); |
| 154 |
get_number_from_style (style, "border-left-width", NULL, &bl); |
| 155 |
} |
| 156 |
|
| 157 |
if (get_number_from_style (style, "padding", NULL, &pl)) |
| 158 |
{ |
| 159 |
pt = pr = pb = pl; |
| 160 |
} |
| 161 |
else |
| 162 |
{ |
| 163 |
get_number_from_style (style, "padding-top", NULL, &pt); |
| 164 |
get_number_from_style (style, "padding-right", NULL, &pr); |
| 165 |
get_number_from_style (style, "padding-bottom", NULL, &pb); |
| 166 |
get_number_from_style (style, "padding-left", NULL, &pl); |
| 167 |
} |
| 168 |
|
| 169 |
/* FIXME honour honour_margins */ |
| 170 |
|
| 171 |
bt += pt; |
| 172 |
br += pr; |
| 173 |
bb += pb; |
| 174 |
bl += pl; |
| 175 |
|
| 176 |
if (reverse) |
| 177 |
{ |
| 178 |
if (x) *x -= bl; |
| 179 |
if (y) *y -= bt; |
| 180 |
if (w) *w += (bl+br); |
| 181 |
if (h) *h += (bt+bb); |
| 182 |
} |
| 183 |
else |
| 184 |
{ |
| 185 |
if (x) *x += bl; |
| 186 |
if (y) *y += bt; |
| 187 |
if (w) *w -= (bl+br); |
| 188 |
if (h) *h -= (bt+bb); |
| 189 |
} |
| 190 |
|
| 191 |
ccss_style_destroy (style); |
| 192 |
} |
| 193 |
|
| 194 |
static gint |
| 195 |
draw_rectangle (ccss_stylesheet_t *stylesheet, |
| 196 |
cairo_t *cr, |
| 197 |
CopperClasses style_id, |
| 198 |
int x, int y, int w, int h, |
| 199 |
gboolean honour_margins, |
| 200 |
gboolean from_the_right, |
| 201 |
PangoLayout *layout) |
| 202 |
{ |
| 203 |
ccss_style_t *style = ccss_stylesheet_query (stylesheet, |
| 204 |
(ccss_node_t*) &copper_nodes[style_id]); |
| 205 |
int full_width; |
| 206 |
int horizontal_margin = 0; |
| 207 |
|
| 208 |
if (!style) return 0; |
| 209 |
|
| 210 |
if (honour_margins) |
| 211 |
{ |
| 212 |
int mn, mt, mr, mb, ml; |
| 213 |
/* FIXME: Setting just "margin" doesn't work |
| 214 |
* because libccss doesn't know about margins. |
| 215 |
*/ |
| 216 |
get_number_from_style (style, "margin", NULL, &mn); |
| 217 |
if (mn) |
| 218 |
{ |
| 219 |
/* FIXME this is broken; "margin" may have multiple values */ |
| 220 |
mt = mr = mb = ml = mn; |
| 221 |
} |
| 222 |
else |
| 223 |
{ |
| 224 |
get_number_from_style (style, "margin-top", NULL, &mt); |
| 225 |
get_number_from_style (style, "margin-right", NULL, &mr); |
| 226 |
get_number_from_style (style, "margin-bottom", NULL, &mb); |
| 227 |
get_number_from_style (style, "margin-left", NULL, &ml); |
| 228 |
} |
| 229 |
|
| 230 |
x += ml; |
| 231 |
y += mt; |
| 232 |
horizontal_margin = ml+mr; |
| 233 |
h -= (mt+mb); |
| 234 |
} |
| 235 |
|
| 236 |
if (w==0) |
| 237 |
{ |
| 238 |
int height, width; |
| 239 |
|
| 240 |
get_number_from_style (style, "height", NULL, &height); |
| 241 |
get_number_from_style (style, "width", NULL, &width); |
| 242 |
|
| 243 |
if (height!=0 && width!=0) |
| 244 |
{ |
| 245 |
int min_w, max_w; |
| 246 |
double scale = ((double)h/(double)height); |
| 247 |
|
| 248 |
w = (int) ((double)width) * scale; |
| 249 |
|
| 250 |
get_number_from_style (style, "min-width", NULL, &min_w); |
| 251 |
get_number_from_style (style, "max-width", NULL, &max_w); |
| 252 |
|
| 253 |
if (max_w && w>max_w && max_w>min_w) w = max_w; |
| 254 |
if (w<min_w) w = min_w; |
| 255 |
} |
| 256 |
} |
| 257 |
|
| 258 |
full_width = w+ horizontal_margin; |
| 259 |
|
| 260 |
if (from_the_right) |
| 261 |
x -= full_width; |
| 262 |
|
| 263 |
ccss_cairo_style_draw_rectangle (style, cr, x, y, w, h); |
| 264 |
|
| 265 |
if (layout) |
| 266 |
{ |
| 267 |
reduce_by_padding_borders_and_margins (stylesheet, CC_TITLE, |
| 268 |
&x, &y, &w, &h, |
| 269 |
TRUE, FALSE); |
| 270 |
/* |
| 271 |
* we should maybe undo the translate later, but |
| 272 |
* we're always the last one to use this context anyway |
| 273 |
*/ |
| 274 |
cairo_translate (cr, x, y); |
| 275 |
|
| 276 |
pango_cairo_show_layout (cr, layout); |
| 277 |
} |
| 278 |
|
| 279 |
ccss_style_destroy (style); |
| 280 |
|
| 281 |
return full_width; |
| 282 |
} |
| 283 |
|
| 284 |
static char* |
| 285 |
handle_data_url(char *data) |
| 286 |
{ |
| 287 |
const char* base64 = "base64"; |
| 288 |
char *cursor = data; |
| 289 |
const char *base64_cursor = base64; |
| 290 |
gchar *result; |
| 291 |
gsize result_length; |
| 292 |
gchar *md5, *filename, *uri; |
| 293 |
|
| 294 |
md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, cursor, -1); |
| 295 |
|
| 296 |
/* FIXME: here we should check whether it already exists in /tmp |
| 297 |
* and return that if so |
| 298 |
*/ |
| 299 |
|
| 300 |
while (*cursor && *cursor!=',') |
| 301 |
{ |
| 302 |
if (*base64_cursor) |
| 303 |
{ |
| 304 |
if (*cursor==*base64_cursor) |
| 305 |
base64_cursor++; |
| 306 |
else |
| 307 |
base64_cursor = base64; |
| 308 |
} |
| 309 |
cursor++; |
| 310 |
} |
| 311 |
|
| 312 |
if (*base64_cursor) |
| 313 |
{ |
| 314 |
/* we didn't see "base64" */ |
| 315 |
g_warning ("data: URLs must be base64 encoded in this system!"); |
| 316 |
g_free (md5); |
| 317 |
return NULL; |
| 318 |
} |
| 319 |
|
| 320 |
result = g_base64_decode (cursor+1, &result_length); |
| 321 |
|
| 322 |
/* unfortunately we can't just return the data; libccss requires it |
| 323 |
* to be in a file. |
| 324 |
*/ |
| 325 |
filename = g_build_filename ("/tmp", md5, NULL); |
| 326 |
g_file_set_contents (filename, result, result_length, NULL); |
| 327 |
|
| 328 |
uri = g_strdup_printf ("file://%s", filename); |
| 329 |
g_free (filename); |
| 330 |
g_free (result); |
| 331 |
g_free (md5); |
| 332 |
|
| 333 |
return uri; |
| 334 |
} |
| 335 |
|
| 336 |
static char * |
| 337 |
url (GSList const *args, |
| 338 |
void *user_data) |
| 339 |
{ |
| 340 |
char *cwd; |
| 341 |
char *path; |
| 342 |
char *uri; |
| 343 |
char *filename = NULL; |
| 344 |
|
| 345 |
g_return_val_if_fail (args && args->data, NULL); |
| 346 |
filename = (char*) args->data; |
| 347 |
|
| 348 |
if (strcmp (filename, "wm:icon")==0) |
| 349 |
{ |
| 350 |
/* this is magic */ |
| 351 |
return g_strdup ("file:///service/website/themecreator.marnanel.org/htdocs/gtk-edit.png"); |
| 352 |
} |
| 353 |
#if 0 |
| 354 |
else if (strncmp (filename, "data:", 5)==0) |
| 355 |
{ |
| 356 |
return handle_data_url (filename+5); |
| 357 |
} |
| 358 |
#endif |
| 359 |
else if (index (filename, '/') == NULL) |
| 360 |
{ |
| 361 |
char *md5, *result; |
| 362 |
md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, filename, -1); |
| 363 |
result = g_strdup_printf ("file:///service/website/themecreator.marnanel.org/htdocs/w/images/%c/%c%c/%s", |
| 364 |
md5[0], md5[0], md5[1], |
| 365 |
filename); |
| 366 |
g_free (md5); |
| 367 |
return result; |
| 368 |
} |
| 369 |
else |
| 370 |
{ |
| 371 |
return g_strdup ("file:///service/website/themecreator.marnanel.org/htdocs/gtk-error.png"); |
| 372 |
} |
| 373 |
} |
| 374 |
|
| 375 |
static char * |
| 376 |
named (GSList const *args, void *user_data) |
| 377 |
{ |
| 378 |
char *name = NULL; |
| 379 |
|
| 380 |
if (!name) |
| 381 |
/* fall back to black */ |
| 382 |
return g_strdup("#00000000"); |
| 383 |
|
| 384 |
name = args->data; |
| 385 |
g_warning ("Looking up named colour %s", name); |
| 386 |
|
| 387 |
/* stub: magenta for everything */ |
| 388 |
return g_strdup("#FF00FFFF"); |
| 389 |
} |
| 390 |
|
| 391 |
static ccss_function_t const _functions[] = |
| 392 |
{ |
| 393 |
{ "url", url, NULL }, |
| 394 |
{ "named", named, NULL }, |
| 395 |
{ NULL } |
| 396 |
}; |
| 397 |
|
| 398 |
static PangoLayout* |
| 399 |
title_text (ccss_stylesheet_t *stylesheet, |
| 400 |
cairo_t *cr, |
| 401 |
char *text) |
| 402 |
{ |
| 403 |
PangoLayout *layout; |
| 404 |
PangoAttrList *attrs = NULL; |
| 405 |
ccss_style_t *style = ccss_stylesheet_query (stylesheet, |
| 406 |
(ccss_node_t*) &copper_nodes[CC_TITLE]); |
| 407 |
char *align; |
| 408 |
ccss_color_t const *colour; |
| 409 |
|
| 410 |
layout = pango_cairo_create_layout (cr); |
| 411 |
|
| 412 |
attrs = pango_attr_list_new (); |
| 413 |
|
| 414 |
/* We have to handle CSS text properties ourselves here because |
| 415 |
* libccss doesn't know how to render them yet. We don't try |
| 416 |
* to do all the funky effects like shadows and so on, |
| 417 |
* unfortunately. |
| 418 |
*/ |
| 419 |
|
| 420 |
if (ccss_style_get_property (style, "color", |
| 421 |
(const ccss_property_base_t**) &colour)) |
| 422 |
{ |
| 423 |
pango_attr_list_insert (attrs, |
| 424 |
pango_attr_foreground_new |
| 425 |
((int)65535.0*colour->red, |
| 426 |
(int)65535.0*colour->green, |
| 427 |
(int)65535.0*colour->blue)); |
| 428 |
} |
| 429 |
|
| 430 |
/* Alignment */ |
| 431 |
|
| 432 |
if (ccss_style_get_string (style, "text-align", &align)) |
| 433 |
{ |
| 434 |
if (strcmp (align, "left")==0) |
| 435 |
{ |
| 436 |
pango_layout_set_alignment (layout, PANGO_ALIGN_LEFT); |
| 437 |
} |
| 438 |
else if (strcmp (align, "center")==0) |
| 439 |
{ |
| 440 |
pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER); |
| 441 |
} |
| 442 |
else if (strcmp (align, "right")==0) |
| 443 |
{ |
| 444 |
pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT); |
| 445 |
} |
| 446 |
else |
| 447 |
/* FIXME: "align" contains garbage if it's undefined; |
| 448 |
* is there an easy way of checking? |
| 449 |
*/ |
| 450 |
g_warning ("Unknown alignment: %s", align); |
| 451 |
} |
| 452 |
|
| 453 |
pango_layout_set_attributes (layout, attrs); |
| 454 |
|
| 455 |
/* The actual text */ |
| 456 |
pango_layout_set_text (layout, text, -1); |
| 457 |
|
| 458 |
ccss_style_destroy (style); |
| 459 |
|
| 460 |
return layout; |
| 461 |
} |
| 462 |
|
| 463 |
static void |
| 464 |
render_copper (cairo_surface_t *surface, |
| 465 |
ccss_stylesheet_t *stylesheet) |
| 466 |
{ |
| 467 |
cairo_t *cr = cairo_create (surface); |
| 468 |
|
| 469 |
int x = 0; |
| 470 |
int y = 0; |
| 471 |
int w = cairo_image_surface_get_width (surface); |
| 472 |
int h = cairo_image_surface_get_height (surface); |
| 473 |
|
| 474 |
PangoRectangle text_extents; |
| 475 |
|
| 476 |
int titlebar_height; |
| 477 |
|
| 478 |
CopperClasses left_buttons[] = {CC_MENU, CC_LAST}; |
| 479 |
CopperClasses right_buttons[] = {CC_CLOSE, CC_MAXIMIZE, CC_MINIMIZE, CC_LAST}; |
| 480 |
CopperClasses *cursor; |
| 481 |
int leftpos, rightpos; |
| 482 |
|
| 483 |
PangoLayout *layout = title_text (stylesheet, cr, "Badgers"); |
| 484 |
|
| 485 |
pango_layout_get_pixel_extents (layout, NULL, &text_extents); |
| 486 |
reduce_by_padding_borders_and_margins (stylesheet, CC_TITLE, |
| 487 |
&text_extents.x, &text_extents.y, |
| 488 |
&text_extents.width, |
| 489 |
&text_extents.height, |
| 490 |
TRUE, TRUE); |
| 491 |
|
| 492 |
titlebar_height = text_extents.height; |
| 493 |
|
| 494 |
reduce_by_padding_borders_and_margins (stylesheet, CC_TITLEBAR, |
| 495 |
NULL, NULL, NULL, |
| 496 |
&titlebar_height, |
| 497 |
TRUE, TRUE); |
| 498 |
|
| 499 |
draw_rectangle (stylesheet, cr, CC_FRAME, x, y, w, h, FALSE, FALSE, NULL); |
| 500 |
|
| 501 |
reduce_by_padding_borders_and_margins (stylesheet, CC_FRAME, &x, &y, &w, &h, FALSE, FALSE); |
| 502 |
|
| 503 |
draw_rectangle (stylesheet, cr, CC_TITLEBAR, x, y, w, titlebar_height, TRUE, FALSE, NULL); |
| 504 |
|
| 505 |
draw_rectangle (stylesheet, cr, CC_CONTENT, x, y+titlebar_height, w, h-titlebar_height, TRUE, FALSE, NULL); |
| 506 |
|
| 507 |
h = titlebar_height; |
| 508 |
|
| 509 |
reduce_by_padding_borders_and_margins (stylesheet, CC_TITLEBAR, &x, &y, &w, &h, TRUE, FALSE); |
| 510 |
|
| 511 |
cursor = left_buttons; |
| 512 |
leftpos = x; |
| 513 |
while (*cursor != CC_LAST) |
| 514 |
{ |
| 515 |
leftpos += draw_rectangle (stylesheet, cr, *cursor, leftpos, y, 0, h, TRUE, FALSE, NULL); |
| 516 |
cursor++; |
| 517 |
} |
| 518 |
|
| 519 |
cursor = right_buttons; |
| 520 |
rightpos = x+w; |
| 521 |
while (*cursor != CC_LAST) |
| 522 |
{ |
| 523 |
rightpos -= draw_rectangle (stylesheet, cr, *cursor, rightpos, y, 0, h, TRUE, TRUE, NULL); |
| 524 |
cursor++; |
| 525 |
} |
| 526 |
|
| 527 |
switch (pango_layout_get_alignment (layout)) |
| 528 |
{ |
| 529 |
case PANGO_ALIGN_LEFT: |
| 530 |
x = leftpos; |
| 531 |
draw_rectangle (stylesheet, cr, CC_FILLER, leftpos+text_extents.width, y, |
| 532 |
(rightpos-leftpos)-text_extents.width, h, TRUE, FALSE, NULL); |
| 533 |
break; |
| 534 |
case PANGO_ALIGN_CENTER: |
| 535 |
x = leftpos + ((rightpos-leftpos)/2 - text_extents.width); |
| 536 |
draw_rectangle (stylesheet, cr, CC_FILLER, leftpos, y, |
| 537 |
x-leftpos, h, TRUE, FALSE, NULL); |
| 538 |
draw_rectangle (stylesheet, cr, CC_FILLER, x+text_extents.width, y, |
| 539 |
rightpos-(x+text_extents.width), h, TRUE, FALSE, NULL); |
| 540 |
break; |
| 541 |
case PANGO_ALIGN_RIGHT: |
| 542 |
x = rightpos - text_extents.width; |
| 543 |
draw_rectangle (stylesheet, cr, CC_FILLER, leftpos, y, |
| 544 |
rightpos-(leftpos-text_extents.width), h, TRUE, FALSE, NULL); |
| 545 |
break; |
| 546 |
default: |
| 547 |
g_error ("Unknown alignment"); |
| 548 |
} |
| 549 |
|
| 550 |
draw_rectangle (stylesheet, cr, CC_TITLE, x, y, text_extents.width, h, TRUE, FALSE, layout); |
| 551 |
|
| 552 |
g_object_unref (G_OBJECT (layout)); |
| 553 |
cairo_destroy (cr); |
| 554 |
} |
| 555 |
|
| 556 |
int |
| 557 |
main (int argc, |
| 558 |
char **argv) |
| 559 |
{ |
| 560 |
ccss_grammar_t *grammar; |
| 561 |
ccss_stylesheet_t *stylesheet; |
| 562 |
ccss_style_t *style; |
| 563 |
|
| 564 |
cairo_surface_t *surface; |
| 565 |
|
| 566 |
int image_width = 800; |
| 567 |
int image_height = 250; |
| 568 |
|
| 569 |
initialise_classes (); |
| 570 |
|
| 571 |
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, |
| 572 |
image_width, |
| 573 |
image_height); |
| 574 |
|
| 575 |
grammar = ccss_cairo_grammar_create (); |
| 576 |
ccss_grammar_add_functions (grammar, _functions); |
| 577 |
stylesheet = ccss_grammar_create_stylesheet_from_file (grammar, |
| 578 |
argv[1], |
| 579 |
NULL); |
| 580 |
|
| 581 |
render_copper (surface, stylesheet); |
| 582 |
|
| 583 |
ccss_stylesheet_destroy (stylesheet); |
| 584 |
ccss_grammar_destroy (grammar); |
| 585 |
|
| 586 |
cairo_surface_write_to_png (surface, argv[2]); |
| 587 |
cairo_surface_destroy (surface); |
| 588 |
|
| 589 |
return EXIT_SUCCESS; |
| 590 |
} |