Lines
10.4 %
Functions
30.77 %
Branches
2.7 %
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2016 Adrian Johnson
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
* The Original Code is the cairo graphics library.
* The Initial Developer of the Original Code is Adrian Johnson.
* Contributor(s):
* Adrian Johnson <ajohnson@redneon.com>
*/
#include "cairoint.h"
#include "cairo-tag-stack-private.h"
/* Tagged PDF must have one of these tags at the top level */
static const char * _cairo_tag_stack_tagged_pdf_top_level_element_list[] =
{
"Document",
"Part",
"Art",
"Sect",
"Div",
NULL
};
/* List of valid tag names. Table numbers reference PDF 32000 */
static const char * _cairo_tag_stack_struct_pdf_list[] =
/* Table 333 - Grouping Elements */
"BlockQuote",
"Caption",
"TOC",
"TOCI",
"Index",
"NonStruct",
"Private",
/* Table 335 - Standard structure types for paragraphlike elements */
"P", "H",
"H1", "H2", "H3", "H4", "H5", "H6",
/* Table 336 - Standard structure types for list elements */
"L", "LI", "Lbl", "LBody",
/* Table 337 - Standard structure types for table elements */
"Table",
"TR", "TH", "TD",
"THead", "TBody", "TFoot",
/* Table 338 - Standard structure types for inline-level structure elements */
"Span",
"Quote",
"Note",
"Reference",
"BibEntry",
"Code",
"Link", /* CAIRO_TAG_LINK */
"Annot",
"Ruby",
"Warichu",
/* Table 339 - Standard structure types for Ruby and Warichu elements */
"RB", "RT", "RP",
"WT", "WP",
/* Table 340 - Standard structure types for illustration elements */
"Figure",
"Formula",
"Form",
/* Section 14.8.2.2.2 - Artifacts */
"Artifact",
/* List of cairo specific tag names */
static const char * _cairo_tag_stack_cairo_tag_list[] =
CAIRO_TAG_DEST,
CAIRO_TAG_CONTENT,
CAIRO_TAG_CONTENT_REF,
void
_cairo_tag_stack_init (cairo_tag_stack_t *stack)
cairo_list_init (&stack->list);
stack->type = TAG_TREE_TYPE_NO_TAGS;
stack->size = 0;
}
_cairo_tag_stack_fini (cairo_tag_stack_t *stack)
while (! cairo_list_is_empty (&stack->list)) {
cairo_tag_stack_elem_t *elem;
elem = cairo_list_first_entry (&stack->list, cairo_tag_stack_elem_t, link);
cairo_list_del (&elem->link);
free (elem->name);
free (elem->attributes);
free (elem);
cairo_tag_stack_structure_type_t
_cairo_tag_stack_get_structure_type (cairo_tag_stack_t *stack)
return stack->type;
static cairo_bool_t
name_in_list (const char *name, const char **list)
if (! name)
return FALSE;
while (*list) {
if (strcmp (name, *list) == 0)
return TRUE;
list++;
cairo_int_status_t
_cairo_tag_stack_push (cairo_tag_stack_t *stack,
const char *name,
const char *attributes)
if (! name_in_list (name, _cairo_tag_stack_struct_pdf_list) &&
! name_in_list (name, _cairo_tag_stack_cairo_tag_list))
stack->type = TAG_TYPE_INVALID;
return _cairo_tag_error ("Invalid tag: %s", name);
cairo_tag_stack_elem_t *top = _cairo_tag_stack_top_elem (stack);
if (top &&
(strcmp (top->name, CAIRO_TAG_CONTENT) == 0 ||
strcmp (top->name, CAIRO_TAG_CONTENT_REF) == 0 ||
strcmp (top->name, "Artifact") == 0))
return _cairo_tag_error ("%s tag can not contain nested tags",
(strcmp (top->name, CAIRO_TAG_CONTENT) == 0) ? "CAIRO_TAG_CONTENT" :
((strcmp (top->name, CAIRO_TAG_CONTENT_REF) == 0) ? "CAIRO_TAG_CONTENT_REF" : top->name));
if (stack->type == TAG_TREE_TYPE_NO_TAGS) {
if (name_in_list (name, _cairo_tag_stack_tagged_pdf_top_level_element_list))
stack->type = TAG_TREE_TYPE_TAGGED;
else if (strcmp (name, "Link") == 0)
stack->type = TAG_TREE_TYPE_LINK_ONLY;
else if (name_in_list (name, _cairo_tag_stack_struct_pdf_list))
stack->type = TAG_TREE_TYPE_STRUCTURE;
} else {
if (stack->type == TAG_TREE_TYPE_LINK_ONLY &&
(strcmp (name, "Link") != 0) &&
name_in_list (name, _cairo_tag_stack_struct_pdf_list))
elem = _cairo_calloc (sizeof(cairo_tag_stack_elem_t));
if (unlikely (elem == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
elem->name = strdup (name);
if (unlikely (elem->name == NULL))
if (attributes) {
elem->attributes = strdup (attributes);
if (unlikely (elem->attributes == NULL))
elem->attributes = NULL;
elem->data = NULL;
cairo_list_add_tail (&elem->link, &stack->list);
stack->size++;
return CAIRO_STATUS_SUCCESS;
cairo_private void
_cairo_tag_stack_set_top_data (cairo_tag_stack_t *stack,
void *data)
cairo_tag_stack_elem_t *top;
top = _cairo_tag_stack_top_elem (stack);
if (top)
top->data = data;
_cairo_tag_stack_pop (cairo_tag_stack_t *stack,
cairo_tag_stack_elem_t **elem)
if (!top) {
return _cairo_tag_error ("cairo_tag_end(\"%s\") no matching begin tag", name);
cairo_list_del (&top->link);
stack->size--;
if (strcmp (top->name, name) != 0) {
cairo_status_t status =
_cairo_tag_error ("cairo_tag_end(\"%s\") does not matching previous begin tag \"%s\"",
name, top->name);
_cairo_tag_stack_free_elem (top);
return status;
if (elem)
*elem = top;
else
cairo_tag_stack_elem_t *
_cairo_tag_stack_top_elem (cairo_tag_stack_t *stack)
if (cairo_list_is_empty (&stack->list))
return NULL;
return cairo_list_last_entry (&stack->list, cairo_tag_stack_elem_t, link);
_cairo_tag_stack_foreach (cairo_tag_stack_t *stack,
void (*func)(cairo_tag_stack_elem_t *elem,
void *closure),
void *closure)
cairo_list_foreach_entry (elem, cairo_tag_stack_elem_t, &stack->list, link) {
func (elem, closure);
_cairo_tag_stack_free_elem (cairo_tag_stack_elem_t *elem)
cairo_tag_type_t
_cairo_tag_get_type (const char *name)
return TAG_TYPE_INVALID;
if (strcmp(name, "Link") == 0)
return (TAG_TYPE_LINK | TAG_TYPE_STRUCTURE);
if (strcmp(name, CAIRO_TAG_DEST) == 0)
return TAG_TYPE_DEST;
if (strcmp(name, CAIRO_TAG_CONTENT) == 0)
return TAG_TYPE_CONTENT;
if (strcmp(name, CAIRO_TAG_CONTENT_REF) == 0)
return TAG_TYPE_CONTENT_REF;
return TAG_TYPE_STRUCTURE;
cairo_status_t
_cairo_tag_error (const char *fmt, ...)
va_list ap;
if (getenv ("CAIRO_DEBUG_TAG") != NULL) {
printf ("TAG ERROR: ");
va_start (ap, fmt);
vprintf (fmt, ap);
va_end (ap);
printf ("\n");
return _cairo_error (CAIRO_STATUS_TAG_ERROR);
_cairo_tag_warning (const char *fmt, ...)
printf ("TAG WARNING: ");