basic/qa/vba_tests/format.vb                                |    2 
 include/xmloff/xmlnumfe.hxx                                 |    2 
 include/xmloff/xmltoken.hxx                                 |    1 
 sc/qa/unit/data/ods/tdf156449-Blank-In-Exponent.ods         |binary
 sc/qa/unit/subsequent_export_test4.cxx                      |   31 ++++++++++++
 schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng |    9 +++
 svl/qa/unit/svl.cxx                                         |    8 +++
 svl/source/numbers/zformat.cxx                              |    8 +--
 xmloff/source/core/xmltoken.cxx                             |    1 
 xmloff/source/style/xmlnumfe.cxx                            |   20 ++++++-
 xmloff/source/style/xmlnumfi.cxx                            |   16 +++++-
 xmloff/source/token/tokens.txt                              |    1 
 12 files changed, 89 insertions(+), 10 deletions(-)

New commits:
commit 443027cd71aef3eb45bbf3631869ea63457f2c81
Author:     Laurent Balland <laurent.ball...@mailo.fr>
AuthorDate: Wed Jul 26 17:58:00 2023 +0200
Commit:     Laurent Balland <laurent.ball...@mailo.fr>
CommitDate: Wed Nov 1 11:43:54 2023 +0100

    tdf#156449 Preserve '0' or '?' in exponent
    
    Exponent in scientific number may use '?' as blank like in format "0.00E+?0"
    This change:
    - adds interpreatation of '0' and '?' in exponent
    - adds "blank-exponent-digits" attribute to scientific number for import
      and export to ODF
    - prevents using exponent with only '?'. There must be at least one '0'
      in exponent
    - adds QA test of such format and test import/export/import to ODF and OOXML
    - corrects one basic test
    
    Change-Id: If52edc632a161f842270bb2fd77af535e2b978d4
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/154986
    Tested-by: Jenkins
    Reviewed-by: Laurent Balland <laurent.ball...@mailo.fr>

diff --git a/basic/qa/vba_tests/format.vb b/basic/qa/vba_tests/format.vb
index 0e997ca824db..4e62e87e498e 100644
--- a/basic/qa/vba_tests/format.vb
+++ b/basic/qa/vba_tests/format.vb
@@ -135,7 +135,7 @@ Sub Custom_Number_Format_Sample()
     TestUtil.AssertEqual(Format(12345.25, "#,###.##"),       "12,345.25",    
"Format(12345.25, ""#,###.##"")")
     TestUtil.AssertEqual(Format(0.25, "##.00%"),             "25.00%",       
"Format(0.25, ""##.00%"")")
     TestUtil.AssertEqual(Format(1000000, "#,###"),           "1,000,000",    
"Format(1000000, ""#,###"")")
-    TestUtil.AssertEqual(Format(1.09837555, "#.#####E+###"), "1.09838E+000", 
"Format(1.09837555, ""#.#####E+###"")")
+    TestUtil.AssertEqual(Format(1.09837555, "#.#####E+000"), "1.09838E+000", 
"Format(1.09837555, ""#.#####E+000"")")
     TestUtil.AssertEqual(Format(1.09837555, "###.####E#"),   "1.0984E0",     
"Format(1.09837555, ""###.####E#"")")
     TestUtil.AssertEqual(Format(1098.37555, "###.####E#"),   "1.0984E3",     
"Format(1098.37555, ""###.####E#"")")
     TestUtil.AssertEqual(Format(1098375.55, "###.####E#"),   "1.0984E6",     
"Format(1098375.55, ""###.####E#"")")
diff --git a/include/xmloff/xmlnumfe.hxx b/include/xmloff/xmlnumfe.hxx
index 8421c5f7e323..d96f40c53a04 100644
--- a/include/xmloff/xmlnumfe.hxx
+++ b/include/xmloff/xmlnumfe.hxx
@@ -71,7 +71,7 @@ private:
                                         bool bGrouping, sal_Int32 
nTrailingThousands,
                                         const SvXMLEmbeddedTextEntryArr& 
rEmbeddedEntries );
     SAL_DLLPRIVATE void WriteScientificElement_Impl( sal_Int32 nDecimals, 
sal_Int32 nMinDecimals, sal_Int32 nInteger, sal_Int32 nBlankInteger,
-                                        bool bGrouping, sal_Int32 nExp, 
sal_Int32 nExpInterval, bool bExpSign, bool bExponentLowercase,
+                                        bool bGrouping, sal_Int32 nExp, 
sal_Int32 nExpInterval, bool bExpSign, bool bExponentLowercase, sal_Int32 
nBlankExp,
                                         const SvXMLEmbeddedTextEntryArr& 
rEmbeddedEntries );
     SAL_DLLPRIVATE void WriteFractionElement_Impl( sal_Int32 nInteger, 
sal_Int32 nBlankInteger, bool bGrouping,
                                                    const SvNumberformat& 
rFormat, sal_uInt16 nPart );
diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx
index 62f3ebcd613d..4e6441841774 100644
--- a/include/xmloff/xmltoken.hxx
+++ b/include/xmloff/xmltoken.hxx
@@ -3469,6 +3469,7 @@ namespace xmloff::token {
         XML_EXPONENT_INTERVAL,
         XML_EXPONENT_LOWERCASE,
         XML_FORCED_EXPONENT_SIGN,
+        XML_BLANK_EXPONENT_DIGITS,
         XML_MIN_DECIMAL_PLACES,
         XML_MAX_DENOMINATOR_VALUE,
         XML_MAX_NUMERATOR_DIGITS,
diff --git a/sc/qa/unit/data/ods/tdf156449-Blank-In-Exponent.ods 
b/sc/qa/unit/data/ods/tdf156449-Blank-In-Exponent.ods
new file mode 100644
index 000000000000..73647881e73d
Binary files /dev/null and 
b/sc/qa/unit/data/ods/tdf156449-Blank-In-Exponent.ods differ
diff --git a/sc/qa/unit/subsequent_export_test4.cxx 
b/sc/qa/unit/subsequent_export_test4.cxx
index 2b4141425136..390783131733 100644
--- a/sc/qa/unit/subsequent_export_test4.cxx
+++ b/sc/qa/unit/subsequent_export_test4.cxx
@@ -1468,6 +1468,37 @@ void lcl_TestNumberFormat(ScDocument& rDoc, const 
OUString& rFormatStrOK)
 
     CPPUNIT_ASSERT_EQUAL(rFormatStrOK, rFormatStr);
 }
+
+void lcl_SetNumberFormat(ScDocument& rDoc, const OUString& rFormat)
+{
+    sal_Int32 nCheckPos;
+    SvNumFormatType nType;
+    sal_uInt32 nFormat;
+    OUString aNewFormat = rFormat;
+    SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+    if (pFormatter)
+    {
+        pFormatter->PutEntry(aNewFormat, nCheckPos, nType, nFormat);
+        rDoc.SetNumberFormat(ScAddress(0, 0, 0), nFormat);
+    }
+}
+}
+
+CPPUNIT_TEST_FIXTURE(ScExportTest4, testBlankInExponent)
+{
+    createScDoc("ods/tdf156449-Blank-In-Exponent.ods");
+
+    // save to ODS and reload
+    saveAndReload("calc8");
+    lcl_TestNumberFormat(*getScDoc(), "0.00E+?0");
+    lcl_SetNumberFormat(*getScDoc(), "0.00E+??");
+    // at least one '0' in exponent
+    saveAndReload("calc8");
+    lcl_TestNumberFormat(*getScDoc(), "0.00E+?0");
+
+    // save to XLSX and reload
+    saveAndReload("Calc Office Open XML");
+    lcl_TestNumberFormat(*getScDoc(), "0.00E+?0");
 }
 
 CPPUNIT_TEST_FIXTURE(ScExportTest4, testSecondsWithoutTruncateAndDecimals)
diff --git a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng 
b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
index c9993768aa40..aeb4c77ea40e 100644
--- a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
+++ b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
@@ -2854,6 +2854,15 @@ 
xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.
     </rng:optional>
   </rng:define>
 
+  <!-- TODO no proposal,  -->
+  <rng:define name="number-scientific-number-attlist" combine="interleave">
+    <rng:optional>
+      <rng:attribute name="loext:blank-exponent-digits">
+        <rng:ref name="positiveInteger"/>
+      </rng:attribute>
+    </rng:optional>
+  </rng:define>
+
   <!-- TODO no proposal -->
   <rng:define name="table-data-pilot-level-attlist" combine="interleave">
     <rng:optional>
diff --git a/svl/qa/unit/svl.cxx b/svl/qa/unit/svl.cxx
index f43cfb9e437e..4fa56f4bccd4 100644
--- a/svl/qa/unit/svl.cxx
+++ b/svl/qa/unit/svl.cxx
@@ -1743,6 +1743,14 @@ void Test::testUserDefinedNumberFormats()
         sExpected = "271433.605";
         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
     }
+    {   // tdf#156449 Use '?' in exponent of scientific number
+        sCode =     "0.00E+?0";
+        sExpected = "3.14E+ 0"; // before change it was "3.14E+00"
+        checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
+        // There should be at least one '0' in exponent
+        sCode =     "0.00E+??";
+        checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
+    }
     {   // tdf#33689 use English NfKeywords in non-English language
         eLang = LANGUAGE_DUTCH;
         sExpected = "Dutch: 1900/01/02 03:23:53";
diff --git a/svl/source/numbers/zformat.cxx b/svl/source/numbers/zformat.cxx
index 174ab1c15667..b5c8757ef2e6 100644
--- a/svl/source/numbers/zformat.cxx
+++ b/svl/source/numbers/zformat.cxx
@@ -2752,11 +2752,10 @@ bool SvNumberformat::ImpGetScientificOutput(double 
fNumber,
     }
 
     sal_uInt16 j = nCnt-1;  // Last symbol
-    sal_Int32 k;  // Position in ExpStr
+    sal_Int32 k = ExpStr.getLength() - 1;  // Position in ExpStr
     sal_Int32 nZeros = 0; // Erase leading zeros
 
-    bRes |= ImpNumberFill(ExpStr, fNumber, k, j, nIx, NF_SYMBOLTYPE_EXP);
-
+    // erase all leading zeros except last one
     while (nZeros < k && ExpStr[nZeros] == '0')
     {
         ++nZeros;
@@ -2766,6 +2765,9 @@ bool SvNumberformat::ImpGetScientificOutput(double 
fNumber,
         ExpStr.remove( 0, nZeros);
     }
 
+    // restore leading zeros or blanks according to format '0' or '?' 
tdf#156449
+    bRes |= ImpNumberFill(ExpStr, fNumber, k, j, nIx, NF_SYMBOLTYPE_EXP);
+
     bool bCont = true;
 
     if (rInfo.nTypeArray[j] == NF_SYMBOLTYPE_EXP)
diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx
index 00234904e7b4..6879f37db295 100644
--- a/xmloff/source/core/xmltoken.cxx
+++ b/xmloff/source/core/xmltoken.cxx
@@ -3474,6 +3474,7 @@ namespace xmloff::token {
         TOKEN( "exponent-interval",               XML_EXPONENT_INTERVAL ),
         TOKEN( "exponent-lowercase",              XML_EXPONENT_LOWERCASE ),
         TOKEN( "forced-exponent-sign",            XML_FORCED_EXPONENT_SIGN ),
+        TOKEN( "blank-exponent-digits",           XML_BLANK_EXPONENT_DIGITS ),
         TOKEN( "min-decimal-places",              XML_MIN_DECIMAL_PLACES ),
         TOKEN( "max-denominator-value",           XML_MAX_DENOMINATOR_VALUE ),
         TOKEN( "max-numerator-digits",            XML_MAX_NUMERATOR_DIGITS ),
diff --git a/xmloff/source/style/xmlnumfe.cxx b/xmloff/source/style/xmlnumfe.cxx
index ee09dd0b39d8..406c22236a71 100644
--- a/xmloff/source/style/xmlnumfe.cxx
+++ b/xmloff/source/style/xmlnumfe.cxx
@@ -695,7 +695,7 @@ void SvXMLNumFmtExport::WriteNumberElement_Impl(
 
 void SvXMLNumFmtExport::WriteScientificElement_Impl(
                             sal_Int32 nDecimals, sal_Int32 nMinDecimals, 
sal_Int32 nInteger, sal_Int32 nBlankInteger,
-                            bool bGrouping, sal_Int32 nExp, sal_Int32 
nExpInterval, bool bExpSign, bool bExponentLowercase,
+                            bool bGrouping, sal_Int32 nExp, sal_Int32 
nExpInterval, bool bExpSign, bool bExponentLowercase, sal_Int32 nBlankExp,
                             const SvXMLEmbeddedTextEntryArr& rEmbeddedEntries )
 {
     FinishTextElement_Impl();
@@ -759,6 +759,12 @@ void SvXMLNumFmtExport::WriteScientificElement_Impl(
     {
         if (bExponentLowercase)
             m_rExport.AddAttribute( XML_NAMESPACE_LO_EXT, 
XML_EXPONENT_LOWERCASE, XML_TRUE );
+        if (nBlankExp > 0)
+        {
+            if (nBlankExp >= nExp)
+                nBlankExp = nExp - 1; // preserve at least one '0' in exponent
+            m_rExport.AddAttribute( XML_NAMESPACE_LO_EXT, 
XML_BLANK_EXPONENT_DIGITS, OUString::number( nBlankExp ) );
+        }
     }
 
     SvXMLElementExport aElem( m_rExport,
@@ -1360,7 +1366,8 @@ void SvXMLNumFmtExport::ExportPart_Impl( const 
SvNumberformat& rFormat, sal_uInt
         bool bExpSign = true;
         bool bExponentLowercase = false;        // 'e' or 'E' for scientific 
notation
         bool bDecAlign   = false;               // decimal alignment with "?"
-        sal_Int32 nExpDigits = 0;
+        sal_Int32 nExpDigits = 0;               // '0' and '?' in exponent
+        sal_Int32 nBlankExp = 0;                // only '?' in exponent
         sal_Int32 nIntegerSymbols = 0;          // for embedded-text, 
including "#"
         sal_Int32 nTrailingThousands = 0;       // thousands-separators after 
all digits
         sal_Int32 nMinDecimals = nPrecision;
@@ -1383,7 +1390,14 @@ void SvXMLNumFmtExport::ExportPart_Impl( const 
SvNumberformat& rFormat, sal_uInt
                     break;
                 case NF_SYMBOLTYPE_DIGIT:
                     if ( bExpFound && pElemStr )
+                    {
                         nExpDigits += pElemStr->getLength();
+                        for ( sal_Int32 i = pElemStr->getLength()-1; i >= 0 ; 
i-- )
+                        {
+                            if ( (*pElemStr)[i] == '?' )
+                                nBlankExp ++;
+                        }
+                    }
                     else if ( !bDecDashes && pElemStr && (*pElemStr)[0] == '-' 
)
                     {
                         bDecDashes = true;
@@ -1682,7 +1696,7 @@ void SvXMLNumFmtExport::ExportPart_Impl( const 
SvNumberformat& rFormat, sal_uInt
                                 // as integer digits: use nIntegerSymbols 
instead of nLeading
                                 // nIntegerSymbols represents exponent 
interval (for engineering notation)
                                 WriteScientificElement_Impl( nPrecision, 
nMinDecimals, nLeading, nBlankInteger, bThousand, nExpDigits, nIntegerSymbols, 
bExpSign,
-                                    bExponentLowercase, aEmbeddedEntries );
+                                    bExponentLowercase, nBlankExp, 
aEmbeddedEntries );
                                 bAnyContent = true;
                                 break;
                             case SvNumFormatType::FRACTION:
diff --git a/xmloff/source/style/xmlnumfi.cxx b/xmloff/source/style/xmlnumfi.cxx
index d72914937eb3..f6d05e94c1bf 100644
--- a/xmloff/source/style/xmlnumfi.cxx
+++ b/xmloff/source/style/xmlnumfi.cxx
@@ -90,7 +90,8 @@ struct SvXMLNumberInfo
     sal_Int32   nDecimals           = -1;
     sal_Int32   nInteger            = -1;       /// Total min number of digits 
in integer part ('0' + '?')
     sal_Int32   nBlankInteger       = -1;       /// Number of '?' in integer 
part
-    sal_Int32   nExpDigits          = -1;
+    sal_Int32   nExpDigits          = -1;       /// Number of '0' and '?' in 
exponent
+    sal_Int32   nBlankExp           = -1;       /// Number of '?' in exponent
     sal_Int32   nExpInterval        = -1;
     sal_Int32   nMinNumerDigits     = -1;
     sal_Int32   nMinDenomDigits     = -1;
@@ -713,6 +714,11 @@ SvXMLNumFmtElementContext::SvXMLNumFmtElementContext( 
SvXMLImport& rImport,
                 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 
0 ))
                     aNumInfo.nExpDigits = std::min<sal_Int32>(nAttrVal, 
NF_MAX_FORMAT_SYMBOLS);
                 break;
+            case XML_ELEMENT(NUMBER, XML_BLANK_EXPONENT_DIGITS):
+            case XML_ELEMENT(LO_EXT, XML_BLANK_EXPONENT_DIGITS):
+                if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 
0 ))
+                    aNumInfo.nBlankExp = std::min<sal_Int32>(nAttrVal, 
NF_MAX_FORMAT_SYMBOLS);
+                break;
             case XML_ELEMENT(NUMBER, XML_EXPONENT_INTERVAL):
             case XML_ELEMENT(LO_EXT, XML_EXPONENT_INTERVAL):
                 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 
0 ))
@@ -808,6 +814,9 @@ SvXMLNumFmtElementContext::SvXMLNumFmtElementContext( 
SvXMLImport& rImport,
         else
             aNumInfo.nMinDecimalDigits = aNumInfo.nDecimals;
     }
+    if ( aNumInfo.nExpDigits > 0 && aNumInfo.nBlankExp >= aNumInfo.nExpDigits )
+        aNumInfo.nBlankExp = aNumInfo.nExpDigits - 1; // at least one '0' in 
exponent
+
     if ( aNumInfo.nZerosDenomDigits > 0 )
     {   // nMin = count of '0' and '?'
         if ( aNumInfo.nMinDenomDigits < aNumInfo.nZerosDenomDigits )
@@ -1878,7 +1887,10 @@ void SvXMLNumFormatContext::AddNumber( const 
SvXMLNumberInfo& rInfo )
         }
         for (sal_Int32 i=0; i<rInfo.nExpDigits; i++)
         {
-            aNumStr.append( '0' );
+            if ( i < rInfo.nBlankExp )
+                aNumStr.append( '?' );
+            else
+                aNumStr.append( '0' );
         }
     }
 
diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt
index cdd387702531..7eb09f6f2f9f 100644
--- a/xmloff/source/token/tokens.txt
+++ b/xmloff/source/token/tokens.txt
@@ -3230,6 +3230,7 @@ external-data
 exponent-interval
 exponent-lowercase
 forced-exponent-sign
+blank-exponent-digits
 min-decimal-places
 max-denominator-value
 max-numerator-digits

Reply via email to