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/xfa_utils.h"
8
9 #include <algorithm>
10 #include <vector>
11
12 #include "core/fxcrt/cfx_memorystream.h"
13 #include "core/fxcrt/cfx_widetextbuf.h"
14 #include "core/fxcrt/fx_codepage.h"
15 #include "core/fxcrt/fx_extension.h"
16 #include "core/fxcrt/xml/cfx_xmlchardata.h"
17 #include "core/fxcrt/xml/cfx_xmlelement.h"
18 #include "core/fxcrt/xml/cfx_xmlnode.h"
19 #include "core/fxcrt/xml/cfx_xmltext.h"
20 #include "fxjs/xfa/cjx_object.h"
21 #include "third_party/base/stl_util.h"
22 #include "xfa/fxfa/parser/cxfa_document.h"
23 #include "xfa/fxfa/parser/cxfa_localemgr.h"
24 #include "xfa/fxfa/parser/cxfa_localevalue.h"
25 #include "xfa/fxfa/parser/cxfa_measurement.h"
26 #include "xfa/fxfa/parser/cxfa_node.h"
27 #include "xfa/fxfa/parser/cxfa_ui.h"
28 #include "xfa/fxfa/parser/cxfa_value.h"
29 #include "xfa/fxfa/parser/xfa_basic_data.h"
30
31 namespace {
32
33 const char kFormNS[] = "http://www.xfa.org/schema/xfa-form/";
34
ExportEncodeAttribute(const WideString & str)35 WideString ExportEncodeAttribute(const WideString& str) {
36 CFX_WideTextBuf textBuf;
37 int32_t iLen = str.GetLength();
38 for (int32_t i = 0; i < iLen; i++) {
39 switch (str[i]) {
40 case '&':
41 textBuf << "&";
42 break;
43 case '<':
44 textBuf << "<";
45 break;
46 case '>':
47 textBuf << ">";
48 break;
49 case '\'':
50 textBuf << "'";
51 break;
52 case '\"':
53 textBuf << """;
54 break;
55 default:
56 textBuf.AppendChar(str[i]);
57 }
58 }
59 return textBuf.MakeString();
60 }
61
IsXMLValidChar(wchar_t ch)62 bool IsXMLValidChar(wchar_t ch) {
63 return ch == 0x09 || ch == 0x0A || ch == 0x0D ||
64 (ch >= 0x20 && ch <= 0xD7FF) || (ch >= 0xE000 && ch <= 0xFFFD);
65 }
66
ExportEncodeContent(const WideString & str)67 WideString ExportEncodeContent(const WideString& str) {
68 CFX_WideTextBuf textBuf;
69 int32_t iLen = str.GetLength();
70 for (int32_t i = 0; i < iLen; i++) {
71 wchar_t ch = str[i];
72 if (!IsXMLValidChar(ch))
73 continue;
74
75 if (ch == '&') {
76 textBuf << "&";
77 } else if (ch == '<') {
78 textBuf << "<";
79 } else if (ch == '>') {
80 textBuf << ">";
81 } else if (ch == '\'') {
82 textBuf << "'";
83 } else if (ch == '\"') {
84 textBuf << """;
85 } else if (ch == ' ') {
86 if (i && str[i - 1] != ' ') {
87 textBuf.AppendChar(' ');
88 } else {
89 textBuf << " ";
90 }
91 } else {
92 textBuf.AppendChar(str[i]);
93 }
94 }
95 return textBuf.MakeString();
96 }
97
AttributeSaveInDataModel(CXFA_Node * pNode,XFA_Attribute eAttribute)98 bool AttributeSaveInDataModel(CXFA_Node* pNode, XFA_Attribute eAttribute) {
99 bool bSaveInDataModel = false;
100 if (pNode->GetElementType() != XFA_Element::Image)
101 return bSaveInDataModel;
102
103 CXFA_Node* pValueNode = pNode->GetParent();
104 if (!pValueNode || pValueNode->GetElementType() != XFA_Element::Value)
105 return bSaveInDataModel;
106
107 CXFA_Node* pFieldNode = pValueNode->GetParent();
108 if (pFieldNode && pFieldNode->GetBindData() &&
109 eAttribute == XFA_Attribute::Href) {
110 bSaveInDataModel = true;
111 }
112 return bSaveInDataModel;
113 }
114
ContentNodeNeedtoExport(CXFA_Node * pContentNode)115 bool ContentNodeNeedtoExport(CXFA_Node* pContentNode) {
116 Optional<WideString> wsContent =
117 pContentNode->JSObject()->TryContent(false, false);
118 if (!wsContent)
119 return false;
120
121 ASSERT(pContentNode->IsContentNode());
122 CXFA_Node* pParentNode = pContentNode->GetParent();
123 if (!pParentNode || pParentNode->GetElementType() != XFA_Element::Value)
124 return true;
125
126 CXFA_Node* pGrandParentNode = pParentNode->GetParent();
127 if (!pGrandParentNode || !pGrandParentNode->IsContainerNode())
128 return true;
129 if (!pGrandParentNode->GetBindData())
130 return false;
131 if (pGrandParentNode->GetFFWidgetType() == XFA_FFWidgetType::kPasswordEdit)
132 return false;
133 return true;
134 }
135
SaveAttribute(CXFA_Node * pNode,XFA_Attribute eName,const WideString & wsName,bool bProto,WideString & wsOutput)136 void SaveAttribute(CXFA_Node* pNode,
137 XFA_Attribute eName,
138 const WideString& wsName,
139 bool bProto,
140 WideString& wsOutput) {
141 if (!bProto && !pNode->JSObject()->HasAttribute(eName))
142 return;
143
144 Optional<WideString> value = pNode->JSObject()->TryAttribute(eName, false);
145 if (!value)
146 return;
147
148 wsOutput += L" ";
149 wsOutput += wsName;
150 wsOutput += L"=\"";
151 wsOutput += ExportEncodeAttribute(*value);
152 wsOutput += L"\"";
153 }
154
RegenerateFormFile_Changed(CXFA_Node * pNode,CFX_WideTextBuf & buf,bool bSaveXML)155 void RegenerateFormFile_Changed(CXFA_Node* pNode,
156 CFX_WideTextBuf& buf,
157 bool bSaveXML) {
158 WideString wsAttrs;
159 for (size_t i = 0;; ++i) {
160 XFA_Attribute attr = pNode->GetAttribute(i);
161 if (attr == XFA_Attribute::Unknown)
162 break;
163
164 if (attr == XFA_Attribute::Name ||
165 (AttributeSaveInDataModel(pNode, attr) && !bSaveXML)) {
166 continue;
167 }
168 WideString wsAttr;
169 SaveAttribute(pNode, attr, WideString::FromASCII(XFA_AttributeToName(attr)),
170 bSaveXML, wsAttr);
171 wsAttrs += wsAttr;
172 }
173
174 WideString wsChildren;
175 switch (pNode->GetObjectType()) {
176 case XFA_ObjectType::ContentNode: {
177 if (!bSaveXML && !ContentNodeNeedtoExport(pNode))
178 break;
179
180 CXFA_Node* pRawValueNode = pNode->GetFirstChild();
181 while (pRawValueNode &&
182 pRawValueNode->GetElementType() != XFA_Element::SharpxHTML &&
183 pRawValueNode->GetElementType() != XFA_Element::Sharptext &&
184 pRawValueNode->GetElementType() != XFA_Element::Sharpxml) {
185 pRawValueNode = pRawValueNode->GetNextSibling();
186 }
187 if (!pRawValueNode)
188 break;
189
190 Optional<WideString> contentType =
191 pNode->JSObject()->TryAttribute(XFA_Attribute::ContentType, false);
192 if (pRawValueNode->GetElementType() == XFA_Element::SharpxHTML &&
193 contentType.has_value() &&
194 contentType.value().EqualsASCII("text/html")) {
195 CFX_XMLNode* pExDataXML = pNode->GetXMLMappingNode();
196 if (!pExDataXML)
197 break;
198
199 CFX_XMLNode* pRichTextXML = pExDataXML->GetFirstChild();
200 if (!pRichTextXML)
201 break;
202
203 auto pMemStream = pdfium::MakeRetain<CFX_MemoryStream>();
204 pRichTextXML->Save(pMemStream);
205 wsChildren += WideString::FromUTF8(
206 ByteStringView(pMemStream->GetBuffer(), pMemStream->GetSize()));
207 } else if (pRawValueNode->GetElementType() == XFA_Element::Sharpxml &&
208 contentType.has_value() &&
209 contentType.value().EqualsASCII("text/xml")) {
210 Optional<WideString> rawValue = pRawValueNode->JSObject()->TryAttribute(
211 XFA_Attribute::Value, false);
212 if (!rawValue || rawValue->IsEmpty())
213 break;
214
215 std::vector<WideString> wsSelTextArray =
216 fxcrt::Split(rawValue.value(), L'\n');
217
218 CXFA_Node* pParentNode = pNode->GetParent();
219 CXFA_Node* pGrandparentNode = pParentNode->GetParent();
220 WideString bodyTagName =
221 pGrandparentNode->JSObject()->GetCData(XFA_Attribute::Name);
222 if (bodyTagName.IsEmpty())
223 bodyTagName = L"ListBox1";
224
225 buf << "<";
226 buf << bodyTagName;
227 buf << " xmlns=\"\"\n>";
228 for (int32_t i = 0; i < pdfium::CollectionSize<int32_t>(wsSelTextArray);
229 i++) {
230 buf << "<value\n>";
231 buf << ExportEncodeContent(wsSelTextArray[i]);
232 buf << "</value\n>";
233 }
234 buf << "</";
235 buf << bodyTagName;
236 buf << "\n>";
237 wsChildren += buf.AsStringView();
238 buf.Clear();
239 } else {
240 WideString wsValue =
241 pRawValueNode->JSObject()->GetCData(XFA_Attribute::Value);
242 wsChildren += ExportEncodeContent(wsValue);
243 }
244 break;
245 }
246 case XFA_ObjectType::TextNode:
247 case XFA_ObjectType::NodeC:
248 case XFA_ObjectType::NodeV: {
249 WideString wsValue = pNode->JSObject()->GetCData(XFA_Attribute::Value);
250 wsChildren += ExportEncodeContent(wsValue);
251 break;
252 }
253 default:
254 if (pNode->GetElementType() == XFA_Element::Items) {
255 CXFA_Node* pTemplateNode = pNode->GetTemplateNodeIfExists();
256 if (!pTemplateNode ||
257 pTemplateNode->CountChildren(XFA_Element::Unknown, false) !=
258 pNode->CountChildren(XFA_Element::Unknown, false)) {
259 bSaveXML = true;
260 }
261 }
262 CFX_WideTextBuf newBuf;
263 CXFA_Node* pChildNode = pNode->GetFirstChild();
264 while (pChildNode) {
265 RegenerateFormFile_Changed(pChildNode, newBuf, bSaveXML);
266 wsChildren += newBuf.AsStringView();
267 newBuf.Clear();
268 pChildNode = pChildNode->GetNextSibling();
269 }
270 if (!bSaveXML && !wsChildren.IsEmpty() &&
271 pNode->GetElementType() == XFA_Element::Items) {
272 wsChildren.clear();
273 bSaveXML = true;
274 CXFA_Node* pChild = pNode->GetFirstChild();
275 while (pChild) {
276 RegenerateFormFile_Changed(pChild, newBuf, bSaveXML);
277 wsChildren += newBuf.AsStringView();
278 newBuf.Clear();
279 pChild = pChild->GetNextSibling();
280 }
281 }
282 break;
283 }
284
285 if (!wsChildren.IsEmpty() || !wsAttrs.IsEmpty() ||
286 pNode->JSObject()->HasAttribute(XFA_Attribute::Name)) {
287 WideString wsElement = WideString::FromASCII(pNode->GetClassName());
288 WideString wsName;
289 SaveAttribute(pNode, XFA_Attribute::Name, L"name", true, wsName);
290 buf << "<";
291 buf << wsElement;
292 buf << wsName;
293 buf << wsAttrs;
294 if (wsChildren.IsEmpty()) {
295 buf << "\n/>";
296 } else {
297 buf << "\n>";
298 buf << wsChildren;
299 buf << "</";
300 buf << wsElement;
301 buf << "\n>";
302 }
303 }
304 }
305
RegenerateFormFile_Container(CXFA_Node * pNode,const RetainPtr<IFX_SeekableStream> & pStream,bool bSaveXML)306 void RegenerateFormFile_Container(CXFA_Node* pNode,
307 const RetainPtr<IFX_SeekableStream>& pStream,
308 bool bSaveXML) {
309 XFA_Element eType = pNode->GetElementType();
310 if (eType == XFA_Element::Field || eType == XFA_Element::Draw ||
311 !pNode->IsContainerNode()) {
312 CFX_WideTextBuf buf;
313 RegenerateFormFile_Changed(pNode, buf, bSaveXML);
314 size_t nLen = buf.GetLength();
315 if (nLen > 0)
316 pStream->WriteString(buf.MakeString().ToUTF8().AsStringView());
317 return;
318 }
319
320 WideString wsElement = WideString::FromASCII(pNode->GetClassName());
321 pStream->WriteString("<");
322 pStream->WriteString(wsElement.ToUTF8().AsStringView());
323
324 WideString wsOutput;
325 SaveAttribute(pNode, XFA_Attribute::Name, L"name", true, wsOutput);
326
327 WideString wsAttrs;
328 for (size_t i = 0;; ++i) {
329 XFA_Attribute attr = pNode->GetAttribute(i);
330 if (attr == XFA_Attribute::Unknown)
331 break;
332 if (attr == XFA_Attribute::Name)
333 continue;
334
335 WideString wsAttr;
336 SaveAttribute(pNode, attr, WideString::FromASCII(XFA_AttributeToName(attr)),
337 false, wsAttr);
338 wsOutput += wsAttr;
339 }
340
341 if (!wsOutput.IsEmpty())
342 pStream->WriteString(wsOutput.ToUTF8().AsStringView());
343
344 CXFA_Node* pChildNode = pNode->GetFirstChild();
345 if (!pChildNode) {
346 pStream->WriteString(" />\n");
347 return;
348 }
349
350 pStream->WriteString(">\n");
351 while (pChildNode) {
352 RegenerateFormFile_Container(pChildNode, pStream, bSaveXML);
353 pChildNode = pChildNode->GetNextSibling();
354 }
355 pStream->WriteString("</");
356 pStream->WriteString(wsElement.ToUTF8().AsStringView());
357 pStream->WriteString(">\n");
358 }
359
RecognizeXFAVersionNumber(CXFA_Node * pTemplateRoot)360 WideString RecognizeXFAVersionNumber(CXFA_Node* pTemplateRoot) {
361 if (!pTemplateRoot)
362 return WideString();
363
364 Optional<WideString> templateNS = pTemplateRoot->JSObject()->TryNamespace();
365 if (!templateNS)
366 return WideString();
367
368 XFA_VERSION eVersion =
369 pTemplateRoot->GetDocument()->RecognizeXFAVersionNumber(*templateNS);
370 if (eVersion == XFA_VERSION_UNKNOWN)
371 eVersion = XFA_VERSION_DEFAULT;
372
373 return WideString::Format(L"%i.%i", eVersion / 100, eVersion % 100);
374 }
375
376 } // namespace
377
XFA_GetLocaleValue(CXFA_Node * pNode)378 CXFA_LocaleValue XFA_GetLocaleValue(CXFA_Node* pNode) {
379 CXFA_Value* pNodeValue =
380 pNode->GetChild<CXFA_Value>(0, XFA_Element::Value, false);
381 if (!pNodeValue)
382 return CXFA_LocaleValue();
383
384 CXFA_Node* pValueChild = pNodeValue->GetFirstChild();
385 if (!pValueChild)
386 return CXFA_LocaleValue();
387
388 int32_t iVTType = XFA_VT_NULL;
389 switch (pValueChild->GetElementType()) {
390 case XFA_Element::Decimal:
391 iVTType = XFA_VT_DECIMAL;
392 break;
393 case XFA_Element::Float:
394 iVTType = XFA_VT_FLOAT;
395 break;
396 case XFA_Element::Date:
397 iVTType = XFA_VT_DATE;
398 break;
399 case XFA_Element::Time:
400 iVTType = XFA_VT_TIME;
401 break;
402 case XFA_Element::DateTime:
403 iVTType = XFA_VT_DATETIME;
404 break;
405 case XFA_Element::Boolean:
406 iVTType = XFA_VT_BOOLEAN;
407 break;
408 case XFA_Element::Integer:
409 iVTType = XFA_VT_INTEGER;
410 break;
411 case XFA_Element::Text:
412 iVTType = XFA_VT_TEXT;
413 break;
414 default:
415 iVTType = XFA_VT_NULL;
416 break;
417 }
418 return CXFA_LocaleValue(iVTType, pNode->GetRawValue(),
419 pNode->GetDocument()->GetLocaleMgr());
420 }
421
XFA_FDEExtension_ResolveNamespaceQualifier(CFX_XMLElement * pNode,const WideString & wsQualifier,WideString * wsNamespaceURI)422 bool XFA_FDEExtension_ResolveNamespaceQualifier(CFX_XMLElement* pNode,
423 const WideString& wsQualifier,
424 WideString* wsNamespaceURI) {
425 if (!pNode)
426 return false;
427
428 CFX_XMLNode* pFakeRoot = pNode->GetRoot();
429 WideString wsNSAttribute;
430 bool bRet = false;
431 if (wsQualifier.IsEmpty()) {
432 wsNSAttribute = L"xmlns";
433 bRet = true;
434 } else {
435 wsNSAttribute = L"xmlns:" + wsQualifier;
436 }
437 for (CFX_XMLNode* pParent = pNode; pParent != pFakeRoot;
438 pParent = pParent->GetParent()) {
439 CFX_XMLElement* pElement = ToXMLElement(pParent);
440 if (pElement && pElement->HasAttribute(wsNSAttribute)) {
441 *wsNamespaceURI = pElement->GetAttribute(wsNSAttribute);
442 return true;
443 }
444 }
445 wsNamespaceURI->clear();
446 return bRet;
447 }
448
XFA_DataExporter_DealWithDataGroupNode(CXFA_Node * pDataNode)449 void XFA_DataExporter_DealWithDataGroupNode(CXFA_Node* pDataNode) {
450 if (!pDataNode || pDataNode->GetElementType() == XFA_Element::DataValue)
451 return;
452
453 int32_t iChildNum = 0;
454 for (CXFA_Node* pChildNode = pDataNode->GetFirstChild(); pChildNode;
455 pChildNode = pChildNode->GetNextSibling()) {
456 iChildNum++;
457 XFA_DataExporter_DealWithDataGroupNode(pChildNode);
458 }
459
460 if (pDataNode->GetElementType() != XFA_Element::DataGroup)
461 return;
462
463 CFX_XMLElement* pElement = ToXMLElement(pDataNode->GetXMLMappingNode());
464 if (iChildNum > 0) {
465 if (pElement->HasAttribute(L"xfa:dataNode"))
466 pElement->RemoveAttribute(L"xfa:dataNode");
467 return;
468 }
469 pElement->SetAttribute(L"xfa:dataNode", L"dataGroup");
470 }
471
XFA_DataExporter_RegenerateFormFile(CXFA_Node * pNode,const RetainPtr<IFX_SeekableStream> & pStream,bool bSaveXML)472 void XFA_DataExporter_RegenerateFormFile(
473 CXFA_Node* pNode,
474 const RetainPtr<IFX_SeekableStream>& pStream,
475 bool bSaveXML) {
476 if (pNode->IsModelNode()) {
477 pStream->WriteString("<form xmlns=\"");
478 pStream->WriteString(kFormNS);
479
480 WideString wsVersionNumber = RecognizeXFAVersionNumber(
481 ToNode(pNode->GetDocument()->GetXFAObject(XFA_HASHCODE_Template)));
482 if (wsVersionNumber.IsEmpty())
483 wsVersionNumber = L"2.8";
484
485 wsVersionNumber += L"/\"\n>";
486 pStream->WriteString(wsVersionNumber.ToUTF8().AsStringView());
487
488 CXFA_Node* pChildNode = pNode->GetFirstChild();
489 while (pChildNode) {
490 RegenerateFormFile_Container(pChildNode, pStream, false);
491 pChildNode = pChildNode->GetNextSibling();
492 }
493 pStream->WriteString("</form\n>");
494 } else {
495 RegenerateFormFile_Container(pNode, pStream, bSaveXML);
496 }
497 }
498
XFA_FieldIsMultiListBox(CXFA_Node * pFieldNode)499 bool XFA_FieldIsMultiListBox(CXFA_Node* pFieldNode) {
500 if (!pFieldNode)
501 return false;
502
503 CXFA_Ui* pUIChild = pFieldNode->GetChild<CXFA_Ui>(0, XFA_Element::Ui, false);
504 if (!pUIChild)
505 return false;
506
507 CXFA_Node* pFirstChild = pUIChild->GetFirstChild();
508 if (!pFirstChild ||
509 pFirstChild->GetElementType() != XFA_Element::ChoiceList) {
510 return false;
511 }
512
513 return pFirstChild->JSObject()->GetEnum(XFA_Attribute::Open) ==
514 XFA_AttributeValue::MultiSelect;
515 }
516
XFA_MapRotation(int32_t nRotation)517 int32_t XFA_MapRotation(int32_t nRotation) {
518 nRotation = nRotation % 360;
519 nRotation = nRotation < 0 ? nRotation + 360 : nRotation;
520 return nRotation;
521 }
522
XFA_EventErrorAccumulate(XFA_EventError * pAcc,XFA_EventError eNew)523 void XFA_EventErrorAccumulate(XFA_EventError* pAcc, XFA_EventError eNew) {
524 if (*pAcc == XFA_EventError::kNotExist || eNew == XFA_EventError::kError)
525 *pAcc = eNew;
526 }
527