Class StructureTreeUpdater


  • public final class StructureTreeUpdater
    extends java.lang.Object
    Wires a brand-new PDF annotation -- one XfdfImporter just added from an <annots> entry, not one it merely updated in place -- into an already-tagged target document's accessibility structure tree (/StructTreeRoot), so the imported annotation doesn't silently fall outside it.

    Per the Matterhorn Protocol (the PDF/UA-1 test suite -- checkpoint 13), every visible annotation other than Popup needs a structure element referencing it via an OBJR; a Link annotation specifically needs the standard Link structure type, everything else uses the generic Annot type (a real standard structure type as of PDF 2.0/ISO 32000-2; on an older-version document a RoleMap fallback to the PDF 1.7 standard type Note is added alongside it so a reader that only knows the older type set still resolves it to something sensible). Popup, and any annotation flagged Hidden or NoView (ISO 32000-1 Table 165 -- it's never rendered either way), are intentionally left untagged, matching the same exemptions.

    Scope: this class only ever extends a structure tree that already exists (see forDocument(COSDocument)) -- it never adds /StructTreeRoot to a PDF that didn't have one, since building a whole tag hierarchy from scratch for a document that was never tagged is a much larger, different problem than keeping an existing one consistent. A new element is appended directly under /StructTreeRoot/K (rather than nested under whichever existing top-level element happens to "belong" to the same page) -- simple and always structurally valid, at the cost of the newly-tagged annotation reading last in the document's overall structure order regardless of which page it's actually on.

    Thread-safety: an instance is stateful (it accumulates the parent tree/RoleMap bookkeeping for one import batch across possibly several tagNewAnnotation(com.pd4ml.pdf.cos.COSDictionary, com.pd4ml.pdf.cos.COSDictionary) calls before finish()) and mutates the one COSDocument it was built for -- confine each instance to a single thread and a single XfdfImporter.importInto(...) call, exactly how XfdfImporter itself already uses it (one fresh instance per call, via forDocument(COSDocument), never shared or retained afterwards). Distinct instances over distinct target documents -- e.g. concurrent importInto(...) calls on different threads -- don't interact at all: nothing here is static or shared across instances.

    • Method Summary

      All Methods Static Methods Instance Methods Concrete Methods 
      Modifier and Type Method Description
      void finish()
      Finalizes bookkeeping once every annotation in this import batch has been processed: closes off a /Kids-based leaf's /Limits, and restates /StructTreeRoot/ParentTreeNextKey.
      static StructureTreeUpdater forDocument​(COSDocument document)
      Returns a new updater for document, or null if it isn't tagged (no /StructTreeRoot) -- the caller's cue to skip structure-tree work for this import entirely, exactly as if this class didn't exist.
      void tagNewAnnotation​(COSDictionary page, COSDictionary annotDict)
      Tags annotDict (already attached to page's /Annots) if it's the kind of annotation that should be, per this class's own documentation.
      • Methods inherited from class java.lang.Object

        equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    • Method Detail

      • forDocument

        public static StructureTreeUpdater forDocument​(COSDocument document)
        Returns a new updater for document, or null if it isn't tagged (no /StructTreeRoot) -- the caller's cue to skip structure-tree work for this import entirely, exactly as if this class didn't exist.
      • tagNewAnnotation

        public void tagNewAnnotation​(COSDictionary page,
                                     COSDictionary annotDict)
        Tags annotDict (already attached to page's /Annots) if it's the kind of annotation that should be, per this class's own documentation. A no-op for Popup and for any annotation that never actually renders (Hidden/NoView).
      • finish

        public void finish()
        Finalizes bookkeeping once every annotation in this import batch has been processed: closes off a /Kids-based leaf's /Limits, and restates /StructTreeRoot/ParentTreeNextKey. A no-op, touching nothing at all, if this batch never actually tagged anything (e.g. an import that only updated <fields> values, or whose every <annots> entry matched and updated an existing annotation) -- minimal footprint on a document this class otherwise had no reason to touch.