• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CJX_LayoutPseudoModel(CScript_LayoutPseudoModel * model)49 CJX_LayoutPseudoModel::CJX_LayoutPseudoModel(CScript_LayoutPseudoModel* model)
50     : CJX_Object(model) {
51   DefineMethods(MethodSpecs, FX_ArraySize(MethodSpecs));
52 }
53 
~CJX_LayoutPseudoModel()54 CJX_LayoutPseudoModel::~CJX_LayoutPseudoModel() {}
55 
ready(CFXJSE_Value * pValue,bool bSetting,XFA_Attribute eAttribute)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 
HWXY(CJS_V8 * runtime,const std::vector<v8::Local<v8::Value>> & params,XFA_LAYOUTMODEL_HWXY layoutModel)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 
h(CJS_V8 * runtime,const std::vector<v8::Local<v8::Value>> & params)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 
w(CJS_V8 * runtime,const std::vector<v8::Local<v8::Value>> & params)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 
x(CJS_V8 * runtime,const std::vector<v8::Local<v8::Value>> & params)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 
y(CJS_V8 * runtime,const std::vector<v8::Local<v8::Value>> & params)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 
NumberedPageCount(CJS_V8 * runtime,bool bNumbered)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 
pageCount(CJS_V8 * runtime,const std::vector<v8::Local<v8::Value>> & params)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 
pageSpan(CJS_V8 * runtime,const std::vector<v8::Local<v8::Value>> & params)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 
page(CJS_V8 * runtime,const std::vector<v8::Local<v8::Value>> & params)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 
GetObjArray(CXFA_LayoutProcessor * pDocLayout,int32_t iPageNo,const WideString & wsType,bool bOnPageArea)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 
pageContent(CJS_V8 * runtime,const std::vector<v8::Local<v8::Value>> & params)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 
absPageCount(CJS_V8 * runtime,const std::vector<v8::Local<v8::Value>> & params)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 
absPageCountInBatch(CJS_V8 * runtime,const std::vector<v8::Local<v8::Value>> & params)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 
sheetCountInBatch(CJS_V8 * runtime,const std::vector<v8::Local<v8::Value>> & params)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 
relayout(CJS_V8 * runtime,const std::vector<v8::Local<v8::Value>> & params)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 
absPageSpan(CJS_V8 * runtime,const std::vector<v8::Local<v8::Value>> & params)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 
absPageInBatch(CJS_V8 * runtime,const std::vector<v8::Local<v8::Value>> & params)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 
sheetInBatch(CJS_V8 * runtime,const std::vector<v8::Local<v8::Value>> & params)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 
sheet(CJS_V8 * runtime,const std::vector<v8::Local<v8::Value>> & params)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 
relayoutPageArea(CJS_V8 * runtime,const std::vector<v8::Local<v8::Value>> & params)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 
sheetCount(CJS_V8 * runtime,const std::vector<v8::Local<v8::Value>> & params)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 
absPage(CJS_V8 * runtime,const std::vector<v8::Local<v8::Value>> & params)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 
PageInternals(CJS_V8 * runtime,const std::vector<v8::Local<v8::Value>> & params,bool bAbsPage)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