1 // Copyright 2014 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 "xfa/fxfa/parser/cscript_layoutpseudomodel.h"
8
9 #include <set>
10
11 #include "fxjs/cfxjse_arguments.h"
12 #include "third_party/base/stl_util.h"
13 #include "xfa/fxfa/app/xfa_ffnotify.h"
14 #include "xfa/fxfa/parser/cxfa_containerlayoutitem.h"
15 #include "xfa/fxfa/parser/cxfa_contentlayoutitem.h"
16 #include "xfa/fxfa/parser/cxfa_document.h"
17 #include "xfa/fxfa/parser/cxfa_layoutitem.h"
18 #include "xfa/fxfa/parser/cxfa_layoutprocessor.h"
19 #include "xfa/fxfa/parser/cxfa_measurement.h"
20 #include "xfa/fxfa/parser/cxfa_scriptcontext.h"
21 #include "xfa/fxfa/parser/cxfa_traversestrategy_contentlayoutitem.h"
22 #include "xfa/fxfa/parser/xfa_localemgr.h"
23 #include "xfa/fxfa/parser/xfa_object.h"
24 #include "xfa/fxfa/parser/xfa_utils.h"
25
CScript_LayoutPseudoModel(CXFA_Document * pDocument)26 CScript_LayoutPseudoModel::CScript_LayoutPseudoModel(CXFA_Document* pDocument)
27 : CXFA_Object(pDocument,
28 XFA_ObjectType::Object,
29 XFA_Element::LayoutPseudoModel,
30 CFX_WideStringC(L"layoutPseudoModel")) {}
31
~CScript_LayoutPseudoModel()32 CScript_LayoutPseudoModel::~CScript_LayoutPseudoModel() {}
33
Ready(CFXJSE_Value * pValue,bool bSetting,XFA_ATTRIBUTE eAttribute)34 void CScript_LayoutPseudoModel::Ready(CFXJSE_Value* pValue,
35 bool bSetting,
36 XFA_ATTRIBUTE eAttribute) {
37 CXFA_FFNotify* pNotify = m_pDocument->GetNotify();
38 if (!pNotify) {
39 return;
40 }
41 if (bSetting) {
42 ThrowSetReadyException();
43 return;
44 }
45 int32_t iStatus = pNotify->GetLayoutStatus();
46 pValue->SetBoolean(iStatus >= 2);
47 }
48
HWXY(CFXJSE_Arguments * pArguments,XFA_LAYOUTMODEL_HWXY layoutModel)49 void CScript_LayoutPseudoModel::HWXY(CFXJSE_Arguments* pArguments,
50 XFA_LAYOUTMODEL_HWXY layoutModel) {
51 int32_t iLength = pArguments->GetLength();
52 if (iLength < 1 || iLength > 3) {
53 const FX_WCHAR* methodName = nullptr;
54 switch (layoutModel) {
55 case XFA_LAYOUTMODEL_H:
56 methodName = L"h";
57 break;
58 case XFA_LAYOUTMODEL_W:
59 methodName = L"w";
60 break;
61 case XFA_LAYOUTMODEL_X:
62 methodName = L"x";
63 break;
64 case XFA_LAYOUTMODEL_Y:
65 methodName = L"y";
66 break;
67 }
68 ThrowParamCountMismatchException(methodName);
69 return;
70 }
71 CXFA_Node* pNode = nullptr;
72 CFX_WideString wsUnit(L"pt");
73 int32_t iIndex = 0;
74 if (iLength >= 1) {
75 pNode = static_cast<CXFA_Node*>(pArguments->GetObject(0));
76 }
77 if (iLength >= 2) {
78 CFX_ByteString bsUnit = pArguments->GetUTF8String(1);
79 if (!bsUnit.IsEmpty()) {
80 wsUnit = CFX_WideString::FromUTF8(bsUnit.AsStringC());
81 }
82 }
83 if (iLength >= 3) {
84 iIndex = pArguments->GetInt32(2);
85 }
86 if (!pNode) {
87 return;
88 }
89 CXFA_LayoutProcessor* pDocLayout = m_pDocument->GetDocLayout();
90 if (!pDocLayout) {
91 return;
92 }
93
94 CXFA_Measurement measure;
95 CXFA_LayoutItem* pLayoutItem = pDocLayout->GetLayoutItem(pNode);
96 if (!pLayoutItem) {
97 return;
98 }
99 while (iIndex > 0 && pLayoutItem) {
100 pLayoutItem = pLayoutItem->GetNext();
101 iIndex--;
102 }
103 CFXJSE_Value* pValue = pArguments->GetReturnValue();
104 if (!pLayoutItem) {
105 pValue->SetFloat(0);
106 return;
107 }
108
109 CFX_RectF rtRect = pLayoutItem->GetRect(true);
110 switch (layoutModel) {
111 case XFA_LAYOUTMODEL_H:
112 measure.Set(rtRect.height, XFA_UNIT_Pt);
113 break;
114 case XFA_LAYOUTMODEL_W:
115 measure.Set(rtRect.width, XFA_UNIT_Pt);
116 break;
117 case XFA_LAYOUTMODEL_X:
118 measure.Set(rtRect.left, XFA_UNIT_Pt);
119 break;
120 case XFA_LAYOUTMODEL_Y:
121 measure.Set(rtRect.top, XFA_UNIT_Pt);
122 break;
123 }
124 XFA_UNIT unit = measure.GetUnit(wsUnit.AsStringC());
125 FX_FLOAT fValue = measure.ToUnit(unit);
126 fValue = FXSYS_round(fValue * 1000) / 1000.0f;
127 if (pValue)
128 pValue->SetFloat(fValue);
129 }
130
H(CFXJSE_Arguments * pArguments)131 void CScript_LayoutPseudoModel::H(CFXJSE_Arguments* pArguments) {
132 HWXY(pArguments, XFA_LAYOUTMODEL_H);
133 }
134
W(CFXJSE_Arguments * pArguments)135 void CScript_LayoutPseudoModel::W(CFXJSE_Arguments* pArguments) {
136 HWXY(pArguments, XFA_LAYOUTMODEL_W);
137 }
138
X(CFXJSE_Arguments * pArguments)139 void CScript_LayoutPseudoModel::X(CFXJSE_Arguments* pArguments) {
140 HWXY(pArguments, XFA_LAYOUTMODEL_X);
141 }
142
Y(CFXJSE_Arguments * pArguments)143 void CScript_LayoutPseudoModel::Y(CFXJSE_Arguments* pArguments) {
144 HWXY(pArguments, XFA_LAYOUTMODEL_Y);
145 }
146
NumberedPageCount(CFXJSE_Arguments * pArguments,bool bNumbered)147 void CScript_LayoutPseudoModel::NumberedPageCount(CFXJSE_Arguments* pArguments,
148 bool bNumbered) {
149 CXFA_LayoutProcessor* pDocLayout = m_pDocument->GetDocLayout();
150 if (!pDocLayout) {
151 return;
152 }
153 int32_t iPageCount = 0;
154 int32_t iPageNum = pDocLayout->CountPages();
155 if (bNumbered) {
156 for (int32_t i = 0; i < iPageNum; i++) {
157 CXFA_ContainerLayoutItem* pLayoutPage = pDocLayout->GetPage(i);
158 if (!pLayoutPage) {
159 continue;
160 }
161 CXFA_Node* pMasterPage = pLayoutPage->GetMasterPage();
162 if (pMasterPage->GetInteger(XFA_ATTRIBUTE_Numbered)) {
163 iPageCount++;
164 }
165 }
166 } else {
167 iPageCount = iPageNum;
168 }
169 CFXJSE_Value* pValue = pArguments->GetReturnValue();
170 if (pValue)
171 pValue->SetInteger(iPageCount);
172 }
173
PageCount(CFXJSE_Arguments * pArguments)174 void CScript_LayoutPseudoModel::PageCount(CFXJSE_Arguments* pArguments) {
175 NumberedPageCount(pArguments, true);
176 }
177
PageSpan(CFXJSE_Arguments * pArguments)178 void CScript_LayoutPseudoModel::PageSpan(CFXJSE_Arguments* pArguments) {
179 int32_t iLength = pArguments->GetLength();
180 if (iLength != 1) {
181 ThrowParamCountMismatchException(L"pageSpan");
182 return;
183 }
184 CXFA_Node* pNode = nullptr;
185 if (iLength >= 1) {
186 pNode = static_cast<CXFA_Node*>(pArguments->GetObject(0));
187 }
188 if (!pNode) {
189 return;
190 }
191 CXFA_LayoutProcessor* pDocLayout = m_pDocument->GetDocLayout();
192 if (!pDocLayout) {
193 return;
194 }
195 CFXJSE_Value* pValue = pArguments->GetReturnValue();
196 CXFA_LayoutItem* pLayoutItem = pDocLayout->GetLayoutItem(pNode);
197 if (!pLayoutItem) {
198 pValue->SetInteger(-1);
199 return;
200 }
201 int32_t iLast = pLayoutItem->GetLast()->GetPage()->GetPageIndex();
202 int32_t iFirst = pLayoutItem->GetFirst()->GetPage()->GetPageIndex();
203 int32_t iPageSpan = iLast - iFirst + 1;
204 if (pValue)
205 pValue->SetInteger(iPageSpan);
206 }
207
Page(CFXJSE_Arguments * pArguments)208 void CScript_LayoutPseudoModel::Page(CFXJSE_Arguments* pArguments) {
209 PageImp(pArguments, false);
210 }
211
GetObjArray(CXFA_LayoutProcessor * pDocLayout,int32_t iPageNo,const CFX_WideString & wsType,bool bOnPageArea,CXFA_NodeArray & retArray)212 void CScript_LayoutPseudoModel::GetObjArray(CXFA_LayoutProcessor* pDocLayout,
213 int32_t iPageNo,
214 const CFX_WideString& wsType,
215 bool bOnPageArea,
216 CXFA_NodeArray& retArray) {
217 CXFA_ContainerLayoutItem* pLayoutPage = pDocLayout->GetPage(iPageNo);
218 if (!pLayoutPage) {
219 return;
220 }
221 if (wsType == L"pageArea") {
222 if (CXFA_Node* pMasterPage = pLayoutPage->m_pFormNode) {
223 retArray.Add(pMasterPage);
224 }
225 return;
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.Add(pItem->m_pFormNode);
232 }
233 }
234 return;
235 }
236 std::set<CXFA_Node*> formItems;
237 if (wsType.IsEmpty()) {
238 if (CXFA_Node* pMasterPage = pLayoutPage->m_pFormNode) {
239 retArray.Add(pMasterPage);
240 }
241 for (CXFA_LayoutItem* pItem = pLayoutPage->m_pFirstChild; pItem;
242 pItem = pItem->m_pNextSibling) {
243 if (pItem->m_pFormNode->GetElementType() == XFA_Element::ContentArea) {
244 retArray.Add(pItem->m_pFormNode);
245 if (!bOnPageArea) {
246 CXFA_NodeIteratorTemplate<CXFA_ContentLayoutItem,
247 CXFA_TraverseStrategy_ContentLayoutItem>
248 iterator(static_cast<CXFA_ContentLayoutItem*>(pItem->m_pFirstChild));
249 for (CXFA_ContentLayoutItem* pItemChild = iterator.GetCurrent();
250 pItemChild; pItemChild = iterator.MoveToNext()) {
251 if (!pItemChild->IsContentLayoutItem()) {
252 continue;
253 }
254 XFA_Element eType = pItemChild->m_pFormNode->GetElementType();
255 if (eType != XFA_Element::Field && eType != XFA_Element::Draw &&
256 eType != XFA_Element::Subform && eType != XFA_Element::Area) {
257 continue;
258 }
259 if (pdfium::ContainsValue(formItems, pItemChild->m_pFormNode))
260 continue;
261
262 formItems.insert(pItemChild->m_pFormNode);
263 retArray.Add(pItemChild->m_pFormNode);
264 }
265 }
266 } else {
267 if (bOnPageArea) {
268 CXFA_NodeIteratorTemplate<CXFA_ContentLayoutItem,
269 CXFA_TraverseStrategy_ContentLayoutItem>
270 iterator(static_cast<CXFA_ContentLayoutItem*>(pItem));
271 for (CXFA_ContentLayoutItem* pItemChild = iterator.GetCurrent();
272 pItemChild; pItemChild = iterator.MoveToNext()) {
273 if (!pItemChild->IsContentLayoutItem()) {
274 continue;
275 }
276 XFA_Element eType = pItemChild->m_pFormNode->GetElementType();
277 if (eType != XFA_Element::Field && eType != XFA_Element::Draw &&
278 eType != XFA_Element::Subform && eType != XFA_Element::Area) {
279 continue;
280 }
281 if (pdfium::ContainsValue(formItems, pItemChild->m_pFormNode))
282 continue;
283 formItems.insert(pItemChild->m_pFormNode);
284 retArray.Add(pItemChild->m_pFormNode);
285 }
286 }
287 }
288 }
289 return;
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 formItems.insert(pItemChild->m_pFormNode);
318 retArray.Add(pItemChild->m_pFormNode);
319 }
320 }
321 } else {
322 if (bOnPageArea) {
323 CXFA_NodeIteratorTemplate<CXFA_ContentLayoutItem,
324 CXFA_TraverseStrategy_ContentLayoutItem>
325 iterator(static_cast<CXFA_ContentLayoutItem*>(pItem));
326 for (CXFA_ContentLayoutItem* pItemChild = iterator.GetCurrent();
327 pItemChild; pItemChild = iterator.MoveToNext()) {
328 if (!pItemChild->IsContentLayoutItem())
329 continue;
330 if (pItemChild->m_pFormNode->GetElementType() != eType)
331 continue;
332 if (pdfium::ContainsValue(formItems, pItemChild->m_pFormNode))
333 continue;
334 formItems.insert(pItemChild->m_pFormNode);
335 retArray.Add(pItemChild->m_pFormNode);
336 }
337 }
338 }
339 }
340 return;
341 }
342 }
343
PageContent(CFXJSE_Arguments * pArguments)344 void CScript_LayoutPseudoModel::PageContent(CFXJSE_Arguments* pArguments) {
345 int32_t iLength = pArguments->GetLength();
346 if (iLength < 1 || iLength > 3) {
347 ThrowParamCountMismatchException(L"pageContent");
348 return;
349 }
350 int32_t iIndex = 0;
351 CFX_WideString wsType;
352 bool bOnPageArea = false;
353 if (iLength >= 1) {
354 iIndex = pArguments->GetInt32(0);
355 }
356 if (iLength >= 2) {
357 CFX_ByteString bsType = pArguments->GetUTF8String(1);
358 wsType = CFX_WideString::FromUTF8(bsType.AsStringC());
359 }
360 if (iLength >= 3) {
361 bOnPageArea = pArguments->GetInt32(2) == 0 ? false : true;
362 }
363 CXFA_FFNotify* pNotify = m_pDocument->GetNotify();
364 if (!pNotify) {
365 return;
366 }
367 CXFA_LayoutProcessor* pDocLayout = m_pDocument->GetDocLayout();
368 if (!pDocLayout) {
369 return;
370 }
371 CXFA_NodeArray retArray;
372 GetObjArray(pDocLayout, iIndex, wsType, bOnPageArea, retArray);
373 CXFA_ArrayNodeList* pArrayNodeList = new CXFA_ArrayNodeList(m_pDocument);
374 pArrayNodeList->SetArrayNodeList(retArray);
375 pArguments->GetReturnValue()->SetObject(
376 pArrayNodeList, m_pDocument->GetScriptContext()->GetJseNormalClass());
377 }
378
AbsPageCount(CFXJSE_Arguments * pArguments)379 void CScript_LayoutPseudoModel::AbsPageCount(CFXJSE_Arguments* pArguments) {
380 NumberedPageCount(pArguments, false);
381 }
382
AbsPageCountInBatch(CFXJSE_Arguments * pArguments)383 void CScript_LayoutPseudoModel::AbsPageCountInBatch(
384 CFXJSE_Arguments* pArguments) {
385 CFXJSE_Value* pValue = pArguments->GetReturnValue();
386 if (pValue)
387 pValue->SetInteger(0);
388 }
389
SheetCountInBatch(CFXJSE_Arguments * pArguments)390 void CScript_LayoutPseudoModel::SheetCountInBatch(
391 CFXJSE_Arguments* pArguments) {
392 CFXJSE_Value* pValue = pArguments->GetReturnValue();
393 if (pValue)
394 pValue->SetInteger(0);
395 }
396
Relayout(CFXJSE_Arguments * pArguments)397 void CScript_LayoutPseudoModel::Relayout(CFXJSE_Arguments* pArguments) {
398 CXFA_Node* pRootNode = m_pDocument->GetRoot();
399 CXFA_Node* pFormRoot = pRootNode->GetFirstChildByClass(XFA_Element::Form);
400 ASSERT(pFormRoot);
401 CXFA_Node* pContentRootNode = pFormRoot->GetNodeItem(XFA_NODEITEM_FirstChild);
402 CXFA_LayoutProcessor* pLayoutProcessor = m_pDocument->GetLayoutProcessor();
403 if (pContentRootNode) {
404 pLayoutProcessor->AddChangedContainer(pContentRootNode);
405 }
406 pLayoutProcessor->SetForceReLayout(true);
407 }
408
AbsPageSpan(CFXJSE_Arguments * pArguments)409 void CScript_LayoutPseudoModel::AbsPageSpan(CFXJSE_Arguments* pArguments) {
410 PageSpan(pArguments);
411 }
412
AbsPageInBatch(CFXJSE_Arguments * pArguments)413 void CScript_LayoutPseudoModel::AbsPageInBatch(CFXJSE_Arguments* pArguments) {
414 if (pArguments->GetLength() != 1) {
415 ThrowParamCountMismatchException(L"absPageInBatch");
416 return;
417 }
418
419 CFXJSE_Value* pValue = pArguments->GetReturnValue();
420 if (pValue)
421 pValue->SetInteger(0);
422 }
423
SheetInBatch(CFXJSE_Arguments * pArguments)424 void CScript_LayoutPseudoModel::SheetInBatch(CFXJSE_Arguments* pArguments) {
425 if (pArguments->GetLength() != 1) {
426 ThrowParamCountMismatchException(L"sheetInBatch");
427 return;
428 }
429
430 CFXJSE_Value* pValue = pArguments->GetReturnValue();
431 if (pValue)
432 pValue->SetInteger(0);
433 }
434
Sheet(CFXJSE_Arguments * pArguments)435 void CScript_LayoutPseudoModel::Sheet(CFXJSE_Arguments* pArguments) {
436 PageImp(pArguments, true);
437 }
438
RelayoutPageArea(CFXJSE_Arguments * pArguments)439 void CScript_LayoutPseudoModel::RelayoutPageArea(CFXJSE_Arguments* pArguments) {
440 }
441
SheetCount(CFXJSE_Arguments * pArguments)442 void CScript_LayoutPseudoModel::SheetCount(CFXJSE_Arguments* pArguments) {
443 NumberedPageCount(pArguments, false);
444 }
445
AbsPage(CFXJSE_Arguments * pArguments)446 void CScript_LayoutPseudoModel::AbsPage(CFXJSE_Arguments* pArguments) {
447 PageImp(pArguments, true);
448 }
449
PageImp(CFXJSE_Arguments * pArguments,bool bAbsPage)450 void CScript_LayoutPseudoModel::PageImp(CFXJSE_Arguments* pArguments,
451 bool bAbsPage) {
452 int32_t iLength = pArguments->GetLength();
453 if (iLength != 1) {
454 const FX_WCHAR* methodName;
455 if (bAbsPage) {
456 methodName = L"absPage";
457 } else {
458 methodName = L"page";
459 }
460 ThrowParamCountMismatchException(methodName);
461 return;
462 }
463 CXFA_Node* pNode = nullptr;
464 if (iLength >= 1) {
465 pNode = static_cast<CXFA_Node*>(pArguments->GetObject(0));
466 }
467 int32_t iPage = 0;
468 CFXJSE_Value* pValue = pArguments->GetReturnValue();
469 if (!pNode && pValue)
470 pValue->SetInteger(iPage);
471
472 CXFA_LayoutProcessor* pDocLayout = m_pDocument->GetDocLayout();
473 if (!pDocLayout) {
474 return;
475 }
476 CXFA_LayoutItem* pLayoutItem = pDocLayout->GetLayoutItem(pNode);
477 if (!pLayoutItem) {
478 pValue->SetInteger(-1);
479 return;
480 }
481 iPage = pLayoutItem->GetFirst()->GetPage()->GetPageIndex();
482 if (pValue)
483 pValue->SetInteger(bAbsPage ? iPage : iPage + 1);
484 }
485
ThrowSetReadyException() const486 void CScript_LayoutPseudoModel::ThrowSetReadyException() const {
487 ThrowException(L"Unable to set ready value.");
488 }
489