Home | History | Annotate | Download | only in parser
      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 "xfa/fxfa/parser/cscript_layoutpseudomodel.h"
      8 
      9 #include <set>
     10 
     11 #include "fxjs/cfxjse_arguments.h"
     12 #include "third_party/base/stl_util.h"
     13 #include "xfa/fxfa/app/xfa_ffnotify.h"
     14 #include "xfa/fxfa/parser/cxfa_containerlayoutitem.h"
     15 #include "xfa/fxfa/parser/cxfa_contentlayoutitem.h"
     16 #include "xfa/fxfa/parser/cxfa_document.h"
     17 #include "xfa/fxfa/parser/cxfa_layoutitem.h"
     18 #include "xfa/fxfa/parser/cxfa_layoutprocessor.h"
     19 #include "xfa/fxfa/parser/cxfa_measurement.h"
     20 #include "xfa/fxfa/parser/cxfa_scriptcontext.h"
     21 #include "xfa/fxfa/parser/cxfa_traversestrategy_contentlayoutitem.h"
     22 #include "xfa/fxfa/parser/xfa_localemgr.h"
     23 #include "xfa/fxfa/parser/xfa_object.h"
     24 #include "xfa/fxfa/parser/xfa_utils.h"
     25 
     26 CScript_LayoutPseudoModel::CScript_LayoutPseudoModel(CXFA_Document* pDocument)
     27     : CXFA_Object(pDocument,
     28                   XFA_ObjectType::Object,
     29                   XFA_Element::LayoutPseudoModel,
     30                   CFX_WideStringC(L"layoutPseudoModel")) {}
     31 
     32 CScript_LayoutPseudoModel::~CScript_LayoutPseudoModel() {}
     33 
     34 void CScript_LayoutPseudoModel::Ready(CFXJSE_Value* pValue,
     35                                       bool bSetting,
     36                                       XFA_ATTRIBUTE eAttribute) {
     37   CXFA_FFNotify* pNotify = m_pDocument->GetNotify();
     38   if (!pNotify) {
     39     return;
     40   }
     41   if (bSetting) {
     42     ThrowSetReadyException();
     43     return;
     44   }
     45   int32_t iStatus = pNotify->GetLayoutStatus();
     46   pValue->SetBoolean(iStatus >= 2);
     47 }
     48 
     49 void CScript_LayoutPseudoModel::HWXY(CFXJSE_Arguments* pArguments,
     50                                      XFA_LAYOUTMODEL_HWXY layoutModel) {
     51   int32_t iLength = pArguments->GetLength();
     52   if (iLength < 1 || iLength > 3) {
     53     const FX_WCHAR* methodName = nullptr;
     54     switch (layoutModel) {
     55       case XFA_LAYOUTMODEL_H:
     56         methodName = L"h";
     57         break;
     58       case XFA_LAYOUTMODEL_W:
     59         methodName = L"w";
     60         break;
     61       case XFA_LAYOUTMODEL_X:
     62         methodName = L"x";
     63         break;
     64       case XFA_LAYOUTMODEL_Y:
     65         methodName = L"y";
     66         break;
     67     }
     68     ThrowParamCountMismatchException(methodName);
     69     return;
     70   }
     71   CXFA_Node* pNode = nullptr;
     72   CFX_WideString wsUnit(L"pt");
     73   int32_t iIndex = 0;
     74   if (iLength >= 1) {
     75     pNode = static_cast<CXFA_Node*>(pArguments->GetObject(0));
     76   }
     77   if (iLength >= 2) {
     78     CFX_ByteString bsUnit = pArguments->GetUTF8String(1);
     79     if (!bsUnit.IsEmpty()) {
     80       wsUnit = CFX_WideString::FromUTF8(bsUnit.AsStringC());
     81     }
     82   }
     83   if (iLength >= 3) {
     84     iIndex = pArguments->GetInt32(2);
     85   }
     86   if (!pNode) {
     87     return;
     88   }
     89   CXFA_LayoutProcessor* pDocLayout = m_pDocument->GetDocLayout();
     90   if (!pDocLayout) {
     91     return;
     92   }
     93 
     94   CXFA_Measurement measure;
     95   CXFA_LayoutItem* pLayoutItem = pDocLayout->GetLayoutItem(pNode);
     96   if (!pLayoutItem) {
     97     return;
     98   }
     99   while (iIndex > 0 && pLayoutItem) {
    100     pLayoutItem = pLayoutItem->GetNext();
    101     iIndex--;
    102   }
    103   CFXJSE_Value* pValue = pArguments->GetReturnValue();
    104   if (!pLayoutItem) {
    105     pValue->SetFloat(0);
    106     return;
    107   }
    108 
    109   CFX_RectF rtRect = pLayoutItem->GetRect(true);
    110   switch (layoutModel) {
    111     case XFA_LAYOUTMODEL_H:
    112       measure.Set(rtRect.height, XFA_UNIT_Pt);
    113       break;
    114     case XFA_LAYOUTMODEL_W:
    115       measure.Set(rtRect.width, XFA_UNIT_Pt);
    116       break;
    117     case XFA_LAYOUTMODEL_X:
    118       measure.Set(rtRect.left, XFA_UNIT_Pt);
    119       break;
    120     case XFA_LAYOUTMODEL_Y:
    121       measure.Set(rtRect.top, XFA_UNIT_Pt);
    122       break;
    123   }
    124   XFA_UNIT unit = measure.GetUnit(wsUnit.AsStringC());
    125   FX_FLOAT fValue = measure.ToUnit(unit);
    126   fValue = FXSYS_round(fValue * 1000) / 1000.0f;
    127   if (pValue)
    128     pValue->SetFloat(fValue);
    129 }
    130 
    131 void CScript_LayoutPseudoModel::H(CFXJSE_Arguments* pArguments) {
    132   HWXY(pArguments, XFA_LAYOUTMODEL_H);
    133 }
    134 
    135 void CScript_LayoutPseudoModel::W(CFXJSE_Arguments* pArguments) {
    136   HWXY(pArguments, XFA_LAYOUTMODEL_W);
    137 }
    138 
    139 void CScript_LayoutPseudoModel::X(CFXJSE_Arguments* pArguments) {
    140   HWXY(pArguments, XFA_LAYOUTMODEL_X);
    141 }
    142 
    143 void CScript_LayoutPseudoModel::Y(CFXJSE_Arguments* pArguments) {
    144   HWXY(pArguments, XFA_LAYOUTMODEL_Y);
    145 }
    146 
    147 void CScript_LayoutPseudoModel::NumberedPageCount(CFXJSE_Arguments* pArguments,
    148                                                   bool bNumbered) {
    149   CXFA_LayoutProcessor* pDocLayout = m_pDocument->GetDocLayout();
    150   if (!pDocLayout) {
    151     return;
    152   }
    153   int32_t iPageCount = 0;
    154   int32_t iPageNum = pDocLayout->CountPages();
    155   if (bNumbered) {
    156     for (int32_t i = 0; i < iPageNum; i++) {
    157       CXFA_ContainerLayoutItem* pLayoutPage = pDocLayout->GetPage(i);
    158       if (!pLayoutPage) {
    159         continue;
    160       }
    161       CXFA_Node* pMasterPage = pLayoutPage->GetMasterPage();
    162       if (pMasterPage->GetInteger(XFA_ATTRIBUTE_Numbered)) {
    163         iPageCount++;
    164       }
    165     }
    166   } else {
    167     iPageCount = iPageNum;
    168   }
    169   CFXJSE_Value* pValue = pArguments->GetReturnValue();
    170   if (pValue)
    171     pValue->SetInteger(iPageCount);
    172 }
    173 
    174 void CScript_LayoutPseudoModel::PageCount(CFXJSE_Arguments* pArguments) {
    175   NumberedPageCount(pArguments, true);
    176 }
    177 
    178 void CScript_LayoutPseudoModel::PageSpan(CFXJSE_Arguments* pArguments) {
    179   int32_t iLength = pArguments->GetLength();
    180   if (iLength != 1) {
    181     ThrowParamCountMismatchException(L"pageSpan");
    182     return;
    183   }
    184   CXFA_Node* pNode = nullptr;
    185   if (iLength >= 1) {
    186     pNode = static_cast<CXFA_Node*>(pArguments->GetObject(0));
    187   }
    188   if (!pNode) {
    189     return;
    190   }
    191   CXFA_LayoutProcessor* pDocLayout = m_pDocument->GetDocLayout();
    192   if (!pDocLayout) {
    193     return;
    194   }
    195   CFXJSE_Value* pValue = pArguments->GetReturnValue();
    196   CXFA_LayoutItem* pLayoutItem = pDocLayout->GetLayoutItem(pNode);
    197   if (!pLayoutItem) {
    198     pValue->SetInteger(-1);
    199     return;
    200   }
    201   int32_t iLast = pLayoutItem->GetLast()->GetPage()->GetPageIndex();
    202   int32_t iFirst = pLayoutItem->GetFirst()->GetPage()->GetPageIndex();
    203   int32_t iPageSpan = iLast - iFirst + 1;
    204   if (pValue)
    205     pValue->SetInteger(iPageSpan);
    206 }
    207 
    208 void CScript_LayoutPseudoModel::Page(CFXJSE_Arguments* pArguments) {
    209   PageImp(pArguments, false);
    210 }
    211 
    212 void CScript_LayoutPseudoModel::GetObjArray(CXFA_LayoutProcessor* pDocLayout,
    213                                             int32_t iPageNo,
    214                                             const CFX_WideString& wsType,
    215                                             bool bOnPageArea,
    216                                             CXFA_NodeArray& retArray) {
    217   CXFA_ContainerLayoutItem* pLayoutPage = pDocLayout->GetPage(iPageNo);
    218   if (!pLayoutPage) {
    219     return;
    220   }
    221   if (wsType == L"pageArea") {
    222     if (CXFA_Node* pMasterPage = pLayoutPage->m_pFormNode) {
    223       retArray.Add(pMasterPage);
    224     }
    225     return;
    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.Add(pItem->m_pFormNode);
    232       }
    233     }
    234     return;
    235   }
    236   std::set<CXFA_Node*> formItems;
    237   if (wsType.IsEmpty()) {
    238     if (CXFA_Node* pMasterPage = pLayoutPage->m_pFormNode) {
    239       retArray.Add(pMasterPage);
    240     }
    241     for (CXFA_LayoutItem* pItem = pLayoutPage->m_pFirstChild; pItem;
    242          pItem = pItem->m_pNextSibling) {
    243       if (pItem->m_pFormNode->GetElementType() == XFA_Element::ContentArea) {
    244         retArray.Add(pItem->m_pFormNode);
    245         if (!bOnPageArea) {
    246           CXFA_NodeIteratorTemplate<CXFA_ContentLayoutItem,
    247                                     CXFA_TraverseStrategy_ContentLayoutItem>
    248           iterator(static_cast<CXFA_ContentLayoutItem*>(pItem->m_pFirstChild));
    249           for (CXFA_ContentLayoutItem* pItemChild = iterator.GetCurrent();
    250                pItemChild; pItemChild = iterator.MoveToNext()) {
    251             if (!pItemChild->IsContentLayoutItem()) {
    252               continue;
    253             }
    254             XFA_Element eType = pItemChild->m_pFormNode->GetElementType();
    255             if (eType != XFA_Element::Field && eType != XFA_Element::Draw &&
    256                 eType != XFA_Element::Subform && eType != XFA_Element::Area) {
    257               continue;
    258             }
    259             if (pdfium::ContainsValue(formItems, pItemChild->m_pFormNode))
    260               continue;
    261 
    262             formItems.insert(pItemChild->m_pFormNode);
    263             retArray.Add(pItemChild->m_pFormNode);
    264           }
    265         }
    266       } else {
    267         if (bOnPageArea) {
    268           CXFA_NodeIteratorTemplate<CXFA_ContentLayoutItem,
    269                                     CXFA_TraverseStrategy_ContentLayoutItem>
    270           iterator(static_cast<CXFA_ContentLayoutItem*>(pItem));
    271           for (CXFA_ContentLayoutItem* pItemChild = iterator.GetCurrent();
    272                pItemChild; pItemChild = iterator.MoveToNext()) {
    273             if (!pItemChild->IsContentLayoutItem()) {
    274               continue;
    275             }
    276             XFA_Element eType = pItemChild->m_pFormNode->GetElementType();
    277             if (eType != XFA_Element::Field && eType != XFA_Element::Draw &&
    278                 eType != XFA_Element::Subform && eType != XFA_Element::Area) {
    279               continue;
    280             }
    281             if (pdfium::ContainsValue(formItems, pItemChild->m_pFormNode))
    282               continue;
    283             formItems.insert(pItemChild->m_pFormNode);
    284             retArray.Add(pItemChild->m_pFormNode);
    285           }
    286         }
    287       }
    288     }
    289     return;
    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             formItems.insert(pItemChild->m_pFormNode);
    318             retArray.Add(pItemChild->m_pFormNode);
    319           }
    320         }
    321       } else {
    322         if (bOnPageArea) {
    323           CXFA_NodeIteratorTemplate<CXFA_ContentLayoutItem,
    324                                     CXFA_TraverseStrategy_ContentLayoutItem>
    325           iterator(static_cast<CXFA_ContentLayoutItem*>(pItem));
    326           for (CXFA_ContentLayoutItem* pItemChild = iterator.GetCurrent();
    327                pItemChild; pItemChild = iterator.MoveToNext()) {
    328             if (!pItemChild->IsContentLayoutItem())
    329               continue;
    330             if (pItemChild->m_pFormNode->GetElementType() != eType)
    331               continue;
    332             if (pdfium::ContainsValue(formItems, pItemChild->m_pFormNode))
    333               continue;
    334             formItems.insert(pItemChild->m_pFormNode);
    335             retArray.Add(pItemChild->m_pFormNode);
    336           }
    337         }
    338       }
    339     }
    340     return;
    341   }
    342 }
    343 
    344 void CScript_LayoutPseudoModel::PageContent(CFXJSE_Arguments* pArguments) {
    345   int32_t iLength = pArguments->GetLength();
    346   if (iLength < 1 || iLength > 3) {
    347     ThrowParamCountMismatchException(L"pageContent");
    348     return;
    349   }
    350   int32_t iIndex = 0;
    351   CFX_WideString wsType;
    352   bool bOnPageArea = false;
    353   if (iLength >= 1) {
    354     iIndex = pArguments->GetInt32(0);
    355   }
    356   if (iLength >= 2) {
    357     CFX_ByteString bsType = pArguments->GetUTF8String(1);
    358     wsType = CFX_WideString::FromUTF8(bsType.AsStringC());
    359   }
    360   if (iLength >= 3) {
    361     bOnPageArea = pArguments->GetInt32(2) == 0 ? false : true;
    362   }
    363   CXFA_FFNotify* pNotify = m_pDocument->GetNotify();
    364   if (!pNotify) {
    365     return;
    366   }
    367   CXFA_LayoutProcessor* pDocLayout = m_pDocument->GetDocLayout();
    368   if (!pDocLayout) {
    369     return;
    370   }
    371   CXFA_NodeArray retArray;
    372   GetObjArray(pDocLayout, iIndex, wsType, bOnPageArea, retArray);
    373   CXFA_ArrayNodeList* pArrayNodeList = new CXFA_ArrayNodeList(m_pDocument);
    374   pArrayNodeList->SetArrayNodeList(retArray);
    375   pArguments->GetReturnValue()->SetObject(
    376       pArrayNodeList, m_pDocument->GetScriptContext()->GetJseNormalClass());
    377 }
    378 
    379 void CScript_LayoutPseudoModel::AbsPageCount(CFXJSE_Arguments* pArguments) {
    380   NumberedPageCount(pArguments, false);
    381 }
    382 
    383 void CScript_LayoutPseudoModel::AbsPageCountInBatch(
    384     CFXJSE_Arguments* pArguments) {
    385   CFXJSE_Value* pValue = pArguments->GetReturnValue();
    386   if (pValue)
    387     pValue->SetInteger(0);
    388 }
    389 
    390 void CScript_LayoutPseudoModel::SheetCountInBatch(
    391     CFXJSE_Arguments* pArguments) {
    392   CFXJSE_Value* pValue = pArguments->GetReturnValue();
    393   if (pValue)
    394     pValue->SetInteger(0);
    395 }
    396 
    397 void CScript_LayoutPseudoModel::Relayout(CFXJSE_Arguments* pArguments) {
    398   CXFA_Node* pRootNode = m_pDocument->GetRoot();
    399   CXFA_Node* pFormRoot = pRootNode->GetFirstChildByClass(XFA_Element::Form);
    400   ASSERT(pFormRoot);
    401   CXFA_Node* pContentRootNode = pFormRoot->GetNodeItem(XFA_NODEITEM_FirstChild);
    402   CXFA_LayoutProcessor* pLayoutProcessor = m_pDocument->GetLayoutProcessor();
    403   if (pContentRootNode) {
    404     pLayoutProcessor->AddChangedContainer(pContentRootNode);
    405   }
    406   pLayoutProcessor->SetForceReLayout(true);
    407 }
    408 
    409 void CScript_LayoutPseudoModel::AbsPageSpan(CFXJSE_Arguments* pArguments) {
    410   PageSpan(pArguments);
    411 }
    412 
    413 void CScript_LayoutPseudoModel::AbsPageInBatch(CFXJSE_Arguments* pArguments) {
    414   if (pArguments->GetLength() != 1) {
    415     ThrowParamCountMismatchException(L"absPageInBatch");
    416     return;
    417   }
    418 
    419   CFXJSE_Value* pValue = pArguments->GetReturnValue();
    420   if (pValue)
    421     pValue->SetInteger(0);
    422 }
    423 
    424 void CScript_LayoutPseudoModel::SheetInBatch(CFXJSE_Arguments* pArguments) {
    425   if (pArguments->GetLength() != 1) {
    426     ThrowParamCountMismatchException(L"sheetInBatch");
    427     return;
    428   }
    429 
    430   CFXJSE_Value* pValue = pArguments->GetReturnValue();
    431   if (pValue)
    432     pValue->SetInteger(0);
    433 }
    434 
    435 void CScript_LayoutPseudoModel::Sheet(CFXJSE_Arguments* pArguments) {
    436   PageImp(pArguments, true);
    437 }
    438 
    439 void CScript_LayoutPseudoModel::RelayoutPageArea(CFXJSE_Arguments* pArguments) {
    440 }
    441 
    442 void CScript_LayoutPseudoModel::SheetCount(CFXJSE_Arguments* pArguments) {
    443   NumberedPageCount(pArguments, false);
    444 }
    445 
    446 void CScript_LayoutPseudoModel::AbsPage(CFXJSE_Arguments* pArguments) {
    447   PageImp(pArguments, true);
    448 }
    449 
    450 void CScript_LayoutPseudoModel::PageImp(CFXJSE_Arguments* pArguments,
    451                                         bool bAbsPage) {
    452   int32_t iLength = pArguments->GetLength();
    453   if (iLength != 1) {
    454     const FX_WCHAR* methodName;
    455     if (bAbsPage) {
    456       methodName = L"absPage";
    457     } else {
    458       methodName = L"page";
    459     }
    460     ThrowParamCountMismatchException(methodName);
    461     return;
    462   }
    463   CXFA_Node* pNode = nullptr;
    464   if (iLength >= 1) {
    465     pNode = static_cast<CXFA_Node*>(pArguments->GetObject(0));
    466   }
    467   int32_t iPage = 0;
    468   CFXJSE_Value* pValue = pArguments->GetReturnValue();
    469   if (!pNode && pValue)
    470     pValue->SetInteger(iPage);
    471 
    472   CXFA_LayoutProcessor* pDocLayout = m_pDocument->GetDocLayout();
    473   if (!pDocLayout) {
    474     return;
    475   }
    476   CXFA_LayoutItem* pLayoutItem = pDocLayout->GetLayoutItem(pNode);
    477   if (!pLayoutItem) {
    478     pValue->SetInteger(-1);
    479     return;
    480   }
    481   iPage = pLayoutItem->GetFirst()->GetPage()->GetPageIndex();
    482   if (pValue)
    483     pValue->SetInteger(bAbsPage ? iPage : iPage + 1);
    484 }
    485 
    486 void CScript_LayoutPseudoModel::ThrowSetReadyException() const {
    487   ThrowException(L"Unable to set ready value.");
    488 }
    489