Coverage Report

Created: 2025-12-08 09:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/xmloff/source/style/xmlnumfe.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <comphelper/sequence.hxx>
21
#include <comphelper/string.hxx>
22
#include <svl/numformat.hxx>
23
#include <svl/zforlist.hxx>
24
#include <svl/zformat.hxx>
25
#include <svl/numuno.hxx>
26
#include <i18nlangtag/mslangid.hxx>
27
#include <i18nlangtag/languagetag.hxx>
28
#include <tools/debug.hxx>
29
#include <rtl/math.hxx>
30
#include <unotools/calendarwrapper.hxx>
31
#include <unotools/charclass.hxx>
32
#include <com/sun/star/lang/Locale.hpp>
33
#include <rtl/ustrbuf.hxx>
34
#include <sal/log.hxx>
35
#include <osl/diagnose.h>
36
#include <tools/color.hxx>
37
#include <sax/tools/converter.hxx>
38
39
#include <com/sun/star/i18n/NativeNumberXmlAttributes2.hpp>
40
41
#include <utility>
42
#include <xmloff/xmlnumfe.hxx>
43
#include <xmloff/xmlnamespace.hxx>
44
#include <xmloff/xmlnumfi.hxx>
45
46
#include <svl/nfsymbol.hxx>
47
#include <xmloff/xmltoken.hxx>
48
#include <xmloff/xmlexp.hxx>
49
#include <o3tl/string_view.hxx>
50
51
#include <float.h>
52
#include <set>
53
#include <string_view>
54
#include <vector>
55
56
using namespace ::com::sun::star;
57
using namespace ::xmloff::token;
58
using namespace ::svt;
59
60
typedef std::set< sal_uInt32 >  SvXMLuInt32Set;
61
62
namespace {
63
64
struct SvXMLEmbeddedTextEntry
65
{
66
    sal_uInt16      nSourcePos;     // position in NumberFormat (to skip later)
67
    sal_Int32       nFormatPos;     // resulting position in embedded-text element
68
    OUString   aText;
69
    bool            isBlankWidth;   // "_x"
70
71
    SvXMLEmbeddedTextEntry( sal_uInt16 nSP, sal_Int32 nFP, OUString aT, bool bBW = false ) :
72
0
        nSourcePos(nSP), nFormatPos(nFP), aText(std::move(aT)), isBlankWidth( bBW ) {}
73
};
74
75
}
76
77
class SvXMLEmbeddedTextEntryArr
78
{
79
    typedef std::vector<SvXMLEmbeddedTextEntry> DataType;
80
    DataType maData;
81
82
public:
83
84
    void push_back( SvXMLEmbeddedTextEntry const& r )
85
0
    {
86
0
        maData.push_back(r);
87
0
    }
88
89
    const SvXMLEmbeddedTextEntry& operator[] ( size_t i ) const
90
0
    {
91
0
        return maData[i];
92
0
    }
93
94
    size_t size() const
95
0
    {
96
0
        return maData.size();
97
0
    }
98
};
99
100
class SvXMLNumUsedList_Impl
101
{
102
    SvXMLuInt32Set              aUsed;
103
    SvXMLuInt32Set              aWasUsed;
104
    SvXMLuInt32Set::iterator    aCurrentUsedPos;
105
    sal_uInt32                  nUsedCount;
106
    sal_uInt32                  nWasUsedCount;
107
108
public:
109
            SvXMLNumUsedList_Impl();
110
111
    void    SetUsed( sal_uInt32 nKey );
112
    bool    IsUsed( sal_uInt32 nKey ) const;
113
    bool    IsWasUsed( sal_uInt32 nKey ) const;
114
    void    Export();
115
116
    bool    GetFirstUsed(sal_uInt32& nKey);
117
    bool    GetNextUsed(sal_uInt32& nKey);
118
119
    uno::Sequence<sal_Int32> GetWasUsed() const;
120
    void SetWasUsed(const uno::Sequence<sal_Int32>& rWasUsed);
121
};
122
123
//! SvXMLNumUsedList_Impl should be optimized!
124
125
SvXMLNumUsedList_Impl::SvXMLNumUsedList_Impl() :
126
0
    nUsedCount(0),
127
0
    nWasUsedCount(0)
128
0
{
129
0
}
130
131
void SvXMLNumUsedList_Impl::SetUsed( sal_uInt32 nKey )
132
0
{
133
0
    if ( !IsWasUsed(nKey) )
134
0
    {
135
0
        std::pair<SvXMLuInt32Set::iterator, bool> aPair = aUsed.insert( nKey );
136
0
        if (aPair.second)
137
0
            nUsedCount++;
138
0
    }
139
0
}
140
141
bool SvXMLNumUsedList_Impl::IsUsed( sal_uInt32 nKey ) const
142
0
{
143
0
    SvXMLuInt32Set::const_iterator aItr = aUsed.find(nKey);
144
0
    return (aItr != aUsed.end());
145
0
}
146
147
bool SvXMLNumUsedList_Impl::IsWasUsed( sal_uInt32 nKey ) const
148
0
{
149
0
    SvXMLuInt32Set::const_iterator aItr = aWasUsed.find(nKey);
150
0
    return (aItr != aWasUsed.end());
151
0
}
152
153
void SvXMLNumUsedList_Impl::Export()
154
0
{
155
0
    SvXMLuInt32Set::const_iterator aItr = aUsed.begin();
156
0
    while (aItr != aUsed.end())
157
0
    {
158
0
        std::pair<SvXMLuInt32Set::const_iterator, bool> aPair = aWasUsed.insert( *aItr );
159
0
        if (aPair.second)
160
0
            nWasUsedCount++;
161
0
        ++aItr;
162
0
    }
163
0
    aUsed.clear();
164
0
    nUsedCount = 0;
165
0
}
166
167
bool SvXMLNumUsedList_Impl::GetFirstUsed(sal_uInt32& nKey)
168
0
{
169
0
    bool bRet(false);
170
0
    aCurrentUsedPos = aUsed.begin();
171
0
    if(nUsedCount)
172
0
    {
173
0
        DBG_ASSERT(aCurrentUsedPos != aUsed.end(), "something went wrong");
174
0
        nKey = *aCurrentUsedPos;
175
0
        bRet = true;
176
0
    }
177
0
    return bRet;
178
0
}
179
180
bool SvXMLNumUsedList_Impl::GetNextUsed(sal_uInt32& nKey)
181
0
{
182
0
    bool bRet(false);
183
0
    if (aCurrentUsedPos != aUsed.end())
184
0
    {
185
0
        ++aCurrentUsedPos;
186
0
        if (aCurrentUsedPos != aUsed.end())
187
0
        {
188
0
            nKey = *aCurrentUsedPos;
189
0
            bRet = true;
190
0
        }
191
0
    }
192
0
    return bRet;
193
0
}
194
195
uno::Sequence<sal_Int32> SvXMLNumUsedList_Impl::GetWasUsed() const
196
0
{
197
0
    return comphelper::containerToSequence<sal_Int32>(aWasUsed);
198
0
}
199
200
void SvXMLNumUsedList_Impl::SetWasUsed(const uno::Sequence<sal_Int32>& rWasUsed)
201
0
{
202
0
    DBG_ASSERT(nWasUsedCount == 0, "WasUsed should be empty");
203
0
    for (const auto nWasUsed : rWasUsed)
204
0
    {
205
0
        std::pair<SvXMLuInt32Set::const_iterator, bool> aPair = aWasUsed.insert( nWasUsed );
206
0
        if (aPair.second)
207
0
            nWasUsedCount++;
208
0
    }
209
0
}
210
211
SvXMLNumFmtExport::SvXMLNumFmtExport(
212
            SvXMLExport& rExp,
213
            const uno::Reference< util::XNumberFormatsSupplier >& rSupp ) :
214
0
    m_rExport( rExp ),
215
0
    m_sPrefix( u"N"_ustr ),
216
0
    m_pFormatter( nullptr ),
217
0
    m_bHasText( false )
218
0
{
219
    //  supplier must be SvNumberFormatsSupplierObj
220
0
    SvNumberFormatsSupplierObj* pObj =
221
0
                    comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>( rSupp );
222
0
    if (pObj)
223
0
        m_pFormatter = pObj->GetNumberFormatter();
224
225
0
    if ( m_pFormatter )
226
0
    {
227
0
        m_pLocaleData = LocaleDataWrapper::get( m_pFormatter->GetLanguageTag() );
228
0
    }
229
0
    else
230
0
    {
231
0
        LanguageTag aLanguageTag( MsLangId::getConfiguredSystemLanguage() );
232
233
0
        m_pLocaleData = LocaleDataWrapper::get( std::move(aLanguageTag) );
234
0
    }
235
236
0
    m_pUsedList.reset(new SvXMLNumUsedList_Impl);
237
0
}
238
239
SvXMLNumFmtExport::SvXMLNumFmtExport(
240
                       SvXMLExport& rExp,
241
                       const css::uno::Reference< css::util::XNumberFormatsSupplier >& rSupp,
242
                       OUString aPrefix ) :
243
0
    m_rExport( rExp ),
244
0
    m_sPrefix(std::move( aPrefix )),
245
0
    m_pFormatter( nullptr ),
246
0
    m_bHasText( false )
247
0
{
248
    //  supplier must be SvNumberFormatsSupplierObj
249
0
    SvNumberFormatsSupplierObj* pObj =
250
0
                    comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>( rSupp );
251
0
    if (pObj)
252
0
        m_pFormatter = pObj->GetNumberFormatter();
253
254
0
    if ( m_pFormatter )
255
0
    {
256
0
        m_pLocaleData = LocaleDataWrapper::get( m_pFormatter->GetLanguageTag() );
257
0
    }
258
0
    else
259
0
    {
260
0
        LanguageTag aLanguageTag( MsLangId::getConfiguredSystemLanguage() );
261
262
0
        m_pLocaleData = LocaleDataWrapper::get( std::move(aLanguageTag) );
263
0
    }
264
265
0
    m_pUsedList.reset(new SvXMLNumUsedList_Impl);
266
0
}
267
268
SvXMLNumFmtExport::~SvXMLNumFmtExport()
269
0
{
270
0
}
271
272
//  helper methods
273
274
static OUString lcl_CreateStyleName( sal_Int32 nKey, sal_Int32 nPart, bool bDefPart, std::u16string_view rPrefix )
275
0
{
276
0
    if (bDefPart)
277
0
        return rPrefix + OUString::number(nKey);
278
0
    else
279
0
        return rPrefix + OUString::number(nKey) + "P" + OUString::number( nPart );
280
0
}
281
282
void SvXMLNumFmtExport::AddCalendarAttr_Impl( const OUString& rCalendar )
283
0
{
284
0
    if ( !rCalendar.isEmpty() )
285
0
    {
286
0
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_CALENDAR, rCalendar );
287
0
    }
288
0
}
289
290
void SvXMLNumFmtExport::AddStyleAttr_Impl( bool bLong )
291
0
{
292
0
    if ( bLong )            // short is default
293
0
    {
294
0
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_STYLE, XML_LONG );
295
0
    }
296
0
}
297
298
void SvXMLNumFmtExport::AddLanguageAttr_Impl( LanguageType nLang )
299
0
{
300
0
    if ( nLang != LANGUAGE_SYSTEM )
301
0
    {
302
0
        m_rExport.AddLanguageTagAttributes( XML_NAMESPACE_NUMBER, XML_NAMESPACE_NUMBER,
303
0
                LanguageTag( nLang), false);
304
0
    }
305
0
}
306
307
//  methods to write individual elements within a format
308
309
void SvXMLNumFmtExport::AddToTextElement_Impl( std::u16string_view rString )
310
0
{
311
    //  append to sTextContent, write element in FinishTextElement_Impl
312
    //  to avoid several text elements following each other
313
314
0
    m_sTextContent.append( rString );
315
    // Also empty string leads to a number:text element as it may separate
316
    // keywords of the same letter (e.g. MM""MMM) that otherwise would be
317
    // concatenated when reading back in.
318
0
    m_bHasText = true;
319
0
}
320
321
void SvXMLNumFmtExport::FinishTextElement_Impl(bool bUseExtensionNS)
322
0
{
323
0
    if ( m_bHasText )
324
0
    {
325
0
        if ( !m_sBlankWidthString.isEmpty() )
326
0
        {
327
            // Export only for 1.3 with extensions and later.
328
0
            SvtSaveOptions::ODFSaneDefaultVersion eVersion = m_rExport.getSaneDefaultVersion();
329
0
            if (eVersion > SvtSaveOptions::ODFSVER_013 && ( (eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0 ))
330
0
            {
331
0
                m_rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_BLANK_WIDTH_CHAR,
332
0
                                      m_sBlankWidthString.makeStringAndClear() );
333
0
            }
334
0
        }
335
0
        sal_uInt16 nNS = bUseExtensionNS ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER;
336
0
        SvXMLElementExport aElem( m_rExport, nNS, XML_TEXT,
337
0
                                  true, false );
338
0
        m_rExport.Characters( m_sTextContent.makeStringAndClear() );
339
0
        m_bHasText = false;
340
0
    }
341
0
}
342
343
void SvXMLNumFmtExport::WriteColorElement_Impl( const Color& rColor )
344
0
{
345
0
    FinishTextElement_Impl();
346
347
0
    OUStringBuffer aColStr( 7 );
348
0
    ::sax::Converter::convertColor( aColStr, rColor );
349
0
    m_rExport.AddAttribute( XML_NAMESPACE_FO, XML_COLOR,
350
0
                          aColStr.makeStringAndClear() );
351
352
0
    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_STYLE, XML_TEXT_PROPERTIES,
353
0
                              true, false );
354
0
}
355
356
void SvXMLNumFmtExport::WriteCurrencyElement_Impl( const OUString& rString,
357
                                                    std::u16string_view rExt )
358
0
{
359
0
    FinishTextElement_Impl();
360
361
0
    if ( !rExt.empty() )
362
0
    {
363
        // rExt should be a 16-bit hex value max FFFF which may contain a
364
        // leading "-" separator (that is not a minus sign, but toInt32 can be
365
        // used to parse it, with post-processing as necessary):
366
0
        sal_Int32 nLang = o3tl::toInt32(rExt, 16);
367
0
        if ( nLang < 0 )
368
0
            nLang = -nLang;
369
0
        SAL_WARN_IF(nLang > 0xFFFF, "xmloff.style", "Out of range Lang Id: " << nLang << " from input string: " << OUString(rExt));
370
0
        AddLanguageAttr_Impl( LanguageType(nLang & 0xFFFF) );          // adds to pAttrList
371
0
    }
372
373
0
    SvXMLElementExport aElem( m_rExport,
374
0
                              XML_NAMESPACE_NUMBER, XML_CURRENCY_SYMBOL,
375
0
                              true, false );
376
0
    m_rExport.Characters( rString );
377
0
}
378
379
void SvXMLNumFmtExport::WriteBooleanElement_Impl()
380
0
{
381
0
    FinishTextElement_Impl();
382
383
0
    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_BOOLEAN,
384
0
                              true, false );
385
0
}
386
387
void SvXMLNumFmtExport::WriteTextContentElement_Impl()
388
0
{
389
0
    FinishTextElement_Impl();
390
391
0
    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_TEXT_CONTENT,
392
0
                              true, false );
393
0
}
394
395
//  date elements
396
397
void SvXMLNumFmtExport::WriteDayElement_Impl( const OUString& rCalendar, bool bLong )
398
0
{
399
0
    FinishTextElement_Impl();
400
401
0
    AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
402
0
    AddStyleAttr_Impl( bLong );     // adds to pAttrList
403
404
0
    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_DAY,
405
0
                              true, false );
406
0
}
407
408
void SvXMLNumFmtExport::WriteMonthElement_Impl( const OUString& rCalendar, bool bLong, bool bText )
409
0
{
410
0
    FinishTextElement_Impl();
411
412
0
    AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
413
0
    AddStyleAttr_Impl( bLong );     // adds to pAttrList
414
0
    if ( bText )
415
0
    {
416
0
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TEXTUAL, XML_TRUE );
417
0
    }
418
419
0
    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_MONTH,
420
0
                              true, false );
421
0
}
422
423
void SvXMLNumFmtExport::WriteYearElement_Impl( const OUString& rCalendar, bool bLong )
424
0
{
425
0
    FinishTextElement_Impl();
426
427
0
    AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
428
0
    AddStyleAttr_Impl( bLong );     // adds to pAttrList
429
430
0
    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_YEAR,
431
0
                              true, false );
432
0
}
433
434
void SvXMLNumFmtExport::WriteEraElement_Impl( const OUString& rCalendar, bool bLong )
435
0
{
436
0
    FinishTextElement_Impl();
437
438
0
    AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
439
0
    AddStyleAttr_Impl( bLong );     // adds to pAttrList
440
441
0
    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_ERA,
442
0
                              true, false );
443
0
}
444
445
void SvXMLNumFmtExport::WriteDayOfWeekElement_Impl( const OUString& rCalendar, bool bLong )
446
0
{
447
0
    FinishTextElement_Impl();
448
449
0
    AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
450
0
    AddStyleAttr_Impl( bLong );     // adds to pAttrList
451
452
0
    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_DAY_OF_WEEK,
453
0
                              true, false );
454
0
}
455
456
void SvXMLNumFmtExport::WriteWeekElement_Impl( const OUString& rCalendar )
457
0
{
458
0
    FinishTextElement_Impl();
459
460
0
    AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
461
462
0
    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_WEEK_OF_YEAR,
463
0
                              true, false );
464
0
}
465
466
void SvXMLNumFmtExport::WriteQuarterElement_Impl( const OUString& rCalendar, bool bLong )
467
0
{
468
0
    FinishTextElement_Impl();
469
470
0
    AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
471
0
    AddStyleAttr_Impl( bLong );     // adds to pAttrList
472
473
0
    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_QUARTER,
474
0
                              true, false );
475
0
}
476
477
//  time elements
478
479
void SvXMLNumFmtExport::WriteHoursElement_Impl( bool bLong )
480
0
{
481
0
    FinishTextElement_Impl();
482
483
0
    AddStyleAttr_Impl( bLong );     // adds to pAttrList
484
485
0
    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_HOURS,
486
0
                              true, false );
487
0
}
488
489
void SvXMLNumFmtExport::WriteMinutesElement_Impl( bool bLong )
490
0
{
491
0
    FinishTextElement_Impl();
492
493
0
    AddStyleAttr_Impl( bLong );     // adds to pAttrList
494
495
0
    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_MINUTES,
496
0
                              true, false );
497
0
}
498
499
void SvXMLNumFmtExport::WriteRepeatedElement_Impl( sal_Unicode nChar )
500
0
{
501
    // Export only for 1.2 with extensions or 1.3 and later.
502
0
    SvtSaveOptions::ODFSaneDefaultVersion eVersion = m_rExport.getSaneDefaultVersion();
503
0
    if (eVersion > SvtSaveOptions::ODFSVER_012)
504
0
    {
505
0
        FinishTextElement_Impl(eVersion < SvtSaveOptions::ODFSVER_013);
506
        // OFFICE-3765 For 1.2+ use loext namespace, for 1.3 use number namespace.
507
0
        SvXMLElementExport aElem( m_rExport,
508
0
                                  ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
509
0
                                  XML_FILL_CHARACTER, true, false );
510
0
        m_rExport.Characters( OUString( nChar ) );
511
0
    }
512
0
}
513
514
namespace {
515
void lcl_WriteBlankWidthString( std::u16string_view rBlankWidthChar, OUStringBuffer& rBlankWidthString, OUStringBuffer& rTextContent )
516
0
{
517
    // export "_x"
518
0
    if ( rBlankWidthString.isEmpty() )
519
0
    {
520
0
        rBlankWidthString.append( rBlankWidthChar );
521
0
        if ( !rTextContent.isEmpty() )
522
0
        {
523
            // add position in rTextContent
524
0
            rBlankWidthString.append( rTextContent.getLength() );
525
0
        }
526
0
    }
527
0
    else
528
0
    {
529
        // add "_" as separator if there are several blank width char
530
0
        rBlankWidthString.append( "_" );
531
0
        rBlankWidthString.append( rBlankWidthChar );
532
0
        rBlankWidthString.append( rTextContent.getLength() );
533
0
    }
534
    // for previous versions, turn "_x" into the number of spaces used for x in InsertBlanks in the NumberFormat
535
0
    if ( !rBlankWidthChar.empty() )
536
0
    {
537
0
        OUString aBlanks;
538
0
        SvNumberformat::InsertBlanks( aBlanks, 0, rBlankWidthChar[0] );
539
0
        rTextContent.append( aBlanks );
540
0
    }
541
0
}
542
}
543
544
void SvXMLNumFmtExport::WriteSecondsElement_Impl( bool bLong, sal_uInt16 nDecimals )
545
0
{
546
0
    FinishTextElement_Impl();
547
548
0
    AddStyleAttr_Impl( bLong );     // adds to pAttrList
549
0
    if ( nDecimals > 0 )
550
0
    {
551
0
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_PLACES,
552
0
                              OUString::number(  nDecimals ) );
553
0
    }
554
555
0
    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_SECONDS,
556
0
                              true, false );
557
0
}
558
559
void SvXMLNumFmtExport::WriteAMPMElement_Impl()
560
0
{
561
0
    FinishTextElement_Impl();
562
563
0
    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_AM_PM,
564
0
                              true, false );
565
0
}
566
567
//  numbers
568
569
void SvXMLNumFmtExport::WriteIntegerElement_Impl(
570
                            sal_Int32 nInteger, sal_Int32 nBlankInteger, bool bGrouping )
571
0
{
572
    //  integer digits: '0' and '?'
573
0
    if ( nInteger >= 0 )    // negative = automatic
574
0
    {
575
0
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_INTEGER_DIGITS,
576
0
                              OUString::number( nInteger ) );
577
0
    }
578
0
    SvtSaveOptions::ODFSaneDefaultVersion eVersion = m_rExport.getSaneDefaultVersion();
579
    //  blank integer digits: '?'
580
0
    if ( nBlankInteger > 0 && ( (eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0 ) )
581
0
    {
582
0
        m_rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_MAX_BLANK_INTEGER_DIGITS,
583
0
                              OUString::number( nBlankInteger ) );
584
0
    }
585
    //  (automatic) grouping separator
586
0
    if ( bGrouping )
587
0
    {
588
0
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_GROUPING, XML_TRUE );
589
0
    }
590
0
}
591
592
void SvXMLNumFmtExport::WriteEmbeddedEntries_Impl( const SvXMLEmbeddedTextEntryArr& rEmbeddedEntries )
593
0
{
594
0
    auto nEntryCount = rEmbeddedEntries.size();
595
0
    SvtSaveOptions::ODFSaneDefaultVersion eVersion = m_rExport.getSaneDefaultVersion();
596
0
    for (decltype(nEntryCount) nEntry=0; nEntry < nEntryCount; ++nEntry)
597
0
    {
598
0
        const SvXMLEmbeddedTextEntry* pObj = &rEmbeddedEntries[nEntry];
599
600
        //  position attribute
601
        // position == 0 is between first integer digit and decimal separator
602
        // position < 0 is inside decimal part
603
0
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_POSITION,
604
0
                                OUString::number( pObj->nFormatPos ) );
605
606
        //  text as element content
607
0
        OUStringBuffer aContent;
608
0
        OUStringBuffer aBlankWidthString;
609
0
        do
610
0
        {
611
0
            pObj = &rEmbeddedEntries[nEntry];
612
0
            if ( pObj->isBlankWidth  )
613
0
            {
614
                //  (#i20396# the spaces may also be in embedded-text elements)
615
0
                lcl_WriteBlankWidthString( pObj->aText, aBlankWidthString, aContent );
616
0
            }
617
0
            else
618
0
            {
619
                // The array can contain several elements for the same position in the number.
620
                // Literal texts are merged into a single embedded-text element.
621
0
                aContent.append( pObj->aText );
622
0
            }
623
0
            ++nEntry;
624
0
        }
625
0
        while ( nEntry < nEntryCount
626
0
            && rEmbeddedEntries[nEntry].nFormatPos == pObj->nFormatPos );
627
0
        --nEntry;
628
629
        // Export only for 1.3 with extensions and later.
630
0
        if ( !aBlankWidthString.isEmpty() && eVersion > SvtSaveOptions::ODFSVER_013 && ( (eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0 ) )
631
0
            m_rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_BLANK_WIDTH_CHAR, aBlankWidthString.makeStringAndClear() );
632
0
        SvXMLElementExport aChildElem( m_rExport, XML_NAMESPACE_NUMBER, XML_EMBEDDED_TEXT,
633
0
                                          true, false );
634
0
        m_rExport.Characters( aContent.makeStringAndClear() );
635
0
    }
636
0
}
637
638
void SvXMLNumFmtExport::WriteNumberElement_Impl(
639
                            sal_Int32 nDecimals, sal_Int32 nMinDecimals,
640
                            sal_Int32 nInteger, sal_Int32 nBlankInteger, const OUString& rDashStr,
641
                            bool bGrouping, sal_Int32 nTrailingThousands,
642
                            const SvXMLEmbeddedTextEntryArr& rEmbeddedEntries )
643
0
{
644
0
    FinishTextElement_Impl();
645
646
    //  decimals
647
0
    if ( nDecimals >= 0 )   // negative = automatic
648
0
    {
649
0
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_PLACES,
650
0
                              OUString::number( nDecimals ) );
651
0
    }
652
653
0
    if ( nMinDecimals >= 0 )   // negative = automatic
654
0
    {
655
        // Export only for 1.2 with extensions or 1.3 and later.
656
0
        SvtSaveOptions::ODFSaneDefaultVersion eVersion = m_rExport.getSaneDefaultVersion();
657
0
        if (eVersion > SvtSaveOptions::ODFSVER_012)
658
0
        {
659
            // OFFICE-3860 For 1.2+ use loext namespace, for 1.3 use number namespace.
660
0
            m_rExport.AddAttribute(
661
0
                ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
662
0
                                 XML_MIN_DECIMAL_PLACES,
663
0
                                 OUString::number( nMinDecimals ) );
664
0
        }
665
0
    }
666
    //  decimal replacement (dashes) or variable decimals (#)
667
0
    if ( !rDashStr.isEmpty() ||  nMinDecimals < nDecimals )
668
0
    {
669
        // full variable decimals means an empty replacement string
670
0
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_REPLACEMENT,
671
0
                              rDashStr );
672
0
    }
673
674
0
    WriteIntegerElement_Impl( nInteger, nBlankInteger, bGrouping );
675
676
    //  display-factor if there are trailing thousands separators
677
0
    if ( nTrailingThousands )
678
0
    {
679
        //  each separator character removes three digits
680
0
        double fFactor = ::rtl::math::pow10Exp( 1.0, 3 * nTrailingThousands );
681
682
0
        OUStringBuffer aFactStr;
683
0
        ::sax::Converter::convertDouble( aFactStr, fFactor );
684
0
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DISPLAY_FACTOR, aFactStr.makeStringAndClear() );
685
0
    }
686
687
0
    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_NUMBER,
688
0
                              true, true );
689
690
    //  number:embedded-text as child elements
691
0
    WriteEmbeddedEntries_Impl( rEmbeddedEntries );
692
0
}
693
694
void SvXMLNumFmtExport::WriteScientificElement_Impl(
695
                            sal_Int32 nDecimals, sal_Int32 nMinDecimals, sal_Int32 nInteger, sal_Int32 nBlankInteger,
696
                            bool bGrouping, sal_Int32 nExp, sal_Int32 nExpInterval, bool bExpSign, bool bExponentLowercase, sal_Int32 nBlankExp,
697
                            const SvXMLEmbeddedTextEntryArr& rEmbeddedEntries )
698
0
{
699
0
    FinishTextElement_Impl();
700
701
    //  decimals
702
0
    if ( nDecimals >= 0 )   // negative = automatic
703
0
    {
704
0
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_PLACES,
705
0
                              OUString::number( nDecimals ) );
706
0
    }
707
708
0
    SvtSaveOptions::ODFSaneDefaultVersion eVersion = m_rExport.getSaneDefaultVersion();
709
0
    if ( nMinDecimals >= 0 )   // negative = automatic
710
0
    {
711
        // Export only for 1.2 with extensions or 1.3 and later.
712
0
        if (eVersion > SvtSaveOptions::ODFSVER_012)
713
0
        {
714
            // OFFICE-3860 For 1.2+ use loext namespace, for 1.3 use number namespace.
715
0
            m_rExport.AddAttribute(
716
0
                ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
717
0
                                 XML_MIN_DECIMAL_PLACES,
718
0
                                 OUString::number( nMinDecimals ) );
719
0
        }
720
0
    }
721
722
0
    WriteIntegerElement_Impl( nInteger, nBlankInteger, bGrouping );
723
724
    //  exponent digits
725
0
    if ( nExp >= 0 )
726
0
    {
727
0
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_EXPONENT_DIGITS,
728
0
                              OUString::number( nExp ) );
729
0
    }
730
731
    //  exponent interval for engineering notation
732
0
    if ( nExpInterval >= 0 )
733
0
    {
734
        // Export only for 1.2 with extensions or 1.3 and later.
735
0
        if (eVersion > SvtSaveOptions::ODFSVER_012)
736
0
        {
737
            // OFFICE-1828 For 1.2+ use loext namespace, for 1.3 use number namespace.
738
0
            m_rExport.AddAttribute(
739
0
                    ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
740
0
                    XML_EXPONENT_INTERVAL, OUString::number( nExpInterval ) );
741
0
        }
742
0
    }
743
744
    //  exponent sign
745
    // Export only for 1.2 with extensions or 1.3 and later.
746
0
    if (eVersion > SvtSaveOptions::ODFSVER_012)
747
0
    {
748
        // OFFICE-3860 For 1.2+ use loext namespace, for 1.3 use number namespace.
749
0
        m_rExport.AddAttribute(
750
0
            ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
751
0
                             XML_FORCED_EXPONENT_SIGN,
752
0
                             bExpSign? XML_TRUE : XML_FALSE );
753
0
    }
754
    //  exponent string
755
    // Export only for 1.x with extensions
756
0
    if (eVersion & SvtSaveOptions::ODFSVER_EXTENDED)
757
0
    {
758
0
        if (bExponentLowercase)
759
0
            m_rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_EXPONENT_LOWERCASE, XML_TRUE );
760
0
        if (nBlankExp > 0)
761
0
        {
762
0
            if (nBlankExp >= nExp)
763
0
                nBlankExp = nExp - 1; // preserve at least one '0' in exponent
764
0
            m_rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_BLANK_EXPONENT_DIGITS, OUString::number( nBlankExp ) );
765
0
        }
766
0
    }
767
768
0
    SvXMLElementExport aElem( m_rExport,
769
0
                              XML_NAMESPACE_NUMBER, XML_SCIENTIFIC_NUMBER,
770
0
                              true, false );
771
772
    //  number:embedded-text as child elements
773
    // Export only for 1.x with extensions
774
0
    if (eVersion & SvtSaveOptions::ODFSVER_EXTENDED)
775
0
        WriteEmbeddedEntries_Impl( rEmbeddedEntries );
776
0
}
777
778
void SvXMLNumFmtExport::WriteFractionElement_Impl(
779
                            sal_Int32 nInteger, sal_Int32 nBlankInteger, bool bGrouping,
780
                            const SvNumberformat& rFormat, sal_uInt16 nPart )
781
0
{
782
0
    FinishTextElement_Impl();
783
0
    WriteIntegerElement_Impl( nInteger, nBlankInteger, bGrouping );
784
785
0
    const OUString aNumeratorString = rFormat.GetNumeratorString( nPart );
786
0
    const OUString aDenominatorString = rFormat.GetDenominatorString( nPart );
787
0
    const OUString aIntegerFractionDelimiterString = rFormat.GetIntegerFractionDelimiterString( nPart );
788
0
    sal_Int32 nMaxNumeratorDigits = aNumeratorString.getLength();
789
    // Count '0' as '?'
790
0
    sal_Int32 nMinNumeratorDigits = aNumeratorString.replaceAll("0","?").indexOf('?');
791
0
    sal_Int32 nZerosNumeratorDigits = aNumeratorString.indexOf('0');
792
0
    if ( nMinNumeratorDigits >= 0 )
793
0
        nMinNumeratorDigits = nMaxNumeratorDigits - nMinNumeratorDigits;
794
0
    else
795
0
        nMinNumeratorDigits = 0;
796
0
    if ( nZerosNumeratorDigits >= 0 )
797
0
        nZerosNumeratorDigits = nMaxNumeratorDigits - nZerosNumeratorDigits;
798
0
    else
799
0
        nZerosNumeratorDigits = 0;
800
0
    sal_Int32 nMaxDenominatorDigits = aDenominatorString.getLength();
801
0
    sal_Int32 nMinDenominatorDigits = aDenominatorString.replaceAll("0","?").indexOf('?');
802
0
    sal_Int32 nZerosDenominatorDigits = aDenominatorString.indexOf('0');
803
0
    if ( nMinDenominatorDigits >= 0 )
804
0
        nMinDenominatorDigits = nMaxDenominatorDigits - nMinDenominatorDigits;
805
0
    else
806
0
        nMinDenominatorDigits = 0;
807
0
    if ( nZerosDenominatorDigits >= 0 )
808
0
        nZerosDenominatorDigits = nMaxDenominatorDigits - nZerosDenominatorDigits;
809
0
    else
810
0
        nZerosDenominatorDigits = 0;
811
0
    sal_Int32 nDenominator = aDenominatorString.toInt32();
812
813
0
    SvtSaveOptions::ODFSaneDefaultVersion eVersion = m_rExport.getSaneDefaultVersion();
814
815
    // integer/fraction delimiter
816
0
    if ( !aIntegerFractionDelimiterString.isEmpty() && aIntegerFractionDelimiterString != " "
817
0
        && ((eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0) )
818
0
    {   // Export only for 1.2/1.3 with extensions.
819
0
        m_rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_INTEGER_FRACTION_DELIMITER,
820
0
                              aIntegerFractionDelimiterString );
821
0
    }
822
823
    //  numerator digits
824
0
    if ( nMinNumeratorDigits == 0 ) // at least one digit to keep compatibility with previous versions
825
0
        nMinNumeratorDigits++;
826
0
    m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_NUMERATOR_DIGITS,
827
0
                          OUString::number( nMinNumeratorDigits ) );
828
    // Export only for 1.2/1.3 with extensions.
829
0
    if ((eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0)
830
0
    {
831
        // For extended ODF use loext namespace
832
0
        m_rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_MAX_NUMERATOR_DIGITS,
833
0
                              OUString::number( nMaxNumeratorDigits ) );
834
0
    }
835
0
    if ( nZerosNumeratorDigits && ((eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0) )
836
0
        m_rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_ZEROS_NUMERATOR_DIGITS,
837
0
                              OUString::number( nZerosNumeratorDigits ) );
838
839
0
    if ( nDenominator )
840
0
    {
841
0
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DENOMINATOR_VALUE,
842
0
                              OUString::number( nDenominator) );
843
0
    }
844
    //  it's not necessary to export nDenominatorDigits
845
    //  if we have a forced denominator
846
0
    else
847
0
    {
848
0
        if ( nMinDenominatorDigits == 0 ) // at least one digit to keep compatibility with previous versions
849
0
            nMinDenominatorDigits++;
850
0
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_DENOMINATOR_DIGITS,
851
0
                              OUString::number( nMinDenominatorDigits ) );
852
0
        if (eVersion > SvtSaveOptions::ODFSVER_012)
853
0
        {
854
            // OFFICE-3695 For 1.2+ use loext namespace, for 1.3 use number namespace.
855
0
            m_rExport.AddAttribute(
856
0
                ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
857
0
                                 XML_MAX_DENOMINATOR_VALUE,
858
0
                                 OUString::number( pow ( 10.0, nMaxDenominatorDigits ) - 1 ) ); // 9, 99 or 999
859
0
        }
860
0
        if ( nZerosDenominatorDigits && ((eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0) )
861
0
            m_rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_ZEROS_DENOMINATOR_DIGITS,
862
0
                                  OUString::number( nZerosDenominatorDigits ) );
863
0
    }
864
865
0
    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_FRACTION,
866
0
                              true, false );
867
0
}
868
869
//  mapping (condition)
870
871
void SvXMLNumFmtExport::WriteMapElement_Impl( sal_Int32 nOp, double fLimit,
872
                                                sal_Int32 nKey, sal_Int32 nPart )
873
0
{
874
0
    FinishTextElement_Impl();
875
876
0
    if ( nOp == NUMBERFORMAT_OP_NO )
877
0
        return;
878
879
    // style namespace
880
881
0
    OUStringBuffer aCondStr(20);
882
0
    aCondStr.append( "value()" );          //! define constant
883
0
    switch ( nOp )
884
0
    {
885
0
        case NUMBERFORMAT_OP_EQ: aCondStr.append( '=' );  break;
886
0
        case NUMBERFORMAT_OP_NE: aCondStr.append( "!=" );          break;
887
0
        case NUMBERFORMAT_OP_LT: aCondStr.append( '<' );  break;
888
0
        case NUMBERFORMAT_OP_LE: aCondStr.append( "<=" );          break;
889
0
        case NUMBERFORMAT_OP_GT: aCondStr.append( '>' );  break;
890
0
        case NUMBERFORMAT_OP_GE: aCondStr.append( ">=" );          break;
891
0
        default:
892
0
            OSL_FAIL("unknown operator");
893
0
    }
894
0
    ::rtl::math::doubleToUStringBuffer( aCondStr, fLimit,
895
0
            rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
896
0
            '.', true );
897
898
0
    m_rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_CONDITION,
899
0
                          aCondStr.makeStringAndClear() );
900
901
0
    m_rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_APPLY_STYLE_NAME,
902
0
                          m_rExport.EncodeStyleName( lcl_CreateStyleName( nKey, nPart, false,
903
0
                                               m_sPrefix ) ) );
904
905
0
    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_STYLE, XML_MAP,
906
0
                              true, false );
907
0
}
908
909
//  for old (automatic) currency formats: parse currency symbol from text
910
911
static sal_Int32 lcl_FindSymbol( const OUString& sUpperStr, std::u16string_view sCurString )
912
0
{
913
    //  search for currency symbol
914
    //  Quoting as in ImpSvNumberformatScan::Symbol_Division
915
916
0
    sal_Int32 nCPos = 0;
917
0
    while (nCPos >= 0)
918
0
    {
919
0
        nCPos = sUpperStr.indexOf( sCurString, nCPos );
920
0
        if (nCPos >= 0)
921
0
        {
922
            // in Quotes?
923
0
            sal_Int32 nQ = SvNumberformat::GetQuoteEnd( sUpperStr, nCPos );
924
0
            if ( nQ < 0 )
925
0
            {
926
                //  dm can be escaped as "dm or \d
927
0
                sal_Unicode c;
928
0
                if ( nCPos == 0 )
929
0
                    return nCPos;                   // found
930
0
                c = sUpperStr[nCPos-1];
931
0
                if ( c != '"' && c != '\\')
932
0
                {
933
0
                    return nCPos;                   // found
934
0
                }
935
0
                else
936
0
                {
937
0
                    nCPos++;                        // continue
938
0
                }
939
0
            }
940
0
            else
941
0
            {
942
0
                nCPos = nQ + 1;                     // continue after quote end
943
0
            }
944
0
        }
945
0
    }
946
0
    return -1;
947
0
}
948
949
bool SvXMLNumFmtExport::WriteTextWithCurrency_Impl( const OUString& rString,
950
                            const css::lang::Locale& rLocale )
951
0
{
952
    //  returns true if currency element was written
953
954
0
    bool bRet = false;
955
956
0
    LanguageTag aLanguageTag( rLocale );
957
0
    m_pFormatter->ChangeIntl( aLanguageTag.getLanguageType( false) );
958
0
    OUString sCurString, sDummy;
959
0
    m_pFormatter->GetCompatibilityCurrency( sCurString, sDummy );
960
961
0
    OUString sUpperStr = m_pFormatter->GetCharClass()->uppercase(rString);
962
0
    sal_Int32 nPos = lcl_FindSymbol( sUpperStr, sCurString );
963
0
    if ( nPos >= 0 )
964
0
    {
965
0
        sal_Int32 nLength = rString.getLength();
966
0
        sal_Int32 nCurLen = sCurString.getLength();
967
0
        sal_Int32 nCont = nPos + nCurLen;
968
969
        //  text before currency symbol
970
0
        if ( nPos > 0 )
971
0
        {
972
0
            AddToTextElement_Impl( rString.subView( 0, nPos ) );
973
0
        }
974
        //  currency symbol (empty string -> default)
975
0
        WriteCurrencyElement_Impl( u""_ustr, u"" );
976
0
        bRet = true;
977
978
        //  text after currency symbol
979
0
        if ( nCont < nLength )
980
0
        {
981
0
            AddToTextElement_Impl( rString.subView( nCont, nLength-nCont ) );
982
0
        }
983
0
    }
984
0
    else
985
0
    {
986
0
        AddToTextElement_Impl( rString );       // simple text
987
0
    }
988
989
0
    return bRet;        // true: currency element written
990
0
}
991
992
static OUString lcl_GetDefaultCalendar( SvNumberFormatter const * pFormatter, LanguageType nLang )
993
0
{
994
    //  get name of first non-gregorian calendar for the language
995
996
0
    OUString aCalendar;
997
0
    CalendarWrapper* pCalendar = pFormatter->GetCalendar();
998
0
    if (pCalendar)
999
0
    {
1000
0
        lang::Locale aLocale( LanguageTag::convertToLocale( nLang ) );
1001
1002
0
        const uno::Sequence<OUString> aCals = pCalendar->getAllCalendars( aLocale );
1003
0
        auto pCal = std::find_if(aCals.begin(), aCals.end(),
1004
0
            [](const OUString& rCal) { return rCal != "gregorian"; });
1005
0
        if (pCal != aCals.end())
1006
0
            aCalendar = *pCal;
1007
0
    }
1008
0
    return aCalendar;
1009
0
}
1010
1011
static bool lcl_IsInEmbedded( const SvXMLEmbeddedTextEntryArr& rEmbeddedEntries, sal_uInt16 nPos )
1012
0
{
1013
0
    auto nCount = rEmbeddedEntries.size();
1014
0
    for (decltype(nCount) i=0; i<nCount; i++)
1015
0
        if ( rEmbeddedEntries[i].nSourcePos == nPos )
1016
0
            return true;
1017
1018
0
    return false;       // not found
1019
0
}
1020
1021
static bool lcl_IsDefaultDateFormat( const SvNumberformat& rFormat, bool bSystemDate, NfIndexTableOffset eBuiltIn )
1022
0
{
1023
    //  make an extra loop to collect date elements, to check if it is a default format
1024
    //  before adding the automatic-order attribute
1025
1026
0
    SvXMLDateElementAttributes eDateDOW = XML_DEA_NONE;
1027
0
    SvXMLDateElementAttributes eDateDay = XML_DEA_NONE;
1028
0
    SvXMLDateElementAttributes eDateMonth = XML_DEA_NONE;
1029
0
    SvXMLDateElementAttributes eDateYear = XML_DEA_NONE;
1030
0
    SvXMLDateElementAttributes eDateHours = XML_DEA_NONE;
1031
0
    SvXMLDateElementAttributes eDateMins = XML_DEA_NONE;
1032
0
    SvXMLDateElementAttributes eDateSecs = XML_DEA_NONE;
1033
0
    bool bDateNoDefault = false;
1034
1035
0
    sal_uInt16 nPos = 0;
1036
0
    bool bEnd = false;
1037
0
    short nLastType = 0;
1038
0
    while (!bEnd)
1039
0
    {
1040
0
        short nElemType = rFormat.GetNumForType( 0, nPos );
1041
0
        switch ( nElemType )
1042
0
        {
1043
0
            case 0:
1044
0
                if ( nLastType == NF_SYMBOLTYPE_STRING )
1045
0
                    bDateNoDefault = true;  // text at the end -> no default date format
1046
0
                bEnd = true;                // end of format reached
1047
0
                break;
1048
0
            case NF_SYMBOLTYPE_STRING:
1049
0
            case NF_SYMBOLTYPE_DATESEP:
1050
0
            case NF_SYMBOLTYPE_TIMESEP:
1051
0
            case NF_SYMBOLTYPE_TIME100SECSEP:
1052
                // text is ignored, except at the end
1053
0
                break;
1054
            // same mapping as in SvXMLNumFormatContext::AddNfKeyword:
1055
0
            case NF_KEY_NN:     eDateDOW = XML_DEA_SHORT;       break;
1056
0
            case NF_KEY_NNN:
1057
0
            case NF_KEY_NNNN:   eDateDOW = XML_DEA_LONG;        break;
1058
0
            case NF_KEY_D:      eDateDay = XML_DEA_SHORT;       break;
1059
0
            case NF_KEY_DD:     eDateDay = XML_DEA_LONG;        break;
1060
0
            case NF_KEY_M:      eDateMonth = XML_DEA_SHORT;     break;
1061
0
            case NF_KEY_MM:     eDateMonth = XML_DEA_LONG;      break;
1062
0
            case NF_KEY_MMM:    eDateMonth = XML_DEA_TEXTSHORT; break;
1063
0
            case NF_KEY_MMMM:   eDateMonth = XML_DEA_TEXTLONG;  break;
1064
0
            case NF_KEY_YY:     eDateYear = XML_DEA_SHORT;      break;
1065
0
            case NF_KEY_YYYY:   eDateYear = XML_DEA_LONG;       break;
1066
0
            case NF_KEY_H:      eDateHours = XML_DEA_SHORT;     break;
1067
0
            case NF_KEY_HH:     eDateHours = XML_DEA_LONG;      break;
1068
0
            case NF_KEY_MI:     eDateMins = XML_DEA_SHORT;      break;
1069
0
            case NF_KEY_MMI:    eDateMins = XML_DEA_LONG;       break;
1070
0
            case NF_KEY_S:      eDateSecs = XML_DEA_SHORT;      break;
1071
0
            case NF_KEY_SS:     eDateSecs = XML_DEA_LONG;       break;
1072
0
            case NF_KEY_AP:
1073
0
            case NF_KEY_AMPM:   break;          // AM/PM may or may not be in date/time formats -> ignore by itself
1074
0
            default:
1075
0
                bDateNoDefault = true;      // any other element -> no default format
1076
0
        }
1077
0
        nLastType = nElemType;
1078
0
        ++nPos;
1079
0
    }
1080
1081
0
    if ( bDateNoDefault )
1082
0
        return false;                       // additional elements
1083
0
    else
1084
0
    {
1085
0
        NfIndexTableOffset eFound = static_cast<NfIndexTableOffset>(SvXMLNumFmtDefaults::GetDefaultDateFormat(
1086
0
                eDateDOW, eDateDay, eDateMonth, eDateYear, eDateHours, eDateMins, eDateSecs, bSystemDate ));
1087
1088
0
        return ( eFound == eBuiltIn );
1089
0
    }
1090
0
}
1091
1092
//  export one part (condition)
1093
1094
void SvXMLNumFmtExport::ExportPart_Impl( const SvNumberformat& rFormat, sal_uInt32 nKey, sal_uInt32 nRealKey,
1095
                                            sal_uInt16 nPart, bool bDefPart )
1096
0
{
1097
    //! for the default part, pass the conditions from the other parts!
1098
1099
    //  element name
1100
1101
0
    NfIndexTableOffset eBuiltIn = SvNumberFormatter::GetIndexTableOffset( nRealKey );
1102
1103
0
    SvNumFormatType nFmtType = SvNumFormatType::ALL;
1104
0
    bool bThousand = false;
1105
0
    sal_uInt16 nPrecision = 0;
1106
0
    sal_uInt16 nLeading = 0;
1107
0
    rFormat.GetNumForInfo( nPart, nFmtType, bThousand, nPrecision, nLeading);
1108
0
    nFmtType &= ~SvNumFormatType::DEFINED;
1109
1110
    //  special treatment of builtin formats that aren't detected by normal parsing
1111
    //  (the same formats that get the type set in SvNumberFormatter::ImpGenerateFormats)
1112
0
    if ( eBuiltIn == NF_NUMBER_STANDARD )
1113
0
        nFmtType = SvNumFormatType::NUMBER;
1114
0
    else if ( eBuiltIn == NF_BOOLEAN )
1115
0
        nFmtType = SvNumFormatType::LOGICAL;
1116
0
    else if ( eBuiltIn == NF_TEXT )
1117
0
        nFmtType = SvNumFormatType::TEXT;
1118
1119
    // #101606# An empty subformat is a valid number-style resulting in an
1120
    // empty display string for the condition of the subformat.
1121
1122
0
    XMLTokenEnum eType = XML_TOKEN_INVALID;
1123
0
    switch ( nFmtType )
1124
0
    {
1125
        // Type UNDEFINED likely is a crappy format string for that we could
1126
        // not decide on any format type (and maybe could try harder?), but the
1127
        // resulting XMLTokenEnum should be something valid, so make that
1128
        // number-style.
1129
0
        case SvNumFormatType::UNDEFINED:
1130
0
            SAL_WARN("xmloff.style","UNDEFINED number format: '" << rFormat.GetFormatstring() << "'");
1131
0
            [[fallthrough]];
1132
        // Type is 0 if a format contains no recognized elements
1133
        // (like text only) - this is handled as a number-style.
1134
0
        case SvNumFormatType::ALL:
1135
0
        case SvNumFormatType::EMPTY:
1136
0
        case SvNumFormatType::NUMBER:
1137
0
        case SvNumFormatType::SCIENTIFIC:
1138
0
        case SvNumFormatType::FRACTION:
1139
0
            eType = XML_NUMBER_STYLE;
1140
0
            break;
1141
0
        case SvNumFormatType::PERCENT:
1142
0
            eType = XML_PERCENTAGE_STYLE;
1143
0
            break;
1144
0
        case SvNumFormatType::CURRENCY:
1145
0
            eType = XML_CURRENCY_STYLE;
1146
0
            break;
1147
0
        case SvNumFormatType::DATE:
1148
0
        case SvNumFormatType::DATETIME:
1149
0
            eType = XML_DATE_STYLE;
1150
0
            break;
1151
0
        case SvNumFormatType::TIME:
1152
0
            eType = XML_TIME_STYLE;
1153
0
            break;
1154
0
        case SvNumFormatType::TEXT:
1155
0
            eType = XML_TEXT_STYLE;
1156
0
            break;
1157
0
        case SvNumFormatType::LOGICAL:
1158
0
            eType = XML_BOOLEAN_STYLE;
1159
0
            break;
1160
0
        default: break;
1161
0
    }
1162
0
    SAL_WARN_IF( eType == XML_TOKEN_INVALID, "xmloff.style", "unknown format type" );
1163
1164
0
    OUString sAttrValue;
1165
0
    bool bUserDef( rFormat.GetType() & SvNumFormatType::DEFINED );
1166
1167
    //  common attributes for format
1168
1169
    //  format name (generated from key) - style namespace
1170
0
    m_rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_NAME,
1171
0
                        lcl_CreateStyleName( nKey, nPart, bDefPart, m_sPrefix ) );
1172
1173
    //  "volatile" attribute for styles used only in maps
1174
0
    if ( !bDefPart )
1175
0
        m_rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_VOLATILE, XML_TRUE );
1176
1177
    //  language / country
1178
0
    LanguageType nLang = rFormat.GetLanguage();
1179
0
    AddLanguageAttr_Impl( nLang );                  // adds to pAttrList
1180
1181
    //  title (comment)
1182
    //  titles for builtin formats are not written
1183
0
    sAttrValue = rFormat.GetComment();
1184
0
    if ( !sAttrValue.isEmpty() && bUserDef && bDefPart )
1185
0
    {
1186
0
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TITLE, sAttrValue );
1187
0
    }
1188
1189
    //  automatic ordering for currency and date formats
1190
    //  only used for some built-in formats
1191
0
    bool bAutoOrder = ( eBuiltIn == NF_CURRENCY_1000INT     || eBuiltIn == NF_CURRENCY_1000DEC2 ||
1192
0
                        eBuiltIn == NF_CURRENCY_1000INT_RED || eBuiltIn == NF_CURRENCY_1000DEC2_RED ||
1193
0
                        eBuiltIn == NF_CURRENCY_1000DEC2_DASHED ||
1194
0
                        eBuiltIn == NF_DATE_SYSTEM_SHORT    || eBuiltIn == NF_DATE_SYSTEM_LONG ||
1195
0
                        eBuiltIn == NF_DATE_SYS_MMYY        || eBuiltIn == NF_DATE_SYS_DDMMM ||
1196
0
                        eBuiltIn == NF_DATE_SYS_DDMMYYYY    || eBuiltIn == NF_DATE_SYS_DDMMYY ||
1197
0
                        eBuiltIn == NF_DATE_SYS_DMMMYY      || eBuiltIn == NF_DATE_SYS_DMMMYYYY ||
1198
0
                        eBuiltIn == NF_DATE_SYS_DMMMMYYYY   || eBuiltIn == NF_DATE_SYS_NNDMMMYY ||
1199
0
                        eBuiltIn == NF_DATE_SYS_NNDMMMMYYYY || eBuiltIn == NF_DATE_SYS_NNNNDMMMMYYYY ||
1200
0
                        eBuiltIn == NF_DATETIME_SYSTEM_SHORT_HHMM || eBuiltIn == NF_DATETIME_SYS_DDMMYYYY_HHMM ||
1201
0
                        eBuiltIn == NF_DATETIME_SYS_DDMMYYYY_HHMMSS );
1202
1203
    //  format source (for date and time formats)
1204
    //  only used for some built-in formats
1205
0
    bool bSystemDate = ( eBuiltIn == NF_DATE_SYSTEM_SHORT ||
1206
0
                         eBuiltIn == NF_DATE_SYSTEM_LONG  ||
1207
0
                         eBuiltIn == NF_DATETIME_SYSTEM_SHORT_HHMM );
1208
0
    bool bLongSysDate = ( eBuiltIn == NF_DATE_SYSTEM_LONG );
1209
1210
    // check if the format definition matches the key
1211
0
    if ( bAutoOrder && ( nFmtType == SvNumFormatType::DATE || nFmtType == SvNumFormatType::DATETIME ) &&
1212
0
            !lcl_IsDefaultDateFormat( rFormat, bSystemDate, eBuiltIn ) )
1213
0
    {
1214
0
        bAutoOrder = bSystemDate = bLongSysDate = false;        // don't write automatic-order attribute then
1215
0
    }
1216
1217
0
    if ( bAutoOrder &&
1218
0
        ( nFmtType == SvNumFormatType::CURRENCY || nFmtType == SvNumFormatType::DATE || nFmtType == SvNumFormatType::DATETIME ) )
1219
0
    {
1220
        //  #85109# format type must be checked to avoid dtd errors if
1221
        //  locale data contains other format types at the built-in positions
1222
1223
0
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_AUTOMATIC_ORDER,
1224
0
                              XML_TRUE );
1225
0
    }
1226
1227
0
    if ( bSystemDate && bAutoOrder &&
1228
0
        ( nFmtType == SvNumFormatType::DATE || nFmtType == SvNumFormatType::DATETIME ) )
1229
0
    {
1230
        //  #85109# format type must be checked to avoid dtd errors if
1231
        //  locale data contains other format types at the built-in positions
1232
1233
0
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_FORMAT_SOURCE,
1234
0
                              XML_LANGUAGE );
1235
0
    }
1236
1237
    //  overflow for time formats as in [hh]:mm
1238
    //  controlled by bThousand from number format info
1239
    //  default for truncate-on-overflow is true
1240
0
    if ( nFmtType == SvNumFormatType::TIME && bThousand )
1241
0
    {
1242
0
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRUNCATE_ON_OVERFLOW,
1243
0
                              XML_FALSE );
1244
0
    }
1245
1246
    // Native number transliteration
1247
0
    css::i18n::NativeNumberXmlAttributes2 aAttr;
1248
0
    rFormat.GetNatNumXml( aAttr, nPart, m_pFormatter->GetNatNum() );
1249
0
    if ( !aAttr.Format.isEmpty() )
1250
0
    {
1251
0
        assert(aAttr.Spellout.isEmpty());   // mutually exclusive
1252
1253
        /* FIXME-BCP47: ODF defines no transliteration-script or
1254
         * transliteration-rfc-language-tag */
1255
0
        LanguageTag aLanguageTag( aAttr.Locale);
1256
0
        OUString aLanguage, aScript, aCountry;
1257
0
        aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, aCountry);
1258
0
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_FORMAT,
1259
0
                              aAttr.Format );
1260
0
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_LANGUAGE,
1261
0
                              aLanguage );
1262
0
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_COUNTRY,
1263
0
                              aCountry );
1264
0
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_STYLE,
1265
0
                              aAttr.Style );
1266
0
    }
1267
1268
0
    SvtSaveOptions::ODFSaneDefaultVersion eVersion = m_rExport.getSaneDefaultVersion();
1269
0
    if ( !aAttr.Spellout.isEmpty() )
1270
0
    {
1271
0
        const bool bWriteSpellout = aAttr.Format.isEmpty();
1272
0
        assert(bWriteSpellout);     // mutually exclusive
1273
1274
        // Export only for 1.2 and later with extensions
1275
        // Also ensure that duplicated transliteration-language and
1276
        // transliteration-country attributes never escape into the wild with
1277
        // releases.
1278
0
        if ( (eVersion & SvtSaveOptions::ODFSVER_EXTENDED) && bWriteSpellout )
1279
0
        {
1280
            /* FIXME-BCP47: ODF defines no transliteration-script or
1281
             * transliteration-rfc-language-tag */
1282
0
            LanguageTag aLanguageTag( aAttr.Locale);
1283
0
            OUString aLanguage, aScript, aCountry;
1284
0
            aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, aCountry);
1285
            // For 1.2/1.3+ use loext namespace.
1286
0
            m_rExport.AddAttribute( /*((eVersion < SvtSaveOptions::ODFSVER_)
1287
0
                        ? */ XML_NAMESPACE_LO_EXT /*: XML_NAMESPACE_NUMBER)*/,
1288
0
                    XML_TRANSLITERATION_SPELLOUT, aAttr.Spellout );
1289
0
            m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_LANGUAGE,
1290
0
                                  aLanguage );
1291
0
            m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_COUNTRY,
1292
0
                                  aCountry );
1293
0
        }
1294
0
    }
1295
1296
    // The element
1297
0
    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, eType,
1298
0
                              true, true );
1299
1300
    //  color (properties element)
1301
1302
0
    const Color* pCol = rFormat.GetColor( nPart );
1303
0
    if (pCol)
1304
0
        WriteColorElement_Impl(*pCol);
1305
1306
    //  detect if there is "real" content, excluding color and maps
1307
    //! move to implementation of Write... methods?
1308
0
    bool bAnyContent = false;
1309
1310
    //  format elements
1311
1312
0
    SvXMLEmbeddedTextEntryArr aEmbeddedEntries;
1313
0
    if ( eBuiltIn == NF_NUMBER_STANDARD )
1314
0
    {
1315
        //  default number format contains just one number element
1316
0
        WriteNumberElement_Impl( -1, -1, 1, -1, OUString(), false, 0, aEmbeddedEntries );
1317
0
        bAnyContent = true;
1318
0
    }
1319
0
    else if ( eBuiltIn == NF_BOOLEAN )
1320
0
    {
1321
        //  boolean format contains just one boolean element
1322
0
        WriteBooleanElement_Impl();
1323
0
        bAnyContent = true;
1324
0
    }
1325
0
    else if (eType == XML_BOOLEAN_STYLE)
1326
0
    {
1327
        // <number:boolean-style> may contain only <number:boolean> and
1328
        // <number:text> elements.
1329
0
        sal_uInt16 nPos = 0;
1330
0
        bool bEnd = false;
1331
0
        while (!bEnd)
1332
0
        {
1333
0
            const short nElemType = rFormat.GetNumForType( nPart, nPos );
1334
0
            switch (nElemType)
1335
0
            {
1336
0
                case 0:
1337
0
                    bEnd = true;                // end of format reached
1338
0
                    if (m_bHasText && m_sTextContent.isEmpty())
1339
0
                        m_bHasText = false;       // don't write trailing empty text
1340
0
                break;
1341
0
                case NF_SYMBOLTYPE_STRING:
1342
0
                    {
1343
0
                        const OUString* pElemStr = rFormat.GetNumForString( nPart, nPos );
1344
0
                        if (pElemStr)
1345
0
                            AddToTextElement_Impl( *pElemStr );
1346
0
                    }
1347
0
                break;
1348
0
                case NF_KEY_BOOLEAN:
1349
0
                    WriteBooleanElement_Impl();
1350
0
                    bAnyContent = true;
1351
0
                break;
1352
0
            }
1353
0
            ++nPos;
1354
0
        }
1355
0
    }
1356
0
    else
1357
0
    {
1358
        //  first loop to collect attributes
1359
1360
0
        bool bDecDashes  = false;
1361
0
        bool bExpFound   = false;
1362
0
        bool bCurrFound  = false;
1363
0
        bool bInInteger  = true;
1364
0
        bool bExpSign = true;
1365
0
        bool bExponentLowercase = false;        // 'e' or 'E' for scientific notation
1366
0
        bool bDecAlign   = false;               // decimal alignment with "?"
1367
0
        sal_Int32 nExpDigits = 0;               // '0' and '?' in exponent
1368
0
        sal_Int32 nBlankExp = 0;                // only '?' in exponent
1369
0
        sal_Int32 nIntegerSymbols = 0;          // for embedded-text, including "#"
1370
0
        sal_Int32 nTrailingThousands = 0;       // thousands-separators after all digits
1371
0
        sal_Int32 nMinDecimals = nPrecision;
1372
0
        sal_Int32 nBlankInteger = 0;
1373
0
        OUString sCurrExt;
1374
0
        OUString aCalendar;
1375
0
        bool bImplicitOtherCalendar = false;
1376
0
        bool bExplicitCalendar = false;
1377
0
        sal_uInt16 nPos = 0;
1378
0
        bool bEnd = false;
1379
0
        while (!bEnd)
1380
0
        {
1381
0
            short nElemType = rFormat.GetNumForType( nPart, nPos );
1382
0
            const OUString* pElemStr = rFormat.GetNumForString( nPart, nPos );
1383
1384
0
            switch ( nElemType )
1385
0
            {
1386
0
                case 0:
1387
0
                    bEnd = true;                // end of format reached
1388
0
                    break;
1389
0
                case NF_SYMBOLTYPE_DIGIT:
1390
0
                    if ( bExpFound && pElemStr )
1391
0
                    {
1392
0
                        nExpDigits += pElemStr->getLength();
1393
0
                        for ( sal_Int32 i = pElemStr->getLength()-1; i >= 0 ; i-- )
1394
0
                        {
1395
0
                            if ( (*pElemStr)[i] == '?' )
1396
0
                                nBlankExp ++;
1397
0
                        }
1398
0
                    }
1399
0
                    else if ( !bDecDashes && pElemStr && (*pElemStr)[0] == '-' )
1400
0
                    {
1401
0
                        bDecDashes = true;
1402
0
                        nMinDecimals = 0;
1403
0
                    }
1404
0
                    else if ( nFmtType != SvNumFormatType::FRACTION && !bInInteger && pElemStr )
1405
0
                    {
1406
0
                        for ( sal_Int32 i = pElemStr->getLength()-1; i >= 0 ; i-- )
1407
0
                        {
1408
0
                            sal_Unicode aChar = (*pElemStr)[i];
1409
0
                            if ( aChar == '#' || aChar == '?' )
1410
0
                            {
1411
0
                                nMinDecimals --;
1412
0
                                if ( aChar == '?' )
1413
0
                                    bDecAlign = true;
1414
0
                            }
1415
0
                            else
1416
0
                                break;
1417
0
                        }
1418
0
                    }
1419
0
                    if ( bInInteger && pElemStr )
1420
0
                    {
1421
0
                        nIntegerSymbols += pElemStr->getLength();
1422
0
                        for ( sal_Int32 i = pElemStr->getLength()-1; i >= 0 ; i-- )
1423
0
                        {
1424
0
                            if ( (*pElemStr)[i] == '?' )
1425
0
                                nBlankInteger ++;
1426
0
                        }
1427
0
                    }
1428
0
                    nTrailingThousands = 0;
1429
0
                    break;
1430
0
                case NF_SYMBOLTYPE_FRACBLANK:
1431
0
                case NF_SYMBOLTYPE_DECSEP:
1432
0
                    bInInteger = false;
1433
0
                    break;
1434
0
                case NF_SYMBOLTYPE_THSEP:
1435
0
                    if (pElemStr)
1436
0
                        nTrailingThousands += pElemStr->getLength();      // is reset to 0 if digits follow
1437
0
                    break;
1438
0
                case NF_SYMBOLTYPE_EXP:
1439
0
                    bExpFound = true;           // following digits are exponent digits
1440
0
                    bInInteger = false;
1441
0
                    if ( pElemStr && ( pElemStr->getLength() == 1
1442
0
                                  || ( pElemStr->getLength() == 2 && (*pElemStr)[1] == '-' ) ) )
1443
0
                        bExpSign = false;       // for 0.00E0 or 0.00E-00
1444
0
                    if ( pElemStr && (*pElemStr)[0] == 'e' )
1445
0
                        bExponentLowercase = true;   // for 0.00e+00
1446
0
                    break;
1447
0
                case NF_SYMBOLTYPE_CURRENCY:
1448
0
                    bCurrFound = true;
1449
0
                    break;
1450
0
                case NF_SYMBOLTYPE_CURREXT:
1451
0
                    if (pElemStr)
1452
0
                        sCurrExt = *pElemStr;
1453
0
                    break;
1454
1455
                // E, EE, R, RR: select non-gregorian calendar
1456
                // AAA, AAAA: calendar is switched at the position of the element
1457
0
                case NF_KEY_EC:
1458
0
                case NF_KEY_EEC:
1459
0
                case NF_KEY_R:
1460
0
                case NF_KEY_RR:
1461
0
                    if (aCalendar.isEmpty())
1462
0
                    {
1463
0
                        aCalendar = lcl_GetDefaultCalendar( m_pFormatter, nLang );
1464
0
                        bImplicitOtherCalendar = true;
1465
0
                    }
1466
0
                    break;
1467
0
            }
1468
0
            ++nPos;
1469
0
        }
1470
1471
        //  collect strings for embedded-text (must be known before number element is written)
1472
0
        bool bAllowEmbedded = ( nFmtType == SvNumFormatType::ALL || nFmtType == SvNumFormatType::NUMBER ||
1473
0
                                        nFmtType == SvNumFormatType::CURRENCY ||
1474
                                        // Export only for 1.x with extensions
1475
0
                                        ( nFmtType == SvNumFormatType::SCIENTIFIC && (eVersion & SvtSaveOptions::ODFSVER_EXTENDED) )||
1476
0
                                        nFmtType == SvNumFormatType::PERCENT );
1477
0
        if ( bAllowEmbedded )
1478
0
        {
1479
0
            sal_Int32 nDigitsPassed = 0;
1480
0
            sal_Int32 nEmbeddedPositionsMax = nIntegerSymbols;
1481
            // Enable embedded text in decimal part only if there's a decimal part
1482
0
            if ( nPrecision )
1483
0
                nEmbeddedPositionsMax += nPrecision + 1;
1484
            // Enable embedded text in exponent in scientific number
1485
0
            if ( nFmtType == SvNumFormatType::SCIENTIFIC )
1486
0
                nEmbeddedPositionsMax += 1 + nExpDigits;
1487
0
            nPos = 0;
1488
0
            bEnd = false;
1489
0
            bExpFound = false;
1490
0
            while (!bEnd)
1491
0
            {
1492
0
                short nElemType = rFormat.GetNumForType( nPart, nPos );
1493
0
                const OUString* pElemStr = rFormat.GetNumForString( nPart, nPos );
1494
1495
0
                switch ( nElemType )
1496
0
                {
1497
0
                    case 0:
1498
0
                        bEnd = true;                // end of format reached
1499
0
                        break;
1500
0
                    case NF_SYMBOLTYPE_DIGIT:
1501
0
                        if ( pElemStr )
1502
0
                            nDigitsPassed += pElemStr->getLength();
1503
0
                        break;
1504
0
                    case NF_SYMBOLTYPE_EXP:
1505
0
                        bExpFound = true;
1506
0
                        [[fallthrough]];
1507
0
                    case NF_SYMBOLTYPE_DECSEP:
1508
0
                        nDigitsPassed++;
1509
0
                        break;
1510
0
                    case NF_SYMBOLTYPE_STRING:
1511
0
                    case NF_SYMBOLTYPE_BLANK:
1512
0
                    case NF_SYMBOLTYPE_PERCENT:
1513
0
                        if ( 0 < nDigitsPassed && nDigitsPassed < nEmbeddedPositionsMax && pElemStr )
1514
0
                        {
1515
                            //  text (literal or underscore) within the integer (>=0) or decimal (<0) part of a number:number element
1516
1517
0
                            OUString aEmbeddedStr;
1518
0
                            bool bSaveBlankWidthSymbol = false;
1519
0
                            if ( nElemType == NF_SYMBOLTYPE_STRING || nElemType == NF_SYMBOLTYPE_PERCENT )
1520
0
                            {
1521
0
                                aEmbeddedStr = *pElemStr;
1522
0
                            }
1523
0
                            else if (pElemStr->getLength() >= 2)
1524
0
                            {
1525
0
                                if ( eVersion > SvtSaveOptions::ODFSVER_013 && ( (eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0 ) )
1526
0
                                {
1527
0
                                    aEmbeddedStr = pElemStr->copy( 1, 1 );
1528
0
                                    bSaveBlankWidthSymbol = true;
1529
0
                                }
1530
0
                                else //  turn "_x" into the number of spaces used for x in InsertBlanks in the NumberFormat
1531
0
                                    SvNumberformat::InsertBlanks( aEmbeddedStr, 0, (*pElemStr)[1] );
1532
0
                            }
1533
0
                            sal_Int32 nEmbedPos = nIntegerSymbols - nDigitsPassed;
1534
1535
0
                            aEmbeddedEntries.push_back(
1536
0
                                SvXMLEmbeddedTextEntry( nPos, nEmbedPos, aEmbeddedStr, bSaveBlankWidthSymbol ));
1537
                            // exponent sign is required with embedded text in exponent
1538
0
                            if ( bExpFound && !bExpSign )
1539
0
                                bExpSign = true;
1540
0
                        }
1541
0
                        break;
1542
0
                }
1543
0
                ++nPos;
1544
0
            }
1545
0
        }
1546
1547
        //  final loop to write elements
1548
1549
0
        bool bNumWritten = false;
1550
0
        bool bCurrencyWritten = false;
1551
0
        short nPrevType = 0;
1552
0
        nPos = 0;
1553
0
        bEnd = false;
1554
0
        while (!bEnd)
1555
0
        {
1556
0
            short nElemType = rFormat.GetNumForType( nPart, nPos );
1557
0
            const OUString* pElemStr = rFormat.GetNumForString( nPart, nPos );
1558
1559
0
            switch ( nElemType )
1560
0
            {
1561
0
                case 0:
1562
0
                    bEnd = true;                // end of format reached
1563
0
                    if (m_bHasText && m_sTextContent.isEmpty())
1564
0
                        m_bHasText = false;       // don't write trailing empty text
1565
0
                    break;
1566
0
                case NF_SYMBOLTYPE_STRING:
1567
0
                case NF_SYMBOLTYPE_DATESEP:
1568
0
                case NF_SYMBOLTYPE_TIMESEP:
1569
0
                case NF_SYMBOLTYPE_TIME100SECSEP:
1570
0
                case NF_SYMBOLTYPE_PERCENT:
1571
0
                    if (pElemStr)
1572
0
                    {
1573
0
                        if ( ( nElemType == NF_SYMBOLTYPE_TIME100SECSEP ) &&
1574
0
                             ( nPrevType == NF_KEY_S || nPrevType == NF_KEY_SS ||
1575
0
                               ( nPos > 0 && (*rFormat.GetNumForString( nPart, nPos-1 ))[0] == ']' &&
1576
0
                               ( nFmtType == SvNumFormatType::TIME || nFmtType == SvNumFormatType::DATETIME ) ) ) &&
1577
0
                             nPrecision > 0 )
1578
0
                        {
1579
                            //  decimal separator after seconds or [SS] is implied by
1580
                            //  "decimal-places" attribute and must not be written
1581
                            //  as text element
1582
                            //! difference between '.' and ',' is lost here
1583
0
                        }
1584
0
                        else if ( lcl_IsInEmbedded( aEmbeddedEntries, nPos ) )
1585
0
                        {
1586
                            //  text is written as embedded-text child of the number,
1587
                            //  don't create a text element
1588
0
                        }
1589
0
                        else if ( nFmtType == SvNumFormatType::CURRENCY && !bCurrFound && !bCurrencyWritten )
1590
0
                        {
1591
                            //  automatic currency symbol is implemented as part of
1592
                            //  normal text -> search for the symbol
1593
0
                            bCurrencyWritten = WriteTextWithCurrency_Impl( *pElemStr,
1594
0
                                LanguageTag::convertToLocale( nLang ) );
1595
0
                            bAnyContent = true;
1596
0
                        }
1597
0
                        else
1598
0
                            AddToTextElement_Impl( *pElemStr );
1599
0
                    }
1600
0
                    break;
1601
0
                case NF_SYMBOLTYPE_BLANK:
1602
0
                    if ( pElemStr && !lcl_IsInEmbedded( aEmbeddedEntries, nPos ) )
1603
0
                    {
1604
0
                        if ( pElemStr->getLength() == 2 )
1605
0
                        {
1606
0
                            OUString aBlankWidthChar = pElemStr->copy( 1 );
1607
0
                            lcl_WriteBlankWidthString( aBlankWidthChar, m_sBlankWidthString, m_sTextContent );
1608
0
                            m_bHasText = true;
1609
0
                        }
1610
0
                    }
1611
0
                    break;
1612
0
                case NF_KEY_GENERAL :
1613
0
                        WriteNumberElement_Impl( -1, -1, 1, -1, OUString(), false, 0, aEmbeddedEntries );
1614
0
                        bAnyContent = true;
1615
0
                    break;
1616
0
                case NF_KEY_CCC:
1617
0
                    if (pElemStr)
1618
0
                    {
1619
0
                        if ( bCurrencyWritten )
1620
0
                            AddToTextElement_Impl( *pElemStr );     // never more than one currency element
1621
0
                        else
1622
0
                        {
1623
                            //! must be different from short automatic format
1624
                            //! but should still be empty (meaning automatic)
1625
                            //  pElemStr is "CCC"
1626
1627
0
                            WriteCurrencyElement_Impl( *pElemStr, u"" );
1628
0
                            bAnyContent = true;
1629
0
                            bCurrencyWritten = true;
1630
0
                        }
1631
0
                    }
1632
0
                    break;
1633
0
                case NF_SYMBOLTYPE_CURRENCY:
1634
0
                    if (pElemStr)
1635
0
                    {
1636
0
                        if ( bCurrencyWritten )
1637
0
                            AddToTextElement_Impl( *pElemStr );     // never more than one currency element
1638
0
                        else
1639
0
                        {
1640
0
                            WriteCurrencyElement_Impl( *pElemStr, sCurrExt );
1641
0
                            bAnyContent = true;
1642
0
                            bCurrencyWritten = true;
1643
0
                        }
1644
0
                    }
1645
0
                    break;
1646
0
                case NF_SYMBOLTYPE_DIGIT:
1647
0
                    if (!bNumWritten)           // write number part
1648
0
                    {
1649
0
                        switch ( nFmtType )
1650
0
                        {
1651
                            // for type 0 (not recognized as a special type),
1652
                            // write a "normal" number
1653
0
                            case SvNumFormatType::ALL:
1654
0
                            case SvNumFormatType::NUMBER:
1655
0
                            case SvNumFormatType::CURRENCY:
1656
0
                            case SvNumFormatType::PERCENT:
1657
0
                                {
1658
                                    //  decimals
1659
                                    //  only some built-in formats have automatic decimals
1660
0
                                    sal_Int32 nDecimals = nPrecision;   // from GetFormatSpecialInfo
1661
0
                                    if ( eBuiltIn == NF_NUMBER_STANDARD ||
1662
0
                                         eBuiltIn == NF_CURRENCY_1000DEC2 ||
1663
0
                                         eBuiltIn == NF_CURRENCY_1000DEC2_RED ||
1664
0
                                         eBuiltIn == NF_CURRENCY_1000DEC2_CCC ||
1665
0
                                         eBuiltIn == NF_CURRENCY_1000DEC2_DASHED )
1666
0
                                        nDecimals = -1;
1667
1668
                                    //  integer digits
1669
                                    //  only one built-in format has automatic integer digits
1670
0
                                    sal_Int32 nInteger = nLeading;
1671
0
                                    if ( eBuiltIn == NF_NUMBER_SYSTEM )
1672
0
                                    {
1673
0
                                        nInteger = -1;
1674
0
                                        nBlankInteger = -1;
1675
0
                                    }
1676
1677
                                    //  string for decimal replacement
1678
                                    //  has to be taken from nPrecision
1679
                                    //  (positive number even for automatic decimals)
1680
0
                                    OUString sDashStr;
1681
0
                                    if (bDecDashes && nPrecision > 0)
1682
0
                                        sDashStr = OUString::Concat(RepeatedUChar('-', nPrecision));
1683
                                    // "?" in decimal part are replaced by space character
1684
0
                                    if (bDecAlign && nPrecision > 0)
1685
0
                                        sDashStr = " ";
1686
1687
0
                                    WriteNumberElement_Impl(nDecimals, nMinDecimals, nInteger, nBlankInteger, sDashStr,
1688
0
                                        bThousand, nTrailingThousands, aEmbeddedEntries);
1689
0
                                    bAnyContent = true;
1690
0
                                }
1691
0
                                break;
1692
0
                            case SvNumFormatType::SCIENTIFIC:
1693
                                // #i43959# for scientific numbers, count all integer symbols ("0", "?" and "#")
1694
                                // as integer digits: use nIntegerSymbols instead of nLeading
1695
                                // nIntegerSymbols represents exponent interval (for engineering notation)
1696
0
                                WriteScientificElement_Impl( nPrecision, nMinDecimals, nLeading, nBlankInteger, bThousand, nExpDigits, nIntegerSymbols, bExpSign,
1697
0
                                    bExponentLowercase, nBlankExp, aEmbeddedEntries );
1698
0
                                bAnyContent = true;
1699
0
                                break;
1700
0
                            case SvNumFormatType::FRACTION:
1701
0
                                {
1702
0
                                    sal_Int32 nInteger = nLeading;
1703
0
                                    if ( rFormat.GetNumForNumberElementCount( nPart ) == 3 )
1704
0
                                    {
1705
                                        //  If there is only two numbers + fraction in format string
1706
                                        //  the fraction doesn't have an integer part, and no
1707
                                        //  min-integer-digits attribute must be written.
1708
0
                                        nInteger = -1;
1709
0
                                        nBlankInteger = -1;
1710
0
                                    }
1711
0
                                    WriteFractionElement_Impl( nInteger, nBlankInteger, bThousand,  rFormat, nPart );
1712
0
                                    bAnyContent = true;
1713
0
                                }
1714
0
                                break;
1715
0
                            default: break;
1716
0
                        }
1717
1718
0
                        bNumWritten = true;
1719
0
                    }
1720
0
                    break;
1721
0
                case NF_SYMBOLTYPE_DECSEP:
1722
0
                    if ( pElemStr && nPrecision == 0 )
1723
0
                    {
1724
                        //  A decimal separator after the number, without following decimal digits,
1725
                        //  isn't modelled as part of the number element, so it's written as text
1726
                        //  (the distinction between a quoted and non-quoted, locale-dependent
1727
                        //  character is lost here).
1728
1729
0
                        AddToTextElement_Impl( *pElemStr );
1730
0
                    }
1731
0
                    break;
1732
0
                case NF_SYMBOLTYPE_DEL:
1733
0
                    if ( pElemStr && *pElemStr == "@" )
1734
0
                    {
1735
0
                        WriteTextContentElement_Impl();
1736
0
                        bAnyContent = true;
1737
0
                    }
1738
0
                    break;
1739
1740
0
                case NF_SYMBOLTYPE_CALENDAR:
1741
0
                    if ( pElemStr )
1742
0
                    {
1743
0
                        aCalendar = *pElemStr;
1744
0
                        bExplicitCalendar = true;
1745
0
                    }
1746
0
                    break;
1747
1748
                // date elements:
1749
1750
0
                case NF_KEY_D:
1751
0
                case NF_KEY_DD:
1752
0
                    {
1753
0
                        bool bLong = ( nElemType == NF_KEY_DD );
1754
0
                        WriteDayElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ) );
1755
0
                        bAnyContent = true;
1756
0
                    }
1757
0
                    break;
1758
0
                case NF_KEY_DDD:
1759
0
                case NF_KEY_DDDD:
1760
0
                case NF_KEY_NN:
1761
0
                case NF_KEY_NNN:
1762
0
                case NF_KEY_NNNN:
1763
0
                case NF_KEY_AAA:
1764
0
                case NF_KEY_AAAA:
1765
0
                    {
1766
0
                        OUString aCalAttr = aCalendar;
1767
0
                        if ( nElemType == NF_KEY_AAA || nElemType == NF_KEY_AAAA )
1768
0
                        {
1769
                            //  calendar attribute for AAA and AAAA is switched only for this element
1770
0
                            if (aCalAttr.isEmpty())
1771
0
                                aCalAttr = lcl_GetDefaultCalendar( m_pFormatter, nLang );
1772
0
                        }
1773
1774
0
                        bool bLong = ( nElemType == NF_KEY_NNN || nElemType == NF_KEY_NNNN ||
1775
0
                                           nElemType == NF_KEY_DDDD || nElemType == NF_KEY_AAAA );
1776
0
                        WriteDayOfWeekElement_Impl( aCalAttr, ( bSystemDate ? bLongSysDate : bLong ) );
1777
0
                        bAnyContent = true;
1778
0
                        if ( nElemType == NF_KEY_NNNN )
1779
0
                        {
1780
                            //  write additional text element for separator
1781
0
                            m_pLocaleData = LocaleDataWrapper::get( LanguageTag( nLang ) );
1782
0
                            AddToTextElement_Impl( m_pLocaleData->getLongDateDayOfWeekSep() );
1783
0
                        }
1784
0
                    }
1785
0
                    break;
1786
0
                case NF_KEY_M:
1787
0
                case NF_KEY_MM:
1788
0
                case NF_KEY_MMM:
1789
0
                case NF_KEY_MMMM:
1790
0
                case NF_KEY_MMMMM:      //! first letter of month name, no attribute available
1791
0
                    {
1792
0
                        bool bLong = ( nElemType == NF_KEY_MM  || nElemType == NF_KEY_MMMM );
1793
0
                        bool bText = ( nElemType == NF_KEY_MMM || nElemType == NF_KEY_MMMM ||
1794
0
                                            nElemType == NF_KEY_MMMMM );
1795
0
                        WriteMonthElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ), bText );
1796
0
                        bAnyContent = true;
1797
0
                    }
1798
0
                    break;
1799
0
                case NF_KEY_YY:
1800
0
                case NF_KEY_YYYY:
1801
0
                case NF_KEY_EC:
1802
0
                case NF_KEY_EEC:
1803
0
                case NF_KEY_R:      //! R acts as EE, no attribute available
1804
0
                    {
1805
                        //! distinguish EE and R
1806
                        // Calendar attribute for E and EE and R is set in
1807
                        // first loop. If set and not an explicit calendar and
1808
                        // YY or YYYY is encountered, switch temporarily to
1809
                        // Gregorian.
1810
0
                        bool bLong = ( nElemType == NF_KEY_YYYY || nElemType == NF_KEY_EEC ||
1811
0
                                            nElemType == NF_KEY_R );
1812
0
                        WriteYearElement_Impl(
1813
0
                                ((bImplicitOtherCalendar && !bExplicitCalendar
1814
0
                                  && (nElemType == NF_KEY_YY || nElemType == NF_KEY_YYYY)) ? u"gregorian"_ustr : aCalendar),
1815
0
                                (bSystemDate ? bLongSysDate : bLong));
1816
0
                        bAnyContent = true;
1817
0
                    }
1818
0
                    break;
1819
0
                case NF_KEY_G:
1820
0
                case NF_KEY_GG:
1821
0
                case NF_KEY_GGG:
1822
0
                case NF_KEY_RR:     //! RR acts as GGGEE, no attribute available
1823
0
                    {
1824
                        //! distinguish GG and GGG and RR
1825
0
                        bool bLong = ( nElemType == NF_KEY_GGG || nElemType == NF_KEY_RR );
1826
0
                        WriteEraElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ) );
1827
0
                        bAnyContent = true;
1828
0
                        if ( nElemType == NF_KEY_RR )
1829
0
                        {
1830
                            //  calendar attribute for RR is set in first loop
1831
0
                            WriteYearElement_Impl( aCalendar, ( bSystemDate || bLongSysDate ) );
1832
0
                        }
1833
0
                    }
1834
0
                    break;
1835
0
                case NF_KEY_Q:
1836
0
                case NF_KEY_QQ:
1837
0
                    {
1838
0
                        bool bLong = ( nElemType == NF_KEY_QQ );
1839
0
                        WriteQuarterElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ) );
1840
0
                        bAnyContent = true;
1841
0
                    }
1842
0
                    break;
1843
0
                case NF_KEY_WW:
1844
0
                    WriteWeekElement_Impl( aCalendar );
1845
0
                    bAnyContent = true;
1846
0
                    break;
1847
1848
                // time elements (bSystemDate is not used):
1849
1850
0
                case NF_KEY_H:
1851
0
                case NF_KEY_HH:
1852
0
                    WriteHoursElement_Impl( nElemType == NF_KEY_HH );
1853
0
                    bAnyContent = true;
1854
0
                    break;
1855
0
                case NF_KEY_MI:
1856
0
                case NF_KEY_MMI:
1857
0
                    WriteMinutesElement_Impl( nElemType == NF_KEY_MMI );
1858
0
                    bAnyContent = true;
1859
0
                    break;
1860
0
                case NF_KEY_S:
1861
0
                case NF_KEY_SS:
1862
0
                    WriteSecondsElement_Impl( ( nElemType == NF_KEY_SS ), nPrecision );
1863
0
                    bAnyContent = true;
1864
0
                    break;
1865
0
                case NF_KEY_AMPM:
1866
0
                case NF_KEY_AP:
1867
0
                    WriteAMPMElement_Impl();        // short/long?
1868
0
                    bAnyContent = true;
1869
0
                    break;
1870
0
                case NF_SYMBOLTYPE_STAR :
1871
                    // export only if ODF 1.2 extensions are enabled
1872
0
                    if (m_rExport.getSaneDefaultVersion() > SvtSaveOptions::ODFSVER_012)
1873
0
                    {
1874
0
                        if ( pElemStr && pElemStr->getLength() > 1 )
1875
0
                            WriteRepeatedElement_Impl( (*pElemStr)[1] );
1876
0
                    }
1877
0
                    break;
1878
0
            }
1879
0
            nPrevType = nElemType;
1880
0
            ++nPos;
1881
0
        }
1882
0
    }
1883
1884
0
    if ( !m_sTextContent.isEmpty() )
1885
0
        bAnyContent = true;     // element written in FinishTextElement_Impl
1886
1887
0
    FinishTextElement_Impl();       // final text element - before maps
1888
1889
0
    if ( !bAnyContent )
1890
0
    {
1891
        //  for an empty format, write an empty text element
1892
0
        SvXMLElementExport aTElem( m_rExport, XML_NAMESPACE_NUMBER, XML_TEXT,
1893
0
                                   true, false );
1894
0
    }
1895
1896
    //  mapping (conditions) must be last elements
1897
1898
0
    if (!bDefPart)
1899
0
        return;
1900
1901
0
    SvNumberformatLimitOps eOp1, eOp2;
1902
0
    double fLimit1, fLimit2;
1903
0
    rFormat.GetConditions( eOp1, fLimit1, eOp2, fLimit2 );
1904
1905
0
    WriteMapElement_Impl( eOp1, fLimit1, nKey, 0 );
1906
0
    WriteMapElement_Impl( eOp2, fLimit2, nKey, 1 );
1907
1908
0
    if ( !rFormat.HasTextFormat() )
1909
0
        return;
1910
1911
    //  4th part is for text -> make an "all other numbers" condition for the 3rd part
1912
    //  by reversing the 2nd condition.
1913
    //  For a trailing text format like  0;@  that has no conditions
1914
    //  use a "less or equal than biggest" condition for the number
1915
    //  part, ODF can't store subformats (style maps) without
1916
    //  conditions.
1917
1918
0
    SvNumberformatLimitOps eOp3 = NUMBERFORMAT_OP_NO;
1919
0
    double fLimit3 = fLimit2;
1920
0
    sal_uInt16 nLastPart = 2;
1921
0
    SvNumberformatLimitOps eOpLast = eOp2;
1922
0
    if (eOp2 == NUMBERFORMAT_OP_NO)
1923
0
    {
1924
0
        eOpLast = eOp1;
1925
0
        fLimit3 = fLimit1;
1926
0
        nLastPart = (eOp1 == NUMBERFORMAT_OP_NO) ? 0 : 1;
1927
0
    }
1928
0
    switch ( eOpLast )
1929
0
    {
1930
0
        case NUMBERFORMAT_OP_EQ: eOp3 = NUMBERFORMAT_OP_NE; break;
1931
0
        case NUMBERFORMAT_OP_NE: eOp3 = NUMBERFORMAT_OP_EQ; break;
1932
0
        case NUMBERFORMAT_OP_LT: eOp3 = NUMBERFORMAT_OP_GE; break;
1933
0
        case NUMBERFORMAT_OP_LE: eOp3 = NUMBERFORMAT_OP_GT; break;
1934
0
        case NUMBERFORMAT_OP_GT: eOp3 = NUMBERFORMAT_OP_LE; break;
1935
0
        case NUMBERFORMAT_OP_GE: eOp3 = NUMBERFORMAT_OP_LT; break;
1936
0
        case NUMBERFORMAT_OP_NO: eOp3 = NUMBERFORMAT_OP_LE; fLimit3 = DBL_MAX; break;
1937
0
    }
1938
1939
0
    if ( fLimit1 == fLimit2 &&
1940
0
            ( ( eOp1 == NUMBERFORMAT_OP_LT && eOp2 == NUMBERFORMAT_OP_GT ) ||
1941
0
              ( eOp1 == NUMBERFORMAT_OP_GT && eOp2 == NUMBERFORMAT_OP_LT ) ) )
1942
0
    {
1943
        //  For <x and >x, add =x as last condition
1944
        //  (just for readability, <=x would be valid, too)
1945
1946
0
        eOp3 = NUMBERFORMAT_OP_EQ;
1947
0
    }
1948
1949
0
    WriteMapElement_Impl( eOp3, fLimit3, nKey, nLastPart );
1950
0
}
1951
1952
//  export one format
1953
1954
void SvXMLNumFmtExport::ExportFormat_Impl( const SvNumberformat& rFormat, sal_uInt32 nKey, sal_uInt32 nRealKey )
1955
0
{
1956
0
    const sal_uInt16 XMLNUM_MAX_PARTS = 4;
1957
0
    bool bParts[XMLNUM_MAX_PARTS] = { false, false, false, false };
1958
0
    sal_uInt16 nUsedParts = 0;
1959
0
    for (sal_uInt16 nPart=0; nPart<XMLNUM_MAX_PARTS; ++nPart)
1960
0
    {
1961
0
        if (rFormat.GetNumForInfoScannedType( nPart) != SvNumFormatType::UNDEFINED)
1962
0
        {
1963
0
            bParts[nPart] = true;
1964
0
            nUsedParts = nPart + 1;
1965
0
        }
1966
0
    }
1967
1968
0
    SvNumberformatLimitOps eOp1, eOp2;
1969
0
    double fLimit1, fLimit2;
1970
0
    rFormat.GetConditions( eOp1, fLimit1, eOp2, fLimit2 );
1971
1972
    //  if conditions are set, even empty formats must be written
1973
1974
0
    if ( eOp1 != NUMBERFORMAT_OP_NO )
1975
0
    {
1976
0
        bParts[1] = true;
1977
0
        if (nUsedParts < 2)
1978
0
            nUsedParts = 2;
1979
0
    }
1980
0
    if ( eOp2 != NUMBERFORMAT_OP_NO )
1981
0
    {
1982
0
        bParts[2] = true;
1983
0
        if (nUsedParts < 3)
1984
0
            nUsedParts = 3;
1985
0
    }
1986
0
    if ( rFormat.HasTextFormat() )
1987
0
    {
1988
0
        bParts[3] = true;
1989
0
        if (nUsedParts < 4)
1990
0
            nUsedParts = 4;
1991
0
    }
1992
1993
0
    for (sal_uInt16 nPart=0; nPart<XMLNUM_MAX_PARTS; ++nPart)
1994
0
    {
1995
0
        if (bParts[nPart])
1996
0
        {
1997
0
            bool bDefault = ( nPart+1 == nUsedParts );          // last = default
1998
0
            ExportPart_Impl( rFormat, nKey, nRealKey, nPart, bDefault );
1999
0
        }
2000
0
    }
2001
0
}
2002
2003
//  export method called by application
2004
2005
void SvXMLNumFmtExport::Export( bool bIsAutoStyle )
2006
0
{
2007
0
    if ( !m_pFormatter )
2008
0
        return;                         // no formatter -> no entries
2009
2010
0
    sal_uInt32 nKey;
2011
0
    const SvNumberformat* pFormat = nullptr;
2012
0
    bool bNext(m_pUsedList->GetFirstUsed(nKey));
2013
0
    while(bNext)
2014
0
    {
2015
        // ODF has its notation of system formats, so obtain the "real" already
2016
        // substituted format but use the original key for style name.
2017
0
        sal_uInt32 nRealKey = nKey;
2018
0
        pFormat = m_pFormatter->GetSubstitutedEntry( nKey, nRealKey);
2019
0
        if(pFormat)
2020
0
            ExportFormat_Impl( *pFormat, nKey, nRealKey );
2021
0
        bNext = m_pUsedList->GetNextUsed(nKey);
2022
0
    }
2023
0
    if (!bIsAutoStyle)
2024
0
    {
2025
0
        std::vector<LanguageType> aLanguages;
2026
0
        m_pFormatter->GetUsedLanguages( aLanguages );
2027
0
        for (const auto& nLang : aLanguages)
2028
0
        {
2029
0
            sal_uInt32 nDefaultIndex = 0;
2030
0
            SvNumberFormatTable& rTable = m_pFormatter->GetEntryTable(
2031
0
                                         SvNumFormatType::DEFINED, nDefaultIndex, nLang );
2032
0
            for (const auto& rTableEntry : rTable)
2033
0
            {
2034
0
                nKey = rTableEntry.first;
2035
0
                pFormat = rTableEntry.second;
2036
0
                if (!m_pUsedList->IsUsed(nKey))
2037
0
                {
2038
0
                    DBG_ASSERT((pFormat->GetType() & SvNumFormatType::DEFINED), "a not user defined numberformat found");
2039
0
                    sal_uInt32 nRealKey = nKey;
2040
0
                    if (pFormat->IsSubstituted())
2041
0
                    {
2042
0
                        pFormat = m_pFormatter->GetSubstitutedEntry( nKey, nRealKey); // export the "real" format
2043
0
                        assert(pFormat);
2044
0
                    }
2045
                    //  user-defined and used formats are exported
2046
0
                    ExportFormat_Impl( *pFormat, nKey, nRealKey );
2047
                    // if it is a user-defined Format it will be added else nothing will happen
2048
0
                    m_pUsedList->SetUsed(nKey);
2049
0
                }
2050
0
            }
2051
0
        }
2052
0
    }
2053
0
    m_pUsedList->Export();
2054
0
}
2055
2056
OUString SvXMLNumFmtExport::GetStyleName( sal_uInt32 nKey )
2057
0
{
2058
0
    if(m_pUsedList->IsUsed(nKey) || m_pUsedList->IsWasUsed(nKey))
2059
0
        return lcl_CreateStyleName( nKey, 0, true, m_sPrefix );
2060
0
    else
2061
0
    {
2062
0
        OSL_FAIL("There is no written Data-Style");
2063
0
        return OUString();
2064
0
    }
2065
0
}
2066
2067
void SvXMLNumFmtExport::SetUsed( sal_uInt32 nKey )
2068
0
{
2069
0
    SAL_WARN_IF( m_pFormatter == nullptr, "xmloff.style", "missing formatter" );
2070
0
    if( !m_pFormatter )
2071
0
        return;
2072
2073
0
    if (m_pFormatter->GetEntry(nKey))
2074
0
        m_pUsedList->SetUsed( nKey );
2075
0
    else {
2076
0
        OSL_FAIL("no existing Numberformat found with this key");
2077
0
    }
2078
0
}
2079
2080
uno::Sequence<sal_Int32> SvXMLNumFmtExport::GetWasUsed() const
2081
0
{
2082
0
    if (m_pUsedList)
2083
0
        return m_pUsedList->GetWasUsed();
2084
0
    return uno::Sequence<sal_Int32>();
2085
0
}
2086
2087
void SvXMLNumFmtExport::SetWasUsed(const uno::Sequence<sal_Int32>& rWasUsed)
2088
0
{
2089
0
    if (m_pUsedList)
2090
0
        m_pUsedList->SetWasUsed(rWasUsed);
2091
0
}
2092
2093
static const SvNumberformat* lcl_GetFormat( SvNumberFormatter const * pFormatter,
2094
                           sal_uInt32 nKey )
2095
0
{
2096
0
    return ( pFormatter != nullptr ) ? pFormatter->GetEntry( nKey ) : nullptr;
2097
0
}
2098
2099
sal_uInt32 SvXMLNumFmtExport::ForceSystemLanguage( sal_uInt32 nKey )
2100
0
{
2101
0
    sal_uInt32 nRet = nKey;
2102
2103
0
    const SvNumberformat* pFormat = lcl_GetFormat( m_pFormatter, nKey );
2104
0
    if( pFormat != nullptr )
2105
0
    {
2106
0
        SAL_WARN_IF( m_pFormatter == nullptr, "xmloff.style", "format without formatter?" );
2107
2108
0
        SvNumFormatType nType = pFormat->GetType();
2109
2110
0
        sal_uInt32 nNewKey = m_pFormatter->GetFormatForLanguageIfBuiltIn(
2111
0
                       nKey, LANGUAGE_SYSTEM );
2112
2113
0
        if( nNewKey != nKey )
2114
0
        {
2115
0
            nRet = nNewKey;
2116
0
        }
2117
0
        else
2118
0
        {
2119
0
            OUString aFormatString( pFormat->GetFormatstring() );
2120
0
            sal_Int32 nErrorPos;
2121
0
            m_pFormatter->PutandConvertEntry(
2122
0
                            aFormatString,
2123
0
                            nErrorPos, nType, nNewKey,
2124
0
                            pFormat->GetLanguage(), LANGUAGE_SYSTEM, true);
2125
2126
            // success? Then use new key.
2127
0
            if( nErrorPos == 0 )
2128
0
                nRet = nNewKey;
2129
0
        }
2130
0
    }
2131
2132
0
    return nRet;
2133
0
}
2134
2135
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */