/src/poppler/poppler/StructElement.h
Line | Count | Source |
1 | | //======================================================================== |
2 | | // |
3 | | // StructElement.h |
4 | | // |
5 | | // This file is licensed under the GPLv2 or later |
6 | | // |
7 | | // Copyright 2013, 2014 Igalia S.L. |
8 | | // Copyright 2014 Luigi Scarso <luigi.scarso@gmail.com> |
9 | | // Copyright 2014, 2018, 2019, 2021, 2023-2026 Albert Astals Cid <aacid@kde.org> |
10 | | // Copyright 2018 Adam Reichold <adam.reichold@t-online.de> |
11 | | // Copyright 2021, 2023 Adrian Johnson <ajohnson@redneon.com> |
12 | | // Copyright 2025, 2026 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk> |
13 | | // |
14 | | //======================================================================== |
15 | | |
16 | | #ifndef STRUCTELEMENT_H |
17 | | #define STRUCTELEMENT_H |
18 | | |
19 | | #include "goo/GooString.h" |
20 | | #include "MarkedContentOutputDev.h" |
21 | | #include "Object.h" |
22 | | #include "poppler_private_export.h" |
23 | | #include <vector> |
24 | | |
25 | | class Dict; |
26 | | class StructElement; |
27 | | class StructTreeRoot; |
28 | | |
29 | | class POPPLER_PRIVATE_EXPORT Attribute |
30 | | { |
31 | | public: |
32 | | enum Type |
33 | | { |
34 | | Unknown = 0, // Uninitialized, parsing error, etc. |
35 | | UserProperty, // User defined attribute (i.e. non-standard) |
36 | | |
37 | | // Common standard attributes |
38 | | Placement, |
39 | | WritingMode, |
40 | | BackgroundColor, |
41 | | BorderColor, |
42 | | BorderStyle, |
43 | | BorderThickness, |
44 | | Color, |
45 | | Padding, |
46 | | |
47 | | // Block element standard attributes |
48 | | SpaceBefore, |
49 | | SpaceAfter, |
50 | | StartIndent, |
51 | | EndIndent, |
52 | | TextIndent, |
53 | | TextAlign, |
54 | | BBox, |
55 | | Width, |
56 | | Height, |
57 | | BlockAlign, |
58 | | InlineAlign, |
59 | | TBorderStyle, |
60 | | TPadding, |
61 | | |
62 | | // Inline element standard attributes |
63 | | BaselineShift, |
64 | | LineHeight, |
65 | | TextDecorationColor, |
66 | | TextDecorationThickness, |
67 | | TextDecorationType, |
68 | | RubyAlign, |
69 | | RubyPosition, |
70 | | GlyphOrientationVertical, |
71 | | |
72 | | // Column-only standard attributes |
73 | | ColumnCount, |
74 | | ColumnGap, |
75 | | ColumnWidths, |
76 | | |
77 | | // List-only standard attributes |
78 | | ListNumbering, |
79 | | |
80 | | // PrintField-only standard attributes |
81 | | Role, |
82 | | checked, |
83 | | Desc, |
84 | | |
85 | | // Table-only standard attributes |
86 | | RowSpan, |
87 | | ColSpan, |
88 | | Headers, |
89 | | Scope, |
90 | | Summary, |
91 | | }; |
92 | | |
93 | | enum Owner |
94 | | { |
95 | | UnknownOwner = 0, |
96 | | // User-defined attributes |
97 | | UserProperties, |
98 | | // Standard attributes |
99 | | Layout, |
100 | | List, |
101 | | PrintField, |
102 | | Table, |
103 | | // Translation to other formats |
104 | | XML_1_00, |
105 | | HTML_3_20, |
106 | | HTML_4_01, |
107 | | OEB_1_00, |
108 | | RTF_1_05, |
109 | | CSS_1_00, |
110 | | CSS_2_00, |
111 | | }; |
112 | | |
113 | | // Creates a standard attribute. The name is predefined, and the |
114 | | // value is type-checked to conform to the PDF specification. |
115 | | Attribute(Type type, Object *value); |
116 | | |
117 | | // Creates an UserProperty attribute, with an arbitrary name and value. |
118 | | Attribute(GooString &&name, Object *value); |
119 | | |
120 | 0 | bool isOk() const { return type != Unknown; } |
121 | | |
122 | | // Name, type and value can be set only on construction. |
123 | 0 | Type getType() const { return type; } |
124 | 0 | Owner getOwner() const { return owner; } |
125 | | const char *getTypeName() const; |
126 | | const char *getOwnerName() const; |
127 | 0 | const Object *getValue() const { return &value; } |
128 | | static Object *getDefaultValue(Type type); |
129 | | |
130 | 0 | const char *getName() const { return (type == UserProperty ? name.c_str() : getTypeName()); } |
131 | | |
132 | | // The revision is optional, and defaults to zero. |
133 | 0 | unsigned int getRevision() const { return revision; } |
134 | 0 | void setRevision(unsigned int revisionA) { revision = revisionA; } |
135 | | |
136 | | // Hidden elements should not be displayed by the user agent |
137 | 0 | bool isHidden() const { return hidden; } |
138 | 0 | void setHidden(bool hiddenA) { hidden = hiddenA; } |
139 | | |
140 | | // The formatted value may be in the PDF, or be left undefined (nullptr). |
141 | | // In the later case the user agent should provide a default representation. |
142 | 0 | const char *getFormattedValue() const { return formatted ? formatted->c_str() : nullptr; } |
143 | | void setFormattedValue(const std::optional<std::string> &formattedA); |
144 | | |
145 | | ~Attribute(); |
146 | | |
147 | | private: |
148 | | Type type; |
149 | | Owner owner; |
150 | | unsigned int revision; |
151 | | GooString name; |
152 | | Object value; |
153 | | bool hidden; |
154 | | std::optional<std::string> formatted; |
155 | | |
156 | | bool checkType(StructElement *element = nullptr); |
157 | | static Type getTypeForName(const std::string &name, StructElement *element = nullptr); |
158 | | static Attribute *parseUserProperty(Dict *property); |
159 | | |
160 | | friend class StructElement; |
161 | | }; |
162 | | |
163 | | class POPPLER_PRIVATE_EXPORT StructElement |
164 | | { |
165 | | public: |
166 | | enum Type |
167 | | { |
168 | | Unknown = 0, |
169 | | MCID, // MCID reference, used internally |
170 | | OBJR, // Object reference, used internally |
171 | | |
172 | | Document, |
173 | | Part, |
174 | | Art, |
175 | | Sect, |
176 | | Div, // Structural elements |
177 | | |
178 | | Span, |
179 | | Quote, |
180 | | Note, |
181 | | Reference, |
182 | | BibEntry, // Inline elements |
183 | | Code, |
184 | | Link, |
185 | | Annot, |
186 | | BlockQuote, |
187 | | Caption, |
188 | | NonStruct, |
189 | | TOC, |
190 | | TOCI, |
191 | | Index, |
192 | | Private, |
193 | | |
194 | | P, |
195 | | H, |
196 | | H1, |
197 | | H2, |
198 | | H3, |
199 | | H4, |
200 | | H5, |
201 | | H6, // Paragraph-like |
202 | | |
203 | | L, |
204 | | LI, |
205 | | Lbl, |
206 | | LBody, // List elements |
207 | | |
208 | | Table, |
209 | | TR, |
210 | | TH, |
211 | | TD, |
212 | | THead, |
213 | | TFoot, |
214 | | TBody, // Table elements |
215 | | |
216 | | Ruby, |
217 | | RB, |
218 | | RT, |
219 | | RP, // Ruby text elements |
220 | | Warichu, |
221 | | WT, |
222 | | WP, |
223 | | |
224 | | Figure, |
225 | | Formula, |
226 | | Form, // Illustration-like elements |
227 | | }; |
228 | | |
229 | | static const Ref InvalidRef; |
230 | | |
231 | | const char *getTypeName() const; |
232 | 0 | Type getType() const { return type; } |
233 | 0 | bool isOk() const { return type != Unknown; } |
234 | | bool isBlock() const; |
235 | | bool isInline() const; |
236 | | bool isGrouping() const; |
237 | | |
238 | 0 | bool isContent() const { return (type == MCID) || isObjectRef(); } |
239 | 0 | bool isObjectRef() const { return (type == OBJR && c->ref != Ref::INVALID()); } |
240 | | |
241 | 0 | int getMCID() const { return c->mcid; } |
242 | 0 | Ref getObjectRef() const { return c->ref; } |
243 | 0 | Ref getParentRef() const { return isContent() ? parent->getParentRef() : s->parentRef; } |
244 | 0 | StructElement *getParent() const { return parent; } // returns NULL if parent is StructTreeRoot |
245 | | bool hasPageRef() const; |
246 | | bool getPageRef(Ref &ref) const; |
247 | 0 | bool hasStmRef() const { return stmRef.isRef(); } |
248 | | bool getStmRef(Ref &ref) const; |
249 | 0 | StructTreeRoot *getStructTreeRoot() { return treeRoot; } |
250 | | |
251 | | // Optional element identifier. |
252 | 0 | const GooString *getID() const { return isContent() ? nullptr : s->id.get(); } |
253 | | |
254 | | // Optional ISO language name, e.g. en_US |
255 | | GooString *getLanguage() |
256 | 0 | { |
257 | 0 | if (!isContent() && s->language) { |
258 | 0 | return s->language.get(); |
259 | 0 | } |
260 | 0 | return parent ? parent->getLanguage() : nullptr; |
261 | 0 | } |
262 | | const GooString *getLanguage() const |
263 | 0 | { |
264 | 0 | if (!isContent() && s->language) { |
265 | 0 | return s->language.get(); |
266 | 0 | } |
267 | 0 | return parent ? parent->getLanguage() : nullptr; |
268 | 0 | } |
269 | | |
270 | | // Optional revision number, defaults to zero. |
271 | 0 | unsigned int getRevision() const { return isContent() ? 0 : s->revision; } |
272 | | void setRevision(unsigned int revision) const |
273 | 0 | { |
274 | 0 | if (isContent()) { |
275 | 0 | s->revision = revision; |
276 | 0 | } |
277 | 0 | } |
278 | | |
279 | | // Optional element title, in human-readable form. |
280 | 0 | const GooString *getTitle() const { return isContent() ? nullptr : s->title.get(); } |
281 | | |
282 | | // Optional element expanded abbreviation text. |
283 | 0 | const GooString *getExpandedAbbr() const { return isContent() ? nullptr : s->expandedAbbr.get(); } |
284 | | |
285 | 0 | unsigned getNumChildren() const { return isContent() ? 0 : s->elements.size(); } |
286 | 0 | const StructElement *getChild(int i) const { return isContent() ? nullptr : s->elements.at(i); } |
287 | | |
288 | | void appendChild(StructElement *element) const |
289 | 0 | { |
290 | 0 | if (!isContent() && element && element->isOk()) { |
291 | 0 | s->elements.push_back(element); |
292 | 0 | } |
293 | 0 | } |
294 | | |
295 | 0 | unsigned getNumAttributes() const { return isContent() ? 0 : s->attributes.size(); } |
296 | 0 | const Attribute *getAttribute(int i) const { return isContent() ? nullptr : s->attributes.at(i); } |
297 | 0 | Attribute *getNonConstAttribute(int i) const { return isContent() ? nullptr : s->attributes.at(i); } |
298 | | |
299 | | void appendAttribute(Attribute *attribute) const |
300 | 0 | { |
301 | 0 | if (!isContent() && attribute) { |
302 | 0 | s->attributes.push_back(attribute); |
303 | 0 | } |
304 | 0 | } |
305 | | |
306 | | const Attribute *findAttribute(Attribute::Type attributeType, bool inherit = false, Attribute::Owner owner = Attribute::UnknownOwner) const; |
307 | | |
308 | 0 | const GooString *getAltText() const { return isContent() ? nullptr : s->altText.get(); } |
309 | | |
310 | 0 | const GooString *getActualText() const { return isContent() ? nullptr : s->actualText.get(); } |
311 | | |
312 | | // Content text referenced by the element: |
313 | | // |
314 | | // - For MCID reference elements, this is just the text of the |
315 | | // corresponding marked content object in the page stream, regardless |
316 | | // of the setting of the "recursive" flag. |
317 | | // - For other elements, if the "recursive" flag is set, the text |
318 | | // enclosed by *all* the child MCID reference elements of the subtree |
319 | | // is returned. The text is assembled by traversing the leaf MCID |
320 | | // reference elements in logical order. |
321 | | // - In any other case, the function returns nullptr. |
322 | | // |
323 | | // A new string is returned, and the ownership passed to the caller. |
324 | | // |
325 | 0 | GooString *getText(bool recursive = true) const { return appendSubTreeText(nullptr, recursive); } |
326 | | |
327 | | TextSpanArray getTextSpans() const |
328 | 0 | { |
329 | 0 | if (!isContent()) { |
330 | 0 | return TextSpanArray(); |
331 | 0 | } |
332 | 0 | MarkedContentOutputDev mcdev(getMCID(), stmRef); |
333 | 0 | return getTextSpansInternal(mcdev); |
334 | 0 | } |
335 | | |
336 | | ~StructElement(); |
337 | | |
338 | | private: |
339 | | GooString *appendSubTreeText(GooString *string, bool recursive) const; |
340 | | const TextSpanArray &getTextSpansInternal(MarkedContentOutputDev &mcdev) const; |
341 | | |
342 | | using AttrPtrArray = std::vector<Attribute *>; |
343 | | using ElemPtrArray = std::vector<StructElement *>; |
344 | | |
345 | | struct StructData |
346 | | { |
347 | | Ref parentRef; |
348 | | std::unique_ptr<GooString> altText; |
349 | | std::unique_ptr<GooString> actualText; |
350 | | std::unique_ptr<GooString> id; |
351 | | std::unique_ptr<GooString> title; |
352 | | std::unique_ptr<GooString> expandedAbbr; |
353 | | std::unique_ptr<GooString> language; |
354 | | unsigned int revision = 0; |
355 | | ElemPtrArray elements; |
356 | | AttrPtrArray attributes; |
357 | | |
358 | | StructData(); |
359 | | ~StructData(); |
360 | | |
361 | | StructData(const StructData &) = delete; |
362 | | StructData &operator=(const StructData &) = delete; |
363 | | }; |
364 | | |
365 | | // Data in content elements (MCID, MCR) |
366 | | struct ContentData |
367 | | { |
368 | | union { |
369 | | int mcid; |
370 | | Ref ref; |
371 | | }; |
372 | | |
373 | 0 | explicit ContentData(int mcidA) : mcid(mcidA) { } |
374 | 0 | explicit ContentData(const Ref r) { ref = r; } |
375 | | }; |
376 | | |
377 | | // Common data |
378 | | Type type; |
379 | | StructTreeRoot *treeRoot; |
380 | | StructElement *parent; |
381 | | mutable Object pageRef; |
382 | | Object stmRef; |
383 | | |
384 | | union { |
385 | | StructData *s; |
386 | | ContentData *c; |
387 | | }; |
388 | | |
389 | | StructElement(Dict *elementDict, StructTreeRoot *treeRootA, StructElement *parentA, RefRecursionChecker &seen); |
390 | | StructElement(int mcid, StructTreeRoot *treeRootA, StructElement *parentA); |
391 | | StructElement(Ref ref, StructTreeRoot *treeRootA, StructElement *parentA); |
392 | | |
393 | | void parse(Dict *elementDict); |
394 | | StructElement *parseChild(const Object *ref, Object *childObj, RefRecursionChecker &seen); |
395 | | void parseChildren(Dict *element, RefRecursionChecker &seen); |
396 | | void parseAttributes(Dict *attributes, bool keepExisting = false); |
397 | | |
398 | | friend class StructTreeRoot; |
399 | | }; |
400 | | |
401 | | #endif |