include/svx/sdr/contact/objectcontact.hxx | 1 include/svx/sdr/contact/objectcontactofpageview.hxx | 1 sd/source/ui/unoidl/unomodel.cxx | 68 -------- svx/inc/sdr/contact/objectcontactofobjlistpainter.hxx | 1 svx/source/sdr/contact/objectcontact.cxx | 5 svx/source/sdr/contact/objectcontactofobjlistpainter.cxx | 16 + svx/source/sdr/contact/objectcontactofpageview.cxx | 16 + svx/source/sdr/contact/viewobjectcontact.cxx | 123 ++++++++++----- sw/source/core/text/EnhancedPDFExportHelper.cxx | 2 9 files changed, 130 insertions(+), 103 deletions(-)
New commits: commit 87383b341a6bf515a209ad2e7a2a1289059b781e Author: Armin Le Grand (allotropia) <armin.le.grand.ext...@allotropia.de> AuthorDate: Thu Nov 17 18:57:34 2022 +0100 Commit: Michael Stahl <michael.st...@allotropia.de> CommitDate: Tue Nov 22 13:04:00 2022 +0100 tdf#135638 svx,sd: PDF/UA export: tag SdrObject shapes as Figure etc. Move the code that already exists in sd class ImplRenderPaintProc to ViewObjectContact::getPrimitive2DSequence(), where it is used by all applications, and take into account the caching there, which matters for performance. Change-Id: Iabc00b7c894f042673c7217199236a05a5d67959 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/142901 Tested-by: Jenkins Reviewed-by: Armin Le Grand <armin.le.gr...@me.com> Reviewed-by: Michael Stahl <michael.st...@allotropia.de> diff --git a/include/svx/sdr/contact/objectcontact.hxx b/include/svx/sdr/contact/objectcontact.hxx index 6d5d63a27b1c..3e9b1b76d05d 100644 --- a/include/svx/sdr/contact/objectcontact.hxx +++ b/include/svx/sdr/contact/objectcontact.hxx @@ -137,6 +137,7 @@ public: // pdf export? Default is false virtual bool isOutputToPDFFile() const; + virtual bool isExportTaggedPDF() 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 6ff7761326c1..8d18083b99b8 100644 --- a/include/svx/sdr/contact/objectcontactofpageview.hxx +++ b/include/svx/sdr/contact/objectcontactofpageview.hxx @@ -92,6 +92,7 @@ namespace sdr::contact // pdf export? Default is false virtual bool isOutputToPDFFile() const override; + virtual bool isExportTaggedPDF() const override; // gray display mode virtual bool isDrawModeGray() const override; diff --git a/sd/source/ui/unoidl/unomodel.cxx b/sd/source/ui/unoidl/unomodel.cxx index 36688e31640b..2deb5d296c22 100644 --- a/sd/source/ui/unoidl/unomodel.cxx +++ b/sd/source/ui/unoidl/unomodel.cxx @@ -1534,15 +1534,12 @@ class ImplRenderPaintProc : public sdr::contact::ViewObjectContactRedirector { const SdrLayerAdmin& rLayerAdmin; SdrPageView* pSdrPageView; - vcl::PDFExtOutDevData* pPDFExtOutDevData; - - vcl::PDFWriter::StructElement ImplBegStructureTag( const SdrObject& rObject ); public: bool IsVisible ( const SdrObject* pObj ) const; bool IsPrintable( const SdrObject* pObj ) const; - ImplRenderPaintProc( const SdrLayerAdmin& rLA, SdrPageView* pView, vcl::PDFExtOutDevData* pData ); + ImplRenderPaintProc(const SdrLayerAdmin& rLA, SdrPageView* pView); // all default implementations just call the same methods at the original. To do something // different, override the method and at least do what the method does. @@ -1554,10 +1551,9 @@ public: } -ImplRenderPaintProc::ImplRenderPaintProc( const SdrLayerAdmin& rLA, SdrPageView* pView, vcl::PDFExtOutDevData* pData ) -: rLayerAdmin ( rLA ), - pSdrPageView ( pView ), - pPDFExtOutDevData ( pData ) +ImplRenderPaintProc::ImplRenderPaintProc(const SdrLayerAdmin& rLA, SdrPageView *const pView) + : rLayerAdmin(rLA) + , pSdrPageView(pView) { } @@ -1755,32 +1751,6 @@ static void ImplPDFExportShapeInteraction( const uno::Reference< drawing::XShape } } -vcl::PDFWriter::StructElement ImplRenderPaintProc::ImplBegStructureTag( const SdrObject& rObject ) -{ - vcl::PDFWriter::StructElement eElement(vcl::PDFWriter::NonStructElement); - - if ( pPDFExtOutDevData && pPDFExtOutDevData->GetIsExportTaggedPDF() ) - { - SdrInventor nInventor = rObject.GetObjInventor(); - SdrObjKind nIdentifier = rObject.GetObjIdentifier(); - bool bIsTextObj = DynCastSdrTextObj( &rObject ) != nullptr; - - if ( nInventor == SdrInventor::Default ) - { - if ( nIdentifier == SdrObjKind::Group ) - eElement = vcl::PDFWriter::Section; - else if ( nIdentifier == SdrObjKind::TitleText ) - eElement = vcl::PDFWriter::Heading; - else if ( nIdentifier == SdrObjKind::OutlineText ) - eElement = vcl::PDFWriter::Division; - else if ( !bIsTextObj || !static_cast<const SdrTextObj&>(rObject).HasText() ) - eElement = vcl::PDFWriter::Figure; - } - } - - return eElement; -} - void ImplRenderPaintProc::createRedirectedPrimitive2DSequence( const sdr::contact::ViewObjectContact& rOriginal, const sdr::contact::DisplayInfo& rDisplayInfo, @@ -1801,31 +1771,7 @@ void ImplRenderPaintProc::createRedirectedPrimitive2DSequence( if(!IsVisible(pObject) || !IsPrintable(pObject)) return; - const vcl::PDFWriter::StructElement eElement(ImplBegStructureTag( *pObject )); - const bool bTagUsed(vcl::PDFWriter::NonStructElement != eElement); - - drawinglayer::primitive2d::Primitive2DContainer xRetval; - sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(rOriginal, rDisplayInfo, xRetval); - - if(!xRetval.empty() && bTagUsed) - { - // embed Primitive2DSequence in a structure tag element for - // exactly this purpose (StructureTagPrimitive2D) - - const bool bBackground(pSdrPage->IsMasterPage()); - const bool bImage(pObject->GetObjIdentifier() == SdrObjKind::Graphic); - - drawinglayer::primitive2d::Primitive2DReference xReference( - new drawinglayer::primitive2d::StructureTagPrimitive2D( - eElement, - bBackground, - bImage, - std::move(xRetval))); - - xRetval = drawinglayer::primitive2d::Primitive2DContainer { xReference }; - } - - rVisitor.visit(xRetval); + sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(rOriginal, rDisplayInfo, rVisitor); } bool ImplRenderPaintProc::IsVisible( const SdrObject* pObj ) const @@ -1959,7 +1905,7 @@ void SAL_CALL SdXImpressDocument::render( sal_Int32 nRenderer, const uno::Any& r } ImplRenderPaintProc aImplRenderPaintProc( mpDoc->GetLayerAdmin(), - pPV, pPDFExtOutDevData ); + pPV); // background color for outliner :o SdPage* pPage = pPV ? static_cast<SdPage*>(pPV->GetPage()) : nullptr; @@ -2192,7 +2138,7 @@ void SAL_CALL SdXImpressDocument::render( sal_Int32 nRenderer, const uno::Any& r SdrPageView* pPV = nullptr; ImplRenderPaintProc aImplRenderPaintProc( mpDoc->GetLayerAdmin(), - pOldSdView ? pOldSdView->GetSdrPageView() : nullptr, pPDFExtOutDevData ); + pOldSdView ? pOldSdView->GetSdrPageView() : nullptr); for( sal_uInt32 i = 0, nCount = xShapes->getCount(); i < nCount; i++ ) { diff --git a/svx/inc/sdr/contact/objectcontactofobjlistpainter.hxx b/svx/inc/sdr/contact/objectcontactofobjlistpainter.hxx index fcfe510845b7..af72593adef2 100644 --- a/svx/inc/sdr/contact/objectcontactofobjlistpainter.hxx +++ b/svx/inc/sdr/contact/objectcontactofobjlistpainter.hxx @@ -77,6 +77,7 @@ public: // pdf export? Default is false virtual bool isOutputToPDFFile() const override; + virtual bool isExportTaggedPDF() const override; virtual OutputDevice* TryToGetOutputDevice() const override; }; diff --git a/svx/source/sdr/contact/objectcontact.cxx b/svx/source/sdr/contact/objectcontact.cxx index c76288d6cac6..d135a2a29336 100644 --- a/svx/source/sdr/contact/objectcontact.cxx +++ b/svx/source/sdr/contact/objectcontact.cxx @@ -171,6 +171,11 @@ bool ObjectContact::isOutputToPDFFile() const return false; } +bool ObjectContact::isExportTaggedPDF() const +{ + return false; +} + // gray display mode bool ObjectContact::isDrawModeGray() const { diff --git a/svx/source/sdr/contact/objectcontactofobjlistpainter.cxx b/svx/source/sdr/contact/objectcontactofobjlistpainter.cxx index cef1cbc1f289..b4727ce30b12 100644 --- a/svx/source/sdr/contact/objectcontactofobjlistpainter.cxx +++ b/svx/source/sdr/contact/objectcontactofobjlistpainter.cxx @@ -29,6 +29,7 @@ #include <svx/unoapi.hxx> #include <tools/debug.hxx> #include <vcl/gdimtf.hxx> +#include <vcl/pdfextoutdevdata.hxx> #include <memory> namespace sdr::contact { @@ -136,6 +137,21 @@ bool ObjectContactOfObjListPainter::isOutputToPDFFile() const return OUTDEV_PDF == mrTargetOutputDevice.GetOutDevType(); } +bool ObjectContactOfObjListPainter::isExportTaggedPDF() const +{ + if (isOutputToPDFFile()) + { + vcl::PDFExtOutDevData* pPDFExtOutDevData(dynamic_cast<vcl::PDFExtOutDevData*>( + mrTargetOutputDevice.GetExtOutDevData())); + + if (nullptr != pPDFExtOutDevData) + { + return pPDFExtOutDevData->GetIsExportTaggedPDF(); + } + } + return false; +} + OutputDevice* ObjectContactOfObjListPainter::TryToGetOutputDevice() const { return &mrTargetOutputDevice; diff --git a/svx/source/sdr/contact/objectcontactofpageview.cxx b/svx/source/sdr/contact/objectcontactofpageview.cxx index 9d0918a5d22a..e4cec99e5e53 100644 --- a/svx/source/sdr/contact/objectcontactofpageview.cxx +++ b/svx/source/sdr/contact/objectcontactofpageview.cxx @@ -37,6 +37,7 @@ #include <svx/unoapi.hxx> #include <unotools/configmgr.hxx> #include <vcl/canvastools.hxx> +#include <vcl/pdfextoutdevdata.hxx> #include <comphelper/lok.hxx> #include <memory> @@ -372,6 +373,21 @@ namespace sdr::contact return OUTDEV_PDF == mrPageWindow.GetPaintWindow().GetOutputDevice().GetOutDevType(); } + bool ObjectContactOfPageView::isExportTaggedPDF() const + { + if (isOutputToPDFFile()) + { + vcl::PDFExtOutDevData* pPDFExtOutDevData(dynamic_cast<vcl::PDFExtOutDevData*>( + mrPageWindow.GetPaintWindow().GetOutputDevice().GetExtOutDevData())); + + if (nullptr != pPDFExtOutDevData) + { + return pPDFExtOutDevData->GetIsExportTaggedPDF(); + } + } + return false; + } + // gray display mode bool ObjectContactOfPageView::isDrawModeGray() const { diff --git a/svx/source/sdr/contact/viewobjectcontact.cxx b/svx/source/sdr/contact/viewobjectcontact.cxx index 9adc4e713b22..15dea8262a5b 100644 --- a/svx/source/sdr/contact/viewobjectcontact.cxx +++ b/svx/source/sdr/contact/viewobjectcontact.cxx @@ -29,8 +29,12 @@ #include <drawinglayer/processor2d/baseprocessor2d.hxx> #include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> #include <drawinglayer/primitive2d/transformprimitive2d.hxx> +#include <drawinglayer/primitive2d/structuretagprimitive2d.hxx> #include <svx/svdobj.hxx> #include <svx/svdmodel.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdotext.hxx> +#include <vcl/pdfwriter.hxx> using namespace com::sun::star; @@ -338,19 +342,15 @@ void ViewObjectContact::createPrimitive2DSequence(const DisplayInfo& rDisplayInf drawinglayer::primitive2d::Primitive2DContainer const & ViewObjectContact::getPrimitive2DSequence(const DisplayInfo& rDisplayInfo) const { // only some of the top-level apps are any good at reliably invalidating us (e.g. writer is not) - if (SdrObject* pSdrObj = mrViewContact.TryToGetSdrObject()) - if (pSdrObj->getSdrModelFromSdrObject().IsVOCInvalidationIsReliable()) - { - if (!mxPrimitive2DSequence.empty()) - return mxPrimitive2DSequence; - } + SdrObject* pSdrObj(mrViewContact.TryToGetSdrObject()); - /** - This method is weird because - (1) we have to re-walk the primitive tree because the flushing is unreliable - (2) we cannot just always use the new data because the old data has cached bitmaps in it e.g. see the documents in tdf#104878 - */ + if (nullptr != pSdrObj && pSdrObj->getSdrModelFromSdrObject().IsVOCInvalidationIsReliable()) + { + if (!mxPrimitive2DSequence.empty()) + return mxPrimitive2DSequence; + } + // prepare new representation drawinglayer::primitive2d::Primitive2DContainer xNewPrimitiveSequence; // take care of redirectors and create new list @@ -365,22 +365,8 @@ drawinglayer::primitive2d::Primitive2DContainer const & ViewObjectContact::getPr createPrimitive2DSequence(rDisplayInfo, xNewPrimitiveSequence); } - // local up-to-date checks. New list different from local one? - if(mxPrimitive2DSequence == xNewPrimitiveSequence) - return mxPrimitive2DSequence; - - // has changed, copy content - const_cast< ViewObjectContact* >(this)->mxPrimitive2DSequence = std::move(xNewPrimitiveSequence); - - // check for animated stuff - const_cast< ViewObjectContact* >(this)->checkForPrimitive2DAnimations(); - - // always update object range when PrimitiveSequence changes - const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D()); - const_cast< ViewObjectContact* >(this)->maObjectRange = mxPrimitive2DSequence.getB2DRange(rViewInformation2D); - - // check and eventually embed to GridOffset transform primitive - if(GetObjectContact().supportsGridOffsets()) + // check and eventually embed to GridOffset transform primitive (calc only) + if(!xNewPrimitiveSequence.empty() && GetObjectContact().supportsGridOffsets()) { const basegfx::B2DVector& rGridOffset(getGridOffset()); @@ -390,22 +376,77 @@ drawinglayer::primitive2d::Primitive2DContainer const & ViewObjectContact::getPr basegfx::utils::createTranslateB2DHomMatrix( rGridOffset)); drawinglayer::primitive2d::Primitive2DReference aEmbed( - new drawinglayer::primitive2d::TransformPrimitive2D( + new drawinglayer::primitive2d::TransformPrimitive2D( aTranslateGridOffset, - std::move(const_cast< ViewObjectContact* >(this)->mxPrimitive2DSequence))); - - // Set values at local data. So for now, the mechanism is to reset some of the - // defining things (mxPrimitive2DSequence, maGridOffset) and re-create the - // buffered data (including maObjectRange). It *could* be changed to keep - // the unmodified PrimitiveSequence and only update the GridOffset, but this - // would require a 2nd instance of maObjectRange and mxPrimitive2DSequence. I - // started doing so, but it just makes the code more complicated. For now, - // just allow re-creation of the PrimitiveSequence (and removing buffered - // decomposed content of it). May be optimized, though. OTOH it only happens - // in calc which traditionally does not have a huge amount of DrawObjects anyways. - const_cast< ViewObjectContact* >(this)->mxPrimitive2DSequence = drawinglayer::primitive2d::Primitive2DContainer { aEmbed }; - const_cast< ViewObjectContact* >(this)->maObjectRange.transform(aTranslateGridOffset); + std::move(xNewPrimitiveSequence))); + xNewPrimitiveSequence = drawinglayer::primitive2d::Primitive2DContainer { aEmbed }; + } + } + + // Check if we need to embed to a StructureTagPrimitive2D, too. This + // was done at ImplRenderPaintProc::createRedirectedPrimitive2DSequence before + if(!xNewPrimitiveSequence.empty() && nullptr != pSdrObj && GetObjectContact().isExportTaggedPDF()) + { + vcl::PDFWriter::StructElement eElement(vcl::PDFWriter::NonStructElement); + const SdrInventor nInventor(pSdrObj->GetObjInventor()); + const SdrObjKind nIdentifier(pSdrObj->GetObjIdentifier()); + const bool bIsTextObj(nullptr != DynCastSdrTextObj(pSdrObj)); + + if ( nInventor == SdrInventor::Default ) + { + if ( nIdentifier == SdrObjKind::Group ) + eElement = vcl::PDFWriter::Section; + else if ( nIdentifier == SdrObjKind::TitleText ) + eElement = vcl::PDFWriter::Heading; + else if ( nIdentifier == SdrObjKind::OutlineText ) + eElement = vcl::PDFWriter::Division; + else if ( !bIsTextObj || !static_cast<const SdrTextObj&>(*pSdrObj).HasText() ) + eElement = vcl::PDFWriter::Figure; } + + if(vcl::PDFWriter::NonStructElement != eElement) + { + SdrPage* pSdrPage(pSdrObj->getSdrPageFromSdrObject()); + + if(pSdrPage) + { + const bool bBackground(pSdrPage->IsMasterPage()); + const bool bImage(SdrObjKind::Graphic == pSdrObj->GetObjIdentifier()); + + drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::StructureTagPrimitive2D( + eElement, + bBackground, + bImage, + std::move(xNewPrimitiveSequence))); + xNewPrimitiveSequence = drawinglayer::primitive2d::Primitive2DContainer { xReference }; + } + } + } + + // Local up-to-date checks. New list different from local one? + // This is the important point where it gets decided if the current or the new + // representation gets used. This is important for performance, since the + // current representation contains possible precious decompositions. That + // comparisons triggers exactly if something in the object visualization + // has changed. + // Note: That is the main reason for BasePrimitive2D::operator== at all. I + // have alternatively tried to invalidate the local representation on object + // change, but that is simply not reliable. + // Note2: I did that once in aw080, the lost CWS, and it worked well enough + // so that I could remove *all* operator== from all derivations of + // BasePrimitive2D, so it can be done again (with the needed ressources) + if(mxPrimitive2DSequence != xNewPrimitiveSequence) + { + // has changed, copy content + const_cast< ViewObjectContact* >(this)->mxPrimitive2DSequence = std::move(xNewPrimitiveSequence); + + // check for animated stuff + const_cast< ViewObjectContact* >(this)->checkForPrimitive2DAnimations(); + + // always update object range when PrimitiveSequence changes + const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D()); + const_cast< ViewObjectContact* >(this)->maObjectRange = mxPrimitive2DSequence.getB2DRange(rViewInformation2D); } // return current Primitive2DContainer diff --git a/sw/source/core/text/EnhancedPDFExportHelper.cxx b/sw/source/core/text/EnhancedPDFExportHelper.cxx index 0f5f13c05966..8ed9392bdb48 100644 --- a/sw/source/core/text/EnhancedPDFExportHelper.cxx +++ b/sw/source/core/text/EnhancedPDFExportHelper.cxx @@ -661,7 +661,7 @@ void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType ) } } - // Formally here bAlternateText was triggered for PDF export, but this + // Formerly here bAlternateText was triggered for PDF export, but this // was moved for more general use to primitives and usage in // VclMetafileProcessor2D (see processGraphicPrimitive2D).