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
32 namespace {
33
34 struct LocaleDateTimeSubcategoryWithHash {
35 uint32_t uHash; // Hashed as wide string.
36 LocaleIface::DateTimeSubcategory eSubCategory;
37 };
38
39 struct 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
46 constexpr 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
54 constexpr 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
62 struct FX_LOCALETIMEZONEINFO {
63 const wchar_t* name;
64 int16_t iHour;
65 int16_t iMinute;
66 };
67
68 constexpr 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
73 constexpr wchar_t kTimeSymbols[] = L"hHkKMSFAzZ";
74 constexpr wchar_t kDateSymbols[] = L"DJMEeGgYwW";
75 constexpr wchar_t kConstChars[] = L",-:/. ";
76
77 constexpr wchar_t kDateStr[] = L"date";
78 constexpr wchar_t kTimeStr[] = L"time";
79 constexpr wchar_t kDateTimeStr[] = L"datetime";
80 constexpr wchar_t kNumStr[] = L"num";
81 constexpr wchar_t kTextStr[] = L"text";
82 constexpr wchar_t kZeroStr[] = L"zero";
83 constexpr wchar_t kNullStr[] = L"null";
84
ParseTimeZone(pdfium::span<const wchar_t> spStr,int * tz)85 size_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
ConvertHex(int32_t iKeyValue,wchar_t ch)114 int32_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
GetLiteralText(pdfium::span<const wchar_t> spStrPattern,size_t * iPattern)120 WideString 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
GetLiteralTextReverse(pdfium::span<const wchar_t> spStrPattern,size_t * iPattern)156 WideString 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
GetNumericDotIndex(const WideString & wsNum,const WideString & wsDotSymbol,size_t * iDotIndex)196 bool 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
ExtractCountDigits(pdfium::span<const wchar_t> spStr,size_t count,size_t * cc,uint32_t * value)217 bool 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
ExtractCountDigitsWithOptional(pdfium::span<const wchar_t> spStr,int count,size_t * cc,uint32_t * value)229 bool 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
ParseLocaleDate(const WideString & wsDate,const WideString & wsDatePattern,LocaleIface * pLocale,CFX_DateTime * datetime,size_t * cc)239 bool 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
ResolveZone(int tz_diff_minutes,const LocaleIface * pLocale,uint32_t * wHour,uint32_t * wMinute)339 void 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
ParseLocaleTime(const WideString & wsTime,const WideString & wsTimePattern,LocaleIface * pLocale,CFX_DateTime * datetime,size_t * cc)355 bool 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
GetNumTrailingLimit(const WideString & wsFormat,size_t iDotPos,bool * bTrimTailZeros)490 size_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
IsLeapYear(uint32_t year)505 bool IsLeapYear(uint32_t year) {
506 return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
507 }
508
MonthHas30Days(uint32_t month)509 bool MonthHas30Days(uint32_t month) {
510 return month == 4 || month == 6 || month == 9 || month == 11;
511 }
512
MonthHas31Days(uint32_t month)513 bool MonthHas31Days(uint32_t month) {
514 return month != 2 && !MonthHas30Days(month);
515 }
516
517 // |month| is 1-based. e.g. 1 means January.
GetSolarMonthDays(uint16_t year,uint16_t month)518 uint16_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
GetWeekDay(uint16_t year,uint16_t month,uint16_t day)525 uint16_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
GetWeekOfMonth(uint16_t year,uint16_t month,uint16_t day)535 uint16_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
GetWeekOfYear(uint16_t year,uint16_t month,uint16_t day)545 uint16_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
NumToString(size_t fmt_size,int32_t value)560 WideString 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
DateFormat(const WideString & wsDatePattern,LocaleIface * pLocale,const CFX_DateTime & datetime)565 WideString 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
TimeFormat(const WideString & wsTimePattern,LocaleIface * pLocale,const CFX_DateTime & datetime)625 WideString 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
FormatDateTimeInternal(const CFX_DateTime & dt,const WideString & wsDatePattern,const WideString & wsTimePattern,bool bDateFirst,LocaleIface * pLocale)694 WideString 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
HasDate(CFGAS_StringFormatter::DateTimeType type)710 bool HasDate(CFGAS_StringFormatter::DateTimeType type) {
711 return type == CFGAS_StringFormatter::DateTimeType::kDate ||
712 type == CFGAS_StringFormatter::DateTimeType::kDateTime ||
713 type == CFGAS_StringFormatter::DateTimeType::kTimeDate;
714 }
715
HasTime(CFGAS_StringFormatter::DateTimeType type)716 bool HasTime(CFGAS_StringFormatter::DateTimeType type) {
717 return type == CFGAS_StringFormatter::DateTimeType::kTime ||
718 type == CFGAS_StringFormatter::DateTimeType::kDateTime ||
719 type == CFGAS_StringFormatter::DateTimeType::kTimeDate;
720 }
721
AddDateToDatelessType(CFGAS_StringFormatter::DateTimeType type)722 CFGAS_StringFormatter::DateTimeType AddDateToDatelessType(
723 CFGAS_StringFormatter::DateTimeType type) {
724 switch (type) {
725 case CFGAS_StringFormatter::DateTimeType::kUnknown:
726 return CFGAS_StringFormatter::DateTimeType::kDate;
727 case CFGAS_StringFormatter::DateTimeType::kTime:
728 return CFGAS_StringFormatter::DateTimeType::kTimeDate;
729 default:
730 NOTREACHED();
731 return type;
732 }
733 }
734
AddTimeToTimelessType(CFGAS_StringFormatter::DateTimeType type)735 CFGAS_StringFormatter::DateTimeType AddTimeToTimelessType(
736 CFGAS_StringFormatter::DateTimeType type) {
737 switch (type) {
738 case CFGAS_StringFormatter::DateTimeType::kUnknown:
739 return CFGAS_StringFormatter::DateTimeType::kTime;
740 case CFGAS_StringFormatter::DateTimeType::kDate:
741 return CFGAS_StringFormatter::DateTimeType::kDateTime;
742 default:
743 NOTREACHED();
744 return type;
745 }
746 }
747
748 } // namespace
749
FX_DateFromCanonical(pdfium::span<const wchar_t> spDate,CFX_DateTime * datetime)750 bool FX_DateFromCanonical(pdfium::span<const wchar_t> spDate,
751 CFX_DateTime* datetime) {
752 if (spDate.size() > 10)
753 return false;
754
755 size_t cc = 0;
756 uint32_t year = 0;
757 if (!ExtractCountDigits(spDate, 4, &cc, &year))
758 return false;
759 if (year < 1900)
760 return false;
761 if (cc >= spDate.size()) {
762 datetime->SetDate(year, 1, 1);
763 return true;
764 }
765
766 if (spDate[cc] == '-')
767 cc++;
768
769 uint32_t month = 0;
770 if (!ExtractCountDigits(spDate, 2, &cc, &month) || month < 1 || month > 12)
771 return false;
772
773 if (cc >= spDate.size()) {
774 datetime->SetDate(year, month, 1);
775 return true;
776 }
777
778 if (spDate[cc] == '-')
779 cc++;
780
781 uint32_t day = 0;
782 if (!ExtractCountDigits(spDate, 2, &cc, &day))
783 return false;
784 if (day < 1)
785 return false;
786 if ((MonthHas31Days(month) && day > 31) ||
787 (MonthHas30Days(month) && day > 30)) {
788 return false;
789 }
790 if (month == 2 && day > (IsLeapYear(year) ? 29U : 28U))
791 return false;
792
793 datetime->SetDate(year, month, day);
794 return true;
795 }
796
FX_TimeFromCanonical(const LocaleIface * pLocale,pdfium::span<const wchar_t> spTime,CFX_DateTime * datetime)797 bool FX_TimeFromCanonical(const LocaleIface* pLocale,
798 pdfium::span<const wchar_t> spTime,
799 CFX_DateTime* datetime) {
800 if (spTime.empty())
801 return false;
802
803 size_t cc = 0;
804 uint32_t hour = 0;
805 if (!ExtractCountDigits(spTime, 2, &cc, &hour) || hour >= 24)
806 return false;
807
808 if (cc >= spTime.size()) {
809 datetime->SetTime(hour, 0, 0, 0);
810 return true;
811 }
812
813 if (spTime[cc] == ':')
814 cc++;
815
816 uint32_t minute = 0;
817 if (!ExtractCountDigits(spTime, 2, &cc, &minute) || minute >= 60)
818 return false;
819
820 if (cc >= spTime.size()) {
821 datetime->SetTime(hour, minute, 0, 0);
822 return true;
823 }
824
825 if (spTime[cc] == ':')
826 cc++;
827
828 uint32_t second = 0;
829 uint32_t millisecond = 0;
830 if (cc < spTime.size() && spTime[cc] != 'Z') {
831 if (!ExtractCountDigits(spTime, 2, &cc, &second) || second >= 60)
832 return false;
833
834 if (cc < spTime.size() && spTime[cc] == '.') {
835 cc++;
836 if (!ExtractCountDigits(spTime, 3, &cc, &millisecond))
837 return false;
838 }
839 }
840
841 // Skip until we find a + or - for the time zone.
842 while (cc < spTime.size()) {
843 if (spTime[cc] == '+' || spTime[cc] == '-')
844 break;
845 ++cc;
846 }
847
848 if (cc < spTime.size()) {
849 int tz_diff_minutes = 0;
850 if (spTime[cc] != 'Z')
851 cc += ParseTimeZone(spTime.subspan(cc), &tz_diff_minutes);
852 ResolveZone(tz_diff_minutes, pLocale, &hour, &minute);
853 }
854
855 datetime->SetTime(hour, minute, second, millisecond);
856 return true;
857 }
858
CFGAS_StringFormatter(const WideString & wsPattern)859 CFGAS_StringFormatter::CFGAS_StringFormatter(const WideString& wsPattern)
860 : m_wsPattern(wsPattern), m_spPattern(m_wsPattern.span()) {}
861
862 CFGAS_StringFormatter::~CFGAS_StringFormatter() = default;
863
864 // static
SplitOnBars(const WideString & wsFormatString)865 std::vector<WideString> CFGAS_StringFormatter::SplitOnBars(
866 const WideString& wsFormatString) {
867 std::vector<WideString> wsPatterns;
868 pdfium::span<const wchar_t> spFormatString = wsFormatString.span();
869 size_t index = 0;
870 size_t token = 0;
871 bool bQuote = false;
872 for (; index < spFormatString.size(); ++index) {
873 if (spFormatString[index] == '\'') {
874 bQuote = !bQuote;
875 } else if (spFormatString[index] == L'|' && !bQuote) {
876 wsPatterns.emplace_back(spFormatString.data() + token, index - token);
877 token = index + 1;
878 }
879 }
880 wsPatterns.emplace_back(spFormatString.data() + token, index - token);
881 return wsPatterns;
882 }
883
GetCategory() const884 CFGAS_StringFormatter::Category CFGAS_StringFormatter::GetCategory() const {
885 Category eCategory = Category::kUnknown;
886 size_t ccf = 0;
887 bool bBraceOpen = false;
888 while (ccf < m_spPattern.size()) {
889 if (m_spPattern[ccf] == '\'') {
890 GetLiteralText(m_spPattern, &ccf);
891 } else if (!bBraceOpen &&
892 !pdfium::Contains(kConstChars, m_spPattern[ccf])) {
893 WideString wsCategory(m_spPattern[ccf]);
894 ccf++;
895 while (true) {
896 if (ccf >= m_spPattern.size())
897 return eCategory;
898 if (m_spPattern[ccf] == '.' || m_spPattern[ccf] == '(')
899 break;
900 if (m_spPattern[ccf] == '{') {
901 bBraceOpen = true;
902 break;
903 }
904 wsCategory += m_spPattern[ccf];
905 ccf++;
906 }
907 if (wsCategory == kDateTimeStr)
908 return Category::kDateTime;
909 if (wsCategory == kTextStr)
910 return Category::kText;
911 if (wsCategory == kNumStr)
912 return Category::kNum;
913 if (wsCategory == kZeroStr)
914 return Category::kZero;
915 if (wsCategory == kNullStr)
916 return Category::kNull;
917 if (wsCategory == kDateStr) {
918 if (eCategory == Category::kTime)
919 return Category::kDateTime;
920 eCategory = Category::kDate;
921 } else if (wsCategory == kTimeStr) {
922 if (eCategory == Category::kDate)
923 return Category::kDateTime;
924 eCategory = Category::kTime;
925 }
926 } else if (m_spPattern[ccf] == '}') {
927 bBraceOpen = false;
928 }
929 ccf++;
930 }
931 return eCategory;
932 }
933
GetTextFormat(WideStringView wsCategory) const934 WideString CFGAS_StringFormatter::GetTextFormat(
935 WideStringView wsCategory) const {
936 size_t ccf = 0;
937 bool bBrackOpen = false;
938 WideString wsPurgePattern;
939 while (ccf < m_spPattern.size()) {
940 if (m_spPattern[ccf] == '\'') {
941 size_t iCurChar = ccf;
942 GetLiteralText(m_spPattern, &ccf);
943 wsPurgePattern +=
944 WideStringView(m_spPattern.data() + iCurChar, ccf - iCurChar + 1);
945 } else if (!bBrackOpen &&
946 !pdfium::Contains(kConstChars, m_spPattern[ccf])) {
947 WideString wsSearchCategory(m_spPattern[ccf]);
948 ccf++;
949 while (ccf < m_spPattern.size() && m_spPattern[ccf] != '{' &&
950 m_spPattern[ccf] != '.' && m_spPattern[ccf] != '(') {
951 wsSearchCategory += m_spPattern[ccf];
952 ccf++;
953 }
954 if (wsSearchCategory != wsCategory)
955 continue;
956
957 while (ccf < m_spPattern.size()) {
958 if (m_spPattern[ccf] == '(') {
959 ccf++;
960 // Skip over the encoding name.
961 while (ccf < m_spPattern.size() && m_spPattern[ccf] != ')')
962 ccf++;
963 } else if (m_spPattern[ccf] == '{') {
964 bBrackOpen = true;
965 break;
966 }
967 ccf++;
968 }
969 } else if (m_spPattern[ccf] != '}') {
970 wsPurgePattern += m_spPattern[ccf];
971 }
972 ccf++;
973 }
974 if (!bBrackOpen)
975 wsPurgePattern = m_wsPattern;
976
977 return wsPurgePattern;
978 }
979
GetNumericFormat(LocaleMgrIface * pLocaleMgr,size_t * iDotIndex,uint32_t * dwStyle,WideString * wsPurgePattern) const980 LocaleIface* CFGAS_StringFormatter::GetNumericFormat(
981 LocaleMgrIface* pLocaleMgr,
982 size_t* iDotIndex,
983 uint32_t* dwStyle,
984 WideString* wsPurgePattern) const {
985 *dwStyle = 0;
986 LocaleIface* pLocale = nullptr;
987 size_t ccf = 0;
988 bool bFindDot = false;
989 bool bBrackOpen = false;
990 while (ccf < m_spPattern.size()) {
991 if (m_spPattern[ccf] == '\'') {
992 size_t iCurChar = ccf;
993 GetLiteralText(m_spPattern, &ccf);
994 *wsPurgePattern +=
995 WideStringView(m_spPattern.data() + iCurChar, ccf - iCurChar + 1);
996 } else if (!bBrackOpen &&
997 !pdfium::Contains(kConstChars, m_spPattern[ccf])) {
998 WideString wsCategory(m_spPattern[ccf]);
999 ccf++;
1000 while (ccf < m_spPattern.size() && m_spPattern[ccf] != '{' &&
1001 m_spPattern[ccf] != '.' && m_spPattern[ccf] != '(') {
1002 wsCategory += m_spPattern[ccf];
1003 ccf++;
1004 }
1005 if (!wsCategory.EqualsASCII("num")) {
1006 bBrackOpen = true;
1007 ccf = 0;
1008 continue;
1009 }
1010 while (ccf < m_spPattern.size()) {
1011 if (m_spPattern[ccf] == '{') {
1012 bBrackOpen = true;
1013 break;
1014 }
1015 if (m_spPattern[ccf] == '(') {
1016 ccf++;
1017 WideString wsLCID;
1018 while (ccf < m_spPattern.size() && m_spPattern[ccf] != ')')
1019 wsLCID += m_spPattern[ccf++];
1020
1021 pLocale = pLocaleMgr->GetLocaleByName(wsLCID);
1022 } else if (m_spPattern[ccf] == '.') {
1023 WideString wsSubCategory;
1024 ccf++;
1025 while (ccf < m_spPattern.size() && m_spPattern[ccf] != '(' &&
1026 m_spPattern[ccf] != '{') {
1027 wsSubCategory += m_spPattern[ccf++];
1028 }
1029 uint32_t dwSubHash = FX_HashCode_GetW(wsSubCategory.AsStringView());
1030 LocaleIface::NumSubcategory eSubCategory =
1031 LocaleIface::NumSubcategory::kDecimal;
1032 for (const auto& data : kLocaleNumSubcategoryData) {
1033 if (data.uHash == dwSubHash) {
1034 eSubCategory = data.eSubCategory;
1035 break;
1036 }
1037 }
1038 if (!pLocale)
1039 pLocale = pLocaleMgr->GetDefLocale();
1040
1041 wsSubCategory = pLocale->GetNumPattern(eSubCategory);
1042 auto result = wsSubCategory.Find('.');
1043 if (result.has_value() && result.value() != 0) {
1044 if (!bFindDot)
1045 *iDotIndex = wsPurgePattern->GetLength() + result.value();
1046 bFindDot = true;
1047 *dwStyle |= FX_NUMSTYLE_DotVorv;
1048 }
1049 *wsPurgePattern += wsSubCategory;
1050 if (eSubCategory == LocaleIface::NumSubcategory::kPercent)
1051 *dwStyle |= FX_NUMSTYLE_Percent;
1052 continue;
1053 }
1054 ccf++;
1055 }
1056 } else if (m_spPattern[ccf] == 'E') {
1057 *dwStyle |= FX_NUMSTYLE_Exponent;
1058 *wsPurgePattern += m_spPattern[ccf];
1059 } else if (m_spPattern[ccf] == '%') {
1060 *dwStyle |= FX_NUMSTYLE_Percent;
1061 *wsPurgePattern += m_spPattern[ccf];
1062 } else if (m_spPattern[ccf] != '}') {
1063 *wsPurgePattern += m_spPattern[ccf];
1064 }
1065 if (!bFindDot && ccf < m_spPattern.size() &&
1066 (m_spPattern[ccf] == '.' || m_spPattern[ccf] == 'V' ||
1067 m_spPattern[ccf] == 'v')) {
1068 bFindDot = true;
1069 *iDotIndex = wsPurgePattern->GetLength() - 1;
1070 *dwStyle |= FX_NUMSTYLE_DotVorv;
1071 }
1072 ccf++;
1073 }
1074 if (!bFindDot)
1075 *iDotIndex = wsPurgePattern->GetLength();
1076 if (!pLocale)
1077 pLocale = pLocaleMgr->GetDefLocale();
1078 return pLocale;
1079 }
1080
ParseText(const WideString & wsSrcText,WideString * wsValue) const1081 bool CFGAS_StringFormatter::ParseText(const WideString& wsSrcText,
1082 WideString* wsValue) const {
1083 wsValue->clear();
1084 if (wsSrcText.IsEmpty() || m_spPattern.empty())
1085 return false;
1086
1087 WideString wsTextFormat = GetTextFormat(L"text");
1088 if (wsTextFormat.IsEmpty())
1089 return false;
1090
1091 pdfium::span<const wchar_t> spSrcText = wsSrcText.span();
1092 pdfium::span<const wchar_t> spTextFormat = wsTextFormat.span();
1093
1094 size_t iText = 0;
1095 size_t iPattern = 0;
1096 while (iPattern < spTextFormat.size() && iText < spSrcText.size()) {
1097 switch (spTextFormat[iPattern]) {
1098 case '\'': {
1099 WideString wsLiteral = GetLiteralText(spTextFormat, &iPattern);
1100 size_t iLiteralLen = wsLiteral.GetLength();
1101 if (iText + iLiteralLen > spSrcText.size() ||
1102 wcsncmp(spSrcText.data() + iText, wsLiteral.c_str(), iLiteralLen) !=
1103 0) {
1104 *wsValue = wsSrcText;
1105 return false;
1106 }
1107 iText += iLiteralLen;
1108 iPattern++;
1109 break;
1110 }
1111 case 'A':
1112 if (FXSYS_iswalpha(spSrcText[iText])) {
1113 *wsValue += spSrcText[iText];
1114 iText++;
1115 }
1116 iPattern++;
1117 break;
1118 case 'X':
1119 *wsValue += spSrcText[iText];
1120 iText++;
1121 iPattern++;
1122 break;
1123 case 'O':
1124 case '0':
1125 if (FXSYS_IsDecimalDigit(spSrcText[iText]) ||
1126 FXSYS_iswalpha(spSrcText[iText])) {
1127 *wsValue += spSrcText[iText];
1128 iText++;
1129 }
1130 iPattern++;
1131 break;
1132 case '9':
1133 if (FXSYS_IsDecimalDigit(spSrcText[iText])) {
1134 *wsValue += spSrcText[iText];
1135 iText++;
1136 }
1137 iPattern++;
1138 break;
1139 default:
1140 if (spTextFormat[iPattern] != spSrcText[iText]) {
1141 *wsValue = wsSrcText;
1142 return false;
1143 }
1144 iPattern++;
1145 iText++;
1146 break;
1147 }
1148 }
1149 return iPattern == spTextFormat.size() && iText == spSrcText.size();
1150 }
1151
ParseNum(LocaleMgrIface * pLocaleMgr,const WideString & wsSrcNum,WideString * wsValue) const1152 bool CFGAS_StringFormatter::ParseNum(LocaleMgrIface* pLocaleMgr,
1153 const WideString& wsSrcNum,
1154 WideString* wsValue) const {
1155 wsValue->clear();
1156 if (wsSrcNum.IsEmpty() || m_spPattern.empty())
1157 return false;
1158
1159 size_t dot_index_f = m_spPattern.size();
1160 uint32_t dwFormatStyle = 0;
1161 WideString wsNumFormat;
1162 LocaleIface* pLocale =
1163 GetNumericFormat(pLocaleMgr, &dot_index_f, &dwFormatStyle, &wsNumFormat);
1164 if (!pLocale || wsNumFormat.IsEmpty())
1165 return false;
1166
1167 int32_t iExponent = 0;
1168 WideString wsDotSymbol = pLocale->GetDecimalSymbol();
1169 WideString wsGroupSymbol = pLocale->GetGroupingSymbol();
1170 WideString wsMinus = pLocale->GetMinusSymbol();
1171 size_t iGroupLen = wsGroupSymbol.GetLength();
1172 size_t iMinusLen = wsMinus.GetLength();
1173
1174 pdfium::span<const wchar_t> spSrcNum = wsSrcNum.span();
1175 pdfium::span<const wchar_t> spNumFormat = wsNumFormat.span();
1176
1177 bool bHavePercentSymbol = false;
1178 bool bNeg = false;
1179 bool bReverseParse = false;
1180 size_t dot_index = 0;
1181
1182 // If we're looking for a '.', 'V' or 'v' and the input string does not
1183 // have a dot index for one of those, then we disable parsing the decimal.
1184 if (!GetNumericDotIndex(wsSrcNum, wsDotSymbol, &dot_index) &&
1185 (dwFormatStyle & FX_NUMSTYLE_DotVorv))
1186 bReverseParse = true;
1187
1188 // This parse is broken into two parts based on the '.' in the number
1189 // (or 'V' or 'v'). |dot_index_f| is the location of the dot in the format and
1190 // |dot_index| is the location of the dot in the number.
1191 //
1192 // This first while() starts at the '.' and walks backwards to the start of
1193 // the number. The second while() walks from the dot forwards to the end of
1194 // the decimal.
1195
1196 size_t cc = dot_index - 1;
1197 size_t ccf = dot_index_f - 1;
1198 while (ccf < spNumFormat.size() && cc < spSrcNum.size()) {
1199 switch (spNumFormat[ccf]) {
1200 case '\'': {
1201 WideString wsLiteral = GetLiteralTextReverse(spNumFormat, &ccf);
1202 size_t iLiteralLen = wsLiteral.GetLength();
1203 cc -= iLiteralLen - 1;
1204 if (cc >= spSrcNum.size() ||
1205 wcsncmp(spSrcNum.data() + cc, wsLiteral.c_str(), iLiteralLen) !=
1206 0) {
1207 return false;
1208 }
1209 cc--;
1210 ccf--;
1211 break;
1212 }
1213 case '9':
1214 if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
1215 return false;
1216
1217 wsValue->InsertAtFront(spSrcNum[cc]);
1218 cc--;
1219 ccf--;
1220 break;
1221 case 'z':
1222 case 'Z':
1223 if (spNumFormat[ccf] == 'z' || spSrcNum[cc] != ' ') {
1224 if (FXSYS_IsDecimalDigit(spSrcNum[cc])) {
1225 wsValue->InsertAtFront(spSrcNum[cc]);
1226 cc--;
1227 }
1228 } else {
1229 cc--;
1230 }
1231 ccf--;
1232 break;
1233 case 'S':
1234 case 's':
1235 if (spSrcNum[cc] == '+' ||
1236 (spNumFormat[ccf] == 'S' && spSrcNum[cc] == ' ')) {
1237 cc--;
1238 } else {
1239 cc -= iMinusLen - 1;
1240 if (cc >= spSrcNum.size() ||
1241 wcsncmp(spSrcNum.data() + cc, wsMinus.c_str(), iMinusLen) != 0) {
1242 return false;
1243 }
1244 cc--;
1245 bNeg = true;
1246 }
1247 ccf--;
1248 break;
1249 case 'E': {
1250 iExponent = 0;
1251 bool bExpSign = false;
1252 while (cc < spSrcNum.size()) {
1253 if (spSrcNum[cc] == 'E' || spSrcNum[cc] == 'e')
1254 break;
1255 if (FXSYS_IsDecimalDigit(spSrcNum[cc])) {
1256 if (iExponent > std::numeric_limits<int>::max() / 10)
1257 return false;
1258 iExponent = iExponent + FXSYS_DecimalCharToInt(spSrcNum[cc]) * 10;
1259 cc--;
1260 continue;
1261 }
1262 if (spSrcNum[cc] == '+') {
1263 cc--;
1264 continue;
1265 }
1266 if (cc - iMinusLen + 1 <= spSrcNum.size() &&
1267 wcsncmp(spSrcNum.data() + (cc - iMinusLen + 1), wsMinus.c_str(),
1268 iMinusLen) == 0) {
1269 bExpSign = true;
1270 cc -= iMinusLen;
1271 continue;
1272 }
1273
1274 return false;
1275 }
1276 cc--;
1277 iExponent = bExpSign ? -iExponent : iExponent;
1278 ccf--;
1279 break;
1280 }
1281 case '$': {
1282 WideString wsSymbol = pLocale->GetCurrencySymbol();
1283 size_t iSymbolLen = wsSymbol.GetLength();
1284 cc -= iSymbolLen - 1;
1285 if (cc >= spSrcNum.size() ||
1286 wcsncmp(spSrcNum.data() + cc, wsSymbol.c_str(), iSymbolLen) != 0) {
1287 return false;
1288 }
1289 cc--;
1290 ccf--;
1291 break;
1292 }
1293 case 'r':
1294 case 'R':
1295 if (ccf - 1 < spNumFormat.size() &&
1296 ((spNumFormat[ccf] == 'R' && spNumFormat[ccf - 1] == 'C') ||
1297 (spNumFormat[ccf] == 'r' && spNumFormat[ccf - 1] == 'c'))) {
1298 if (spNumFormat[ccf] == 'R' && spSrcNum[cc] == ' ') {
1299 cc -= 2;
1300 } else if (spSrcNum[cc] == 'R' && cc - 1 < spSrcNum.size() &&
1301 spSrcNum[cc - 1] == 'C') {
1302 bNeg = true;
1303 cc -= 2;
1304 }
1305 ccf -= 2;
1306 } else {
1307 ccf--;
1308 }
1309 break;
1310 case 'b':
1311 case 'B':
1312 if (ccf - 1 < spNumFormat.size() &&
1313 ((spNumFormat[ccf] == 'B' && spNumFormat[ccf - 1] == 'D') ||
1314 (spNumFormat[ccf] == 'b' && spNumFormat[ccf - 1] == 'd'))) {
1315 if (spNumFormat[ccf] == 'B' && spSrcNum[cc] == ' ') {
1316 cc -= 2;
1317 } else if (spSrcNum[cc] == 'B' && cc - 1 < spSrcNum.size() &&
1318 spSrcNum[cc - 1] == 'D') {
1319 bNeg = true;
1320 cc -= 2;
1321 }
1322 ccf -= 2;
1323 } else {
1324 ccf--;
1325 }
1326 break;
1327 case '%': {
1328 WideString wsSymbol = pLocale->GetPercentSymbol();
1329 size_t iSymbolLen = wsSymbol.GetLength();
1330 cc -= iSymbolLen - 1;
1331 if (cc >= spSrcNum.size() ||
1332 wcsncmp(spSrcNum.data() + cc, wsSymbol.c_str(), iSymbolLen) != 0) {
1333 return false;
1334 }
1335 cc--;
1336 ccf--;
1337 bHavePercentSymbol = true;
1338 break;
1339 }
1340 case '.':
1341 case 'V':
1342 case 'v':
1343 case '8':
1344 return false;
1345 case ',': {
1346 if (cc < spSrcNum.size()) {
1347 cc -= iGroupLen - 1;
1348 if (cc < spSrcNum.size() &&
1349 wcsncmp(spSrcNum.data() + cc, wsGroupSymbol.c_str(), iGroupLen) ==
1350 0) {
1351 cc--;
1352 } else {
1353 cc += iGroupLen - 1;
1354 }
1355 }
1356 ccf--;
1357 break;
1358 }
1359 case '(':
1360 case ')':
1361 if (spSrcNum[cc] == spNumFormat[ccf])
1362 bNeg = true;
1363 else if (spSrcNum[cc] != L' ')
1364 return false;
1365
1366 cc--;
1367 ccf--;
1368 break;
1369 default:
1370 if (spNumFormat[ccf] != spSrcNum[cc])
1371 return false;
1372
1373 cc--;
1374 ccf--;
1375 }
1376 }
1377 if (cc < spSrcNum.size()) {
1378 if (spSrcNum[cc] == '-') {
1379 bNeg = true;
1380 cc--;
1381 }
1382 if (cc < spSrcNum.size())
1383 return false;
1384 }
1385 if ((dwFormatStyle & FX_NUMSTYLE_DotVorv) && dot_index < spSrcNum.size())
1386 *wsValue += '.';
1387
1388 if (!bReverseParse) {
1389 cc = (dot_index == spSrcNum.size()) ? spSrcNum.size() : dot_index + 1;
1390 for (ccf = dot_index_f + 1;
1391 cc < spSrcNum.size() && ccf < spNumFormat.size(); ++ccf) {
1392 switch (spNumFormat[ccf]) {
1393 case '\'': {
1394 WideString wsLiteral = GetLiteralText(spNumFormat, &ccf);
1395 size_t iLiteralLen = wsLiteral.GetLength();
1396 if (cc + iLiteralLen > spSrcNum.size() ||
1397 wcsncmp(spSrcNum.data() + cc, wsLiteral.c_str(), iLiteralLen) !=
1398 0) {
1399 return false;
1400 }
1401 cc += iLiteralLen;
1402 break;
1403 }
1404 case '9':
1405 if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
1406 return false;
1407
1408 *wsValue += spSrcNum[cc];
1409 cc++;
1410 break;
1411 case 'z':
1412 case 'Z':
1413 if (spNumFormat[ccf] == 'z' || spSrcNum[cc] != ' ') {
1414 if (FXSYS_IsDecimalDigit(spSrcNum[cc])) {
1415 *wsValue += spSrcNum[cc];
1416 cc++;
1417 }
1418 } else {
1419 cc++;
1420 }
1421 break;
1422 case 'S':
1423 case 's':
1424 if (spSrcNum[cc] == '+' ||
1425 (spNumFormat[ccf] == 'S' && spSrcNum[cc] == ' ')) {
1426 cc++;
1427 } else {
1428 if (cc + iMinusLen > spSrcNum.size() ||
1429 wcsncmp(spSrcNum.data() + cc, wsMinus.c_str(), iMinusLen) !=
1430 0) {
1431 return false;
1432 }
1433 bNeg = true;
1434 cc += iMinusLen;
1435 }
1436 break;
1437 case 'E': {
1438 if (cc >= spSrcNum.size() ||
1439 (spSrcNum[cc] != 'E' && spSrcNum[cc] != 'e')) {
1440 return false;
1441 }
1442 iExponent = 0;
1443 bool bExpSign = false;
1444 cc++;
1445 if (cc < spSrcNum.size()) {
1446 if (spSrcNum[cc] == '+') {
1447 cc++;
1448 } else if (spSrcNum[cc] == '-') {
1449 bExpSign = true;
1450 cc++;
1451 }
1452 }
1453 while (cc < spSrcNum.size()) {
1454 if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
1455 break;
1456 int digit = FXSYS_DecimalCharToInt(spSrcNum[cc]);
1457 if (iExponent > (std::numeric_limits<int>::max() - digit) / 10)
1458 return false;
1459 iExponent = iExponent * 10 + digit;
1460 cc++;
1461 }
1462 iExponent = bExpSign ? -iExponent : iExponent;
1463 break;
1464 }
1465 case '$': {
1466 WideString wsSymbol = pLocale->GetCurrencySymbol();
1467 size_t iSymbolLen = wsSymbol.GetLength();
1468 if (cc + iSymbolLen > spSrcNum.size() ||
1469 wcsncmp(spSrcNum.data() + cc, wsSymbol.c_str(), iSymbolLen) !=
1470 0) {
1471 return false;
1472 }
1473 cc += iSymbolLen;
1474 break;
1475 }
1476 case 'c':
1477 case 'C':
1478 if (ccf + 1 < spNumFormat.size() &&
1479 ((spNumFormat[ccf] == 'C' && spNumFormat[ccf + 1] == 'R') ||
1480 (spNumFormat[ccf] == 'c' && spNumFormat[ccf + 1] == 'r'))) {
1481 if (spNumFormat[ccf] == 'C' && spSrcNum[cc] == ' ') {
1482 cc++;
1483 } else if (spSrcNum[cc] == 'C' && cc + 1 < spSrcNum.size() &&
1484 spSrcNum[cc + 1] == 'R') {
1485 bNeg = true;
1486 cc += 2;
1487 }
1488 ccf++;
1489 }
1490 break;
1491 case 'd':
1492 case 'D':
1493 if (ccf + 1 < spNumFormat.size() &&
1494 ((spNumFormat[ccf] == 'D' && spNumFormat[ccf + 1] == 'B') ||
1495 (spNumFormat[ccf] == 'd' && spNumFormat[ccf + 1] == 'b'))) {
1496 if (spNumFormat[ccf] == 'D' && spSrcNum[cc] == ' ') {
1497 cc++;
1498 } else if (spSrcNum[cc] == 'D' && cc + 1 < spSrcNum.size() &&
1499 spSrcNum[cc + 1] == 'B') {
1500 bNeg = true;
1501 cc += 2;
1502 }
1503 ccf++;
1504 }
1505 break;
1506 case '.':
1507 case 'V':
1508 case 'v':
1509 return false;
1510 case '%': {
1511 WideString wsSymbol = pLocale->GetPercentSymbol();
1512 size_t iSymbolLen = wsSymbol.GetLength();
1513 if (cc + iSymbolLen <= spSrcNum.size() &&
1514 wcsncmp(spSrcNum.data() + cc, wsSymbol.c_str(), iSymbolLen) ==
1515 0) {
1516 cc += iSymbolLen;
1517 }
1518 bHavePercentSymbol = true;
1519 } break;
1520 case '8': {
1521 while (ccf + 1 < spNumFormat.size() && spNumFormat[ccf + 1] == '8')
1522 ccf++;
1523
1524 while (cc < spSrcNum.size() && FXSYS_IsDecimalDigit(spSrcNum[cc])) {
1525 *wsValue += spSrcNum[cc];
1526 cc++;
1527 }
1528 } break;
1529 case ',': {
1530 if (cc + iGroupLen <= spSrcNum.size() &&
1531 wcsncmp(spSrcNum.data() + cc, wsGroupSymbol.c_str(), iGroupLen) ==
1532 0) {
1533 cc += iGroupLen;
1534 }
1535 break;
1536 }
1537 case '(':
1538 case ')':
1539 if (spSrcNum[cc] == spNumFormat[ccf])
1540 bNeg = true;
1541 else if (spSrcNum[cc] != L' ')
1542 return false;
1543
1544 cc++;
1545 break;
1546 default:
1547 if (spNumFormat[ccf] != spSrcNum[cc])
1548 return false;
1549
1550 cc++;
1551 }
1552 }
1553 if (cc != spSrcNum.size())
1554 return false;
1555 }
1556 if (iExponent || bHavePercentSymbol) {
1557 CFGAS_Decimal decimal = CFGAS_Decimal(wsValue->AsStringView());
1558 if (iExponent)
1559 decimal = decimal * CFGAS_Decimal(powf(10, iExponent), 3);
1560 if (bHavePercentSymbol)
1561 decimal = decimal / CFGAS_Decimal(100);
1562 *wsValue = decimal.ToWideString();
1563 }
1564 if (bNeg)
1565 wsValue->InsertAtFront(L'-');
1566
1567 return true;
1568 }
1569
GetDateTimeFormat(LocaleMgrIface * pLocaleMgr,LocaleIface ** pLocale,WideString * wsDatePattern,WideString * wsTimePattern) const1570 CFGAS_StringFormatter::DateTimeType CFGAS_StringFormatter::GetDateTimeFormat(
1571 LocaleMgrIface* pLocaleMgr,
1572 LocaleIface** pLocale,
1573 WideString* wsDatePattern,
1574 WideString* wsTimePattern) const {
1575 *pLocale = nullptr;
1576 WideString wsTempPattern;
1577 Category eCategory = Category::kUnknown;
1578 DateTimeType eDateTimeType = DateTimeType::kUnknown;
1579 size_t ccf = 0;
1580 bool bBraceOpen = false;
1581 while (ccf < m_spPattern.size()) {
1582 if (m_spPattern[ccf] == '\'') {
1583 size_t iCurChar = ccf;
1584 GetLiteralText(m_spPattern, &ccf);
1585 wsTempPattern +=
1586 WideStringView(m_spPattern.data() + iCurChar, ccf - iCurChar + 1);
1587 } else if (!bBraceOpen && eDateTimeType != DateTimeType::kDateTime &&
1588 !pdfium::Contains(kConstChars, m_spPattern[ccf])) {
1589 WideString wsCategory(m_spPattern[ccf]);
1590 ccf++;
1591 while (ccf < m_spPattern.size() && m_spPattern[ccf] != '{' &&
1592 m_spPattern[ccf] != '.' && m_spPattern[ccf] != '(') {
1593 if (m_spPattern[ccf] == 'T') {
1594 *wsDatePattern = m_wsPattern.First(ccf);
1595 *wsTimePattern = m_wsPattern.Last(m_wsPattern.GetLength() - ccf);
1596 wsTimePattern->SetAt(0, ' ');
1597 if (!*pLocale)
1598 *pLocale = pLocaleMgr->GetDefLocale();
1599 return DateTimeType::kDateTime;
1600 }
1601 wsCategory += m_spPattern[ccf];
1602 ccf++;
1603 }
1604 if (!HasDate(eDateTimeType) && wsCategory.EqualsASCII("date")) {
1605 eDateTimeType = AddDateToDatelessType(eDateTimeType);
1606 eCategory = Category::kDate;
1607 } else if (!HasTime(eDateTimeType) && wsCategory.EqualsASCII("time")) {
1608 eDateTimeType = AddTimeToTimelessType(eDateTimeType);
1609 eCategory = Category::kTime;
1610 } else if (wsCategory.EqualsASCII("datetime")) {
1611 eDateTimeType = DateTimeType::kDateTime;
1612 eCategory = Category::kDateTime;
1613 } else {
1614 continue;
1615 }
1616 while (ccf < m_spPattern.size()) {
1617 if (m_spPattern[ccf] == '{') {
1618 bBraceOpen = true;
1619 break;
1620 }
1621 if (m_spPattern[ccf] == '(') {
1622 ccf++;
1623 WideString wsLCID;
1624 while (ccf < m_spPattern.size() && m_spPattern[ccf] != ')')
1625 wsLCID += m_spPattern[ccf++];
1626
1627 *pLocale = pLocaleMgr->GetLocaleByName(wsLCID);
1628 } else if (m_spPattern[ccf] == '.') {
1629 WideString wsSubCategory;
1630 ccf++;
1631 while (ccf < m_spPattern.size() && m_spPattern[ccf] != '(' &&
1632 m_spPattern[ccf] != '{')
1633 wsSubCategory += m_spPattern[ccf++];
1634
1635 uint32_t dwSubHash = FX_HashCode_GetW(wsSubCategory.AsStringView());
1636 LocaleIface::DateTimeSubcategory eSubCategory =
1637 LocaleIface::DateTimeSubcategory::kMedium;
1638 for (const auto& data : kLocaleDateTimeSubcategoryData) {
1639 if (data.uHash == dwSubHash) {
1640 eSubCategory = data.eSubCategory;
1641 break;
1642 }
1643 }
1644 if (!*pLocale)
1645 *pLocale = pLocaleMgr->GetDefLocale();
1646
1647 switch (eCategory) {
1648 case Category::kDate:
1649 *wsDatePattern =
1650 wsTempPattern + (*pLocale)->GetDatePattern(eSubCategory);
1651 break;
1652 case Category::kTime:
1653 *wsTimePattern =
1654 wsTempPattern + (*pLocale)->GetTimePattern(eSubCategory);
1655 break;
1656 case Category::kDateTime:
1657 *wsDatePattern =
1658 wsTempPattern + (*pLocale)->GetDatePattern(eSubCategory);
1659 *wsTimePattern = (*pLocale)->GetTimePattern(eSubCategory);
1660 break;
1661 default:
1662 break;
1663 }
1664 wsTempPattern.clear();
1665 continue;
1666 }
1667 ccf++;
1668 }
1669 } else if (m_spPattern[ccf] == '}') {
1670 bBraceOpen = false;
1671 if (!wsTempPattern.IsEmpty()) {
1672 if (eCategory == Category::kTime)
1673 *wsTimePattern = std::move(wsTempPattern);
1674 else if (eCategory == Category::kDate)
1675 *wsDatePattern = std::move(wsTempPattern);
1676 else
1677 wsTempPattern.clear();
1678 }
1679 } else {
1680 wsTempPattern += m_spPattern[ccf];
1681 }
1682 ccf++;
1683 }
1684
1685 if (!wsTempPattern.IsEmpty()) {
1686 if (eCategory == Category::kDate)
1687 *wsDatePattern += wsTempPattern;
1688 else
1689 *wsTimePattern += wsTempPattern;
1690 }
1691 if (!*pLocale)
1692 *pLocale = pLocaleMgr->GetDefLocale();
1693 if (eDateTimeType == DateTimeType::kUnknown) {
1694 wsTimePattern->clear();
1695 *wsDatePattern = m_wsPattern;
1696 }
1697 return eDateTimeType;
1698 }
1699
ParseDateTime(LocaleMgrIface * pLocaleMgr,const WideString & wsSrcDateTime,DateTimeType eDateTimeType,CFX_DateTime * dtValue) const1700 bool CFGAS_StringFormatter::ParseDateTime(LocaleMgrIface* pLocaleMgr,
1701 const WideString& wsSrcDateTime,
1702 DateTimeType eDateTimeType,
1703 CFX_DateTime* dtValue) const {
1704 dtValue->Reset();
1705 if (wsSrcDateTime.IsEmpty() || m_spPattern.empty())
1706 return false;
1707
1708 LocaleIface* pLocale = nullptr;
1709 WideString wsDatePattern;
1710 WideString wsTimePattern;
1711 DateTimeType eCategory =
1712 GetDateTimeFormat(pLocaleMgr, &pLocale, &wsDatePattern, &wsTimePattern);
1713 if (!pLocale)
1714 return false;
1715
1716 if (eCategory == DateTimeType::kUnknown)
1717 eCategory = eDateTimeType;
1718
1719 size_t iStart = 0;
1720 switch (eCategory) {
1721 case DateTimeType::kDate:
1722 return ParseLocaleDate(wsSrcDateTime, wsDatePattern, pLocale, dtValue,
1723 &iStart);
1724 case DateTimeType::kTime:
1725 return ParseLocaleTime(wsSrcDateTime, wsTimePattern, pLocale, dtValue,
1726 &iStart);
1727 case DateTimeType::kDateTime:
1728 return ParseLocaleDate(wsSrcDateTime, wsTimePattern, pLocale, dtValue,
1729 &iStart) &&
1730 ParseLocaleTime(wsSrcDateTime, wsDatePattern, pLocale, dtValue,
1731 &iStart);
1732 case DateTimeType::kTimeDate:
1733 return ParseLocaleTime(wsSrcDateTime, wsTimePattern, pLocale, dtValue,
1734 &iStart) &&
1735 ParseLocaleDate(wsSrcDateTime, wsDatePattern, pLocale, dtValue,
1736 &iStart);
1737 case DateTimeType::kUnknown:
1738 default:
1739 return false;
1740 }
1741 }
1742
ParseZero(const WideString & wsSrcText) const1743 bool CFGAS_StringFormatter::ParseZero(const WideString& wsSrcText) const {
1744 WideString wsTextFormat = GetTextFormat(L"zero");
1745 pdfium::span<const wchar_t> spSrcText = wsSrcText.span();
1746 pdfium::span<const wchar_t> spTextFormat = wsTextFormat.span();
1747
1748 size_t iText = 0;
1749 size_t iPattern = 0;
1750 while (iPattern < spTextFormat.size() && iText < spSrcText.size()) {
1751 if (spTextFormat[iPattern] == '\'') {
1752 WideString wsLiteral = GetLiteralText(spTextFormat, &iPattern);
1753 size_t iLiteralLen = wsLiteral.GetLength();
1754 if (iText + iLiteralLen > spSrcText.size() ||
1755 wcsncmp(spSrcText.data() + iText, wsLiteral.c_str(), iLiteralLen)) {
1756 return false;
1757 }
1758 iText += iLiteralLen;
1759 iPattern++;
1760 continue;
1761 }
1762 if (spTextFormat[iPattern] != spSrcText[iText])
1763 return false;
1764
1765 iText++;
1766 iPattern++;
1767 }
1768 return iPattern == spTextFormat.size() && iText == spSrcText.size();
1769 }
1770
ParseNull(const WideString & wsSrcText) const1771 bool CFGAS_StringFormatter::ParseNull(const WideString& wsSrcText) const {
1772 WideString wsTextFormat = GetTextFormat(L"null");
1773 pdfium::span<const wchar_t> spSrcText = wsSrcText.span();
1774 pdfium::span<const wchar_t> spTextFormat = wsTextFormat.span();
1775
1776 size_t iText = 0;
1777 size_t iPattern = 0;
1778 while (iPattern < spTextFormat.size() && iText < spSrcText.size()) {
1779 if (spTextFormat[iPattern] == '\'') {
1780 WideString wsLiteral = GetLiteralText(spTextFormat, &iPattern);
1781 size_t iLiteralLen = wsLiteral.GetLength();
1782 if (iText + iLiteralLen > spSrcText.size() ||
1783 wcsncmp(spSrcText.data() + iText, wsLiteral.c_str(), iLiteralLen)) {
1784 return false;
1785 }
1786 iText += iLiteralLen;
1787 iPattern++;
1788 continue;
1789 }
1790 if (spTextFormat[iPattern] != spSrcText[iText])
1791 return false;
1792
1793 iText++;
1794 iPattern++;
1795 }
1796 return iPattern == spTextFormat.size() && iText == spSrcText.size();
1797 }
1798
FormatText(const WideString & wsSrcText,WideString * wsOutput) const1799 bool CFGAS_StringFormatter::FormatText(const WideString& wsSrcText,
1800 WideString* wsOutput) const {
1801 if (wsSrcText.IsEmpty() || m_spPattern.empty())
1802 return false;
1803
1804 WideString wsTextFormat = GetTextFormat(L"text");
1805 pdfium::span<const wchar_t> spSrcText = wsSrcText.span();
1806 pdfium::span<const wchar_t> spTextFormat = wsTextFormat.span();
1807
1808 size_t iText = 0;
1809 size_t iPattern = 0;
1810 while (iPattern < spTextFormat.size()) {
1811 switch (spTextFormat[iPattern]) {
1812 case '\'': {
1813 *wsOutput += GetLiteralText(spTextFormat, &iPattern);
1814 iPattern++;
1815 break;
1816 }
1817 case 'A':
1818 if (iText >= spSrcText.size() || !FXSYS_iswalpha(spSrcText[iText]))
1819 return false;
1820
1821 *wsOutput += spSrcText[iText++];
1822 iPattern++;
1823 break;
1824 case 'X':
1825 if (iText >= spSrcText.size())
1826 return false;
1827
1828 *wsOutput += spSrcText[iText++];
1829 iPattern++;
1830 break;
1831 case 'O':
1832 case '0':
1833 if (iText >= spSrcText.size() ||
1834 (!FXSYS_IsDecimalDigit(spSrcText[iText]) &&
1835 !FXSYS_iswalpha(spSrcText[iText]))) {
1836 return false;
1837 }
1838 *wsOutput += spSrcText[iText++];
1839 iPattern++;
1840 break;
1841 case '9':
1842 if (iText >= spSrcText.size() ||
1843 !FXSYS_IsDecimalDigit(spSrcText[iText]))
1844 return false;
1845
1846 *wsOutput += spSrcText[iText++];
1847 iPattern++;
1848 break;
1849 default:
1850 *wsOutput += spTextFormat[iPattern++];
1851 break;
1852 }
1853 }
1854 return iText == spSrcText.size();
1855 }
1856
FormatNum(LocaleMgrIface * pLocaleMgr,const WideString & wsInputNum,WideString * wsOutput) const1857 bool CFGAS_StringFormatter::FormatNum(LocaleMgrIface* pLocaleMgr,
1858 const WideString& wsInputNum,
1859 WideString* wsOutput) const {
1860 if (wsInputNum.IsEmpty() || m_spPattern.empty())
1861 return false;
1862
1863 size_t dot_index_f = m_spPattern.size();
1864 uint32_t dwNumStyle = 0;
1865 WideString wsNumFormat;
1866 LocaleIface* pLocale =
1867 GetNumericFormat(pLocaleMgr, &dot_index_f, &dwNumStyle, &wsNumFormat);
1868 if (!pLocale || wsNumFormat.IsEmpty())
1869 return false;
1870
1871 pdfium::span<const wchar_t> spNumFormat = wsNumFormat.span();
1872 WideString wsSrcNum = wsInputNum;
1873 wsSrcNum.TrimLeft('0');
1874 if (wsSrcNum.IsEmpty() || wsSrcNum[0] == '.')
1875 wsSrcNum.InsertAtFront('0');
1876
1877 CFGAS_Decimal decimal = CFGAS_Decimal(wsSrcNum.AsStringView());
1878 if (dwNumStyle & FX_NUMSTYLE_Percent) {
1879 decimal = decimal * CFGAS_Decimal(100);
1880 wsSrcNum = decimal.ToWideString();
1881 }
1882
1883 int32_t exponent = 0;
1884 if (dwNumStyle & FX_NUMSTYLE_Exponent) {
1885 int fixed_count = 0;
1886 for (size_t ccf = 0; ccf < dot_index_f; ++ccf) {
1887 switch (spNumFormat[ccf]) {
1888 case '\'':
1889 GetLiteralText(spNumFormat, &ccf);
1890 break;
1891 case '9':
1892 case 'z':
1893 case 'Z':
1894 fixed_count++;
1895 break;
1896 }
1897 }
1898
1899 FX_SAFE_UINT32 threshold = 1;
1900 while (fixed_count > 1) {
1901 threshold *= 10;
1902 fixed_count--;
1903 }
1904 if (!threshold.IsValid())
1905 return false;
1906
1907 bool bAdjusted = false;
1908 while (decimal.IsNotZero() &&
1909 fabs(decimal.ToDouble()) < threshold.ValueOrDie()) {
1910 decimal = decimal * CFGAS_Decimal(10);
1911 --exponent;
1912 bAdjusted = true;
1913 }
1914 if (!bAdjusted) {
1915 threshold *= 10;
1916 if (!threshold.IsValid())
1917 return false;
1918
1919 while (decimal.IsNotZero() &&
1920 fabs(decimal.ToDouble()) > threshold.ValueOrDie()) {
1921 decimal = decimal / CFGAS_Decimal(10);
1922 ++exponent;
1923 }
1924 }
1925 }
1926
1927 bool bTrimTailZeros = false;
1928 size_t iTreading =
1929 GetNumTrailingLimit(wsNumFormat, dot_index_f, &bTrimTailZeros);
1930 uint8_t scale = decimal.GetScale();
1931 if (iTreading < scale) {
1932 decimal.SetScale(iTreading);
1933 wsSrcNum = decimal.ToWideString();
1934 }
1935 if (bTrimTailZeros && scale > 0 && iTreading > 0) {
1936 wsSrcNum.TrimRight(L"0");
1937 wsSrcNum.TrimRight(L".");
1938 }
1939
1940 WideString wsGroupSymbol = pLocale->GetGroupingSymbol();
1941 bool bNeg = false;
1942 if (wsSrcNum[0] == '-') {
1943 bNeg = true;
1944 wsSrcNum.Delete(0, 1);
1945 }
1946
1947 bool bAddNeg = false;
1948 pdfium::span<const wchar_t> spSrcNum = wsSrcNum.span();
1949 auto dot_index = wsSrcNum.Find('.');
1950 if (!dot_index.has_value())
1951 dot_index = spSrcNum.size();
1952
1953 size_t cc = dot_index.value() - 1;
1954 for (size_t ccf = dot_index_f - 1; ccf < spNumFormat.size(); --ccf) {
1955 switch (spNumFormat[ccf]) {
1956 case '9':
1957 if (cc < spSrcNum.size()) {
1958 if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
1959 return false;
1960 wsOutput->InsertAtFront(spSrcNum[cc]);
1961 cc--;
1962 } else {
1963 wsOutput->InsertAtFront(L'0');
1964 }
1965 break;
1966 case 'z':
1967 if (cc < spSrcNum.size()) {
1968 if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
1969 return false;
1970 if (spSrcNum[0] != '0')
1971 wsOutput->InsertAtFront(spSrcNum[cc]);
1972 cc--;
1973 }
1974 break;
1975 case 'Z':
1976 if (cc < spSrcNum.size()) {
1977 if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
1978 return false;
1979 wsOutput->InsertAtFront(spSrcNum[0] == '0' ? L' ' : spSrcNum[cc]);
1980 cc--;
1981 } else {
1982 wsOutput->InsertAtFront(L' ');
1983 }
1984 break;
1985 case 'S':
1986 if (bNeg) {
1987 *wsOutput = pLocale->GetMinusSymbol() + *wsOutput;
1988 bAddNeg = true;
1989 } else {
1990 wsOutput->InsertAtFront(L' ');
1991 }
1992 break;
1993 case 's':
1994 if (bNeg) {
1995 *wsOutput = pLocale->GetMinusSymbol() + *wsOutput;
1996 bAddNeg = true;
1997 }
1998 break;
1999 case 'E':
2000 *wsOutput = WideString::Format(L"E%+d", exponent) + *wsOutput;
2001 break;
2002 case '$':
2003 *wsOutput = pLocale->GetCurrencySymbol() + *wsOutput;
2004 break;
2005 case 'r':
2006 if (ccf - 1 < spNumFormat.size() && spNumFormat[ccf - 1] == 'c') {
2007 if (bNeg)
2008 *wsOutput = L"CR" + *wsOutput;
2009 ccf--;
2010 bAddNeg = true;
2011 } else {
2012 wsOutput->InsertAtFront('r');
2013 }
2014 break;
2015 case 'R':
2016 if (ccf - 1 < spNumFormat.size() && spNumFormat[ccf - 1] == 'C') {
2017 *wsOutput = bNeg ? L"CR" : L" " + *wsOutput;
2018 ccf--;
2019 bAddNeg = true;
2020 } else {
2021 wsOutput->InsertAtFront('R');
2022 }
2023 break;
2024 case 'b':
2025 if (ccf - 1 < spNumFormat.size() && spNumFormat[ccf - 1] == 'd') {
2026 if (bNeg)
2027 *wsOutput = L"db" + *wsOutput;
2028 ccf--;
2029 bAddNeg = true;
2030 } else {
2031 wsOutput->InsertAtFront('b');
2032 }
2033 break;
2034 case 'B':
2035 if (ccf - 1 < spNumFormat.size() && spNumFormat[ccf - 1] == 'D') {
2036 *wsOutput = bNeg ? L"DB" : L" " + *wsOutput;
2037 ccf--;
2038 bAddNeg = true;
2039 } else {
2040 wsOutput->InsertAtFront('B');
2041 }
2042 break;
2043 case '%':
2044 *wsOutput = pLocale->GetPercentSymbol() + *wsOutput;
2045 break;
2046 case ',':
2047 if (cc < spSrcNum.size())
2048 *wsOutput = wsGroupSymbol + *wsOutput;
2049 break;
2050 case '(':
2051 wsOutput->InsertAtFront(bNeg ? L'(' : L' ');
2052 bAddNeg = true;
2053 break;
2054 case ')':
2055 wsOutput->InsertAtFront(bNeg ? L')' : L' ');
2056 break;
2057 case '\'':
2058 *wsOutput = GetLiteralTextReverse(spNumFormat, &ccf) + *wsOutput;
2059 break;
2060 default:
2061 wsOutput->InsertAtFront(spNumFormat[ccf]);
2062 break;
2063 }
2064 }
2065
2066 if (cc < spSrcNum.size()) {
2067 size_t nPos = dot_index.value() % 3;
2068 wsOutput->clear();
2069 for (size_t i = 0; i < dot_index.value(); i++) {
2070 if (i % 3 == nPos && i != 0)
2071 *wsOutput += wsGroupSymbol;
2072 *wsOutput += wsSrcNum[i];
2073 }
2074 if (dot_index.value() < spSrcNum.size()) {
2075 *wsOutput += pLocale->GetDecimalSymbol();
2076 *wsOutput += wsSrcNum.Last(spSrcNum.size() - dot_index.value() - 1);
2077 }
2078 if (bNeg)
2079 *wsOutput = pLocale->GetMinusSymbol() + *wsOutput;
2080 return true;
2081 }
2082 if (dot_index_f == wsNumFormat.GetLength()) {
2083 if (!bAddNeg && bNeg)
2084 *wsOutput = pLocale->GetMinusSymbol() + *wsOutput;
2085 return true;
2086 }
2087
2088 WideString wsDotSymbol = pLocale->GetDecimalSymbol();
2089 if (spNumFormat[dot_index_f] == 'V') {
2090 *wsOutput += wsDotSymbol;
2091 } else if (spNumFormat[dot_index_f] == '.') {
2092 if (dot_index.value() < spSrcNum.size()) {
2093 *wsOutput += wsDotSymbol;
2094 } else if (dot_index_f + 1 < spNumFormat.size() &&
2095 (spNumFormat[dot_index_f + 1] == '9' ||
2096 spNumFormat[dot_index_f + 1] == 'Z')) {
2097 *wsOutput += wsDotSymbol;
2098 }
2099 }
2100
2101 cc = dot_index.value() + 1;
2102 for (size_t ccf = dot_index_f + 1; ccf < spNumFormat.size(); ++ccf) {
2103 switch (spNumFormat[ccf]) {
2104 case '\'':
2105 *wsOutput += GetLiteralText(spNumFormat, &ccf);
2106 break;
2107 case '9':
2108 if (cc < spSrcNum.size()) {
2109 if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
2110 return false;
2111 *wsOutput += spSrcNum[cc];
2112 cc++;
2113 } else {
2114 *wsOutput += L'0';
2115 }
2116 break;
2117 case 'z':
2118 if (cc < spSrcNum.size()) {
2119 if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
2120 return false;
2121 *wsOutput += spSrcNum[cc];
2122 cc++;
2123 }
2124 break;
2125 case 'Z':
2126 if (cc < spSrcNum.size()) {
2127 if (!FXSYS_IsDecimalDigit(spSrcNum[cc]))
2128 return false;
2129 *wsOutput += spSrcNum[cc];
2130 cc++;
2131 } else {
2132 *wsOutput += L'0';
2133 }
2134 break;
2135 case 'E': {
2136 *wsOutput += WideString::Format(L"E%+d", exponent);
2137 break;
2138 }
2139 case '$':
2140 *wsOutput += pLocale->GetCurrencySymbol();
2141 break;
2142 case 'c':
2143 if (ccf + 1 < spNumFormat.size() && spNumFormat[ccf + 1] == 'r') {
2144 if (bNeg)
2145 *wsOutput += L"CR";
2146 ccf++;
2147 bAddNeg = true;
2148 }
2149 break;
2150 case 'C':
2151 if (ccf + 1 < spNumFormat.size() && spNumFormat[ccf + 1] == 'R') {
2152 *wsOutput += bNeg ? L"CR" : L" ";
2153 ccf++;
2154 bAddNeg = true;
2155 }
2156 break;
2157 case 'd':
2158 if (ccf + 1 < spNumFormat.size() && spNumFormat[ccf + 1] == 'b') {
2159 if (bNeg)
2160 *wsOutput += L"db";
2161 ccf++;
2162 bAddNeg = true;
2163 }
2164 break;
2165 case 'D':
2166 if (ccf + 1 < spNumFormat.size() && spNumFormat[ccf + 1] == 'B') {
2167 *wsOutput += bNeg ? L"DB" : L" ";
2168 ccf++;
2169 bAddNeg = true;
2170 }
2171 break;
2172 case '%':
2173 *wsOutput += pLocale->GetPercentSymbol();
2174 break;
2175 case '8':
2176 while (ccf + 1 < spNumFormat.size() && spNumFormat[ccf + 1] == '8')
2177 ccf++;
2178 while (cc < spSrcNum.size() && FXSYS_IsDecimalDigit(spSrcNum[cc])) {
2179 *wsOutput += spSrcNum[cc];
2180 cc++;
2181 }
2182 break;
2183 case ',':
2184 *wsOutput += wsGroupSymbol;
2185 break;
2186 case '(':
2187 *wsOutput += bNeg ? '(' : ' ';
2188 bAddNeg = true;
2189 break;
2190 case ')':
2191 *wsOutput += bNeg ? ')' : ' ';
2192 break;
2193 default:
2194 break;
2195 }
2196 }
2197 if (!bAddNeg && bNeg)
2198 *wsOutput = pLocale->GetMinusSymbol() + *wsOutput;
2199
2200 return true;
2201 }
2202
FormatDateTime(LocaleMgrIface * pLocaleMgr,const WideString & wsSrcDateTime,DateTimeType eDateTimeType,WideString * wsOutput) const2203 bool CFGAS_StringFormatter::FormatDateTime(LocaleMgrIface* pLocaleMgr,
2204 const WideString& wsSrcDateTime,
2205 DateTimeType eDateTimeType,
2206 WideString* wsOutput) const {
2207 if (wsSrcDateTime.IsEmpty() || m_spPattern.empty())
2208 return false;
2209
2210 WideString wsDatePattern;
2211 WideString wsTimePattern;
2212 LocaleIface* pLocale = nullptr;
2213 DateTimeType eCategory =
2214 GetDateTimeFormat(pLocaleMgr, &pLocale, &wsDatePattern, &wsTimePattern);
2215 if (!pLocale)
2216 return false;
2217
2218 if (eCategory == DateTimeType::kUnknown) {
2219 if (eDateTimeType == DateTimeType::kTime) {
2220 wsTimePattern = std::move(wsDatePattern);
2221 wsDatePattern = WideString();
2222 }
2223 eCategory = eDateTimeType;
2224 if (eCategory == DateTimeType::kUnknown)
2225 return false;
2226 }
2227
2228 CFX_DateTime dt;
2229 auto iT = wsSrcDateTime.Find(L"T");
2230 if (!iT.has_value()) {
2231 if (eCategory == DateTimeType::kDate &&
2232 FX_DateFromCanonical(wsSrcDateTime.span(), &dt)) {
2233 *wsOutput = FormatDateTimeInternal(dt, wsDatePattern, wsTimePattern, true,
2234 pLocale);
2235 return true;
2236 }
2237 if (eCategory == DateTimeType::kTime &&
2238 FX_TimeFromCanonical(pLocale, wsSrcDateTime.span(), &dt)) {
2239 *wsOutput = FormatDateTimeInternal(dt, wsDatePattern, wsTimePattern, true,
2240 pLocale);
2241 return true;
2242 }
2243 } else {
2244 pdfium::span<const wchar_t> wsSrcDate =
2245 wsSrcDateTime.span().first(iT.value());
2246 pdfium::span<const wchar_t> wsSrcTime =
2247 wsSrcDateTime.span().subspan(iT.value() + 1);
2248 if (wsSrcDate.empty() || wsSrcTime.empty())
2249 return false;
2250
2251 if (FX_DateFromCanonical(wsSrcDate, &dt) &&
2252 FX_TimeFromCanonical(pLocale, wsSrcTime, &dt)) {
2253 *wsOutput =
2254 FormatDateTimeInternal(dt, wsDatePattern, wsTimePattern,
2255 eCategory != DateTimeType::kTimeDate, pLocale);
2256 return true;
2257 }
2258 }
2259 return false;
2260 }
2261
FormatZero(WideString * wsOutput) const2262 bool CFGAS_StringFormatter::FormatZero(WideString* wsOutput) const {
2263 if (m_spPattern.empty())
2264 return false;
2265
2266 WideString wsTextFormat = GetTextFormat(L"zero");
2267 pdfium::span<const wchar_t> spTextFormat = wsTextFormat.span();
2268 for (size_t iPattern = 0; iPattern < spTextFormat.size(); ++iPattern) {
2269 if (spTextFormat[iPattern] == '\'') {
2270 *wsOutput += GetLiteralText(spTextFormat, &iPattern);
2271 continue;
2272 }
2273 *wsOutput += spTextFormat[iPattern];
2274 }
2275 return true;
2276 }
2277
FormatNull(WideString * wsOutput) const2278 bool CFGAS_StringFormatter::FormatNull(WideString* wsOutput) const {
2279 if (m_spPattern.empty())
2280 return false;
2281
2282 WideString wsTextFormat = GetTextFormat(L"null");
2283 pdfium::span<const wchar_t> spTextFormat = wsTextFormat.span();
2284 for (size_t iPattern = 0; iPattern < spTextFormat.size(); ++iPattern) {
2285 if (spTextFormat[iPattern] == '\'') {
2286 *wsOutput += GetLiteralText(spTextFormat, &iPattern);
2287 continue;
2288 }
2289 *wsOutput += spTextFormat[iPattern];
2290 }
2291 return true;
2292 }
2293