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