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