Home | History | Annotate | Download | only in xfa
      1 // Copyright 2017 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 "fxjs/xfa/cjx_layoutpseudomodel.h"
      8 
      9 #include <set>
     10 
     11 #include "core/fxcrt/fx_coordinates.h"
     12 #include "fxjs/cfxjse_engine.h"
     13 #include "fxjs/cfxjse_value.h"
     14 #include "fxjs/js_resources.h"
     15 #include "xfa/fxfa/cxfa_ffnotify.h"
     16 #include "xfa/fxfa/parser/cscript_layoutpseudomodel.h"
     17 #include "xfa/fxfa/parser/cxfa_arraynodelist.h"
     18 #include "xfa/fxfa/parser/cxfa_containerlayoutitem.h"
     19 #include "xfa/fxfa/parser/cxfa_document.h"
     20 #include "xfa/fxfa/parser/cxfa_form.h"
     21 #include "xfa/fxfa/parser/cxfa_layoutitem.h"
     22 #include "xfa/fxfa/parser/cxfa_layoutprocessor.h"
     23 #include "xfa/fxfa/parser/cxfa_measurement.h"
     24 #include "xfa/fxfa/parser/cxfa_node.h"
     25 #include "xfa/fxfa/parser/cxfa_nodeiteratortemplate.h"
     26 #include "xfa/fxfa/parser/cxfa_traversestrategy_contentlayoutitem.h"
     27 
     28 const CJX_MethodSpec CJX_LayoutPseudoModel::MethodSpecs[] = {
     29     {"absPage", absPage_static},
     30     {"absPageCount", absPageCount_static},
     31     {"absPageCountInBatch", absPageCountInBatch_static},
     32     {"absPageInBatch", absPageInBatch_static},
     33     {"absPageSpan", absPageSpan_static},
     34     {"h", h_static},
     35     {"page", page_static},
     36     {"pageContent", pageContent_static},
     37     {"pageCount", pageCount_static},
     38     {"pageSpan", pageSpan_static},
     39     {"relayout", relayout_static},
     40     {"relayoutPageArea", relayoutPageArea_static},
     41     {"sheet", sheet_static},
     42     {"sheetCount", sheetCount_static},
     43     {"sheetCountInBatch", sheetCountInBatch_static},
     44     {"sheetInBatch", sheetInBatch_static},
     45     {"w", w_static},
     46     {"x", x_static},
     47     {"y", y_static}};
     48 
     49 CJX_LayoutPseudoModel::CJX_LayoutPseudoModel(CScript_LayoutPseudoModel* model)
     50     : CJX_Object(model) {
     51   DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs));
     52 }
     53 
     54 CJX_LayoutPseudoModel::~CJX_LayoutPseudoModel() {}
     55 
     56 void CJX_LayoutPseudoModel::ready(CFXJSE_Value* pValue,
     57                                   bool bSetting,
     58                                   XFA_Attribute eAttribute) {
     59   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
     60   if (!pNotify)
     61     return;
     62   if (bSetting) {
     63     ThrowException(L"Unable to set ready value.");
     64     return;
     65   }
     66 
     67   int32_t iStatus = pNotify->GetLayoutStatus();
     68   pValue->SetBoolean(iStatus >= 2);
     69 }
     70 
     71 CJS_Return CJX_LayoutPseudoModel::HWXY(
     72     CJS_V8* runtime,
     73     const std::vector<v8::Local<v8::Value>>& params,
     74     XFA_LAYOUTMODEL_HWXY layoutModel) {
     75   if (params.empty() || params.size() > 3)
     76     return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
     77 
     78   CXFA_Node* pNode = ToNode(runtime->ToXFAObject(params[0]));
     79   if (!pNode)
     80     return CJS_Return(true);
     81 
     82   WideString unit(L"pt");
     83   if (params.size() >= 2) {
     84     WideString tmp_unit = runtime->ToWideString(params[1]);
     85     if (!tmp_unit.IsEmpty())
     86       unit = tmp_unit;
     87   }
     88   int32_t iIndex = params.size() >= 3 ? runtime->ToInt32(params[2]) : 0;
     89 
     90   CXFA_LayoutProcessor* pDocLayout = GetDocument()->GetDocLayout();
     91   if (!pDocLayout)
     92     return CJS_Return(true);
     93 
     94   CXFA_LayoutItem* pLayoutItem = pDocLayout->GetLayoutItem(pNode);
     95   if (!pLayoutItem)
     96     return CJS_Return(true);
     97 
     98   while (iIndex > 0 && pLayoutItem) {
     99     pLayoutItem = pLayoutItem->GetNext();
    100     --iIndex;
    101   }
    102 
    103   if (!pLayoutItem)
    104     return CJS_Return(runtime->NewNumber(0.0));
    105 
    106   CXFA_Measurement measure;
    107   CFX_RectF rtRect = pLayoutItem->GetRect(true);
    108   switch (layoutModel) {
    109     case XFA_LAYOUTMODEL_H:
    110       measure.Set(rtRect.height, XFA_Unit::Pt);
    111       break;
    112     case XFA_LAYOUTMODEL_W:
    113       measure.Set(rtRect.width, XFA_Unit::Pt);
    114       break;
    115     case XFA_LAYOUTMODEL_X:
    116       measure.Set(rtRect.left, XFA_Unit::Pt);
    117       break;
    118     case XFA_LAYOUTMODEL_Y:
    119       measure.Set(rtRect.top, XFA_Unit::Pt);
    120       break;
    121   }
    122 
    123   float fValue =
    124       measure.ToUnit(CXFA_Measurement::GetUnitFromString(unit.AsStringView()));
    125   return CJS_Return(runtime->NewNumber(FXSYS_round(fValue * 1000) / 1000.0f));
    126 }
    127 
    128 CJS_Return CJX_LayoutPseudoModel::h(
    129     CJS_V8* runtime,
    130     const std::vector<v8::Local<v8::Value>>& params) {
    131   return HWXY(runtime, params, XFA_LAYOUTMODEL_H);
    132 }
    133 
    134 CJS_Return CJX_LayoutPseudoModel::w(
    135     CJS_V8* runtime,
    136     const std::vector<v8::Local<v8::Value>>& params) {
    137   return HWXY(runtime, params, XFA_LAYOUTMODEL_W);
    138 }
    139 
    140 CJS_Return CJX_LayoutPseudoModel::x(
    141     CJS_V8* runtime,
    142     const std::vector<v8::Local<v8::Value>>& params) {
    143   return HWXY(runtime, params, XFA_LAYOUTMODEL_X);
    144 }
    145 
    146 CJS_Return CJX_LayoutPseudoModel::y(
    147     CJS_V8* runtime,
    148     const std::vector<v8::Local<v8::Value>>& params) {
    149   return HWXY(runtime, params, XFA_LAYOUTMODEL_Y);
    150 }
    151 
    152 CJS_Return CJX_LayoutPseudoModel::NumberedPageCount(CJS_V8* runtime,
    153                                                     bool bNumbered) {
    154   CXFA_LayoutProcessor* pDocLayout = GetDocument()->GetDocLayout();
    155   if (!pDocLayout)
    156     return CJS_Return(true);
    157 
    158   int32_t iPageCount = 0;
    159   int32_t iPageNum = pDocLayout->CountPages();
    160   if (bNumbered) {
    161     for (int32_t i = 0; i < iPageNum; i++) {
    162       CXFA_ContainerLayoutItem* pLayoutPage = pDocLayout->GetPage(i);
    163       if (!pLayoutPage)
    164         continue;
    165 
    166       CXFA_Node* pMasterPage = pLayoutPage->GetMasterPage();
    167       if (pMasterPage->JSObject()->GetInteger(XFA_Attribute::Numbered))
    168         iPageCount++;
    169     }
    170   } else {
    171     iPageCount = iPageNum;
    172   }
    173   return CJS_Return(runtime->NewNumber(iPageCount));
    174 }
    175 
    176 CJS_Return CJX_LayoutPseudoModel::pageCount(
    177     CJS_V8* runtime,
    178     const std::vector<v8::Local<v8::Value>>& params) {
    179   return NumberedPageCount(runtime, true);
    180 }
    181 
    182 CJS_Return CJX_LayoutPseudoModel::pageSpan(
    183     CJS_V8* runtime,
    184     const std::vector<v8::Local<v8::Value>>& params) {
    185   if (params.size() != 1)
    186     return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
    187 
    188   CXFA_Node* pNode = ToNode(runtime->ToXFAObject(params[0]));
    189   if (!pNode)
    190     return CJS_Return(true);
    191 
    192   CXFA_LayoutProcessor* pDocLayout = GetDocument()->GetDocLayout();
    193   if (!pDocLayout)
    194     return CJS_Return(true);
    195 
    196   CXFA_LayoutItem* pLayoutItem = pDocLayout->GetLayoutItem(pNode);
    197   if (!pLayoutItem)
    198     return CJS_Return(runtime->NewNumber(-1));
    199 
    200   int32_t iLast = pLayoutItem->GetLast()->GetPage()->GetPageIndex();
    201   int32_t iFirst = pLayoutItem->GetFirst()->GetPage()->GetPageIndex();
    202   int32_t iPageSpan = iLast - iFirst + 1;
    203   return CJS_Return(runtime->NewNumber(iPageSpan));
    204 }
    205 
    206 CJS_Return CJX_LayoutPseudoModel::page(
    207     CJS_V8* runtime,
    208     const std::vector<v8::Local<v8::Value>>& params) {
    209   return PageInternals(runtime, params, false);
    210 }
    211 
    212 std::vector<CXFA_Node*> CJX_LayoutPseudoModel::GetObjArray(
    213     CXFA_LayoutProcessor* pDocLayout,
    214     int32_t iPageNo,
    215     const WideString& wsType,
    216     bool bOnPageArea) {
    217   CXFA_ContainerLayoutItem* pLayoutPage = pDocLayout->GetPage(iPageNo);
    218   if (!pLayoutPage)
    219     return std::vector<CXFA_Node*>();
    220 
    221   std::vector<CXFA_Node*> retArray;
    222   if (wsType == L"pageArea") {
    223     if (pLayoutPage->m_pFormNode)
    224       retArray.push_back(pLayoutPage->m_pFormNode);
    225     return retArray;
    226   }
    227   if (wsType == L"contentArea") {
    228     for (CXFA_LayoutItem* pItem = pLayoutPage->m_pFirstChild; pItem;
    229          pItem = pItem->m_pNextSibling) {
    230       if (pItem->m_pFormNode->GetElementType() == XFA_Element::ContentArea)
    231         retArray.push_back(pItem->m_pFormNode);
    232     }
    233     return retArray;
    234   }
    235   std::set<CXFA_Node*> formItems;
    236   if (wsType.IsEmpty()) {
    237     if (pLayoutPage->m_pFormNode)
    238       retArray.push_back(pLayoutPage->m_pFormNode);
    239 
    240     for (CXFA_LayoutItem* pItem = pLayoutPage->m_pFirstChild; pItem;
    241          pItem = pItem->m_pNextSibling) {
    242       if (pItem->m_pFormNode->GetElementType() == XFA_Element::ContentArea) {
    243         retArray.push_back(pItem->m_pFormNode);
    244         if (!bOnPageArea) {
    245           CXFA_NodeIteratorTemplate<CXFA_ContentLayoutItem,
    246                                     CXFA_TraverseStrategy_ContentLayoutItem>
    247           iterator(static_cast<CXFA_ContentLayoutItem*>(pItem->m_pFirstChild));
    248           for (CXFA_ContentLayoutItem* pItemChild = iterator.GetCurrent();
    249                pItemChild; pItemChild = iterator.MoveToNext()) {
    250             if (!pItemChild->IsContentLayoutItem()) {
    251               continue;
    252             }
    253             XFA_Element eType = pItemChild->m_pFormNode->GetElementType();
    254             if (eType != XFA_Element::Field && eType != XFA_Element::Draw &&
    255                 eType != XFA_Element::Subform && eType != XFA_Element::Area) {
    256               continue;
    257             }
    258             if (pdfium::ContainsValue(formItems, pItemChild->m_pFormNode))
    259               continue;
    260 
    261             formItems.insert(pItemChild->m_pFormNode);
    262             retArray.push_back(pItemChild->m_pFormNode);
    263           }
    264         }
    265       } else {
    266         if (bOnPageArea) {
    267           CXFA_NodeIteratorTemplate<CXFA_ContentLayoutItem,
    268                                     CXFA_TraverseStrategy_ContentLayoutItem>
    269           iterator(static_cast<CXFA_ContentLayoutItem*>(pItem));
    270           for (CXFA_ContentLayoutItem* pItemChild = iterator.GetCurrent();
    271                pItemChild; pItemChild = iterator.MoveToNext()) {
    272             if (!pItemChild->IsContentLayoutItem()) {
    273               continue;
    274             }
    275             XFA_Element eType = pItemChild->m_pFormNode->GetElementType();
    276             if (eType != XFA_Element::Field && eType != XFA_Element::Draw &&
    277                 eType != XFA_Element::Subform && eType != XFA_Element::Area) {
    278               continue;
    279             }
    280             if (pdfium::ContainsValue(formItems, pItemChild->m_pFormNode))
    281               continue;
    282             formItems.insert(pItemChild->m_pFormNode);
    283             retArray.push_back(pItemChild->m_pFormNode);
    284           }
    285         }
    286       }
    287     }
    288     return retArray;
    289   }
    290 
    291   XFA_Element eType = XFA_Element::Unknown;
    292   if (wsType == L"field")
    293     eType = XFA_Element::Field;
    294   else if (wsType == L"draw")
    295     eType = XFA_Element::Draw;
    296   else if (wsType == L"subform")
    297     eType = XFA_Element::Subform;
    298   else if (wsType == L"area")
    299     eType = XFA_Element::Area;
    300 
    301   if (eType != XFA_Element::Unknown) {
    302     for (CXFA_LayoutItem* pItem = pLayoutPage->m_pFirstChild; pItem;
    303          pItem = pItem->m_pNextSibling) {
    304       if (pItem->m_pFormNode->GetElementType() == XFA_Element::ContentArea) {
    305         if (!bOnPageArea) {
    306           CXFA_NodeIteratorTemplate<CXFA_ContentLayoutItem,
    307                                     CXFA_TraverseStrategy_ContentLayoutItem>
    308           iterator(static_cast<CXFA_ContentLayoutItem*>(pItem->m_pFirstChild));
    309           for (CXFA_ContentLayoutItem* pItemChild = iterator.GetCurrent();
    310                pItemChild; pItemChild = iterator.MoveToNext()) {
    311             if (!pItemChild->IsContentLayoutItem())
    312               continue;
    313             if (pItemChild->m_pFormNode->GetElementType() != eType)
    314               continue;
    315             if (pdfium::ContainsValue(formItems, pItemChild->m_pFormNode))
    316               continue;
    317 
    318             formItems.insert(pItemChild->m_pFormNode);
    319             retArray.push_back(pItemChild->m_pFormNode);
    320           }
    321         }
    322       } else {
    323         if (bOnPageArea) {
    324           CXFA_NodeIteratorTemplate<CXFA_ContentLayoutItem,
    325                                     CXFA_TraverseStrategy_ContentLayoutItem>
    326           iterator(static_cast<CXFA_ContentLayoutItem*>(pItem));
    327           for (CXFA_ContentLayoutItem* pItemChild = iterator.GetCurrent();
    328                pItemChild; pItemChild = iterator.MoveToNext()) {
    329             if (!pItemChild->IsContentLayoutItem())
    330               continue;
    331             if (pItemChild->m_pFormNode->GetElementType() != eType)
    332               continue;
    333             if (pdfium::ContainsValue(formItems, pItemChild->m_pFormNode))
    334               continue;
    335 
    336             formItems.insert(pItemChild->m_pFormNode);
    337             retArray.push_back(pItemChild->m_pFormNode);
    338           }
    339         }
    340       }
    341     }
    342   }
    343   return retArray;
    344 }
    345 
    346 CJS_Return CJX_LayoutPseudoModel::pageContent(
    347     CJS_V8* runtime,
    348     const std::vector<v8::Local<v8::Value>>& params) {
    349   if (params.empty() || params.size() > 3)
    350     return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
    351 
    352   int32_t iIndex = 0;
    353   if (params.size() >= 1)
    354     iIndex = runtime->ToInt32(params[0]);
    355 
    356   WideString wsType;
    357   if (params.size() >= 2)
    358     wsType = runtime->ToWideString(params[1]);
    359 
    360   bool bOnPageArea = false;
    361   if (params.size() >= 3)
    362     bOnPageArea = runtime->ToBoolean(params[2]);
    363 
    364   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
    365   if (!pNotify)
    366     return CJS_Return(true);
    367 
    368   CXFA_LayoutProcessor* pDocLayout = GetDocument()->GetDocLayout();
    369   if (!pDocLayout)
    370     return CJS_Return(true);
    371 
    372   auto pArrayNodeList = pdfium::MakeUnique<CXFA_ArrayNodeList>(GetDocument());
    373   pArrayNodeList->SetArrayNodeList(
    374       GetObjArray(pDocLayout, iIndex, wsType, bOnPageArea));
    375 
    376   // TODO(dsinclair): Who owns the array once we release it? Won't this leak?
    377   return CJS_Return(runtime->NewXFAObject(
    378       pArrayNodeList.release(),
    379       GetDocument()->GetScriptContext()->GetJseNormalClass()->GetTemplate()));
    380 }
    381 
    382 CJS_Return CJX_LayoutPseudoModel::absPageCount(
    383     CJS_V8* runtime,
    384     const std::vector<v8::Local<v8::Value>>& params) {
    385   return NumberedPageCount(runtime, false);
    386 }
    387 
    388 CJS_Return CJX_LayoutPseudoModel::absPageCountInBatch(
    389     CJS_V8* runtime,
    390     const std::vector<v8::Local<v8::Value>>& params) {
    391   return CJS_Return(runtime->NewNumber(0));
    392 }
    393 
    394 CJS_Return CJX_LayoutPseudoModel::sheetCountInBatch(
    395     CJS_V8* runtime,
    396     const std::vector<v8::Local<v8::Value>>& params) {
    397   return CJS_Return(runtime->NewNumber(0));
    398 }
    399 
    400 CJS_Return CJX_LayoutPseudoModel::relayout(
    401     CJS_V8* runtime,
    402     const std::vector<v8::Local<v8::Value>>& params) {
    403   CXFA_Node* pRootNode = GetDocument()->GetRoot();
    404   CXFA_Form* pFormRoot =
    405       pRootNode->GetFirstChildByClass<CXFA_Form>(XFA_Element::Form);
    406   CXFA_Node* pContentRootNode = pFormRoot->GetFirstChild();
    407   CXFA_LayoutProcessor* pLayoutProcessor = GetDocument()->GetLayoutProcessor();
    408   if (pContentRootNode)
    409     pLayoutProcessor->AddChangedContainer(pContentRootNode);
    410 
    411   pLayoutProcessor->SetForceReLayout(true);
    412   return CJS_Return(true);
    413 }
    414 
    415 CJS_Return CJX_LayoutPseudoModel::absPageSpan(
    416     CJS_V8* runtime,
    417     const std::vector<v8::Local<v8::Value>>& params) {
    418   return pageSpan(runtime, params);
    419 }
    420 
    421 CJS_Return CJX_LayoutPseudoModel::absPageInBatch(
    422     CJS_V8* runtime,
    423     const std::vector<v8::Local<v8::Value>>& params) {
    424   if (params.size() != 1)
    425     return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
    426   return CJS_Return(runtime->NewNumber(0));
    427 }
    428 
    429 CJS_Return CJX_LayoutPseudoModel::sheetInBatch(
    430     CJS_V8* runtime,
    431     const std::vector<v8::Local<v8::Value>>& params) {
    432   if (params.size() != 1)
    433     return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
    434   return CJS_Return(runtime->NewNumber(0));
    435 }
    436 
    437 CJS_Return CJX_LayoutPseudoModel::sheet(
    438     CJS_V8* runtime,
    439     const std::vector<v8::Local<v8::Value>>& params) {
    440   return PageInternals(runtime, params, true);
    441 }
    442 
    443 CJS_Return CJX_LayoutPseudoModel::relayoutPageArea(
    444     CJS_V8* runtime,
    445     const std::vector<v8::Local<v8::Value>>& params) {
    446   return CJS_Return(true);
    447 }
    448 
    449 CJS_Return CJX_LayoutPseudoModel::sheetCount(
    450     CJS_V8* runtime,
    451     const std::vector<v8::Local<v8::Value>>& params) {
    452   return NumberedPageCount(runtime, false);
    453 }
    454 
    455 CJS_Return CJX_LayoutPseudoModel::absPage(
    456     CJS_V8* runtime,
    457     const std::vector<v8::Local<v8::Value>>& params) {
    458   return PageInternals(runtime, params, true);
    459 }
    460 
    461 CJS_Return CJX_LayoutPseudoModel::PageInternals(
    462     CJS_V8* runtime,
    463     const std::vector<v8::Local<v8::Value>>& params,
    464     bool bAbsPage) {
    465   if (params.size() != 1)
    466     return CJS_Return(JSGetStringFromID(JSMessage::kParamError));
    467 
    468   CXFA_Node* pNode = ToNode(runtime->ToXFAObject(params[0]));
    469   if (!pNode)
    470     return CJS_Return(runtime->NewNumber(0));
    471 
    472   CXFA_LayoutProcessor* pDocLayout = GetDocument()->GetDocLayout();
    473   if (!pDocLayout)
    474     return CJS_Return(true);
    475 
    476   CXFA_LayoutItem* pLayoutItem = pDocLayout->GetLayoutItem(pNode);
    477   if (!pLayoutItem)
    478     return CJS_Return(runtime->NewNumber(-1));
    479 
    480   int32_t iPage = pLayoutItem->GetFirst()->GetPage()->GetPageIndex();
    481   return CJS_Return(runtime->NewNumber(bAbsPage ? iPage : iPage + 1));
    482 }
    483