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