include/xmloff/xmlnumfe.hxx                                 |    7 
 include/xmloff/xmltoken.hxx                                 |    1 
 sc/qa/unit/data/contentCSV/testNumberFormats.csv            |   32 +++
 sc/qa/unit/data/ods/testNumberFormats.ods                   |binary
 sc/qa/unit/helper/qahelper.cxx                              |    5 
 sc/qa/unit/helper/qahelper.hxx                              |    2 
 sc/qa/unit/subsequent_export_test.cxx                       |   31 +++
 schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng |    9 +
 svl/source/numbers/zformat.cxx                              |    3 
 xmloff/source/core/xmltoken.cxx                             |    1 
 xmloff/source/style/xmlnumfe.cxx                            |  101 ++++++------
 xmloff/source/style/xmlnumfi.cxx                            |   29 +++
 xmloff/source/token/tokens.txt                              |    1 
 13 files changed, 165 insertions(+), 57 deletions(-)

New commits:
commit 46b7fd59f9d0dfb5544df99494d3a3d3a372ae8b
Author:     Laurent Balland <laurent.ball...@mailo.fr>
AuthorDate: Sat Jan 14 15:48:12 2023 +0100
Commit:     Eike Rathke <er...@redhat.com>
CommitDate: Mon Jan 30 17:00:46 2023 +0000

    tdf#118324 Treat blank ? in integer
    
    Restore and update change 56352
    Test of 
https://cgit.freedesktop.org/libreoffice/core/commit/?id=8ca6468f0f4900d4d3bb45e0e938fe35c308512c
 is now ok
    
    Add disambiguation between '0' and '?' in integer part
    XML_MAX_BLANK_INTEGER_DIGITS is added for the number of '?'
    XML_MIN_INTEGER_DIGITS is the number of '?' and '0'
    This preserve compatibility with previous versions:
    in previous versions '?' will be transformed in '0'
    It also applies to scientific and fraction numbers.
    Integer part for number, scientific and fraction are
    treated the same way
    
    Include QA unit test
    
    Change-Id: Iab3127bf07223caac60e409306a1bee2edc37428
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145932
    Tested-by: Jenkins
    Reviewed-by: Eike Rathke <er...@redhat.com>

diff --git a/include/xmloff/xmlnumfe.hxx b/include/xmloff/xmlnumfe.hxx
index 5d971179bbd8..61836f534245 100644
--- a/include/xmloff/xmlnumfe.hxx
+++ b/include/xmloff/xmlnumfe.hxx
@@ -63,13 +63,14 @@ private:
     SAL_DLLPRIVATE void FinishTextElement_Impl(bool bUseExtensionNS = false);
 
     SAL_DLLPRIVATE void WriteColorElement_Impl( const Color& rColor );
+    SAL_DLLPRIVATE void WriteIntegerElement_Impl( sal_Int32 nInteger, 
sal_Int32 nBlankInteger, bool bGrouping );
     SAL_DLLPRIVATE void WriteNumberElement_Impl( sal_Int32 nDecimals, 
sal_Int32 nMinDecimals,
-                                        sal_Int32 nInteger, const OUString& 
rDashStr,
+                                        sal_Int32 nInteger, sal_Int32 
nBlankInteger, const OUString& rDashStr,
                                         bool bGrouping, sal_Int32 
nTrailingThousands,
                                         const SvXMLEmbeddedTextEntryArr& 
rEmbeddedEntries );
-    SAL_DLLPRIVATE void WriteScientificElement_Impl( sal_Int32 nDecimals, 
sal_Int32 nMinDecimals, sal_Int32 nInteger,
+    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 );
-    SAL_DLLPRIVATE void WriteFractionElement_Impl( sal_Int32 nInteger, bool 
bGrouping,
+    SAL_DLLPRIVATE void WriteFractionElement_Impl( sal_Int32 nInteger, 
sal_Int32 nBlankInteger, bool bGrouping,
                                                    const SvNumberformat& 
rFormat, sal_uInt16 nPart );
     SAL_DLLPRIVATE void WriteCurrencyElement_Impl( const OUString& rString,
                                         std::u16string_view rExt );
diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx
index e33a39deb449..3b45568e7d24 100644
--- a/include/xmloff/xmltoken.hxx
+++ b/include/xmloff/xmltoken.hxx
@@ -3445,6 +3445,7 @@ namespace xmloff::token {
         XML_ZEROS_NUMERATOR_DIGITS,
         XML_ZEROS_DENOMINATOR_DIGITS,
         XML_INTEGER_FRACTION_DELIMITER,
+        XML_MAX_BLANK_INTEGER_DIGITS,
 
         // tdf#115319
         XML_REFERENCE_LANGUAGE,
diff --git a/sc/qa/unit/data/contentCSV/testNumberFormats.csv 
b/sc/qa/unit/data/contentCSV/testNumberFormats.csv
new file mode 100644
index 000000000000..266f4baa8a46
--- /dev/null
+++ b/sc/qa/unit/data/contentCSV/testNumberFormats.csv
@@ -0,0 +1,32 @@
+;Integer;;;Thousand separator;;;Fraction;;;Fraction + Thousand 
separator;;;Scientific;;;Engineering;;
+Format;"""format=""000000";"""format=""??????";"""format=""??0000";"""format=""000,000";"""format=""???,???";"""format=""??0,000";"""format=""000
 ?/?";"""format=""??? ?/?";"""format=""?00 ?/?";"""format=""0,000 
?/?";"""format=""?,??? ?/?";"""format=""?,?00 
?/?";"""format=""0.000E+00";"""format=""?.###E+00";"""format=""?.0##E+00";"""format=""000E+00";"""format=""???E+00";"""format=""?00E+00"
+0.123456;format=000000;format=      ;format=  0000;format=000,000;format=      
 ;format=  0,000;format=000 1/8;format=    1/8;format= 00 1/8;format=0,000 
1/8;format=      1/8;format=   00 
1/8;format=1.235E-01;format=1.235E-01;format=1.235E-01;format=123E-03;format=123E-03;format=123E-03
+1.23456;format=000001;format=     1;format=  0001;format=000,001;format=      
1;format=  0,001;format=001 2/9;format=  1 2/9;format= 01 2/9;format=0,001 
2/9;format=    1 2/9;format=   01 
2/9;format=1.235E+00;format=1.235E+00;format=1.235E+00;format=001E+00;format=  
1E+00;format= 01E+00
+12.3456;format=000012;format=    12;format=  0012;format=000,012;format=     
12;format=  0,012;format=012 1/3;format= 12 1/3;format= 12 1/3;format=0,012 
1/3;format=   12 1/3;format=   12 
1/3;format=1.235E+01;format=1.235E+01;format=1.235E+01;format=012E+00;format= 
12E+00;format= 12E+00
+123.456;format=000123;format=   123;format=  0123;format=000,123;format=    
123;format=  0,123;format=123 4/9;format=123 4/9;format=123 4/9;format=0,123 
4/9;format=  123 4/9;format=  123 
4/9;format=1.235E+02;format=1.235E+02;format=1.235E+02;format=123E+00;format=123E+00;format=123E+00
+1234.56;format=001235;format=  1235;format=  1235;format=001,235;format=  
1,235;format=  1,235;format=1234 5/9;format=1234 5/9;format=1234 
5/9;format=1,234 5/9;format=1,234 5/9;format=1,234 
5/9;format=1.235E+03;format=1.235E+03;format=1.235E+03;format=001E+03;format=  
1E+03;format= 01E+03
+12345.6;format=012346;format= 12346;format= 12346;format=012,346;format= 
12,346;format= 12,346;format=12345 3/5;format=12345 3/5;format=12345 
3/5;format=12,345 3/5;format=12,345 3/5;format=12,345 
3/5;format=1.235E+04;format=1.235E+04;format=1.235E+04;format=012E+03;format= 
12E+03;format= 12E+03
+123456;format=123456;format=123456;format=123456;format=123,456;format=123,456;format=123,456;format=123456
    ;format=123456    ;format=123456    ;format=123,456    ;format=123,456    
;format=123,456    
;format=1.235E+05;format=1.235E+05;format=1.235E+05;format=123E+03;format=123E+03;format=123E+03
+1234560;format=1234560;format=1234560;format=1234560;format=1,234,560;format=1,234,560;format=1,234,560;format=1234560
    ;format=1234560    ;format=1234560    ;format=1,234,560    
;format=1,234,560    ;format=1,234,560    
;format=1.235E+06;format=1.235E+06;format=1.235E+06;format=001E+06;format=  
1E+06;format= 01E+06
+12345600;format=12345600;format=12345600;format=12345600;format=12,345,600;format=12,345,600;format=12,345,600;format=12345600
    ;format=12345600    ;format=12345600    ;format=12,345,600    
;format=12,345,600    ;format=12,345,600    
;format=1.235E+07;format=1.235E+07;format=1.235E+07;format=012E+06;format= 
12E+06;format= 12E+06
+-0.123456;format=000000;format=      ;format=  0000;format=000,000;format=     
  ;format=  0,000;-format=000 1/8;-format=    1/8;-format= 00 1/8;-format=0,000 
1/8;-format=      1/8;-format=   00 
1/8;-format=1.235E-01;-format=1.235E-01;-format=1.235E-01;-format=123E-03;-format=123E-03;-format=123E-03
+-1.23456;-format=000001;-format=     1;-format=  0001;-format=000,001;-format= 
     1;-format=  0,001;-format=001 2/9;-format=  1 2/9;-format= 01 
2/9;-format=0,001 2/9;-format=    1 2/9;-format=   01 
2/9;-format=1.235E+00;-format=1.235E+00;-format=1.235E+00;-format=001E+00;-format=
  1E+00;-format= 01E+00
+-12.3456;-format=000012;-format=    12;-format=  0012;-format=000,012;-format= 
    12;-format=  0,012;-format=012 1/3;-format= 12 1/3;-format= 12 
1/3;-format=0,012 1/3;-format=   12 1/3;-format=   12 
1/3;-format=1.235E+01;-format=1.235E+01;-format=1.235E+01;-format=012E+00;-format=
 12E+00;-format= 12E+00
+-123.456;-format=000123;-format=   123;-format=  0123;-format=000,123;-format= 
   123;-format=  0,123;-format=123 4/9;-format=123 4/9;-format=123 
4/9;-format=0,123 4/9;-format=  123 4/9;-format=  123 
4/9;-format=1.235E+02;-format=1.235E+02;-format=1.235E+02;-format=123E+00;-format=123E+00;-format=123E+00
+-1234.56;-format=001235;-format=  1235;-format=  1235;-format=001,235;-format= 
 1,235;-format=  1,235;-format=1234 5/9;-format=1234 5/9;-format=1234 
5/9;-format=1,234 5/9;-format=1,234 5/9;-format=1,234 
5/9;-format=1.235E+03;-format=1.235E+03;-format=1.235E+03;-format=001E+03;-format=
  1E+03;-format= 01E+03
+-12345.6;-format=012346;-format= 12346;-format= 12346;-format=012,346;-format= 
12,346;-format= 12,346;-format=12345 3/5;-format=12345 3/5;-format=12345 
3/5;-format=12,345 3/5;-format=12,345 3/5;-format=12,345 
3/5;-format=1.235E+04;-format=1.235E+04;-format=1.235E+04;-format=012E+03;-format=
 12E+03;-format= 12E+03
+-123456;-format=123456;-format=123456;-format=123456;-format=123,456;-format=123,456;-format=123,456;-format=123456
    ;-format=123456    ;-format=123456    ;-format=123,456    ;-format=123,456  
  ;-format=123,456    
;-format=1.235E+05;-format=1.235E+05;-format=1.235E+05;-format=123E+03;-format=123E+03;-format=123E+03
+-1234560;-format=1234560;-format=1234560;-format=1234560;-format=1,234,560;-format=1,234,560;-format=1,234,560;-format=1234560
    ;-format=1234560    ;-format=1234560    ;-format=1,234,560    
;-format=1,234,560    ;-format=1,234,560    
;-format=1.235E+06;-format=1.235E+06;-format=1.235E+06;-format=001E+06;-format= 
 1E+06;-format= 01E+06
+-12345600;-format=12345600;-format=12345600;-format=12345600;-format=12,345,600;-format=12,345,600;-format=12,345,600;-format=12345600
    ;-format=12345600    ;-format=12345600    ;-format=12,345,600    
;-format=12,345,600    ;-format=12,345,600    
;-format=1.235E+07;-format=1.235E+07;-format=1.235E+07;-format=012E+06;-format= 
12E+06;-format= 12E+06
+0.2;format=000000;format=      ;format=  0000;format=000,000;format=       
;format=  0,000;format=000 1/5;format=    1/5;format= 00 1/5;format=0,000 
1/5;format=      1/5;format=   00 
1/5;format=2.000E-01;format=2E-01;format=2.0E-01;format=200E-03;format=200E-03;format=200E-03
+2;format=000002;format=     2;format=  0002;format=000,002;format=      
2;format=  0,002;format=002    ;format=  2    ;format= 02    ;format=0,002    
;format=    2    ;format=   02    
;format=2.000E+00;format=2E+00;format=2.0E+00;format=002E+00;format=  
2E+00;format= 02E+00
+20;format=000020;format=    20;format=  0020;format=000,020;format=     
20;format=  0,020;format=020    ;format= 20    ;format= 20    ;format=0,020    
;format=   20    ;format=   20    
;format=2.000E+01;format=2E+01;format=2.0E+01;format=020E+00;format= 
20E+00;format= 20E+00
+200;format=000200;format=   200;format=  0200;format=000,200;format=    
200;format=  0,200;format=200    ;format=200    ;format=200    ;format=0,200    
;format=  200    ;format=  200    
;format=2.000E+02;format=2E+02;format=2.0E+02;format=200E+00;format=200E+00;format=200E+00
+2000;format=002000;format=  2000;format=  2000;format=002,000;format=  
2,000;format=  2,000;format=2000    ;format=2000    ;format=2000    
;format=2,000    ;format=2,000    ;format=2,000    
;format=2.000E+03;format=2E+03;format=2.0E+03;format=002E+03;format=  
2E+03;format= 02E+03
+20000;format=020000;format= 20000;format= 20000;format=020,000;format= 
20,000;format= 20,000;format=20000    ;format=20000    ;format=20000    
;format=20,000    ;format=20,000    ;format=20,000    
;format=2.000E+04;format=2E+04;format=2.0E+04;format=020E+03;format= 
20E+03;format= 20E+03
+200000;format=200000;format=200000;format=200000;format=200,000;format=200,000;format=200,000;format=200000
    ;format=200000    ;format=200000    ;format=200,000    ;format=200,000    
;format=200,000    
;format=2.000E+05;format=2E+05;format=2.0E+05;format=200E+03;format=200E+03;format=200E+03
+2000000;format=2000000;format=2000000;format=2000000;format=2,000,000;format=2,000,000;format=2,000,000;format=2000000
    ;format=2000000    ;format=2000000    ;format=2,000,000    
;format=2,000,000    ;format=2,000,000    
;format=2.000E+06;format=2E+06;format=2.0E+06;format=002E+06;format=  
2E+06;format= 02E+06
+20000000;format=20000000;format=20000000;format=20000000;format=20,000,000;format=20,000,000;format=20,000,000;format=20000000
    ;format=20000000    ;format=20000000    ;format=20,000,000    
;format=20,000,000    ;format=20,000,000    
;format=2.000E+07;format=2E+07;format=2.0E+07;format=020E+06;format= 
20E+06;format= 20E+06
+200000000;format=200000000;format=200000000;format=200000000;format=200,000,000;format=200,000,000;format=200,000,000;format=200000000
    ;format=200000000    ;format=200000000    ;format=200,000,000    
;format=200,000,000    ;format=200,000,000    
;format=2.000E+08;format=2E+08;format=2.0E+08;format=200E+06;format=200E+06;format=200E+06
+;;;;;;;;;;;;;;;;;;
+0;1;2;3;4;5;6;7;8;9;10;11;12;13;14;15;16;17;18
diff --git a/sc/qa/unit/data/ods/testNumberFormats.ods 
b/sc/qa/unit/data/ods/testNumberFormats.ods
new file mode 100644
index 000000000000..aa2f073c27ee
Binary files /dev/null and b/sc/qa/unit/data/ods/testNumberFormats.ods differ
diff --git a/sc/qa/unit/helper/qahelper.cxx b/sc/qa/unit/helper/qahelper.cxx
index c928af32ed4b..e132b5dd7bb5 100644
--- a/sc/qa/unit/helper/qahelper.cxx
+++ b/sc/qa/unit/helper/qahelper.cxx
@@ -133,11 +133,12 @@ void ScModelTestBase::testFile(const OUString& aFileName, 
ScDocument& rDoc, SCTA
     }
 }
 
-void ScModelTestBase::testCondFile(const OUString& aFileName, ScDocument* 
pDoc, SCTAB nTab)
+void ScModelTestBase::testCondFile( const OUString& aFileName, ScDocument* 
pDoc, SCTAB nTab, bool bCommaAsDelimiter )
 {
     conditional_format_handler aHandler(pDoc, nTab);
     orcus::csv::parser_config aConfig;
-    aConfig.delimiters.push_back(',');
+    if ( bCommaAsDelimiter )
+        aConfig.delimiters.push_back(',');
     aConfig.delimiters.push_back(';');
     aConfig.text_qualifier = '"';
     std::string aContent;
diff --git a/sc/qa/unit/helper/qahelper.hxx b/sc/qa/unit/helper/qahelper.hxx
index 67d58897ef2a..f632d57bdbbb 100644
--- a/sc/qa/unit/helper/qahelper.hxx
+++ b/sc/qa/unit/helper/qahelper.hxx
@@ -163,7 +163,7 @@ public:
     void testFile(const OUString& aFileName, ScDocument& rDoc, SCTAB nTab, 
StringType aStringFormat = StringType::StringValue);
 
     //need own handler because conditional formatting strings must be generated
-    void testCondFile(const OUString& aFileName, ScDocument* pDoc, SCTAB nTab);
+    void testCondFile(const OUString& aFileName, ScDocument* pDoc, SCTAB nTab, 
bool bCommaAsDelimiter = true);
 
     const SdrOle2Obj* getSingleOleObject(ScDocument& rDoc, sal_uInt16 nPage);
 
diff --git a/sc/qa/unit/subsequent_export_test.cxx 
b/sc/qa/unit/subsequent_export_test.cxx
index 5b66ae23f3d9..aefd1a8eda48 100644
--- a/sc/qa/unit/subsequent_export_test.cxx
+++ b/sc/qa/unit/subsequent_export_test.cxx
@@ -30,6 +30,8 @@
 
 #include <svx/svdpage.hxx>
 #include <svx/svdograf.hxx>
+#include <svl/zformat.hxx>
+#include <svl/numformat.hxx>
 #include <tabprotection.hxx>
 #include <editeng/wghtitem.hxx>
 #include <editeng/postitem.hxx>
@@ -183,6 +185,7 @@ public:
     void testPreserveTextWhitespace2XLSX();
     void testTdf113646();
     void testDateStandardfilterXLSX();
+    void testNumberFormatODS();
 
     CPPUNIT_TEST_SUITE(ScExportTest);
     CPPUNIT_TEST(test);
@@ -290,6 +293,7 @@ public:
     CPPUNIT_TEST(testMoveCellAnchoredShapesODS);
     CPPUNIT_TEST(testTdf113646);
     CPPUNIT_TEST(testDateStandardfilterXLSX);
+    CPPUNIT_TEST(testNumberFormatODS);
     CPPUNIT_TEST_SUITE_END();
 
 private:
@@ -4305,6 +4309,33 @@ void ScExportTest::testDateStandardfilterXLSX()
                 "dateTimeGrouping", "day");
 }
 
+void ScExportTest::testNumberFormatODS()
+{
+    createScDoc("ods/testNumberFormats.ods");
+    saveAndReload("calc8");
+    ScDocument* pDoc = getScDoc();
+    sal_uInt32 nNumberFormat;
+    const sal_Int32 nCountFormats = 18;
+    const OUString aExpectedFormatStr[nCountFormats]
+        = { "\"format=\"000000",        "\"format=\"??????",        
"\"format=\"??0000",
+            "\"format=\"000,000",       "\"format=\"???,???",       
"\"format=\"??0,000",
+            "\"format=\"000\" \"?/?",   "\"format=\"???\" \"?/?",   
"\"format=\"?00\" \"?/?",
+            "\"format=\"0,000\" \"?/?", "\"format=\"?,???\" \"?/?", 
"\"format=\"?,?00\" \"?/?",
+            "\"format=\"0.000E+00",     "\"format=\"?.###E+00",     
"\"format=\"?.0##E+00",
+            "\"format=\"000E+00",       "\"format=\"???E+00",       
"\"format=\"?00E+00" };
+    for (sal_Int32 i = 0; i < nCountFormats; i++)
+    {
+        nNumberFormat = pDoc->GetNumberFormat(i + 1, 2, 0);
+        const SvNumberformat* pNumberFormat = 
pDoc->GetFormatTable()->GetEntry(nNumberFormat);
+        const OUString& rFormatStr = pNumberFormat->GetFormatstring();
+        CPPUNIT_ASSERT_EQUAL_MESSAGE("Number format modified during 
export/import",
+                                     aExpectedFormatStr[i], rFormatStr);
+    }
+    OUString aCSVPath = createFilePath(u"contentCSV/testNumberFormats.csv");
+    testCondFile(aCSVPath, &*pDoc, 0,
+                 false); // comma is thousand separator and cannot be used as 
delimiter
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(ScExportTest);
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng 
b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
index 98e6015ad23f..e6510e6c0d7a 100644
--- a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
+++ b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
@@ -2633,6 +2633,15 @@ 
xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.
     </rng:optional>
   </rng:define>
 
+  <rng:define name="common-number-attlist" combine="interleave">
+    <!-- TODO no proposal,  -->
+    <rng:optional>
+      <rng:attribute name="loext:max-blank-integer-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/source/numbers/zformat.cxx b/svl/source/numbers/zformat.cxx
index eb790ee425b0..effb34ad996f 100644
--- a/svl/source/numbers/zformat.cxx
+++ b/svl/source/numbers/zformat.cxx
@@ -4947,9 +4947,10 @@ void SvNumberformat::GetNumForInfo( sal_uInt16 nNumFor, 
SvNumFormatType& rScanne
                 {
                     p++;
                 }
-                while ( *p++ == '0' )
+                while ( *p == '0' || *p == '?' )
                 {
                     nLeadingCnt++;
+                    p++;
                 }
             }
             else if (nType == NF_SYMBOLTYPE_DECSEP
diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx
index d1d2047a50b9..78ef34951013 100644
--- a/xmloff/source/core/xmltoken.cxx
+++ b/xmloff/source/core/xmltoken.cxx
@@ -3450,6 +3450,7 @@ namespace xmloff::token {
         TOKEN( "zeros-numerator-digits",          XML_ZEROS_NUMERATOR_DIGITS ),
         TOKEN( "zeros-denominator-digits",        XML_ZEROS_DENOMINATOR_DIGITS 
),
         TOKEN( "integer-fraction-delimiter",      
XML_INTEGER_FRACTION_DELIMITER ),
+        TOKEN( "max-blank-integer-digits",        XML_MAX_BLANK_INTEGER_DIGITS 
),
 
         // for optional language-dependent reference formats
         TOKEN( "reference-language",              XML_REFERENCE_LANGUAGE ),
diff --git a/xmloff/source/style/xmlnumfe.cxx b/xmloff/source/style/xmlnumfe.cxx
index 01367fe12e55..7d44f43342ef 100644
--- a/xmloff/source/style/xmlnumfe.cxx
+++ b/xmloff/source/style/xmlnumfe.cxx
@@ -532,9 +532,32 @@ void SvXMLNumFmtExport::WriteAMPMElement_Impl()
 
 //  numbers
 
+void SvXMLNumFmtExport::WriteIntegerElement_Impl(
+                            sal_Int32 nInteger, sal_Int32 nBlankInteger, bool 
bGrouping )
+{
+    //  integer digits: '0' and '?'
+    if ( nInteger >= 0 )    // negative = automatic
+    {
+        rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_INTEGER_DIGITS,
+                              OUString::number( nInteger ) );
+    }
+    SvtSaveOptions::ODFSaneDefaultVersion eVersion = 
rExport.getSaneDefaultVersion();
+    //  blank integer digits: '?'
+    if ( nBlankInteger > 0 && ( (eVersion & SvtSaveOptions::ODFSVER_EXTENDED) 
!= 0 ) )
+    {
+        rExport.AddAttribute( XML_NAMESPACE_LO_EXT, 
XML_MAX_BLANK_INTEGER_DIGITS,
+                              OUString::number( nBlankInteger ) );
+    }
+    //  (automatic) grouping separator
+    if ( bGrouping )
+    {
+        rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_GROUPING, XML_TRUE );
+    }
+}
+
 void SvXMLNumFmtExport::WriteNumberElement_Impl(
                             sal_Int32 nDecimals, sal_Int32 nMinDecimals,
-                            sal_Int32 nInteger, const OUString& rDashStr,
+                            sal_Int32 nInteger, sal_Int32 nBlankInteger, const 
OUString& rDashStr,
                             bool bGrouping, sal_Int32 nTrailingThousands,
                             const SvXMLEmbeddedTextEntryArr& rEmbeddedEntries )
 {
@@ -547,10 +570,10 @@ void SvXMLNumFmtExport::WriteNumberElement_Impl(
                               OUString::number( nDecimals ) );
     }
 
+    SvtSaveOptions::ODFSaneDefaultVersion eVersion = 
rExport.getSaneDefaultVersion();
     if ( nMinDecimals >= 0 )   // negative = automatic
     {
         // Export only for 1.2 with extensions or 1.3 and later.
-        SvtSaveOptions::ODFSaneDefaultVersion eVersion = 
rExport.getSaneDefaultVersion();
         if (eVersion > SvtSaveOptions::ODFSVER_012)
         {
             // OFFICE-3860 For 1.2+ use loext namespace, for 1.3 use number 
namespace.
@@ -560,14 +583,6 @@ void SvXMLNumFmtExport::WriteNumberElement_Impl(
                                  OUString::number( nMinDecimals ) );
         }
     }
-
-    //  integer digits
-    if ( nInteger >= 0 )    // negative = automatic
-    {
-        rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_INTEGER_DIGITS,
-                              OUString::number( nInteger ) );
-    }
-
     //  decimal replacement (dashes) or variable decimals (#)
     if ( !rDashStr.isEmpty() ||  nMinDecimals < nDecimals )
     {
@@ -576,11 +591,7 @@ void SvXMLNumFmtExport::WriteNumberElement_Impl(
                               rDashStr );
     }
 
-    //  (automatic) grouping separator
-    if ( bGrouping )
-    {
-        rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_GROUPING, XML_TRUE );
-    }
+    WriteIntegerElement_Impl( nInteger, nBlankInteger, bGrouping );
 
     //  display-factor if there are trailing thousands separators
     if ( nTrailingThousands )
@@ -626,7 +637,7 @@ void SvXMLNumFmtExport::WriteNumberElement_Impl(
 }
 
 void SvXMLNumFmtExport::WriteScientificElement_Impl(
-                            sal_Int32 nDecimals, sal_Int32 nMinDecimals, 
sal_Int32 nInteger,
+                            sal_Int32 nDecimals, sal_Int32 nMinDecimals, 
sal_Int32 nInteger, sal_Int32 nBlankInteger,
                             bool bGrouping, sal_Int32 nExp, sal_Int32 
nExpInterval, bool bExpSign )
 {
     FinishTextElement_Impl();
@@ -652,18 +663,7 @@ void SvXMLNumFmtExport::WriteScientificElement_Impl(
         }
     }
 
-    //  integer digits
-    if ( nInteger >= 0 )    // negative = automatic
-    {
-        rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_INTEGER_DIGITS,
-                              OUString::number( nInteger ) );
-    }
-
-    //  (automatic) grouping separator
-    if ( bGrouping )
-    {
-        rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_GROUPING, XML_TRUE );
-    }
+    WriteIntegerElement_Impl( nInteger, nBlankInteger, bGrouping );
 
     //  exponent digits
     if ( nExp >= 0 )
@@ -702,10 +702,12 @@ void SvXMLNumFmtExport::WriteScientificElement_Impl(
 }
 
 void SvXMLNumFmtExport::WriteFractionElement_Impl(
-                            sal_Int32 nInteger, bool bGrouping,
+                            sal_Int32 nInteger, sal_Int32 nBlankInteger, bool 
bGrouping,
                             const SvNumberformat& rFormat, sal_uInt16 nPart )
 {
     FinishTextElement_Impl();
+    WriteIntegerElement_Impl( nInteger, nBlankInteger, bGrouping );
+
     const OUString aNumeratorString = rFormat.GetNumeratorString( nPart );
     const OUString aDenominatorString = rFormat.GetDenominatorString( nPart );
     const OUString aIntegerFractionDelimiterString = 
rFormat.GetIntegerFractionDelimiterString( nPart );
@@ -734,21 +736,9 @@ void SvXMLNumFmtExport::WriteFractionElement_Impl(
         nZerosDenominatorDigits = 0;
     sal_Int32 nDenominator = aDenominatorString.toInt32();
 
-    //  integer digits
-    if ( nInteger >= 0 )        // negative = default (no integer part)
-    {
-        rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_INTEGER_DIGITS,
-                              OUString::number( nInteger ) );
-    }
-
-    //  (automatic) grouping separator
-    if ( bGrouping )
-    {
-        rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_GROUPING, XML_TRUE );
-    }
+    SvtSaveOptions::ODFSaneDefaultVersion eVersion = 
rExport.getSaneDefaultVersion();
 
     // integer/fraction delimiter
-    SvtSaveOptions::ODFSaneDefaultVersion eVersion = 
rExport.getSaneDefaultVersion();
     if ( !aIntegerFractionDelimiterString.isEmpty() && 
aIntegerFractionDelimiterString != " "
         && ((eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0) )
     {   // Export only for 1.2/1.3 with extensions.
@@ -1249,7 +1239,7 @@ void SvXMLNumFmtExport::ExportPart_Impl( const 
SvNumberformat& rFormat, sal_uInt
     if ( eBuiltIn == NF_NUMBER_STANDARD )
     {
         //  default number format contains just one number element
-        WriteNumberElement_Impl( -1, -1, 1, OUString(), false, 0, 
aEmbeddedEntries );
+        WriteNumberElement_Impl( -1, -1, 1, -1, OUString(), false, 0, 
aEmbeddedEntries );
         bAnyContent = true;
     }
     else if ( eBuiltIn == NF_BOOLEAN )
@@ -1303,6 +1293,7 @@ void SvXMLNumFmtExport::ExportPart_Impl( const 
SvNumberformat& rFormat, sal_uInt
         sal_Int32 nIntegerSymbols = 0;          // for embedded-text, 
including "#"
         sal_Int32 nTrailingThousands = 0;       // thousands-separators after 
all digits
         sal_Int32 nMinDecimals = nPrecision;
+        sal_Int32 nBlankInteger = 0;
         OUString sCurrExt;
         OUString aCalendar;
         bool bImplicitOtherCalendar = false;
@@ -1327,7 +1318,7 @@ void SvXMLNumFmtExport::ExportPart_Impl( const 
SvNumberformat& rFormat, sal_uInt
                         bDecDashes = true;
                         nMinDecimals = 0;
                     }
-                    else if ( !bInInteger && pElemStr )
+                    else if ( nFmtType != SvNumFormatType::FRACTION && 
!bInInteger && pElemStr )
                     {
                         for ( sal_Int32 i = pElemStr->getLength()-1; i >= 0 ; 
i-- )
                         {
@@ -1343,9 +1334,17 @@ void SvXMLNumFmtExport::ExportPart_Impl( const 
SvNumberformat& rFormat, sal_uInt
                         }
                     }
                     if ( bInInteger && pElemStr )
+                    {
                         nIntegerSymbols += pElemStr->getLength();
+                        for ( sal_Int32 i = pElemStr->getLength()-1; i >= 0 ; 
i-- )
+                        {
+                            if ( (*pElemStr)[i] == '?' )
+                                nBlankInteger ++;
+                        }
+                    }
                     nTrailingThousands = 0;
                     break;
+                case NF_SYMBOLTYPE_FRACBLANK:
                 case NF_SYMBOLTYPE_DECSEP:
                     bInInteger = false;
                     break;
@@ -1507,7 +1506,7 @@ void SvXMLNumFmtExport::ExportPart_Impl( const 
SvNumberformat& rFormat, sal_uInt
                     }
                     break;
                 case NF_KEY_GENERAL :
-                        WriteNumberElement_Impl( -1, -1, 1, OUString(), false, 
0, aEmbeddedEntries );
+                        WriteNumberElement_Impl( -1, -1, 1, -1, OUString(), 
false, 0, aEmbeddedEntries );
                         bAnyContent = true;
                     break;
                 case NF_KEY_CCC:
@@ -1566,7 +1565,10 @@ void SvXMLNumFmtExport::ExportPart_Impl( const 
SvNumberformat& rFormat, sal_uInt
                                     //  only one built-in format has automatic 
integer digits
                                     sal_Int32 nInteger = nLeading;
                                     if ( eBuiltIn == NF_NUMBER_SYSTEM )
+                                    {
                                         nInteger = -1;
+                                        nBlankInteger = -1;
+                                    }
 
                                     //  string for decimal replacement
                                     //  has to be taken from nPrecision
@@ -1578,16 +1580,16 @@ void SvXMLNumFmtExport::ExportPart_Impl( const 
SvNumberformat& rFormat, sal_uInt
                                     if (bDecAlign && nPrecision > 0)
                                         sDashStr = " ";
 
-                                    WriteNumberElement_Impl(nDecimals, 
nMinDecimals, nInteger, sDashStr.makeStringAndClear(),
+                                    WriteNumberElement_Impl(nDecimals, 
nMinDecimals, nInteger, nBlankInteger, sDashStr.makeStringAndClear(),
                                         bThousand, nTrailingThousands, 
aEmbeddedEntries);
                                     bAnyContent = true;
                                 }
                                 break;
                             case SvNumFormatType::SCIENTIFIC:
-                                // #i43959# for scientific numbers, count all 
integer symbols ("0" and "#")
+                                // #i43959# for scientific numbers, count all 
integer symbols ("0", "?" and "#")
                                 // as integer digits: use nIntegerSymbols 
instead of nLeading
                                 // nIntegerSymbols represents exponent 
interval (for engineering notation)
-                                WriteScientificElement_Impl( nPrecision, 
nMinDecimals, nLeading, bThousand, nExpDigits, nIntegerSymbols, bExpSign );
+                                WriteScientificElement_Impl( nPrecision, 
nMinDecimals, nLeading, nBlankInteger, bThousand, nExpDigits, nIntegerSymbols, 
bExpSign );
                                 bAnyContent = true;
                                 break;
                             case SvNumFormatType::FRACTION:
@@ -1599,8 +1601,9 @@ void SvXMLNumFmtExport::ExportPart_Impl( const 
SvNumberformat& rFormat, sal_uInt
                                         //  the fraction doesn't have an 
integer part, and no
                                         //  min-integer-digits attribute must 
be written.
                                         nInteger = -1;
+                                        nBlankInteger = -1;
                                     }
-                                    WriteFractionElement_Impl( nInteger, 
bThousand,  rFormat, nPart );
+                                    WriteFractionElement_Impl( nInteger, 
nBlankInteger, bThousand,  rFormat, nPart );
                                     bAnyContent = true;
                                 }
                                 break;
diff --git a/xmloff/source/style/xmlnumfi.cxx b/xmloff/source/style/xmlnumfi.cxx
index 370af2bde369..7cb1b7d679ef 100644
--- a/xmloff/source/style/xmlnumfi.cxx
+++ b/xmloff/source/style/xmlnumfi.cxx
@@ -88,7 +88,8 @@ public:
 struct SvXMLNumberInfo
 {
     sal_Int32   nDecimals           = -1;
-    sal_Int32   nInteger            = -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   nExpInterval        = -1;
     sal_Int32   nMinNumerDigits     = -1;
@@ -675,6 +676,11 @@ SvXMLNumFmtElementContext::SvXMLNumFmtElementContext( 
SvXMLImport& rImport,
                 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 
0 ))
                     aNumInfo.nInteger = nAttrVal;
                 break;
+            case XML_ELEMENT(LO_EXT, XML_MAX_BLANK_INTEGER_DIGITS):
+            case XML_ELEMENT(NUMBER, XML_MAX_BLANK_INTEGER_DIGITS):
+                if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 
0 ))
+                    aNumInfo.nBlankInteger = nAttrVal;
+                break;
             case XML_ELEMENT(NUMBER, XML_GROUPING):
                 if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
                     aNumInfo.bGrouping = bAttrBool;
@@ -776,6 +782,8 @@ SvXMLNumFmtElementContext::SvXMLNumFmtElementContext( 
SvXMLImport& rImport,
                 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
         }
     }
+    if ( aNumInfo.nBlankInteger > aNumInfo.nInteger )
+        aNumInfo.nInteger = aNumInfo.nBlankInteger;
     if ( aNumInfo.nMinDecimalDigits == -1)
     {
         if ( bVarDecimals || aNumInfo.bDecReplace )
@@ -1722,6 +1730,25 @@ void SvXMLNumFormatContext::AddNumber( const 
SvXMLNumberInfo& rInfo )
         aNumStr.stripStart('#');
     }
 
+    if ( rInfo.nBlankInteger > 0 )
+    {
+        // Replace nBlankInteger '0' by '?'
+        sal_Int32 nIndex = 0;
+        sal_Int32 nBlanks = rInfo.nBlankInteger;
+        sal_Int32 nIntegerEnd = aNumStr.indexOf( 
pFormatter->GetNumDecimalSep() );
+        if ( nIntegerEnd < 0 )
+            nIntegerEnd = aNumStr.getLength();
+        while ( nIndex < nIntegerEnd && nBlanks > 0 )
+        {
+            if ( aNumStr[nIndex] == '0' )
+            {
+                aNumStr[nIndex] = '?';
+                nBlanks--;
+            }
+            nIndex++;
+        }
+    }
+
     if ( bGrouping && rInfo.nExpInterval > rInfo.nInteger )
     {
         sal_Int32 nIndex = 0;
diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt
index e4a0a5fe7c36..74a725d91b30 100644
--- a/xmloff/source/token/tokens.txt
+++ b/xmloff/source/token/tokens.txt
@@ -3206,6 +3206,7 @@ max-numerator-digits
 zeros-numerator-digits
 zeros-denominator-digits
 integer-fraction-delimiter
+max-blank-integer-digits
 reference-language
 newline
 creator-initials

Reply via email to