diff options
Diffstat (limited to 'Build/source/libs/poppler/poppler-src/poppler/StructElement.cc')
-rw-r--r-- | Build/source/libs/poppler/poppler-src/poppler/StructElement.cc | 1347 |
1 files changed, 0 insertions, 1347 deletions
diff --git a/Build/source/libs/poppler/poppler-src/poppler/StructElement.cc b/Build/source/libs/poppler/poppler-src/poppler/StructElement.cc deleted file mode 100644 index d87cb20cc98..00000000000 --- a/Build/source/libs/poppler/poppler-src/poppler/StructElement.cc +++ /dev/null @@ -1,1347 +0,0 @@ -//======================================================================== -// -// StructElement.cc -// -// This file is licensed under the GPLv2 or later -// -// Copyright 2013, 2014 Igalia S.L. -// Copyright 2014 Luigi Scarso <luigi.scarso@gmail.com> -// Copyright 2014, 2017, 2018 Albert Astals Cid <aacid@kde.org> -// Copyright 2015 Dmytro Morgun <lztoad@gmail.com> -// Copyright 2018 Adrian Johnson <ajohnson@redneon.com> -// Copyright 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by the LiMux project of the city of Munich -// -//======================================================================== - -#ifdef USE_GCC_PRAGMAS -#pragma interface -#endif - -#include "StructElement.h" -#include "StructTreeRoot.h" -#include "GlobalParams.h" -#include "UnicodeMap.h" -#include "PDFDoc.h" -#include "Dict.h" - -#include <assert.h> - -class GfxState; - - -static GBool isPlacementName(Object *value) -{ - return value->isName("Block") - || value->isName("Inline") - || value->isName("Before") - || value->isName("Start") - || value->isName("End"); -} - -static GBool isWritingModeName(Object *value) -{ - return value->isName("LrTb") - || value->isName("RlTb") - || value->isName("TbRl"); -} - -static GBool isBorderStyleName(Object *value) -{ - return value->isName("None") - || value->isName("Hidden") - || value->isName("Dotted") - || value->isName("Dashed") - || value->isName("Solid") - || value->isName("Double") - || value->isName("Groove") - || value->isName("Ridge") - || value->isName("Inset") - || value->isName("Outset"); -} - -static GBool isTextAlignName(Object *value) -{ - return value->isName("Start") - || value->isName("End") - || value->isName("Center") - || value->isName("Justify"); -} - -static GBool isBlockAlignName(Object *value) -{ - return value->isName("Before") - || value->isName("Middle") - || value->isName("After") - || value->isName("Justify"); -} - -static GBool isInlineAlignName(Object *value) -{ - return value->isName("Start") - || value->isName("End") - || value->isName("Center"); -} - -static GBool isNumber(Object *value) -{ - return value->isNum(); -} - -static GBool isLineHeight(Object *value) -{ - return value->isName("Normal") - || value->isName("Auto") - || isNumber(value); -} - -static GBool isTextDecorationName(Object *value) -{ - return value->isName("None") - || value->isName("Underline") - || value->isName("Overline") - || value->isName("LineThrough"); -} - -static GBool isRubyAlignName(Object *value) -{ - return value->isName("Start") - || value->isName("End") - || value->isName("Center") - || value->isName("Justify") - || value->isName("Distribute"); -} - -static GBool isRubyPositionName(Object *value) -{ - return value->isName("Before") - || value->isName("After") - || value->isName("Warichu") - || value->isName("Inline"); -} - -static GBool isGlyphOrientationName(Object *value) -{ - return value->isName("Auto") - || value->isName("90") - || value->isName("180") - || value->isName("270") - || value->isName("360") - || value->isName("-90") - || value->isName("-180"); -} - -static GBool isListNumberingName(Object *value) -{ - return value->isName("None") - || value->isName("Disc") - || value->isName("Circle") - || value->isName("Square") - || value->isName("Decimal") - || value->isName("UpperRoman") - || value->isName("LowerRoman") - || value->isName("UpperAlpha") - || value->isName("LowerAlpha"); -} - -static GBool isFieldRoleName(Object *value) -{ - return value->isName("rb") - || value->isName("cb") - || value->isName("pb") - || value->isName("tv"); -} - -static GBool isFieldCheckedName(Object *value) -{ - return value->isName("on") - || value->isName("off") - || value->isName("neutral"); -} - -static GBool isTableScopeName(Object *value) -{ - return value->isName("Row") - || value->isName("Column") - || value->isName("Both"); -} - -static GBool isRGBColor(Object *value) -{ - if (!(value->isArray() && value->arrayGetLength() == 3)) - return gFalse; - - GBool okay = gTrue; - for (int i = 0; i < 3; i++) { - Object obj = value->arrayGet(i); - if (!obj.isNum()) { - okay = gFalse; - break; - } - if (obj.getNum() < 0.0 || obj.getNum() > 1.0) { - okay = gFalse; - break; - } - } - - return okay; -} - -static GBool isNatural(Object *value) -{ - return (value->isInt() && value->getInt() > 0) - || (value->isInt64() && value->getInt64() > 0); -} - -static GBool isPositive(Object *value) -{ - return value->isNum() && value->getNum() >= 0.0; -} - -static GBool isNumberOrAuto(Object *value) -{ - return isNumber(value) || value->isName("Auto"); -} - -static GBool isTextString(Object *value) -{ - // XXX: Shall isName() also be checked? - return value->isString(); -} - - -#define ARRAY_CHECKER(name, checkItem, length, allowSingle, allowNulls) \ - static GBool name(Object *value) { \ - if (!value->isArray()) \ - return allowSingle ? checkItem(value) : gFalse; \ - \ - if (length && value->arrayGetLength() != length) \ - return gFalse; \ - \ - GBool okay = gTrue; \ - for (int i = 0; i < value->arrayGetLength(); i++) { \ - Object obj = value->arrayGet(i); \ - if ((!allowNulls && obj.isNull()) || !checkItem(&obj)) { \ - okay = gFalse; \ - break; \ - } \ - } \ - return okay; \ - } - -ARRAY_CHECKER(isRGBColorOrOptionalArray4, isRGBColor, 4, gTrue, gTrue ) -ARRAY_CHECKER(isPositiveOrOptionalArray4, isPositive, 4, gTrue, gTrue ) -ARRAY_CHECKER(isPositiveOrArray4, isPositive, 4, gTrue, gFalse) -ARRAY_CHECKER(isBorderStyle, isBorderStyleName, 4, gTrue, gTrue ) -ARRAY_CHECKER(isNumberArray4, isNumber, 4, gFalse, gFalse) -ARRAY_CHECKER(isNumberOrArrayN, isNumber, 0, gTrue, gFalse) -ARRAY_CHECKER(isTableHeaders, isTextString, 0, gFalse, gFalse) - - -// Type of functions used to do type-checking on attribute values -typedef GBool (*AttributeCheckFunc)(Object*); - -// Maps attributes to their names and whether the attribute can be inherited. -struct AttributeMapEntry { - Attribute::Type type; - const char *name; - const Object *defval; - GBool inherit; - AttributeCheckFunc check; -}; - -struct AttributeDefaults { - AttributeDefaults() {}; // needed to support old clang - - Object Inline = Object(objName, "Inline"); - Object LrTb = Object(objName, "LrTb"); - Object Normal = Object(objName, "Normal"); - Object Distribute = Object(objName, "Distribute"); - Object off = Object(objName, "off"); - Object Zero = Object(0.0); - Object Auto = Object(objName, "Auto"); - Object Start = Object(objName, "Start"); - Object None = Object(objName, "None"); - Object Before = Object(objName, "Before"); - Object Nat1 = Object(1); -}; - -static const AttributeDefaults attributeDefaults; - - -#define ATTR_LIST_END \ - { Attribute::Unknown, nullptr, nullptr, gFalse, nullptr } - -#define ATTR_WITH_DEFAULT(name, inherit, check, defval) \ - { Attribute::name, \ - #name, \ - &attributeDefaults.defval, \ - inherit, \ - check } - -#define ATTR(name, inherit, check) \ - { Attribute::name, \ - #name, \ - nullptr, \ - inherit, \ - check } - -static const AttributeMapEntry attributeMapCommonShared[] = -{ - ATTR_WITH_DEFAULT(Placement, gFalse, isPlacementName, Inline), - ATTR_WITH_DEFAULT(WritingMode, gTrue, isWritingModeName, LrTb), - ATTR (BackgroundColor, gFalse, isRGBColor), - ATTR (BorderColor, gTrue, isRGBColorOrOptionalArray4), - ATTR_WITH_DEFAULT(BorderStyle, gFalse, isBorderStyle, None), - ATTR (BorderThickness, gTrue, isPositiveOrOptionalArray4), - ATTR_WITH_DEFAULT(Padding, gFalse, isPositiveOrArray4, Zero), - ATTR (Color, gTrue, isRGBColor), - ATTR_LIST_END -}; - -static const AttributeMapEntry attributeMapCommonBlock[] = -{ - ATTR_WITH_DEFAULT(SpaceBefore, gFalse, isPositive, Zero), - ATTR_WITH_DEFAULT(SpaceAfter, gFalse, isPositive, Zero), - ATTR_WITH_DEFAULT(StartIndent, gTrue, isNumber, Zero), - ATTR_WITH_DEFAULT(EndIndent, gTrue, isNumber, Zero), - ATTR_WITH_DEFAULT(TextIndent, gTrue, isNumber, Zero), - ATTR_WITH_DEFAULT(TextAlign, gTrue, isTextAlignName, Start), - ATTR (BBox, gFalse, isNumberArray4), - ATTR_WITH_DEFAULT(Width, gFalse, isNumberOrAuto, Auto), - ATTR_WITH_DEFAULT(Height, gFalse, isNumberOrAuto, Auto), - ATTR_WITH_DEFAULT(BlockAlign, gTrue, isBlockAlignName, Before), - ATTR_WITH_DEFAULT(InlineAlign, gTrue, isInlineAlignName, Start), - ATTR_LIST_END -}; - -static const AttributeMapEntry attributeMapCommonInline[] = -{ - ATTR_WITH_DEFAULT(BaselineShift, gFalse, isNumber, Zero), - ATTR_WITH_DEFAULT(LineHeight, gTrue, isLineHeight, Normal), - ATTR (TextDecorationColor, gTrue, isRGBColor), - ATTR (TextDecorationThickness, gTrue, isPositive), - ATTR_WITH_DEFAULT(TextDecorationType, gFalse, isTextDecorationName, None), - ATTR_WITH_DEFAULT(GlyphOrientationVertical, gTrue, isGlyphOrientationName, Auto), - ATTR_LIST_END -}; - -static const AttributeMapEntry attributeMapCommonRubyText[] = -{ - ATTR_WITH_DEFAULT(RubyPosition, gTrue, isRubyPositionName, Before), - ATTR_WITH_DEFAULT(RubyAlign, gTrue, isRubyAlignName, Distribute), - ATTR_LIST_END -}; - -static const AttributeMapEntry attributeMapCommonColumns[] = -{ - ATTR_WITH_DEFAULT(ColumnCount, gFalse, isNatural, Nat1), - ATTR (ColumnGap, gFalse, isNumberOrArrayN), - ATTR (ColumnWidths, gFalse, isNumberOrArrayN), - ATTR_LIST_END -}; - -static const AttributeMapEntry attributeMapCommonList[] = { - ATTR_WITH_DEFAULT(ListNumbering, gTrue, isListNumberingName, None), - ATTR_LIST_END -}; - -static const AttributeMapEntry attributeMapCommonPrintField[] = -{ - ATTR (Role, gFalse, isFieldRoleName), - ATTR_WITH_DEFAULT(checked, gFalse, isFieldCheckedName, off), - ATTR (Desc, gFalse, isTextString), - ATTR_LIST_END -}; - -static const AttributeMapEntry attributeMapCommonTable[] = -{ - ATTR(Headers, gFalse, isTableHeaders), - ATTR(Scope, gFalse, isTableScopeName), - ATTR(Summary, gFalse, isTextString), - ATTR_LIST_END -}; - -static const AttributeMapEntry attributeMapCommonTableCell[] = -{ - ATTR_WITH_DEFAULT(RowSpan, gFalse, isNatural, Nat1), - ATTR_WITH_DEFAULT(ColSpan, gFalse, isNatural, Nat1), - ATTR_WITH_DEFAULT(TBorderStyle, gTrue, isBorderStyle, None), - ATTR_WITH_DEFAULT(TPadding, gTrue, isPositiveOrArray4, Zero), - ATTR_LIST_END -}; - -#undef ATTR_WITH_DEFAULT -#undef ATTR - - -static const AttributeMapEntry *attributeMapAll[] = { - attributeMapCommonShared, - attributeMapCommonBlock, - attributeMapCommonInline, - attributeMapCommonRubyText, - attributeMapCommonColumns, - attributeMapCommonList, - attributeMapCommonPrintField, - attributeMapCommonTable, - attributeMapCommonTableCell, - nullptr, -}; - -static const AttributeMapEntry *attributeMapShared[] = { - attributeMapCommonShared, - nullptr, -}; - -static const AttributeMapEntry *attributeMapBlock[] = { - attributeMapCommonShared, - attributeMapCommonBlock, - nullptr, -}; - -static const AttributeMapEntry *attributeMapInline[] = { - attributeMapCommonShared, - attributeMapCommonInline, - nullptr, -}; - -static const AttributeMapEntry *attributeMapTableCell[] = { - attributeMapCommonShared, - attributeMapCommonBlock, - attributeMapCommonTable, - attributeMapCommonTableCell, - nullptr, -}; - -static const AttributeMapEntry *attributeMapRubyText[] = { - attributeMapCommonShared, - attributeMapCommonInline, - attributeMapCommonRubyText, - nullptr, -}; - -static const AttributeMapEntry *attributeMapColumns[] = { - attributeMapCommonShared, - attributeMapCommonInline, - attributeMapCommonColumns, - nullptr, -}; - -static const AttributeMapEntry *attributeMapList[] = { - attributeMapCommonShared, - attributeMapCommonList, - nullptr, -}; - -static const AttributeMapEntry *attributeMapTable[] = { - attributeMapCommonShared, - attributeMapCommonBlock, - attributeMapCommonTable, - nullptr, -}; - -static const AttributeMapEntry *attributeMapIllustration[] = { - // XXX: Illustrations may have some attributes from the "shared", "inline", - // the "block" sets. This is a loose specification; making it better - // means duplicating entries from the sets. This seems good enough... - attributeMapCommonShared, - attributeMapCommonBlock, - attributeMapCommonInline, - nullptr, -}; - -// Table mapping owners of attributes to their names. -static const struct OwnerMapEntry { - Attribute::Owner owner; - const char *name; -} ownerMap[] = { - // XXX: Those are sorted in the owner priority resolution order. If the - // same attribute is defined with two owners, the order in the table - // can be used to know which one has more priority. - { Attribute::XML_1_00, "XML-1.00" }, - { Attribute::HTML_3_20, "HTML-3.20" }, - { Attribute::HTML_4_01, "HTML-4.01" }, - { Attribute::OEB_1_00, "OEB-1.00" }, - { Attribute::RTF_1_05, "RTF-1.05" }, - { Attribute::CSS_1_00, "CSS-1.00" }, - { Attribute::CSS_2_00, "CSS-2.00" }, - { Attribute::Layout, "Layout" }, - { Attribute::PrintField, "PrintField" }, - { Attribute::Table, "Table" }, - { Attribute::List, "List" }, - { Attribute::UserProperties, "UserProperties" }, -}; - - -static GBool ownerHasMorePriority(Attribute::Owner a, Attribute::Owner b) -{ - unsigned aIndex, bIndex; - - for (unsigned i = aIndex = bIndex = 0; i < sizeof(ownerMap) / sizeof(ownerMap[0]); i++) { - if (ownerMap[i].owner == a) - aIndex = i; - if (ownerMap[i].owner == b) - bIndex = i; - } - - return aIndex < bIndex; -} - - -// Maps element types to their names and also serves as lookup table -// for additional element type attributes. - -enum ElementType { - elementTypeUndefined, - elementTypeGrouping, - elementTypeInline, - elementTypeBlock, -}; - -static const struct TypeMapEntry { - StructElement::Type type; - const char *name; - ElementType elementType; - const AttributeMapEntry **attributes; -} typeMap[] = { - { StructElement::Document, "Document", elementTypeGrouping, attributeMapShared }, - { StructElement::Part, "Part", elementTypeGrouping, attributeMapShared }, - { StructElement::Art, "Art", elementTypeGrouping, attributeMapColumns }, - { StructElement::Sect, "Sect", elementTypeGrouping, attributeMapColumns }, - { StructElement::Div, "Div", elementTypeGrouping, attributeMapColumns }, - { StructElement::BlockQuote, "BlockQuote", elementTypeGrouping, attributeMapInline }, - { StructElement::Caption, "Caption", elementTypeGrouping, attributeMapInline }, - { StructElement::NonStruct, "NonStruct", elementTypeGrouping, attributeMapInline }, - { StructElement::Index, "Index", elementTypeGrouping, attributeMapInline }, - { StructElement::Private, "Private", elementTypeGrouping, attributeMapInline }, - { StructElement::Span, "Span", elementTypeInline, attributeMapInline }, - { StructElement::Quote, "Quote", elementTypeInline, attributeMapInline }, - { StructElement::Note, "Note", elementTypeInline, attributeMapInline }, - { StructElement::Reference, "Reference", elementTypeInline, attributeMapInline }, - { StructElement::BibEntry, "BibEntry", elementTypeInline, attributeMapInline }, - { StructElement::Code, "Code", elementTypeInline, attributeMapInline }, - { StructElement::Link, "Link", elementTypeInline, attributeMapInline }, - { StructElement::Annot, "Annot", elementTypeInline, attributeMapInline }, - { StructElement::Ruby, "Ruby", elementTypeInline, attributeMapRubyText }, - { StructElement::RB, "RB", elementTypeUndefined, attributeMapRubyText }, - { StructElement::RT, "RT", elementTypeUndefined, attributeMapRubyText }, - { StructElement::RP, "RP", elementTypeUndefined, attributeMapShared }, - { StructElement::Warichu, "Warichu", elementTypeInline, attributeMapRubyText }, - { StructElement::WT, "WT", elementTypeUndefined, attributeMapShared }, - { StructElement::WP, "WP", elementTypeUndefined, attributeMapShared }, - { StructElement::P, "P", elementTypeBlock, attributeMapBlock }, - { StructElement::H, "H", elementTypeBlock, attributeMapBlock }, - { StructElement::H1, "H1", elementTypeBlock, attributeMapBlock }, - { StructElement::H2, "H2", elementTypeBlock, attributeMapBlock }, - { StructElement::H3, "H3", elementTypeBlock, attributeMapBlock }, - { StructElement::H4, "H4", elementTypeBlock, attributeMapBlock }, - { StructElement::H5, "H5", elementTypeBlock, attributeMapBlock }, - { StructElement::H6, "H6", elementTypeBlock, attributeMapBlock }, - { StructElement::L, "L", elementTypeBlock, attributeMapList }, - { StructElement::LI, "LI", elementTypeBlock, attributeMapBlock }, - { StructElement::Lbl, "Lbl", elementTypeBlock, attributeMapBlock }, - { StructElement::LBody, "LBody", elementTypeBlock, attributeMapBlock }, - { StructElement::Table, "Table", elementTypeBlock, attributeMapTable }, - { StructElement::TR, "TR", elementTypeUndefined, attributeMapShared }, - { StructElement::TH, "TH", elementTypeUndefined, attributeMapTableCell }, - { StructElement::TD, "TD", elementTypeUndefined, attributeMapTableCell }, - { StructElement::THead, "THead", elementTypeUndefined, attributeMapShared }, - { StructElement::TFoot, "TFoot", elementTypeUndefined, attributeMapShared }, - { StructElement::TBody, "TBody", elementTypeUndefined, attributeMapShared }, - { StructElement::Figure, "Figure", elementTypeUndefined, attributeMapIllustration }, - { StructElement::Formula, "Formula", elementTypeUndefined, attributeMapIllustration }, - { StructElement::Form, "Form", elementTypeUndefined, attributeMapIllustration }, - { StructElement::TOC, "TOC", elementTypeGrouping, attributeMapShared }, - { StructElement::TOCI, "TOCI", elementTypeGrouping, attributeMapShared }, -}; - - -//------------------------------------------------------------------------ -// Helpers for the attribute and structure type tables -//------------------------------------------------------------------------ - -static inline const AttributeMapEntry * -getAttributeMapEntry(const AttributeMapEntry **entryList, Attribute::Type type) -{ - assert(entryList); - while (*entryList) { - const AttributeMapEntry *entry = *entryList; - while (entry->type != Attribute::Unknown) { - assert(entry->name); - if (type == entry->type) - return entry; - entry++; - } - entryList++; - } - return nullptr; -} - -static inline const AttributeMapEntry * -getAttributeMapEntry(const AttributeMapEntry **entryList, const char *name) -{ - assert(entryList); - while (*entryList) { - const AttributeMapEntry *entry = *entryList; - while (entry->type != Attribute::Unknown) { - assert(entry->name); - if (strcmp(name, entry->name) == 0) - return entry; - entry++; - } - entryList++; - } - return nullptr; -} - -static inline const OwnerMapEntry *getOwnerMapEntry(Attribute::Owner owner) -{ - for (unsigned i = 0; i < sizeof(ownerMap) / sizeof(ownerMap[0]); i++) { - if (owner == ownerMap[i].owner) - return &ownerMap[i]; - } - return nullptr; -} - -static inline const OwnerMapEntry *getOwnerMapEntry(const char *name) -{ - for (unsigned i = 0; i < sizeof(ownerMap) / sizeof(ownerMap[0]); i++) { - if (strcmp(name, ownerMap[i].name) == 0) - return &ownerMap[i]; - } - return nullptr; -} - -static const char *ownerToName(Attribute::Owner owner) -{ - const OwnerMapEntry *entry = getOwnerMapEntry(owner); - return entry ? entry->name : "UnknownOwner"; -} - -static Attribute::Owner nameToOwner(const char *name) -{ - const OwnerMapEntry *entry = getOwnerMapEntry(name); - return entry ? entry->owner : Attribute::UnknownOwner; -} - -static inline const TypeMapEntry *getTypeMapEntry(StructElement::Type type) -{ - for (unsigned i = 0; i < sizeof(typeMap) / sizeof(typeMap[0]); i++) { - if (type == typeMap[i].type) - return &typeMap[i]; - } - return nullptr; -} - -static inline const TypeMapEntry *getTypeMapEntry(const char *name) -{ - for (unsigned i = 0; i < sizeof(typeMap) / sizeof(typeMap[0]); i++) { - if (strcmp(name, typeMap[i].name) == 0) - return &typeMap[i]; - } - return nullptr; -} - -static const char *typeToName(StructElement::Type type) -{ - if (type == StructElement::MCID) - return "MarkedContent"; - if (type == StructElement::OBJR) - return "ObjectReference"; - - const TypeMapEntry *entry = getTypeMapEntry(type); - return entry ? entry->name : "Unknown"; -} - -static StructElement::Type nameToType(const char *name) -{ - const TypeMapEntry *entry = getTypeMapEntry(name); - return entry ? entry->type : StructElement::Unknown; -} - - -//------------------------------------------------------------------------ -// Attribute -//------------------------------------------------------------------------ - -Attribute::Attribute(const char *nameA, int nameLenA, Object *valueA): - type(UserProperty), - owner(UserProperties), - revision(0), - name(nameA, nameLenA), - value(), - hidden(gFalse), - formatted(nullptr) -{ - assert(valueA); - value = valueA->copy(); -} - -Attribute::Attribute(Type typeA, Object *valueA): - type(typeA), - owner(UserProperties), // TODO: Determine corresponding owner from Type - revision(0), - name(), - value(), - hidden(gFalse), - formatted(nullptr) -{ - assert(valueA); - - value = valueA->copy(); - - if (!checkType()) - type = Unknown; -} - -Attribute::~Attribute() -{ - delete formatted; -} - -const char *Attribute::getTypeName() const -{ - if (type == UserProperty) - return name.getCString(); - - const AttributeMapEntry *entry = getAttributeMapEntry(attributeMapAll, type); - if (entry) - return entry->name; - - return "Unknown"; -} - -const char *Attribute::getOwnerName() const -{ - return ownerToName(owner); -} - -Object *Attribute::getDefaultValue(Attribute::Type type) -{ - const AttributeMapEntry *entry = getAttributeMapEntry(attributeMapAll, type); - return entry ? const_cast<Object*>(entry->defval) : nullptr; -} - -void Attribute::setFormattedValue(const char *formattedA) -{ - if (formattedA) { - if (formatted) - formatted->Set(formattedA); - else - formatted = new GooString(formattedA); - } else { - delete formatted; - formatted = nullptr; - } -} - -GBool Attribute::checkType(StructElement *element) -{ - // If an element is passed, tighther type-checking can be done. - if (!element) - return gTrue; - - const TypeMapEntry *elementTypeEntry = getTypeMapEntry(element->getType()); - if (elementTypeEntry && elementTypeEntry->attributes) { - const AttributeMapEntry *entry = getAttributeMapEntry(elementTypeEntry->attributes, type); - if (entry) { - if (entry->check && !((*entry->check)(&value))) { - return gFalse; - } - } else { - // No entry: the attribute is not valid for the containing element. - return gFalse; - } - } - - return gTrue; -} - -Attribute::Type Attribute::getTypeForName(const char *name, StructElement *element) -{ - const AttributeMapEntry **attributes = attributeMapAll; - if (element) { - const TypeMapEntry *elementTypeEntry = getTypeMapEntry(element->getType()); - if (elementTypeEntry && elementTypeEntry->attributes) { - attributes = elementTypeEntry->attributes; - } - } - - const AttributeMapEntry *entry = getAttributeMapEntry(attributes, name); - return entry ? entry->type : Unknown; -} - -Attribute *Attribute::parseUserProperty(Dict *property) -{ - Object obj, value; - const char *name = nullptr; - int nameLen = GooString::CALC_STRING_LEN; - - obj = property->lookup("N"); - if (obj.isString()) { - const GooString *s = obj.getString(); - name = s->getCString(); - nameLen = s->getLength(); - } else if (obj.isName()) - name = obj.getName(); - else { - error(errSyntaxError, -1, "N object is wrong type ({0:s})", obj.getTypeName()); - return nullptr; - } - - value = property->lookup("V"); - if (value.isNull()) { - error(errSyntaxError, -1, "V object is wrong type ({0:s})", value.getTypeName()); - return nullptr; - } - - Attribute *attribute = new Attribute(name, nameLen, &value); - obj = property->lookup("F"); - if (obj.isString()) { - attribute->setFormattedValue(obj.getString()->getCString()); - } else if (!obj.isNull()) { - error(errSyntaxWarning, -1, "F object is wrong type ({0:s})", obj.getTypeName()); - } - - obj = property->lookup("H"); - if (obj.isBool()) { - attribute->setHidden(obj.getBool()); - } else if (!obj.isNull()) { - error(errSyntaxWarning, -1, "H object is wrong type ({0:s})", obj.getTypeName()); - } - - return attribute; -} - - -//------------------------------------------------------------------------ -// StructElement -//------------------------------------------------------------------------ - -StructElement::StructData::StructData(): - altText(nullptr), - actualText(nullptr), - id(nullptr), - title(nullptr), - expandedAbbr(nullptr), - language(nullptr), - revision(0) -{ -} - -StructElement::StructData::~StructData() -{ - delete altText; - delete actualText; - delete id; - delete title; - delete language; - for (ElemPtrArray::iterator i = elements.begin(); i != elements.end(); ++i) delete *i; - for (AttrPtrArray::iterator i = attributes.begin(); i != attributes.end(); ++i) delete *i; -} - - -StructElement::StructElement(Dict *element, - StructTreeRoot *treeRootA, - StructElement *parentA, - std::set<int> &seen): - type(Unknown), - treeRoot(treeRootA), - parent(parentA), - s(new StructData()) -{ - assert(treeRoot); - assert(element); - - parse(element); - parseChildren(element, seen); -} - -StructElement::StructElement(int mcid, StructTreeRoot *treeRootA, StructElement *parentA): - type(MCID), - treeRoot(treeRootA), - parent(parentA), - c(new ContentData(mcid)) -{ - assert(treeRoot); - assert(parent); -} - -StructElement::StructElement(const Ref& ref, StructTreeRoot *treeRootA, StructElement *parentA): - type(OBJR), - treeRoot(treeRootA), - parent(parentA), - c(new ContentData(ref)) -{ - assert(treeRoot); - assert(parent); -} - -StructElement::~StructElement() -{ - if (isContent()) - delete c; - else - delete s; -} - -GBool StructElement::isBlock() const -{ - const TypeMapEntry *entry = getTypeMapEntry(type); - return entry ? (entry->elementType == elementTypeBlock) : gFalse; -} - -GBool StructElement::isInline() const -{ - const TypeMapEntry *entry = getTypeMapEntry(type); - return entry ? (entry->elementType == elementTypeInline) : gFalse; -} - -GBool StructElement::isGrouping() const -{ - const TypeMapEntry *entry = getTypeMapEntry(type); - return entry ? (entry->elementType == elementTypeGrouping) : gFalse; -} - -GBool StructElement::hasPageRef() const -{ - return pageRef.isRef() || (parent && parent->hasPageRef()); -} - -bool StructElement::getPageRef(Ref& ref) const -{ - if (pageRef.isRef()) { - ref = pageRef.getRef(); - return gTrue; - } - - if (parent) - return parent->getPageRef(ref); - - return gFalse; -} - -const char *StructElement::getTypeName() const -{ - return typeToName(type); -} - -const Attribute *StructElement::findAttribute(Attribute::Type attributeType, GBool inherit, - Attribute::Owner attributeOwner) const -{ - if (isContent()) - return parent->findAttribute(attributeType, inherit, attributeOwner); - - if (attributeType == Attribute::Unknown || attributeType == Attribute::UserProperty) - return nullptr; - - const Attribute *result = nullptr; - - if (attributeOwner == Attribute::UnknownOwner) { - // Search for the attribute, no matter who the owner is - for (unsigned i = 0; i < getNumAttributes(); i++) { - const Attribute *attr = getAttribute(i); - if (attributeType == attr->getType()) { - if (!result || ownerHasMorePriority(attr->getOwner(), result->getOwner())) - result = attr; - } - } - } else { - // Search for the attribute, with a specific owner - for (unsigned i = 0; i < getNumAttributes(); i++) { - const Attribute *attr = getAttribute(i); - if (attributeType == attr->getType() && attributeOwner == attr->getOwner()) { - result = attr; - break; - } - } - } - - if (result) - return result; - - if (inherit && parent) { - const AttributeMapEntry *entry = getAttributeMapEntry(attributeMapAll, attributeType); - assert(entry); - // TODO: Take into account special inheritance cases, for example: - // inline elements which have been changed to be block using - // "/Placement/Block" have slightly different rules. - if (entry->inherit) - return parent->findAttribute(attributeType, inherit, attributeOwner); - } - - return nullptr; -} - -GooString* StructElement::appendSubTreeText(GooString *string, GBool recursive) const -{ - if (isContent() && !isObjectRef()) { - MarkedContentOutputDev mcdev(getMCID()); - const TextSpanArray& spans(getTextSpansInternal(mcdev)); - - if (!string) - string = new GooString(); - - for (TextSpanArray::const_iterator i = spans.begin(); i != spans.end(); ++i) - string->append(i->getText()); - - return string; - } - - if (!recursive) - return nullptr; - - // Do a depth-first traversal, to get elements in logical order - if (!string) - string = new GooString(); - - for (unsigned i = 0; i < getNumChildren(); i++) - getChild(i)->appendSubTreeText(string, recursive); - - return string; -} - -const TextSpanArray& StructElement::getTextSpansInternal(MarkedContentOutputDev& mcdev) const -{ - assert(isContent()); - - int startPage = 0, endPage = 0; - - Ref ref; - if (getPageRef(ref)) { - startPage = endPage = treeRoot->getDoc()->findPage(ref.num, ref.gen); - } - - if (!(startPage && endPage)) { - startPage = 1; - endPage = treeRoot->getDoc()->getNumPages(); - } - - treeRoot->getDoc()->displayPages(&mcdev, startPage, endPage, 72.0, 72.0, 0, gTrue, gFalse, gFalse); - return mcdev.getTextSpans(); -} - -static StructElement::Type roleMapResolve(Dict *roleMap, const char *name, const char *curName) -{ - // Circular reference - if (curName && !strcmp(name, curName)) - return StructElement::Unknown; - - Object resolved = roleMap->lookup(curName ? curName : name); - if (resolved.isName()) { - StructElement::Type type = nameToType(resolved.getName()); - return type == StructElement::Unknown - ? roleMapResolve(roleMap, name, resolved.getName()) - : type; - } - - if (!resolved.isNull()) - error(errSyntaxWarning, -1, "RoleMap entry is wrong type ({0:s})", resolved.getTypeName()); - return StructElement::Unknown; -} - -void StructElement::parse(Dict *element) -{ - Object obj; - - // Type is optional, but if present must be StructElem - obj = element->lookup("Type"); - if (!obj.isNull() && !obj.isName("StructElem")) { - error(errSyntaxError, -1, "Type of StructElem object is wrong"); - return; - } - - // Parent object reference (required). - s->parentRef = element->lookupNF("P"); - if (!s->parentRef.isRef()) { - error(errSyntaxError, -1, "P object is wrong type ({0:s})", obj.getTypeName()); - return; - } - - // Check whether the S-type is valid for the top level - // element and create a node of the appropriate type. - obj = element->lookup("S"); - if (!obj.isName()) { - error(errSyntaxError, -1, "S object is wrong type ({0:s})", obj.getTypeName()); - return; - } - - // Type name may not be standard, resolve through RoleMap first. - if (treeRoot->getRoleMap()) { - type = roleMapResolve(treeRoot->getRoleMap(), obj.getName(), nullptr); - } - - // Resolving through RoleMap may leave type as Unknown, e.g. for types - // which are not present in it, yet they are standard element types. - if (type == Unknown) - type = nameToType(obj.getName()); - - // At this point either the type name must have been resolved. - if (type == Unknown) { - error(errSyntaxError, -1, "StructElem object is wrong type ({0:s})", obj.getName()); - return; - } - - // Object ID (optional), to be looked at the IDTree in the tree root. - obj = element->lookup("ID"); - if (obj.isString()) { - s->id = obj.takeString(); - } - - // Page reference (optional) in which at least one of the child items - // is to be rendered in. Note: each element stores only the /Pg value - // contained by it, and StructElement::getPageRef() may look in parent - // elements to find the page where an element belongs. - pageRef = element->lookupNF("Pg"); - - // Revision number (optional). - obj = element->lookup("R"); - if (obj.isInt()) { - s->revision = obj.getInt(); - } - - // Element title (optional). - obj = element->lookup("T"); - if (obj.isString()) { - s->title = obj.takeString(); - } - - // Language (optional). - obj = element->lookup("Lang"); - if (obj.isString()) { - s->language = obj.takeString(); - } - - // Alternative text (optional). - obj = element->lookup("Alt"); - if (obj.isString()) { - s->altText = obj.takeString(); - } - - // Expanded form of an abbreviation (optional). - obj = element->lookup("E"); - if (obj.isString()) { - s->expandedAbbr = obj.takeString(); - } - - // Actual text (optional). - obj = element->lookup("ActualText"); - if (obj.isString()) { - s->actualText = obj.takeString(); - } - - // Attributes directly attached to the element (optional). - obj = element->lookup("A"); - if (obj.isDict()) { - parseAttributes(obj.getDict()); - } else if (obj.isArray()) { - unsigned attrIndex = getNumAttributes(); - for (int i = 0; i < obj.arrayGetLength(); i++) { - Object iobj = obj.arrayGet(i); - if (iobj.isDict()) { - attrIndex = getNumAttributes(); - parseAttributes(iobj.getDict()); - } else if (iobj.isInt()) { - const int revision = iobj.getInt(); - // Set revision numbers for the elements previously created. - for (unsigned j = attrIndex; j < getNumAttributes(); j++) - getAttribute(j)->setRevision(revision); - } else { - error(errSyntaxWarning, -1, "A item is wrong type ({0:s})", iobj.getTypeName()); - } - } - } else if (!obj.isNull()) { - error(errSyntaxWarning, -1, "A is wrong type ({0:s})", obj.getTypeName()); - } - - // Attributes referenced indirectly through the ClassMap (optional). - if (treeRoot->getClassMap()) { - Object classes = element->lookup("C"); - if (classes.isName()) { - Object attr = treeRoot->getClassMap()->lookup(classes.getName()); - if (attr.isDict()) { - parseAttributes(attr.getDict(), gTrue); - } else if (attr.isArray()) { - for (int i = 0; i < attr.arrayGetLength(); i++) { - unsigned attrIndex = getNumAttributes(); - Object iobj = attr.arrayGet(i); - if (iobj.isDict()) { - attrIndex = getNumAttributes(); - parseAttributes(iobj.getDict(), gTrue); - } else if (iobj.isInt()) { - // Set revision numbers for the elements previously created. - const int revision = iobj.getInt(); - for (unsigned j = attrIndex; j < getNumAttributes(); j++) - getAttribute(j)->setRevision(revision); - } else { - error(errSyntaxWarning, -1, "C item is wrong type ({0:s})", iobj.getTypeName()); - } - } - } else if (!attr.isNull()) { - error(errSyntaxWarning, -1, "C object is wrong type ({0:s})", classes.getTypeName()); - } - } - } -} - -StructElement *StructElement::parseChild(Object *ref, - Object *childObj, - std::set<int> &seen) -{ - assert(childObj); - assert(ref); - - StructElement *child = nullptr; - - if (childObj->isInt()) { - child = new StructElement(childObj->getInt(), treeRoot, this); - } else if (childObj->isDict("MCR")) { - /* - * TODO: The optional Stm/StwOwn attributes are not handled, so all the - * page will be always scanned when calling StructElement::getText(). - */ - Object mcidObj = childObj->dictLookup("MCID"); - if (!mcidObj.isInt()) { - error(errSyntaxError, -1, "MCID object is wrong type ({0:s})", mcidObj.getTypeName()); - return nullptr; - } - - child = new StructElement(mcidObj.getInt(), treeRoot, this); - - Object pageRefObj = childObj->dictLookupNF("Pg"); - if (pageRefObj.isRef()) { - child->pageRef = std::move(pageRefObj); - } - } else if (childObj->isDict("OBJR")) { - Object refObj = childObj->dictLookupNF("Obj"); - if (refObj.isRef()) { - - child = new StructElement(refObj.getRef(), treeRoot, this); - - Object pageRefObj = childObj->dictLookupNF("Pg"); - if (pageRefObj.isRef()) { - child->pageRef = std::move(pageRefObj); - } - } else { - error(errSyntaxError, -1, "Obj object is wrong type ({0:s})", refObj.getTypeName()); - } - } else if (childObj->isDict()) { - if (!ref->isRef()) { - error(errSyntaxError, -1, - "Structure element dictionary is not an indirect reference ({0:s})", - ref->getTypeName()); - } else if (seen.find(ref->getRefNum()) == seen.end()) { - seen.insert(ref->getRefNum()); - child = new StructElement(childObj->getDict(), treeRoot, this, seen); - } else { - error(errSyntaxWarning, -1, - "Loop detected in structure tree, skipping subtree at object {0:d}:{1:d}", - ref->getRefNum(), ref->getRefGen()); - } - } else { - error(errSyntaxWarning, -1, "K has a child of wrong type ({0:s})", childObj->getTypeName()); - } - - if (child) { - if (child->isOk()) { - appendChild(child); - if (ref->isRef()) - treeRoot->parentTreeAdd(ref->getRef(), child); - } else { - delete child; - child = nullptr; - } - } - - return child; -} - -void StructElement::parseChildren(Dict *element, std::set<int> &seen) -{ - Object kids = element->lookup("K"); - if (kids.isArray()) { - for (int i = 0; i < kids.arrayGetLength(); i++) { - Object obj = kids.arrayGet(i); - Object ref = kids.arrayGetNF(i); - parseChild(&ref, &obj, seen); - } - } else if (kids.isDict() || kids.isInt()) { - Object ref = element->lookupNF("K"); - parseChild(&ref, &kids, seen); - } -} - -void StructElement::parseAttributes(Dict *attributes, GBool keepExisting) -{ - Object owner = attributes->lookup("O"); - if (owner.isName("UserProperties")) { - // In this case /P is an array of UserProperty dictionaries - Object userProperties = attributes->lookup("P"); - if (userProperties.isArray()) { - for (int i = 0; i < userProperties.arrayGetLength(); i++) { - Object property = userProperties.arrayGet(i); - if (property.isDict()) { - Attribute *attribute = Attribute::parseUserProperty(property.getDict()); - if (attribute && attribute->isOk()) { - appendAttribute(attribute); - } else { - error(errSyntaxWarning, -1, "Item in P is invalid"); - delete attribute; - } - } else { - error(errSyntaxWarning, -1, "Item in P is wrong type ({0:s})", property.getTypeName()); - } - } - } - } else if (owner.isName()) { - // In this case /P contains standard attributes. - // Check first if the owner is a valid standard one. - Attribute::Owner ownerValue = nameToOwner(owner.getName()); - if (ownerValue != Attribute::UnknownOwner) { - // Iterate over the entries of the "attributes" dictionary. - // The /O entry (owner) is skipped. - for (int i = 0; i < attributes->getLength(); i++) { - const char *key = attributes->getKey(i); - if (strcmp(key, "O") != 0) { - Attribute::Type type = Attribute::getTypeForName(key, this); - - // Check if the attribute is already defined. - if (keepExisting) { - GBool exists = gFalse; - for (unsigned j = 0; j < getNumAttributes(); j++) { - if (getAttribute(j)->getType() == type) { - exists = gTrue; - break; - } - } - if (exists) - continue; - } - - if (type != Attribute::Unknown) { - Object value = attributes->getVal(i); - GBool typeCheckOk = gTrue; - Attribute *attribute = new Attribute(type, &value); - - if (attribute->isOk() && (typeCheckOk = attribute->checkType(this))) { - appendAttribute(attribute); - } else { - // It is not needed to free "value", the Attribute instance - // owns the contents, so deleting "attribute" is enough. - if (!typeCheckOk) { - error(errSyntaxWarning, -1, "Attribute {0:s} value is of wrong type ({1:s})", - attribute->getTypeName(), attribute->getValue()->getTypeName()); - } - delete attribute; - } - } else { - error(errSyntaxWarning, -1, "Wrong Attribute '{0:s}' in element {1:s}", key, getTypeName()); - } - } - } - } else { - error(errSyntaxWarning, -1, "O object is invalid value ({0:s})", owner.getName()); - } - } else if (!owner.isNull()) { - error(errSyntaxWarning, -1, "O is wrong type ({0:s})", owner.getTypeName()); - } -} |