oox/inc/drawingml/textliststyle.hxx              |    6 +++
 oox/inc/drawingml/textparagraphproperties.hxx    |    3 +
 oox/qa/unit/data/outliner-list-style.pptx        |binary
 oox/qa/unit/drawingml.cxx                        |   34 +++++++++++++++++++
 oox/source/drawingml/textliststyle.cxx           |   41 +++++++++++++++++++++++
 oox/source/drawingml/textparagraph.cxx           |    2 -
 oox/source/drawingml/textparagraphproperties.cxx |   10 +++++
 7 files changed, 93 insertions(+), 3 deletions(-)

New commits:
commit 92f97778b87348b0b82c0497c4daee077005cbd1
Author:     Miklos Vajna <[email protected]>
AuthorDate: Fri Oct 10 08:50:25 2025 +0200
Commit:     Miklos Vajna <[email protected]>
CommitDate: Fri Oct 10 16:30:47 2025 +0200

    tdf#168559 PPTX imp: fix missing custom level list style for outline shapes
    
    Export the bugdoc to PPTX, reopen it in Impress, go to the end of the
    first bullet, enter, tab, indent grows to a large value, while it should
    only grow to match the existing other already indented bullet.
    
    This happens because the shape text's paragraphs have a numbering rule
    defined for the current list level, but the other list levels are left
    untouched by the PPTX import.
    
    Fix the problem by optionally giving the shape's list style to
    TextParagraphProperties::pushToPropSet(), and if that's given, then use
    the list style to initialize the numbering rules of the paragraph for
    all non-current levels. (Current level was initialized already.) Not all
    possible properties are set, but left/first margin is done, which is the
    primary use-case of the bug.
    
    This fixes, the bug, but a full PPTX export+import+export+import cycle
    is still bad, probably because something is not imported on the
    masterpage, that's still to be done. (PPTX export+import+export then
    edit in PowerPoint works.)
    
    Change-Id: I3575b986faaacb4103e6dde083b13b10b7d957c1
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192168
    Reviewed-by: Miklos Vajna <[email protected]>
    Tested-by: Jenkins

diff --git a/oox/inc/drawingml/textliststyle.hxx 
b/oox/inc/drawingml/textliststyle.hxx
index 464e48756165..88de4621de13 100644
--- a/oox/inc/drawingml/textliststyle.hxx
+++ b/oox/inc/drawingml/textliststyle.hxx
@@ -20,6 +20,8 @@
 #ifndef INCLUDED_OOX_DRAWINGML_TEXTLISTSTYLE_HXX
 #define INCLUDED_OOX_DRAWINGML_TEXTLISTSTYLE_HXX
 
+#include <com/sun/star/container/XIndexReplace.hpp>
+
 #include <drawingml/textparagraphproperties.hxx>
 #include <array>
 
@@ -57,6 +59,10 @@ public:
      */
     bool hasListStyleOnImport() const { return mbHasListStyleOnImport; }
 
+    /// Set properties on xNumRules based on maListStyle, for all levels 
except nIgnoreLevel.
+    void pushToNumberingRules(const 
css::uno::Reference<css::container::XIndexReplace>& xNumRules,
+                              size_t nIgnoreLevel);
+
 #ifdef DBG_UTIL
     void dump() const;
 #endif
diff --git a/oox/inc/drawingml/textparagraphproperties.hxx 
b/oox/inc/drawingml/textparagraphproperties.hxx
index e362119ed6f9..9903c502cba4 100644
--- a/oox/inc/drawingml/textparagraphproperties.hxx
+++ b/oox/inc/drawingml/textparagraphproperties.hxx
@@ -104,7 +104,8 @@ public:
                                                 const BulletList* 
pMasterBuList,
                                                 bool bApplyBulletList,
                                                 float fFontSize,
-                                                bool bPushDefaultValues = 
false ) const;
+                                                bool bPushDefaultValues = 
false,
+                                                TextListStyle* pTextListStyle 
= nullptr ) const;
 
     /** Returns the largest character size of this paragraph. If possible the
         masterstyle should have been applied before, otherwise the character
diff --git a/oox/qa/unit/data/outliner-list-style.pptx 
b/oox/qa/unit/data/outliner-list-style.pptx
new file mode 100644
index 000000000000..d1d37432e2a6
Binary files /dev/null and b/oox/qa/unit/data/outliner-list-style.pptx differ
diff --git a/oox/qa/unit/drawingml.cxx b/oox/qa/unit/drawingml.cxx
index 49a037c2cc0f..8b443970f691 100644
--- a/oox/qa/unit/drawingml.cxx
+++ b/oox/qa/unit/drawingml.cxx
@@ -786,6 +786,40 @@ CPPUNIT_TEST_FIXTURE(OoxDrawingmlTest, 
testDOCXVerticalLineRotation)
     CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), nRotateAngle);
 }
 
+CPPUNIT_TEST_FIXTURE(OoxDrawingmlTest, 
testPPTXImportOutlinerListStyleDirectFormat)
+{
+    // Given a PPTX file with a slide with an outline shape:
+    // When loading that document:
+    loadFromFile(u"outliner-list-style.pptx");
+
+    // Then make sure that the resulting shape has enough numbering rules, so 
adding a new paragraph
+    // and increasing indent results in correct behavior of 1499 mm100 / 1.5cm 
indent:
+    uno::Reference<drawing::XDrawPagesSupplier> 
xDrawPagesSupplier(mxComponent, uno::UNO_QUERY);
+    uno::Reference<drawing::XDrawPage> 
xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+                                                 uno::UNO_QUERY);
+    uno::Reference<text::XTextRange> xShape(xDrawPage->getByIndex(0), 
uno::UNO_QUERY);
+    uno::Reference<container::XEnumerationAccess> 
xShapeText(xShape->getText(), uno::UNO_QUERY);
+    uno::Reference<container::XEnumeration> xParagraphs = 
xShapeText->createEnumeration();
+    xParagraphs->nextElement();
+    uno::Reference<beans::XPropertySet> xParagraph(xParagraphs->nextElement(), 
uno::UNO_QUERY);
+    uno::Reference<container::XIndexAccess> xNumberingRules;
+    xParagraph->getPropertyValue(u"NumberingRules"_ustr) >>= xNumberingRules;
+    comphelper::SequenceAsHashMap aMap(xNumberingRules->getByIndex(2));
+    sal_Int32 nLeftMargin{};
+    aMap["LeftMargin"] >>= nLeftMargin;
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: 2388
+    // - Actual  : 3600
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2388), nLeftMargin);
+    sal_Int32 nFirstLineOffset{};
+    aMap["FirstLineOffset"] >>= nFirstLineOffset;
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: -889
+    // - Actual  : -800
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(-889), nFirstLineOffset);
+    // i.e. the sum of these two were 2800, not 1499, the indent was larger 
than expected.
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/drawingml/textliststyle.cxx 
b/oox/source/drawingml/textliststyle.cxx
index 42679ad4451c..ee695908d48a 100644
--- a/oox/source/drawingml/textliststyle.cxx
+++ b/oox/source/drawingml/textliststyle.cxx
@@ -19,6 +19,9 @@
 
 #include <drawingml/textliststyle.hxx>
 #include <sal/log.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+
+using namespace css;
 
 namespace oox::drawingml {
 
@@ -66,6 +69,44 @@ void TextListStyle::apply( const TextListStyle& 
rTextListStyle )
     applyStyleList( rTextListStyle.getListStyle(), getListStyle() );
 }
 
+void TextListStyle::pushToNumberingRules(const 
uno::Reference<container::XIndexReplace>& xNumRules,
+                                         size_t nIgnoreLevel)
+{
+    TextParagraphPropertiesArray& rListStyle = getListStyle();
+    size_t nLevels = xNumRules->getCount();
+    if (rListStyle.size() < nLevels)
+    {
+        nLevels = rListStyle.size();
+    }
+
+    for (size_t nLevel = 0; nLevel < nLevels; ++nLevel)
+    {
+        if (nLevel == nIgnoreLevel)
+        {
+            continue;
+        }
+
+        comphelper::SequenceAsHashMap aMap(xNumRules->getByIndex(nLevel));
+        TextParagraphProperties& rLevel = rListStyle[nLevel];
+        bool bChanged = false;
+        if (rLevel.getParaLeftMargin())
+        {
+            aMap["LeftMargin"] <<= *rLevel.getParaLeftMargin();
+            bChanged = true;
+        }
+        if (rLevel.getFirstLineIndentation())
+        {
+            aMap["FirstLineOffset"] <<= *rLevel.getFirstLineIndentation();
+            bChanged = true;
+        }
+
+        if (bChanged)
+        {
+            xNumRules->replaceByIndex(nLevel, aMap.getAsConstAny(true));
+        }
+    }
+}
+
 #ifdef DBG_UTIL
 void TextListStyle::dump() const
 {
diff --git a/oox/source/drawingml/textparagraph.cxx 
b/oox/source/drawingml/textparagraph.cxx
index 60615e6563e0..d256f9d8af64 100644
--- a/oox/source/drawingml/textparagraph.cxx
+++ b/oox/source/drawingml/textparagraph.cxx
@@ -187,7 +187,7 @@ void TextParagraph::insertAt(
             }
 
             float fCharacterSize = nCharHeight > 0 ? GetFontHeight ( 
nCharHeight ) : pTextParagraphStyle->getCharHeightPoints( 12 );
-            aParaProp.pushToPropSet( &rFilterBase, xProps, aioBulletList, 
&pTextParagraphStyle->getBulletList(), true, fCharacterSize, true );
+            aParaProp.pushToPropSet( &rFilterBase, xProps, aioBulletList, 
&pTextParagraphStyle->getBulletList(), true, fCharacterSize, true, 
&aCombinedTextStyle );
         }
 
         // empty paragraphs do not have bullets in ppt
diff --git a/oox/source/drawingml/textparagraphproperties.cxx 
b/oox/source/drawingml/textparagraphproperties.cxx
index 54c1d23c7b1a..44b1e10702da 100644
--- a/oox/source/drawingml/textparagraphproperties.cxx
+++ b/oox/source/drawingml/textparagraphproperties.cxx
@@ -18,6 +18,7 @@
  */
 
 #include <drawingml/textparagraphproperties.hxx>
+#include <drawingml/textliststyle.hxx>
 
 #include <com/sun/star/text/XNumberingRulesSupplier.hpp>
 #include <com/sun/star/container/XIndexReplace.hpp>
@@ -410,7 +411,7 @@ void TextParagraphProperties::apply( const 
TextParagraphProperties& rSourceProps
 
 void TextParagraphProperties::pushToPropSet( const ::oox::core::XmlFilterBase* 
pFilterBase,
     const Reference < XPropertySet >& xPropSet, PropertyMap& rioBulletMap, 
const BulletList* pMasterBuList, bool bApplyBulletMap, float fCharacterSize,
-    bool bPushDefaultValues ) const
+    bool bPushDefaultValues, TextListStyle* pTextListStyle ) const
 {
     PropertySet aPropSet( xPropSet );
     aPropSet.setProperties( maTextParagraphPropertyMap );
@@ -474,6 +475,13 @@ void TextParagraphProperties::pushToPropSet( const 
::oox::core::XmlFilterBase* p
                         rioBulletMap.setProperty<sal_Int16>( 
PROP_BulletRelSize, 100);
                     Sequence< PropertyValue > aBulletPropSeq = 
rioBulletMap.makePropertyValueSequence();
                     xNumRule->replaceByIndex( getLevel(), Any( aBulletPropSeq 
) );
+
+                    if (pTextListStyle)
+                    {
+                        // We got the list style of this shape, then set the 
other levels of the
+                        // numbering rules, too.
+                        pTextListStyle->pushToNumberingRules(xNumRule, 
getLevel());
+                    }
                 }
 
                 aPropSet.setProperty( PROP_NumberingRules, xNumRule );

Reply via email to