// Copyright 2017 PDFium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com #include "fxjs/cjx_layoutpseudomodel.h" #include #include "core/fxcrt/fx_coordinates.h" #include "fxjs/cfxjse_arguments.h" #include "fxjs/cfxjse_engine.h" #include "fxjs/cfxjse_value.h" #include "xfa/fxfa/cxfa_ffnotify.h" #include "xfa/fxfa/parser/cscript_layoutpseudomodel.h" #include "xfa/fxfa/parser/cxfa_arraynodelist.h" #include "xfa/fxfa/parser/cxfa_containerlayoutitem.h" #include "xfa/fxfa/parser/cxfa_document.h" #include "xfa/fxfa/parser/cxfa_layoutitem.h" #include "xfa/fxfa/parser/cxfa_layoutprocessor.h" #include "xfa/fxfa/parser/cxfa_measurement.h" #include "xfa/fxfa/parser/cxfa_node.h" #include "xfa/fxfa/parser/cxfa_nodeiteratortemplate.h" #include "xfa/fxfa/parser/cxfa_traversestrategy_contentlayoutitem.h" const CJX_MethodSpec CJX_LayoutPseudoModel::MethodSpecs[] = { {"absPage", absPage_static}, {"absPageCount", absPageCount_static}, {"absPageCountInBatch", absPageCountInBatch_static}, {"absPageInBatch", absPageInBatch_static}, {"absPageSpan", absPageSpan_static}, {"h", h_static}, {"page", page_static}, {"pageContent", pageContent_static}, {"pageCount", pageCount_static}, {"pageSpan", pageSpan_static}, {"relayout", relayout_static}, {"relayoutPageArea", relayoutPageArea_static}, {"sheet", sheet_static}, {"sheetCount", sheetCount_static}, {"sheetCountInBatch", sheetCountInBatch_static}, {"sheetInBatch", sheetInBatch_static}, {"w", w_static}, {"x", x_static}, {"y", y_static}, {"", nullptr}}; CJX_LayoutPseudoModel::CJX_LayoutPseudoModel(CScript_LayoutPseudoModel* model) : CJX_Object(model) { DefineMethods(MethodSpecs); } CJX_LayoutPseudoModel::~CJX_LayoutPseudoModel() {} void CJX_LayoutPseudoModel::Ready(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute) { CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); if (!pNotify) return; if (bSetting) { ThrowException(L"Unable to set ready value."); return; } int32_t iStatus = pNotify->GetLayoutStatus(); pValue->SetBoolean(iStatus >= 2); } void CJX_LayoutPseudoModel::HWXY(CFXJSE_Arguments* pArguments, XFA_LAYOUTMODEL_HWXY layoutModel) { int32_t iLength = pArguments->GetLength(); if (iLength < 1 || iLength > 3) { const wchar_t* methodName = nullptr; switch (layoutModel) { case XFA_LAYOUTMODEL_H: methodName = L"h"; break; case XFA_LAYOUTMODEL_W: methodName = L"w"; break; case XFA_LAYOUTMODEL_X: methodName = L"x"; break; case XFA_LAYOUTMODEL_Y: methodName = L"y"; break; } ThrowParamCountMismatchException(methodName); return; } CXFA_Node* pNode = static_cast(pArguments->GetObject(0)); if (!pNode) return; WideString wsUnit(L"pt"); if (iLength >= 2) { ByteString bsUnit = pArguments->GetUTF8String(1); if (!bsUnit.IsEmpty()) wsUnit = WideString::FromUTF8(bsUnit.AsStringView()); } int32_t iIndex = iLength >= 3 ? pArguments->GetInt32(2) : 0; CXFA_LayoutProcessor* pDocLayout = GetDocument()->GetDocLayout(); if (!pDocLayout) return; CXFA_LayoutItem* pLayoutItem = pDocLayout->GetLayoutItem(pNode); if (!pLayoutItem) return; while (iIndex > 0 && pLayoutItem) { pLayoutItem = pLayoutItem->GetNext(); iIndex--; } CFXJSE_Value* pValue = pArguments->GetReturnValue(); if (!pLayoutItem) { pValue->SetFloat(0); return; } CXFA_Measurement measure; CFX_RectF rtRect = pLayoutItem->GetRect(true); switch (layoutModel) { case XFA_LAYOUTMODEL_H: measure.Set(rtRect.height, XFA_Unit::Pt); break; case XFA_LAYOUTMODEL_W: measure.Set(rtRect.width, XFA_Unit::Pt); break; case XFA_LAYOUTMODEL_X: measure.Set(rtRect.left, XFA_Unit::Pt); break; case XFA_LAYOUTMODEL_Y: measure.Set(rtRect.top, XFA_Unit::Pt); break; } float fValue = measure.ToUnit( CXFA_Measurement::GetUnitFromString(wsUnit.AsStringView())); fValue = FXSYS_round(fValue * 1000) / 1000.0f; pValue->SetFloat(fValue); } void CJX_LayoutPseudoModel::h(CFXJSE_Arguments* pArguments) { HWXY(pArguments, XFA_LAYOUTMODEL_H); } void CJX_LayoutPseudoModel::w(CFXJSE_Arguments* pArguments) { HWXY(pArguments, XFA_LAYOUTMODEL_W); } void CJX_LayoutPseudoModel::x(CFXJSE_Arguments* pArguments) { HWXY(pArguments, XFA_LAYOUTMODEL_X); } void CJX_LayoutPseudoModel::y(CFXJSE_Arguments* pArguments) { HWXY(pArguments, XFA_LAYOUTMODEL_Y); } void CJX_LayoutPseudoModel::NumberedPageCount(CFXJSE_Arguments* pArguments, bool bNumbered) { CXFA_LayoutProcessor* pDocLayout = GetDocument()->GetDocLayout(); if (!pDocLayout) return; int32_t iPageCount = 0; int32_t iPageNum = pDocLayout->CountPages(); if (bNumbered) { for (int32_t i = 0; i < iPageNum; i++) { CXFA_ContainerLayoutItem* pLayoutPage = pDocLayout->GetPage(i); if (!pLayoutPage) continue; CXFA_Node* pMasterPage = pLayoutPage->GetMasterPage(); if (pMasterPage->JSNode()->GetInteger(XFA_Attribute::Numbered)) iPageCount++; } } else { iPageCount = iPageNum; } pArguments->GetReturnValue()->SetInteger(iPageCount); } void CJX_LayoutPseudoModel::pageCount(CFXJSE_Arguments* pArguments) { NumberedPageCount(pArguments, true); } void CJX_LayoutPseudoModel::pageSpan(CFXJSE_Arguments* pArguments) { int32_t iLength = pArguments->GetLength(); if (iLength != 1) { ThrowParamCountMismatchException(L"pageSpan"); return; } CXFA_Node* pNode = nullptr; if (iLength >= 1) pNode = static_cast(pArguments->GetObject(0)); if (!pNode) return; CXFA_LayoutProcessor* pDocLayout = GetDocument()->GetDocLayout(); if (!pDocLayout) return; CFXJSE_Value* pValue = pArguments->GetReturnValue(); CXFA_LayoutItem* pLayoutItem = pDocLayout->GetLayoutItem(pNode); if (!pLayoutItem) { pValue->SetInteger(-1); return; } int32_t iLast = pLayoutItem->GetLast()->GetPage()->GetPageIndex(); int32_t iFirst = pLayoutItem->GetFirst()->GetPage()->GetPageIndex(); int32_t iPageSpan = iLast - iFirst + 1; pValue->SetInteger(iPageSpan); } void CJX_LayoutPseudoModel::page(CFXJSE_Arguments* pArguments) { PageInternals(pArguments, false); } std::vector CJX_LayoutPseudoModel::GetObjArray( CXFA_LayoutProcessor* pDocLayout, int32_t iPageNo, const WideString& wsType, bool bOnPageArea) { CXFA_ContainerLayoutItem* pLayoutPage = pDocLayout->GetPage(iPageNo); if (!pLayoutPage) return std::vector(); std::vector retArray; if (wsType == L"pageArea") { if (pLayoutPage->m_pFormNode) retArray.push_back(pLayoutPage->m_pFormNode); return retArray; } if (wsType == L"contentArea") { for (CXFA_LayoutItem* pItem = pLayoutPage->m_pFirstChild; pItem; pItem = pItem->m_pNextSibling) { if (pItem->m_pFormNode->GetElementType() == XFA_Element::ContentArea) retArray.push_back(pItem->m_pFormNode); } return retArray; } std::set formItems; if (wsType.IsEmpty()) { if (pLayoutPage->m_pFormNode) retArray.push_back(pLayoutPage->m_pFormNode); for (CXFA_LayoutItem* pItem = pLayoutPage->m_pFirstChild; pItem; pItem = pItem->m_pNextSibling) { if (pItem->m_pFormNode->GetElementType() == XFA_Element::ContentArea) { retArray.push_back(pItem->m_pFormNode); if (!bOnPageArea) { CXFA_NodeIteratorTemplate iterator(static_cast(pItem->m_pFirstChild)); for (CXFA_ContentLayoutItem* pItemChild = iterator.GetCurrent(); pItemChild; pItemChild = iterator.MoveToNext()) { if (!pItemChild->IsContentLayoutItem()) { continue; } XFA_Element eType = pItemChild->m_pFormNode->GetElementType(); if (eType != XFA_Element::Field && eType != XFA_Element::Draw && eType != XFA_Element::Subform && eType != XFA_Element::Area) { continue; } if (pdfium::ContainsValue(formItems, pItemChild->m_pFormNode)) continue; formItems.insert(pItemChild->m_pFormNode); retArray.push_back(pItemChild->m_pFormNode); } } } else { if (bOnPageArea) { CXFA_NodeIteratorTemplate iterator(static_cast(pItem)); for (CXFA_ContentLayoutItem* pItemChild = iterator.GetCurrent(); pItemChild; pItemChild = iterator.MoveToNext()) { if (!pItemChild->IsContentLayoutItem()) { continue; } XFA_Element eType = pItemChild->m_pFormNode->GetElementType(); if (eType != XFA_Element::Field && eType != XFA_Element::Draw && eType != XFA_Element::Subform && eType != XFA_Element::Area) { continue; } if (pdfium::ContainsValue(formItems, pItemChild->m_pFormNode)) continue; formItems.insert(pItemChild->m_pFormNode); retArray.push_back(pItemChild->m_pFormNode); } } } } return retArray; } XFA_Element eType = XFA_Element::Unknown; if (wsType == L"field") eType = XFA_Element::Field; else if (wsType == L"draw") eType = XFA_Element::Draw; else if (wsType == L"subform") eType = XFA_Element::Subform; else if (wsType == L"area") eType = XFA_Element::Area; if (eType != XFA_Element::Unknown) { for (CXFA_LayoutItem* pItem = pLayoutPage->m_pFirstChild; pItem; pItem = pItem->m_pNextSibling) { if (pItem->m_pFormNode->GetElementType() == XFA_Element::ContentArea) { if (!bOnPageArea) { CXFA_NodeIteratorTemplate iterator(static_cast(pItem->m_pFirstChild)); for (CXFA_ContentLayoutItem* pItemChild = iterator.GetCurrent(); pItemChild; pItemChild = iterator.MoveToNext()) { if (!pItemChild->IsContentLayoutItem()) continue; if (pItemChild->m_pFormNode->GetElementType() != eType) continue; if (pdfium::ContainsValue(formItems, pItemChild->m_pFormNode)) continue; formItems.insert(pItemChild->m_pFormNode); retArray.push_back(pItemChild->m_pFormNode); } } } else { if (bOnPageArea) { CXFA_NodeIteratorTemplate iterator(static_cast(pItem)); for (CXFA_ContentLayoutItem* pItemChild = iterator.GetCurrent(); pItemChild; pItemChild = iterator.MoveToNext()) { if (!pItemChild->IsContentLayoutItem()) continue; if (pItemChild->m_pFormNode->GetElementType() != eType) continue; if (pdfium::ContainsValue(formItems, pItemChild->m_pFormNode)) continue; formItems.insert(pItemChild->m_pFormNode); retArray.push_back(pItemChild->m_pFormNode); } } } } } return retArray; } void CJX_LayoutPseudoModel::pageContent(CFXJSE_Arguments* pArguments) { int32_t iLength = pArguments->GetLength(); if (iLength < 1 || iLength > 3) { ThrowParamCountMismatchException(L"pageContent"); return; } int32_t iIndex = 0; WideString wsType; bool bOnPageArea = false; if (iLength >= 1) iIndex = pArguments->GetInt32(0); if (iLength >= 2) { ByteString bsType = pArguments->GetUTF8String(1); wsType = WideString::FromUTF8(bsType.AsStringView()); } if (iLength >= 3) bOnPageArea = pArguments->GetInt32(2) == 0 ? false : true; CXFA_FFNotify* pNotify = GetDocument()->GetNotify(); if (!pNotify) return; CXFA_LayoutProcessor* pDocLayout = GetDocument()->GetDocLayout(); if (!pDocLayout) return; auto pArrayNodeList = pdfium::MakeUnique(GetDocument()); pArrayNodeList->SetArrayNodeList( GetObjArray(pDocLayout, iIndex, wsType, bOnPageArea)); pArguments->GetReturnValue()->SetObject( pArrayNodeList.release(), GetDocument()->GetScriptContext()->GetJseNormalClass()); } void CJX_LayoutPseudoModel::absPageCount(CFXJSE_Arguments* pArguments) { NumberedPageCount(pArguments, false); } void CJX_LayoutPseudoModel::absPageCountInBatch(CFXJSE_Arguments* pArguments) { pArguments->GetReturnValue()->SetInteger(0); } void CJX_LayoutPseudoModel::sheetCountInBatch(CFXJSE_Arguments* pArguments) { pArguments->GetReturnValue()->SetInteger(0); } void CJX_LayoutPseudoModel::relayout(CFXJSE_Arguments* pArguments) { CXFA_Node* pRootNode = GetDocument()->GetRoot(); CXFA_Node* pFormRoot = pRootNode->GetFirstChildByClass(XFA_Element::Form); CXFA_Node* pContentRootNode = pFormRoot->GetNodeItem(XFA_NODEITEM_FirstChild); CXFA_LayoutProcessor* pLayoutProcessor = GetDocument()->GetLayoutProcessor(); if (pContentRootNode) pLayoutProcessor->AddChangedContainer(pContentRootNode); pLayoutProcessor->SetForceReLayout(true); } void CJX_LayoutPseudoModel::absPageSpan(CFXJSE_Arguments* pArguments) { pageSpan(pArguments); } void CJX_LayoutPseudoModel::absPageInBatch(CFXJSE_Arguments* pArguments) { if (pArguments->GetLength() != 1) { ThrowParamCountMismatchException(L"absPageInBatch"); return; } pArguments->GetReturnValue()->SetInteger(0); } void CJX_LayoutPseudoModel::sheetInBatch(CFXJSE_Arguments* pArguments) { if (pArguments->GetLength() != 1) { ThrowParamCountMismatchException(L"sheetInBatch"); return; } pArguments->GetReturnValue()->SetInteger(0); } void CJX_LayoutPseudoModel::sheet(CFXJSE_Arguments* pArguments) { PageInternals(pArguments, true); } void CJX_LayoutPseudoModel::relayoutPageArea(CFXJSE_Arguments* pArguments) {} void CJX_LayoutPseudoModel::sheetCount(CFXJSE_Arguments* pArguments) { NumberedPageCount(pArguments, false); } void CJX_LayoutPseudoModel::absPage(CFXJSE_Arguments* pArguments) { PageInternals(pArguments, true); } void CJX_LayoutPseudoModel::PageInternals(CFXJSE_Arguments* pArguments, bool bAbsPage) { int32_t iLength = pArguments->GetLength(); if (iLength != 1) { ThrowParamCountMismatchException(bAbsPage ? L"absPage" : L"page"); return; } CXFA_Node* pNode = static_cast(pArguments->GetObject(0)); CFXJSE_Value* pValue = pArguments->GetReturnValue(); if (!pNode) pValue->SetInteger(0); CXFA_LayoutProcessor* pDocLayout = GetDocument()->GetDocLayout(); if (!pDocLayout) return; CXFA_LayoutItem* pLayoutItem = pDocLayout->GetLayoutItem(pNode); if (!pLayoutItem) { pValue->SetInteger(-1); return; } int32_t iPage = pLayoutItem->GetFirst()->GetPage()->GetPageIndex(); pValue->SetInteger(bAbsPage ? iPage : iPage + 1); }