1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <svl/zforlist.hxx>
21 #include <svl/numformat.hxx>
22 #include <svl/zformat.hxx>
23 #include <svl/numuno.hxx>
24 #include <i18nlangtag/languagetag.hxx>
25 #include <tools/color.hxx>
26 #include <osl/diagnose.h>
27 #include <rtl/math.hxx>
28 #include <rtl/ustrbuf.hxx>
29 #include <sal/log.hxx>
31 #include <sax/tools/converter.hxx>
34 #include <xmloff/xmlement.hxx>
35 #include <xmloff/xmlnumfi.hxx>
36 #include <xmloff/xmltkmap.hxx>
37 #include <xmloff/xmlnamespace.hxx>
38 #include <xmloff/xmlictxt.hxx>
39 #include <xmloff/xmlimp.hxx>
40 #include <xmloff/xmluconv.hxx>
41 #include <xmloff/namespacemap.hxx>
42 #include <xmloff/families.hxx>
43 #include <xmloff/xmltoken.hxx>
44 #include <xmloff/languagetagodf.hxx>
47 #include <string_view>
50 using namespace ::com::sun::star
;
51 using namespace ::xmloff::token
;
55 struct SvXMLNumFmtEntry
61 SvXMLNumFmtEntry( OUString aN
, sal_uInt32 nK
, bool bR
) :
62 aName(std::move(aN
)), nKey(nK
), bRemoveAfterUse(bR
) {}
69 SvNumberFormatter
* pFormatter
;
70 std::unique_ptr
<LocaleDataWrapper
> pLocaleData
;
71 std::vector
<SvXMLNumFmtEntry
> m_NameEntries
;
73 uno::Reference
< uno::XComponentContext
> m_xContext
;
77 SvNumberFormatter
* pFmt
,
78 const uno::Reference
<uno::XComponentContext
>& rxContext
);
80 SvNumberFormatter
* GetNumberFormatter() const { return pFormatter
; }
81 const LocaleDataWrapper
& GetLocaleData( LanguageType nLang
);
82 sal_uInt32
GetKeyForName( std::u16string_view rName
);
83 void AddKey( sal_uInt32 nKey
, const OUString
& rName
, bool bRemoveAfterUse
);
84 void SetUsed( sal_uInt32 nKey
);
85 void RemoveVolatileFormats();
88 struct SvXMLNumberInfo
90 sal_Int32 nDecimals
= -1;
91 sal_Int32 nInteger
= -1; /// Total min number of digits in integer part ('0' + '?')
92 sal_Int32 nBlankInteger
= -1; /// Number of '?' in integer part
93 sal_Int32 nExpDigits
= -1;
94 sal_Int32 nExpInterval
= -1;
95 sal_Int32 nMinNumerDigits
= -1;
96 sal_Int32 nMinDenomDigits
= -1;
97 sal_Int32 nMaxNumerDigits
= -1;
98 sal_Int32 nMaxDenomDigits
= -1;
99 sal_Int32 nFracDenominator
= -1;
100 sal_Int32 nMinDecimalDigits
= -1;
101 sal_Int32 nZerosNumerDigits
= -1;
102 sal_Int32 nZerosDenomDigits
= -1;
103 bool bGrouping
= false;
104 bool bDecReplace
= false;
105 bool bExpSign
= true;
106 bool bDecAlign
= false;
107 double fDisplayFactor
= 1.0;
108 OUString aIntegerFractionDelimiter
;
109 std::map
<sal_Int32
, OUString
> m_EmbeddedElements
;
114 enum class SvXMLStyleTokens
;
116 class SvXMLNumFmtElementContext
: public SvXMLImportContext
118 SvXMLNumFormatContext
& rParent
;
119 SvXMLStyleTokens nType
;
120 OUStringBuffer aContent
;
121 SvXMLNumberInfo aNumInfo
;
122 LanguageType nElementLang
;
128 SvXMLNumFmtElementContext( SvXMLImport
& rImport
, sal_Int32 nElement
,
129 SvXMLNumFormatContext
& rParentContext
, SvXMLStyleTokens nNewType
,
130 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& xAttrList
);
132 virtual css::uno::Reference
< css::xml::sax::XFastContextHandler
> SAL_CALL
createFastChildContext(
133 sal_Int32 nElement
, const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& AttrList
) override
;
134 virtual void SAL_CALL
characters( const OUString
& rChars
) override
;
135 virtual void SAL_CALL
endFastElement(sal_Int32 nElement
) override
;
137 void AddEmbeddedElement( sal_Int32 nFormatPos
, const OUString
& rContent
);
140 class SvXMLNumFmtEmbeddedTextContext
: public SvXMLImportContext
142 SvXMLNumFmtElementContext
& rParent
;
143 OUStringBuffer aContent
;
144 sal_Int32 nTextPosition
;
147 SvXMLNumFmtEmbeddedTextContext( SvXMLImport
& rImport
, sal_Int32 nElement
,
148 SvXMLNumFmtElementContext
& rParentContext
,
149 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& xAttrList
);
151 virtual void SAL_CALL
characters( const OUString
& rChars
) override
;
152 virtual void SAL_CALL
endFastElement(sal_Int32 nElement
) override
;
155 class SvXMLNumFmtMapContext
: public SvXMLImportContext
157 SvXMLNumFormatContext
& rParent
;
162 SvXMLNumFmtMapContext( SvXMLImport
& rImport
, sal_Int32 nElement
,
163 SvXMLNumFormatContext
& rParentContext
,
164 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& xAttrList
);
166 virtual void SAL_CALL
endFastElement(sal_Int32 nElement
) override
;
169 class SvXMLNumFmtPropContext
: public SvXMLImportContext
171 SvXMLNumFormatContext
& rParent
;
176 SvXMLNumFmtPropContext( SvXMLImport
& rImport
, sal_Int32 nElement
,
177 SvXMLNumFormatContext
& rParentContext
,
178 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& xAttrList
);
180 virtual void SAL_CALL
endFastElement(sal_Int32 nElement
) override
;
183 enum class SvXMLStyleTokens
211 #define XML_NUMF_COLORCOUNT 10
213 const Color aNumFmtStdColors
[XML_NUMF_COLORCOUNT
] =
231 // maps for SvXMLUnitConverter::convertEnum
233 const SvXMLEnumMapEntry
<bool> aStyleValueMap
[] =
235 { XML_SHORT
, false },
237 { XML_TOKEN_INVALID
, false }
240 const SvXMLEnumMapEntry
<bool> aFormatSourceMap
[] =
242 { XML_FIXED
, false },
243 { XML_LANGUAGE
, true },
244 { XML_TOKEN_INVALID
, false }
249 struct SvXMLDefaultDateFormat
251 NfIndexTableOffset eFormat
;
252 SvXMLDateElementAttributes eDOW
;
253 SvXMLDateElementAttributes eDay
;
254 SvXMLDateElementAttributes eMonth
;
255 SvXMLDateElementAttributes eYear
;
256 SvXMLDateElementAttributes eHours
;
257 SvXMLDateElementAttributes eMins
;
258 SvXMLDateElementAttributes eSecs
;
264 const SvXMLDefaultDateFormat aDefaultDateFormats
[] =
266 // format day-of-week day month year hours minutes seconds format-source
268 { NF_DATE_SYSTEM_SHORT
, XML_DEA_NONE
, XML_DEA_ANY
, XML_DEA_ANY
, XML_DEA_ANY
, XML_DEA_NONE
, XML_DEA_NONE
, XML_DEA_NONE
, true },
269 { NF_DATE_SYSTEM_LONG
, XML_DEA_ANY
, XML_DEA_ANY
, XML_DEA_ANY
, XML_DEA_ANY
, XML_DEA_NONE
, XML_DEA_NONE
, XML_DEA_NONE
, true },
270 { NF_DATE_SYS_MMYY
, XML_DEA_NONE
, XML_DEA_NONE
, XML_DEA_LONG
, XML_DEA_SHORT
, XML_DEA_NONE
, XML_DEA_NONE
, XML_DEA_NONE
, false },
271 { NF_DATE_SYS_DDMMM
, XML_DEA_NONE
, XML_DEA_LONG
, XML_DEA_TEXTSHORT
, XML_DEA_NONE
, XML_DEA_NONE
, XML_DEA_NONE
, XML_DEA_NONE
, false },
272 { NF_DATE_SYS_DDMMYYYY
, XML_DEA_NONE
, XML_DEA_LONG
, XML_DEA_LONG
, XML_DEA_LONG
, XML_DEA_NONE
, XML_DEA_NONE
, XML_DEA_NONE
, false },
273 { NF_DATE_SYS_DDMMYY
, XML_DEA_NONE
, XML_DEA_LONG
, XML_DEA_LONG
, XML_DEA_SHORT
, XML_DEA_NONE
, XML_DEA_NONE
, XML_DEA_NONE
, false },
274 { NF_DATE_SYS_DMMMYY
, XML_DEA_NONE
, XML_DEA_SHORT
, XML_DEA_TEXTSHORT
, XML_DEA_SHORT
, XML_DEA_NONE
, XML_DEA_NONE
, XML_DEA_NONE
, false },
275 { NF_DATE_SYS_DMMMYYYY
, XML_DEA_NONE
, XML_DEA_SHORT
, XML_DEA_TEXTSHORT
, XML_DEA_LONG
, XML_DEA_NONE
, XML_DEA_NONE
, XML_DEA_NONE
, false },
276 { NF_DATE_SYS_DMMMMYYYY
, XML_DEA_NONE
, XML_DEA_SHORT
, XML_DEA_TEXTLONG
, XML_DEA_LONG
, XML_DEA_NONE
, XML_DEA_NONE
, XML_DEA_NONE
, false },
277 { NF_DATE_SYS_NNDMMMYY
, XML_DEA_SHORT
, XML_DEA_SHORT
, XML_DEA_TEXTSHORT
, XML_DEA_SHORT
, XML_DEA_NONE
, XML_DEA_NONE
, XML_DEA_NONE
, false },
278 { NF_DATE_SYS_NNDMMMMYYYY
, XML_DEA_SHORT
, XML_DEA_SHORT
, XML_DEA_TEXTLONG
, XML_DEA_LONG
, XML_DEA_NONE
, XML_DEA_NONE
, XML_DEA_NONE
, false },
279 { NF_DATE_SYS_NNNNDMMMMYYYY
, XML_DEA_LONG
, XML_DEA_SHORT
, XML_DEA_TEXTLONG
, XML_DEA_LONG
, XML_DEA_NONE
, XML_DEA_NONE
, XML_DEA_NONE
, false },
280 { NF_DATETIME_SYS_DDMMYYYY_HHMM
, XML_DEA_NONE
, XML_DEA_ANY
, XML_DEA_ANY
, XML_DEA_LONG
, XML_DEA_ANY
, XML_DEA_ANY
, XML_DEA_NONE
, false },
281 { NF_DATETIME_SYSTEM_SHORT_HHMM
, XML_DEA_NONE
, XML_DEA_ANY
, XML_DEA_ANY
, XML_DEA_ANY
, XML_DEA_ANY
, XML_DEA_ANY
, XML_DEA_NONE
, true },
282 { NF_DATETIME_SYS_DDMMYYYY_HHMMSS
, XML_DEA_NONE
, XML_DEA_ANY
, XML_DEA_ANY
, XML_DEA_ANY
, XML_DEA_ANY
, XML_DEA_ANY
, XML_DEA_ANY
, false }
289 SvXMLNumImpData::SvXMLNumImpData(
290 SvNumberFormatter
* pFmt
,
291 const uno::Reference
<uno::XComponentContext
>& rxContext
)
293 m_xContext(rxContext
)
295 SAL_WARN_IF( !rxContext
.is(), "xmloff", "got no service manager" );
298 sal_uInt32
SvXMLNumImpData::GetKeyForName( std::u16string_view rName
)
300 for (const auto& rObj
: m_NameEntries
)
302 if (rObj
.aName
== rName
)
303 return rObj
.nKey
; // found
305 return NUMBERFORMAT_ENTRY_NOT_FOUND
;
308 void SvXMLNumImpData::AddKey( sal_uInt32 nKey
, const OUString
& rName
, bool bRemoveAfterUse
)
310 if ( bRemoveAfterUse
)
312 // if there is already an entry for this key without the bRemoveAfterUse flag,
313 // clear the flag for this entry, too
315 for (const auto& rObj
: m_NameEntries
)
317 if (rObj
.nKey
== nKey
&& !rObj
.bRemoveAfterUse
)
319 bRemoveAfterUse
= false; // clear flag for new entry
326 // call SetUsed to clear the bRemoveAfterUse flag for other entries for this key
330 m_NameEntries
.emplace_back(rName
, nKey
, bRemoveAfterUse
);
333 void SvXMLNumImpData::SetUsed( sal_uInt32 nKey
)
335 for (auto& rObj
: m_NameEntries
)
337 if (rObj
.nKey
== nKey
)
339 rObj
.bRemoveAfterUse
= false; // used -> don't remove
341 // continue searching - there may be several entries for the same key
342 // (with different names), the format must not be deleted if any one of
348 void SvXMLNumImpData::RemoveVolatileFormats()
350 // remove temporary (volatile) formats from NumberFormatter
351 // called at the end of each import (styles and content), so volatile formats
352 // from styles can't be used in content
357 for (const auto& rObj
: m_NameEntries
)
359 if (rObj
.bRemoveAfterUse
)
361 const SvNumberformat
* pFormat
= pFormatter
->GetEntry(rObj
.nKey
);
362 if (pFormat
&& (pFormat
->GetType() & SvNumFormatType::DEFINED
))
363 pFormatter
->DeleteEntry(rObj
.nKey
);
368 const LocaleDataWrapper
& SvXMLNumImpData::GetLocaleData( LanguageType nLang
)
370 if ( !pLocaleData
|| pLocaleData
->getLanguageTag() != LanguageTag(nLang
) )
371 pLocaleData
= std::make_unique
<LocaleDataWrapper
>(
372 pFormatter
? pFormatter
->GetComponentContext() : m_xContext
,
373 LanguageTag( nLang
) );
378 // SvXMLNumFmtMapContext
381 SvXMLNumFmtMapContext::SvXMLNumFmtMapContext( SvXMLImport
& rImport
,
382 sal_Int32
/*nElement*/,
383 SvXMLNumFormatContext
& rParentContext
,
384 const uno::Reference
<xml::sax::XFastAttributeList
>& xAttrList
) :
385 SvXMLImportContext( rImport
),
386 rParent( rParentContext
)
388 for( auto &aIter
: sax_fastparser::castToFastAttributeList( xAttrList
) )
390 OUString sValue
= aIter
.toString();
391 switch(aIter
.getToken())
393 case XML_ELEMENT(STYLE
, XML_CONDITION
):
396 case XML_ELEMENT(STYLE
, XML_APPLY_STYLE_NAME
):
400 XMLOFF_WARN_UNKNOWN("xmloff", aIter
);
405 void SvXMLNumFmtMapContext::endFastElement(sal_Int32
)
407 rParent
.AddCondition( sCondition
, sName
);
411 // SvXMLNumFmtPropContext
414 SvXMLNumFmtPropContext::SvXMLNumFmtPropContext( SvXMLImport
& rImport
,
415 sal_Int32
/*nElement*/,
416 SvXMLNumFormatContext
& rParentContext
,
417 const uno::Reference
<xml::sax::XFastAttributeList
>& xAttrList
) :
418 SvXMLImportContext( rImport
),
419 rParent( rParentContext
),
423 for( auto &aIter
: sax_fastparser::castToFastAttributeList( xAttrList
) )
425 switch ( aIter
.getToken())
427 case XML_ELEMENT(FO
, XML_COLOR
):
428 case XML_ELEMENT(FO_COMPAT
, XML_COLOR
):
429 bColSet
= ::sax::Converter::convertColor( m_nColor
, aIter
.toView() );
432 XMLOFF_WARN_UNKNOWN("xmloff", aIter
);
437 void SvXMLNumFmtPropContext::endFastElement(sal_Int32
)
440 rParent
.AddColor( m_nColor
);
444 // SvXMLNumFmtEmbeddedTextContext
447 SvXMLNumFmtEmbeddedTextContext::SvXMLNumFmtEmbeddedTextContext( SvXMLImport
& rImport
,
448 sal_Int32
/*nElement*/,
449 SvXMLNumFmtElementContext
& rParentContext
,
450 const uno::Reference
<xml::sax::XFastAttributeList
>& xAttrList
) :
451 SvXMLImportContext( rImport
),
452 rParent( rParentContext
),
457 for( auto &aIter
: sax_fastparser::castToFastAttributeList( xAttrList
) )
459 if ( aIter
.getToken() == XML_ELEMENT(NUMBER
, XML_POSITION
) )
461 if (::sax::Converter::convertNumber( nAttrVal
, aIter
.toView() ))
462 nTextPosition
= nAttrVal
;
465 XMLOFF_WARN_UNKNOWN("xmloff", aIter
);
469 void SvXMLNumFmtEmbeddedTextContext::characters( const OUString
& rChars
)
471 aContent
.append( rChars
);
474 void SvXMLNumFmtEmbeddedTextContext::endFastElement(sal_Int32
)
476 rParent
.AddEmbeddedElement( nTextPosition
, aContent
.makeStringAndClear() );
479 static bool lcl_ValidChar( sal_Unicode cChar
, const SvXMLNumFormatContext
& rParent
)
481 SvXMLStylesTokens nFormatType
= rParent
.GetType();
483 // Treat space equal to non-breaking space separator.
484 const sal_Unicode cNBSP
= 0x00A0;
486 if ( ( nFormatType
== SvXMLStylesTokens::NUMBER_STYLE
||
487 nFormatType
== SvXMLStylesTokens::CURRENCY_STYLE
||
488 nFormatType
== SvXMLStylesTokens::PERCENTAGE_STYLE
) &&
489 (cChar
== (cTS
= rParent
.GetLocaleData().getNumThousandSep()[0]) ||
490 (cChar
== ' ' && cTS
== cNBSP
)) )
492 // #i22394# Extra occurrences of thousands separator must be quoted, so they
493 // aren't mis-interpreted as display-factor.
494 // This must be limited to the format types that can contain a number element,
495 // because the same character can be a date separator that should not be quoted
498 return false; // force quotes
501 // see ImpSvNumberformatScan::Next_Symbol
503 // All format types except BOOLEAN may contain minus sign or delimiter.
505 return nFormatType
!= SvXMLStylesTokens::BOOLEAN_STYLE
;
507 if ( ( cChar
== ' ' ||
513 ( nFormatType
== SvXMLStylesTokens::CURRENCY_STYLE
||
514 nFormatType
== SvXMLStylesTokens::DATE_STYLE
||
515 nFormatType
== SvXMLStylesTokens::TIME_STYLE
) ) // other formats do not require delimiter tdf#97837
518 // percent sign must be used without quotes for percentage styles only
519 if ( nFormatType
== SvXMLStylesTokens::PERCENTAGE_STYLE
&& cChar
== '%' )
522 // don't put quotes around single parentheses (often used for negative numbers)
523 if ( ( nFormatType
== SvXMLStylesTokens::NUMBER_STYLE
||
524 nFormatType
== SvXMLStylesTokens::CURRENCY_STYLE
||
525 nFormatType
== SvXMLStylesTokens::PERCENTAGE_STYLE
) &&
526 ( cChar
== '(' || cChar
== ')' ) )
532 static void lcl_EnquoteIfNecessary( OUStringBuffer
& rContent
, const SvXMLNumFormatContext
& rParent
)
535 sal_Int32 nLength
= rContent
.getLength();
536 const SvXMLStylesTokens nFormatType
= rParent
.GetType();
538 if (nFormatType
!= SvXMLStylesTokens::BOOLEAN_STYLE
&&
539 ((nLength
== 1 && lcl_ValidChar( rContent
[0], rParent
)) ||
541 ((rContent
[0] == ' ' && rContent
[1] == '-') ||
542 (rContent
[1] == ' ' && lcl_ValidChar( rContent
[0], rParent
))))))
544 // Don't quote single separator characters like space or percent,
545 // or separator characters followed by space (used in date formats).
546 // Or space followed by minus (used in currency formats) that would
547 // lead to almost duplicated formats with built-in formats just with
548 // the difference of quotes.
551 else if ( nFormatType
== SvXMLStylesTokens::PERCENTAGE_STYLE
&& nLength
> 1 )
553 // the percent character in percentage styles must be left out of quoting
554 // (one occurrence is enough even if there are several percent characters in the string)
556 sal_Int32 nPos
= rContent
.indexOf( '%' );
559 if ( nPos
+ 1 < nLength
)
561 if ( nPos
+ 2 == nLength
&& lcl_ValidChar( rContent
[nPos
+ 1], rParent
) )
563 // single character that doesn't need quoting
567 // quote text behind percent character
568 rContent
.insert( nPos
+ 1, '"' );
569 rContent
.append( '"' );
574 if ( nPos
== 1 && lcl_ValidChar( rContent
[0], rParent
) )
576 // single character that doesn't need quoting
580 // quote text before percent character
581 rContent
.insert( nPos
, '"' );
582 rContent
.insert( 0, '"' );
587 // else: normal quoting (below)
593 // #i55469# quotes in the string itself have to be escaped
594 bool bEscape
= ( rContent
.indexOf( '"' ) >= 0 );
597 // A quote is turned into "\"" - a quote to end quoted text, an escaped quote,
598 // and a quote to resume quoting.
599 OUString
aInsert( "\"\\\"" );
602 while ( nPos
< rContent
.getLength() )
604 if ( rContent
[nPos
] == '"' )
606 rContent
.insert( nPos
, aInsert
);
607 nPos
+= aInsert
.getLength();
613 // quote string literals
614 rContent
.insert( 0, '"' );
615 rContent
.append( '"' );
617 // remove redundant double quotes at start or end
621 if ( rContent
.getLength() > 2 &&
622 rContent
[0] == '"' &&
625 rContent
.remove(0, 2);
628 sal_Int32 nLen
= rContent
.getLength();
630 rContent
[nLen
- 1] == '"' &&
631 rContent
[nLen
- 2] == '"' )
633 rContent
.truncate(nLen
- 2);
638 // SvXMLNumFmtElementContext
641 SvXMLNumFmtElementContext::SvXMLNumFmtElementContext( SvXMLImport
& rImport
,
642 sal_Int32
/*nElement*/,
643 SvXMLNumFormatContext
& rParentContext
, SvXMLStyleTokens nNewType
,
644 const uno::Reference
<xml::sax::XFastAttributeList
>& xAttrList
) :
645 SvXMLImportContext( rImport
),
646 rParent( rParentContext
),
648 nElementLang( LANGUAGE_SYSTEM
),
652 LanguageTagODF aLanguageTagODF
;
654 bool bAttrBool(false);
655 bool bVarDecimals
= false;
656 bool bIsMaxDenominator
= false;
659 for( auto &aIter
: sax_fastparser::castToFastAttributeList( xAttrList
) )
661 switch (aIter
.getToken())
663 case XML_ELEMENT(NUMBER
, XML_DECIMAL_PLACES
):
664 if (::sax::Converter::convertNumber( nAttrVal
, aIter
.toView(), 0 ))
666 // fdo#58539 & gnome#627420: limit number of digits during import
667 aNumInfo
.nDecimals
= std::min
<sal_Int32
>(nAttrVal
, NF_MAX_FORMAT_SYMBOLS
);
670 case XML_ELEMENT(LO_EXT
, XML_MIN_DECIMAL_PLACES
):
671 case XML_ELEMENT(NUMBER
, XML_MIN_DECIMAL_PLACES
):
672 if (::sax::Converter::convertNumber( nAttrVal
, aIter
.toView(), 0 ))
673 aNumInfo
.nMinDecimalDigits
= nAttrVal
;
675 case XML_ELEMENT(NUMBER
, XML_MIN_INTEGER_DIGITS
):
676 if (::sax::Converter::convertNumber( nAttrVal
, aIter
.toView(), 0 ))
677 aNumInfo
.nInteger
= nAttrVal
;
679 case XML_ELEMENT(LO_EXT
, XML_MAX_BLANK_INTEGER_DIGITS
):
680 case XML_ELEMENT(NUMBER
, XML_MAX_BLANK_INTEGER_DIGITS
):
681 if (::sax::Converter::convertNumber( nAttrVal
, aIter
.toView(), 0 ))
682 aNumInfo
.nBlankInteger
= nAttrVal
;
684 case XML_ELEMENT(NUMBER
, XML_GROUPING
):
685 if (::sax::Converter::convertBool( bAttrBool
, aIter
.toView() ))
686 aNumInfo
.bGrouping
= bAttrBool
;
688 case XML_ELEMENT(NUMBER
, XML_DISPLAY_FACTOR
):
689 if (::sax::Converter::convertDouble( fAttrDouble
, aIter
.toView() ))
690 aNumInfo
.fDisplayFactor
= fAttrDouble
;
692 case XML_ELEMENT(NUMBER
, XML_DECIMAL_REPLACEMENT
):
693 if ( aIter
.toView() == " " )
695 aNumInfo
.bDecAlign
= true; // space replacement for "?"
699 if ( aIter
.isEmpty() )
700 bVarDecimals
= true; // empty replacement string: variable decimals
701 else // all other strings
702 aNumInfo
.bDecReplace
= true; // decimal replacement with dashes
704 case XML_ELEMENT(NUMBER
, XML_MIN_EXPONENT_DIGITS
):
705 if (::sax::Converter::convertNumber( nAttrVal
, aIter
.toView(), 0 ))
706 aNumInfo
.nExpDigits
= std::min
<sal_Int32
>(nAttrVal
, NF_MAX_FORMAT_SYMBOLS
);
708 case XML_ELEMENT(NUMBER
, XML_EXPONENT_INTERVAL
):
709 case XML_ELEMENT(LO_EXT
, XML_EXPONENT_INTERVAL
):
710 if (::sax::Converter::convertNumber( nAttrVal
, aIter
.toView(), 0 ))
711 aNumInfo
.nExpInterval
= nAttrVal
;
713 case XML_ELEMENT(NUMBER
, XML_FORCED_EXPONENT_SIGN
):
714 case XML_ELEMENT(LO_EXT
, XML_FORCED_EXPONENT_SIGN
):
715 if (::sax::Converter::convertBool( bAttrBool
, aIter
.toView() ))
716 aNumInfo
.bExpSign
= bAttrBool
;
718 case XML_ELEMENT(NUMBER
, XML_MIN_NUMERATOR_DIGITS
):
719 if (::sax::Converter::convertNumber( nAttrVal
, aIter
.toView(), 0 ))
720 aNumInfo
.nMinNumerDigits
= nAttrVal
;
722 case XML_ELEMENT(NUMBER
, XML_MIN_DENOMINATOR_DIGITS
):
723 if (::sax::Converter::convertNumber( nAttrVal
, aIter
.toView(), 0 ))
724 aNumInfo
.nMinDenomDigits
= nAttrVal
;
726 case XML_ELEMENT(LO_EXT
, XML_MAX_NUMERATOR_DIGITS
):
727 if (::sax::Converter::convertNumber( nAttrVal
, aIter
.toView(), 1 )) // at least one '#'
728 aNumInfo
.nMaxNumerDigits
= nAttrVal
;
730 case XML_ELEMENT(NUMBER
, XML_DENOMINATOR_VALUE
):
731 if (::sax::Converter::convertNumber( nAttrVal
, aIter
.toView(), 1 )) // 0 is not valid
733 aNumInfo
.nFracDenominator
= nAttrVal
;
734 bIsMaxDenominator
= false;
737 case XML_ELEMENT(NUMBER
, XML_MAX_DENOMINATOR_VALUE
): // part of ODF 1.3
738 case XML_ELEMENT(LO_EXT
, XML_MAX_DENOMINATOR_VALUE
):
739 if (::sax::Converter::convertNumber( nAttrVal
, aIter
.toView(), 1 ) && aNumInfo
.nFracDenominator
<= 0)
740 { // if denominator value not yet defined
741 aNumInfo
.nFracDenominator
= nAttrVal
;
742 bIsMaxDenominator
= true;
745 case XML_ELEMENT(LO_EXT
, XML_ZEROS_NUMERATOR_DIGITS
):
746 case XML_ELEMENT(NUMBER
, XML_ZEROS_NUMERATOR_DIGITS
):
747 if (::sax::Converter::convertNumber( nAttrVal
, aIter
.toView(), 0 ))
748 aNumInfo
.nZerosNumerDigits
= nAttrVal
;
750 case XML_ELEMENT(NUMBER
, XML_ZEROS_DENOMINATOR_DIGITS
):
751 case XML_ELEMENT(LO_EXT
, XML_ZEROS_DENOMINATOR_DIGITS
):
752 if (::sax::Converter::convertNumber( nAttrVal
, aIter
.toView(), 0 ))
753 aNumInfo
.nZerosDenomDigits
= nAttrVal
;
755 case XML_ELEMENT(NUMBER
, XML_INTEGER_FRACTION_DELIMITER
):
756 case XML_ELEMENT(LO_EXT
, XML_INTEGER_FRACTION_DELIMITER
):
757 aNumInfo
.aIntegerFractionDelimiter
= aIter
.toString();
759 case XML_ELEMENT(NUMBER
, XML_RFC_LANGUAGE_TAG
):
760 aLanguageTagODF
.maRfcLanguageTag
= aIter
.toString();
762 case XML_ELEMENT(NUMBER
, XML_LANGUAGE
):
763 aLanguageTagODF
.maLanguage
= aIter
.toString();
765 case XML_ELEMENT(NUMBER
, XML_SCRIPT
):
766 aLanguageTagODF
.maScript
= aIter
.toString();
768 case XML_ELEMENT(NUMBER
, XML_COUNTRY
):
769 aLanguageTagODF
.maCountry
= aIter
.toString();
771 case XML_ELEMENT(NUMBER
, XML_STYLE
):
772 SvXMLUnitConverter::convertEnum( bLong
, aIter
.toView(), aStyleValueMap
);
774 case XML_ELEMENT(NUMBER
, XML_TEXTUAL
):
775 if (::sax::Converter::convertBool( bAttrBool
, aIter
.toView() ))
776 bTextual
= bAttrBool
;
778 case XML_ELEMENT(NUMBER
, XML_CALENDAR
):
779 sCalendar
= aIter
.toString();
782 XMLOFF_WARN_UNKNOWN("xmloff", aIter
);
785 if ( aNumInfo
.nBlankInteger
> aNumInfo
.nInteger
)
786 aNumInfo
.nInteger
= aNumInfo
.nBlankInteger
;
787 if ( aNumInfo
.nMinDecimalDigits
== -1)
789 if ( bVarDecimals
|| aNumInfo
.bDecReplace
)
790 aNumInfo
.nMinDecimalDigits
= 0;
792 aNumInfo
.nMinDecimalDigits
= aNumInfo
.nDecimals
;
794 if ( aNumInfo
.nZerosDenomDigits
> 0 )
795 { // nMin = count of '0' and '?'
796 if ( aNumInfo
.nMinDenomDigits
< aNumInfo
.nZerosDenomDigits
)
797 aNumInfo
.nMinDenomDigits
= aNumInfo
.nZerosDenomDigits
;
800 aNumInfo
.nZerosDenomDigits
= 0;
801 if ( aNumInfo
.nMinDenomDigits
>= 0 )
802 if ( aNumInfo
.nMaxDenomDigits
< aNumInfo
.nMinDenomDigits
)
803 aNumInfo
.nMaxDenomDigits
= ( aNumInfo
.nMinDenomDigits
? aNumInfo
.nMinDenomDigits
: 1 );
804 if ( aNumInfo
.nZerosNumerDigits
> 0 )
806 if ( aNumInfo
.nMinNumerDigits
< aNumInfo
.nZerosNumerDigits
)
807 aNumInfo
.nMinNumerDigits
= aNumInfo
.nZerosNumerDigits
;
810 aNumInfo
.nZerosNumerDigits
= 0;
811 if ( aNumInfo
.nMinNumerDigits
>= 0 )
812 if ( aNumInfo
.nMaxNumerDigits
< aNumInfo
.nMinNumerDigits
)
813 aNumInfo
.nMaxNumerDigits
= ( aNumInfo
.nMinNumerDigits
? aNumInfo
.nMinNumerDigits
: 1 );
814 if ( bIsMaxDenominator
&& aNumInfo
.nFracDenominator
> 0 )
816 aNumInfo
.nMaxDenomDigits
= floor( log10( aNumInfo
.nFracDenominator
) ) + 1;
817 aNumInfo
.nFracDenominator
= -1; // Max denominator value only gives number of digits at denominator
819 if ( aNumInfo
.nMaxDenomDigits
> 0 )
821 if ( aNumInfo
.nMinDenomDigits
< 0 )
822 aNumInfo
.nMinDenomDigits
= 0;
823 else if ( aNumInfo
.nMinDenomDigits
> aNumInfo
.nMaxDenomDigits
)
824 aNumInfo
.nMinDenomDigits
= aNumInfo
.nMaxDenomDigits
;
827 if ( !aLanguageTagODF
.isEmpty() )
829 nElementLang
= aLanguageTagODF
.getLanguageTag().getLanguageType( false);
830 if ( nElementLang
== LANGUAGE_DONTKNOW
)
831 nElementLang
= LANGUAGE_SYSTEM
; //! error handling for unknown locales?
834 if ( aNumInfo
.aIntegerFractionDelimiter
.isEmpty() )
835 aNumInfo
.aIntegerFractionDelimiter
= " ";
838 css::uno::Reference
< css::xml::sax::XFastContextHandler
> SvXMLNumFmtElementContext::createFastChildContext(
840 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& xAttrList
)
842 // only number:number supports number:embedded-text child element
844 if ( nType
== SvXMLStyleTokens::Number
&&
845 nElement
== XML_ELEMENT(NUMBER
, XML_EMBEDDED_TEXT
) )
847 return new SvXMLNumFmtEmbeddedTextContext( GetImport(), nElement
, *this, xAttrList
);
850 XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement
);
854 void SvXMLNumFmtElementContext::characters( const OUString
& rChars
)
856 aContent
.append( rChars
);
859 void SvXMLNumFmtElementContext::AddEmbeddedElement( sal_Int32 nFormatPos
, const OUString
& rContent
)
861 if (rContent
.isEmpty())
864 auto iterPair
= aNumInfo
.m_EmbeddedElements
.emplace(nFormatPos
, rContent
);
865 if (!iterPair
.second
)
866 // there's already an element at this position - append text to existing element
867 iterPair
.first
->second
+= rContent
;
870 void SvXMLNumFmtElementContext::endFastElement(sal_Int32
)
872 bool bEffLong
= bLong
;
875 case SvXMLStyleTokens::Text
:
876 if ( rParent
.HasLongDoW() &&
877 std::u16string_view(aContent
) == rParent
.GetLocaleData().getLongDateDayOfWeekSep() )
879 // skip separator constant after long day of week
880 // (NF_KEY_NNNN contains the separator)
882 if ( rParent
.ReplaceNfKeyword( NF_KEY_NNN
, NF_KEY_NNNN
) )
887 rParent
.SetHasLongDoW( false ); // only once
889 if ( !aContent
.isEmpty() )
891 lcl_EnquoteIfNecessary( aContent
, rParent
);
892 rParent
.AddToCode( aContent
);
893 aContent
.setLength(0);
897 // Quoted empty text may be significant to separate.
898 aContent
.append("\"\"");
899 rParent
.AddToCode( aContent
);
900 aContent
.setLength(0);
901 rParent
.SetHasTrailingEmptyText(true); // *after* AddToCode()
905 case SvXMLStyleTokens::Number
:
906 rParent
.AddNumber( aNumInfo
);
909 case SvXMLStyleTokens::CurrencySymbol
:
910 rParent
.AddCurrency( aContent
.makeStringAndClear(), nElementLang
);
913 case SvXMLStyleTokens::TextContent
:
914 rParent
.AddToCode( '@');
916 case SvXMLStyleTokens::FillCharacter
:
917 if ( !aContent
.isEmpty() )
919 rParent
.AddToCode( '*' );
920 rParent
.AddToCode( aContent
[0] );
923 case SvXMLStyleTokens::Boolean
:
924 rParent
.AddNfKeyword( NF_KEY_BOOLEAN
);
927 case SvXMLStyleTokens::Day
:
928 rParent
.UpdateCalendar( sCalendar
);
929 //! I18N doesn't provide SYSTEM or extended date information yet
931 rParent
.AddNfKeyword(
932 sal::static_int_cast
< sal_uInt16
>(
933 bEffLong
? NF_KEY_DD
: NF_KEY_D
) );
935 case SvXMLStyleTokens::Month
:
936 rParent
.UpdateCalendar( sCalendar
);
937 //! I18N doesn't provide SYSTEM or extended date information yet
939 rParent
.AddNfKeyword(
940 sal::static_int_cast
< sal_uInt16
>(
942 ? ( bEffLong
? NF_KEY_MMMM
: NF_KEY_MMM
)
943 : ( bEffLong
? NF_KEY_MM
: NF_KEY_M
) ) );
945 case SvXMLStyleTokens::Year
:
946 //! I18N doesn't provide SYSTEM or extended date information yet
948 // Y after G (era) is replaced by E for a secondary calendar.
949 // Do not replace for default calendar.
950 // Also replace Y by E if we're switching to the secondary
951 // calendar of a locale if it is known to implicitly use E.
952 rParent
.UpdateCalendar( sCalendar
);
953 const SvXMLNumFormatContext::ImplicitCalendar eCal
= rParent
.GetImplicitCalendarState();
954 if (eCal
== SvXMLNumFormatContext::ImplicitCalendar::SECONDARY
955 || eCal
== SvXMLNumFormatContext::ImplicitCalendar::SECONDARY_FROM_OTHER
)
957 rParent
.AddNfKeyword(
958 sal::static_int_cast
< sal_uInt16
>(
959 bEffLong
? NF_KEY_EEC
: NF_KEY_EC
) );
963 rParent
.AddNfKeyword(
964 sal::static_int_cast
< sal_uInt16
>(
965 bEffLong
? NF_KEY_YYYY
: NF_KEY_YY
) );
969 case SvXMLStyleTokens::Era
:
970 rParent
.UpdateCalendar( sCalendar
);
971 //! I18N doesn't provide SYSTEM or extended date information yet
972 rParent
.AddNfKeyword(
973 sal::static_int_cast
< sal_uInt16
>(
974 bEffLong
? NF_KEY_GGG
: NF_KEY_G
) );
975 // HasEra flag is set
977 case SvXMLStyleTokens::DayOfWeek
:
978 //! I18N doesn't provide SYSTEM or extended date information yet
980 // Implicit secondary calendar uses A keyword, default and
981 // explicit calendar N keyword.
982 rParent
.UpdateCalendar( sCalendar
);
983 const SvXMLNumFormatContext::ImplicitCalendar eCal
= rParent
.GetImplicitCalendarState();
984 if (eCal
== SvXMLNumFormatContext::ImplicitCalendar::SECONDARY
985 || eCal
== SvXMLNumFormatContext::ImplicitCalendar::SECONDARY_FROM_OTHER
)
987 rParent
.AddNfKeyword(
988 sal::static_int_cast
< sal_uInt16
>(
989 bEffLong
? NF_KEY_AAAA
: NF_KEY_AAA
) );
993 rParent
.AddNfKeyword(
994 sal::static_int_cast
< sal_uInt16
>(
995 bEffLong
? NF_KEY_NNNN
: NF_KEY_NN
) );
999 case SvXMLStyleTokens::WeekOfYear
:
1000 rParent
.UpdateCalendar( sCalendar
);
1001 rParent
.AddNfKeyword( NF_KEY_WW
);
1003 case SvXMLStyleTokens::Quarter
:
1004 rParent
.UpdateCalendar( sCalendar
);
1005 rParent
.AddNfKeyword(
1006 sal::static_int_cast
< sal_uInt16
>(
1007 bEffLong
? NF_KEY_QQ
: NF_KEY_Q
) );
1009 case SvXMLStyleTokens::Hours
:
1010 rParent
.AddNfKeyword(
1011 sal::static_int_cast
< sal_uInt16
>(
1012 bEffLong
? NF_KEY_HH
: NF_KEY_H
) );
1014 case SvXMLStyleTokens::AmPm
:
1016 rParent
.AddNfKeyword( NF_KEY_AMPM
);
1018 case SvXMLStyleTokens::Minutes
:
1019 rParent
.AddNfKeyword(
1020 sal::static_int_cast
< sal_uInt16
>(
1021 bEffLong
? NF_KEY_MMI
: NF_KEY_MI
) );
1023 case SvXMLStyleTokens::Seconds
:
1024 rParent
.AddNfKeyword(
1025 sal::static_int_cast
< sal_uInt16
>(
1026 bEffLong
? NF_KEY_SS
: NF_KEY_S
) );
1027 if ( aNumInfo
.nDecimals
> 0 )
1029 // manually add the decimal places
1030 rParent
.AddToCode(rParent
.GetLocaleData().getNumDecimalSep());
1031 for (sal_Int32 i
=0; i
<aNumInfo
.nDecimals
; i
++)
1033 rParent
.AddToCode( '0');
1038 case SvXMLStyleTokens::Fraction
:
1040 if ( aNumInfo
.nInteger
>= 0 )
1042 // add integer part only if min-integer-digits attribute is there
1043 aNumInfo
.nDecimals
= 0;
1044 rParent
.AddNumber( aNumInfo
); // number without decimals
1045 OUStringBuffer
sIntegerFractionDelimiter(aNumInfo
.aIntegerFractionDelimiter
);
1046 lcl_EnquoteIfNecessary( sIntegerFractionDelimiter
, rParent
);
1047 rParent
.AddToCode( sIntegerFractionDelimiter
); // default is ' '
1050 //! build string and add at once
1053 for (i
=aNumInfo
.nMaxNumerDigits
; i
> 0; i
--)
1055 if ( i
> aNumInfo
.nMinNumerDigits
)
1056 rParent
.AddToCode( '#' );
1057 else if ( i
> aNumInfo
.nZerosNumerDigits
)
1058 rParent
.AddToCode( '?' );
1060 rParent
.AddToCode( '0' );
1062 rParent
.AddToCode( '/' );
1063 if ( aNumInfo
.nFracDenominator
> 0 )
1065 rParent
.AddToCode( OUString::number( aNumInfo
.nFracDenominator
) );
1069 for (i
=aNumInfo
.nMaxDenomDigits
; i
> 0 ; i
--)
1071 if ( i
> aNumInfo
.nMinDenomDigits
)
1072 rParent
.AddToCode( '#' );
1073 else if ( i
> aNumInfo
.nZerosDenomDigits
)
1074 rParent
.AddToCode( '?' );
1076 rParent
.AddToCode( '0' );
1082 case SvXMLStyleTokens::ScientificNumber
:
1084 // exponential interval for engineering notation
1085 if( !aNumInfo
.bGrouping
&& aNumInfo
.nExpInterval
> aNumInfo
.nInteger
)
1087 for (sal_Int32 i
=aNumInfo
.nInteger
; i
<aNumInfo
.nExpInterval
; i
++)
1089 rParent
.AddToCode( '#' );
1092 rParent
.AddNumber( aNumInfo
); // simple number
1094 if ( aNumInfo
.bExpSign
)
1095 rParent
.AddToCode( u
"E+" );
1097 rParent
.AddToCode( u
"E" );
1098 for (sal_Int32 i
=0; i
<aNumInfo
.nExpDigits
; i
++)
1100 rParent
.AddToCode( '0' );
1106 assert(false && "invalid element ID");
1110 sal_uInt16
SvXMLNumFmtDefaults::GetDefaultDateFormat( SvXMLDateElementAttributes eDOW
,
1111 SvXMLDateElementAttributes eDay
, SvXMLDateElementAttributes eMonth
,
1112 SvXMLDateElementAttributes eYear
, SvXMLDateElementAttributes eHours
,
1113 SvXMLDateElementAttributes eMins
, SvXMLDateElementAttributes eSecs
,
1116 for (const auto & rEntry
: aDefaultDateFormats
)
1118 if ( bSystem
== rEntry
.bSystem
&&
1119 ( eDOW
== rEntry
.eDOW
|| ( rEntry
.eDOW
== XML_DEA_ANY
&& eDOW
!= XML_DEA_NONE
) ) &&
1120 ( eDay
== rEntry
.eDay
|| ( rEntry
.eDay
== XML_DEA_ANY
&& eDay
!= XML_DEA_NONE
) ) &&
1121 ( eMonth
== rEntry
.eMonth
|| ( rEntry
.eMonth
== XML_DEA_ANY
&& eMonth
!= XML_DEA_NONE
) ) &&
1122 ( eYear
== rEntry
.eYear
|| ( rEntry
.eYear
== XML_DEA_ANY
&& eYear
!= XML_DEA_NONE
) ) &&
1123 ( eHours
== rEntry
.eHours
|| ( rEntry
.eHours
== XML_DEA_ANY
&& eHours
!= XML_DEA_NONE
) ) &&
1124 ( eMins
== rEntry
.eMins
|| ( rEntry
.eMins
== XML_DEA_ANY
&& eMins
!= XML_DEA_NONE
) ) &&
1125 ( eSecs
== rEntry
.eSecs
|| ( rEntry
.eSecs
== XML_DEA_ANY
&& eSecs
!= XML_DEA_NONE
) ) )
1127 return sal::static_int_cast
< sal_uInt16
>(rEntry
.eFormat
);
1131 return NF_INDEX_TABLE_ENTRIES
; // invalid
1135 // SvXMLNumFormatContext
1137 SvXMLNumFormatContext::SvXMLNumFormatContext( SvXMLImport
& rImport
,
1138 sal_Int32
/*nElement*/,
1139 SvXMLNumImpData
* pNewData
, SvXMLStylesTokens nNewType
,
1140 const uno::Reference
<xml::sax::XFastAttributeList
>& xAttrList
,
1141 SvXMLStylesContext
& rStyles
) :
1142 SvXMLStyleContext( rImport
),
1143 m_pData( pNewData
),
1144 m_pStyles( &rStyles
),
1145 m_nType( nNewType
),
1147 m_eImplicitCalendar(ImplicitCalendar::DEFAULT
),
1148 m_nFormatLang( LANGUAGE_SYSTEM
),
1149 m_bAutoOrder( false ),
1150 m_bFromSystem( false ),
1151 m_bTruncate( true ),
1152 m_bAutoDec( false ),
1153 m_bAutoInt( false ),
1154 m_bHasExtraText( false ),
1155 m_bHasTrailingEmptyText( false ),
1156 m_bHasLongDoW( false ),
1157 m_bHasDateTime( false ),
1158 m_bRemoveAfterUse( false ),
1159 m_eDateDOW( XML_DEA_NONE
),
1160 m_eDateDay( XML_DEA_NONE
),
1161 m_eDateMonth( XML_DEA_NONE
),
1162 m_eDateYear( XML_DEA_NONE
),
1163 m_eDateHours( XML_DEA_NONE
),
1164 m_eDateMins( XML_DEA_NONE
),
1165 m_eDateSecs( XML_DEA_NONE
),
1166 m_bDateNoDefault( false )
1168 LanguageTagODF aLanguageTagODF
;
1169 css::i18n::NativeNumberXmlAttributes aNatNumAttr
;
1171 bool bAttrBool(false);
1173 for( auto &aIter
: sax_fastparser::castToFastAttributeList( xAttrList
) )
1175 switch (aIter
.getToken())
1177 // attributes for a style
1178 case XML_ELEMENT(STYLE
, XML_NAME
):
1180 case XML_ELEMENT(NUMBER
, XML_RFC_LANGUAGE_TAG
):
1181 aLanguageTagODF
.maRfcLanguageTag
= aIter
.toString();
1183 case XML_ELEMENT(NUMBER
, XML_LANGUAGE
):
1184 aLanguageTagODF
.maLanguage
= aIter
.toString();
1186 case XML_ELEMENT(NUMBER
, XML_SCRIPT
):
1187 aLanguageTagODF
.maScript
= aIter
.toString();
1189 case XML_ELEMENT(NUMBER
, XML_COUNTRY
):
1190 aLanguageTagODF
.maCountry
= aIter
.toString();
1192 case XML_ELEMENT(NUMBER
, XML_TITLE
):
1193 m_sFormatTitle
= aIter
.toString();
1195 case XML_ELEMENT(NUMBER
, XML_AUTOMATIC_ORDER
):
1196 if (::sax::Converter::convertBool( bAttrBool
, aIter
.toView() ))
1197 m_bAutoOrder
= bAttrBool
;
1199 case XML_ELEMENT(NUMBER
, XML_FORMAT_SOURCE
):
1200 SvXMLUnitConverter::convertEnum( m_bFromSystem
, aIter
.toView(), aFormatSourceMap
);
1202 case XML_ELEMENT(NUMBER
, XML_TRUNCATE_ON_OVERFLOW
):
1203 if (::sax::Converter::convertBool( bAttrBool
, aIter
.toView() ))
1204 m_bTruncate
= bAttrBool
;
1206 case XML_ELEMENT(STYLE
, XML_VOLATILE
):
1207 // volatile formats can be removed after importing
1208 // if not used in other styles
1209 if (::sax::Converter::convertBool( bAttrBool
, aIter
.toView() ))
1210 m_bRemoveAfterUse
= bAttrBool
;
1212 case XML_ELEMENT(NUMBER
, XML_TRANSLITERATION_FORMAT
):
1213 aNatNumAttr
.Format
= aIter
.toString();
1215 case XML_ELEMENT(LO_EXT
, XML_TRANSLITERATION_SPELLOUT
):
1216 case XML_ELEMENT(NUMBER
, XML_TRANSLITERATION_SPELLOUT
):
1217 aSpellout
= aIter
.toString();
1219 case XML_ELEMENT(NUMBER
, XML_TRANSLITERATION_LANGUAGE
):
1220 aNatNumAttr
.Locale
.Language
= aIter
.toString();
1222 case XML_ELEMENT(NUMBER
, XML_TRANSLITERATION_COUNTRY
):
1223 aNatNumAttr
.Locale
.Country
= aIter
.toString();
1225 case XML_ELEMENT(NUMBER
, XML_TRANSLITERATION_STYLE
):
1226 aNatNumAttr
.Style
= aIter
.toString();
1229 XMLOFF_WARN_UNKNOWN("xmloff", aIter
);
1233 if (!aLanguageTagODF
.isEmpty())
1235 m_nFormatLang
= aLanguageTagODF
.getLanguageTag().getLanguageType( false);
1236 if ( m_nFormatLang
== LANGUAGE_DONTKNOW
)
1237 m_nFormatLang
= LANGUAGE_SYSTEM
; //! error handling for unknown locales?
1240 if (aNatNumAttr
.Format
.isEmpty() && aSpellout
.isEmpty())
1243 LanguageTag
aLanguageTag( OUString(), aNatNumAttr
.Locale
.Language
,
1244 std::u16string_view(), aNatNumAttr
.Locale
.Country
);
1245 aNatNumAttr
.Locale
= aLanguageTag
.getLocale( false);
1247 // NatNum12 spell out formula (cardinal, ordinal, ordinal-feminine etc.)
1248 if ( !aSpellout
.isEmpty() )
1250 m_aFormatCode
.append( "[NatNum12 " );
1251 m_aFormatCode
.append( aSpellout
);
1253 SvNumberFormatter
* pFormatter
= m_pData
->GetNumberFormatter();
1254 if ( !pFormatter
) return;
1256 sal_Int32 nNatNum
= pFormatter
->GetNatNum()->convertFromXmlAttributes( aNatNumAttr
);
1257 m_aFormatCode
.append( "[NatNum" );
1258 m_aFormatCode
.append( nNatNum
);
1261 LanguageType eLang
= aLanguageTag
.getLanguageType( false );
1262 if ( eLang
== LANGUAGE_DONTKNOW
)
1263 eLang
= LANGUAGE_SYSTEM
; //! error handling for unknown locales?
1264 if ( eLang
!= m_nFormatLang
&& eLang
!= LANGUAGE_SYSTEM
)
1266 m_aFormatCode
.append( "][$-" );
1267 // language code in upper hex:
1268 m_aFormatCode
.append(OUString::number(static_cast<sal_uInt16
>(eLang
), 16).toAsciiUpperCase());
1270 m_aFormatCode
.append( ']' );
1273 SvXMLNumFormatContext::SvXMLNumFormatContext( SvXMLImport
& rImport
,
1274 const OUString
& rName
,
1275 const uno::Reference
<xml::sax::XFastAttributeList
>& /*xAttrList*/,
1276 const sal_Int32 nTempKey
, LanguageType nLang
,
1277 SvXMLStylesContext
& rStyles
) :
1278 SvXMLStyleContext( rImport
, XmlStyleFamily::DATA_STYLE
),
1280 m_pStyles( &rStyles
),
1281 m_nType( SvXMLStylesTokens::NUMBER_STYLE
),
1283 m_eImplicitCalendar(ImplicitCalendar::DEFAULT
),
1284 m_nFormatLang( nLang
),
1285 m_bAutoOrder( false ),
1286 m_bFromSystem( false ),
1287 m_bTruncate( true ),
1288 m_bAutoDec( false ),
1289 m_bAutoInt( false ),
1290 m_bHasExtraText( false ),
1291 m_bHasTrailingEmptyText( false ),
1292 m_bHasLongDoW( false ),
1293 m_bHasDateTime( false ),
1294 m_bRemoveAfterUse( false ),
1295 m_eDateDOW( XML_DEA_NONE
),
1296 m_eDateDay( XML_DEA_NONE
),
1297 m_eDateMonth( XML_DEA_NONE
),
1298 m_eDateYear( XML_DEA_NONE
),
1299 m_eDateHours( XML_DEA_NONE
),
1300 m_eDateMins( XML_DEA_NONE
),
1301 m_eDateSecs( XML_DEA_NONE
),
1302 m_bDateNoDefault( false )
1304 SetAttribute(XML_ELEMENT(STYLE
, XML_NAME
), rName
);
1307 SvXMLNumFormatContext::~SvXMLNumFormatContext()
1311 css::uno::Reference
< css::xml::sax::XFastContextHandler
> SvXMLNumFormatContext::createFastChildContext(
1313 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& xAttrList
)
1315 SvXMLImportContext
* pContext
= nullptr;
1319 case XML_ELEMENT(LO_EXT
, XML_TEXT
):
1320 case XML_ELEMENT(NUMBER
, XML_TEXT
):
1321 pContext
= new SvXMLNumFmtElementContext( GetImport(), nElement
,
1322 *this, SvXMLStyleTokens::Text
, xAttrList
);
1324 case XML_ELEMENT(LO_EXT
, XML_FILL_CHARACTER
):
1325 case XML_ELEMENT(NUMBER
, XML_FILL_CHARACTER
):
1326 pContext
= new SvXMLNumFmtElementContext( GetImport(), nElement
,
1327 *this, SvXMLStyleTokens::FillCharacter
, xAttrList
);
1329 case XML_ELEMENT(NUMBER
, XML_NUMBER
):
1330 pContext
= new SvXMLNumFmtElementContext( GetImport(), nElement
,
1331 *this, SvXMLStyleTokens::Number
, xAttrList
);
1333 case XML_ELEMENT(NUMBER
, XML_SCIENTIFIC_NUMBER
):
1334 pContext
= new SvXMLNumFmtElementContext( GetImport(), nElement
,
1335 *this, SvXMLStyleTokens::ScientificNumber
, xAttrList
);
1337 case XML_ELEMENT(NUMBER
, XML_FRACTION
):
1338 pContext
= new SvXMLNumFmtElementContext( GetImport(), nElement
,
1339 *this, SvXMLStyleTokens::Fraction
, xAttrList
);
1341 case XML_ELEMENT(NUMBER
, XML_CURRENCY_SYMBOL
):
1342 pContext
= new SvXMLNumFmtElementContext( GetImport(), nElement
,
1343 *this, SvXMLStyleTokens::CurrencySymbol
, xAttrList
);
1345 case XML_ELEMENT(NUMBER
, XML_DAY
):
1346 pContext
= new SvXMLNumFmtElementContext( GetImport(), nElement
,
1347 *this, SvXMLStyleTokens::Day
, xAttrList
);
1349 case XML_ELEMENT(NUMBER
, XML_MONTH
):
1350 pContext
= new SvXMLNumFmtElementContext( GetImport(), nElement
,
1351 *this, SvXMLStyleTokens::Month
, xAttrList
);
1353 case XML_ELEMENT(NUMBER
, XML_YEAR
):
1354 pContext
= new SvXMLNumFmtElementContext( GetImport(), nElement
,
1355 *this, SvXMLStyleTokens::Year
, xAttrList
);
1357 case XML_ELEMENT(NUMBER
, XML_ERA
):
1358 pContext
= new SvXMLNumFmtElementContext( GetImport(), nElement
,
1359 *this, SvXMLStyleTokens::Era
, xAttrList
);
1361 case XML_ELEMENT(NUMBER
, XML_DAY_OF_WEEK
):
1362 pContext
= new SvXMLNumFmtElementContext( GetImport(), nElement
,
1363 *this, SvXMLStyleTokens::DayOfWeek
, xAttrList
);
1365 case XML_ELEMENT(NUMBER
, XML_WEEK_OF_YEAR
):
1366 pContext
= new SvXMLNumFmtElementContext( GetImport(), nElement
,
1367 *this, SvXMLStyleTokens::WeekOfYear
, xAttrList
);
1369 case XML_ELEMENT(NUMBER
, XML_QUARTER
):
1370 pContext
= new SvXMLNumFmtElementContext( GetImport(), nElement
,
1371 *this, SvXMLStyleTokens::Quarter
, xAttrList
);
1373 case XML_ELEMENT(NUMBER
, XML_HOURS
):
1374 pContext
= new SvXMLNumFmtElementContext( GetImport(), nElement
,
1375 *this, SvXMLStyleTokens::Hours
, xAttrList
);
1377 case XML_ELEMENT(NUMBER
, XML_AM_PM
):
1378 pContext
= new SvXMLNumFmtElementContext( GetImport(), nElement
,
1379 *this, SvXMLStyleTokens::AmPm
, xAttrList
);
1381 case XML_ELEMENT(NUMBER
, XML_MINUTES
):
1382 pContext
= new SvXMLNumFmtElementContext( GetImport(), nElement
,
1383 *this, SvXMLStyleTokens::Minutes
, xAttrList
);
1385 case XML_ELEMENT(NUMBER
, XML_SECONDS
):
1386 pContext
= new SvXMLNumFmtElementContext( GetImport(), nElement
,
1387 *this, SvXMLStyleTokens::Seconds
, xAttrList
);
1389 case XML_ELEMENT(NUMBER
, XML_BOOLEAN
):
1390 pContext
= new SvXMLNumFmtElementContext( GetImport(), nElement
,
1391 *this, SvXMLStyleTokens::Boolean
, xAttrList
);
1393 case XML_ELEMENT(NUMBER
, XML_TEXT_CONTENT
):
1394 pContext
= new SvXMLNumFmtElementContext( GetImport(), nElement
,
1395 *this, SvXMLStyleTokens::TextContent
, xAttrList
);
1398 case XML_ELEMENT(STYLE
, XML_TEXT_PROPERTIES
):
1399 pContext
= new SvXMLNumFmtPropContext( GetImport(), nElement
,
1402 case XML_ELEMENT(STYLE
, XML_MAP
):
1404 // SvXMLNumFmtMapContext::EndElement adds to aMyConditions,
1405 // so there's no need for an extra flag
1406 pContext
= new SvXMLNumFmtMapContext( GetImport(), nElement
,
1414 SAL_WARN("xmloff.core", "No context for unknown-element " << SvXMLImport::getPrefixAndNameFromToken(nElement
));
1415 pContext
= new SvXMLImportContext(GetImport());
1421 sal_Int32
SvXMLNumFormatContext::GetKey()
1425 if (m_bRemoveAfterUse
)
1427 // format is used -> don't remove
1428 m_bRemoveAfterUse
= false;
1430 m_pData
->SetUsed(m_nKey
);
1432 // Add to import's list of keys now - CreateAndInsert didn't add
1433 // the style if bRemoveAfterUse was set.
1434 GetImport().AddNumberStyle( m_nKey
, GetName() );
1440 // reset bRemoveAfterUse before CreateAndInsert, so AddKey is called without bRemoveAfterUse set
1441 m_bRemoveAfterUse
= false;
1442 CreateAndInsert(true);
1447 sal_Int32
SvXMLNumFormatContext::PrivateGetKey()
1449 // used for map elements in CreateAndInsert - don't reset bRemoveAfterUse flag
1455 CreateAndInsert(true);
1460 sal_Int32
SvXMLNumFormatContext::CreateAndInsert( css::uno::Reference
< css::util::XNumberFormatsSupplier
> const & xFormatsSupplier
)
1464 SvNumberFormatter
* pFormatter
= nullptr;
1465 SvNumberFormatsSupplierObj
* pObj
=
1466 comphelper::getFromUnoTunnel
<SvNumberFormatsSupplierObj
>( xFormatsSupplier
);
1468 pFormatter
= pObj
->GetNumberFormatter();
1471 return CreateAndInsert( pFormatter
);
1479 void SvXMLNumFormatContext::CreateAndInsert(bool /*bOverwrite*/)
1482 CreateAndInsert(m_pData
->GetNumberFormatter());
1485 sal_Int32
SvXMLNumFormatContext::CreateAndInsert(SvNumberFormatter
* pFormatter
)
1489 OSL_FAIL("no number formatter");
1493 sal_uInt32 nIndex
= NUMBERFORMAT_ENTRY_NOT_FOUND
;
1495 for (size_t i
= 0; i
< m_aMyConditions
.size(); i
++)
1497 SvXMLNumFormatContext
* pStyle
= const_cast<SvXMLNumFormatContext
*>( static_cast<const SvXMLNumFormatContext
*>(m_pStyles
->FindStyleChildContext(
1498 XmlStyleFamily::DATA_STYLE
, m_aMyConditions
[i
].sMapName
)));
1501 SAL_INFO("xmloff.style", "invalid style:map references containing style");
1506 if (pStyle
->PrivateGetKey() > -1) // don't reset pStyle's bRemoveAfterUse flag
1512 if ( m_aFormatCode
.isEmpty() )
1514 // insert empty format as empty string (with quotes)
1515 // #93901# this check has to be done before inserting the conditions
1516 m_aFormatCode
.append("\"\""); // ""
1518 else if (m_bHasTrailingEmptyText
&& (nBufLen
= m_aFormatCode
.getLength()) >= 3)
1520 // Remove a trailing empty text. Earlier this may had been written to
1521 // file, like in "General;General" written with elements for
1522 // 'General"";General""' (whyever); when reading, empty text was
1523 // ignored, which it isn't anymore, so get rid of those.
1524 if (m_aFormatCode
[nBufLen
-1] == '"' && m_aFormatCode
[nBufLen
-2] == '"')
1525 m_aFormatCode
.truncate( nBufLen
- 2);
1528 m_aFormatCode
.insert( 0, m_aConditions
);
1529 m_aConditions
.setLength(0);
1530 OUString sFormat
= m_aFormatCode
.makeStringAndClear();
1532 // test special cases
1534 if ( m_bAutoDec
) // automatic decimal places
1536 // #99391# adjust only if the format contains no text elements, no conditions
1537 // and no color definition (detected by the '[' at the start)
1539 if ( m_nType
== SvXMLStylesTokens::NUMBER_STYLE
&& !m_bHasExtraText
&&
1540 m_aMyConditions
.empty() && sFormat
.toChar() != '[' )
1541 nIndex
= pFormatter
->GetStandardIndex( m_nFormatLang
);
1543 if ( m_bAutoInt
) // automatic integer digits
1545 //! only if two decimal places was set?
1547 if ( m_nType
== SvXMLStylesTokens::NUMBER_STYLE
&& !m_bHasExtraText
&&
1548 m_aMyConditions
.empty() && sFormat
.toChar() != '[' )
1549 nIndex
= pFormatter
->GetFormatIndex( NF_NUMBER_SYSTEM
, m_nFormatLang
);
1552 if ( m_nType
== SvXMLStylesTokens::BOOLEAN_STYLE
&& !m_bHasExtraText
&&
1553 m_aMyConditions
.empty() && sFormat
.toChar() != '[' )
1554 nIndex
= pFormatter
->GetFormatIndex( NF_BOOLEAN
, m_nFormatLang
);
1556 // check for default date formats
1557 if ( m_nType
== SvXMLStylesTokens::DATE_STYLE
&& m_bAutoOrder
&& !m_bDateNoDefault
)
1559 NfIndexTableOffset eFormat
= static_cast<NfIndexTableOffset
>(SvXMLNumFmtDefaults::GetDefaultDateFormat(
1560 m_eDateDOW
, m_eDateDay
, m_eDateMonth
, m_eDateYear
,
1561 m_eDateHours
, m_eDateMins
, m_eDateSecs
, m_bFromSystem
));
1562 if ( eFormat
< NF_INDEX_TABLE_RESERVED_START
)
1564 // #109651# if a date format has the automatic-order attribute and
1565 // contains exactly the elements of one of the default date formats,
1566 // use that default format, with the element order and separators
1567 // from the current locale settings
1569 nIndex
= pFormatter
->GetFormatIndex( eFormat
, m_nFormatLang
);
1573 if ( nIndex
== NUMBERFORMAT_ENTRY_NOT_FOUND
&& !sFormat
.isEmpty() )
1575 // insert by format string
1577 OUString
aFormatStr( sFormat
);
1578 nIndex
= pFormatter
->GetEntryKey( aFormatStr
, m_nFormatLang
);
1579 if ( nIndex
== NUMBERFORMAT_ENTRY_NOT_FOUND
)
1581 sal_Int32 nErrPos
= 0;
1582 SvNumFormatType l_nType
= SvNumFormatType::ALL
;
1583 bool bOk
= pFormatter
->PutEntry( aFormatStr
, nErrPos
, l_nType
, nIndex
, m_nFormatLang
);
1584 if ( !bOk
&& nErrPos
== 0 && aFormatStr
!= sFormat
)
1586 // if the string was modified by PutEntry, look for an existing format
1587 // with the modified string
1588 nIndex
= pFormatter
->GetEntryKey( aFormatStr
, m_nFormatLang
);
1589 if ( nIndex
!= NUMBERFORMAT_ENTRY_NOT_FOUND
)
1593 nIndex
= NUMBERFORMAT_ENTRY_NOT_FOUND
;
1597 //! I18N doesn't provide SYSTEM or extended date information yet
1598 if ( nIndex
!= NUMBERFORMAT_ENTRY_NOT_FOUND
&& !m_bAutoOrder
)
1600 // use fixed-order formats instead of SYS... if bAutoOrder is false
1601 // (only if the format strings are equal for the locale)
1603 NfIndexTableOffset eOffset
= pFormatter
->GetIndexTableOffset( nIndex
);
1604 if ( eOffset
== NF_DATE_SYS_DMMMYYYY
)
1606 sal_uInt32 nNewIndex
= pFormatter
->GetFormatIndex( NF_DATE_DIN_DMMMYYYY
, m_nFormatLang
);
1607 const SvNumberformat
* pOldEntry
= pFormatter
->GetEntry( nIndex
);
1608 const SvNumberformat
* pNewEntry
= pFormatter
->GetEntry( nNewIndex
);
1609 if ( pOldEntry
&& pNewEntry
&& pOldEntry
->GetFormatstring() == pNewEntry
->GetFormatstring() )
1612 else if ( eOffset
== NF_DATE_SYS_DMMMMYYYY
)
1614 sal_uInt32 nNewIndex
= pFormatter
->GetFormatIndex( NF_DATE_DIN_DMMMMYYYY
, m_nFormatLang
);
1615 const SvNumberformat
* pOldEntry
= pFormatter
->GetEntry( nIndex
);
1616 const SvNumberformat
* pNewEntry
= pFormatter
->GetEntry( nNewIndex
);
1617 if ( pOldEntry
&& pNewEntry
&& pOldEntry
->GetFormatstring() == pNewEntry
->GetFormatstring() )
1622 if ((nIndex
!= NUMBERFORMAT_ENTRY_NOT_FOUND
) && !m_sFormatTitle
.isEmpty())
1624 SvNumberformat
* pFormat
= const_cast<SvNumberformat
*>(pFormatter
->GetEntry( nIndex
));
1627 pFormat
->SetComment(m_sFormatTitle
);
1631 if ( nIndex
== NUMBERFORMAT_ENTRY_NOT_FOUND
)
1633 OSL_FAIL("invalid number format");
1634 nIndex
= pFormatter
->GetStandardIndex( m_nFormatLang
);
1637 m_pData
->AddKey( nIndex
, GetName(), m_bRemoveAfterUse
);
1640 // Add to import's list of keys (shared between styles and content import)
1641 // only if not volatile - formats are removed from NumberFormatter at the
1642 // end of each import (in SvXMLNumFmtHelper dtor).
1643 // If bRemoveAfterUse is reset later in GetKey, AddNumberStyle is called there.
1645 if (!m_bRemoveAfterUse
)
1646 GetImport().AddNumberStyle( m_nKey
, GetName() );
1651 const LocaleDataWrapper
& SvXMLNumFormatContext::GetLocaleData() const
1653 return m_pData
->GetLocaleData( m_nFormatLang
);
1656 void SvXMLNumFormatContext::AddToCode( sal_Unicode c
)
1658 m_aFormatCode
.append( c
);
1659 m_bHasExtraText
= true;
1662 void SvXMLNumFormatContext::AddToCode( std::u16string_view rString
)
1664 m_aFormatCode
.append( rString
);
1665 m_bHasExtraText
= true;
1666 m_bHasTrailingEmptyText
= false; // is set by caller again if so
1669 void SvXMLNumFormatContext::AddNumber( const SvXMLNumberInfo
& rInfo
)
1671 SvNumberFormatter
* pFormatter
= m_pData
->GetNumberFormatter();
1675 // store special conditions
1676 m_bAutoDec
= ( rInfo
.nDecimals
< 0 );
1677 m_bAutoInt
= ( rInfo
.nInteger
< 0 );
1679 sal_uInt16 nPrec
= 0;
1680 sal_uInt16 nLeading
= 0;
1681 if ( rInfo
.nDecimals
>= 0 ) // < 0 : Default
1682 nPrec
= static_cast<sal_uInt16
>(rInfo
.nDecimals
);
1683 if ( rInfo
.nInteger
>= 0 ) // < 0 : Default
1684 nLeading
= static_cast<sal_uInt16
>(rInfo
.nInteger
);
1688 if ( m_nType
== SvXMLStylesTokens::CURRENCY_STYLE
)
1690 // for currency formats, "automatic decimals" is used for the automatic
1691 // currency format with (fixed) decimals from the locale settings
1693 const LocaleDataWrapper
& rLoc
= m_pData
->GetLocaleData( m_nFormatLang
);
1694 nPrec
= rLoc
.getCurrDigits();
1698 // for other types, "automatic decimals" means dynamic determination of
1699 // decimals, as achieved with the "general" keyword
1701 m_aFormatCode
.append( pFormatter
->GetStandardName( m_nFormatLang
) );
1710 sal_uInt16 nGenPrec
= nPrec
;
1711 if ( rInfo
.nMinDecimalDigits
>= 0 )
1712 nGenPrec
= rInfo
.nMinDecimalDigits
;
1713 if ( rInfo
.bDecReplace
)
1714 nGenPrec
= 0; // generate format without decimals...
1716 bool bGrouping
= rInfo
.bGrouping
;
1717 size_t const nEmbeddedCount
= rInfo
.m_EmbeddedElements
.size();
1718 if ( nEmbeddedCount
&& rInfo
.m_EmbeddedElements
.rbegin()->first
> 0 )
1719 bGrouping
= false; // grouping and embedded characters in integer part can't be used together
1721 sal_uInt32 nStdIndex
= pFormatter
->GetStandardIndex( m_nFormatLang
);
1722 OUStringBuffer
aNumStr(pFormatter
->GenerateFormat( nStdIndex
, m_nFormatLang
,
1723 bGrouping
, false, nGenPrec
, nLeading
));
1725 if ( rInfo
.nExpDigits
>= 0 && nLeading
== 0 && !bGrouping
&& nEmbeddedCount
== 0 )
1727 // #i43959# For scientific numbers, "#" in the integer part forces a digit,
1728 // so it has to be removed if nLeading is 0 (".00E+0", not "#.00E+0").
1730 aNumStr
.stripStart('#');
1733 if ( rInfo
.nBlankInteger
> 0 )
1735 // Replace nBlankInteger '0' by '?'
1736 sal_Int32 nIndex
= 0;
1737 sal_Int32 nBlanks
= rInfo
.nBlankInteger
;
1738 sal_Int32 nIntegerEnd
= aNumStr
.indexOf( pFormatter
->GetNumDecimalSep() );
1739 if ( nIntegerEnd
< 0 )
1740 nIntegerEnd
= aNumStr
.getLength();
1741 while ( nIndex
< nIntegerEnd
&& nBlanks
> 0 )
1743 if ( aNumStr
[nIndex
] == '0' )
1745 aNumStr
[nIndex
] = '?';
1752 if ( bGrouping
&& rInfo
.nExpInterval
> rInfo
.nInteger
)
1754 sal_Int32 nIndex
= 0;
1755 sal_Int32 nDigits
= rInfo
.nInteger
;
1756 sal_Int32 nIntegerEnd
= aNumStr
.indexOf( pFormatter
->GetNumDecimalSep() );
1757 if ( nIntegerEnd
< 0 )
1758 nIntegerEnd
= aNumStr
.getLength();
1759 while ( nIndex
>= 0 && nIndex
< nIntegerEnd
)
1761 if ( ( nIndex
= aNumStr
.indexOf( '#', nIndex
) ) >= 0 )
1769 while ( rInfo
.nExpInterval
> nDigits
)
1772 aNumStr
.insert( 0, '#' );
1776 if ( ( rInfo
.bDecReplace
|| rInfo
.nMinDecimalDigits
< rInfo
.nDecimals
) && nPrec
) // add decimal replacement (dashes)
1778 // add dashes for explicit decimal replacement, # or ? for variable decimals
1779 sal_Unicode cAdd
= rInfo
.bDecReplace
? '-' : ( rInfo
.bDecAlign
? '?': '#' );
1781 if ( rInfo
.nMinDecimalDigits
== 0 )
1782 aNumStr
.append( m_pData
->GetLocaleData( m_nFormatLang
).getNumDecimalSep() );
1783 for ( sal_uInt16 i
=rInfo
.nMinDecimalDigits
; i
<nPrec
; i
++)
1784 aNumStr
.append( cAdd
);
1787 if ( nEmbeddedCount
)
1789 // insert embedded strings into number string
1790 // support integer (position >=0) and decimal (position <0) part
1791 // nZeroPos is the string position where format position 0 is inserted
1793 sal_Int32 nZeroPos
= aNumStr
.indexOf( m_pData
->GetLocaleData( m_nFormatLang
).getNumDecimalSep() );
1796 nZeroPos
= aNumStr
.getLength();
1799 // m_EmbeddedElements is sorted - last entry has the largest position (leftmost)
1800 sal_Int32
const nLastFormatPos
= rInfo
.m_EmbeddedElements
.rbegin()->first
;
1801 if ( nLastFormatPos
>= nZeroPos
)
1803 // add '#' characters so all embedded texts are really embedded in digits
1804 // (there always has to be a digit before the leftmost embedded text)
1806 sal_Int32 nAddCount
= nLastFormatPos
+ 1 - nZeroPos
;
1807 for(sal_Int32 index
= 0; index
< nAddCount
; ++index
)
1809 aNumStr
.insert(0, '#');
1811 nZeroPos
= nZeroPos
+ nAddCount
;
1814 // m_EmbeddedElements is sorted with ascending positions - loop is from right to left
1815 for (auto const& it
: rInfo
.m_EmbeddedElements
)
1817 sal_Int32
const nFormatPos
= it
.first
;
1818 sal_Int32 nInsertPos
= nZeroPos
- nFormatPos
;
1819 if ( nInsertPos
>= 0 )
1821 // #107805# always quote embedded strings - even space would otherwise
1822 // be recognized as thousands separator in French.
1824 aNumStr
.insert(nInsertPos
, OUString::Concat("\"") + it
.second
+ "\"");
1829 m_aFormatCode
.append( aNumStr
);
1831 // add extra thousands separators for display factor
1833 if (rInfo
.fDisplayFactor
== 1.0 || rInfo
.fDisplayFactor
<= 0.0)
1836 // test for 1.0 is just for optimization - nSepCount would be 0
1838 // one separator for each factor of 1000
1839 sal_Int32 nSepCount
= static_cast<sal_Int32
>(::rtl::math::round( log10(rInfo
.fDisplayFactor
) / 3.0 ));
1840 if ( nSepCount
> 0 )
1842 OUString aSep
= m_pData
->GetLocaleData( m_nFormatLang
).getNumThousandSep();
1843 for ( sal_Int32 i
=0; i
<nSepCount
; i
++ )
1844 m_aFormatCode
.append( aSep
);
1848 void SvXMLNumFormatContext::AddCurrency( const OUString
& rContent
, LanguageType nLang
)
1850 bool bAutomatic
= false;
1851 OUString aSymbol
= rContent
;
1852 if ( aSymbol
.isEmpty())
1854 SvNumberFormatter
* pFormatter
= m_pData
->GetNumberFormatter();
1857 pFormatter
->ChangeIntl( m_nFormatLang
);
1858 OUString sCurString
, sDummy
;
1859 pFormatter
->GetCompatibilityCurrency( sCurString
, sDummy
);
1860 aSymbol
= sCurString
;
1865 else if ( nLang
== LANGUAGE_SYSTEM
&& aSymbol
== "CCC" )
1867 // "CCC" is used for automatic long symbol
1873 // remove unnecessary quotes before automatic symbol (formats like "-(0DM)")
1874 // otherwise the currency symbol isn't recognized (#94048#)
1876 sal_Int32 nLength
= m_aFormatCode
.getLength();
1877 if ( nLength
> 1 && m_aFormatCode
[nLength
- 1] == '"' )
1879 // find start of quoted string
1880 // When SvXMLNumFmtElementContext::EndElement creates escaped quotes,
1881 // they must be handled here, too.
1883 sal_Int32 nFirst
= nLength
- 2;
1884 while ( nFirst
>= 0 && m_aFormatCode
[nFirst
] != '"' )
1888 // remove both quotes from aFormatCode
1889 OUString aOld
= m_aFormatCode
.makeStringAndClear();
1891 m_aFormatCode
.append( aOld
.subView( 0, nFirst
) );
1892 if ( nLength
> nFirst
+ 2 )
1893 m_aFormatCode
.append( aOld
.subView( nFirst
+ 1, nLength
- nFirst
- 2 ) );
1899 m_aFormatCode
.append( "[$" ); // intro for "new" currency symbols
1901 m_aFormatCode
.append( aSymbol
);
1905 if ( nLang
!= LANGUAGE_SYSTEM
)
1907 // '-' sign and language code in hex:
1908 m_aFormatCode
.append("-" + OUString(OUString::number(sal_uInt16(nLang
), 16)).toAsciiUpperCase());
1911 m_aFormatCode
.append( ']' ); // end of "new" currency symbol
1915 void SvXMLNumFormatContext::AddNfKeyword( sal_uInt16 nIndex
)
1917 SvNumberFormatter
* pFormatter
= m_pData
->GetNumberFormatter();
1921 if ( nIndex
== NF_KEY_NNNN
)
1923 nIndex
= NF_KEY_NNN
;
1924 m_bHasLongDoW
= true; // to remove string constant with separator
1927 OUString sKeyword
= pFormatter
->GetKeyword( m_nFormatLang
, nIndex
);
1929 if ( nIndex
== NF_KEY_H
|| nIndex
== NF_KEY_HH
||
1930 nIndex
== NF_KEY_MI
|| nIndex
== NF_KEY_MMI
||
1931 nIndex
== NF_KEY_S
|| nIndex
== NF_KEY_SS
)
1933 if ( !m_bTruncate
&& !m_bHasDateTime
)
1935 // with truncate-on-overflow = false, add "[]" to first time part
1936 m_aFormatCode
.append("[" + sKeyword
+ "]");
1940 m_aFormatCode
.append( sKeyword
);
1942 m_bHasDateTime
= true;
1946 m_aFormatCode
.append( sKeyword
);
1948 // collect the date elements that the format contains, to recognize default date formats
1951 case NF_KEY_NN
: m_eDateDOW
= XML_DEA_SHORT
; break;
1953 case NF_KEY_NNNN
: m_eDateDOW
= XML_DEA_LONG
; break;
1954 case NF_KEY_D
: m_eDateDay
= XML_DEA_SHORT
; break;
1955 case NF_KEY_DD
: m_eDateDay
= XML_DEA_LONG
; break;
1956 case NF_KEY_M
: m_eDateMonth
= XML_DEA_SHORT
; break;
1957 case NF_KEY_MM
: m_eDateMonth
= XML_DEA_LONG
; break;
1958 case NF_KEY_MMM
: m_eDateMonth
= XML_DEA_TEXTSHORT
; break;
1959 case NF_KEY_MMMM
: m_eDateMonth
= XML_DEA_TEXTLONG
; break;
1960 case NF_KEY_YY
: m_eDateYear
= XML_DEA_SHORT
; break;
1961 case NF_KEY_YYYY
: m_eDateYear
= XML_DEA_LONG
; break;
1962 case NF_KEY_H
: m_eDateHours
= XML_DEA_SHORT
; break;
1963 case NF_KEY_HH
: m_eDateHours
= XML_DEA_LONG
; break;
1964 case NF_KEY_MI
: m_eDateMins
= XML_DEA_SHORT
; break;
1965 case NF_KEY_MMI
: m_eDateMins
= XML_DEA_LONG
; break;
1966 case NF_KEY_S
: m_eDateSecs
= XML_DEA_SHORT
; break;
1967 case NF_KEY_SS
: m_eDateSecs
= XML_DEA_LONG
; break;
1969 case NF_KEY_AMPM
: break; // AM/PM may or may not be in date/time formats -> ignore by itself
1971 m_bDateNoDefault
= true; // any other element -> no default format
1975 static bool lcl_IsAtEnd( OUStringBuffer
& rBuffer
, std::u16string_view rToken
)
1977 sal_Int32 nBufLen
= rBuffer
.getLength();
1978 sal_Int32 nTokLen
= rToken
.size();
1980 if ( nTokLen
> nBufLen
)
1983 sal_Int32 nStartPos
= nBufLen
- nTokLen
;
1984 for ( sal_Int32 nTokPos
= 0; nTokPos
< nTokLen
; nTokPos
++ )
1985 if ( rToken
[ nTokPos
] != rBuffer
[nStartPos
+ nTokPos
] )
1991 bool SvXMLNumFormatContext::ReplaceNfKeyword( sal_uInt16 nOld
, sal_uInt16 nNew
)
1993 // replaces one keyword with another if it is found at the end of the code
1995 SvNumberFormatter
* pFormatter
= m_pData
->GetNumberFormatter();
1999 OUString sOldStr
= pFormatter
->GetKeyword( m_nFormatLang
, nOld
);
2000 if ( lcl_IsAtEnd( m_aFormatCode
, sOldStr
) )
2002 // remove old keyword
2003 m_aFormatCode
.setLength( m_aFormatCode
.getLength() - sOldStr
.getLength() );
2006 OUString sNewStr
= pFormatter
->GetKeyword( m_nFormatLang
, nNew
);
2007 m_aFormatCode
.append( sNewStr
);
2009 return true; // changed
2011 return false; // not found
2014 void SvXMLNumFormatContext::AddCondition( const sal_Int32 nIndex
)
2016 OUString rApplyName
= m_aMyConditions
[nIndex
].sMapName
;
2017 OUString rCondition
= m_aMyConditions
[nIndex
].sCondition
;
2018 SvNumberFormatter
* pFormatter
= m_pData
->GetNumberFormatter();
2019 sal_uInt32 l_nKey
= m_pData
->GetKeyForName( rApplyName
);
2022 if ( !(pFormatter
&& l_nKey
!= NUMBERFORMAT_ENTRY_NOT_FOUND
&&
2023 rCondition
.startsWith("value()", &sRealCond
)) )
2026 //! test for valid conditions
2027 //! test for default conditions
2029 bool bDefaultCond
= false;
2031 //! collect all conditions first and adjust default to >=0, >0 or <0 depending on count
2032 //! allow blanks in conditions
2033 if ( m_aConditions
.isEmpty() && m_aMyConditions
.size() == 1 && sRealCond
== ">=0" )
2034 bDefaultCond
= true;
2036 if ( m_nType
== SvXMLStylesTokens::TEXT_STYLE
&& static_cast<size_t>(nIndex
) == m_aMyConditions
.size() - 1 )
2038 // The last condition in a number format with a text part can only
2039 // be "all other numbers", the condition string must be empty.
2040 bDefaultCond
= true;
2046 sal_Int32 nPos
= sRealCond
.indexOf( "!=" );
2049 sRealCond
= sRealCond
.replaceAt( nPos
, 2, u
"<>" );
2052 nPos
= sRealCond
.indexOf( '.' );
2055 // #i8026# #103991# localize decimal separator
2056 const OUString
& rDecSep
= GetLocaleData().getNumDecimalSep();
2057 if ( rDecSep
.getLength() > 1 || rDecSep
[0] != '.' )
2059 sRealCond
= sRealCond
.replaceAt( nPos
, 1, rDecSep
);
2062 m_aConditions
.append("[" + sRealCond
+ "]");
2065 const SvNumberformat
* pFormat
= pFormatter
->GetEntry(l_nKey
);
2067 m_aConditions
.append( pFormat
->GetFormatstring() );
2069 m_aConditions
.append( ';' );
2072 void SvXMLNumFormatContext::AddCondition( const OUString
& rCondition
, const OUString
& rApplyName
)
2074 MyCondition aCondition
;
2075 aCondition
.sCondition
= rCondition
;
2076 aCondition
.sMapName
= rApplyName
;
2077 m_aMyConditions
.push_back(aCondition
);
2080 void SvXMLNumFormatContext::AddColor( Color
const nColor
)
2082 SvNumberFormatter
* pFormatter
= m_pData
->GetNumberFormatter();
2086 OUStringBuffer aColName
;
2087 for ( sal_uInt16 i
=0; i
<XML_NUMF_COLORCOUNT
; i
++ )
2088 if (nColor
== aNumFmtStdColors
[i
])
2090 aColName
= pFormatter
->GetKeyword( m_nFormatLang
, sal::static_int_cast
< sal_uInt16
>(NF_KEY_FIRSTCOLOR
+ i
) );
2094 if ( !aColName
.isEmpty() )
2096 aColName
.insert( 0, '[' );
2097 aColName
.append( ']' );
2098 m_aFormatCode
.insert( 0, aColName
);
2102 void SvXMLNumFormatContext::UpdateCalendar( const OUString
& rNewCalendar
)
2104 if ( rNewCalendar
== m_sCalendar
)
2107 if (rNewCalendar
.isEmpty() || rNewCalendar
== m_aImplicitCalendar
[0])
2109 m_eImplicitCalendar
= (m_eImplicitCalendar
== ImplicitCalendar::OTHER
?
2110 ImplicitCalendar::DEFAULT_FROM_OTHER
: ImplicitCalendar::DEFAULT
);
2112 else if (m_aImplicitCalendar
[0].isEmpty() && rNewCalendar
== GetLocaleData().getDefaultCalendar()->Name
)
2114 m_eImplicitCalendar
= (m_eImplicitCalendar
== ImplicitCalendar::OTHER
?
2115 ImplicitCalendar::DEFAULT_FROM_OTHER
: ImplicitCalendar::DEFAULT
);
2116 m_aImplicitCalendar
[0] = rNewCalendar
;
2118 else if (rNewCalendar
== m_aImplicitCalendar
[1])
2120 m_eImplicitCalendar
= (m_eImplicitCalendar
== ImplicitCalendar::OTHER
?
2121 ImplicitCalendar::SECONDARY_FROM_OTHER
: ImplicitCalendar::SECONDARY
);
2123 else if (m_aImplicitCalendar
[1].isEmpty() && GetLocaleData().doesSecondaryCalendarUseEC( rNewCalendar
))
2125 m_eImplicitCalendar
= (m_eImplicitCalendar
== ImplicitCalendar::OTHER
?
2126 ImplicitCalendar::SECONDARY_FROM_OTHER
: ImplicitCalendar::SECONDARY
);
2127 m_aImplicitCalendar
[1] = rNewCalendar
;
2131 m_eImplicitCalendar
= ImplicitCalendar::OTHER
;
2134 if (m_eImplicitCalendar
!= ImplicitCalendar::DEFAULT
&& m_eImplicitCalendar
!= ImplicitCalendar::SECONDARY
)
2136 // A switch from empty default calendar to named default calendar or
2137 // vice versa is not a switch.
2138 bool bSameDefault
= false;
2139 if (m_sCalendar
.isEmpty() || rNewCalendar
.isEmpty())
2141 // As both are not equal, only one can be empty here, the other
2143 const OUString
& rDefaultCalendar
= GetLocaleData().getDefaultCalendar()->Name
;
2144 // So if one is the named default calendar the other is the
2145 // empty default calendar.
2146 bSameDefault
= (rNewCalendar
== rDefaultCalendar
|| m_sCalendar
== rDefaultCalendar
);
2150 m_aFormatCode
.append( "[~" ); // intro for calendar code
2151 if (rNewCalendar
.isEmpty())
2153 // Empty calendar name here means switching to default calendar
2154 // from a different calendar. Needs to be explicitly stated in
2156 m_aFormatCode
.append( GetLocaleData().getDefaultCalendar()->Name
);
2160 m_aFormatCode
.append( rNewCalendar
);
2162 m_aFormatCode
.append( ']' ); // end of calendar code
2165 m_sCalendar
= rNewCalendar
;
2168 bool SvXMLNumFormatContext::IsSystemLanguage() const
2170 return m_nFormatLang
== LANGUAGE_SYSTEM
;
2174 // SvXMLNumFmtHelper
2177 SvXMLNumFmtHelper::SvXMLNumFmtHelper(
2178 const uno::Reference
<util::XNumberFormatsSupplier
>& rSupp
,
2179 const uno::Reference
<uno::XComponentContext
>& rxContext
)
2181 SAL_WARN_IF( !rxContext
.is(), "xmloff", "got no service manager" );
2183 SvNumberFormatter
* pFormatter
= nullptr;
2184 SvNumberFormatsSupplierObj
* pObj
=
2185 comphelper::getFromUnoTunnel
<SvNumberFormatsSupplierObj
>( rSupp
);
2187 pFormatter
= pObj
->GetNumberFormatter();
2189 m_pData
= std::make_unique
<SvXMLNumImpData
>( pFormatter
, rxContext
);
2192 SvXMLNumFmtHelper::SvXMLNumFmtHelper(
2193 SvNumberFormatter
* pNumberFormatter
,
2194 const uno::Reference
<uno::XComponentContext
>& rxContext
)
2196 SAL_WARN_IF( !rxContext
.is(), "xmloff", "got no service manager" );
2198 m_pData
= std::make_unique
<SvXMLNumImpData
>( pNumberFormatter
, rxContext
);
2201 SvXMLNumFmtHelper::~SvXMLNumFmtHelper()
2203 // remove temporary (volatile) formats from NumberFormatter
2204 m_pData
->RemoveVolatileFormats();
2208 SvXMLStyleContext
* SvXMLNumFmtHelper::CreateChildContext( SvXMLImport
& rImport
,
2210 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& xAttrList
,
2211 SvXMLStylesContext
& rStyles
)
2213 SvXMLStylesTokens nStyleToken
;
2216 case XML_ELEMENT(NUMBER
, XML_NUMBER_STYLE
):
2217 nStyleToken
= SvXMLStylesTokens::NUMBER_STYLE
;
2219 case XML_ELEMENT(NUMBER
, XML_CURRENCY_STYLE
):
2220 nStyleToken
= SvXMLStylesTokens::CURRENCY_STYLE
;
2222 case XML_ELEMENT(NUMBER
, XML_PERCENTAGE_STYLE
):
2223 nStyleToken
= SvXMLStylesTokens::PERCENTAGE_STYLE
;
2225 case XML_ELEMENT(NUMBER
, XML_DATE_STYLE
):
2226 nStyleToken
= SvXMLStylesTokens::DATE_STYLE
;
2228 case XML_ELEMENT(NUMBER
, XML_TIME_STYLE
):
2229 nStyleToken
= SvXMLStylesTokens::TIME_STYLE
;
2231 case XML_ELEMENT(NUMBER
, XML_BOOLEAN_STYLE
):
2232 nStyleToken
= SvXMLStylesTokens::BOOLEAN_STYLE
;
2234 case XML_ELEMENT(NUMBER
, XML_TEXT_STYLE
):
2235 nStyleToken
= SvXMLStylesTokens::TEXT_STYLE
;
2238 // return NULL if not a data style, caller must handle other elements
2241 return new SvXMLNumFormatContext( rImport
, nElement
,
2242 m_pData
.get(), nStyleToken
, xAttrList
, rStyles
);
2245 LanguageType
SvXMLNumFmtHelper::GetLanguageForKey(sal_Int32 nKey
) const
2247 if (m_pData
->GetNumberFormatter())
2249 const SvNumberformat
* pEntry
= m_pData
->GetNumberFormatter()->GetEntry(nKey
);
2251 return pEntry
->GetLanguage();
2254 return LANGUAGE_SYSTEM
;
2257 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */