summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomaž Vajngerl <tomaz.vajngerl@collabora.co.uk>2022-03-04 20:40:14 +0900
committerTomaž Vajngerl <quikee@gmail.com>2022-04-11 01:59:20 +0200
commit44d599ca1c4bfdf1b803b707cadc7eb5158af979 (patch)
tree967fa964264fd97ac944805274649e4f0bb0ce62
parentd076da9d3d9a66ad3f1c28ee2688b9040e8a6ade (diff)
sc: Sparkline export for OOXML documents + roundtrip test
Change-Id: I4ab93d7ad33867ae817aa98d13ea9bc724b7d710 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132248 Tested-by: Jenkins Reviewed-by: Tomaž Vajngerl <quikee@gmail.com> (cherry picked from commit 0d9aa7388541d10a463c2d0a8d752d234aecfddd) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132776 Tested-by: Tomaž Vajngerl <quikee@gmail.com>
-rw-r--r--sc/CppunitTest_sc_sparkline_test.mk1
-rw-r--r--sc/Library_scfilt.mk1
-rw-r--r--sc/qa/unit/SparklineImportExportTest.cxx21
-rw-r--r--sc/source/filter/excel/excdoc.cxx3
-rw-r--r--sc/source/filter/excel/export/SparklineExt.cxx246
-rw-r--r--sc/source/filter/inc/export/SparklineExt.hxx54
-rw-r--r--sc/source/filter/inc/xeextlst.hxx3
7 files changed, 323 insertions, 6 deletions
diff --git a/sc/CppunitTest_sc_sparkline_test.mk b/sc/CppunitTest_sc_sparkline_test.mk
index 5dcd9a9921b5..30513b273791 100644
--- a/sc/CppunitTest_sc_sparkline_test.mk
+++ b/sc/CppunitTest_sc_sparkline_test.mk
@@ -32,6 +32,7 @@ $(eval $(call gb_CppunitTest_use_libraries,sc_sparkline_test, \
test \
tl \
unotest \
+ utl \
vcl \
))
diff --git a/sc/Library_scfilt.mk b/sc/Library_scfilt.mk
index da73e3c43f15..eff7cca485e4 100644
--- a/sc/Library_scfilt.mk
+++ b/sc/Library_scfilt.mk
@@ -130,6 +130,7 @@ $(eval $(call gb_Library_add_exception_objects,scfilt,\
sc/source/filter/excel/xltools \
sc/source/filter/excel/xltracer \
sc/source/filter/excel/xlview \
+ sc/source/filter/excel/export/SparklineExt \
sc/source/filter/ftools/fapihelper \
sc/source/filter/ftools/fprogressbar \
sc/source/filter/ftools/ftools \
diff --git a/sc/qa/unit/SparklineImportExportTest.cxx b/sc/qa/unit/SparklineImportExportTest.cxx
index cf970ae8995c..f1af8f9d5eb1 100644
--- a/sc/qa/unit/SparklineImportExportTest.cxx
+++ b/sc/qa/unit/SparklineImportExportTest.cxx
@@ -50,12 +50,10 @@ public:
CPPUNIT_TEST_SUITE_END();
};
-void SparklineImportExportTest::testSparklines()
+namespace
+{
+void checkSparklines(ScDocument& rDocument)
{
- ScDocShellRef xDocSh = loadDoc(u"Sparklines.", FORMAT_XLSX);
- CPPUNIT_ASSERT(xDocSh);
-
- ScDocument& rDocument = xDocSh->GetDocument();
// Sparkline at Sheet1:A2
{
sc::Sparkline* pSparkline = rDocument.GetSparkline(ScAddress(0, 1, 0)); // A2
@@ -148,6 +146,19 @@ void SparklineImportExportTest::testSparklines()
sc::Sparkline* pSparkline = rDocument.GetSparkline(ScAddress(0, 3, 0)); //A4
CPPUNIT_ASSERT(!pSparkline);
}
+}
+} // end anonymous namespace
+
+void SparklineImportExportTest::testSparklines()
+{
+ ScDocShellRef xDocSh = loadDoc(u"Sparklines.", FORMAT_XLSX);
+ CPPUNIT_ASSERT(xDocSh);
+
+ checkSparklines(xDocSh->GetDocument());
+
+ xDocSh = saveAndReload(*xDocSh, FORMAT_XLSX);
+
+ checkSparklines(xDocSh->GetDocument());
xDocSh->DoClose();
}
diff --git a/sc/source/filter/excel/excdoc.cxx b/sc/source/filter/excel/excdoc.cxx
index 6b02457f70f6..529e76971cbc 100644
--- a/sc/source/filter/excel/excdoc.cxx
+++ b/sc/source/filter/excel/excdoc.cxx
@@ -39,6 +39,7 @@
#include <xecontent.hxx>
#include <xeescher.hxx>
#include <xepivot.hxx>
+#include <export/SparklineExt.hxx>
#include <XclExpChangeTrack.hxx>
#include <xepivotxml.hxx>
#include <xedbdata.hxx>
@@ -611,6 +612,8 @@ void ExcTable::FillAsTableXml()
// conditional formats
Add( new XclExpCondFormatBuffer( GetRoot(), xExtLst ) );
+ Add(new xcl::exp::SparklineBuffer(GetRoot(), xExtLst));
+
// data validation (DVAL and list of DV records), generated by the cell table
aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_DVAL ) );
diff --git a/sc/source/filter/excel/export/SparklineExt.cxx b/sc/source/filter/excel/export/SparklineExt.cxx
new file mode 100644
index 000000000000..f0f3cd9e1d98
--- /dev/null
+++ b/sc/source/filter/excel/export/SparklineExt.cxx
@@ -0,0 +1,246 @@
+/* -*- 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 <export/SparklineExt.hxx>
+
+#include <oox/export/utils.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+
+using namespace oox;
+
+namespace xcl::exp
+{
+SparklineExt::SparklineExt(const XclExpRoot& rRoot,
+ std::vector<std::shared_ptr<sc::Sparkline>> const& pSparklines)
+ : XclExpExt(rRoot)
+{
+ maURI = "{05C60535-1F16-4fd2-B633-F4F36F0B64E0}";
+
+ for (auto const& pSparkline : pSparklines)
+ {
+ auto* pGroupPointer = pSparkline->getSparklineGroup().get();
+
+ auto aIterator = m_aSparklineGroupMap.find(pGroupPointer);
+ if (aIterator == m_aSparklineGroupMap.end())
+ {
+ std::vector<std::shared_ptr<sc::Sparkline>> aSparklineVector;
+ aSparklineVector.push_back(pSparkline);
+ m_aSparklineGroupMap.emplace(pGroupPointer, aSparklineVector);
+ }
+ else
+ {
+ aIterator->second.push_back(pSparkline);
+ }
+ }
+}
+
+void SparklineExt::SaveXml(XclExpXmlStream& rStream)
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStream.GetCurrentStream();
+ rWorksheet->startElement(XML_ext, FSNS(XML_xmlns, XML_x14),
+ rStream.getNamespaceURL(OOX_NS(xls14Lst)), XML_uri, maURI);
+
+ rWorksheet->startElementNS(XML_x14, XML_sparklineGroups, FSNS(XML_xmlns, XML_xm),
+ rStream.getNamespaceURL(OOX_NS(xm)));
+
+ for (auto const & [ pSparklineGroup, rSparklineVector ] : m_aSparklineGroupMap)
+ {
+ addSparklineGroup(rStream, *pSparklineGroup, rSparklineVector);
+ }
+
+ rWorksheet->endElementNS(XML_x14, XML_sparklineGroups);
+ rWorksheet->endElement(XML_ext);
+}
+
+void SparklineExt::addSparklineGroupAttributes(
+ rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList,
+ sc::SparklineGroup& rSparklineGroup)
+{
+ if (rSparklineGroup.m_fLineWeight != 0.75)
+ pAttrList->add(XML_lineWeight, OString::number(rSparklineGroup.m_fLineWeight));
+
+ if (rSparklineGroup.m_eType != sc::SparklineType::Line)
+ {
+ if (rSparklineGroup.m_eType == sc::SparklineType::Column)
+ pAttrList->add(XML_type, "column");
+ else if (rSparklineGroup.m_eType == sc::SparklineType::Stacked)
+ pAttrList->add(XML_type, "stacked");
+ }
+
+ if (rSparklineGroup.m_bDateAxis)
+ pAttrList->add(XML_dateAxis, "1");
+
+ if (rSparklineGroup.m_eDisplayEmptyCellsAs != sc::DisplayEmptyCellAs::Zero)
+ {
+ if (rSparklineGroup.m_eDisplayEmptyCellsAs == sc::DisplayEmptyCellAs::Gap)
+ pAttrList->add(XML_displayEmptyCellsAs, "gap");
+ else if (rSparklineGroup.m_eDisplayEmptyCellsAs == sc::DisplayEmptyCellAs::Span)
+ pAttrList->add(XML_displayEmptyCellsAs, "span");
+ }
+
+ if (rSparklineGroup.m_bMarkers)
+ pAttrList->add(XML_markers, "1");
+ if (rSparklineGroup.m_bHigh)
+ pAttrList->add(XML_high, "1");
+ if (rSparklineGroup.m_bLow)
+ pAttrList->add(XML_low, "1");
+ if (rSparklineGroup.m_bFirst)
+ pAttrList->add(XML_first, "1");
+ if (rSparklineGroup.m_bLast)
+ pAttrList->add(XML_last, "1");
+ if (rSparklineGroup.m_bNegative)
+ pAttrList->add(XML_negative, "1");
+ if (rSparklineGroup.m_bDisplayXAxis)
+ pAttrList->add(XML_displayXAxis, "1");
+ if (rSparklineGroup.m_bDisplayHidden)
+ pAttrList->add(XML_displayHidden, "1");
+
+ if (rSparklineGroup.m_eMinAxisType != sc::AxisType::Individual)
+ {
+ if (rSparklineGroup.m_eMinAxisType == sc::AxisType::Group)
+ pAttrList->add(XML_minAxisType, "group");
+ else if (rSparklineGroup.m_eMinAxisType == sc::AxisType::Custom)
+ pAttrList->add(XML_minAxisType, "custom");
+ }
+
+ if (rSparklineGroup.m_eMaxAxisType != sc::AxisType::Individual)
+ {
+ if (rSparklineGroup.m_eMaxAxisType == sc::AxisType::Group)
+ pAttrList->add(XML_maxAxisType, "group");
+ else if (rSparklineGroup.m_eMaxAxisType == sc::AxisType::Custom)
+ pAttrList->add(XML_maxAxisType, "custom");
+ }
+
+ if (rSparklineGroup.m_bRightToLeft)
+ pAttrList->add(XML_rightToLeft, "1");
+
+ if (rSparklineGroup.m_aManualMax && rSparklineGroup.m_eMaxAxisType == sc::AxisType::Custom)
+ pAttrList->add(XML_manualMax, OString::number(*rSparklineGroup.m_aManualMax));
+
+ if (rSparklineGroup.m_aManualMin && rSparklineGroup.m_eMinAxisType == sc::AxisType::Custom)
+ pAttrList->add(XML_manualMin, OString::number(*rSparklineGroup.m_aManualMin));
+}
+
+void SparklineExt::addSparklineGroupColors(XclExpXmlStream& rStream,
+ sc::SparklineGroup& rSparklineGroup)
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStream.GetCurrentStream();
+
+ rWorksheet->singleElementNS(XML_x14, XML_colorSeries, XML_rgb,
+ XclXmlUtils::ToOString(rSparklineGroup.m_aColorSeries));
+
+ if (rSparklineGroup.m_aColorSeries != COL_TRANSPARENT)
+ {
+ rWorksheet->singleElementNS(XML_x14, XML_colorNegative, XML_rgb,
+ XclXmlUtils::ToOString(rSparklineGroup.m_aColorNegative));
+ }
+
+ if (rSparklineGroup.m_aColorAxis != COL_TRANSPARENT)
+ {
+ rWorksheet->singleElementNS(XML_x14, XML_colorAxis, XML_rgb,
+ XclXmlUtils::ToOString(rSparklineGroup.m_aColorAxis));
+ }
+
+ if (rSparklineGroup.m_aColorMarkers != COL_TRANSPARENT)
+ {
+ rWorksheet->singleElementNS(XML_x14, XML_colorMarkers, XML_rgb,
+ XclXmlUtils::ToOString(rSparklineGroup.m_aColorMarkers));
+ }
+
+ if (rSparklineGroup.m_aColorFirst != COL_TRANSPARENT)
+ {
+ rWorksheet->singleElementNS(XML_x14, XML_colorFirst, XML_rgb,
+ XclXmlUtils::ToOString(rSparklineGroup.m_aColorFirst));
+ }
+
+ if (rSparklineGroup.m_aColorLast != COL_TRANSPARENT)
+ {
+ rWorksheet->singleElementNS(XML_x14, XML_colorLast, XML_rgb,
+ XclXmlUtils::ToOString(rSparklineGroup.m_aColorLast));
+ }
+
+ if (rSparklineGroup.m_aColorHigh != COL_TRANSPARENT)
+ {
+ rWorksheet->singleElementNS(XML_x14, XML_colorHigh, XML_rgb,
+ XclXmlUtils::ToOString(rSparklineGroup.m_aColorHigh));
+ }
+
+ if (rSparklineGroup.m_aColorLow != COL_TRANSPARENT)
+ {
+ rWorksheet->singleElementNS(XML_x14, XML_colorLow, XML_rgb,
+ XclXmlUtils::ToOString(rSparklineGroup.m_aColorLow));
+ }
+}
+
+void SparklineExt::addSparklineGroup(XclExpXmlStream& rStream, sc::SparklineGroup& rSparklineGroup,
+ std::vector<std::shared_ptr<sc::Sparkline>> const& rSparklines)
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStream.GetCurrentStream();
+
+ // Sparkline Group Attributes
+ auto pAttrList = sax_fastparser::FastSerializerHelper::createAttrList();
+ addSparklineGroupAttributes(pAttrList, rSparklineGroup);
+
+ rWorksheet->startElementNS(XML_x14, XML_sparklineGroup, pAttrList);
+
+ addSparklineGroupColors(rStream, rSparklineGroup);
+
+ // Sparklines
+
+ rWorksheet->startElementNS(XML_x14, XML_sparklines);
+ for (auto const& rSparkline : rSparklines)
+ {
+ rWorksheet->startElementNS(XML_x14, XML_sparkline);
+
+ {
+ rWorksheet->startElementNS(XML_xm, XML_f);
+
+ OUString sRangeFormula;
+ ScRefFlags eFlags = ScRefFlags::VALID | ScRefFlags::TAB_3D;
+ rSparkline->getInputRange().Format(sRangeFormula, eFlags, GetDoc(),
+ formula::FormulaGrammar::CONV_XL_OOX, ' ', true);
+
+ rWorksheet->writeEscaped(sRangeFormula);
+ rWorksheet->endElementNS(XML_xm, XML_f);
+ }
+
+ {
+ rWorksheet->startElementNS(XML_xm, XML_sqref);
+
+ ScAddress::Details detailsXL(formula::FormulaGrammar::CONV_XL_OOX);
+ ScAddress aAddress(rSparkline->getColumn(), rSparkline->getRow(), GetCurrScTab());
+ OUString sLocation = aAddress.Format(ScRefFlags::VALID, &GetDoc(), detailsXL);
+
+ rWorksheet->writeEscaped(sLocation);
+ rWorksheet->endElementNS(XML_xm, XML_sqref);
+ }
+
+ rWorksheet->endElementNS(XML_x14, XML_sparkline);
+ }
+ rWorksheet->endElementNS(XML_x14, XML_sparklines);
+ rWorksheet->endElementNS(XML_x14, XML_sparklineGroup);
+}
+
+SparklineBuffer::SparklineBuffer(const XclExpRoot& rRoot, XclExtLstRef const& xExtLst)
+ : XclExpRoot(rRoot)
+{
+ if (sc::SparklineList* pSparklineList = GetDoc().GetSparklineList(GetCurrScTab()))
+ {
+ auto pSparklines = pSparklineList->getSparklines();
+ if (!pSparklines.empty())
+ {
+ xExtLst->AddRecord(new xcl::exp::SparklineExt(GetRoot(), pSparklines));
+ }
+ }
+}
+
+} // end namespace xcl::exp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/export/SparklineExt.hxx b/sc/source/filter/inc/export/SparklineExt.hxx
new file mode 100644
index 000000000000..aa649d89e772
--- /dev/null
+++ b/sc/source/filter/inc/export/SparklineExt.hxx
@@ -0,0 +1,54 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <memory>
+#include <vector>
+#include <map>
+#include <rangelst.hxx>
+#include <Sparkline.hxx>
+
+#include <sax/fastattribs.hxx>
+
+#include <xerecord.hxx>
+#include <xeroot.hxx>
+#include <xeextlst.hxx>
+
+namespace xcl::exp
+{
+class SparklineExt : public XclExpExt
+{
+ std::map<sc::SparklineGroup*, std::vector<std::shared_ptr<sc::Sparkline>>> m_aSparklineGroupMap;
+
+public:
+ SparklineExt(const XclExpRoot& rRoot,
+ std::vector<std::shared_ptr<sc::Sparkline>> const& pSparklines);
+
+ void SaveXml(XclExpXmlStream& rStream) override;
+ void addSparklineGroup(XclExpXmlStream& rStream, sc::SparklineGroup& rSparklineGroup,
+ std::vector<std::shared_ptr<sc::Sparkline>> const& rSparklines);
+ static void
+ addSparklineGroupAttributes(rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList,
+ sc::SparklineGroup& rSparklineGroup);
+ static void addSparklineGroupColors(XclExpXmlStream& rStream,
+ sc::SparklineGroup& rSparklineGroup);
+
+ XclExpExtType GetType() override { return XclExpExtSparklineType; }
+};
+
+class SparklineBuffer : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit SparklineBuffer(const XclExpRoot& rRoot, const XclExtLstRef& xExtLst);
+};
+
+} // end namespace xcl::exp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xeextlst.hxx b/sc/source/filter/inc/xeextlst.hxx
index c5db5fcaff99..1770f9af191d 100644
--- a/sc/source/filter/inc/xeextlst.hxx
+++ b/sc/source/filter/inc/xeextlst.hxx
@@ -19,7 +19,8 @@
enum XclExpExtType
{
XclExpExtDataBarType,
- XclExpExtDataFooType
+ XclExpExtDataFooType,
+ XclExpExtSparklineType,
};
struct XclExpExtCondFormatData