tdf#57423 drawinglayer: PDF/UA export: more Alt texts for SdrObjects

Currently /Alt texts are generated from ObjectInfoPrimitive2D, but this
is only evaluated in VclMetafileProcessor2D::processGraphicPrimitive2D()
so while it's created for every SdrObject, it's ignored in most cases.

There doesn't appear to be a reason why this is done in
processGraphicPrimitive2D() and not a more generic location, the special
conditions that are checked there don't have anything to do with Alt
texts, they effectively guard some optimisation done to bitmaps in
PageSyncData::PlaySyncPageAct().

There is another issue in that not every SdrObject gets a
StructureTagPrimitive2D, and even if there is a StructureTagPrimitive2D
it may be ignored if it's in the background.

The Alt text must only be produced if there is a structure element for
the same SdrObject, else it would end up on some unrelated structure
element.

Fix all this by moving processing to a new function which checks that
there is a current StructureTagPrimitive2D for a SdrObject in effect.

The only problem with this is that previously Writer images produced Alt
text, and now they don't - but Writer needs some fixing anyway.

Change-Id: I2a362e8a9cd93e5bc817c6eed546c46b46e14980
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143189
Tested-by: Jenkins
Reviewed-by: Michael Stahl <michael.stahl@allotropia.de>
(cherry picked from commit 78681cd0829dcb6a73690e1a63ae3808d297677a)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143291
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
diff --git a/drawinglayer/source/primitive2d/structuretagprimitive2d.cxx b/drawinglayer/source/primitive2d/structuretagprimitive2d.cxx
index 01d09d8..7e4de87 100644
--- a/drawinglayer/source/primitive2d/structuretagprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/structuretagprimitive2d.cxx
@@ -59,6 +59,13 @@ namespace drawinglayer::primitive2d
            return PRIMITIVE2D_ID_STRUCTURETAGPRIMITIVE2D;
        }

        bool StructureTagPrimitive2D::isTaggedSdrObject() const
        {
            // note at the moment *all* StructureTagPrimitive2D are created for
            // SdrObjects - if that ever changes, need another condition here
            return !isBackground() || isImage();
        }

} // end of namespace

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
index 1dcc486..1bb8bb9 100644
--- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
@@ -25,6 +25,7 @@
#include <tools/gen.hxx>
#include <tools/stream.hxx>
#include <tools/diagnose_ex.h>
#include <comphelper/flagguard.hxx>
#include <comphelper/processfactory.hxx>
#include <config_global.h>
#include <basegfx/polygon/b2dpolygonclipper.hxx>
@@ -924,7 +925,7 @@ void VclMetafileProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimi
        }
        case PRIMITIVE2D_ID_OBJECTINFOPRIMITIVE2D:
        {
            RenderObjectInfoPrimitive2D(
            processObjectInfoPrimitive2D(
                static_cast<const primitive2d::ObjectInfoPrimitive2D&>(rCandidate));
            break;
        }
@@ -944,6 +945,51 @@ void VclMetafileProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimi
    }
}

void VclMetafileProcessor2D::processObjectInfoPrimitive2D(
    primitive2d::ObjectInfoPrimitive2D const& rObjectInfoPrimitive2D)
{
    // currently StructureTagPrimitive2D is only used for SdrObjects - have to
    // avoid adding Alt text if the SdrObject is not actually tagged, as it
    // would then end up on an unrelated structure element.
    if (mpCurrentStructureTag && mpCurrentStructureTag->isTaggedSdrObject())
    {
        // Create image alternative description from ObjectInfoPrimitive2D info
        // for PDF export, for the currently active SdrObject's structure element
        if (mpPDFExtOutDevData->GetIsExportTaggedPDF())
        {
            OUString aAlternateDescription;

            if (!rObjectInfoPrimitive2D.getTitle().isEmpty())
            {
                aAlternateDescription += rObjectInfoPrimitive2D.getTitle();
            }

            if (!rObjectInfoPrimitive2D.getDesc().isEmpty())
            {
                if (!aAlternateDescription.isEmpty())
                {
                    aAlternateDescription += " - ";
                }

                aAlternateDescription += rObjectInfoPrimitive2D.getDesc();
            }

            // Use SetAlternateText to set it. This will work as long as some
            // structure is used (see PDFWriterImpl::setAlternateText and
            // m_nCurrentStructElement - tagged PDF export works with this in
            // Draw/Impress/Writer, but not in Calc due to too less structure...)
            //Z maybe add structure to Calc PDF export, may need some BeginGroup/EndGroup stuff ..?
            if (!aAlternateDescription.isEmpty())
            {
                mpPDFExtOutDevData->SetAlternateText(aAlternateDescription);
            }
        }
    }

    // process content
    process(rObjectInfoPrimitive2D.getChildren());
}

void VclMetafileProcessor2D::processGraphicPrimitive2D(
    const primitive2d::GraphicPrimitive2D& rGraphicPrimitive)
{
@@ -1035,38 +1081,6 @@ void VclMetafileProcessor2D::processGraphicPrimitive2D(
            sal_Int32(ceil(aCropRange.getMaxX())), sal_Int32(ceil(aCropRange.getMaxY())));
    }

    // Create image alternative description from ObjectInfoPrimitive2D info
    // for PDF export
    if (mpPDFExtOutDevData->GetIsExportTaggedPDF() && nullptr != getObjectInfoPrimitive2D())
    {
        OUString aAlternateDescription;

        if (!getObjectInfoPrimitive2D()->getTitle().isEmpty())
        {
            aAlternateDescription += getObjectInfoPrimitive2D()->getTitle();
        }

        if (!getObjectInfoPrimitive2D()->getDesc().isEmpty())
        {
            if (!aAlternateDescription.isEmpty())
            {
                aAlternateDescription += " - ";
            }

            aAlternateDescription += getObjectInfoPrimitive2D()->getDesc();
        }

        // Use SetAlternateText to set it. This will work as long as some
        // structure is used (see PDFWriterImpl::setAlternateText and
        // m_nCurrentStructElement - tagged PDF export works with this in
        // Draw/Impress/Writer, but not in Calc due to too less structure...)
        //Z maybe add structure to Calc PDF export, may need some BeginGroup/EndGroup stuff ..?
        if (!aAlternateDescription.isEmpty())
        {
            mpPDFExtOutDevData->SetAlternateText(aAlternateDescription);
        }
    }

    // #i123295# 3rd param is uncropped rect, 4th is cropped. The primitive has the cropped
    // object transformation, thus aCurrentRect *is* the clip region while aCropRect is the expanded,
    // uncropped region. Thus, correct order is aCropRect, aCurrentRect
@@ -2344,11 +2358,18 @@ void VclMetafileProcessor2D::processTransparencePrimitive2D(
void VclMetafileProcessor2D::processStructureTagPrimitive2D(
    const primitive2d::StructureTagPrimitive2D& rStructureTagCandidate)
{
    ::comphelper::ValueRestorationGuard const g(mpCurrentStructureTag, &rStructureTagCandidate);

    // structured tag primitive
    const vcl::PDFWriter::StructElement& rTagElement(rStructureTagCandidate.getStructureElement());
    bool bTagUsed((vcl::PDFWriter::NonStructElement != rTagElement));
    sal_Int32 nPreviousElement(-1);

    if (!rStructureTagCandidate.isTaggedSdrObject())
    {
        bTagUsed = false;
    }

    if (mpPDFExtOutDevData && bTagUsed)
    {
        // foreground object: tag as regular structure element
@@ -2421,7 +2442,7 @@ void VclMetafileProcessor2D::processStructureTagPrimitive2D(
                mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::NonStructElement);
            // any other background object: do not tag
            else
                bTagUsed = false;
                assert(false);
        }
    }

diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx
index 06fd61e..6b66a16 100644
--- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx
+++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx
@@ -63,6 +63,7 @@ class PolyPolygonColorPrimitive2D;
class MaskPrimitive2D;
class UnifiedTransparencePrimitive2D;
class TransparencePrimitive2D;
class ObjectInfoPrimitive2D;
class StructureTagPrimitive2D;
}

@@ -142,6 +143,8 @@ private:
        const primitive2d::UnifiedTransparencePrimitive2D& rUniTransparenceCandidate);
    void processTransparencePrimitive2D(
        const primitive2d::TransparencePrimitive2D& rTransparenceCandidate);
    void
    processObjectInfoPrimitive2D(const primitive2d::ObjectInfoPrimitive2D& rObjectInfoPrimitive2D);
    void processStructureTagPrimitive2D(
        const primitive2d::StructureTagPrimitive2D& rStructureTagCandidate);
    void processPrimitive2DOnPixelProcessor(const primitive2d::BasePrimitive2D& rCandidate);
@@ -195,6 +198,8 @@ private:

    std::stack<vcl::PDFWriter::StructElement> maListElements;

    primitive2d::StructureTagPrimitive2D const* mpCurrentStructureTag = nullptr;

protected:
    /*  the local processor for BasePrimitive2D-Implementation based primitives,
        called from the common process()-implementation
diff --git a/drawinglayer/source/processor2d/vclprocessor2d.cxx b/drawinglayer/source/processor2d/vclprocessor2d.cxx
index da34cd5..b77c1d9 100644
--- a/drawinglayer/source/processor2d/vclprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclprocessor2d.cxx
@@ -1262,20 +1262,6 @@ void VclProcessor2D::RenderEpsPrimitive2D(const primitive2d::EpsPrimitive2D& rEp
    }
}

void VclProcessor2D::RenderObjectInfoPrimitive2D(
    const primitive2d::ObjectInfoPrimitive2D& rObjectInfoPrimitive2D)
{
    // remember current ObjectInfoPrimitive2D and set new current one (build a stack - push)
    const primitive2d::ObjectInfoPrimitive2D* pLast(getObjectInfoPrimitive2D());
    mpObjectInfoPrimitive2D = &rObjectInfoPrimitive2D;

    // process content
    process(rObjectInfoPrimitive2D.getChildren());

    // restore current ObjectInfoPrimitive2D (pop)
    mpObjectInfoPrimitive2D = pLast;
}

void VclProcessor2D::RenderSvgLinearAtomPrimitive2D(
    const primitive2d::SvgLinearAtomPrimitive2D& rCandidate)
{
@@ -1482,7 +1468,6 @@ VclProcessor2D::VclProcessor2D(const geometry::ViewInformation2D& rViewInformati
    , mpOutputDevice(&rOutDev)
    , maBColorModifierStack(rInitStack)
    , mnPolygonStrokePrimitive2D(0)
    , mpObjectInfoPrimitive2D(nullptr)
{
    // set digit language, derived from SvtCTLOptions to have the correct
    // number display for arabic/hindi numerals
diff --git a/drawinglayer/source/processor2d/vclprocessor2d.hxx b/drawinglayer/source/processor2d/vclprocessor2d.hxx
index 7e251aa0..1506f48 100644
--- a/drawinglayer/source/processor2d/vclprocessor2d.hxx
+++ b/drawinglayer/source/processor2d/vclprocessor2d.hxx
@@ -46,7 +46,6 @@ class PolygonStrokePrimitive2D;
class ControlPrimitive2D;
class PagePreviewPrimitive2D;
class EpsPrimitive2D;
class ObjectInfoPrimitive2D;
class SvgLinearAtomPrimitive2D;
class SvgRadialAtomPrimitive2D;
}
@@ -76,9 +75,6 @@ protected:
    // PolygonStrokePrimitive2D's decompositions (normally only one)
    sal_uInt32 mnPolygonStrokePrimitive2D;

    // currently used ObjectInfoPrimitive2D
    const primitive2d::ObjectInfoPrimitive2D* mpObjectInfoPrimitive2D;

    // common VCL rendering support
    void RenderTextSimpleOrDecoratedPortionPrimitive2D(
        const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate);
@@ -105,8 +101,6 @@ protected:
    void RenderPolygonStrokePrimitive2D(
        const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokeCandidate);
    void RenderEpsPrimitive2D(const primitive2d::EpsPrimitive2D& rEpsPrimitive2D);
    void
    RenderObjectInfoPrimitive2D(const primitive2d::ObjectInfoPrimitive2D& rObjectInfoPrimitive2D);
    void RenderSvgLinearAtomPrimitive2D(const primitive2d::SvgLinearAtomPrimitive2D& rCandidate);
    void RenderSvgRadialAtomPrimitive2D(const primitive2d::SvgRadialAtomPrimitive2D& rCandidate);

@@ -119,12 +113,6 @@ public:
    VclProcessor2D(const geometry::ViewInformation2D& rViewInformation, OutputDevice& rOutDev,
                   const basegfx::BColorModifierStack& rInitStack = basegfx::BColorModifierStack());
    virtual ~VclProcessor2D() override;

    // access to currently used ObjectInfoPrimitive2D
    const primitive2d::ObjectInfoPrimitive2D* getObjectInfoPrimitive2D() const
    {
        return mpObjectInfoPrimitive2D;
    }
};
} // end of namespace drawinglayer::processor2d

diff --git a/include/drawinglayer/primitive2d/structuretagprimitive2d.hxx b/include/drawinglayer/primitive2d/structuretagprimitive2d.hxx
index bf8a48f..40ad422 100644
--- a/include/drawinglayer/primitive2d/structuretagprimitive2d.hxx
+++ b/include/drawinglayer/primitive2d/structuretagprimitive2d.hxx
@@ -65,6 +65,7 @@ namespace drawinglayer::primitive2d
            const vcl::PDFWriter::StructElement& getStructureElement() const { return maStructureElement; }
            bool isBackground() const { return mbBackground; }
            bool isImage() const { return mbIsImage; }
            bool isTaggedSdrObject() const;
            sal_Int32 GetAnchorStructureElementId() const { return m_nAnchorStructureElementId; }

            /// compare operator