/src/libreoffice/xmloff/source/style/xmlnumfi.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
5 | | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
11 | | * Licensed to the Apache Software Foundation (ASF) under one or more |
12 | | * contributor license agreements. See the NOTICE file distributed |
13 | | * with this work for additional information regarding copyright |
14 | | * ownership. The ASF licenses this file to you under the Apache |
15 | | * License, Version 2.0 (the "License"); you may not use this file |
16 | | * except in compliance with the License. You may obtain a copy of |
17 | | * the License at http://www.apache.org/licenses/LICENSE-2.0 . |
18 | | */ |
19 | | |
20 | | #include <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> |
30 | | |
31 | | #include <sax/tools/converter.hxx> |
32 | | |
33 | | #include <com/sun/star/i18n/Calendar2.hpp> |
34 | | #include <com/sun/star/i18n/NativeNumberXmlAttributes.hpp> |
35 | | |
36 | | #include <utility> |
37 | | #include <xmloff/xmlement.hxx> |
38 | | #include <xmloff/xmlnumfi.hxx> |
39 | | #include <xmloff/xmlnamespace.hxx> |
40 | | #include <xmloff/xmlictxt.hxx> |
41 | | #include <xmloff/xmlimp.hxx> |
42 | | #include <xmloff/xmluconv.hxx> |
43 | | #include <xmloff/families.hxx> |
44 | | #include <xmloff/xmltoken.hxx> |
45 | | #include <xmloff/languagetagodf.hxx> |
46 | | |
47 | | #include <memory> |
48 | | #include <string_view> |
49 | | #include <vector> |
50 | | |
51 | | using namespace ::com::sun::star; |
52 | | using namespace ::xmloff::token; |
53 | | |
54 | | namespace { |
55 | | |
56 | | struct SvXMLNumFmtEntry |
57 | | { |
58 | | OUString aName; |
59 | | sal_uInt32 nKey; |
60 | | bool bRemoveAfterUse; |
61 | | |
62 | | SvXMLNumFmtEntry( OUString aN, sal_uInt32 nK, bool bR ) : |
63 | 11.1k | aName(std::move(aN)), nKey(nK), bRemoveAfterUse(bR) {} |
64 | | }; |
65 | | |
66 | | } |
67 | | |
68 | | class SvXMLNumImpData |
69 | | { |
70 | | SvNumberFormatter* pFormatter; |
71 | | const LocaleDataWrapper* pLocaleData { nullptr }; |
72 | | std::vector<SvXMLNumFmtEntry> m_NameEntries; |
73 | | |
74 | | public: |
75 | | SvXMLNumImpData(SvNumberFormatter* pFmt); |
76 | | |
77 | 47.2k | SvNumberFormatter* GetNumberFormatter() const { return pFormatter; } |
78 | | const LocaleDataWrapper& GetLocaleData( LanguageType nLang ); |
79 | | sal_uInt32 GetKeyForName( std::u16string_view rName ); |
80 | | void AddKey( sal_uInt32 nKey, const OUString& rName, bool bRemoveAfterUse ); |
81 | | void SetUsed( sal_uInt32 nKey ); |
82 | | void RemoveVolatileFormats(); |
83 | | }; |
84 | | |
85 | | struct SvXMLNumberInfo |
86 | | { |
87 | | sal_Int32 nDecimals = -1; |
88 | | sal_Int32 nInteger = -1; /// Total min number of digits in integer part ('0' + '?') |
89 | | sal_Int32 nBlankInteger = -1; /// Number of '?' in integer part |
90 | | sal_Int32 nExpDigits = -1; /// Number of '0' and '?' in exponent |
91 | | sal_Int32 nBlankExp = -1; /// Number of '?' in exponent |
92 | | sal_Int32 nExpInterval = -1; |
93 | | sal_Int32 nMinNumerDigits = -1; |
94 | | sal_Int32 nMinDenomDigits = -1; |
95 | | sal_Int32 nMaxNumerDigits = -1; |
96 | | sal_Int32 nMaxDenomDigits = -1; |
97 | | sal_Int32 nFracDenominator = -1; |
98 | | sal_Int32 nMinDecimalDigits = -1; |
99 | | sal_Int32 nZerosNumerDigits = -1; |
100 | | sal_Int32 nZerosDenomDigits = -1; |
101 | | bool bGrouping = false; |
102 | | bool bDecReplace = false; |
103 | | bool bExpSign = true; |
104 | | bool bExponentLowercase = false; /// Exponent is 'e' instead of 'E' |
105 | | bool bDecAlign = false; |
106 | | double fDisplayFactor = 1.0; |
107 | | OUString aIntegerFractionDelimiter; |
108 | | std::map<sal_Int32, OUString> m_EmbeddedElements; |
109 | | }; |
110 | | |
111 | | namespace { |
112 | | |
113 | | enum class SvXMLStyleTokens; |
114 | | |
115 | | class SvXMLNumFmtElementContext : public SvXMLImportContext |
116 | | { |
117 | | SvXMLNumFormatContext& rParent; |
118 | | SvXMLStyleTokens nType; |
119 | | OUStringBuffer aContent; |
120 | | SvXMLNumberInfo aNumInfo; |
121 | | LanguageType nElementLang; |
122 | | bool bLong; |
123 | | bool bTextual; |
124 | | OUString sCalendar; |
125 | | OUString sBlankWidthString; |
126 | | |
127 | | public: |
128 | | SvXMLNumFmtElementContext( SvXMLImport& rImport, sal_Int32 nElement, |
129 | | SvXMLNumFormatContext& rParentContext, SvXMLStyleTokens nNewType, |
130 | | const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList ); |
131 | | |
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; |
136 | | |
137 | | void AddEmbeddedElement( sal_Int32 nFormatPos, std::u16string_view rContent, std::u16string_view rBlankWidthString ); |
138 | | }; |
139 | | |
140 | | class SvXMLNumFmtEmbeddedTextContext : public SvXMLImportContext |
141 | | { |
142 | | SvXMLNumFmtElementContext& rParent; |
143 | | OUStringBuffer aContent; |
144 | | sal_Int32 nTextPosition; |
145 | | OUString aBlankWidthString; |
146 | | |
147 | | public: |
148 | | SvXMLNumFmtEmbeddedTextContext( SvXMLImport& rImport, sal_Int32 nElement, |
149 | | SvXMLNumFmtElementContext& rParentContext, |
150 | | const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList ); |
151 | | |
152 | | virtual void SAL_CALL characters( const OUString& rChars ) override; |
153 | | virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; |
154 | | }; |
155 | | |
156 | | class SvXMLNumFmtMapContext : public SvXMLImportContext |
157 | | { |
158 | | SvXMLNumFormatContext& rParent; |
159 | | OUString sCondition; |
160 | | OUString sName; |
161 | | |
162 | | public: |
163 | | SvXMLNumFmtMapContext( SvXMLImport& rImport, sal_Int32 nElement, |
164 | | SvXMLNumFormatContext& rParentContext, |
165 | | const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList ); |
166 | | |
167 | | virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; |
168 | | }; |
169 | | |
170 | | class SvXMLNumFmtPropContext : public SvXMLImportContext |
171 | | { |
172 | | SvXMLNumFormatContext& rParent; |
173 | | Color m_nColor; |
174 | | bool bColSet; |
175 | | |
176 | | public: |
177 | | SvXMLNumFmtPropContext( SvXMLImport& rImport, sal_Int32 nElement, |
178 | | SvXMLNumFormatContext& rParentContext, |
179 | | const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList ); |
180 | | |
181 | | virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; |
182 | | }; |
183 | | |
184 | | enum class SvXMLStyleTokens |
185 | | { |
186 | | Text, |
187 | | FillCharacter, |
188 | | Number, |
189 | | ScientificNumber, |
190 | | Fraction, |
191 | | CurrencySymbol, |
192 | | Day, |
193 | | Month, |
194 | | Year, |
195 | | Era, |
196 | | DayOfWeek, |
197 | | WeekOfYear, |
198 | | Quarter, |
199 | | Hours, |
200 | | AmPm, |
201 | | Minutes, |
202 | | Seconds, |
203 | | Boolean, |
204 | | TextContent |
205 | | }; |
206 | | |
207 | | } |
208 | | |
209 | | // standard colors |
210 | | |
211 | | |
212 | 6.68k | #define XML_NUMF_COLORCOUNT 10 |
213 | | |
214 | | const Color aNumFmtStdColors[XML_NUMF_COLORCOUNT] = |
215 | | { |
216 | | COL_BLACK, |
217 | | COL_LIGHTBLUE, |
218 | | COL_LIGHTGREEN, |
219 | | COL_LIGHTCYAN, |
220 | | COL_LIGHTRED, |
221 | | COL_LIGHTMAGENTA, |
222 | | COL_BROWN, |
223 | | COL_GRAY, |
224 | | COL_YELLOW, |
225 | | COL_WHITE |
226 | | }; |
227 | | |
228 | | |
229 | | // token maps |
230 | | |
231 | | |
232 | | // maps for SvXMLUnitConverter::convertEnum |
233 | | |
234 | | const SvXMLEnumMapEntry<bool> aStyleValueMap[] = |
235 | | { |
236 | | { XML_SHORT, false }, |
237 | | { XML_LONG, true }, |
238 | | { XML_TOKEN_INVALID, false } |
239 | | }; |
240 | | |
241 | | const SvXMLEnumMapEntry<bool> aFormatSourceMap[] = |
242 | | { |
243 | | { XML_FIXED, false }, |
244 | | { XML_LANGUAGE, true }, |
245 | | { XML_TOKEN_INVALID, false } |
246 | | }; |
247 | | |
248 | | namespace { |
249 | | |
250 | | struct SvXMLDefaultDateFormat |
251 | | { |
252 | | NfIndexTableOffset eFormat; |
253 | | SvXMLDateElementAttributes eDOW; |
254 | | SvXMLDateElementAttributes eDay; |
255 | | SvXMLDateElementAttributes eMonth; |
256 | | SvXMLDateElementAttributes eYear; |
257 | | SvXMLDateElementAttributes eHours; |
258 | | SvXMLDateElementAttributes eMins; |
259 | | SvXMLDateElementAttributes eSecs; |
260 | | bool bSystem; |
261 | | }; |
262 | | |
263 | | } |
264 | | |
265 | | const SvXMLDefaultDateFormat aDefaultDateFormats[] = |
266 | | { |
267 | | // format day-of-week day month year hours minutes seconds format-source |
268 | | |
269 | | { 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 }, |
270 | | { 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 }, |
271 | | { 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 }, |
272 | | { 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 }, |
273 | | { 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 }, |
274 | | { 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 }, |
275 | | { 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 }, |
276 | | { 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 }, |
277 | | { 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 }, |
278 | | { 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 }, |
279 | | { 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 }, |
280 | | { 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 }, |
281 | | { 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 }, |
282 | | { 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 }, |
283 | | { 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 } |
284 | | }; |
285 | | |
286 | | |
287 | | // SvXMLNumImpData |
288 | | |
289 | | |
290 | | SvXMLNumImpData::SvXMLNumImpData( |
291 | | SvNumberFormatter* pFmt ) |
292 | 15.0k | : pFormatter(pFmt) |
293 | 15.0k | { |
294 | 15.0k | } |
295 | | |
296 | | sal_uInt32 SvXMLNumImpData::GetKeyForName( std::u16string_view rName ) |
297 | 3.38k | { |
298 | 3.38k | for (const auto& rObj : m_NameEntries) |
299 | 78.3k | { |
300 | 78.3k | if (rObj.aName == rName) |
301 | 3.38k | return rObj.nKey; // found |
302 | 78.3k | } |
303 | 0 | return NUMBERFORMAT_ENTRY_NOT_FOUND; |
304 | 3.38k | } |
305 | | |
306 | | void SvXMLNumImpData::AddKey( sal_uInt32 nKey, const OUString& rName, bool bRemoveAfterUse ) |
307 | 11.1k | { |
308 | 11.1k | if ( bRemoveAfterUse ) |
309 | 3.83k | { |
310 | | // if there is already an entry for this key without the bRemoveAfterUse flag, |
311 | | // clear the flag for this entry, too |
312 | | |
313 | 3.83k | for (const auto& rObj : m_NameEntries) |
314 | 97.9k | { |
315 | 97.9k | if (rObj.nKey == nKey && !rObj.bRemoveAfterUse) |
316 | 21 | { |
317 | 21 | bRemoveAfterUse = false; // clear flag for new entry |
318 | 21 | break; |
319 | 21 | } |
320 | 97.9k | } |
321 | 3.83k | } |
322 | 7.32k | else |
323 | 7.32k | { |
324 | | // call SetUsed to clear the bRemoveAfterUse flag for other entries for this key |
325 | 7.32k | SetUsed( nKey ); |
326 | 7.32k | } |
327 | | |
328 | 11.1k | m_NameEntries.emplace_back(rName, nKey, bRemoveAfterUse); |
329 | 11.1k | } |
330 | | |
331 | | void SvXMLNumImpData::SetUsed( sal_uInt32 nKey ) |
332 | 7.32k | { |
333 | 7.32k | for (auto& rObj : m_NameEntries) |
334 | 107k | { |
335 | 107k | if (rObj.nKey == nKey) |
336 | 457 | { |
337 | 457 | rObj.bRemoveAfterUse = false; // used -> don't remove |
338 | | |
339 | | // continue searching - there may be several entries for the same key |
340 | | // (with different names), the format must not be deleted if any one of |
341 | | // them is used |
342 | 457 | } |
343 | 107k | } |
344 | 7.32k | } |
345 | | |
346 | | void SvXMLNumImpData::RemoveVolatileFormats() |
347 | 15.0k | { |
348 | | // remove temporary (volatile) formats from NumberFormatter |
349 | | // called at the end of each import (styles and content), so volatile formats |
350 | | // from styles can't be used in content |
351 | | |
352 | 15.0k | if ( !pFormatter ) |
353 | 0 | return; |
354 | | |
355 | 15.0k | for (const auto& rObj : m_NameEntries) |
356 | 11.1k | { |
357 | 11.1k | if (rObj.bRemoveAfterUse ) |
358 | 3.79k | { |
359 | 3.79k | const SvNumberformat* pFormat = pFormatter->GetEntry(rObj.nKey); |
360 | 3.79k | if (pFormat && (pFormat->GetType() & SvNumFormatType::DEFINED)) |
361 | 3.10k | pFormatter->DeleteEntry(rObj.nKey); |
362 | 3.79k | } |
363 | 11.1k | } |
364 | 15.0k | } |
365 | | |
366 | | const LocaleDataWrapper& SvXMLNumImpData::GetLocaleData( LanguageType nLang ) |
367 | 6.59k | { |
368 | 6.59k | if ( !pLocaleData || pLocaleData->getLanguageTag() != LanguageTag(nLang) ) |
369 | 1.79k | pLocaleData = LocaleDataWrapper::get( LanguageTag( nLang ) ); |
370 | 6.59k | return *pLocaleData; |
371 | 6.59k | } |
372 | | |
373 | | |
374 | | // SvXMLNumFmtMapContext |
375 | | |
376 | | |
377 | | SvXMLNumFmtMapContext::SvXMLNumFmtMapContext( SvXMLImport& rImport, |
378 | | sal_Int32 /*nElement*/, |
379 | | SvXMLNumFormatContext& rParentContext, |
380 | | const uno::Reference<xml::sax::XFastAttributeList>& xAttrList ) : |
381 | 5.06k | SvXMLImportContext( rImport ), |
382 | 5.06k | rParent( rParentContext ) |
383 | 5.06k | { |
384 | 5.06k | for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) ) |
385 | 9.70k | { |
386 | 9.70k | OUString sValue = aIter.toString(); |
387 | 9.70k | switch(aIter.getToken()) |
388 | 9.70k | { |
389 | 4.76k | case XML_ELEMENT(STYLE, XML_CONDITION): |
390 | 4.76k | sCondition = sValue; |
391 | 4.76k | break; |
392 | 4.89k | case XML_ELEMENT(STYLE, XML_APPLY_STYLE_NAME): |
393 | 4.89k | sName = sValue; |
394 | 4.89k | break; |
395 | 42 | default: |
396 | 42 | XMLOFF_WARN_UNKNOWN("xmloff", aIter); |
397 | 9.70k | } |
398 | 9.70k | } |
399 | 5.06k | } |
400 | | |
401 | | void SvXMLNumFmtMapContext::endFastElement(sal_Int32 ) |
402 | 5.05k | { |
403 | 5.05k | rParent.AddCondition( sCondition, sName ); |
404 | 5.05k | } |
405 | | |
406 | | |
407 | | // SvXMLNumFmtPropContext |
408 | | |
409 | | |
410 | | SvXMLNumFmtPropContext::SvXMLNumFmtPropContext( SvXMLImport& rImport, |
411 | | sal_Int32 /*nElement*/, |
412 | | SvXMLNumFormatContext& rParentContext, |
413 | | const uno::Reference<xml::sax::XFastAttributeList>& xAttrList ) : |
414 | 1.74k | SvXMLImportContext( rImport ), |
415 | 1.74k | rParent( rParentContext ), |
416 | 1.74k | m_nColor( 0 ), |
417 | 1.74k | bColSet( false ) |
418 | 1.74k | { |
419 | 1.74k | for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) ) |
420 | 1.60k | { |
421 | 1.60k | switch ( aIter.getToken()) |
422 | 1.60k | { |
423 | 0 | case XML_ELEMENT(FO, XML_COLOR): |
424 | 1.34k | case XML_ELEMENT(FO_COMPAT, XML_COLOR): |
425 | 1.34k | bColSet = ::sax::Converter::convertColor( m_nColor, aIter.toView() ); |
426 | 1.34k | break; |
427 | 260 | default: |
428 | 260 | XMLOFF_WARN_UNKNOWN("xmloff", aIter); |
429 | 1.60k | } |
430 | 1.60k | } |
431 | 1.74k | } |
432 | | |
433 | | void SvXMLNumFmtPropContext::endFastElement(sal_Int32 ) |
434 | 1.73k | { |
435 | 1.73k | if (bColSet) |
436 | 1.32k | rParent.AddColor( m_nColor ); |
437 | 1.73k | } |
438 | | |
439 | | |
440 | | // SvXMLNumFmtEmbeddedTextContext |
441 | | |
442 | | |
443 | | SvXMLNumFmtEmbeddedTextContext::SvXMLNumFmtEmbeddedTextContext( SvXMLImport& rImport, |
444 | | sal_Int32 /*nElement*/, |
445 | | SvXMLNumFmtElementContext& rParentContext, |
446 | | const uno::Reference<xml::sax::XFastAttributeList>& xAttrList ) : |
447 | 0 | SvXMLImportContext( rImport ), |
448 | 0 | rParent( rParentContext ), |
449 | 0 | nTextPosition( 0 ) |
450 | 0 | { |
451 | 0 | sal_Int32 nAttrVal; |
452 | |
|
453 | 0 | for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) ) |
454 | 0 | { |
455 | 0 | if ( aIter.getToken() == XML_ELEMENT(NUMBER, XML_POSITION) ) |
456 | 0 | { |
457 | 0 | if (::sax::Converter::convertNumber( nAttrVal, aIter.toView() )) |
458 | 0 | nTextPosition = nAttrVal; |
459 | 0 | } |
460 | 0 | else if ( aIter.getToken() == XML_ELEMENT(LO_EXT, XML_BLANK_WIDTH_CHAR) |
461 | 0 | || aIter.getToken() == XML_ELEMENT(NUMBER, XML_BLANK_WIDTH_CHAR) ) |
462 | 0 | { |
463 | 0 | aBlankWidthString = aIter.toString(); |
464 | 0 | } |
465 | 0 | else |
466 | 0 | XMLOFF_WARN_UNKNOWN("xmloff", aIter); |
467 | 0 | } |
468 | 0 | } |
469 | | |
470 | | void SvXMLNumFmtEmbeddedTextContext::characters( const OUString& rChars ) |
471 | 0 | { |
472 | 0 | aContent.append( rChars ); |
473 | 0 | } |
474 | | |
475 | | void SvXMLNumFmtEmbeddedTextContext::endFastElement(sal_Int32 ) |
476 | 0 | { |
477 | 0 | rParent.AddEmbeddedElement( nTextPosition, aContent.makeStringAndClear(), aBlankWidthString ); |
478 | 0 | } |
479 | | |
480 | | static bool lcl_ValidChar( sal_Unicode cChar, const SvXMLNumFormatContext& rParent ) |
481 | 12.4k | { |
482 | 12.4k | SvXMLStylesTokens nFormatType = rParent.GetType(); |
483 | | |
484 | | // Treat space equal to non-breaking space separator. |
485 | 12.4k | const sal_Unicode cNBSP = 0x00A0; |
486 | 12.4k | sal_Unicode cTS; |
487 | 12.4k | if ( ( nFormatType == SvXMLStylesTokens::NUMBER_STYLE || |
488 | 8.83k | nFormatType == SvXMLStylesTokens::CURRENCY_STYLE || |
489 | 6.42k | nFormatType == SvXMLStylesTokens::PERCENTAGE_STYLE ) && |
490 | 6.03k | (cChar == (cTS = rParent.GetLocaleData().getNumThousandSep()[0]) || |
491 | 6.03k | (cChar == ' ' && cTS == cNBSP)) ) |
492 | 1 | { |
493 | | // #i22394# Extra occurrences of thousands separator must be quoted, so they |
494 | | // aren't mis-interpreted as display-factor. |
495 | | // This must be limited to the format types that can contain a number element, |
496 | | // because the same character can be a date separator that should not be quoted |
497 | | // in date formats. |
498 | | |
499 | 1 | return false; // force quotes |
500 | 1 | } |
501 | | |
502 | | // see ImpSvNumberformatScan::Next_Symbol |
503 | | |
504 | | // All format types except BOOLEAN may contain minus sign or delimiter. |
505 | 12.4k | if ( cChar == '-' ) |
506 | 4.25k | return nFormatType != SvXMLStylesTokens::BOOLEAN_STYLE; |
507 | | |
508 | 8.18k | if ( ( cChar == ' ' || |
509 | 4.74k | cChar == '/' || |
510 | 4.71k | cChar == '.' || |
511 | 4.23k | cChar == ',' || |
512 | 4.19k | cChar == ':' || |
513 | 875 | cChar == '\'' ) && |
514 | 7.30k | ( nFormatType == SvXMLStylesTokens::CURRENCY_STYLE || |
515 | 6.24k | nFormatType == SvXMLStylesTokens::DATE_STYLE || |
516 | 5.23k | nFormatType == SvXMLStylesTokens::TIME_STYLE ) ) // other formats do not require delimiter tdf#97837 |
517 | 5.81k | return true; |
518 | | |
519 | | // percent sign must be used without quotes for percentage styles only |
520 | 2.36k | if ( nFormatType == SvXMLStylesTokens::PERCENTAGE_STYLE && cChar == '%' ) |
521 | 18 | return true; |
522 | | |
523 | | // don't put quotes around single parentheses (often used for negative numbers) |
524 | 2.34k | if ( ( nFormatType == SvXMLStylesTokens::NUMBER_STYLE || |
525 | 583 | nFormatType == SvXMLStylesTokens::CURRENCY_STYLE || |
526 | 550 | nFormatType == SvXMLStylesTokens::PERCENTAGE_STYLE ) && |
527 | 1.79k | ( cChar == '(' || cChar == ')' ) ) |
528 | 814 | return true; |
529 | | |
530 | 1.53k | return false; |
531 | 2.34k | } |
532 | | |
533 | | static void lcl_EnquoteIfNecessary( OUStringBuffer& rContent, const SvXMLNumFormatContext& rParent, const bool bIsBlankWidthStringEmpty = true ) |
534 | 17.0k | { |
535 | 17.0k | bool bQuote = true; |
536 | 17.0k | sal_Int32 nLength = rContent.getLength(); |
537 | 17.0k | const SvXMLStylesTokens nFormatType = rParent.GetType(); |
538 | | |
539 | 17.0k | if (nFormatType != SvXMLStylesTokens::BOOLEAN_STYLE && bIsBlankWidthStringEmpty && |
540 | 17.0k | ((nLength == 1 && lcl_ValidChar( rContent[0], rParent)) || |
541 | 6.15k | (nLength == 2 && |
542 | 329 | ((rContent[0] == ' ' && rContent[1] == '-') || |
543 | 114 | (rContent[1] == ' ' && lcl_ValidChar( rContent[0], rParent)))))) |
544 | 11.1k | { |
545 | | // Don't quote single separator characters like space or percent, |
546 | | // or separator characters followed by space (used in date formats). |
547 | | // Or space followed by minus (used in currency formats) that would |
548 | | // lead to almost duplicated formats with built-in formats just with |
549 | | // the difference of quotes. |
550 | | // tdf#170670: except if it is inside a blank width string |
551 | 11.1k | bQuote = false; |
552 | 11.1k | } |
553 | 5.89k | else if ( nFormatType == SvXMLStylesTokens::PERCENTAGE_STYLE && nLength > 1 ) |
554 | 0 | { |
555 | | // the percent character in percentage styles must be left out of quoting |
556 | | // (one occurrence is enough even if there are several percent characters in the string) |
557 | |
|
558 | 0 | sal_Int32 nPos = rContent.indexOf( '%' ); |
559 | 0 | if ( nPos >= 0 ) |
560 | 0 | { |
561 | 0 | if ( nPos + 1 < nLength ) |
562 | 0 | { |
563 | 0 | if ( nPos + 2 == nLength && lcl_ValidChar( rContent[nPos + 1], rParent ) ) |
564 | 0 | { |
565 | | // single character that doesn't need quoting |
566 | 0 | } |
567 | 0 | else |
568 | 0 | { |
569 | | // quote text behind percent character |
570 | 0 | rContent.insert( nPos + 1, '"' ); |
571 | 0 | rContent.append( '"' ); |
572 | 0 | } |
573 | 0 | } |
574 | 0 | if ( nPos > 0 ) |
575 | 0 | { |
576 | 0 | if ( nPos == 1 && lcl_ValidChar( rContent[0], rParent ) ) |
577 | 0 | { |
578 | | // single character that doesn't need quoting |
579 | 0 | } |
580 | 0 | else |
581 | 0 | { |
582 | | // quote text before percent character |
583 | 0 | rContent.insert( nPos, '"' ); |
584 | 0 | rContent.insert( 0, '"' ); |
585 | 0 | } |
586 | 0 | } |
587 | 0 | bQuote = false; |
588 | 0 | } |
589 | | // else: normal quoting (below) |
590 | 0 | } |
591 | | |
592 | 17.0k | if ( !bQuote ) |
593 | 11.1k | return; |
594 | | |
595 | | // #i55469# quotes in the string itself have to be escaped |
596 | 5.89k | bool bEscape = ( rContent.indexOf( '"' ) >= 0 ); |
597 | 5.89k | if ( bEscape ) |
598 | 0 | { |
599 | | // A quote is turned into "\"" - a quote to end quoted text, an escaped quote, |
600 | | // and a quote to resume quoting. |
601 | 0 | OUString aInsert( u"\"\\\""_ustr ); |
602 | |
|
603 | 0 | sal_Int32 nPos = 0; |
604 | 0 | while ( nPos < rContent.getLength() ) |
605 | 0 | { |
606 | 0 | if ( rContent[nPos] == '"' ) |
607 | 0 | { |
608 | 0 | rContent.insert( nPos, aInsert ); |
609 | 0 | nPos += aInsert.getLength(); |
610 | 0 | } |
611 | 0 | ++nPos; |
612 | 0 | } |
613 | 0 | } |
614 | | |
615 | | // quote string literals |
616 | 5.89k | rContent.insert( 0, '"' ); |
617 | 5.89k | rContent.append( '"' ); |
618 | | |
619 | | // remove redundant double quotes at start or end |
620 | 5.89k | if ( !bEscape ) |
621 | 5.89k | return; |
622 | | |
623 | 0 | if ( rContent.getLength() > 2 && |
624 | 0 | rContent[0] == '"' && |
625 | 0 | rContent[1] == '"' ) |
626 | 0 | { |
627 | 0 | rContent.remove(0, 2); |
628 | 0 | } |
629 | |
|
630 | 0 | sal_Int32 nLen = rContent.getLength(); |
631 | 0 | if ( nLen > 2 && |
632 | 0 | rContent[nLen - 1] == '"' && |
633 | 0 | rContent[nLen - 2] == '"' ) |
634 | 0 | { |
635 | 0 | rContent.truncate(nLen - 2); |
636 | 0 | } |
637 | 0 | } |
638 | | |
639 | | |
640 | | // SvXMLNumFmtElementContext |
641 | | |
642 | | |
643 | | SvXMLNumFmtElementContext::SvXMLNumFmtElementContext( SvXMLImport& rImport, |
644 | | sal_Int32 /*nElement*/, |
645 | | SvXMLNumFormatContext& rParentContext, SvXMLStyleTokens nNewType, |
646 | | const uno::Reference<xml::sax::XFastAttributeList>& xAttrList ) : |
647 | 40.8k | SvXMLImportContext( rImport ), |
648 | 40.8k | rParent( rParentContext ), |
649 | 40.8k | nType( nNewType ), |
650 | 40.8k | nElementLang( LANGUAGE_SYSTEM ), |
651 | 40.8k | bLong( false ), |
652 | 40.8k | bTextual( false ) |
653 | 40.8k | { |
654 | 40.8k | LanguageTagODF aLanguageTagODF; |
655 | 40.8k | sal_Int32 nAttrVal; |
656 | 40.8k | bool bAttrBool(false); |
657 | 40.8k | bool bVarDecimals = false; |
658 | 40.8k | bool bIsMaxDenominator = false; |
659 | 40.8k | double fAttrDouble; |
660 | | |
661 | 40.8k | for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) ) |
662 | 36.1k | { |
663 | 36.1k | switch (aIter.getToken()) |
664 | 36.1k | { |
665 | 7.70k | case XML_ELEMENT(NUMBER, XML_DECIMAL_PLACES): |
666 | 7.70k | if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 )) |
667 | 7.65k | { |
668 | | // fdo#58539 & gnome#627420: limit number of digits during import |
669 | 7.65k | aNumInfo.nDecimals = std::min<sal_Int32>(nAttrVal, NF_MAX_FORMAT_SYMBOLS); |
670 | 7.65k | } |
671 | 7.70k | break; |
672 | 0 | case XML_ELEMENT(LO_EXT, XML_MIN_DECIMAL_PLACES): |
673 | 0 | case XML_ELEMENT(NUMBER, XML_MIN_DECIMAL_PLACES): |
674 | 0 | if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 )) |
675 | 0 | aNumInfo.nMinDecimalDigits = nAttrVal; |
676 | 0 | break; |
677 | 8.64k | case XML_ELEMENT(NUMBER, XML_MIN_INTEGER_DIGITS): |
678 | 8.64k | if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 )) |
679 | 8.63k | aNumInfo.nInteger = nAttrVal; |
680 | 8.64k | break; |
681 | 0 | case XML_ELEMENT(LO_EXT, XML_MAX_BLANK_INTEGER_DIGITS): |
682 | 0 | case XML_ELEMENT(NUMBER, XML_MAX_BLANK_INTEGER_DIGITS): |
683 | 0 | if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 )) |
684 | 0 | aNumInfo.nBlankInteger = nAttrVal; |
685 | 0 | break; |
686 | 6.76k | case XML_ELEMENT(NUMBER, XML_GROUPING): |
687 | 6.76k | if (::sax::Converter::convertBool( bAttrBool, aIter.toView() )) |
688 | 6.65k | aNumInfo.bGrouping = bAttrBool; |
689 | 6.76k | break; |
690 | 0 | case XML_ELEMENT(NUMBER, XML_DISPLAY_FACTOR): |
691 | 0 | if (::sax::Converter::convertDouble( fAttrDouble, aIter.toView() )) |
692 | 0 | aNumInfo.fDisplayFactor = fAttrDouble; |
693 | 0 | break; |
694 | 0 | case XML_ELEMENT(NUMBER, XML_DECIMAL_REPLACEMENT): |
695 | 0 | if ( aIter.toView() == " " ) |
696 | 0 | { |
697 | 0 | aNumInfo.bDecAlign = true; // space replacement for "?" |
698 | 0 | bVarDecimals = true; |
699 | 0 | } |
700 | 0 | else |
701 | 0 | if ( aIter.isEmpty() ) |
702 | 0 | bVarDecimals = true; // empty replacement string: variable decimals |
703 | 0 | else // all other strings |
704 | 0 | aNumInfo.bDecReplace = true; // decimal replacement with dashes |
705 | 0 | break; |
706 | 303 | case XML_ELEMENT(NUMBER, XML_MIN_EXPONENT_DIGITS): |
707 | 303 | if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 )) |
708 | 302 | aNumInfo.nExpDigits = std::min<sal_Int32>(nAttrVal, NF_MAX_FORMAT_SYMBOLS); |
709 | 303 | break; |
710 | 0 | case XML_ELEMENT(NUMBER, XML_BLANK_EXPONENT_DIGITS): |
711 | 0 | case XML_ELEMENT(LO_EXT, XML_BLANK_EXPONENT_DIGITS): |
712 | 0 | if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 )) |
713 | 0 | aNumInfo.nBlankExp = std::min<sal_Int32>(nAttrVal, NF_MAX_FORMAT_SYMBOLS); |
714 | 0 | break; |
715 | 0 | case XML_ELEMENT(NUMBER, XML_EXPONENT_INTERVAL): |
716 | 0 | case XML_ELEMENT(LO_EXT, XML_EXPONENT_INTERVAL): |
717 | 0 | if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 )) |
718 | 0 | aNumInfo.nExpInterval = nAttrVal; |
719 | 0 | break; |
720 | 0 | case XML_ELEMENT(NUMBER, XML_FORCED_EXPONENT_SIGN): |
721 | 0 | case XML_ELEMENT(LO_EXT, XML_FORCED_EXPONENT_SIGN): |
722 | 0 | if (::sax::Converter::convertBool( bAttrBool, aIter.toView() )) |
723 | 0 | aNumInfo.bExpSign = bAttrBool; |
724 | 0 | break; |
725 | 0 | case XML_ELEMENT(NUMBER, XML_EXPONENT_LOWERCASE): |
726 | 0 | case XML_ELEMENT(LO_EXT, XML_EXPONENT_LOWERCASE): |
727 | 0 | if (::sax::Converter::convertBool( bAttrBool, aIter.toView() )) |
728 | 0 | aNumInfo.bExponentLowercase = bAttrBool; |
729 | 0 | break; |
730 | 0 | case XML_ELEMENT(NUMBER, XML_MIN_NUMERATOR_DIGITS): |
731 | 0 | if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 )) |
732 | 0 | aNumInfo.nMinNumerDigits = nAttrVal; |
733 | 0 | break; |
734 | 0 | case XML_ELEMENT(NUMBER, XML_MIN_DENOMINATOR_DIGITS): |
735 | 0 | if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 )) |
736 | 0 | aNumInfo.nMinDenomDigits = nAttrVal; |
737 | 0 | break; |
738 | 0 | case XML_ELEMENT(LO_EXT, XML_MAX_NUMERATOR_DIGITS): |
739 | 0 | if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 1 )) // at least one '#' |
740 | 0 | aNumInfo.nMaxNumerDigits = nAttrVal; |
741 | 0 | break; |
742 | 0 | case XML_ELEMENT(NUMBER, XML_DENOMINATOR_VALUE): |
743 | 0 | if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 1 )) // 0 is not valid |
744 | 0 | { |
745 | 0 | aNumInfo.nFracDenominator = nAttrVal; |
746 | 0 | bIsMaxDenominator = false; |
747 | 0 | } |
748 | 0 | break; |
749 | 0 | case XML_ELEMENT(NUMBER, XML_MAX_DENOMINATOR_VALUE): // part of ODF 1.3 |
750 | 0 | case XML_ELEMENT(LO_EXT, XML_MAX_DENOMINATOR_VALUE): |
751 | 0 | if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 1 ) && aNumInfo.nFracDenominator <= 0) |
752 | 0 | { // if denominator value not yet defined |
753 | 0 | aNumInfo.nFracDenominator = nAttrVal; |
754 | 0 | bIsMaxDenominator = true; |
755 | 0 | } |
756 | 0 | break; |
757 | 0 | case XML_ELEMENT(LO_EXT, XML_ZEROS_NUMERATOR_DIGITS): |
758 | 0 | case XML_ELEMENT(NUMBER, XML_ZEROS_NUMERATOR_DIGITS): |
759 | 0 | if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 )) |
760 | 0 | aNumInfo.nZerosNumerDigits = nAttrVal; |
761 | 0 | break; |
762 | 0 | case XML_ELEMENT(NUMBER, XML_ZEROS_DENOMINATOR_DIGITS): |
763 | 0 | case XML_ELEMENT(LO_EXT, XML_ZEROS_DENOMINATOR_DIGITS): |
764 | 0 | if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 )) |
765 | 0 | aNumInfo.nZerosDenomDigits = nAttrVal; |
766 | 0 | break; |
767 | 0 | case XML_ELEMENT(NUMBER, XML_INTEGER_FRACTION_DELIMITER): |
768 | 0 | case XML_ELEMENT(LO_EXT, XML_INTEGER_FRACTION_DELIMITER): |
769 | 0 | aNumInfo.aIntegerFractionDelimiter = aIter.toString(); |
770 | 0 | break; |
771 | 0 | case XML_ELEMENT(NUMBER, XML_RFC_LANGUAGE_TAG): |
772 | 0 | aLanguageTagODF.maRfcLanguageTag = aIter.toString(); |
773 | 0 | break; |
774 | 2.81k | case XML_ELEMENT(NUMBER, XML_LANGUAGE): |
775 | 2.81k | aLanguageTagODF.maLanguage = aIter.toString(); |
776 | 2.81k | break; |
777 | 0 | case XML_ELEMENT(NUMBER, XML_SCRIPT): |
778 | 0 | aLanguageTagODF.maScript = aIter.toString(); |
779 | 0 | break; |
780 | 2.71k | case XML_ELEMENT(NUMBER, XML_COUNTRY): |
781 | 2.71k | aLanguageTagODF.maCountry = aIter.toString(); |
782 | 2.71k | break; |
783 | 6.62k | case XML_ELEMENT(NUMBER, XML_STYLE): |
784 | 6.62k | SvXMLUnitConverter::convertEnum( bLong, aIter.toView(), aStyleValueMap ); |
785 | 6.62k | break; |
786 | 516 | case XML_ELEMENT(NUMBER, XML_TEXTUAL): |
787 | 516 | if (::sax::Converter::convertBool( bAttrBool, aIter.toView() )) |
788 | 514 | bTextual = bAttrBool; |
789 | 516 | break; |
790 | 0 | case XML_ELEMENT(NUMBER, XML_CALENDAR): |
791 | 0 | sCalendar = aIter.toString(); |
792 | 0 | break; |
793 | 0 | case XML_ELEMENT(NUMBER, XML_BLANK_WIDTH_CHAR): |
794 | 0 | case XML_ELEMENT(LO_EXT, XML_BLANK_WIDTH_CHAR): |
795 | 0 | sBlankWidthString = aIter.toString(); |
796 | 0 | break; |
797 | 81 | default: |
798 | 81 | XMLOFF_WARN_UNKNOWN("xmloff", aIter); |
799 | 36.1k | } |
800 | 36.1k | } |
801 | 40.8k | if ( aNumInfo.nBlankInteger > aNumInfo.nInteger ) |
802 | 0 | aNumInfo.nInteger = aNumInfo.nBlankInteger; |
803 | 40.8k | if ( aNumInfo.nMinDecimalDigits == -1) |
804 | 40.8k | { |
805 | 40.8k | if ( bVarDecimals || aNumInfo.bDecReplace ) |
806 | 0 | aNumInfo.nMinDecimalDigits = 0; |
807 | 40.8k | else |
808 | 40.8k | aNumInfo.nMinDecimalDigits = aNumInfo.nDecimals; |
809 | 40.8k | } |
810 | 40.8k | if ( aNumInfo.nExpDigits > 0 && aNumInfo.nBlankExp >= aNumInfo.nExpDigits ) |
811 | 0 | aNumInfo.nBlankExp = aNumInfo.nExpDigits - 1; // at least one '0' in exponent |
812 | | |
813 | 40.8k | if ( aNumInfo.nZerosDenomDigits > 0 ) |
814 | 0 | { // nMin = count of '0' and '?' |
815 | 0 | if ( aNumInfo.nMinDenomDigits < aNumInfo.nZerosDenomDigits ) |
816 | 0 | aNumInfo.nMinDenomDigits = aNumInfo.nZerosDenomDigits; |
817 | 0 | } |
818 | 40.8k | else |
819 | 40.8k | aNumInfo.nZerosDenomDigits = 0; |
820 | 40.8k | if ( aNumInfo.nMinDenomDigits >= 0 ) |
821 | 0 | if ( aNumInfo.nMaxDenomDigits < aNumInfo.nMinDenomDigits ) |
822 | 0 | aNumInfo.nMaxDenomDigits = ( aNumInfo.nMinDenomDigits ? aNumInfo.nMinDenomDigits : 1 ); |
823 | 40.8k | if ( aNumInfo.nZerosNumerDigits > 0 ) |
824 | 0 | { |
825 | 0 | if ( aNumInfo.nMinNumerDigits < aNumInfo.nZerosNumerDigits ) |
826 | 0 | aNumInfo.nMinNumerDigits = aNumInfo.nZerosNumerDigits; |
827 | 0 | } |
828 | 40.8k | else |
829 | 40.8k | aNumInfo.nZerosNumerDigits = 0; |
830 | 40.8k | if ( aNumInfo.nMinNumerDigits >= 0 ) |
831 | 0 | if ( aNumInfo.nMaxNumerDigits < aNumInfo.nMinNumerDigits ) |
832 | 0 | aNumInfo.nMaxNumerDigits = ( aNumInfo.nMinNumerDigits ? aNumInfo.nMinNumerDigits : 1 ); |
833 | 40.8k | if ( bIsMaxDenominator && aNumInfo.nFracDenominator > 0 ) |
834 | 0 | { |
835 | 0 | aNumInfo.nMaxDenomDigits = floor( log10( aNumInfo.nFracDenominator ) ) + 1; |
836 | 0 | aNumInfo.nFracDenominator = -1; // Max denominator value only gives number of digits at denominator |
837 | 0 | } |
838 | 40.8k | if ( aNumInfo.nMaxDenomDigits > 0 ) |
839 | 0 | { |
840 | 0 | if ( aNumInfo.nMinDenomDigits < 0 ) |
841 | 0 | aNumInfo.nMinDenomDigits = 0; |
842 | 0 | else if ( aNumInfo.nMinDenomDigits > aNumInfo.nMaxDenomDigits ) |
843 | 0 | aNumInfo.nMinDenomDigits = aNumInfo.nMaxDenomDigits; |
844 | 0 | } |
845 | | |
846 | 40.8k | if ( !aLanguageTagODF.isEmpty() ) |
847 | 2.93k | { |
848 | 2.93k | nElementLang = aLanguageTagODF.getLanguageTag().getLanguageType( false); |
849 | 2.93k | if ( nElementLang == LANGUAGE_DONTKNOW ) |
850 | 72 | nElementLang = LANGUAGE_SYSTEM; //! error handling for unknown locales? |
851 | 2.93k | } |
852 | | |
853 | 40.8k | if ( aNumInfo.aIntegerFractionDelimiter.isEmpty() ) |
854 | 40.8k | aNumInfo.aIntegerFractionDelimiter = " "; |
855 | 40.8k | } |
856 | | |
857 | | css::uno::Reference< css::xml::sax::XFastContextHandler > SvXMLNumFmtElementContext::createFastChildContext( |
858 | | sal_Int32 nElement, |
859 | | const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) |
860 | 141 | { |
861 | | // only number:number and number:scientific-number supports number:embedded-text child element |
862 | | |
863 | 141 | if ( ( nType == SvXMLStyleTokens::Number || nType == SvXMLStyleTokens::ScientificNumber ) && |
864 | 103 | nElement == XML_ELEMENT(NUMBER, XML_EMBEDDED_TEXT) ) |
865 | 0 | { |
866 | 0 | return new SvXMLNumFmtEmbeddedTextContext( GetImport(), nElement, *this, xAttrList ); |
867 | 0 | } |
868 | 141 | else |
869 | 141 | XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); |
870 | 141 | return nullptr; |
871 | 141 | } |
872 | | |
873 | | void SvXMLNumFmtElementContext::characters( const OUString& rChars ) |
874 | 20.1k | { |
875 | 20.1k | aContent.append( rChars ); |
876 | 20.1k | } |
877 | | |
878 | | namespace { |
879 | | void lcl_InsertBlankWidthChars( std::u16string_view rBlankWidthString, OUStringBuffer& rContent ) |
880 | 0 | { |
881 | 0 | sal_Int32 nShiftPosition = 1; // rContent starts with a quote |
882 | 0 | const size_t nLenBlank = rBlankWidthString.size(); |
883 | 0 | for ( size_t i = 0 ; i < nLenBlank ; i++ ) |
884 | 0 | { |
885 | 0 | sal_Unicode nChar = rBlankWidthString[ i ]; |
886 | 0 | OUString aBlanks; |
887 | 0 | SvNumberformat::InsertBlanks( aBlanks, 0, nChar ); |
888 | 0 | sal_Int32 nPositionContent = 0; |
889 | 0 | if ( ++i < nLenBlank ) |
890 | 0 | { |
891 | 0 | sal_Int32 nNext = rBlankWidthString.find( '_', i ); |
892 | 0 | if ( static_cast<sal_Int32>( i ) < nNext ) |
893 | 0 | { |
894 | 0 | nPositionContent = o3tl::toInt32( rBlankWidthString.substr( i, nNext - i ) ); |
895 | 0 | i = nNext; |
896 | 0 | } |
897 | 0 | else |
898 | 0 | nPositionContent = o3tl::toInt32( rBlankWidthString.substr( i ) ); |
899 | 0 | } |
900 | 0 | nPositionContent += nShiftPosition; |
901 | 0 | if ( nPositionContent >= 0 ) |
902 | 0 | { |
903 | 0 | rContent.remove( nPositionContent, aBlanks.getLength() ); |
904 | 0 | if ( nPositionContent >= 1 && rContent[ nPositionContent-1 ] == '\"' ) |
905 | 0 | { |
906 | 0 | nPositionContent--; |
907 | 0 | rContent.insert( nPositionContent, nChar ); |
908 | 0 | rContent.insert( nPositionContent, '_' ); |
909 | 0 | } |
910 | 0 | else |
911 | 0 | { |
912 | 0 | rContent.insert( nPositionContent, '\"' ); |
913 | 0 | rContent.insert( nPositionContent, nChar ); |
914 | 0 | rContent.insert( nPositionContent, "\"_" ); |
915 | 0 | nShiftPosition += 2; |
916 | 0 | } |
917 | | // rContent length was modified: remove blanks, add "_x" |
918 | 0 | nShiftPosition += 2 - aBlanks.getLength(); |
919 | 0 | } |
920 | 0 | } |
921 | | // remove empty string at the end of rContent |
922 | 0 | if ( std::u16string_view( rContent ).substr( rContent.getLength() - 2 ) == u"\"\"" ) |
923 | 0 | { |
924 | 0 | sal_Int32 nLen = rContent.getLength(); |
925 | 0 | if ( nLen >= 3 && rContent[ nLen-3 ] != '\\' ) |
926 | 0 | rContent.truncate( nLen - 2 ); |
927 | 0 | } |
928 | 0 | } |
929 | | } |
930 | | |
931 | | void SvXMLNumFmtElementContext::AddEmbeddedElement( sal_Int32 nFormatPos, std::u16string_view rContentEmbedded, std::u16string_view rBlankWidthString ) |
932 | 0 | { |
933 | 0 | if ( rContentEmbedded.empty() ) |
934 | 0 | return; |
935 | 0 | OUStringBuffer aContentEmbedded( rContentEmbedded ); |
936 | | // #107805# always quote embedded strings - even space would otherwise |
937 | | // be recognized as thousands separator in French. |
938 | 0 | aContentEmbedded.insert( 0, '"' ); |
939 | 0 | aContentEmbedded.append( '"' ); |
940 | 0 | if ( !rBlankWidthString.empty() ) |
941 | 0 | lcl_InsertBlankWidthChars( rBlankWidthString, aContentEmbedded ); |
942 | |
|
943 | 0 | auto iterPair = aNumInfo.m_EmbeddedElements.emplace( nFormatPos, aContentEmbedded.toString() ); |
944 | 0 | if (!iterPair.second) |
945 | 0 | { |
946 | | // there's already an element at this position - append text to existing element |
947 | 0 | if ( iterPair.first->second.endsWith( "\"" ) && aContentEmbedded[ 0 ] == '"' ) |
948 | 0 | { // remove double quote |
949 | 0 | iterPair.first->second = OUString::Concat( iterPair.first->second.subView( 0, iterPair.first->second.getLength() - 1 ) ) |
950 | 0 | + aContentEmbedded.subView( 1, aContentEmbedded.getLength() - 1 ); |
951 | 0 | } |
952 | 0 | else |
953 | 0 | iterPair.first->second += aContentEmbedded; |
954 | 0 | } |
955 | 0 | } |
956 | | |
957 | | void SvXMLNumFmtElementContext::endFastElement(sal_Int32 ) |
958 | 40.8k | { |
959 | 40.8k | bool bEffLong = bLong; |
960 | 40.8k | switch (nType) |
961 | 40.8k | { |
962 | 17.0k | case SvXMLStyleTokens::Text: |
963 | 17.0k | if ( rParent.HasLongDoW() && |
964 | 13 | std::u16string_view(aContent) == rParent.GetLocaleData().getLongDateDayOfWeekSep() ) |
965 | 13 | { |
966 | | // skip separator constant after long day of week |
967 | | // (NF_KEY_NNNN contains the separator) |
968 | | |
969 | 13 | if ( rParent.ReplaceNfKeyword( NF_KEY_NNN, NF_KEY_NNNN ) ) |
970 | 13 | { |
971 | 13 | aContent.truncate(); |
972 | 13 | } |
973 | | |
974 | 13 | rParent.SetHasLongDoW( false ); // only once |
975 | 13 | } |
976 | 17.0k | if ( !aContent.isEmpty() ) |
977 | 17.0k | { |
978 | 17.0k | lcl_EnquoteIfNecessary( aContent, rParent, sBlankWidthString.isEmpty() ); |
979 | 17.0k | if ( !sBlankWidthString.isEmpty() ) |
980 | 0 | { |
981 | 0 | lcl_InsertBlankWidthChars( sBlankWidthString, aContent ); |
982 | 0 | sBlankWidthString = ""; |
983 | 0 | } |
984 | 17.0k | rParent.AddToCode( aContent ); |
985 | 17.0k | aContent.setLength(0); |
986 | 17.0k | } |
987 | 13 | else |
988 | 13 | { |
989 | | // Quoted empty text may be significant to separate. |
990 | 13 | aContent.append("\"\""); |
991 | 13 | rParent.AddToCode( aContent ); |
992 | 13 | aContent.setLength(0); |
993 | 13 | rParent.SetHasTrailingEmptyText(true); // *after* AddToCode() |
994 | 13 | } |
995 | 17.0k | break; |
996 | | |
997 | 9.16k | case SvXMLStyleTokens::Number: |
998 | 9.16k | rParent.AddNumber( aNumInfo ); |
999 | 9.16k | break; |
1000 | | |
1001 | 2.99k | case SvXMLStyleTokens::CurrencySymbol: |
1002 | 2.99k | rParent.AddCurrency( aContent.makeStringAndClear(), nElementLang ); |
1003 | 2.99k | break; |
1004 | | |
1005 | 1.31k | case SvXMLStyleTokens::TextContent: |
1006 | 1.31k | rParent.AddToCode( '@'); |
1007 | 1.31k | break; |
1008 | 0 | case SvXMLStyleTokens::FillCharacter: |
1009 | 0 | if ( !aContent.isEmpty() ) |
1010 | 0 | { |
1011 | 0 | rParent.AddToCode( '*' ); |
1012 | 0 | rParent.AddToCode( aContent[0] ); |
1013 | 0 | } |
1014 | 0 | break; |
1015 | 67 | case SvXMLStyleTokens::Boolean: |
1016 | 67 | rParent.AddNfKeyword( NF_KEY_BOOLEAN ); |
1017 | 67 | break; |
1018 | | |
1019 | 1.17k | case SvXMLStyleTokens::Day: |
1020 | 1.17k | rParent.UpdateCalendar( sCalendar ); |
1021 | | //! I18N doesn't provide SYSTEM or extended date information yet |
1022 | | |
1023 | 1.17k | rParent.AddNfKeyword( |
1024 | 1.17k | sal::static_int_cast< sal_uInt16 >( |
1025 | 1.17k | bEffLong ? NF_KEY_DD : NF_KEY_D ) ); |
1026 | 1.17k | break; |
1027 | 1.33k | case SvXMLStyleTokens::Month: |
1028 | 1.33k | rParent.UpdateCalendar( sCalendar ); |
1029 | | //! I18N doesn't provide SYSTEM or extended date information yet |
1030 | | |
1031 | 1.33k | rParent.AddNfKeyword( |
1032 | 1.33k | sal::static_int_cast< sal_uInt16 >( |
1033 | 1.33k | bTextual |
1034 | 1.33k | ? ( bEffLong ? NF_KEY_MMMM : NF_KEY_MMM ) |
1035 | 1.33k | : ( bEffLong ? NF_KEY_MM : NF_KEY_M ) ) ); |
1036 | 1.33k | break; |
1037 | 1.11k | case SvXMLStyleTokens::Year: |
1038 | | //! I18N doesn't provide SYSTEM or extended date information yet |
1039 | 1.11k | { |
1040 | | // Y after G (era) is replaced by E for a secondary calendar. |
1041 | | // Do not replace for default calendar. |
1042 | | // Also replace Y by E if we're switching to the secondary |
1043 | | // calendar of a locale if it is known to implicitly use E. |
1044 | 1.11k | rParent.UpdateCalendar( sCalendar); |
1045 | 1.11k | const SvXMLNumFormatContext::ImplicitCalendar eCal = rParent.GetImplicitCalendarState(); |
1046 | 1.11k | if (eCal == SvXMLNumFormatContext::ImplicitCalendar::SECONDARY |
1047 | 1.11k | || eCal == SvXMLNumFormatContext::ImplicitCalendar::SECONDARY_FROM_OTHER) |
1048 | 0 | { |
1049 | 0 | rParent.AddNfKeyword( |
1050 | 0 | sal::static_int_cast< sal_uInt16 >( |
1051 | 0 | bEffLong ? NF_KEY_EEC : NF_KEY_EC ) ); |
1052 | 0 | } |
1053 | 1.11k | else |
1054 | 1.11k | { |
1055 | 1.11k | rParent.AddNfKeyword( |
1056 | 1.11k | sal::static_int_cast< sal_uInt16 >( |
1057 | 1.11k | bEffLong ? NF_KEY_YYYY : NF_KEY_YY ) ); |
1058 | 1.11k | } |
1059 | 1.11k | } |
1060 | 1.11k | break; |
1061 | 0 | case SvXMLStyleTokens::Era: |
1062 | 0 | rParent.UpdateCalendar( sCalendar ); |
1063 | | //! I18N doesn't provide SYSTEM or extended date information yet |
1064 | 0 | rParent.AddNfKeyword( |
1065 | 0 | sal::static_int_cast< sal_uInt16 >( |
1066 | 0 | bEffLong ? NF_KEY_GGG : NF_KEY_G ) ); |
1067 | | // HasEra flag is set |
1068 | 0 | break; |
1069 | 98 | case SvXMLStyleTokens::DayOfWeek: |
1070 | | //! I18N doesn't provide SYSTEM or extended date information yet |
1071 | 98 | { |
1072 | | // Implicit secondary calendar uses A keyword, default and |
1073 | | // explicit calendar N keyword. |
1074 | 98 | rParent.UpdateCalendar( sCalendar); |
1075 | 98 | const SvXMLNumFormatContext::ImplicitCalendar eCal = rParent.GetImplicitCalendarState(); |
1076 | 98 | if (eCal == SvXMLNumFormatContext::ImplicitCalendar::SECONDARY |
1077 | 98 | || eCal == SvXMLNumFormatContext::ImplicitCalendar::SECONDARY_FROM_OTHER) |
1078 | 0 | { |
1079 | 0 | rParent.AddNfKeyword( |
1080 | 0 | sal::static_int_cast< sal_uInt16 >( |
1081 | 0 | bEffLong ? NF_KEY_AAAA : NF_KEY_AAA ) ); |
1082 | 0 | } |
1083 | 98 | else |
1084 | 98 | { |
1085 | 98 | rParent.AddNfKeyword( |
1086 | 98 | sal::static_int_cast< sal_uInt16 >( |
1087 | 98 | bEffLong ? NF_KEY_NNNN : NF_KEY_NN ) ); |
1088 | 98 | } |
1089 | 98 | } |
1090 | 98 | break; |
1091 | 0 | case SvXMLStyleTokens::WeekOfYear: |
1092 | 0 | rParent.UpdateCalendar( sCalendar ); |
1093 | 0 | rParent.AddNfKeyword( NF_KEY_WW ); |
1094 | 0 | break; |
1095 | 0 | case SvXMLStyleTokens::Quarter: |
1096 | 0 | rParent.UpdateCalendar( sCalendar ); |
1097 | 0 | rParent.AddNfKeyword( |
1098 | 0 | sal::static_int_cast< sal_uInt16 >( |
1099 | 0 | bEffLong ? NF_KEY_QQ : NF_KEY_Q ) ); |
1100 | 0 | break; |
1101 | 1.80k | case SvXMLStyleTokens::Hours: |
1102 | 1.80k | rParent.AddNfKeyword( |
1103 | 1.80k | sal::static_int_cast< sal_uInt16 >( |
1104 | 1.80k | bEffLong ? NF_KEY_HH : NF_KEY_H ) ); |
1105 | 1.80k | break; |
1106 | 560 | case SvXMLStyleTokens::AmPm: |
1107 | | //! short/long? |
1108 | 560 | rParent.AddNfKeyword( NF_KEY_AMPM ); |
1109 | 560 | break; |
1110 | 2.52k | case SvXMLStyleTokens::Minutes: |
1111 | 2.52k | rParent.AddNfKeyword( |
1112 | 2.52k | sal::static_int_cast< sal_uInt16 >( |
1113 | 2.52k | bEffLong ? NF_KEY_MMI : NF_KEY_MI ) ); |
1114 | 2.52k | break; |
1115 | 1.33k | case SvXMLStyleTokens::Seconds: |
1116 | 1.33k | rParent.AddNfKeyword( |
1117 | 1.33k | sal::static_int_cast< sal_uInt16 >( |
1118 | 1.33k | bEffLong ? NF_KEY_SS : NF_KEY_S ) ); |
1119 | 1.33k | if ( aNumInfo.nDecimals > 0 ) |
1120 | 168 | { |
1121 | | // manually add the decimal places |
1122 | 168 | rParent.AddToCode(rParent.GetLocaleData().getNumDecimalSep()); |
1123 | 390 | for (sal_Int32 i=0; i<aNumInfo.nDecimals; i++) |
1124 | 222 | { |
1125 | 222 | rParent.AddToCode( '0'); |
1126 | 222 | } |
1127 | 168 | } |
1128 | 1.33k | break; |
1129 | | |
1130 | 0 | case SvXMLStyleTokens::Fraction: |
1131 | 0 | { |
1132 | 0 | if ( aNumInfo.nInteger >= 0 ) |
1133 | 0 | { |
1134 | | // add integer part only if min-integer-digits attribute is there |
1135 | 0 | aNumInfo.nDecimals = 0; |
1136 | 0 | rParent.AddNumber( aNumInfo ); // number without decimals |
1137 | 0 | OUStringBuffer sIntegerFractionDelimiter(aNumInfo.aIntegerFractionDelimiter); |
1138 | 0 | lcl_EnquoteIfNecessary( sIntegerFractionDelimiter, rParent ); |
1139 | 0 | rParent.AddToCode( sIntegerFractionDelimiter ); // default is ' ' |
1140 | 0 | } |
1141 | | |
1142 | | //! build string and add at once |
1143 | |
|
1144 | 0 | sal_Int32 i; |
1145 | 0 | for (i=aNumInfo.nMaxNumerDigits; i > 0; i--) |
1146 | 0 | { |
1147 | 0 | if ( i > aNumInfo.nMinNumerDigits ) |
1148 | 0 | rParent.AddToCode( '#' ); |
1149 | 0 | else if ( i > aNumInfo.nZerosNumerDigits ) |
1150 | 0 | rParent.AddToCode( '?' ); |
1151 | 0 | else |
1152 | 0 | rParent.AddToCode( '0' ); |
1153 | 0 | } |
1154 | 0 | rParent.AddToCode( '/' ); |
1155 | 0 | if ( aNumInfo.nFracDenominator > 0 ) |
1156 | 0 | { |
1157 | 0 | rParent.AddToCode( OUString::number( aNumInfo.nFracDenominator ) ); |
1158 | 0 | } |
1159 | 0 | else |
1160 | 0 | { |
1161 | 0 | for (i=aNumInfo.nMaxDenomDigits; i > 0 ; i--) |
1162 | 0 | { |
1163 | 0 | if ( i > aNumInfo.nMinDenomDigits ) |
1164 | 0 | rParent.AddToCode( '#' ); |
1165 | 0 | else if ( i > aNumInfo.nZerosDenomDigits ) |
1166 | 0 | rParent.AddToCode( '?' ); |
1167 | 0 | else |
1168 | 0 | rParent.AddToCode( '0' ); |
1169 | 0 | } |
1170 | 0 | } |
1171 | 0 | } |
1172 | 0 | break; |
1173 | | |
1174 | 310 | case SvXMLStyleTokens::ScientificNumber: |
1175 | 310 | { |
1176 | | // exponential interval for engineering notation |
1177 | 310 | if( !aNumInfo.bGrouping && aNumInfo.nExpInterval > aNumInfo.nInteger ) |
1178 | 0 | { |
1179 | 0 | for (sal_Int32 i=aNumInfo.nInteger; i<aNumInfo.nExpInterval; i++) |
1180 | 0 | { |
1181 | 0 | rParent.AddToCode( '#' ); |
1182 | 0 | } |
1183 | 0 | } |
1184 | 310 | rParent.AddNumber( aNumInfo ); // number and exponent |
1185 | 310 | } |
1186 | 310 | break; |
1187 | | |
1188 | 0 | default: |
1189 | 0 | assert(false && "invalid element ID"); |
1190 | 40.8k | } |
1191 | 40.8k | } |
1192 | | |
1193 | | sal_uInt16 SvXMLNumFmtDefaults::GetDefaultDateFormat( SvXMLDateElementAttributes eDOW, |
1194 | | SvXMLDateElementAttributes eDay, SvXMLDateElementAttributes eMonth, |
1195 | | SvXMLDateElementAttributes eYear, SvXMLDateElementAttributes eHours, |
1196 | | SvXMLDateElementAttributes eMins, SvXMLDateElementAttributes eSecs, |
1197 | | bool bSystem ) |
1198 | 151 | { |
1199 | 151 | for (const auto & rEntry : aDefaultDateFormats) |
1200 | 960 | { |
1201 | 960 | if ( bSystem == rEntry.bSystem && |
1202 | 651 | ( eDOW == rEntry.eDOW || ( rEntry.eDOW == XML_DEA_ANY && eDOW != XML_DEA_NONE ) ) && |
1203 | 630 | ( eDay == rEntry.eDay || ( rEntry.eDay == XML_DEA_ANY && eDay != XML_DEA_NONE ) ) && |
1204 | 461 | ( eMonth == rEntry.eMonth || ( rEntry.eMonth == XML_DEA_ANY && eMonth != XML_DEA_NONE ) ) && |
1205 | 303 | ( eYear == rEntry.eYear || ( rEntry.eYear == XML_DEA_ANY && eYear != XML_DEA_NONE ) ) && |
1206 | 147 | ( eHours == rEntry.eHours || ( rEntry.eHours == XML_DEA_ANY && eHours != XML_DEA_NONE ) ) && |
1207 | 144 | ( eMins == rEntry.eMins || ( rEntry.eMins == XML_DEA_ANY && eMins != XML_DEA_NONE ) ) && |
1208 | 144 | ( eSecs == rEntry.eSecs || ( rEntry.eSecs == XML_DEA_ANY && eSecs != XML_DEA_NONE ) ) ) |
1209 | 144 | { |
1210 | 144 | return sal::static_int_cast< sal_uInt16 >(rEntry.eFormat); |
1211 | 144 | } |
1212 | 960 | } |
1213 | | |
1214 | 7 | return NF_INDEX_TABLE_ENTRIES; // invalid |
1215 | 151 | } |
1216 | | |
1217 | | |
1218 | | // SvXMLNumFormatContext |
1219 | | |
1220 | | SvXMLNumFormatContext::SvXMLNumFormatContext( SvXMLImport& rImport, |
1221 | | sal_Int32 /*nElement*/, |
1222 | | SvXMLNumImpData* pNewData, SvXMLStylesTokens nNewType, |
1223 | | const uno::Reference<xml::sax::XFastAttributeList>& xAttrList, |
1224 | | SvXMLStylesContext& rStyles ) : |
1225 | 15.5k | SvXMLStyleContext( rImport ), |
1226 | 15.5k | m_pData( pNewData ), |
1227 | 15.5k | m_pStyles( &rStyles ), |
1228 | 15.5k | m_nType( nNewType ), |
1229 | 15.5k | m_nKey(-1), |
1230 | 15.5k | m_eImplicitCalendar(ImplicitCalendar::DEFAULT), |
1231 | 15.5k | m_nFormatLang( LANGUAGE_SYSTEM ), |
1232 | 15.5k | m_bAutoOrder( false ), |
1233 | 15.5k | m_bFromSystem( false ), |
1234 | 15.5k | m_bTruncate( true ), |
1235 | 15.5k | m_bAutoDec( false ), |
1236 | 15.5k | m_bAutoInt( false ), |
1237 | 15.5k | m_bHasExtraText( false ), |
1238 | 15.5k | m_bHasTrailingEmptyText( false ), |
1239 | 15.5k | m_bHasLongDoW( false ), |
1240 | 15.5k | m_bHasDateTime( false ), |
1241 | 15.5k | m_bRemoveAfterUse( false ), |
1242 | 15.5k | m_eDateDOW( XML_DEA_NONE ), |
1243 | 15.5k | m_eDateDay( XML_DEA_NONE ), |
1244 | 15.5k | m_eDateMonth( XML_DEA_NONE ), |
1245 | 15.5k | m_eDateYear( XML_DEA_NONE ), |
1246 | 15.5k | m_eDateHours( XML_DEA_NONE ), |
1247 | 15.5k | m_eDateMins( XML_DEA_NONE ), |
1248 | 15.5k | m_eDateSecs( XML_DEA_NONE ), |
1249 | 15.5k | m_bDateNoDefault( false ) |
1250 | 15.5k | { |
1251 | 15.5k | LanguageTagODF aLanguageTagODF; |
1252 | 15.5k | css::i18n::NativeNumberXmlAttributes aNatNumAttr; |
1253 | 15.5k | OUString aSpellout; |
1254 | 15.5k | bool bAttrBool(false); |
1255 | | |
1256 | 15.5k | for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) ) |
1257 | 31.2k | { |
1258 | 31.2k | switch (aIter.getToken()) |
1259 | 31.2k | { |
1260 | | // attributes for a style |
1261 | 13.9k | case XML_ELEMENT(STYLE, XML_NAME): |
1262 | 13.9k | break; |
1263 | 0 | case XML_ELEMENT(NUMBER, XML_RFC_LANGUAGE_TAG): |
1264 | 0 | aLanguageTagODF.maRfcLanguageTag = aIter.toString(); |
1265 | 0 | break; |
1266 | 5.42k | case XML_ELEMENT(NUMBER, XML_LANGUAGE): |
1267 | 5.42k | aLanguageTagODF.maLanguage = aIter.toString(); |
1268 | 5.42k | break; |
1269 | 0 | case XML_ELEMENT(NUMBER, XML_SCRIPT): |
1270 | 0 | aLanguageTagODF.maScript = aIter.toString(); |
1271 | 0 | break; |
1272 | 5.43k | case XML_ELEMENT(NUMBER, XML_COUNTRY): |
1273 | 5.43k | aLanguageTagODF.maCountry = aIter.toString(); |
1274 | 5.43k | break; |
1275 | 14 | case XML_ELEMENT(NUMBER, XML_TITLE): |
1276 | 14 | m_sFormatTitle = aIter.toString(); |
1277 | 14 | break; |
1278 | 250 | case XML_ELEMENT(NUMBER, XML_AUTOMATIC_ORDER): |
1279 | 250 | if (::sax::Converter::convertBool( bAttrBool, aIter.toView() )) |
1280 | 247 | m_bAutoOrder = bAttrBool; |
1281 | 250 | break; |
1282 | 0 | case XML_ELEMENT(NUMBER, XML_FORMAT_SOURCE): |
1283 | 0 | SvXMLUnitConverter::convertEnum( m_bFromSystem, aIter.toView(), aFormatSourceMap ); |
1284 | 0 | break; |
1285 | 861 | case XML_ELEMENT(NUMBER, XML_TRUNCATE_ON_OVERFLOW): |
1286 | 861 | if (::sax::Converter::convertBool( bAttrBool, aIter.toView() )) |
1287 | 848 | m_bTruncate = bAttrBool; |
1288 | 861 | break; |
1289 | 5.08k | case XML_ELEMENT(STYLE, XML_VOLATILE): |
1290 | | // volatile formats can be removed after importing |
1291 | | // if not used in other styles |
1292 | 5.08k | if (::sax::Converter::convertBool( bAttrBool, aIter.toView() )) |
1293 | 5.03k | m_bRemoveAfterUse = bAttrBool; |
1294 | 5.08k | break; |
1295 | 0 | case XML_ELEMENT(NUMBER, XML_TRANSLITERATION_FORMAT): |
1296 | 0 | aNatNumAttr.Format = aIter.toString(); |
1297 | 0 | break; |
1298 | 0 | case XML_ELEMENT(LO_EXT, XML_TRANSLITERATION_SPELLOUT): |
1299 | 0 | case XML_ELEMENT(NUMBER, XML_TRANSLITERATION_SPELLOUT): |
1300 | 0 | aSpellout = aIter.toString(); |
1301 | 0 | break; |
1302 | 0 | case XML_ELEMENT(NUMBER, XML_TRANSLITERATION_LANGUAGE): |
1303 | 0 | aNatNumAttr.Locale.Language = aIter.toString(); |
1304 | 0 | break; |
1305 | 0 | case XML_ELEMENT(NUMBER, XML_TRANSLITERATION_COUNTRY): |
1306 | 0 | aNatNumAttr.Locale.Country = aIter.toString(); |
1307 | 0 | break; |
1308 | 0 | case XML_ELEMENT(NUMBER, XML_TRANSLITERATION_STYLE): |
1309 | 0 | aNatNumAttr.Style = aIter.toString(); |
1310 | 0 | break; |
1311 | 235 | default: |
1312 | 235 | XMLOFF_WARN_UNKNOWN("xmloff", aIter); |
1313 | 31.2k | } |
1314 | 31.2k | } |
1315 | | |
1316 | 15.5k | if (!aLanguageTagODF.isEmpty()) |
1317 | 5.61k | { |
1318 | 5.61k | m_nFormatLang = aLanguageTagODF.getLanguageTag().getLanguageType( false); |
1319 | 5.61k | if ( m_nFormatLang == LANGUAGE_DONTKNOW ) |
1320 | 202 | m_nFormatLang = LANGUAGE_SYSTEM; //! error handling for unknown locales? |
1321 | 5.61k | } |
1322 | | |
1323 | 15.5k | if (aNatNumAttr.Format.isEmpty() && aSpellout.isEmpty()) |
1324 | 15.5k | return; |
1325 | | |
1326 | 0 | LanguageTag aLanguageTag( OUString(), aNatNumAttr.Locale.Language, |
1327 | 0 | std::u16string_view(), aNatNumAttr.Locale.Country); |
1328 | 0 | aNatNumAttr.Locale = aLanguageTag.getLocale( false); |
1329 | | |
1330 | | // NatNum12 spell out formula (cardinal, ordinal, ordinal-feminine etc.) |
1331 | 0 | if ( !aSpellout.isEmpty() ) |
1332 | 0 | { |
1333 | 0 | m_aFormatCode.append( "[NatNum12 " ); |
1334 | 0 | m_aFormatCode.append( aSpellout ); |
1335 | 0 | } else { |
1336 | 0 | SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter(); |
1337 | 0 | if ( !pFormatter ) return; |
1338 | | |
1339 | 0 | sal_Int32 nNatNum = pFormatter->GetNatNum().convertFromXmlAttributes( aNatNumAttr ); |
1340 | 0 | m_aFormatCode.append( "[NatNum" ); |
1341 | 0 | m_aFormatCode.append( nNatNum ); |
1342 | 0 | } |
1343 | | |
1344 | 0 | LanguageType eLang = aLanguageTag.getLanguageType( false ); |
1345 | 0 | if ( eLang == LANGUAGE_DONTKNOW ) |
1346 | 0 | eLang = LANGUAGE_SYSTEM; //! error handling for unknown locales? |
1347 | 0 | if ( eLang != m_nFormatLang && eLang != LANGUAGE_SYSTEM ) |
1348 | 0 | { |
1349 | 0 | m_aFormatCode.append("][$-" + |
1350 | | // language code in upper hex: |
1351 | 0 | OUString::number(eLang.get(), 16).toAsciiUpperCase()); |
1352 | 0 | } |
1353 | 0 | m_aFormatCode.append( ']' ); |
1354 | 0 | } |
1355 | | |
1356 | | SvXMLNumFormatContext::SvXMLNumFormatContext( SvXMLImport& rImport, |
1357 | | const OUString& rName, |
1358 | | const uno::Reference<xml::sax::XFastAttributeList>& /*xAttrList*/, |
1359 | | const sal_Int32 nTempKey, LanguageType nLang, |
1360 | | SvXMLStylesContext& rStyles ) : |
1361 | 5.93k | SvXMLStyleContext( rImport, XmlStyleFamily::DATA_STYLE ), |
1362 | 5.93k | m_pData( nullptr ), |
1363 | 5.93k | m_pStyles( &rStyles ), |
1364 | 5.93k | m_nType( SvXMLStylesTokens::NUMBER_STYLE ), |
1365 | 5.93k | m_nKey(nTempKey), |
1366 | 5.93k | m_eImplicitCalendar(ImplicitCalendar::DEFAULT), |
1367 | 5.93k | m_nFormatLang( nLang ), |
1368 | 5.93k | m_bAutoOrder( false ), |
1369 | 5.93k | m_bFromSystem( false ), |
1370 | 5.93k | m_bTruncate( true ), |
1371 | 5.93k | m_bAutoDec( false ), |
1372 | 5.93k | m_bAutoInt( false ), |
1373 | 5.93k | m_bHasExtraText( false ), |
1374 | 5.93k | m_bHasTrailingEmptyText( false ), |
1375 | 5.93k | m_bHasLongDoW( false ), |
1376 | 5.93k | m_bHasDateTime( false ), |
1377 | 5.93k | m_bRemoveAfterUse( false ), |
1378 | 5.93k | m_eDateDOW( XML_DEA_NONE ), |
1379 | 5.93k | m_eDateDay( XML_DEA_NONE ), |
1380 | 5.93k | m_eDateMonth( XML_DEA_NONE ), |
1381 | 5.93k | m_eDateYear( XML_DEA_NONE ), |
1382 | 5.93k | m_eDateHours( XML_DEA_NONE ), |
1383 | 5.93k | m_eDateMins( XML_DEA_NONE ), |
1384 | 5.93k | m_eDateSecs( XML_DEA_NONE ), |
1385 | 5.93k | m_bDateNoDefault( false ) |
1386 | 5.93k | { |
1387 | 5.93k | SetAttribute(XML_ELEMENT(STYLE, XML_NAME), rName); |
1388 | 5.93k | } |
1389 | | |
1390 | | SvXMLNumFormatContext::~SvXMLNumFormatContext() |
1391 | 21.4k | { |
1392 | 21.4k | } |
1393 | | |
1394 | | css::uno::Reference< css::xml::sax::XFastContextHandler > SvXMLNumFormatContext::createFastChildContext( |
1395 | | sal_Int32 nElement, |
1396 | | const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) |
1397 | 48.0k | { |
1398 | 48.0k | SvXMLImportContext* pContext = nullptr; |
1399 | | |
1400 | 48.0k | switch (nElement) |
1401 | 48.0k | { |
1402 | 0 | case XML_ELEMENT(LO_EXT, XML_TEXT): |
1403 | 17.0k | case XML_ELEMENT(NUMBER, XML_TEXT): |
1404 | 17.0k | pContext = new SvXMLNumFmtElementContext( GetImport(), nElement, |
1405 | 17.0k | *this, SvXMLStyleTokens::Text, xAttrList ); |
1406 | 17.0k | break; |
1407 | 0 | case XML_ELEMENT(LO_EXT, XML_FILL_CHARACTER): |
1408 | 0 | case XML_ELEMENT(NUMBER, XML_FILL_CHARACTER): |
1409 | 0 | pContext = new SvXMLNumFmtElementContext( GetImport(), nElement, |
1410 | 0 | *this, SvXMLStyleTokens::FillCharacter, xAttrList ); |
1411 | 0 | break; |
1412 | 9.18k | case XML_ELEMENT(NUMBER, XML_NUMBER): |
1413 | 9.18k | pContext = new SvXMLNumFmtElementContext( GetImport(), nElement, |
1414 | 9.18k | *this, SvXMLStyleTokens::Number, xAttrList ); |
1415 | 9.18k | break; |
1416 | 314 | case XML_ELEMENT(NUMBER, XML_SCIENTIFIC_NUMBER): |
1417 | 314 | pContext = new SvXMLNumFmtElementContext( GetImport(), nElement, |
1418 | 314 | *this, SvXMLStyleTokens::ScientificNumber, xAttrList ); |
1419 | 314 | break; |
1420 | 0 | case XML_ELEMENT(NUMBER, XML_FRACTION): |
1421 | 0 | pContext = new SvXMLNumFmtElementContext( GetImport(), nElement, |
1422 | 0 | *this, SvXMLStyleTokens::Fraction, xAttrList ); |
1423 | 0 | break; |
1424 | 3.00k | case XML_ELEMENT(NUMBER, XML_CURRENCY_SYMBOL): |
1425 | 3.00k | pContext = new SvXMLNumFmtElementContext( GetImport(), nElement, |
1426 | 3.00k | *this, SvXMLStyleTokens::CurrencySymbol, xAttrList ); |
1427 | 3.00k | break; |
1428 | 1.17k | case XML_ELEMENT(NUMBER, XML_DAY): |
1429 | 1.17k | pContext = new SvXMLNumFmtElementContext( GetImport(), nElement, |
1430 | 1.17k | *this, SvXMLStyleTokens::Day, xAttrList ); |
1431 | 1.17k | break; |
1432 | 1.33k | case XML_ELEMENT(NUMBER, XML_MONTH): |
1433 | 1.33k | pContext = new SvXMLNumFmtElementContext( GetImport(), nElement, |
1434 | 1.33k | *this, SvXMLStyleTokens::Month, xAttrList ); |
1435 | 1.33k | break; |
1436 | 1.11k | case XML_ELEMENT(NUMBER, XML_YEAR): |
1437 | 1.11k | pContext = new SvXMLNumFmtElementContext( GetImport(), nElement, |
1438 | 1.11k | *this, SvXMLStyleTokens::Year, xAttrList ); |
1439 | 1.11k | break; |
1440 | 0 | case XML_ELEMENT(NUMBER, XML_ERA): |
1441 | 0 | pContext = new SvXMLNumFmtElementContext( GetImport(), nElement, |
1442 | 0 | *this, SvXMLStyleTokens::Era, xAttrList ); |
1443 | 0 | break; |
1444 | 98 | case XML_ELEMENT(NUMBER, XML_DAY_OF_WEEK): |
1445 | 98 | pContext = new SvXMLNumFmtElementContext( GetImport(), nElement, |
1446 | 98 | *this, SvXMLStyleTokens::DayOfWeek, xAttrList ); |
1447 | 98 | break; |
1448 | 0 | case XML_ELEMENT(NUMBER, XML_WEEK_OF_YEAR): |
1449 | 0 | pContext = new SvXMLNumFmtElementContext( GetImport(), nElement, |
1450 | 0 | *this, SvXMLStyleTokens::WeekOfYear, xAttrList ); |
1451 | 0 | break; |
1452 | 0 | case XML_ELEMENT(NUMBER, XML_QUARTER): |
1453 | 0 | pContext = new SvXMLNumFmtElementContext( GetImport(), nElement, |
1454 | 0 | *this, SvXMLStyleTokens::Quarter, xAttrList ); |
1455 | 0 | break; |
1456 | 1.80k | case XML_ELEMENT(NUMBER, XML_HOURS): |
1457 | 1.80k | pContext = new SvXMLNumFmtElementContext( GetImport(), nElement, |
1458 | 1.80k | *this, SvXMLStyleTokens::Hours, xAttrList ); |
1459 | 1.80k | break; |
1460 | 560 | case XML_ELEMENT(NUMBER, XML_AM_PM): |
1461 | 560 | pContext = new SvXMLNumFmtElementContext( GetImport(), nElement, |
1462 | 560 | *this, SvXMLStyleTokens::AmPm, xAttrList ); |
1463 | 560 | break; |
1464 | 2.52k | case XML_ELEMENT(NUMBER, XML_MINUTES): |
1465 | 2.52k | pContext = new SvXMLNumFmtElementContext( GetImport(), nElement, |
1466 | 2.52k | *this, SvXMLStyleTokens::Minutes, xAttrList ); |
1467 | 2.52k | break; |
1468 | 1.33k | case XML_ELEMENT(NUMBER, XML_SECONDS): |
1469 | 1.33k | pContext = new SvXMLNumFmtElementContext( GetImport(), nElement, |
1470 | 1.33k | *this, SvXMLStyleTokens::Seconds, xAttrList ); |
1471 | 1.33k | break; |
1472 | 67 | case XML_ELEMENT(NUMBER, XML_BOOLEAN): |
1473 | 67 | pContext = new SvXMLNumFmtElementContext( GetImport(), nElement, |
1474 | 67 | *this, SvXMLStyleTokens::Boolean, xAttrList ); |
1475 | 67 | break; |
1476 | 1.31k | case XML_ELEMENT(NUMBER, XML_TEXT_CONTENT): |
1477 | 1.31k | pContext = new SvXMLNumFmtElementContext( GetImport(), nElement, |
1478 | 1.31k | *this, SvXMLStyleTokens::TextContent, xAttrList ); |
1479 | 1.31k | break; |
1480 | | |
1481 | 1.74k | case XML_ELEMENT(STYLE, XML_TEXT_PROPERTIES): |
1482 | 1.74k | pContext = new SvXMLNumFmtPropContext( GetImport(), nElement, |
1483 | 1.74k | *this, xAttrList ); |
1484 | 1.74k | break; |
1485 | 5.06k | case XML_ELEMENT(STYLE, XML_MAP): |
1486 | 5.06k | { |
1487 | | // SvXMLNumFmtMapContext::EndElement adds to aMyConditions, |
1488 | | // so there's no need for an extra flag |
1489 | 5.06k | pContext = new SvXMLNumFmtMapContext( GetImport(), nElement, |
1490 | 5.06k | *this, xAttrList ); |
1491 | 5.06k | } |
1492 | 5.06k | break; |
1493 | 48.0k | } |
1494 | | |
1495 | 48.0k | if( !pContext ) |
1496 | 372 | { |
1497 | 372 | SAL_WARN("xmloff.core", "No context for unknown-element " << SvXMLImport::getPrefixAndNameFromToken(nElement)); |
1498 | 372 | pContext = new SvXMLImportContext(GetImport()); |
1499 | 372 | } |
1500 | | |
1501 | 48.0k | return pContext; |
1502 | 48.0k | } |
1503 | | |
1504 | | sal_Int32 SvXMLNumFormatContext::GetKey() |
1505 | 950 | { |
1506 | 950 | if (m_nKey > -1) |
1507 | 670 | { |
1508 | 670 | if (m_bRemoveAfterUse) |
1509 | 0 | { |
1510 | | // format is used -> don't remove |
1511 | 0 | m_bRemoveAfterUse = false; |
1512 | 0 | if (m_pData) |
1513 | 0 | m_pData->SetUsed(m_nKey); |
1514 | | |
1515 | | // Add to import's list of keys now - CreateAndInsert didn't add |
1516 | | // the style if bRemoveAfterUse was set. |
1517 | 0 | GetImport().AddNumberStyle( m_nKey, GetName() ); |
1518 | 0 | } |
1519 | 670 | return m_nKey; |
1520 | 670 | } |
1521 | 280 | else |
1522 | 280 | { |
1523 | | // reset bRemoveAfterUse before CreateAndInsert, so AddKey is called without bRemoveAfterUse set |
1524 | 280 | m_bRemoveAfterUse = false; |
1525 | 280 | CreateAndInsert(true); |
1526 | 280 | return m_nKey; |
1527 | 280 | } |
1528 | 950 | } |
1529 | | |
1530 | | sal_Int32 SvXMLNumFormatContext::PrivateGetKey(std::vector<SvXMLNumFormatContext*>& rCreateStack) |
1531 | 3.38k | { |
1532 | | // used for map elements in CreateAndInsert - don't reset bRemoveAfterUse flag |
1533 | | |
1534 | 3.38k | if (m_nKey > -1) |
1535 | 3.34k | return m_nKey; |
1536 | 42 | else |
1537 | 42 | { |
1538 | 42 | CreateAndInsert(true, rCreateStack); |
1539 | 42 | return m_nKey; |
1540 | 42 | } |
1541 | 3.38k | } |
1542 | | |
1543 | | sal_Int32 SvXMLNumFormatContext::CreateAndInsert( css::uno::Reference< css::util::XNumberFormatsSupplier > const & xFormatsSupplier ) |
1544 | 0 | { |
1545 | 0 | if (m_nKey <= -1) |
1546 | 0 | { |
1547 | 0 | SvNumberFormatter* pFormatter = nullptr; |
1548 | 0 | SvNumberFormatsSupplierObj* pObj = |
1549 | 0 | comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>( xFormatsSupplier ); |
1550 | 0 | if (pObj) |
1551 | 0 | pFormatter = pObj->GetNumberFormatter(); |
1552 | |
|
1553 | 0 | if ( pFormatter ) |
1554 | 0 | { |
1555 | 0 | std::vector<SvXMLNumFormatContext*> aCreateStack; |
1556 | 0 | return CreateAndInsert(pFormatter, aCreateStack); |
1557 | 0 | } |
1558 | 0 | else |
1559 | 0 | return -1; |
1560 | 0 | } |
1561 | 0 | else |
1562 | 0 | return m_nKey; |
1563 | 0 | } |
1564 | | |
1565 | | void SvXMLNumFormatContext::CreateAndInsert(bool bOverwrite) |
1566 | 11.1k | { |
1567 | 11.1k | std::vector<SvXMLNumFormatContext*> aCreateStack; |
1568 | 11.1k | return CreateAndInsert(bOverwrite, aCreateStack); |
1569 | 11.1k | } |
1570 | | |
1571 | | void SvXMLNumFormatContext::CreateAndInsert(bool /*bOverwrite*/, std::vector<SvXMLNumFormatContext*>& rCreateStack) |
1572 | 11.2k | { |
1573 | 11.2k | if (m_nKey <= -1) |
1574 | 11.1k | CreateAndInsert(m_pData->GetNumberFormatter(), rCreateStack); |
1575 | 11.2k | } |
1576 | | |
1577 | | sal_Int32 SvXMLNumFormatContext::CreateAndInsert(SvNumberFormatter* pFormatter, std::vector<SvXMLNumFormatContext*>& rCreateStack) |
1578 | 11.1k | { |
1579 | 11.1k | if (!pFormatter) |
1580 | 0 | { |
1581 | 0 | OSL_FAIL("no number formatter"); |
1582 | 0 | return -1; |
1583 | 0 | } |
1584 | | |
1585 | 11.1k | rCreateStack.push_back(this); |
1586 | | |
1587 | 11.1k | sal_uInt32 nIndex = NUMBERFORMAT_ENTRY_NOT_FOUND; |
1588 | | |
1589 | 15.1k | for (size_t i = 0; i < m_aMyConditions.size(); i++) |
1590 | 3.97k | { |
1591 | 3.97k | SvXMLNumFormatContext* pStyle = const_cast<SvXMLNumFormatContext*>( static_cast<const SvXMLNumFormatContext *>(m_pStyles->FindStyleChildContext( |
1592 | 3.97k | XmlStyleFamily::DATA_STYLE, m_aMyConditions[i].sMapName))); |
1593 | 3.97k | if (std::find(rCreateStack.begin(), rCreateStack.end(), pStyle) != rCreateStack.end()) |
1594 | 74 | { |
1595 | 74 | SAL_INFO("xmloff.style", "invalid style:map references containing style"); |
1596 | 74 | pStyle = nullptr; |
1597 | 74 | } |
1598 | 3.97k | if (pStyle) |
1599 | 3.38k | { |
1600 | 3.38k | if (pStyle->PrivateGetKey(rCreateStack) > -1) // don't reset pStyle's bRemoveAfterUse flag |
1601 | 3.38k | AddCondition(i); |
1602 | 3.38k | } |
1603 | 3.97k | } |
1604 | | |
1605 | 11.1k | sal_Int32 nBufLen; |
1606 | 11.1k | if ( m_aFormatCode.isEmpty() ) |
1607 | 280 | { |
1608 | | // insert empty format as empty string (with quotes) |
1609 | | // #93901# this check has to be done before inserting the conditions |
1610 | 280 | m_aFormatCode.append("\"\""); // "" |
1611 | 280 | } |
1612 | 10.8k | else if (m_bHasTrailingEmptyText && (nBufLen = m_aFormatCode.getLength()) >= 3) |
1613 | 0 | { |
1614 | | // Remove a trailing empty text. Earlier this may had been written to |
1615 | | // file, like in "General;General" written with elements for |
1616 | | // 'General"";General""' (whyever); when reading, empty text was |
1617 | | // ignored, which it isn't anymore, so get rid of those. |
1618 | 0 | if (m_aFormatCode[nBufLen-1] == '"' && m_aFormatCode[nBufLen-2] == '"') |
1619 | 0 | m_aFormatCode.truncate( nBufLen - 2); |
1620 | 0 | } |
1621 | | |
1622 | 11.1k | m_aFormatCode.insert( 0, m_aConditions ); |
1623 | 11.1k | m_aConditions.setLength(0); |
1624 | 11.1k | OUString sFormat = m_aFormatCode.makeStringAndClear(); |
1625 | | |
1626 | | // test special cases |
1627 | | |
1628 | 11.1k | if ( m_bAutoDec ) // automatic decimal places |
1629 | 1.50k | { |
1630 | | // #99391# adjust only if the format contains no text elements, no conditions |
1631 | | // and no color definition (detected by the '[' at the start) |
1632 | | |
1633 | 1.50k | if ( m_nType == SvXMLStylesTokens::NUMBER_STYLE && !m_bHasExtraText && |
1634 | 994 | m_aMyConditions.empty() && sFormat.toChar() != '[' ) |
1635 | 994 | nIndex = pFormatter->GetStandardIndex( m_nFormatLang ); |
1636 | 1.50k | } |
1637 | 11.1k | if ( m_bAutoInt ) // automatic integer digits |
1638 | 519 | { |
1639 | | //! only if two decimal places was set? |
1640 | | |
1641 | 519 | if ( m_nType == SvXMLStylesTokens::NUMBER_STYLE && !m_bHasExtraText && |
1642 | 128 | m_aMyConditions.empty() && sFormat.toChar() != '[' ) |
1643 | 128 | nIndex = pFormatter->GetFormatIndex( NF_NUMBER_SYSTEM, m_nFormatLang ); |
1644 | 519 | } |
1645 | | |
1646 | 11.1k | if ( m_nType == SvXMLStylesTokens::BOOLEAN_STYLE && !m_bHasExtraText && |
1647 | 0 | m_aMyConditions.empty() && sFormat.toChar() != '[' ) |
1648 | 0 | nIndex = pFormatter->GetFormatIndex( NF_BOOLEAN, m_nFormatLang ); |
1649 | | |
1650 | | // check for default date formats |
1651 | 11.1k | if ( m_nType == SvXMLStylesTokens::DATE_STYLE && m_bAutoOrder && !m_bDateNoDefault ) |
1652 | 151 | { |
1653 | 151 | NfIndexTableOffset eFormat = static_cast<NfIndexTableOffset>(SvXMLNumFmtDefaults::GetDefaultDateFormat( |
1654 | 151 | m_eDateDOW, m_eDateDay, m_eDateMonth, m_eDateYear, |
1655 | 151 | m_eDateHours, m_eDateMins, m_eDateSecs, m_bFromSystem )); |
1656 | 151 | if ( eFormat < NF_INDEX_TABLE_RESERVED_START ) |
1657 | 144 | { |
1658 | | // #109651# if a date format has the automatic-order attribute and |
1659 | | // contains exactly the elements of one of the default date formats, |
1660 | | // use that default format, with the element order and separators |
1661 | | // from the current locale settings |
1662 | | |
1663 | 144 | nIndex = pFormatter->GetFormatIndex( eFormat, m_nFormatLang ); |
1664 | 144 | } |
1665 | 151 | } |
1666 | | |
1667 | 11.1k | if ( nIndex == NUMBERFORMAT_ENTRY_NOT_FOUND && !sFormat.isEmpty() ) |
1668 | 10.0k | { |
1669 | | // insert by format string |
1670 | | |
1671 | 10.0k | OUString aFormatStr( sFormat ); |
1672 | 10.0k | nIndex = pFormatter->GetEntryKey( aFormatStr, m_nFormatLang ); |
1673 | 10.0k | if ( nIndex == NUMBERFORMAT_ENTRY_NOT_FOUND ) |
1674 | 8.63k | { |
1675 | 8.63k | sal_Int32 nErrPos = 0; |
1676 | 8.63k | SvNumFormatType l_nType = SvNumFormatType::ALL; |
1677 | 8.63k | bool bOk = pFormatter->PutEntry( aFormatStr, nErrPos, l_nType, nIndex, m_nFormatLang ); |
1678 | 8.63k | if ( !bOk && nErrPos == 0 && aFormatStr != sFormat ) |
1679 | 2 | { |
1680 | | // if the string was modified by PutEntry, look for an existing format |
1681 | | // with the modified string |
1682 | 2 | nIndex = pFormatter->GetEntryKey( aFormatStr, m_nFormatLang ); |
1683 | 2 | if ( nIndex != NUMBERFORMAT_ENTRY_NOT_FOUND ) |
1684 | 2 | bOk = true; |
1685 | 2 | } |
1686 | 8.63k | if (!bOk) |
1687 | 51 | nIndex = NUMBERFORMAT_ENTRY_NOT_FOUND; |
1688 | 8.63k | } |
1689 | 10.0k | } |
1690 | | |
1691 | | //! I18N doesn't provide SYSTEM or extended date information yet |
1692 | 11.1k | if ( nIndex != NUMBERFORMAT_ENTRY_NOT_FOUND && !m_bAutoOrder ) |
1693 | 10.9k | { |
1694 | | // use fixed-order formats instead of SYS... if bAutoOrder is false |
1695 | | // (only if the format strings are equal for the locale) |
1696 | | |
1697 | 10.9k | NfIndexTableOffset eOffset = SvNumberFormatter::GetIndexTableOffset( nIndex ); |
1698 | 10.9k | if ( eOffset == NF_DATE_SYS_DMMMYYYY ) |
1699 | 0 | { |
1700 | 0 | sal_uInt32 nNewIndex = pFormatter->GetFormatIndex( NF_DATE_DIN_DMMMYYYY, m_nFormatLang ); |
1701 | 0 | const SvNumberformat* pOldEntry = pFormatter->GetEntry( nIndex ); |
1702 | 0 | const SvNumberformat* pNewEntry = pFormatter->GetEntry( nNewIndex ); |
1703 | 0 | if ( pOldEntry && pNewEntry && pOldEntry->GetFormatstring() == pNewEntry->GetFormatstring() ) |
1704 | 0 | nIndex = nNewIndex; |
1705 | 0 | } |
1706 | 10.9k | else if ( eOffset == NF_DATE_SYS_DMMMMYYYY ) |
1707 | 0 | { |
1708 | 0 | sal_uInt32 nNewIndex = pFormatter->GetFormatIndex( NF_DATE_DIN_DMMMMYYYY, m_nFormatLang ); |
1709 | 0 | const SvNumberformat* pOldEntry = pFormatter->GetEntry( nIndex ); |
1710 | 0 | const SvNumberformat* pNewEntry = pFormatter->GetEntry( nNewIndex ); |
1711 | 0 | if ( pOldEntry && pNewEntry && pOldEntry->GetFormatstring() == pNewEntry->GetFormatstring() ) |
1712 | 0 | nIndex = nNewIndex; |
1713 | 0 | } |
1714 | 10.9k | } |
1715 | | |
1716 | 11.1k | if ((nIndex != NUMBERFORMAT_ENTRY_NOT_FOUND) && !m_sFormatTitle.isEmpty()) |
1717 | 0 | { |
1718 | 0 | SvNumberformat* pFormat = const_cast<SvNumberformat*>(pFormatter->GetEntry( nIndex )); |
1719 | 0 | if (pFormat) |
1720 | 0 | { |
1721 | 0 | pFormat->SetComment(m_sFormatTitle); |
1722 | 0 | } |
1723 | 0 | } |
1724 | | |
1725 | 11.1k | if ( nIndex == NUMBERFORMAT_ENTRY_NOT_FOUND ) |
1726 | 51 | { |
1727 | 51 | OSL_FAIL("invalid number format"); |
1728 | 51 | nIndex = pFormatter->GetStandardIndex( m_nFormatLang ); |
1729 | 51 | } |
1730 | | |
1731 | 11.1k | m_pData->AddKey( nIndex, GetName(), m_bRemoveAfterUse ); |
1732 | 11.1k | m_nKey = nIndex; |
1733 | | |
1734 | | // Add to import's list of keys (shared between styles and content import) |
1735 | | // only if not volatile - formats are removed from NumberFormatter at the |
1736 | | // end of each import (in SvXMLNumFmtHelper dtor). |
1737 | | // If bRemoveAfterUse is reset later in GetKey, AddNumberStyle is called there. |
1738 | | |
1739 | 11.1k | if (!m_bRemoveAfterUse) |
1740 | 7.32k | GetImport().AddNumberStyle( m_nKey, GetName() ); |
1741 | | |
1742 | 11.1k | rCreateStack.pop_back(); |
1743 | | |
1744 | 11.1k | return m_nKey; |
1745 | 11.1k | } |
1746 | | |
1747 | | const LocaleDataWrapper& SvXMLNumFormatContext::GetLocaleData() const |
1748 | 6.21k | { |
1749 | 6.21k | return m_pData->GetLocaleData( m_nFormatLang ); |
1750 | 6.21k | } |
1751 | | |
1752 | | void SvXMLNumFormatContext::AddToCode( sal_Unicode c ) |
1753 | 1.53k | { |
1754 | 1.53k | m_aFormatCode.append( c ); |
1755 | 1.53k | m_bHasExtraText = true; |
1756 | 1.53k | } |
1757 | | |
1758 | | void SvXMLNumFormatContext::AddToCode( std::u16string_view rString ) |
1759 | 17.1k | { |
1760 | 17.1k | m_aFormatCode.append( rString ); |
1761 | 17.1k | m_bHasExtraText = true; |
1762 | 17.1k | m_bHasTrailingEmptyText = false; // is set by caller again if so |
1763 | 17.1k | } |
1764 | | |
1765 | | void SvXMLNumFormatContext::AddNumber( const SvXMLNumberInfo& rInfo ) |
1766 | 9.47k | { |
1767 | 9.47k | SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter(); |
1768 | 9.47k | if (!pFormatter) |
1769 | 0 | return; |
1770 | | |
1771 | | // store special conditions |
1772 | 9.47k | m_bAutoDec = ( rInfo.nDecimals < 0 ); |
1773 | 9.47k | m_bAutoInt = ( rInfo.nInteger < 0 ); |
1774 | | |
1775 | 9.47k | sal_uInt16 nPrec = 0; |
1776 | 9.47k | sal_uInt16 nLeading = 0; |
1777 | 9.47k | if ( rInfo.nDecimals >= 0 ) // < 0 : Default |
1778 | 7.37k | nPrec = static_cast<sal_uInt16>(rInfo.nDecimals); |
1779 | 9.47k | if ( rInfo.nInteger >= 0 ) // < 0 : Default |
1780 | 8.62k | nLeading = static_cast<sal_uInt16>(rInfo.nInteger); |
1781 | | |
1782 | 9.47k | if ( m_bAutoDec ) |
1783 | 2.09k | { |
1784 | 2.09k | if ( m_nType == SvXMLStylesTokens::CURRENCY_STYLE ) |
1785 | 380 | { |
1786 | | // for currency formats, "automatic decimals" is used for the automatic |
1787 | | // currency format with (fixed) decimals from the locale settings |
1788 | | |
1789 | 380 | const LocaleDataWrapper& rLoc = m_pData->GetLocaleData( m_nFormatLang ); |
1790 | 380 | nPrec = rLoc.getCurrDigits(); |
1791 | 380 | } |
1792 | 1.71k | else |
1793 | 1.71k | { |
1794 | | // for other types, "automatic decimals" means dynamic determination of |
1795 | | // decimals, as achieved with the "general" keyword |
1796 | | |
1797 | 1.71k | m_aFormatCode.append( pFormatter->GetStandardName( m_nFormatLang ) ); |
1798 | 1.71k | return; |
1799 | 1.71k | } |
1800 | 2.09k | } |
1801 | 7.75k | if ( m_bAutoInt ) |
1802 | 497 | { |
1803 | | //!... |
1804 | 497 | } |
1805 | | |
1806 | 7.75k | sal_uInt16 nGenPrec = nPrec; |
1807 | 7.75k | if ( rInfo.nMinDecimalDigits >= 0 ) |
1808 | 7.37k | nGenPrec = rInfo.nMinDecimalDigits; |
1809 | 7.75k | if ( rInfo.bDecReplace ) |
1810 | 0 | nGenPrec = 0; // generate format without decimals... |
1811 | | |
1812 | 7.75k | bool bGrouping = rInfo.bGrouping; |
1813 | 7.75k | size_t const nEmbeddedCount = rInfo.m_EmbeddedElements.size(); |
1814 | 7.75k | if ( nEmbeddedCount && rInfo.m_EmbeddedElements.rbegin()->first > 0 ) |
1815 | 0 | bGrouping = false; // grouping and embedded characters in integer part can't be used together |
1816 | | |
1817 | 7.75k | sal_uInt32 nStdIndex = pFormatter->GetStandardIndex( m_nFormatLang ); |
1818 | 7.75k | OUStringBuffer aNumStr(pFormatter->GenerateFormat( nStdIndex, m_nFormatLang, |
1819 | 7.75k | bGrouping, false, nGenPrec, nLeading )); |
1820 | | |
1821 | 7.75k | if ( rInfo.nExpDigits >= 0 && nLeading == 0 && !bGrouping && nEmbeddedCount == 0 ) |
1822 | 27 | { |
1823 | | // #i43959# For scientific numbers, "#" in the integer part forces a digit, |
1824 | | // so it has to be removed if nLeading is 0 (".00E+0", not "#.00E+0"). |
1825 | | |
1826 | 27 | aNumStr.stripStart('#'); |
1827 | 27 | } |
1828 | | |
1829 | 7.75k | if ( rInfo.nBlankInteger > 0 ) |
1830 | 0 | { |
1831 | | // Replace nBlankInteger '0' by '?' |
1832 | 0 | sal_Int32 nIndex = 0; |
1833 | 0 | sal_Int32 nBlanks = rInfo.nBlankInteger; |
1834 | 0 | sal_Int32 nIntegerEnd = aNumStr.indexOf( pFormatter->GetNumDecimalSep() ); |
1835 | 0 | if ( nIntegerEnd < 0 ) |
1836 | 0 | nIntegerEnd = aNumStr.getLength(); |
1837 | 0 | while ( nIndex < nIntegerEnd && nBlanks > 0 ) |
1838 | 0 | { |
1839 | 0 | if ( aNumStr[nIndex] == '0' ) |
1840 | 0 | { |
1841 | 0 | aNumStr[nIndex] = '?'; |
1842 | 0 | nBlanks--; |
1843 | 0 | } |
1844 | 0 | nIndex++; |
1845 | 0 | } |
1846 | 0 | } |
1847 | | |
1848 | 7.75k | if ( bGrouping && rInfo.nExpInterval > rInfo.nInteger ) |
1849 | 0 | { |
1850 | 0 | sal_Int32 nIndex = 0; |
1851 | 0 | sal_Int32 nDigits = rInfo.nInteger; |
1852 | 0 | sal_Int32 nIntegerEnd = aNumStr.indexOf( pFormatter->GetNumDecimalSep() ); |
1853 | 0 | if ( nIntegerEnd < 0 ) |
1854 | 0 | nIntegerEnd = aNumStr.getLength(); |
1855 | 0 | while ( nIndex >= 0 && nIndex < nIntegerEnd ) |
1856 | 0 | { |
1857 | 0 | if ( ( nIndex = aNumStr.indexOf( '#', nIndex ) ) >= 0 ) |
1858 | 0 | { |
1859 | 0 | nDigits ++; |
1860 | 0 | nIndex ++; |
1861 | 0 | } |
1862 | 0 | else |
1863 | 0 | nIndex = -1; |
1864 | 0 | } |
1865 | 0 | while ( rInfo.nExpInterval > nDigits ) |
1866 | 0 | { |
1867 | 0 | nDigits++; |
1868 | 0 | aNumStr.insert( 0, '#' ); |
1869 | 0 | } |
1870 | 0 | } |
1871 | | |
1872 | 7.75k | if ( ( rInfo.bDecReplace || rInfo.nMinDecimalDigits < rInfo.nDecimals ) && nPrec ) // add decimal replacement (dashes) |
1873 | 0 | { |
1874 | | // add dashes for explicit decimal replacement, # or ? for variable decimals |
1875 | 0 | sal_Unicode cAdd = rInfo.bDecReplace ? '-' : ( rInfo.bDecAlign ? '?': '#' ); |
1876 | |
|
1877 | 0 | if ( rInfo.nMinDecimalDigits == 0 ) |
1878 | 0 | aNumStr.append( m_pData->GetLocaleData( m_nFormatLang ).getNumDecimalSep() ); |
1879 | 0 | for ( sal_uInt16 i=rInfo.nMinDecimalDigits; i<nPrec; i++) |
1880 | 0 | aNumStr.append( cAdd ); |
1881 | 0 | } |
1882 | | |
1883 | | // Scientific number |
1884 | 7.75k | sal_Int32 nExpPos = -1; |
1885 | 7.75k | if ( rInfo.nExpDigits > 0 ) |
1886 | 262 | { |
1887 | 262 | nExpPos = aNumStr.getLength(); |
1888 | 262 | aNumStr.append( rInfo.bExponentLowercase ? u"e" : u"E" ); |
1889 | | // exponent sign is required with embedded text in exponent |
1890 | 262 | if ( rInfo.bExpSign || ( nEmbeddedCount && ( rInfo.nDecimals + 1 < -rInfo.m_EmbeddedElements.begin()->first ) ) ) |
1891 | 262 | { |
1892 | 262 | aNumStr.append( u"+" ); |
1893 | 262 | } |
1894 | 525 | for (sal_Int32 i=0; i<rInfo.nExpDigits; i++) |
1895 | 263 | { |
1896 | 263 | if ( i < rInfo.nBlankExp ) |
1897 | 0 | aNumStr.append( '?' ); |
1898 | 263 | else |
1899 | 263 | aNumStr.append( '0' ); |
1900 | 263 | } |
1901 | 262 | } |
1902 | | |
1903 | 7.75k | if ( nEmbeddedCount ) |
1904 | 0 | { |
1905 | | // insert embedded strings into number string |
1906 | | // support integer (position >=0) and decimal (position <0) part |
1907 | | // nZeroPos is the string position where format position 0 is inserted |
1908 | |
|
1909 | 0 | sal_Int32 nZeroPos = aNumStr.indexOf( m_pData->GetLocaleData( m_nFormatLang ).getNumDecimalSep() ); |
1910 | 0 | if ( nZeroPos < 0 ) |
1911 | 0 | { |
1912 | 0 | nZeroPos = aNumStr.getLength(); |
1913 | 0 | } |
1914 | | |
1915 | | // m_EmbeddedElements is sorted - last entry has the largest position (leftmost) |
1916 | 0 | sal_Int32 const nLastFormatPos = rInfo.m_EmbeddedElements.rbegin()->first; |
1917 | 0 | if ( nLastFormatPos >= nZeroPos ) |
1918 | 0 | { |
1919 | | // add '#' characters so all embedded texts are really embedded in digits |
1920 | | // (there always has to be a digit before the leftmost embedded text) |
1921 | |
|
1922 | 0 | sal_Int32 nAddCount = nLastFormatPos + 1 - nZeroPos; |
1923 | 0 | for(sal_Int32 index = 0; index < nAddCount; ++index) |
1924 | 0 | { |
1925 | 0 | aNumStr.insert(0, '#'); |
1926 | 0 | } |
1927 | 0 | nZeroPos = nZeroPos + nAddCount; |
1928 | 0 | if ( nExpPos > 0 ) |
1929 | 0 | nExpPos = nExpPos + nAddCount; |
1930 | 0 | } |
1931 | | |
1932 | | // m_EmbeddedElements is sorted with ascending positions - loop is from right to left |
1933 | 0 | for (auto const& it : rInfo.m_EmbeddedElements) |
1934 | 0 | { |
1935 | 0 | sal_Int32 const nFormatPos = it.first; |
1936 | 0 | sal_Int32 nInsertPos = nZeroPos - nFormatPos; |
1937 | 0 | if ( nExpPos > 0 && nInsertPos > nExpPos ) |
1938 | 0 | nInsertPos ++; |
1939 | 0 | if ( 0 <= nInsertPos && nInsertPos <= aNumStr.getLength() ) |
1940 | 0 | { |
1941 | 0 | aNumStr.insert( nInsertPos, it.second ); |
1942 | 0 | } |
1943 | 0 | } |
1944 | 0 | } |
1945 | | |
1946 | 7.75k | m_aFormatCode.append( aNumStr ); |
1947 | | |
1948 | | // add extra thousands separators for display factor |
1949 | | |
1950 | 7.75k | if (rInfo.fDisplayFactor == 1.0 || rInfo.fDisplayFactor <= 0.0) |
1951 | 7.75k | return; |
1952 | | |
1953 | | // test for 1.0 is just for optimization - nSepCount would be 0 |
1954 | | |
1955 | | // one separator for each factor of 1000 |
1956 | 0 | sal_Int32 nSepCount = static_cast<sal_Int32>(::rtl::math::round( log10(rInfo.fDisplayFactor) / 3.0 )); |
1957 | 0 | if ( nSepCount > 0 ) |
1958 | 0 | { |
1959 | 0 | OUString aSep = m_pData->GetLocaleData( m_nFormatLang ).getNumThousandSep(); |
1960 | 0 | for ( sal_Int32 i=0; i<nSepCount; i++ ) |
1961 | 0 | m_aFormatCode.append( aSep ); |
1962 | 0 | } |
1963 | 0 | } |
1964 | | |
1965 | | void SvXMLNumFormatContext::AddCurrency( const OUString& rContent, LanguageType nLang ) |
1966 | 2.99k | { |
1967 | 2.99k | bool bAutomatic = false; |
1968 | 2.99k | OUString aSymbol = rContent; |
1969 | 2.99k | if ( aSymbol.isEmpty()) |
1970 | 6 | { |
1971 | 6 | SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter(); |
1972 | 6 | if ( pFormatter ) |
1973 | 6 | { |
1974 | 6 | pFormatter->ChangeIntl( m_nFormatLang ); |
1975 | 6 | OUString sCurString, sDummy; |
1976 | 6 | pFormatter->GetCompatibilityCurrency( sCurString, sDummy ); |
1977 | 6 | aSymbol = sCurString; |
1978 | | |
1979 | 6 | bAutomatic = true; |
1980 | 6 | } |
1981 | 6 | } |
1982 | 2.98k | else if ( nLang == LANGUAGE_SYSTEM && aSymbol == "CCC" ) |
1983 | 0 | { |
1984 | | // "CCC" is used for automatic long symbol |
1985 | 0 | bAutomatic = true; |
1986 | 0 | } |
1987 | | |
1988 | 2.99k | if ( bAutomatic ) |
1989 | 6 | { |
1990 | | // remove unnecessary quotes before automatic symbol (formats like "-(0DM)") |
1991 | | // otherwise the currency symbol isn't recognized (#94048#) |
1992 | | |
1993 | 6 | sal_Int32 nLength = m_aFormatCode.getLength(); |
1994 | 6 | if ( nLength > 1 && m_aFormatCode[nLength - 1] == '"' ) |
1995 | 0 | { |
1996 | | // find start of quoted string |
1997 | | // When SvXMLNumFmtElementContext::EndElement creates escaped quotes, |
1998 | | // they must be handled here, too. |
1999 | |
|
2000 | 0 | sal_Int32 nFirst = nLength - 2; |
2001 | 0 | while ( nFirst >= 0 && m_aFormatCode[nFirst] != '"' ) |
2002 | 0 | --nFirst; |
2003 | 0 | if ( nFirst >= 0 ) |
2004 | 0 | { |
2005 | | // remove both quotes from aFormatCode |
2006 | 0 | OUString aOld = m_aFormatCode.makeStringAndClear(); |
2007 | 0 | if ( nFirst > 0 ) |
2008 | 0 | m_aFormatCode.append( aOld.subView( 0, nFirst ) ); |
2009 | 0 | if ( nLength > nFirst + 2 ) |
2010 | 0 | m_aFormatCode.append( aOld.subView( nFirst + 1, nLength - nFirst - 2 ) ); |
2011 | 0 | } |
2012 | 0 | } |
2013 | 6 | } |
2014 | | |
2015 | 2.99k | if (!bAutomatic) |
2016 | 2.98k | m_aFormatCode.append( "[$" ); // intro for "new" currency symbols |
2017 | | |
2018 | 2.99k | m_aFormatCode.append( aSymbol ); |
2019 | | |
2020 | 2.99k | if (!bAutomatic) |
2021 | 2.98k | { |
2022 | 2.98k | if ( nLang != LANGUAGE_SYSTEM ) |
2023 | 2.73k | { |
2024 | | // '-' sign and language code in hex: |
2025 | 2.73k | m_aFormatCode.append("-" + OUString(OUString::number(sal_uInt16(nLang), 16)).toAsciiUpperCase()); |
2026 | 2.73k | } |
2027 | | |
2028 | 2.98k | m_aFormatCode.append( ']' ); // end of "new" currency symbol |
2029 | 2.98k | } |
2030 | 2.99k | } |
2031 | | |
2032 | | void SvXMLNumFormatContext::AddNfKeyword( sal_uInt16 nIndex ) |
2033 | 10.0k | { |
2034 | 10.0k | SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter(); |
2035 | 10.0k | if (!pFormatter) |
2036 | 0 | return; |
2037 | | |
2038 | 10.0k | if ( nIndex == NF_KEY_NNNN ) |
2039 | 13 | { |
2040 | 13 | nIndex = NF_KEY_NNN; |
2041 | 13 | m_bHasLongDoW = true; // to remove string constant with separator |
2042 | 13 | } |
2043 | | |
2044 | 10.0k | OUString sKeyword = pFormatter->GetKeyword( m_nFormatLang, nIndex ); |
2045 | | |
2046 | 10.0k | if ( nIndex == NF_KEY_H || nIndex == NF_KEY_HH || |
2047 | 8.20k | nIndex == NF_KEY_MI || nIndex == NF_KEY_MMI || |
2048 | 5.67k | nIndex == NF_KEY_S || nIndex == NF_KEY_SS ) |
2049 | 5.66k | { |
2050 | 5.66k | if ( !m_bTruncate && !m_bHasDateTime ) |
2051 | 843 | { |
2052 | | // with truncate-on-overflow = false, add "[]" to first time part |
2053 | 843 | m_aFormatCode.append("[" + sKeyword + "]"); |
2054 | 843 | } |
2055 | 4.82k | else |
2056 | 4.82k | { |
2057 | 4.82k | m_aFormatCode.append( sKeyword ); |
2058 | 4.82k | } |
2059 | 5.66k | m_bHasDateTime = true; |
2060 | 5.66k | } |
2061 | 4.34k | else |
2062 | 4.34k | { |
2063 | 4.34k | m_aFormatCode.append( sKeyword ); |
2064 | 4.34k | } |
2065 | | // collect the date elements that the format contains, to recognize default date formats |
2066 | 10.0k | switch ( nIndex ) |
2067 | 10.0k | { |
2068 | 85 | case NF_KEY_NN: m_eDateDOW = XML_DEA_SHORT; break; |
2069 | 13 | case NF_KEY_NNN: |
2070 | 13 | case NF_KEY_NNNN: m_eDateDOW = XML_DEA_LONG; break; |
2071 | 33 | case NF_KEY_D: m_eDateDay = XML_DEA_SHORT; break; |
2072 | 1.13k | case NF_KEY_DD: m_eDateDay = XML_DEA_LONG; break; |
2073 | 37 | case NF_KEY_M: m_eDateMonth = XML_DEA_SHORT; break; |
2074 | 780 | case NF_KEY_MM: m_eDateMonth = XML_DEA_LONG; break; |
2075 | 506 | case NF_KEY_MMM: m_eDateMonth = XML_DEA_TEXTSHORT; break; |
2076 | 8 | case NF_KEY_MMMM: m_eDateMonth = XML_DEA_TEXTLONG; break; |
2077 | 836 | case NF_KEY_YY: m_eDateYear = XML_DEA_SHORT; break; |
2078 | 276 | case NF_KEY_YYYY: m_eDateYear = XML_DEA_LONG; break; |
2079 | 1.19k | case NF_KEY_H: m_eDateHours = XML_DEA_SHORT; break; |
2080 | 614 | case NF_KEY_HH: m_eDateHours = XML_DEA_LONG; break; |
2081 | 108 | case NF_KEY_MI: m_eDateMins = XML_DEA_SHORT; break; |
2082 | 2.41k | case NF_KEY_MMI: m_eDateMins = XML_DEA_LONG; break; |
2083 | 50 | case NF_KEY_S: m_eDateSecs = XML_DEA_SHORT; break; |
2084 | 1.28k | case NF_KEY_SS: m_eDateSecs = XML_DEA_LONG; break; |
2085 | 0 | case NF_KEY_AP: |
2086 | 560 | case NF_KEY_AMPM: break; // AM/PM may or may not be in date/time formats -> ignore by itself |
2087 | 67 | default: |
2088 | 67 | m_bDateNoDefault = true; // any other element -> no default format |
2089 | 10.0k | } |
2090 | 10.0k | } |
2091 | | |
2092 | | static bool lcl_IsAtEnd( OUStringBuffer& rBuffer, std::u16string_view rToken ) |
2093 | 13 | { |
2094 | 13 | sal_Int32 nBufLen = rBuffer.getLength(); |
2095 | 13 | sal_Int32 nTokLen = rToken.size(); |
2096 | | |
2097 | 13 | if ( nTokLen > nBufLen ) |
2098 | 0 | return false; |
2099 | | |
2100 | 13 | sal_Int32 nStartPos = nBufLen - nTokLen; |
2101 | 52 | for ( sal_Int32 nTokPos = 0; nTokPos < nTokLen; nTokPos++ ) |
2102 | 39 | if ( rToken[ nTokPos ] != rBuffer[nStartPos + nTokPos] ) |
2103 | 0 | return false; |
2104 | | |
2105 | 13 | return true; |
2106 | 13 | } |
2107 | | |
2108 | | bool SvXMLNumFormatContext::ReplaceNfKeyword( sal_uInt16 nOld, sal_uInt16 nNew ) |
2109 | 13 | { |
2110 | | // replaces one keyword with another if it is found at the end of the code |
2111 | | |
2112 | 13 | SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter(); |
2113 | 13 | if (!pFormatter) |
2114 | 0 | return false; |
2115 | | |
2116 | 13 | OUString sOldStr = pFormatter->GetKeyword( m_nFormatLang, nOld ); |
2117 | 13 | if ( lcl_IsAtEnd( m_aFormatCode, sOldStr ) ) |
2118 | 13 | { |
2119 | | // remove old keyword |
2120 | 13 | m_aFormatCode.setLength( m_aFormatCode.getLength() - sOldStr.getLength() ); |
2121 | | |
2122 | | // add new keyword |
2123 | 13 | OUString sNewStr = pFormatter->GetKeyword( m_nFormatLang, nNew ); |
2124 | 13 | m_aFormatCode.append( sNewStr ); |
2125 | | |
2126 | 13 | return true; // changed |
2127 | 13 | } |
2128 | 0 | return false; // not found |
2129 | 13 | } |
2130 | | |
2131 | | void SvXMLNumFormatContext::AddCondition( const sal_Int32 nIndex ) |
2132 | 3.38k | { |
2133 | 3.38k | OUString rApplyName = m_aMyConditions[nIndex].sMapName; |
2134 | 3.38k | OUString rCondition = m_aMyConditions[nIndex].sCondition; |
2135 | 3.38k | SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter(); |
2136 | 3.38k | sal_uInt32 l_nKey = m_pData->GetKeyForName( rApplyName ); |
2137 | | |
2138 | 3.38k | OUString sRealCond; |
2139 | 3.38k | if ( !(pFormatter && l_nKey != NUMBERFORMAT_ENTRY_NOT_FOUND && |
2140 | 3.38k | rCondition.startsWith("value()", &sRealCond)) ) |
2141 | 227 | return; |
2142 | | |
2143 | | //! test for valid conditions |
2144 | | //! test for default conditions |
2145 | | |
2146 | 3.16k | bool bDefaultCond = false; |
2147 | | |
2148 | | //! collect all conditions first and adjust default to >=0, >0 or <0 depending on count |
2149 | | //! allow blanks in conditions |
2150 | 3.16k | if ( m_aConditions.isEmpty() && m_aMyConditions.size() == 1 && sRealCond == ">=0" ) |
2151 | 1.97k | bDefaultCond = true; |
2152 | | |
2153 | 3.16k | if ( m_nType == SvXMLStylesTokens::TEXT_STYLE && static_cast<size_t>(nIndex) == m_aMyConditions.size() - 1 ) |
2154 | 852 | { |
2155 | | // The last condition in a number format with a text part can only |
2156 | | // be "all other numbers", the condition string must be empty. |
2157 | 852 | bDefaultCond = true; |
2158 | 852 | } |
2159 | | |
2160 | 3.16k | if (!bDefaultCond) |
2161 | 800 | { |
2162 | | // Convert != to <> |
2163 | 800 | sal_Int32 nPos = sRealCond.indexOf( "!=" ); |
2164 | 800 | if ( nPos >= 0 ) |
2165 | 0 | { |
2166 | 0 | sRealCond = sRealCond.replaceAt( nPos, 2, u"<>" ); |
2167 | 0 | } |
2168 | | |
2169 | 800 | nPos = sRealCond.indexOf( '.' ); |
2170 | 800 | if ( nPos >= 0 ) |
2171 | 1 | { |
2172 | | // #i8026# #103991# localize decimal separator |
2173 | 1 | const OUString& rDecSep = GetLocaleData().getNumDecimalSep(); |
2174 | 1 | if ( rDecSep.getLength() > 1 || rDecSep[0] != '.' ) |
2175 | 0 | { |
2176 | 0 | sRealCond = sRealCond.replaceAt( nPos, 1, rDecSep ); |
2177 | 0 | } |
2178 | 1 | } |
2179 | 800 | m_aConditions.append("[" + sRealCond + "]"); |
2180 | 800 | } |
2181 | | |
2182 | 3.16k | const SvNumberformat* pFormat = pFormatter->GetEntry(l_nKey); |
2183 | 3.16k | if ( pFormat ) |
2184 | 3.16k | m_aConditions.append( pFormat->GetFormatstring() ); |
2185 | | |
2186 | 3.16k | m_aConditions.append( ';' ); |
2187 | 3.16k | } |
2188 | | |
2189 | | void SvXMLNumFormatContext::AddCondition( const OUString& rCondition, const OUString& rApplyName ) |
2190 | 5.05k | { |
2191 | 5.05k | MyCondition aCondition; |
2192 | 5.05k | aCondition.sCondition = rCondition; |
2193 | 5.05k | aCondition.sMapName = rApplyName; |
2194 | 5.05k | m_aMyConditions.push_back(aCondition); |
2195 | 5.05k | } |
2196 | | |
2197 | | void SvXMLNumFormatContext::AddColor( Color const nColor ) |
2198 | 1.32k | { |
2199 | 1.32k | SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter(); |
2200 | 1.32k | if (!pFormatter) |
2201 | 0 | return; |
2202 | | |
2203 | 1.32k | OUStringBuffer aColName; |
2204 | 6.68k | for ( sal_uInt16 i=0; i<XML_NUMF_COLORCOUNT; i++ ) |
2205 | 6.65k | if (nColor == aNumFmtStdColors[i]) |
2206 | 1.30k | { |
2207 | 1.30k | aColName = pFormatter->GetKeyword( m_nFormatLang, sal::static_int_cast< sal_uInt16 >(NF_KEY_FIRSTCOLOR + i) ); |
2208 | 1.30k | break; |
2209 | 1.30k | } |
2210 | | |
2211 | 1.32k | if ( !aColName.isEmpty() ) |
2212 | 1.30k | { |
2213 | 1.30k | aColName.insert( 0, '[' ); |
2214 | 1.30k | aColName.append( ']' ); |
2215 | 1.30k | m_aFormatCode.insert( 0, aColName ); |
2216 | 1.30k | } |
2217 | 1.32k | } |
2218 | | |
2219 | | void SvXMLNumFormatContext::UpdateCalendar( const OUString& rNewCalendar ) |
2220 | 3.71k | { |
2221 | 3.71k | if ( rNewCalendar == m_sCalendar ) |
2222 | 3.71k | return; |
2223 | | |
2224 | 0 | if (rNewCalendar.isEmpty() || rNewCalendar == m_aImplicitCalendar[0]) |
2225 | 0 | { |
2226 | 0 | m_eImplicitCalendar = (m_eImplicitCalendar == ImplicitCalendar::OTHER ? |
2227 | 0 | ImplicitCalendar::DEFAULT_FROM_OTHER : ImplicitCalendar::DEFAULT); |
2228 | 0 | } |
2229 | 0 | else if (m_aImplicitCalendar[0].isEmpty() && rNewCalendar == GetLocaleData().getDefaultCalendar()->Name) |
2230 | 0 | { |
2231 | 0 | m_eImplicitCalendar = (m_eImplicitCalendar == ImplicitCalendar::OTHER ? |
2232 | 0 | ImplicitCalendar::DEFAULT_FROM_OTHER : ImplicitCalendar::DEFAULT); |
2233 | 0 | m_aImplicitCalendar[0] = rNewCalendar; |
2234 | 0 | } |
2235 | 0 | else if (rNewCalendar == m_aImplicitCalendar[1]) |
2236 | 0 | { |
2237 | 0 | m_eImplicitCalendar = (m_eImplicitCalendar == ImplicitCalendar::OTHER ? |
2238 | 0 | ImplicitCalendar::SECONDARY_FROM_OTHER : ImplicitCalendar::SECONDARY); |
2239 | 0 | } |
2240 | 0 | else if (m_aImplicitCalendar[1].isEmpty() && GetLocaleData().doesSecondaryCalendarUseEC( rNewCalendar)) |
2241 | 0 | { |
2242 | 0 | m_eImplicitCalendar = (m_eImplicitCalendar == ImplicitCalendar::OTHER ? |
2243 | 0 | ImplicitCalendar::SECONDARY_FROM_OTHER : ImplicitCalendar::SECONDARY); |
2244 | 0 | m_aImplicitCalendar[1] = rNewCalendar; |
2245 | 0 | } |
2246 | 0 | else |
2247 | 0 | { |
2248 | 0 | m_eImplicitCalendar = ImplicitCalendar::OTHER; |
2249 | 0 | } |
2250 | |
|
2251 | 0 | if (m_eImplicitCalendar != ImplicitCalendar::DEFAULT && m_eImplicitCalendar != ImplicitCalendar::SECONDARY) |
2252 | 0 | { |
2253 | | // A switch from empty default calendar to named default calendar or |
2254 | | // vice versa is not a switch. |
2255 | 0 | bool bSameDefault = false; |
2256 | 0 | if (m_sCalendar.isEmpty() || rNewCalendar.isEmpty()) |
2257 | 0 | { |
2258 | | // As both are not equal, only one can be empty here, the other |
2259 | | // can not. |
2260 | 0 | const OUString& rDefaultCalendar = GetLocaleData().getDefaultCalendar()->Name; |
2261 | | // So if one is the named default calendar the other is the |
2262 | | // empty default calendar. |
2263 | 0 | bSameDefault = (rNewCalendar == rDefaultCalendar || m_sCalendar == rDefaultCalendar); |
2264 | 0 | } |
2265 | 0 | if (!bSameDefault) |
2266 | 0 | { |
2267 | 0 | m_aFormatCode.append( "[~" ); // intro for calendar code |
2268 | 0 | if (rNewCalendar.isEmpty()) |
2269 | 0 | { |
2270 | | // Empty calendar name here means switching to default calendar |
2271 | | // from a different calendar. Needs to be explicitly stated in |
2272 | | // format code. |
2273 | 0 | m_aFormatCode.append( GetLocaleData().getDefaultCalendar()->Name ); |
2274 | 0 | } |
2275 | 0 | else |
2276 | 0 | { |
2277 | 0 | m_aFormatCode.append( rNewCalendar ); |
2278 | 0 | } |
2279 | 0 | m_aFormatCode.append( ']' ); // end of calendar code |
2280 | 0 | } |
2281 | 0 | } |
2282 | 0 | m_sCalendar = rNewCalendar; |
2283 | 0 | } |
2284 | | |
2285 | | bool SvXMLNumFormatContext::IsSystemLanguage() const |
2286 | 299 | { |
2287 | 299 | return m_nFormatLang == LANGUAGE_SYSTEM; |
2288 | 299 | } |
2289 | | |
2290 | | |
2291 | | // SvXMLNumFmtHelper |
2292 | | |
2293 | | |
2294 | | SvXMLNumFmtHelper::SvXMLNumFmtHelper( |
2295 | | const uno::Reference<util::XNumberFormatsSupplier>& rSupp ) |
2296 | 10.8k | { |
2297 | 10.8k | SvNumberFormatter* pFormatter = nullptr; |
2298 | 10.8k | SvNumberFormatsSupplierObj* pObj = |
2299 | 10.8k | comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>( rSupp ); |
2300 | 10.8k | if (pObj) |
2301 | 10.8k | pFormatter = pObj->GetNumberFormatter(); |
2302 | | |
2303 | 10.8k | m_pData = std::make_unique<SvXMLNumImpData>( pFormatter ); |
2304 | 10.8k | } |
2305 | | |
2306 | | SvXMLNumFmtHelper::SvXMLNumFmtHelper( SvNumberFormatter* pNumberFormatter ) |
2307 | 4.26k | { |
2308 | 4.26k | m_pData = std::make_unique<SvXMLNumImpData>( pNumberFormatter ); |
2309 | 4.26k | } |
2310 | | |
2311 | | SvXMLNumFmtHelper::~SvXMLNumFmtHelper() |
2312 | 15.0k | { |
2313 | | // remove temporary (volatile) formats from NumberFormatter |
2314 | 15.0k | m_pData->RemoveVolatileFormats(); |
2315 | 15.0k | } |
2316 | | |
2317 | | |
2318 | | SvXMLStyleContext* SvXMLNumFmtHelper::CreateChildContext( SvXMLImport& rImport, |
2319 | | sal_Int32 nElement, |
2320 | | const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList, |
2321 | | SvXMLStylesContext& rStyles ) |
2322 | 158k | { |
2323 | 158k | SvXMLStylesTokens nStyleToken; |
2324 | 158k | switch (nElement) |
2325 | 158k | { |
2326 | 7.41k | case XML_ELEMENT(NUMBER, XML_NUMBER_STYLE): |
2327 | 7.41k | nStyleToken = SvXMLStylesTokens::NUMBER_STYLE; |
2328 | 7.41k | break; |
2329 | 2.76k | case XML_ELEMENT(NUMBER, XML_CURRENCY_STYLE): |
2330 | 2.76k | nStyleToken = SvXMLStylesTokens::CURRENCY_STYLE; |
2331 | 2.76k | break; |
2332 | 19 | case XML_ELEMENT(NUMBER, XML_PERCENTAGE_STYLE): |
2333 | 19 | nStyleToken = SvXMLStylesTokens::PERCENTAGE_STYLE; |
2334 | 19 | break; |
2335 | 1.36k | case XML_ELEMENT(NUMBER, XML_DATE_STYLE): |
2336 | 1.36k | nStyleToken = SvXMLStylesTokens::DATE_STYLE; |
2337 | 1.36k | break; |
2338 | 2.43k | case XML_ELEMENT(NUMBER, XML_TIME_STYLE): |
2339 | 2.43k | nStyleToken = SvXMLStylesTokens::TIME_STYLE; |
2340 | 2.43k | break; |
2341 | 67 | case XML_ELEMENT(NUMBER, XML_BOOLEAN_STYLE): |
2342 | 67 | nStyleToken = SvXMLStylesTokens::BOOLEAN_STYLE; |
2343 | 67 | break; |
2344 | 1.40k | case XML_ELEMENT(NUMBER, XML_TEXT_STYLE): |
2345 | 1.40k | nStyleToken = SvXMLStylesTokens::TEXT_STYLE; |
2346 | 1.40k | break; |
2347 | 142k | default: |
2348 | | // return NULL if not a data style, caller must handle other elements |
2349 | 142k | return nullptr; |
2350 | 158k | } |
2351 | 15.4k | return new SvXMLNumFormatContext( rImport, nElement, |
2352 | 15.4k | m_pData.get(), nStyleToken, xAttrList, rStyles ); |
2353 | 158k | } |
2354 | | |
2355 | | LanguageType SvXMLNumFmtHelper::GetLanguageForKey(sal_Int32 nKey) const |
2356 | 5.93k | { |
2357 | 5.93k | if (m_pData->GetNumberFormatter()) |
2358 | 5.93k | { |
2359 | 5.93k | const SvNumberformat* pEntry = m_pData->GetNumberFormatter()->GetEntry(nKey); |
2360 | 5.93k | if (pEntry) |
2361 | 5.93k | return pEntry->GetLanguage(); |
2362 | 5.93k | } |
2363 | | |
2364 | 0 | return LANGUAGE_SYSTEM; |
2365 | 5.93k | } |
2366 | | |
2367 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |