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 40f13a8fc7676f7ed0741b3b4c46d0c06637ce53
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Tue Aug 19 23:33:02 2025 +0500
Commit:     Xisco Fauli <xiscofa...@libreoffice.org>
CommitDate: Thu Aug 21 00:07:02 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.
    
    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/+/189942
    Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org>

diff --git a/editeng/source/editeng/impedit.hxx 
b/editeng/source/editeng/impedit.hxx
index e91e2f4a5191..8dd95542981a 100644
--- a/editeng/source/editeng/impedit.hxx
+++ b/editeng/source/editeng/impedit.hxx
@@ -663,7 +663,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(const ParaPortion& rParaPortion, bool 
bIsScaling);
 
     void                InsertUndo( std::unique_ptr<EditUndo> pUndo, bool 
bTryMerge = false );
@@ -695,14 +695,14 @@ 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);
     void populateRubyInfo(ParaPortion& rParaPortion, EditLine* pLine);
-    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);
@@ -993,6 +993,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 62262a2f83a5..5ce813b2d8f7 100644
--- a/editeng/source/editeng/impedit2.cxx
+++ b/editeng/source/editeng/impedit2.cxx
@@ -4665,12 +4665,12 @@ bool ImpEditEngine::isInEmptyClusterAtTheEnd(const 
ParaPortion& rPortion, bool b
     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 8eb2f8ee73b0..c8e47018e5d7 100644
--- a/editeng/source/editeng/impedit3.cxx
+++ b/editeng/source/editeng/impedit3.cxx
@@ -323,7 +323,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++)
@@ -332,7 +332,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())
                 {
@@ -361,8 +361,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
@@ -595,7 +597,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())
@@ -604,7 +606,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,
@@ -725,7 +727,7 @@ void ImpEditEngine::populateRubyInfo(ParaPortion& 
rParaPortion, EditLine* pLine)
     }
 }
 
-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);
@@ -742,7 +744,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,
@@ -2040,12 +2042,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 b5b9ebb591ad..2884afefaa4e 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 a90694947368..b86d9c20f92a 100644
--- a/svx/source/svdraw/svdotextdecomposition.cxx
+++ b/svx/source/svdraw/svdotextdecomposition.cxx
@@ -456,7 +456,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