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