blob: 5e548cd83179c0c60ae9b29a548f25f6036ca154 [file]
/*
* Copyright 2018 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkPDFTag_DEFINED
#define SkPDFTag_DEFINED
#include "include/core/SkSpan.h"
#include "include/core/SkString.h"
#include "include/docs/SkPDFDocument.h"
#include "include/private/SkTArray.h"
#include "src/core/SkArenaAlloc.h"
#include "src/core/SkTHash.h"
#include "src/pdf/SkPDFTypes.h"
#include <cstddef>
#include <variant>
class SkPDFDocument;
struct SkPDFStructElem;
struct SkPoint;
class SkPDFStructTree {
public:
SkPDFStructTree(SkPDF::StructureElementNode*, SkPDF::Metadata::Outline);
SkPDFStructTree(const SkPDFStructTree&) = delete;
SkPDFStructTree& operator=(const SkPDFStructTree&) = delete;
SkPDFStructTree(SkPDFStructTree&&) = delete;
SkPDFStructTree& operator=(SkPDFStructTree&&) = delete;
~SkPDFStructTree();
class Mark {
SkPDFStructElem* fStructElem;
size_t fMarkIndex;
public:
Mark(SkPDFStructElem* structElem, size_t markIndex)
: fStructElem(structElem), fMarkIndex(markIndex) {}
Mark() : Mark(nullptr, 0) {}
Mark(const Mark&) = default;
Mark& operator=(const Mark&) = default;
Mark(Mark&&) = default;
Mark& operator=(Mark&&) = default;
explicit operator bool() const { return fStructElem; }
int mcid() const; // mcid < 0 means no active mark, if bool(this) always >= 0
int elemId() const; // 0 elemId means no active structure element
SkString structType() const; // only call when bool(this)
void accumulate(SkPoint); // only call when bool(this)
};
// Create a new marked-content identifier (MCID) to be used with a marked-content sequence
// parented by the structure element (StructElem) with the given element identifier (elemId).
// The StructTreeRoot::ParentTree[?::StructParents][mcid] will refer to the structure element.
// The structure element will add this MCID as its next child (in StructElem::K).
// Returns a false Mark if if elemId does not refer to a StructElem.
//
// If a true Mark is returned and structParentsKey is false, the Mark will be added to a new
// StructParents and structParentsKey will be updated to reference the StructParents entry.
SkPDFStructTree::Mark createMarkForElemId(int elemId, unsigned pageIndex,
SkPDFParentTreeKey& structParentsKey);
static constexpr SkPDFIndirectReference kPageContentStreamRef = SkPDFIndirectReference{-2};
void setContentStreamRefForStructParentsKey(SkPDFParentTreeKey structParentsKey,
SkPDFIndirectReference contentStreamRef);
SkPDFIndirectReference getContentStreamRefForStructParentsKey(
SkPDFParentTreeKey structParentsKey) const;
// Create a key to use with /StructParent in a content item (usually an annotation) which refers
// to the structure element (StructElem) with the given element identifier (elemId).
// The StructTreeRoot ParentTree will map from this key to the structure element.
// The structure element will add the content item as its next child (as StructElem::K::OBJR).
// Returns -1 if elemId does not refer to a StructElem.
SkPDFParentTreeKey createStructParentKeyForElemId(int elemId, unsigned pageIndex,
SkPDFIndirectReference contentItemRef);
SkPDFIndirectReference getContentItemRefForStructParentKey(
SkPDFParentTreeKey structParentKey) const;
void addStructElemTitle(int elemId, SkSpan<const char>);
SkPDFIndirectReference emitStructTreeRoot(SkPDFDocument* doc) const;
SkPDFIndirectReference makeOutline(SkPDFDocument* doc) const;
SkString getRootLanguage();
// An entry in an ordered map from an element identifier to an indirect reference to its
// corresponding structure element.
struct IDTreeEntry {
int elemId;
SkPDFIndirectReference structElemRef;
};
private:
void move(SkPDF::StructureElementNode& node, SkPDFStructElem* structElem, bool wantTitle);
SkArenaAlloc fArena;
skia_private::THashMap<int, SkPDFStructElem*> fStructElemForElemId;
SkPDFStructElem* fRoot = nullptr;
SkPDF::Metadata::Outline fOutline = SkPDF::Metadata::Outline::None;
struct Item {
SkPDFStructElem* fStructElem;
SkPDFIndirectReference fContentItemRef;
};
struct Stream {
skia_private::TArray<SkPDFStructElem*> fChildren; // indexed by mcid
// the non-page content stream reference, kPageContentStreamRef, or empty
SkPDFIndirectReference fContentStreamRef;
};
using ParentTreeEntry = std::variant<Item, Stream>;
// indexed by ?::StructParent or ?::StructParents
skia_private::TArray<ParentTreeEntry> fParentTree;
};
#endif