From 82189fdc93ac337e1de3379d678eca6b7654e6fc Mon Sep 17 00:00:00 2001 From: László Németh Date: Mon, 17 Aug 2020 14:00:54 +0200 Subject: tdf133647 tdf123386 tdf123389 fix DOCX table formula export MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep original DOCX table formula during round-trip using grab-bagging. This is a temporary solution until fixing formula export and a proposed solution for formula cannot be converted. Follow-up of commit 68e74bdf63e992666016c790e8e4cfd5b28d6abe (tdf133647 tdf123386 tdf123389 Improved .docx table formula import). Change-Id: Ia4759e250c06e9cc0495fb0b57fccd1ee1f50da9 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/100872 Tested-by: Jenkins Reviewed-by: László Németh --- sw/qa/extras/ooxmlexport/data/tdf123386.docx | Bin 0 -> 14928 bytes sw/qa/extras/ooxmlexport/data/tdf123389.docx | Bin 0 -> 13649 bytes sw/qa/extras/ooxmlexport/data/tdf133647.docx | Bin 0 -> 14536 bytes sw/qa/extras/ooxmlexport/ooxmlexport.cxx | 46 ++++++++++++++++++++++ sw/source/filter/ww8/docxattributeoutput.cxx | 35 +++++++++++++--- writerfilter/Library_writerfilter.mk | 6 +-- writerfilter/source/dmapper/DomainMapper_Impl.cxx | 8 ++++ writerfilter/source/dmapper/PropertyIds.cxx | 2 + writerfilter/source/dmapper/PropertyIds.hxx | 2 + 9 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 sw/qa/extras/ooxmlexport/data/tdf123386.docx create mode 100644 sw/qa/extras/ooxmlexport/data/tdf123389.docx create mode 100644 sw/qa/extras/ooxmlexport/data/tdf133647.docx diff --git a/sw/qa/extras/ooxmlexport/data/tdf123386.docx b/sw/qa/extras/ooxmlexport/data/tdf123386.docx new file mode 100644 index 000000000000..1278068ddedf Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf123386.docx differ diff --git a/sw/qa/extras/ooxmlexport/data/tdf123389.docx b/sw/qa/extras/ooxmlexport/data/tdf123389.docx new file mode 100644 index 000000000000..4245464b820d Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf123389.docx differ diff --git a/sw/qa/extras/ooxmlexport/data/tdf133647.docx b/sw/qa/extras/ooxmlexport/data/tdf133647.docx new file mode 100644 index 000000000000..fb525446c7fc Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf133647.docx differ diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport.cxx index 017fa21a6dec..e0ce31a44d1b 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport.cxx @@ -1009,6 +1009,52 @@ DECLARE_OOXMLEXPORT_TEST(testTdf133163, "tdf133163.fodt") assertXPathContent(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[4]/w:tc/w:p/w:r[2]/w:instrText", " = SUM(A1:A3)"); } +DECLARE_OOXMLEXPORT_TEST(testTdf133647, "tdf133647.docx") +{ + xmlDocUniquePtr pXmlDoc = parseExport(); + if (!pXmlDoc) + return; + + // Keep original formula during round-trip + assertXPathContent(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[4]/w:tc[4]/w:p/w:r[2]/w:instrText", " = SUM(A1,B1)"); + assertXPathContent(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[5]/w:tc[4]/w:p/w:r[2]/w:instrText", " = SUM(C1:D1)"); + assertXPathContent(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[6]/w:tc[4]/w:p/w:r[2]/w:instrText", " = SUM(A1,5,B1:C1,6)"); + assertXPathContent(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[7]/w:tc[4]/w:p/w:r[2]/w:instrText", " = (1+2)*SUM(C1,D1)"); + assertXPathContent(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[8]/w:tc[4]/w:p/w:r[2]/w:instrText", " = 3*(2+SUM(A1:C1)+7)"); + assertXPathContent(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[9]/w:tc[4]/w:p/w:r[2]/w:instrText", " = 1+(SUM(1,2))"); + assertXPathContent(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[10]/w:tc[4]/w:p/w:r[2]/w:instrText", " = (SUM(C1,5)*(2+7))*(3+SUM(1,B1))"); + assertXPathContent(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[11]/w:tc[4]/w:p/w:r[2]/w:instrText", " = sum(a1,b1)"); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf123386, "tdf123386.docx") +{ + xmlDocUniquePtr pXmlDoc = parseExport(); + if (!pXmlDoc) + return; + + // Keep original formula during round-trip + assertXPathContent(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[3]/w:tc[4]/w:p/w:r[2]/w:instrText", " = A1 < 2"); + assertXPathContent(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[4]/w:tc[4]/w:p/w:r[2]/w:instrText", " = B1 > 1"); + assertXPathContent(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[5]/w:tc[4]/w:p/w:r[2]/w:instrText", " = C1=3"); + assertXPathContent(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[6]/w:tc[4]/w:p/w:r[2]/w:instrText", " = D1 <> 3"); + assertXPathContent(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[7]/w:tc[4]/w:p/w:r[2]/w:instrText", " = AND(A1=1,B1=2)"); + assertXPathContent(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[8]/w:tc[4]/w:p/w:r[2]/w:instrText", " = AND((A1<1),(B1<>2))"); + assertXPathContent(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[9]/w:tc[4]/w:p/w:r[2]/w:instrText", " = OR(A1=1,B1=2)"); + assertXPathContent(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[10]/w:tc[4]/w:p/w:r[2]/w:instrText", " = OR(TRUE,FALSE)"); + assertXPathContent(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[11]/w:tc[4]/w:p/w:r[2]/w:instrText", " = NOT(TRUE)"); + assertXPathContent(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[12]/w:tc[4]/w:p/w:r[2]/w:instrText", " = AND(1,DEFINED(ABC1))"); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf123389, "tdf123389.docx") +{ + xmlDocUniquePtr pXmlDoc = parseExport(); + if (!pXmlDoc) + return; + + // Keep original formula during round-trip + assertXPathContent(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[3]/w:tc[4]/w:p/w:r[2]/w:instrText", " = ROUND(2.345,1)"); + assertXPathContent(pXmlDoc, "/w:document/w:body/w:tbl/w:tr[4]/w:tc[4]/w:p/w:r[2]/w:instrText", " = ROUND(A1,2)"); +} DECLARE_OOXMLEXPORT_TEST(testTdf106953, "tdf106953.docx") { diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index fd3c9aeb360b..81760cc044ab 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -2150,11 +2150,36 @@ void DocxAttributeOutput::CmdField_Impl( const SwTextNode* pNode, sal_Int32 nPos } else if ( rInfos.eType == ww::eEquals ) { - UErrorCode nErr(U_ZERO_ERROR); - icu::UnicodeString sInput(sToken.getStr()); - // remove < and > around cell references, e.g. to A1, to A1:B2 - icu::RegexMatcher aMatcher("<([A-Z]{1,3}[0-9]+(:[A-Z]{1,3}[0-9]+)?)>", sInput, 0, nErr); - sToken = aMatcher.replaceAll(icu::UnicodeString("$1"), nErr).getTerminatedBuffer(); + // Use original OOXML formula, if it exists and its conversion hasn't been changed + bool bIsChanged = true; + if ( pNode->GetTableBox() ) + { + if ( const SfxGrabBagItem* pItem = pNode->GetTableBox()->GetFrameFormat()->GetAttrSet().GetItem(RES_FRMATR_GRABBAG) ) + { + OUString sActualFormula = sToken.trim(); + const std::map& rGrabBag = pItem->GetGrabBag(); + std::map::const_iterator aStoredFormula = rGrabBag.find("CellFormulaConverted"); + if ( aStoredFormula != rGrabBag.end() && sActualFormula.indexOf('=') == 0 && + sActualFormula.copy(1).trim() == aStoredFormula->second.get().trim() ) + { + aStoredFormula = rGrabBag.find("CellFormula"); + if ( aStoredFormula != rGrabBag.end() ) + { + sToken = " = " + aStoredFormula->second.get(); + bIsChanged = false; + } + } + } + } + + if ( bIsChanged ) + { + UErrorCode nErr(U_ZERO_ERROR); + icu::UnicodeString sInput(sToken.getStr()); + // remove < and > around cell references, e.g. to A1, to A1:B2 + icu::RegexMatcher aMatcher("<([A-Z]{1,3}[0-9]+(:[A-Z]{1,3}[0-9]+)?)>", sInput, 0, nErr); + sToken = aMatcher.replaceAll(icu::UnicodeString("$1"), nErr).getTerminatedBuffer(); + } } // Write the Field command diff --git a/writerfilter/Library_writerfilter.mk b/writerfilter/Library_writerfilter.mk index cbda03e9dcdd..add7c20bb2ea 100644 --- a/writerfilter/Library_writerfilter.mk +++ b/writerfilter/Library_writerfilter.mk @@ -51,9 +51,9 @@ $(eval $(call gb_Library_use_libraries,writerfilter,\ $(eval $(call gb_Library_use_externals,writerfilter,\ boost_headers \ - icui18n \ - icuuc \ - icu_headers \ + icui18n \ + icuuc \ + icu_headers \ libxml2 \ )) diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx index 4e8afddc8d50..1d23295fd759 100644 --- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx +++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx @@ -4286,6 +4286,14 @@ void DomainMapper_Impl::handleFieldFormula // we don't copy the = symbol from the command OUString formula = convertFieldFormula(command.copy(1)); + // grab-bag the original and converted formula + if (getTableManager().isInTable()) + { + TablePropertyMapPtr pPropMap(new TablePropertyMap()); + pPropMap->Insert(PROP_CELL_FORMULA, uno::makeAny(command.copy(1)), true, CELL_GRAB_BAG); + pPropMap->Insert(PROP_CELL_FORMULA_CONVERTED, uno::makeAny(formula), true, CELL_GRAB_BAG); + getTableManager().cellProps(pPropMap); + } xFieldProperties->setPropertyValue(getPropertyName(PROP_CONTENT), uno::makeAny(formula)); xFieldProperties->setPropertyValue(getPropertyName(PROP_NUMBER_FORMAT), uno::makeAny(sal_Int32(0))); xFieldProperties->setPropertyValue("IsShowFormula", uno::makeAny(false)); diff --git a/writerfilter/source/dmapper/PropertyIds.cxx b/writerfilter/source/dmapper/PropertyIds.cxx index 7428097f0953..3aafab2c9354 100644 --- a/writerfilter/source/dmapper/PropertyIds.cxx +++ b/writerfilter/source/dmapper/PropertyIds.cxx @@ -355,6 +355,8 @@ OUString getPropertyName( PropertyIds eId ) case PROP_DATATABLE_NAME: sName = "DataTableName"; break; case PROP_DATACOLUMN_NAME: sName = "DataColumnName"; break; case PROP_CHAR_TRANSPARENCE: sName = "CharTransparence"; break; + case PROP_CELL_FORMULA: sName = "CellFormula"; break; + case PROP_CELL_FORMULA_CONVERTED: sName = "CellFormulaConverted"; break; } assert(sName.getLength()>0); return sName; diff --git a/writerfilter/source/dmapper/PropertyIds.hxx b/writerfilter/source/dmapper/PropertyIds.hxx index c1c0e25698d6..5eed2297ff0d 100644 --- a/writerfilter/source/dmapper/PropertyIds.hxx +++ b/writerfilter/source/dmapper/PropertyIds.hxx @@ -357,6 +357,8 @@ enum PropertyIds ,PROP_DATATABLE_NAME ,PROP_DATACOLUMN_NAME ,PROP_CHAR_TRANSPARENCE + ,PROP_CELL_FORMULA + ,PROP_CELL_FORMULA_CONVERTED }; //Returns the UNO string equivalent to eId. -- cgit