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 26CScript_LayoutPseudoModel::CScript_LayoutPseudoModel(CXFA_Document* pDocument) 27 : CXFA_Object(pDocument, 28 XFA_ObjectType::Object, 29 XFA_Element::LayoutPseudoModel, 30 CFX_WideStringC(L"layoutPseudoModel")) {} 31 32CScript_LayoutPseudoModel::~CScript_LayoutPseudoModel() {} 33 34void 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 49void 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 131void CScript_LayoutPseudoModel::H(CFXJSE_Arguments* pArguments) { 132 HWXY(pArguments, XFA_LAYOUTMODEL_H); 133} 134 135void CScript_LayoutPseudoModel::W(CFXJSE_Arguments* pArguments) { 136 HWXY(pArguments, XFA_LAYOUTMODEL_W); 137} 138 139void CScript_LayoutPseudoModel::X(CFXJSE_Arguments* pArguments) { 140 HWXY(pArguments, XFA_LAYOUTMODEL_X); 141} 142 143void CScript_LayoutPseudoModel::Y(CFXJSE_Arguments* pArguments) { 144 HWXY(pArguments, XFA_LAYOUTMODEL_Y); 145} 146 147void 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 174void CScript_LayoutPseudoModel::PageCount(CFXJSE_Arguments* pArguments) { 175 NumberedPageCount(pArguments, true); 176} 177 178void 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 208void CScript_LayoutPseudoModel::Page(CFXJSE_Arguments* pArguments) { 209 PageImp(pArguments, false); 210} 211 212void 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 344void 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 379void CScript_LayoutPseudoModel::AbsPageCount(CFXJSE_Arguments* pArguments) { 380 NumberedPageCount(pArguments, false); 381} 382 383void CScript_LayoutPseudoModel::AbsPageCountInBatch( 384 CFXJSE_Arguments* pArguments) { 385 CFXJSE_Value* pValue = pArguments->GetReturnValue(); 386 if (pValue) 387 pValue->SetInteger(0); 388} 389 390void CScript_LayoutPseudoModel::SheetCountInBatch( 391 CFXJSE_Arguments* pArguments) { 392 CFXJSE_Value* pValue = pArguments->GetReturnValue(); 393 if (pValue) 394 pValue->SetInteger(0); 395} 396 397void 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 409void CScript_LayoutPseudoModel::AbsPageSpan(CFXJSE_Arguments* pArguments) { 410 PageSpan(pArguments); 411} 412 413void 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 424void 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 435void CScript_LayoutPseudoModel::Sheet(CFXJSE_Arguments* pArguments) { 436 PageImp(pArguments, true); 437} 438 439void CScript_LayoutPseudoModel::RelayoutPageArea(CFXJSE_Arguments* pArguments) { 440} 441 442void CScript_LayoutPseudoModel::SheetCount(CFXJSE_Arguments* pArguments) { 443 NumberedPageCount(pArguments, false); 444} 445 446void CScript_LayoutPseudoModel::AbsPage(CFXJSE_Arguments* pArguments) { 447 PageImp(pArguments, true); 448} 449 450void 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 486void CScript_LayoutPseudoModel::ThrowSetReadyException() const { 487 ThrowException(L"Unable to set ready value."); 488} 489