1 /* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
2 /* cairo - a vector graphics library with display and print output
3 *
4 * Copyright © 2016 Adrian Johnson
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it either under the terms of the GNU Lesser General Public
8 * License version 2.1 as published by the Free Software Foundation
9 * (the "LGPL") or, at your option, under the terms of the Mozilla
10 * Public License Version 1.1 (the "MPL"). If you do not alter this
11 * notice, a recipient may use your version of this file under either
12 * the MPL or the LGPL.
13 *
14 * You should have received a copy of the LGPL along with this library
15 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
17 * You should have received a copy of the MPL along with this library
18 * in the file COPYING-MPL-1.1
19 *
20 * The contents of this file are subject to the Mozilla Public License
21 * Version 1.1 (the "License"); you may not use this file except in
22 * compliance with the License. You may obtain a copy of the License at
23 * http://www.mozilla.org/MPL/
24 *
25 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
26 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
27 * the specific language governing rights and limitations.
28 *
29 * The Original Code is the cairo graphics library.
30 *
31 * The Initial Developer of the Original Code is Adrian Johnson.
32 *
33 * Contributor(s):
34 * Adrian Johnson <ajohnson@redneon.com>
35 */
36
37 #include "cairoint.h"
38
39 #include "cairo-array-private.h"
40 #include "cairo-list-inline.h"
41 #include "cairo-tag-attributes-private.h"
42
43 #include <string.h>
44
45 typedef enum {
46 ATTRIBUTE_BOOL, /* Either true/false or 1/0 may be used. */
47 ATTRIBUTE_INT,
48 ATTRIBUTE_FLOAT, /* Decimal separator is in current locale. */
49 ATTRIBUTE_STRING, /* Enclose in single quotes. String escapes:
50 * \' - single quote
51 * \\ - backslash
52 */
53 } attribute_type_t;
54
55 typedef struct _attribute_spec {
56 const char *name;
57 attribute_type_t type;
58 int array_size; /* 0 = scalar, -1 = variable size array */
59 } attribute_spec_t;
60
61 /*
62 * name [required] Unique name of this destination (UTF-8)
63 * x [optional] x coordinate of destination on page. Default is x coord of
64 * extents of operations enclosed by the dest begin/end tags.
65 * y [optional] y coordinate of destination on page. Default is y coord of
66 * extents of operations enclosed by the dest begin/end tags.
67 * internal [optional] If true, the name may be optimized out of the PDF where
68 * possible. Default false.
69 */
70 static attribute_spec_t _dest_attrib_spec[] = {
71 { "name", ATTRIBUTE_STRING },
72 { "x", ATTRIBUTE_FLOAT },
73 { "y", ATTRIBUTE_FLOAT },
74 { "internal", ATTRIBUTE_BOOL },
75 { NULL }
76 };
77
78 /*
79 * rect [optional] One or more rectangles to define link region. Default
80 * is the extents of the operations enclosed by the link begin/end tags.
81 * Each rectangle is specified by four array elements: x, y, width, height.
82 * ie the array size must be a multiple of four.
83 *
84 * Internal Links
85 * --------------
86 * either:
87 * dest - name of dest tag in the PDF file to link to (UTF8)
88 * or
89 * page - Page number in the PDF file to link to
90 * pos - [optional] Position of destination on page. Default is 0,0.
91 *
92 * URI Links
93 * ---------
94 * uri [required] Uniform resource identifier (ASCII).
95
96 * File Links
97 * ----------
98 * file - [required] File name of PDF file to link to.
99 * either:
100 * dest - name of dest tag in the PDF file to link to (UTF8)
101 * or
102 * page - Page number in the PDF file to link to
103 * pos - [optional] Position of destination on page. Default is 0,0.
104 */
105 static attribute_spec_t _link_attrib_spec[] =
106 {
107 { "rect", ATTRIBUTE_FLOAT, -1 },
108 { "dest", ATTRIBUTE_STRING },
109 { "uri", ATTRIBUTE_STRING },
110 { "file", ATTRIBUTE_STRING },
111 { "page", ATTRIBUTE_INT },
112 { "pos", ATTRIBUTE_FLOAT, 2 },
113 { NULL }
114 };
115
116 /*
117 * Required:
118 * Columns - width of the image in pixels.
119 * Rows - height of the image in scan lines.
120 *
121 * Optional:
122 * K - An integer identifying the encoding scheme used. < 0 is 2 dimensional
123 * Group 4, = 0 is Group3 1 dimensional, > 0 is mixed 1 and 2 dimensional
124 * encoding. Default: 0.
125 * EndOfLine - If true end-of-line bit patterns are present. Default: false.
126 * EncodedByteAlign - If true the end of line is padded with 0 bits so the next
127 * line begins on a byte boundary. Default: false.
128 * EndOfBlock - If true the data contains an end-of-block pattern. Default: true.
129 * BlackIs1 - If true 1 bits are black pixels. Default: false.
130 * DamagedRowsBeforeError - Number of damages rows tolerated before an error
131 * occurs. Default: 0.
132 */
133 static attribute_spec_t _ccitt_params_spec[] =
134 {
135 { "Columns", ATTRIBUTE_INT },
136 { "Rows", ATTRIBUTE_INT },
137 { "K", ATTRIBUTE_INT },
138 { "EndOfLine", ATTRIBUTE_BOOL },
139 { "EncodedByteAlign", ATTRIBUTE_BOOL },
140 { "EndOfBlock", ATTRIBUTE_BOOL },
141 { "BlackIs1", ATTRIBUTE_BOOL },
142 { "DamagedRowsBeforeError", ATTRIBUTE_INT },
143 { NULL }
144 };
145
146 /*
147 * bbox - Bounding box of EPS file. The format is [ llx lly urx ury ]
148 * llx - lower left x xoordinate
149 * lly - lower left y xoordinate
150 * urx - upper right x xoordinate
151 * ury - upper right y xoordinate
152 * all coordinates are in PostScript coordinates.
153 */
154 static attribute_spec_t _eps_params_spec[] =
155 {
156 { "bbox", ATTRIBUTE_FLOAT, 4 },
157 { NULL }
158 };
159
160 typedef union {
161 cairo_bool_t b;
162 int i;
163 double f;
164 char *s;
165 } attrib_val_t;
166
167 typedef struct _attribute {
168 char *name;
169 attribute_type_t type;
170 int array_len; /* 0 = scalar */
171 attrib_val_t scalar;
172 cairo_array_t array; /* array of attrib_val_t */
173 cairo_list_t link;
174 } attribute_t;
175
176 static const char *
skip_space(const char * p)177 skip_space (const char *p)
178 {
179 while (_cairo_isspace (*p))
180 p++;
181
182 return p;
183 }
184
185 static const char *
parse_bool(const char * p,cairo_bool_t * b)186 parse_bool (const char *p, cairo_bool_t *b)
187 {
188 if (*p == '1') {
189 *b = TRUE;
190 return p + 1;
191 } else if (*p == '0') {
192 *b = FALSE;
193 return p + 1;
194 } else if (strcmp (p, "true") == 0) {
195 *b = TRUE;
196 return p + 4;
197 } else if (strcmp (p, "false") == 0) {
198 *b = FALSE;
199 return p + 5;
200 }
201
202 return NULL;
203 }
204
205 static const char *
parse_int(const char * p,int * i)206 parse_int (const char *p, int *i)
207 {
208 int n;
209
210 if (sscanf(p, "%d%n", i, &n) > 0)
211 return p + n;
212
213 return NULL;
214 }
215
216 static const char *
parse_float(const char * p,double * d)217 parse_float (const char *p, double *d)
218 {
219 int n;
220 const char *start = p;
221 cairo_bool_t has_decimal_point = FALSE;
222
223 while (*p) {
224 if (*p == '.' || *p == ']' || _cairo_isspace (*p))
225 break;
226 p++;
227 }
228
229 if (*p == '.')
230 has_decimal_point = TRUE;
231
232 if (has_decimal_point) {
233 char *end;
234 *d = _cairo_strtod (start, &end);
235 if (end)
236 return end;
237
238 } else {
239 if (sscanf(start, "%lf%n", d, &n) > 0)
240 return start + n;
241 }
242
243 return NULL;
244 }
245
246 static const char *
decode_string(const char * p,int * len,char * s)247 decode_string (const char *p, int *len, char *s)
248 {
249 if (*p != '\'')
250 return NULL;
251
252 p++;
253 if (! *p)
254 return NULL;
255
256 *len = 0;
257 while (*p) {
258 if (*p == '\\') {
259 p++;
260 if (*p) {
261 if (s)
262 *s++ = *p;
263 p++;
264 (*len)++;
265 }
266 } else if (*p == '\'') {
267 return p + 1;
268 } else {
269 if (s)
270 *s++ = *p;
271 p++;
272 (*len)++;
273 }
274 }
275
276 return NULL;
277 }
278
279 static const char *
parse_string(const char * p,char ** s)280 parse_string (const char *p, char **s)
281 {
282 const char *end;
283 int len;
284
285 end = decode_string (p, &len, NULL);
286 if (!end)
287 return NULL;
288
289 *s = _cairo_malloc (len + 1);
290 decode_string (p, &len, *s);
291 (*s)[len] = 0;
292
293 return end;
294 }
295
296 static const char *
parse_scalar(const char * p,attribute_type_t type,attrib_val_t * scalar)297 parse_scalar (const char *p, attribute_type_t type, attrib_val_t *scalar)
298 {
299 switch (type) {
300 case ATTRIBUTE_BOOL:
301 return parse_bool (p, &scalar->b);
302 case ATTRIBUTE_INT:
303 return parse_int (p, &scalar->i);
304 case ATTRIBUTE_FLOAT:
305 return parse_float (p, &scalar->f);
306 case ATTRIBUTE_STRING:
307 return parse_string (p, &scalar->s);
308 }
309
310 return NULL;
311 }
312
313 static cairo_int_status_t
parse_array(const char * p,attribute_type_t type,cairo_array_t * array,const char ** end)314 parse_array (const char *p, attribute_type_t type, cairo_array_t *array, const char **end)
315 {
316 attrib_val_t val;
317 cairo_int_status_t status;
318
319 p = skip_space (p);
320 if (! *p)
321 return _cairo_error (CAIRO_INT_STATUS_TAG_ERROR);
322
323 if (*p++ != '[')
324 return _cairo_error (CAIRO_INT_STATUS_TAG_ERROR);
325
326 while (TRUE) {
327 p = skip_space (p);
328 if (! *p)
329 return _cairo_error (CAIRO_INT_STATUS_TAG_ERROR);
330
331 if (*p == ']') {
332 *end = p + 1;
333 return CAIRO_INT_STATUS_SUCCESS;
334 }
335
336 p = parse_scalar (p, type, &val);
337 if (!p)
338 return _cairo_error (CAIRO_INT_STATUS_TAG_ERROR);
339
340 status = _cairo_array_append (array, &val);
341 if (unlikely (status))
342 return status;
343 }
344
345 return _cairo_error (CAIRO_INT_STATUS_TAG_ERROR);
346 }
347
348 static cairo_int_status_t
parse_name(const char * p,const char ** end,char ** s)349 parse_name (const char *p, const char **end, char **s)
350 {
351 const char *p2;
352 char *name;
353 int len;
354
355 if (! _cairo_isalpha (*p))
356 return _cairo_error (CAIRO_INT_STATUS_TAG_ERROR);
357
358 p2 = p;
359 while (_cairo_isalpha (*p2) || _cairo_isdigit (*p2))
360 p2++;
361
362 len = p2 - p;
363 name = _cairo_malloc (len + 1);
364 if (unlikely (name == NULL))
365 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
366
367 memcpy (name, p, len);
368 name[len] = 0;
369 *s = name;
370 *end = p2;
371
372 return CAIRO_INT_STATUS_SUCCESS;
373 }
374
375 static cairo_int_status_t
parse_attributes(const char * attributes,attribute_spec_t * attrib_def,cairo_list_t * list)376 parse_attributes (const char *attributes, attribute_spec_t *attrib_def, cairo_list_t *list)
377 {
378 attribute_spec_t *def;
379 attribute_t *attrib;
380 char *name = NULL;
381 cairo_int_status_t status;
382 const char *p = attributes;
383
384 if (! p)
385 return _cairo_error (CAIRO_INT_STATUS_TAG_ERROR);
386
387 while (*p) {
388 p = skip_space (p);
389 if (! *p)
390 break;
391
392 status = parse_name (p, &p, &name);
393 if (status)
394 return status;
395
396 def = attrib_def;
397 while (def->name) {
398 if (strcmp (name, def->name) == 0)
399 break;
400 def++;
401 }
402 if (! def->name) {
403 status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR);
404 goto fail1;
405 }
406
407 attrib = calloc (1, sizeof (attribute_t));
408 if (unlikely (attrib == NULL)) {
409 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
410 goto fail1;
411 }
412
413 attrib->name = name;
414 attrib->type = def->type;
415 _cairo_array_init (&attrib->array, sizeof(attrib_val_t));
416
417 p = skip_space (p);
418 if (def->type == ATTRIBUTE_BOOL && *p != '=') {
419 attrib->scalar.b = TRUE;
420 } else {
421 if (*p++ != '=') {
422 status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR);
423 goto fail2;
424 }
425
426 if (def->array_size == 0) {
427 p = parse_scalar (p, def->type, &attrib->scalar);
428 if (!p) {
429 status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR);
430 goto fail2;
431 }
432
433 attrib->array_len = 0;
434 } else {
435 status = parse_array (p, def->type, &attrib->array, &p);
436 if (unlikely (status))
437 goto fail2;
438
439 attrib->array_len = _cairo_array_num_elements (&attrib->array);
440 if (def->array_size > 0 && attrib->array_len != def->array_size) {
441 status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR);
442 goto fail2;
443 }
444 }
445 }
446
447 cairo_list_add_tail (&attrib->link, list);
448 }
449
450 return CAIRO_INT_STATUS_SUCCESS;
451
452 fail2:
453 _cairo_array_fini (&attrib->array);
454 if (attrib->type == ATTRIBUTE_STRING)
455 free (attrib->scalar.s);
456 free (attrib);
457 fail1:
458 free (name);
459
460 return status;
461 }
462
463 static void
free_attributes_list(cairo_list_t * list)464 free_attributes_list (cairo_list_t *list)
465 {
466 attribute_t *attr, *next;
467
468 cairo_list_foreach_entry_safe (attr, next, attribute_t, list, link)
469 {
470 cairo_list_del (&attr->link);
471 free (attr->name);
472 _cairo_array_fini (&attr->array);
473 if (attr->type == ATTRIBUTE_STRING)
474 free (attr->scalar.s);
475 free (attr);
476 }
477 }
478
479 static attribute_t *
find_attribute(cairo_list_t * list,const char * name)480 find_attribute (cairo_list_t *list, const char *name)
481 {
482 attribute_t *attr;
483
484 cairo_list_foreach_entry (attr, attribute_t, list, link)
485 {
486 if (strcmp (attr->name, name) == 0)
487 return attr;
488 }
489
490 return NULL;
491 }
492
493 cairo_int_status_t
_cairo_tag_parse_link_attributes(const char * attributes,cairo_link_attrs_t * link_attrs)494 _cairo_tag_parse_link_attributes (const char *attributes, cairo_link_attrs_t *link_attrs)
495 {
496 cairo_list_t list;
497 cairo_int_status_t status;
498 attribute_t *attr;
499 attrib_val_t val;
500
501 cairo_list_init (&list);
502 status = parse_attributes (attributes, _link_attrib_spec, &list);
503 if (unlikely (status))
504 return status;
505
506 memset (link_attrs, 0, sizeof (cairo_link_attrs_t));
507 _cairo_array_init (&link_attrs->rects, sizeof (cairo_rectangle_t));
508 if (find_attribute (&list, "uri")) {
509 link_attrs->link_type = TAG_LINK_URI;
510 } else if (find_attribute (&list, "file")) {
511 link_attrs->link_type = TAG_LINK_FILE;
512 } else if (find_attribute (&list, "dest")) {
513 link_attrs->link_type = TAG_LINK_DEST;
514 } else if (find_attribute (&list, "page")) {
515 link_attrs->link_type = TAG_LINK_DEST;
516 } else {
517 link_attrs->link_type = TAG_LINK_EMPTY;
518 goto cleanup;
519 }
520
521 cairo_list_foreach_entry (attr, attribute_t, &list, link)
522 {
523 if (strcmp (attr->name, "uri") == 0) {
524 if (link_attrs->link_type != TAG_LINK_URI) {
525 status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR);
526 goto cleanup;
527 }
528
529 link_attrs->uri = strdup (attr->scalar.s);
530 } else if (strcmp (attr->name, "file") == 0) {
531 if (link_attrs->link_type != TAG_LINK_FILE) {
532 status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR);
533 goto cleanup;
534 }
535
536 link_attrs->file = strdup (attr->scalar.s);
537 } else if (strcmp (attr->name, "dest") == 0) {
538 if (! (link_attrs->link_type == TAG_LINK_DEST ||
539 link_attrs->link_type != TAG_LINK_FILE)) {
540 status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR);
541 goto cleanup;
542 }
543
544 link_attrs->dest = strdup (attr->scalar.s);
545 } else if (strcmp (attr->name, "page") == 0) {
546 if (! (link_attrs->link_type == TAG_LINK_DEST ||
547 link_attrs->link_type != TAG_LINK_FILE)) {
548 status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR);
549 goto cleanup;
550 }
551
552 link_attrs->page = attr->scalar.i;
553
554 } else if (strcmp (attr->name, "pos") == 0) {
555 if (! (link_attrs->link_type == TAG_LINK_DEST ||
556 link_attrs->link_type != TAG_LINK_FILE)) {
557 status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR);
558 goto cleanup;
559 }
560
561 _cairo_array_copy_element (&attr->array, 0, &val);
562 link_attrs->pos.x = val.f;
563 _cairo_array_copy_element (&attr->array, 1, &val);
564 link_attrs->pos.y = val.f;
565 link_attrs->has_pos = TRUE;
566 } else if (strcmp (attr->name, "rect") == 0) {
567 cairo_rectangle_t rect;
568 int i;
569 int num_elem = _cairo_array_num_elements (&attr->array);
570 if (num_elem == 0 || num_elem % 4 != 0) {
571 status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR);
572 goto cleanup;
573 }
574
575 for (i = 0; i < num_elem; i += 4) {
576 _cairo_array_copy_element (&attr->array, i, &val);
577 rect.x = val.f;
578 _cairo_array_copy_element (&attr->array, i+1, &val);
579 rect.y = val.f;
580 _cairo_array_copy_element (&attr->array, i+2, &val);
581 rect.width = val.f;
582 _cairo_array_copy_element (&attr->array, i+3, &val);
583 rect.height = val.f;
584 status = _cairo_array_append (&link_attrs->rects, &rect);
585 if (unlikely (status))
586 goto cleanup;
587 }
588 }
589 }
590
591 cleanup:
592 free_attributes_list (&list);
593 if (unlikely (status)) {
594 free (link_attrs->dest);
595 free (link_attrs->uri);
596 free (link_attrs->file);
597 _cairo_array_fini (&link_attrs->rects);
598 }
599
600 return status;
601 }
602
603 cairo_int_status_t
_cairo_tag_parse_dest_attributes(const char * attributes,cairo_dest_attrs_t * dest_attrs)604 _cairo_tag_parse_dest_attributes (const char *attributes, cairo_dest_attrs_t *dest_attrs)
605 {
606 cairo_list_t list;
607 cairo_int_status_t status;
608 attribute_t *attr;
609
610 memset (dest_attrs, 0, sizeof (cairo_dest_attrs_t));
611 cairo_list_init (&list);
612 status = parse_attributes (attributes, _dest_attrib_spec, &list);
613 if (unlikely (status))
614 goto cleanup;
615
616 cairo_list_foreach_entry (attr, attribute_t, &list, link)
617 {
618 if (strcmp (attr->name, "name") == 0) {
619 dest_attrs->name = strdup (attr->scalar.s);
620 } else if (strcmp (attr->name, "x") == 0) {
621 dest_attrs->x = attr->scalar.f;
622 dest_attrs->x_valid = TRUE;
623 } else if (strcmp (attr->name, "y") == 0) {
624 dest_attrs->y = attr->scalar.f;
625 dest_attrs->y_valid = TRUE;
626 } else if (strcmp (attr->name, "internal") == 0) {
627 dest_attrs->internal = attr->scalar.b;
628 }
629 }
630
631 if (! dest_attrs->name)
632 status = _cairo_error (CAIRO_INT_STATUS_TAG_ERROR);
633
634 cleanup:
635 free_attributes_list (&list);
636
637 return status;
638 }
639
640 cairo_int_status_t
_cairo_tag_parse_ccitt_params(const char * attributes,cairo_ccitt_params_t * ccitt_params)641 _cairo_tag_parse_ccitt_params (const char *attributes, cairo_ccitt_params_t *ccitt_params)
642 {
643 cairo_list_t list;
644 cairo_int_status_t status;
645 attribute_t *attr;
646
647 ccitt_params->columns = -1;
648 ccitt_params->rows = -1;
649
650 /* set defaults */
651 ccitt_params->k = 0;
652 ccitt_params->end_of_line = FALSE;
653 ccitt_params->encoded_byte_align = FALSE;
654 ccitt_params->end_of_block = TRUE;
655 ccitt_params->black_is_1 = FALSE;
656 ccitt_params->damaged_rows_before_error = 0;
657
658 cairo_list_init (&list);
659 status = parse_attributes (attributes, _ccitt_params_spec, &list);
660 if (unlikely (status))
661 goto cleanup;
662
663 cairo_list_foreach_entry (attr, attribute_t, &list, link)
664 {
665 if (strcmp (attr->name, "Columns") == 0) {
666 ccitt_params->columns = attr->scalar.i;
667 } else if (strcmp (attr->name, "Rows") == 0) {
668 ccitt_params->rows = attr->scalar.i;
669 } else if (strcmp (attr->name, "K") == 0) {
670 ccitt_params->k = attr->scalar.i;
671 } else if (strcmp (attr->name, "EndOfLine") == 0) {
672 ccitt_params->end_of_line = attr->scalar.b;
673 } else if (strcmp (attr->name, "EncodedByteAlign") == 0) {
674 ccitt_params->encoded_byte_align = attr->scalar.b;
675 } else if (strcmp (attr->name, "EndOfBlock") == 0) {
676 ccitt_params->end_of_block = attr->scalar.b;
677 } else if (strcmp (attr->name, "BlackIs1") == 0) {
678 ccitt_params->black_is_1 = attr->scalar.b;
679 } else if (strcmp (attr->name, "DamagedRowsBeforeError") == 0) {
680 ccitt_params->damaged_rows_before_error = attr->scalar.b;
681 }
682 }
683
684 cleanup:
685 free_attributes_list (&list);
686
687 return status;
688 }
689
690 cairo_int_status_t
_cairo_tag_parse_eps_params(const char * attributes,cairo_eps_params_t * eps_params)691 _cairo_tag_parse_eps_params (const char *attributes, cairo_eps_params_t *eps_params)
692 {
693 cairo_list_t list;
694 cairo_int_status_t status;
695 attribute_t *attr;
696 attrib_val_t val;
697
698 cairo_list_init (&list);
699 status = parse_attributes (attributes, _eps_params_spec, &list);
700 if (unlikely (status))
701 goto cleanup;
702
703 cairo_list_foreach_entry (attr, attribute_t, &list, link)
704 {
705 if (strcmp (attr->name, "bbox") == 0) {
706 _cairo_array_copy_element (&attr->array, 0, &val);
707 eps_params->bbox.p1.x = val.f;
708 _cairo_array_copy_element (&attr->array, 1, &val);
709 eps_params->bbox.p1.y = val.f;
710 _cairo_array_copy_element (&attr->array, 2, &val);
711 eps_params->bbox.p2.x = val.f;
712 _cairo_array_copy_element (&attr->array, 3, &val);
713 eps_params->bbox.p2.y = val.f;
714 }
715 }
716
717 cleanup:
718 free_attributes_list (&list);
719
720 return status;
721 }
722