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