cxfa_dataexporter.cpp revision 4d3acf4ec42bf6e838f9060103aff98fbf170794
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/cxfa_dataexporter.h"
8
9#include <vector>
10
11#include "core/fxcrt/fx_basic.h"
12#include "third_party/base/stl_util.h"
13#include "xfa/fde/xml/fde_xml_imp.h"
14#include "xfa/fgas/crt/fgas_codepage.h"
15#include "xfa/fxfa/parser/cxfa_document.h"
16#include "xfa/fxfa/parser/cxfa_widgetdata.h"
17#include "xfa/fxfa/parser/xfa_object.h"
18
19namespace {
20
21CFX_WideString ExportEncodeAttribute(const CFX_WideString& str) {
22  CFX_WideTextBuf textBuf;
23  int32_t iLen = str.GetLength();
24  for (int32_t i = 0; i < iLen; i++) {
25    switch (str[i]) {
26      case '&':
27        textBuf << FX_WSTRC(L"&amp;");
28        break;
29      case '<':
30        textBuf << FX_WSTRC(L"&lt;");
31        break;
32      case '>':
33        textBuf << FX_WSTRC(L"&gt;");
34        break;
35      case '\'':
36        textBuf << FX_WSTRC(L"&apos;");
37        break;
38      case '\"':
39        textBuf << FX_WSTRC(L"&quot;");
40        break;
41      default:
42        textBuf.AppendChar(str[i]);
43    }
44  }
45  return textBuf.MakeString();
46}
47
48CFX_WideString ExportEncodeContent(const CFX_WideStringC& str) {
49  CFX_WideTextBuf textBuf;
50  int32_t iLen = str.GetLength();
51  for (int32_t i = 0; i < iLen; i++) {
52    FX_WCHAR ch = str.GetAt(i);
53    if (!FDE_IsXMLValidChar(ch))
54      continue;
55
56    if (ch == '&') {
57      textBuf << FX_WSTRC(L"&amp;");
58    } else if (ch == '<') {
59      textBuf << FX_WSTRC(L"&lt;");
60    } else if (ch == '>') {
61      textBuf << FX_WSTRC(L"&gt;");
62    } else if (ch == '\'') {
63      textBuf << FX_WSTRC(L"&apos;");
64    } else if (ch == '\"') {
65      textBuf << FX_WSTRC(L"&quot;");
66    } else if (ch == ' ') {
67      if (i && str.GetAt(i - 1) != ' ') {
68        textBuf.AppendChar(' ');
69      } else {
70        textBuf << FX_WSTRC(L"&#x20;");
71      }
72    } else {
73      textBuf.AppendChar(str.GetAt(i));
74    }
75  }
76  return textBuf.MakeString();
77}
78
79void SaveAttribute(CXFA_Node* pNode,
80                   XFA_ATTRIBUTE eName,
81                   const CFX_WideStringC& wsName,
82                   bool bProto,
83                   CFX_WideString& wsOutput) {
84  CFX_WideString wsValue;
85  if ((!bProto && !pNode->HasAttribute((XFA_ATTRIBUTE)eName, bProto)) ||
86      !pNode->GetAttribute((XFA_ATTRIBUTE)eName, wsValue, false)) {
87    return;
88  }
89  wsValue = ExportEncodeAttribute(wsValue);
90  wsOutput += FX_WSTRC(L" ");
91  wsOutput += wsName;
92  wsOutput += FX_WSTRC(L"=\"");
93  wsOutput += wsValue;
94  wsOutput += FX_WSTRC(L"\"");
95}
96
97bool AttributeSaveInDataModel(CXFA_Node* pNode, XFA_ATTRIBUTE eAttribute) {
98  bool bSaveInDataModel = false;
99  if (pNode->GetElementType() != XFA_Element::Image)
100    return bSaveInDataModel;
101
102  CXFA_Node* pValueNode = pNode->GetNodeItem(XFA_NODEITEM_Parent);
103  if (!pValueNode || pValueNode->GetElementType() != XFA_Element::Value)
104    return bSaveInDataModel;
105
106  CXFA_Node* pFieldNode = pValueNode->GetNodeItem(XFA_NODEITEM_Parent);
107  if (pFieldNode && pFieldNode->GetBindData() &&
108      eAttribute == XFA_ATTRIBUTE_Href) {
109    bSaveInDataModel = true;
110  }
111  return bSaveInDataModel;
112}
113
114bool ContentNodeNeedtoExport(CXFA_Node* pContentNode) {
115  CFX_WideString wsContent;
116  if (!pContentNode->TryContent(wsContent, false, false))
117    return false;
118
119  ASSERT(pContentNode->IsContentNode());
120  CXFA_Node* pParentNode = pContentNode->GetNodeItem(XFA_NODEITEM_Parent);
121  if (!pParentNode || pParentNode->GetElementType() != XFA_Element::Value)
122    return true;
123
124  CXFA_Node* pGrandParentNode = pParentNode->GetNodeItem(XFA_NODEITEM_Parent);
125  if (!pGrandParentNode || !pGrandParentNode->IsContainerNode())
126    return true;
127  if (pGrandParentNode->GetBindData())
128    return false;
129
130  CXFA_WidgetData* pWidgetData = pGrandParentNode->GetWidgetData();
131  XFA_Element eUIType = pWidgetData->GetUIType();
132  if (eUIType == XFA_Element::PasswordEdit)
133    return false;
134  return true;
135}
136
137void RecognizeXFAVersionNumber(CXFA_Node* pTemplateRoot,
138                               CFX_WideString& wsVersionNumber) {
139  wsVersionNumber.clear();
140  if (!pTemplateRoot)
141    return;
142
143  CFX_WideString wsTemplateNS;
144  if (!pTemplateRoot->TryNamespace(wsTemplateNS))
145    return;
146
147  XFA_VERSION eVersion =
148      pTemplateRoot->GetDocument()->RecognizeXFAVersionNumber(wsTemplateNS);
149  if (eVersion == XFA_VERSION_UNKNOWN)
150    eVersion = XFA_VERSION_DEFAULT;
151
152  wsVersionNumber.Format(L"%i.%i", eVersion / 100, eVersion % 100);
153}
154
155void RegenerateFormFile_Changed(CXFA_Node* pNode,
156                                CFX_WideTextBuf& buf,
157                                bool bSaveXML) {
158  CFX_WideString wsAttrs;
159  int32_t iAttrs = 0;
160  const uint8_t* pAttrs =
161      XFA_GetElementAttributes(pNode->GetElementType(), iAttrs);
162  while (iAttrs--) {
163    const XFA_ATTRIBUTEINFO* pAttr =
164        XFA_GetAttributeByID((XFA_ATTRIBUTE)pAttrs[iAttrs]);
165    if (pAttr->eName == XFA_ATTRIBUTE_Name ||
166        (AttributeSaveInDataModel(pNode, pAttr->eName) && !bSaveXML)) {
167      continue;
168    }
169    CFX_WideString wsAttr;
170    SaveAttribute(pNode, pAttr->eName, pAttr->pName, bSaveXML, wsAttr);
171    wsAttrs += wsAttr;
172  }
173
174  CFX_WideString wsChildren;
175  switch (pNode->GetObjectType()) {
176    case XFA_ObjectType::ContentNode: {
177      if (!bSaveXML && !ContentNodeNeedtoExport(pNode))
178        break;
179
180      CXFA_Node* pRawValueNode = pNode->GetNodeItem(XFA_NODEITEM_FirstChild);
181      while (pRawValueNode &&
182             pRawValueNode->GetElementType() != XFA_Element::SharpxHTML &&
183             pRawValueNode->GetElementType() != XFA_Element::Sharptext &&
184             pRawValueNode->GetElementType() != XFA_Element::Sharpxml) {
185        pRawValueNode = pRawValueNode->GetNodeItem(XFA_NODEITEM_NextSibling);
186      }
187      if (!pRawValueNode)
188        break;
189
190      CFX_WideString wsContentType;
191      pNode->GetAttribute(XFA_ATTRIBUTE_ContentType, wsContentType, false);
192      if (pRawValueNode->GetElementType() == XFA_Element::SharpxHTML &&
193          wsContentType == FX_WSTRC(L"text/html")) {
194        CFDE_XMLNode* pExDataXML = pNode->GetXMLMappingNode();
195        if (!pExDataXML)
196          break;
197
198        CFDE_XMLNode* pRichTextXML =
199            pExDataXML->GetNodeItem(CFDE_XMLNode::FirstChild);
200        if (!pRichTextXML)
201          break;
202
203        CFX_RetainPtr<IFX_MemoryStream> pMemStream =
204            IFX_MemoryStream::Create(true);
205
206        // Note: ambiguous without cast below.
207        CFX_RetainPtr<IFGAS_Stream> pTempStream = IFGAS_Stream::CreateStream(
208            CFX_RetainPtr<IFX_SeekableWriteStream>(pMemStream),
209            FX_STREAMACCESS_Text | FX_STREAMACCESS_Write |
210                FX_STREAMACCESS_Append);
211
212        pTempStream->SetCodePage(FX_CODEPAGE_UTF8);
213        pRichTextXML->SaveXMLNode(pTempStream);
214        wsChildren += CFX_WideString::FromUTF8(
215            CFX_ByteStringC(pMemStream->GetBuffer(), pMemStream->GetSize()));
216      } else if (pRawValueNode->GetElementType() == XFA_Element::Sharpxml &&
217                 wsContentType == FX_WSTRC(L"text/xml")) {
218        CFX_WideString wsRawValue;
219        pRawValueNode->GetAttribute(XFA_ATTRIBUTE_Value, wsRawValue, false);
220        if (wsRawValue.IsEmpty())
221          break;
222
223        std::vector<CFX_WideString> wsSelTextArray;
224        int32_t iStart = 0;
225        int32_t iEnd = wsRawValue.Find(L'\n', iStart);
226        iEnd = (iEnd == -1) ? wsRawValue.GetLength() : iEnd;
227        while (iEnd >= iStart) {
228          wsSelTextArray.push_back(wsRawValue.Mid(iStart, iEnd - iStart));
229          iStart = iEnd + 1;
230          if (iStart >= wsRawValue.GetLength())
231            break;
232
233          iEnd = wsRawValue.Find(L'\n', iStart);
234        }
235        CXFA_Node* pParentNode = pNode->GetNodeItem(XFA_NODEITEM_Parent);
236        ASSERT(pParentNode);
237        CXFA_Node* pGrandparentNode =
238            pParentNode->GetNodeItem(XFA_NODEITEM_Parent);
239        ASSERT(pGrandparentNode);
240        CFX_WideString bodyTagName;
241        bodyTagName = pGrandparentNode->GetCData(XFA_ATTRIBUTE_Name);
242        if (bodyTagName.IsEmpty())
243          bodyTagName = FX_WSTRC(L"ListBox1");
244
245        buf << FX_WSTRC(L"<");
246        buf << bodyTagName;
247        buf << FX_WSTRC(L" xmlns=\"\"\n>");
248        for (int32_t i = 0; i < pdfium::CollectionSize<int32_t>(wsSelTextArray);
249             i++) {
250          buf << FX_WSTRC(L"<value\n>");
251          buf << ExportEncodeContent(wsSelTextArray[i].AsStringC());
252          buf << FX_WSTRC(L"</value\n>");
253        }
254        buf << FX_WSTRC(L"</");
255        buf << bodyTagName;
256        buf << FX_WSTRC(L"\n>");
257        wsChildren += buf.AsStringC();
258        buf.Clear();
259      } else {
260        CFX_WideStringC wsValue = pRawValueNode->GetCData(XFA_ATTRIBUTE_Value);
261        wsChildren += ExportEncodeContent(wsValue);
262      }
263      break;
264    }
265    case XFA_ObjectType::TextNode:
266    case XFA_ObjectType::NodeC:
267    case XFA_ObjectType::NodeV: {
268      CFX_WideStringC wsValue = pNode->GetCData(XFA_ATTRIBUTE_Value);
269      wsChildren += ExportEncodeContent(wsValue);
270      break;
271    }
272    default:
273      if (pNode->GetElementType() == XFA_Element::Items) {
274        CXFA_Node* pTemplateNode = pNode->GetTemplateNode();
275        if (!pTemplateNode ||
276            pTemplateNode->CountChildren(XFA_Element::Unknown) !=
277                pNode->CountChildren(XFA_Element::Unknown)) {
278          bSaveXML = true;
279        }
280      }
281      CFX_WideTextBuf newBuf;
282      CXFA_Node* pChildNode = pNode->GetNodeItem(XFA_NODEITEM_FirstChild);
283      while (pChildNode) {
284        RegenerateFormFile_Changed(pChildNode, newBuf, bSaveXML);
285        wsChildren += newBuf.AsStringC();
286        newBuf.Clear();
287        pChildNode = pChildNode->GetNodeItem(XFA_NODEITEM_NextSibling);
288      }
289      if (!bSaveXML && !wsChildren.IsEmpty() &&
290          pNode->GetElementType() == XFA_Element::Items) {
291        wsChildren.clear();
292        bSaveXML = true;
293        CXFA_Node* pChild = pNode->GetNodeItem(XFA_NODEITEM_FirstChild);
294        while (pChild) {
295          RegenerateFormFile_Changed(pChild, newBuf, bSaveXML);
296          wsChildren += newBuf.AsStringC();
297          newBuf.Clear();
298          pChild = pChild->GetNodeItem(XFA_NODEITEM_NextSibling);
299        }
300      }
301      break;
302  }
303
304  if (!wsChildren.IsEmpty() || !wsAttrs.IsEmpty() ||
305      pNode->HasAttribute(XFA_ATTRIBUTE_Name)) {
306    CFX_WideStringC wsElement = pNode->GetClassName();
307    CFX_WideString wsName;
308    SaveAttribute(pNode, XFA_ATTRIBUTE_Name, FX_WSTRC(L"name"), true, wsName);
309    buf << FX_WSTRC(L"<");
310    buf << wsElement;
311    buf << wsName;
312    buf << wsAttrs;
313    if (wsChildren.IsEmpty()) {
314      buf << FX_WSTRC(L"\n/>");
315    } else {
316      buf << FX_WSTRC(L"\n>");
317      buf << wsChildren;
318      buf << FX_WSTRC(L"</");
319      buf << wsElement;
320      buf << FX_WSTRC(L"\n>");
321    }
322  }
323}
324
325void RegenerateFormFile_Container(CXFA_Node* pNode,
326                                  const CFX_RetainPtr<IFGAS_Stream>& pStream,
327                                  bool bSaveXML = false) {
328  XFA_Element eType = pNode->GetElementType();
329  if (eType == XFA_Element::Field || eType == XFA_Element::Draw ||
330      !pNode->IsContainerNode()) {
331    CFX_WideTextBuf buf;
332    RegenerateFormFile_Changed(pNode, buf, bSaveXML);
333    FX_STRSIZE nLen = buf.GetLength();
334    if (nLen > 0)
335      pStream->WriteString((const FX_WCHAR*)buf.GetBuffer(), nLen);
336    return;
337  }
338
339  CFX_WideStringC wsElement = pNode->GetClassName();
340  pStream->WriteString(L"<", 1);
341  pStream->WriteString(wsElement.c_str(), wsElement.GetLength());
342  CFX_WideString wsOutput;
343  SaveAttribute(pNode, XFA_ATTRIBUTE_Name, FX_WSTRC(L"name"), true, wsOutput);
344  CFX_WideString wsAttrs;
345  int32_t iAttrs = 0;
346  const uint8_t* pAttrs =
347      XFA_GetElementAttributes(pNode->GetElementType(), iAttrs);
348  while (iAttrs--) {
349    const XFA_ATTRIBUTEINFO* pAttr =
350        XFA_GetAttributeByID((XFA_ATTRIBUTE)pAttrs[iAttrs]);
351    if (pAttr->eName == XFA_ATTRIBUTE_Name)
352      continue;
353
354    CFX_WideString wsAttr;
355    SaveAttribute(pNode, pAttr->eName, pAttr->pName, false, wsAttr);
356    wsOutput += wsAttr;
357  }
358
359  if (!wsOutput.IsEmpty())
360    pStream->WriteString(wsOutput.c_str(), wsOutput.GetLength());
361
362  CXFA_Node* pChildNode = pNode->GetNodeItem(XFA_NODEITEM_FirstChild);
363  if (pChildNode) {
364    pStream->WriteString(L"\n>", 2);
365    while (pChildNode) {
366      RegenerateFormFile_Container(pChildNode, pStream, bSaveXML);
367      pChildNode = pChildNode->GetNodeItem(XFA_NODEITEM_NextSibling);
368    }
369    pStream->WriteString(L"</", 2);
370    pStream->WriteString(wsElement.c_str(), wsElement.GetLength());
371    pStream->WriteString(L"\n>", 2);
372  } else {
373    pStream->WriteString(L"\n/>", 3);
374  }
375}
376
377}  // namespace
378
379void XFA_DataExporter_RegenerateFormFile(
380    CXFA_Node* pNode,
381    const CFX_RetainPtr<IFGAS_Stream>& pStream,
382    const FX_CHAR* pChecksum,
383    bool bSaveXML) {
384  if (pNode->IsModelNode()) {
385    static const FX_WCHAR s_pwsTagName[] = L"<form";
386    static const FX_WCHAR s_pwsClose[] = L"</form\n>";
387    pStream->WriteString(s_pwsTagName, FXSYS_wcslen(s_pwsTagName));
388    if (pChecksum) {
389      static const FX_WCHAR s_pwChecksum[] = L" checksum=\"";
390      CFX_WideString wsChecksum = CFX_WideString::FromUTF8(pChecksum);
391      pStream->WriteString(s_pwChecksum, FXSYS_wcslen(s_pwChecksum));
392      pStream->WriteString(wsChecksum.c_str(), wsChecksum.GetLength());
393      pStream->WriteString(L"\"", 1);
394    }
395    pStream->WriteString(L" xmlns=\"", FXSYS_wcslen(L" xmlns=\""));
396    const FX_WCHAR* pURI = XFA_GetPacketByIndex(XFA_PACKET_Form)->pURI;
397    pStream->WriteString(pURI, FXSYS_wcslen(pURI));
398    CFX_WideString wsVersionNumber;
399    RecognizeXFAVersionNumber(
400        ToNode(pNode->GetDocument()->GetXFAObject(XFA_HASHCODE_Template)),
401        wsVersionNumber);
402    if (wsVersionNumber.IsEmpty()) {
403      wsVersionNumber = FX_WSTRC(L"2.8");
404    }
405    wsVersionNumber += FX_WSTRC(L"/\"\n>");
406    pStream->WriteString(wsVersionNumber.c_str(), wsVersionNumber.GetLength());
407    CXFA_Node* pChildNode = pNode->GetNodeItem(XFA_NODEITEM_FirstChild);
408    while (pChildNode) {
409      RegenerateFormFile_Container(pChildNode, pStream);
410      pChildNode = pChildNode->GetNodeItem(XFA_NODEITEM_NextSibling);
411    }
412    pStream->WriteString(s_pwsClose, FXSYS_wcslen(s_pwsClose));
413  } else {
414    RegenerateFormFile_Container(pNode, pStream, bSaveXML);
415  }
416}
417
418void XFA_DataExporter_DealWithDataGroupNode(CXFA_Node* pDataNode) {
419  if (!pDataNode || pDataNode->GetElementType() == XFA_Element::DataValue)
420    return;
421
422  int32_t iChildNum = 0;
423  for (CXFA_Node* pChildNode = pDataNode->GetNodeItem(XFA_NODEITEM_FirstChild);
424       pChildNode;
425       pChildNode = pChildNode->GetNodeItem(XFA_NODEITEM_NextSibling)) {
426    iChildNum++;
427    XFA_DataExporter_DealWithDataGroupNode(pChildNode);
428  }
429
430  if (pDataNode->GetElementType() != XFA_Element::DataGroup)
431    return;
432
433  if (iChildNum > 0) {
434    CFDE_XMLNode* pXMLNode = pDataNode->GetXMLMappingNode();
435    ASSERT(pXMLNode->GetType() == FDE_XMLNODE_Element);
436    CFDE_XMLElement* pXMLElement = static_cast<CFDE_XMLElement*>(pXMLNode);
437    if (pXMLElement->HasAttribute(L"xfa:dataNode"))
438      pXMLElement->RemoveAttribute(L"xfa:dataNode");
439
440    return;
441  }
442
443  CFDE_XMLNode* pXMLNode = pDataNode->GetXMLMappingNode();
444  ASSERT(pXMLNode->GetType() == FDE_XMLNODE_Element);
445  static_cast<CFDE_XMLElement*>(pXMLNode)->SetString(L"xfa:dataNode",
446                                                     L"dataGroup");
447}
448
449CXFA_DataExporter::CXFA_DataExporter(CXFA_Document* pDocument)
450    : m_pDocument(pDocument) {
451  ASSERT(m_pDocument);
452}
453
454bool CXFA_DataExporter::Export(
455    const CFX_RetainPtr<IFX_SeekableWriteStream>& pWrite) {
456  return Export(pWrite, m_pDocument->GetRoot(), 0, nullptr);
457}
458
459bool CXFA_DataExporter::Export(
460    const CFX_RetainPtr<IFX_SeekableWriteStream>& pWrite,
461    CXFA_Node* pNode,
462    uint32_t dwFlag,
463    const FX_CHAR* pChecksum) {
464  ASSERT(pWrite);
465  if (!pWrite)
466    return false;
467
468  CFX_RetainPtr<IFGAS_Stream> pStream = IFGAS_Stream::CreateStream(
469      pWrite,
470      FX_STREAMACCESS_Text | FX_STREAMACCESS_Write | FX_STREAMACCESS_Append);
471  if (!pStream)
472    return false;
473
474  pStream->SetCodePage(FX_CODEPAGE_UTF8);
475  return Export(pStream, pNode, dwFlag, pChecksum);
476}
477
478bool CXFA_DataExporter::Export(const CFX_RetainPtr<IFGAS_Stream>& pStream,
479                               CXFA_Node* pNode,
480                               uint32_t dwFlag,
481                               const FX_CHAR* pChecksum) {
482  CFDE_XMLDoc* pXMLDoc = m_pDocument->GetXMLDoc();
483  if (pNode->IsModelNode()) {
484    switch (pNode->GetPacketID()) {
485      case XFA_XDPPACKET_XDP: {
486        static const FX_WCHAR s_pwsPreamble[] =
487            L"<xdp:xdp xmlns:xdp=\"http://ns.adobe.com/xdp/\">";
488        pStream->WriteString(s_pwsPreamble, FXSYS_wcslen(s_pwsPreamble));
489        for (CXFA_Node* pChild = pNode->GetNodeItem(XFA_NODEITEM_FirstChild);
490             pChild; pChild = pChild->GetNodeItem(XFA_NODEITEM_NextSibling)) {
491          Export(pStream, pChild, dwFlag, pChecksum);
492        }
493        static const FX_WCHAR s_pwsPostamble[] = L"</xdp:xdp\n>";
494        pStream->WriteString(s_pwsPostamble, FXSYS_wcslen(s_pwsPostamble));
495        break;
496      }
497      case XFA_XDPPACKET_Datasets: {
498        CFDE_XMLElement* pElement =
499            static_cast<CFDE_XMLElement*>(pNode->GetXMLMappingNode());
500        if (!pElement || pElement->GetType() != FDE_XMLNODE_Element)
501          return false;
502
503        CXFA_Node* pDataNode = pNode->GetNodeItem(XFA_NODEITEM_FirstChild);
504        ASSERT(pDataNode);
505        XFA_DataExporter_DealWithDataGroupNode(pDataNode);
506        pXMLDoc->SaveXMLNode(pStream, pElement);
507        break;
508      }
509      case XFA_XDPPACKET_Form: {
510        XFA_DataExporter_RegenerateFormFile(pNode, pStream, pChecksum);
511        break;
512      }
513      case XFA_XDPPACKET_Template:
514      default: {
515        CFDE_XMLElement* pElement =
516            static_cast<CFDE_XMLElement*>(pNode->GetXMLMappingNode());
517        if (!pElement || pElement->GetType() != FDE_XMLNODE_Element)
518          return false;
519
520        pXMLDoc->SaveXMLNode(pStream, pElement);
521        break;
522      }
523    }
524    return true;
525  }
526
527  CXFA_Node* pDataNode = pNode->GetNodeItem(XFA_NODEITEM_Parent);
528  CXFA_Node* pExportNode = pNode;
529  for (CXFA_Node* pChildNode = pDataNode->GetNodeItem(XFA_NODEITEM_FirstChild);
530       pChildNode;
531       pChildNode = pChildNode->GetNodeItem(XFA_NODEITEM_NextSibling)) {
532    if (pChildNode != pNode) {
533      pExportNode = pDataNode;
534      break;
535    }
536  }
537  CFDE_XMLElement* pElement =
538      static_cast<CFDE_XMLElement*>(pExportNode->GetXMLMappingNode());
539  if (!pElement || pElement->GetType() != FDE_XMLNODE_Element)
540    return false;
541
542  XFA_DataExporter_DealWithDataGroupNode(pExportNode);
543  pElement->SetString(L"xmlns:xfa", L"http://www.xfa.org/schema/xfa-data/1.0/");
544  pXMLDoc->SaveXMLNode(pStream, pElement);
545  pElement->RemoveAttribute(L"xmlns:xfa");
546
547  return true;
548}
549