drawinglayer/source/primitive2d/structuretagprimitive2d.cxx  |    7 
 drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx   |    5 
 include/drawinglayer/primitive2d/structuretagprimitive2d.hxx |    6 
 include/svx/sdr/contact/objectcontact.hxx                    |    5 
 include/svx/sdr/contact/objectcontactofpageview.hxx          |    1 
 include/vcl/pdfextoutdevdata.hxx                             |   14 +
 include/vcl/pdfwriter.hxx                                    |    7 
 sd/source/ui/unoidl/unomodel.cxx                             |    3 
 svx/inc/sdr/contact/objectcontactofobjlistpainter.hxx        |    1 
 svx/source/sdr/contact/objectcontact.cxx                     |    5 
 svx/source/sdr/contact/objectcontactofobjlistpainter.cxx     |   11 
 svx/source/sdr/contact/objectcontactofpageview.cxx           |   11 
 svx/source/sdr/contact/viewobjectcontact.cxx                 |   14 +
 svx/source/sdr/contact/viewobjectcontactofpageobj.cxx        |    4 
 sw/source/core/text/EnhancedPDFExportHelper.cxx              |    2 
 vcl/inc/pdf/pdfwriter_impl.hxx                               |    7 
 vcl/qa/cppunit/pdfexport/data/vid.odt                        |binary
 vcl/qa/cppunit/pdfexport/pdfexport.cxx                       |  130 +++++++++++
 vcl/source/gdi/pdfextoutdevdata.cxx                          |   44 +++
 vcl/source/gdi/pdfwriter.cxx                                 |    5 
 vcl/source/gdi/pdfwriter_impl.cxx                            |   85 +++++--
 21 files changed, 336 insertions(+), 31 deletions(-)

New commits:
commit e84b310b59825fd572d79def98c3d21566aac603
Author:     Michael Stahl <michael.st...@allotropia.de>
AuthorDate: Thu Mar 16 19:48:31 2023 +0100
Commit:     Michael Stahl <michael.st...@allotropia.de>
CommitDate: Wed Mar 22 11:53:38 2023 +0000

    vcl,drawinglayer,svx,sw,sd: PDF/UA export: Annot StructElem for SdrMediaObj
    
    veraPDF complains:
    
      Specification: ISO 14289-1:2014, Clause: 7.18.1, Test number: 1
      An annotation, excluding annotations of subtype Widget, Popup or
      Link, shall be nested within an Annot tag
    
    This is very similar to Link annotations, that is to say, extremely
    complicated to get it thought the convoluted PDF export code, with
    additional complication that the StructElem is produced by drawinglayer
    and the page annotation by sw.
    
    Put another map into PDFExtOutDevData where sw code puts stuff for the
    SdrObject that drawinglayer can find.
    
    The test had the problem that PDFObjectParser::parse() could not handle:
    
    <</Nums[
    0 [ 6 0 R ]
    1 6 0 R
    ]>>
    
    Fix dropping the "1".
    
    Change-Id: If5bf7c552e26ebb7e631030b8aaecd4281e77acc
    (cherry picked from commit c78e90bd28cc4d6d3bde473535107784b12d9c0d)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/149008
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <michael.st...@allotropia.de>

diff --git a/drawinglayer/source/primitive2d/structuretagprimitive2d.cxx 
b/drawinglayer/source/primitive2d/structuretagprimitive2d.cxx
index 7e4de87ed64a..62da91ecc00f 100644
--- a/drawinglayer/source/primitive2d/structuretagprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/structuretagprimitive2d.cxx
@@ -31,13 +31,18 @@ namespace drawinglayer::primitive2d
             bool bBackground,
             bool bIsImage,
             Primitive2DContainer&& aChildren,
-            sal_Int32 const nAnchorStructureElementId)
+            sal_Int32 const nAnchorStructureElementId,
+            ::std::vector<sal_Int32> const*const pAnnotIds)
         :   GroupPrimitive2D(std::move(aChildren)),
             maStructureElement(rStructureElement),
             mbBackground(bBackground),
             mbIsImage(bIsImage)
         ,   m_nAnchorStructureElementId(nAnchorStructureElementId)
         {
+            if (pAnnotIds)
+            {
+                m_AnnotIds = *pAnnotIds;
+            }
         }
 
         bool StructureTagPrimitive2D::operator==(const BasePrimitive2D& 
rPrimitive) const
diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx 
b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
index d75b67716de8..da99da610883 100644
--- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
@@ -2460,6 +2460,7 @@ void 
VclMetafileProcessor2D::processStructureTagPrimitive2D(
                 case vcl::PDFWriter::Table:
                 case vcl::PDFWriter::Formula:
                 case vcl::PDFWriter::Figure:
+                case vcl::PDFWriter::Annot:
                 {
                     auto const 
range(rStructureTagCandidate.getB2DRange(getViewInformation2D()));
                     tools::Rectangle const aLogicRect(
@@ -2471,6 +2472,10 @@ void 
VclMetafileProcessor2D::processStructureTagPrimitive2D(
                 default:
                     break;
             }
+            if (rTagElement == vcl::PDFWriter::Annot)
+            {
+                
mpPDFExtOutDevData->SetStructureAnnotIds(rStructureTagCandidate.GetAnnotIds());
+            }
             if (rTagElement == vcl::PDFWriter::TableHeader)
             {
                 
mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Scope,
diff --git a/include/drawinglayer/primitive2d/structuretagprimitive2d.hxx 
b/include/drawinglayer/primitive2d/structuretagprimitive2d.hxx
index 40ad422c2b9a..8ee2b267b57f 100644
--- a/include/drawinglayer/primitive2d/structuretagprimitive2d.hxx
+++ b/include/drawinglayer/primitive2d/structuretagprimitive2d.hxx
@@ -51,6 +51,8 @@ namespace drawinglayer::primitive2d
             bool                                    mbIsImage;
             /// anchor structure element (Writer)
             sal_Int32 m_nAnchorStructureElementId;
+            /// for Annot structure element, the ids of the annotations
+            ::std::vector<sal_Int32> m_AnnotIds;
 
         public:
             /// constructor
@@ -59,7 +61,8 @@ namespace drawinglayer::primitive2d
                 bool bBackground,
                 bool bIsImage,
                 Primitive2DContainer&& aChildren,
-                sal_Int32 nAnchorStructureElementId = -1);
+                sal_Int32 nAnchorStructureElementId = -1,
+                ::std::vector<sal_Int32> const* pAnnotIds = nullptr);
 
             /// data read access
             const vcl::PDFWriter::StructElement& getStructureElement() const { 
return maStructureElement; }
@@ -67,6 +70,7 @@ namespace drawinglayer::primitive2d
             bool isImage() const { return mbIsImage; }
             bool isTaggedSdrObject() const;
             sal_Int32 GetAnchorStructureElementId() const { return 
m_nAnchorStructureElementId; }
+            ::std::vector<sal_Int32> GetAnnotIds() const { return m_AnnotIds; }
 
             /// compare operator
             virtual bool operator==(const BasePrimitive2D& rPrimitive) const 
override;
diff --git a/include/svx/sdr/contact/objectcontact.hxx 
b/include/svx/sdr/contact/objectcontact.hxx
index 3e9b1b76d05d..40d631f3fae8 100644
--- a/include/svx/sdr/contact/objectcontact.hxx
+++ b/include/svx/sdr/contact/objectcontact.hxx
@@ -30,6 +30,10 @@ namespace tools { class Rectangle; }
 class SdrPageView;
 class OutputDevice;
 
+namespace vcl {
+    class PDFExtOutDevData;
+}
+
 namespace basegfx {
     class B2DRange;
     class B2DHomMatrix;
@@ -138,6 +142,7 @@ public:
     // pdf export? Default is false
     virtual bool isOutputToPDFFile() const;
     virtual bool isExportTaggedPDF() const;
+    virtual ::vcl::PDFExtOutDevData const* GetPDFExtOutDevData() const;
 
     // gray display mode
     virtual bool isDrawModeGray() const;
diff --git a/include/svx/sdr/contact/objectcontactofpageview.hxx 
b/include/svx/sdr/contact/objectcontactofpageview.hxx
index 8d18083b99b8..1512542dc395 100644
--- a/include/svx/sdr/contact/objectcontactofpageview.hxx
+++ b/include/svx/sdr/contact/objectcontactofpageview.hxx
@@ -93,6 +93,7 @@ namespace sdr::contact
             // pdf export? Default is false
             virtual bool isOutputToPDFFile() const override;
             virtual bool isExportTaggedPDF() const override;
+            virtual ::vcl::PDFExtOutDevData const* GetPDFExtOutDevData() const 
override;
 
             // gray display mode
             virtual bool isDrawModeGray() const override;
diff --git a/include/vcl/pdfextoutdevdata.hxx b/include/vcl/pdfextoutdevdata.hxx
index 81ea37b86d9b..2f0360d1fdd5 100644
--- a/include/vcl/pdfextoutdevdata.hxx
+++ b/include/vcl/pdfextoutdevdata.hxx
@@ -25,10 +25,12 @@
 #include <vcl/pdfwriter.hxx>
 #include <vcl/extoutdevdata.hxx>
 #include <vector>
+#include <map>
 #include <memory>
 
 class Graphic;
 class GDIMetaFile;
+class SdrObject;
 
 namespace vcl
 {
@@ -91,6 +93,8 @@ class VCL_DLLPUBLIC PDFExtOutDevData final : public 
ExtOutDevData
 
     std::vector< PDFExtOutDevBookmarkEntry > maBookmarks;
     std::vector<OUString> maChapterNames;
+    // map from annotation SdrObject to annotation index
+    ::std::map<SdrObject const*, ::std::vector<sal_Int32>> m_ScreenAnnotations;
 
 public:
 
@@ -266,7 +270,10 @@ public:
     sal_Int32 CreateLink(const tools::Rectangle& rRect, OUString const& 
rAltText, sal_Int32 nPageNr = -1);
 
     /// Create a Screen annotation.
-    sal_Int32 CreateScreen(const tools::Rectangle& rRect, OUString const& 
rAltText, sal_Int32 nPageNr);
+    sal_Int32 CreateScreen(const tools::Rectangle& rRect, OUString const& 
rAltText, sal_Int32 nPageNr, SdrObject const* pObj);
+
+    /// Get back the annotations created for one SdrObject.
+    ::std::vector<sal_Int32> const& GetScreenAnnotIds(SdrObject const* pObj) 
const;
 
     /** Set the destination for a link
         <p>will change a URL type link to a dest link if necessary</p>
@@ -438,6 +445,11 @@ public:
      */
     void SetStructureBoundingBox( const tools::Rectangle& rRect );
 
+    /** set the annotations that should be referenced as children of the
+        current structural element.
+     */
+    void SetStructureAnnotIds(::std::vector<sal_Int32> const& rAnnotIds);
+
     /** set the ActualText attribute of a structural element
 
     ActualText contains the Unicode text without layout artifacts that is 
shown by
diff --git a/include/vcl/pdfwriter.hxx b/include/vcl/pdfwriter.hxx
index 1319909961a9..b16e7120b398 100644
--- a/include/vcl/pdfwriter.hxx
+++ b/include/vcl/pdfwriter.hxx
@@ -127,7 +127,7 @@ public:
         Table, TableRow, TableHeader, TableData,
 
         // inline level elements
-        Span, Quote, Note, Reference, BibEntry, Code, Link,
+        Span, Quote, Note, Reference, BibEntry, Code, Link, Annot,
 
         // illustration elements
         Figure, Formula, Form
@@ -1147,6 +1147,11 @@ The following structure describes the permissions used 
in PDF security
      */
     void SetStructureBoundingBox( const tools::Rectangle& rRect );
 
+    /** set the annotations that should be referenced as children of the
+        current structural element.
+     */
+    void SetStructureAnnotIds(::std::vector<sal_Int32> const& rAnnotIds);
+
     /** set the ActualText attribute of a structural element
 
     ActualText contains the Unicode text without layout artifacts that is 
shown by
diff --git a/sd/source/ui/unoidl/unomodel.cxx b/sd/source/ui/unoidl/unomodel.cxx
index 3a89e7728e89..bab827c6be5f 100644
--- a/sd/source/ui/unoidl/unomodel.cxx
+++ b/sd/source/ui/unoidl/unomodel.cxx
@@ -1658,7 +1658,8 @@ static void ImplPDFExportShapeInteraction( const 
uno::Reference< drawing::XShape
                 xShapePropSet->getPropertyValue("MediaURL") >>= aMediaURL;
                 if (!aMediaURL.isEmpty())
                 {
-                    sal_Int32 nScreenId = 
rPDFExtOutDevData.CreateScreen(aLinkRect, altText, 
rPDFExtOutDevData.GetCurrentPageNumber());
+                    SdrObject const*const 
pSdrObj(SdrObject::getSdrObjectFromXShape(xShape));
+                    sal_Int32 nScreenId = 
rPDFExtOutDevData.CreateScreen(aLinkRect, altText, 
rPDFExtOutDevData.GetCurrentPageNumber(), pSdrObj);
                     if (aMediaURL.startsWith("vnd.sun.star.Package:"))
                     {
                         OUString aTempFileURL;
diff --git a/svx/inc/sdr/contact/objectcontactofobjlistpainter.hxx 
b/svx/inc/sdr/contact/objectcontactofobjlistpainter.hxx
index af72593adef2..c977e04a9f69 100644
--- a/svx/inc/sdr/contact/objectcontactofobjlistpainter.hxx
+++ b/svx/inc/sdr/contact/objectcontactofobjlistpainter.hxx
@@ -78,6 +78,7 @@ public:
     // pdf export? Default is false
     virtual bool isOutputToPDFFile() const override;
     virtual bool isExportTaggedPDF() const override;
+    virtual ::vcl::PDFExtOutDevData const* GetPDFExtOutDevData() const 
override;
 
     virtual OutputDevice* TryToGetOutputDevice() const override;
 };
diff --git a/svx/source/sdr/contact/objectcontact.cxx 
b/svx/source/sdr/contact/objectcontact.cxx
index d135a2a29336..4555068ccf01 100644
--- a/svx/source/sdr/contact/objectcontact.cxx
+++ b/svx/source/sdr/contact/objectcontact.cxx
@@ -176,6 +176,11 @@ bool ObjectContact::isExportTaggedPDF() const
     return false;
 }
 
+::vcl::PDFExtOutDevData const* ObjectContact::GetPDFExtOutDevData() const
+{
+    return nullptr;
+}
+
 // gray display mode
 bool ObjectContact::isDrawModeGray() const
 {
diff --git a/svx/source/sdr/contact/objectcontactofobjlistpainter.cxx 
b/svx/source/sdr/contact/objectcontactofobjlistpainter.cxx
index b4727ce30b12..d20b1426e63b 100644
--- a/svx/source/sdr/contact/objectcontactofobjlistpainter.cxx
+++ b/svx/source/sdr/contact/objectcontactofobjlistpainter.cxx
@@ -152,6 +152,17 @@ bool ObjectContactOfObjListPainter::isExportTaggedPDF() 
const
     return false;
 }
 
+::vcl::PDFExtOutDevData const* 
ObjectContactOfObjListPainter::GetPDFExtOutDevData() const
+{
+    if (!isOutputToPDFFile())
+    {
+        return nullptr;
+    }
+    vcl::PDFExtOutDevData *const pPDFExtOutDevData(
+        
dynamic_cast<vcl::PDFExtOutDevData*>(mrTargetOutputDevice.GetExtOutDevData()));
+    return pPDFExtOutDevData;
+}
+
 OutputDevice* ObjectContactOfObjListPainter::TryToGetOutputDevice() const
 {
     return &mrTargetOutputDevice;
diff --git a/svx/source/sdr/contact/objectcontactofpageview.cxx 
b/svx/source/sdr/contact/objectcontactofpageview.cxx
index 9d47bab76dd2..26c295472e0b 100644
--- a/svx/source/sdr/contact/objectcontactofpageview.cxx
+++ b/svx/source/sdr/contact/objectcontactofpageview.cxx
@@ -392,6 +392,17 @@ namespace sdr::contact
             return false;
         }
 
+        ::vcl::PDFExtOutDevData const* 
ObjectContactOfPageView::GetPDFExtOutDevData() const
+        {
+            if (!isOutputToPDFFile())
+            {
+                return nullptr;
+            }
+            vcl::PDFExtOutDevData* 
pPDFExtOutDevData(dynamic_cast<vcl::PDFExtOutDevData*>(
+                
mrPageWindow.GetPaintWindow().GetOutputDevice().GetExtOutDevData()));
+            return pPDFExtOutDevData;
+        }
+
         // gray display mode
         bool ObjectContactOfPageView::isDrawModeGray() const
         {
diff --git a/svx/source/sdr/contact/viewobjectcontact.cxx 
b/svx/source/sdr/contact/viewobjectcontact.cxx
index cae227438061..00f30f4248c5 100644
--- a/svx/source/sdr/contact/viewobjectcontact.cxx
+++ b/svx/source/sdr/contact/viewobjectcontact.cxx
@@ -35,6 +35,7 @@
 #include <svx/svdpage.hxx>
 #include <svx/svdotext.hxx>
 #include <vcl/pdfwriter.hxx>
+#include <vcl/pdfextoutdevdata.hxx>
 
 using namespace com::sun::star;
 
@@ -404,6 +405,8 @@ drawinglayer::primitive2d::Primitive2DContainer const & 
ViewObjectContact::getPr
                     eElement = vcl::PDFWriter::Section;
                 else if (nIdentifier == SdrObjKind::Table)
                     eElement = vcl::PDFWriter::Table;
+                else if (nIdentifier == SdrObjKind::Media)
+                    eElement = vcl::PDFWriter::Annot;
                 else if ( nIdentifier == SdrObjKind::TitleText )
                     eElement = vcl::PDFWriter::Heading;
                 else if ( nIdentifier == SdrObjKind::OutlineText )
@@ -429,13 +432,22 @@ drawinglayer::primitive2d::Primitive2DContainer const & 
ViewObjectContact::getPr
                         nAnchorId = 
pUserCall->GetPDFAnchorStructureElementId(*pSdrObj);
                     }
 
+                    ::std::vector<sal_Int32> annotIds;
+                    if (eElement == vcl::PDFWriter::Annot)
+                    {
+                        auto const 
pPDFExtOutDevData(GetObjectContact().GetPDFExtOutDevData());
+                        assert(pPDFExtOutDevData);
+                        annotIds = 
pPDFExtOutDevData->GetScreenAnnotIds(pSdrObj);
+                    }
+
                     drawinglayer::primitive2d::Primitive2DReference xReference(
                         new drawinglayer::primitive2d::StructureTagPrimitive2D(
                             eElement,
                             bBackground,
                             bImage,
                             std::move(xNewPrimitiveSequence),
-                            nAnchorId));
+                            nAnchorId,
+                            &annotIds));
                     xNewPrimitiveSequence = 
drawinglayer::primitive2d::Primitive2DContainer { xReference };
                 }
             }
diff --git a/svx/source/sdr/contact/viewobjectcontactofpageobj.cxx 
b/svx/source/sdr/contact/viewobjectcontactofpageobj.cxx
index fb60e6b55965..d66d3852791f 100644
--- a/svx/source/sdr/contact/viewobjectcontactofpageobj.cxx
+++ b/svx/source/sdr/contact/viewobjectcontactofpageobj.cxx
@@ -67,6 +67,8 @@ public:
     virtual bool isOutputToPrinter() const override;
     virtual bool isOutputToRecordingMetaFile() const override;
     virtual bool isOutputToPDFFile() const override;
+    virtual bool isExportTaggedPDF() const override;
+    virtual ::vcl::PDFExtOutDevData const* GetPDFExtOutDevData() const 
override;
     virtual bool isDrawModeGray() const override;
     virtual bool isDrawModeHighContrast() const override;
     virtual SdrPageView* TryToGetSdrPageView() const override;
@@ -177,6 +179,8 @@ void PagePrimitiveExtractor::InvalidatePartOfView(const 
basegfx::B2DRange& rRang
 bool PagePrimitiveExtractor::isOutputToPrinter() const { return 
mrViewObjectContactOfPageObj.GetObjectContact().isOutputToPrinter(); }
 bool PagePrimitiveExtractor::isOutputToRecordingMetaFile() const { return 
mrViewObjectContactOfPageObj.GetObjectContact().isOutputToRecordingMetaFile(); }
 bool PagePrimitiveExtractor::isOutputToPDFFile() const { return 
mrViewObjectContactOfPageObj.GetObjectContact().isOutputToPDFFile(); }
+bool PagePrimitiveExtractor::isExportTaggedPDF() const { return 
mrViewObjectContactOfPageObj.GetObjectContact().isExportTaggedPDF(); }
+::vcl::PDFExtOutDevData const* PagePrimitiveExtractor::GetPDFExtOutDevData() 
const { return 
mrViewObjectContactOfPageObj.GetObjectContact().GetPDFExtOutDevData(); }
 bool PagePrimitiveExtractor::isDrawModeGray() const { return 
mrViewObjectContactOfPageObj.GetObjectContact().isDrawModeGray(); }
 bool PagePrimitiveExtractor::isDrawModeHighContrast() const { return 
mrViewObjectContactOfPageObj.GetObjectContact().isDrawModeHighContrast(); }
 SdrPageView* PagePrimitiveExtractor::TryToGetSdrPageView() const { return 
mrViewObjectContactOfPageObj.GetObjectContact().TryToGetSdrPageView(); }
diff --git a/sw/source/core/text/EnhancedPDFExportHelper.cxx 
b/sw/source/core/text/EnhancedPDFExportHelper.cxx
index 7c518f8b4594..26970499ca40 100644
--- a/sw/source/core/text/EnhancedPDFExportHelper.cxx
+++ b/sw/source/core/text/EnhancedPDFExportHelper.cxx
@@ -2040,7 +2040,7 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport()
                             tools::Rectangle 
aPDFRect(SwRectToPDFRect(pCurrPage, aSnapRect.SVRect()));
                             for (sal_Int32 nScreenPageNum : aScreenPageNums)
                             {
-                                sal_Int32 nScreenId = 
pPDFExtOutDevData->CreateScreen(aPDFRect, altText, nScreenPageNum);
+                                sal_Int32 nScreenId = 
pPDFExtOutDevData->CreateScreen(aPDFRect, altText, nScreenPageNum, pObject);
                                 if 
(aMediaURL.startsWith("vnd.sun.star.Package:"))
                                 {
                                     // Embedded media.
diff --git a/vcl/inc/pdf/pdfwriter_impl.hxx b/vcl/inc/pdf/pdfwriter_impl.hxx
index cc1c23a974a2..bb48f7c08e9f 100644
--- a/vcl/inc/pdf/pdfwriter_impl.hxx
+++ b/vcl/inc/pdf/pdfwriter_impl.hxx
@@ -479,10 +479,12 @@ struct PDFScreen : public PDFAnnotation
     sal_Int32 m_nTempFileObject;
     /// alternative text description
     OUString m_AltText;
+    sal_Int32 m_nStructParent;
 
     PDFScreen(OUString const& rAltText)
         : m_nTempFileObject(0)
         , m_AltText(rAltText)
+        , m_nStructParent(-1)
     {
     }
 };
@@ -586,6 +588,7 @@ struct PDFStructureElement
     std::list< PDFStructureElementKid >                 m_aKids;
     std::map<PDFWriter::StructAttribute, PDFStructureAttribute >
                                                         m_aAttributes;
+    ::std::vector<sal_Int32>                            m_AnnotIds;
     tools::Rectangle                                    m_aBBox;
     OUString                                            m_aActualText;
     OUString                                            m_aAltText;
@@ -687,7 +690,7 @@ class PDFWriterImpl final : public VirtualDevice, public 
PDFObjectContainer
 public:
     friend struct vcl::pdf::PDFPage;
 
-    static const char* getStructureTag( PDFWriter::StructElement );
+    const char* getStructureTag( PDFWriter::StructElement );
     static const char* getAttributeTag( PDFWriter::StructAttribute eAtr );
     static const char* getAttributeValueTag( PDFWriter::StructAttributeValue 
eVal );
 
@@ -986,6 +989,7 @@ i12626
     sal_Int32 emitNamedDestinations();//i56629
     // writes outline dict and tree
     sal_Int32 emitOutline();
+    template<typename T> void AppendAnnotKid(PDFStructureElement& i_rEle, T & 
rAnnot);
     // puts the attribute objects of a structure element into the returned 
string,
     // helper for emitStructure
     OString emitStructureAttributes( PDFStructureElement& rEle );
@@ -1329,6 +1333,7 @@ public:
     bool setStructureAttribute( enum PDFWriter::StructAttribute eAttr, enum 
PDFWriter::StructAttributeValue eVal );
     bool setStructureAttributeNumerical( enum PDFWriter::StructAttribute 
eAttr, sal_Int32 nValue );
     void setStructureBoundingBox( const tools::Rectangle& rRect );
+    void setStructureAnnotIds(::std::vector<sal_Int32> const& rAnnotIds);
     void setActualText( const OUString& rText );
     void setAlternateText( const OUString& rText );
 
diff --git a/vcl/qa/cppunit/pdfexport/data/vid.odt 
b/vcl/qa/cppunit/pdfexport/data/vid.odt
new file mode 100644
index 000000000000..730358e261fa
Binary files /dev/null and b/vcl/qa/cppunit/pdfexport/data/vid.odt differ
diff --git a/vcl/qa/cppunit/pdfexport/pdfexport.cxx 
b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
index d04f3e98f75f..4304846c9834 100644
--- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx
+++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
@@ -3567,6 +3567,136 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf135192)
     CPPUNIT_ASSERT_EQUAL(int(1), nTable);
 }
 
+CPPUNIT_TEST_FIXTURE(PdfExportTest, testMediaShapeAnnot)
+{
+    aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+
+    // Enable PDF/UA
+    uno::Sequence<beans::PropertyValue> aFilterData(
+        comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) 
} }));
+    aMediaDescriptor["FilterData"] <<= aFilterData;
+
+    saveAsPDF(u"vid.odt");
+
+    vcl::filter::PDFDocument aDocument;
+    SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+    CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+    // The document has one page.
+    std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
+
+    auto pAnnots = 
dynamic_cast<vcl::filter::PDFArrayElement*>(aPages[0]->Lookup("Annots"));
+    CPPUNIT_ASSERT(pAnnots);
+
+    // There should be one annotation
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), 
pAnnots->GetElements().size());
+    auto pAnnotReference
+        = 
dynamic_cast<vcl::filter::PDFReferenceElement*>(pAnnots->GetElements()[0]);
+    CPPUNIT_ASSERT(pAnnotReference);
+    // check /Annot - produced by sw
+    vcl::filter::PDFObjectElement* pAnnot = pAnnotReference->LookupObject();
+    CPPUNIT_ASSERT(pAnnot);
+    CPPUNIT_ASSERT_EQUAL(
+        OString("Annot"),
+        
static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"))->GetValue());
+    CPPUNIT_ASSERT_EQUAL(
+        OString("Screen"),
+        
static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"))->GetValue());
+
+    auto pA = 
dynamic_cast<vcl::filter::PDFDictionaryElement*>(pAnnot->Lookup("A"));
+    CPPUNIT_ASSERT(pA);
+    auto pR = 
dynamic_cast<vcl::filter::PDFDictionaryElement*>(pA->LookupElement("R"));
+    CPPUNIT_ASSERT(pR);
+    auto pC = 
dynamic_cast<vcl::filter::PDFDictionaryElement*>(pR->LookupElement("C"));
+    CPPUNIT_ASSERT(pC);
+    auto pAlt = 
dynamic_cast<vcl::filter::PDFArrayElement*>(pC->LookupElement("Alt"));
+    CPPUNIT_ASSERT(pAlt);
+    auto pLang = 
dynamic_cast<vcl::filter::PDFLiteralStringElement*>(pAlt->GetElement(0));
+    CPPUNIT_ASSERT_EQUAL(OString(""), pLang->GetValue());
+    auto pAltText = 
dynamic_cast<vcl::filter::PDFLiteralStringElement*>(pAlt->GetElement(1));
+    CPPUNIT_ASSERT_EQUAL(OString("alternativloser text\\nand some 
description"),
+                         pAltText->GetValue());
+
+    auto pStructParent
+        = 
dynamic_cast<vcl::filter::PDFNumberElement*>(pAnnot->Lookup("StructParent"));
+    CPPUNIT_ASSERT(pStructParent);
+
+    vcl::filter::PDFReferenceElement* pStructElemRef(nullptr);
+
+    // check ParentTree to find StructElem
+    auto nRoots(0);
+    for (const auto& rDocElement : aDocument.GetElements())
+    {
+        auto pObject1 = 
dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
+        if (!pObject1)
+            continue;
+        auto pType1 = 
dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
+        if (pType1 && pType1->GetValue() == "StructTreeRoot")
+        {
+            ++nRoots;
+            auto pParentTree
+                = 
dynamic_cast<vcl::filter::PDFReferenceElement*>(pObject1->Lookup("ParentTree"));
+            CPPUNIT_ASSERT(pParentTree);
+            auto pNumTree = pParentTree->LookupObject();
+            CPPUNIT_ASSERT(pNumTree);
+            auto pNums = 
dynamic_cast<vcl::filter::PDFArrayElement*>(pNumTree->Lookup("Nums"));
+            CPPUNIT_ASSERT(pNums);
+            auto nFound(0);
+            for (size_t i = 0; i < pNums->GetElements().size(); i += 2)
+            {
+                auto pI = 
dynamic_cast<vcl::filter::PDFNumberElement*>(pNums->GetElement(i));
+                if (pI->GetValue() == pStructParent->GetValue())
+                {
+                    ++nFound;
+                    CPPUNIT_ASSERT(i < pNums->GetElements().size() - 1);
+                    pStructElemRef
+                        = 
dynamic_cast<vcl::filter::PDFReferenceElement*>(pNums->GetElement(i + 1));
+                    CPPUNIT_ASSERT(pStructElemRef);
+                }
+            }
+            CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nFound)>(1), nFound);
+        }
+    }
+    CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRoots)>(1), nRoots);
+
+    // check /StructElem - produced by drawinglayer
+    CPPUNIT_ASSERT(pStructElemRef);
+    auto pStructElem(pStructElemRef->LookupObject());
+    CPPUNIT_ASSERT(pStructElem);
+
+    auto pType = 
dynamic_cast<vcl::filter::PDFNameElement*>(pStructElem->Lookup("Type"));
+    CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType->GetValue());
+    auto pS = 
dynamic_cast<vcl::filter::PDFNameElement*>(pStructElem->Lookup("S"));
+    CPPUNIT_ASSERT_EQUAL(OString("Annot"), pS->GetValue());
+    auto pSEAlt = 
dynamic_cast<vcl::filter::PDFHexStringElement*>(pStructElem->Lookup("Alt"));
+    CPPUNIT_ASSERT_EQUAL(OUString("alternativloser text - and some 
description"),
+                         
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pSEAlt));
+    auto pKids = 
dynamic_cast<vcl::filter::PDFArrayElement*>(pStructElem->Lookup("K"));
+    auto nMCID(0);
+    auto nRef(0);
+    for (size_t i = 0; i < pKids->GetElements().size(); ++i)
+    {
+        auto pNum = 
dynamic_cast<vcl::filter::PDFNumberElement*>(pKids->GetElement(i));
+        auto pRef = 
dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids->GetElement(i));
+        if (pNum)
+        {
+            ++nMCID;
+        }
+        if (pRef)
+        {
+            ++nRef;
+            auto pObjR = pRef->LookupObject();
+            auto pOType = 
dynamic_cast<vcl::filter::PDFNameElement*>(pObjR->Lookup("Type"));
+            CPPUNIT_ASSERT_EQUAL(OString("OBJR"), pOType->GetValue());
+            auto pAnnotRef = 
dynamic_cast<vcl::filter::PDFReferenceElement*>(pObjR->Lookup("Obj"));
+            CPPUNIT_ASSERT_EQUAL(pAnnot, pAnnotRef->LookupObject());
+        }
+    }
+    CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID);
+    CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
+}
+
 CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf142129)
 {
     loadFromURL(u"master.odm");
diff --git a/vcl/source/gdi/pdfextoutdevdata.cxx 
b/vcl/source/gdi/pdfextoutdevdata.cxx
index c40e9a73fba3..5dfcc5b19d6e 100644
--- a/vcl/source/gdi/pdfextoutdevdata.cxx
+++ b/vcl/source/gdi/pdfextoutdevdata.cxx
@@ -59,6 +59,7 @@ struct PDFExtOutDevDataSync
                     SetStructureAttribute,
                     SetStructureAttributeNumerical,
                     SetStructureBoundingBox,
+                    SetStructureAnnotIds,
                     SetActualText,
                     SetAlternateText,
                     CreateControl,
@@ -196,6 +197,8 @@ void GlobalSyncData::PlayGlobalActions( PDFWriter& rWriter )
                 rWriter.SetMapMode(mParaMapModes.front());
                 mParaMapModes.pop_front();
                 mParaIds.push_back(rWriter.CreateScreen(mParaRects.front(), 
mParaInts.front(), mParaOUStrings.front()));
+                // resolve AnnotIds structural attribute
+                rWriter.SetLinkPropertyID(mParaIds.back(), 
sal_Int32(mParaIds.size()-1));
                 mParaRects.pop_front();
                 mParaInts.pop_front();
                 mParaOUStrings.pop_front();
@@ -278,6 +281,7 @@ void GlobalSyncData::PlayGlobalActions( PDFWriter& rWriter )
             case PDFExtOutDevDataSync::SetStructureAttribute:
             case PDFExtOutDevDataSync::SetStructureAttributeNumerical:
             case PDFExtOutDevDataSync::SetStructureBoundingBox:
+            case PDFExtOutDevDataSync::SetStructureAnnotIds:
             case PDFExtOutDevDataSync::SetActualText:
             case PDFExtOutDevDataSync::SetAlternateText:
             case PDFExtOutDevDataSync::CreateControl:
@@ -376,6 +380,19 @@ bool PageSyncData::PlaySyncPageAct( PDFWriter& rWriter, 
sal_uInt32& rCurGDIMtfAc
                 mParaRects.pop_front();
             }
             break;
+            case PDFExtOutDevDataSync::SetStructureAnnotIds:
+            {
+                ::std::vector<sal_Int32> annotIds;
+                auto size(mParaInts.front());
+                mParaInts.pop_front();
+                for (auto i = 0; i < size; ++i)
+                {
+                    annotIds.push_back(mParaInts.front());
+                    mParaInts.pop_front();
+                }
+                rWriter.SetStructureAnnotIds(annotIds);
+            }
+            break;
             case PDFExtOutDevDataSync::SetActualText :
             {
                 rWriter.SetActualText( mParaOUStrings.front() );
@@ -676,14 +693,26 @@ sal_Int32 PDFExtOutDevData::CreateLink(const 
tools::Rectangle& rRect, OUString c
     return mpGlobalSyncData->mCurId++;
 }
 
-sal_Int32 PDFExtOutDevData::CreateScreen(const tools::Rectangle& rRect, 
OUString const& rAltText, sal_Int32 nPageNr)
+sal_Int32 PDFExtOutDevData::CreateScreen(const tools::Rectangle& rRect, 
OUString const& rAltText, sal_Int32 nPageNr, SdrObject const*const pObj)
 {
     mpGlobalSyncData->mActions.push_back(PDFExtOutDevDataSync::CreateScreen);
     mpGlobalSyncData->mParaRects.push_back(rRect);
     mpGlobalSyncData->mParaMapModes.push_back(mrOutDev.GetMapMode());
     mpGlobalSyncData->mParaInts.push_back(nPageNr);
     mpGlobalSyncData->mParaOUStrings.push_back(rAltText);
-    return mpGlobalSyncData->mCurId++;
+    auto const ret(mpGlobalSyncData->mCurId++);
+    m_ScreenAnnotations[pObj].push_back(ret);
+    return ret;
+}
+
+::std::vector<sal_Int32> const& PDFExtOutDevData::GetScreenAnnotIds(SdrObject 
const*const pObj) const
+{
+    auto const it(m_ScreenAnnotations.find(pObj));
+    if (it == m_ScreenAnnotations.end())
+    {
+        assert(false); // expected?
+    }
+    return it->second;
 }
 
 void PDFExtOutDevData::SetLinkDest( sal_Int32 nLinkId, sal_Int32 nDestId )
@@ -792,6 +821,17 @@ void PDFExtOutDevData::SetStructureBoundingBox( const 
tools::Rectangle& rRect )
     mpPageSyncData->PushAction( mrOutDev, 
PDFExtOutDevDataSync::SetStructureBoundingBox );
     mpPageSyncData->mParaRects.push_back( rRect );
 }
+
+void PDFExtOutDevData::SetStructureAnnotIds(::std::vector<sal_Int32> const& 
rAnnotIds)
+{
+    mpPageSyncData->PushAction(mrOutDev, 
PDFExtOutDevDataSync::SetStructureAnnotIds);
+    mpPageSyncData->mParaInts.push_back(rAnnotIds.size());
+    for (sal_Int32 const id : rAnnotIds)
+    {
+        mpPageSyncData->mParaInts.push_back(id);
+    }
+}
+
 void PDFExtOutDevData::SetActualText( const OUString& rText )
 {
     mpPageSyncData->PushAction( mrOutDev, PDFExtOutDevDataSync::SetActualText 
);
diff --git a/vcl/source/gdi/pdfwriter.cxx b/vcl/source/gdi/pdfwriter.cxx
index d1399ee0912b..f0314ce0fe82 100644
--- a/vcl/source/gdi/pdfwriter.cxx
+++ b/vcl/source/gdi/pdfwriter.cxx
@@ -419,6 +419,11 @@ void PDFWriter::SetStructureBoundingBox( const 
tools::Rectangle& rRect )
     xImplementation->setStructureBoundingBox( rRect );
 }
 
+void PDFWriter::SetStructureAnnotIds(::std::vector<sal_Int32> const& rAnnotIds)
+{
+    xImplementation->setStructureAnnotIds(rAnnotIds);
+}
+
 void PDFWriter::SetActualText( const OUString& rText )
 {
     xImplementation->setActualText( rText );
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx 
b/vcl/source/gdi/pdfwriter_impl.cxx
index 4ff97a5c0988..f6776c1d560b 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -1964,6 +1964,31 @@ static void appendStructureAttributeLine( 
PDFWriter::StructAttribute i_eAttr, co
     o_rLine.append( "\n" );
 }
 
+template<typename T>
+void PDFWriterImpl::AppendAnnotKid(PDFStructureElement& i_rEle, T & rAnnot)
+{
+    // update struct parent of link
+    OString const aStructParentEntry(OString::number(i_rEle.m_nObject) + " 0 
R");
+    m_aStructParentTree.push_back( aStructParentEntry );
+    rAnnot.m_nStructParent = m_aStructParentTree.size()-1;
+    sal_Int32 const nAnnotObj(rAnnot.m_nObject);
+
+    sal_Int32 const nRefObject = createObject();
+    if (updateObject(nRefObject))
+    {
+        OString aRef =
+            OString::number( nRefObject ) +
+            " 0 obj\n"
+            "<</Type/OBJR/Obj " +
+            OString::number(nAnnotObj) +
+            " 0 R>>\n"
+            "endobj\n\n";
+        writeBuffer( aRef );
+    }
+
+    i_rEle.m_aKids.emplace_back( nRefObject );
+}
+
 OString PDFWriterImpl::emitStructureAttributes( PDFStructureElement& i_rEle )
 {
     // create layout, list and table attribute sets
@@ -1987,27 +2012,7 @@ OString PDFWriterImpl::emitStructureAttributes( 
PDFStructureElement& i_rEle )
                 nLink = link_it->second;
             if( nLink >= 0 && o3tl::make_unsigned(nLink) < m_aLinks.size() )
             {
-                // update struct parent of link
-                OString aStructParentEntry =
-                    OString::number( i_rEle.m_nObject ) +
-                    " 0 R";
-                m_aStructParentTree.push_back( aStructParentEntry );
-                m_aLinks[ nLink ].m_nStructParent = 
m_aStructParentTree.size()-1;
-
-                sal_Int32 nRefObject = createObject();
-                if (updateObject(nRefObject))
-                {
-                    OString aRef =
-                        OString::number( nRefObject ) +
-                        " 0 obj\n"
-                        "<</Type/OBJR/Obj " +
-                        OString::number( m_aLinks[ nLink ].m_nObject ) +
-                        " 0 R>>\n"
-                        "endobj\n\n";
-                    writeBuffer( aRef );
-                }
-
-                i_rEle.m_aKids.emplace_back( nRefObject );
+                AppendAnnotKid(i_rEle, m_aLinks[nLink]);
             }
             else
             {
@@ -2231,6 +2236,17 @@ sal_Int32 PDFWriterImpl::emitStructure( 
PDFStructureElement& rEle )
             aLine.append( "\n" );
         }
     }
+    if (!rEle.m_AnnotIds.empty())
+    {
+        for (auto const id : rEle.m_AnnotIds)
+        {
+            auto const it(m_aLinkPropertyMap.find(id));
+            assert(it != m_aLinkPropertyMap.end());
+
+            assert(0 <= it->second && o3tl::make_unsigned(it->second) < 
m_aScreens.size());
+            AppendAnnotKid(rEle, m_aScreens[it->second]);
+        }
+    }
     if( ! rEle.m_aKids.empty() )
     {
         unsigned int i = 0;
@@ -3621,6 +3637,13 @@ bool PDFWriterImpl::emitScreenAnnotations()
         // End Action dictionary.
         aLine.append("/OP 0 >>");
 
+        if (0 < rScreen.m_nStructParent)
+        {
+            aLine.append("\n/StructParent ");
+            aLine.append(rScreen.m_nStructParent);
+            aLine.append("\n");
+        }
+
         // End Annot dictionary.
         aLine.append("/P ");
         aLine.append(m_aPages[rScreen.m_nPage].m_nPageObject);
@@ -10558,11 +10581,19 @@ const char* PDFWriterImpl::getStructureTag( 
PDFWriter::StructElement eType )
         aTagStrings[ PDFWriter::BibEntry ]      = "BibEntry";
         aTagStrings[ PDFWriter::Code ]          = "Code";
         aTagStrings[ PDFWriter::Link ]          = "Link";
+        aTagStrings[ PDFWriter::Annot ]         = "Annot";
         aTagStrings[ PDFWriter::Figure ]        = "Figure";
         aTagStrings[ PDFWriter::Formula ]       = "Formula";
         aTagStrings[ PDFWriter::Form ]          = "Form";
     }
 
+    if (eType == PDFWriter::Annot
+        && (m_aContext.Version == PDFWriter::PDFVersion::PDF_A_1
+            || m_aContext.Version < PDFWriter::PDFVersion::PDF_1_5))
+    {
+        return "Figure"; // fallback
+    }
+
     std::map< PDFWriter::StructElement, const char* >::const_iterator it = 
aTagStrings.find( eType );
 
     return it != aTagStrings.end() ? it->second : "Div";
@@ -11315,6 +11346,18 @@ void PDFWriterImpl::setStructureBoundingBox( const 
tools::Rectangle& rRect )
     }
 }
 
+void PDFWriterImpl::setStructureAnnotIds(::std::vector<sal_Int32> const& 
rAnnotIds)
+{
+    assert(!(m_nCurrentPage < 0 || m_aPages.size() <= 
o3tl::make_unsigned(m_nCurrentPage)));
+
+    if (!m_aContext.Tagged || m_nCurrentStructElement <= 0 || 
!m_bEmitStructure)
+    {
+        return;
+    }
+
+    m_aStructure[m_nCurrentStructElement].m_AnnotIds = rAnnotIds;
+}
+
 void PDFWriterImpl::setActualText( const OUString& rText )
 {
     if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure )

Reply via email to