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