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
#include "cairo-tag-stack-private.h"
43

            
44
#include <string.h>
45

            
46
typedef enum {
47
    ATTRIBUTE_BOOL,  /* Either true/false or 1/0 may be used. */
48
    ATTRIBUTE_INT,
49
    ATTRIBUTE_FLOAT, /* Decimal separator is in current locale. */
50
    ATTRIBUTE_STRING, /* Enclose in single quotes. String escapes:
51
                       *   \'  - single quote
52
                       *   \\  - backslash
53
                       */
54
} attribute_type_t;
55

            
56
typedef struct _attribute_spec {
57
    const char *name;
58
    attribute_type_t type;
59
    int array_size; /* 0 = scalar, -1 = variable size array */
60
} attribute_spec_t;
61

            
62
/*
63
 * id      [optional] content id
64
 * content [optional] One or more content ids
65
 */
66
static const attribute_spec_t _content_attrib_spec[] = {
67
    { "tag_name", ATTRIBUTE_STRING },
68
    { "id",       ATTRIBUTE_STRING },
69
    { NULL }
70
};
71

            
72
/*
73
 * id      [optional] content id
74
 * content [optional] One or more content ids
75
 */
76
static const attribute_spec_t _content_ref_attrib_spec[] = {
77
    { "ref",       ATTRIBUTE_STRING },
78
    { NULL }
79
};
80

            
81
/*
82
 * name [required] Unique name of this destination (UTF-8)
83
 * x    [optional] x coordinate of destination on page. Default is x coord of
84
 *                 extents of operations enclosed by the dest begin/end tags.
85
 * y    [optional] y coordinate of destination on page. Default is y coord of
86
 *                 extents of operations enclosed by the dest begin/end tags.
87
 * internal [optional] If true, the name may be optimized out of the PDF where
88
 *                     possible. Default false.
89
 */
90
static const attribute_spec_t _dest_attrib_spec[] = {
91
    { "name",     ATTRIBUTE_STRING },
92
    { "x",        ATTRIBUTE_FLOAT },
93
    { "y",        ATTRIBUTE_FLOAT },
94
    { "internal", ATTRIBUTE_BOOL },
95
    { NULL }
96
};
97

            
98
/*
99
 * rect [optional] One or more rectangles to define link region. Default
100
 *                 is the extents of the operations enclosed by the link begin/end tags.
101
 *                 Each rectangle is specified by four array elements: x, y, width, height.
102
 *                 ie the array size must be a multiple of four.
103
 *
104
 * Internal Links
105
 * --------------
106
 * either:
107
 *   dest - name of dest tag in the PDF file to link to (UTF8)
108
 * or
109
 *   page - Page number in the PDF file to link to
110
 *   pos  - [optional] Position of destination on page. Default is 0,0.
111
 *
112
 * URI Links
113
 * ---------
114
 * uri [required] Uniform resource identifier (ASCII).
115

            
116
 * File Links
117
 * ----------
118
 * file - [required] File name of PDF file to link to.
119
 *   either:
120
 *     dest - name of dest tag in the PDF file to link to (UTF8)
121
 *   or
122
 *     page - Page number in the PDF file to link to
123
 *     pos  - [optional] Position of destination on page. Default is 0,0.
124
 */
125
static const attribute_spec_t _link_attrib_spec[] =
126
{
127
    { "rect", ATTRIBUTE_FLOAT, -1 },
128
    { "dest", ATTRIBUTE_STRING },
129
    { "uri",  ATTRIBUTE_STRING },
130
    { "file", ATTRIBUTE_STRING },
131
    { "page", ATTRIBUTE_INT },
132
    { "pos",  ATTRIBUTE_FLOAT, 2 },
133
    { "id",   ATTRIBUTE_STRING },
134
    { "ref",  ATTRIBUTE_STRING },
135
    { "link_page", ATTRIBUTE_INT },
136
    { NULL }
137
};
138

            
139
/*
140
 * Required:
141
 *   Columns - width of the image in pixels.
142
 *   Rows    - height of the image in scan lines.
143
 *
144
 * Optional:
145
 *   K - An integer identifying the encoding scheme used. < 0 is 2 dimensional
146
 *       Group 4, = 0 is Group3 1 dimensional, > 0 is mixed 1 and 2 dimensional
147
 *       encoding. Default: 0.
148
 *   EndOfLine  - If true end-of-line bit patterns are present. Default: false.
149
 *   EncodedByteAlign - If true the end of line is padded with 0 bits so the next
150
 *                      line begins on a byte boundary. Default: false.
151
 *   EndOfBlock - If true the data contains an end-of-block pattern. Default: true.
152
 *   BlackIs1   - If true 1 bits are black pixels. Default: false.
153
 *   DamagedRowsBeforeError - Number of damages rows tolerated before an error
154
 *                            occurs. Default: 0.
155
 */
156
static const attribute_spec_t _ccitt_params_spec[] =
157
{
158
    { "Columns",                ATTRIBUTE_INT },
159
    { "Rows",                   ATTRIBUTE_INT },
160
    { "K",                      ATTRIBUTE_INT },
161
    { "EndOfLine",              ATTRIBUTE_BOOL },
162
    { "EncodedByteAlign",       ATTRIBUTE_BOOL },
163
    { "EndOfBlock",             ATTRIBUTE_BOOL },
164
    { "BlackIs1",               ATTRIBUTE_BOOL },
165
    { "DamagedRowsBeforeError", ATTRIBUTE_INT },
166
    { NULL }
167
};
168

            
169
/*
170
 * bbox - Bounding box of EPS file. The format is [ llx lly urx ury ]
171
 *          llx - lower left x xoordinate
172
 *          lly - lower left y xoordinate
173
 *          urx - upper right x xoordinate
174
 *          ury - upper right y xoordinate
175
 *        all coordinates are in PostScript coordinates.
176
 */
177
static const attribute_spec_t _eps_params_spec[] =
178
{
179
    { "bbox", ATTRIBUTE_FLOAT, 4 },
180
    { NULL }
181
};
182

            
183
typedef union {
184
    cairo_bool_t b;
185
    int i;
186
    double f;
187
    char *s;
188
} attrib_val_t;
189

            
190
typedef struct _attribute {
191
    char *name;
192
    attribute_type_t type;
193
    int array_len; /* 0 = scalar */
194
    attrib_val_t scalar;
195
    cairo_array_t array; /* array of attrib_val_t */
196
    cairo_list_t link;
197
} attribute_t;
198

            
199
static const char *
200
skip_space (const char *p)
201
{
202
    while (_cairo_isspace (*p))
203
	p++;
204

            
205
    return p;
206
}
207

            
208
static const char *
209
parse_bool (const char *p, cairo_bool_t *b)
210
{
211
    if (*p == '1') {
212
	*b = TRUE;
213
	return p + 1;
214
    } else if (*p == '0') {
215
	*b = FALSE;
216
	return p + 1;
217
    } else if (strcmp (p, "true") == 0) {
218
	*b = TRUE;
219
	return p + 4;
220
    } else if (strcmp (p, "false") == 0) {
221
	*b = FALSE;
222
	return p + 5;
223
    }
224

            
225
    return NULL;
226
}
227

            
228
static const char *
229
parse_int (const char *p, int *i)
230
{
231
    int n;
232

            
233
    if (sscanf(p, "%d%n", i, &n) > 0)
234
	return p + n;
235

            
236
    return NULL;
237
}
238

            
239
static const char *
240
parse_float (const char *p, double *d)
241
{
242
    int n;
243
    const char *start = p;
244
    cairo_bool_t has_decimal_point = FALSE;
245

            
246
    while (*p) {
247
	if (*p == '.' || *p == ']' || _cairo_isspace (*p))
248
	    break;
249
	p++;
250
    }
251

            
252
    if (*p == '.')
253
	has_decimal_point = TRUE;
254

            
255
    if (has_decimal_point) {
256
	char *end;
257
	*d = _cairo_strtod (start, &end);
258
	if (end && end != start)
259
	    return end;
260

            
261
    } else {
262
	if (sscanf(start, "%lf%n", d, &n) > 0)
263
	    return start + n;
264
    }
265

            
266
    return NULL;
267
}
268

            
269
static const char *
270
decode_string (const char *p, int *len, char *s)
271
{
272
    if (*p != '\'')
273
	return NULL;
274

            
275
    p++;
276
    if (! *p)
277
	return NULL;
278

            
279
    *len = 0;
280
    while (*p) {
281
	if (*p == '\\') {
282
	    p++;
283
	    if (*p) {
284
		if (s)
285
		    *s++ = *p;
286
		p++;
287
		(*len)++;
288
	    }
289
	} else if (*p == '\'') {
290
	    return p + 1;
291
	} else {
292
	    if (s)
293
		*s++ = *p;
294
	    p++;
295
	    (*len)++;
296
	}
297
    }
298

            
299
    return NULL;
300
}
301

            
302
static const char *
303
parse_string (const char *p, char **s)
304
{
305
    const char *end;
306
    int len;
307

            
308
    end = decode_string (p, &len, NULL);
309
    if (!end)
310
	return NULL;
311

            
312
    *s = _cairo_malloc (len + 1);
313
    decode_string (p, &len, *s);
314
    (*s)[len] = 0;
315

            
316
    return end;
317
}
318

            
319
static const char *
320
parse_scalar (const char *p, attribute_type_t type, attrib_val_t *scalar)
321
{
322
    switch (type) {
323
	case ATTRIBUTE_BOOL:
324
	    return parse_bool (p, &scalar->b);
325
	case ATTRIBUTE_INT:
326
	    return parse_int (p, &scalar->i);
327
	case ATTRIBUTE_FLOAT:
328
	    return parse_float (p, &scalar->f);
329
	case ATTRIBUTE_STRING:
330
	    return parse_string (p, &scalar->s);
331
    }
332

            
333
    return NULL;
334
}
335

            
336
static cairo_int_status_t
337
parse_array (const char *attributes, const char *p, attribute_type_t type, cairo_array_t *array, const char **end)
338
{
339
    attrib_val_t val;
340
    cairo_int_status_t status;
341

            
342
    p = skip_space (p);
343
    if (! *p)
344
	goto error;
345

            
346
    if (*p++ != '[')
347
	goto error;
348

            
349
    while (TRUE) {
350
	p = skip_space (p);
351
	if (! *p)
352
	    goto error;
353

            
354
	if (*p == ']') {
355
	    *end = p + 1;
356
	    return CAIRO_INT_STATUS_SUCCESS;
357
	}
358

            
359
	p = parse_scalar (p, type, &val);
360
	if (!p)
361
	    goto error;
362

            
363
	status = _cairo_array_append (array, &val);
364
	if (unlikely (status))
365
	    return status;
366
    }
367

            
368
  error:
369
    return _cairo_tag_error (
370
	"while parsing attributes: \"%s\". Error parsing array", attributes);
371
}
372

            
373
static cairo_int_status_t
374
parse_name (const char *attributes, const char *p, const char **end, char **s)
375
{
376
    const char *p2;
377
    char *name;
378
    int len;
379

            
380
    if (! _cairo_isalpha (*p))
381
	return _cairo_tag_error (
382
	    "while parsing attributes: \"%s\". Error parsing name."
383
	    " \"%s\" does not start with an alphabetic character",
384
	    attributes, p);
385

            
386
    p2 = p;
387
    while (_cairo_isalpha (*p2) || _cairo_isdigit (*p2) || *p2  == '_')
388
	p2++;
389

            
390
    len = p2 - p;
391
    name = _cairo_strndup (p, len);
392
    if (unlikely (name == NULL))
393
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
394
    *s = name;
395
    *end = p2;
396

            
397
    return CAIRO_INT_STATUS_SUCCESS;
398
}
399

            
400
static cairo_int_status_t
401
parse_attributes (const char *attributes, const attribute_spec_t *attrib_def, cairo_list_t *list)
402
{
403
    const attribute_spec_t *def;
404
    attribute_t *attrib;
405
    char *name = NULL;
406
    cairo_int_status_t status;
407
    const char *p = attributes;
408

            
409
    if (! p)
410
	return CAIRO_INT_STATUS_SUCCESS;
411

            
412
    while (*p) {
413
	p = skip_space (p);
414
	if (! *p)
415
	    break;
416

            
417
	status = parse_name (attributes, p, &p, &name);
418
	if (status)
419
	    return status;
420

            
421
	def = attrib_def;
422
	while (def->name) {
423
	    if (strcmp (name, def->name) == 0)
424
		break;
425
	    def++;
426
	}
427

            
428
	if (! def->name) {
429
	    status = _cairo_tag_error (
430
		"while parsing attributes: \"%s\". Unknown attribute name \"%s\"",
431
		attributes, name);
432
	    goto fail1;
433
	}
434

            
435
	attrib = _cairo_calloc (sizeof (attribute_t));
436
	if (unlikely (attrib == NULL)) {
437
	    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
438
	    goto fail1;
439
	}
440

            
441
	attrib->name = name;
442
	attrib->type = def->type;
443
	_cairo_array_init (&attrib->array, sizeof(attrib_val_t));
444

            
445
	p = skip_space (p);
446
	if (def->type == ATTRIBUTE_BOOL && *p != '=') {
447
	    attrib->scalar.b = TRUE;
448
	} else {
449
	    if (*p++ != '=') {
450
		status = _cairo_tag_error (
451
		    "while parsing attributes: \"%s\". Expected '=' after \"%s\"",
452
		    attributes, name);
453
		goto fail2;
454
	    }
455

            
456
	    if (def->array_size == 0) {
457
		const char *s = p;
458
		p = parse_scalar (p, def->type, &attrib->scalar);
459
		if (!p) {
460
		    status = _cairo_tag_error (
461
			"while parsing attributes: \"%s\". Error parsing \"%s\"",
462
			attributes, s);
463
		    goto fail2;
464
		}
465

            
466
		attrib->array_len = 0;
467
	    } else {
468
		status = parse_array (attributes, p, def->type, &attrib->array, &p);
469
		if (unlikely (status))
470
		    goto fail2;
471

            
472
		attrib->array_len = _cairo_array_num_elements (&attrib->array);
473
		if (def->array_size > 0 && attrib->array_len != def->array_size) {
474
		    status = _cairo_tag_error (
475
			"while parsing attributes: \"%s\". Expected %d elements in array. Found %d",
476
			attributes, def->array_size, attrib->array_len);
477
		    goto fail2;
478
		}
479
	    }
480
	}
481

            
482
	cairo_list_add_tail (&attrib->link, list);
483
    }
484

            
485
    return CAIRO_INT_STATUS_SUCCESS;
486

            
487
  fail2:
488
    _cairo_array_fini (&attrib->array);
489
    if (attrib->type == ATTRIBUTE_STRING)
490
	free (attrib->scalar.s);
491
    free (attrib);
492
  fail1:
493
    free (name);
494

            
495
    return status;
496
}
497

            
498
static void
499
free_attributes_list (cairo_list_t *list)
500
{
501
    attribute_t *attr, *next;
502

            
503
    cairo_list_foreach_entry_safe (attr, next, attribute_t, list, link)
504
    {
505
	cairo_list_del (&attr->link);
506
	free (attr->name);
507
	_cairo_array_fini (&attr->array);
508
	if (attr->type == ATTRIBUTE_STRING)
509
	    free (attr->scalar.s);
510
	free (attr);
511
    }
512
}
513

            
514
cairo_int_status_t
515
_cairo_tag_parse_content_attributes (const char *attributes, cairo_content_attrs_t *content_attrs)
516
{
517
    cairo_list_t list;
518
    cairo_int_status_t status;
519
    attribute_t *attr;
520

            
521
    cairo_list_init (&list);
522
    status = parse_attributes (attributes, _content_attrib_spec, &list);
523
    if (unlikely (status))
524
	goto cleanup;
525

            
526
    memset (content_attrs, 0, sizeof (cairo_content_attrs_t));
527
    cairo_list_foreach_entry (attr, attribute_t, &list, link)
528
    {
529
	if (strcmp (attr->name, "tag_name") == 0) {
530
	    content_attrs->tag_name = strdup (attr->scalar.s);
531
	} else if (strcmp (attr->name, "id") == 0) {
532
	    content_attrs->id = strdup (attr->scalar.s);
533
	}
534
    }
535

            
536
    if (! content_attrs->tag_name) {
537
	status = _cairo_tag_error ("CONTENT attributes: \"%s\" missing tag_name attribute",
538
				   attributes);
539
    } else if (! content_attrs->tag_name) {
540
	status = _cairo_tag_error ("CONTENT attributes: \"%s\" missing id attribute",
541
				   attributes);
542
    }
543

            
544
  cleanup:
545
    free_attributes_list (&list);
546

            
547
    return status;
548
}
549

            
550
cairo_int_status_t
551
_cairo_tag_parse_content_ref_attributes (const char *attributes, cairo_content_ref_attrs_t *content_ref_attrs)
552
{
553
    cairo_list_t list;
554
    cairo_int_status_t status;
555
    attribute_t *attr;
556

            
557
    cairo_list_init (&list);
558
    status = parse_attributes (attributes, _content_ref_attrib_spec, &list);
559
    if (unlikely (status))
560
	goto cleanup;
561

            
562
    memset (content_ref_attrs, 0, sizeof (cairo_content_ref_attrs_t));
563
    cairo_list_foreach_entry (attr, attribute_t, &list, link)
564
    {
565
	if (strcmp (attr->name, "ref") == 0) {
566
	    content_ref_attrs->ref = strdup (attr->scalar.s);
567
	}
568
    }
569

            
570
    if (! content_ref_attrs->ref) {
571
	status = _cairo_tag_error ("CONTENT_REF  attributes: \"%s\" missing ref attribute",
572
				   attributes);
573
    }
574

            
575
  cleanup:
576
    free_attributes_list (&list);
577

            
578
    return status;
579
}
580

            
581
cairo_int_status_t
582
_cairo_tag_parse_link_attributes (const char *attributes, cairo_link_attrs_t *link_attrs)
583
{
584
    cairo_list_t list;
585
    cairo_int_status_t status;
586
    attribute_t *attr;
587
    attrib_val_t val;
588
    cairo_bool_t invalid_combination = FALSE;
589

            
590
    cairo_list_init (&list);
591
    status = parse_attributes (attributes, _link_attrib_spec, &list);
592
    if (unlikely (status))
593
	return status;
594

            
595
    memset (link_attrs, 0, sizeof (cairo_link_attrs_t));
596
    _cairo_array_init (&link_attrs->rects, sizeof (cairo_rectangle_t));
597

            
598
    cairo_list_foreach_entry (attr, attribute_t, &list, link) {
599
	if (strcmp (attr->name, "dest") == 0) {
600
	    link_attrs->dest = strdup (attr->scalar.s);
601

            
602
	} else if (strcmp (attr->name, "page") == 0) {
603
	    link_attrs->page = attr->scalar.i;
604
	    if (link_attrs->page < 1) {
605
		status = _cairo_tag_error ("Link attributes: \"%s\" page must be >= 1", attributes);
606
		goto cleanup;
607
	    }
608

            
609
	} else if (strcmp (attr->name, "pos") == 0) {
610
	    _cairo_array_copy_element (&attr->array, 0, &val);
611
	    link_attrs->pos.x = val.f;
612
	    _cairo_array_copy_element (&attr->array, 1, &val);
613
	    link_attrs->pos.y = val.f;
614
	    link_attrs->has_pos = TRUE;
615

            
616
	} else if (strcmp (attr->name, "uri") == 0) {
617
	    link_attrs->uri = strdup (attr->scalar.s);
618

            
619
	} else if (strcmp (attr->name, "file") == 0) {
620
	    link_attrs->file = strdup (attr->scalar.s);
621

            
622
	} else if (strcmp (attr->name, "rect") == 0) {
623
	    cairo_rectangle_t rect;
624
	    int i;
625
	    int num_elem = _cairo_array_num_elements (&attr->array);
626
	    if (num_elem == 0 || num_elem % 4 != 0) {
627
		status = _cairo_tag_error ("Link attributes: \"%s\" rect array size must be multiple of 4",
628
					   attributes);
629
		goto cleanup;
630
	    }
631

            
632
	    for (i = 0; i < num_elem; i += 4) {
633
		_cairo_array_copy_element (&attr->array, i, &val);
634
		rect.x = val.f;
635
		_cairo_array_copy_element (&attr->array, i+1, &val);
636
		rect.y = val.f;
637
		_cairo_array_copy_element (&attr->array, i+2, &val);
638
		rect.width = val.f;
639
		_cairo_array_copy_element (&attr->array, i+3, &val);
640
		rect.height = val.f;
641
		status = _cairo_array_append (&link_attrs->rects, &rect);
642
		if (unlikely (status))
643
		    goto cleanup;
644
	    }
645
	} else if (strcmp (attr->name, "id") == 0) {
646
	    link_attrs->id = strdup (attr->scalar.s);
647
	} else if (strcmp (attr->name, "ref") == 0) {
648
	    link_attrs->ref = strdup (attr->scalar.s);
649
	} else if (strcmp (attr->name, "link_page") == 0) {
650
	    link_attrs->link_page = attr->scalar.i;
651
	    if (link_attrs->link_page < 1) {
652
		status = _cairo_tag_error ("Link attributes: \"%s\" page must be >= 1", attributes);
653
		goto cleanup;
654
	    }
655
	}
656
    }
657

            
658
    if (link_attrs->file) {
659
	link_attrs->link_type = TAG_LINK_FILE;
660
	if (link_attrs->uri)
661
	    invalid_combination = TRUE;
662
	else if (link_attrs->dest && (link_attrs->page || link_attrs->has_pos))
663
	    invalid_combination = TRUE;
664

            
665
    } else if (link_attrs->uri && link_attrs->dest) {
666
	link_attrs->link_type = TAG_LINK_DEST_AND_URI;
667
	if (link_attrs->page || link_attrs->has_pos || link_attrs->file)
668
	    invalid_combination = TRUE;
669

            
670
    } else if (link_attrs->uri) {
671
	link_attrs->link_type = TAG_LINK_URI;
672
	if (link_attrs->dest || link_attrs->page || link_attrs->has_pos || link_attrs->file)
673
	    invalid_combination = TRUE;
674

            
675
    } else if (link_attrs->dest) {
676
	link_attrs->link_type = TAG_LINK_DEST;
677
	if (link_attrs->uri || link_attrs->page || link_attrs->has_pos)
678
	    invalid_combination = TRUE;
679

            
680
    } else if (link_attrs->page) {
681
	link_attrs->link_type = TAG_LINK_PAGE;
682
	if (link_attrs->uri || link_attrs->dest)
683
	    invalid_combination = TRUE;
684

            
685
    } else {
686
	link_attrs->link_type = TAG_LINK_EMPTY;
687
	if (link_attrs->has_pos)
688
	    invalid_combination = TRUE;
689
    }
690

            
691
    if (invalid_combination) {
692
	status = _cairo_tag_error (
693
	    "Link attributes: \"%s\" invalid combination of attributes", attributes);
694
    }
695

            
696
  cleanup:
697
    free_attributes_list (&list);
698
    if (unlikely (status)) {
699
	free (link_attrs->dest);
700
	free (link_attrs->uri);
701
	free (link_attrs->file);
702
	_cairo_array_fini (&link_attrs->rects);
703
    }
704

            
705
    return status;
706
}
707

            
708
cairo_int_status_t
709
_cairo_tag_parse_dest_attributes (const char *attributes, cairo_dest_attrs_t *dest_attrs)
710
{
711
    cairo_list_t list;
712
    cairo_int_status_t status;
713
    attribute_t *attr;
714

            
715
    memset (dest_attrs, 0, sizeof (cairo_dest_attrs_t));
716
    cairo_list_init (&list);
717
    status = parse_attributes (attributes, _dest_attrib_spec, &list);
718
    if (unlikely (status))
719
	goto cleanup;
720

            
721
    cairo_list_foreach_entry (attr, attribute_t, &list, link)
722
    {
723
	if (strcmp (attr->name, "name") == 0) {
724
	    dest_attrs->name = strdup (attr->scalar.s);
725
	} else if (strcmp (attr->name, "x") == 0) {
726
	    dest_attrs->x = attr->scalar.f;
727
	    dest_attrs->x_valid = TRUE;
728
	} else if (strcmp (attr->name, "y") == 0) {
729
	    dest_attrs->y = attr->scalar.f;
730
	    dest_attrs->y_valid = TRUE;
731
	} else if (strcmp (attr->name, "internal") == 0) {
732
	    dest_attrs->internal = attr->scalar.b;
733
	}
734
    }
735

            
736
    if (! dest_attrs->name)
737
	status = _cairo_tag_error ("Destination attributes: \"%s\" missing name attribute",
738
				   attributes);
739

            
740
  cleanup:
741
    free_attributes_list (&list);
742

            
743
    return status;
744
}
745

            
746
cairo_int_status_t
747
_cairo_tag_parse_ccitt_params (const char *attributes, cairo_ccitt_params_t *ccitt_params)
748
{
749
    cairo_list_t list;
750
    cairo_int_status_t status;
751
    attribute_t *attr;
752

            
753
    ccitt_params->columns = -1;
754
    ccitt_params->rows = -1;
755

            
756
    /* set defaults */
757
    ccitt_params->k = 0;
758
    ccitt_params->end_of_line = FALSE;
759
    ccitt_params->encoded_byte_align = FALSE;
760
    ccitt_params->end_of_block = TRUE;
761
    ccitt_params->black_is_1 = FALSE;
762
    ccitt_params->damaged_rows_before_error = 0;
763

            
764
    cairo_list_init (&list);
765
    status = parse_attributes (attributes, _ccitt_params_spec, &list);
766
    if (unlikely (status))
767
	goto cleanup;
768

            
769
    cairo_list_foreach_entry (attr, attribute_t, &list, link)
770
    {
771
	if (strcmp (attr->name, "Columns") == 0) {
772
	    ccitt_params->columns = attr->scalar.i;
773
	} else if (strcmp (attr->name, "Rows") == 0) {
774
	    ccitt_params->rows = attr->scalar.i;
775
	} else if (strcmp (attr->name, "K") == 0) {
776
	    ccitt_params->k = attr->scalar.i;
777
	} else if (strcmp (attr->name, "EndOfLine") == 0) {
778
	    ccitt_params->end_of_line = attr->scalar.b;
779
	} else if (strcmp (attr->name, "EncodedByteAlign") == 0) {
780
	    ccitt_params->encoded_byte_align = attr->scalar.b;
781
	} else if (strcmp (attr->name, "EndOfBlock") == 0) {
782
	    ccitt_params->end_of_block = attr->scalar.b;
783
	} else if (strcmp (attr->name, "BlackIs1") == 0) {
784
	    ccitt_params->black_is_1 = attr->scalar.b;
785
	} else if (strcmp (attr->name, "DamagedRowsBeforeError") == 0) {
786
	    ccitt_params->damaged_rows_before_error = attr->scalar.b;
787
	}
788
    }
789

            
790
  cleanup:
791
    free_attributes_list (&list);
792

            
793
    return status;
794
}
795

            
796
cairo_int_status_t
797
_cairo_tag_parse_eps_params (const char *attributes, cairo_eps_params_t *eps_params)
798
{
799
    cairo_list_t list;
800
    cairo_int_status_t status;
801
    attribute_t *attr;
802
    attrib_val_t val;
803

            
804
    cairo_list_init (&list);
805
    status = parse_attributes (attributes, _eps_params_spec, &list);
806
    if (unlikely (status))
807
	goto cleanup;
808

            
809
    cairo_list_foreach_entry (attr, attribute_t, &list, link)
810
    {
811
	if (strcmp (attr->name, "bbox") == 0) {
812
	    _cairo_array_copy_element (&attr->array, 0, &val);
813
	    eps_params->bbox.p1.x = val.f;
814
	    _cairo_array_copy_element (&attr->array, 1, &val);
815
	    eps_params->bbox.p1.y = val.f;
816
	    _cairo_array_copy_element (&attr->array, 2, &val);
817
	    eps_params->bbox.p2.x = val.f;
818
	    _cairo_array_copy_element (&attr->array, 3, &val);
819
	    eps_params->bbox.p2.y = val.f;
820
	}
821
    }
822

            
823
  cleanup:
824
    free_attributes_list (&list);
825

            
826
    return status;
827
}
828

            
829
void
830
5
_cairo_tag_free_link_attributes (cairo_link_attrs_t *link_attrs)
831
{
832
5
    _cairo_array_fini (&link_attrs->rects);
833
5
    free (link_attrs->dest);
834
5
    free (link_attrs->uri);
835
5
    free (link_attrs->file);
836
5
    free (link_attrs->id);
837
5
    free (link_attrs->ref);
838
5
}
839

            
840
void
841
_cairo_tag_free_dest_attributes (cairo_dest_attrs_t *dest_attrs)
842
{
843
    free (dest_attrs->name);
844
}
845

            
846
void
847
_cairo_tag_free_content_attributes (cairo_content_attrs_t *content_attrs)
848
{
849
    free (content_attrs->id);
850
    free (content_attrs->tag_name);
851
}
852

            
853
void
854
_cairo_tag_free_content_ref_attributes (cairo_content_ref_attrs_t *content_ref_attrs)
855
{
856
    free (content_ref_attrs->ref);
857
}