core.git: Branch 'private/mmeeks/l10nmapper' - 812 commits - android/Bootstrap android/source basic/qa basic/source bin/find-can-be-private-symbols.functions.results bin/find-mergedlib-can-be-private-symbols.functions.results bin/oss-fuzz-setup.sh chart2/inc chart2/Library_chartcontroller.mk chart2/qa chart2/source chart2/uiconfig chart2/UIConfig_chart2.mk codemaker/Executable_cppumaker.mk codemaker/Executable_javamaker.mk comphelper/Library_comphelper.mk comphelper/source compilerplugins/clang config_host.mk.in configmgr/source configure.ac connectivity/source cui/qa cui/source cui/uiconfig dbaccess/source desktop/inc desktop/Library_sofficeapp.mk desktop/qa desktop/source dictionaries distro-configs/CPMacOS-LOKit.conf distro-configs/Jenkins distro-configs/LibreOfficeOssFuzz.conf docmodel/Library_docmodel.mk docmodel/source download.lst drawinglayer/source editeng/CppunitTest_editeng_editeng.mk editeng/CustomTarget_generated.mk editeng/inc editeng/source extensions/source e xternal/afdko external/cairo external/clucene external/coinmp external/curl external/dtoa external/fast_float external/fontconfig external/freetype external/gpgmepp external/harfbuzz external/hunspell external/lcms2 external/libeot external/liblangtag external/libwebp external/libxml2 external/libxslt external/mariadb-connector-c external/md4c external/Module_external.mk external/nss external/openldap external/openssl external/pdfium external/poppler external/xmlsec external/zxcvbn-c extras/source filter/CustomTarget_svg.mk filter/qa filter/source forms/source formula/inc formula/source formula/uiconfig fpicker/source framework/source helpcontent2 hwpfilter/source i18npool/CustomTarget_breakiterator.mk i18npool/qa i18npool/source i18nutil/source idl/Executable_svidl.mk idl/source include/comphelper include/docmodel include/drawinglayer include/editeng include/formula include/i18nutil include/LibreOfficeKit include/oox include/sal include/sfx2 include/sot include/svl include/svtools include/svx include/test include/tools include/unotools include/vcl include/xmloff instsetoo_native/CustomTarget_emscripten-install.mk ios/UnitTest jurt/Library_jpipe.mk jurt/source jvmfwk/plugins libreofficekit/source linguistic/source Makefile.fetch offapi/com offapi/UnoApi_offapi.mk officecfg/registry oox/inc oox/IwyuFilter_oox.yaml oox/Library_oox.mk oox/qa oox/source package/inc package/source pyuno/source readlicense_oo/license README.md RepositoryExternal.mk Repository.mk sal/CppunitTest_sal_osl_file_details.mk sal/Library_sal.mk sal/osl sal/qa sal/rtl sax/source scaddins/source sc/CppunitTest_sc_inlinearray.mk sc/CppunitTest_sc_sheetview_test.mk schema/libreoffice sc/inc sc/Library_sc.mk sc/Module_sc.mk sc/qa sc/README.md scripting/source sc/sdi sc/source sc/uiconfig sc/UIConfig_scalc.mk sdext/CustomTarget_pdfimport.mk sdext/source sd/inc sd/Library_sd.mk sd/qa sd/sdi sd/source sd/uiconfig sd/util sfx2/Library_sfx.mk sfx2/sdi sfx2/source sfx2/uiconfig sfx2/UIConfig_sfx.mk sl ideshow/source solenv/bin solenv/clang-format solenv/flatpak-manifest.in solenv/gbuild solenv/inc solenv/sanitizers sot/source starmath/source static/CustomTarget_emscripten_fs_image.mk static/source stoc/source svgio/inc svgio/qa svgio/source svl/Library_svl.mk svl/qa svl/source svtools/source svx/CppunitTest_svx_removewhichrange.mk svx/inc svx/Library_svxcore.mk svx/Library_svx.mk svx/sdi svx/source svx/uiconfig svx/UIConfig_svx.mk sw/CppunitTest_sw_core_unocore.mk sw/CppunitTest_sw_embedded_fonts.mk sw/CppunitTest_sw_filter_md.mk sw/CppunitTest_sw_globalfilter.mk sw/CppunitTest_sw_uiwriter11.mk sw/CppunitTest_sw_unowriter.mk sw/CppunitTest_sw_uwriter.mk sw/CppunitTest_sw_writerfilter_dmapper.mk sw/CppunitTest_sw_writerfilter_rtftok.mk sw/CustomTarget_generated.mk sw/inc sw/Library_sw.mk sw/Module_sw.mk sw/qa sw/sdi sw/source sw/uiconfig sysui/desktop test/source tools/source translations ucb/source unoidl/Executable_unoidl-check.mk unoidl/Executable_unoidl-write.mk unotest/source unotools/source uui/inc uui/source vcl/commonfuzzer.mk vcl/Executable_pdf2fodgfuzzer.mk vcl/headless vcl/inc vcl/jsdialog vcl/Library_vcl.mk vcl/Module_vcl.mk vcl/osx vcl/qa vcl/qt5 vcl/quartz vcl/source vcl/unx vcl/win vcl/workben wizards/source writerperfect/source xmloff/CppunitTest_xmloff_text.mk xmloff/CustomTarget_generated.mk xmloff/inc xmloff/IwyuFilter_xmloff.yaml xmloff/qa xmloff/source xmlsecurity/Module_xmlsecurity.mk

Fri, 17 Oct 2025 10:39:16 -0700

Rebased ref, commits from common ancestor:
commit a61ab353cd3157fc055cd854af9443b9be43cfb9
Author:     Michael Meeks <[email protected]>
AuthorDate: Sat Jul 12 12:51:39 2025 +0100
Commit:     Michael Meeks <[email protected]>
CommitDate: Fri Oct 17 17:57:29 2025 +0100

    WIP: implement translation mapper for XML content.
    
    Needs to load content from a stream or storage of course.
    
    Change-Id: Id6e3c0de03f57c68246fd3eee7b4912f9adc218e
    Signed-off-by: Michael Meeks <[email protected]>

diff --git a/xmloff/source/core/xmlimp.cxx b/xmloff/source/core/xmlimp.cxx
index e5edc29a41cd..9765d471d52b 100644
--- a/xmloff/source/core/xmlimp.cxx
+++ b/xmloff/source/core/xmlimp.cxx
@@ -22,6 +22,7 @@
 #include <memory>
 #include <optional>
 
+
 #include <comphelper/diagnose_ex.hxx>
 #include <sal/log.hxx>
 #include <com/sun/star/beans/XPropertySetInfo.hpp>
@@ -65,10 +66,12 @@
 #include <cppuhelper/implbase.hxx>
 #include <cppuhelper/supportsservice.hxx>
 #include <comphelper/extract.hxx>
+#include <comphelper/processfactory.hxx>
 #include <comphelper/documentconstants.hxx>
 #include <comphelper/documentinfo.hxx>
 #include <comphelper/storagehelper.hxx>
 #include <comphelper/attributelist.hxx>
+#include <comphelper/string.hxx>
 #include <unotools/fontcvt.hxx>
 #include <fasttokenhandler.hxx>
 #include <vcl/GraphicExternalLink.hxx>
@@ -78,6 +81,14 @@
 #include <com/sun/star/rdf/XRepositorySupplier.hpp>
 #include <RDFaImportHelper.hxx>
 
+// L10nMapper bits to split out ...
+#include <com/sun/star/uno/Type.hxx>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/io/XTextInputStream2.hpp>
+#include <com/sun/star/io/TextInputStream.hpp>
+#include <com/sun/star/container/XMap.hpp>
+#include <cppuhelper/compbase.hxx>
+
 using ::com::sun::star::beans::XPropertySetInfo;
 
 using namespace ::com::sun::star;
@@ -436,6 +447,119 @@ void SvXMLImport::InitCtor_()
     }
 }
 
+namespace {
+
+    class L10nMapper : public cppu::WeakImplHelper<css::container::XMap>
+    {
+        std::unordered_map<OUString, OUString> maMap;
+
+        L10nMapper() { }
+        virtual ~L10nMapper() override {}
+
+    public:
+        static css::uno::Reference< XMap > load(const 
uno::Reference<css::embed::XStorage> &xStorage)
+        {
+            try {
+                OUString streamName = u"l10n"_ustr;
+                if (xStorage && xStorage->isStreamElement(streamName))
+                {
+                    auto xIn = xStorage->openStreamElement(streamName, 
css::embed::ElementModes::READ|css::embed::ElementModes::NOCREATE);
+                    if (!xIn)
+                        return css::uno::Reference< XMap >();
+
+                    auto xInStrm = uno::Reference<css::io::XInputStream>(xIn, 
UNO_QUERY_THROW);
+
+                    L10nMapper *pMap = new L10nMapper();
+
+                    Reference< XTextInputStream2 > xTextStrm;
+                    xTextStrm = css::io::TextInputStream::create(
+                        comphelper::getProcessComponentContext());
+                    xTextStrm->setInputStream(xInStrm);
+                    xTextStrm->setEncoding(u"utf8"_ustr);
+
+                    // FIXME: our locale
+                    OUString locale = u"fr-FR"_ustr;
+
+                    // Format is <string>
locale  <translated-string>
<repeat>


+                    OUString aKeyString;
+                    size_t nLine = 0;
+                    while (!xTextStrm->isEOF())
+                    {
+                        OUString aStr = xTextStrm->readLine();
+                        nLine++;
+                        if (aStr.getLength() < 1 || aStr[0] == '#')
+                        {
+                            aKeyString = "";
+                            continue;
+                        }
+                        if (aKeyString.isEmpty())
+                            aKeyString = aStr;
+                        else if (aStr.startsWithIgnoreAsciiCase(locale))
+                        {
+                            auto aValues = comphelper::string::split(aStr, '   
');
+                            if (aValues.size() > 1)
+                                pMap->maMap[aKeyString] = aValues[1];
+                            else
+                                pMap->maMap[aKeyString] = OUString(u"invalid 
line "_ustr) + OUString::number(nLine);
+                        }
+                    }
+
+                    xTextStrm->closeInput();
+
+                    // debug
+                    pMap->maMap["_Foo"] = "Baz";
+
+                    return css::uno::Reference< XMap >(pMap);
+                }
+            }
+            catch (Exception const&)
+            {
+                DBG_UNHANDLED_EXCEPTION("xmloff.core", "exception getting 
BuildId");
+            }
+            return css::uno::Reference< XMap >();
+        }
+
+        // XMap
+        virtual Type SAL_CALL getKeyType() override   { return 
cppu::UnoType<OUString>::get(); };
+        virtual Type SAL_CALL getValueType() override { return 
cppu::UnoType<OUString>::get(); };
+        virtual void SAL_CALL clear() override { maMap.clear(); }
+        virtual sal_Bool SAL_CALL containsKey( const Any& ) override
+        {
+            throw css::uno::RuntimeException(u"not implemented"_ustr);
+        }
+        virtual sal_Bool SAL_CALL containsValue( const Any& ) override
+        {
+            throw css::uno::RuntimeException(u"not implemented"_ustr);
+        }
+        virtual Any SAL_CALL get( const Any& key ) override
+        {
+            auto it = maMap.find(key.get<OUString>());
+            if (it == maMap.end())
+                return css::uno::Any(key); // or throw ?
+            else
+                return css::uno::Any(it->second);
+        }
+        virtual Any SAL_CALL put( const Any&, const Any& ) override
+        {
+            throw css::uno::RuntimeException(u"not implemented"_ustr);
+        }
+        virtual Any SAL_CALL remove( const Any& ) override
+        {
+            throw css::uno::RuntimeException(u"not implemented"_ustr);
+        }
+
+        // XElementAccess (base)
+        virtual Type SAL_CALL getElementType() override
+        {
+            throw css::uno::RuntimeException(u"not implemented"_ustr);
+        }
+        virtual sal_Bool SAL_CALL hasElements() override
+        {
+            return !maMap.empty();
+        }
+};
+}
+
 SvXMLImport::SvXMLImport(
     const css::uno::Reference< css::uno::XComponentContext >& xContext,
     OUString const & implementationName,
@@ -457,6 +581,7 @@ SvXMLImport::SvXMLImport(
     SAL_WARN_IF( !xContext.is(), "xmloff.core", "got no service manager" );
     InitCtor_();
     mxParser = xml::sax::FastParser::create( xContext );
+
     setNamespaceHandler( maNamespaceHandler );
     setTokenHandler( xTokenHandler  );
     if ( !bIsNSMapsInitialized )
@@ -1067,7 +1192,12 @@ void SAL_CALL SvXMLImport::initialize( const 
uno::Sequence< uno::Any >& aArgumen
     }
 
     uno::Reference<lang::XInitialization> const xInit(mxParser, 
uno::UNO_QUERY_THROW);
-    xInit->initialize( { Any(u"IgnoreMissingNSDecl"_ustr) });
+
+    css::uno::Reference< XMap > xMap = L10nMapper::load(GetSourceStorage());
+    css::uno::Sequence< css::uno::Any > args{
+        css::uno::Any(OUString(u"IgnoreMissingNSDecl"_ustr)),
+        css::uno::Any(xMap) };
+    xInit->initialize(args);
 }
 
 // XServiceInfo
commit cc582e2a2a85c515a576cb2ec5c7c5cce012af54
Author:     Michael Meeks <[email protected]>
AuthorDate: Sat Jul 12 12:03:15 2025 +0100
Commit:     Michael Meeks <[email protected]>
CommitDate: Mon Oct 13 11:45:40 2025 +0100

    Add content mapping into XFastParser.
    
    Ideally this gets done for free in the parsing thread.
    
    Change-Id: I2392fd51c8152c48cf03ac9ed26e6f62aa6ac4f8
    Signed-off-by: Michael Meeks <[email protected]>

diff --git a/sax/source/fastparser/fastparser.cxx 
b/sax/source/fastparser/fastparser.cxx
index 8815cd58329e..5b217224e6b2 100644
--- a/sax/source/fastparser/fastparser.cxx
+++ b/sax/source/fastparser/fastparser.cxx
@@ -26,6 +26,7 @@
 #include <com/sun/star/lang/DisposedException.hpp>
 #include <com/sun/star/lang/IllegalArgumentException.hpp>
 #include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/container/XMap.hpp>
 #include <com/sun/star/xml/sax/FastToken.hpp>
 #include <com/sun/star/xml/sax/SAXParseException.hpp>
 #include <com/sun/star/xml/sax/XFastContextHandler.hpp>
@@ -37,6 +38,7 @@
 #include <sal/log.hxx>
 #include <salhelper/thread.hxx>
 #include <comphelper/diagnose_ex.hxx>
+#include <comphelper/string.hxx>
 #include <o3tl/string_view.hxx>
 
 #include <queue>
@@ -270,6 +272,7 @@ public:
     void produce( bool bForceFlush = false );
     bool m_bIgnoreMissingNSDecl;
     bool m_bDisableThreadedParser;
+    css::uno::Reference<css::container::XMap> mxMap; /// _ prefix string 
mapper for translation
 
 private:
     bool consume(EventList&);
@@ -1351,6 +1354,10 @@ void FastSaxParserImpl::sendPendingCharacters()
 {
     Entity& rEntity = getEntity();
     OUString sChars( pendingCharacters.data(), pendingCharacters.size(), 
RTL_TEXTENCODING_UTF8 );
+
+    if (sChars[0] == '_' && mxMap)
+        mxMap->get(uno::Any(sChars)) >>= sChars;
+
     if (rEntity.mbEnableThreads)
     {
         Event& rEvent = rEntity.getEvent( CallbackType::CHARACTERS );
@@ -1454,15 +1461,21 @@ FastSaxParser::initialize(css::uno::Sequence< 
css::uno::Any > const& rArguments)
     if ( !(rArguments[0] >>= str) )
         throw IllegalArgumentException();
 
-    if ( str == "IgnoreMissingNSDecl" )
-        mpImpl->m_bIgnoreMissingNSDecl = true;
-    else if ( str == "DoSmeplease" )
-        ; //just ignore as this is already immune to billion laughs
-    else if ( str == "DisableThreadedParser" )
-        mpImpl->m_bDisableThreadedParser = true;
-    else
-        throw IllegalArgumentException();
+    auto opts = comphelper::string::split(str, ',');
+    for (auto &s : opts)
+    {
+        if ( s == "IgnoreMissingNSDecl" )
+            mpImpl->m_bIgnoreMissingNSDecl = true;
+        else if ( s == "DoSmeplease" )
+            ; //just ignore as this is already immune to billion laughs
+        else if ( s == "DisableThreadedParser" )
+            mpImpl->m_bDisableThreadedParser = true;
+        else
+            throw IllegalArgumentException();
+    }
 
+    if (rArguments.size() > 1)
+        rArguments[1] >>= mpImpl->mxMap;
 }
 
 void FastSaxParser::parseStream( const xml::sax::InputSource& aInputSource )
commit b2c5d52f733cca2656daa0a2cfdd85a1108635f4
Author:     Miklos Vajna <[email protected]>
AuthorDate: Mon Oct 13 09:02:10 2025 +0200
Commit:     Caolán McNamara <[email protected]>
CommitDate: Mon Oct 13 09:54:56 2025 +0200

    tdf#168559 PPTX imp: fix missing list style for outline shapes on master 
pages
    
    Load oox/qa/unit/data/outliner-list-style.odp into Impress, save as
    PPTX, load it the PPTX, save the PPTX again (source is now PPTX), open
    it again, go to the end of the first bullet, enter, tab, indent grows to
    a large value.
    
    What happens is that the PPTX export could write the correct list style
    for the shape (based on the master page) when the source was ODP, but it
    can't do the same when the source was PPTX, so after all this a PPTX
    import problem.
    
    Fix this by improving the formatting of the outliner shape on master
    pages: if the outliner shape was empty, then we don't insert formatted
    text body, so instead try to format the existing content of the outliner
    shape, given that such shapes have pre-existing content on the master
    page.
    
    Notes:
    - Do this only for outliner shapes, CppunitTest_sd_import_tests2's
      testTdf103792 is a sample that shows doing this for all placeholders
      on the master page is not correct.
    - The first line offset is typically negative, but do guard for positive
      values, otherwise the exported ODP would be invalid, see
      CppunitTest_sd_export_tests's testTdf128550.
    - It's possible to have outliner shapes with custom prompt text, which
      is typically not bulleted, so leave those alone, otherwise we would
      lose that prompt text, as visible in
      CppunitTest_sd_export_tests-ooxml4's testCustomPromptTexts.
    
    Change-Id: I5b1768a4a4b9cb8494785a38e040dacdf4ab5289
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192264
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Caolán McNamara <[email protected]>

diff --git a/oox/inc/drawingml/textliststyle.hxx 
b/oox/inc/drawingml/textliststyle.hxx
index 88de4621de13..282391452456 100644
--- a/oox/inc/drawingml/textliststyle.hxx
+++ b/oox/inc/drawingml/textliststyle.hxx
@@ -61,7 +61,7 @@ public:
 
     /// 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);
+                              std::optional<size_t> nIgnoreLevel);
 
 #ifdef DBG_UTIL
     void dump() const;
diff --git a/oox/qa/unit/drawingml.cxx b/oox/qa/unit/drawingml.cxx
index a442ef1f28ef..e5fc762aa12d 100644
--- a/oox/qa/unit/drawingml.cxx
+++ b/oox/qa/unit/drawingml.cxx
@@ -31,6 +31,7 @@
 #include <com/sun/star/util/XTheme.hpp>
 #include <com/sun/star/drawing/HomogenMatrix3.hpp>
 #include <com/sun/star/drawing/PointSequenceSequence.hpp>
+#include <com/sun/star/drawing/XMasterPagesSupplier.hpp>
 
 #include <docmodel/uno/UnoGradientTools.hxx>
 #include <docmodel/uno/UnoComplexColor.hxx>
@@ -786,7 +787,7 @@ CPPUNIT_TEST_FIXTURE(OoxDrawingmlTest, 
testDOCXVerticalLineRotation)
     CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), nRotateAngle);
 }
 
-CPPUNIT_TEST_FIXTURE(OoxDrawingmlTest, 
testPPTXImportOutlinerListStyleDirectFormat)
+CPPUNIT_TEST_FIXTURE(OoxDrawingmlTest, testPPTXImportOutlinerListStyle)
 {
     // Given a PPTX file with a slide with an outline shape:
     // When loading that document:
@@ -818,6 +819,27 @@ CPPUNIT_TEST_FIXTURE(OoxDrawingmlTest, 
testPPTXImportOutlinerListStyleDirectForm
     // - 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.
+
+    // Also check that the master slide's outliner has the correct numbering 
rules:
+    uno::Reference<drawing::XMasterPagesSupplier> 
xMasterPagesSupplier(mxComponent, uno::UNO_QUERY);
+    uno::Reference<drawing::XDrawPage> xMasterPage(
+        xMasterPagesSupplier->getMasterPages()->getByIndex(0), uno::UNO_QUERY);
+    // First would be the title shape.
+    uno::Reference<text::XTextRange> xMasterShape(xMasterPage->getByIndex(1), 
uno::UNO_QUERY);
+    xShapeText.set(xMasterShape->getText(), uno::UNO_QUERY);
+    xParagraphs = xShapeText->createEnumeration();
+    // As a sample, check the 2nd paragraph's level 3 left margin.
+    xParagraphs->nextElement();
+    xParagraph.set(xParagraphs->nextElement(), uno::UNO_QUERY);
+    xParagraph->getPropertyValue(u"NumberingRules"_ustr) >>= xNumberingRules;
+    aMap = comphelper::SequenceAsHashMap(xNumberingRules->getByIndex(2));
+    nLeftMargin = 0;
+    aMap["LeftMargin"] >>= nLeftMargin;
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: 2388
+    // - Actual  : 3600
+    // i.e. the master was wrong, re-export to PPTX could not write the 
correct numbering rules.
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2388), nLeftMargin);
 }
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/oox/source/drawingml/shape.cxx b/oox/source/drawingml/shape.cxx
index 897a7ac9fc51..8967f616c958 100644
--- a/oox/source/drawingml/shape.cxx
+++ b/oox/source/drawingml/shape.cxx
@@ -71,6 +71,7 @@
 #include <com/sun/star/awt/FontWeight.hpp>
 #include <com/sun/star/graphic/XGraphic.hpp>
 #include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
 #include <com/sun/star/xml/dom/XDocument.hpp>
 #include <com/sun/star/xml/sax/XFastSAXSerializable.hpp>
@@ -914,6 +915,45 @@ Degree100 lcl_MSORotateAngleToAPIAngle(const sal_Int32 
nMSORotationAngle)
     // API RotateAngle has opposite direction than nMSORotationAngle, thus 
'minus'.
     return NormAngle36000(-nAngle);
 }
+
+void PushMasterTextListStyleToMasterShapeParagraphs(TextListStyle& 
rMasterTextListStyle, const uno::Reference<drawing::XShape>& xShape)
+{
+    uno::Reference<text::XTextRange> xShapeText(xShape, UNO_QUERY);
+    if (!xShapeText.is())
+    {
+        return;
+    }
+
+    uno::Reference<container::XEnumerationAccess> xText(xShapeText->getText(), 
uno::UNO_QUERY);
+    if (!xText.is())
+    {
+        return;
+    }
+
+    uno::Reference<container::XEnumeration> xParagraphs = 
xText->createEnumeration();
+    if (!xParagraphs.is())
+    {
+        return;
+    }
+
+    while (xParagraphs->hasMoreElements())
+    {
+        uno::Reference<beans::XPropertySet> 
xParagraph(xParagraphs->nextElement(), uno::UNO_QUERY);
+        if (!xParagraph.is())
+        {
+            continue;
+        }
+
+        uno::Reference<container::XIndexReplace> xNumberingRules;
+        if (!(xParagraph->getPropertyValue("NumberingRules") >>= 
xNumberingRules))
+        {
+            continue;
+        }
+
+        rMasterTextListStyle.pushToNumberingRules(xNumberingRules, 
std::nullopt);
+        xParagraph->setPropertyValue("NumberingRules", 
uno::Any(xNumberingRules));
+    }
+}
 }
 
 Reference< XShape > const & Shape::createAndInsert(
@@ -1286,6 +1326,7 @@ Reference< XShape > const & Shape::createAndInsert(
     }
 
     Reference< XPropertySet > xSet( mxShape, UNO_QUERY );
+    bool bPlaceholderWithCustomPrompt = pPlaceholder && 
pPlaceholder->getCustomPrompt();
     if (xSet.is())
     {
         if (bTopWriterLine && !maSize.Width)
@@ -2209,6 +2250,13 @@ Reference< XShape > const & Shape::createAndInsert(
                 }
             }
         }
+        else if (mpTextBody && aServiceName == 
"com.sun.star.presentation.OutlinerShape"
+                 && mpMasterTextListStyle && !bPlaceholderWithCustomPrompt)
+        {
+            // If mpTextBody is not empty, then the insertAt() above inserts 
formatted text. If it's
+            // empty, then we format the existing text of the outliner shape 
here.
+            
PushMasterTextListStyleToMasterShapeParagraphs(*mpMasterTextListStyle, mxShape);
+        }
         else if (mbTextBox)
         {
             // No drawingML text, but WPS text is expected: save the theme
diff --git a/oox/source/drawingml/textliststyle.cxx 
b/oox/source/drawingml/textliststyle.cxx
index ee695908d48a..0ad0b5108b27 100644
--- a/oox/source/drawingml/textliststyle.cxx
+++ b/oox/source/drawingml/textliststyle.cxx
@@ -70,7 +70,7 @@ void TextListStyle::apply( const TextListStyle& 
rTextListStyle )
 }
 
 void TextListStyle::pushToNumberingRules(const 
uno::Reference<container::XIndexReplace>& xNumRules,
-                                         size_t nIgnoreLevel)
+                                         std::optional<size_t> oIgnoreLevel)
 {
     TextParagraphPropertiesArray& rListStyle = getListStyle();
     size_t nLevels = xNumRules->getCount();
@@ -81,7 +81,7 @@ void TextListStyle::pushToNumberingRules(const 
uno::Reference<container::XIndexR
 
     for (size_t nLevel = 0; nLevel < nLevels; ++nLevel)
     {
-        if (nLevel == nIgnoreLevel)
+        if (oIgnoreLevel && nLevel == *oIgnoreLevel)
         {
             continue;
         }
@@ -96,7 +96,14 @@ void TextListStyle::pushToNumberingRules(const 
uno::Reference<container::XIndexR
         }
         if (rLevel.getFirstLineIndentation())
         {
-            aMap["FirstLineOffset"] <<= *rLevel.getFirstLineIndentation();
+            sal_Int32 nFirstLineIndentation = 
*rLevel.getFirstLineIndentation();
+            if (nFirstLineIndentation > 0)
+            {
+                // The opposite of this would be exported as 
<style:list-level-properties
+                // text:min-label-width="..."> to ODF, where negative values 
are not allowed.
+                nFirstLineIndentation = 0;
+            }
+            aMap["FirstLineOffset"] <<= nFirstLineIndentation;
             bChanged = true;
         }
 
commit 7504cf29784cb1f04de1364e2f0aa68698809495
Author:     Caolán McNamara <[email protected]>
AuthorDate: Sun Oct 12 12:48:19 2025 +0100
Commit:     Miklos Vajna <[email protected]>
CommitDate: Mon Oct 13 09:35:30 2025 +0200

    ofz#451188537 Null-dereference READ
    
    Change-Id: Ida4e0deb43eb04ec0286645bb84d3c6bbb8e1e5c
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192246
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>

diff --git a/vcl/source/filter/ipdf/pdfread.cxx 
b/vcl/source/filter/ipdf/pdfread.cxx
index b705f2b76263..f8c83f4b942c 100644
--- a/vcl/source/filter/ipdf/pdfread.cxx
+++ b/vcl/source/filter/ipdf/pdfread.cxx
@@ -417,6 +417,11 @@ size_t ImportPDFUnloaded(SvStream& rStream, 
std::vector<PDFGraphicResult>& rGrap
         Graphic aGraphic(pGfxLink, nPageIndex);
 
         auto pPage = pPdfDocument->openPage(nPageIndex);
+        if (!pPage)
+        {
+            SAL_WARN("vcl.filter", "ImportPDF: unable to open page: " << 
nPageIndex);
+            continue;
+        }
 
         std::vector<PDFGraphicAnnotation> aPDFGraphicAnnotations
             = findAnnotations(pPage, aPageSize);
commit 44cee3c6c37b1ca8f19e7ffbd7193be1bcd0fc58
Author:     Caolán McNamara <[email protected]>
AuthorDate: Thu Oct 9 20:15:14 2025 +0100
Commit:     Miklos Vajna <[email protected]>
CommitDate: Mon Oct 13 08:59:35 2025 +0200

    add oss-fuzz pdf2fodg fuzzer
    
    Change-Id: I5cb7653339c6280ee7b8f208ce92a771cb1dcc17
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192129
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>

diff --git a/Repository.mk b/Repository.mk
index 8ee3c0d3c26f..805e362a96ad 100644
--- a/Repository.mk
+++ b/Repository.mk
@@ -141,6 +141,7 @@ $(eval $(call 
gb_Helper_register_executables_for_install,OOO,brand, \
        $(call gb_Helper_optional,FUZZERS,602fuzzer) \
        $(call gb_Helper_optional,FUZZERS,lwpfuzzer) \
        $(call gb_Helper_optional,FUZZERS,olefuzzer) \
+       $(call gb_Helper_optional,FUZZERS,pdf2fodgfuzzer) \
        $(call gb_Helper_optional,FUZZERS,pptfuzzer) \
        $(call gb_Helper_optional,FUZZERS,rtffuzzer) \
        $(call gb_Helper_optional,FUZZERS,rtf2pdffuzzer) \
diff --git a/bin/oss-fuzz-setup.sh b/bin/oss-fuzz-setup.sh
index f53daa5ab988..ee131c4357e5 100755
--- a/bin/oss-fuzz-setup.sh
+++ b/bin/oss-fuzz-setup.sh
@@ -81,6 +81,7 @@ cd $SRC
 curl --no-progress-meter -S \
     -C - -O 
https://raw.githubusercontent.com/google/fuzzing/master/dictionaries/gif.dict \
     -C - -O 
https://raw.githubusercontent.com/google/fuzzing/master/dictionaries/jpeg.dict \
+    -C - -O 
https://raw.githubusercontent.com/google/fuzzing/master/dictionaries/pdf.dict \
     -C - -O 
https://raw.githubusercontent.com/google/fuzzing/master/dictionaries/png.dict \
     -C - -O 
https://raw.githubusercontent.com/google/fuzzing/master/dictionaries/tiff.dict \
     -C - -O 
https://raw.githubusercontent.com/google/fuzzing/master/dictionaries/xml.dict \
@@ -111,6 +112,8 @@ mkdir -p afl-testcases && cd afl-testcases/ && tar xf 
$SRC/afl_testcases.tgz &&
     zip -q $SRC/bmpfuzzer_seed_corpus.zip afl-testcases/bmp*/full/images/* && \
     zip -q $SRC/pngfuzzer_seed_corpus.zip afl-testcases/png*/full/images/* && \
     zip -q $SRC/webpfuzzer_seed_corpus.zip afl-testcases/webp*/full/images/*
+
+# TTF/OTF/SFT
 # using github's svn view to use svn export as a hack to just export part of 
the git repo
 # svn support turned off now: 
https://github.blog/2023-01-20-sunsetting-subversion-support/
 # and git sparse checkout is a total pain
@@ -121,6 +124,15 @@ mkdir -p $SRC/sample-sft-fonts/adobe
 curl --no-progress-meter -S \
     -C - -o $SRC/sample-sft-fonts/adobe/AdobeVFPrototype.otf 
https://github.com/adobe-fonts/adobe-variable-font-prototype/releases/download/1.005a/AdobeVFPrototype.otf
 zip -qr $SRC/sftfuzzer_seed_corpus.zip $SRC/sample-sft-fonts
+
+# PDF
+git clone --depth 1 https://github.com/strongcourage/fuzzing-corpus.git && \
+    zip -q -r $SRC/pdffuzzer_seed_corpus.zip fuzzing-corpus/pdf/* && \
+    rm -rf fuzzing-corpus && \
+git clone --depth 1 https://github.com/mozilla/pdf.js pdf.js && \
+    zip -q $SRC/pdffuzzer_seed_corpus.zip pdf.js/test/pdfs/*.pdf && \
+    rm -rf pdf.js
+
 curl --no-progress-meter -S -C - 
https://storage.googleapis.com/skia-fuzzer/oss-fuzz/svg_seed_corpus.zip -o 
svgfuzzer_seed_corpus.zip
 curl --no-progress-meter -S \
     -C - -O https://dev-www.libreoffice.org/corpus/wmffuzzer_seed_corpus.zip \
@@ -170,5 +182,6 @@ cp fodtfuzzer_seed_corpus.zip fodt2pdffuzzer_seed_corpus.zip
 cp rtffuzzer_seed_corpus.zip rtf2pdffuzzer_seed_corpus.zip
 cp fodsfuzzer_seed_corpus.zip fods2xlsfuzzer_seed_corpus.zip
 cp htmlfuzzer_seed_corpus.zip schtmlfuzzer_seed_corpus.zip
+cp pdffuzzer_seed_corpus.zip pdf2fodgfuzzer_seed_corpus.zip
 
 echo end downloading dependencies at `date -u`
diff --git a/distro-configs/LibreOfficeOssFuzz.conf 
b/distro-configs/LibreOfficeOssFuzz.conf
index 4524a8760f82..a0364d18cee5 100644
--- a/distro-configs/LibreOfficeOssFuzz.conf
+++ b/distro-configs/LibreOfficeOssFuzz.conf
@@ -20,8 +20,7 @@
 --disable-lpsolve
 --disable-mariadb-sdbc
 --disable-odk
---disable-pdfimport
---disable-pdfium
+--disable-poppler
 --disable-postgresql-sdbc
 --disable-sdremote
 --disable-skia
diff --git a/external/afdko/mergeFonts_crash.patch 
b/external/afdko/mergeFonts_crash.patch
index b14ea4e1ee91..87c0c1cd9e9c 100644
--- a/external/afdko/mergeFonts_crash.patch
+++ b/external/afdko/mergeFonts_crash.patch
@@ -29,3 +29,16 @@
      if (start > 0)
          memmove(str, &str[start], (end - start) + 2);
      if (strlen(str) == 0) {
+@@ -2775,6 +2775,7 @@
+     free(h);
+ }
+ 
++#if 0
+ /* Main program. */
+ int CTL_CDECL main(int argc, char *argv[]) {
+     txCtx h;
+@@ -2826,3 +2827,4 @@
+ 
+     return 0;
+ }
++#endif
diff --git a/vcl/Executable_pdf2fodgfuzzer.mk b/vcl/Executable_pdf2fodgfuzzer.mk
new file mode 100644
index 000000000000..9ea0d8d0dd8f
--- /dev/null
+++ b/vcl/Executable_pdf2fodgfuzzer.mk
@@ -0,0 +1,48 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+include $(SRCDIR)/vcl/commonfuzzer.mk
+
+$(eval $(call gb_Executable_Executable,pdf2fodgfuzzer))
+
+$(eval $(call gb_Executable_use_api,pdf2fodgfuzzer,\
+    offapi \
+    udkapi \
+))
+
+$(eval $(call gb_Executable_use_externals,pdf2fodgfuzzer,\
+    $(fuzzer_externals) \
+))
+
+$(eval $(call gb_Executable_set_include,pdf2fodgfuzzer,\
+    $$(INCLUDE) \
+    -I$(SRCDIR)/vcl/inc \
+))
+
+$(eval $(call gb_Executable_use_libraries,pdf2fodgfuzzer,\
+    $(fuzzer_draw_libraries) \
+    $(fuzzer_core_libraries) \
+    pdffilter \
+))
+
+$(eval $(call gb_Executable_use_static_libraries,pdf2fodgfuzzer,\
+    $(fuzzer_statics) \
+    fuzzer_draw \
+))
+
+$(eval $(call gb_Executable_add_exception_objects,pdf2fodgfuzzer,\
+    vcl/workben/pdf2fodgfuzzer \
+))
+
+$(eval $(call gb_Executable_add_libs,pdf2fodgfuzzer,\
+    $(LIB_FUZZING_ENGINE) \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/vcl/Module_vcl.mk b/vcl/Module_vcl.mk
index 292861585d44..21225339f266 100644
--- a/vcl/Module_vcl.mk
+++ b/vcl/Module_vcl.mk
@@ -182,6 +182,7 @@ $(eval $(call gb_Module_add_targets,vcl,\
     Executable_602fuzzer \
     Executable_lwpfuzzer \
     Executable_olefuzzer \
+    Executable_pdf2fodgfuzzer \
     Executable_pptfuzzer \
     Executable_rtffuzzer \
     Executable_rtf2pdffuzzer \
diff --git a/vcl/commonfuzzer.mk b/vcl/commonfuzzer.mk
index 5302b572e450..3f13d5775ff7 100644
--- a/vcl/commonfuzzer.mk
+++ b/vcl/commonfuzzer.mk
@@ -36,6 +36,8 @@ fuzzer_externals = \
     libpng \
     libtiff \
     libwebp \
+    pdfium \
+    md4c \
     openssl \
     expat \
     mythes \
@@ -136,6 +138,7 @@ fuzzer_core_libraries = \
     mtfrenderer \
     canvasfactory \
     vclcanvas \
+    pdfimport \
     xof \
     xmlfa \
     xmlfd \
diff --git a/vcl/workben/pdf2fodgfuzzer.cxx b/vcl/workben/pdf2fodgfuzzer.cxx
new file mode 100644
index 000000000000..be304cfeba90
--- /dev/null
+++ b/vcl/workben/pdf2fodgfuzzer.cxx
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <tools/stream.hxx>
+#include <vcl/FilterConfigItem.hxx>
+#include "commonfuzzer.hxx"
+
+extern "C" bool TestFODGExportPDF(SvStream& rStream);
+
+extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv)
+{
+    TypicalFuzzerInitialize(argc, argv);
+    return 0;
+}
+
+extern "C" void* SdCreateDialogFactory() { return nullptr; }
+
+extern "C" void* com_sun_star_comp_Draw_VisioImportFilter_get_implementation() 
{ return nullptr; }
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
+{
+    SvMemoryStream aStream(const_cast<uint8_t*>(data), size, StreamMode::READ);
+    (void)TestFODGExportPDF(aStream);
+    return 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/workben/pdf2fodgfuzzer.options 
b/vcl/workben/pdf2fodgfuzzer.options
new file mode 100644
index 000000000000..13baefc2d7be
--- /dev/null
+++ b/vcl/workben/pdf2fodgfuzzer.options
@@ -0,0 +1,3 @@
+[libfuzzer]
+max_len = 1024
+dict = pdf.dict
commit 81ed490103dcf8b07834de3bf3a76f7086b16f11
Author:     Caolán McNamara <[email protected]>
AuthorDate: Thu Oct 9 20:05:27 2025 +0100
Commit:     Miklos Vajna <[email protected]>
CommitDate: Mon Oct 13 08:58:32 2025 +0200

    add pdf2fodg fuzzer entry point
    
    Change-Id: If698320def5fe90140f63f9a0b4b73a57c8fa791
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192128
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>

diff --git a/sd/source/filter/pdf/sdpdffilter.cxx 
b/sd/source/filter/pdf/sdpdffilter.cxx
index ee1862a1c968..14bd593d54d7 100644
--- a/sd/source/filter/pdf/sdpdffilter.cxx
+++ b/sd/source/filter/pdf/sdpdffilter.cxx
@@ -19,6 +19,7 @@
 
 #include <sal/config.h>
 
+#include <osl/process.h>
 #include <sfx2/docfile.hxx>
 #include <svx/svdograf.hxx>
 #include <o3tl/safeint.hxx>
@@ -31,11 +32,23 @@
 #include <vcl/pdfread.hxx>
 
 #include <Annotation.hxx>
+#include <DrawDocShell.hxx>
+#include <unomodel.hxx>
 
+#include <com/sun/star/document/XExporter.hpp>
+#include <com/sun/star/document/XFilter.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
 #include <com/sun/star/office/XAnnotation.hpp>
 #include <com/sun/star/text/XText.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
 
 #include <basegfx/polygon/b2dpolygontools.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <unotools/streamwrap.hxx>
+#include <unotools/tempfile.hxx>
 #include <unotools/ucbstreamhelper.hxx>
 
 using namespace css;
@@ -245,4 +258,78 @@ bool SdPdfFilter::Import()
 
 bool SdPdfFilter::Export() { return false; }
 
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestFODGExportPDF(SvStream& rStream)
+{
+    bool bResetEnvVar = false;
+    if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
+    {
+        bResetEnvVar = true;
+        osl_setEnvironment(OUString("LO_IMPORT_USE_PDFIUM").pData, 
OUString("1").pData);
+    }
+    comphelper::ScopeGuard aPDFiumEnvVarGuard([&]() {
+        if (bResetEnvVar)
+            osl_clearEnvironment(OUString("LO_IMPORT_USE_PDFIUM").pData);
+    });
+
+    const uno::Reference<uno::XComponentContext>& xContext(
+        comphelper::getProcessComponentContext());
+    uno::Reference<css::frame::XModel2> xModel(
+        xContext->getServiceManager()->createInstanceWithContext(
+            u"com.sun.star.drawing.DrawingDocument"_ustr, xContext),
+        uno::UNO_QUERY_THROW);
+
+    uno::Reference<css::frame::XLoadable> xModelLoad(xModel, 
uno::UNO_QUERY_THROW);
+    xModelLoad->initNew();
+
+    SdXImpressDocument* pDrawDoc = 
dynamic_cast<SdXImpressDocument*>(xModel.get());
+
+    bool ret = ImportPDF(rStream, *pDrawDoc->GetDoc());
+
+    if (ret)
+    {
+        utl::TempFileFast aTempFile;
+
+        uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(
+            comphelper::getProcessServiceFactory());
+        uno::Reference<uno::XInterface> xInterface(
+            
xMultiServiceFactory->createInstance(u"com.sun.star.comp.Writer.XmlFilterAdaptor"_ustr),
+            uno::UNO_QUERY);
+
+        css::uno::Sequence<OUString> aUserData{ 
u"com.sun.star.comp.filter.OdfFlatXml"_ustr,
+                                                u""_ustr,
+                                                
u"com.sun.star.comp.Draw.XMLOasisImporter"_ustr,
+                                                
u"com.sun.star.comp.Draw.XMLOasisExporter"_ustr,
+                                                u""_ustr,
+                                                u""_ustr,
+                                                u"true"_ustr };
+        uno::Sequence<beans::PropertyValue> 
aAdaptorArgs(comphelper::InitPropertySequence({
+            { "UserData", uno::Any(aUserData) },
+        }));
+        css::uno::Sequence<uno::Any> aOuterArgs{ uno::Any(aAdaptorArgs) };
+
+        uno::Reference<lang::XInitialization> xInit(xInterface, 
uno::UNO_QUERY_THROW);
+        xInit->initialize(aOuterArgs);
+
+        uno::Reference<document::XFilter> xFODGFilter(xInterface, 
uno::UNO_QUERY);
+        uno::Reference<document::XExporter> xExporter(xFODGFilter, 
uno::UNO_QUERY);
+        xExporter->setSourceDocument(xModel);
+
+        uno::Reference<io::XOutputStream> xOutputStream(
+            new 
utl::OStreamWrapper(*aTempFile.GetStream(StreamMode::READWRITE)));
+
+        uno::Sequence<beans::PropertyValue> 
aDescriptor(comphelper::InitPropertySequence(
+            { { "FilterName", uno::Any(u"OpenDocument Drawing Flat XML"_ustr) 
},
+              { "OutputStream", uno::Any(xOutputStream) },
+              { "FilterOptions",
+                uno::Any(
+                    
u"{\"DecomposePDF\":{\"type\":\"boolean\",\"value\":\"true\"}}"_ustr) } }));
+        xFODGFilter->filter(aDescriptor);
+    }
+
+    css::uno::Reference<css::util::XCloseable> xClose(xModel, 
css::uno::UNO_QUERY);
+    xClose->close(false);
+
+    return ret;
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/workben/fftester.cxx b/vcl/workben/fftester.cxx
index 75d511b1278f..9a1202ee47d9 100644
--- a/vcl/workben/fftester.cxx
+++ b/vcl/workben/fftester.cxx
@@ -472,6 +472,16 @@ SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv)
             SvFileStream aFileStream(out, StreamMode::READ);
             ret = static_cast<int>((*pfnImport)(aFileStream));
         }
+        else if (strcmp(argv[2], "pdf2fodg") == 0)
+        {
+            static FFilterCall pfnImport(nullptr);
+            if (!pfnImport)
+            {
+                pfnImport = load(u"libsdlo.so", "TestFODGExportPDF");
+            }
+            SvFileStream aFileStream(out, StreamMode::READ);
+            ret = static_cast<int>((*pfnImport)(aFileStream));
+        }
         else if (strcmp(argv[2], "qpw") == 0)
         {
             static FFilterCall pfnImport(nullptr);
commit 149a36915df7558f740bb7607f1ed1f05d7a34be
Author:     Caolán McNamara <[email protected]>
AuthorDate: Thu Oct 9 13:15:10 2025 +0100
Commit:     Miklos Vajna <[email protected]>
CommitDate: Mon Oct 13 08:55:10 2025 +0200

    tweak pdf import to allow import from SvStream
    
    Change-Id: I3c602b01195b1a89072c22716ad9f241529b2a82
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192127
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>

diff --git a/sd/source/filter/pdf/sdpdffilter.cxx 
b/sd/source/filter/pdf/sdpdffilter.cxx
index 84c3e9bb01a9..ee1862a1c968 100644
--- a/sd/source/filter/pdf/sdpdffilter.cxx
+++ b/sd/source/filter/pdf/sdpdffilter.cxx
@@ -36,6 +36,7 @@
 #include <com/sun/star/text/XText.hpp>
 
 #include <basegfx/polygon/b2dpolygontools.hxx>
+#include <unotools/ucbstreamhelper.hxx>
 
 using namespace css;
 
@@ -46,30 +47,27 @@ SdPdfFilter::SdPdfFilter(SfxMedium& rMedium, 
sd::DrawDocShell& rDocShell)
 
 SdPdfFilter::~SdPdfFilter() {}
 
-bool SdPdfFilter::Import()
+static bool ImportPDF(SvStream& rStream, SdDrawDocument& rDocument)
 {
-    const OUString aFileName(
-        
mrMedium.GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE));
-
     std::vector<vcl::PDFGraphicResult> aGraphics;
-    if (vcl::ImportPDFUnloaded(aFileName, aGraphics) == 0)
+    if (vcl::ImportPDFUnloaded(rStream, aGraphics) == 0)
         return false;
 
-    bool bWasLocked = mrDocument.isLocked();
-    mrDocument.setLock(true);
-    const bool bSavedUndoEnabled = mrDocument.IsUndoEnabled();
-    mrDocument.EnableUndo(false);
-    mrDocument.setPDFDocument(true);
+    bool bWasLocked = rDocument.isLocked();
+    rDocument.setLock(true);
+    const bool bSavedUndoEnabled = rDocument.IsUndoEnabled();
+    rDocument.EnableUndo(false);
+    rDocument.setPDFDocument(true);
 
-    SdrModel& rModel = mrDocument;
+    SdrModel& rModel = rDocument;
 
     // Add as many pages as we need up-front.
-    mrDocument.CreateFirstPages();
+    rDocument.CreateFirstPages();
     sal_uInt16 nPageToDuplicate = 0;
     for (size_t i = 0; i < aGraphics.size() - 1; ++i)
     {
         // duplicating the last page is cheaper than repeatedly duplicating 
the first one
-        nPageToDuplicate = mrDocument.DuplicatePage(nPageToDuplicate);
+        nPageToDuplicate = rDocument.DuplicatePage(nPageToDuplicate);
     }
 
     for (vcl::PDFGraphicResult const& rPDFGraphicResult : aGraphics)
@@ -81,7 +79,7 @@ bool SdPdfFilter::Import()
         assert(nPageNumber >= 0 && o3tl::make_unsigned(nPageNumber) < 
aGraphics.size());
 
         // Create the page and insert the Graphic.
-        SdPage* pPage = mrDocument.GetSdPage(nPageNumber, PageKind::Standard);
+        SdPage* pPage = rDocument.GetSdPage(nPageNumber, PageKind::Standard);
         if (!pPage) // failed to duplicate page, out of memory?
             return false;
 
@@ -229,11 +227,22 @@ bool SdPdfFilter::Import()
         }
         pPage->setLinkAnnotations(rPDFGraphicResult.GetLinksInfo());
     }
-    mrDocument.setLock(bWasLocked);
-    mrDocument.EnableUndo(bSavedUndoEnabled);
+    rDocument.setLock(bWasLocked);
+    rDocument.EnableUndo(bSavedUndoEnabled);
     return true;
 }
 
+bool SdPdfFilter::Import()
+{
+    const OUString aFileName(
+        
mrMedium.GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE));
+
+    std::unique_ptr<SvStream> xStream(::utl::UcbStreamHelper::CreateStream(
+        aFileName, StreamMode::READ | StreamMode::SHARE_DENYNONE));
+
+    return ImportPDF(*xStream, mrDocument);
+}
+
 bool SdPdfFilter::Export() { return false; }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit a8842b8d15d7a10f20a106d3c31efd2b57e66a8c
Author:     Caolán McNamara <[email protected]>
AuthorDate: Thu Oct 9 13:07:08 2025 +0100
Commit:     Miklos Vajna <[email protected]>
CommitDate: Mon Oct 13 08:53:14 2025 +0200

    add a ImportPDFUnloaded variant for a SvStream
    
    Change-Id: I4bcf0fe17046bb4ac2e105d7310c3af64da521e9
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192126
    Reviewed-by: Miklos Vajna <[email protected]>
    Tested-by: Jenkins CollaboraOffice <[email protected]>

diff --git a/include/vcl/pdfread.hxx b/include/vcl/pdfread.hxx
index d819f1dff2f2..5e3c1cc0d2ff 100644
--- a/include/vcl/pdfread.hxx
+++ b/include/vcl/pdfread.hxx
@@ -108,6 +108,7 @@ public:
 /// Returns the number of pages read.
 VCL_DLLPUBLIC size_t ImportPDFUnloaded(const OUString& rURL,
                                        std::vector<PDFGraphicResult>& 
rGraphics);
+VCL_DLLPUBLIC size_t ImportPDFUnloaded(SvStream& rStream, 
std::vector<PDFGraphicResult>& rGraphics);
 }
 
 #endif // INCLUDED_VCL_SOURCE_FILTER_IPDF_PDFREAD_HXX
diff --git a/vcl/source/filter/ipdf/pdfread.cxx 
b/vcl/source/filter/ipdf/pdfread.cxx
index 3332b76d5254..b705f2b76263 100644
--- a/vcl/source/filter/ipdf/pdfread.cxx
+++ b/vcl/source/filter/ipdf/pdfread.cxx
@@ -373,13 +373,10 @@ findLinks(const std::unique_ptr<vcl::pdf::PDFiumPage>& 
pPage,
 
 } // end anonymous namespace
 
-size_t ImportPDFUnloaded(const OUString& rURL, std::vector<PDFGraphicResult>& 
rGraphics)
+size_t ImportPDFUnloaded(SvStream& rStream, std::vector<PDFGraphicResult>& 
rGraphics)
 {
-    std::unique_ptr<SvStream> xStream(
-        ::utl::UcbStreamHelper::CreateStream(rURL, StreamMode::READ | 
StreamMode::SHARE_DENYNONE));
-
     // Save the original PDF stream for later use.
-    BinaryDataContainer aDataContainer = 
vcl::pdf::createBinaryDataContainer(*xStream);
+    BinaryDataContainer aDataContainer = 
vcl::pdf::createBinaryDataContainer(rStream);
     if (aDataContainer.isEmpty())
         return 0;
 
@@ -433,6 +430,13 @@ size_t ImportPDFUnloaded(const OUString& rURL, 
std::vector<PDFGraphicResult>& rG
 
     return rGraphics.size();
 }
+
+size_t ImportPDFUnloaded(const OUString& rURL, std::vector<PDFGraphicResult>& 
rGraphics)
+{
+    std::unique_ptr<SvStream> xStream(
+        ::utl::UcbStreamHelper::CreateStream(rURL, StreamMode::READ | 
StreamMode::SHARE_DENYNONE));
+    return ImportPDFUnloaded(*xStream, rGraphics);
+}
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit 2f157a9eac2fab72af4b8019c9d10b4f3be1aa50
Author:     Mike Kaganski <[email protected]>
AuthorDate: Mon Oct 6 18:10:53 2025 +0500
Commit:     Miklos Vajna <[email protected]>
CommitDate: Mon Oct 13 08:50:33 2025 +0200

    LOK Transform API: add generic UnoCommand command
    
    There already was UnoCommand support inside SlideCommands. This change
    introduces it at top level.
    
    The syntax of the command is:
    
    {"UnoCommand": {
        "name": ".uno:Foo",
        "arguments": {
            "Arg1": {
                "type": "long",
                "value": 0
            },
            "Arg2": {
                "type": "long",
                "value": 1
            }
        }
    }}
    
    Change-Id: I0015e937495ad1ae756c5366b555a3b0d5fb09ef
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192001
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>

diff --git a/comphelper/source/misc/sequenceashashmap.cxx 
b/comphelper/source/misc/sequenceashashmap.cxx
index 46a23db43ba9..7c3c99016912 100644
--- a/comphelper/source/misc/sequenceashashmap.cxx
+++ b/comphelper/source/misc/sequenceashashmap.cxx
@@ -26,6 +26,7 @@
 #include <com/sun/star/lang/IllegalArgumentException.hpp>
 #include <com/sun/star/reflection/XIdlField.hpp>
 #include <com/sun/star/reflection/theCoreReflection.hpp>
+#include <comphelper/JsonToPropertyValues_with_boost.hxx>
 #include <comphelper/sequenceashashmap.hxx>
 #include <comphelper/processfactory.hxx>
 #include <comphelper/propertysequence.hxx>
@@ -302,7 +303,7 @@ void SequenceAsHashMap::update(const SequenceAsHashMap& 
rUpdate)
     }
 }
 
-static std::vector<css::beans::PropertyValue> JsonToPropertyValues(const 
boost::property_tree::ptree& aTree)
+std::vector<css::beans::PropertyValue> JsonToPropertyValues(const 
boost::property_tree::ptree& aTree)
 {
     std::vector<beans::PropertyValue> aArguments;
     boost::property_tree::ptree aNodeNull, aNodeValue;
diff --git a/include/comphelper/JsonToPropertyValues_with_boost.hxx 
b/include/comphelper/JsonToPropertyValues_with_boost.hxx
new file mode 100644
index 000000000000..2c67cc8f3a77
--- /dev/null
+++ b/include/comphelper/JsonToPropertyValues_with_boost.hxx
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <vector>
+
+#include <boost/property_tree/ptree_fwd.hpp>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <comphelper/comphelperdllapi.h>
+
+namespace comphelper
+{
+COMPHELPER_DLLPUBLIC std::vector<css::beans::PropertyValue>
+JsonToPropertyValues(const boost::property_tree::ptree& rJson);
+} // namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/include/sfx2/lokhelper.hxx b/include/sfx2/lokhelper.hxx
index 099c45025328..ab8dc309afa1 100644
--- a/include/sfx2/lokhelper.hxx
+++ b/include/sfx2/lokhelper.hxx
@@ -30,6 +30,8 @@
 #include <string_view>
 #include <unordered_map>
 
+#include <boost/property_tree/ptree_fwd.hpp>
+
 #define LOK_NOTIFY_LOG_TO_CLIENT 1
 
 #define LOK_LOG_STREAM(level, area, stream) \
@@ -270,6 +272,8 @@ public:
     /// Registers function pointers in comphelper/ to set/get of the current 
LOK view.
     static void registerViewCallbacks();
 
+    static void dispatchUnoCommand(const boost::property_tree::ptree& tree);
+
 private:
     static int createView(SfxViewFrame& rViewFrame, ViewShellDocId docId);
 };
diff --git a/sd/source/ui/view/drviews2.cxx b/sd/source/ui/view/drviews2.cxx
index 1c64ad96806b..b2affde7eafe 100644
--- a/sd/source/ui/view/drviews2.cxx
+++ b/sd/source/ui/view/drviews2.cxx
@@ -1372,6 +1372,10 @@ void DrawViewShell::FuTemporary(SfxRequest& rReq)
                         }
                     }
                 }
+                else if (aItem.first == "UnoCommand")
+                {
+                    SfxLokHelper::dispatchUnoCommand(aItem.second);
+                }
             }
             rReq.Done();
         }
diff --git a/sfx2/source/view/lokhelper.cxx b/sfx2/source/view/lokhelper.cxx
index f554ae32daad..4512b0ad4a57 100644
--- a/sfx2/source/view/lokhelper.cxx
+++ b/sfx2/source/view/lokhelper.cxx
@@ -16,13 +16,18 @@
 
 #include <sfx2/lokcomponenthelpers.hxx>
 #include <sfx2/lokhelper.hxx>
+#include <sfx2/lokunocmdlist.hxx>
 
 #include <com/sun/star/frame/Desktop.hpp>
 #include <com/sun/star/ui/ContextChangeEventObject.hpp>
 #include <com/sun/star/xml/crypto/SEInitializer.hpp>
 #include <com/sun/star/xml/crypto/XCertificateCreator.hpp>
 
+#include <comphelper/dispatchcommand.hxx>
+#include <comphelper/JsonToPropertyValues_with_boost.hxx>
 #include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequence.hxx>
 #include <o3tl/string_view.hxx>
 #include <rtl/strbuf.hxx>
 #include <vcl/lok.hxx>
@@ -1190,6 +1195,29 @@ void SfxLokHelper::registerViewCallbacks()
     });
 }
 
+void SfxLokHelper::dispatchUnoCommand(const boost::property_tree::ptree& tree)
+{
+    auto command = 
OStringToOUString(tree.get_child("name").get_value<std::string>(),
+                                     RTL_TEXTENCODING_UTF8);
+    // Check if the uno command is allowed
+    if (std::u16string_view rest;
+        !command.startsWith(".uno:", &rest) || 
!GetKitUnoCommandList().contains(rest))
+    {
+        LOK_WARN("lok.transform",
+                 "UnoCommand command is not recognized: '" << command << "'");
+        return;
+    }
+    std::vector<beans::PropertyValue> arguments;
+    if (auto args = tree.get_child_optional("arguments"))
+    {
+        arguments = comphelper::JsonToPropertyValues(*args);
+    }
+    // Make the uno command synchron
+    arguments.push_back(comphelper::makePropertyValue(u"SynchronMode"_ustr, 
true));
+
+    comphelper::dispatchCommand(command, 
comphelper::containerToSequence(arguments));
+}
+
 namespace
 {
     struct LOKAsyncEventData
diff --git a/sw/source/uibase/shells/textsh1.cxx 
b/sw/source/uibase/shells/textsh1.cxx
index d79da729e054..32d6127576ea 100644
--- a/sw/source/uibase/shells/textsh1.cxx
+++ b/sw/source/uibase/shells/textsh1.cxx
@@ -3552,6 +3552,10 @@ void SwTextShell::Execute(SfxRequest &rReq)
                         }
                     }
                 }
+                else if (aItem.first == "UnoCommand")
+                {
+                    SfxLokHelper::dispatchUnoCommand(aItem.second);
+                }
             }
         }
         break;
commit b685463a1a586ffbd8a190657603750a990a8994
Author:     Kurt Nordback <[email protected]>
AuthorDate: Fri Jul 4 12:43:15 2025 -0600
Commit:     Tomaž Vajngerl <[email protected]>
CommitDate: Mon Oct 13 06:43:29 2025 +0200

    tdf#165742 Step 4.7: Add support for internal data
    
    Word and Powerpoint store data internal to the chart in .docx and .pptx
    formats, respectively. In chartex this uses the <cx:chartData> and <cx:data>
    tags, and unlike in pre-2016 charts, is at the chartSpace level rather than
    internal to individual series. Provide support for these tags and data
    structures.
    
    Note that this does not yet allow .docx and .pptx files with chartex
    contents to round-trip successfully (MSO -> LO -> MSO). Additional work
    remains needed for this.
    
    Change-Id: I1e91cd0e014787ce19303a906ff76cd6c7c4b0fa
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/187426
    Reviewed-by: Tomaž Vajngerl <[email protected]>
    Tested-by: Jenkins
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/188562
    Tested-by: Jenkins CollaboraOffice <[email protected]>

diff --git a/chart2/qa/extras/chart2export2.cxx 
b/chart2/qa/extras/chart2export2.cxx
index d1e6a8846072..34a10808d790 100644
--- a/chart2/qa/extras/chart2export2.cxx
+++ b/chart2/qa/extras/chart2export2.cxx
@@ -171,6 +171,17 @@ CPPUNIT_TEST_FIXTURE(Chart2ExportTest2, 
testChartexTitleXLSX)
                        u"Funnel chart!");
 }
 
+CPPUNIT_TEST_FIXTURE(Chart2ExportTest2, testChartexPPTX)
+{
+    loadFromFile(u"pptx/funnel-pp1.pptx");
+    save(u"Impress Office Open XML"_ustr);
+    xmlDocUniquePtr pXmlDoc = parseExport(u"ppt/charts/chartEx1.xml"_ustr);
+    CPPUNIT_ASSERT(pXmlDoc);
+
+    assertXPath(pXmlDoc, 
"/cx:chartSpace/cx:chart/cx:plotArea/cx:plotAreaRegion/cx:series", 3, 0,
+                "layoutId", u"funnel");
+}
+
 CPPUNIT_TEST_FIXTURE(Chart2ExportTest2, testAxisTitleRotationXLSX)
 {
     loadFromFile(u"xlsx/axis_title_rotation.xlsx");
diff --git a/chart2/qa/extras/data/pptx/funnel-pp1.pptx 
b/chart2/qa/extras/data/pptx/funnel-pp1.pptx
new file mode 100644
index 000000000000..df34aa1618d0
Binary files /dev/null and b/chart2/qa/extras/data/pptx/funnel-pp1.pptx differ
diff --git a/include/oox/drawingml/chart/datasourcemodel.hxx 
b/include/oox/drawingml/chart/datasourcemodel.hxx
index 49b8dcd90717..7cc52208e6a0 100644
--- a/include/oox/drawingml/chart/datasourcemodel.hxx
+++ b/include/oox/drawingml/chart/datasourcemodel.hxx
@@ -58,6 +58,24 @@ struct DataSourceModel
                         ~DataSourceModel();
 };
 
+enum class DataSourceType: sal_Int32;
+
+// Data source for chartex
+struct DataSourceCxModel
+{
+    // Same as definition in SeriesModel
+    typedef ModelMap< DataSourceType, DataSourceModel > DataSourceMap;
+    typedef ModelMap<sal_Int32, DataSourceMap> DataMap;
+
+    // Chartex data can have three levels of lists/maps:
+    // 1. Multiple data series, each with a <cx:data> tag and indexed by id.
+    // 2. Within a series, multiple sources ("val", "cat", etc.)
+    // 3. Within a source, multiple points, with corresponding index.
+    DataMap             maSourceMap; /// Data for chartex.
+
+    explicit            DataSourceCxModel() = default;
+};
+
 
 } // namespace oox::drawingml::chart
 
diff --git a/include/oox/export/chartexport.hxx 
b/include/oox/export/chartexport.hxx
index 2d745d295986..348af941c88a 100644
--- a/include/oox/export/chartexport.hxx
+++ b/include/oox/export/chartexport.hxx
@@ -174,9 +174,8 @@ private:
     void exportChart( const css::uno::Reference<
                           css::chart::XChartDocument >& rChartDoc,
                           bool bIsChartex);
-    void exportData( const css::uno::Reference<
-                              css::chart::XChartDocument >& rChartDoc,
-                              bool bIsChartex);
+    void exportData_chartex( const css::uno::Reference<
+                              css::chart::XChartDocument >& rChartDoc);
     void exportExternalData( const css::uno::Reference<
                               css::chart::XChartDocument >& rChartDoc,
                               bool bIsChartex);
@@ -217,10 +216,12 @@ private:
     void exportUpDownBars(const css::uno::Reference< css::chart2::XChartType 
>& xChartType );
 
     void exportAllSeries(const css::uno::Reference<css::chart2::XChartType>& 
xChartType, bool& rPrimaryAxes);
-    void exportSeries(const css::uno::Reference< css::chart2::XChartType >& 
xChartType,
+    void exportSeries_chart(const css::uno::Reference< css::chart2::XChartType 
>& xChartType,
             const 
css::uno::Sequence<css::uno::Reference<css::chart2::XDataSeries> >& rSeriesSeq,
-            bool& rPrimaryAxes,
-            bool bIsChartex);
+            bool& rPrimaryAxes);
+    void exportSeries_chartex(const css::uno::Reference< 
css::chart2::XChartType >& xChartType,
+            const 
css::uno::Sequence<css::uno::Reference<css::chart2::XDataSeries> >& rSeriesSeq,
+            const char* sTypeName);
 
     void exportVaryColors(const css::uno::Reference<css::chart2::XChartType>& 
xChartType);
     void exportCandleStickSeries(
diff --git a/include/test/xmltesttools.hxx b/include/test/xmltesttools.hxx
index 6a44b6c235b2..555c64afe333 100644
--- a/include/test/xmltesttools.hxx
+++ b/include/test/xmltesttools.hxx
@@ -56,6 +56,16 @@ protected:
     {
         return getXPath(pXmlDoc, sXPath.getStr(), pAttribute);
     }
+    /**
+     * Same as above, but where the expected number of nodes with
+     * the given path is nNumPaths and the desired node index is nPathIdx.
+     */
+    OUString      getXPath(const xmlDocUniquePtr& pXmlDoc, const char* pXPath, 
int nNumPaths, int nPathIdx, const char* pAttribute);
+    OUString getXPath(const xmlDocUniquePtr& pXmlDoc, const OString& sXPath, 
int nNumPaths, int nPathIdx, const char* pAttribute)
+    {
+        assert(nPathIdx < nNumPaths);
+        return getXPath(pXmlDoc, sXPath.getStr(), nNumPaths, nPathIdx, 
pAttribute);
+    }
     /**
      * Same as the assertXPathContent(), but don't assert: return the string 
instead.
      */
@@ -106,6 +116,19 @@ protected:
     {
         assertXPathAttrs(pXmlDoc, sXPath.getStr(), aPairVector);
     }
+    /**
+     * Assert that pXPath exists, returns nNumNodes nodes, and the attribute
+     * value of node number nPathIdx equals the rExpected value.
+     */
+    void          assertXPath(const xmlDocUniquePtr& pXmlDoc, const char* 
pXPath,
+                              int nNumNodes, int nNodeIdx,
+                              const char* pAttribute, std::u16string_view 
rExpectedValue);
+    void assertXPath(const xmlDocUniquePtr& pXmlDoc, const OString& sXPath,
+                     int nNumNodes, int nNodeIdx,
+                     const char* pAttribute, std::u16string_view 
rExpectedValue)
+    {
+        assertXPath(pXmlDoc, sXPath.getStr(), nNumNodes, nNodeIdx, pAttribute, 
rExpectedValue);
+    }
 
     /**
      * Given a double for the rExpected value, assert that pXPath exists, 
returns exactly one node,
diff --git a/oox/inc/drawingml/chart/chartspacemodel.hxx 
b/oox/inc/drawingml/chart/chartspacemodel.hxx
index d66abb21f9d7..dc3ab8674634 100644
--- a/oox/inc/drawingml/chart/chartspacemodel.hxx
+++ b/oox/inc/drawingml/chart/chartspacemodel.hxx
@@ -39,6 +39,7 @@ struct ChartSpaceModel
     typedef ModelRef< View3DModel >     View3DRef;
     typedef ModelRef< TitleModel >      TitleRef;
     typedef ModelRef< LegendModel >     LegendRef;
+    typedef ModelRef< DataSourceCxModel > DataSourceRef;
 
     ShapeRef            mxShapeProp;        /// Chart frame formatting.
     TextBodyRef         mxTextProp;         /// Global chart text formatting.
@@ -59,6 +60,7 @@ struct ChartSpaceModel
     bool                mbPlotVisOnly;      /// True = plot visible cells in a 
sheet only.
     bool                mbShowLabelsOverMax;/// True = show labels over chart 
maximum.
     bool                mbPivotChart;       /// True = pivot chart.
+    DataSourceRef       maCxData;           /// Data for Chartex.
 
     explicit            ChartSpaceModel(bool bMSO2007Doc);
                         ~ChartSpaceModel();
diff --git a/oox/inc/drawingml/chart/datasourcecontext.hxx 
b/oox/inc/drawingml/chart/datasourcecontext.hxx
index 7edb029eb336..eeeccb6d89b8 100644
--- a/oox/inc/drawingml/chart/datasourcecontext.hxx
+++ b/oox/inc/drawingml/chart/datasourcecontext.hxx
@@ -21,6 +21,7 @@
 #define INCLUDED_OOX_DRAWINGML_CHART_DATASOURCECONTEXT_HXX
 
 #include <memory>
+#include <oox/drawingml/chart/datasourcemodel.hxx>
 #include <drawingml/chart/chartcontextbase.hxx>
 
 class SvNumberFormatter;
@@ -86,6 +87,22 @@ public:
     virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 
nElement, const AttributeList& rAttribs ) override;
 };
 
+struct DataSourceCxModel;
+
+/** Handler for a chartex data source context (cx:chartData, cx:data elements).
+ */
+class DataSourceCxContext final : public ContextBase< DataSourceCxModel >
+{
+public:
+    explicit            DataSourceCxContext( 
::oox::core::ContextHandler2Helper& rParent,
+            DataSourceCxModel& rModel);
+    virtual             ~DataSourceCxContext() override;
+
+    virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 
nElement, const AttributeList& rAttribs ) override;
+
+    DataSourceCxModel::DataSourceMap *paCurSource;
+};
+
 
 } // namespace oox::drawingml::chart
 
diff --git a/oox/inc/drawingml/chart/plotareaconverter.hxx 
b/oox/inc/drawingml/chart/plotareaconverter.hxx
index 988405b3247c..750d91cb916c 100644
--- a/oox/inc/drawingml/chart/plotareaconverter.hxx
+++ b/oox/inc/drawingml/chart/plotareaconverter.hxx
@@ -21,6 +21,8 @@
 #define INCLUDED_OOX_DRAWINGML_CHART_PLOTAREACONVERTER_HXX
 
 #include <drawingml/chart/converterbase.hxx>
+#include <drawingml/chart/seriesmodel.hxx>
+#include <oox/drawingml/chart/datasourcemodel.hxx>
 
 namespace com::sun::star {
     namespace chart2 { class XDiagram; }
@@ -68,7 +70,8 @@ public:
     virtual             ~PlotAreaConverter() override;
 
     /** Converts the OOXML plot area model to a chart2 diagram. */
-    void                convertFromModel( View3DModel& rView3DModel );
+    void                convertFromModel( View3DModel& rView3DModel,
+            DataSourceCxModel& raDataMap );
     /** Converts the manual plot area position and size, if set. */
     void                convertPositionFromModel();
 
diff --git a/oox/inc/drawingml/chart/seriesconverter.hxx 
b/oox/inc/drawingml/chart/seriesconverter.hxx
index c8cca31d5e3e..23a2c02cc7ef 100644
--- a/oox/inc/drawingml/chart/seriesconverter.hxx
+++ b/oox/inc/drawingml/chart/seriesconverter.hxx
@@ -21,6 +21,7 @@
 #define INCLUDED_OOX_DRAWINGML_CHART_SERIESCONVERTER_HXX
 
 #include <drawingml/chart/converterbase.hxx>
+#include <oox/drawingml/chart/datasourcemodel.hxx>
 #include <drawingml/chart/seriesmodel.hxx>
 
 namespace com::sun::star {
@@ -134,7 +135,7 @@ public:
 private:
     css::uno::Reference< css::chart2::data::XLabeledDataSequence >
                         createLabeledDataSequence(
-                            SeriesModel::SourceType eSourceType,
+                            enum DataSourceType eSourceType,
                             const OUString& rRole,
                             bool bUseTextLabel );
 };
diff --git a/oox/inc/drawingml/chart/seriesmodel.hxx 
b/oox/inc/drawingml/chart/seriesmodel.hxx
index 81c6e770ee1c..a42bf8b4036a 100644
--- a/oox/inc/drawingml/chart/seriesmodel.hxx
+++ b/oox/inc/drawingml/chart/seriesmodel.hxx
@@ -174,17 +174,17 @@ struct DataPointModel
                         ~DataPointModel();
 };
 
-struct SeriesModel
+enum class DataSourceType: sal_Int32
 {
-    enum SourceType
-    {
-        CATEGORIES,         /// Data point categories.
-        VALUES,             /// Data point values.
-        POINTS,             /// Data point size (e.g. bubble size in bubble 
charts).
-        DATALABELS,         /// Data point labels.
-    };
+    CATEGORIES,         /// Data point categories.
+    VALUES,             /// Data point values.
+    POINTS,             /// Data point size (e.g. bubble size in bubble 
charts).
+    DATALABELS,         /// Data point labels.
+};
 
-    typedef ModelMap< SourceType, DataSourceModel > DataSourceMap;
+struct SeriesModel
+{
+    typedef ModelMap< DataSourceType, DataSourceModel > DataSourceMap;
     typedef ModelVector< ErrorBarModel >            ErrorBarVector;
     typedef ModelVector< TrendlineModel >           TrendlineVector;
     typedef ModelVector< DataPointModel >           DataPointVector;
@@ -208,6 +208,7 @@ struct SeriesModel
     sal_Int32           mnMarkerSize;       /// Size of the series line marker 
(2...72).
     sal_Int32           mnMarkerSymbol;     /// Series line marker symbol.
     sal_Int32           mnOrder;            /// Series order.
+    sal_Int32           mnDataId;           /// Reference to correct data 
chunk (chartex)
     bool                mbBubble3d;         /// True = show bubbles with 3D 
shade.
     bool                mbInvertNeg;        /// True = invert negative data 
points.
     bool                mbSmooth;           /// True = smooth series line.
diff --git a/oox/inc/drawingml/chart/typegroupconverter.hxx 
b/oox/inc/drawingml/chart/typegroupconverter.hxx
index fac7eb70f7c4..720edf318a00 100644
--- a/oox/inc/drawingml/chart/typegroupconverter.hxx
+++ b/oox/inc/drawingml/chart/typegroupconverter.hxx
@@ -21,6 +21,8 @@
 #define INCLUDED_OOX_DRAWINGML_CHART_TYPEGROUPCONVERTER_HXX
 
 #include <drawingml/chart/converterbase.hxx>
+#include <oox/drawingml/chart/datasourcemodel.hxx>
+#include <drawingml/chart/seriesmodel.hxx>
 #include <com/sun/star/chart2/PieChartSubType.hpp>
 
 namespace com::sun::star {
@@ -165,6 +167,12 @@ public:
     void                convertPieExplosion( PropertySet& rPropSet, sal_Int32 
nOoxExplosion ) const;
     /** Converts of-pie types */
     css::chart2::PieChartSubType convertOfPieType(sal_Int32 nOoxOfPieType ) 
const;
+    /** Move any internal data to the appropriate series.  In chartex the data
+       (if any is internal) is given outside the series, in a child element of
+       <cx:chartSpace>. Pre-2016 charts have the data inside the series, and
+       SeriesModel and subsequent handling reflects this. So this function gets
+       the data to the right place for processing. */
+    void                moveDataToSeries(DataSourceCxModel::DataMap& 
raDataMap);
 
 private:
     /** Inserts the passed series into the chart type. Adds additional 
properties to the series. */
diff --git a/oox/source/drawingml/chart/chartconverter.cxx 
b/oox/source/drawingml/chart/chartconverter.cxx
index 71f359d282fa..1b281bd6cbc5 100644
--- a/oox/source/drawingml/chart/chartconverter.cxx
+++ b/oox/source/drawingml/chart/chartconverter.cxx
@@ -123,6 +123,7 @@ Reference< XDataSequence > 
ChartConverter::createDataSequence(
         {
             // create a single-row array from constant source data
             // (multiple levels in the case of complex categories)
+            assert( rDataSeq.mnPointCount > 0);
             std::vector<Any> aRow(rDataSeq.mnLevelCount * 
rDataSeq.mnPointCount);
             for (auto const& elem : rDataSeq.maData)
                 aRow.at(elem.first) = elem.second;
diff --git a/oox/source/drawingml/chart/chartspaceconverter.cxx 
b/oox/source/drawingml/chart/chartspaceconverter.cxx
index d2d51fad3c43..37111b865194 100644
--- a/oox/source/drawingml/chart/chartspaceconverter.cxx
+++ b/oox/source/drawingml/chart/chartspaceconverter.cxx
@@ -157,7 +157,8 @@ void ChartSpaceConverter::convertFromModel( const 
Reference< XShapes >& rxExtern
     bool bMSO2007Doc = getFilter().isMSO2007Document();
     // convert plot area (container of all chart type groups)
     PlotAreaConverter aPlotAreaConv( *this, mrModel.mxPlotArea.getOrCreate() );
-    aPlotAreaConv.convertFromModel( mrModel.mxView3D.getOrCreate(bMSO2007Doc) 
);
+    aPlotAreaConv.convertFromModel( mrModel.mxView3D.getOrCreate(bMSO2007Doc),
+            mrModel.maCxData.getOrCreate());
 
     // plot area converter has created the diagram object
     Reference< XDiagram > xDiagram = getChartDocument()->getFirstDiagram();
diff --git a/oox/source/drawingml/chart/chartspacefragment.cxx 
b/oox/source/drawingml/chart/chartspacefragment.cxx
index 29e77e4b5095..f26b3cd924b4 100644
--- a/oox/source/drawingml/chart/chartspacefragment.cxx
+++ b/oox/source/drawingml/chart/chartspacefragment.cxx
@@ -24,6 +24,7 @@
 #include <drawingml/chart/chartspacemodel.hxx>
 #include <drawingml/chart/plotareacontext.hxx>
 #include <drawingml/chart/titlecontext.hxx>
+#include <drawingml/chart/datasourcecontext.hxx>
 #include <oox/core/xmlfilterbase.hxx>
 #include <oox/helper/attributelist.hxx>
 #include <oox/token/namespaces.hxx>
@@ -142,8 +143,7 @@ ContextHandlerRef ChartSpaceFragment::onCreateContext( 
sal_Int32 nElement, const
         case CX_TOKEN(chartSpace) :
             switch (nElement) {
                 case CX_TOKEN(chartData):
-                    // TODO
-                    return nullptr;
+                    return new DataSourceCxContext(*this, 
mrModel.maCxData.create());
                 case CX_TOKEN(chart):
                     return this;
                 case CX_TOKEN(spPr):
diff --git a/oox/source/drawingml/chart/datasourcecontext.cxx 
b/oox/source/drawingml/chart/datasourcecontext.cxx
index f4660f5db708..d27b715dbbc7 100644
--- a/oox/source/drawingml/chart/datasourcecontext.cxx
+++ b/oox/source/drawingml/chart/datasourcecontext.cxx
@@ -17,10 +17,11 @@
  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
  */
 
-#include <drawingml/chart/datasourcecontext.hxx>
-
 #include <oox/drawingml/chart/datasourcemodel.hxx>
 
+#include <drawingml/chart/seriesmodel.hxx>
+#include <drawingml/chart/datasourcecontext.hxx>
+
 #include <oox/core/xmlfilterbase.hxx>
 #include <oox/helper/attributelist.hxx>
 #include <oox/token/namespaces.hxx>
@@ -81,6 +82,23 @@ ContextHandlerRef DoubleSequenceContext::onCreateContext( 
sal_Int32 nElement, co
                     return this;
             }
         break;
+        case CX_TOKEN(numDim) :
+            switch (nElement) {
+                case CX_TOKEN(f):
+                    return this;
+                case CX_TOKEN(lvl):
+                    mrModel.mnPointCount = rAttribs.getInteger(XML_ptCount, 
-1);
+                    mrModel.maFormatCode = rAttribs.getString(XML_formatCode, 
"");
+                    return this;
+            }
+            break;
+        case CX_TOKEN(lvl) :
+            switch(nElement) {
+                case CX_TOKEN(pt):
+                    mnPtIndex = rAttribs.getInteger(XML_idx, -1);
+                    return this;
+            }
+            break;
     }
     return nullptr;
 }
@@ -90,12 +108,14 @@ void DoubleSequenceContext::onCharacters( const OUString& 
rChars )
     switch( getCurrentElement() )
     {
         case C_TOKEN( f ):
+        case CX_TOKEN( f ):
             mrModel.maFormula = rChars;
         break;
         case C_TOKEN( formatCode ):
             mrModel.maFormatCode = rChars;
         break;
         case C_TOKEN( v ):
+        case CX_TOKEN(pt):
             if( mnPtIndex >= 0 )
             {
                 /* Import categories as String even though it could
@@ -244,9 +264,11 @@ ContextHandlerRef StringSequenceContext::onCreateContext( 
sal_Int32 nElement, co
             break;
 
         case C_TOKEN( lvl ):
+        case CX_TOKEN( lvl ):
             switch (nElement)
             {
                 case C_TOKEN(pt):
+                case CX_TOKEN(pt):
                     mnPtIndex = rAttribs.getInteger(XML_idx, -1);
                     return this;
             }
@@ -259,6 +281,14 @@ ContextHandlerRef StringSequenceContext::onCreateContext( 
sal_Int32 nElement, co
                     return this;
             }
         break;
+        case CX_TOKEN(strDim) :
+            switch (nElement) {
+                case CX_TOKEN(f):
+                    return this;
+                case CX_TOKEN(lvl):
+                    mrModel.mnPointCount = rAttribs.getInteger(XML_ptCount, 
-1);
+                    return this;
+            }
     }
     return nullptr;
 }
@@ -275,8 +305,11 @@ void StringSequenceContext::onCharacters( const OUString& 
rChars )
                 mrModel.maFormula = rChars;
         break;
         case C_TOKEN( v ):
-            if( mnPtIndex >= 0 )
+        case CX_TOKEN(pt):
+            if( mnPtIndex >= 0 ) {
+                assert(mrModel.mnPointCount > 0);
                 mrModel.maData[ (mrModel.mnLevelCount-1) * 
mrModel.mnPointCount + mnPtIndex ] <<= rChars;
+            }
         break;
     }
 }
@@ -290,7 +323,8 @@ DataSourceContext::~DataSourceContext()
 {
 }
 
-ContextHandlerRef DataSourceContext::onCreateContext( sal_Int32 nElement, 
const AttributeList& )
+ContextHandlerRef DataSourceContext::onCreateContext( sal_Int32 nElement, const
+        AttributeList&)
 {
     switch( getCurrentElement() )
     {
@@ -330,6 +364,74 @@ ContextHandlerRef DataSourceContext::onCreateContext( 
sal_Int32 nElement, const
     return nullptr;
 }
 
+// =====
+// DataSourceCxContext: handler for chartex data sources
+// =====
+DataSourceCxContext::DataSourceCxContext( ContextHandler2Helper& rParent,
+        DataSourceCxModel& rModel ) :
+    ContextBase< DataSourceCxModel >( rParent, rModel ),
+    paCurSource(nullptr)
+{
+}
+
+DataSourceCxContext::~DataSourceCxContext()
+{
+}
+
+ContextHandlerRef DataSourceCxContext::onCreateContext(sal_Int32 nElement, 
const AttributeList& rAttribs)
+{
+    switch( getCurrentElement() )
+    {
+        case CX_TOKEN(chartData) :
+            switch (nElement) {
+                case CX_TOKEN(externalData) :
+                    return nullptr; // TODO
+                case CX_TOKEN(data) :
+                    paCurSource = 
&mrModel.maSourceMap.create(rAttribs.getInteger(XML_id, -1));
+                    return this;
+            }
+            break;
+        case CX_TOKEN(data) :
+            switch (nElement) {
+                case CX_TOKEN(numDim) :
+                {
+                    assert(paCurSource);
+                    OUString sType = rAttribs.getString(XML_type, "val");
+                    if (sType == "cat") {
+                        DataSourceModel& rDataModel = 
paCurSource->create(DataSourceType::CATEGORIES);
+                        return new DoubleSequenceContext(*this,
+                                rDataModel.mxDataSeq.create());
+                    } else {
+                        // default is VALUES
+                        DataSourceModel& rDataModel = 
paCurSource->create(DataSourceType::VALUES);
+                        return new DoubleSequenceContext(*this,
+                                rDataModel.mxDataSeq.create());
+                    }
+                    break;
+                }
+
+                case CX_TOKEN(strDim) :
+                {
+                    assert(paCurSource);
+                    OUString sType = rAttribs.getString(XML_type, "cat");
+                    if (sType == "val") {
+                        DataSourceModel& rDataModel = 
paCurSource->create(DataSourceType::VALUES);
+                        return new StringSequenceContext(*this,
+                                rDataModel.mxDataSeq.create());
+                    } else {
+                        // default is CATEGORIES
+                        DataSourceModel& rDataModel = 
paCurSource->create(DataSourceType::CATEGORIES);
+                        return new StringSequenceContext(*this,
+                                rDataModel.mxDataSeq.create());
+                    }
+                }
+            }
+            break;
+
+    }
+    return nullptr;
+}
+
 } // namespace oox::drawingml::chart
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/drawingml/chart/plotareaconverter.cxx 
b/oox/source/drawingml/chart/plotareaconverter.cxx
index 1fa8b6f9b673..95496906fdea 100644
--- a/oox/source/drawingml/chart/plotareaconverter.cxx
+++ b/oox/source/drawingml/chart/plotareaconverter.cxx
@@ -32,6 +32,7 @@
 #include <drawingml/chart/axisconverter.hxx>
 #include <drawingml/chart/plotareamodel.hxx>
 #include <drawingml/chart/typegroupconverter.hxx>
+#include <drawingml/chart/seriesmodel.hxx>
 #include <oox/core/xmlfilterbase.hxx>
 #include <oox/token/namespaces.hxx>
 #include <oox/token/properties.hxx>
@@ -71,6 +72,7 @@ public:
                             const Reference< XDiagram >& rxDiagram,
                             View3DModel& rView3DModel,
                             sal_Int32 nAxesSetIdx,
+                            DataSourceCxModel::DataMap& raSourceMap,
                             bool bSupportsVaryColorsByPoint,
                             bool bUseFixedInnerSize );
 
@@ -112,6 +114,7 @@ ModelRef< AxisModel > lclGetOrCreateAxis( const 
AxesSetModel::AxisMap& rFromAxes
 
 void AxesSetConverter::convertFromModel( const Reference< XDiagram >& 
rxDiagram,
                                         View3DModel& rView3DModel, sal_Int32 
nAxesSetIdx,
+                                        DataSourceCxModel::DataMap& 
raSourceMap,
                                         bool bSupportsVaryColorsByPoint, bool 
bUseFixedInnerSize)
 {
     // create type group converter objects for all type groups
@@ -169,6 +172,13 @@ void AxesSetConverter::convertFromModel( const Reference< 
XDiagram >& rxDiagram,
             to the data provider attached to the chart document. */
         if( xCoordSystem.is() )
         {
+            // Transfer any (chartex) data, specified at the chartSpace level,
+            // into the appropriate series. This needs to happen before the
+            // calls to AxisConverter::convertFromModel() below.
+            for (auto const& typeGroup : aTypeGroups) {
+                typeGroup->moveDataToSeries(raSourceMap);
+            }
+
             bool bMSO2007Doc = getFilter().isMSO2007Document();
             // convert all axes (create missing axis models)
             ModelRef< AxisModel > xXAxis = lclGetOrCreateAxis( mrModel.maAxes, 
API_X_AXIS, rFirstTypeGroup.getTypeInfo().mbCategoryAxis ? C_TOKEN( catAx ) : 
C_TOKEN( valAx ), bMSO2007Doc );
@@ -191,7 +201,8 @@ void AxesSetConverter::convertFromModel( const Reference< 
XDiagram >& rxDiagram,
 
             // convert all chart type groups, this converts all series data 
and formatting
             for (auto const& typeGroup : aTypeGroups)
-                typeGroup->convertFromModel( rxDiagram, xCoordSystem, 
nAxesSetIdx, bSupportsVaryColorsByPoint );
+                typeGroup->convertFromModel( rxDiagram, xCoordSystem,
+                        nAxesSetIdx,bSupportsVaryColorsByPoint );
         }
     }
     catch( Exception& )
@@ -313,7 +324,8 @@ PlotAreaConverter::~PlotAreaConverter()
 {
 }
 
-void PlotAreaConverter::convertFromModel( View3DModel& rView3DModel )
+void PlotAreaConverter::convertFromModel( View3DModel& rView3DModel,
+        DataSourceCxModel& rDataCxModel )
 {
     /*  Create the diagram object and attach it to the chart document. One
         diagram is used to carry all coordinate systems and data series. */
@@ -426,7 +438,7 @@ void PlotAreaConverter::convertFromModel( View3DModel& 
rView3DModel )
     {
         AxesSetConverter aAxesSetConv(*this, *axesSet);
         aAxesSetConv.convertFromModel(xDiagram, rView3DModel, nAxesSetIdx,
-                                      bSupportsVaryColorsByPoint, 
bUseFixedInnerSize);
+                rDataCxModel.maSourceMap, bSupportsVaryColorsByPoint, 
bUseFixedInnerSize);
         if(nAxesSetIdx == nStartAxesSetIdx)
         {
             maAutoTitle = aAxesSetConv.getAutomaticTitle();
diff --git a/oox/source/drawingml/chart/seriescontext.cxx 
b/oox/source/drawingml/chart/seriescontext.cxx
index 88688642bebe..19134c7a54a7 100644
--- a/oox/source/drawingml/chart/seriescontext.cxx
+++ b/oox/source/drawingml/chart/seriescontext.cxx
@@ -452,10 +452,10 @@ ContextHandlerRef SeriesContextBase::onCreateContext( 
sal_Int32 nElement, const
             {
                 case C_TOKEN( ext ):
                 case CX_TOKEN( ext ):
-                    if (mrModel.maSources.has( SeriesModel::DATALABELS ))
+                    if (mrModel.maSources.has( DataSourceType::DATALABELS ))
                         break;
 
-                    DataSourceModel& rLabelsSource = mrModel.maSources.create( 
SeriesModel::DATALABELS );
+                    DataSourceModel& rLabelsSource = mrModel.maSources.create( 
DataSourceType::DATALABELS );
                     if (mrModel.mxLabels.is())
                         mrModel.mxLabels->mpLabelsSource = &rLabelsSource;
                     return new DataSourceContext( *this, rLabelsSource );
@@ -483,7 +483,7 @@ ContextHandlerRef AreaSeriesContext::onCreateContext( 
sal_Int32 nElement, const
             switch( nElement )
             {
                 case C_TOKEN( cat ):
-                    return new DataSourceContext( *this, 
mrModel.maSources.create( SeriesModel::CATEGORIES ) );
+                    return new DataSourceContext( *this, 
mrModel.maSources.create( DataSourceType::CATEGORIES ) );
                 case C_TOKEN( errBars ):
                     return new ErrorBarContext( *this, 
mrModel.maErrorBars.create(bMSO2007Doc) );
                 case C_TOKEN( dLbls ):
@@ -493,7 +493,7 @@ ContextHandlerRef AreaSeriesContext::onCreateContext( 
sal_Int32 nElement, const
                 case C_TOKEN( trendline ):
                     return new TrendlineContext( *this, 
mrModel.maTrendlines.create(bMSO2007Doc) );
                 case C_TOKEN( val ):
-                    return new DataSourceContext( *this, 
mrModel.maSources.create( SeriesModel::VALUES ) );
+                    return new DataSourceContext( *this, 
mrModel.maSources.create( DataSourceType::VALUES ) );
             }
         break;
     }
@@ -518,7 +518,7 @@ ContextHandlerRef BarSeriesContext::onCreateContext( 
sal_Int32 nElement, const A
             switch( nElement )
             {
                 case C_TOKEN( cat ):
-                    return new DataSourceContext( *this, 
mrModel.maSources.create( SeriesModel::CATEGORIES ) );
+                    return new DataSourceContext( *this, 
mrModel.maSources.create( DataSourceType::CATEGORIES ) );
                 case C_TOKEN( dLbls ):
                     return new DataLabelsContext( *this, 
mrModel.mxLabels.create(bMSO2007Doc) );
                 case C_TOKEN( dPt ):
@@ -536,7 +536,7 @@ ContextHandlerRef BarSeriesContext::onCreateContext( 
sal_Int32 nElement, const A
                 case C_TOKEN( trendline ):
                     return new TrendlineContext( *this, 
mrModel.maTrendlines.create(bMSO2007Doc) );
                 case C_TOKEN( val ):
-                    return new DataSourceContext( *this, 
mrModel.maSources.create( SeriesModel::VALUES ) );
+                    return new DataSourceContext( *this, 
mrModel.maSources.create( DataSourceType::VALUES ) );
             }
         break;
     }
@@ -564,7 +564,7 @@ ContextHandlerRef BubbleSeriesContext::onCreateContext( 
sal_Int32 nElement, cons
                     mrModel.mbBubble3d = rAttribs.getBool( XML_val, 
!bMSO2007Doc );
                     return nullptr;
                 case C_TOKEN( bubbleSize ):
-                    return new DataSourceContext( *this, 
mrModel.maSources.create( SeriesModel::POINTS ) );
+                    return new DataSourceContext( *this, 
mrModel.maSources.create( DataSourceType::POINTS ) );
                 case C_TOKEN( dLbls ):
                     return new DataLabelsContext( *this, 
mrModel.mxLabels.create(bMSO2007Doc) );
                 case C_TOKEN( dPt ):
@@ -577,9 +577,9 @@ ContextHandlerRef BubbleSeriesContext::onCreateContext( 
sal_Int32 nElement, cons
                 case C_TOKEN( trendline ):
                     return new TrendlineContext( *this, 
mrModel.maTrendlines.create(bMSO2007Doc) );
                 case C_TOKEN( xVal ):
-                    return new DataSourceContext( *this, 
mrModel.maSources.create( SeriesModel::CATEGORIES ) );
+                    return new DataSourceContext( *this, 
mrModel.maSources.create( DataSourceType::CATEGORIES ) );
                 case C_TOKEN( yVal ):
-                    return new DataSourceContext( *this, 
mrModel.maSources.create( SeriesModel::VALUES ) );
+                    return new DataSourceContext( *this, 
mrModel.maSources.create( DataSourceType::VALUES ) );
             }
         break;
     }
@@ -604,7 +604,7 @@ ContextHandlerRef LineSeriesContext::onCreateContext( 
sal_Int32 nElement, const
             switch( nElement )
             {
                 case C_TOKEN( cat ):
-                    return new DataSourceContext( *this, 
mrModel.maSources.create( SeriesModel::CATEGORIES ) );
+                    return new DataSourceContext( *this, 
mrModel.maSources.create( DataSourceType::CATEGORIES ) );
                 case C_TOKEN( dLbls ):
                     return new DataLabelsContext( *this, 
mrModel.mxLabels.create(bMSO2007Doc) );
                 case C_TOKEN( dPt ):
@@ -619,7 +619,7 @@ ContextHandlerRef LineSeriesContext::onCreateContext( 
sal_Int32 nElement, const
                 case C_TOKEN( trendline ):
                     return new TrendlineContext( *this, 
mrModel.maTrendlines.create(bMSO2007Doc) );
                 case C_TOKEN( val ):
-                    return new DataSourceContext( *this, 
mrModel.maSources.create( SeriesModel::VALUES ) );
+                    return new DataSourceContext( *this, 
mrModel.maSources.create( DataSourceType::VALUES ) );
             }
         break;
     }
@@ -644,7 +644,7 @@ ContextHandlerRef PieSeriesContext::onCreateContext( 
sal_Int32 nElement, const A
             switch( nElement )
             {
                 case C_TOKEN( cat ):
-                    return new DataSourceContext( *this, 
mrModel.maSources.create( SeriesModel::CATEGORIES ) );
+                    return new DataSourceContext( *this, 
mrModel.maSources.create( DataSourceType::CATEGORIES ) );
                 case C_TOKEN( dLbls ):
                     return new DataLabelsContext( *this, 
mrModel.mxLabels.create(bMSO2007Doc) );
                 case C_TOKEN( dPt ):
@@ -653,7 +653,7 @@ ContextHandlerRef PieSeriesContext::onCreateContext( 
sal_Int32 nElement, const A
                     mrModel.mnExplosion = rAttribs.getInteger( XML_val, 0 );
                     return nullptr;
                 case C_TOKEN( val ):
-                    return new DataSourceContext( *this, 
mrModel.maSources.create( SeriesModel::VALUES ) );
+                    return new DataSourceContext( *this, 
mrModel.maSources.create( DataSourceType::VALUES ) );
             }
         break;
     }
@@ -678,7 +678,7 @@ ContextHandlerRef RadarSeriesContext::onCreateContext( 
sal_Int32 nElement, const
             switch( nElement )
             {
                 case C_TOKEN( cat ):
-                    return new DataSourceContext( *this, 
mrModel.maSources.create( SeriesModel::CATEGORIES ) );
+                    return new DataSourceContext( *this, 
mrModel.maSources.create( DataSourceType::CATEGORIES ) );
                 case C_TOKEN( dLbls ):
                     return new DataLabelsContext( *this, 
mrModel.mxLabels.create(bMSO2007Doc) );
                 case C_TOKEN( dPt ):
@@ -689,7 +689,7 @@ ContextHandlerRef RadarSeriesContext::onCreateContext( 
sal_Int32 nElement, const
                     mrModel.mbSmooth = rAttribs.getBool( XML_val, bMSO2007Doc 
);
                     return nullptr;
                 case C_TOKEN( val ):
-                    return new DataSourceContext( *this, 
mrModel.maSources.create( SeriesModel::VALUES ) );
+                    return new DataSourceContext( *this, 
mrModel.maSources.create( DataSourceType::VALUES ) );
             }
         break;
     }
@@ -727,9 +727,9 @@ ContextHandlerRef ScatterSeriesContext::onCreateContext( 
sal_Int32 nElement, con
                 case C_TOKEN( trendline ):
                     return new TrendlineContext( *this, 
mrModel.maTrendlines.create(bMSO2007Doc) );
                 case C_TOKEN( xVal ):
-                    return new DataSourceContext( *this, 
mrModel.maSources.create( SeriesModel::CATEGORIES ) );
+                    return new DataSourceContext( *this, 
mrModel.maSources.create( DataSourceType::CATEGORIES ) );
                 case C_TOKEN( yVal ):
-                    return new DataSourceContext( *this, 
mrModel.maSources.create( SeriesModel::VALUES ) );
+                    return new DataSourceContext( *this, 
mrModel.maSources.create( DataSourceType::VALUES ) );
             }
         break;
     }
@@ -753,9 +753,9 @@ ContextHandlerRef SurfaceSeriesContext::onCreateContext( 
sal_Int32 nElement, con
             switch( nElement )
             {
                 case C_TOKEN( cat ):
-                    return new DataSourceContext( *this, 
mrModel.maSources.create( SeriesModel::CATEGORIES ) );
+                    return new DataSourceContext( *this, 
mrModel.maSources.create( DataSourceType::CATEGORIES ) );
                 case C_TOKEN( val ):
-                    return new DataSourceContext( *this, 
mrModel.maSources.create( SeriesModel::VALUES ) );
+                    return new DataSourceContext( *this, 
mrModel.maSources.create( DataSourceType::VALUES ) );
             }
         break;
     }
@@ -789,7 +789,7 @@ ContextHandlerRef ChartexSeriesContext::onCreateContext( 
sal_Int32 nElement, con
                 case CX_TOKEN( dataLabels ):
                     return new DataLabelsContext( *this, 
mrModel.mxLabels.create(false) );
                 case CX_TOKEN( dataId ):
-                    // TODO
+                    mrModel.mnDataId = rAttribs.getInteger(XML_val, -1);
                     return nullptr;
                 case CX_TOKEN( layoutPr ):
                     // This looks complicated. TODO
diff --git a/oox/source/drawingml/chart/seriesconverter.cxx 
b/oox/source/drawingml/chart/seriesconverter.cxx
index 27f25057badf..c0a15a85d922 100644
--- a/oox/source/drawingml/chart/seriesconverter.cxx
+++ b/oox/source/drawingml/chart/seriesconverter.cxx
@@ -799,12 +799,12 @@ SeriesConverter::~SeriesConverter()
 
 Reference< XLabeledDataSequence > SeriesConverter::createCategorySequence( 
const OUString& rRole )
 {
-    return createLabeledDataSequence(SeriesModel::CATEGORIES, rRole, false);
+    return createLabeledDataSequence(DataSourceType::CATEGORIES, rRole, false);
 }
 
 Reference< XLabeledDataSequence > SeriesConverter::createValueSequence( const 
OUString& rRole )
 {
-    return createLabeledDataSequence( SeriesModel::VALUES, rRole, true );
+    return createLabeledDataSequence( DataSourceType::VALUES, rRole, true );
 }
 
 Reference< XDataSeries > SeriesConverter::createDataSeries( const 
TypeGroupConverter& rTypeGroup, bool bVaryColorsByPoint )
@@ -844,7 +844,8 @@ Reference< XDataSeries > SeriesConverter::createDataSeries( 
const TypeGroupConve
             // add size values of bubble charts
             if( rTypeInfo.meTypeId == TYPEID_BUBBLE )
             {
-                Reference< XLabeledDataSequence > xSizeValueSeq = 
createLabeledDataSequence( SeriesModel::POINTS, u"values-size"_ustr, true );
+                Reference< XLabeledDataSequence > xSizeValueSeq =
+                    createLabeledDataSequence( DataSourceType::POINTS, 
u"values-size"_ustr, true );
                 if( xSizeValueSeq.is() )
                     aLabeledSeqVec.push_back( xSizeValueSeq );
             }
@@ -936,7 +937,7 @@ Reference< XDataSeries > SeriesConverter::createDataSeries( 
const TypeGroupConve
         if( xLabels->maNumberFormat.maFormatCode.isEmpty() )
         {
             // Use number format code from Value series
-            DataSourceModel* pValues = mrModel.maSources.get( 
SeriesModel::VALUES ).get();
+            DataSourceModel* pValues = mrModel.maSources.get( 
DataSourceType::VALUES ).get();
             if( pValues )
                 xLabels->maNumberFormat.maFormatCode = 
pValues->mxDataSeq->maFormatCode;
         }
@@ -950,7 +951,7 @@ Reference< XDataSeries > SeriesConverter::createDataSeries( 
const TypeGroupConve
 // private --------------------------------------------------------------------
 
 Reference< XLabeledDataSequence > SeriesConverter::createLabeledDataSequence(
-        SeriesModel::SourceType eSourceType, const OUString& rRole, bool 
bUseTextLabel )
+        enum DataSourceType eSourceType, const OUString& rRole, bool 
bUseTextLabel )
 {
     DataSourceModel* pValues = mrModel.maSources.get( eSourceType ).get();
     TextModel* pTitle = bUseTextLabel ? mrModel.mxText.get() : nullptr;
diff --git a/oox/source/drawingml/chart/seriesmodel.cxx 
b/oox/source/drawingml/chart/seriesmodel.cxx
index 95b0deb225e7..194d4f6ecf3d 100644
--- a/oox/source/drawingml/chart/seriesmodel.cxx
+++ b/oox/source/drawingml/chart/seriesmodel.cxx
@@ -112,6 +112,7 @@ SeriesModel::SeriesModel(bool bMSO2007Doc) :
     mnMarkerSize( 5 ),
     mnMarkerSymbol( XML_auto ),
     mnOrder( -1 ),
+    mnDataId(-1),
     mbBubble3d( !bMSO2007Doc ),
     mbInvertNeg( !bMSO2007Doc ),
     mbSmooth( !bMSO2007Doc )
diff --git a/oox/source/drawingml/chart/typegroupconverter.cxx 
b/oox/source/drawingml/chart/typegroupconverter.cxx
index 8f586524af5c..938c09646a04 100644
--- a/oox/source/drawingml/chart/typegroupconverter.cxx
+++ b/oox/source/drawingml/chart/typegroupconverter.cxx
@@ -39,6 +39,7 @@
 #include <comphelper/sequence.hxx>
 #include <osl/diagnose.h>
 #include <drawingml/lineproperties.hxx>
+#include <drawingml/chart/seriesmodel.hxx>
 #include <drawingml/chart/seriesconverter.hxx>
 #include <drawingml/chart/typegroupmodel.hxx>
 #include <oox/core/xmlfilterbase.hxx>
@@ -286,16 +287,16 @@ Reference< XLabeledDataSequence > 
TypeGroupConverter::createCategorySequence()
         first series, even if it was empty. */
     for (auto const& elem : mrModel.maSeries)
     {
-        if( elem->maSources.has( SeriesModel::CATEGORIES ) )
+        if( elem->maSources.has( DataSourceType::CATEGORIES ) )
         {
             SeriesConverter aSeriesConv(*this, *elem);
             xLabeledSeq = aSeriesConv.createCategorySequence( 
u"categories"_ustr );
             if (xLabeledSeq.is())
                 break;
         }
-        else if( nMaxValues <= 0 && elem->maSources.has( SeriesModel::VALUES ) 
)
+        else if( nMaxValues <= 0 && elem->maSources.has( 
DataSourceType::VALUES ) )
         {
-            DataSourceModel *pValues = elem->maSources.get( 
SeriesModel::VALUES ).get();
+            DataSourceModel *pValues = elem->maSources.get( 
DataSourceType::VALUES ).get();
             if( pValues->mxDataSeq.is() )
                 nMaxValues = pValues->mxDataSeq->maData.size();
         }
@@ -306,9 +307,9 @@ Reference< XLabeledDataSequence > 
TypeGroupConverter::createCategorySequence()
             nMaxValues = 2;
         typedef RefVector<SeriesModel> SeriesModelVector;
         SeriesModelVector::value_type aModel = mrModel.maSeries.get(0);
-        if (!aModel->maSources.has(SeriesModel::CATEGORIES))
+        if (!aModel->maSources.has(DataSourceType::CATEGORIES))
         {
-            DataSourceModel &aSrc = aModel->maSources.create( 
SeriesModel::CATEGORIES );
+            DataSourceModel &aSrc = aModel->maSources.create( 
DataSourceType::CATEGORIES );
             DataSequenceModel &aSeq = aSrc.mxDataSeq.create();
             aSeq.mnPointCount = nMaxValues;
             for( sal_Int32 i = 0; i < nMaxValues; i++ )
@@ -322,7 +323,8 @@ Reference< XLabeledDataSequence > 
TypeGroupConverter::createCategorySequence()
 
 void TypeGroupConverter::convertFromModel( const Reference< XDiagram >& 
rxDiagram,
         const Reference< XCoordinateSystem >& rxCoordSystem,
-        sal_Int32 nAxesSetIdx, bool bSupportsVaryColorsByPoint )
+        sal_Int32 nAxesSetIdx,
+        bool bSupportsVaryColorsByPoint )
 {
     try
     {
@@ -610,6 +612,22 @@ PieChartSubType 
TypeGroupConverter::convertOfPieType(sal_Int32 nOoxOfPieType ) c
     }
 }
 
+void TypeGroupConverter::moveDataToSeries(DataSourceCxModel::DataMap& 
raDataMap)
+{
+    // For chartex, move data from rDataMap to the appropriate series. In
+    // chartex the data is given outside the series, in a child element of
+    // <cx:chartSpace>. Pre-2016 charts have the data inside the series, and
+    // SeriesModel and subsequent handling reflects this. So here we get the
+    // data to the right place for processing.
+    if (!raDataMap.empty()) {
+        // should only happen for chartex
+        for (auto const& elem : mrModel.maSeries) {
+            // This ID must be present in the map
+            assert(raDataMap.find(elem->mnDataId) != raDataMap.end());
+            elem->maSources = *(raDataMap[elem->mnDataId]);
+        }
+    }
+}
 
 // private --------------------------------------------------------------------
 
diff --git a/oox/source/export/chartexport.cxx 
b/oox/source/export/chartexport.cxx
index c0179d033bc8..ee6150d47f2a 100644
--- a/oox/source/export/chartexport.cxx
+++ b/oox/source/export/chartexport.cxx
@@ -230,12 +230,66 @@ void outputDataPointStyleEntry(FSHelperPtr pFS)
 
     pFS->endElement(FSNS(XML_cs, XML_dataPoint));
 }
-  
+
+std::vector<Sequence<Reference<chart2::XDataSeries> > > 
splitDataSeriesByAxis(const Reference< chart2::XChartType >& xChartType)
+{
+    std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitSeries;
+    std::map<sal_Int32, size_t> aMapAxisToIndex;
+
+    Reference< chart2::XDataSeriesContainer > xDSCnt(xChartType, 
uno::UNO_QUERY);
+    if (xDSCnt.is())
+    {
+        sal_Int32 nAxisIndexOfFirstSeries = -1;
+        const Sequence< Reference< chart2::XDataSeries > > 
aSeriesSeq(xDSCnt->getDataSeries());
+        for (const uno::Reference<chart2::XDataSeries>& xSeries : aSeriesSeq)
+        {
+            Reference<beans::XPropertySet> xPropSet(xSeries, uno::UNO_QUERY);
+            if (!xPropSet.is())
+                continue;
+
+            sal_Int32 nAxisIndex = -1;
+            uno::Any aAny = 
xPropSet->getPropertyValue(u"AttachedAxisIndex"_ustr);
+            aAny >>= nAxisIndex;
+            size_t nVectorPos = 0;
+            if (nAxisIndexOfFirstSeries == -1)
+            {
+                nAxisIndexOfFirstSeries = nAxisIndex;
+            }
+
+            auto it = aMapAxisToIndex.find(nAxisIndex);
+            if (it == aMapAxisToIndex.end())
+            {
+                aSplitSeries.emplace_back();
+                nVectorPos = aSplitSeries.size() - 1;
+                aMapAxisToIndex.insert(std::pair<sal_Int32, 
size_t>(nAxisIndex, nVectorPos));
+            }
+            else
+            {
+                nVectorPos = it->second;
+            }
+
+            uno::Sequence<Reference<chart2::XDataSeries> >& rAxisSeriesSeq = 
aSplitSeries[nVectorPos];
+            sal_Int32 nLength = rAxisSeriesSeq.getLength();
+            rAxisSeriesSeq.realloc(nLength + 1);
+            rAxisSeriesSeq.getArray()[nLength] = xSeries;
+        }
+        // if the first series attached to secondary axis, then export those 
series first, which are attached to primary axis
+        // also the MS Office export every time in this order
+        if (aSplitSeries.size() > 1 && nAxisIndexOfFirstSeries == 1)
+        {
+            std::swap(aSplitSeries[0], aSplitSeries[1]);
+        }
+    }
+
+    return aSplitSeries;
 }
 
-static Reference< chart2::data::XLabeledDataSequence > lcl_getCategories( 
const Reference< chart2::XDiagram > & xDiagram, bool& bHasDateCategories )
+}   // unnamed namespace
+
+static Reference< chart2::data::XLabeledDataSequence > lcl_getCategories(
+        const Reference< chart2::XDiagram > & xDiagram, bool 
*pbHasDateCategories )
 {
-    bHasDateCategories = false;
+    *pbHasDateCategories = false;
     Reference< chart2::data::XLabeledDataSequence >  xResult;
     try
     {
@@ -258,7 +312,7 @@ static Reference< chart2::data::XLabeledDataSequence > 
lcl_getCategories( const
                         chart2::ScaleData aScaleData = xAxis->getScaleData();
                         if( aScaleData.Categories.is())
                         {
-                            bHasDateCategories = aScaleData.AxisType == 
chart2::AxisType::DATE;
+                            *pbHasDateCategories = aScaleData.AxisType == 
chart2::AxisType::DATE;
                             xResult.set( aScaleData.Categories );
                             break;
                         }
@@ -298,7 +352,8 @@ static bool lcl_hasCategoryLabels( const Reference< 
chart2::XChartDocument >& xC
     //categories are always the first sequence
     Reference< chart2::XDiagram > xDiagram( xChartDoc->getFirstDiagram());
     bool bDateCategories;
-    Reference< chart2::data::XLabeledDataSequence > xCategories( 
lcl_getCategories( xDiagram, bDateCategories ) );
+    Reference< chart2::data::XLabeledDataSequence > xCategories(
+            lcl_getCategories( xDiagram, &bDateCategories ) );
     return xCategories.is();
 }
 
@@ -1272,7 +1327,7 @@ void ChartExport::exportChartSpace( const Reference< 
css::chart::XChartDocument
         pFS->startElement(FSNS(XML_cx, XML_chartData));
 
         exportExternalData(xChartDoc, true);
-        exportData(xChartDoc, true);
+        exportData_chartex(xChartDoc);
 
         pFS->endElement(FSNS(XML_cx, XML_chartData));
     } else {
@@ -1321,22 +1376,239 @@ void ChartExport::exportChartSpace( const Reference< 
css::chart::XChartDocument
     pFS->endElement( FSNS( nChartNS, XML_chartSpace ) );
 }
 
-void ChartExport::exportData( [[maybe_unused]] const Reference< 
css::chart::XChartDocument >& xChartDoc,
-        bool bIsChartex)
+void ChartExport::exportData_chartex( [[maybe_unused]] const Reference< 
css::chart::XChartDocument >& xChartDoc)
 {
-    if (bIsChartex) {
-        FSHelperPtr pFS = GetFS();
-
-        pFS->startElement(FSNS(XML_cx, XML_data), XML_id, "0");
-        // Just hard-coding this for now
-        pFS->startElement(FSNS(XML_cx, XML_numDim), XML_type, "val");
-        pFS->startElement(FSNS(XML_cx, XML_f));
-        pFS->writeEscaped("_xlchart.v2.0");    // I have no idea what this
-                                                // means or what it should be 
in
-                                                // general
-        pFS->endElement(FSNS(XML_cx, XML_f));
-        pFS->endElement(FSNS(XML_cx, XML_numDim));
-        pFS->endElement(FSNS(XML_cx, XML_data));
+    Reference< chart2::XCoordinateSystemContainer > xBCooSysCnt( mxNewDiagram, 
uno::UNO_QUERY );
+    if( ! xBCooSysCnt.is()) return;
+    const Sequence< Reference< chart2::XCoordinateSystem > >
+        aCooSysSeq( xBCooSysCnt->getCoordinateSystems());
+
+    if (!aCooSysSeq.hasElements()) return;
+
+    for( const auto& rCS : aCooSysSeq ) {
-e 
... etc. - the rest is truncated

Reply via email to