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)

Reply via email to