sc/qa/unit/data/xlsx/embedded-text-in-decimal.xlsx |binary sc/qa/unit/subsequent_export_test2.cxx | 28 ++++++++++++++++++ xmloff/source/style/xmlnumfe.cxx | 13 +++++++- xmloff/source/style/xmlnumfi.cxx | 32 ++++++++++----------- 4 files changed, 55 insertions(+), 18 deletions(-)
New commits: commit 070552010d27fab9e224ad5289cc26a99440fecf Author: Laurent BP <laurent.balland-poir...@laposte.net> AuthorDate: Mon May 16 22:47:25 2022 +0200 Commit: Xisco Fauli <xiscofa...@libreoffice.org> CommitDate: Mon Jul 18 12:07:34 2022 +0200 tdf#96723 Number format: embedded text in decimal Embedded text in decimal part is represented by negative position Use number:position as it is defined as integer in schema [1] Add Unit test to import XLSX file with embedded text in decimal and export to ODS [1] https://opengrok.libreoffice.org/xref/core/schema/odf1.3/OpenDocument-v1.3-schema.rng?r=7f3c9da5#7142 Change-Id: Ic68471a071ccbb1c3bec442bfcbe21d84f41ebd8 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/135918 Tested-by: Jenkins Reviewed-by: Eike Rathke <er...@redhat.com> (cherry picked from commit 56dff7b244fb0ef28951193a410dd5c4a3126590) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/136986 Reviewed-by: Laurent Balland <laurent.ball...@mailo.fr> Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org> diff --git a/sc/qa/unit/data/xlsx/embedded-text-in-decimal.xlsx b/sc/qa/unit/data/xlsx/embedded-text-in-decimal.xlsx new file mode 100644 index 000000000000..77b2d022e09f Binary files /dev/null and b/sc/qa/unit/data/xlsx/embedded-text-in-decimal.xlsx differ diff --git a/sc/qa/unit/subsequent_export_test2.cxx b/sc/qa/unit/subsequent_export_test2.cxx index 6cdb9854ae81..a49cb97f8376 100644 --- a/sc/qa/unit/subsequent_export_test2.cxx +++ b/sc/qa/unit/subsequent_export_test2.cxx @@ -190,6 +190,7 @@ public: void testXlsxRowsOrder(); void testTdf91286(); void testTdf148820(); + void testEmbeddedTextInDecimal(); CPPUNIT_TEST_SUITE(ScExportTest2); @@ -312,6 +313,7 @@ public: CPPUNIT_TEST(testXlsxRowsOrder); CPPUNIT_TEST(testTdf91286); CPPUNIT_TEST(testTdf148820); + CPPUNIT_TEST(testEmbeddedTextInDecimal); CPPUNIT_TEST_SUITE_END(); @@ -3094,6 +3096,32 @@ void ScExportTest2::testTdf148820() xDocSh->DoClose(); } +namespace +{ +void lcl_TestEmbeddedTextInDecimal(ScDocShellRef xDocSh) +{ + CPPUNIT_ASSERT(xDocSh); + ScDocument& rDoc = xDocSh->GetDocument(); + sal_uInt32 nNumberFormat = rDoc.GetNumberFormat(0, 0, 0); + const SvNumberformat* pNumberFormat = rDoc.GetFormatTable()->GetEntry(nNumberFormat); + const OUString& rFormatStr = pNumberFormat->GetFormatstring(); + + CPPUNIT_ASSERT_EQUAL(OUString("#,##0.000\" \"###\" \"###"), rFormatStr); +} +} + +void ScExportTest2::testEmbeddedTextInDecimal() +{ + ScDocShellRef xDocSh = loadDoc(u"embedded-text-in-decimal.", FORMAT_XLSX); + lcl_TestEmbeddedTextInDecimal(xDocSh); + + // save to ODS and reload + xDocSh = saveAndReload(*xDocSh, FORMAT_ODS); + lcl_TestEmbeddedTextInDecimal(xDocSh); + + xDocSh->DoClose(); +} + CPPUNIT_TEST_SUITE_REGISTRATION(ScExportTest2); CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/xmloff/source/style/xmlnumfe.cxx b/xmloff/source/style/xmlnumfe.cxx index d928f8be2a79..b41149490d01 100644 --- a/xmloff/source/style/xmlnumfe.cxx +++ b/xmloff/source/style/xmlnumfe.cxx @@ -602,6 +602,8 @@ void SvXMLNumFmtExport::WriteNumberElement_Impl( const SvXMLEmbeddedTextEntry *const pObj = &rEmbeddedEntries[nEntry]; // position attribute + // position == 0 is between first integer digit and decimal separator + // position < 0 is inside decimal part rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_POSITION, OUString::number( pObj->nFormatPos ) ); SvXMLElementExport aChildElem( rExport, XML_NAMESPACE_NUMBER, XML_EMBEDDED_TEXT, @@ -1388,6 +1390,10 @@ void SvXMLNumFmtExport::ExportPart_Impl( const SvNumberformat& rFormat, sal_uInt if ( bAllowEmbedded ) { sal_Int32 nDigitsPassed = 0; + sal_Int32 nEmbeddedPositionsMax = nIntegerSymbols; + // Enable embedded text in decimal part only if there's a decimal part + if ( nPrecision ) + nEmbeddedPositionsMax += nPrecision + 1; nPos = 0; bEnd = false; while (!bEnd) @@ -1404,12 +1410,15 @@ void SvXMLNumFmtExport::ExportPart_Impl( const SvNumberformat& rFormat, sal_uInt if ( pElemStr ) nDigitsPassed += pElemStr->getLength(); break; + case NF_SYMBOLTYPE_DECSEP: + nDigitsPassed++; + break; case NF_SYMBOLTYPE_STRING: case NF_SYMBOLTYPE_BLANK: case NF_SYMBOLTYPE_PERCENT: - if ( nDigitsPassed > 0 && nDigitsPassed < nIntegerSymbols && pElemStr ) + if ( 0 < nDigitsPassed && nDigitsPassed < nEmbeddedPositionsMax && pElemStr ) { - // text (literal or underscore) within the integer part of a number:number element + // text (literal or underscore) within the integer (>=0) or decimal (<0) part of a number:number element OUString aEmbeddedStr; if ( nElemType == NF_SYMBOLTYPE_STRING || nElemType == NF_SYMBOLTYPE_PERCENT ) diff --git a/xmloff/source/style/xmlnumfi.cxx b/xmloff/source/style/xmlnumfi.cxx index 46ee44b3080f..b046e5f5939c 100644 --- a/xmloff/source/style/xmlnumfi.cxx +++ b/xmloff/source/style/xmlnumfi.cxx @@ -456,7 +456,7 @@ SvXMLNumFmtEmbeddedTextContext::SvXMLNumFmtEmbeddedTextContext( SvXMLImport& rIm { if ( aIter.getToken() == XML_ELEMENT(NUMBER, XML_POSITION) ) { - if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 )) + if (::sax::Converter::convertNumber( nAttrVal, aIter.toView() )) nTextPosition = nAttrVal; } else @@ -1706,8 +1706,8 @@ void SvXMLNumFormatContext::AddNumber( const SvXMLNumberInfo& rInfo ) bool bGrouping = rInfo.bGrouping; size_t const nEmbeddedCount = rInfo.m_EmbeddedElements.size(); - if ( nEmbeddedCount ) - bGrouping = false; // grouping and embedded characters can't be used together + if ( nEmbeddedCount && rInfo.m_EmbeddedElements.rbegin()->first > 0 ) + bGrouping = false; // grouping and embedded characters in integer part can't be used together sal_uInt32 nStdIndex = pFormatter->GetStandardIndex( nFormatLang ); OUStringBuffer aNumStr(pFormatter->GenerateFormat( nStdIndex, nFormatLang, @@ -1745,10 +1745,21 @@ void SvXMLNumFormatContext::AddNumber( const SvXMLNumberInfo& rInfo ) } } + if ( ( rInfo.bDecReplace || rInfo.nMinDecimalDigits < rInfo.nDecimals ) && nPrec ) // add decimal replacement (dashes) + { + // add dashes for explicit decimal replacement, # or ? for variable decimals + sal_Unicode cAdd = rInfo.bDecReplace ? '-' : ( rInfo.bDecAlign ? '?': '#' ); + + if ( rInfo.nMinDecimalDigits == 0 ) + aNumStr.append( pData->GetLocaleData( nFormatLang ).getNumDecimalSep() ); + for ( sal_uInt16 i=rInfo.nMinDecimalDigits; i<nPrec; i++) + aNumStr.append( cAdd ); + } + if ( nEmbeddedCount ) { // insert embedded strings into number string - // only the integer part is supported + // support integer (position >=0) and decimal (position <0) part // nZeroPos is the string position where format position 0 is inserted sal_Int32 nZeroPos = aNumStr.indexOf( pData->GetLocaleData( nFormatLang ).getNumDecimalSep() ); @@ -1777,7 +1788,7 @@ void SvXMLNumFormatContext::AddNumber( const SvXMLNumberInfo& rInfo ) { sal_Int32 const nFormatPos = it.first; sal_Int32 nInsertPos = nZeroPos - nFormatPos; - if ( nFormatPos >= 0 && nInsertPos >= 0 ) + if ( nInsertPos >= 0 ) { // #107805# always quote embedded strings - even space would otherwise // be recognized as thousands separator in French. @@ -1791,17 +1802,6 @@ void SvXMLNumFormatContext::AddNumber( const SvXMLNumberInfo& rInfo ) aFormatCode.append( aNumStr ); - if ( ( rInfo.bDecReplace || rInfo.nMinDecimalDigits < rInfo.nDecimals ) && nPrec ) // add decimal replacement (dashes) - { - // add dashes for explicit decimal replacement, # or ? for variable decimals - sal_Unicode cAdd = rInfo.bDecReplace ? '-' : ( rInfo.bDecAlign ? '?': '#' ); - - if ( rInfo.nMinDecimalDigits == 0 ) - aFormatCode.append( pData->GetLocaleData( nFormatLang ).getNumDecimalSep() ); - for ( sal_uInt16 i=rInfo.nMinDecimalDigits; i<nPrec; i++) - aFormatCode.append( cAdd ); - } - // add extra thousands separators for display factor if (rInfo.fDisplayFactor == 1.0 || rInfo.fDisplayFactor <= 0.0)