editeng/source/editeng/impedit.hxx                 |    9 -
 editeng/source/editeng/impedit2.cxx                |    4 
 editeng/source/editeng/impedit3.cxx                |   20 +--
 include/svx/compatflags.hxx                        |    3 
 sd/qa/unit/data/odp/trailing-paragraphs-compat.odp |binary
 sd/qa/unit/data/odp/trailing-paragraphs.odp        |binary
 sd/qa/unit/data/pptx/trailing-paragraphs.pptx      |binary
 sd/qa/unit/layout-tests.cxx                        |  126 ++++++++++++++++++++-
 sd/source/ui/docshell/docshel4.cxx                 |    4 
 svx/source/svdraw/svdmodel.cxx                     |    9 +
 svx/source/svdraw/svdotextdecomposition.cxx        |   12 +-
 11 files changed, 167 insertions(+), 20 deletions(-)

New commits:
commit 055008aca0cfd9de44b8dd96b32e55ddaf13bfe5
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Tue Aug 19 23:33:02 2025 +0500
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Thu Aug 21 11:50:54 2025 +0200

    tdf#168010: introduce UseTrailingEmptyLinesInLayout compat option
    
    In PowerPoint, autoshrink text size is calculated the same way as in
    Impress, ignoring the empty lines block in the end. But when the text
    is positioned, these lines are taken into account, which may change
    position of texts aligned to bottom.
    
    Since this method of positioning text may change text placement very
    seriously, it can't be used unconditionally, and requires a compat
    flag, which is introduced here, only enabled for PPTX import, and in
    ODP documents having the option explicitly set.
    
    It is not used for other MS Office document types. I saw that Excel
    behaves similarly to PowerPoint, so maybe it makes sense to enable
    it for XLSX, too; on the other hand, MS Word works differently. Also
    I couldn't prepare a test document in binary PPT to test hehavior.
    The decisions about these file types should go in separate changes.
    
    Commit f61ea135430d7b4a1fac7de1e57a1314fbb1b49e (editeng: use text
    scaling that better mimics MSO text scaling, 2024-04-03) introduced
    ImpEditEngine::FormatParagraphs, that calculated paragraphs' heights,
    and returned the height of the part before trailing empty paragraphs
    block. It needed that height to make decisions about text scaling;
    and the implementation simply assigned zero height to the trailing
    empty paragraphs, therefore adding their heights didn't count.
    
    Now we need the heights of the trailing paragraphs in the new compat
    mode. This change modifies implementation of FormatParagraphs and
    CalcHeight: the latter now calculates paragraph heights regardless
    of the paragraph content; and the former checks if current paragraph
    height should count in the returned result. This keeps the scaling
    algorithm, and at the same time, provides the required height data.
    We could possibly make the height calculation depend on the compat
    flag; but this would make the code more complex, without a benefit.
    The actual difference that the trailing empty paragraphs do is in
    SdrTextObj::impDecomposeAutoFitTextPrimitive.
    
    Change-Id: Ie37f1d2b3393f9c52be89586c73df70b108190a1
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/189935
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>
    Tested-by: Jenkins
    (cherry picked from commit 425b55efaf1f04e8f91cb049a5820fcf61e678dd)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/189945
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/editeng/source/editeng/impedit.hxx 
b/editeng/source/editeng/impedit.hxx
index 12428d1e054f..4bdcdaa6eefd 100644
--- a/editeng/source/editeng/impedit.hxx
+++ b/editeng/source/editeng/impedit.hxx
@@ -651,7 +651,7 @@ private:
 
     void                ParaAttribsChanged( ContentNode const * pNode, bool 
bIgnoreUndoCheck = false );
     void                TextModified();
-    void                CalcHeight(ParaPortion& rParaPortion, bool bIsScaling 
= false);
+    void                CalcHeight(ParaPortion& rParaPortion);
     bool isInEmptyClusterAtTheEnd(ParaPortion& rParaPortion, bool bIsScaling);
 
     void                InsertUndo( std::unique_ptr<EditUndo> pUndo, bool 
bTryMerge = false );
@@ -683,13 +683,13 @@ private:
     void                Clear();
     EditPaM             RemoveText();
 
-    bool createLinesForEmptyParagraph(ParaPortion& rParaPortion, bool 
bIsScaling = false);
+    bool createLinesForEmptyParagraph(ParaPortion& rParaPortion);
     tools::Long calculateMaxLineWidth(tools::Long nStartX, SvxLRSpaceItem 
const& rLRItem,
                                       const SvxFontUnitMetrics& rMetrics);
-    bool CreateLines(sal_Int32 nPara, sal_uInt32 nStartPosY, bool bIsScaling = 
false);
+    bool CreateLines(sal_Int32 nPara, sal_uInt32 nStartPosY);
 
     void                CreateAndInsertEmptyLine(ParaPortion& rParaPortion);
-    bool                FinishCreateLines(ParaPortion& rParaPortion, bool 
bIsScaling = false);
+    bool                FinishCreateLines(ParaPortion& rParaPortion);
     void                CreateTextPortions(ParaPortion& rParaPortion, 
sal_Int32& rStartPos);
     void                RecalcTextPortion(ParaPortion& rParaPortion, sal_Int32 
nStartPos, sal_Int32 nNewChars);
     sal_Int32           SplitTextPortion(ParaPortion& rParaPortion, sal_Int32 
nPos,  EditLine* pCurLine = nullptr);
@@ -980,6 +980,7 @@ public:
 
     void SetMinColumnWrapHeight(tools::Long nVal) { mnMinColumnWrapHeight = 
nVal; }
 
+    // Returns the height of the text, excluding empty lines in the end
     tools::Long FormatParagraphs(o3tl::sorted_vector<sal_Int32>& 
rRepaintParagraphs, bool bIsScaling);
     void ScaleContentToFitWindow(o3tl::sorted_vector<sal_Int32>& 
rRepaintParagraphs);
     void FormatDoc();
diff --git a/editeng/source/editeng/impedit2.cxx 
b/editeng/source/editeng/impedit2.cxx
index 5b8abb8f2461..642f84946250 100644
--- a/editeng/source/editeng/impedit2.cxx
+++ b/editeng/source/editeng/impedit2.cxx
@@ -4431,12 +4431,12 @@ bool 
ImpEditEngine::isInEmptyClusterAtTheEnd(ParaPortion& rPortion, bool bIsScal
     return false;
 }
 
-void ImpEditEngine::CalcHeight(ParaPortion& rPortion, bool bIsScaling)
+void ImpEditEngine::CalcHeight(ParaPortion& rPortion)
 {
     rPortion.mnHeight = 0;
     rPortion.mnFirstLineOffset = 0;
 
-    if (!rPortion.IsVisible() || isInEmptyClusterAtTheEnd(rPortion, 
bIsScaling))
+    if (!rPortion.IsVisible())
         return;
 
     OSL_ENSURE(rPortion.GetLines().Count(), "Paragraph with no lines in 
ParaPortion::CalcHeight");
diff --git a/editeng/source/editeng/impedit3.cxx 
b/editeng/source/editeng/impedit3.cxx
index 7f5cf45bffb2..85a21a39c20b 100644
--- a/editeng/source/editeng/impedit3.cxx
+++ b/editeng/source/editeng/impedit3.cxx
@@ -321,7 +321,7 @@ void ImpEditEngine::FormatFullDoc()
 tools::Long ImpEditEngine::FormatParagraphs(o3tl::sorted_vector<sal_Int32>& 
aRepaintParagraphList, bool bIsScaling)
 {
     sal_Int32 nParaCount = GetParaPortions().Count();
-    tools::Long nY = 0;
+    tools::Long nY = 0, nResult = 0;
     bool bGrow = false;
 
     for (sal_Int32 nParagraph = 0; nParagraph < nParaCount; nParagraph++)
@@ -330,7 +330,7 @@ tools::Long 
ImpEditEngine::FormatParagraphs(o3tl::sorted_vector<sal_Int32>& aRep
         if (rParaPortion.MustRepaint() || (rParaPortion.IsInvalid() && 
rParaPortion.IsVisible()))
         {
             // No formatting should be necessary for MustRepaint()!
-            if (CreateLines(nParagraph, nY, bIsScaling))
+            if (CreateLines(nParagraph, nY))
             {
                 if (!bGrow && GetTextRanger())
                 {
@@ -359,8 +359,10 @@ tools::Long 
ImpEditEngine::FormatParagraphs(o3tl::sorted_vector<sal_Int32>& aRep
             aRepaintParagraphList.insert(nParagraph);
         }
         nY += rParaPortion.GetHeight();
+        if (!isInEmptyClusterAtTheEnd(rParaPortion, bIsScaling))
+            nResult = nY; // The total height excluding trailing blank 
paragraphs
     }
-    return nY;
+    return nResult;
 }
 
 namespace
@@ -593,7 +595,7 @@ tools::Long ImpEditEngine::GetColumnWidth(const Size& 
rPaperSize) const
     return (nWidth - mnColumnSpacing * (mnColumns - 1)) / mnColumns;
 }
 
-bool ImpEditEngine::createLinesForEmptyParagraph(ParaPortion& rParaPortion, 
bool bIsScaling)
+bool ImpEditEngine::createLinesForEmptyParagraph(ParaPortion& rParaPortion)
 {
     // fast special treatment...
     if (rParaPortion.GetTextPortions().Count())
@@ -602,7 +604,7 @@ bool 
ImpEditEngine::createLinesForEmptyParagraph(ParaPortion& rParaPortion, bool
         rParaPortion.GetLines().Reset();
 
     CreateAndInsertEmptyLine(rParaPortion);
-    return FinishCreateLines(rParaPortion, bIsScaling);
+    return FinishCreateLines(rParaPortion);
 }
 
 tools::Long ImpEditEngine::calculateMaxLineWidth(tools::Long nStartX, 
SvxLRSpaceItem const& rLRItem,
@@ -627,7 +629,7 @@ tools::Long 
ImpEditEngine::calculateMaxLineWidth(tools::Long nStartX, SvxLRSpace
     return nMaxLineWidth;
 }
 
-bool ImpEditEngine::CreateLines( sal_Int32 nPara, sal_uInt32 nStartPosY, bool 
bIsScaling )
+bool ImpEditEngine::CreateLines( sal_Int32 nPara, sal_uInt32 nStartPosY )
 {
     assert(GetParaPortions().exists(nPara) && "Portion paragraph index is not 
valid");
     ParaPortion& rParaPortion = GetParaPortions().getRef(nPara);
@@ -644,7 +646,7 @@ bool ImpEditEngine::CreateLines( sal_Int32 nPara, 
sal_uInt32 nStartPosY, bool bI
     // Fast special treatment for empty paragraphs...
     bool bEmptyParagraph = rParaPortion.GetNode()->Len() == 0 && 
!GetTextRanger();
     if (bEmptyParagraph)
-        return createLinesForEmptyParagraph(rParaPortion, bIsScaling);
+        return createLinesForEmptyParagraph(rParaPortion);
 
     sal_Int64 nCurrentPosY = nStartPosY;
     // If we're allowed to skip parts outside and this cannot possibly fit in 
the given height,
@@ -1936,12 +1938,12 @@ void 
ImpEditEngine::CreateAndInsertEmptyLine(ParaPortion& rParaPortion)
     }
 }
 
-bool ImpEditEngine::FinishCreateLines(ParaPortion& rParaPortion, bool 
bIsScaling)
+bool ImpEditEngine::FinishCreateLines(ParaPortion& rParaPortion)
 {
 //  CalcCharPositions( pParaPortion );
     rParaPortion.SetValid();
     tools::Long nOldHeight = rParaPortion.GetHeight();
-    CalcHeight(rParaPortion, bIsScaling);
+    CalcHeight(rParaPortion);
 
     DBG_ASSERT(rParaPortion.GetTextPortions().Count(), "FinishCreateLines: No 
Text-Portion?");
     bool bRet = rParaPortion.GetHeight() != nOldHeight;
diff --git a/include/svx/compatflags.hxx b/include/svx/compatflags.hxx
index 4caecf18be55..75b85f79607f 100644
--- a/include/svx/compatflags.hxx
+++ b/include/svx/compatflags.hxx
@@ -14,7 +14,8 @@ enum class SdrCompatibilityFlag
     LegacyFontwork, ///< for tdf#148000 false == Fontwork works in PowerPoint 
compat mode
     ConnectorUseSnapRect, ///< for tdf#149756
     IgnoreBreakAfterMultilineField, ///< for tdf#148966
-    LAST = IgnoreBreakAfterMultilineField /// add new items above
+    UseTrailingEmptyLinesInLayout, ///< for tdf#168010
+    LAST = UseTrailingEmptyLinesInLayout /// add new items above
 };
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/sd/qa/unit/data/odp/trailing-paragraphs-compat.odp 
b/sd/qa/unit/data/odp/trailing-paragraphs-compat.odp
new file mode 100644
index 000000000000..b7f5798e00d4
Binary files /dev/null and b/sd/qa/unit/data/odp/trailing-paragraphs-compat.odp 
differ
diff --git a/sd/qa/unit/data/odp/trailing-paragraphs.odp 
b/sd/qa/unit/data/odp/trailing-paragraphs.odp
new file mode 100644
index 000000000000..ea87cc8f6001
Binary files /dev/null and b/sd/qa/unit/data/odp/trailing-paragraphs.odp differ
diff --git a/sd/qa/unit/data/pptx/trailing-paragraphs.pptx 
b/sd/qa/unit/data/pptx/trailing-paragraphs.pptx
new file mode 100644
index 000000000000..fa8907e543bf
Binary files /dev/null and b/sd/qa/unit/data/pptx/trailing-paragraphs.pptx 
differ
diff --git a/sd/qa/unit/layout-tests.cxx b/sd/qa/unit/layout-tests.cxx
index 6fae40d135c7..9a593b611da0 100644
--- a/sd/qa/unit/layout-tests.cxx
+++ b/sd/qa/unit/layout-tests.cxx
@@ -10,6 +10,10 @@
 
 #include <sfx2/objsh.hxx>
 #include <sfx2/sfxbasemodel.hxx>
+#include <svx/compatflags.hxx>
+
+#include <drawdoc.hxx>
+#include <unomodel.hxx>
 
 class SdLayoutTest : public UnoApiXmlTest
 {
@@ -19,9 +23,8 @@ public:
     {
     }
 
-    xmlDocUniquePtr load(const char* pName)
+    xmlDocUniquePtr parseLayout() const
     {
-        loadFromFile(OUString::createFromAscii(pName));
         SfxBaseModel* pModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
         CPPUNIT_ASSERT(pModel);
         SfxObjectShell* pShell = pModel->GetObjectShell();
@@ -33,6 +36,21 @@ public:
 
         return pXmlDoc;
     }
+
+    xmlDocUniquePtr load(const char* pName)
+    {
+        loadFromFile(OUString::createFromAscii(pName));
+        return parseLayout();
+    }
+
+    SdDrawDocument* getDoc()
+    {
+        auto* pImpress = dynamic_cast<SdXImpressDocument*>(mxComponent.get());
+        CPPUNIT_ASSERT(pImpress);
+        auto* pDoc = pImpress->GetDoc();
+        CPPUNIT_ASSERT(pDoc);
+        return pDoc;
+    }
 };
 
 CPPUNIT_TEST_FIXTURE(SdLayoutTest, testTdf104722)
@@ -458,6 +476,110 @@ CPPUNIT_TEST_FIXTURE(SdLayoutTest, testTdf164622)
                 "y", u"892");
 }
 
+CPPUNIT_TEST_FIXTURE(SdLayoutTest, testTdf168010)
+{
+    // Test UseTrailingEmptyLinesInLayout compatibility option.
+    // The test documents have an auto-shrink text "textbox

"; the box itself is positioned
+    // identically in all cases; the text is aligned to bottom.
+    // When "UseTrailingEmptyLinesInLayout" is set, "textbox" string is placed 
higher, than when
+    // the setting is not set (y value ~6700 vs. ~8100).
+
+    // The existing ODPs have a standard draw:text-box. It produces three 
textarray elements,
+    // in order from bottom to top. We need the topmost, third.
+
+    // 1. UseTrailingEmptyLinesInLayout must be enabled in an existing ODP 
with respective option
+    // in settings.xml
+    loadFromFile(u"odp/trailing-paragraphs-compat.odp");
+    {
+        CPPUNIT_ASSERT(
+            
getDoc()->GetCompatibilityFlag(SdrCompatibilityFlag::UseTrailingEmptyLinesInLayout));
+
+        xmlDocUniquePtr pXml = parseLayout();
+        sal_Int32 y = getXPath(pXml, "/metafile['1']/push/push/textarray[3]", 
"y").toInt32();
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(6700, y, 100); // could be 6641 or 6760
+        assertXPathContent(pXml, "/metafile['1']/push/push/textarray[3]/text", 
u"textbox");
+    }
+
+    // 2. It must stay enabled after ODP round-trip
+    saveAndReload(u"impress8"_ustr);
+    {
+        CPPUNIT_ASSERT(
+            
getDoc()->GetCompatibilityFlag(SdrCompatibilityFlag::UseTrailingEmptyLinesInLayout));
+
+        xmlDocUniquePtr pXml = parseLayout();
+        sal_Int32 y = getXPath(pXml, "/metafile['2']/push/push/textarray[3]", 
"y").toInt32();
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(6700, y, 100);
+        assertXPathContent(pXml, "/metafile['2']/push/push/textarray[3]/text", 
u"textbox");
+    }
+
+    // 3. It must be disabled in an existing ODP without that option in 
settings.xml
+    loadFromFile(u"odp/trailing-paragraphs.odp");
+    {
+        CPPUNIT_ASSERT(
+            
!getDoc()->GetCompatibilityFlag(SdrCompatibilityFlag::UseTrailingEmptyLinesInLayout));
+
+        xmlDocUniquePtr pXml = parseLayout();
+        sal_Int32 y = getXPath(pXml, "/metafile['3']/push/push/textarray[3]", 
"y").toInt32();
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(8100, y, 100);
+        assertXPathContent(pXml, "/metafile['3']/push/push/textarray[3]/text", 
u"textbox");
+    }
+
+    // 4. It must stay disabled after ODP round-trip
+    saveAndReload(u"impress8"_ustr);
+    {
+        CPPUNIT_ASSERT(
+            
!getDoc()->GetCompatibilityFlag(SdrCompatibilityFlag::UseTrailingEmptyLinesInLayout));
+
+        xmlDocUniquePtr pXml = parseLayout();
+        sal_Int32 y = getXPath(pXml, "/metafile['4']/push/push/textarray[3]", 
"y").toInt32();
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(8100, y, 100);
+        assertXPathContent(pXml, "/metafile['4']/push/push/textarray[3]/text", 
u"textbox");
+    }
+
+    // Now test PPTX and its round-trip. The text there imports as 
draw:custom-shape; it generates
+    // a single textarray element.
+
+    // 5. It must be enabled for PPTX documents unconditionally
+    loadFromFile(u"pptx/trailing-paragraphs.pptx");
+    {
+        CPPUNIT_ASSERT(
+            
getDoc()->GetCompatibilityFlag(SdrCompatibilityFlag::UseTrailingEmptyLinesInLayout));
+
+        xmlDocUniquePtr pXml = parseLayout();
+        sal_Int32 y = getXPath(pXml, "/metafile['5']/push/push/textarray", 
"y").toInt32();
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(6700, y, 100);
+        assertXPathContent(pXml, "/metafile['5']/push/push/textarray/text", 
u"textbox");
+    }
+
+    // 6. Check PPTX round-trip
+    saveAndReload(u"Impress Office Open XML"_ustr);
+    {
+        CPPUNIT_ASSERT(
+            
getDoc()->GetCompatibilityFlag(SdrCompatibilityFlag::UseTrailingEmptyLinesInLayout));
+
+        xmlDocUniquePtr pXml = parseLayout();
+        sal_Int32 y = getXPath(pXml, "/metafile['6']/push/push/textarray", 
"y").toInt32();
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(6700, y, 100);
+        assertXPathContent(pXml, "/metafile['6']/push/push/textarray/text", 
u"textbox");
+    }
+
+    // For some reason, saving PPTX to ODT in step 7 below produces negative 
fo:padding-top, which
+    // fails validation; that is unrelated, so disable validation for now.
+    skipValidation();
+
+    // 7. It must round-trip to ODP
+    saveAndReload(u"impress8"_ustr);
+    {
+        CPPUNIT_ASSERT(
+            
getDoc()->GetCompatibilityFlag(SdrCompatibilityFlag::UseTrailingEmptyLinesInLayout));
+
+        xmlDocUniquePtr pXml = parseLayout();
+        sal_Int32 y = getXPath(pXml, "/metafile['7']/push/push/textarray", 
"y").toInt32();
+        CPPUNIT_ASSERT_DOUBLES_EQUAL(6700, y, 100);
+        assertXPathContent(pXml, "/metafile['7']/push/push/textarray/text", 
u"textbox");
+    }
+}
+
 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 dca885bcd2cd..223b5921cc4c 100644
--- a/sd/source/ui/docshell/docshel4.cxx
+++ b/sd/source/ui/docshell/docshel4.cxx
@@ -430,6 +430,10 @@ bool DrawDocShell::ImportFrom(SfxMedium &rMedium,
 
         // compatibility flag for tdf#148966
         
mpDoc->SetCompatibilityFlag(SdrCompatibilityFlag::IgnoreBreakAfterMultilineField,
 true);
+
+        // tdf#168010: PowerPoint ignores empty trailing lines in autoshrink 
text when scaling font
+        // (same as Impress), but takes into account in layout:
+        
mpDoc->SetCompatibilityFlag(SdrCompatibilityFlag::UseTrailingEmptyLinesInLayout,
 true);
     }
 
     if (aFilterName == "Impress MS PowerPoint 2007 XML" ||
diff --git a/svx/source/svdraw/svdmodel.cxx b/svx/source/svdraw/svdmodel.cxx
index d75ea61595f7..e3da9decea16 100644
--- a/svx/source/svdraw/svdmodel.cxx
+++ b/svx/source/svdraw/svdmodel.cxx
@@ -98,6 +98,7 @@ struct SdrModelImpl
             false, // tdf#148000 LegacyFontwork
             false, // tdf#149756 ConnectorUseSnapRect
             false, // tdf#148966 IgnoreBreakAfterMultilineField
+            false, // tdf#168010 UseTrailingEmptyLinesInLayout
           }
         , mpTheme(new model::Theme(u"Office"_ustr))
     {}
@@ -1836,6 +1837,11 @@ void SdrModel::ReadUserDataSequenceValue(const 
beans::PropertyValue* pValue)
             
SetCompatibilityFlag(SdrCompatibilityFlag::IgnoreBreakAfterMultilineField, 
bBool);
         }
     }
+    else if (pValue->Name == "UseTrailingEmptyLinesInLayout")
+    {
+        if (bool bBool; pValue->Value >>= bBool)
+            
SetCompatibilityFlag(SdrCompatibilityFlag::UseTrailingEmptyLinesInLayout, 
bBool);
+    }
 }
 
 void SdrModel::WriteUserDataSequence(uno::Sequence <beans::PropertyValue>& 
rValues)
@@ -1845,7 +1851,8 @@ void SdrModel::WriteUserDataSequence(uno::Sequence 
<beans::PropertyValue>& rValu
         { "AnchoredTextOverflowLegacy", 
uno::Any(GetCompatibilityFlag(SdrCompatibilityFlag::AnchoredTextOverflowLegacy))
 },
         { "LegacySingleLineFontwork", 
uno::Any(GetCompatibilityFlag(SdrCompatibilityFlag::LegacyFontwork)) },
         { "ConnectorUseSnapRect", 
uno::Any(GetCompatibilityFlag(SdrCompatibilityFlag::ConnectorUseSnapRect)) },
-        { "IgnoreBreakAfterMultilineField", 
uno::Any(GetCompatibilityFlag(SdrCompatibilityFlag::IgnoreBreakAfterMultilineField))
 }
+        { "IgnoreBreakAfterMultilineField", 
uno::Any(GetCompatibilityFlag(SdrCompatibilityFlag::IgnoreBreakAfterMultilineField))
 },
+        { "UseTrailingEmptyLinesInLayout", 
uno::Any(GetCompatibilityFlag(SdrCompatibilityFlag::UseTrailingEmptyLinesInLayout))
 },
     };
 
     const sal_Int32 nOldLength = rValues.getLength();
diff --git a/svx/source/svdraw/svdotextdecomposition.cxx 
b/svx/source/svdraw/svdotextdecomposition.cxx
index af7b3e1e4831..d5870e4cf7f6 100644
--- a/svx/source/svdraw/svdotextdecomposition.cxx
+++ b/svx/source/svdraw/svdotextdecomposition.cxx
@@ -1001,7 +1001,17 @@ void SdrTextObj::impDecomposeAutoFitTextPrimitive(
     rOutliner.SetFixedCellHeight(rSdrAutofitTextPrimitive.isFixedCellHeight());
 
     // now get back the layouted text size from outliner
-    const Size aOutlinerTextSize(rOutliner.GetPaperSize());
+    Size aOutlinerTextSize(rOutliner.GetPaperSize());
+    if (getSdrModelFromSdrObject().GetCompatibilityFlag(
+            SdrCompatibilityFlag::UseTrailingEmptyLinesInLayout))
+    {
+        // The height of the text may be larger than the box height, because 
of the trailing
+        // empty paragraphs, ignored when scaling, and normally ignored for 
layout. PowerPoint
+        // has a different handling: it also ignores the lines when scaling, 
but uses them for
+        // positioning of the text.
+        if (tools::Long h = rOutliner.GetTextHeight(); h > 
aOutlinerTextSize.Height())
+            aOutlinerTextSize.setHeight(h);
+    }
     const basegfx::B2DVector aOutlinerScale(aOutlinerTextSize.Width(), 
aOutlinerTextSize.Height());
     basegfx::B2DVector aAdjustTranslate(0.0, 0.0);
 

Reply via email to