blob: b8c52c640e940a19b04381bd1975c3ec786c0f8d [file] [log] [blame]
//========================================================================
//
// StructTreeRoot.cc
//
// This file is licensed under the GPLv2 or later
//
// Copyright 2013, 2014 Igalia S.L.
// Copyright 2014 Fabio D'Urso <fabiodurso@hotmail.it>
// Copyright 2017 Jan-Erik S <janerik234678@gmail.com>
// Copyright 2017 Albert Astals Cid <aacid@kde.org>
//
//========================================================================
#ifdef USE_GCC_PRAGMAS
#pragma interface
#endif
#include "goo/GooString.h"
#include "StructTreeRoot.h"
#include "StructElement.h"
#include "PDFDoc.h"
#include "Object.h"
#include "Dict.h"
#include <set>
#include <assert.h>
StructTreeRoot::StructTreeRoot(PDFDoc *docA, Dict *structTreeRootDict):
doc(docA)
{
assert(doc);
assert(structTreeRootDict);
parse(structTreeRootDict);
}
StructTreeRoot::~StructTreeRoot()
{
for (ElemPtrArray::iterator i = elements.begin(); i != elements.end(); ++i)
delete *i;
}
void StructTreeRoot::parse(Dict *root)
{
// The RoleMap/ClassMap dictionaries are needed by all the parsing
// functions, which will resolve the custom names to canonical
// standard names.
roleMap = root->lookup("RoleMap");
classMap = root->lookup("ClassMap");
// ParentTree (optional). If present, it must be a number tree,
// otherwise it is not possible to map stream objects to their
// corresponsing structure element. Here only the references are
// loaded into the array, the pointers to the StructElements will
// be filled-in later when parsing them.
Object obj = root->lookup("ParentTree");
if (obj.isDict()) {
Object nums = obj.dictLookup("Nums");
if (nums.isArray()) {
if (nums.arrayGetLength() % 2 == 0) {
parentTree.resize(nums.arrayGetLength() / 2);
// Index numbers in even positions, references in odd ones
for (int i = 0; i < nums.arrayGetLength(); i += 2) {
Object index = nums.arrayGet(i);
if (!index.isInt()) {
error(errSyntaxError, -1, "Nums item at position {0:d} is wrong type ({1:s})", i, index.getTypeName());
continue;
}
const unsigned idx = index.getInt();
if (idx < 0 || idx >= parentTree.size()) {
error(errSyntaxError, -1, "Nums item at position {0:d} is invalid value ({1:d}): [0..{2:d}]", i, idx, parentTree.size() - 1);
continue;
}
Object value = nums.arrayGetNF(i + 1);
if (value.isRef()) {
parentTree[idx].resize(1);
parentTree[idx][0].ref = value.getRef();
} else {
value = nums.arrayGet(i + 1);
if (value.isArray()) {
parentTree[idx].resize(value.arrayGetLength());
for (int j = 0; j < value.arrayGetLength(); j++) {
Object itemvalue = value.arrayGetNF(j);
if (itemvalue.isRef())
parentTree[idx][j].ref = itemvalue.getRef();
else
error(errSyntaxError, -1, "Nums array item at position {0:d}/{1:d} is invalid type ({2:s})", i, j, itemvalue.getTypeName());
}
} else {
error(errSyntaxError, -1, "Nums item at position {0:d} is wrong type ({1:s})", i + 1, value.getTypeName());
}
}
}
} else {
error(errSyntaxError, -1, "Nums array length is not a even ({0:d})", nums.arrayGetLength());
}
} else {
error(errSyntaxError, -1, "Nums object is wrong type ({0:s})", nums.getTypeName());
}
}
std::set<int> seenElements;
// Parse the children StructElements
const GBool marked = doc->getCatalog()->getMarkInfo() & Catalog::markInfoMarked;
Object kids = root->lookup("K");
if (kids.isArray()) {
if (marked && kids.arrayGetLength() > 1) {
error(errSyntaxWarning, -1, "K in StructTreeRoot has more than one children in a tagged PDF");
}
for (int i = 0; i < kids.arrayGetLength(); i++) {
Object ref = kids.arrayGetNF(i);
if (ref.isRef()) {
seenElements.insert(ref.getRefNum());
}
Object obj = kids.arrayGet(i);
if (obj.isDict()) {
StructElement *child = new StructElement(obj.getDict(), this, NULL, seenElements);
if (child->isOk()) {
if (marked && !(child->getType() == StructElement::Document ||
child->getType() == StructElement::Part ||
child->getType() == StructElement::Art ||
child->getType() == StructElement::Div)) {
error(errSyntaxWarning, -1, "StructTreeRoot element of tagged PDF is wrong type ({0:s})", child->getTypeName());
}
appendChild(child);
if (ref.isRef()) {
parentTreeAdd(ref.getRef(), child);
}
} else {
error(errSyntaxWarning, -1, "StructTreeRoot element could not be parsed");
delete child;
}
} else {
error(errSyntaxWarning, -1, "K has a child of wrong type ({0:s})", obj.getTypeName());
}
}
} else if (kids.isDict()) {
if (marked) {
error(errSyntaxWarning, -1, "K has a child of wrong type for a tagged PDF ({0:s})", kids.getTypeName());
}
StructElement *child = new StructElement(kids.getDict(), this, NULL, seenElements);
if (child->isOk()) {
appendChild(child);
Object ref = root->lookupNF("K");
if (ref.isRef())
parentTreeAdd(ref.getRef(), child);
} else {
error(errSyntaxWarning, -1, "StructTreeRoot element could not be parsed");
delete child;
}
} else if (!kids.isNull()) {
error(errSyntaxWarning, -1, "K in StructTreeRoot is wrong type ({0:s})", kids.getTypeName());
}
}
void StructTreeRoot::parentTreeAdd(const Ref &objectRef, StructElement *element)
{
for (std::vector< std::vector<Parent> >::iterator i = parentTree.begin(); i != parentTree.end(); ++i) {
for (std::vector<Parent>::iterator j = i->begin(); j != i->end(); ++j) {
if (j->ref.num == objectRef.num && j->ref.gen == objectRef.gen)
j->element = element;
}
}
}