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