include/xmloff/xmlnumfe.hxx                                 |    2 
 include/xmloff/xmltoken.hxx                                 |    1 
 sc/qa/unit/data/ods/tdf153993-Exponent-lower-case.ods       |binary
 sc/qa/unit/subsequent_export_test4.cxx                      |   14 ++++
 schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng |    9 ++
 xmloff/source/core/xmltoken.cxx                             |    1 
 xmloff/source/style/xmlnumfe.cxx                            |   24 ++++++-
 xmloff/source/style/xmlnumfi.cxx                            |   40 ++++++++----
 xmloff/source/token/tokens.txt                              |    1 
 9 files changed, 78 insertions(+), 14 deletions(-)

New commits:
commit cc721291cc4a53503398b82dd86baa521b2b13cc
Author:     Laurent Balland <laurent.ball...@mailo.fr>
AuthorDate: Sat Jul 1 10:43:39 2023 +0200
Commit:     Laurent Balland <laurent.ball...@mailo.fr>
CommitDate: Wed Nov 1 09:10:19 2023 +0100

    tdf#153993 Extend ODF: lowercase for exponent char
    
    In scientific format, user may want to have clearer format such as:
    0.000" "000" "e+" "0
    
    This change
    - adds "exponent-lowercase" boolean attribute to scientific-number format 
to preserve "e" or "E" char
    - includes all positions up to exponent for embedded text of scientific 
number
    
    Add QA test
    
    Change-Id: Ie263f4ecf30a1a8dcd8046e1e048767020e54dc2
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/153824
    Tested-by: Jenkins
    Reviewed-by: Laurent Balland <laurent.ball...@mailo.fr>

diff --git a/include/xmloff/xmlnumfe.hxx b/include/xmloff/xmlnumfe.hxx
index 64a66a27ef05..8421c5f7e323 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 bGrouping, sal_Int32 nExp, 
sal_Int32 nExpInterval, bool bExpSign, bool bExponentLowercase,
                                         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 066a55960e0b..62f3ebcd613d 100644
--- a/include/xmloff/xmltoken.hxx
+++ b/include/xmloff/xmltoken.hxx
@@ -3467,6 +3467,7 @@ namespace xmloff::token {
         XML_EXTERNALDATA,
 
         XML_EXPONENT_INTERVAL,
+        XML_EXPONENT_LOWERCASE,
         XML_FORCED_EXPONENT_SIGN,
         XML_MIN_DECIMAL_PLACES,
         XML_MAX_DENOMINATOR_VALUE,
diff --git a/sc/qa/unit/data/ods/tdf153993-Exponent-lower-case.ods 
b/sc/qa/unit/data/ods/tdf153993-Exponent-lower-case.ods
new file mode 100644
index 000000000000..aaa07ca80342
Binary files /dev/null and 
b/sc/qa/unit/data/ods/tdf153993-Exponent-lower-case.ods differ
diff --git a/sc/qa/unit/subsequent_export_test4.cxx 
b/sc/qa/unit/subsequent_export_test4.cxx
index b611ceb5ba6f..2b4141425136 100644
--- a/sc/qa/unit/subsequent_export_test4.cxx
+++ b/sc/qa/unit/subsequent_export_test4.cxx
@@ -1503,6 +1503,20 @@ CPPUNIT_TEST_FIXTURE(ScExportTest4, 
testEmbeddedTextInDecimal)
     lcl_TestNumberFormat(*getScDoc(), "#,##0.000\" \"###\" \"###");
 }
 
+CPPUNIT_TEST_FIXTURE(ScExportTest4, testLowercaseExponent)
+{
+    createScDoc("ods/tdf153993-Exponent-lower-case.ods");
+
+    // save to ODS and reload
+    saveAndReload("calc8");
+    lcl_TestNumberFormat(*getScDoc(), "0.000\" \"000\" \"e+\" \"0");
+
+    // save to XLSX and reload
+    // lower case not preserve in XLSX
+    saveAndReload("Calc Office Open XML");
+    lcl_TestNumberFormat(*getScDoc(), "0.000 000 E+ 0");
+}
+
 CPPUNIT_TEST_FIXTURE(ScExportTest4, testTotalsRowFunction)
 {
     createScDoc("xlsx/totalsRowFunction.xlsx");
diff --git a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng 
b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
index b9fbcfc7ce4b..c9993768aa40 100644
--- a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
+++ b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
@@ -2845,6 +2845,15 @@ 
xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.
     </rng:interleave>
   </rng:define>
 
+  <!-- TODO no proposal,  -->
+  <rng:define name="number-scientific-number-attlist" combine="interleave">
+    <rng:optional>
+      <rng:attribute name="loext:exponent-lowercase">
+        <rng:ref name="boolean"/>
+      </rng:attribute>
+    </rng:optional>
+  </rng:define>
+
   <!-- TODO no proposal -->
   <rng:define name="table-data-pilot-level-attlist" combine="interleave">
     <rng:optional>
diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx
index fa439f9f367b..00234904e7b4 100644
--- a/xmloff/source/core/xmltoken.cxx
+++ b/xmloff/source/core/xmltoken.cxx
@@ -3472,6 +3472,7 @@ namespace xmloff::token {
         TOKEN( "external-data", XML_EXTERNALDATA),
 
         TOKEN( "exponent-interval",               XML_EXPONENT_INTERVAL ),
+        TOKEN( "exponent-lowercase",              XML_EXPONENT_LOWERCASE ),
         TOKEN( "forced-exponent-sign",            XML_FORCED_EXPONENT_SIGN ),
         TOKEN( "min-decimal-places",              XML_MIN_DECIMAL_PLACES ),
         TOKEN( "max-denominator-value",           XML_MAX_DENOMINATOR_VALUE ),
diff --git a/xmloff/source/style/xmlnumfe.cxx b/xmloff/source/style/xmlnumfe.cxx
index 67675cf22ab3..ee09dd0b39d8 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 bGrouping, sal_Int32 nExp, sal_Int32 
nExpInterval, bool bExpSign, bool bExponentLowercase,
                             const SvXMLEmbeddedTextEntryArr& rEmbeddedEntries )
 {
     FinishTextElement_Impl();
@@ -753,6 +753,13 @@ void SvXMLNumFmtExport::WriteScientificElement_Impl(
                              XML_FORCED_EXPONENT_SIGN,
                              bExpSign? XML_TRUE : XML_FALSE );
     }
+    //  exponent string
+    // Export only for 1.x with extensions
+    if (eVersion & SvtSaveOptions::ODFSVER_EXTENDED)
+    {
+        if (bExponentLowercase)
+            m_rExport.AddAttribute( XML_NAMESPACE_LO_EXT, 
XML_EXPONENT_LOWERCASE, XML_TRUE );
+    }
 
     SvXMLElementExport aElem( m_rExport,
                               XML_NAMESPACE_NUMBER, XML_SCIENTIFIC_NUMBER,
@@ -1351,6 +1358,7 @@ void SvXMLNumFmtExport::ExportPart_Impl( const 
SvNumberformat& rFormat, sal_uInt
         bool bCurrFound  = false;
         bool bInInteger  = true;
         bool bExpSign = true;
+        bool bExponentLowercase = false;        // 'e' or 'E' for scientific 
notation
         bool bDecAlign   = false;               // decimal alignment with "?"
         sal_Int32 nExpDigits = 0;
         sal_Int32 nIntegerSymbols = 0;          // for embedded-text, 
including "#"
@@ -1421,6 +1429,8 @@ void SvXMLNumFmtExport::ExportPart_Impl( const 
SvNumberformat& rFormat, sal_uInt
                     if ( pElemStr && ( pElemStr->getLength() == 1
                                   || ( pElemStr->getLength() == 2 && 
(*pElemStr)[1] == '-' ) ) )
                         bExpSign = false;       // for 0.00E0 or 0.00E-00
+                    if ( pElemStr && (*pElemStr)[0] == 'e' )
+                        bExponentLowercase = true;   // for 0.00e+00
                     break;
                 case NF_SYMBOLTYPE_CURRENCY:
                     bCurrFound = true;
@@ -1459,8 +1469,12 @@ void SvXMLNumFmtExport::ExportPart_Impl( const 
SvNumberformat& rFormat, sal_uInt
             // Enable embedded text in decimal part only if there's a decimal 
part
             if ( nPrecision )
                 nEmbeddedPositionsMax += nPrecision + 1;
+            // Enable embedded text in exponent in scientific number
+            if ( nFmtType == SvNumFormatType::SCIENTIFIC )
+                nEmbeddedPositionsMax += 1 + nExpDigits;
             nPos = 0;
             bEnd = false;
+            bExpFound = false;
             while (!bEnd)
             {
                 short nElemType = rFormat.GetNumForType( nPart, nPos );
@@ -1475,6 +1489,9 @@ void SvXMLNumFmtExport::ExportPart_Impl( const 
SvNumberformat& rFormat, sal_uInt
                         if ( pElemStr )
                             nDigitsPassed += pElemStr->getLength();
                         break;
+                    case NF_SYMBOLTYPE_EXP:
+                        bExpFound = true;
+                        [[fallthrough]];
                     case NF_SYMBOLTYPE_DECSEP:
                         nDigitsPassed++;
                         break;
@@ -1505,6 +1522,9 @@ void SvXMLNumFmtExport::ExportPart_Impl( const 
SvNumberformat& rFormat, sal_uInt
 
                             aEmbeddedEntries.push_back(
                                 SvXMLEmbeddedTextEntry( nPos, nEmbedPos, 
aEmbeddedStr, bSaveBlankWidthSymbol ));
+                            // exponent sign is required with embedded text in 
exponent
+                            if ( bExpFound && !bExpSign )
+                                bExpSign = true;
                         }
                         break;
                 }
@@ -1662,7 +1682,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,
-                                    aEmbeddedEntries );
+                                    bExponentLowercase, aEmbeddedEntries );
                                 bAnyContent = true;
                                 break;
                             case SvNumFormatType::FRACTION:
diff --git a/xmloff/source/style/xmlnumfi.cxx b/xmloff/source/style/xmlnumfi.cxx
index f9f9bce5f675..d72914937eb3 100644
--- a/xmloff/source/style/xmlnumfi.cxx
+++ b/xmloff/source/style/xmlnumfi.cxx
@@ -103,6 +103,7 @@ struct SvXMLNumberInfo
     bool        bGrouping           = false;
     bool        bDecReplace         = false;
     bool        bExpSign            = true;
+    bool        bExponentLowercase  = false;     /// Exponent is 'e' instead 
of 'E'
     bool        bDecAlign           = false;
     double      fDisplayFactor      = 1.0;
     OUString    aIntegerFractionDelimiter;
@@ -722,6 +723,11 @@ SvXMLNumFmtElementContext::SvXMLNumFmtElementContext( 
SvXMLImport& rImport,
                 if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
                     aNumInfo.bExpSign = bAttrBool;
                 break;
+            case XML_ELEMENT(NUMBER, XML_EXPONENT_LOWERCASE):
+            case XML_ELEMENT(LO_EXT, XML_EXPONENT_LOWERCASE):
+                if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
+                    aNumInfo.bExponentLowercase = bAttrBool;
+                break;
             case XML_ELEMENT(NUMBER, XML_MIN_NUMERATOR_DIGITS):
                 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 
0 ))
                     aNumInfo.nMinNumerDigits = nAttrVal;
@@ -1173,16 +1179,7 @@ void SvXMLNumFmtElementContext::endFastElement(sal_Int32 
)
                         rParent.AddToCode( '#' );
                     }
                 }
-                rParent.AddNumber( aNumInfo );      // simple number
-
-                if ( aNumInfo.bExpSign )
-                    rParent.AddToCode( u"E+" );
-                else
-                    rParent.AddToCode( u"E" );
-                for (sal_Int32 i=0; i<aNumInfo.nExpDigits; i++)
-                {
-                    rParent.AddToCode( '0' );
-                }
+                rParent.AddNumber( aNumInfo );      //  number and exponent
             }
             break;
 
@@ -1868,6 +1865,23 @@ void SvXMLNumFormatContext::AddNumber( const 
SvXMLNumberInfo& rInfo )
             aNumStr.append( cAdd );
     }
 
+    // Scientific number
+    sal_Int32 nExpPos = -1;
+    if ( rInfo.nExpDigits > 0 )
+    {
+        nExpPos = aNumStr.getLength();
+        aNumStr.append( rInfo.bExponentLowercase ? u"e" : u"E" );
+                                // exponent sign is required with embedded 
text in exponent
+        if ( rInfo.bExpSign || ( nEmbeddedCount && ( rInfo.nDecimals + 1 < 
-rInfo.m_EmbeddedElements.begin()->first ) ) )
+        {
+            aNumStr.append( u"+" );
+        }
+        for (sal_Int32 i=0; i<rInfo.nExpDigits; i++)
+        {
+            aNumStr.append( '0' );
+        }
+    }
+
     if ( nEmbeddedCount )
     {
         //  insert embedded strings into number string
@@ -1893,6 +1907,8 @@ void SvXMLNumFormatContext::AddNumber( const 
SvXMLNumberInfo& rInfo )
                 aNumStr.insert(0, '#');
             }
             nZeroPos = nZeroPos + nAddCount;
+            if ( nExpPos > 0 )
+                nExpPos = nExpPos + nAddCount;
         }
 
         // m_EmbeddedElements is sorted with ascending positions - loop is 
from right to left
@@ -1900,7 +1916,9 @@ void SvXMLNumFormatContext::AddNumber( const 
SvXMLNumberInfo& rInfo )
         {
             sal_Int32 const nFormatPos = it.first;
             sal_Int32 nInsertPos = nZeroPos - nFormatPos;
-            if ( nInsertPos >= 0 )
+            if ( nExpPos > 0 && nInsertPos > nExpPos )
+                nInsertPos ++;
+            if ( 0 <= nInsertPos && nInsertPos <= aNumStr.getLength() )
             {
                 aNumStr.insert( nInsertPos, it.second );
             }
diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt
index 85d947387e20..cdd387702531 100644
--- a/xmloff/source/token/tokens.txt
+++ b/xmloff/source/token/tokens.txt
@@ -3228,6 +3228,7 @@ display-units
 display-units-built-in-unit
 external-data
 exponent-interval
+exponent-lowercase
 forced-exponent-sign
 min-decimal-places
 max-denominator-value

Reply via email to