summaryrefslogtreecommitdiff
path: root/Build/source/libs/poppler/poppler-src/poppler/StructElement.cc
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/libs/poppler/poppler-src/poppler/StructElement.cc')
-rw-r--r--Build/source/libs/poppler/poppler-src/poppler/StructElement.cc1347
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());
- }
-}