Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
cfgas_stringformatter.cpp
Go to the documentation of this file.
1// Copyright 2014 The PDFium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7#include "xfa/fgas/crt/cfgas_stringformatter.h"
8
9#include <math.h>
10
11#include <algorithm>
12#include <limits>
13#include <utility>
14#include <vector>
15
16#include "core/fxcrt/cfx_datetime.h"
17#include "core/fxcrt/fx_extension.h"
18#include "core/fxcrt/fx_safe_types.h"
19#include "third_party/base/containers/contains.h"
20#include "third_party/base/notreached.h"
21#include "xfa/fgas/crt/cfgas_decimal.h"
22#include "xfa/fgas/crt/locale_mgr_iface.h"
23
24// NOTE: Code uses the convention for backwards-looping with unsigned types
25// that exploits the well-defined behaviour for unsigned underflow (and hence
26// the standard x < size() can be used in all cases to validate indices).
27
28#define FX_NUMSTYLE_Percent 0x01
29#define FX_NUMSTYLE_Exponent 0x02
30#define FX_NUMSTYLE_DotVorv 0x04
31
32namespace {
33
34struct LocaleDateTimeSubcategoryWithHash {
35 uint32_t uHash; // Hashed as wide string.
36 LocaleIface::DateTimeSubcategory eSubCategory;
37};
38
39struct LocaleNumberSubcategoryWithHash {
40 uint32_t uHash; // Hashed as wide string.
41 LocaleIface::NumSubcategory eSubCategory;
42};
43
44#undef SUBC
45#define SUBC(a, b, c) a, c
46constexpr LocaleDateTimeSubcategoryWithHash kLocaleDateTimeSubcategoryData[] = {
47 {SUBC(0x14da2125, "default", LocaleIface::DateTimeSubcategory::kDefault)},
48 {SUBC(0x9041d4b0, "short", LocaleIface::DateTimeSubcategory::kShort)},
49 {SUBC(0xa084a381, "medium", LocaleIface::DateTimeSubcategory::kMedium)},
50 {SUBC(0xcdce56b3, "full", LocaleIface::DateTimeSubcategory::kFull)},
51 {SUBC(0xf6b4afb0, "long", LocaleIface::DateTimeSubcategory::kLong)},
52};
53
54constexpr LocaleNumberSubcategoryWithHash kLocaleNumSubcategoryData[] = {
55 {SUBC(0x46f95531, "percent", LocaleIface::NumSubcategory::kPercent)},
56 {SUBC(0x4c4e8acb, "currency", LocaleIface::NumSubcategory::kCurrency)},
57 {SUBC(0x54034c2f, "decimal", LocaleIface::NumSubcategory::kDecimal)},
58 {SUBC(0x7568e6ae, "integer", LocaleIface::NumSubcategory::kInteger)},
59};
60#undef SUBC
61
62struct FX_LOCALETIMEZONEINFO {
63 const wchar_t* name;
64 int16_t iHour;
65 int16_t iMinute;
66};
67
68constexpr FX_LOCALETIMEZONEINFO kFXLocaleTimeZoneData[] = {
69 {L"CDT", -5, 0}, {L"CST", -6, 0}, {L"EDT", -4, 0}, {L"EST", -5, 0},
70 {L"MDT", -6, 0}, {L"MST", -7, 0}, {L"PDT", -7, 0}, {L"PST", -8, 0},
71};
72
73constexpr wchar_t kTimeSymbols[] = L"hHkKMSFAzZ";
74constexpr wchar_t kDateSymbols[] = L"DJMEeGgYwW";
75constexpr wchar_t kConstChars[] = L",-:/. ";
76
77constexpr wchar_t kDateStr[] = L"date";
78constexpr wchar_t kTimeStr[] = L"time";
79constexpr wchar_t kDateTimeStr[] = L"datetime";
80constexpr wchar_t kNumStr[] = L"num";
81constexpr wchar_t kTextStr[] = L"text";
82constexpr wchar_t kZeroStr[] = L"zero";
83constexpr wchar_t kNullStr[] = L"null";
84
85size_t ParseTimeZone(pdfium::span<const wchar_t> spStr, int* tz) {
86 *tz = 0;
87 if (spStr.empty())
88 return 0;
89
90 // Keep index by 0 close to empty() check above for optimizer's sake.
91 const bool bNegative = (spStr[0] == '-');
92
93 size_t iStart = 1;
94 size_t iEnd = iStart + 2;
95 int tz_hour = 0;
96 while (iStart < spStr.size() && iStart < iEnd)
97 tz_hour = tz_hour * 10 + FXSYS_DecimalCharToInt(spStr[iStart++]);
98
99 if (iStart < spStr.size() && spStr[iStart] == ':')
100 iStart++;
101
102 iEnd = iStart + 2;
103 int tz_minute = 0;
104 while (iStart < spStr.size() && iStart < iEnd)
105 tz_minute = tz_minute * 10 + FXSYS_DecimalCharToInt(spStr[iStart++]);
106
107 *tz = tz_hour * 60 + tz_minute;
108 if (bNegative)
109 *tz *= -1;
110
111 return iStart;
112}
113
114int32_t ConvertHex(int32_t iKeyValue, wchar_t ch) {
115 if (FXSYS_IsHexDigit(ch))
116 return iKeyValue * 16 + FXSYS_HexCharToInt(ch);
117 return iKeyValue;
118}
119
120WideString GetLiteralText(pdfium::span<const wchar_t> spStrPattern,
121 size_t* iPattern) {
122 WideString wsOutput;
123 if (*iPattern >= spStrPattern.size() || spStrPattern[*iPattern] != '\'')
124 return wsOutput;
125
126 (*iPattern)++;
127 int32_t iQuote = 1;
128 while (*iPattern < spStrPattern.size()) {
129 if (spStrPattern[*iPattern] == '\'') {
130 iQuote++;
131 if ((*iPattern + 1 >= spStrPattern.size()) ||
132 ((spStrPattern[*iPattern + 1] != '\'') && (iQuote % 2 == 0))) {
133 break;
134 }
135 iQuote++;
136 (*iPattern)++;
137 } else if (spStrPattern[*iPattern] == '\\' &&
138 (*iPattern + 1 < spStrPattern.size()) &&
139 spStrPattern[*iPattern + 1] == 'u') {
140 int32_t iKeyValue = 0;
141 *iPattern += 2;
142 for (int32_t i = 0; *iPattern < spStrPattern.size() && i < 4; ++i) {
143 wchar_t ch = spStrPattern[(*iPattern)++];
144 iKeyValue = ConvertHex(iKeyValue, ch);
145 }
146 if (iKeyValue != 0)
147 wsOutput += static_cast<wchar_t>(iKeyValue & 0x0000FFFF);
148
149 continue;
150 }
151 wsOutput += spStrPattern[(*iPattern)++];
152 }
153 return wsOutput;
154}
155
156WideString GetLiteralTextReverse(pdfium::span<const wchar_t> spStrPattern,
157 size_t* iPattern) {
158 WideString wsOutput;
159 if (*iPattern >= spStrPattern.size() || spStrPattern[*iPattern] != '\'')
160 return wsOutput;
161
162 (*iPattern)--;
163 int32_t iQuote = 1;
164
165 while (*iPattern < spStrPattern.size()) {
166 if (spStrPattern[*iPattern] == '\'') {
167 iQuote++;
168 if (*iPattern - 1 >= spStrPattern.size() ||
169 ((spStrPattern[*iPattern - 1] != '\'') && (iQuote % 2 == 0))) {
170 break;
171 }
172 iQuote++;
173 (*iPattern)--;
174 } else if (spStrPattern[*iPattern] == '\\' &&
175 *iPattern + 1 < spStrPattern.size() &&
176 spStrPattern[*iPattern + 1] == 'u') {
177 (*iPattern)--;
178 int32_t iKeyValue = 0;
179 size_t iLen = std::min<size_t>(wsOutput.GetLength(), 5);
180 size_t i = 1;
181 for (; i < iLen; i++) {
182 wchar_t ch = wsOutput[i];
183 iKeyValue = ConvertHex(iKeyValue, ch);
184 }
185 if (iKeyValue != 0) {
186 wsOutput.Delete(0, i);
187 wsOutput = (wchar_t)(iKeyValue & 0x0000FFFF) + wsOutput;
188 }
189 continue;
190 }
191 wsOutput = spStrPattern[(*iPattern)--] + wsOutput;
192 }
193 return wsOutput;
194}
195
196bool GetNumericDotIndex(const WideString& wsNum,
197 const WideString& wsDotSymbol,
198 size_t* iDotIndex) {
199 pdfium::span<const wchar_t> spNum = wsNum.span();
200 pdfium::span<const wchar_t> spDotSymbol = wsDotSymbol.span();
201 for (size_t ccf = 0; ccf < spNum.size(); ++ccf) {
202 if (spNum[ccf] == '\'') {
203 GetLiteralText(spNum, &ccf);
204 continue;
205 }
206 if (ccf + spDotSymbol.size() <= spNum.size() &&
207 wcsncmp(&spNum[ccf], spDotSymbol.data(), spDotSymbol.size()) == 0) {
208 *iDotIndex = ccf;
209 return true;
210 }
211 }
212 auto result = wsNum.Find('.');
213 *iDotIndex = result.value_or(spNum.size());
214 return result.has_value();
215}
216
217bool ExtractCountDigits(pdfium::span<const wchar_t> spStr,
218 size_t count,
219 size_t* cc,
220 uint32_t* value) {
221 for (size_t i = 0; i < count; ++i) {
222 if (*cc >= spStr.size() || !FXSYS_IsDecimalDigit(spStr[*cc]))
223 return false;
224 *value = *value * 10 + FXSYS_DecimalCharToInt(spStr[(*cc)++]);
225 }
226 return true;
227}
228
229bool ExtractCountDigitsWithOptional(pdfium::span<const wchar_t> spStr,
230 int count,
231 size_t* cc,
232 uint32_t* value) {
233 if (!ExtractCountDigits(spStr, count, cc, value))
234 return false;
235 ExtractCountDigits(spStr, 1, cc, value);
236 return true;
237}
238
239bool ParseLocaleDate(const WideString& wsDate,
240 const WideString& wsDatePattern,
241 LocaleIface* pLocale,
242 CFX_DateTime* datetime,
243 size_t* cc) {
244 uint32_t year = 1900;
245 uint32_t month = 1;
246 uint32_t day = 1;
247 size_t ccf = 0;
248 pdfium::span<const wchar_t> spDate = wsDate.span();
249 pdfium::span<const wchar_t> spDatePattern = wsDatePattern.span();
250 while (*cc < spDate.size() && ccf < spDatePattern.size()) {
251 if (spDatePattern[ccf] == '\'') {
252 WideString wsLiteral = GetLiteralText(spDatePattern, &ccf);
253 size_t iLiteralLen = wsLiteral.GetLength();
254 if (*cc + iLiteralLen > spDate.size() ||
255 wcsncmp(spDate.data() + *cc, wsLiteral.c_str(), iLiteralLen) != 0) {
256 return false;
257 }
258 *cc += iLiteralLen;
259 ccf++;
260 continue;
261 }
262 if (!pdfium::Contains(kDateSymbols, spDatePattern[ccf])) {
263 if (spDatePattern[ccf] != spDate[*cc])
264 return false;
265 (*cc)++;
266 ccf++;
267 continue;
268 }
269
270 WideString symbol;
271 symbol.Reserve(4);
272 symbol += spDatePattern[ccf++];
273 while (ccf < spDatePattern.size() && spDatePattern[ccf] == symbol[0]) {
274 symbol += spDatePattern[ccf++];
275 }
276 if (symbol.EqualsASCII("D") || symbol.EqualsASCII("DD")) {
277 day = 0;
278 if (!ExtractCountDigitsWithOptional(spDate, 1, cc, &day))
279 return false;
280 } else if (symbol.EqualsASCII("J")) {
281 uint32_t val = 0;
282 ExtractCountDigits(spDate, 3, cc, &val);
283 } else if (symbol.EqualsASCII("M") || symbol.EqualsASCII("MM")) {
284 month = 0;
285 if (!ExtractCountDigitsWithOptional(spDate, 1, cc, &month))
286 return false;
287 } else if (symbol.EqualsASCII("MMM") || symbol.EqualsASCII("MMMM")) {
288 for (uint16_t i = 0; i < 12; i++) {
289 WideString wsMonthName =
290 pLocale->GetMonthName(i, symbol.EqualsASCII("MMM"));
291 if (wsMonthName.IsEmpty())
292 continue;
293 if (wcsncmp(wsMonthName.c_str(), spDate.data() + *cc,
294 wsMonthName.GetLength()) == 0) {
295 *cc += wsMonthName.GetLength();
296 month = i + 1;
297 break;
298 }
299 }
300 } else if (symbol.EqualsASCII("EEE") || symbol.EqualsASCII("EEEE")) {
301 for (uint16_t i = 0; i < 7; i++) {
302 WideString wsDayName =
303 pLocale->GetDayName(i, symbol.EqualsASCII("EEE"));
304 if (wsDayName.IsEmpty())
305 continue;
306 if (wcsncmp(wsDayName.c_str(), spDate.data() + *cc,
307 wsDayName.GetLength()) == 0) {
308 *cc += wsDayName.GetLength();
309 break;
310 }
311 }
312 } else if (symbol.EqualsASCII("YY") || symbol.EqualsASCII("YYYY")) {
313 if (*cc + symbol.GetLength() > spDate.size())
314 return false;
315
316 year = 0;
317 if (!ExtractCountDigits(spDate, symbol.GetLength(), cc, &year))
318 return false;
319 if (symbol.EqualsASCII("YY")) {
320 if (year <= 29)
321 year += 2000;
322 else
323 year += 1900;
324 }
325 } else if (symbol.EqualsASCII("G")) {
326 *cc += 2;
327 } else if (symbol.EqualsASCII("JJJ") || symbol.EqualsASCIINoCase("E") ||
328 symbol.EqualsASCII("w") || symbol.EqualsASCII("WW")) {
329 *cc += symbol.GetLength();
330 }
331 }
332 if (*cc < spDate.size())
333 return false;
334
335 datetime->SetDate(year, month, day);
336 return !!(*cc);
337}
338
339void ResolveZone(int tz_diff_minutes,
340 const LocaleIface* pLocale,
341 uint32_t* wHour,
342 uint32_t* wMinute) {
343 int32_t iMinuteDiff = *wHour * 60 + *wMinute;
344 iMinuteDiff += pLocale->GetTimeZoneInMinutes();
345 iMinuteDiff -= tz_diff_minutes;
346
347 iMinuteDiff %= 1440;
348 if (iMinuteDiff < 0)
349 iMinuteDiff += 1440;
350
351 *wHour = iMinuteDiff / 60;
352 *wMinute = iMinuteDiff % 60;
353}
354
355bool ParseLocaleTime(const WideString& wsTime,
356 const WideString& wsTimePattern,
357 LocaleIface* pLocale,
358 CFX_DateTime* datetime,
359 size_t* cc) {
360 uint32_t hour = 0;
361 uint32_t minute = 0;
362 uint32_t second = 0;
363 uint32_t millisecond = 0;
364 size_t ccf = 0;
365 pdfium::span<const wchar_t> spTime = wsTime.span();
366 pdfium::span<const wchar_t> spTimePattern = wsTimePattern.span();
367 bool bHasA = false;
368 bool bPM = false;
369 while (*cc < spTime.size() && ccf < spTimePattern.size()) {
370 if (spTimePattern[ccf] == '\'') {
371 WideString wsLiteral = GetLiteralText(spTimePattern, &ccf);
372 size_t iLiteralLen = wsLiteral.GetLength();
373 if (*cc + iLiteralLen > spTime.size() ||
374 wcsncmp(spTime.data() + *cc, wsLiteral.c_str(), iLiteralLen) != 0) {
375 return false;
376 }
377 *cc += iLiteralLen;
378 ccf++;
379 continue;
380 }
381 if (!pdfium::Contains(kTimeSymbols, spTimePattern[ccf])) {
382 if (spTimePattern[ccf] != spTime[*cc])
383 return false;
384 (*cc)++;
385 ccf++;
386 continue;
387 }
388
389 WideString symbol;
390 symbol.Reserve(4);
391 symbol += spTimePattern[ccf++];
392 while (ccf < spTimePattern.size() && spTimePattern[ccf] == symbol[0])
393 symbol += spTimePattern[ccf++];
394
395 if (symbol.EqualsASCIINoCase("k") || symbol.EqualsASCIINoCase("h")) {
396 hour = 0;
397 if (!ExtractCountDigitsWithOptional(spTime, 1, cc, &hour))
398 return false;
399 if (symbol.EqualsASCII("K") && hour == 24)
400 hour = 0;
401 } else if (symbol.EqualsASCIINoCase("kk") ||
402 symbol.EqualsASCIINoCase("hh")) {
403 hour = 0;
404 if (!ExtractCountDigits(spTime, 2, cc, &hour))
405 return false;
406 if (symbol.EqualsASCII("KK") && hour == 24)
407 hour = 0;
408 } else if (symbol.EqualsASCII("M")) {
409 minute = 0;
410 if (!ExtractCountDigitsWithOptional(spTime, 1, cc, &minute))
411 return false;
412 } else if (symbol.EqualsASCII("MM")) {
413 minute = 0;
414 if (!ExtractCountDigits(spTime, 2, cc, &minute))
415 return false;
416 } else if (symbol.EqualsASCII("S")) {
417 second = 0;
418 if (!ExtractCountDigitsWithOptional(spTime, 1, cc, &second))
419 return false;
420 } else if (symbol.EqualsASCII("SS")) {
421 second = 0;
422 if (!ExtractCountDigits(spTime, 2, cc, &second))
423 return false;
424 } else if (symbol.EqualsASCII("FFF")) {
425 millisecond = 0;
426 if (!ExtractCountDigits(spTime, 3, cc, &millisecond))
427 return false;
428 } else if (symbol.EqualsASCII("A")) {
429 WideString wsAM = pLocale->GetMeridiemName(true);
430 WideString wsPM = pLocale->GetMeridiemName(false);
431 if (*cc + wsAM.GetLength() <= spTime.size() &&
432 WideStringView(spTime.data() + *cc, wsAM.GetLength()) == wsAM) {
433 *cc += wsAM.GetLength();
434 bHasA = true;
435 } else if (*cc + wsPM.GetLength() <= spTime.size() &&
436 WideStringView(spTime.data() + *cc, wsPM.GetLength()) ==
437 wsPM) {
438 *cc += wsPM.GetLength();
439 bHasA = true;
440 bPM = true;
441 }
442 } else if (symbol.EqualsASCII("Z")) {
443 if (*cc + 3 > spTime.size())
444 continue;
445
446 WideString tz(spTime[(*cc)++]);
447 tz += spTime[(*cc)++];
448 tz += spTime[(*cc)++];
449 if (tz.EqualsASCII("GMT")) {
450 int tz_diff_minutes = 0;
451 if (*cc < spTime.size() && (spTime[*cc] == '-' || spTime[*cc] == '+'))
452 *cc += ParseTimeZone(spTime.subspan(*cc), &tz_diff_minutes);
453 ResolveZone(tz_diff_minutes, pLocale, &hour, &minute);
454 } else {
455 // Search the timezone list. There are only 8 of them, so linear scan.
456 for (size_t i = 0; i < std::size(kFXLocaleTimeZoneData); ++i) {
457 const FX_LOCALETIMEZONEINFO& info = kFXLocaleTimeZoneData[i];
458 if (tz != info.name)
459 continue;
460
461 hour += info.iHour;
462 minute += info.iHour > 0 ? info.iMinute : -info.iMinute;
463 break;
464 }
465 }
466 } else if (symbol.EqualsASCII("z")) {
467 if (spTime[*cc] != 'Z') {
468 int tz_diff_minutes = 0;
469 *cc += ParseTimeZone(spTime.subspan(*cc), &tz_diff_minutes);
470 ResolveZone(tz_diff_minutes, pLocale, &hour, &minute);
471 } else {
472 (*cc)++;
473 }
474 }
475 }
476 if (bHasA) {
477 if (bPM) {
478 hour += 12;
479 if (hour == 24)
480 hour = 12;
481 } else {
482 if (hour == 12)
483 hour = 0;
484 }
485 }
486 datetime->SetTime(hour, minute, second, millisecond);
487 return !!(*cc);
488}
489
490size_t GetNumTrailingLimit(const WideString& wsFormat,
491 size_t iDotPos,
492 bool* bTrimTailZeros) {
493 const pdfium::span<const wchar_t> spFormat = wsFormat.span();
494 size_t iTrailing = 0;
495 for (++iDotPos; iDotPos < spFormat.size(); ++iDotPos) {
496 wchar_t wc = spFormat[iDotPos];
497 if (wc == L'z' || wc == L'9' || wc == 'Z') {
498 iTrailing++;
499 *bTrimTailZeros = wc != L'9';
500 }
501 }
502 return iTrailing;
503}
504
505bool IsLeapYear(uint32_t year) {
506 return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
507}
508
509bool MonthHas30Days(uint32_t month) {
510 return month == 4 || month == 6 || month == 9 || month == 11;
511}
512
513bool MonthHas31Days(uint32_t month) {
514 return month != 2 && !MonthHas30Days(month);
515}
516
517// |month| is 1-based. e.g. 1 means January.
518uint16_t GetSolarMonthDays(uint16_t year, uint16_t month) {
519 if (month == 2)
520 return FX_IsLeapYear(year) ? 29 : 28;
521
522 return MonthHas30Days(month) ? 30 : 31;
523}
524
525uint16_t GetWeekDay(uint16_t year, uint16_t month, uint16_t day) {
526 static const uint8_t kMonthDay[] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
527 uint16_t nDays =
528 (year - 1) % 7 + (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400;
529 nDays += kMonthDay[month - 1] + day;
530 if (FX_IsLeapYear(year) && month > 2)
531 nDays++;
532 return nDays % 7;
533}
534
535uint16_t GetWeekOfMonth(uint16_t year, uint16_t month, uint16_t day) {
536 uint16_t week_day = GetWeekDay(year, month, 1);
537 uint16_t week_index = 0;
538 week_index += day / 7;
539 day = day % 7;
540 if (week_day + day > 7)
541 week_index++;
542 return week_index;
543}
544
545uint16_t GetWeekOfYear(uint16_t year, uint16_t month, uint16_t day) {
546 uint16_t nDays = 0;
547 for (uint16_t i = 1; i < month; i++)
548 nDays += GetSolarMonthDays(year, i);
549
550 nDays += day;
551 uint16_t week_day = GetWeekDay(year, 1, 1);
552 uint16_t week_index = 1;
553 week_index += nDays / 7;
554 nDays = nDays % 7;
555 if (week_day + nDays > 7)
556 week_index++;
557 return week_index;
558}
559
560WideString NumToString(size_t fmt_size, int32_t value) {
561 return WideString::Format(
562 fmt_size == 1 ? L"%d" : fmt_size == 2 ? L"%02d" : L"%03d", value);
563}
564
565WideString DateFormat(const WideString& wsDatePattern,
566 LocaleIface* pLocale,
567 const CFX_DateTime& datetime) {
568 WideString wsResult;
569 int32_t year = datetime.GetYear();
570 uint8_t month = datetime.GetMonth();
571 uint8_t day = datetime.GetDay();
572 size_t ccf = 0;
573 pdfium::span<const wchar_t> spDatePattern = wsDatePattern.span();
574 while (ccf < spDatePattern.size()) {
575 if (spDatePattern[ccf] == '\'') {
576 wsResult += GetLiteralText(spDatePattern, &ccf);
577 ccf++;
578 continue;
579 }
580 if (!pdfium::Contains(kDateSymbols, spDatePattern[ccf])) {
581 wsResult += spDatePattern[ccf++];
582 continue;
583 }
584 WideString symbol;
585 symbol.Reserve(4);
586 symbol += spDatePattern[ccf++];
587 while (ccf < spDatePattern.size() && spDatePattern[ccf] == symbol[0])
588 symbol += spDatePattern[ccf++];
589
590 if (symbol.EqualsASCII("D") || symbol.EqualsASCII("DD")) {
591 wsResult += NumToString(symbol.GetLength(), day);
592 } else if (symbol.EqualsASCII("J") || symbol.EqualsASCII("JJJ")) {
593 uint16_t nDays = 0;
594 for (int i = 1; i < month; i++)
595 nDays += GetSolarMonthDays(year, i);
596 nDays += day;
597 wsResult += NumToString(symbol.GetLength(), nDays);
598 } else if (symbol.EqualsASCII("M") || symbol.EqualsASCII("MM")) {
599 wsResult += NumToString(symbol.GetLength(), month);
600 } else if (symbol.EqualsASCII("MMM") || symbol.EqualsASCII("MMMM")) {
601 wsResult += pLocale->GetMonthName(month - 1, symbol.EqualsASCII("MMM"));
602 } else if (symbol.EqualsASCIINoCase("e")) {
603 uint16_t wWeekDay = GetWeekDay(year, month, day);
604 wsResult +=
605 NumToString(1, symbol.EqualsASCII("E") ? wWeekDay + 1
606 : (wWeekDay ? wWeekDay : 7));
607 } else if (symbol.EqualsASCII("EEE") || symbol.EqualsASCII("EEEE")) {
608 wsResult += pLocale->GetDayName(GetWeekDay(year, month, day),
609 symbol.EqualsASCII("EEE"));
610 } else if (symbol.EqualsASCII("G")) {
611 wsResult += pLocale->GetEraName(year > 0);
612 } else if (symbol.EqualsASCII("YY")) {
613 wsResult += NumToString(2, year % 100);
614 } else if (symbol.EqualsASCII("YYYY")) {
615 wsResult += NumToString(1, year);
616 } else if (symbol.EqualsASCII("w")) {
617 wsResult += NumToString(1, GetWeekOfMonth(year, month, day));
618 } else if (symbol.EqualsASCII("WW")) {
619 wsResult += NumToString(2, GetWeekOfYear(year, month, day));
620 }
621 }
622 return wsResult;
623}
624
625WideString TimeFormat(const WideString& wsTimePattern,
626 LocaleIface* pLocale,
627 const CFX_DateTime& datetime) {
628 WideString wsResult;
629 uint8_t hour = datetime.GetHour();
630 uint8_t minute = datetime.GetMinute();
631 uint8_t second = datetime.GetSecond();
632 uint16_t millisecond = datetime.GetMillisecond();
633 size_t ccf = 0;
634 pdfium::span<const wchar_t> spTimePattern = wsTimePattern.span();
635 uint16_t wHour = hour;
636 bool bPM = false;
637 if (wsTimePattern.Contains('A')) {
638 if (wHour >= 12)
639 bPM = true;
640 }
641
642 while (ccf < spTimePattern.size()) {
643 if (spTimePattern[ccf] == '\'') {
644 wsResult += GetLiteralText(spTimePattern, &ccf);
645 ccf++;
646 continue;
647 }
648 if (!pdfium::Contains(kTimeSymbols, spTimePattern[ccf])) {
649 wsResult += spTimePattern[ccf++];
650 continue;
651 }
652
653 WideString symbol;
654 symbol.Reserve(4);
655 symbol += spTimePattern[ccf++];
656 while (ccf < spTimePattern.size() && spTimePattern[ccf] == symbol[0])
657 symbol += spTimePattern[ccf++];
658
659 if (symbol.EqualsASCII("h") || symbol.EqualsASCII("hh")) {
660 if (wHour > 12)
661 wHour -= 12;
662 wsResult += NumToString(symbol.GetLength(), wHour == 0 ? 12 : wHour);
663 } else if (symbol.EqualsASCII("K") || symbol.EqualsASCII("KK")) {
664 wsResult += NumToString(symbol.GetLength(), wHour == 0 ? 24 : wHour);
665 } else if (symbol.EqualsASCII("k") || symbol.EqualsASCII("kk")) {
666 if (wHour > 12)
667 wHour -= 12;
668 wsResult += NumToString(symbol.GetLength(), wHour);
669 } else if (symbol.EqualsASCII("H") || symbol.EqualsASCII("HH")) {
670 wsResult += NumToString(symbol.GetLength(), wHour);
671 } else if (symbol.EqualsASCII("M") || symbol.EqualsASCII("MM")) {
672 wsResult += NumToString(symbol.GetLength(), minute);
673 } else if (symbol.EqualsASCII("S") || symbol.EqualsASCII("SS")) {
674 wsResult += NumToString(symbol.GetLength(), second);
675 } else if (symbol.EqualsASCII("FFF")) {
676 wsResult += NumToString(3, millisecond);
677 } else if (symbol.EqualsASCII("A")) {
678 wsResult += pLocale->GetMeridiemName(!bPM);
679 } else if (symbol.EqualsASCIINoCase("z")) {
680 if (symbol.EqualsASCII("Z"))
681 wsResult += L"GMT";
682 int tz_minutes = pLocale->GetTimeZoneInMinutes();
683 if (tz_minutes != 0) {
684 wsResult += tz_minutes < 0 ? L"-" : L"+";
685 int abs_tz_minutes = abs(tz_minutes);
686 wsResult += WideString::Format(L"%02d:%02d", abs_tz_minutes / 60,
687 abs_tz_minutes % 60);
688 }
689 }
690 }
691 return wsResult;
692}
693
694WideString FormatDateTimeInternal(const CFX_DateTime& dt,
695 const WideString& wsDatePattern,
696 const WideString& wsTimePattern,
697 bool bDateFirst,
698 LocaleIface* pLocale) {
699 WideString wsDateOut;
700 if (!wsDatePattern.IsEmpty())
701 wsDateOut = DateFormat(wsDatePattern, pLocale, dt);
702
703 WideString wsTimeOut;
704 if (!wsTimePattern.IsEmpty())
705 wsTimeOut = TimeFormat(wsTimePattern, pLocale, dt);
706
707 return bDateFirst ? wsDateOut + wsTimeOut : wsTimeOut + wsDateOut;
708}
709
710bool HasDate(CFGAS_StringFormatter::DateTimeType type) {
714}
715
716bool HasTime(CFGAS_StringFormatter::DateTimeType type) {
720}
721
722CFGAS_StringFormatter::DateTimeType AddDateToDatelessType(
724 switch (type) {
729 default:
730 NOTREACHED_NORETURN();
731 }
732}
733
734CFGAS_StringFormatter::DateTimeType AddTimeToTimelessType(
736 switch (type) {
741 default:
742 NOTREACHED_NORETURN();
743 }
744}
745
746} // namespace
747
748bool FX_DateFromCanonical(pdfium::span<const wchar_t> spDate,
749 CFX_DateTime* datetime) {
750 if (spDate.size() > 10)
751 return false;
752
753 size_t cc = 0;
754 uint32_t year = 0;
755 if (!ExtractCountDigits(spDate, 4, &cc, &year))
756 return false;
757 if (year < 1900)
758 return false;
759 if (cc >= spDate.size()) {
760 datetime->SetDate(year, 1, 1);
761 return true;
762 }
763
764 if (spDate[cc] == '-')
765 cc++;
766
767 uint32_t month = 0;
768 if (!ExtractCountDigits(spDate, 2, &cc, &month) || month < 1 || month > 12)
769 return false;
770
771 if (cc >= spDate.size()) {
772 datetime->SetDate(year, month, 1);
773 return true;
774 }
775
776 if (spDate[cc] == '-')
777 cc++;
778
779 uint32_t day = 0;
780 if (!ExtractCountDigits(spDate, 2, &cc, &day))
781 return false;
782 if (day < 1)
783 return false;
784 if ((MonthHas31Days(month) && day > 31) ||
785 (MonthHas30Days(month) && day > 30)) {
786 return false;
787 }
788 if (month == 2 && day > (IsLeapYear(year) ? 29U : 28U))
789 return false;
790
791 datetime->SetDate(year, month, day);
792 return true;
793}
794
796 pdfium::span<const wchar_t> spTime,
797 CFX_DateTime* datetime) {
798 if (spTime.empty())
799 return false;
800
801 size_t cc = 0;
802 uint32_t hour = 0;
803 if (!ExtractCountDigits(spTime, 2, &cc, &hour) || hour >= 24)
804 return false;
805
806 if (cc >= spTime.size()) {
807 datetime->SetTime(hour, 0, 0, 0);
808 return true;
809 }
810
811 if (spTime[cc] == ':')
812 cc++;
813
814 uint32_t minute = 0;
815 if (!ExtractCountDigits(spTime, 2, &cc, &minute) || minute >= 60)
816 return false;
817
818 if (cc >= spTime.size()) {
819 datetime->SetTime(hour, minute, 0, 0);
820 return true;
821 }
822
823 if (spTime[cc] == ':')
824 cc++;
825
826 uint32_t second = 0;
827 uint32_t millisecond = 0;
828 if (cc < spTime.size() && spTime[cc] != 'Z') {
829 if (!ExtractCountDigits(spTime, 2, &cc, &second) || second >= 60)
830 return false;
831
832 if (cc < spTime.size() && spTime[cc] == '.') {
833 cc++;
834 if (!ExtractCountDigits(spTime, 3, &cc, &millisecond))
835 return false;
836 }
837 }
838
839 // Skip until we find a + or - for the time zone.
840 while (cc < spTime.size()) {
841 if (spTime[cc] == '+' || spTime[cc] == '-')
842 break;
843 ++cc;
844 }
845
846 if (cc < spTime.size()) {
847 int tz_diff_minutes = 0;
848 if (spTime[cc] != 'Z')
849 cc += ParseTimeZone(spTime.subspan(cc), &tz_diff_minutes);
850 ResolveZone(tz_diff_minutes, pLocale, &hour, &minute);
851 }
852
853 datetime->SetTime(hour, minute, second, millisecond);
854 return true;
855}
856
859
861
862// static
863std::vector<WideString> CFGAS_StringFormatter::SplitOnBars(
864 const WideString& wsFormatString) {
865 std::vector<WideString> wsPatterns;
866 pdfium::span<const wchar_t> spFormatString = wsFormatString.span();
867 size_t index = 0;
868 size_t token = 0;
869 bool bQuote = false;
870 for (; index < spFormatString.size(); ++index) {
871 if (spFormatString[index] == '\'') {
872 bQuote = !bQuote;
873 } else if (spFormatString[index] == L'|' && !bQuote) {
874 wsPatterns.emplace_back(spFormatString.data() + token, index - token);
875 token = index + 1;
876 }
877 }
878 wsPatterns.emplace_back(spFormatString.data() + token, index - token);
879 return wsPatterns;
880}
881
883 Category eCategory = Category::kUnknown;
884 size_t ccf = 0;
885 bool bBraceOpen = false;
886 while (ccf < m_spPattern.size()) {
887 if (m_spPattern[ccf] == '\'') {
888 GetLiteralText(m_spPattern, &ccf);
889 } else if (!bBraceOpen &&
890 !pdfium::Contains(kConstChars, m_spPattern[ccf])) {
891 WideString wsCategory(m_spPattern[ccf]);
892 ccf++;
893 while (true) {
894 if (ccf >= m_spPattern.size())
895 return eCategory;
896 if (m_spPattern[ccf] == '.' || m_spPattern[ccf] == '(')
897 break;
898 if (m_spPattern[ccf] == '{') {
899 bBraceOpen = true;
900 break;
901 }
902 wsCategory += m_spPattern[ccf];
903 ccf++;
904 }
905 if (wsCategory == kDateTimeStr)
906 return Category::kDateTime;
907 if (wsCategory == kTextStr)
908 return Category::kText;
909 if (wsCategory == kNumStr)
910 return Category::kNum;
911 if (wsCategory == kZeroStr)
912 return Category::kZero;
913 if (wsCategory == kNullStr)
914 return Category::kNull;
915 if (wsCategory == kDateStr) {
916 if (eCategory == Category::kTime)
917 return Category::kDateTime;
918 eCategory = Category::kDate;
919 } else if (wsCategory == kTimeStr) {
920 if (eCategory == Category::kDate)
921 return Category::kDateTime;
922 eCategory = Category::kTime;
923 }
924 } else if (m_spPattern[ccf] == '}') {
925 bBraceOpen = false;
926 }
927 ccf++;
928 }
929 return eCategory;
930}
931
932WideString CFGAS_StringFormatter::GetTextFormat(
933 WideStringView wsCategory) const {
934 size_t ccf = 0;
935 bool bBrackOpen = false;
936 WideString wsPurgePattern;
937 while (ccf < m_spPattern.size()) {
938 if (m_spPattern[ccf] == '\'') {
939 size_t iCurChar = ccf;
940 GetLiteralText(m_spPattern, &ccf);
941 wsPurgePattern +=
942 WideStringView(m_spPattern.data() + iCurChar, ccf - iCurChar + 1);
943 } else if (!bBrackOpen &&
944 !pdfium::Contains(kConstChars, m_spPattern[ccf])) {
945 WideString wsSearchCategory(m_spPattern[ccf]);
946 ccf++;
947 while (ccf < m_spPattern.size() && m_spPattern[ccf] != '{' &&
948 m_spPattern[ccf] != '.' && m_spPattern[ccf] != '(') {
949 wsSearchCategory += m_spPattern[ccf];
950 ccf++;
951 }
952 if (wsSearchCategory != wsCategory)
953 continue;
954
955 while (ccf < m_spPattern.size()) {
956 if (m_spPattern[ccf] == '(') {
957 ccf++;
958 // Skip over the encoding name.
959 while (ccf < m_spPattern.size() && m_spPattern[ccf] != ')')
960 ccf++;
961 } else if (m_spPattern[ccf] == '{') {
962 bBrackOpen = true;
963 break;
964 }
965 ccf++;
966 }
967 } else if (m_spPattern[ccf] != '}') {
968 wsPurgePattern += m_spPattern[ccf];
969 }
970 ccf++;
971 }
972 if (!bBrackOpen)
973 wsPurgePattern = m_wsPattern;
974
975 return wsPurgePattern;
976}
977
978LocaleIface* CFGAS_StringFormatter::GetNumericFormat(
979 LocaleMgrIface* pLocaleMgr,
980 size_t* iDotIndex,
981 uint32_t* dwStyle,
982 WideString* wsPurgePattern) const {
983 *dwStyle = 0;
984 LocaleIface* pLocale = nullptr;
985 size_t ccf = 0;
986 bool bFindDot = false;
987 bool bBrackOpen = false;
988 while (ccf < m_spPattern.size()) {
989 if (m_spPattern[ccf] == '\'') {
990 size_t iCurChar = ccf;
991 GetLiteralText(m_spPattern, &ccf);
992 *wsPurgePattern +=
993 WideStringView(m_spPattern.data() + iCurChar, ccf - iCurChar + 1);
994 } else if (!bBrackOpen &&
995 !pdfium::Contains(kConstChars, m_spPattern[ccf])) {
996 WideString wsCategory(m_spPattern[ccf]);
997 ccf++;
998 while (ccf < m_spPattern.size() && m_spPattern[ccf] != '{' &&
999 m_spPattern[ccf] != '.' && m_spPattern[ccf] != '(') {
1000 wsCategory += m_spPattern[ccf];
1001 ccf++;
1002 }
1003 if (!wsCategory.EqualsASCII("num")) {
1004 bBrackOpen = true;
1005 ccf = 0;
1006 continue;
1007 }
1008 while (ccf < m_spPattern.size()) {
1009 if (m_spPattern[ccf] == '{') {
1010 bBrackOpen = true;
1011 break;
1012 }
1013 if (m_spPattern[ccf] == '(') {
1014 ccf++;
1015 WideString wsLCID;
1016 while (ccf < m_spPattern.size() && m_spPattern[ccf] != ')')
1017 wsLCID += m_spPattern[ccf++];
1018
1019 pLocale = pLocaleMgr->GetLocaleByName(wsLCID);
1020 } else if (m_spPattern[ccf] == '.') {
1021 WideString wsSubCategory;
1022 ccf++;
1023 while (ccf < m_spPattern.size() && m_spPattern[ccf] != '(' &&
1024 m_spPattern[ccf] != '{') {
1025 wsSubCategory += m_spPattern[ccf++];
1026 }
1027 uint32_t dwSubHash = FX_HashCode_GetW(wsSubCategory.AsStringView());
1028 LocaleIface::NumSubcategory eSubCategory =
1030 for (const auto& data : kLocaleNumSubcategoryData) {
1031 if (data.uHash == dwSubHash) {
1032 eSubCategory = data.eSubCategory;
1033 break;
1034 }
1035 }
1036 if (!pLocale)
1037 pLocale = pLocaleMgr->GetDefLocale();
1038
1039 wsSubCategory = pLocale->GetNumPattern(eSubCategory);
1040 auto result = wsSubCategory.Find('.');
1041 if (result.has_value() && result.value() != 0) {
1042 if (!bFindDot)
1043 *iDotIndex = wsPurgePattern->GetLength() + result.value();
1044 bFindDot = true;
1045 *dwStyle |= FX_NUMSTYLE_DotVorv;
1046 }
1047 *wsPurgePattern += wsSubCategory;
1048 if (eSubCategory == LocaleIface::NumSubcategory::kPercent)
1049 *dwStyle |= FX_NUMSTYLE_Percent;
1050 continue;
1051 }
1052 ccf++;
1053 }
1054 } else if (m_spPattern[ccf] == 'E') {
1055 *dwStyle |= FX_NUMSTYLE_Exponent;
1056 *wsPurgePattern += m_spPattern[ccf];
1057 } else if (m_spPattern[ccf] == '%') {
1058 *dwStyle |= FX_NUMSTYLE_Percent;
1059 *wsPurgePattern += m_spPattern[ccf];
1060 } else if (m_spPattern[ccf] != '}') {
1061 *wsPurgePattern += m_spPattern[ccf];
1062 }
1063 if (!bFindDot && ccf < m_spPattern.size() &&
1064 (m_spPattern[ccf] == '.' || m_spPattern[ccf] == 'V' ||
1065 m_spPattern[ccf] == 'v')) {
1066 bFindDot = true;
1067 *iDotIndex = wsPurgePattern->GetLength() - 1;
1068 *dwStyle |= FX_NUMSTYLE_DotVorv;
1069 }
1070 ccf++;
1071 }
1072 if (!bFindDot)
1073 *iDotIndex = wsPurgePattern->GetLength();
1074 if (!pLocale)
1075 pLocale = pLocaleMgr->GetDefLocale();
1076 return pLocale;
1077}
1078
1079bool CFGAS_StringFormatter::ParseText(const WideString& wsSrcText,
1080 WideString* wsValue) const {
1081 wsValue->clear();
1082 if (wsSrcText.IsEmpty() || m_spPattern.empty())
1083 return false;
1084
1085 WideString wsTextFormat = GetTextFormat(L"text");
1086 if (wsTextFormat.IsEmpty())
1087 return false;
1088
1089 pdfium::span<const wchar_t> spSrcText = wsSrcText.span();
1090 pdfium::span<const wchar_t> spTextFormat = wsTextFormat.span();
1091
1092 size_t iText = 0;
1093 size_t iPattern = 0;
1094 while (iPattern < spTextFormat.size() && iText < spSrcText.size()) {
1095 switch (spTextFormat[iPattern]) {
1096 case '\'': {
1097 WideString wsLiteral = GetLiteralText(spTextFormat, &iPattern);
1098 size_t iLiteralLen = wsLiteral.GetLength();
1099 if (iText + iLiteralLen > spSrcText.size() ||
1100 wcsncmp(spSrcText.data() + iText, wsLiteral.c_str(), iLiteralLen) !=
1101 0) {
1102 *wsValue = wsSrcText;
1103 return false;
1104 }
1105 iText += iLiteralLen;
1106 iPattern++;
1107 break;
1108 }
1109 case 'A':
1110 if (FXSYS_iswalpha(spSrcText[iText])) {
1111 *wsValue += spSrcText[iText];
1112 iText++;
1113 }
1114 iPattern++;
1115 break;
1116 case 'X':
1117 *wsValue += spSrcText[iText];
1118 iText++;
1119 iPattern++;
1120 break;
1121 case 'O':
1122 case '0':
1123 if (FXSYS_IsDecimalDigit(spSrcText[iText]) ||
1124 FXSYS_iswalpha(spSrcText[iText])) {
1125 *wsValue += spSrcText[iText];
1126 iText++;
1127 }
1128 iPattern++;
1129 break;
1130 case '9':
1131 if (FXSYS_IsDecimalDigit(spSrcText[iText])) {
1132 *wsValue += spSrcText[iText];
1133 iText++;
1134 }
1135 iPattern++;
1136 break;
1137 default:
1138 if (spTextFormat[iPattern] != spSrcText[iText]) {
1139 *wsValue = wsSrcText;
1140 return false;
1141 }
1142 iPattern++;
1143 iText++;
1144 break;
1145 }
1146 }
1147 return iPattern == spTextFormat.size() && iText == spSrcText.size();
1148}
1149
1151 const WideString& wsSrcNum,
1152 WideString* wsValue) const {
1153 wsValue->clear();
1154 if (wsSrcNum.IsEmpty() || m_spPattern.empty())
1155 return false;
1156
1157 size_t dot_index_f = m_spPattern.size();
1158 uint32_t dwFormatStyle = 0;
1159 WideString wsNumFormat;
1160 LocaleIface* pLocale =
1161 GetNumericFormat(pLocaleMgr, &dot_index_f, &dwFormatStyle, &wsNumFormat);
1162 if (!pLocale || wsNumFormat.IsEmpty())
1163 return false;
1164
1165 int32_t iExponent = 0;
1166 WideString wsDotSymbol = pLocale->GetDecimalSymbol();
1167 WideString wsGroupSymbol = pLocale->GetGroupingSymbol();
1168 WideString wsMinus = pLocale->GetMinusSymbol();
1169 size_t iGroupLen = wsGroupSymbol.GetLength();
1170 size_t iMinusLen = wsMinus.GetLength();
1171
1172 pdfium::span<const wchar_t> spSrcNum = wsSrcNum.span();
1173 pdfium::span<const wchar_t> spNumFormat = wsNumFormat.span();
1174
1175 bool bHavePercentSymbol = false;
1176 bool bNeg = false;
1177 bool bReverseParse = false;
1178 size_t dot_index = 0;
1179
1180 // If we're looking for a '.', 'V' or 'v' and the input string does not
1181 // have a dot index for one of those, then we disable parsing the decimal.
1182 if (!GetNumericDotIndex(wsSrcNum, wsDotSymbol, &dot_index) &&
1183 (dwFormatStyle & FX_NUMSTYLE_DotVorv))
1184 bReverseParse = true;
1185
1186 // This parse is broken into two parts based on the '.' in the number
1187 // (or 'V' or 'v'). |dot_index_f| is the location of the dot in the format and
1188 // |dot_index| is the location of the dot in the number.
1189 //
1190 // This first while() starts at the '.' and walks backwards to the start of
1191 // the number. The second while() walks from the dot forwards to the end of
1192 // the decimal.
1193
1194 size_t cc = dot_index - 1;
1195 size_t ccf = dot_index_f - 1;
1196 while (ccf < spNumFormat.size() && cc < spSrcNum.size()) {
1197 switch (spNumFormat[ccf]) {
1198 case '\'': {
1199 WideString wsLiteral = GetLiteralTextReverse(spNumFormat, &ccf);
1200 size_t iLiteralLen = wsLiteral.GetLength();
1201 cc -= iLiteralLen - 1;
1202 if (cc >= spSrcNum.size() ||
1203 wcsncmp(spSrcNum.data() + cc, wsLiteral.c_str(), iLiteralLen) !=
1204 0) {
1205 return false;
1206 }
1207 cc--;
1208 ccf--;
1209 break;
1210 }
1211 case '9':
1212 if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
1213 return false;
1214
1215 wsValue->InsertAtFront(spSrcNum[cc]);
1216 cc--;
1217 ccf--;
1218 break;
1219 case 'z':
1220 case 'Z':
1221 if (spNumFormat[ccf] == 'z' || spSrcNum[cc] != ' ') {
1222 if (FXSYS_IsDecimalDigit(spSrcNum[cc])) {
1223 wsValue->InsertAtFront(spSrcNum[cc]);
1224 cc--;
1225 }
1226 } else {
1227 cc--;
1228 }
1229 ccf--;
1230 break;
1231 case 'S':
1232 case 's':
1233 if (spSrcNum[cc] == '+' ||
1234 (spNumFormat[ccf] == 'S' && spSrcNum[cc] == ' ')) {
1235 cc--;
1236 } else {
1237 cc -= iMinusLen - 1;
1238 if (cc >= spSrcNum.size() ||
1239 wcsncmp(spSrcNum.data() + cc, wsMinus.c_str(), iMinusLen) != 0) {
1240 return false;
1241 }
1242 cc--;
1243 bNeg = true;
1244 }
1245 ccf--;
1246 break;
1247 case 'E': {
1248 iExponent = 0;
1249 bool bExpSign = false;
1250 while (cc < spSrcNum.size()) {
1251 if (spSrcNum[cc] == 'E' || spSrcNum[cc] == 'e')
1252 break;
1253 if (FXSYS_IsDecimalDigit(spSrcNum[cc])) {
1254 if (iExponent > std::numeric_limits<int>::max() / 10)
1255 return false;
1256 iExponent = iExponent + FXSYS_DecimalCharToInt(spSrcNum[cc]) * 10;
1257 cc--;
1258 continue;
1259 }
1260 if (spSrcNum[cc] == '+') {
1261 cc--;
1262 continue;
1263 }
1264 if (cc - iMinusLen + 1 <= spSrcNum.size() &&
1265 wcsncmp(spSrcNum.data() + (cc - iMinusLen + 1), wsMinus.c_str(),
1266 iMinusLen) == 0) {
1267 bExpSign = true;
1268 cc -= iMinusLen;
1269 continue;
1270 }
1271
1272 return false;
1273 }
1274 cc--;
1275 iExponent = bExpSign ? -iExponent : iExponent;
1276 ccf--;
1277 break;
1278 }
1279 case '$': {
1280 WideString wsSymbol = pLocale->GetCurrencySymbol();
1281 size_t iSymbolLen = wsSymbol.GetLength();
1282 cc -= iSymbolLen - 1;
1283 if (cc >= spSrcNum.size() ||
1284 wcsncmp(spSrcNum.data() + cc, wsSymbol.c_str(), iSymbolLen) != 0) {
1285 return false;
1286 }
1287 cc--;
1288 ccf--;
1289 break;
1290 }
1291 case 'r':
1292 case 'R':
1293 if (ccf - 1 < spNumFormat.size() &&
1294 ((spNumFormat[ccf] == 'R' && spNumFormat[ccf - 1] == 'C') ||
1295 (spNumFormat[ccf] == 'r' && spNumFormat[ccf - 1] == 'c'))) {
1296 if (spNumFormat[ccf] == 'R' && spSrcNum[cc] == ' ') {
1297 cc -= 2;
1298 } else if (spSrcNum[cc] == 'R' && cc - 1 < spSrcNum.size() &&
1299 spSrcNum[cc - 1] == 'C') {
1300 bNeg = true;
1301 cc -= 2;
1302 }
1303 ccf -= 2;
1304 } else {
1305 ccf--;
1306 }
1307 break;
1308 case 'b':
1309 case 'B':
1310 if (ccf - 1 < spNumFormat.size() &&
1311 ((spNumFormat[ccf] == 'B' && spNumFormat[ccf - 1] == 'D') ||
1312 (spNumFormat[ccf] == 'b' && spNumFormat[ccf - 1] == 'd'))) {
1313 if (spNumFormat[ccf] == 'B' && spSrcNum[cc] == ' ') {
1314 cc -= 2;
1315 } else if (spSrcNum[cc] == 'B' && cc - 1 < spSrcNum.size() &&
1316 spSrcNum[cc - 1] == 'D') {
1317 bNeg = true;
1318 cc -= 2;
1319 }
1320 ccf -= 2;
1321 } else {
1322 ccf--;
1323 }
1324 break;
1325 case '%': {
1326 WideString wsSymbol = pLocale->GetPercentSymbol();
1327 size_t iSymbolLen = wsSymbol.GetLength();
1328 cc -= iSymbolLen - 1;
1329 if (cc >= spSrcNum.size() ||
1330 wcsncmp(spSrcNum.data() + cc, wsSymbol.c_str(), iSymbolLen) != 0) {
1331 return false;
1332 }
1333 cc--;
1334 ccf--;
1335 bHavePercentSymbol = true;
1336 break;
1337 }
1338 case '.':
1339 case 'V':
1340 case 'v':
1341 case '8':
1342 return false;
1343 case ',': {
1344 if (cc < spSrcNum.size()) {
1345 cc -= iGroupLen - 1;
1346 if (cc < spSrcNum.size() &&
1347 wcsncmp(spSrcNum.data() + cc, wsGroupSymbol.c_str(), iGroupLen) ==
1348 0) {
1349 cc--;
1350 } else {
1351 cc += iGroupLen - 1;
1352 }
1353 }
1354 ccf--;
1355 break;
1356 }
1357 case '(':
1358 case ')':
1359 if (spSrcNum[cc] == spNumFormat[ccf])
1360 bNeg = true;
1361 else if (spSrcNum[cc] != L' ')
1362 return false;
1363
1364 cc--;
1365 ccf--;
1366 break;
1367 default:
1368 if (spNumFormat[ccf] != spSrcNum[cc])
1369 return false;
1370
1371 cc--;
1372 ccf--;
1373 }
1374 }
1375 if (cc < spSrcNum.size()) {
1376 if (spSrcNum[cc] == '-') {
1377 bNeg = true;
1378 cc--;
1379 }
1380 if (cc < spSrcNum.size())
1381 return false;
1382 }
1383 if ((dwFormatStyle & FX_NUMSTYLE_DotVorv) && dot_index < spSrcNum.size())
1384 *wsValue += '.';
1385
1386 if (!bReverseParse) {
1387 cc = (dot_index == spSrcNum.size()) ? spSrcNum.size() : dot_index + 1;
1388 for (ccf = dot_index_f + 1;
1389 cc < spSrcNum.size() && ccf < spNumFormat.size(); ++ccf) {
1390 switch (spNumFormat[ccf]) {
1391 case '\'': {
1392 WideString wsLiteral = GetLiteralText(spNumFormat, &ccf);
1393 size_t iLiteralLen = wsLiteral.GetLength();
1394 if (cc + iLiteralLen > spSrcNum.size() ||
1395 wcsncmp(spSrcNum.data() + cc, wsLiteral.c_str(), iLiteralLen) !=
1396 0) {
1397 return false;
1398 }
1399 cc += iLiteralLen;
1400 break;
1401 }
1402 case '9':
1403 if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
1404 return false;
1405
1406 *wsValue += spSrcNum[cc];
1407 cc++;
1408 break;
1409 case 'z':
1410 case 'Z':
1411 if (spNumFormat[ccf] == 'z' || spSrcNum[cc] != ' ') {
1412 if (FXSYS_IsDecimalDigit(spSrcNum[cc])) {
1413 *wsValue += spSrcNum[cc];
1414 cc++;
1415 }
1416 } else {
1417 cc++;
1418 }
1419 break;
1420 case 'S':
1421 case 's':
1422 if (spSrcNum[cc] == '+' ||
1423 (spNumFormat[ccf] == 'S' && spSrcNum[cc] == ' ')) {
1424 cc++;
1425 } else {
1426 if (cc + iMinusLen > spSrcNum.size() ||
1427 wcsncmp(spSrcNum.data() + cc, wsMinus.c_str(), iMinusLen) !=
1428 0) {
1429 return false;
1430 }
1431 bNeg = true;
1432 cc += iMinusLen;
1433 }
1434 break;
1435 case 'E': {
1436 if (cc >= spSrcNum.size() ||
1437 (spSrcNum[cc] != 'E' && spSrcNum[cc] != 'e')) {
1438 return false;
1439 }
1440 iExponent = 0;
1441 bool bExpSign = false;
1442 cc++;
1443 if (cc < spSrcNum.size()) {
1444 if (spSrcNum[cc] == '+') {
1445 cc++;
1446 } else if (spSrcNum[cc] == '-') {
1447 bExpSign = true;
1448 cc++;
1449 }
1450 }
1451 while (cc < spSrcNum.size()) {
1452 if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
1453 break;
1454 int digit = FXSYS_DecimalCharToInt(spSrcNum[cc]);
1455 if (iExponent > (std::numeric_limits<int>::max() - digit) / 10)
1456 return false;
1457 iExponent = iExponent * 10 + digit;
1458 cc++;
1459 }
1460 iExponent = bExpSign ? -iExponent : iExponent;
1461 break;
1462 }
1463 case '$': {
1464 WideString wsSymbol = pLocale->GetCurrencySymbol();
1465 size_t iSymbolLen = wsSymbol.GetLength();
1466 if (cc + iSymbolLen > spSrcNum.size() ||
1467 wcsncmp(spSrcNum.data() + cc, wsSymbol.c_str(), iSymbolLen) !=
1468 0) {
1469 return false;
1470 }
1471 cc += iSymbolLen;
1472 break;
1473 }
1474 case 'c':
1475 case 'C':
1476 if (ccf + 1 < spNumFormat.size() &&
1477 ((spNumFormat[ccf] == 'C' && spNumFormat[ccf + 1] == 'R') ||
1478 (spNumFormat[ccf] == 'c' && spNumFormat[ccf + 1] == 'r'))) {
1479 if (spNumFormat[ccf] == 'C' && spSrcNum[cc] == ' ') {
1480 cc++;
1481 } else if (spSrcNum[cc] == 'C' && cc + 1 < spSrcNum.size() &&
1482 spSrcNum[cc + 1] == 'R') {
1483 bNeg = true;
1484 cc += 2;
1485 }
1486 ccf++;
1487 }
1488 break;
1489 case 'd':
1490 case 'D':
1491 if (ccf + 1 < spNumFormat.size() &&
1492 ((spNumFormat[ccf] == 'D' && spNumFormat[ccf + 1] == 'B') ||
1493 (spNumFormat[ccf] == 'd' && spNumFormat[ccf + 1] == 'b'))) {
1494 if (spNumFormat[ccf] == 'D' && spSrcNum[cc] == ' ') {
1495 cc++;
1496 } else if (spSrcNum[cc] == 'D' && cc + 1 < spSrcNum.size() &&
1497 spSrcNum[cc + 1] == 'B') {
1498 bNeg = true;
1499 cc += 2;
1500 }
1501 ccf++;
1502 }
1503 break;
1504 case '.':
1505 case 'V':
1506 case 'v':
1507 return false;
1508 case '%': {
1509 WideString wsSymbol = pLocale->GetPercentSymbol();
1510 size_t iSymbolLen = wsSymbol.GetLength();
1511 if (cc + iSymbolLen <= spSrcNum.size() &&
1512 wcsncmp(spSrcNum.data() + cc, wsSymbol.c_str(), iSymbolLen) ==
1513 0) {
1514 cc += iSymbolLen;
1515 }
1516 bHavePercentSymbol = true;
1517 } break;
1518 case '8': {
1519 while (ccf + 1 < spNumFormat.size() && spNumFormat[ccf + 1] == '8')
1520 ccf++;
1521
1522 while (cc < spSrcNum.size() && FXSYS_IsDecimalDigit(spSrcNum[cc])) {
1523 *wsValue += spSrcNum[cc];
1524 cc++;
1525 }
1526 } break;
1527 case ',': {
1528 if (cc + iGroupLen <= spSrcNum.size() &&
1529 wcsncmp(spSrcNum.data() + cc, wsGroupSymbol.c_str(), iGroupLen) ==
1530 0) {
1531 cc += iGroupLen;
1532 }
1533 break;
1534 }
1535 case '(':
1536 case ')':
1537 if (spSrcNum[cc] == spNumFormat[ccf])
1538 bNeg = true;
1539 else if (spSrcNum[cc] != L' ')
1540 return false;
1541
1542 cc++;
1543 break;
1544 default:
1545 if (spNumFormat[ccf] != spSrcNum[cc])
1546 return false;
1547
1548 cc++;
1549 }
1550 }
1551 if (cc != spSrcNum.size())
1552 return false;
1553 }
1554 if (iExponent || bHavePercentSymbol) {
1555 CFGAS_Decimal decimal = CFGAS_Decimal(wsValue->AsStringView());
1556 if (iExponent)
1557 decimal = decimal * CFGAS_Decimal(powf(10, iExponent), 3);
1558 if (bHavePercentSymbol)
1559 decimal = decimal / CFGAS_Decimal(100);
1560 *wsValue = decimal.ToWideString();
1561 }
1562 if (bNeg)
1563 wsValue->InsertAtFront(L'-');
1564
1565 return true;
1566}
1567
1569 LocaleMgrIface* pLocaleMgr,
1570 LocaleIface** pLocale,
1571 WideString* wsDatePattern,
1572 WideString* wsTimePattern) const {
1573 *pLocale = nullptr;
1574 WideString wsTempPattern;
1575 Category eCategory = Category::kUnknown;
1576 DateTimeType eDateTimeType = DateTimeType::kUnknown;
1577 size_t ccf = 0;
1578 bool bBraceOpen = false;
1579 while (ccf < m_spPattern.size()) {
1580 if (m_spPattern[ccf] == '\'') {
1581 size_t iCurChar = ccf;
1582 GetLiteralText(m_spPattern, &ccf);
1583 wsTempPattern +=
1584 WideStringView(m_spPattern.data() + iCurChar, ccf - iCurChar + 1);
1585 } else if (!bBraceOpen && eDateTimeType != DateTimeType::kDateTime &&
1586 !pdfium::Contains(kConstChars, m_spPattern[ccf])) {
1587 WideString wsCategory(m_spPattern[ccf]);
1588 ccf++;
1589 while (ccf < m_spPattern.size() && m_spPattern[ccf] != '{' &&
1590 m_spPattern[ccf] != '.' && m_spPattern[ccf] != '(') {
1591 if (m_spPattern[ccf] == 'T') {
1592 *wsDatePattern = m_wsPattern.First(ccf);
1593 *wsTimePattern = m_wsPattern.Last(m_wsPattern.GetLength() - ccf);
1594 wsTimePattern->SetAt(0, ' ');
1595 if (!*pLocale)
1596 *pLocale = pLocaleMgr->GetDefLocale();
1598 }
1599 wsCategory += m_spPattern[ccf];
1600 ccf++;
1601 }
1602 if (!HasDate(eDateTimeType) && wsCategory.EqualsASCII("date")) {
1603 eDateTimeType = AddDateToDatelessType(eDateTimeType);
1604 eCategory = Category::kDate;
1605 } else if (!HasTime(eDateTimeType) && wsCategory.EqualsASCII("time")) {
1606 eDateTimeType = AddTimeToTimelessType(eDateTimeType);
1607 eCategory = Category::kTime;
1608 } else if (wsCategory.EqualsASCII("datetime")) {
1609 eDateTimeType = DateTimeType::kDateTime;
1610 eCategory = Category::kDateTime;
1611 } else {
1612 continue;
1613 }
1614 while (ccf < m_spPattern.size()) {
1615 if (m_spPattern[ccf] == '{') {
1616 bBraceOpen = true;
1617 break;
1618 }
1619 if (m_spPattern[ccf] == '(') {
1620 ccf++;
1621 WideString wsLCID;
1622 while (ccf < m_spPattern.size() && m_spPattern[ccf] != ')')
1623 wsLCID += m_spPattern[ccf++];
1624
1625 *pLocale = pLocaleMgr->GetLocaleByName(wsLCID);
1626 } else if (m_spPattern[ccf] == '.') {
1627 WideString wsSubCategory;
1628 ccf++;
1629 while (ccf < m_spPattern.size() && m_spPattern[ccf] != '(' &&
1630 m_spPattern[ccf] != '{')
1631 wsSubCategory += m_spPattern[ccf++];
1632
1633 uint32_t dwSubHash = FX_HashCode_GetW(wsSubCategory.AsStringView());
1634 LocaleIface::DateTimeSubcategory eSubCategory =
1636 for (const auto& data : kLocaleDateTimeSubcategoryData) {
1637 if (data.uHash == dwSubHash) {
1638 eSubCategory = data.eSubCategory;
1639 break;
1640 }
1641 }
1642 if (!*pLocale)
1643 *pLocale = pLocaleMgr->GetDefLocale();
1644
1645 switch (eCategory) {
1646 case Category::kDate:
1647 *wsDatePattern =
1648 wsTempPattern + (*pLocale)->GetDatePattern(eSubCategory);
1649 break;
1650 case Category::kTime:
1651 *wsTimePattern =
1652 wsTempPattern + (*pLocale)->GetTimePattern(eSubCategory);
1653 break;
1655 *wsDatePattern =
1656 wsTempPattern + (*pLocale)->GetDatePattern(eSubCategory);
1657 *wsTimePattern = (*pLocale)->GetTimePattern(eSubCategory);
1658 break;
1659 default:
1660 break;
1661 }
1662 wsTempPattern.clear();
1663 continue;
1664 }
1665 ccf++;
1666 }
1667 } else if (m_spPattern[ccf] == '}') {
1668 bBraceOpen = false;
1669 if (!wsTempPattern.IsEmpty()) {
1670 if (eCategory == Category::kTime)
1671 *wsTimePattern = std::move(wsTempPattern);
1672 else if (eCategory == Category::kDate)
1673 *wsDatePattern = std::move(wsTempPattern);
1674 else
1675 wsTempPattern.clear();
1676 }
1677 } else {
1678 wsTempPattern += m_spPattern[ccf];
1679 }
1680 ccf++;
1681 }
1682
1683 if (!wsTempPattern.IsEmpty()) {
1684 if (eCategory == Category::kDate)
1685 *wsDatePattern += wsTempPattern;
1686 else
1687 *wsTimePattern += wsTempPattern;
1688 }
1689 if (!*pLocale)
1690 *pLocale = pLocaleMgr->GetDefLocale();
1691 if (eDateTimeType == DateTimeType::kUnknown) {
1692 wsTimePattern->clear();
1693 *wsDatePattern = m_wsPattern;
1694 }
1695 return eDateTimeType;
1696}
1697
1699 const WideString& wsSrcDateTime,
1700 DateTimeType eDateTimeType,
1701 CFX_DateTime* dtValue) const {
1702 dtValue->Reset();
1703 if (wsSrcDateTime.IsEmpty() || m_spPattern.empty())
1704 return false;
1705
1706 LocaleIface* pLocale = nullptr;
1707 WideString wsDatePattern;
1708 WideString wsTimePattern;
1709 DateTimeType eCategory =
1710 GetDateTimeFormat(pLocaleMgr, &pLocale, &wsDatePattern, &wsTimePattern);
1711 if (!pLocale)
1712 return false;
1713
1714 if (eCategory == DateTimeType::kUnknown)
1715 eCategory = eDateTimeType;
1716
1717 size_t iStart = 0;
1718 switch (eCategory) {
1719 case DateTimeType::kDate:
1720 return ParseLocaleDate(wsSrcDateTime, wsDatePattern, pLocale, dtValue,
1721 &iStart);
1722 case DateTimeType::kTime:
1723 return ParseLocaleTime(wsSrcDateTime, wsTimePattern, pLocale, dtValue,
1724 &iStart);
1725 case DateTimeType::kDateTime:
1726 return ParseLocaleDate(wsSrcDateTime, wsTimePattern, pLocale, dtValue,
1727 &iStart) &&
1728 ParseLocaleTime(wsSrcDateTime, wsDatePattern, pLocale, dtValue,
1729 &iStart);
1730 case DateTimeType::kTimeDate:
1731 return ParseLocaleTime(wsSrcDateTime, wsTimePattern, pLocale, dtValue,
1732 &iStart) &&
1733 ParseLocaleDate(wsSrcDateTime, wsDatePattern, pLocale, dtValue,
1734 &iStart);
1736 return false;
1737 }
1738}
1739
1740bool CFGAS_StringFormatter::ParseZero(const WideString& wsSrcText) const {
1741 WideString wsTextFormat = GetTextFormat(L"zero");
1742 pdfium::span<const wchar_t> spSrcText = wsSrcText.span();
1743 pdfium::span<const wchar_t> spTextFormat = wsTextFormat.span();
1744
1745 size_t iText = 0;
1746 size_t iPattern = 0;
1747 while (iPattern < spTextFormat.size() && iText < spSrcText.size()) {
1748 if (spTextFormat[iPattern] == '\'') {
1749 WideString wsLiteral = GetLiteralText(spTextFormat, &iPattern);
1750 size_t iLiteralLen = wsLiteral.GetLength();
1751 if (iText + iLiteralLen > spSrcText.size() ||
1752 wcsncmp(spSrcText.data() + iText, wsLiteral.c_str(), iLiteralLen)) {
1753 return false;
1754 }
1755 iText += iLiteralLen;
1756 iPattern++;
1757 continue;
1758 }
1759 if (spTextFormat[iPattern] != spSrcText[iText])
1760 return false;
1761
1762 iText++;
1763 iPattern++;
1764 }
1765 return iPattern == spTextFormat.size() && iText == spSrcText.size();
1766}
1767
1768bool CFGAS_StringFormatter::ParseNull(const WideString& wsSrcText) const {
1769 WideString wsTextFormat = GetTextFormat(L"null");
1770 pdfium::span<const wchar_t> spSrcText = wsSrcText.span();
1771 pdfium::span<const wchar_t> spTextFormat = wsTextFormat.span();
1772
1773 size_t iText = 0;
1774 size_t iPattern = 0;
1775 while (iPattern < spTextFormat.size() && iText < spSrcText.size()) {
1776 if (spTextFormat[iPattern] == '\'') {
1777 WideString wsLiteral = GetLiteralText(spTextFormat, &iPattern);
1778 size_t iLiteralLen = wsLiteral.GetLength();
1779 if (iText + iLiteralLen > spSrcText.size() ||
1780 wcsncmp(spSrcText.data() + iText, wsLiteral.c_str(), iLiteralLen)) {
1781 return false;
1782 }
1783 iText += iLiteralLen;
1784 iPattern++;
1785 continue;
1786 }
1787 if (spTextFormat[iPattern] != spSrcText[iText])
1788 return false;
1789
1790 iText++;
1791 iPattern++;
1792 }
1793 return iPattern == spTextFormat.size() && iText == spSrcText.size();
1794}
1795
1796bool CFGAS_StringFormatter::FormatText(const WideString& wsSrcText,
1797 WideString* wsOutput) const {
1798 if (wsSrcText.IsEmpty() || m_spPattern.empty())
1799 return false;
1800
1801 WideString wsTextFormat = GetTextFormat(L"text");
1802 pdfium::span<const wchar_t> spSrcText = wsSrcText.span();
1803 pdfium::span<const wchar_t> spTextFormat = wsTextFormat.span();
1804
1805 size_t iText = 0;
1806 size_t iPattern = 0;
1807 while (iPattern < spTextFormat.size()) {
1808 switch (spTextFormat[iPattern]) {
1809 case '\'': {
1810 *wsOutput += GetLiteralText(spTextFormat, &iPattern);
1811 iPattern++;
1812 break;
1813 }
1814 case 'A':
1815 if (iText >= spSrcText.size() || !FXSYS_iswalpha(spSrcText[iText]))
1816 return false;
1817
1818 *wsOutput += spSrcText[iText++];
1819 iPattern++;
1820 break;
1821 case 'X':
1822 if (iText >= spSrcText.size())
1823 return false;
1824
1825 *wsOutput += spSrcText[iText++];
1826 iPattern++;
1827 break;
1828 case 'O':
1829 case '0':
1830 if (iText >= spSrcText.size() ||
1831 (!FXSYS_IsDecimalDigit(spSrcText[iText]) &&
1832 !FXSYS_iswalpha(spSrcText[iText]))) {
1833 return false;
1834 }
1835 *wsOutput += spSrcText[iText++];
1836 iPattern++;
1837 break;
1838 case '9':
1839 if (iText >= spSrcText.size() ||
1840 !FXSYS_IsDecimalDigit(spSrcText[iText]))
1841 return false;
1842
1843 *wsOutput += spSrcText[iText++];
1844 iPattern++;
1845 break;
1846 default:
1847 *wsOutput += spTextFormat[iPattern++];
1848 break;
1849 }
1850 }
1851 return iText == spSrcText.size();
1852}
1853
1855 const WideString& wsInputNum,
1856 WideString* wsOutput) const {
1857 if (wsInputNum.IsEmpty() || m_spPattern.empty())
1858 return false;
1859
1860 size_t dot_index_f = m_spPattern.size();
1861 uint32_t dwNumStyle = 0;
1862 WideString wsNumFormat;
1863 LocaleIface* pLocale =
1864 GetNumericFormat(pLocaleMgr, &dot_index_f, &dwNumStyle, &wsNumFormat);
1865 if (!pLocale || wsNumFormat.IsEmpty())
1866 return false;
1867
1868 pdfium::span<const wchar_t> spNumFormat = wsNumFormat.span();
1869 WideString wsSrcNum = wsInputNum;
1870 wsSrcNum.TrimLeft('0');
1871 if (wsSrcNum.IsEmpty() || wsSrcNum[0] == '.')
1872 wsSrcNum.InsertAtFront('0');
1873
1874 CFGAS_Decimal decimal = CFGAS_Decimal(wsSrcNum.AsStringView());
1875 if (dwNumStyle & FX_NUMSTYLE_Percent) {
1876 decimal = decimal * CFGAS_Decimal(100);
1877 wsSrcNum = decimal.ToWideString();
1878 }
1879
1880 int32_t exponent = 0;
1881 if (dwNumStyle & FX_NUMSTYLE_Exponent) {
1882 int fixed_count = 0;
1883 for (size_t ccf = 0; ccf < dot_index_f; ++ccf) {
1884 switch (spNumFormat[ccf]) {
1885 case '\'':
1886 GetLiteralText(spNumFormat, &ccf);
1887 break;
1888 case '9':
1889 case 'z':
1890 case 'Z':
1891 fixed_count++;
1892 break;
1893 }
1894 }
1895
1896 FX_SAFE_UINT32 threshold = 1;
1897 while (fixed_count > 1) {
1898 threshold *= 10;
1899 fixed_count--;
1900 }
1901 if (!threshold.IsValid())
1902 return false;
1903
1904 bool bAdjusted = false;
1905 while (decimal.IsNotZero() &&
1906 fabs(decimal.ToDouble()) < threshold.ValueOrDie()) {
1907 decimal = decimal * CFGAS_Decimal(10);
1908 --exponent;
1909 bAdjusted = true;
1910 }
1911 if (!bAdjusted) {
1912 threshold *= 10;
1913 if (!threshold.IsValid())
1914 return false;
1915
1916 while (decimal.IsNotZero() &&
1917 fabs(decimal.ToDouble()) > threshold.ValueOrDie()) {
1918 decimal = decimal / CFGAS_Decimal(10);
1919 ++exponent;
1920 }
1921 }
1922 }
1923
1924 bool bTrimTailZeros = false;
1925 size_t iTreading =
1926 GetNumTrailingLimit(wsNumFormat, dot_index_f, &bTrimTailZeros);
1927 uint8_t scale = decimal.GetScale();
1928 if (iTreading < scale) {
1929 decimal.SetScale(iTreading);
1930 wsSrcNum = decimal.ToWideString();
1931 }
1932 if (bTrimTailZeros && scale > 0 && iTreading > 0) {
1933 wsSrcNum.TrimRight(L"0");
1934 wsSrcNum.TrimRight(L".");
1935 }
1936
1937 WideString wsGroupSymbol = pLocale->GetGroupingSymbol();
1938 bool bNeg = false;
1939 if (wsSrcNum[0] == '-') {
1940 bNeg = true;
1941 wsSrcNum.Delete(0, 1);
1942 }
1943
1944 bool bAddNeg = false;
1945 pdfium::span<const wchar_t> spSrcNum = wsSrcNum.span();
1946 auto dot_index = wsSrcNum.Find('.');
1947 if (!dot_index.has_value())
1948 dot_index = spSrcNum.size();
1949
1950 size_t cc = dot_index.value() - 1;
1951 for (size_t ccf = dot_index_f - 1; ccf < spNumFormat.size(); --ccf) {
1952 switch (spNumFormat[ccf]) {
1953 case '9':
1954 if (cc < spSrcNum.size()) {
1955 if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
1956 return false;
1957 wsOutput->InsertAtFront(spSrcNum[cc]);
1958 cc--;
1959 } else {
1960 wsOutput->InsertAtFront(L'0');
1961 }
1962 break;
1963 case 'z':
1964 if (cc < spSrcNum.size()) {
1965 if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
1966 return false;
1967 if (spSrcNum[0] != '0')
1968 wsOutput->InsertAtFront(spSrcNum[cc]);
1969 cc--;
1970 }
1971 break;
1972 case 'Z':
1973 if (cc < spSrcNum.size()) {
1974 if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
1975 return false;
1976 wsOutput->InsertAtFront(spSrcNum[0] == '0' ? L' ' : spSrcNum[cc]);
1977 cc--;
1978 } else {
1979 wsOutput->InsertAtFront(L' ');
1980 }
1981 break;
1982 case 'S':
1983 if (bNeg) {
1984 *wsOutput = pLocale->GetMinusSymbol() + *wsOutput;
1985 bAddNeg = true;
1986 } else {
1987 wsOutput->InsertAtFront(L' ');
1988 }
1989 break;
1990 case 's':
1991 if (bNeg) {
1992 *wsOutput = pLocale->GetMinusSymbol() + *wsOutput;
1993 bAddNeg = true;
1994 }
1995 break;
1996 case 'E':
1997 *wsOutput = WideString::Format(L"E%+d", exponent) + *wsOutput;
1998 break;
1999 case '$':
2000 *wsOutput = pLocale->GetCurrencySymbol() + *wsOutput;
2001 break;
2002 case 'r':
2003 if (ccf - 1 < spNumFormat.size() && spNumFormat[ccf - 1] == 'c') {
2004 if (bNeg)
2005 *wsOutput = L"CR" + *wsOutput;
2006 ccf--;
2007 bAddNeg = true;
2008 } else {
2009 wsOutput->InsertAtFront('r');
2010 }
2011 break;
2012 case 'R':
2013 if (ccf - 1 < spNumFormat.size() && spNumFormat[ccf - 1] == 'C') {
2014 *wsOutput = bNeg ? L"CR" : L" " + *wsOutput;
2015 ccf--;
2016 bAddNeg = true;
2017 } else {
2018 wsOutput->InsertAtFront('R');
2019 }
2020 break;
2021 case 'b':
2022 if (ccf - 1 < spNumFormat.size() && spNumFormat[ccf - 1] == 'd') {
2023 if (bNeg)
2024 *wsOutput = L"db" + *wsOutput;
2025 ccf--;
2026 bAddNeg = true;
2027 } else {
2028 wsOutput->InsertAtFront('b');
2029 }
2030 break;
2031 case 'B':
2032 if (ccf - 1 < spNumFormat.size() && spNumFormat[ccf - 1] == 'D') {
2033 *wsOutput = bNeg ? L"DB" : L" " + *wsOutput;
2034 ccf--;
2035 bAddNeg = true;
2036 } else {
2037 wsOutput->InsertAtFront('B');
2038 }
2039 break;
2040 case '%':
2041 *wsOutput = pLocale->GetPercentSymbol() + *wsOutput;
2042 break;
2043 case ',':
2044 if (cc < spSrcNum.size())
2045 *wsOutput = wsGroupSymbol + *wsOutput;
2046 break;
2047 case '(':
2048 wsOutput->InsertAtFront(bNeg ? L'(' : L' ');
2049 bAddNeg = true;
2050 break;
2051 case ')':
2052 wsOutput->InsertAtFront(bNeg ? L')' : L' ');
2053 break;
2054 case '\'':
2055 *wsOutput = GetLiteralTextReverse(spNumFormat, &ccf) + *wsOutput;
2056 break;
2057 default:
2058 wsOutput->InsertAtFront(spNumFormat[ccf]);
2059 break;
2060 }
2061 }
2062
2063 if (cc < spSrcNum.size()) {
2064 size_t nPos = dot_index.value() % 3;
2065 wsOutput->clear();
2066 for (size_t i = 0; i < dot_index.value(); i++) {
2067 if (i % 3 == nPos && i != 0)
2068 *wsOutput += wsGroupSymbol;
2069 *wsOutput += wsSrcNum[i];
2070 }
2071 if (dot_index.value() < spSrcNum.size()) {
2072 *wsOutput += pLocale->GetDecimalSymbol();
2073 *wsOutput += wsSrcNum.Last(spSrcNum.size() - dot_index.value() - 1);
2074 }
2075 if (bNeg)
2076 *wsOutput = pLocale->GetMinusSymbol() + *wsOutput;
2077 return true;
2078 }
2079 if (dot_index_f == wsNumFormat.GetLength()) {
2080 if (!bAddNeg && bNeg)
2081 *wsOutput = pLocale->GetMinusSymbol() + *wsOutput;
2082 return true;
2083 }
2084
2085 WideString wsDotSymbol = pLocale->GetDecimalSymbol();
2086 if (spNumFormat[dot_index_f] == 'V') {
2087 *wsOutput += wsDotSymbol;
2088 } else if (spNumFormat[dot_index_f] == '.') {
2089 if (dot_index.value() < spSrcNum.size()) {
2090 *wsOutput += wsDotSymbol;
2091 } else if (dot_index_f + 1 < spNumFormat.size() &&
2092 (spNumFormat[dot_index_f + 1] == '9' ||
2093 spNumFormat[dot_index_f + 1] == 'Z')) {
2094 *wsOutput += wsDotSymbol;
2095 }
2096 }
2097
2098 cc = dot_index.value() + 1;
2099 for (size_t ccf = dot_index_f + 1; ccf < spNumFormat.size(); ++ccf) {
2100 switch (spNumFormat[ccf]) {
2101 case '\'':
2102 *wsOutput += GetLiteralText(spNumFormat, &ccf);
2103 break;
2104 case '9':
2105 if (cc < spSrcNum.size()) {
2106 if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
2107 return false;
2108 *wsOutput += spSrcNum[cc];
2109 cc++;
2110 } else {
2111 *wsOutput += L'0';
2112 }
2113 break;
2114 case 'z':
2115 if (cc < spSrcNum.size()) {
2116 if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
2117 return false;
2118 *wsOutput += spSrcNum[cc];
2119 cc++;
2120 }
2121 break;
2122 case 'Z':
2123 if (cc < spSrcNum.size()) {
2124 if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
2125 return false;
2126 *wsOutput += spSrcNum[cc];
2127 cc++;
2128 } else {
2129 *wsOutput += L'0';
2130 }
2131 break;
2132 case 'E': {
2133 *wsOutput += WideString::Format(L"E%+d", exponent);
2134 break;
2135 }
2136 case '$':
2137 *wsOutput += pLocale->GetCurrencySymbol();
2138 break;
2139 case 'c':
2140 if (ccf + 1 < spNumFormat.size() && spNumFormat[ccf + 1] == 'r') {
2141 if (bNeg)
2142 *wsOutput += L"CR";
2143 ccf++;
2144 bAddNeg = true;
2145 }
2146 break;
2147 case 'C':
2148 if (ccf + 1 < spNumFormat.size() && spNumFormat[ccf + 1] == 'R') {
2149 *wsOutput += bNeg ? L"CR" : L" ";
2150 ccf++;
2151 bAddNeg = true;
2152 }
2153 break;
2154 case 'd':
2155 if (ccf + 1 < spNumFormat.size() && spNumFormat[ccf + 1] == 'b') {
2156 if (bNeg)
2157 *wsOutput += L"db";
2158 ccf++;
2159 bAddNeg = true;
2160 }
2161 break;
2162 case 'D':
2163 if (ccf + 1 < spNumFormat.size() && spNumFormat[ccf + 1] == 'B') {
2164 *wsOutput += bNeg ? L"DB" : L" ";
2165 ccf++;
2166 bAddNeg = true;
2167 }
2168 break;
2169 case '%':
2170 *wsOutput += pLocale->GetPercentSymbol();
2171 break;
2172 case '8':
2173 while (ccf + 1 < spNumFormat.size() && spNumFormat[ccf + 1] == '8')
2174 ccf++;
2175 while (cc < spSrcNum.size() && FXSYS_IsDecimalDigit(spSrcNum[cc])) {
2176 *wsOutput += spSrcNum[cc];
2177 cc++;
2178 }
2179 break;
2180 case ',':
2181 *wsOutput += wsGroupSymbol;
2182 break;
2183 case '(':
2184 *wsOutput += bNeg ? '(' : ' ';
2185 bAddNeg = true;
2186 break;
2187 case ')':
2188 *wsOutput += bNeg ? ')' : ' ';
2189 break;
2190 default:
2191 break;
2192 }
2193 }
2194 if (!bAddNeg && bNeg)
2195 *wsOutput = pLocale->GetMinusSymbol() + *wsOutput;
2196
2197 return true;
2198}
2199
2201 const WideString& wsSrcDateTime,
2202 DateTimeType eDateTimeType,
2203 WideString* wsOutput) const {
2204 if (wsSrcDateTime.IsEmpty() || m_spPattern.empty())
2205 return false;
2206
2207 WideString wsDatePattern;
2208 WideString wsTimePattern;
2209 LocaleIface* pLocale = nullptr;
2210 DateTimeType eCategory =
2211 GetDateTimeFormat(pLocaleMgr, &pLocale, &wsDatePattern, &wsTimePattern);
2212 if (!pLocale)
2213 return false;
2214
2215 if (eCategory == DateTimeType::kUnknown) {
2216 if (eDateTimeType == DateTimeType::kTime) {
2217 wsTimePattern = std::move(wsDatePattern);
2218 wsDatePattern = WideString();
2219 }
2220 eCategory = eDateTimeType;
2221 if (eCategory == DateTimeType::kUnknown)
2222 return false;
2223 }
2224
2225 CFX_DateTime dt;
2226 auto iT = wsSrcDateTime.Find(L"T");
2227 if (!iT.has_value()) {
2228 if (eCategory == DateTimeType::kDate &&
2229 FX_DateFromCanonical(wsSrcDateTime.span(), &dt)) {
2230 *wsOutput = FormatDateTimeInternal(dt, wsDatePattern, wsTimePattern, true,
2231 pLocale);
2232 return true;
2233 }
2234 if (eCategory == DateTimeType::kTime &&
2235 FX_TimeFromCanonical(pLocale, wsSrcDateTime.span(), &dt)) {
2236 *wsOutput = FormatDateTimeInternal(dt, wsDatePattern, wsTimePattern, true,
2237 pLocale);
2238 return true;
2239 }
2240 } else {
2241 pdfium::span<const wchar_t> wsSrcDate =
2242 wsSrcDateTime.span().first(iT.value());
2243 pdfium::span<const wchar_t> wsSrcTime =
2244 wsSrcDateTime.span().subspan(iT.value() + 1);
2245 if (wsSrcDate.empty() || wsSrcTime.empty())
2246 return false;
2247
2248 if (FX_DateFromCanonical(wsSrcDate, &dt) &&
2249 FX_TimeFromCanonical(pLocale, wsSrcTime, &dt)) {
2250 *wsOutput =
2251 FormatDateTimeInternal(dt, wsDatePattern, wsTimePattern,
2252 eCategory != DateTimeType::kTimeDate, pLocale);
2253 return true;
2254 }
2255 }
2256 return false;
2257}
2258
2259bool CFGAS_StringFormatter::FormatZero(WideString* wsOutput) const {
2260 if (m_spPattern.empty())
2261 return false;
2262
2263 WideString wsTextFormat = GetTextFormat(L"zero");
2264 pdfium::span<const wchar_t> spTextFormat = wsTextFormat.span();
2265 for (size_t iPattern = 0; iPattern < spTextFormat.size(); ++iPattern) {
2266 if (spTextFormat[iPattern] == '\'') {
2267 *wsOutput += GetLiteralText(spTextFormat, &iPattern);
2268 continue;
2269 }
2270 *wsOutput += spTextFormat[iPattern];
2271 }
2272 return true;
2273}
2274
2275bool CFGAS_StringFormatter::FormatNull(WideString* wsOutput) const {
2276 if (m_spPattern.empty())
2277 return false;
2278
2279 WideString wsTextFormat = GetTextFormat(L"null");
2280 pdfium::span<const wchar_t> spTextFormat = wsTextFormat.span();
2281 for (size_t iPattern = 0; iPattern < spTextFormat.size(); ++iPattern) {
2282 if (spTextFormat[iPattern] == '\'') {
2283 *wsOutput += GetLiteralText(spTextFormat, &iPattern);
2284 continue;
2285 }
2286 *wsOutput += spTextFormat[iPattern];
2287 }
2288 return true;
2289}
#define SUBC(a, b, c)
#define FX_NUMSTYLE_Exponent
#define FX_NUMSTYLE_Percent
bool FX_TimeFromCanonical(const LocaleIface *pLocale, pdfium::span< const wchar_t > spTime, CFX_DateTime *datetime)
#define FX_NUMSTYLE_DotVorv
bool FX_DateFromCanonical(pdfium::span< const wchar_t > spDate, CFX_DateTime *datetime)
bool FX_IsLeapYear(int32_t iYear)
bool IsNotZero() const
uint8_t GetScale() const
void SetScale(uint8_t newScale)
CFGAS_Decimal(float val, uint8_t scale)
CFGAS_Decimal operator*(const CFGAS_Decimal &val) const
double ToDouble() const
WideString ToWideString() const
CFGAS_Decimal operator/(const CFGAS_Decimal &val) const
CFGAS_Decimal(int32_t val)
bool FormatText(const WideString &wsSrcText, WideString *wsOutput) const
bool ParseText(const WideString &wsSrcText, WideString *wsValue) const
bool FormatNull(WideString *wsOutput) const
bool FormatZero(WideString *wsOutput) const
bool ParseDateTime(LocaleMgrIface *pLocaleMgr, const WideString &wsSrcDateTime, DateTimeType eDateTimeType, CFX_DateTime *dtValue) const
bool ParseNull(const WideString &wsSrcText) const
bool ParseZero(const WideString &wsSrcText) const
bool FormatDateTime(LocaleMgrIface *pLocaleMgr, const WideString &wsSrcDateTime, DateTimeType eDateTimeType, WideString *wsOutput) const
bool FormatNum(LocaleMgrIface *pLocaleMgr, const WideString &wsSrcNum, WideString *wsOutput) const
bool ParseNum(LocaleMgrIface *pLocaleMgr, const WideString &wsSrcNum, WideString *wsValue) const
CFGAS_StringFormatter(const WideString &wsPattern)
int32_t GetYear() const
void SetDate(int32_t year, uint8_t month, uint8_t day)
uint8_t GetSecond() const
uint8_t GetHour() const
uint8_t GetDay() const
uint8_t GetMinute() const
void SetTime(uint8_t hour, uint8_t minute, uint8_t second, uint16_t millisecond)
uint16_t GetMillisecond() const
uint8_t GetMonth() const
virtual WideString GetDayName(int32_t nWeek, bool bAbbr) const =0
virtual WideString GetDecimalSymbol() const =0
virtual WideString GetMonthName(int32_t nMonth, bool bAbbr) const =0
virtual WideString GetNumPattern(NumSubcategory eType) const =0
virtual WideString GetDatePattern(DateTimeSubcategory eType) const =0
virtual int GetTimeZoneInMinutes() const =0
virtual WideString GetMeridiemName(bool bAM) const =0
virtual WideString GetCurrencySymbol() const =0
virtual WideString GetTimePattern(DateTimeSubcategory eType) const =0
virtual WideString GetPercentSymbol() const =0
virtual WideString GetGroupingSymbol() const =0
virtual WideString GetEraName(bool bAD) const =0
virtual WideString GetMinusSymbol() const =0
virtual LocaleIface * GetLocaleByName(const WideString &wsLCID)=0
virtual LocaleIface * GetDefLocale()=0
WideString & operator+=(const WideString &str)
static WideString Format(const wchar_t *pFormat,...)
WideString & operator=(WideString &&that) noexcept
WideString & operator+=(const wchar_t *str)
bool operator==(const wchar_t *ptr) const
WideString & operator+=(wchar_t ch)
void TrimLeft(wchar_t target)
bool EqualsASCIINoCase(ByteStringView that) const
Definition widestring.h:219
CharType operator[](const size_t index) const
Definition widestring.h:146
bool IsEmpty() const
Definition widestring.h:118
const wchar_t * c_str() const
Definition widestring.h:81
WideString & operator=(const WideString &that)
bool operator!=(const wchar_t *ptr) const
Definition widestring.h:138
bool EqualsASCII(ByteStringView that) const
Definition widestring.h:216
bool FXSYS_IsHexDigit(char c)
int FXSYS_HexCharToInt(char c)
WideString operator+(const WideString &str1, const WideString &str2)
Definition widestring.h:269
WideString operator+(wchar_t ch, const WideString &str2)
Definition widestring.h:275
WideString operator+(const wchar_t *str1, const WideString &str2)
Definition widestring.h:281