Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
cjs_publicmethods.cpp
Go to the documentation of this file.
1// Copyright 2014 The PDFium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7#include "fxjs/cjs_publicmethods.h"
8
9#include <math.h>
10
11#include <algorithm>
12#include <iomanip>
13#include <iterator>
14#include <limits>
15#include <sstream>
16#include <string>
17#include <utility>
18#include <vector>
19
20#include "build/build_config.h"
21#include "core/fpdfapi/parser/cpdf_stream.h"
22#include "core/fpdfdoc/cpdf_formcontrol.h"
23#include "core/fpdfdoc/cpdf_interactiveform.h"
24#include "core/fxcrt/fx_extension.h"
25#include "core/fxcrt/fx_string_wrappers.h"
26#include "core/fxge/cfx_color.h"
27#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
28#include "fpdfsdk/cpdfsdk_interactiveform.h"
29#include "fxjs/cjs_color.h"
30#include "fxjs/cjs_event_context.h"
31#include "fxjs/cjs_field.h"
32#include "fxjs/cjs_object.h"
33#include "fxjs/cjs_runtime.h"
34#include "fxjs/cjs_util.h"
35#include "fxjs/fx_date_helpers.h"
36#include "fxjs/js_define.h"
37#include "fxjs/js_resources.h"
38#include "third_party/abseil-cpp/absl/types/optional.h"
39#include "third_party/base/check.h"
40#include "third_party/base/containers/span.h"
41#include "third_party/base/numerics/safe_conversions.h"
42#include "v8/include/v8-container.h"
43
44// static
45const JSMethodSpec CJS_PublicMethods::GlobalFunctionSpecs[] = {
46 {"AFDate_Format", AFDate_Format_static},
47 {"AFDate_FormatEx", AFDate_FormatEx_static},
48 {"AFDate_Keystroke", AFDate_Keystroke_static},
49 {"AFDate_KeystrokeEx", AFDate_KeystrokeEx_static},
50 {"AFExtractNums", AFExtractNums_static},
51 {"AFMakeNumber", AFMakeNumber_static},
52 {"AFMergeChange", AFMergeChange_static},
53 {"AFNumber_Format", AFNumber_Format_static},
54 {"AFNumber_Keystroke", AFNumber_Keystroke_static},
55 {"AFParseDateEx", AFParseDateEx_static},
56 {"AFPercent_Format", AFPercent_Format_static},
57 {"AFPercent_Keystroke", AFPercent_Keystroke_static},
58 {"AFRange_Validate", AFRange_Validate_static},
59 {"AFSimple", AFSimple_static},
60 {"AFSimple_Calculate", AFSimple_Calculate_static},
61 {"AFSpecial_Format", AFSpecial_Format_static},
62 {"AFSpecial_Keystroke", AFSpecial_Keystroke_static},
63 {"AFSpecial_KeystrokeEx", AFSpecial_KeystrokeEx_static},
64 {"AFTime_Format", AFTime_Format_static},
65 {"AFTime_FormatEx", AFTime_FormatEx_static},
66 {"AFTime_Keystroke", AFTime_Keystroke_static},
67 {"AFTime_KeystrokeEx", AFTime_KeystrokeEx_static},
68};
69
70namespace {
71
72#if !BUILDFLAG(IS_ANDROID)
73constexpr double kDoubleCorrect = 0.000000000000001;
74#endif
75
76constexpr const wchar_t* kDateFormats[] = {L"m/d",
77 L"m/d/yy",
78 L"mm/dd/yy",
79 L"mm/yy",
80 L"d-mmm",
81 L"d-mmm-yy",
82 L"dd-mmm-yy",
83 L"yy-mm-dd",
84 L"mmm-yy",
85 L"mmmm-yy",
86 L"mmm d, yyyy",
87 L"mmmm d, yyyy",
88 L"m/d/yy h:MM tt",
89 L"m/d/yy HH:MM"};
90
91constexpr const wchar_t* kTimeFormats[] = {L"HH:MM", L"h:MM tt", L"HH:MM:ss",
92 L"h:MM:ss tt"};
93
94template <typename T>
95T StrTrim(const T& str) {
96 T result = str;
97 result.Trim(' ');
98 return result;
99}
100
101void AlertIfPossible(CJS_EventContext* pContext,
102 const WideString& wsCaller,
103 const WideString& wsMsg) {
104 CPDFSDK_FormFillEnvironment* pFormFillEnv = pContext->GetFormFillEnv();
105 if (pFormFillEnv) {
106 pFormFillEnv->JS_appAlert(wsMsg, wsCaller, JSPLATFORM_ALERT_BUTTON_OK,
108 }
109}
110
111#if !BUILDFLAG(IS_ANDROID)
112ByteString CalculateString(double dValue,
113 int iDec,
114 int* iDec2,
115 bool* bNegative) {
116 *bNegative = dValue < 0;
117 if (*bNegative)
118 dValue = -dValue;
119
120 // Make sure the number of precision characters will fit.
121 iDec = std::min(iDec, std::numeric_limits<double>::digits10);
122
123 fxcrt::ostringstream ss;
124 ss << std::fixed << std::setprecision(iDec) << dValue;
125 fxcrt::string value = ss.str();
126 size_t pos = value.find('.');
127 *iDec2 = pdfium::base::checked_cast<int>(
128 pos == fxcrt::string::npos ? value.size() : pos);
129 return ByteString(value.c_str());
130}
131#endif
132
133WideString CalcMergedString(const CJS_EventContext* event,
134 const WideString& value,
135 const WideString& change) {
136 WideString prefix = value.First(event->SelStart());
137 WideString postfix;
138 int end = event->SelEnd();
139 if (end >= 0 && static_cast<size_t>(end) < value.GetLength())
140 postfix = value.Last(value.GetLength() - static_cast<size_t>(end));
141 return prefix + change + postfix;
142}
143
144template <CJS_Result (*F)(CJS_Runtime*, pdfium::span<v8::Local<v8::Value>>)>
145void JSGlobalFunc(const char* func_name_string,
146 const v8::FunctionCallbackInfo<v8::Value>& info) {
147 CJS_Object* pObj =
148 CFXJS_Engine::GetObjectPrivate(info.GetIsolate(), info.Holder());
149 if (!pObj)
150 return;
151
152 CJS_Runtime* pRuntime = pObj->GetRuntime();
153 if (!pRuntime)
154 return;
155
156 v8::LocalVector<v8::Value> parameters(info.GetIsolate());
157 for (int i = 0; i < info.Length(); ++i)
158 parameters.push_back(info[i]);
159
160 CJS_Result result = (*F)(pRuntime, parameters);
161 if (result.HasError()) {
162 pRuntime->Error(
163 JSFormatErrorString(func_name_string, nullptr, result.Error()));
164 return;
165 }
166
167 if (result.HasReturn())
168 info.GetReturnValue().Set(result.Return());
169}
170
171int WithinBoundsOrZero(int value, size_t size) {
172 return value >= 0 && static_cast<size_t>(value) < size ? value : 0;
173}
174
175int ValidStyleOrZero(int style) {
176 return WithinBoundsOrZero(style, 4);
177}
178
179bool IsDigitSeparatorOrDecimalMark(int c) {
180 return c == '.' || c == ',';
181}
182
183#if !BUILDFLAG(IS_ANDROID)
184bool IsStyleWithDigitSeparator(int style) {
185 return style == 0 || style == 2;
186}
187
188char DigitSeparatorForStyle(int style) {
189 DCHECK(IsStyleWithDigitSeparator(style));
190 return style == 0 ? ',' : '.';
191}
192
193bool IsStyleWithApostropheSeparator(int style) {
194 return style >= 4;
195}
196#endif
197
198bool IsStyleWithCommaDecimalMark(int style) {
199 return style == 2 || style == 3;
200}
201
202char DecimalMarkForStyle(int style) {
203 return IsStyleWithCommaDecimalMark(style) ? ',' : '.';
204}
205
206#if !BUILDFLAG(IS_ANDROID)
207void NormalizeDecimalMark(ByteString* str) {
208 str->Replace(",", ".");
209}
210#endif
211
212void NormalizeDecimalMarkW(WideString* str) {
213 str->Replace(L",", L".");
214}
215
216absl::optional<double> ApplyNamedOperation(const WideString& wsFunction,
217 double dValue1,
218 double dValue2) {
219 if (wsFunction.EqualsASCIINoCase("AVG") ||
220 wsFunction.EqualsASCIINoCase("SUM")) {
221 return dValue1 + dValue2;
222 }
223 if (wsFunction.EqualsASCIINoCase("PRD"))
224 return dValue1 * dValue2;
225 if (wsFunction.EqualsASCIINoCase("MIN"))
226 return std::min(dValue1, dValue2);
227 if (wsFunction.EqualsASCIINoCase("MAX"))
228 return std::max(dValue1, dValue2);
229 return absl::nullopt;
230}
231
232} // namespace
233
234// static
235void CJS_PublicMethods::DefineJSObjects(CFXJS_Engine* pEngine) {
236 for (const auto& spec : GlobalFunctionSpecs)
237 pEngine->DefineGlobalMethod(spec.pName, spec.pMethodCall);
238}
239
240#define JS_STATIC_GLOBAL_FUN(fun_name)
241 void CJS_PublicMethods::fun_name##_static(
242 const v8::FunctionCallbackInfo<v8::Value>& info) {
243 JSGlobalFunc<fun_name>(#fun_name, info);
244 }
245
246JS_STATIC_GLOBAL_FUN(AFNumber_Format)
247JS_STATIC_GLOBAL_FUN(AFNumber_Keystroke)
248JS_STATIC_GLOBAL_FUN(AFPercent_Format)
249JS_STATIC_GLOBAL_FUN(AFPercent_Keystroke)
250JS_STATIC_GLOBAL_FUN(AFDate_FormatEx)
251JS_STATIC_GLOBAL_FUN(AFDate_KeystrokeEx)
252JS_STATIC_GLOBAL_FUN(AFDate_Format)
253JS_STATIC_GLOBAL_FUN(AFDate_Keystroke)
254JS_STATIC_GLOBAL_FUN(AFTime_FormatEx)
255JS_STATIC_GLOBAL_FUN(AFTime_KeystrokeEx)
256JS_STATIC_GLOBAL_FUN(AFTime_Format)
257JS_STATIC_GLOBAL_FUN(AFTime_Keystroke)
258JS_STATIC_GLOBAL_FUN(AFSpecial_Format)
259JS_STATIC_GLOBAL_FUN(AFSpecial_Keystroke)
260JS_STATIC_GLOBAL_FUN(AFSpecial_KeystrokeEx)
261JS_STATIC_GLOBAL_FUN(AFSimple)
262JS_STATIC_GLOBAL_FUN(AFMakeNumber)
263JS_STATIC_GLOBAL_FUN(AFSimple_Calculate)
264JS_STATIC_GLOBAL_FUN(AFRange_Validate)
265JS_STATIC_GLOBAL_FUN(AFMergeChange)
266JS_STATIC_GLOBAL_FUN(AFParseDateEx)
267JS_STATIC_GLOBAL_FUN(AFExtractNums)
268
269bool CJS_PublicMethods::IsNumber(const WideString& str) {
270 WideString sTrim = StrTrim(str);
271 const wchar_t* pTrim = sTrim.c_str();
272 const wchar_t* p = pTrim;
273 bool bDot = false;
274 bool bKXJS = false;
275
276 wchar_t c;
277 while ((c = *p) != L'\0') {
278 if (IsDigitSeparatorOrDecimalMark(c)) {
279 if (bDot)
280 return false;
281 bDot = true;
282 } else if (c == L'-' || c == L'+') {
283 if (p != pTrim)
284 return false;
285 } else if (c == L'e' || c == L'E') {
286 if (bKXJS)
287 return false;
288
289 p++;
290 c = *p;
291 if (c != L'+' && c != L'-')
292 return false;
293 bKXJS = true;
294 } else if (!FXSYS_IsDecimalDigit(c)) {
295 return false;
296 }
297 p++;
298 }
299
300 return true;
301}
302
303bool CJS_PublicMethods::MaskSatisfied(wchar_t c_Change, wchar_t c_Mask) {
304 switch (c_Mask) {
305 case L'9':
306 return !!FXSYS_IsDecimalDigit(c_Change);
307 case L'A':
308 return isascii(c_Change) && isalpha(c_Change);
309 case L'O':
310 return isascii(c_Change) && isalnum(c_Change);
311 case L'X':
312 return true;
313 default:
314 return (c_Change == c_Mask);
315 }
316}
317
318bool CJS_PublicMethods::IsReservedMaskChar(wchar_t ch) {
319 return ch == L'9' || ch == L'A' || ch == L'O' || ch == L'X';
320}
321
322v8::Local<v8::Array> CJS_PublicMethods::AF_MakeArrayFromList(
323 CJS_Runtime* pRuntime,
324 v8::Local<v8::Value> val) {
325 DCHECK(!val.IsEmpty());
326 if (val->IsArray())
327 return pRuntime->ToArray(val);
328
329 DCHECK(val->IsString());
330 ByteString bsVal = pRuntime->ToByteString(val);
331 const char* p = bsVal.c_str();
332
333 int nIndex = 0;
334 v8::Local<v8::Array> StrArray = pRuntime->NewArray();
335 while (*p) {
336 const char* pTemp = strchr(p, ',');
337 if (!pTemp) {
338 pRuntime->PutArrayElement(
339 StrArray, nIndex,
340 pRuntime->NewString(StrTrim(ByteString(p)).AsStringView()));
341 break;
342 }
343
344 pRuntime->PutArrayElement(
345 StrArray, nIndex,
346 pRuntime->NewString(StrTrim(ByteString(p, pTemp - p)).AsStringView()));
347
348 nIndex++;
349 p = ++pTemp;
350 }
351 return StrArray;
352}
353
354double CJS_PublicMethods::ParseDate(v8::Isolate* isolate,
355 const WideString& value,
356 bool* bWrongFormat) {
357 double dt = FX_GetDateTime();
358 int nYear = FX_GetYearFromTime(dt);
359 int nMonth = FX_GetMonthFromTime(dt) + 1;
360 int nDay = FX_GetDayFromTime(dt);
361 int nHour = FX_GetHourFromTime(dt);
362 int nMin = FX_GetMinFromTime(dt);
363 int nSec = FX_GetSecFromTime(dt);
364
365 int number[3];
366
367 size_t nSkip = 0;
368 size_t nLen = value.GetLength();
369 size_t nIndex = 0;
370 size_t i = 0;
371 while (i < nLen) {
372 if (nIndex > 2)
373 break;
374
375 wchar_t c = value[i];
377 number[nIndex++] = FX_ParseStringInteger(value, i, &nSkip, 4);
378 i += nSkip;
379 } else {
380 i++;
381 }
382 }
383
384 if (nIndex == 2) {
385 // TODO(thestig): Should the else case set |bWrongFormat| to true?
386 // case2: month/day
387 // case3: day/month
388 if (FX_IsValidMonth(number[0]) && FX_IsValidDay(number[1])) {
389 nMonth = number[0];
390 nDay = number[1];
391 } else if (FX_IsValidDay(number[0]) && FX_IsValidMonth(number[1])) {
392 nDay = number[0];
393 nMonth = number[1];
394 }
395
396 if (bWrongFormat)
397 *bWrongFormat = false;
398 } else if (nIndex == 3) {
399 // TODO(thestig): Should the else case set |bWrongFormat| to true?
400 // case1: year/month/day
401 // case2: month/day/year
402 // case3: day/month/year
403 if (number[0] > 12 && FX_IsValidMonth(number[1]) &&
404 FX_IsValidDay(number[2])) {
405 nYear = number[0];
406 nMonth = number[1];
407 nDay = number[2];
408 } else if (FX_IsValidMonth(number[0]) && FX_IsValidDay(number[1]) &&
409 number[2] > 31) {
410 nMonth = number[0];
411 nDay = number[1];
412 nYear = number[2];
413 } else if (FX_IsValidDay(number[0]) && FX_IsValidMonth(number[1]) &&
414 number[2] > 31) {
415 nDay = number[0];
416 nMonth = number[1];
417 nYear = number[2];
418 }
419
420 if (bWrongFormat)
421 *bWrongFormat = false;
422 } else {
423 if (bWrongFormat)
424 *bWrongFormat = true;
425 return dt;
426 }
427
428 // TODO(thestig): Should we set |bWrongFormat| to false here too?
429 return JS_DateParse(
430 isolate, WideString::Format(L"%d/%d/%d %d:%d:%d", nMonth, nDay, nYear,
431 nHour, nMin, nSec));
432}
433
434double CJS_PublicMethods::ParseDateUsingFormat(v8::Isolate* isolate,
435 const WideString& value,
436 const WideString& format,
437 bool* bWrongFormat) {
438 double dRet = nan("");
439 fxjs::ConversionStatus status = FX_ParseDateUsingFormat(value, format, &dRet);
441 return dRet;
442
443 if (status == fxjs::ConversionStatus::kBadDate) {
444 dRet = JS_DateParse(isolate, value);
445 if (!isnan(dRet))
446 return dRet;
447 }
448
449 bool bBadFormat = false;
450 dRet = ParseDate(isolate, value, &bBadFormat);
451 if (bWrongFormat)
452 *bWrongFormat = bBadFormat;
453
454 return dRet;
455}
456
457WideString CJS_PublicMethods::PrintDateUsingFormat(double dDate,
458 const WideString& format) {
459 WideString sRet;
460 WideString sPart;
461
462 int nYear = FX_GetYearFromTime(dDate);
463 int nMonth = FX_GetMonthFromTime(dDate) + 1;
464 int nDay = FX_GetDayFromTime(dDate);
465 int nHour = FX_GetHourFromTime(dDate);
466 int nMin = FX_GetMinFromTime(dDate);
467 int nSec = FX_GetSecFromTime(dDate);
468
469 size_t i = 0;
470 while (i < format.GetLength()) {
471 wchar_t c = format[i];
472 size_t remaining = format.GetLength() - i - 1;
473 sPart.clear();
474 switch (c) {
475 case 'y':
476 case 'm':
477 case 'd':
478 case 'H':
479 case 'h':
480 case 'M':
481 case 's':
482 case 't':
483 if (remaining == 0 || format[i + 1] != c) {
484 switch (c) {
485 case 'y':
486 sPart += c;
487 break;
488 case 'm':
489 sPart = WideString::FormatInteger(nMonth);
490 break;
491 case 'd':
492 sPart = WideString::FormatInteger(nDay);
493 break;
494 case 'H':
495 sPart = WideString::FormatInteger(nHour);
496 break;
497 case 'h':
498 sPart =
499 WideString::FormatInteger(nHour > 12 ? nHour - 12 : nHour);
500 break;
501 case 'M':
502 sPart = WideString::FormatInteger(nMin);
503 break;
504 case 's':
505 sPart = WideString::FormatInteger(nSec);
506 break;
507 case 't':
508 sPart += nHour > 12 ? 'p' : 'a';
509 break;
510 }
511 i++;
512 } else if (remaining == 1 || format[i + 2] != c) {
513 switch (c) {
514 case 'y':
515 sPart = WideString::Format(L"%02d", nYear - (nYear / 100) * 100);
516 break;
517 case 'm':
518 sPart = WideString::Format(L"%02d", nMonth);
519 break;
520 case 'd':
521 sPart = WideString::Format(L"%02d", nDay);
522 break;
523 case 'H':
524 sPart = WideString::Format(L"%02d", nHour);
525 break;
526 case 'h':
527 sPart =
528 WideString::Format(L"%02d", nHour > 12 ? nHour - 12 : nHour);
529 break;
530 case 'M':
531 sPart = WideString::Format(L"%02d", nMin);
532 break;
533 case 's':
534 sPart = WideString::Format(L"%02d", nSec);
535 break;
536 case 't':
537 sPart = nHour > 12 ? L"pm" : L"am";
538 break;
539 }
540 i += 2;
541 } else if (remaining == 2 || format[i + 3] != c) {
542 switch (c) {
543 case 'm':
544 i += 3;
545 if (FX_IsValidMonth(nMonth))
546 sPart += fxjs::kMonths[nMonth - 1];
547 break;
548 default:
549 i += 3;
550 sPart += c;
551 sPart += c;
552 sPart += c;
553 break;
554 }
555 } else if (remaining == 3 || format[i + 4] != c) {
556 switch (c) {
557 case 'y':
558 sPart = WideString::Format(L"%04d", nYear);
559 i += 4;
560 break;
561 case 'm':
562 i += 4;
563 if (FX_IsValidMonth(nMonth))
564 sPart += fxjs::kFullMonths[nMonth - 1];
565 break;
566 default:
567 i += 4;
568 sPart += c;
569 sPart += c;
570 sPart += c;
571 sPart += c;
572 break;
573 }
574 } else {
575 i++;
576 sPart += c;
577 }
578 break;
579 default:
580 i++;
581 sPart += c;
582 break;
583 }
584
585 sRet += sPart;
586 }
587
588 return sRet;
589}
590
591// function AFNumber_Format(nDec, sepStyle, negStyle, currStyle, strCurrency,
592// bCurrencyPrepend)
593CJS_Result CJS_PublicMethods::AFNumber_Format(
594 CJS_Runtime* pRuntime,
595 pdfium::span<v8::Local<v8::Value>> params) {
596#if !BUILDFLAG(IS_ANDROID)
597 if (params.size() != 6)
598 return CJS_Result::Failure(JSMessage::kParamError);
599
600 CJS_EventContext* pEventContext = pRuntime->GetCurrentEventContext();
601 if (!pEventContext->HasValue())
602 return CJS_Result::Failure(WideString::FromASCII("No event handler"));
603
604 WideString& Value = pEventContext->Value();
605 ByteString strValue = StrTrim(Value.ToUTF8());
606 if (strValue.IsEmpty())
607 return CJS_Result::Success();
608
609 int iDec = abs(pRuntime->ToInt32(params[0]));
610 int iSepStyle = ValidStyleOrZero(pRuntime->ToInt32(params[1]));
611 int iNegStyle = ValidStyleOrZero(pRuntime->ToInt32(params[2]));
612 // params[3] is iCurrStyle, it's not used.
613 WideString wstrCurrency = pRuntime->ToWideString(params[4]);
614 bool bCurrencyPrepend = pRuntime->ToBoolean(params[5]);
615
616 // Processing decimal places
617 NormalizeDecimalMark(&strValue);
618 double dValue = atof(strValue.c_str());
619 if (iDec > 0)
620 dValue += kDoubleCorrect;
621
622 // Calculating number string
623 bool bNegative;
624 int iDec2;
625 strValue = CalculateString(dValue, iDec, &iDec2, &bNegative);
626 if (strValue.IsEmpty()) {
627 dValue = 0;
628 strValue = CalculateString(dValue, iDec, &iDec2, &bNegative);
629 if (strValue.IsEmpty()) {
630 strValue = "0";
631 iDec2 = 1;
632 }
633 }
634 DCHECK(iDec2 >= 0);
635
636 // Processing separator style
637 if (static_cast<size_t>(iDec2) < strValue.GetLength()) {
638 if (IsStyleWithCommaDecimalMark(iSepStyle))
639 strValue.Replace(".", ",");
640
641 if (iDec2 == 0)
642 strValue.Insert(iDec2, '0');
643 }
644 if (IsStyleWithDigitSeparator(iSepStyle)) {
645 char cSeparator = DigitSeparatorForStyle(iSepStyle);
646 for (int iDecPositive = iDec2 - 3; iDecPositive > 0; iDecPositive -= 3)
647 strValue.Insert(iDecPositive, cSeparator);
648 }
649
650 // Processing currency string
651 Value = WideString::FromUTF8(strValue.AsStringView());
652 if (bCurrencyPrepend)
653 Value = wstrCurrency + Value;
654 else
655 Value = Value + wstrCurrency;
656
657 // Processing negative style
658 if (bNegative) {
659 if (iNegStyle == 0) {
660 Value.InsertAtFront(L'-');
661 } else if (iNegStyle == 2 || iNegStyle == 3) {
662 Value.InsertAtFront(L'(');
663 Value += L')';
664 }
665 if (iNegStyle == 1 || iNegStyle == 3) {
666 if (CJS_Field* fTarget = pEventContext->TargetField()) {
667 v8::Local<v8::Array> arColor = pRuntime->NewArray();
668 pRuntime->PutArrayElement(arColor, 0, pRuntime->NewString("RGB"));
669 pRuntime->PutArrayElement(arColor, 1, pRuntime->NewNumber(1));
670 pRuntime->PutArrayElement(arColor, 2, pRuntime->NewNumber(0));
671 pRuntime->PutArrayElement(arColor, 3, pRuntime->NewNumber(0));
672 fTarget->set_text_color(pRuntime, arColor);
673 }
674 }
675 } else {
676 if (iNegStyle == 1 || iNegStyle == 3) {
677 if (CJS_Field* fTarget = pEventContext->TargetField()) {
678 v8::Local<v8::Array> arColor = pRuntime->NewArray();
679 pRuntime->PutArrayElement(arColor, 0, pRuntime->NewString("RGB"));
680 pRuntime->PutArrayElement(arColor, 1, pRuntime->NewNumber(0));
681 pRuntime->PutArrayElement(arColor, 2, pRuntime->NewNumber(0));
682 pRuntime->PutArrayElement(arColor, 3, pRuntime->NewNumber(0));
683
684 CJS_Result result = fTarget->get_text_color(pRuntime);
685 CFX_Color crProp = CJS_Color::ConvertArrayToPWLColor(
686 pRuntime, pRuntime->ToArray(result.Return()));
687 CFX_Color crColor =
688 CJS_Color::ConvertArrayToPWLColor(pRuntime, arColor);
689 if (crColor != crProp)
690 fTarget->set_text_color(pRuntime, arColor);
691 }
692 }
693 }
694#endif
696}
697
698// function AFNumber_Keystroke(nDec, sepStyle, negStyle, currStyle, strCurrency,
699// bCurrencyPrepend)
700CJS_Result CJS_PublicMethods::AFNumber_Keystroke(
701 CJS_Runtime* pRuntime,
702 pdfium::span<v8::Local<v8::Value>> params) {
703 if (params.size() < 2)
705
706 CJS_EventContext* pContext = pRuntime->GetCurrentEventContext();
707 if (!pContext->HasValue())
709
710 WideString& val = pContext->Value();
711 WideString& wstrChange = pContext->Change();
712 WideString wstrValue = val;
713
714 if (pContext->WillCommit()) {
715 WideString swTemp = StrTrim(wstrValue);
716 if (swTemp.IsEmpty())
718
719 NormalizeDecimalMarkW(&swTemp);
720 if (!IsNumber(swTemp)) {
721 pContext->Rc() = false;
723 AlertIfPossible(pContext, L"AFNumber_Keystroke", sError);
724 return CJS_Result::Failure(sError);
725 }
726 // It happens after the last keystroke and before validating,
728 }
729
730 WideString wstrSelected;
731 if (pContext->SelStart() != -1) {
732 wstrSelected = wstrValue.Substr(pContext->SelStart(),
733 pContext->SelEnd() - pContext->SelStart());
734 }
735
736 bool bHasSign = wstrValue.Contains(L'-') && !wstrSelected.Contains(L'-');
737 if (bHasSign) {
738 // can't insert "change" in front of sign position.
739 if (!wstrSelected.IsEmpty() && pContext->SelStart() == 0) {
740 pContext->Rc() = false;
742 }
743 }
744
745 int iSepStyle = ValidStyleOrZero(pRuntime->ToInt32(params[1]));
746 const wchar_t cSep = DecimalMarkForStyle(iSepStyle);
747
748 bool bHasSep = wstrValue.Contains(cSep);
749 for (size_t i = 0; i < wstrChange.GetLength(); ++i) {
750 if (wstrChange[i] == cSep) {
751 if (bHasSep) {
752 pContext->Rc() = false;
754 }
755 bHasSep = true;
756 continue;
757 }
758 if (wstrChange[i] == L'-') {
759 if (bHasSign) {
760 pContext->Rc() = false;
762 }
763 // sign's position is not correct
764 if (i != 0) {
765 pContext->Rc() = false;
767 }
768 if (pContext->SelStart() != 0) {
769 pContext->Rc() = false;
771 }
772 bHasSign = true;
773 continue;
774 }
775
776 if (!FXSYS_IsDecimalDigit(wstrChange[i])) {
777 pContext->Rc() = false;
779 }
780 }
781
782 val = CalcMergedString(pContext, wstrValue, wstrChange);
784}
785
786// function AFPercent_Format(nDec, sepStyle, bPercentPrepend)
787CJS_Result CJS_PublicMethods::AFPercent_Format(
788 CJS_Runtime* pRuntime,
789 pdfium::span<v8::Local<v8::Value>> params) {
790#if !BUILDFLAG(IS_ANDROID)
791 if (params.size() < 2)
792 return CJS_Result::Failure(JSMessage::kParamError);
793
794 CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
795 if (!pEvent->HasValue())
796 return CJS_Result::Failure(JSMessage::kBadObjectError);
797
798 // Acrobat will accept this. Anything larger causes it to throw an error.
799 static constexpr int kMaxSepStyle = 49;
800
801 int iDec = pRuntime->ToInt32(params[0]);
802 int iSepStyle = pRuntime->ToInt32(params[1]);
803 // TODO(thestig): How do we handle negative raw |bPercentPrepend| values?
804 bool bPercentPrepend = params.size() > 2 && pRuntime->ToBoolean(params[2]);
805 if (iDec < 0 || iSepStyle < 0 || iSepStyle > kMaxSepStyle)
806 return CJS_Result::Failure(JSMessage::kValueError);
807
808 // When the |iDec| value is too big, Acrobat will just return "%".
809 static constexpr int kDecLimit = 512;
810 // This count must be in sync with |kDecLimit|.
811 static constexpr size_t kDigitsInDecLimit = 3;
812 WideString& Value = pEvent->Value();
813 if (iDec > kDecLimit) {
814 Value = L"%";
815 return CJS_Result::Success();
816 }
817
818 ByteString strValue = StrTrim(Value.ToUTF8());
819 if (strValue.IsEmpty())
820 strValue = "0";
821
822 // for processing decimal places
823 double dValue = atof(strValue.c_str());
824 dValue *= 100;
825
826 size_t szNewSize;
827 {
828 // Figure out the format to use with FXSYS_snprintf() below.
829 // |format| is small because |iDec| is limited in size.
830 char format[sizeof("%.f") + kDigitsInDecLimit]; // e.g. "%.512f"
831 FXSYS_snprintf(format, sizeof(format), "%%.%df", iDec);
832
833 // Calculate the new size for |strValue| and get a span.
834 size_t szBufferSize = iDec + 3; // Negative sign, decimal point, and NUL.
835 double dValueCopy = fabs(dValue);
836 while (dValueCopy > 1) {
837 dValueCopy /= 10;
838 ++szBufferSize;
839 }
840
841 // Write into |strValue|.
842 pdfium::span<char> span = strValue.GetBuffer(szBufferSize);
843 FXSYS_snprintf(span.data(), szBufferSize, format, dValue);
844 szNewSize = strlen(span.data());
845 }
846 strValue.ReleaseBuffer(szNewSize);
847
848 // for processing separator style
849 absl::optional<size_t> mark_pos = strValue.Find('.');
850 if (mark_pos.has_value()) {
851 char mark = DecimalMarkForStyle(iSepStyle);
852 if (mark != '.')
853 strValue.SetAt(mark_pos.value(), mark);
854 }
855 bool bUseDigitSeparator = IsStyleWithDigitSeparator(iSepStyle);
856 if (bUseDigitSeparator || IsStyleWithApostropheSeparator(iSepStyle)) {
857 char cSeparator =
858 bUseDigitSeparator ? DigitSeparatorForStyle(iSepStyle) : '\'';
859 int iEnd = mark_pos.value_or(strValue.GetLength());
860 int iStop = dValue < 0 ? 1 : 0;
861 for (int i = iEnd - 3; i > iStop; i -= 3)
862 strValue.Insert(i, cSeparator);
863 }
864
865 if (bPercentPrepend)
866 strValue.InsertAtFront('%');
867 else
868 strValue.InsertAtBack('%');
869 Value = WideString::FromUTF8(strValue.AsStringView());
870#endif
872}
873
874// AFPercent_Keystroke(nDec, sepStyle)
875CJS_Result CJS_PublicMethods::AFPercent_Keystroke(
876 CJS_Runtime* pRuntime,
877 pdfium::span<v8::Local<v8::Value>> params) {
878 return AFNumber_Keystroke(pRuntime, params);
879}
880
881// function AFDate_FormatEx(cFormat)
882CJS_Result CJS_PublicMethods::AFDate_FormatEx(
883 CJS_Runtime* pRuntime,
884 pdfium::span<v8::Local<v8::Value>> params) {
885 if (params.size() != 1)
887
888 CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
889 if (!pEvent->HasValue())
891
892 WideString& val = pEvent->Value();
893 WideString strValue = val;
894 if (strValue.IsEmpty())
896
897 WideString sFormat = pRuntime->ToWideString(params[0]);
898 double dDate;
899 if (strValue.Contains(L"GMT")) {
900 // e.g. "Tue Aug 11 14:24:16 GMT+08002009"
901 dDate = ParseDateAsGMT(pRuntime->GetIsolate(), strValue);
902 } else {
903 dDate = ParseDateUsingFormat(pRuntime->GetIsolate(), strValue, sFormat,
904 nullptr);
905 }
906
907 if (isnan(dDate)) {
908 WideString swMsg = WideString::Format(
910 AlertIfPossible(pEvent, L"AFDate_FormatEx", swMsg);
912 }
913
914 val = PrintDateUsingFormat(dDate, sFormat);
916}
917
918double CJS_PublicMethods::ParseDateAsGMT(v8::Isolate* isolate,
919 const WideString& strValue) {
920 std::vector<WideString> wsArray;
921 WideString sTemp;
922 for (const auto& c : strValue) {
923 if (c == L' ' || c == L':') {
924 wsArray.push_back(std::move(sTemp));
925 continue;
926 }
927 sTemp += c;
928 }
929 wsArray.push_back(std::move(sTemp));
930 if (wsArray.size() != 8)
931 return 0;
932
933 int nMonth = 1;
934 sTemp = wsArray[1];
935 for (size_t i = 0; i < std::size(fxjs::kMonths); ++i) {
936 if (sTemp == fxjs::kMonths[i]) {
937 nMonth = static_cast<int>(i) + 1;
938 break;
939 }
940 }
941
942 int nDay = StringToFloat(wsArray[2].AsStringView());
943 int nHour = StringToFloat(wsArray[3].AsStringView());
944 int nMin = StringToFloat(wsArray[4].AsStringView());
945 int nSec = StringToFloat(wsArray[5].AsStringView());
946 int nYear = StringToFloat(wsArray[7].AsStringView());
947 double dRet = FX_MakeDate(FX_MakeDay(nYear, nMonth - 1, nDay),
948 FX_MakeTime(nHour, nMin, nSec, 0));
949 if (isnan(dRet))
950 dRet = JS_DateParse(isolate, strValue);
951
952 return dRet;
953}
954
955// AFDate_KeystrokeEx(cFormat)
956CJS_Result CJS_PublicMethods::AFDate_KeystrokeEx(
957 CJS_Runtime* pRuntime,
958 pdfium::span<v8::Local<v8::Value>> params) {
959 if (params.size() != 1) {
960 return CJS_Result::Failure(WideString::FromASCII(
961 "AFDate_KeystrokeEx's parameter size not correct"));
962 }
963
964 CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
965 if (!pEvent->WillCommit())
967
968 if (!pEvent->HasValue())
970
971 const WideString& strValue = pEvent->Value();
972 if (strValue.IsEmpty())
974
975 bool bWrongFormat = false;
976 WideString sFormat = pRuntime->ToWideString(params[0]);
977 double dRet = ParseDateUsingFormat(pRuntime->GetIsolate(), strValue, sFormat,
978 &bWrongFormat);
979 if (bWrongFormat || isnan(dRet)) {
980 WideString swMsg = WideString::Format(
982 AlertIfPossible(pEvent, L"AFDate_KeystrokeEx", swMsg);
983 pEvent->Rc() = false;
984 }
986}
987
988CJS_Result CJS_PublicMethods::AFDate_Format(
989 CJS_Runtime* pRuntime,
990 pdfium::span<v8::Local<v8::Value>> params) {
991 if (params.size() != 1)
993
994 int iIndex =
995 WithinBoundsOrZero(pRuntime->ToInt32(params[0]), std::size(kDateFormats));
996 v8::LocalVector<v8::Value> newParams(pRuntime->GetIsolate());
997 newParams.push_back(pRuntime->NewString(kDateFormats[iIndex]));
998 return AFDate_FormatEx(pRuntime, newParams);
999}
1000
1001// AFDate_KeystrokeEx(cFormat)
1002CJS_Result CJS_PublicMethods::AFDate_Keystroke(
1003 CJS_Runtime* pRuntime,
1004 pdfium::span<v8::Local<v8::Value>> params) {
1005 if (params.size() != 1)
1007
1008 int iIndex =
1009 WithinBoundsOrZero(pRuntime->ToInt32(params[0]), std::size(kDateFormats));
1010 v8::LocalVector<v8::Value> newParams(pRuntime->GetIsolate());
1011 newParams.push_back(pRuntime->NewString(kDateFormats[iIndex]));
1012 return AFDate_KeystrokeEx(pRuntime, newParams);
1013}
1014
1015// function AFTime_Format(ptf)
1016CJS_Result CJS_PublicMethods::AFTime_Format(
1017 CJS_Runtime* pRuntime,
1018 pdfium::span<v8::Local<v8::Value>> params) {
1019 if (params.size() != 1)
1021
1022 int iIndex =
1023 WithinBoundsOrZero(pRuntime->ToInt32(params[0]), std::size(kTimeFormats));
1024 v8::LocalVector<v8::Value> newParams(pRuntime->GetIsolate());
1025 newParams.push_back(pRuntime->NewString(kTimeFormats[iIndex]));
1026 return AFDate_FormatEx(pRuntime, newParams);
1027}
1028
1029CJS_Result CJS_PublicMethods::AFTime_Keystroke(
1030 CJS_Runtime* pRuntime,
1031 pdfium::span<v8::Local<v8::Value>> params) {
1032 if (params.size() != 1)
1034
1035 int iIndex =
1036 WithinBoundsOrZero(pRuntime->ToInt32(params[0]), std::size(kTimeFormats));
1037 v8::LocalVector<v8::Value> newParams(pRuntime->GetIsolate());
1038 newParams.push_back(pRuntime->NewString(kTimeFormats[iIndex]));
1039 return AFDate_KeystrokeEx(pRuntime, newParams);
1040}
1041
1042CJS_Result CJS_PublicMethods::AFTime_FormatEx(
1043 CJS_Runtime* pRuntime,
1044 pdfium::span<v8::Local<v8::Value>> params) {
1045 return AFDate_FormatEx(pRuntime, params);
1046}
1047
1048CJS_Result CJS_PublicMethods::AFTime_KeystrokeEx(
1049 CJS_Runtime* pRuntime,
1050 pdfium::span<v8::Local<v8::Value>> params) {
1051 return AFDate_KeystrokeEx(pRuntime, params);
1052}
1053
1054// function AFSpecial_Format(psf)
1055CJS_Result CJS_PublicMethods::AFSpecial_Format(
1056 CJS_Runtime* pRuntime,
1057 pdfium::span<v8::Local<v8::Value>> params) {
1058 if (params.size() != 1)
1060
1061 CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
1062 if (!pEvent->HasValue())
1064
1065 const WideString& wsSource = pEvent->Value();
1066 WideString wsFormat;
1067 switch (pRuntime->ToInt32(params[0])) {
1068 case 0:
1069 wsFormat = L"99999";
1070 break;
1071 case 1:
1072 wsFormat = L"99999-9999";
1073 break;
1074 case 2:
1075 if (CJS_Util::StringPrintx(L"9999999999", wsSource).GetLength() >= 10)
1076 wsFormat = L"(999) 999-9999";
1077 else
1078 wsFormat = L"999-9999";
1079 break;
1080 case 3:
1081 wsFormat = L"999-99-9999";
1082 break;
1083 }
1084
1085 pEvent->Value() = CJS_Util::StringPrintx(wsFormat, wsSource);
1086 return CJS_Result::Success();
1087}
1088
1089// function AFSpecial_KeystrokeEx(mask)
1090CJS_Result CJS_PublicMethods::AFSpecial_KeystrokeEx(
1091 CJS_Runtime* pRuntime,
1092 pdfium::span<v8::Local<v8::Value>> params) {
1093 if (params.size() < 1)
1095
1096 CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
1097 if (!pEvent->HasValue())
1099
1100 const WideString& valEvent = pEvent->Value();
1101 WideString wstrMask = pRuntime->ToWideString(params[0]);
1102 if (wstrMask.IsEmpty())
1103 return CJS_Result::Success();
1104
1105 if (pEvent->WillCommit()) {
1106 if (valEvent.IsEmpty())
1107 return CJS_Result::Success();
1108
1109 if (valEvent.GetLength() > wstrMask.GetLength()) {
1110 AlertIfPossible(pEvent, L"AFSpecial_KeystrokeEx",
1112 pEvent->Rc() = false;
1113 return CJS_Result::Success();
1114 }
1115
1116 size_t iIndex = 0;
1117 for (iIndex = 0; iIndex < valEvent.GetLength(); ++iIndex) {
1118 if (!MaskSatisfied(valEvent[iIndex], wstrMask[iIndex]))
1119 break;
1120 }
1121 if (iIndex != wstrMask.GetLength()) {
1122 AlertIfPossible(pEvent, L"AFSpecial_KeystrokeEx",
1124 pEvent->Rc() = false;
1125 }
1126 return CJS_Result::Success();
1127 }
1128
1129 WideString& wideChange = pEvent->Change();
1130 if (wideChange.IsEmpty())
1131 return CJS_Result::Success();
1132
1133 WideString wChange = wideChange;
1134 size_t iIndexMask = pEvent->SelStart();
1135 size_t combined_len = valEvent.GetLength() + wChange.GetLength() +
1136 pEvent->SelStart() - pEvent->SelEnd();
1137 if (combined_len > wstrMask.GetLength()) {
1138 AlertIfPossible(pEvent, L"AFSpecial_KeystrokeEx",
1140 pEvent->Rc() = false;
1141 return CJS_Result::Success();
1142 }
1143
1144 if (iIndexMask >= wstrMask.GetLength() && !wChange.IsEmpty()) {
1145 AlertIfPossible(pEvent, L"AFSpecial_KeystrokeEx",
1147 pEvent->Rc() = false;
1148 return CJS_Result::Success();
1149 }
1150
1151 for (size_t i = 0; i < wChange.GetLength(); ++i) {
1152 if (iIndexMask >= wstrMask.GetLength()) {
1153 AlertIfPossible(pEvent, L"AFSpecial_KeystrokeEx",
1155 pEvent->Rc() = false;
1156 return CJS_Result::Success();
1157 }
1158 wchar_t wMask = wstrMask[iIndexMask];
1159 if (!IsReservedMaskChar(wMask))
1160 wChange.SetAt(i, wMask);
1161
1162 if (!MaskSatisfied(wChange[i], wMask)) {
1163 pEvent->Rc() = false;
1164 return CJS_Result::Success();
1165 }
1166 iIndexMask++;
1167 }
1168 wideChange = std::move(wChange);
1169 return CJS_Result::Success();
1170}
1171
1172// function AFSpecial_Keystroke(psf)
1173CJS_Result CJS_PublicMethods::AFSpecial_Keystroke(
1174 CJS_Runtime* pRuntime,
1175 pdfium::span<v8::Local<v8::Value>> params) {
1176 if (params.size() != 1)
1178
1179 CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
1180 if (!pEvent->HasValue())
1182
1183 const char* cFormat = "";
1184 switch (pRuntime->ToInt32(params[0])) {
1185 case 0:
1186 cFormat = "99999";
1187 break;
1188 case 1:
1189 cFormat = "999999999";
1190 break;
1191 case 2:
1192 if (pEvent->Value().GetLength() + pEvent->Change().GetLength() > 7)
1193 cFormat = "9999999999";
1194 else
1195 cFormat = "9999999";
1196 break;
1197 case 3:
1198 cFormat = "999999999";
1199 break;
1200 }
1201
1202 v8::LocalVector<v8::Value> params2(pRuntime->GetIsolate());
1203 params2.push_back(pRuntime->NewString(cFormat));
1204 return AFSpecial_KeystrokeEx(pRuntime, params2);
1205}
1206
1207CJS_Result CJS_PublicMethods::AFMergeChange(
1208 CJS_Runtime* pRuntime,
1209 pdfium::span<v8::Local<v8::Value>> params) {
1210 if (params.size() != 1)
1212
1213 CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
1214
1215 WideString swValue;
1216 if (pEvent->HasValue())
1217 swValue = pEvent->Value();
1218
1219 if (pEvent->WillCommit())
1220 return CJS_Result::Success(pRuntime->NewString(swValue.AsStringView()));
1221
1222 return CJS_Result::Success(pRuntime->NewString(
1223 CalcMergedString(pEvent, swValue, pEvent->Change()).AsStringView()));
1224}
1225
1226CJS_Result CJS_PublicMethods::AFParseDateEx(
1227 CJS_Runtime* pRuntime,
1228 pdfium::span<v8::Local<v8::Value>> params) {
1229 if (params.size() != 2)
1231
1232 WideString sValue = pRuntime->ToWideString(params[0]);
1233 WideString sFormat = pRuntime->ToWideString(params[1]);
1234 double dDate =
1235 ParseDateUsingFormat(pRuntime->GetIsolate(), sValue, sFormat, nullptr);
1236 if (isnan(dDate)) {
1237 WideString swMsg = WideString::Format(
1239 AlertIfPossible(pRuntime->GetCurrentEventContext(), L"AFParseDateEx",
1240 swMsg);
1242 }
1243 return CJS_Result::Success(pRuntime->NewNumber(dDate));
1244}
1245
1246CJS_Result CJS_PublicMethods::AFSimple(
1247 CJS_Runtime* pRuntime,
1248 pdfium::span<v8::Local<v8::Value>> params) {
1249 if (params.size() != 3)
1251
1252 WideString sFunction = pRuntime->ToWideString(params[0]);
1253 double arg1 = pRuntime->ToDouble(params[1]);
1254 double arg2 = pRuntime->ToDouble(params[2]);
1255 if (isnan(arg1) || isnan(arg2))
1257
1258 absl::optional<double> result = ApplyNamedOperation(sFunction, arg1, arg2);
1259 if (!result.has_value())
1261
1262 double dValue = result.value();
1263 if (wcscmp(sFunction.c_str(), L"AVG") == 0)
1264 dValue /= 2.0;
1265
1266 return CJS_Result::Success(pRuntime->NewNumber(dValue));
1267}
1268
1269CJS_Result CJS_PublicMethods::AFMakeNumber(
1270 CJS_Runtime* pRuntime,
1271 pdfium::span<v8::Local<v8::Value>> params) {
1272 if (params.size() != 1)
1274
1275 WideString ws = pRuntime->ToWideString(params[0]);
1276 NormalizeDecimalMarkW(&ws);
1277
1278 v8::Local<v8::Value> val =
1279 pRuntime->MaybeCoerceToNumber(pRuntime->NewString(ws.AsStringView()));
1280 if (!val->IsNumber())
1281 return CJS_Result::Success(pRuntime->NewNumber(0));
1282
1283 return CJS_Result::Success(val);
1284}
1285
1286CJS_Result CJS_PublicMethods::AFSimple_Calculate(
1287 CJS_Runtime* pRuntime,
1288 pdfium::span<v8::Local<v8::Value>> params) {
1289 if (params.size() != 2)
1291
1292 if (params[1].IsEmpty() || (!params[1]->IsArray() && !params[1]->IsString()))
1294
1295 WideString sFunction = pRuntime->ToWideString(params[0]);
1296 v8::Local<v8::Array> FieldNameArray =
1297 AF_MakeArrayFromList(pRuntime, params[1]);
1298
1299 CPDFSDK_InteractiveForm* pReaderForm =
1301 CPDF_InteractiveForm* pForm = pReaderForm->GetInteractiveForm();
1302
1303 double dValue = wcscmp(sFunction.c_str(), L"PRD") == 0 ? 1.0 : 0.0;
1304 int nFieldsCount = 0;
1305 for (size_t i = 0; i < pRuntime->GetArrayLength(FieldNameArray); ++i) {
1306 WideString wsFieldName =
1307 pRuntime->ToWideString(pRuntime->GetArrayElement(FieldNameArray, i));
1308
1309 for (size_t j = 0; j < pForm->CountFields(wsFieldName); ++j) {
1310 CPDF_FormField* pFormField = pForm->GetField(j, wsFieldName);
1311 if (!pFormField)
1312 continue;
1313
1314 double dTemp = 0.0;
1315 switch (pFormField->GetFieldType()) {
1318 WideString trimmed = pFormField->GetValue();
1319 trimmed.TrimRight();
1320 trimmed.TrimLeft();
1321 dTemp = StringToDouble(trimmed.AsStringView());
1322 break;
1323 }
1325 break;
1328 for (int c = 0; c < pFormField->CountControls(); ++c) {
1329 CPDF_FormControl* pFormCtrl = pFormField->GetControl(c);
1330 if (!pFormField || !pFormCtrl->IsChecked())
1331 continue;
1332
1333 WideString trimmed = pFormCtrl->GetExportValue();
1334 trimmed.TrimRight();
1335 trimmed.TrimLeft();
1336 dTemp = StringToFloat(trimmed.AsStringView());
1337 break;
1338 }
1339 break;
1341 if (pFormField->CountSelectedItems() <= 1) {
1342 WideString trimmed = pFormField->GetValue();
1343 trimmed.TrimRight();
1344 trimmed.TrimLeft();
1345 dTemp = StringToFloat(trimmed.AsStringView());
1346 }
1347 break;
1348 default:
1349 break;
1350 }
1351
1352 if (i == 0 && j == 0 &&
1353 (wcscmp(sFunction.c_str(), L"MIN") == 0 ||
1354 wcscmp(sFunction.c_str(), L"MAX") == 0)) {
1355 dValue = dTemp;
1356 }
1357 absl::optional<double> dResult =
1358 ApplyNamedOperation(sFunction, dValue, dTemp);
1359 if (!dResult.has_value())
1361
1362 dValue = dResult.value();
1363 nFieldsCount++;
1364 }
1365 }
1366
1367 if (wcscmp(sFunction.c_str(), L"AVG") == 0 && nFieldsCount > 0)
1368 dValue /= nFieldsCount;
1369
1370 dValue = floor(dValue * powf(10, 6) + 0.49) / powf(10, 6);
1371
1372 CJS_EventContext* pContext = pRuntime->GetCurrentEventContext();
1373 if (pContext->HasValue())
1374 pContext->Value() = pRuntime->ToWideString(pRuntime->NewNumber(dValue));
1375
1376 return CJS_Result::Success();
1377}
1378
1379// This function validates the current event to ensure that its value is
1380// within the specified range.
1381CJS_Result CJS_PublicMethods::AFRange_Validate(
1382 CJS_Runtime* pRuntime,
1383 pdfium::span<v8::Local<v8::Value>> params) {
1384 if (params.size() != 4)
1386
1387 CJS_EventContext* pEvent = pRuntime->GetCurrentEventContext();
1388 if (!pEvent->HasValue())
1390
1391 if (pEvent->Value().IsEmpty())
1392 return CJS_Result::Success();
1393
1394 double dEventValue = atof(pEvent->Value().ToUTF8().c_str());
1395 bool bGreaterThan = pRuntime->ToBoolean(params[0]);
1396 double dGreaterThan = pRuntime->ToDouble(params[1]);
1397 bool bLessThan = pRuntime->ToBoolean(params[2]);
1398 double dLessThan = pRuntime->ToDouble(params[3]);
1399 WideString swMsg;
1400
1401 if (bGreaterThan && bLessThan) {
1402 if (dEventValue < dGreaterThan || dEventValue > dLessThan)
1403 swMsg = WideString::Format(
1404 JSGetStringFromID(JSMessage::kRangeBetweenError).c_str(),
1405 pRuntime->ToWideString(params[1]).c_str(),
1406 pRuntime->ToWideString(params[3]).c_str());
1407 } else if (bGreaterThan) {
1408 if (dEventValue < dGreaterThan)
1409 swMsg = WideString::Format(
1410 JSGetStringFromID(JSMessage::kRangeGreaterError).c_str(),
1411 pRuntime->ToWideString(params[1]).c_str());
1412 } else if (bLessThan) {
1413 if (dEventValue > dLessThan)
1414 swMsg = WideString::Format(
1415 JSGetStringFromID(JSMessage::kRangeLessError).c_str(),
1416 pRuntime->ToWideString(params[3]).c_str());
1417 }
1418
1419 if (!swMsg.IsEmpty()) {
1420 AlertIfPossible(pEvent, L"AFRange_Validate", swMsg);
1421 pEvent->Rc() = false;
1422 }
1423 return CJS_Result::Success();
1424}
1425
1426CJS_Result CJS_PublicMethods::AFExtractNums(
1427 CJS_Runtime* pRuntime,
1428 pdfium::span<v8::Local<v8::Value>> params) {
1429 if (params.size() != 1)
1431
1432 WideString str = pRuntime->ToWideString(params[0]);
1433 if (str.GetLength() > 0 && IsDigitSeparatorOrDecimalMark(str[0]))
1434 str.InsertAtFront(L'0');
1435
1436 WideString sPart;
1437 v8::Local<v8::Array> nums = pRuntime->NewArray();
1438 int nIndex = 0;
1439 for (const auto& wc : str) {
1440 if (FXSYS_IsDecimalDigit(wc)) {
1441 sPart += wc;
1442 } else if (sPart.GetLength() > 0) {
1443 pRuntime->PutArrayElement(nums, nIndex,
1444 pRuntime->NewString(sPart.AsStringView()));
1445 sPart.clear();
1446 nIndex++;
1447 }
1448 }
1449 if (sPart.GetLength() > 0) {
1450 pRuntime->PutArrayElement(nums, nIndex,
1451 pRuntime->NewString(sPart.AsStringView()));
1452 }
1453 if (pRuntime->GetArrayLength(nums) > 0)
1454 return CJS_Result::Success(nums);
1455
1456 return CJS_Result::Success(pRuntime->NewUndefined());
1457}
#define JS_STATIC_GLOBAL_FUN(fun_name)
void Error(const WideString &message)
WideString & Value()
bool WillCommit() const
CPDFSDK_FormFillEnvironment * GetFormFillEnv() const
CJS_Runtime * GetRuntime() const
Definition cjs_object.h:54
static WideString PrintDateUsingFormat(double dDate, const WideString &format)
static void DefineJSObjects(CFXJS_Engine *pEngine)
static bool IsReservedMaskChar(wchar_t ch)
static bool IsNumber(const WideString &str)
static bool MaskSatisfied(wchar_t c_Change, wchar_t c_Mask)
bool HasReturn() const
Definition cjs_result.h:44
static CJS_Result Success()
Definition cjs_result.h:27
bool HasError() const
Definition cjs_result.h:41
static CJS_Result Failure(JSMessage id)
Definition cjs_result.h:34
const WideString & Error() const
Definition cjs_result.h:42
static CJS_Result Failure(const WideString &str)
Definition cjs_result.h:31
CJS_EventContext * GetCurrentEventContext() const
CPDFSDK_FormFillEnvironment * GetFormFillEnv() const override
static WideString StringPrintx(const WideString &cFormat, const WideString &cSource)
Definition cjs_util.cpp:283
CPDFSDK_InteractiveForm * GetInteractiveForm()
CPDF_InteractiveForm * GetInteractiveForm() const
WideString GetExportValue() const
WideString GetValue() const
int CountControls() const
int CountSelectedItems() const
CPDF_FormControl * GetControl(int index) const
FormFieldType GetFieldType() const
const char * c_str() const
Definition bytestring.h:76
WideString & operator+=(const WideString &str)
ByteString ToUTF8() const
static WideString Format(const wchar_t *pFormat,...)
WideString & operator=(WideString &&that) noexcept
WideString & operator+=(const wchar_t *str)
WideString & operator+=(wchar_t ch)
bool EqualsASCIINoCase(ByteStringView that) const
Definition widestring.h:219
CharType operator[](const size_t index) const
Definition widestring.h:146
bool IsEmpty() const
Definition widestring.h:118
const wchar_t * c_str() const
Definition widestring.h:81
WideString & operator=(const WideString &that)
static WideString FromASCII(ByteStringView str)
WideString & operator=(const wchar_t *str)
static WideString FormatInteger(int i)
FormFieldType
#define JSPLATFORM_ALERT_BUTTON_OK
#define JSPLATFORM_ALERT_ICON_STATUS
bool FXSYS_IsDecimalDigit(wchar_t c)
JSMessage
@ kParamTooLongError
@ kInvalidInputError
WideString JSFormatErrorString(const char *class_name, const char *property_name, const WideString &details)
WideString JSGetStringFromID(JSMessage msg)
WideString operator+(const WideString &str1, const WideString &str2)
Definition widestring.h:269
int FX_GetMonthFromTime(double dt)
const wchar_t *const kMonths[12]
int FX_GetYearFromTime(double dt)
const wchar_t *const kFullMonths[12]
ConversionStatus FX_ParseDateUsingFormat(const WideString &value, const WideString &format, double *result)
double FX_MakeDate(double day, double time)
double FX_GetDateTime()
int FX_GetDayFromTime(double dt)
int FX_GetSecFromTime(double dt)
bool FX_IsValidMonth(int m)
int FX_GetHourFromTime(double dt)
double FX_MakeDay(int nYear, int nMonth, int nDay)
bool FX_IsValidDay(int d)
int FX_GetMinFromTime(double dt)
double FX_MakeTime(int nHour, int nMin, int nSec, int nMs)