Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkPDFTag.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2018 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
9#include "src/pdf/SkPDFTag.h"
10
11using namespace skia_private;
12
13// The struct parent tree consists of one entry per page, followed by
14// entries for individual struct tree nodes corresponding to
15// annotations. Each entry is a key/value pair with an integer key
16// and an indirect reference key.
17//
18// The page entries get consecutive keys starting at 0. Since we don't
19// know the total number of pages in the document at the time we start
20// processing annotations, start the key for annotations with a large
21// number, which effectively becomes the maximum number of pages in a
22// PDF we can handle.
24
25namespace {
26struct Location {
28 unsigned fPageIndex{0};
29
30 void accumulate(Location const& child) {
31 if (!child.fPoint.isFinite()) {
32 return;
33 }
34 if (!fPoint.isFinite()) {
35 *this = child;
36 return;
37 }
38 if (child.fPageIndex < fPageIndex) {
39 *this = child;
40 return;
41 }
42 if (child.fPageIndex == fPageIndex) {
43 fPoint.fX = std::min(child.fPoint.fX, fPoint.fX);
44 fPoint.fY = std::max(child.fPoint.fY, fPoint.fY); // PDF y-up
45 return;
46 }
47 }
48};
49} // namespace
50
52 // Structure element nodes need a unique alphanumeric ID,
53 // and we need to be able to output them sorted in lexicographic
54 // order. This helper function takes one of our node IDs and
55 // builds an ID string that zero-pads the digits so that lexicographic
56 // order matches numeric order.
57 static SkString nodeIdToString(int nodeId) {
58 SkString idString;
59 idString.printf("node%08d", nodeId);
60 return idString;
61 }
62
64 size_t fChildCount = 0;
66 Location fLocation;
68 };
82 std::unique_ptr<SkPDFArray> fAttributes;
87 std::vector<AnnotationInfo> fAnnotations;
88};
89
91
93
95 const char* owner, const char* name, int value) {
96 if (!fAttrs)
97 fAttrs = SkPDFMakeArray();
98 std::unique_ptr<SkPDFDict> attrDict = SkPDFMakeDict();
99 attrDict->insertName("O", owner);
100 attrDict->insertInt(name, value);
101 fAttrs->appendObject(std::move(attrDict));
102}
103
105 const char* owner, const char* name, float value) {
106 if (!fAttrs)
107 fAttrs = SkPDFMakeArray();
108 std::unique_ptr<SkPDFDict> attrDict = SkPDFMakeDict();
109 attrDict->insertName("O", owner);
110 attrDict->insertScalar(name, value);
111 fAttrs->appendObject(std::move(attrDict));
112}
113
115 const char* owner, const char* name, const char* value) {
116 if (!fAttrs)
117 fAttrs = SkPDFMakeArray();
118 std::unique_ptr<SkPDFDict> attrDict = SkPDFMakeDict();
119 attrDict->insertName("O", owner);
120 attrDict->insertName(name, value);
121 fAttrs->appendObject(std::move(attrDict));
122}
123
125 const char* owner, const char* name, const std::vector<float>& value) {
126 if (!fAttrs)
127 fAttrs = SkPDFMakeArray();
128 std::unique_ptr<SkPDFDict> attrDict = SkPDFMakeDict();
129 attrDict->insertName("O", owner);
130 std::unique_ptr<SkPDFArray> pdfArray = SkPDFMakeArray();
131 for (float element : value) {
132 pdfArray->appendScalar(element);
133 }
134 attrDict->insertObject(name, std::move(pdfArray));
135 fAttrs->appendObject(std::move(attrDict));
136}
137
139 const char* owner,
140 const char* name,
141 const std::vector<int>& nodeIds) {
142 if (!fAttrs)
143 fAttrs = SkPDFMakeArray();
144 std::unique_ptr<SkPDFDict> attrDict = SkPDFMakeDict();
145 attrDict->insertName("O", owner);
146 std::unique_ptr<SkPDFArray> pdfArray = SkPDFMakeArray();
147 for (int nodeId : nodeIds) {
148 SkString idString = SkPDFTagNode::nodeIdToString(nodeId);
149 pdfArray->appendByteString(idString);
150 }
151 attrDict->insertObject(name, std::move(pdfArray));
152 fAttrs->appendObject(std::move(attrDict));
153}
154
155SkPDFTagTree::SkPDFTagTree() : fArena(4 * sizeof(SkPDFTagNode)) {}
156
158
159// static
160void SkPDFTagTree::Copy(SkPDF::StructureElementNode& node,
161 SkPDFTagNode* dst,
162 SkArenaAlloc* arena,
164 bool wantTitle) {
165 nodeMap->set(node.fNodeId, dst);
166 for (int nodeId : node.fAdditionalNodeIds) {
167 SkASSERT(!nodeMap->find(nodeId));
168 nodeMap->set(nodeId, dst);
169 }
170 dst->fNodeId = node.fNodeId;
171
172 // Accumulate title text, need to be in sync with create_outline_from_headers
173 const char* type = node.fTypeString.c_str();
174 wantTitle |= fOutline == SkPDF::Metadata::Outline::StructureElementHeaders &&
175 type[0] == 'H' && '1' <= type[1] && type[1] <= '6';
176 dst->fWantTitle = wantTitle;
177
178 dst->fTypeString = node.fTypeString;
179 dst->fAlt = node.fAlt;
180 dst->fLang = node.fLang;
181
182 size_t childCount = node.fChildVector.size();
183 SkPDFTagNode* children = arena->makeArray<SkPDFTagNode>(childCount);
184 dst->fChildCount = childCount;
185 dst->fChildren = children;
186 for (size_t i = 0; i < childCount; ++i) {
187 Copy(*node.fChildVector[i], &children[i], arena, nodeMap, wantTitle);
188 }
189
190 dst->fAttributes = std::move(node.fAttributes.fAttrs);
191}
192
194 if (node) {
195 fRoot = fArena.make<SkPDFTagNode>();
196 fOutline = outline;
197 Copy(*node, fRoot, &fArena, &fNodeMap, false);
198 }
199}
200
202 return fNode ? fNode->fMarkedContent[fMarkIndex].fMarkId : -1;
203}
204
206 return fNode->fMarkedContent[fMarkIndex].fLocation.fPoint;
207}
208
209auto SkPDFTagTree::createMarkIdForNodeId(int nodeId, unsigned pageIndex, SkPoint point) -> Mark {
210 if (!fRoot) {
211 return Mark();
212 }
213 SkPDFTagNode** tagPtr = fNodeMap.find(nodeId);
214 if (!tagPtr) {
215 return Mark();
216 }
217 SkPDFTagNode* tag = *tagPtr;
218 SkASSERT(tag);
219 while (SkToUInt(fMarksPerPage.size()) < pageIndex + 1) {
220 fMarksPerPage.push_back();
221 }
222 TArray<SkPDFTagNode*>& pageMarks = fMarksPerPage[pageIndex];
223 int markId = pageMarks.size();
224 tag->fMarkedContent.push_back({{point, pageIndex}, markId});
225 pageMarks.push_back(tag);
226 return Mark(tag, tag->fMarkedContent.size() - 1);
227}
228
229int SkPDFTagTree::createStructParentKeyForNodeId(int nodeId, unsigned pageIndex) {
230 if (!fRoot) {
231 return -1;
232 }
233 SkPDFTagNode** tagPtr = fNodeMap.find(nodeId);
234 if (!tagPtr) {
235 return -1;
236 }
237 SkPDFTagNode* tag = *tagPtr;
238 SkASSERT(tag);
239
241
242 int nextStructParentKey = kFirstAnnotationStructParentKey +
243 static_cast<int>(fParentTreeAnnotationNodeIds.size());
244 fParentTreeAnnotationNodeIds.push_back(nodeId);
245 return nextStructParentKey;
246}
247
248static bool can_discard(SkPDFTagNode* node) {
249 if (node->fCanDiscard == SkPDFTagNode::kYes) {
250 return true;
251 }
252 if (node->fCanDiscard == SkPDFTagNode::kNo) {
253 return false;
254 }
255 if (!node->fMarkedContent.empty()) {
257 return false;
258 }
259 for (size_t i = 0; i < node->fChildCount; ++i) {
260 if (!can_discard(&node->fChildren[i])) {
262 return false;
263 }
264 }
266 return true;
267}
268
269SkPDFIndirectReference SkPDFTagTree::PrepareTagTreeToEmit(SkPDFIndirectReference parent,
270 SkPDFTagNode* node,
271 SkPDFDocument* doc) {
273 std::unique_ptr<SkPDFArray> kids = SkPDFMakeArray();
274 SkPDFTagNode* children = node->fChildren;
275 size_t childCount = node->fChildCount;
276 for (size_t i = 0; i < childCount; ++i) {
277 SkPDFTagNode* child = &children[i];
278 if (!(can_discard(child))) {
279 kids->appendRef(PrepareTagTreeToEmit(ref, child, doc));
280 }
281 }
282 for (const SkPDFTagNode::MarkedContentInfo& info : node->fMarkedContent) {
283 std::unique_ptr<SkPDFDict> mcr = SkPDFMakeDict("MCR");
284 mcr->insertRef("Pg", doc->getPage(info.fLocation.fPageIndex));
285 mcr->insertInt("MCID", info.fMarkId);
286 kids->appendObject(std::move(mcr));
287 }
288 for (const SkPDFTagNode::AnnotationInfo& annotationInfo : node->fAnnotations) {
289 std::unique_ptr<SkPDFDict> annotationDict = SkPDFMakeDict("OBJR");
290 annotationDict->insertRef("Obj", annotationInfo.fAnnotationRef);
291 annotationDict->insertRef("Pg", doc->getPage(annotationInfo.fPageIndex));
292 kids->appendObject(std::move(annotationDict));
293 }
294 node->fRef = ref;
295 SkPDFDict dict("StructElem");
296 dict.insertName("S", node->fTypeString.isEmpty() ? "NonStruct" : node->fTypeString.c_str());
297 if (!node->fAlt.isEmpty()) {
298 dict.insertTextString("Alt", node->fAlt);
299 }
300 if (!node->fLang.isEmpty()) {
301 dict.insertTextString("Lang", node->fLang);
302 }
303 dict.insertRef("P", parent);
304 dict.insertObject("K", std::move(kids));
305 if (node->fAttributes) {
306 dict.insertObject("A", std::move(node->fAttributes));
307 }
308
309 // Each node has a unique ID that also needs to be referenced
310 // in a separate IDTree node, along with the lowest and highest
311 // unique ID string.
313 dict.insertByteString("ID", idString.c_str());
314 IDTreeEntry idTreeEntry = {node->fNodeId, ref};
315 fIdTreeEntries.push_back(idTreeEntry);
316
317 return doc->emit(dict, ref);
318}
319
320void SkPDFTagTree::addNodeAnnotation(int nodeId, SkPDFIndirectReference annotationRef, unsigned pageIndex) {
321 if (!fRoot) {
322 return;
323 }
324 SkPDFTagNode** tagPtr = fNodeMap.find(nodeId);
325 if (!tagPtr) {
326 return;
327 }
328 SkPDFTagNode* tag = *tagPtr;
329 SkASSERT(tag);
330
331 SkPDFTagNode::AnnotationInfo annotationInfo = {pageIndex, annotationRef};
332 tag->fAnnotations.push_back(annotationInfo);
333}
334
336 if (!fRoot) {
337 return;
338 }
339 SkPDFTagNode** tagPtr = fNodeMap.find(nodeId);
340 if (!tagPtr) {
341 return;
342 }
343 SkPDFTagNode* tag = *tagPtr;
344 SkASSERT(tag);
345
346 if (tag->fWantTitle) {
347 tag->fTitle.append(title.data(), title.size());
348 // Arbitrary cutoff for size.
349 if (tag->fTitle.size() > 1023) {
350 tag->fWantTitle = false;
351 }
352 }
353}
354
356 if (!fRoot || can_discard(fRoot)) {
357 return SkPDFIndirectReference();
358 }
359
361
362 unsigned pageCount = SkToUInt(doc->pageCount());
363
364 // Build the StructTreeRoot.
365 SkPDFDict structTreeRoot("StructTreeRoot");
366 structTreeRoot.insertRef("K", PrepareTagTreeToEmit(ref, fRoot, doc));
367 structTreeRoot.insertInt("ParentTreeNextKey", SkToInt(pageCount));
368
369 // Build the parent tree, which consists of two things:
370 // (1) For each page, a mapping from the marked content IDs on
371 // each page to their corresponding tags
372 // (2) For each annotation, an indirect reference to that
373 // annotation's struct tree element.
374 SkPDFDict parentTree("ParentTree");
375 auto parentTreeNums = SkPDFMakeArray();
376
377 // First, one entry per page.
378 SkASSERT(SkToUInt(fMarksPerPage.size()) <= pageCount);
379 for (int j = 0; j < fMarksPerPage.size(); ++j) {
380 const TArray<SkPDFTagNode*>& pageMarks = fMarksPerPage[j];
381 SkPDFArray markToTagArray;
382 for (SkPDFTagNode* mark : pageMarks) {
383 SkASSERT(mark->fRef);
384 markToTagArray.appendRef(mark->fRef);
385 }
386 parentTreeNums->appendInt(j);
387 parentTreeNums->appendRef(doc->emit(markToTagArray));
388 }
389
390 // Then, one entry per annotation.
391 for (size_t j = 0; j < fParentTreeAnnotationNodeIds.size(); ++j) {
392 int nodeId = fParentTreeAnnotationNodeIds[j];
393 int structParentKey = kFirstAnnotationStructParentKey + static_cast<int>(j);
394
395 SkPDFTagNode** tagPtr = fNodeMap.find(nodeId);
396 if (!tagPtr) {
397 continue;
398 }
399 SkPDFTagNode* tag = *tagPtr;
400 parentTreeNums->appendInt(structParentKey);
401 parentTreeNums->appendRef(tag->fRef);
402 }
403
404 parentTree.insertObject("Nums", std::move(parentTreeNums));
405 structTreeRoot.insertRef("ParentTree", doc->emit(parentTree));
406
407 // Build the IDTree, a mapping from every unique ID string to
408 // a reference to its corresponding structure element node.
409 if (!fIdTreeEntries.empty()) {
410 std::sort(fIdTreeEntries.begin(), fIdTreeEntries.end(),
411 [](const IDTreeEntry& a, const IDTreeEntry& b) {
412 return a.nodeId < b.nodeId;
413 });
414
415 SkPDFDict idTree;
416 SkPDFDict idTreeLeaf;
417 auto limits = SkPDFMakeArray();
418 SkString lowestNodeIdString = SkPDFTagNode::nodeIdToString(
419 fIdTreeEntries.begin()->nodeId);
420 limits->appendByteString(lowestNodeIdString);
421 SkString highestNodeIdString = SkPDFTagNode::nodeIdToString(
422 fIdTreeEntries.rbegin()->nodeId);
423 limits->appendByteString(highestNodeIdString);
424 idTreeLeaf.insertObject("Limits", std::move(limits));
425 auto names = SkPDFMakeArray();
426 for (const IDTreeEntry& entry : fIdTreeEntries) {
427 SkString idString = SkPDFTagNode::nodeIdToString(entry.nodeId);
428 names->appendByteString(idString);
429 names->appendRef(entry.ref);
430 }
431 idTreeLeaf.insertObject("Names", std::move(names));
432 auto idTreeKids = SkPDFMakeArray();
433 idTreeKids->appendRef(doc->emit(idTreeLeaf));
434 idTree.insertObject("Kids", std::move(idTreeKids));
435 structTreeRoot.insertRef("IDTree", doc->emit(idTree));
436 }
437
438 return doc->emit(structTreeRoot, ref);
439}
440
441namespace {
442struct OutlineEntry {
443 struct Content {
444 SkString fText;
445 Location fLocation;
446 void accumulate(Content const& child) {
447 fText += child.fText;
448 fLocation.accumulate(child.fLocation);
449 }
450 };
451
452 Content fContent;
453 int fHeaderLevel;
455 SkPDFIndirectReference fStructureRef;
456 std::vector<OutlineEntry> fChildren = {};
457 size_t fDescendentsEmitted = 0;
458
459 void emitDescendents(SkPDFDocument* const doc) {
460 fDescendentsEmitted = fChildren.size();
461 for (size_t i = 0; i < fChildren.size(); ++i) {
462 auto&& child = fChildren[i];
463 child.emitDescendents(doc);
464 fDescendentsEmitted += child.fDescendentsEmitted;
465
466 SkPDFDict entry;
467 entry.insertTextString("Title", child.fContent.fText);
468
469 auto destination = SkPDFMakeArray();
470 destination->appendRef(doc->getPage(child.fContent.fLocation.fPageIndex));
471 destination->appendName("XYZ");
472 destination->appendScalar(child.fContent.fLocation.fPoint.x());
473 destination->appendScalar(child.fContent.fLocation.fPoint.y());
474 destination->appendInt(0);
475 entry.insertObject("Dest", std::move(destination));
476
477 entry.insertRef("Parent", child.fRef);
478 if (child.fStructureRef) {
479 entry.insertRef("SE", child.fStructureRef);
480 }
481 if (0 < i) {
482 entry.insertRef("Prev", fChildren[i-1].fRef);
483 }
484 if (i < fChildren.size()-1) {
485 entry.insertRef("Next", fChildren[i+1].fRef);
486 }
487 if (!child.fChildren.empty()) {
488 entry.insertRef("First", child.fChildren.front().fRef);
489 entry.insertRef("Last", child.fChildren.back().fRef);
490 entry.insertInt("Count", child.fDescendentsEmitted);
491 }
492 doc->emit(entry, child.fRef);
493 }
494 }
495};
496
497OutlineEntry::Content create_outline_entry_content(SkPDFTagNode* const node) {
499 if (!node->fTitle.isEmpty()) {
500 text = node->fTitle;
501 } else if (!node->fAlt.isEmpty()) {
502 text = node->fAlt;
503 }
504
505 // The uppermost/leftmost point on the earliest page of this node's marks.
506 Location markPoint;
507 for (auto&& mark : node->fMarkedContent) {
508 markPoint.accumulate(mark.fLocation);
509 }
510
511 OutlineEntry::Content content{std::move(text), std::move(markPoint)};
512
513 // Accumulate children
514 SkSpan<SkPDFTagNode> children(node->fChildren, node->fChildCount);
515 for (auto&& child : children) {
516 if (can_discard(&child)) {
517 continue;
518 }
519 content.accumulate(create_outline_entry_content(&child));
520 }
521 return content;
522}
523void create_outline_from_headers(SkPDFDocument* const doc, SkPDFTagNode* const node,
525 char const *type = node->fTypeString.c_str();
526 if (type[0] == 'H' && '1' <= type[1] && type[1] <= '6') {
527 int level = type[1] - '0';
528 while (level <= stack.back()->fHeaderLevel) {
529 stack.pop_back();
530 }
531 OutlineEntry::Content content = create_outline_entry_content(node);
532 if (!content.fText.isEmpty()) {
533 OutlineEntry e{std::move(content), level, doc->reserveRef(), node->fRef};
534 stack.push_back(&stack.back()->fChildren.emplace_back(std::move(e)));
535 return;
536 }
537 }
538
539 SkSpan<SkPDFTagNode> children(node->fChildren, node->fChildCount);
540 for (auto&& child : children) {
541 if (can_discard(&child)) {
542 continue;
543 }
544 create_outline_from_headers(doc, &child, stack);
545 }
546}
547
548} // namespace
549
551 if (!fRoot || can_discard(fRoot) ||
553 {
554 return SkPDFIndirectReference();
555 }
556
558 OutlineEntry top{{SkString(), Location()}, 0, {}, {}};
559 stack.push_back(&top);
560 create_outline_from_headers(doc, fRoot, stack);
561 if (top.fChildren.empty()) {
562 return SkPDFIndirectReference();
563 }
564 top.emitDescendents(doc);
565 SkPDFIndirectReference outlineRef = doc->reserveRef();
566 SkPDFDict outline("Outlines");
567 outline.insertRef("First", top.fChildren.front().fRef);
568 outline.insertRef("Last", top.fChildren.back().fRef);
569 outline.insertInt("Count", top.fDescendentsEmitted);
570
571 return doc->emit(outline, outlineRef);
572}
573
575 return fRoot ? fRoot->fLang : SkString();
576}
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
const char * fTypeString
#define SkASSERT(cond)
Definition SkAssert.h:116
static bool can_discard(SkPDFTagNode *node)
Definition SkPDFTag.cpp:248
const int kFirstAnnotationStructParentKey
Definition SkPDFTag.cpp:23
static std::unique_ptr< SkPDFDict > SkPDFMakeDict(const char *type=nullptr)
Definition SkPDFTypes.h:195
static std::unique_ptr< SkPDFArray > SkPDFMakeArray(Args... args)
Definition SkPDFTypes.h:135
#define SK_ScalarNaN
Definition SkScalar.h:28
constexpr int SkToInt(S x)
Definition SkTo.h:29
constexpr unsigned SkToUInt(S x)
Definition SkTo.h:30
T * makeArray(size_t count)
auto make(Ctor &&ctor) -> decltype(ctor(nullptr))
void appendRef(SkPDFIndirectReference)
void insertObject(const char key[], std::unique_ptr< SkPDFObject > &&)
void insertInt(const char key[], int32_t value)
void insertTextString(const char key[], const char value[])
void insertRef(const char key[], SkPDFIndirectReference)
SkPDFIndirectReference emit(const SkPDFObject &, SkPDFIndirectReference)
SkPDFIndirectReference reserveRef()
SkPDFIndirectReference getPage(size_t pageIndex) const
SkPoint & point()
Definition SkPDFTag.cpp:205
SkPDFIndirectReference makeStructTreeRoot(SkPDFDocument *doc)
Definition SkPDFTag.cpp:355
int createStructParentKeyForNodeId(int nodeId, unsigned pageIndex)
Definition SkPDFTag.cpp:229
void init(SkPDF::StructureElementNode *, SkPDF::Metadata::Outline)
Definition SkPDFTag.cpp:193
SkPDFIndirectReference makeOutline(SkPDFDocument *doc)
Definition SkPDFTag.cpp:550
void addNodeAnnotation(int nodeId, SkPDFIndirectReference annotationRef, unsigned pageIndex)
Definition SkPDFTag.cpp:320
SkString getRootLanguage()
Definition SkPDFTag.cpp:574
Mark createMarkIdForNodeId(int nodeId, unsigned pageIndex, SkPoint)
Definition SkPDFTag.cpp:209
void addNodeTitle(int nodeId, SkSpan< const char >)
Definition SkPDFTag.cpp:335
void appendNodeIdArray(const char *owner, const char *attrName, const std::vector< int > &nodeIds)
Definition SkPDFTag.cpp:138
void appendFloatArray(const char *owner, const char *name, const std::vector< float > &value)
Definition SkPDFTag.cpp:124
void appendName(const char *owner, const char *attrName, const char *value)
Definition SkPDFTag.cpp:114
void appendInt(const char *owner, const char *name, int value)
Definition SkPDFTag.cpp:94
void appendFloat(const char *owner, const char *name, float value)
Definition SkPDFTag.cpp:104
constexpr T * data() const
Definition SkSpan_impl.h:94
constexpr size_t size() const
Definition SkSpan_impl.h:95
void printf(const char format[],...) SK_PRINTF_LIKE(2
Definition SkString.cpp:534
size_t size() const
Definition SkString.h:131
bool isEmpty() const
Definition SkString.h:130
void append(const char text[])
Definition SkString.h:203
const char * c_str() const
Definition SkString.h:133
int size() const
Definition SkTArray.h:416
V * find(const K &key) const
Definition SkTHash.h:479
V * set(K key, V val)
Definition SkTHash.h:472
static bool b
struct MyStruct a[10]
uint8_t value
const char * name
Definition fuchsia.cc:50
static void mark(SkCanvas *canvas, SkScalar x, SkScalar y, Fn &&fn)
Definition gm.cpp:211
std::u16string text
union flutter::testing::@2838::KeyboardChange::@76 content
dst
Definition cp.py:12
SkPDFIndirectReference fAnnotationRef
Definition SkPDFTag.cpp:85
TArray< MarkedContentInfo > fMarkedContent
Definition SkPDFTag.cpp:69
std::unique_ptr< SkPDFArray > fAttributes
Definition SkPDFTag.cpp:82
SkString fTypeString
Definition SkPDFTag.cpp:72
size_t fChildCount
Definition SkPDFTag.cpp:64
SkString fAlt
Definition SkPDFTag.cpp:74
SkString fLang
Definition SkPDFTag.cpp:75
SkPDFIndirectReference fRef
Definition SkPDFTag.cpp:76
std::vector< AnnotationInfo > fAnnotations
Definition SkPDFTag.cpp:87
SkPDFTagNode * fChildren
Definition SkPDFTag.cpp:63
SkString fTitle
Definition SkPDFTag.cpp:73
bool fWantTitle
Definition SkPDFTag.cpp:71
static SkString nodeIdToString(int nodeId)
Definition SkPDFTag.cpp:57
enum SkPDFTagNode::State fCanDiscard
std::vector< std::unique_ptr< StructureElementNode > > fChildVector