drawinglayer/source/primitive2d/controlprimitive2d.cxx | 4 drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx | 20 ++++ include/drawinglayer/primitive2d/controlprimitive2d.hxx | 8 + svx/source/sdr/contact/viewcontactofunocontrol.cxx | 9 + svx/source/sdr/contact/viewobjectcontactofunocontrol.cxx | 10 +- vcl/qa/cppunit/pdfexport/pdfexport.cxx | 63 +++++++++++++ 6 files changed, 109 insertions(+), 5 deletions(-)
New commits: commit d5c19ef9d30cde052e8cd2486ac9395e62d9c9a9 Author: Michael Stahl <michael.st...@allotropia.de> AuthorDate: Wed Oct 11 19:13:53 2023 +0200 Commit: Michael Stahl <michael.st...@allotropia.de> CommitDate: Thu Oct 12 11:54:51 2023 +0200 drawinglayer,svx: PDF/UA export: put Form SE into anchor paragraph SE This is like commit d467f1aa3d028f399826c97e2eecedcd79efcf65 and commit 6e5d59c2ca6969e9491f97cd7a00d094fc62cfb3 but for the form controls, which have their own special ControlPrimitive2D. Change-Id: I4b4ac45e81f490a7b625acd9e8753300d10bf119 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/157847 Tested-by: Jenkins Reviewed-by: Michael Stahl <michael.st...@allotropia.de> diff --git a/drawinglayer/source/primitive2d/controlprimitive2d.cxx b/drawinglayer/source/primitive2d/controlprimitive2d.cxx index 372fb61244a0..c8448efa981f 100644 --- a/drawinglayer/source/primitive2d/controlprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/controlprimitive2d.cxx @@ -243,10 +243,12 @@ namespace drawinglayer::primitive2d uno::Reference< awt::XControlModel > xControlModel, uno::Reference<awt::XControl> xXControl, ::std::u16string_view const rTitle, - ::std::u16string_view const rDescription) + ::std::u16string_view const rDescription, + void const*const pAnchorKey) : maTransform(std::move(aTransform)), mxControlModel(std::move(xControlModel)), mxXControl(std::move(xXControl)) + , m_pAnchorStructureElementKey(pAnchorKey) { ::rtl::OUStringBuffer buf(rTitle); if (!rTitle.empty() && !rDescription.empty()) diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx index d87c70c7e772..084cc0bef17e 100644 --- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx +++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx @@ -1102,6 +1102,18 @@ void VclMetafileProcessor2D::processControlPrimitive2D( if (!bIsPrintableControl) return; + ::std::optional<sal_Int32> oAnchorParent; + if (mpPDFExtOutDevData) + { + if (rControlPrimitive.GetAnchorStructureElementKey()) + { + sal_Int32 const id = mpPDFExtOutDevData->EnsureStructureElement( + rControlPrimitive.GetAnchorStructureElementKey()); + oAnchorParent.emplace(mpPDFExtOutDevData->GetCurrentStructureElement()); + mpPDFExtOutDevData->SetCurrentStructureElement(id); + } + } + const bool bPDFExport(mpPDFExtOutDevData && mpPDFExtOutDevData->GetIsExportFormFields()); bool bDoProcessRecursively(true); @@ -1154,6 +1166,10 @@ void VclMetafileProcessor2D::processControlPrimitive2D( } mpPDFExtOutDevData->CreateControl(*pPDFControl); mpPDFExtOutDevData->EndStructureElement(); + if (oAnchorParent) + { + mpPDFExtOutDevData->SetCurrentStructureElement(*oAnchorParent); + } // no normal paint needed (see original UnoControlPDFExportContact::do_PaintObject); // do not process recursively @@ -1236,6 +1252,10 @@ void VclMetafileProcessor2D::processControlPrimitive2D( if (mpPDFExtOutDevData) { mpPDFExtOutDevData->EndStructureElement(); + if (oAnchorParent) + { + mpPDFExtOutDevData->SetCurrentStructureElement(*oAnchorParent); + } } } diff --git a/include/drawinglayer/primitive2d/controlprimitive2d.hxx b/include/drawinglayer/primitive2d/controlprimitive2d.hxx index e33c26886cd9..a7128b7f7322 100644 --- a/include/drawinglayer/primitive2d/controlprimitive2d.hxx +++ b/include/drawinglayer/primitive2d/controlprimitive2d.hxx @@ -58,6 +58,9 @@ private: /// yet another special snowflake way to generate PDF Alt text OUString m_AltText; + /// anchor structure element (Writer) + void const* const m_pAnchorStructureElementKey; + /** used from getXControl() to create a local awt::XControl which is remembered in mxXControl and from thereon always used and returned by getXControl() */ @@ -81,7 +84,8 @@ public: ControlPrimitive2D(basegfx::B2DHomMatrix aTransform, css::uno::Reference<css::awt::XControlModel> xControlModel, css::uno::Reference<css::awt::XControl> xXControl, - ::std::u16string_view rTitle, ::std::u16string_view rDescription); + ::std::u16string_view rTitle, ::std::u16string_view rDescription, + void const* pAnchorKey); /// data read access const basegfx::B2DHomMatrix& getTransform() const { return maTransform; } @@ -98,6 +102,8 @@ public: OUString const& GetAltText() const { return m_AltText; } + void const* GetAnchorStructureElementKey() const { return m_pAnchorStructureElementKey; } + /// compare operator virtual bool operator==(const BasePrimitive2D& rPrimitive) const override; diff --git a/svx/source/sdr/contact/viewcontactofunocontrol.cxx b/svx/source/sdr/contact/viewcontactofunocontrol.cxx index cc14ae06ad5e..3018551d8175 100644 --- a/svx/source/sdr/contact/viewcontactofunocontrol.cxx +++ b/svx/source/sdr/contact/viewcontactofunocontrol.cxx @@ -106,6 +106,12 @@ namespace sdr::contact { if(xControlModel.is()) { + void const* pAnchorKey(nullptr); + if (auto const pUserCall = GetSdrObject().GetUserCall()) + { + pAnchorKey = pUserCall->GetPDFAnchorStructureElementKey(GetSdrObject()); + } + // create control primitive WITHOUT possibly existing XControl; this would be done in // the VOC in createPrimitive2DSequence() const drawinglayer::primitive2d::Primitive2DReference xRetval( @@ -114,7 +120,8 @@ namespace sdr::contact { xControlModel, nullptr, GetSdrObject().GetTitle(), - GetSdrObject().GetDescription())); + GetSdrObject().GetDescription(), + pAnchorKey)); rVisitor.visit(xRetval); } diff --git a/svx/source/sdr/contact/viewobjectcontactofunocontrol.cxx b/svx/source/sdr/contact/viewobjectcontactofunocontrol.cxx index 27eddc715420..e5292b1515d5 100644 --- a/svx/source/sdr/contact/viewobjectcontactofunocontrol.cxx +++ b/svx/source/sdr/contact/viewobjectcontactofunocontrol.cxx @@ -1564,12 +1564,18 @@ namespace sdr::contact { return; } + SdrObject const& rSdrObj(m_pVOCImpl->getViewContact().GetSdrObject()); + void const* pAnchorKey(nullptr); + if (auto const pUserCall = rSdrObj.GetUserCall()) + { + pAnchorKey = pUserCall->GetPDFAnchorStructureElementKey(rSdrObj); + } + // create a primitive and hand over the existing xControl. This will // allow the primitive to not need to create another one on demand. rContainer.push_back( new ::drawinglayer::primitive2d::ControlPrimitive2D( m_aTransformation, xControlModel, rControl.getControl(), - m_pVOCImpl->getViewContact().GetSdrObject().GetTitle(), - m_pVOCImpl->getViewContact().GetSdrObject().GetDescription()) ); + rSdrObj.GetTitle(), rSdrObj.GetDescription(), pAnchorKey) ); } sal_uInt32 LazyControlCreationPrimitive2D::getPrimitive2DID() const diff --git a/vcl/qa/cppunit/pdfexport/pdfexport.cxx b/vcl/qa/cppunit/pdfexport/pdfexport.cxx index 801a890c6c0b..0cec8d4bdeee 100644 --- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx +++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx @@ -4604,6 +4604,69 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf157397) CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID); CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef); } + + auto pRefKid16 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids1v[6]); + CPPUNIT_ASSERT(pRefKid16); + auto pObject16 = pRefKid16->LookupObject(); + CPPUNIT_ASSERT(pObject16); + auto pType16 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject16->Lookup("Type")); + CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType16->GetValue()); + auto pS16 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject16->Lookup("S")); + CPPUNIT_ASSERT_EQUAL(OString("Text#20body"), pS16->GetValue()); + + auto pKids16 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject16->Lookup("K")); + CPPUNIT_ASSERT(pKids16); + // assume there are no MCID ref at this level + auto pKids16v = pKids16->GetElements(); + auto pRefKid160 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids16v[0]); + CPPUNIT_ASSERT(pRefKid160); + auto pObject160 = pRefKid160->LookupObject(); + CPPUNIT_ASSERT(pObject160); + auto pType160 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject160->Lookup("Type")); + CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType160->GetValue()); + auto pS160 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject160->Lookup("S")); + CPPUNIT_ASSERT_EQUAL(OString("Form"), pS160->GetValue()); + auto pA160 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObject160->Lookup("A")); + CPPUNIT_ASSERT(pA160); + auto pA160Obj = pA160->LookupObject(); + auto pA160O = dynamic_cast<vcl::filter::PDFNameElement*>(pA160Obj->Lookup("O")); + CPPUNIT_ASSERT(pA160O); + CPPUNIT_ASSERT_EQUAL(OString("PrintField"), pA160O->GetValue()); + auto pA160Role = dynamic_cast<vcl::filter::PDFNameElement*>(pA160Obj->Lookup("Role")); + CPPUNIT_ASSERT(pA160Role); + CPPUNIT_ASSERT_EQUAL(OString("tv"), pA160Role->GetValue()); + + { + auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject160->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")); + auto pAnnot = pAnnotRef->LookupObject(); + auto pAType = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type")); + CPPUNIT_ASSERT_EQUAL(OString("Annot"), pAType->GetValue()); + auto pASubtype + = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype")); + CPPUNIT_ASSERT_EQUAL(OString("Widget"), pASubtype->GetValue()); + } + } + CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID); + CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef); + } } CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf135192)