diff options
-rw-r--r-- | include/oox/export/DMLPresetShapeExport.hxx | 138 | ||||
-rw-r--r-- | oox/Library_oox.mk | 1 | ||||
-rw-r--r-- | oox/source/export/DMLPresetShapeExport.cxx | 1287 | ||||
-rw-r--r-- | oox/source/export/shapes.cxx | 15 | ||||
-rw-r--r-- | sw/qa/extras/ooxmlexport/data/testCustomShapePresetExport.odt | bin | 0 -> 86157 bytes | |||
-rw-r--r-- | sw/qa/extras/ooxmlexport/ooxmlexport16.cxx | 31 |
6 files changed, 1471 insertions, 1 deletions
diff --git a/include/oox/export/DMLPresetShapeExport.hxx b/include/oox/export/DMLPresetShapeExport.hxx new file mode 100644 index 000000000000..1ab460d26845 --- /dev/null +++ b/include/oox/export/DMLPresetShapeExport.hxx @@ -0,0 +1,138 @@ +/* -*- 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/. +*/ + +#ifndef INCLUDED_OOX_EXPORT_DMLPRESETSHAPEXPORT_HXX +#define INCLUDED_OOX_EXPORT_DMLPRESETSHAPEXPORT_HXX + +#include <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/Sequence.hxx> + +#include <rtl/ustring.hxx> +#include <sal/types.h> + +#include <string_view> + +#include <oox/export/drawingml.hxx> + +namespace com::sun::star::beans +{ +struct PropertyValue; +} + +namespace com::sun::star::drawing +{ +class XShape; +struct EnhancedCustomShapeAdjustmentValue; +} + +namespace oox::core +{ +class XmlFilterBase; +} + +namespace oox::drawingml +{ +/// Class for exporting the custom shapes to OOXML preset ones, if possible. +/// This functionality needed for keeping the information for the office programs +/// about the shape type, and geometry data. Before these shapes were exported +/// with custom geometry, and they kept their geometry but has no information +/// about the shape itself. This lead to lost textbox size/position/padding for +/// example. +class DMLPresetShapeExporter +{ +private: + // the shape to export + css::uno::Reference<css::drawing::XShape> m_xShape; + // the DMLwriter + DrawingML* m_pDMLexporter; + // the type of the custom shape (diamond/rectangle/circle/triangle...) + OUString m_sPresetShapeType; + // True if the shape has points where its geometry can be modified + bool m_bHasHandleValues; + // The first the x the second the y coordinate, of flipping + std::pair<bool, bool> m_bIsFlipped; + + // Custom Shape Geometry information for export: + + // The adjusting values stored in this sequence: + css::uno::Sequence<css::drawing::EnhancedCustomShapeAdjustmentValue> m_AdjustmentValues; + // Shapes what have adjusting points, the range of these points + // and the index of the value stored in this sequence: + css::uno::Sequence<css::uno::Sequence<css::beans::PropertyValue>> m_HandleValues; + + //TODO: + //css::awt::Rectangle m_ViewBox; + //css::uno::Sequence<css::beans::PropertyValue> m_Path; + //css::uno::Sequence<OUString> m_Equations; + +public: + DMLPresetShapeExporter() = delete; + ~DMLPresetShapeExporter(); + + DMLPresetShapeExporter(DrawingML* pDMLExporter, + css::uno::Reference<css::drawing::XShape> xShape); + + // Writes the preset shape to the xml + bool WriteShape(); + +private: + struct AdjustmentPointValueBase + { + double nMaxVal; + double nMinVal; + double nCurrVal; + }; + + typedef AdjustmentPointValueBase RadiusAdjustmentValue; + typedef AdjustmentPointValueBase AngleAdjustmentValue; + typedef AdjustmentPointValueBase XAdjustmentValue; + typedef AdjustmentPointValueBase YAdjustmentValue; + + // Returns true, if the shape has adjusting points + bool HasHandleValue(); + + // Returns true if the shape flipped. + bool IsXFlipped() { return m_bIsFlipped.first; }; + bool IsYFlipped() { return m_bIsFlipped.second; }; + + // Returns with the shape type, like triangle for example + OUString GetShapeType(); + // Returns with the handle points + css::uno::Sequence<css::uno::Sequence<css::beans::PropertyValue>> GetHandleValues(); + // Returns with the adjustment values + css::uno::Sequence<css::drawing::EnhancedCustomShapeAdjustmentValue> GetAdjustmentValues(); + // Returns with the raw value of the given property of the shape geometry. + css::uno::Any GetHandleValueOfModificationPoint(sal_Int32 nPoint, std::u16string_view sType); + // Returns with the appropriate value of the handle point. + RadiusAdjustmentValue GetAdjustmentPointRadiusValue(sal_Int32 nPoint); + AngleAdjustmentValue GetAdjustmentPointAngleValue(sal_Int32 nPoint); + XAdjustmentValue GetAdjustmentPointXValue(sal_Int32 nPoint); + YAdjustmentValue GetAdjustmentPointYValue(sal_Int32 nPoint); + + // Writes one adjustment point. + bool WriteAV(const OUString& sValName, const OUString& sVal); + // Opens/Closes the AVlist tag. + bool StartAVListWriting(); + bool EndAVListWriting(); + + // Finds the given value in the sequence + static css::uno::Any FindHandleValue(css::uno::Sequence<css::beans::PropertyValue> aValues, + std::u16string_view sKey); + // Writes and converts the adjustment points from sdr to ooxml ones per shape type. + bool WriteShapeWithAVlist(); + +}; // end of DMLPresetShapeExporter class + +} // end of namespace oox::drawingml + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/oox/Library_oox.mk b/oox/Library_oox.mk index 287a4bb70003..6550a3942c88 100644 --- a/oox/Library_oox.mk +++ b/oox/Library_oox.mk @@ -217,6 +217,7 @@ $(eval $(call gb_Library_add_exception_objects,oox,\ oox/source/export/chartexport \ oox/source/export/ColorPropertySet \ oox/source/export/drawingml \ + oox/source/export/DMLPresetShapeExport \ oox/source/export/shapes \ oox/source/export/vmlexport \ oox/source/helper/attributelist \ diff --git a/oox/source/export/DMLPresetShapeExport.cxx b/oox/source/export/DMLPresetShapeExport.cxx new file mode 100644 index 000000000000..8e4ebafcce98 --- /dev/null +++ b/oox/source/export/DMLPresetShapeExport.cxx @@ -0,0 +1,1287 @@ +/* -*- 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 <oox/export/DMLPresetShapeExport.hxx> +#include <oox/token/tokens.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp> +#include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp> +#include <com/sun/star/drawing/XShape.hpp> + +#include <osl/diagnose.h> +#include <filter/msfilter/util.hxx> + +#include <string_view> + +using namespace ::css; +using namespace ::css::drawing; + +namespace oox::drawingml +{ +// DMLPresetShapeExporter class + +// ctor +DMLPresetShapeExporter::DMLPresetShapeExporter(DrawingML* pDMLExporter, + css::uno::Reference<css::drawing::XShape> xShape) + : m_pDMLexporter(pDMLExporter) +{ + // This class only work with custom shapes! + OSL_ASSERT(xShape->getShapeType() == "com.sun.star.drawing.CustomShape"); + + m_xShape = xShape; + m_bHasHandleValues = false; + uno::Reference<beans::XPropertySet> xShapeProps(m_xShape, uno::UNO_QUERY); + css::uno::Sequence<css::beans::PropertyValue> aCustomShapeGeometry + = xShapeProps->getPropertyValue("CustomShapeGeometry") + .get<uno::Sequence<beans::PropertyValue>>(); + + for (sal_uInt32 i = 0; i < aCustomShapeGeometry.size(); i++) + { + if (aCustomShapeGeometry[i].Name == "Type") + { + m_sPresetShapeType = aCustomShapeGeometry[i].Value.get<OUString>(); + } + if (aCustomShapeGeometry[i].Name == "Handles") + { + m_bHasHandleValues = true; + m_HandleValues + = aCustomShapeGeometry[i] + .Value + .get<css::uno::Sequence<css::uno::Sequence<css::beans::PropertyValue>>>(); + } + if (aCustomShapeGeometry[i].Name == "AdjustmentValues") + { + m_AdjustmentValues + = aCustomShapeGeometry[i] + .Value + .get<css::uno::Sequence<css::drawing::EnhancedCustomShapeAdjustmentValue>>(); + } + if (aCustomShapeGeometry[i].Name == "MirroredX") + { + m_bIsFlipped.first = aCustomShapeGeometry[i].Value.get<bool>(); + } + if (aCustomShapeGeometry[i].Name == "MirroredY") + { + m_bIsFlipped.second = aCustomShapeGeometry[i].Value.get<bool>(); + } + //if (aCustomShapeGeometry[i].Name == "Equations") + //{ + // m_Equations = aCustomShapeGeometry[i].Value.get<css::uno::Sequence<OUString>>(); + //} + //if (aCustomShapeGeometry[i].Name == "Path") + //{ + // m_Path = aCustomShapeGeometry[i] + // .Value.get<css::uno::Sequence<css::beans::PropertyValue>>(); + //} + //if (aCustomShapeGeometry[i].Name == "ViewBox") + //{ + // m_ViewBox = aCustomShapeGeometry[i].Value.get<css::awt::Rectangle>(); + //} + } +}; + +// dtor +DMLPresetShapeExporter::~DMLPresetShapeExporter(){ + // Do nothing +}; + +bool DMLPresetShapeExporter::HasHandleValue() { return m_bHasHandleValues; } + +OUString DMLPresetShapeExporter::GetShapeType() { return m_sPresetShapeType; } + +css::uno::Sequence<css::uno::Sequence<css::beans::PropertyValue>> +DMLPresetShapeExporter::GetHandleValues() +{ + return m_HandleValues; +}; + +css::uno::Sequence<css::drawing::EnhancedCustomShapeAdjustmentValue> +DMLPresetShapeExporter::GetAdjustmentValues() +{ + return m_AdjustmentValues; +}; + +css::uno::Any DMLPresetShapeExporter::GetHandleValueOfModificationPoint(sal_Int32 nPoint, + std::u16string_view sType) +{ + uno::Any aRet; + if (GetHandleValues().getLength() > nPoint) + { + for (sal_Int32 i = 0; i < GetHandleValues()[nPoint].getLength(); i++) + { + if (GetHandleValues()[nPoint][i].Name == sType) + { + aRet = GetHandleValues()[nPoint][i].Value; + break; + } + } + } + return aRet; +}; + +DMLPresetShapeExporter::RadiusAdjustmentValue +DMLPresetShapeExporter::GetAdjustmentPointRadiusValue(sal_Int32 nPoint) +{ + RadiusAdjustmentValue aRet; + auto aValPos = GetHandleValueOfModificationPoint(nPoint, u"Position") + .get<EnhancedCustomShapeParameterPair>(); + aRet.nMinVal = GetHandleValueOfModificationPoint(nPoint, u"RadiusRangeMinimum") + .get<EnhancedCustomShapeParameter>() + .Value.get<double>(); + aRet.nMaxVal = GetHandleValueOfModificationPoint(nPoint, u"RadiusRangeMaximum") + .get<EnhancedCustomShapeParameter>() + .Value.get<double>(); + aRet.nCurrVal = GetAdjustmentValues()[aValPos.First.Value.get<long>()].Value.get<double>(); + return aRet; +}; + +DMLPresetShapeExporter::AngleAdjustmentValue +DMLPresetShapeExporter::GetAdjustmentPointAngleValue(sal_Int32 nPoint) +{ + AngleAdjustmentValue aRet; + auto aValPos = GetHandleValueOfModificationPoint(nPoint, u"Position") + .get<EnhancedCustomShapeParameterPair>(); + aRet.nMinVal = 0; + aRet.nMaxVal = 360; + aRet.nCurrVal = GetAdjustmentValues()[aValPos.Second.Value.get<long>()].Value.get<double>(); + return aRet; +}; + +DMLPresetShapeExporter::XAdjustmentValue +DMLPresetShapeExporter::GetAdjustmentPointXValue(sal_Int32 nPoint) +{ + XAdjustmentValue aRet; + auto aValPos = GetHandleValueOfModificationPoint(nPoint, u"Position") + .get<EnhancedCustomShapeParameterPair>(); + aRet.nMinVal = GetHandleValueOfModificationPoint(nPoint, u"RangeXMinimum") + .get<EnhancedCustomShapeParameter>() + .Value.get<double>(); + aRet.nMaxVal = GetHandleValueOfModificationPoint(nPoint, u"RangeXMaximum") + .get<EnhancedCustomShapeParameter>() + .Value.get<double>(); + aRet.nCurrVal = GetAdjustmentValues()[aValPos.First.Value.get<long>()].Value.get<double>(); + return aRet; +}; + +DMLPresetShapeExporter::YAdjustmentValue +DMLPresetShapeExporter::GetAdjustmentPointYValue(sal_Int32 nPoint) +{ + YAdjustmentValue aRet; + auto aValPos = GetHandleValueOfModificationPoint(nPoint, u"Position") + .get<EnhancedCustomShapeParameterPair>(); + aRet.nMinVal = GetHandleValueOfModificationPoint(nPoint, u"RangeYMinimum") + .get<EnhancedCustomShapeParameter>() + .Value.get<double>(); + aRet.nMaxVal = GetHandleValueOfModificationPoint(nPoint, u"RangeYMinimum") + .get<EnhancedCustomShapeParameter>() + .Value.get<double>(); + aRet.nCurrVal = GetAdjustmentValues()[aValPos.Second.Value.get<long>()].Value.get<double>(); + return aRet; +}; + +bool DMLPresetShapeExporter::WriteShape() +{ + if (m_pDMLexporter && m_xShape) + { + // Case 1: We do not have adjustment points of the shape: just export it as preset + if (!m_bHasHandleValues) + { + OUString sShapeType = GetShapeType(); + const char* sPresetShape + = msfilter::util::GetOOXMLPresetGeometry(sShapeType.toUtf8().getStr()); + m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, IsXFlipped(), IsYFlipped(), + false, false); + m_pDMLexporter->WritePresetShape(sPresetShape); + return true; + } + else // Case2: There are adjustment points what have to be converted and exported. + { + return WriteShapeWithAVlist(); + } + } + return false; +}; + +bool DMLPresetShapeExporter::WriteAV(const OUString& sValName, const OUString& sVal) +{ + try + { + m_pDMLexporter->GetFS()->singleElementNS(XML_a, XML_gd, XML_name, sValName, XML_fmla, sVal); + return true; + } + catch (...) + { + return false; + } +}; + +bool DMLPresetShapeExporter::StartAVListWriting() +{ + try + { + const char* pShape + = msfilter::util::GetOOXMLPresetGeometry(GetShapeType().toUtf8().getStr()); + m_pDMLexporter->GetFS()->startElementNS(XML_a, XML_prstGeom, XML_prst, pShape); + m_pDMLexporter->GetFS()->startElementNS(XML_a, XML_avLst); + return true; + } + catch (...) + { + return false; + } +}; +bool DMLPresetShapeExporter::EndAVListWriting() +{ + try + { + m_pDMLexporter->GetFS()->endElementNS(XML_a, XML_avLst); + m_pDMLexporter->GetFS()->endElementNS(XML_a, XML_prstGeom); + return true; + } + catch (...) + { + return false; + } +}; + +bool DMLPresetShapeExporter::WriteShapeWithAVlist() +{ + // Remark: This method is under development. If a shape type is implemented, the corresponding, + // return must be set to true. False means nothing done true, export done. There are many + // types which do not have pairs in LO, they are do not have to be mapped, because import + // filter it does with GrabBag, this method only maps the SDR ones to OOXML shapes. + + OString sShapeType(msfilter::util::GetOOXMLPresetGeometry(GetShapeType().toUtf8().getStr())); + + // OOXML uses 60th of degree, so 360 degree is 21 600 000 60thdeg + const tools::Long nConstOfMaxDegreeOf60th = 21600000; + try + { + if (sShapeType == "accentBorderCallout1") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "accentBorderCallout2") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "accentBorderCallout3") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "accentCallout1") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "accentCallout2") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "accentCallout3") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "actionButtonBackPrevious") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "actionButtonBeginning") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "actionButtonBlank") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "actionButtonDocument") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "actionButtonEnd") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "actionButtonForwardNext") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "actionButtonHelp") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "actionButtonHome") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "actionButtonInformation") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "actionButtonMovie") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "actionButtonReturn") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "actionButtonSound") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "arc") + { + // LO does not have handle points for this, so CustGeom is enough. + return false; + } + if (sShapeType == "bentArrow") + { + // LO has only one type, which have to be rotated, without handling points + // So CustGeom enough. + return false; + } + if (sShapeType == "bentConnector2") + { + // CustGeom Enough + return false; + } + if (sShapeType == "bentConnector3") + { + // CustGeom Enough + return false; + } + if (sShapeType == "bentConnector4") + { + // CustGeom Enough + return false; + } + if (sShapeType == "bentConnector5") + { + // CustGeom Enough + return false; + } + if (sShapeType == "bentUpArrow") + { + // CustGeom Enough, no handle points + return false; + } + if (sShapeType == "bevel") + { + m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, IsXFlipped(), IsYFlipped(), + false, false); + auto aPoint1 = GetAdjustmentPointXValue(0); + tools::Long nVal1 + = std::lround(aPoint1.nCurrVal / (aPoint1.nMaxVal - aPoint1.nMinVal) * 50000); + return StartAVListWriting() + && WriteAV(u"adj", OUString(u"val " + OUString::number(nVal1))) + && EndAVListWriting(); + } + if (sShapeType == "blockArc") + { + m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, IsXFlipped(), IsYFlipped(), + false, false); + auto aPointR = GetAdjustmentPointRadiusValue(0); + auto aPointA = GetAdjustmentPointAngleValue(0); + tools::Long nVal1 + = std::lround((aPointA.nCurrVal < 0 ? 360 + aPointA.nCurrVal : aPointA.nCurrVal) + / (aPointA.nMaxVal - aPointA.nMinVal) * nConstOfMaxDegreeOf60th); + tools::Long nVal2 = std::lround( + (aPointA.nCurrVal > 180 ? 360 - aPointA.nCurrVal : 180 - aPointA.nCurrVal) + / (aPointA.nMaxVal - aPointA.nMinVal) * nConstOfMaxDegreeOf60th); + tools::Long nVal3 = std::lround( + 50000 - (aPointR.nCurrVal / (aPointR.nMaxVal - aPointR.nMinVal) * 50000)); + return StartAVListWriting() + && WriteAV(u"adj1", OUString(u"val " + OUString::number(nVal1))) + && WriteAV(u"adj2", OUString(u"val " + OUString::number(nVal2))) + && WriteAV(u"adj3", OUString(u"val " + OUString::number(nVal3))) + && EndAVListWriting(); + } + if (sShapeType == "borderCallout1") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "borderCallout2") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "borderCallout3") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "bracePair") + { + m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, IsXFlipped(), IsYFlipped(), + false, false); + auto aPoint1 = GetAdjustmentPointXValue(0); + tools::Long nVal1 + = std::lround(aPoint1.nCurrVal / (aPoint1.nMaxVal - aPoint1.nMinVal) * 25000); + return StartAVListWriting() + && WriteAV(u"adj", OUString(u"val " + OUString::number(nVal1))) + && EndAVListWriting(); + } + if (sShapeType == "bracketPair") + { + m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, IsXFlipped(), IsYFlipped(), + false, false); + auto aPoint1 = GetAdjustmentPointYValue(0); + tools::Long nVal1 + = std::lround(aPoint1.nCurrVal / (aPoint1.nMaxVal - aPoint1.nMinVal) * 50000); + return StartAVListWriting() + && WriteAV(u"adj", OUString(u"val " + OUString::number(nVal1))) + && EndAVListWriting(); + } + if (sShapeType == "callout1") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "callout2") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "callout3") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "can") + { + return false; + // Do the export as before. + } + if (sShapeType == "chartPlus") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "chartStar") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "chartX") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "chord") + { + // CustGeom, because LO does not have handle points + return false; + } + if (sShapeType == "circularArrow") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "cloud") + { + // CustGeom enough + return false; + } + if (sShapeType == "cloudCallout") + { + return false; + // Works fine without this, so export it like before. + } + if (sShapeType == "cornerTabs") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "cube") + { + // Works fine without this, so export it like before. + return false; + } + if (sShapeType == "curvedConnector2") + { + // Not necessary to be mapped + return false; + } + if (sShapeType == "curvedConnector3") + { + // Not necessary to be mapped + return false; + } + if (sShapeType == "curvedConnector4") + { + // Not necessary to be mapped + return false; + } + if (sShapeType == "curvedConnector5") + { + // Not necessary to be mapped + return false; + } + if (sShapeType == "curvedDownArrow") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "curvedLeftArrow") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "curvedRightArrow") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "curvedUpArrow") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "decagon") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "diagStripe") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "diamond") + { + // It does not have handle points so it do not have to be mapped. + return false; + } + if (sShapeType == "dodecagon") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "donut") + { + // TODO + return false; + } + if (sShapeType == "doubleWave") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "downArrow") + { + // TODO + return false; + } + if (sShapeType == "downArrowCallout") + { + // TODO + return false; + } + if (sShapeType == "ellipse") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "ellipseRibbon") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "ellipseRibbon2") + { + // LO does not have this type, so it does not necessary to be mapped. + return false; + } + if (sShapeType == "flowChartAlternateProcess") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartCollate") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartConnector") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartDecision") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartDecision") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartDelay") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartDisplay") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartDocument") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartExtract") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartInputOutput") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartInternalStorage") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartMagneticDisk") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartMagneticDrum") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartMagneticTape") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartManualInput") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartManualOperation") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartMerge") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartMultidocument") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartOfflineStorage") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartOffpageConnector") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartOnlineStorage") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartOr") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartDecision") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartPredefinedProcess") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartPreparation") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartPunchedCard") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartPunchedTape") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartSort") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartSummingJunction") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "flowChartTerminator") + { + // Does not have handle points, so preset enough. + return false; + } + if (sShapeType == "foldedCorner") + { + // TODO + return false; + } + if (sShapeType == "frame") + { + // TODO + return false; + } + if (sShapeType == "funnel") + { + // Not found in word + return false; + } + if (sShapeType == "gear6") + { + // Not found in word + return false; + } + if (sShapeType == "gear9") + { + // Not found in word + return false; + } + if (sShapeType == "halfFrame") + { + // LO does not have this type, not necessary to map + return false; + } + if (sShapeType == "heart") + { + // TODO + return false; + } + if (sShapeType == "heptagon") + { + // LO does not have this type, not necessary to map + return false; + } + if (sShapeType == "hexagon") + { + m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, IsXFlipped(), IsYFlipped(), + false, false); + auto aPoint1 = GetAdjustmentPointXValue(0); + tools::Long nMaxVal = 50000 * m_xShape->getSize().Width + / std::min(m_xShape->getSize().Width, m_xShape->getSize().Height); + tools::Long nVal1 + = std::lround(aPoint1.nCurrVal / (aPoint1.nMaxVal - aPoint1.nMinVal) * nMaxVal); + return StartAVListWriting() + && WriteAV(u"adj", OUString(u"val " + OUString::number(nVal1))) + && WriteAV(u"vf", OUString(u"val " + OUString::number(115470))) + && EndAVListWriting(); + } + if (sShapeType == "homePlate") + { + // Not found in word + return false; + } + if (sShapeType == "horizontalScroll") + { + // TODO + return false; + } + if (sShapeType == "irregularSeal1") + { + // Not found in word + return false; + } + if (sShapeType == "irregularSeal2") + { + // Not found in word + return false; + } + if (sShapeType == "leftArrow") + { + // TODO + return false; + } + if (sShapeType == "leftArrowCallout") + { + // TODO + return false; + } + if (sShapeType == "leftBrace") + { + // TODO + return false; + } + if (sShapeType == "leftBracket") + { + // TODO + return false; + } + if (sShapeType == "leftCircularArrow") + { + // TODO + return false; + } + if (sShapeType == "leftRightArrow") + { + // TODO + return false; + } + if (sShapeType == "leftRightArrowCallout") + { + // TODO + return false; + } + if (sShapeType == "leftRightCircularArrow") + { + // Not found in word + return false; + } + if (sShapeType == "leftRightRibbon") + { + // LO does not have this type so mapping not necessary + return false; + } + if (sShapeType == "leftRightUpArrow") + { + // TODO + return false; + } + if (sShapeType == "leftUpArrow") + { + // TODO + return false; + } + if (sShapeType == "lightningBolt") + { + // Difference between the SDR and OOXML variants, custgeom? + return false; + } + if (sShapeType == "line") + { + // Not necessary + return false; + } + if (sShapeType == "lineInv") + { + // Not necessary + return false; + } + if (sShapeType == "mathDivide") + { + // LO does not have this type so mapping not necessary + return false; + } + if (sShapeType == "mathEqual") + { + // LO does not have this type so mapping not necessary + return false; + } + if (sShapeType == "mathMinus") + { + // LO does not have this type so mapping not necessary + return false; + } + if (sShapeType == "mathMultiply") + { + // LO does not have this type so mapping not necessary + return false; + } + if (sShapeType == "mathNotEqual") + { + // LO does not have this type so mapping not necessary + return false; + } + if (sShapeType == "mathPlus") + { + // LO does not have this type so mapping not necessary + return false; + } + if (sShapeType == "nonIsoscelesTrapezoid") + { + // TODO + return false; + } + if (sShapeType == "noSmoking") + { + // TODO + return false; + } + if (sShapeType == "notchedRightArrow") + { + // TODO + return false; + } + if (sShapeType == "octagon") + { + m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, IsXFlipped(), IsYFlipped(), + false, false); + auto aPoint1 = GetAdjustmentPointXValue(0); + tools::Long nVal1 + = std::lround(aPoint1.nCurrVal / (aPoint1.nMaxVal - aPoint1.nMinVal) * 50000); + return StartAVListWriting() + && WriteAV(u"adj", OUString(u"val " + OUString::number(nVal1))) + && EndAVListWriting(); + } + if (sShapeType == "parallelogram") + { + m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, IsXFlipped(), IsYFlipped(), + false, false); + auto aPoint1 = GetAdjustmentPointXValue(0); + tools::Long nMaxVal = 100000 * m_xShape->getSize().Width + / std::min(m_xShape->getSize().Width, m_xShape->getSize().Height); + tools::Long nVal1 + = std::lround(aPoint1.nCurrVal / (aPoint1.nMaxVal - aPoint1.nMinVal) * nMaxVal); + return StartAVListWriting() + && WriteAV(u"adj", OUString(u"val " + OUString::number(nVal1))) + && EndAVListWriting(); + } + if (sShapeType == "pentagon") + { + // TODO + return false; + } + if (sShapeType == "pie") + { + // TODO + return false; + } + if (sShapeType == "pieWedge") + { + // Not found in word. + return false; + } + if (sShapeType == "plaque") + { + // TODO + return false; + } + if (sShapeType == "plaqueTabs") + { + // LO does not have this, so not necessary to map. + return false; + } + if (sShapeType == "plus") + { + m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, IsXFlipped(), IsYFlipped(), + false, false); + auto aPoint1 = GetAdjustmentPointXValue(0); + tools::Long nVal1 + = std::lround(aPoint1.nCurrVal / (aPoint1.nMaxVal - aPoint1.nMinVal) * 50000); + return StartAVListWriting() + && WriteAV(u"adj", OUString(u"val " + OUString::number(nVal1))) + && EndAVListWriting(); + } + if (sShapeType == "quadArrow") + { + // TODO + return false; + } + if (sShapeType == "quadArrowCallout") + { + // TODO + return false; + } + if (sShapeType == "rect") + { + // preset enough without AV points. + return false; + } + if (sShapeType == "ribbon") + { + // LO does not have this, so not necessary to map. + return false; + } + if (sShapeType == "ribbon2") + { + // LO does not have this, so not necessary to map. + return false; + } + if (sShapeType == "rightArrow") + { + // TODO + return false; + } + if (sShapeType == "rightArrowCallout") + { + // TODO + return false; + } + if (sShapeType == "rightBrace") + { + // TODO + return false; + } + if (sShapeType == "rightBracket") + { + // TODO + return false; + } + if (sShapeType == "round1Rect") + { + // LO does not have this, so not necessary to map. + return false; + } + if (sShapeType == "round2DiagRect") + { + // LO does not have this, so not necessary to map. + return false; + } + if (sShapeType == "round2SameRect") + { + // LO does not have this, so not necessary to map. + return false; + } + if (sShapeType == "roundRect") + { + m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, IsXFlipped(), IsYFlipped(), + false, false); + tools::Long nVal1 = 0; + if (m_xShape->getSize().Width >= m_xShape->getSize().Height) + { + auto aPointX = GetAdjustmentPointXValue(0); + nVal1 = std::lround(aPointX.nCurrVal / (aPointX.nMaxVal - aPointX.nMinVal) * 50000); + } + else + { + auto aPointY = GetAdjustmentPointYValue(0); + nVal1 = std::lround(aPointY.nCurrVal / (aPointY.nMaxVal - aPointY.nMinVal) * 50000); + } + return StartAVListWriting() + && WriteAV(u"adj", OUString(u"val " + OUString::number(nVal1))) + && EndAVListWriting(); + } + if (sShapeType == "rtTriangle") + { + // Does not have AV points not necessary to map + return false; + } + if (sShapeType == "smileyFace") + { + // TODO + return false; + } + if (sShapeType == "snip1Rect") + { + // LO does not have this, so not necessary to map. + return false; + } + if (sShapeType == "snip2DiagRect") + { + // LO does not have this, so not necessary to map. + return false; + } + if (sShapeType == "snip2SameRect") + { + // LO does not have this, so not necessary to map. + return false; + } + if (sShapeType == "snipRoundRect") + { + // LO does not have this, so not necessary to map. + return false; + } + if (sShapeType == "squareTabs") + { + // LO does not have this, so not necessary to map. + return false; + } + if (sShapeType == "star10") + { + // LO does not have this, so not necessary to map. + return false; + } + if (sShapeType == "star12") + { + // TODO + return false; + } + if (sShapeType == "star16") + { + // LO does not have this, so not necessary to map. + return false; + } + if (sShapeType == "star24") + { + // TODO + return false; + } + if (sShapeType == "star32") + { + // LO does not have this, so not necessary to map. + return false; + } + if (sShapeType == "star4") + { + // TODO + return false; + } + if (sShapeType == "star5") + { + // TODO + return false; + } + if (sShapeType == "star6") + { + // TODO + return false; + } + if (sShapeType == "star7") + { + // LO does not have this, so not necessary to map. + return false; + } + if (sShapeType == "star8") + { + // TODO + return false; + } + if (sShapeType == "straightConnector1") + { + // Not necessary to map. + return false; + } + if (sShapeType == "stripedRightArrow") + { + // TODO + return false; + } + if (sShapeType == "sun") + { + // TODO + return false; + } + if (sShapeType == "swooshArrow") + { + // Not found in word. + return false; + } + if (sShapeType == "teardrop") + { + // TODO + return false; + } + if (sShapeType == "trapezoid") + { + // Preset enough. + return false; + } + if (sShapeType == "triangle") + { + m_pDMLexporter->WriteShapeTransformation(m_xShape, XML_a, IsXFlipped(), IsYFlipped(), + false, false); + auto aPoint1 = GetAdjustmentPointXValue(0); + tools::Long nMaxVal = 100000; + tools::Long nVal1 + = std::lround(aPoint1.nCurrVal / (aPoint1.nMaxVal - aPoint1.nMinVal) * nMaxVal); + return StartAVListWriting() + && WriteAV(u"adj", OUString(u"val " + OUString::number(nVal1))) + && EndAVListWriting(); + } + if (sShapeType == "upArrowCallout") + { + // TODO + return false; + } + if (sShapeType == "upDownArrow") + { + // TODO + return false; + } + if (sShapeType == "upArrow") + { + // TODO + return false; + } + if (sShapeType == "upDownArrowCallout") + { + // TODO + return false; + } + if (sShapeType == "uturnArrow") + { + // LO does not have like this. + return false; + } + if (sShapeType == "verticalScroll") + { + // TODO + return false; + } + if (sShapeType == "wave") + { + // LO does not have. + return false; + } + if (sShapeType == "wedgeEllipseCallout") + { + // TODO + return false; + } + if (sShapeType == "wedgeRectCallout") + { + // TODO + return false; + } + if (sShapeType == "wedgeRoundRectCallout") + { + // TODO + return false; + } + } + catch (...) + { + // Problem detected with the writing, aborting and trying to find another way. + return false; + } + + // Default, nothing happened return. + return false; +}; +} diff --git a/oox/source/export/shapes.cxx b/oox/source/export/shapes.cxx index f45b0aabc61d..d52d6696be2a 100644 --- a/oox/source/export/shapes.cxx +++ b/oox/source/export/shapes.cxx @@ -73,6 +73,7 @@ #include <oox/export/chartexport.hxx> #include <oox/mathml/export.hxx> #include <basegfx/numeric/ftools.hxx> +#include <oox/export/DMLPresetShapeExport.hxx> using namespace ::css; using namespace ::css::beans; @@ -832,7 +833,19 @@ ShapeExport& ShapeExport::WriteCustomShape( const Reference< XShape >& xShape ) else if( bHasHandles ) bCustGeom = true; - if (bHasHandles && bCustGeom) + bool bPresetWriteSuccessful = false; + // Let the custom shapes what has name and preset information in OOXML, to be written + // as preset ones with parameters. Try that with this converter class. + if (!sShapeType.startsWith("ooxml") && GetDocumentType() == DOCUMENT_DOCX + && xShape->getShapeType() == "com.sun.star.drawing.CustomShape") + { + DMLPresetShapeExporter aCustomShapeConverter(this, xShape); + bPresetWriteSuccessful = aCustomShapeConverter.WriteShape(); + } + // If preset writing has problems try to write the shape as it done before + if (bPresetWriteSuccessful) + ;// Already written do nothing. + else if (bHasHandles && bCustGeom) { WriteShapeTransformation( xShape, XML_a, bFlipH, bFlipV, false, true );// do not flip, polypolygon coordinates are flipped already tools::PolyPolygon aPolyPolygon( rSdrObjCustomShape.GetLineGeometry(true) ); diff --git a/sw/qa/extras/ooxmlexport/data/testCustomShapePresetExport.odt b/sw/qa/extras/ooxmlexport/data/testCustomShapePresetExport.odt Binary files differnew file mode 100644 index 000000000000..4f132e760460 --- /dev/null +++ b/sw/qa/extras/ooxmlexport/data/testCustomShapePresetExport.odt diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx index 5666c3f1b1d1..2b3d92b55701 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx @@ -129,6 +129,37 @@ DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testGutterTop, "gutter-top.docx") assertXPath(pXmlSettings, "/w:settings/w:gutterAtTop", 1); } +DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testCustomShapePresetExport, "testCustomShapePresetExport.odt") +{ + // Check if the load failed. + CPPUNIT_ASSERT(getPages()); + + // Check all shapes of the file + int nCount = 0; + for (int i = 1; i <= getShapes(); i++) + { + uno::Reference<beans::XPropertySet> xProperties(getShape(i), uno::UNO_QUERY); + if (!xProperties->getPropertySetInfo()->hasPropertyByName("CustomShapeGeometry")) + continue; + // Get the custom shape property + auto aCustomShapeGeometry = xProperties->getPropertyValue("CustomShapeGeometry") + .get<uno::Sequence<beans::PropertyValue>>(); + // Find for shape type + for (const auto& aCustomGeometryIterator : std::as_const(aCustomShapeGeometry)) + { + if (aCustomGeometryIterator.Name == "Type") + CPPUNIT_ASSERT_MESSAGE( + "This is an ooxml preset shape with custom geometry! Shape type lost!", + aCustomGeometryIterator.Value.get<OUString>() != "ooxml-non-primitive"); + // Without the fix, all shapes have ooxml-non-primitive type, and lost their + // real type (like triangle) with the textbox padding. + } + nCount++; + } + // Without the fix the count does not match. + CPPUNIT_ASSERT_EQUAL(17, nCount); +} + DECLARE_OOXMLEXPORT_EXPORTONLY_TEST(testTdf69635, "tdf69635.docx") { xmlDocUniquePtr pXmlHeader1 = parseExport("word/header1.xml"); |