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.balland@mailo.fr>
diff --git a/basic/qa/vba_tests/format.vb b/basic/qa/vba_tests/format.vb
index 0e997ca..4e62e87 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 8421c5f..d96f40c 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 62f3ebc..4e644184 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 0000000..7364788
--- /dev/null
+++ b/sc/qa/unit/data/ods/tdf156449-Blank-In-Exponent.ods
Binary files differ
diff --git a/sc/qa/unit/subsequent_export_test4.cxx b/sc/qa/unit/subsequent_export_test4.cxx
index 2b414142..3907831 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 c999376..aeb4c77 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 f43cfb9..4fa56f4 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 174ab1c..b5c8757 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 00234904..6879f37 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 ee09dd0..406c2223 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 d729149..f6d05e9 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 cdd3877..7eb09f6 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