chart2/qa/extras/chart2export.cxx | 14 chart2/qa/extras/chart2export2.cxx | 2 chart2/qa/extras/charttest.hxx | 13 chart2/qa/extras/data/xlsx/column-style.xlsx |binary docmodel/source/uno/UnoChartStyle.cxx | 11 include/docmodel/styles/ChartStyle.hxx | 60 +- include/docmodel/uno/UnoChartStyle.hxx | 2 include/oox/export/ThemeExport.hxx | 5 include/oox/export/chartexport.hxx | 12 include/oox/export/drawingml.hxx | 32 + oox/inc/drawingml/chart/stylefragment.hxx | 6 oox/inc/drawingml/chart/stylemodel.hxx | 4 oox/source/drawingml/chart/stylefragment.cxx | 10 oox/source/drawingml/chart/stylemodel.cxx | 5 oox/source/drawingml/shape.cxx | 34 + oox/source/export/ThemeExport.cxx | 4 oox/source/export/chartexport.cxx | 601 ++++++++++++++++---------- oox/source/export/drawingml.cxx | 610 +++++++++++++-------------- test/source/xmltesttools.cxx | 2 19 files changed, 846 insertions(+), 581 deletions(-)
New commits: commit 526287a4aa7fe06bcfaa8fedc26106bd1a87fc88 Author: Kurt Nordback <[email protected]> AuthorDate: Thu Sep 4 06:46:27 2025 -0600 Commit: Tomaž Vajngerl <[email protected]> CommitDate: Thu Dec 18 01:45:59 2025 +0100 tdf#167941 - Chart style file is not supported in OOXML Implement chart style file export, beginning to use the document model data rather than hard-coded text. Also add a simple test that style data round-trips OOXML -> LO -> OOXML properly. This uses oox structures in docmodel. That will be fixed in a subsequent commit. Change-Id: Icc0ef5b212cc32c8c84b51529bc4cf05ab54636c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/190586 Tested-by: Jenkins Reviewed-by: Tomaž Vajngerl <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195578 Tested-by: Jenkins CollaboraOffice <[email protected]> diff --git a/chart2/qa/extras/chart2export.cxx b/chart2/qa/extras/chart2export.cxx index 291f87586b9f..55f597dbe2f3 100644 --- a/chart2/qa/extras/chart2export.cxx +++ b/chart2/qa/extras/chart2export.cxx @@ -1396,6 +1396,20 @@ CPPUNIT_TEST_FIXTURE(Chart2ExportTest, testInvertNegative) } } +CPPUNIT_TEST_FIXTURE(Chart2ExportTest, testStyleImportExport) +{ + // column-style.xlsx is a hand-edited test that includes an axisTitle style + // with run property text size 777 (chosen simply as a distinctive number). + // Test that this value round-trips successfully. + loadFromFile(std::u16string_view(u"xlsx/column-style.xlsx")); + + save("Calc Office Open XML"); + xmlDocUniquePtr pXmlDoc = parseExport("xl/charts/style1.xml"); + CPPUNIT_ASSERT(pXmlDoc); + assertXPath(pXmlDoc, "/cs:chartStyle"); + assertXPath(pXmlDoc, "/cs:chartStyle/cs:axisTitle"); + assertXPath(pXmlDoc, "/cs:chartStyle/cs:axisTitle/a:defRPr", "sz", std::u16string_view(u"777")); +} CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/chart2/qa/extras/chart2export2.cxx b/chart2/qa/extras/chart2export2.cxx index d4881a353a59..95b03c2b4331 100644 --- a/chart2/qa/extras/chart2export2.cxx +++ b/chart2/qa/extras/chart2export2.cxx @@ -963,7 +963,7 @@ CPPUNIT_TEST_FIXTURE(Chart2ExportTest2, test_style) xmlDocUniquePtr pXmlDoc = parseExport(u"xl/charts/chart1.xml"_ustr); CPPUNIT_ASSERT(pXmlDoc); // workaround: use leave-gap instead of zero to show the original line chart - assertXPath(pXmlDoc, "/c:chartSpace/c:style", "val", u"23"); + assertXPath(pXmlDoc, "/c:chartSpace/c:style", "val", u"36"); } CPPUNIT_TEST_FIXTURE(Chart2ExportTest2, testCustomLabelText) diff --git a/chart2/qa/extras/charttest.hxx b/chart2/qa/extras/charttest.hxx index 696e3c7ab164..a8edb86bbde9 100644 --- a/chart2/qa/extras/charttest.hxx +++ b/chart2/qa/extras/charttest.hxx @@ -109,6 +109,8 @@ public: getShapeByName(const uno::Reference<drawing::XShapes>& rShapes, const OUString& rName, const std::function<bool(const uno::Reference<drawing::XShape>&)>& pCondition = nullptr); + Reference<chart2::XChartStyle> + getStyleFromDoc(Reference<chart2::XChartDocument> const& xChartDoc); }; Reference< lang::XComponent > ChartTest::getChartCompFromSheet( sal_Int32 nSheet, sal_Int32 nChart ) @@ -507,4 +509,15 @@ ChartTest::getShapeByName(const uno::Reference<drawing::XShapes>& rShapes, const return uno::Reference<drawing::XShape>(); } +Reference<chart2::XChartStyle> + getStyleFromDoc(Reference<chart2::XChartDocument> const& xChartDoc) +{ + CPPUNIT_ASSERT( xChartDoc.is() ); + + Reference <chart2::XChartStyle > xStyle = xChartDoc->getStyles(); + CPPUNIT_ASSERT( xStyle.is() ); + + return xStyle; +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/qa/extras/data/xlsx/column-style.xlsx b/chart2/qa/extras/data/xlsx/column-style.xlsx index 95da9c97caa8..be9e29050d11 100644 Binary files a/chart2/qa/extras/data/xlsx/column-style.xlsx and b/chart2/qa/extras/data/xlsx/column-style.xlsx differ diff --git a/docmodel/source/uno/UnoChartStyle.cxx b/docmodel/source/uno/UnoChartStyle.cxx index 8aeb5cb52b99..d7e9b5019b63 100644 --- a/docmodel/source/uno/UnoChartStyle.cxx +++ b/docmodel/source/uno/UnoChartStyle.cxx @@ -20,15 +20,18 @@ uno::Reference<chart2::XChartStyle> createXChartStyle(model::StyleSet const& rSt return new UnoChartStyle(rStyle); } -model::StyleSet getFromXChartStyle(uno::Reference<chart2::XChartStyle> const& rxStyle) +model::StyleSet* getFromXChartStyle(uno::Reference<chart2::XChartStyle> const& rxStyle) { - model::StyleSet aChartStyle; UnoChartStyle* pUnoChartStyle = static_cast<UnoChartStyle*>(rxStyle.get()); + if (pUnoChartStyle) { - aChartStyle = pUnoChartStyle->getChartStyle(); + return &pUnoChartStyle->getChartStyle(); + } + else + { + return nullptr; } - return aChartStyle; } } // end model::style diff --git a/include/docmodel/styles/ChartStyle.hxx b/include/docmodel/styles/ChartStyle.hxx index e455e6e37b1f..18fffdc6b6e9 100644 --- a/include/docmodel/styles/ChartStyle.hxx +++ b/include/docmodel/styles/ChartStyle.hxx @@ -19,8 +19,10 @@ namespace model { -struct StyleColor +struct FontOrStyleRef { + sal_Int32 mnIdx; // required + enum class StyleColorEnum { AUTO @@ -28,32 +30,56 @@ struct StyleColor // The schema uses 'union' here. Implement with a std::variant for safety. using StyleColorVal = std::variant<sal_uInt32, enum StyleColorEnum, OUString>; - typedef std::vector<StyleColorVal> StyleColorVec; + std::unique_ptr<StyleColorVal> maStyleClr; // optional + + // Get a string version of the ColorVal, for output/streaming + OUString getColorValStr() const + { + OUString sV; + + if (maStyleClr) + { + switch (maStyleClr->index()) + { + case 0: // sal_uInt32, a raw color value + sV = OUString::number(std::get<0>(*maStyleClr)); + break; + case 1: // enum; only value "auto" + sV = "auto"; + break; + case 2: // arbitrary string + sV = std::get<2>(*maStyleClr); + break; + } + } + return sV; + } - StyleColorVec maStyleClr; - sal_Int32 mnIdx; - oox::drawingml::Color maColor; - model::ComplexColor maComplexColor; + // A child element of cs:CT_FontReference or cs:CT_StyleReference is + // a:EG_ColorChoice. The latter is handled by ColorValueContext. + oox::drawingml::Color maColor; // needed for ColorValueContext + model::ComplexColor maComplexColor; // needed for ColorValueContext // There's also an a:EG_ColorTransform member and a 'mods' member for - // StyleColor. Ignore those for now. TODO + // FontOrStyleRef. Ignore those for now. TODO }; struct StyleEntry { - std::shared_ptr<StyleColor> mxLnClr; + std::shared_ptr<FontOrStyleRef> mxLnClr; double mfLineWidthScale = 1.0; - std::shared_ptr<StyleColor> mxFillClr; - std::shared_ptr<StyleColor> mxEffectClr; - std::shared_ptr<StyleColor> mxFontClr; + std::shared_ptr<FontOrStyleRef> mxFillClr; + std::shared_ptr<FontOrStyleRef> mxEffectClr; + std::shared_ptr<FontOrStyleRef> mxFontClr; std::shared_ptr<oox::drawingml::Shape> mxShapePr; // The following is derived from a TextCharacterProperties std::shared_ptr<oox::PropertyMap> mrTextCharacterPr; // The following is derived from a TextBodyProperties std::shared_ptr<oox::PropertyMap> mxTextBodyPr; - StyleEntry(std::shared_ptr<StyleColor> aLnClr, double fLineScale, - std::shared_ptr<StyleColor> aFillClr, std::shared_ptr<StyleColor> aEffectClr, - std::shared_ptr<StyleColor> aFontClr, std::shared_ptr<oox::drawingml::Shape> aShape, + StyleEntry(std::shared_ptr<FontOrStyleRef> aLnClr, double fLineScale, + std::shared_ptr<FontOrStyleRef> aFillClr, std::shared_ptr<FontOrStyleRef> aEffectClr, + std::shared_ptr<FontOrStyleRef> aFontClr, + std::shared_ptr<oox::drawingml::Shape> aShape, std::shared_ptr<oox::PropertyMap> aCharProps, std::shared_ptr<oox::PropertyMap> aBodyProps) : mxLnClr(aLnClr) @@ -72,7 +98,8 @@ struct DOCMODEL_DLLPUBLIC StyleSet { enum class StyleEntryType { - AXISTITLE, + BEGIN = 0, // for iteration + AXISTITLE = BEGIN, CATEGORYAXIS, CHARTAREA, DATALABEL, @@ -102,7 +129,8 @@ struct DOCMODEL_DLLPUBLIC StyleSet TRENDLINELABEL, UPBAR, VALUEAXIS, - WALL + WALL, + END // for iteration }; std::map<enum StyleEntryType, StyleEntry> maEntryMap; diff --git a/include/docmodel/uno/UnoChartStyle.hxx b/include/docmodel/uno/UnoChartStyle.hxx index 91588a985c9d..45bb26811ba8 100644 --- a/include/docmodel/uno/UnoChartStyle.hxx +++ b/include/docmodel/uno/UnoChartStyle.hxx @@ -47,7 +47,7 @@ namespace model::style { DOCMODEL_DLLPUBLIC css::uno::Reference<css::chart2::XChartStyle> createXChartStyle(model::StyleSet const& rStyle); -DOCMODEL_DLLPUBLIC model::StyleSet +DOCMODEL_DLLPUBLIC model::StyleSet* getFromXChartStyle(css::uno::Reference<css::chart2::XChartStyle> const& rxStyle); } diff --git a/include/oox/export/ThemeExport.hxx b/include/oox/export/ThemeExport.hxx index c220d2d3037d..91c4b57ad8e8 100644 --- a/include/oox/export/ThemeExport.hxx +++ b/include/oox/export/ThemeExport.hxx @@ -40,9 +40,11 @@ private: sax_fastparser::FSHelperPtr mpFS; public: - ThemeExport(oox::core::XmlFilterBase* pFilterBase, oox::drawingml::DocumentType eDocumentType); + ThemeExport(oox::core::XmlFilterBase* pFilterBase, oox::drawingml::DocumentType eDocumentType, + sax_fastparser::FSHelperPtr pFS = nullptr); void write(OUString const& rPath, model::Theme const& rTheme); + void writeComplexColor(model::ComplexColor const& rComplexColor); private: bool writeColorSet(model::Theme const& rTheme); @@ -58,7 +60,6 @@ private: void writePatternFill(model::PatternFill const& rPatternFill); void writeGradientFill(model::GradientFill const& rGradientFill); void writeSolidFill(model::SolidFill const& rSolidFill); - void writeComplexColor(model::ComplexColor const& rComplexColor); void writeColorPlaceholder(model::ComplexColor const& rComplexColor); void writeColorSystem(model::ComplexColor const& rComplexColor); void writeColorTheme(model::ComplexColor const& rComplexColor); diff --git a/include/oox/export/chartexport.hxx b/include/oox/export/chartexport.hxx index a4821b467434..4201bfab8997 100644 --- a/include/oox/export/chartexport.hxx +++ b/include/oox/export/chartexport.hxx @@ -69,6 +69,11 @@ namespace core { class XmlFilterBase; }} +namespace model { + struct FontOrStyleRef; + struct StyleEntry; +} + namespace oox::drawingml { struct LabelPlacementParam; @@ -237,7 +242,7 @@ private: void exportSeriesValues( const css::uno::Reference< css::chart2::data::XDataSequence >& xValueSeq, sal_Int32 nValueType = XML_val ); void exportShapeProps( const css::uno::Reference< css::beans::XPropertySet >& xPropSet, - bool bIsChartex); + sal_Int32 nNS); void exportDataPoints( const css::uno::Reference< css::beans::XPropertySet >& xSeriesProperties, sal_Int32 nSeriesLength, sal_Int32 eChartType ); @@ -288,6 +293,11 @@ private: const css::uno::Reference<css::beans::XPropertySet>& xPropSet, const LabelPlacementParam& rLabelParam, sal_Int32 nLabelIndex, DataLabelsRange& rDLblsRange, bool bIsChartex); + // Functions for style file output + void outputStyleEntry(::sax_fastparser::FSHelperPtr pFS, + sal_Int32 nElTokenId, model::StyleEntry& aEntry); + void outputFontOrStyleRef(::sax_fastparser::FSHelperPtr pFS, + sal_Int32 nElTokenId, const model::FontOrStyleRef& aColor); public: diff --git a/include/oox/export/drawingml.hxx b/include/oox/export/drawingml.hxx index d398f836b95f..0cfe1cc34763 100644 --- a/include/oox/export/drawingml.hxx +++ b/include/oox/export/drawingml.hxx @@ -50,6 +50,8 @@ #include <vcl/mapmod.hxx> #include <svx/EnhancedCustomShape2d.hxx> #include <basegfx/utils/bgradient.hxx> +#include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp> + class Graphic; class SdrObjCustomShape; @@ -291,6 +293,28 @@ struct WriteRunInput css::uno::Reference<css::beans::XPropertySet> xShapePropSet; }; +struct WriteBodyPropsInput +{ + bool bIsFontworkShape = false; + bool bHasWrap = false; + bool bWrap = false; + bool bFromWordArt = false; + sal_Int32 nTop = 0; + sal_Int32 nBottom = 0; + sal_Int32 nLeft = 0; + sal_Int32 nRight = 0; + std::optional<OUString> sHorzOverflow; + std::optional<OUString> sVertOverflow; + const char *sAnchor = nullptr; + bool bAnchorCtr = false; + std::optional<OString> sWritingMode; + std::optional<OString> sIsUpright; + std::optional<OString> sTextRotateAngleMSUnit; + css::uno::Sequence<css::drawing::EnhancedCustomShapeAdjustmentValue> aAdjustmentSeq; + OUString sPresetWarp; + OUString sMSWordPresetTextWarp; +}; + class DrawingML { @@ -303,6 +327,8 @@ private: /// Parent exporter, used for text callback. DMLTextExport* mpTextExport; + static constexpr const sal_Int32 mconstDefaultLeftRightInset = 254; + static constexpr const sal_Int32 mconstDefaultTopBottomInset = 127; protected: css::uno::Any mAny; @@ -470,6 +496,12 @@ public: bool& rbOverridingCharHeight, sal_Int32& rnCharHeight, const css::uno::Reference<css::beans::XPropertySet>& rXShapePropSet, sal_Int32 nElement); + void WriteBodyProps( + const css::uno::Reference< css::uno::XInterface >& rXIface, + const css::uno::Reference<css::beans::XPropertySet>& rXPropSet, + const css::uno::Reference<css::drawing::XShape>& xShape, + sal_Int32 nXmlNamespace, const WriteBodyPropsInput& aWBPInput); + /** Populates the lstStyle with the shape's text run and paragraph properties */ void WriteLstStyles(const css::uno::Reference<css::text::XTextContent>& rParagraph, bool& rbOverridingCharHeight, sal_Int32& rnCharHeight, diff --git a/oox/inc/drawingml/chart/stylefragment.hxx b/oox/inc/drawingml/chart/stylefragment.hxx index aff1f59f870c..a7bb698a43e2 100644 --- a/oox/inc/drawingml/chart/stylefragment.hxx +++ b/oox/inc/drawingml/chart/stylefragment.hxx @@ -23,18 +23,18 @@ namespace model { -struct StyleColor; +struct FontOrStyleRef; } namespace oox::drawingml::chart { /** Handler for a cs:CT_StyleReference or cs:CT_FontReference element. */ -class StyleReferenceContext final : public ContextBase<model::StyleColor> +class StyleReferenceContext final : public ContextBase<model::FontOrStyleRef> { public: StyleReferenceContext(ContextHandler2Helper& rParent, sal_Int32 nIdx, - model::StyleColor& rModel); + model::FontOrStyleRef& rModel); virtual ~StyleReferenceContext() override; virtual ::oox::core::ContextHandlerRef onCreateContext(sal_Int32 nElement, diff --git a/oox/inc/drawingml/chart/stylemodel.hxx b/oox/inc/drawingml/chart/stylemodel.hxx index d0c1bc92e82f..ce8c2c8e64d6 100644 --- a/oox/inc/drawingml/chart/stylemodel.hxx +++ b/oox/inc/drawingml/chart/stylemodel.hxx @@ -28,8 +28,8 @@ namespace oox::drawingml::chart // model for both. struct StyleEntryModel { - typedef ModelRef<model::StyleColor> StyleRef; // "idx" is ST_StyleMatrixColumnIndex - typedef ModelRef<model::StyleColor> FontRef; // "idx" is ST_FontCollectionIndex + typedef ModelRef<model::FontOrStyleRef> StyleRef; // "idx" is ST_StyleMatrixColumnIndex + typedef ModelRef<model::FontOrStyleRef> FontRef; // "idx" is ST_FontCollectionIndex typedef ModelRef<TextCharacterProperties> TextCharacterPropsRef; typedef ModelRef<TextBodyProperties> TextBodyPropsRef; typedef ModelRef<Shape> ShapeRef; diff --git a/oox/source/drawingml/chart/stylefragment.cxx b/oox/source/drawingml/chart/stylefragment.cxx index 562fe27bda31..a353ce445d91 100644 --- a/oox/source/drawingml/chart/stylefragment.cxx +++ b/oox/source/drawingml/chart/stylefragment.cxx @@ -43,8 +43,8 @@ using namespace model; // StyleReferenceContext //======= StyleReferenceContext::StyleReferenceContext(ContextHandler2Helper& rParent, sal_Int32 nIdx, - model::StyleColor& rModel) - : ContextBase<StyleColor>(rParent, rModel) + model::FontOrStyleRef& rModel) + : ContextBase<FontOrStyleRef>(rParent, rModel) { mrModel.mnIdx = nIdx; } @@ -75,7 +75,7 @@ ContextHandlerRef StyleReferenceContext::onCreateContext(sal_Int32 nElement, std::optional<OUString> str = rAttribs.getString(XML_val); if (str) { - StyleColor::StyleColorVal v; + FontOrStyleRef::StyleColorVal v; const sal_Unicode* pRawStr = str->getStr(); std::wstring sBStr; @@ -96,14 +96,14 @@ ContextHandlerRef StyleReferenceContext::onCreateContext(sal_Int32 nElement, // Not an integer, so see if it's the fixed enum if (*str == "auto") { - v = StyleColor::StyleColorEnum::AUTO; + v = FontOrStyleRef::StyleColorEnum::AUTO; } else { v = *str; } } - mrModel.maStyleClr.push_back(v); + mrModel.maStyleClr = std::make_unique<FontOrStyleRef::StyleColorVal>(v); } return nullptr; } diff --git a/oox/source/drawingml/chart/stylemodel.cxx b/oox/source/drawingml/chart/stylemodel.cxx index c72c8391179a..ddfb24d9c35d 100644 --- a/oox/source/drawingml/chart/stylemodel.cxx +++ b/oox/source/drawingml/chart/stylemodel.cxx @@ -35,11 +35,12 @@ model::StyleEntry StyleEntryModel::toStyleEntry(oox::core::XmlFilterBase& rFilte // which can be set using pushTextDistances(). I'm not sure why the // interfaces are different, but that's why what's below is not parallel to // the above. - std::shared_ptr<PropertyMap> aBodyMap = std::make_shared<PropertyMap>(); + std::shared_ptr<PropertyMap> aBodyMap; if (mxBodyPr) { mxBodyPr->pushTextDistances(Size(0, 0)); - aBodyMap.reset(&mxBodyPr->maPropertyMap); + // Deep copy mxBodyPr->maPropertyMap so the shared_ptr can have ownership + aBodyMap = std::make_shared<PropertyMap>(mxBodyPr->maPropertyMap); } return model::StyleEntry(mxLnRef, mfLineWidthScale, mxFillRef, mxEffectRef, mxFontRef, diff --git a/oox/source/drawingml/shape.cxx b/oox/source/drawingml/shape.cxx index 59f86ec73732..7afa155c976c 100644 --- a/oox/source/drawingml/shape.cxx +++ b/oox/source/drawingml/shape.cxx @@ -2634,11 +2634,31 @@ void Shape::finalizeXShape( XmlFilterBase& rFilter, const Reference< XShapes >& rFilter, mxChartShapeInfo->maFragmentPath, aModel ); rFilter.importFragment( pChartSpaceFragment ); - // Import styles file + // Import styles file. + // Create the styles path from the path to the chart*.xml file sal_Int32 nLastSlash = mxChartShapeInfo->maFragmentPath.lastIndexOf('/'); const sal_Unicode *pFPath = mxChartShapeInfo->maFragmentPath.getStr(); OUString sStylePath(pFPath, nLastSlash + 1); - sStylePath += u"style1.xml"_ustr; + OUString sFullPath(pFPath); + sStylePath += u"style"_ustr; + OUString sChartFName(sFullPath.copy(nLastSlash + 1, sFullPath.getLength() - nLastSlash - 1)); + OUString sTail; + // Verify that the path is the way we expect it, and extract the + // end bit. Don't put the startsWith() call inside an assert, + // because we don't want it optimized away. + const bool bWellFormed = sChartFName.startsWith(std::u16string_view(u"chart"), &sTail); + if (!bWellFormed) { + assert(false); + } + // We should have something of the form 'chart1234.xml'. We + // want to get the numeric ('1234') part, since it should be + // the same for the style file. + sal_Int32 nPeriod = sTail.indexOf('.'); + OUString sNumber(sTail.copy(0, nPeriod)); + + sStylePath += sNumber; + sStylePath += u".xml"_ustr; + chart::StyleModel aStyleModel; rtl::Reference<chart::StyleFragment> pStyleFragment = new chart::StyleFragment( rFilter, sStylePath, aStyleModel ); @@ -2690,14 +2710,8 @@ void Shape::finalizeXShape( XmlFilterBase& rFilter, const Reference< XShapes >& } // convert chart style model to docmodel style data - std::unique_ptr<chart::ChartStyleConverter> pChartStyleConv = std::make_unique<chart::ChartStyleConverter>(); - - if (pChartStyleConv) { - Reference<com::sun::star::chart2::XChartStyle> xStyle = xChartDoc->getStyles(); - oox::drawingml::chart::ChartStyleConverter::convertFromModel(rFilter, aStyleModel, xStyle); - - - } + Reference<com::sun::star::chart2::XChartStyle> xStyle = xChartDoc->getStyles(); + oox::drawingml::chart::ChartStyleConverter::convertFromModel(rFilter, aStyleModel, xStyle); if (pPowerPointImport) { diff --git a/oox/source/export/ThemeExport.cxx b/oox/source/export/ThemeExport.cxx index 069b38b090dd..64a0d9b5155a 100644 --- a/oox/source/export/ThemeExport.cxx +++ b/oox/source/export/ThemeExport.cxx @@ -38,9 +38,11 @@ void writeRelativeRectangle(sax_fastparser::FSHelperPtr pFS, sal_Int32 nToken, } // end anonymous namespace ThemeExport::ThemeExport(oox::core::XmlFilterBase* pFilterBase, - oox::drawingml::DocumentType eDocumentType) + oox::drawingml::DocumentType eDocumentType, + sax_fastparser::FSHelperPtr pFS) : mpFilterBase(pFilterBase) , meDocumentType(eDocumentType) + , mpFS(pFS) { } diff --git a/oox/source/export/chartexport.cxx b/oox/source/export/chartexport.cxx index 4f72421f14e1..4aae477e7052 100644 --- a/oox/source/export/chartexport.cxx +++ b/oox/source/export/chartexport.cxx @@ -115,6 +115,11 @@ #include <o3tl/temporary.hxx> #include <o3tl/sorted_vector.hxx> +#include <docmodel/styles/ChartStyle.hxx> +#include <docmodel/uno/UnoChartStyle.hxx> + +#include <oox/export/ThemeExport.hxx> + using namespace css; using namespace css::uno; using namespace css::drawing; @@ -162,75 +167,6 @@ private: OUString m_aRole; }; -void outputStyleEntry(FSHelperPtr pFS, sal_Int32 nElTokenId) -{ - // Just default values for now - pFS->startElement(FSNS(XML_cs, nElTokenId)); - pFS->singleElement(FSNS(XML_cs, XML_lnRef), XML_idx, "0"); - pFS->singleElement(FSNS(XML_cs, XML_fillRef), XML_idx, "0"); - pFS->singleElement(FSNS(XML_cs, XML_effectRef), XML_idx, "0"); - pFS->singleElement(FSNS(XML_cs, XML_fontRef), XML_idx, "minor"); - pFS->endElement(FSNS(XML_cs, nElTokenId)); -} - -void outputChartAreaStyleEntry(FSHelperPtr pFS) -{ - // Just default values for now - pFS->startElement(FSNS(XML_cs, XML_chartArea), XML_mods, "allowNoFillOverride allowNoLineOverride"); - pFS->singleElement(FSNS(XML_cs, XML_lnRef), XML_idx, "0"); - pFS->singleElement(FSNS(XML_cs, XML_fillRef), XML_idx, "0"); - pFS->singleElement(FSNS(XML_cs, XML_effectRef), XML_idx, "0"); - - pFS->startElement(FSNS(XML_cs, XML_fontRef), XML_idx, "minor"); - pFS->singleElement(FSNS(XML_a, XML_schemeClr), XML_val, "tx1"); - pFS->endElement(FSNS(XML_cs, XML_fontRef)); - - pFS->startElement(FSNS(XML_cs, XML_spPr)); - - pFS->startElement(FSNS(XML_a, XML_solidFill)); - pFS->singleElement(FSNS(XML_a, XML_schemeClr), XML_val, "bg1"); - pFS->endElement(FSNS(XML_a, XML_solidFill)); - - pFS->startElement(FSNS(XML_a, XML_ln), XML_w, "9525", XML_cap, "flat", - XML_cmpd, "sng", XML_algn, "ctr"); - pFS->startElement(FSNS(XML_a, XML_solidFill)); - pFS->startElement(FSNS(XML_a, XML_schemeClr), XML_val, "tx1"); - pFS->singleElement(FSNS(XML_a, XML_lumMod), XML_val, "15000"); - pFS->singleElement(FSNS(XML_a, XML_lumOff), XML_val, "85000"); - pFS->endElement(FSNS(XML_a, XML_schemeClr)); - pFS->endElement(FSNS(XML_a, XML_solidFill)); - pFS->singleElement(FSNS(XML_a, XML_round)); - pFS->endElement(FSNS(XML_a, XML_ln)); - - pFS->endElement(FSNS(XML_cs, XML_spPr)); - - pFS->endElement(FSNS(XML_cs, XML_chartArea)); -} - -void outputDataPointStyleEntry(FSHelperPtr pFS) -{ - pFS->startElement(FSNS(XML_cs, XML_dataPoint)); - pFS->singleElement(FSNS(XML_cs, XML_lnRef), XML_idx, "0"); - - pFS->startElement(FSNS(XML_cs, XML_fillRef), XML_idx, "0"); - pFS->singleElement(FSNS(XML_cs, XML_styleClr), XML_val, "auto"); - pFS->endElement(FSNS(XML_cs, XML_fillRef)); - - pFS->singleElement(FSNS(XML_cs, XML_effectRef), XML_idx, "0"); - - pFS->startElement(FSNS(XML_cs, XML_fontRef), XML_idx, "minor"); - pFS->singleElement(FSNS(XML_cs, XML_schemeClr), XML_val, "tx1"); - pFS->endElement(FSNS(XML_cs, XML_fontRef)); - - pFS->startElement(FSNS(XML_cs, XML_spPr)); - pFS->startElement(FSNS(XML_a, XML_solidFill)); - pFS->singleElement(FSNS(XML_a, XML_schemeClr), XML_val, "phClr"); - pFS->endElement(FSNS(XML_a, XML_solidFill)); - pFS->endElement(FSNS(XML_cs, XML_spPr)); - - 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; @@ -908,6 +844,175 @@ OUString ChartExport::parseFormula( const OUString& rRange ) return aResult; } +// Output the chartex AlternateContent fallback path +static void writeChartexAlternateContent(FSHelperPtr pFS) +{ + pFS->startElementNS(XML_mc, XML_Fallback); + pFS->startElementNS(XML_xdr, XML_sp, XML_macro, "", XML_textlink, ""); + pFS->startElementNS(XML_xdr, XML_nvSpPr); + pFS->singleElementNS(XML_xdr, XML_cNvPr, XML_id, "0", XML_name, ""); + pFS->startElementNS(XML_xdr, XML_cNvSpPr); + pFS->singleElementNS(XML_a, XML_spLocks, XML_noTextEdit, "1"); + pFS->endElementNS(XML_xdr, XML_cNvSpPr); + pFS->endElementNS(XML_xdr, XML_nvSpPr); + pFS->startElementNS(XML_xdr, XML_spPr); + pFS->startElementNS(XML_a, XML_xfrm); + pFS->singleElementNS(XML_a, XML_off, XML_x, "6600825", XML_y, "2533650"); + pFS->singleElementNS(XML_a, XML_ext, XML_cx, "4572000", XML_cy, "2743200"); + pFS->endElementNS(XML_a, XML_xfrm); + pFS->startElementNS(XML_a, XML_prstGeom, XML_prst, "rect"); + pFS->singleElementNS(XML_a, XML_avLst); + pFS->endElementNS(XML_a, XML_prstGeom); + pFS->startElementNS(XML_a, XML_solidFill); + pFS->singleElementNS(XML_a, XML_prstClr, XML_val, "white"); + pFS->endElementNS(XML_a, XML_solidFill); + pFS->startElementNS(XML_a, XML_ln, XML_w, "1"); + pFS->startElementNS(XML_a, XML_solidFill); + pFS->singleElementNS(XML_a, XML_prstClr, XML_val, "green"); + pFS->endElementNS(XML_a, XML_solidFill); + pFS->endElementNS(XML_a, XML_ln); + pFS->endElementNS(XML_xdr, XML_spPr); + pFS->startElementNS(XML_xdr, XML_txBody); + pFS->singleElementNS(XML_a, XML_bodyPr, XML_vertOverflow, "clip", XML_horzOverflow, "clip"); + pFS->singleElementNS(XML_a, XML_lstStyle); + pFS->startElementNS(XML_a, XML_p); + pFS->startElementNS(XML_a, XML_r); + pFS->singleElementNS(XML_a, XML_rPr, XML_sz, "1100"); + pFS->startElementNS(XML_a, XML_t); + + const std::string_view sErrTxt("This chart isn't available in your version of Excel. " + "Editing this shape or saving this workbook into a different file format will permanently break the chart."); + pFS->writeEscaped( sErrTxt ); + + pFS->endElementNS(XML_a, XML_t); + pFS->endElementNS(XML_a, XML_r); + pFS->endElementNS(XML_a, XML_p); + pFS->endElementNS(XML_xdr, XML_txBody); + pFS->endElementNS(XML_xdr, XML_sp); +} + +namespace { + +// Map enumerated style entry types to corresponding XML tags +struct styleTag { + model::StyleSet::StyleEntryType meType; + sal_Int32 mnTag; +}; + +std::array<styleTag, static_cast<int>(model::StyleSet::StyleEntryType::END)> styleTagMap {{ + { model::StyleSet::StyleEntryType::AXISTITLE, XML_axisTitle }, + { model::StyleSet::StyleEntryType::CATEGORYAXIS, XML_categoryAxis }, + { model::StyleSet::StyleEntryType::CHARTAREA, XML_chartArea }, + { model::StyleSet::StyleEntryType::DATALABEL, XML_dataLabel }, + { model::StyleSet::StyleEntryType::DATALABELCALLOUT, XML_dataLabelCallout }, + { model::StyleSet::StyleEntryType::DATAPOINT, XML_dataPoint }, + { model::StyleSet::StyleEntryType::DATAPOINT3D, XML_dataPoint3D }, + { model::StyleSet::StyleEntryType::DATAPOINTLINE, XML_dataPointLine }, + { model::StyleSet::StyleEntryType::DATAPOINTMARKER, XML_dataPointMarker }, + { model::StyleSet::StyleEntryType::DATAPOINTMARKERLAYOUT, XML_dataPointMarkerLayout }, + { model::StyleSet::StyleEntryType::DATAPOINTWIREFRAME, XML_dataPointWireframe }, + { model::StyleSet::StyleEntryType::DATATABLE, XML_dataTable }, + { model::StyleSet::StyleEntryType::DOWNBAR, XML_downBar }, + { model::StyleSet::StyleEntryType::DROPLINE, XML_dropLine }, + { model::StyleSet::StyleEntryType::ERRORBAR, XML_errorBar }, + { model::StyleSet::StyleEntryType::FLOOR, XML_floor }, + { model::StyleSet::StyleEntryType::GRIDLINEMAJOR, XML_gridlineMajor }, + { model::StyleSet::StyleEntryType::GRIDLINEMINOR, XML_gridlineMinor }, + { model::StyleSet::StyleEntryType::HILOLINE, XML_hiLoLine }, + { model::StyleSet::StyleEntryType::LEADERLINE, XML_leaderLine }, + { model::StyleSet::StyleEntryType::LEGEND, XML_legend }, + { model::StyleSet::StyleEntryType::PLOTAREA, XML_plotArea }, + { model::StyleSet::StyleEntryType::PLOTAREA3D, XML_plotArea3D }, + { model::StyleSet::StyleEntryType::SERIESAXIS, XML_seriesAxis }, + { model::StyleSet::StyleEntryType::SERIESLINE, XML_seriesLine }, + { model::StyleSet::StyleEntryType::TITLE, XML_title }, + { model::StyleSet::StyleEntryType::TRENDLINE, XML_trendline }, + { model::StyleSet::StyleEntryType::TRENDLINELABEL, XML_trendlineLabel }, + { model::StyleSet::StyleEntryType::UPBAR, XML_upBar }, + { model::StyleSet::StyleEntryType::VALUEAXIS, XML_valueAxis }, + { model::StyleSet::StyleEntryType::WALL, XML_wall } +}}; + +// The following functions are intended to duplicate what appear to be the MS +// Office defaults for various style entries. These are not documented anywhere, +// so far as I can tell. The values here are just derived by looking at some +// files Office creates. It's very possible that things are not as simple as the +// hard-coded values here. + +// All style entries other than the special cases below +void outputDefaultStyleEntry(FSHelperPtr pFS, sal_Int32 nElTokenId) +{ + pFS->startElement(FSNS(XML_cs, nElTokenId)); + pFS->singleElement(FSNS(XML_cs, XML_lnRef), XML_idx, "0"); + pFS->singleElement(FSNS(XML_cs, XML_fillRef), XML_idx, "0"); + pFS->singleElement(FSNS(XML_cs, XML_effectRef), XML_idx, "0"); + pFS->singleElement(FSNS(XML_cs, XML_fontRef), XML_idx, "minor"); + pFS->endElement(FSNS(XML_cs, nElTokenId)); +} + +// chartArea entry +void outputDefaultChartAreaStyleEntry(FSHelperPtr pFS) +{ + pFS->startElement(FSNS(XML_cs, XML_chartArea), XML_mods, "allowNoFillOverride allowNoLineOverride"); + pFS->singleElement(FSNS(XML_cs, XML_lnRef), XML_idx, "0"); + pFS->singleElement(FSNS(XML_cs, XML_fillRef), XML_idx, "0"); + pFS->singleElement(FSNS(XML_cs, XML_effectRef), XML_idx, "0"); + + pFS->startElement(FSNS(XML_cs, XML_fontRef), XML_idx, "minor"); + pFS->singleElement(FSNS(XML_a, XML_schemeClr), XML_val, "tx1"); + pFS->endElement(FSNS(XML_cs, XML_fontRef)); + + pFS->startElement(FSNS(XML_cs, XML_spPr)); + + pFS->startElement(FSNS(XML_a, XML_solidFill)); + pFS->singleElement(FSNS(XML_a, XML_schemeClr), XML_val, "bg1"); + pFS->endElement(FSNS(XML_a, XML_solidFill)); + + pFS->startElement(FSNS(XML_a, XML_ln), XML_w, "9525", XML_cap, "flat", + XML_cmpd, "sng", XML_algn, "ctr"); + pFS->startElement(FSNS(XML_a, XML_solidFill)); + pFS->startElement(FSNS(XML_a, XML_schemeClr), XML_val, "tx1"); + pFS->singleElement(FSNS(XML_a, XML_lumMod), XML_val, "15000"); + pFS->singleElement(FSNS(XML_a, XML_lumOff), XML_val, "85000"); + pFS->endElement(FSNS(XML_a, XML_schemeClr)); + pFS->endElement(FSNS(XML_a, XML_solidFill)); + pFS->singleElement(FSNS(XML_a, XML_round)); + pFS->endElement(FSNS(XML_a, XML_ln)); + + pFS->endElement(FSNS(XML_cs, XML_spPr)); + + pFS->endElement(FSNS(XML_cs, XML_chartArea)); +} + +// dataPoint entry +void outputDefaultDataPointStyleEntry(FSHelperPtr pFS) +{ + pFS->startElement(FSNS(XML_cs, XML_dataPoint)); + pFS->singleElement(FSNS(XML_cs, XML_lnRef), XML_idx, "0"); + + pFS->startElement(FSNS(XML_cs, XML_fillRef), XML_idx, "0"); + pFS->singleElement(FSNS(XML_cs, XML_styleClr), XML_val, "auto"); + pFS->endElement(FSNS(XML_cs, XML_fillRef)); + + pFS->singleElement(FSNS(XML_cs, XML_effectRef), XML_idx, "0"); + + pFS->startElement(FSNS(XML_cs, XML_fontRef), XML_idx, "minor"); + pFS->singleElement(FSNS(XML_cs, XML_schemeClr), XML_val, "tx1"); + pFS->endElement(FSNS(XML_cs, XML_fontRef)); + + pFS->startElement(FSNS(XML_cs, XML_spPr)); + pFS->startElement(FSNS(XML_a, XML_solidFill)); + pFS->singleElement(FSNS(XML_a, XML_schemeClr), XML_val, "phClr"); + pFS->endElement(FSNS(XML_a, XML_solidFill)); + pFS->endElement(FSNS(XML_cs, XML_spPr)); + + pFS->endElement(FSNS(XML_cs, XML_dataPoint)); +} + +} // unnamed namespace + + + void ChartExport::WriteChartObj( const Reference< XShape >& xShape, sal_Int32 nID, sal_Int32 nChartCount ) { FSHelperPtr pFS = GetFS(); @@ -1069,50 +1174,9 @@ void ChartExport::WriteChartObj( const Reference< XShape >& xShape, sal_Int32 nI pFS->endElementNS( mnXmlNamespace, XML_graphicFrame ); if (bIsChartex) { - // Do the AlternateContent fallback path pFS->endElementNS(XML_mc, XML_Choice); - pFS->startElementNS(XML_mc, XML_Fallback); - pFS->startElementNS(XML_xdr, XML_sp, XML_macro, "", XML_textlink, ""); - pFS->startElementNS(XML_xdr, XML_nvSpPr); - pFS->singleElementNS(XML_xdr, XML_cNvPr, XML_id, "0", XML_name, ""); - pFS->startElementNS(XML_xdr, XML_cNvSpPr); - pFS->singleElementNS(XML_a, XML_spLocks, XML_noTextEdit, "1"); - pFS->endElementNS(XML_xdr, XML_cNvSpPr); - pFS->endElementNS(XML_xdr, XML_nvSpPr); - pFS->startElementNS(XML_xdr, XML_spPr); - pFS->startElementNS(XML_a, XML_xfrm); - pFS->singleElementNS(XML_a, XML_off, XML_x, "6600825", XML_y, "2533650"); - pFS->singleElementNS(XML_a, XML_ext, XML_cx, "4572000", XML_cy, "2743200"); - pFS->endElementNS(XML_a, XML_xfrm); - pFS->startElementNS(XML_a, XML_prstGeom, XML_prst, "rect"); - pFS->singleElementNS(XML_a, XML_avLst); - pFS->endElementNS(XML_a, XML_prstGeom); - pFS->startElementNS(XML_a, XML_solidFill); - pFS->singleElementNS(XML_a, XML_prstClr, XML_val, "white"); - pFS->endElementNS(XML_a, XML_solidFill); - pFS->startElementNS(XML_a, XML_ln, XML_w, "1"); - pFS->startElementNS(XML_a, XML_solidFill); - pFS->singleElementNS(XML_a, XML_prstClr, XML_val, "green"); - pFS->endElementNS(XML_a, XML_solidFill); - pFS->endElementNS(XML_a, XML_ln); - pFS->endElementNS(XML_xdr, XML_spPr); - pFS->startElementNS(XML_xdr, XML_txBody); - pFS->singleElementNS(XML_a, XML_bodyPr, XML_vertOverflow, "clip", XML_horzOverflow, "clip"); - pFS->singleElementNS(XML_a, XML_lstStyle); - pFS->startElementNS(XML_a, XML_p); - pFS->startElementNS(XML_a, XML_r); - pFS->singleElementNS(XML_a, XML_rPr, XML_sz, "1100"); - pFS->startElementNS(XML_a, XML_t); - - const std::string_view sErrTxt("This chart isn't available in your version of Excel. " - "Editing this shape or saving this workbook into a different file format will permanently break the chart."); - pFS->writeEscaped( sErrTxt ); - - pFS->endElementNS(XML_a, XML_t); - pFS->endElementNS(XML_a, XML_r); - pFS->endElementNS(XML_a, XML_p); - pFS->endElementNS(XML_xdr, XML_txBody); - pFS->endElementNS(XML_xdr, XML_sp); + + writeChartexAlternateContent(pFS); pFS->endElementNS(XML_mc, XML_Fallback); pFS->endElementNS(XML_mc, XML_AlternateContent); @@ -1121,112 +1185,114 @@ void ChartExport::WriteChartObj( const Reference< XShape >& xShape, sal_Int32 nI SetFS( pChart ); ExportContent(); - if (bIsChartex) { - SetFS( pChart ); - sRelativePath =""; - - FSHelperPtr pChartFS = GetFS(); + // output style and colorstyle files - // output style and colorstyle files + SetFS( pChart ); + sRelativePath =""; + + FSHelperPtr pChartFS = GetFS(); + + // first style + static constexpr char sStyleFnamePrefix[] = "style"; + OUStringBuffer sFullStreamBuf; + sFullStreamBuf.appendAscii(sFullPath); + sFullStreamBuf = sFullStreamBuf + sStyleFnamePrefix + OUString::number(nChartCount) + ".xml"; + sFullStream = sFullStreamBuf.makeStringAndClear(); + OUStringBuffer sRelativeStreamBuf; + sRelativeStreamBuf.appendAscii(sRelativePath); + sRelativeStreamBuf = sRelativeStreamBuf + sStyleFnamePrefix + OUString::number(nChartCount) + ".xml"; + sRelativeStream = sRelativeStreamBuf.makeStringAndClear(); + + FSHelperPtr pStyle = CreateOutputStream( + sFullStream, + sRelativeStream, + pChartFS->getOutputStream(), + u"application/vnd.ms-office.chartstyle+xml"_ustr, + oox::getRelationship(Relationship::CHARTSTYLE), + &sId, + true /* for some reason this doesn't have a header line */); + + SetFS( pStyle ); + pFS = GetFS(); + + pFS->startElement(FSNS(XML_cs, XML_chartStyle), + FSNS( XML_xmlns, XML_cs ), pFB->getNamespaceURL(OOX_NS(cs)), + FSNS( XML_xmlns, XML_a ), pFB->getNamespaceURL(OOX_NS(dml)), + XML_id, "419" /* no idea what this number is supposed to be */); + + Reference<com::sun::star::chart2::XChartStyle> xStyle = xChartDoc->getStyles(); + model::StyleSet* aSS = model::style::getFromXChartStyle(xStyle); + + for (enum model::StyleSet::StyleEntryType eEType = model::StyleSet::StyleEntryType::BEGIN; + eEType != model::StyleSet::StyleEntryType::END; + eEType = static_cast<model::StyleSet::StyleEntryType>(static_cast<int>(eEType) + 1)) { + auto entryIt = aSS->maEntryMap.find(eEType); + + if (entryIt == aSS->maEntryMap.end()) { + // We haven't stored any style information for this entry type, so + // just output a default value + switch (eEType) { + case model::StyleSet::StyleEntryType::CHARTAREA: + outputDefaultChartAreaStyleEntry(pFS); + break; + case model::StyleSet::StyleEntryType::DATAPOINT: + outputDefaultDataPointStyleEntry(pFS); + break; + case model::StyleSet::StyleEntryType::DATALABELCALLOUT: + // no entry required? + break; + default: + if (styleTagMap[static_cast<int>(eEType)].meType != eEType) { + assert(false); + } + outputDefaultStyleEntry(pFS, styleTagMap[static_cast<int>(eEType)].mnTag); + break; + } + } else { + outputStyleEntry(pFS, styleTagMap[static_cast<int>(eEType)].mnTag, + entryIt->second); + } + } - // first style - static constexpr char sStyleFnamePrefix[] = "style"; - OUStringBuffer sFullStreamBuf; - sFullStreamBuf.appendAscii(sFullPath); - sFullStreamBuf = sFullStreamBuf + sStyleFnamePrefix + OUString::number(nChartCount) + ".xml"; - sFullStream = sFullStreamBuf.makeStringAndClear(); - OUStringBuffer sRelativeStreamBuf; - sRelativeStreamBuf.appendAscii(sRelativePath); - sRelativeStreamBuf = sRelativeStreamBuf + sStyleFnamePrefix + OUString::number(nChartCount) + ".xml"; - sRelativeStream = sRelativeStreamBuf.makeStringAndClear(); + pFS->endElement(FSNS(XML_cs, XML_chartStyle)); - FSHelperPtr pStyle = CreateOutputStream( - sFullStream, - sRelativeStream, - pChartFS->getOutputStream(), - u"application/vnd.ms-office.chartstyle+xml"_ustr, - oox::getRelationship(Relationship::CHARTSTYLE), - &sId, - true /* for some reason this doesn't have a header line */); + pStyle->endDocument(); - SetFS( pStyle ); - pFS = GetFS(); + // now colorstyle + static constexpr char sColorFnamePrefix[] = "colors"; + sFullStreamBuf = OUStringBuffer(); + sFullStreamBuf.appendAscii(sFullPath); + sFullStreamBuf = sFullStreamBuf + sColorFnamePrefix + OUString::number(nChartCount) + ".xml"; + sFullStream = sFullStreamBuf.makeStringAndClear(); + sRelativeStreamBuf = OUStringBuffer(); + sRelativeStreamBuf.appendAscii(sRelativePath); + sRelativeStreamBuf = sRelativeStreamBuf + sColorFnamePrefix + OUString::number(nChartCount) + ".xml"; + sRelativeStream = sRelativeStreamBuf.makeStringAndClear(); - pFS->startElement(FSNS(XML_cs, XML_chartStyle), - FSNS( XML_xmlns, XML_cs ), pFB->getNamespaceURL(OOX_NS(cs)), - FSNS( XML_xmlns, XML_a ), pFB->getNamespaceURL(OOX_NS(dml)), - XML_id, "419" /* no idea what this number is supposed to be */); - - outputStyleEntry(pFS, XML_axisTitle);; - outputStyleEntry(pFS, XML_categoryAxis); - outputChartAreaStyleEntry(pFS); - outputStyleEntry(pFS, XML_dataLabel); - outputDataPointStyleEntry(pFS); - outputStyleEntry(pFS, XML_dataPoint3D); - outputStyleEntry(pFS, XML_dataPointLine); - outputStyleEntry(pFS, XML_dataPointMarker); - outputStyleEntry(pFS, XML_dataPointWireframe); - outputStyleEntry(pFS, XML_dataTable); - outputStyleEntry(pFS, XML_downBar); - outputStyleEntry(pFS, XML_dropLine); - outputStyleEntry(pFS, XML_errorBar); - outputStyleEntry(pFS, XML_floor); - outputStyleEntry(pFS, XML_gridlineMajor); - outputStyleEntry(pFS, XML_gridlineMinor); - outputStyleEntry(pFS, XML_hiLoLine); - outputStyleEntry(pFS, XML_leaderLine); - outputStyleEntry(pFS, XML_legend); - outputStyleEntry(pFS, XML_plotArea); - outputStyleEntry(pFS, XML_plotArea3D); - outputStyleEntry(pFS, XML_seriesAxis); - outputStyleEntry(pFS, XML_seriesLine); - outputStyleEntry(pFS, XML_title); - outputStyleEntry(pFS, XML_trendline); - outputStyleEntry(pFS, XML_trendlineLabel); - outputStyleEntry(pFS, XML_upBar); - outputStyleEntry(pFS, XML_valueAxis); - outputStyleEntry(pFS, XML_wall); - - pFS->endElement(FSNS(XML_cs, XML_chartStyle)); - - pStyle->endDocument(); - - // now colorstyle - static constexpr char sColorFnamePrefix[] = "colors"; - sFullStreamBuf = OUStringBuffer(); - sFullStreamBuf.appendAscii(sFullPath); - sFullStreamBuf = sFullStreamBuf + sColorFnamePrefix + OUString::number(nChartCount) + ".xml"; - sFullStream = sFullStreamBuf.makeStringAndClear(); - sRelativeStreamBuf = OUStringBuffer(); - sRelativeStreamBuf.appendAscii(sRelativePath); - sRelativeStreamBuf = sRelativeStreamBuf + sColorFnamePrefix + OUString::number(nChartCount) + ".xml"; - sRelativeStream = sRelativeStreamBuf.makeStringAndClear(); - - FSHelperPtr pColorStyle = CreateOutputStream( - sFullStream, - sRelativeStream, - pChartFS->getOutputStream(), - u"application/vnd.ms-office.chartcolorstyle+xml"_ustr, - oox::getRelationship(Relationship::CHARTCOLORSTYLE), - &sId, - true /* also no header line */); + FSHelperPtr pColorStyle = CreateOutputStream( + sFullStream, + sRelativeStream, + pChartFS->getOutputStream(), + u"application/vnd.ms-office.chartcolorstyle+xml"_ustr, + oox::getRelationship(Relationship::CHARTCOLORSTYLE), + &sId, + true /* also no header line */); - SetFS( pColorStyle ); - pFS = GetFS(); + SetFS( pColorStyle ); + pFS = GetFS(); - pFS->startElement(FSNS(XML_cs, XML_colorStyle), - FSNS( XML_xmlns, XML_cs ), pFB->getNamespaceURL(OOX_NS(cs)), - FSNS( XML_xmlns, XML_a ), pFB->getNamespaceURL(OOX_NS(dml)), - XML_meth, "cycle", - XML_id, "10" /* no idea what this number is supposed to be */); + pFS->startElement(FSNS(XML_cs, XML_colorStyle), + FSNS( XML_xmlns, XML_cs ), pFB->getNamespaceURL(OOX_NS(cs)), + FSNS( XML_xmlns, XML_a ), pFB->getNamespaceURL(OOX_NS(dml)), + XML_meth, "cycle", + XML_id, "10" /* no idea what this number is supposed to be */); - pFS->singleElement(FSNS(XML_a, XML_schemeClr), - XML_val, "accent1"); + pFS->singleElement(FSNS(XML_a, XML_schemeClr), + XML_val, "accent1"); - pFS->endElement(FSNS(XML_cs, XML_colorStyle)); + pFS->endElement(FSNS(XML_cs, XML_colorStyle)); - pColorStyle->endDocument(); - } + pColorStyle->endDocument(); pChart->endDocument(); } @@ -1360,7 +1426,7 @@ void ChartExport::exportChartSpace( const Reference< css::chart::XChartDocument // TODO: text properties Reference< XPropertySet > xPropSet = xChartDoc->getArea(); if( xPropSet.is() ) - exportShapeProps( xPropSet, bIsChartex ); + exportShapeProps( xPropSet, nChartNS ); // TODO for chartex if (!bIsChartex) { @@ -1839,7 +1905,7 @@ void ChartExport::exportChart( const Reference< css::chart::XChartDocument >& xC if( xFloor.is() ) { pFS->startElement(FSNS(XML_c, XML_floor)); - exportShapeProps( xFloor, false ); + exportShapeProps( xFloor, XML_c ); pFS->endElement( FSNS( XML_c, XML_floor ) ); } @@ -1850,12 +1916,12 @@ void ChartExport::exportChart( const Reference< css::chart::XChartDocument >& xC { // sideWall pFS->startElement(FSNS(XML_c, XML_sideWall)); - exportShapeProps( xWall, false ); + exportShapeProps( xWall, XML_c ); pFS->endElement( FSNS( XML_c, XML_sideWall ) ); // backWall pFS->startElement(FSNS(XML_c, XML_backWall)); - exportShapeProps( xWall, false ); + exportShapeProps( xWall, XML_c ); pFS->endElement( FSNS( XML_c, XML_backWall ) ); } } @@ -2100,7 +2166,7 @@ void ChartExport::exportLegend( const Reference< css::chart::XChartDocument >& x } // shape properties - exportShapeProps( xProp, bIsChartex ); + exportShapeProps( xProp, bIsChartex ? XML_cx : XML_c ); // draw-chart:txPr text properties exportTextProps( xProp, bIsChartex ); @@ -2166,7 +2232,7 @@ void ChartExport::exportTitle( const Reference< XShape >& xShape, bool bIsCharte // shape properties if( xPropSet.is() ) { - exportShapeProps( xPropSet, bIsChartex ); + exportShapeProps( xPropSet, bIsChartex ? XML_cx : XML_c ); } pFS->startElement(FSNS(XML_cx, XML_txPr)); @@ -2300,7 +2366,7 @@ void ChartExport::exportTitle( const Reference< XShape >& xShape, bool bIsCharte // shape properties if( xPropSet.is() ) { - exportShapeProps( xPropSet, bIsChartex ); + exportShapeProps( xPropSet, bIsChartex ? XML_cx : XML_c ); } } @@ -2518,7 +2584,7 @@ void ChartExport::exportPlotArea(const Reference< css::chart::XChartDocument >& { xWallPropSet->setPropertyValue( u"LineStyle"_ustr, uno::Any(drawing::LineStyle_NONE) ); } - exportShapeProps( xWallPropSet, bIsChartex ); + exportShapeProps( xWallPropSet, bIsChartex ? XML_cx : XML_c ); } } @@ -2844,7 +2910,7 @@ void ChartExport::exportDataTable( ) if (bShowKeys) pFS->singleElement(FSNS(XML_c, XML_showKeys), XML_val, "1"); - exportShapeProps(aPropSet, false); + exportShapeProps(aPropSet, XML_c); exportTextProps(aPropSet, false); pFS->endElement(FSNS(XML_c, XML_dTable)); @@ -3278,7 +3344,7 @@ void ChartExport::exportHiLowLines() return; pFS->startElement(FSNS(XML_c, XML_hiLowLines)); - exportShapeProps( xStockPropSet, false ); + exportShapeProps( xStockPropSet, XML_c ); pFS->endElement( FSNS( XML_c, XML_hiLowLines ) ); } @@ -3306,7 +3372,7 @@ void ChartExport::exportUpDownBars( const Reference< chart2::XChartType >& xChar // so no need to call the exportShapeProps() for LineChart if(xChartType->getChartType() == "com.sun.star.chart2.CandleStickChartType") { - exportShapeProps(xChartPropSet, false); + exportShapeProps(xChartPropSet, XML_c); } pFS->endElement( FSNS( XML_c, XML_upBars ) ); } @@ -3316,7 +3382,7 @@ void ChartExport::exportUpDownBars( const Reference< chart2::XChartType >& xChar pFS->startElement(FSNS(XML_c, XML_downBars)); if(xChartType->getChartType() == "com.sun.star.chart2.CandleStickChartType") { - exportShapeProps(xChartPropSet, false); + exportShapeProps(xChartPropSet, XML_c); } pFS->endElement( FSNS( XML_c, XML_downBars ) ); } @@ -3442,7 +3508,7 @@ void ChartExport::exportSeries_chart( const Reference<chart2::XChartType>& xChar rSeries, getModel() ); if( xOldPropSet.is() ) { - exportShapeProps( xOldPropSet, false ); + exportShapeProps( xOldPropSet, XML_c ); } switch( eChartType ) @@ -3626,7 +3692,7 @@ void ChartExport::exportSeries_chartex( const Reference<chart2::XChartType>& xCh rSeries, getModel() ); if( xOldPropSet.is() ) { - exportShapeProps( xOldPropSet, true ); + exportShapeProps( xOldPropSet, XML_cx ); } DataLabelsRange aDLblsRange; @@ -3894,16 +3960,15 @@ void ChartExport::exportSeriesValues( const Reference< chart2::data::XDataSequen } void ChartExport::exportShapeProps( const Reference< XPropertySet >& xPropSet, - bool bIsChartex) + sal_Int32 nNS) { - sal_Int32 nChartNS = bIsChartex ? XML_cx : XML_c; FSHelperPtr pFS = GetFS(); - pFS->startElement(FSNS(nChartNS, XML_spPr)); + pFS->startElement(FSNS(nNS, XML_spPr)); exportFill( xPropSet ); WriteOutline( xPropSet, getModel() ); - pFS->endElement( FSNS( nChartNS, XML_spPr ) ); + pFS->endElement( FSNS( nNS, XML_spPr ) ); } void ChartExport::exportTextProps(const Reference<XPropertySet>& xPropSet, @@ -4288,7 +4353,7 @@ void ChartExport::exportOneAxis_chart( if( xMajorGrid.is()) { pFS->startElement(FSNS(XML_c, XML_majorGridlines)); - exportShapeProps( xMajorGrid, false ); + exportShapeProps( xMajorGrid, XML_c ); pFS->endElement( FSNS( XML_c, XML_majorGridlines ) ); } @@ -4296,7 +4361,7 @@ void ChartExport::exportOneAxis_chart( if( xMinorGrid.is()) { pFS->startElement(FSNS(XML_c, XML_minorGridlines)); - exportShapeProps( xMinorGrid, false ); + exportShapeProps( xMinorGrid, XML_c ); pFS->endElement( FSNS( XML_c, XML_minorGridlines ) ); } @@ -4368,7 +4433,7 @@ void ChartExport::exportOneAxis_chart( pFS->singleElement(FSNS(XML_c, XML_tickLblPos), XML_val, sTickLblPos); // shape properties - exportShapeProps( xAxisProp, false ); + exportShapeProps( xAxisProp, XML_c ); exportTextProps(xAxisProp, false); @@ -4647,7 +4712,7 @@ void ChartExport::exportOneAxis_chartex( if( xMajorGrid.is()) { pFS->startElement(FSNS(XML_cx, XML_majorGridlines)); - exportShapeProps( xMajorGrid, true ); + exportShapeProps( xMajorGrid, XML_cx ); pFS->endElement( FSNS( XML_cx, XML_majorGridlines ) ); } @@ -4655,7 +4720,7 @@ void ChartExport::exportOneAxis_chartex( if( xMinorGrid.is()) { pFS->startElement(FSNS(XML_cx, XML_minorGridlines)); - exportShapeProps( xMinorGrid, true ); + exportShapeProps( xMinorGrid, XML_cx ); pFS->endElement( FSNS( XML_cx, XML_minorGridlines ) ); } @@ -4701,7 +4766,7 @@ void ChartExport::exportOneAxis_chartex( XML_sourceLinked, bLinkedNumFmt ? "1" : "0"); // ==== spPr - exportShapeProps( xAxisProp, true ); + exportShapeProps( xAxisProp, XML_cx ); // ==== txPr exportTextProps(xAxisProp, true); @@ -5246,7 +5311,7 @@ void ChartExport::exportDataPoints( default: break; } - exportShapeProps( xPropSet, false ); + exportShapeProps( xPropSet, XML_c ); pFS->endElement( FSNS( XML_c, XML_dPt ) ); } @@ -5290,7 +5355,7 @@ void ChartExport::exportDataPoints( case chart::TYPEID_HORBAR: case chart::TYPEID_BAR: pFS->singleElement(FSNS(XML_c, XML_invertIfNegative), XML_val, "0"); - exportShapeProps(xPropSet, false); + exportShapeProps(xPropSet, XML_c); break; case chart::TYPEID_LINE: @@ -5300,7 +5365,7 @@ void ChartExport::exportDataPoints( break; default: - exportShapeProps(xPropSet, false); + exportShapeProps(xPropSet, XML_c); break; } @@ -5420,7 +5485,7 @@ void ChartExport::exportTrendlines( const Reference< chart2::XDataSeries >& xSer pFS->endElement( FSNS( XML_c, XML_name) ); } - exportShapeProps( xProperties, false ); + exportShapeProps( xProperties, XML_c ); if( aService == "com.sun.star.chart2.LinearRegressionCurve" ) { @@ -5740,7 +5805,7 @@ void ChartExport::exportErrorBar(const Reference< XPropertySet>& xErrorBarProps, pFS->singleElement(FSNS(XML_c, XML_val), XML_val, OString::number(nVal)); } - exportShapeProps( xErrorBarProps, false ); + exportShapeProps( xErrorBarProps, XML_c ); pFS->endElement( FSNS( XML_c, XML_errBars) ); } @@ -5908,6 +5973,68 @@ OUString ChartExport::getNumberFormatCode(sal_Int32 nKey) const return aCode; } +// Create cs:CT_FontReference or cs:CT_StyleReference +void ChartExport::outputFontOrStyleRef(FSHelperPtr pFS, sal_Int32 nElTokenId, const + model::FontOrStyleRef& aColor) +{ + pFS->startElement(FSNS(XML_cs, nElTokenId), XML_idx, OUString::number(aColor.mnIdx)); + + ThemeExport aTE(mpFB, GetDocumentType(), pFS); + aTE.writeComplexColor(aColor.maComplexColor); + + // Get the string for the StyleColorVal + OUString sV = aColor.getColorValStr(); + if (!sV.isEmpty()) { + pFS->singleElement(FSNS(XML_cs, XML_styleClr), XML_val, sV); + } + + pFS->endElement(FSNS(XML_cs, nElTokenId)); +} + +// Create cs:CT_StyleEntry +void ChartExport::outputStyleEntry(FSHelperPtr pFS, sal_Int32 nElTokenId, model::StyleEntry& aEntry) +{ + // Just default values for now + pFS->startElement(FSNS(XML_cs, nElTokenId)); + + outputFontOrStyleRef(pFS, FSNS(XML_cs, XML_lnRef), *(aEntry.mxLnClr)); + + pFS->startElement(FSNS(XML_cs, XML_lineWidthScale)); + pFS->write(aEntry.mfLineWidthScale); + pFS->endElement(FSNS(XML_cs, XML_lineWidthScale)); + + outputFontOrStyleRef(pFS, FSNS(XML_cs, XML_fillRef), *(aEntry.mxFillClr)); + outputFontOrStyleRef(pFS, FSNS(XML_cs, XML_effectRef), *(aEntry.mxEffectClr)); + outputFontOrStyleRef(pFS, FSNS(XML_cs, XML_fontRef), *(aEntry.mxFontClr)); + + if (aEntry.mxShapePr) { + exportShapeProps(aEntry.mxShapePr->getShapeProperties().makePropertySet(), XML_cs); + } + + if (aEntry.mrTextCharacterPr) { + PropertyMap *pPM = aEntry.mrTextCharacterPr.get(); + Reference< XPropertySet > rPS = pPM->makePropertySet(); + + WriteRunInput aInput; + aInput.bCheckDirect = false; + aInput.bUseTextSchemeColors = false; + WriteRunProperties(rPS, XML_defRPr, aInput ); + } + + if (aEntry.mxTextBodyPr) { + PropertyMap *pPM = aEntry.mxTextBodyPr.get(); + aEntry.mxTextBodyPr.reset(); + Reference< XPropertySet > rPS = pPM->makePropertySet(); + uno::Reference<drawing::XShape> xShape(rPS, UNO_QUERY); + uno::Reference<XPropertySet> rXPropSet = rPS; + WriteBodyPropsInput aWBPInput; + + WriteBodyProps(rPS, rXPropSet, xShape, XML_cs, aWBPInput); + } + + pFS->endElement(FSNS(XML_cs, nElTokenId)); +} + }// oox /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx index a3fd38496787..0ca14190fe50 100644 --- a/oox/source/export/drawingml.cxx +++ b/oox/source/export/drawingml.cxx @@ -2675,10 +2675,10 @@ void DrawingML::WriteRunProperties(const Reference<XPropertySet>& rRun, sal_Int3 usLanguage = aLanguageTag.getBcp47MS(); } - if (GetDirectProperty(rXPropSet, rXPropState, u"CharEscapement"_ustr)) + if (rXPropState && GetDirectProperty(rXPropSet, rXPropState, u"CharEscapement"_ustr)) mAny >>= nCharEscapement; - if (GetDirectProperty(rXPropSet, rXPropState, u"CharEscapementHeight"_ustr)) + if (rXPropState && GetDirectProperty(rXPropSet, rXPropState, u"CharEscapementHeight"_ustr)) mAny >>= nCharEscapementHeight; if (DFLT_ESC_AUTO_SUPER == nCharEscapement) @@ -2870,7 +2870,7 @@ void DrawingML::WriteRunProperties(const Reference<XPropertySet>& rRun, sal_Int3 XML_charset, charset ); } - if (GetDirectProperty(rXPropSet, rXPropState, + if (rXPropState && GetDirectProperty(rXPropSet, rXPropState, bComplex ? u"CharFontNameComplex"_ustr : u"CharFontNameAsian"_ustr)) { const char* const pitch = nullptr; @@ -3958,6 +3958,236 @@ bool DrawingML::IsFontworkShape(const css::uno::Reference<css::beans::XPropertyS return bResult; } +// Output text body properties +void DrawingML::WriteBodyProps(const css::uno::Reference< css::uno::XInterface >& rXIface, + const uno::Reference<XPropertySet>& rXPropSet, + const uno::Reference<drawing::XShape>& xShape, + sal_Int32 nXmlNamespace, const WriteBodyPropsInput& aWBPInput) +{ + const char* pWrap = (aWBPInput.bHasWrap && !aWBPInput.bWrap) || aWBPInput.bIsFontworkShape ? "none" : nullptr; + if (GetDocumentType() == DOCUMENT_DOCX) + { + // In case of DOCX, if we want to have the same effect as + // TextShape's automatic word wrapping, then we need to set + // wrapping to square. + uno::Reference<lang::XServiceInfo> xServiceInfo(rXIface, uno::UNO_QUERY); + if ((xServiceInfo.is() && xServiceInfo->supportsService(u"com.sun.star.drawing.TextShape"_ustr)) + || aWBPInput.bIsFontworkShape) + pWrap = "square"; + } + + sal_Int16 nCols = 0; + sal_Int32 nColSpacing = -1; + if (GetProperty(rXPropSet, u"TextColumns"_ustr)) + { + if (css::uno::Reference<css::text::XTextColumns> xCols{ mAny, css::uno::UNO_QUERY }) + { + nCols = xCols->getColumnCount(); + if (css::uno::Reference<css::beans::XPropertySet> xProps{ mAny, + css::uno::UNO_QUERY }) + { + if (GetProperty(xProps, u"AutomaticDistance"_ustr)) + mAny >>= nColSpacing; + } + } + } + + std::optional<OUString> sVertOverflow = aWBPInput.sVertOverflow; + + if (!sVertOverflow && GetProperty(rXPropSet, u"TextClipVerticalOverflow"_ustr) && mAny.get<bool>()) + { + sVertOverflow = "clip"; + } + + // tdf#151134 When writing placeholder shapes, inset must be explicitly specified + bool bRequireInset = GetProperty(rXPropSet, u"IsPresentationObject"_ustr) && rXPropSet->getPropertyValue(u"IsPresentationObject"_ustr).get<bool>(); + + mpFS->startElementNS( (nXmlNamespace ? nXmlNamespace : XML_a), XML_bodyPr, + XML_numCol, sax_fastparser::UseIf(OString::number(nCols), nCols > 0), + XML_spcCol, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nColSpacing)), nCols > 0 && nColSpacing >= 0), + XML_wrap, pWrap, + XML_horzOverflow, aWBPInput.sHorzOverflow, + XML_vertOverflow, sVertOverflow, + XML_fromWordArt, sax_fastparser::UseIf("1", + aWBPInput.bFromWordArt), + XML_lIns, + sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(aWBPInput.nLeft)), + bRequireInset || + aWBPInput.nLeft != mconstDefaultLeftRightInset), + XML_tIns, + sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(aWBPInput.nTop)), + bRequireInset || + aWBPInput.nTop != mconstDefaultTopBottomInset), + XML_rIns, + sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(aWBPInput.nRight)), + bRequireInset || + aWBPInput.nRight != mconstDefaultLeftRightInset), + XML_bIns, + sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(aWBPInput.nBottom)), + bRequireInset || + aWBPInput.nBottom != mconstDefaultTopBottomInset), + XML_anchor, aWBPInput.sAnchor, + XML_anchorCtr, sax_fastparser::UseIf("1", aWBPInput.bAnchorCtr), + XML_vert, aWBPInput.sWritingMode, + XML_upright, aWBPInput.sIsUpright, + XML_rot, aWBPInput.sTextRotateAngleMSUnit); + + if (aWBPInput.bIsFontworkShape) + { + if (aWBPInput.aAdjustmentSeq.hasElements()) + { + mpFS->startElementNS(XML_a, XML_prstTxWarp, XML_prst, aWBPInput.sPresetWarp); + mpFS->startElementNS(XML_a, XML_avLst); + bool bHasTwoHandles( + aWBPInput.sPresetWarp == "textArchDownPour" || aWBPInput.sPresetWarp == "textArchUpPour" + || aWBPInput.sPresetWarp == "textButtonPour" || aWBPInput.sPresetWarp == "textCirclePour" + || aWBPInput.sPresetWarp == "textDoubleWave1" || aWBPInput.sPresetWarp == "textWave1" + || aWBPInput.sPresetWarp == "textWave2" || aWBPInput.sPresetWarp == "textWave4"); + for (sal_Int32 i = 0, nElems = aWBPInput.aAdjustmentSeq.getLength(); i < nElems; ++i ) + { + OString sName = "adj" + (bHasTwoHandles ? OString::number(i + 1) : OString()); + double fValue(0.0); + if (aWBPInput.aAdjustmentSeq[i].Value.getValueTypeClass() == TypeClass_DOUBLE) + aWBPInput.aAdjustmentSeq[i].Value >>= fValue; + else + { + sal_Int32 nNumber(0); + aWBPInput.aAdjustmentSeq[i].Value >>= nNumber; + fValue = static_cast<double>(nNumber); + } + // Convert from binary coordinate system with viewBox "0 0 21600 21600" and simple degree + // to DrawingML with coordinate range 0..100000 and angle in 1/60000 degree. + // Reverse to conversion in lcl_createPresetShape in drawingml/shape.cxx on import. + if (aWBPInput.sPresetWarp == "textArchDown" || aWBPInput.sPresetWarp == "textArchUp" + || aWBPInput.sPresetWarp == "textButton" || aWBPInput.sPresetWarp == "textCircle" + || ((i == 0) + && (aWBPInput.sPresetWarp == "textArchDownPour" || + aWBPInput.sPresetWarp == "textArchUpPour" + || aWBPInput.sPresetWarp == "textButtonPour" || + aWBPInput.sPresetWarp == "textCirclePour"))) + { + fValue *= 60000.0; + if (fValue < 0) + fValue += 21600000; + } + else if ((i == 1) + && (aWBPInput.sPresetWarp == "textDoubleWave1" || + aWBPInput.sPresetWarp == "textWave1" + || aWBPInput.sPresetWarp == "textWave2" || aWBPInput.sPresetWarp == "textWave4")) + { + fValue = fValue / 0.216 - 50000.0; + } + else if ((i == 1) + && (aWBPInput.sPresetWarp == "textArchDownPour" + || aWBPInput.sPresetWarp == "textArchUpPour" + || aWBPInput.sPresetWarp == "textButtonPour" + || aWBPInput.sPresetWarp == "textCirclePour")) + { + fValue /= 0.108; + } + else + { + fValue /= 0.216; + } + OString sFmla = "val " + OString::number(std::lround(fValue)); + mpFS->singleElementNS(XML_a, XML_gd, XML_name, sName, XML_fmla, sFmla); + // There exists faulty Favorite shapes with one handle but two adjustment values. + if (!bHasTwoHandles) + break; + } + mpFS->endElementNS(XML_a, XML_avLst); + mpFS->endElementNS(XML_a, XML_prstTxWarp); + } + else + { + mpFS->singleElementNS(XML_a, XML_prstTxWarp, XML_prst, aWBPInput.sPresetWarp); + } + } + else if (GetDocumentType() == DOCUMENT_DOCX) + { + // interim solution for fdo#80897, roundtrip DOCX > LO > DOCX + if (!aWBPInput.sMSWordPresetTextWarp.isEmpty()) + mpFS->singleElementNS(XML_a, XML_prstTxWarp, XML_prst, aWBPInput.sMSWordPresetTextWarp); + } + + if (GetDocumentType() == DOCUMENT_DOCX || GetDocumentType() == DOCUMENT_XLSX) + { + // tdf#112312: only custom shapes obey the TextAutoGrowHeight option + bool bTextAutoGrowHeight = false; + auto pSdrObjCustomShape = xShape.is() ? dynamic_cast<SdrObjCustomShape*>(SdrObject::getSdrObjectFromXShape(xShape)) : nullptr; + if (pSdrObjCustomShape && GetProperty(rXPropSet, u"TextAutoGrowHeight"_ustr)) + { + mAny >>= bTextAutoGrowHeight; + } + mpFS->singleElementNS(XML_a, (bTextAutoGrowHeight ? XML_spAutoFit : XML_noAutofit)); + } + if (GetDocumentType() == DOCUMENT_PPTX) + { + TextFitToSizeType eFit = TextFitToSizeType_NONE; + if (GetProperty(rXPropSet, u"TextFitToSize"_ustr)) + mAny >>= eFit; + + if (eFit == TextFitToSizeType_AUTOFIT) + { + const sal_Int32 MAX_SCALE_VAL = 100000; + sal_Int32 nFontScale = MAX_SCALE_VAL; + sal_Int32 nSpacingReduction = 0; + SvxShapeText* pTextShape = dynamic_cast<SvxShapeText*>(rXIface.get()); + if (pTextShape) + { + SdrTextObj* pTextObject = DynCastSdrTextObj(pTextShape->GetSdrObject()); + if (pTextObject) + { + nFontScale = sal_Int32(pTextObject->GetFontScale() * 100000.0); + nSpacingReduction = sal_Int32((1.0 - pTextObject->GetSpacingScale()) * 100000.0); + } + } + + bool bExportFontScale = false; + if (nFontScale < MAX_SCALE_VAL && nFontScale > 0) + bExportFontScale = true; + + bool bExportSpaceReduction = false; + if (nSpacingReduction < MAX_SCALE_VAL && nSpacingReduction > 0) + bExportSpaceReduction = true; + + mpFS->singleElementNS(XML_a, XML_normAutofit, + XML_fontScale, sax_fastparser::UseIf(OString::number(nFontScale), bExportFontScale), + XML_lnSpcReduction, sax_fastparser::UseIf(OString::number(nSpacingReduction), bExportSpaceReduction)); + } + else + { + bool bAutoGrowHeightEnabled = false; + const SdrObject* pObj = xShape.is() ? SdrObject::getSdrObjectFromXShape(xShape) : nullptr; + if (pObj) + { + switch (pObj->GetObjIdentifier()) + { + case SdrObjKind::NONE: + case SdrObjKind::Text: + case SdrObjKind::TitleText: + case SdrObjKind::OutlineText: + case SdrObjKind::Caption: + case SdrObjKind::CustomShape: + bAutoGrowHeightEnabled = true; + break; + default: + bAutoGrowHeightEnabled = false; + } + } + + bool bTextAutoGrowHeight = false; + if (bAutoGrowHeightEnabled && GetProperty(rXPropSet, u"TextAutoGrowHeight"_ustr)) + mAny >>= bTextAutoGrowHeight; + mpFS->singleElementNS(XML_a, (bTextAutoGrowHeight ? XML_spAutoFit : XML_noAutofit)); + } + } + + Write3DEffects( rXPropSet, /*bIsText=*/true ); + + mpFS->endElementNS((nXmlNamespace ? nXmlNamespace : XML_a), XML_bodyPr); +} + void DrawingML::WriteText(const Reference<XInterface>& rXIface, bool bBodyPr, bool bText, sal_Int32 nXmlNamespace, bool bWritePropertiesAsLstStyles) { @@ -3966,26 +4196,28 @@ void DrawingML::WriteText(const Reference<XInterface>& rXIface, bool bBodyPr, bo if( !xXText.is() ) return; + WriteBodyPropsInput aWBPInput; + uno::Reference<drawing::XShape> xShape(rXIface, UNO_QUERY); uno::Reference<XPropertySet> rXPropSet(rXIface, UNO_QUERY); constexpr const sal_Int32 constDefaultLeftRightInset = 254; constexpr const sal_Int32 constDefaultTopBottomInset = 127; - sal_Int32 nLeft = constDefaultLeftRightInset; - sal_Int32 nRight = constDefaultLeftRightInset; - sal_Int32 nTop = constDefaultTopBottomInset; - sal_Int32 nBottom = constDefaultTopBottomInset; + aWBPInput.nLeft = constDefaultLeftRightInset; + aWBPInput.nRight = constDefaultLeftRightInset; + aWBPInput.nTop = constDefaultTopBottomInset; + aWBPInput.nBottom = constDefaultTopBottomInset; // top inset looks a bit different compared to ppt export // check if something related doesn't work as expected if (GetProperty(rXPropSet, u"TextLeftDistance"_ustr)) - mAny >>= nLeft; + mAny >>= aWBPInput.nLeft; if (GetProperty(rXPropSet, u"TextRightDistance"_ustr)) - mAny >>= nRight; + mAny >>= aWBPInput.nRight; if (GetProperty(rXPropSet, u"TextUpperDistance"_ustr)) - mAny >>= nTop; + mAny >>= aWBPInput.nTop; if (GetProperty(rXPropSet, u"TextLowerDistance"_ustr)) - mAny >>= nBottom; + mAny >>= aWBPInput.nBottom; // Transform the text distance values so they are compatible with OOXML insets if (xShape.is()) @@ -4003,23 +4235,22 @@ void DrawingML::WriteText(const Reference<XInterface>& rXIface, bool bBodyPr, bo nTextHeight = convertTwipToMm100(nTextHeight); } - if (nTop + nBottom >= nTextHeight) + if (aWBPInput.nTop + aWBPInput.nBottom >= nTextHeight) { // Effective bottom would be above effective top of text area. LO normalizes the // effective text area in such case implicitly for rendering. MS needs indents so that // the result is the normalized effective text area. - std::swap(nTop, nBottom); - nTop = nTextHeight - nTop; - nBottom = nTextHeight - nBottom; + std::swap(aWBPInput.nTop, aWBPInput.nBottom); + aWBPInput.nTop = nTextHeight - aWBPInput.nTop; + aWBPInput.nBottom = nTextHeight - aWBPInput.nBottom; } } - std::optional<OString> sWritingMode; if (GetProperty(rXPropSet, u"TextWritingMode"_ustr)) { WritingMode eMode; if( ( mAny >>= eMode ) && eMode == WritingMode_TB_RL ) - sWritingMode = "eaVert"; + aWBPInput.sWritingMode = "eaVert"; } if (GetProperty(rXPropSet, u"WritingMode"_ustr)) { @@ -4027,24 +4258,22 @@ void DrawingML::WriteText(const Reference<XInterface>& rXIface, bool bBodyPr, bo if (mAny >>= nWritingMode) { if (nWritingMode == text::WritingMode2::TB_RL) - sWritingMode = "eaVert"; + aWBPInput.sWritingMode = "eaVert"; else if (nWritingMode == text::WritingMode2::BT_LR) - sWritingMode = "vert270"; + aWBPInput.sWritingMode = "vert270"; else if (nWritingMode == text::WritingMode2::TB_RL90) - sWritingMode = "vert"; + aWBPInput.sWritingMode = "vert"; else if (nWritingMode == text::WritingMode2::TB_LR) - sWritingMode = "mongolianVert"; + aWBPInput.sWritingMode = "mongolianVert"; else if (nWritingMode == text::WritingMode2::STACKED) - sWritingMode = "wordArtVert"; + aWBPInput.sWritingMode = "wordArtVert"; } } // read values from CustomShapeGeometry - Sequence<drawing::EnhancedCustomShapeAdjustmentValue> aAdjustmentSeq; uno::Sequence<beans::PropertyValue> aTextPathSeq; bool bScaleX(false); OUString sShapeType(u"non-primitive"_ustr); - OUString sMSWordPresetTextWarp; sal_Int32 nTextPreRotateAngle = 0; // degree std::optional<Degree100> nTextRotateAngleDeg100; // text area rotation @@ -4058,7 +4287,7 @@ void DrawingML::WriteText(const Reference<XInterface>& rXIface, bool bBodyPr, bo if (rProp.Name == "TextPreRotateAngle") rProp.Value >>= nTextPreRotateAngle; else if (rProp.Name == "AdjustmentValues") - rProp.Value >>= aAdjustmentSeq; + rProp.Value >>= aWBPInput.aAdjustmentSeq; else if (rProp.Name == "TextRotateAngle") { double fTextRotateAngle = 0; // degree @@ -4077,7 +4306,7 @@ void DrawingML::WriteText(const Reference<XInterface>& rXIface, bool bBodyPr, bo } } else if (rProp.Name == "PresetTextWarp") - rProp.Value >>= sMSWordPresetTextWarp; + rProp.Value >>= aWBPInput.sMSWordPresetTextWarp; } } } @@ -4098,16 +4327,16 @@ void DrawingML::WriteText(const Reference<XInterface>& rXIface, bool bBodyPr, bo switch (nWritingMode) { case WritingMode2::TB_RL: - sWritingMode = "eaVert"; + aWBPInput.sWritingMode = "eaVert"; break; case WritingMode2::BT_LR: - sWritingMode = "vert270"; + aWBPInput.sWritingMode = "vert270"; break; case WritingMode2::TB_RL90: - sWritingMode = "vert"; + aWBPInput.sWritingMode = "vert"; break; case WritingMode2::TB_LR: - sWritingMode = "mongolianVert"; + aWBPInput.sWritingMode = "mongolianVert"; break; default: break; @@ -4119,10 +4348,7 @@ void DrawingML::WriteText(const Reference<XInterface>& rXIface, bool bBodyPr, bo } // read InteropGrabBag if any - std::optional<OUString> sHorzOverflow; - std::optional<OUString> sVertOverflow; bool bUpright = false; - std::optional<OString> isUpright; if (rXPropSet->getPropertySetInfo()->hasPropertyByName(u"InteropGrabBag"_ustr)) { uno::Sequence<beans::PropertyValue> aGrabBag; @@ -4132,40 +4358,42 @@ void DrawingML::WriteText(const Reference<XInterface>& rXIface, bool bBodyPr, bo if (aProp.Name == "Upright") { aProp.Value >>= bUpright; - isUpright = OString(bUpright ? "1" : "0"); + aWBPInput.sIsUpright = OString(bUpright ? "1" : "0"); } else if (aProp.Name == "horzOverflow") { OUString sValue; aProp.Value >>= sValue; - sHorzOverflow = sValue; + aWBPInput.sHorzOverflow = sValue; } else if (aProp.Name == "vertOverflow") { OUString sValue; aProp.Value >>= sValue; - sVertOverflow = sValue; + aWBPInput.sVertOverflow = sValue; } } } - bool bIsFontworkShape(IsFontworkShape(rXPropSet)); - OUString sPresetWarp(PresetGeometryTypeNames::GetMsoName(sShapeType)); + aWBPInput.bIsFontworkShape = IsFontworkShape(rXPropSet); + aWBPInput.sPresetWarp = PresetGeometryTypeNames::GetMsoName(sShapeType); // ODF may have user defined TextPath, use "textPlain" as ersatz. - if (sPresetWarp.isEmpty()) - sPresetWarp = bIsFontworkShape ? std::u16string_view(u"textPlain") : std::u16string_view(u"textNoShape"); + if (aWBPInput.sPresetWarp.isEmpty()) + aWBPInput.sPresetWarp = aWBPInput.bIsFontworkShape ? std::u16string_view(u"textPlain") : std::u16string_view(u"textNoShape"); - bool bFromWordArt = !bScaleX - && ( sPresetWarp == "textArchDown" || sPresetWarp == "textArchUp" - || sPresetWarp == "textButton" || sPresetWarp == "textCircle"); + aWBPInput.bFromWordArt = !bScaleX + && ( aWBPInput.sPresetWarp == "textArchDown" || + aWBPInput.sPresetWarp == "textArchUp" + || aWBPInput.sPresetWarp == "textButton" || + aWBPInput.sPresetWarp == "textCircle"); // Fontwork shapes in LO ignore insets in rendering, Word interprets them. - if (GetDocumentType() == DOCUMENT_DOCX && bIsFontworkShape) + if (GetDocumentType() == DOCUMENT_DOCX && aWBPInput.bIsFontworkShape) { - nLeft = 0; - nRight = 0; - nTop = 0; - nBottom = 0; + aWBPInput.nLeft = 0; + aWBPInput.nRight = 0; + aWBPInput.nTop = 0; + aWBPInput.nBottom = 0; } if (bUpright) @@ -4196,7 +4424,7 @@ void DrawingML::WriteText(const Reference<XInterface>& rXIface, bool bBodyPr, bo else { // User changes. Keep current angles. - isUpright.reset(); + aWBPInput.sIsUpright.reset(); if (bWasAngleChanged) { nTextPreRotateAngle += 90; @@ -4207,12 +4435,12 @@ void DrawingML::WriteText(const Reference<XInterface>& rXIface, bool bBodyPr, bo // ToDo: Unsure about this. Need to investigate shapes from diagram import, especially diagrams // with vertical text directions. - if (nTextPreRotateAngle != 0 && !sWritingMode) + if (nTextPreRotateAngle != 0 && !aWBPInput.sWritingMode) { if (nTextPreRotateAngle == -90 || nTextPreRotateAngle == 270) - sWritingMode = "vert"; + aWBPInput.sWritingMode = "vert"; else if (nTextPreRotateAngle == -270 || nTextPreRotateAngle == 90) - sWritingMode = "vert270"; + aWBPInput.sWritingMode = "vert270"; else if (nTextPreRotateAngle == -180 || nTextPreRotateAngle == 180) { #if defined __GNUC__ && !defined __clang__ && __GNUC__ == 12 @@ -4229,7 +4457,7 @@ void DrawingML::WriteText(const Reference<XInterface>& rXIface, bool bBodyPr, bo else SAL_WARN("oox", "unsuitable value for TextPreRotateAngle:" << nTextPreRotateAngle); } - else if (nTextPreRotateAngle != 0 && sWritingMode && sWritingMode.value() == "eaVert") + else if (nTextPreRotateAngle != 0 && aWBPInput.sWritingMode && aWBPInput.sWritingMode.value() == "eaVert") { // ToDo: eaVert plus 270deg clockwise rotation has to be written with vert="horz" // plus attribute 'normalEastAsianFlow="1"' on the <wps:wsp> element. @@ -4238,38 +4466,37 @@ void DrawingML::WriteText(const Reference<XInterface>& rXIface, bool bBodyPr, bo // Our WritingMode introduces text pre rotation which includes padding, MSO vert does not include // padding. Therefore set padding so, that is looks the same in MSO as in LO. - if (sWritingMode) + if (aWBPInput.sWritingMode) { - if (sWritingMode.value() == "vert" || sWritingMode.value() == "eaVert") + if (aWBPInput.sWritingMode.value() == "vert" || aWBPInput.sWritingMode.value() == "eaVert") { - sal_Int32 nHelp = nLeft; - nLeft = nBottom; - nBottom = nRight; - nRight = nTop; - nTop = nHelp; + sal_Int32 nHelp = aWBPInput.nLeft; + aWBPInput.nLeft = aWBPInput.nBottom; + aWBPInput.nBottom = aWBPInput.nRight; + aWBPInput.nRight = aWBPInput.nTop; + aWBPInput.nTop = nHelp; } - else if (sWritingMode.value() == "vert270") + else if (aWBPInput.sWritingMode.value() == "vert270") { - sal_Int32 nHelp = nLeft; - nLeft = nTop; - nTop = nRight; - nRight = nBottom; - nBottom = nHelp; + sal_Int32 nHelp = aWBPInput.nLeft; + aWBPInput.nLeft = aWBPInput.nTop; + aWBPInput.nTop = aWBPInput.nRight; + aWBPInput.nRight = aWBPInput.nBottom; + aWBPInput.nBottom = nHelp; } - else if (sWritingMode.value() == "mongolianVert") + else if (aWBPInput.sWritingMode.value() == "mongolianVert") { // ToDo: Examine padding } } - std::optional<OString> sTextRotateAngleMSUnit; if (nTextRotateAngleDeg100.has_value()) #if defined __GNUC__ && !defined __clang__ && __GNUC__ == 12 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif - sTextRotateAngleMSUnit + aWBPInput.sTextRotateAngleMSUnit = oox::drawingml::calcRotationValue(nTextRotateAngleDeg100.value().get()); #if defined __GNUC__ && !defined __clang__ && __GNUC__ == 12 #pragma GCC diagnostic pop @@ -4285,42 +4512,40 @@ void DrawingML::WriteText(const Reference<XInterface>& rXIface, bool bBodyPr, bo if (GetProperty(rXPropSet, u"TextHorizontalAdjust"_ustr)) mAny >>= eHorizontalAlignment; - const char* sAnchor = nullptr; - bool bAnchorCtr = false; - if (sWritingMode.has_value() - && (sWritingMode.value() == "eaVert" || sWritingMode.value() == "mongolianVert")) + if (aWBPInput.sWritingMode.has_value() + && (aWBPInput.sWritingMode.value() == "eaVert" || aWBPInput.sWritingMode.value() == "mongolianVert")) { - bAnchorCtr = eVerticalAlignment == TextVerticalAdjust_CENTER + aWBPInput.bAnchorCtr = eVerticalAlignment == TextVerticalAdjust_CENTER || eVerticalAlignment == TextVerticalAdjust_BOTTOM || eVerticalAlignment == TextVerticalAdjust_BLOCK; switch (eHorizontalAlignment) { case TextHorizontalAdjust_CENTER: - sAnchor = "ctr"; + aWBPInput.sAnchor = "ctr"; break; case TextHorizontalAdjust_LEFT: - sAnchor = sWritingMode.value() == "eaVert" ? "b" : "t"; + aWBPInput.sAnchor = aWBPInput.sWritingMode.value() == "eaVert" ? "b" : "t"; break; case TextHorizontalAdjust_RIGHT: default: // TextHorizontalAdjust_BLOCK, should not happen - sAnchor = sWritingMode.value() == "eaVert" ? "t" : "b"; + aWBPInput.sAnchor = aWBPInput.sWritingMode.value() == "eaVert" ? "t" : "b"; break; } } else { - bAnchorCtr = eHorizontalAlignment == TextHorizontalAdjust_CENTER + aWBPInput.bAnchorCtr = eHorizontalAlignment == TextHorizontalAdjust_CENTER || eHorizontalAlignment == TextHorizontalAdjust_RIGHT; - sAnchor = GetTextVerticalAdjust(eVerticalAlignment); + aWBPInput.sAnchor = GetTextVerticalAdjust(eVerticalAlignment); } - bool bHasWrap = false; - bool bWrap = false; + aWBPInput.bHasWrap = false; + aWBPInput.bWrap = false; // Only custom shapes obey the TextWordWrap option, normal text always wraps. if (dynamic_cast<SvxCustomShape*>(rXIface.get()) && GetProperty(rXPropSet, u"TextWordWrap"_ustr)) { - mAny >>= bWrap; - bHasWrap = true; + mAny >>= aWBPInput.bWrap; + aWBPInput.bHasWrap = true; } // tdf#134401: If AUTOGROWWIDTH and AUTOGROWHEIGHT are set, then export it as TextWordWrap @@ -4334,222 +4559,15 @@ void DrawingML::WriteText(const Reference<XInterface>& rXIface, bool bBodyPr, bo if (rSdrTextFitWidth.GetValue() == true && rSdrTextFitHeight.GetValue() == true) { - bHasWrap = true; - bWrap = false; + aWBPInput.bHasWrap = true; + aWBPInput.bWrap = false; } } if (bBodyPr) { - const char* pWrap = (bHasWrap && !bWrap) || bIsFontworkShape ? "none" : nullptr; - if (GetDocumentType() == DOCUMENT_DOCX) - { - // In case of DOCX, if we want to have the same effect as - // TextShape's automatic word wrapping, then we need to set - // wrapping to square. - uno::Reference<lang::XServiceInfo> xServiceInfo(rXIface, uno::UNO_QUERY); - if ((xServiceInfo.is() && xServiceInfo->supportsService(u"com.sun.star.drawing.TextShape"_ustr)) - || bIsFontworkShape) - pWrap = "square"; - } - - sal_Int16 nCols = 0; - sal_Int32 nColSpacing = -1; - if (GetProperty(rXPropSet, u"TextColumns"_ustr)) - { - if (css::uno::Reference<css::text::XTextColumns> xCols{ mAny, css::uno::UNO_QUERY }) - { - nCols = xCols->getColumnCount(); - if (css::uno::Reference<css::beans::XPropertySet> xProps{ mAny, - css::uno::UNO_QUERY }) - { - if (GetProperty(xProps, u"AutomaticDistance"_ustr)) - mAny >>= nColSpacing; - } - } - } - - if (!sVertOverflow && GetProperty(rXPropSet, u"TextClipVerticalOverflow"_ustr) && mAny.get<bool>()) - { - sVertOverflow = "clip"; - } - - // tdf#151134 When writing placeholder shapes, inset must be explicitly specified - bool bRequireInset = GetProperty(rXPropSet, u"IsPresentationObject"_ustr) && rXPropSet->getPropertyValue(u"IsPresentationObject"_ustr).get<bool>(); - - mpFS->startElementNS( (nXmlNamespace ? nXmlNamespace : XML_a), XML_bodyPr, - XML_numCol, sax_fastparser::UseIf(OString::number(nCols), nCols > 0), - XML_spcCol, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nColSpacing)), nCols > 0 && nColSpacing >= 0), - XML_wrap, pWrap, - XML_horzOverflow, sHorzOverflow, - XML_vertOverflow, sVertOverflow, - XML_fromWordArt, sax_fastparser::UseIf("1", bFromWordArt), - XML_lIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nLeft)), - bRequireInset || nLeft != constDefaultLeftRightInset), - XML_tIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nTop)), - bRequireInset || nTop != constDefaultTopBottomInset), - XML_rIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nRight)), - bRequireInset || nRight != constDefaultLeftRightInset), - XML_bIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nBottom)), - bRequireInset || nBottom != constDefaultTopBottomInset), - XML_anchor, sAnchor, - XML_anchorCtr, sax_fastparser::UseIf("1", bAnchorCtr), - XML_vert, sWritingMode, - XML_upright, isUpright, - XML_rot, sTextRotateAngleMSUnit); - - if (bIsFontworkShape) - { - if (aAdjustmentSeq.hasElements()) - { - mpFS->startElementNS(XML_a, XML_prstTxWarp, XML_prst, sPresetWarp); - mpFS->startElementNS(XML_a, XML_avLst); - bool bHasTwoHandles( - sPresetWarp == "textArchDownPour" || sPresetWarp == "textArchUpPour" - || sPresetWarp == "textButtonPour" || sPresetWarp == "textCirclePour" - || sPresetWarp == "textDoubleWave1" || sPresetWarp == "textWave1" - || sPresetWarp == "textWave2" || sPresetWarp == "textWave4"); - for (sal_Int32 i = 0, nElems = aAdjustmentSeq.getLength(); i < nElems; ++i ) - { - OString sName = "adj" + (bHasTwoHandles ? OString::number(i + 1) : OString()); - double fValue(0.0); - if (aAdjustmentSeq[i].Value.getValueTypeClass() == TypeClass_DOUBLE) - aAdjustmentSeq[i].Value >>= fValue; - else - { - sal_Int32 nNumber(0); - aAdjustmentSeq[i].Value >>= nNumber; - fValue = static_cast<double>(nNumber); - } - // Convert from binary coordinate system with viewBox "0 0 21600 21600" and simple degree - // to DrawingML with coordinate range 0..100000 and angle in 1/60000 degree. - // Reverse to conversion in lcl_createPresetShape in drawingml/shape.cxx on import. - if (sPresetWarp == "textArchDown" || sPresetWarp == "textArchUp" - || sPresetWarp == "textButton" || sPresetWarp == "textCircle" - || ((i == 0) - && (sPresetWarp == "textArchDownPour" || sPresetWarp == "textArchUpPour" - || sPresetWarp == "textButtonPour" || sPresetWarp == "textCirclePour"))) - { - fValue *= 60000.0; - if (fValue < 0) - fValue += 21600000; - } - else if ((i == 1) - && (sPresetWarp == "textDoubleWave1" || sPresetWarp == "textWave1" - || sPresetWarp == "textWave2" || sPresetWarp == "textWave4")) - { - fValue = fValue / 0.216 - 50000.0; - } - else if ((i == 1) - && (sPresetWarp == "textArchDownPour" - || sPresetWarp == "textArchUpPour" - || sPresetWarp == "textButtonPour" - || sPresetWarp == "textCirclePour")) - { - fValue /= 0.108; - } - else - { - fValue /= 0.216; - } - OString sFmla = "val " + OString::number(std::lround(fValue)); - mpFS->singleElementNS(XML_a, XML_gd, XML_name, sName, XML_fmla, sFmla); - // There exists faulty Favorite shapes with one handle but two adjustment values. - if (!bHasTwoHandles) - break; - } - mpFS->endElementNS(XML_a, XML_avLst); - mpFS->endElementNS(XML_a, XML_prstTxWarp); - } - else - { - mpFS->singleElementNS(XML_a, XML_prstTxWarp, XML_prst, sPresetWarp); - } - } - else if (GetDocumentType() == DOCUMENT_DOCX) - { - // interim solution for fdo#80897, roundtrip DOCX > LO > DOCX - if (!sMSWordPresetTextWarp.isEmpty()) - mpFS->singleElementNS(XML_a, XML_prstTxWarp, XML_prst, sMSWordPresetTextWarp); - } - - if (GetDocumentType() == DOCUMENT_DOCX || GetDocumentType() == DOCUMENT_XLSX) - { - // tdf#112312: only custom shapes obey the TextAutoGrowHeight option - bool bTextAutoGrowHeight = false; - auto pSdrObjCustomShape = xShape.is() ? dynamic_cast<SdrObjCustomShape*>(SdrObject::getSdrObjectFromXShape(xShape)) : nullptr; - if (pSdrObjCustomShape && GetProperty(rXPropSet, u"TextAutoGrowHeight"_ustr)) - { - mAny >>= bTextAutoGrowHeight; - } - mpFS->singleElementNS(XML_a, (bTextAutoGrowHeight ? XML_spAutoFit : XML_noAutofit)); - } - if (GetDocumentType() == DOCUMENT_PPTX) - { - TextFitToSizeType eFit = TextFitToSizeType_NONE; - if (GetProperty(rXPropSet, u"TextFitToSize"_ustr)) - mAny >>= eFit; - - if (eFit == TextFitToSizeType_AUTOFIT) - { - const sal_Int32 MAX_SCALE_VAL = 100000; - sal_Int32 nFontScale = MAX_SCALE_VAL; - sal_Int32 nSpacingReduction = 0; - SvxShapeText* pTextShape = dynamic_cast<SvxShapeText*>(rXIface.get()); - if (pTextShape) - { - SdrTextObj* pTextObject = DynCastSdrTextObj(pTextShape->GetSdrObject()); - if (pTextObject) - { - nFontScale = sal_Int32(pTextObject->GetFontScale() * 100000.0); - nSpacingReduction = sal_Int32((1.0 - pTextObject->GetSpacingScale()) * 100000.0); - } - } - - bool bExportFontScale = false; - if (nFontScale < MAX_SCALE_VAL && nFontScale > 0) - bExportFontScale = true; - - bool bExportSpaceReduction = false; - if (nSpacingReduction < MAX_SCALE_VAL && nSpacingReduction > 0) - bExportSpaceReduction = true; - - mpFS->singleElementNS(XML_a, XML_normAutofit, - XML_fontScale, sax_fastparser::UseIf(OString::number(nFontScale), bExportFontScale), - XML_lnSpcReduction, sax_fastparser::UseIf(OString::number(nSpacingReduction), bExportSpaceReduction)); - } - else - { - bool bAutoGrowHeightEnabled = false; - const SdrObject* pObj = xShape.is() ? SdrObject::getSdrObjectFromXShape(xShape) : nullptr; - if (pObj) - { - switch (pObj->GetObjIdentifier()) - { - case SdrObjKind::NONE: - case SdrObjKind::Text: - case SdrObjKind::TitleText: - case SdrObjKind::OutlineText: - case SdrObjKind::Caption: - case SdrObjKind::CustomShape: - bAutoGrowHeightEnabled = true; - break; - default: - bAutoGrowHeightEnabled = false; - } - } - - bool bTextAutoGrowHeight = false; - if (bAutoGrowHeightEnabled && GetProperty(rXPropSet, u"TextAutoGrowHeight"_ustr)) - mAny >>= bTextAutoGrowHeight; - mpFS->singleElementNS(XML_a, (bTextAutoGrowHeight ? XML_spAutoFit : XML_noAutofit)); - } - } - - Write3DEffects( rXPropSet, /*bIsText=*/true ); - - mpFS->endElementNS((nXmlNamespace ? nXmlNamespace : XML_a), XML_bodyPr); - } + WriteBodyProps(rXIface, rXPropSet, xShape, nXmlNamespace, aWBPInput); + } // end of bodyPr Reference< XEnumerationAccess > access( xXText, UNO_QUERY ); if( !access.is() || !bText ) @@ -4564,7 +4582,7 @@ void DrawingML::WriteText(const Reference<XInterface>& rXIface, bool bBodyPr, bo if (pTxtObj && mpTextExport) { -e ... etc. - the rest is truncated
