tdf#92525 tdf#142398: fix export of simple custom shapes
Most of the custom shapes were exported with DrawingML custom
geometry instead of DOCX preset shapes, losing their preset type,
adjust handles and text position. Add a preset exporter class to
handle all possible preset shapes later, and fix the export of
the following shapes now: "bevel", "blockArc", "bracePair",
"bracketPair", "hexagon", "octagon", "parallelogram", "plus",
"roundRect" and "triangle".
tdf#92525: keep text position in triangles with different
adjustments, too.
tdf#142398: part 1: export simple shapes as preset shapes.
Change-Id: I6aee74f7670bea8c1fe5909cbf307778ea728669
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/115606
Tested-by: László Németh <nemeth@numbertext.org>
Reviewed-by: László Németh <nemeth@numbertext.org>
diff --git a/include/oox/export/DMLPresetShapeExport.hxx b/include/oox/export/DMLPresetShapeExport.hxx
new file mode 100644
index 0000000..1ab460d
--- /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 287a4bb..6550a394 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 0000000..8e4ebaf
--- /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 323b0eb..3273217 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
new file mode 100644
index 0000000..4f132e7
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/testCustomShapePresetExport.odt
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
index 5d56754..dcaa4e6 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");