tdf#148966: pptx: workaround for multiline fields followed by linebreaks

In Impress after fields that span multiple lines, a
linebreak is already forced. (PowerPoint doesn't have such
behaviour)

Therefore if the imported pptx file has a line break after
the multiline field - Impress ends up displaying an extra
line break.

This patch implements ignoring of a linebreak that follows
after a multiline field during paint (when not in EditMode),
using a compatibility flag. (IgnoreBreakAfterMultilineField)

Change-Id: I1e6772424cc0eead06b53d104b06820038a81ea1
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/147408
Tested-by: Jenkins
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
diff --git a/editeng/source/outliner/outleeng.hxx b/editeng/inc/outleeng.hxx
similarity index 95%
rename from editeng/source/outliner/outleeng.hxx
rename to editeng/inc/outleeng.hxx
index d19b54e..449b5ca 100644
--- a/editeng/source/outliner/outleeng.hxx
+++ b/editeng/inc/outleeng.hxx
@@ -21,6 +21,8 @@
#include <editeng/outliner.hxx>
#include <editeng/editeng.hxx>

enum class SdrCompatibilityFlag;

typedef std::vector<EENotify> NotifyList;

class OutlinerEditEng : public EditEngine
@@ -75,6 +77,9 @@ public:

    virtual tools::Rectangle   GetBulletArea( sal_Int32 nPara ) override;

    /// @returns state of the SdrCompatibilityFlag
    std::optional<bool> GetCompatFlag(SdrCompatibilityFlag eFlag) const;

       virtual void        SetParaAttribs( sal_Int32 nPara, const SfxItemSet& rSet ) override;

    // belongs into class Outliner, move there before incompatible update!
diff --git a/editeng/source/editeng/impedit3.cxx b/editeng/source/editeng/impedit3.cxx
index 3191b7a..376d1df 100644
--- a/editeng/source/editeng/impedit3.cxx
+++ b/editeng/source/editeng/impedit3.cxx
@@ -24,6 +24,7 @@
#include <vcl/settings.hxx>
#include <vcl/window.hxx>

#include <editeng/outliner.hxx>
#include <editeng/tstpitem.hxx>
#include <editeng/lspcitem.hxx>
#include <editeng/flditem.hxx>
@@ -44,11 +45,14 @@
#include <editeng/scriptspaceitem.hxx>
#include <editeng/charscaleitem.hxx>
#include <editeng/numitem.hxx>
#include <outleeng.hxx>

#include <svtools/colorcfg.hxx>
#include <svl/ctloptions.hxx>
#include <svl/asiancfg.hxx>

#include <svx/compatflags.hxx>

#include <editeng/hngpnctitem.hxx>
#include <editeng/forbiddencharacterstable.hxx>

@@ -3606,6 +3610,22 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, tools::Rectangle aClipRect, Po
                                            nTextStart = *curIt;
                                            nTextLen = nTextLen - nTextStart;
                                            bParsingFields = false;

                                            if (nLine + 1 < nLines)
                                            {
                                                // tdf#148966 don't paint the line break following a
                                                // multiline field based on a compat flag
                                                OutlinerEditEng* pOutlEditEng{ dynamic_cast<OutlinerEditEng*>(pEditEngine) };
                                                if (pOutlEditEng
                                                    && pOutlEditEng->GetCompatFlag(SdrCompatibilityFlag::IgnoreBreakAfterMultilineField)
                                                           .value_or(false))
                                                {
                                                    int nStartNextLine = pPortion->GetLines()[nLine + 1].GetStartPortion();
                                                    const TextPortion& rNextTextPortion = pPortion->GetTextPortions()[nStartNextLine];
                                                    if (rNextTextPortion.GetKind() == PortionKind::LINEBREAK)
                                                        ++nLine; //ignore the following linebreak
                                                }
                                            }
                                        }
                                    }

diff --git a/editeng/source/outliner/outleeng.cxx b/editeng/source/outliner/outleeng.cxx
index 275636b..0a7f8ae 100644
--- a/editeng/source/outliner/outleeng.cxx
+++ b/editeng/source/outliner/outleeng.cxx
@@ -21,9 +21,10 @@
#include <editeng/eerdll.hxx>

#include <editeng/outliner.hxx>
#include "outleeng.hxx"
#include <outleeng.hxx>
#include "paralist.hxx"
#include <editeng/editrids.hrc>
#include <optional>
#include <svl/itemset.hxx>
#include <editeng/editstat.hxx>
#include "outlundo.hxx"
@@ -69,6 +70,15 @@ tools::Rectangle OutlinerEditEng::GetBulletArea( sal_Int32 nPara )
    return aBulletArea;
}

std::optional<bool> OutlinerEditEng::GetCompatFlag(SdrCompatibilityFlag eFlag) const
{
    if(pOwner)
    {
        return pOwner->GetCompatFlag(eFlag);
    }
    return {};
}

void OutlinerEditEng::ParagraphInserted( sal_Int32 nNewParagraph )
{
    pOwner->ParagraphInserted( nNewParagraph );
diff --git a/editeng/source/outliner/outlin2.cxx b/editeng/source/outliner/outlin2.cxx
index 68b1d0f..013c680 100644
--- a/editeng/source/outliner/outlin2.cxx
+++ b/editeng/source/outliner/outlin2.cxx
@@ -30,7 +30,7 @@

#include <editeng/outliner.hxx>
#include "paralist.hxx"
#include "outleeng.hxx"
#include <outleeng.hxx>
#include <editeng/editstat.hxx>


diff --git a/editeng/source/outliner/outliner.cxx b/editeng/source/outliner/outliner.cxx
index 84097ae..fa0e795 100644
--- a/editeng/source/outliner/outliner.cxx
+++ b/editeng/source/outliner/outliner.cxx
@@ -30,7 +30,7 @@
#include <editeng/outliner.hxx>
#include "paralist.hxx"
#include <editeng/outlobj.hxx>
#include "outleeng.hxx"
#include <outleeng.hxx>
#include "outlundo.hxx"
#include <editeng/eeitem.hxx>
#include <editeng/editstat.hxx>
diff --git a/editeng/source/outliner/outlvw.cxx b/editeng/source/outliner/outlvw.cxx
index d5f426a..705b5e1 100644
--- a/editeng/source/outliner/outlvw.cxx
+++ b/editeng/source/outliner/outlvw.cxx
@@ -30,7 +30,7 @@
#include <i18nlangtag/languagetag.hxx>

#include <editeng/outliner.hxx>
#include "outleeng.hxx"
#include <outleeng.hxx>
#include "paralist.hxx"
#include "outlundo.hxx"
#include <editeng/outlobj.hxx>
diff --git a/include/editeng/outliner.hxx b/include/editeng/outliner.hxx
index aa2cad3..bee57d1 100644
--- a/include/editeng/outliner.hxx
+++ b/include/editeng/outliner.hxx
@@ -77,6 +77,7 @@ class SvxFieldData;
enum class PointerStyle;
class SvxNumRule;
enum class TextRotation;
enum class SdrCompatibilityFlag;

namespace com::sun::star::linguistic2 {
    class XSpellChecker1;
@@ -987,6 +988,9 @@ public:

    // convenient method to determine the bullets/numbering status for all paragraphs
    sal_Int32 GetBulletsNumberingStatus() const;

    // overriden in SdrOutliner
    virtual std::optional<bool> GetCompatFlag(SdrCompatibilityFlag /*eFlag*/) const { return {}; };
};

#endif
diff --git a/include/svx/compatflags.hxx b/include/svx/compatflags.hxx
index 6a53c75..f7d021f 100644
--- a/include/svx/compatflags.hxx
+++ b/include/svx/compatflags.hxx
@@ -13,6 +13,7 @@ enum class SdrCompatibilityFlag
    AnchoredTextOverflowLegacy, ///< for tdf#99729
    LegacySingleLineFontwork, ///< for tdf#148000
    ConnectorUseSnapRect, ///< for tdf#149756
    IgnoreBreakAfterMultilineField, ///< for tdf#148966
};

/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/include/svx/svdoutl.hxx b/include/svx/svdoutl.hxx
index b9768b7..2a78fd6 100644
--- a/include/svx/svdoutl.hxx
+++ b/include/svx/svdoutl.hxx
@@ -20,11 +20,13 @@
#pragma once

#include <editeng/outliner.hxx>
#include <optional>
#include <svx/svxdllapi.h>
#include <unotools/weakref.hxx>

class SdrTextObj;
class SdrPage;
enum class SdrCompatibilityFlag;

class SVXCORE_DLLPUBLIC SdrOutliner : public Outliner
{
@@ -45,6 +47,9 @@ public:
    virtual OUString CalcFieldValue(const SvxFieldItem& rField, sal_Int32 nPara, sal_Int32 nPos, std::optional<Color>& rpTxtColor, std::optional<Color>& rpFldColor) override;

    bool hasEditViewCallbacks() const;

    /// @returns state of the SdrCompatibilityFlag
    virtual std::optional<bool> GetCompatFlag(SdrCompatibilityFlag eFlag) const override;
};


diff --git a/sd/qa/unit/data/odp/tdf148966-withflag.odp b/sd/qa/unit/data/odp/tdf148966-withflag.odp
new file mode 100644
index 0000000..7ba89b1
--- /dev/null
+++ b/sd/qa/unit/data/odp/tdf148966-withflag.odp
Binary files differ
diff --git a/sd/qa/unit/data/odp/tdf148966-withoutflag.odp b/sd/qa/unit/data/odp/tdf148966-withoutflag.odp
new file mode 100644
index 0000000..fdc2c25
--- /dev/null
+++ b/sd/qa/unit/data/odp/tdf148966-withoutflag.odp
Binary files differ
diff --git a/sd/qa/unit/data/pptx/tdf148966.pptx b/sd/qa/unit/data/pptx/tdf148966.pptx
new file mode 100644
index 0000000..d6c77f91
--- /dev/null
+++ b/sd/qa/unit/data/pptx/tdf148966.pptx
Binary files differ
diff --git a/sd/qa/unit/layout-tests.cxx b/sd/qa/unit/layout-tests.cxx
index 36293f2..e401f2d 100644
--- a/sd/qa/unit/layout-tests.cxx
+++ b/sd/qa/unit/layout-tests.cxx
@@ -314,6 +314,32 @@ CPPUNIT_TEST_FIXTURE(SdLayoutTest, testFitToFrameTextFitting)
#endif
}

CPPUNIT_TEST_FIXTURE(SdLayoutTest, testTdf148966)
{
    // Test related to IgnoreBreakAfterMultilineField compatibility flag.
    {
        xmlDocUniquePtr pXmlDoc = load("pptx/tdf148966.pptx");
        // Without the accompanying fix, would fail with:
        // - Expected: 5952
        // - Actual  : 7814
        // i.e. Line break after multiline field should have been ignored.
        assertXPath(pXmlDoc, "/metafile/push[1]/push[1]/textarray[3]", "y", "5952");
    }
    {
        xmlDocUniquePtr pXmlDoc = load("odp/tdf148966-withflag.odp");
        // Without the accompanying fix, would fail with:
        // - Expected: 5952
        // - Actual  : 7814
        // i.e. When IgnoreBreakAfterMultilineField flag is set, line break
        // after multiline field should have been ignored.
        assertXPath(pXmlDoc, "/metafile/push[1]/push[1]/textarray[3]", "y", "5952");
    }
    {
        xmlDocUniquePtr pXmlDoc = load("odp/tdf148966-withoutflag.odp");
        assertXPath(pXmlDoc, "/metafile/push[1]/push[1]/textarray[3]", "y", "7814");
    }
}

CPPUNIT_PLUGIN_IMPLEMENT();

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/source/ui/docshell/docshel4.cxx b/sd/source/ui/docshell/docshel4.cxx
index 058e57e..aeee470 100644
--- a/sd/source/ui/docshell/docshel4.cxx
+++ b/sd/source/ui/docshell/docshel4.cxx
@@ -409,6 +409,9 @@ bool DrawDocShell::ImportFrom(SfxMedium &rMedium,
        // The Libreoffice uses bounding rectangle of connected shapes but
        // MSO uses snap rectangle when calculate the edge track.
        mpDoc->SetCompatibilityFlag(SdrCompatibilityFlag::ConnectorUseSnapRect, true);

        // compatibility flag for tdf#148966
        mpDoc->SetCompatibilityFlag(SdrCompatibilityFlag::IgnoreBreakAfterMultilineField, true);
    }

    if (aFilterName == "Impress MS PowerPoint 2007 XML" ||
diff --git a/solenv/clang-format/excludelist b/solenv/clang-format/excludelist
index d9592f9..a26ac14 100644
--- a/solenv/clang-format/excludelist
+++ b/solenv/clang-format/excludelist
@@ -3377,6 +3377,7 @@ editeng/inc/editattr.hxx
editeng/inc/editdoc.hxx
editeng/inc/edtspell.hxx
editeng/inc/eerdll2.hxx
editeng/inc/outleeng.hxx
editeng/inc/unomodel.hxx
editeng/qa/items/borderline_test.cxx
editeng/qa/lookuptree/lookuptree_test.cxx
@@ -3455,7 +3456,6 @@ editeng/source/misc/swafopt.cxx
editeng/source/misc/txtrange.cxx
editeng/source/misc/unolingu.cxx
editeng/source/outliner/outleeng.cxx
editeng/source/outliner/outleeng.hxx
editeng/source/outliner/outlin2.cxx
editeng/source/outliner/outliner.cxx
editeng/source/outliner/outlobj.cxx
diff --git a/svx/source/svdraw/svdmodel.cxx b/svx/source/svdraw/svdmodel.cxx
index 8862522..47f2b51 100644
--- a/svx/source/svdraw/svdmodel.cxx
+++ b/svx/source/svdraw/svdmodel.cxx
@@ -86,6 +86,7 @@ struct SdrModelImpl
    bool mbAnchoredTextOverflowLegacy; // tdf#99729 compatibility flag
    bool mbLegacySingleLineFontwork;   // tdf#148000 compatibility flag
    bool mbConnectorUseSnapRect;       // tdf#149756 compatibility flag
    bool mbIgnoreBreakAfterMultilineField; ///< tdf#148966 compatibility flag
    std::unique_ptr<model::Theme> mpTheme;

    SdrModelImpl()
@@ -94,6 +95,7 @@ struct SdrModelImpl
        , mbAnchoredTextOverflowLegacy(false)
        , mbLegacySingleLineFontwork(false)
        , mbConnectorUseSnapRect(false)
        , mbIgnoreBreakAfterMultilineField(false)
    {}
};

@@ -1704,6 +1706,9 @@ void SdrModel::SetCompatibilityFlag(SdrCompatibilityFlag eFlag, bool bEnabled)
        case SdrCompatibilityFlag::ConnectorUseSnapRect:
            mpImpl->mbConnectorUseSnapRect = bEnabled;
            break;
        case SdrCompatibilityFlag::IgnoreBreakAfterMultilineField:
            mpImpl->mbIgnoreBreakAfterMultilineField = bEnabled;
            break;
    }
}

@@ -1717,6 +1722,8 @@ bool SdrModel::GetCompatibilityFlag(SdrCompatibilityFlag eFlag) const
            return mpImpl->mbLegacySingleLineFontwork;
        case SdrCompatibilityFlag::ConnectorUseSnapRect:
            return mpImpl->mbConnectorUseSnapRect;
        case SdrCompatibilityFlag::IgnoreBreakAfterMultilineField:
            return mpImpl->mbIgnoreBreakAfterMultilineField;
        default:
            return false;
    }
@@ -1799,6 +1806,14 @@ void SdrModel::ReadUserDataSequenceValue(const beans::PropertyValue* pValue)
            }
        }
    }
    else if (pValue->Name == "IgnoreBreakAfterMultilineField")
    {
        bool bBool = false;
        if (pValue->Value >>= bBool)
        {
            mpImpl->mbIgnoreBreakAfterMultilineField = bBool;
        }
    }
}

template <typename T>
@@ -1816,6 +1831,8 @@ void SdrModel::WriteUserDataSequence(uno::Sequence <beans::PropertyValue>& rValu
            GetCompatibilityFlag(SdrCompatibilityFlag::LegacySingleLineFontwork));
    addPair(aUserData, "ConnectorUseSnapRect",
            GetCompatibilityFlag(SdrCompatibilityFlag::ConnectorUseSnapRect));
    addPair(aUserData, "IgnoreBreakAfterMultilineField",
            GetCompatibilityFlag(SdrCompatibilityFlag::IgnoreBreakAfterMultilineField));

    const sal_Int32 nOldLength = rValues.getLength();
    rValues.realloc(nOldLength + aUserData.size());
diff --git a/svx/source/svdraw/svdoutl.cxx b/svx/source/svdraw/svdoutl.cxx
index 1888fbb..5e89f5d 100644
--- a/svx/source/svdraw/svdoutl.cxx
+++ b/svx/source/svdraw/svdoutl.cxx
@@ -17,9 +17,12 @@
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <optional>
#include <svx/svdoutl.hxx>
#include <editeng/outliner.hxx>
#include <svx/svdmodel.hxx>
#include <svx/svdotext.hxx>
#include <svx/svdpage.hxx>
#include <editeng/editstat.hxx>
#include <svl/itempool.hxx>
#include <editeng/editview.hxx>
@@ -104,4 +107,13 @@ bool SdrOutliner::hasEditViewCallbacks() const
    return false;
}

std::optional<bool> SdrOutliner::GetCompatFlag(SdrCompatibilityFlag eFlag) const
{
    if( mpVisualizedPage )
    {
        return {mpVisualizedPage->getSdrModelFromSdrPage().GetCompatibilityFlag(eFlag)};
    }
    return {};
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */