1 // Copyright 2014 PDFium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com 6 7 #include "fpdfsdk/javascript/util.h" 8 9 #include <time.h> 10 11 #include <algorithm> 12 #include <string> 13 #include <vector> 14 15 #include "core/fxcrt/fx_ext.h" 16 #include "fpdfsdk/javascript/JS_Define.h" 17 #include "fpdfsdk/javascript/JS_EventHandler.h" 18 #include "fpdfsdk/javascript/JS_Object.h" 19 #include "fpdfsdk/javascript/JS_Value.h" 20 #include "fpdfsdk/javascript/PublicMethods.h" 21 #include "fpdfsdk/javascript/cjs_event_context.h" 22 #include "fpdfsdk/javascript/cjs_runtime.h" 23 #include "fpdfsdk/javascript/resource.h" 24 25 #if _FX_OS_ == _FX_ANDROID_ 26 #include <ctype.h> 27 #endif 28 29 JSConstSpec CJS_Util::ConstSpecs[] = {{0, JSConstSpec::Number, 0, 0}}; 30 31 JSPropertySpec CJS_Util::PropertySpecs[] = {{0, 0, 0}}; 32 33 JSMethodSpec CJS_Util::MethodSpecs[] = { 34 {"printd", printd_static}, {"printf", printf_static}, 35 {"printx", printx_static}, {"scand", scand_static}, 36 {"byteToChar", byteToChar_static}, {0, 0}}; 37 38 IMPLEMENT_JS_CLASS(CJS_Util, util) 39 40 #define UTIL_INT 0 41 #define UTIL_DOUBLE 1 42 #define UTIL_STRING 2 43 44 namespace { 45 46 // Map PDF-style directives to equivalent wcsftime directives. Not 47 // all have direct equivalents, though. 48 struct TbConvert { 49 const FX_WCHAR* lpszJSMark; 50 const FX_WCHAR* lpszCppMark; 51 }; 52 53 // Map PDF-style directives lacking direct wcsftime directives to 54 // the value with which they will be replaced. 55 struct TbConvertAdditional { 56 const FX_WCHAR* lpszJSMark; 57 int iValue; 58 }; 59 60 const TbConvert TbConvertTable[] = { 61 {L"mmmm", L"%B"}, {L"mmm", L"%b"}, {L"mm", L"%m"}, {L"dddd", L"%A"}, 62 {L"ddd", L"%a"}, {L"dd", L"%d"}, {L"yyyy", L"%Y"}, {L"yy", L"%y"}, 63 {L"HH", L"%H"}, {L"hh", L"%I"}, {L"MM", L"%M"}, {L"ss", L"%S"}, 64 {L"TT", L"%p"}, 65 #if defined(_WIN32) 66 {L"tt", L"%p"}, {L"h", L"%#I"}, 67 #else 68 {L"tt", L"%P"}, {L"h", L"%l"}, 69 #endif 70 }; 71 72 int ParseDataType(std::wstring* sFormat) { 73 bool bPercent = false; 74 for (size_t i = 0; i < sFormat->length(); ++i) { 75 wchar_t c = (*sFormat)[i]; 76 if (c == L'%') { 77 bPercent = true; 78 continue; 79 } 80 81 if (bPercent) { 82 if (c == L'c' || c == L'C' || c == L'd' || c == L'i' || c == L'o' || 83 c == L'u' || c == L'x' || c == L'X') { 84 return UTIL_INT; 85 } 86 if (c == L'e' || c == L'E' || c == L'f' || c == L'g' || c == L'G') { 87 return UTIL_DOUBLE; 88 } 89 if (c == L's' || c == L'S') { 90 // Map s to S since we always deal internally 91 // with wchar_t strings. 92 (*sFormat)[i] = L'S'; 93 return UTIL_STRING; 94 } 95 if (c == L'.' || c == L'+' || c == L'-' || c == L'#' || c == L' ' || 96 FXSYS_iswdigit(c)) { 97 continue; 98 } 99 break; 100 } 101 } 102 103 return -1; 104 } 105 106 } // namespace 107 108 util::util(CJS_Object* pJSObject) : CJS_EmbedObj(pJSObject) {} 109 110 util::~util() {} 111 112 bool util::printf(CJS_Runtime* pRuntime, 113 const std::vector<CJS_Value>& params, 114 CJS_Value& vRet, 115 CFX_WideString& sError) { 116 int iSize = params.size(); 117 if (iSize < 1) 118 return false; 119 std::wstring c_ConvChar(params[0].ToCFXWideString(pRuntime).c_str()); 120 std::vector<std::wstring> c_strConvers; 121 int iOffset = 0; 122 int iOffend = 0; 123 c_ConvChar.insert(c_ConvChar.begin(), L'S'); 124 while (iOffset != -1) { 125 iOffend = c_ConvChar.find(L"%", iOffset + 1); 126 std::wstring strSub; 127 if (iOffend == -1) 128 strSub = c_ConvChar.substr(iOffset); 129 else 130 strSub = c_ConvChar.substr(iOffset, iOffend - iOffset); 131 c_strConvers.push_back(strSub); 132 iOffset = iOffend; 133 } 134 135 std::wstring c_strResult; 136 std::wstring c_strFormat; 137 for (int iIndex = 0; iIndex < (int)c_strConvers.size(); iIndex++) { 138 c_strFormat = c_strConvers[iIndex]; 139 if (iIndex == 0) { 140 c_strResult = c_strFormat; 141 continue; 142 } 143 144 CFX_WideString strSegment; 145 if (iIndex >= iSize) { 146 c_strResult += c_strFormat; 147 continue; 148 } 149 150 switch (ParseDataType(&c_strFormat)) { 151 case UTIL_INT: 152 strSegment.Format(c_strFormat.c_str(), params[iIndex].ToInt(pRuntime)); 153 break; 154 case UTIL_DOUBLE: 155 strSegment.Format(c_strFormat.c_str(), 156 params[iIndex].ToDouble(pRuntime)); 157 break; 158 case UTIL_STRING: 159 strSegment.Format(c_strFormat.c_str(), 160 params[iIndex].ToCFXWideString(pRuntime).c_str()); 161 break; 162 default: 163 strSegment.Format(L"%S", c_strFormat.c_str()); 164 break; 165 } 166 c_strResult += strSegment.GetBuffer(strSegment.GetLength() + 1); 167 } 168 169 c_strResult.erase(c_strResult.begin()); 170 vRet = CJS_Value(pRuntime, c_strResult.c_str()); 171 return true; 172 } 173 174 bool util::printd(CJS_Runtime* pRuntime, 175 const std::vector<CJS_Value>& params, 176 CJS_Value& vRet, 177 CFX_WideString& sError) { 178 int iSize = params.size(); 179 if (iSize < 2) 180 return false; 181 182 CJS_Value p1 = params[0]; 183 CJS_Value p2 = params[1]; 184 CJS_Date jsDate; 185 if (!p2.ConvertToDate(pRuntime, jsDate)) { 186 sError = JSGetStringFromID(IDS_STRING_JSPRINT1); 187 return false; 188 } 189 190 if (!jsDate.IsValidDate(pRuntime)) { 191 sError = JSGetStringFromID(IDS_STRING_JSPRINT2); 192 return false; 193 } 194 195 if (p1.GetType() == CJS_Value::VT_number) { 196 CFX_WideString swResult; 197 switch (p1.ToInt(pRuntime)) { 198 case 0: 199 swResult.Format(L"D:%04d%02d%02d%02d%02d%02d", jsDate.GetYear(pRuntime), 200 jsDate.GetMonth(pRuntime) + 1, jsDate.GetDay(pRuntime), 201 jsDate.GetHours(pRuntime), jsDate.GetMinutes(pRuntime), 202 jsDate.GetSeconds(pRuntime)); 203 break; 204 case 1: 205 swResult.Format(L"%04d.%02d.%02d %02d:%02d:%02d", 206 jsDate.GetYear(pRuntime), jsDate.GetMonth(pRuntime) + 1, 207 jsDate.GetDay(pRuntime), jsDate.GetHours(pRuntime), 208 jsDate.GetMinutes(pRuntime), 209 jsDate.GetSeconds(pRuntime)); 210 break; 211 case 2: 212 swResult.Format(L"%04d/%02d/%02d %02d:%02d:%02d", 213 jsDate.GetYear(pRuntime), jsDate.GetMonth(pRuntime) + 1, 214 jsDate.GetDay(pRuntime), jsDate.GetHours(pRuntime), 215 jsDate.GetMinutes(pRuntime), 216 jsDate.GetSeconds(pRuntime)); 217 break; 218 default: 219 sError = JSGetStringFromID(IDS_STRING_JSVALUEERROR); 220 return false; 221 } 222 223 vRet = CJS_Value(pRuntime, swResult.c_str()); 224 return true; 225 } 226 227 if (p1.GetType() == CJS_Value::VT_string) { 228 if (iSize > 2 && params[2].ToBool(pRuntime)) { 229 sError = JSGetStringFromID(IDS_STRING_JSNOTSUPPORT); 230 return false; // currently, it doesn't support XFAPicture. 231 } 232 233 // Convert PDF-style format specifiers to wcsftime specifiers. Remove any 234 // pre-existing %-directives before inserting our own. 235 std::basic_string<wchar_t> cFormat = p1.ToCFXWideString(pRuntime).c_str(); 236 cFormat.erase(std::remove(cFormat.begin(), cFormat.end(), '%'), 237 cFormat.end()); 238 239 for (size_t i = 0; i < FX_ArraySize(TbConvertTable); ++i) { 240 int iStart = 0; 241 int iEnd; 242 while ((iEnd = cFormat.find(TbConvertTable[i].lpszJSMark, iStart)) != 243 -1) { 244 cFormat.replace(iEnd, FXSYS_wcslen(TbConvertTable[i].lpszJSMark), 245 TbConvertTable[i].lpszCppMark); 246 iStart = iEnd; 247 } 248 } 249 250 int iYear = jsDate.GetYear(pRuntime); 251 int iMonth = jsDate.GetMonth(pRuntime); 252 int iDay = jsDate.GetDay(pRuntime); 253 int iHour = jsDate.GetHours(pRuntime); 254 int iMin = jsDate.GetMinutes(pRuntime); 255 int iSec = jsDate.GetSeconds(pRuntime); 256 257 TbConvertAdditional cTableAd[] = { 258 {L"m", iMonth + 1}, {L"d", iDay}, 259 {L"H", iHour}, {L"h", iHour > 12 ? iHour - 12 : iHour}, 260 {L"M", iMin}, {L"s", iSec}, 261 }; 262 263 for (size_t i = 0; i < FX_ArraySize(cTableAd); ++i) { 264 wchar_t tszValue[16]; 265 CFX_WideString sValue; 266 sValue.Format(L"%d", cTableAd[i].iValue); 267 memcpy(tszValue, (wchar_t*)sValue.GetBuffer(sValue.GetLength() + 1), 268 (sValue.GetLength() + 1) * sizeof(wchar_t)); 269 270 int iStart = 0; 271 int iEnd; 272 while ((iEnd = cFormat.find(cTableAd[i].lpszJSMark, iStart)) != -1) { 273 if (iEnd > 0) { 274 if (cFormat[iEnd - 1] == L'%') { 275 iStart = iEnd + 1; 276 continue; 277 } 278 } 279 cFormat.replace(iEnd, FXSYS_wcslen(cTableAd[i].lpszJSMark), tszValue); 280 iStart = iEnd; 281 } 282 } 283 284 struct tm time = {}; 285 time.tm_year = iYear - 1900; 286 time.tm_mon = iMonth; 287 time.tm_mday = iDay; 288 time.tm_hour = iHour; 289 time.tm_min = iMin; 290 time.tm_sec = iSec; 291 292 wchar_t buf[64] = {}; 293 wcsftime(buf, 64, cFormat.c_str(), &time); 294 cFormat = buf; 295 vRet = CJS_Value(pRuntime, cFormat.c_str()); 296 return true; 297 } 298 299 sError = JSGetStringFromID(IDS_STRING_JSTYPEERROR); 300 return false; 301 } 302 303 bool util::printx(CJS_Runtime* pRuntime, 304 const std::vector<CJS_Value>& params, 305 CJS_Value& vRet, 306 CFX_WideString& sError) { 307 if (params.size() < 2) { 308 sError = JSGetStringFromID(IDS_STRING_JSPARAMERROR); 309 return false; 310 } 311 312 vRet = CJS_Value(pRuntime, printx(params[0].ToCFXWideString(pRuntime), 313 params[1].ToCFXWideString(pRuntime)) 314 .c_str()); 315 316 return true; 317 } 318 319 enum CaseMode { kPreserveCase, kUpperCase, kLowerCase }; 320 321 static FX_WCHAR TranslateCase(FX_WCHAR input, CaseMode eMode) { 322 if (eMode == kLowerCase && input >= 'A' && input <= 'Z') 323 return input | 0x20; 324 if (eMode == kUpperCase && input >= 'a' && input <= 'z') 325 return input & ~0x20; 326 return input; 327 } 328 329 CFX_WideString util::printx(const CFX_WideString& wsFormat, 330 const CFX_WideString& wsSource) { 331 CFX_WideString wsResult; 332 FX_STRSIZE iSourceIdx = 0; 333 FX_STRSIZE iFormatIdx = 0; 334 CaseMode eCaseMode = kPreserveCase; 335 bool bEscaped = false; 336 while (iFormatIdx < wsFormat.GetLength()) { 337 if (bEscaped) { 338 bEscaped = false; 339 wsResult += wsFormat[iFormatIdx]; 340 ++iFormatIdx; 341 continue; 342 } 343 switch (wsFormat[iFormatIdx]) { 344 case '\\': { 345 bEscaped = true; 346 ++iFormatIdx; 347 } break; 348 case '<': { 349 eCaseMode = kLowerCase; 350 ++iFormatIdx; 351 } break; 352 case '>': { 353 eCaseMode = kUpperCase; 354 ++iFormatIdx; 355 } break; 356 case '=': { 357 eCaseMode = kPreserveCase; 358 ++iFormatIdx; 359 } break; 360 case '?': { 361 if (iSourceIdx < wsSource.GetLength()) { 362 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode); 363 ++iSourceIdx; 364 } 365 ++iFormatIdx; 366 } break; 367 case 'X': { 368 if (iSourceIdx < wsSource.GetLength()) { 369 if ((wsSource[iSourceIdx] >= '0' && wsSource[iSourceIdx] <= '9') || 370 (wsSource[iSourceIdx] >= 'a' && wsSource[iSourceIdx] <= 'z') || 371 (wsSource[iSourceIdx] >= 'A' && wsSource[iSourceIdx] <= 'Z')) { 372 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode); 373 ++iFormatIdx; 374 } 375 ++iSourceIdx; 376 } else { 377 ++iFormatIdx; 378 } 379 } break; 380 case 'A': { 381 if (iSourceIdx < wsSource.GetLength()) { 382 if ((wsSource[iSourceIdx] >= 'a' && wsSource[iSourceIdx] <= 'z') || 383 (wsSource[iSourceIdx] >= 'A' && wsSource[iSourceIdx] <= 'Z')) { 384 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode); 385 ++iFormatIdx; 386 } 387 ++iSourceIdx; 388 } else { 389 ++iFormatIdx; 390 } 391 } break; 392 case '9': { 393 if (iSourceIdx < wsSource.GetLength()) { 394 if (wsSource[iSourceIdx] >= '0' && wsSource[iSourceIdx] <= '9') { 395 wsResult += wsSource[iSourceIdx]; 396 ++iFormatIdx; 397 } 398 ++iSourceIdx; 399 } else { 400 ++iFormatIdx; 401 } 402 } break; 403 case '*': { 404 if (iSourceIdx < wsSource.GetLength()) { 405 wsResult += TranslateCase(wsSource[iSourceIdx], eCaseMode); 406 ++iSourceIdx; 407 } else { 408 ++iFormatIdx; 409 } 410 } break; 411 default: { 412 wsResult += wsFormat[iFormatIdx]; 413 ++iFormatIdx; 414 } break; 415 } 416 } 417 return wsResult; 418 } 419 420 bool util::scand(CJS_Runtime* pRuntime, 421 const std::vector<CJS_Value>& params, 422 CJS_Value& vRet, 423 CFX_WideString& sError) { 424 int iSize = params.size(); 425 if (iSize < 2) 426 return false; 427 428 CFX_WideString sFormat = params[0].ToCFXWideString(pRuntime); 429 CFX_WideString sDate = params[1].ToCFXWideString(pRuntime); 430 double dDate = JS_GetDateTime(); 431 if (sDate.GetLength() > 0) { 432 dDate = CJS_PublicMethods::MakeRegularDate(sDate, sFormat, nullptr); 433 } 434 435 if (!JS_PortIsNan(dDate)) { 436 vRet = CJS_Value(pRuntime, CJS_Date(pRuntime, dDate)); 437 } else { 438 vRet.SetNull(pRuntime); 439 } 440 441 return true; 442 } 443 444 bool util::byteToChar(CJS_Runtime* pRuntime, 445 const std::vector<CJS_Value>& params, 446 CJS_Value& vRet, 447 CFX_WideString& sError) { 448 if (params.size() < 1) { 449 sError = JSGetStringFromID(IDS_STRING_JSPARAMERROR); 450 return false; 451 } 452 453 int arg = params[0].ToInt(pRuntime); 454 if (arg < 0 || arg > 255) { 455 sError = JSGetStringFromID(IDS_STRING_JSVALUEERROR); 456 return false; 457 } 458 459 CFX_WideString wStr(static_cast<FX_WCHAR>(arg)); 460 vRet = CJS_Value(pRuntime, wStr.c_str()); 461 return true; 462 } 463