• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The PDFium Authors
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 #include <utility>
11 
12 #include "core/fxcrt/containers/contains.h"
13 #include "core/fxcrt/fx_coordinates.h"
14 #include "fxjs/fxv8.h"
15 #include "fxjs/js_resources.h"
16 #include "fxjs/xfa/cfxjse_class.h"
17 #include "fxjs/xfa/cfxjse_engine.h"
18 #include "v8/include/cppgc/allocation.h"
19 #include "v8/include/v8-object.h"
20 #include "xfa/fxfa/cxfa_ffnotify.h"
21 #include "xfa/fxfa/layout/cxfa_contentlayoutitem.h"
22 #include "xfa/fxfa/layout/cxfa_layoutitem.h"
23 #include "xfa/fxfa/layout/cxfa_layoutprocessor.h"
24 #include "xfa/fxfa/layout/cxfa_traversestrategy_layoutitem.h"
25 #include "xfa/fxfa/layout/cxfa_viewlayoutitem.h"
26 #include "xfa/fxfa/parser/cscript_layoutpseudomodel.h"
27 #include "xfa/fxfa/parser/cxfa_arraynodelist.h"
28 #include "xfa/fxfa/parser/cxfa_document.h"
29 #include "xfa/fxfa/parser/cxfa_form.h"
30 #include "xfa/fxfa/parser/cxfa_measurement.h"
31 #include "xfa/fxfa/parser/cxfa_node.h"
32 #include "xfa/fxfa/parser/cxfa_nodeiteratortemplate.h"
33 
34 const CJX_MethodSpec CJX_LayoutPseudoModel::MethodSpecs[] = {
35     {"absPage", absPage_static},
36     {"absPageCount", absPageCount_static},
37     {"absPageCountInBatch", absPageCountInBatch_static},
38     {"absPageInBatch", absPageInBatch_static},
39     {"absPageSpan", absPageSpan_static},
40     {"h", h_static},
41     {"page", page_static},
42     {"pageContent", pageContent_static},
43     {"pageCount", pageCount_static},
44     {"pageSpan", pageSpan_static},
45     {"relayout", relayout_static},
46     {"relayoutPageArea", relayoutPageArea_static},
47     {"sheet", sheet_static},
48     {"sheetCount", sheetCount_static},
49     {"sheetCountInBatch", sheetCountInBatch_static},
50     {"sheetInBatch", sheetInBatch_static},
51     {"w", w_static},
52     {"x", x_static},
53     {"y", y_static}};
54 
CJX_LayoutPseudoModel(CScript_LayoutPseudoModel * model)55 CJX_LayoutPseudoModel::CJX_LayoutPseudoModel(CScript_LayoutPseudoModel* model)
56     : CJX_Object(model) {
57   DefineMethods(MethodSpecs);
58 }
59 
60 CJX_LayoutPseudoModel::~CJX_LayoutPseudoModel() = default;
61 
DynamicTypeIs(TypeTag eType) const62 bool CJX_LayoutPseudoModel::DynamicTypeIs(TypeTag eType) const {
63   return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
64 }
65 
ready(v8::Isolate * pIsolate,v8::Local<v8::Value> * pValue,bool bSetting,XFA_Attribute eAttribute)66 void CJX_LayoutPseudoModel::ready(v8::Isolate* pIsolate,
67                                   v8::Local<v8::Value>* pValue,
68                                   bool bSetting,
69                                   XFA_Attribute eAttribute) {
70   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
71   if (!pNotify)
72     return;
73   if (bSetting) {
74     ThrowException(pIsolate,
75                    WideString::FromASCII("Unable to set ready value."));
76     return;
77   }
78 
79   CXFA_FFDocView::LayoutStatus iStatus = pNotify->GetLayoutStatus();
80   const bool bReady = iStatus != CXFA_FFDocView::LayoutStatus::kNone &&
81                       iStatus != CXFA_FFDocView::LayoutStatus::kStart;
82   *pValue = fxv8::NewBooleanHelper(pIsolate, bReady);
83 }
84 
DoHWXYInternal(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params,HWXY layoutModel)85 CJS_Result CJX_LayoutPseudoModel::DoHWXYInternal(
86     CFXJSE_Engine* runtime,
87     pdfium::span<v8::Local<v8::Value>> params,
88     HWXY layoutModel) {
89   if (params.empty() || params.size() > 3)
90     return CJS_Result::Failure(JSMessage::kParamError);
91 
92   CXFA_Node* pNode = ToNode(runtime->ToXFAObject(params[0]));
93   if (!pNode)
94     return CJS_Result::Success();
95 
96   WideString unit = WideString::FromASCII("pt");
97   if (params.size() >= 2) {
98     WideString tmp_unit = runtime->ToWideString(params[1]);
99     if (!tmp_unit.IsEmpty())
100       unit = std::move(tmp_unit);
101   }
102   int32_t iIndex = params.size() >= 3 ? runtime->ToInt32(params[2]) : 0;
103   auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(GetDocument());
104   CXFA_ContentLayoutItem* pLayoutItem =
105       ToContentLayoutItem(pDocLayout->GetLayoutItem(pNode));
106   if (!pLayoutItem)
107     return CJS_Result::Success();
108 
109   while (iIndex > 0 && pLayoutItem) {
110     pLayoutItem = pLayoutItem->GetNext();
111     --iIndex;
112   }
113 
114   if (!pLayoutItem)
115     return CJS_Result::Success(runtime->NewNumber(0.0));
116 
117   CXFA_Measurement measure;
118   CFX_RectF rtRect = pLayoutItem->GetRelativeRect();
119   switch (layoutModel) {
120     case HWXY::kH:
121       measure.Set(rtRect.height, XFA_Unit::Pt);
122       break;
123     case HWXY::kW:
124       measure.Set(rtRect.width, XFA_Unit::Pt);
125       break;
126     case HWXY::kX:
127       measure.Set(rtRect.left, XFA_Unit::Pt);
128       break;
129     case HWXY::kY:
130       measure.Set(rtRect.top, XFA_Unit::Pt);
131       break;
132   }
133 
134   XFA_Unit eUnit = CXFA_Measurement::GetUnitFromString(unit.AsStringView());
135   if (eUnit == XFA_Unit::Unknown)
136     return CJS_Result::Failure(JSMessage::kValueError);
137 
138   float fValue = measure.ToUnit(eUnit);
139   return CJS_Result::Success(
140       runtime->NewNumber(FXSYS_roundf(fValue * 1000) / 1000.0f));
141 }
142 
h(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params)143 CJS_Result CJX_LayoutPseudoModel::h(CFXJSE_Engine* runtime,
144                                     pdfium::span<v8::Local<v8::Value>> params) {
145   return DoHWXYInternal(runtime, params, HWXY::kH);
146 }
147 
w(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params)148 CJS_Result CJX_LayoutPseudoModel::w(CFXJSE_Engine* runtime,
149                                     pdfium::span<v8::Local<v8::Value>> params) {
150   return DoHWXYInternal(runtime, params, HWXY::kW);
151 }
152 
x(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params)153 CJS_Result CJX_LayoutPseudoModel::x(CFXJSE_Engine* runtime,
154                                     pdfium::span<v8::Local<v8::Value>> params) {
155   return DoHWXYInternal(runtime, params, HWXY::kX);
156 }
157 
y(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params)158 CJS_Result CJX_LayoutPseudoModel::y(CFXJSE_Engine* runtime,
159                                     pdfium::span<v8::Local<v8::Value>> params) {
160   return DoHWXYInternal(runtime, params, HWXY::kY);
161 }
162 
AllPageCount(CFXJSE_Engine * runtime)163 CJS_Result CJX_LayoutPseudoModel::AllPageCount(CFXJSE_Engine* runtime) {
164   auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(GetDocument());
165   return CJS_Result::Success(runtime->NewNumber(pDocLayout->CountPages()));
166 }
167 
NumberedPageCount(CFXJSE_Engine * runtime)168 CJS_Result CJX_LayoutPseudoModel::NumberedPageCount(CFXJSE_Engine* runtime) {
169   auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(GetDocument());
170   int32_t iPageCount = 0;
171   int32_t iPageNum = pDocLayout->CountPages();
172   for (int32_t i = 0; i < iPageNum; i++) {
173     CXFA_ViewLayoutItem* pLayoutPage = pDocLayout->GetPage(i);
174     if (!pLayoutPage)
175       continue;
176 
177     CXFA_Node* pMasterPage = pLayoutPage->GetMasterPage();
178     if (pMasterPage->JSObject()->GetInteger(XFA_Attribute::Numbered))
179       iPageCount++;
180   }
181   return CJS_Result::Success(runtime->NewNumber(iPageCount));
182 }
183 
pageCount(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params)184 CJS_Result CJX_LayoutPseudoModel::pageCount(
185     CFXJSE_Engine* runtime,
186     pdfium::span<v8::Local<v8::Value>> params) {
187   return NumberedPageCount(runtime);
188 }
189 
pageSpan(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params)190 CJS_Result CJX_LayoutPseudoModel::pageSpan(
191     CFXJSE_Engine* runtime,
192     pdfium::span<v8::Local<v8::Value>> params) {
193   if (params.size() != 1)
194     return CJS_Result::Failure(JSMessage::kParamError);
195 
196   CXFA_Node* pNode = ToNode(runtime->ToXFAObject(params[0]));
197   if (!pNode)
198     return CJS_Result::Success();
199 
200   auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(GetDocument());
201   CXFA_ContentLayoutItem* pLayoutItem =
202       ToContentLayoutItem(pDocLayout->GetLayoutItem(pNode));
203   if (!pLayoutItem)
204     return CJS_Result::Success(runtime->NewNumber(-1));
205 
206   int32_t iLast = pLayoutItem->GetLast()->GetPage()->GetPageIndex();
207   int32_t iFirst = pLayoutItem->GetFirst()->GetPage()->GetPageIndex();
208   int32_t iPageSpan = iLast - iFirst + 1;
209   return CJS_Result::Success(runtime->NewNumber(iPageSpan));
210 }
211 
page(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params)212 CJS_Result CJX_LayoutPseudoModel::page(
213     CFXJSE_Engine* runtime,
214     pdfium::span<v8::Local<v8::Value>> params) {
215   return PageInternals(runtime, params, false);
216 }
217 
GetObjArray(CXFA_LayoutProcessor * pDocLayout,int32_t iPageNo,const WideString & wsType,bool bOnPageArea)218 std::vector<CXFA_Node*> CJX_LayoutPseudoModel::GetObjArray(
219     CXFA_LayoutProcessor* pDocLayout,
220     int32_t iPageNo,
221     const WideString& wsType,
222     bool bOnPageArea) {
223   CXFA_ViewLayoutItem* pLayoutPage = pDocLayout->GetPage(iPageNo);
224   if (!pLayoutPage)
225     return std::vector<CXFA_Node*>();
226 
227   std::vector<CXFA_Node*> retArray;
228   if (wsType.EqualsASCII("pageArea")) {
229     if (pLayoutPage->GetFormNode())
230       retArray.push_back(pLayoutPage->GetFormNode());
231     return retArray;
232   }
233   if (wsType.EqualsASCII("contentArea")) {
234     for (CXFA_LayoutItem* pItem = pLayoutPage->GetFirstChild(); pItem;
235          pItem = pItem->GetNextSibling()) {
236       if (pItem->GetFormNode()->GetElementType() == XFA_Element::ContentArea)
237         retArray.push_back(pItem->GetFormNode());
238     }
239     return retArray;
240   }
241   std::set<CXFA_Node*> formItems;
242   if (wsType.IsEmpty()) {
243     if (pLayoutPage->GetFormNode())
244       retArray.push_back(pLayoutPage->GetFormNode());
245 
246     for (CXFA_LayoutItem* pItem = pLayoutPage->GetFirstChild(); pItem;
247          pItem = pItem->GetNextSibling()) {
248       if (pItem->GetFormNode()->GetElementType() == XFA_Element::ContentArea) {
249         retArray.push_back(pItem->GetFormNode());
250         if (!bOnPageArea) {
251           CXFA_LayoutItemIterator iterator(pItem->GetFirstChild());
252           for (CXFA_LayoutItem* pChild = iterator.GetCurrent(); pChild;
253                pChild = iterator.MoveToNext()) {
254             CXFA_ContentLayoutItem* pItemChild = pChild->AsContentLayoutItem();
255             if (!pItemChild)
256               continue;
257 
258             XFA_Element eType = pItemChild->GetFormNode()->GetElementType();
259             if (eType != XFA_Element::Field && eType != XFA_Element::Draw &&
260                 eType != XFA_Element::Subform && eType != XFA_Element::Area) {
261               continue;
262             }
263             if (pdfium::Contains(formItems, pItemChild->GetFormNode()))
264               continue;
265 
266             formItems.insert(pItemChild->GetFormNode());
267             retArray.push_back(pItemChild->GetFormNode());
268           }
269         }
270       } else {
271         if (bOnPageArea) {
272           CXFA_LayoutItemIterator iterator(pItem);
273           for (CXFA_LayoutItem* pChild = iterator.GetCurrent(); pChild;
274                pChild = iterator.MoveToNext()) {
275             CXFA_ContentLayoutItem* pItemChild = pChild->AsContentLayoutItem();
276             if (!pItemChild)
277               continue;
278 
279             XFA_Element eType = pItemChild->GetFormNode()->GetElementType();
280             if (eType != XFA_Element::Field && eType != XFA_Element::Draw &&
281                 eType != XFA_Element::Subform && eType != XFA_Element::Area) {
282               continue;
283             }
284             if (pdfium::Contains(formItems, pItemChild->GetFormNode()))
285               continue;
286 
287             formItems.insert(pItemChild->GetFormNode());
288             retArray.push_back(pItemChild->GetFormNode());
289           }
290         }
291       }
292     }
293     return retArray;
294   }
295 
296   XFA_Element eType = XFA_Element::Unknown;
297   if (wsType.EqualsASCII("field"))
298     eType = XFA_Element::Field;
299   else if (wsType.EqualsASCII("draw"))
300     eType = XFA_Element::Draw;
301   else if (wsType.EqualsASCII("subform"))
302     eType = XFA_Element::Subform;
303   else if (wsType.EqualsASCII("area"))
304     eType = XFA_Element::Area;
305 
306   if (eType != XFA_Element::Unknown) {
307     for (CXFA_LayoutItem* pItem = pLayoutPage->GetFirstChild(); pItem;
308          pItem = pItem->GetNextSibling()) {
309       if (pItem->GetFormNode()->GetElementType() == XFA_Element::ContentArea) {
310         if (!bOnPageArea) {
311           CXFA_LayoutItemIterator iterator(pItem->GetFirstChild());
312           for (CXFA_LayoutItem* pChild = iterator.GetCurrent(); pChild;
313                pChild = iterator.MoveToNext()) {
314             CXFA_ContentLayoutItem* pItemChild = pChild->AsContentLayoutItem();
315             if (!pItemChild)
316               continue;
317             if (pItemChild->GetFormNode()->GetElementType() != eType)
318               continue;
319             if (pdfium::Contains(formItems, pItemChild->GetFormNode()))
320               continue;
321 
322             formItems.insert(pItemChild->GetFormNode());
323             retArray.push_back(pItemChild->GetFormNode());
324           }
325         }
326       } else {
327         if (bOnPageArea) {
328           CXFA_LayoutItemIterator iterator(pItem);
329           for (CXFA_LayoutItem* pChild = iterator.GetCurrent(); pChild;
330                pChild = iterator.MoveToNext()) {
331             CXFA_ContentLayoutItem* pItemChild = pChild->AsContentLayoutItem();
332             if (!pItemChild)
333               continue;
334             if (pItemChild->GetFormNode()->GetElementType() != eType)
335               continue;
336             if (pdfium::Contains(formItems, pItemChild->GetFormNode()))
337               continue;
338 
339             formItems.insert(pItemChild->GetFormNode());
340             retArray.push_back(pItemChild->GetFormNode());
341           }
342         }
343       }
344     }
345   }
346   return retArray;
347 }
348 
pageContent(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params)349 CJS_Result CJX_LayoutPseudoModel::pageContent(
350     CFXJSE_Engine* runtime,
351     pdfium::span<v8::Local<v8::Value>> params) {
352   if (params.empty() || params.size() > 3)
353     return CJS_Result::Failure(JSMessage::kParamError);
354 
355   int32_t iIndex = 0;
356   if (params.size() >= 1)
357     iIndex = runtime->ToInt32(params[0]);
358 
359   WideString wsType;
360   if (params.size() >= 2)
361     wsType = runtime->ToWideString(params[1]);
362 
363   bool bOnPageArea = false;
364   if (params.size() >= 3)
365     bOnPageArea = runtime->ToBoolean(params[2]);
366 
367   CXFA_FFNotify* pNotify = GetDocument()->GetNotify();
368   if (!pNotify)
369     return CJS_Result::Success();
370 
371   CXFA_Document* pDoc = GetDocument();
372   auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(pDoc);
373   auto* pArrayNodeList = cppgc::MakeGarbageCollected<CXFA_ArrayNodeList>(
374       pDoc->GetHeap()->GetAllocationHandle(), pDoc);
375   pDoc->GetNodeOwner()->PersistList(pArrayNodeList);
376   pArrayNodeList->SetArrayNodeList(
377       GetObjArray(pDocLayout, iIndex, wsType, bOnPageArea));
378   return CJS_Result::Success(runtime->NewNormalXFAObject(pArrayNodeList));
379 }
380 
absPageCount(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params)381 CJS_Result CJX_LayoutPseudoModel::absPageCount(
382     CFXJSE_Engine* runtime,
383     pdfium::span<v8::Local<v8::Value>> params) {
384   return AllPageCount(runtime);
385 }
386 
absPageCountInBatch(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params)387 CJS_Result CJX_LayoutPseudoModel::absPageCountInBatch(
388     CFXJSE_Engine* runtime,
389     pdfium::span<v8::Local<v8::Value>> params) {
390   return CJS_Result::Success(runtime->NewNumber(0));
391 }
392 
sheetCountInBatch(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params)393 CJS_Result CJX_LayoutPseudoModel::sheetCountInBatch(
394     CFXJSE_Engine* runtime,
395     pdfium::span<v8::Local<v8::Value>> params) {
396   return CJS_Result::Success(runtime->NewNumber(0));
397 }
398 
relayout(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params)399 CJS_Result CJX_LayoutPseudoModel::relayout(
400     CFXJSE_Engine* runtime,
401     pdfium::span<v8::Local<v8::Value>> params) {
402   CXFA_Node* pRootNode = GetDocument()->GetRoot();
403   auto* pLayoutProcessor = GetDocument()->GetLayoutProcessor();
404   CXFA_Form* pFormRoot =
405       pRootNode->GetFirstChildByClass<CXFA_Form>(XFA_Element::Form);
406   if (pFormRoot) {
407     if (pFormRoot->GetFirstChild())
408       pLayoutProcessor->SetHasChangedContainer();
409   }
410   pLayoutProcessor->SetForceRelayout();
411   return CJS_Result::Success();
412 }
413 
absPageSpan(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params)414 CJS_Result CJX_LayoutPseudoModel::absPageSpan(
415     CFXJSE_Engine* runtime,
416     pdfium::span<v8::Local<v8::Value>> params) {
417   return pageSpan(runtime, params);
418 }
419 
absPageInBatch(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params)420 CJS_Result CJX_LayoutPseudoModel::absPageInBatch(
421     CFXJSE_Engine* runtime,
422     pdfium::span<v8::Local<v8::Value>> params) {
423   if (params.size() != 1)
424     return CJS_Result::Failure(JSMessage::kParamError);
425 
426   return CJS_Result::Success(runtime->NewNumber(0));
427 }
428 
sheetInBatch(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params)429 CJS_Result CJX_LayoutPseudoModel::sheetInBatch(
430     CFXJSE_Engine* runtime,
431     pdfium::span<v8::Local<v8::Value>> params) {
432   if (params.size() != 1)
433     return CJS_Result::Failure(JSMessage::kParamError);
434 
435   return CJS_Result::Success(runtime->NewNumber(0));
436 }
437 
sheet(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params)438 CJS_Result CJX_LayoutPseudoModel::sheet(
439     CFXJSE_Engine* runtime,
440     pdfium::span<v8::Local<v8::Value>> params) {
441   return PageInternals(runtime, params, true);
442 }
443 
relayoutPageArea(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params)444 CJS_Result CJX_LayoutPseudoModel::relayoutPageArea(
445     CFXJSE_Engine* runtime,
446     pdfium::span<v8::Local<v8::Value>> params) {
447   return CJS_Result::Success();
448 }
449 
sheetCount(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params)450 CJS_Result CJX_LayoutPseudoModel::sheetCount(
451     CFXJSE_Engine* runtime,
452     pdfium::span<v8::Local<v8::Value>> params) {
453   return AllPageCount(runtime);
454 }
455 
absPage(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params)456 CJS_Result CJX_LayoutPseudoModel::absPage(
457     CFXJSE_Engine* runtime,
458     pdfium::span<v8::Local<v8::Value>> params) {
459   return PageInternals(runtime, params, true);
460 }
461 
PageInternals(CFXJSE_Engine * runtime,pdfium::span<v8::Local<v8::Value>> params,bool bAbsPage)462 CJS_Result CJX_LayoutPseudoModel::PageInternals(
463     CFXJSE_Engine* runtime,
464     pdfium::span<v8::Local<v8::Value>> params,
465     bool bAbsPage) {
466   if (params.size() != 1)
467     return CJS_Result::Failure(JSMessage::kParamError);
468 
469   CXFA_Node* pNode = ToNode(runtime->ToXFAObject(params[0]));
470   if (!pNode)
471     return CJS_Result::Success(runtime->NewNumber(0));
472 
473   auto* pDocLayout = CXFA_LayoutProcessor::FromDocument(GetDocument());
474   CXFA_ContentLayoutItem* pLayoutItem =
475       ToContentLayoutItem(pDocLayout->GetLayoutItem(pNode));
476   if (!pLayoutItem)
477     return CJS_Result::Success(runtime->NewNumber(-1));
478 
479   int32_t iPage = pLayoutItem->GetFirst()->GetPage()->GetPageIndex();
480   return CJS_Result::Success(runtime->NewNumber(bAbsPage ? iPage : iPage + 1));
481 }
482