@@ -66,6 +66,18 @@ static const char* tag_name_from_type(SkPDF::DocumentStructureType type) {
66
66
SK_ABORT (" bad tag" );
67
67
}
68
68
69
+ // The struct parent tree consists of one entry per page, followed by
70
+ // entries for individual struct tree nodes corresponding to
71
+ // annotations. Each entry is a key/value pair with an integer key
72
+ // and an indirect reference key.
73
+ //
74
+ // The page entries get consecutive keys starting at 0. Since we don't
75
+ // know the total number of pages in the document at the time we start
76
+ // processing annotations, start the key for annotations with a large
77
+ // number, which effectively becomes the maximum number of pages in a
78
+ // PDF we can handle.
79
+ const int kFirstAnnotationStructParentKey = 100000 ;
80
+
69
81
struct SkPDFTagNode {
70
82
// Structure element nodes need a unique alphanumeric ID,
71
83
// and we need to be able to output them sorted in lexicographic
@@ -97,7 +109,11 @@ struct SkPDFTagNode {
97
109
kNo ,
98
110
} fCanDiscard = kUnknown ;
99
111
std::unique_ptr<SkPDFArray> fAttributes ;
100
- std::vector<SkPDFIndirectReference> fAnnotations ;
112
+ struct AnnotationInfo {
113
+ unsigned fPageIndex ;
114
+ SkPDFIndirectReference fAnnotationRef ;
115
+ };
116
+ std::vector<AnnotationInfo> fAnnotations ;
101
117
};
102
118
103
119
SkPDF::AttributeList::AttributeList () = default;
@@ -238,7 +254,7 @@ void SkPDFTagTree::reset() {
238
254
fRoot = nullptr ;
239
255
}
240
256
241
- int SkPDFTagTree::getMarkIdForNodeId (int nodeId, unsigned pageIndex) {
257
+ int SkPDFTagTree::createMarkIdForNodeId (int nodeId, unsigned pageIndex) {
242
258
if (!fRoot ) {
243
259
return -1 ;
244
260
}
@@ -258,6 +274,25 @@ int SkPDFTagTree::getMarkIdForNodeId(int nodeId, unsigned pageIndex) {
258
274
return markId;
259
275
}
260
276
277
+ int SkPDFTagTree::createStructParentKeyForNodeId (int nodeId, unsigned pageIndex) {
278
+ if (!fRoot ) {
279
+ return -1 ;
280
+ }
281
+ SkPDFTagNode** tagPtr = fNodeMap .find (nodeId);
282
+ if (!tagPtr) {
283
+ return -1 ;
284
+ }
285
+ SkPDFTagNode* tag = *tagPtr;
286
+ SkASSERT (tag);
287
+
288
+ tag->fCanDiscard = SkPDFTagNode::kNo ;
289
+
290
+ int nextStructParentKey = kFirstAnnotationStructParentKey +
291
+ static_cast <int >(fParentTreeAnnotationNodeIds .size ());
292
+ fParentTreeAnnotationNodeIds .push_back (nodeId);
293
+ return nextStructParentKey;
294
+ }
295
+
261
296
static bool can_discard (SkPDFTagNode* node) {
262
297
if (node->fCanDiscard == SkPDFTagNode::kYes ) {
263
298
return true ;
@@ -298,9 +333,10 @@ SkPDFIndirectReference SkPDFTagTree::PrepareTagTreeToEmit(SkPDFIndirectReference
298
333
mcr->insertInt (" MCID" , info.fMarkId );
299
334
kids->appendObject (std::move (mcr));
300
335
}
301
- for (SkPDFIndirectReference annotationRef : node->fAnnotations ) {
336
+ for (const SkPDFTagNode::AnnotationInfo& annotationInfo : node->fAnnotations ) {
302
337
std::unique_ptr<SkPDFDict> annotationDict = SkPDFMakeDict (" OBJR" );
303
- annotationDict->insertRef (" Obj" , annotationRef);
338
+ annotationDict->insertRef (" Obj" , annotationInfo.fAnnotationRef );
339
+ annotationDict->insertRef (" Pg" , doc->getPage (annotationInfo.fPageIndex ));
304
340
kids->appendObject (std::move (annotationDict));
305
341
}
306
342
node->fRef = ref;
@@ -333,7 +369,7 @@ SkPDFIndirectReference SkPDFTagTree::PrepareTagTreeToEmit(SkPDFIndirectReference
333
369
return doc->emit (dict, ref);
334
370
}
335
371
336
- void SkPDFTagTree::addNodeAnnotation (int nodeId, SkPDFIndirectReference annotationRef) {
372
+ void SkPDFTagTree::addNodeAnnotation (int nodeId, SkPDFIndirectReference annotationRef, unsigned pageIndex ) {
337
373
if (!fRoot ) {
338
374
return ;
339
375
}
@@ -343,9 +379,10 @@ void SkPDFTagTree::addNodeAnnotation(int nodeId, SkPDFIndirectReference annotati
343
379
}
344
380
SkPDFTagNode* tag = *tagPtr;
345
381
SkASSERT (tag);
346
- tag->fAnnotations .push_back (annotationRef);
347
- }
348
382
383
+ SkPDFTagNode::AnnotationInfo annotationInfo = {pageIndex, annotationRef};
384
+ tag->fAnnotations .push_back (annotationInfo);
385
+ }
349
386
350
387
SkPDFIndirectReference SkPDFTagTree::makeStructTreeRoot (SkPDFDocument* doc) {
351
388
if (!fRoot ) {
@@ -363,11 +400,15 @@ SkPDFIndirectReference SkPDFTagTree::makeStructTreeRoot(SkPDFDocument* doc) {
363
400
structTreeRoot.insertRef (" K" , PrepareTagTreeToEmit (ref, fRoot , doc));
364
401
structTreeRoot.insertInt (" ParentTreeNextKey" , SkToInt (pageCount));
365
402
366
- // Build the parent tree, which is a mapping from the marked
367
- // content IDs on each page to their corressponding tags.
403
+ // Build the parent tree, which consists of two things:
404
+ // (1) For each page, a mapping from the marked content IDs on
405
+ // each page to their corresponding tags
406
+ // (2) For each annotation, an indirect reference to that
407
+ // annotation's struct tree element.
368
408
SkPDFDict parentTree (" ParentTree" );
369
409
auto parentTreeNums = SkPDFMakeArray ();
370
410
411
+ // First, one entry per page.
371
412
SkASSERT (fMarksPerPage .size () <= pageCount);
372
413
for (size_t j = 0 ; j < fMarksPerPage .size (); ++j) {
373
414
const SkTArray<SkPDFTagNode*>& pageMarks = fMarksPerPage [j];
@@ -379,6 +420,21 @@ SkPDFIndirectReference SkPDFTagTree::makeStructTreeRoot(SkPDFDocument* doc) {
379
420
parentTreeNums->appendInt (j);
380
421
parentTreeNums->appendRef (doc->emit (markToTagArray));
381
422
}
423
+
424
+ // Then, one entry per annotation.
425
+ for (size_t j = 0 ; j < fParentTreeAnnotationNodeIds .size (); ++j) {
426
+ int nodeId = fParentTreeAnnotationNodeIds [j];
427
+ int structParentKey = kFirstAnnotationStructParentKey + static_cast <int >(j);
428
+
429
+ SkPDFTagNode** tagPtr = fNodeMap .find (nodeId);
430
+ if (!tagPtr) {
431
+ continue ;
432
+ }
433
+ SkPDFTagNode* tag = *tagPtr;
434
+ parentTreeNums->appendInt (structParentKey);
435
+ parentTreeNums->appendRef (tag->fRef );
436
+ }
437
+
382
438
parentTree.insertObject (" Nums" , std::move (parentTreeNums));
383
439
structTreeRoot.insertRef (" ParentTree" , doc->emit (parentTree));
384
440
0 commit comments