Home | History | Annotate | Download | only in javascript
      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