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_util.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_util.h"
8
9#include <math.h>
10#include <time.h>
11
12#include <algorithm>
13#include <string>
14#include <vector>
15
16#include "build/build_config.h"
17#include "core/fxcrt/fx_extension.h"
18#include "fxjs/cjs_event_context.h"
19#include "fxjs/cjs_object.h"
20#include "fxjs/cjs_publicmethods.h"
21#include "fxjs/cjs_runtime.h"
22#include "fxjs/fx_date_helpers.h"
23#include "fxjs/fxv8.h"
24#include "fxjs/js_define.h"
25#include "fxjs/js_resources.h"
26#include "third_party/base/check_op.h"
27#include "third_party/base/containers/span.h"
28#include "v8/include/v8-date.h"
29
30#if BUILDFLAG(IS_ANDROID)
31#include <ctype.h>
32#endif
33
34namespace {
35
36// Map PDF-style directives to equivalent wcsftime directives. Not
37// all have direct equivalents, though.
38struct TbConvert {
39 const wchar_t* lpszJSMark;
40 const wchar_t* lpszCppMark;
41};
42
43// Map PDF-style directives lacking direct wcsftime directives to
44// the value with which they will be replaced.
45struct TbConvertAdditional {
46 wchar_t js_mark;
47 int value;
48};
49
50const TbConvert TbConvertTable[] = {
51 {L"mmmm", L"%B"}, {L"mmm", L"%b"}, {L"mm", L"%m"}, {L"dddd", L"%A"},
52 {L"ddd", L"%a"}, {L"dd", L"%d"}, {L"yyyy", L"%Y"}, {L"yy", L"%y"},
53 {L"HH", L"%H"}, {L"hh", L"%I"}, {L"MM", L"%M"}, {L"ss", L"%S"},
54 {L"TT", L"%p"},
55#if BUILDFLAG(IS_WIN)
56 {L"tt", L"%p"}, {L"h", L"%#I"},
57#else
58 {L"tt", L"%P"}, {L"h", L"%l"},
59#endif
60};
61
62enum CaseMode { kPreserveCase, kUpperCase, kLowerCase };
63
64wchar_t TranslateCase(wchar_t input, CaseMode eMode) {
65 if (eMode == kLowerCase && FXSYS_iswupper(input))
66 return input | 0x20;
67 if (eMode == kUpperCase && FXSYS_iswlower(input))
68 return input & ~0x20;
69 return input;
70}
71
72} // namespace
73
74const JSMethodSpec CJS_Util::MethodSpecs[] = {
75 {"printd", printd_static},
76 {"printf", printf_static},
77 {"printx", printx_static},
78 {"scand", scand_static},
79 {"byteToChar", byteToChar_static}};
80
81uint32_t CJS_Util::ObjDefnID = 0;
82const char CJS_Util::kName[] = "util";
83
84// static
85uint32_t CJS_Util::GetObjDefnID() {
86 return ObjDefnID;
87}
88
89// static
90void CJS_Util::DefineJSObjects(CFXJS_Engine* pEngine) {
91 ObjDefnID = pEngine->DefineObj(CJS_Util::kName, FXJSOBJTYPE_STATIC,
92 JSConstructor<CJS_Util>, JSDestructor);
93 DefineMethods(pEngine, ObjDefnID, MethodSpecs);
94}
95
96CJS_Util::CJS_Util(v8::Local<v8::Object> pObject, CJS_Runtime* pRuntime)
97 : CJS_Object(pObject, pRuntime) {}
98
99CJS_Util::~CJS_Util() = default;
100
101CJS_Result CJS_Util::printf(CJS_Runtime* pRuntime,
102 pdfium::span<v8::Local<v8::Value>> params) {
103 const size_t num_params = params.size();
104 if (num_params < 1)
106
107 // Use 'S' as a sentinel to ensure we always have some text before the first
108 // format specifier.
109 WideString unsafe_fmt_string = L'S' + pRuntime->ToWideString(params[0]);
110 std::vector<WideString> unsafe_conversion_specifiers;
111
112 {
113 size_t offset = 0;
114 while (true) {
115 absl::optional<size_t> offset_end =
116 unsafe_fmt_string.Find(L"%", offset + 1);
117 if (!offset_end.has_value()) {
118 unsafe_conversion_specifiers.push_back(
119 unsafe_fmt_string.Last(unsafe_fmt_string.GetLength() - offset));
120 break;
121 }
122
123 unsafe_conversion_specifiers.push_back(
124 unsafe_fmt_string.Substr(offset, offset_end.value() - offset));
125 offset = offset_end.value();
126 }
127 }
128
129 WideString result = unsafe_conversion_specifiers[0];
130 for (size_t i = 1; i < unsafe_conversion_specifiers.size(); ++i) {
131 WideString fmt = unsafe_conversion_specifiers[i];
132 if (i >= num_params) {
133 result += fmt;
134 continue;
135 }
136
137 WideString segment;
138 switch (ParseDataType(&fmt)) {
139 case DataType::kInt:
140 segment = WideString::Format(fmt.c_str(), pRuntime->ToInt32(params[i]));
141 break;
142 case DataType::kDouble:
143 segment =
144 WideString::Format(fmt.c_str(), pRuntime->ToDouble(params[i]));
145 break;
146 case DataType::kString:
147 segment = WideString::Format(fmt.c_str(),
148 pRuntime->ToWideString(params[i]).c_str());
149 break;
150 default:
151 segment = WideString::Format(L"%ls", fmt.c_str());
152 break;
153 }
154 result += segment;
155 }
156
157 // Remove the 'S' sentinel introduced earlier.
158 DCHECK_EQ(L'S', result[0]);
159 auto result_view = result.AsStringView();
161 pRuntime->NewString(result_view.Last(result_view.GetLength() - 1)));
162}
163
164CJS_Result CJS_Util::printd(CJS_Runtime* pRuntime,
165 pdfium::span<v8::Local<v8::Value>> params) {
166 const size_t iSize = params.size();
167 if (iSize < 2)
169
170 if (!fxv8::IsDate(params[1]))
172
173 v8::Local<v8::Date> v8_date = params[1].As<v8::Date>();
174 if (v8_date.IsEmpty() || isnan(pRuntime->ToDouble(v8_date)))
176
177 double date = FX_LocalTime(pRuntime->ToDouble(v8_date));
178 int year = FX_GetYearFromTime(date);
179 int month = FX_GetMonthFromTime(date) + 1; // One-based.
180 int day = FX_GetDayFromTime(date);
181 int hour = FX_GetHourFromTime(date);
182 int min = FX_GetMinFromTime(date);
183 int sec = FX_GetSecFromTime(date);
184
185 if (params[0]->IsNumber()) {
186 WideString swResult;
187 switch (pRuntime->ToInt32(params[0])) {
188 case 0:
189 swResult = WideString::Format(L"D:%04d%02d%02d%02d%02d%02d", year,
190 month, day, hour, min, sec);
191 break;
192 case 1:
193 swResult = WideString::Format(L"%04d.%02d.%02d %02d:%02d:%02d", year,
194 month, day, hour, min, sec);
195 break;
196 case 2:
197 swResult = WideString::Format(L"%04d/%02d/%02d %02d:%02d:%02d", year,
198 month, day, hour, min, sec);
199 break;
200 default:
202 }
203
204 return CJS_Result::Success(pRuntime->NewString(swResult.AsStringView()));
205 }
206
207 if (!params[0]->IsString())
209
210 // We don't support XFAPicture at the moment.
211 if (iSize > 2 && pRuntime->ToBoolean(params[2]))
213
214 // Convert PDF-style format specifiers to wcsftime specifiers. Remove any
215 // pre-existing %-directives before inserting our own.
216 std::wstring cFormat = pRuntime->ToWideString(params[0]).c_str();
217 cFormat.erase(std::remove(cFormat.begin(), cFormat.end(), '%'),
218 cFormat.end());
219
220 for (size_t i = 0; i < std::size(TbConvertTable); ++i) {
221 size_t nFound = 0;
222 while (true) {
223 nFound = cFormat.find(TbConvertTable[i].lpszJSMark, nFound);
224 if (nFound == std::wstring::npos)
225 break;
226
227 cFormat.replace(nFound, wcslen(TbConvertTable[i].lpszJSMark),
228 TbConvertTable[i].lpszCppMark);
229 }
230 }
231
232 if (year < 0)
234
235 const TbConvertAdditional cTableAd[] = {
236 {L'm', month}, {L'd', day},
237 {L'H', hour}, {L'h', hour > 12 ? hour - 12 : hour},
238 {L'M', min}, {L's', sec},
239 };
240
241 for (size_t i = 0; i < std::size(cTableAd); ++i) {
242 size_t nFound = 0;
243 while (true) {
244 nFound = cFormat.find(cTableAd[i].js_mark, nFound);
245 if (nFound == std::wstring::npos)
246 break;
247
248 if (nFound != 0 && cFormat[nFound - 1] == L'%') {
249 ++nFound;
250 continue;
251 }
252 cFormat.replace(nFound, 1,
253 WideString::FormatInteger(cTableAd[i].value).c_str());
254 }
255 }
256
257 struct tm time = {};
258 time.tm_year = year - 1900;
259 time.tm_mon = month - 1;
260 time.tm_mday = day;
261 time.tm_hour = hour;
262 time.tm_min = min;
263 time.tm_sec = sec;
264
265 wchar_t buf[64] = {};
266 FXSYS_wcsftime(buf, 64, cFormat.c_str(), &time);
267 cFormat = buf;
268 return CJS_Result::Success(pRuntime->NewString(cFormat.c_str()));
269}
270
271CJS_Result CJS_Util::printx(CJS_Runtime* pRuntime,
272 pdfium::span<v8::Local<v8::Value>> params) {
273 if (params.size() < 2)
275
276 return CJS_Result::Success(
277 pRuntime->NewString(StringPrintx(pRuntime->ToWideString(params[0]),
278 pRuntime->ToWideString(params[1]))
279 .AsStringView()));
280}
281
282// static
283WideString CJS_Util::StringPrintx(const WideString& wsFormat,
284 const WideString& wsSource) {
285 WideString wsResult;
286 wsResult.Reserve(wsFormat.GetLength());
287 size_t iSourceIdx = 0;
288 size_t iFormatIdx = 0;
289 CaseMode eCaseMode = kPreserveCase;
290 bool bEscaped = false;
291 while (iFormatIdx < wsFormat.GetLength()) {
292 if (bEscaped) {
293 bEscaped = false;
294 wsResult += wsFormat[iFormatIdx];
295 ++iFormatIdx;
296 continue;
297 }
298 switch (wsFormat[iFormatIdx]) {
299 case '\\': {
300 bEscaped = true;
301 ++iFormatIdx;
302 } break;
303 case '<': {
304 eCaseMode = kLowerCase;
305 ++iFormatIdx;
306 } break;
307 case '>': {
308 eCaseMode = kUpperCase;
309 ++iFormatIdx;
310 } break;
311 case '=': {
312 eCaseMode = kPreserveCase;
313 ++iFormatIdx;
314 } break;
315 case '?': {
316 if (iSourceIdx < wsSource.GetLength()) {
317 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
318 ++iSourceIdx;
319 }
320 ++iFormatIdx;
321 } break;
322 case 'X': {
323 if (iSourceIdx < wsSource.GetLength()) {
324 if (isascii(wsSource[iSourceIdx]) && isalnum(wsSource[iSourceIdx])) {
325 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
326 ++iFormatIdx;
327 }
328 ++iSourceIdx;
329 } else {
330 ++iFormatIdx;
331 }
332 } break;
333 case 'A': {
334 if (iSourceIdx < wsSource.GetLength()) {
335 if (isascii(wsSource[iSourceIdx]) && isalpha(wsSource[iSourceIdx])) {
336 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
337 ++iFormatIdx;
338 }
339 ++iSourceIdx;
340 } else {
341 ++iFormatIdx;
342 }
343 } break;
344 case '9': {
345 if (iSourceIdx < wsSource.GetLength()) {
346 if (FXSYS_IsDecimalDigit(wsSource[iSourceIdx])) {
347 wsResult += wsSource[iSourceIdx];
348 ++iFormatIdx;
349 }
350 ++iSourceIdx;
351 } else {
352 ++iFormatIdx;
353 }
354 } break;
355 case '*': {
356 if (iSourceIdx < wsSource.GetLength()) {
357 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode);
358 ++iSourceIdx;
359 } else {
360 ++iFormatIdx;
361 }
362 } break;
363 default: {
364 wsResult += wsFormat[iFormatIdx];
365 ++iFormatIdx;
366 } break;
367 }
368 }
369 return wsResult;
370}
371
372CJS_Result CJS_Util::scand(CJS_Runtime* pRuntime,
373 pdfium::span<v8::Local<v8::Value>> params) {
374 if (params.size() < 2)
376
377 WideString sFormat = pRuntime->ToWideString(params[0]);
378 WideString sDate = pRuntime->ToWideString(params[1]);
379 double dDate = FX_GetDateTime();
380 if (sDate.GetLength() > 0)
381 dDate = CJS_PublicMethods::ParseDateUsingFormat(pRuntime->GetIsolate(),
382 sDate, sFormat, nullptr);
383 if (isnan(dDate))
384 return CJS_Result::Success(pRuntime->NewUndefined());
385
386 return CJS_Result::Success(pRuntime->NewDate(dDate));
387}
388
389CJS_Result CJS_Util::byteToChar(CJS_Runtime* pRuntime,
390 pdfium::span<v8::Local<v8::Value>> params) {
391 if (params.size() < 1)
393
394 int arg = pRuntime->ToInt32(params[0]);
395 if (arg < 0 || arg > 255)
397
398 WideString wStr(static_cast<wchar_t>(arg));
399 return CJS_Result::Success(pRuntime->NewString(wStr.AsStringView()));
400}
401
402// static
403CJS_Util::DataType CJS_Util::ParseDataType(WideString* sFormat) {
404 enum State { kBefore, kFlags, kWidth, kPrecision, kSpecifier, kAfter };
405
407 State state = kBefore;
408 size_t precision_digits = 0;
409 size_t i = 0;
410 while (i < sFormat->GetLength()) {
411 wchar_t c = (*sFormat)[i];
412 switch (state) {
413 case kBefore:
414 if (c == L'%')
415 state = kFlags;
416 break;
417 case kFlags:
418 if (c == L'+' || c == L'-' || c == L'#' || c == L' ') {
419 // Stay in same state.
420 } else {
421 state = kWidth;
422 continue; // Re-process same character.
423 }
424 break;
425 case kWidth:
426 if (c == L'*')
427 return DataType::kInvalid;
429 // Stay in same state.
430 } else if (c == L'.') {
431 state = kPrecision;
432 } else {
433 state = kSpecifier;
434 continue; // Re-process same character.
435 }
436 break;
437 case kPrecision:
438 if (c == L'*')
439 return DataType::kInvalid;
441 // Stay in same state.
442 ++precision_digits;
443 } else {
444 state = kSpecifier;
445 continue; // Re-process same character.
446 }
447 break;
448 case kSpecifier:
449 if (c == L'c' || c == L'C' || c == L'd' || c == L'i' || c == L'o' ||
450 c == L'u' || c == L'x' || c == L'X') {
451 result = DataType::kInt;
452 } else if (c == L'e' || c == L'E' || c == L'f' || c == L'g' ||
453 c == L'G') {
454 result = DataType::kDouble;
455 } else if (c == L's' || c == L'S') {
456 // Map s to S since we always deal internally with wchar_t strings.
457 // TODO(tsepez): Probably 100% borked. %S is not a standard
458 // conversion.
459 sFormat->SetAt(i, L'S');
460 result = DataType::kString;
461 } else {
462 return DataType::kInvalid;
463 }
464 state = kAfter;
465 break;
466 case kAfter:
467 if (c == L'%')
468 return DataType::kInvalid;
469 // Stay in same state until string exhausted.
470 break;
471 }
472 ++i;
473 }
474 // See https://crbug.com/740166
475 if (result == DataType::kInt && precision_digits > 2)
476 return DataType::kInvalid;
477
478 return result;
479}
@ FXJSOBJTYPE_STATIC
static void DefineMethods(CFXJS_Engine *pEngine, uint32_t nObjDefnID, pdfium::span< const JSMethodSpec > consts)
static CJS_Result Success()
Definition cjs_result.h:27
static CJS_Result Failure(JSMessage id)
Definition cjs_result.h:34
CJS_Util(v8::Local< v8::Object > pObject, CJS_Runtime *pRuntime)
Definition cjs_util.cpp:96
~CJS_Util() override
static uint32_t GetObjDefnID()
Definition cjs_util.cpp:85
static void DefineJSObjects(CFXJS_Engine *pEngine)
Definition cjs_util.cpp:90
static DataType ParseDataType(WideString *sFormat)
Definition cjs_util.cpp:403
static WideString StringPrintx(const WideString &cFormat, const WideString &cSource)
Definition cjs_util.cpp:283
WideString & operator+=(const WideString &str)
WideString(wchar_t ch)
static WideString Format(const wchar_t *pFormat,...)
WideString & operator=(WideString &&that) noexcept
CharType operator[](const size_t index) const
Definition widestring.h:146
const wchar_t * c_str() const
Definition widestring.h:81
static WideString FormatInteger(int i)
bool FXSYS_IsDecimalDigit(wchar_t c)
bool FXSYS_iswlower(int32_t c)
bool FXSYS_iswupper(int32_t c)
#define FXSYS_wcsftime
Definition fx_system.h:75
JSMessage
@ kNotSupportedError
@ kSecondParamNotDateError
@ kSecondParamInvalidDateError
int FX_GetMonthFromTime(double dt)
int FX_GetYearFromTime(double dt)
double FX_GetDateTime()
int FX_GetDayFromTime(double dt)
int FX_GetSecFromTime(double dt)
int FX_GetHourFromTime(double dt)
int FX_GetMinFromTime(double dt)