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