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