summaryrefslogtreecommitdiff
path: root/oox
diff options
context:
space:
mode:
authorRegina Henschel <rb.henschel@t-online.de>2022-04-04 01:55:29 +0200
committerRegina Henschel <rb.henschel@t-online.de>2022-04-04 20:21:14 +0200
commit1ad58c77352e418124387b804b18da2aeea22c8b (patch)
tree34450806390b65778b7a16239a0421614e9b668a /oox
parent0225f1dd04e49191bbf1fa787bb15d1617ab996c (diff)
tdf#100391 calculate true textarea rect for custGeom
Without the fix the attributes for <a:rect> were set to 'l', 't', 'r' and 'b'. That means that the textarea rectangle equals the shape rectangle. That is the default and works for many shapes. But 'Puzzle' has a smaller textarea rectangle, for example. Because the values in draw:text-areas are relative to the internal coordinate system given by draw:viewBox in ODF, but the values in <a:rect> are relative to the shape coordinate system in OOXML, we cannot simple write the current absolute values but need to calculate them depending on actual width and height. For that we need guides. The patch introduces a guide list. Currently the list contains only the guides for the textarea rectangle, but it can be extended when export of handles will be implemented one day. Change-Id: I1050627ef6459ab5f8fafa939d7905122870c903 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132489 Tested-by: Jenkins Reviewed-by: Regina Henschel <rb.henschel@t-online.de>
Diffstat (limited to 'oox')
-rwxr-xr-xoox/qa/unit/data/tdf100391_TextAreaRect.odpbin0 -> 13624 bytes
-rw-r--r--oox/qa/unit/export.cxx21
-rw-r--r--oox/source/export/drawingml.cxx98
3 files changed, 116 insertions, 3 deletions
diff --git a/oox/qa/unit/data/tdf100391_TextAreaRect.odp b/oox/qa/unit/data/tdf100391_TextAreaRect.odp
new file mode 100755
index 000000000000..b9b9e5b39e4a
--- /dev/null
+++ b/oox/qa/unit/data/tdf100391_TextAreaRect.odp
Binary files differ
diff --git a/oox/qa/unit/export.cxx b/oox/qa/unit/export.cxx
index 60e20e2d933a..91b8d7608c24 100644
--- a/oox/qa/unit/export.cxx
+++ b/oox/qa/unit/export.cxx
@@ -590,6 +590,27 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf147978_subpath)
assertXPath(pXmlDoc, "//a:pathLst/a:path[4]", "w", "80");
assertXPath(pXmlDoc, "//a:pathLst/a:path[4]", "h", "80");
}
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf100391TextAreaRect)
+{
+ // The document has a custom shape of type "non-primitive" to trigger the custGeom export
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf100391_TextAreaRect.odp";
+ // When saving to PPTX the textarea rect was set to default instead of using the actual area
+ loadAndSave(aURL, "Impress Office Open XML");
+
+ // Verify the markup. Without fix the values were l="l", t="t", r="r", b="b"
+ std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "ppt/slides/slide1.xml");
+ xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+ assertXPath(pXmlDoc, "//a:custGeom/a:rect", "l", "textAreaLeft");
+ assertXPath(pXmlDoc, "//a:custGeom/a:rect", "t", "textAreaTop");
+ assertXPath(pXmlDoc, "//a:custGeom/a:rect", "r", "textAreaRight");
+ assertXPath(pXmlDoc, "//a:custGeom/a:rect", "b", "textAreaBottom");
+ // The values are calculated in guides, for example
+ assertXPath(pXmlDoc, "//a:custGeom/a:gdLst/a:gd[1]", "name", "textAreaLeft");
+ assertXPath(pXmlDoc, "//a:custGeom/a:gdLst/a:gd[1]", "fmla", "*/ 1440000 w 2880000");
+ // The test reflects the state of Apr 2022. It needs to be adapted when export of handles and
+ // guides is implemented.
+}
}
CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx
index 54f94278d782..1d2d601a40f0 100644
--- a/oox/source/export/drawingml.cxx
+++ b/oox/source/export/drawingml.cxx
@@ -3857,6 +3857,83 @@ sal_Int32 GetCustomGeometryPointValue(const css::drawing::EnhancedCustomShapePar
return nValue;
}
+
+struct TextAreaRect
+{
+ OString left;
+ OString top;
+ OString right;
+ OString bottom;
+};
+
+struct Guide
+{
+ OString sName;
+ OString sFormula;
+};
+
+void prepareTextArea(const EnhancedCustomShape2d& rEnhancedCustomShape2d,
+ std::vector<Guide>& rGuideList, TextAreaRect& rTextAreaRect)
+{
+ tools::Rectangle aTextAreaLO(rEnhancedCustomShape2d.GetTextRect());
+ tools::Rectangle aLogicRectLO(rEnhancedCustomShape2d.GetLogicRect());
+ if (aTextAreaLO == aLogicRectLO)
+ {
+ rTextAreaRect.left = "l";
+ rTextAreaRect.top = "t";
+ rTextAreaRect.right = "r";
+ rTextAreaRect.bottom = "b";
+ return;
+ }
+ // Flip aTextAreaLO if shape is flipped
+ if (rEnhancedCustomShape2d.IsFlipHorz())
+ aTextAreaLO.Move((aLogicRectLO.Center().X() - aTextAreaLO.Center().X()) * 2, 0);
+ if (rEnhancedCustomShape2d.IsFlipVert())
+ aTextAreaLO.Move(0, (aLogicRectLO.Center().Y() - aTextAreaLO.Center().Y()) * 2);
+
+ Guide aGuide;
+ // horizontal
+ const sal_Int32 nWidth = aLogicRectLO.Right() - aLogicRectLO.Left();
+ const OString sWidth = OString::number(oox::drawingml::convertHmmToEmu(nWidth));
+
+ // left
+ aGuide.sName = "textAreaLeft";
+ sal_Int32 nHelp = aTextAreaLO.Left() - aLogicRectLO.Left();
+ const OString sLeft = OString::number(oox::drawingml::convertHmmToEmu(nHelp));
+ aGuide.sFormula = "*/ " + sLeft + " w " + sWidth;
+ rTextAreaRect.left = aGuide.sName;
+ rGuideList.push_back(aGuide);
+
+ // right
+ aGuide.sName = "textAreaRight";
+ nHelp = aTextAreaLO.Right() - aLogicRectLO.Left();
+ const OString sRight = OString::number(oox::drawingml::convertHmmToEmu(nHelp));
+ aGuide.sFormula = "*/ " + sRight + " w " + sWidth;
+ rTextAreaRect.right = aGuide.sName;
+ rGuideList.push_back(aGuide);
+
+ // vertical
+ const sal_Int32 nHeight = aLogicRectLO.Bottom() - aLogicRectLO.Top();
+ const OString sHeight = OString::number(oox::drawingml::convertHmmToEmu(nHeight));
+
+ // top
+ aGuide.sName = "textAreaTop";
+ nHelp = aTextAreaLO.Top() - aLogicRectLO.Top();
+ const OString sTop = OString::number(oox::drawingml::convertHmmToEmu(nHelp));
+ aGuide.sFormula = "*/ " + sTop + " h " + sHeight;
+ rTextAreaRect.top = aGuide.sName;
+ rGuideList.push_back(aGuide);
+
+ // bottom
+ aGuide.sName = "textAreaBottom";
+ nHelp = aTextAreaLO.Bottom() - aLogicRectLO.Top();
+ const OString sBottom = OString::number(oox::drawingml::convertHmmToEmu(nHelp));
+ aGuide.sFormula = "*/ " + sBottom + " h " + sHeight;
+ rTextAreaRect.bottom = aGuide.sName;
+ rGuideList.push_back(aGuide);
+
+ return;
+}
}
bool DrawingML::WriteCustomGeometry(
@@ -3933,12 +4010,27 @@ bool DrawingML::WriteCustomGeometry(
// entire method.
const EnhancedCustomShape2d aCustomShape2d(const_cast<SdrObjCustomShape&>(rSdrObjCustomShape));
+ TextAreaRect aTextAreaRect;
+ std::vector<Guide> aGuideList; // for now only for <a:rect>
+ prepareTextArea(aCustomShape2d, aGuideList, aTextAreaRect);
mpFS->startElementNS(XML_a, XML_custGeom);
mpFS->singleElementNS(XML_a, XML_avLst);
- mpFS->singleElementNS(XML_a, XML_gdLst);
+ if (aGuideList.empty())
+ {
+ mpFS->singleElementNS(XML_a, XML_gdLst);
+ }
+ else
+ {
+ mpFS->startElementNS(XML_a, XML_gdLst);
+ for (auto const& elem: aGuideList)
+ {
+ mpFS->singleElementNS(XML_a, XML_gd, XML_name, elem.sName, XML_fmla, elem.sFormula);
+ }
+ mpFS->endElementNS(XML_a, XML_gdLst);
+ }
mpFS->singleElementNS(XML_a, XML_ahLst);
- // ToDO: use draw:TextAreas for <a:rect>
- mpFS->singleElementNS(XML_a, XML_rect, XML_l, "l", XML_t, "t", XML_r, "r", XML_b, "b");
+ mpFS->singleElementNS(XML_a, XML_rect, XML_l, aTextAreaRect.left, XML_t, aTextAreaRect.top,
+ XML_r, aTextAreaRect.right, XML_b, aTextAreaRect.bottom);
mpFS->startElementNS(XML_a, XML_pathLst);
// Prepare width and height for <a:path>