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