1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <memory>
21 #include <iostream>
22 
23 #include <com/sun/star/embed/ElementModes.hpp>
24 #include <com/sun/star/embed/XStorage.hpp>
25 #include <com/sun/star/frame/XModel.hpp>
26 #include <com/sun/star/packages/XPackageEncryption.hpp>
27 #include <com/sun/star/uno/XComponentContext.hpp>
28 #include <unotools/ucbstreamhelper.hxx>
29 #include <unotools/streamwrap.hxx>
30 #include <algorithm>
31 #include <map>
32 #include <hintids.hxx>
33 #include <string.h>
34 #include <o3tl/safeint.hxx>
35 #include <osl/endian.h>
36 #include <sal/log.hxx>
37 #include <docsh.hxx>
38 #include <drawdoc.hxx>
39 
40 #include <unotools/fltrcfg.hxx>
41 #include <sot/storage.hxx>
42 #include <sfx2/docinf.hxx>
43 #include <editeng/tstpitem.hxx>
44 #include <svx/svdpage.hxx>
45 #include <editeng/hyphenzoneitem.hxx>
46 #include <filter/msfilter/classids.hxx>
47 #include <filter/msfilter/msoleexp.hxx>
48 #include <editeng/lrspitem.hxx>
49 #include <editeng/ulspitem.hxx>
50 #include <editeng/boxitem.hxx>
51 #include <editeng/brushitem.hxx>
52 #include <swtypes.hxx>
53 #include <swrect.hxx>
54 #include <swtblfmt.hxx>
55 #include <fmtcntnt.hxx>
56 #include <fmtpdsc.hxx>
57 #include <fmtrowsplt.hxx>
58 #include <frmatr.hxx>
59 #include <../../core/inc/rootfrm.hxx>
60 #include <doc.hxx>
61 #include <IDocumentSettingAccess.hxx>
62 #include <IDocumentDrawModelAccess.hxx>
63 #include <IDocumentStylePoolAccess.hxx>
64 #include <IDocumentStatistics.hxx>
65 #include <IDocumentLayoutAccess.hxx>
66 #include <IDocumentExternalData.hxx>
67 #include <viewopt.hxx>
68 #include <docary.hxx>
69 #include <pam.hxx>
70 #include <ndtxt.hxx>
71 #include <shellio.hxx>
72 #include <docstat.hxx>
73 #include <pagedesc.hxx>
74 #include <poolfmt.hxx>
75 #include <IMark.hxx>
76 #include <swtable.hxx>
77 #include "wrtww8.hxx"
78 #include "ww8par.hxx"
79 #include <swmodule.hxx>
80 #include <section.hxx>
81 #include <fmtinfmt.hxx>
82 #include <txtinet.hxx>
83 #include <fmturl.hxx>
84 #include <vcl/imap.hxx>
85 #include <vcl/imapobj.hxx>
86 #include <mdiexp.hxx>
87 #include <strings.hrc>
88 #include <fmtline.hxx>
89 #include <fmtfsize.hxx>
90 #include "sprmids.hxx"
91 
92 #include <comphelper/sequenceashashmap.hxx>
93 #include <comphelper/processfactory.hxx>
94 #include "writerhelper.hxx"
95 #include "writerwordglue.hxx"
96 #include "ww8attributeoutput.hxx"
97 #include <xmloff/odffields.hxx>
98 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
99 #include <com/sun/star/document/XDocumentProperties.hpp>
100 #include <dbgoutsw.hxx>
101 #include <sfx2/docfile.hxx>
102 #include <sfx2/frame.hxx>
103 #include <svl/stritem.hxx>
104 #include <unotools/tempfile.hxx>
105 #include <filter/msfilter/mscodec.hxx>
106 #include <filter/msfilter/svxmsbas.hxx>
107 #include <rtl/random.h>
108 #include <vcl/svapp.hxx>
109 #include <sfx2/docfilt.hxx>
110 #include "WW8Sttbf.hxx"
111 #include <editeng/charrotateitem.hxx>
112 #include <svx/swframetypes.hxx>
113 #include "WW8FibData.hxx"
114 #include <numrule.hxx>
115 #include <fmtclds.hxx>
116 #include <rdfhelper.hxx>
117 #include <fmtclbl.hxx>
118 #include <iodetect.hxx>
119 
120 using namespace css;
121 using namespace sw::util;
122 using namespace sw::types;
123 
124 /** FKP - Formatted disK Page
125 */
126 class WW8_WrFkp
127 {
128     sal_uInt8* pFkp;         // Fkp total ( first and only FCs and Sprms )
129     sal_uInt8* pOfs;         // pointer to the offset area, later copied to pFkp
130     ePLCFT ePlc;
131     short nStartGrp;    // from here on grpprls
132     short nOldStartGrp;
133     sal_uInt8 nItemSize;
134     sal_uInt8 nIMax;         // number of entry pairs
135     sal_uInt8 nOldVarLen;
136     bool bCombined;     // true : paste not allowed
137 
138     sal_uInt8 SearchSameSprm( sal_uInt16 nVarLen, const sal_uInt8* pSprms );
139 
140     WW8_WrFkp(const WW8_WrFkp&) = delete;
141     WW8_WrFkp& operator=(const WW8_WrFkp&) = delete;
142 
143 public:
144     WW8_WrFkp(ePLCFT ePl, WW8_FC nStartFc);
145     ~WW8_WrFkp();
146     bool Append( WW8_FC nEndFc, sal_uInt16 nVarLen, const sal_uInt8* pSprms );
147     void Combine();
148     void Write( SvStream& rStrm, SwWW8WrGrf& rGrf );
149 
IsEqualPos(WW8_FC nEndFc) const150     bool IsEqualPos(WW8_FC nEndFc) const
151     {   return !bCombined && nIMax && nEndFc == reinterpret_cast<sal_Int32*>(pFkp)[nIMax]; }
152     void MergeToNew( short& rVarLen, sal_uInt8 *& pNewSprms );
IsEmptySprm() const153     bool IsEmptySprm() const
154     {   return !bCombined && nIMax && !nOldVarLen;  }
SetNewEnd(WW8_FC nEnd)155     void SetNewEnd( WW8_FC nEnd )
156     {   reinterpret_cast<sal_Int32*>(pFkp)[nIMax] = nEnd; }
157 
158     WW8_FC GetStartFc() const;
159     WW8_FC GetEndFc() const;
160 
161     sal_uInt8 *CopyLastSprms(sal_uInt8 &rLen);
162 };
163 
164 // class WW8_WrPc collects all piece entries for one piece
165 class WW8_WrPc
166 {
167     WW8_CP nStartCp;                    // Starting character position of the text
168     WW8_FC nStartFc;                    // Starting file position of the text
169     sal_uInt16 nStatus;                     // End of paragraph inside the piece?
170 
171 public:
WW8_WrPc(WW8_FC nSFc,WW8_CP nSCp)172     WW8_WrPc(WW8_FC nSFc, WW8_CP nSCp )
173         : nStartCp( nSCp ), nStartFc( nSFc ), nStatus( 0x0040 )
174     {}
175 
SetStatus()176     void SetStatus()                { nStatus = 0x0050; }
GetStatus() const177     sal_uInt16 GetStatus()  const       { return nStatus; }
GetStartCp() const178     WW8_CP GetStartCp() const       { return nStartCp; }
GetStartFc() const179     WW8_FC GetStartFc() const       { return nStartFc; }
180 };
181 
182 typedef std::map<OUString,tools::Long> BKMKNames;
183 typedef std::pair<bool,OUString> BKMK;
184 typedef std::pair<tools::Long,BKMK> BKMKCP;
185 typedef std::multimap<tools::Long,BKMKCP*> BKMKCPs;
186 typedef BKMKCPs::iterator CPItr;
187 
188 class WW8_WrtBookmarks
189 {
190 private:
191     /// Structure of one item inside this map: (startPos, (endPos, (a bool value?, bookmarkName)))
192     BKMKCPs aSttCps;
193     BKMKNames maSwBkmkNms;
194 
195     WW8_WrtBookmarks(WW8_WrtBookmarks const&) = delete;
196     WW8_WrtBookmarks& operator=(WW8_WrtBookmarks const&) = delete;
197 
198 public:
199     WW8_WrtBookmarks();
200     ~WW8_WrtBookmarks();
201     //! Add a new bookmark to the list OR add an end position to an existing bookmark.
202     void Append( WW8_CP nStartCp, const OUString& rNm );
203     //! Write out bookmarks to file.
204     void Write( WW8Export& rWrt );
205     //! Move existing field marks from one position to another.
206     void MoveFieldMarks(WW8_CP nFrom, WW8_CP nTo);
207 };
208 
WW8_WrtBookmarks()209 WW8_WrtBookmarks::WW8_WrtBookmarks()
210 {}
211 
~WW8_WrtBookmarks()212 WW8_WrtBookmarks::~WW8_WrtBookmarks()
213 {
214     for (auto& rEntry : aSttCps)
215     {
216         if (rEntry.second)
217         {
218             delete rEntry.second;
219             rEntry.second = nullptr;
220         }
221     }
222 }
223 
Append(WW8_CP nStartCp,const OUString & rNm)224 void WW8_WrtBookmarks::Append( WW8_CP nStartCp, const OUString& rNm)
225 {
226     std::pair<BKMKNames::iterator, bool> aResult = maSwBkmkNms.insert(std::pair<OUString,tools::Long>(rNm,0L));
227     if (aResult.second)
228     {
229         BKMK aBK(false,rNm);
230         BKMKCP* pBKCP = new BKMKCP(static_cast<tools::Long>(nStartCp),aBK);
231         aSttCps.insert(std::pair<tools::Long,BKMKCP*>(nStartCp,pBKCP));
232         aResult.first->second = static_cast<tools::Long>(nStartCp);
233     }
234     else
235     {
236         std::pair<CPItr,CPItr> aRange = aSttCps.equal_range(aResult.first->second);
237         for (CPItr aItr = aRange.first;aItr != aRange.second;++aItr)
238         {
239             if (aItr->second && aItr->second->second.second == rNm)
240             {
241                 if (aItr->second->second.first)
242                     nStartCp--;
243                 aItr->second->first = static_cast<tools::Long>(nStartCp);
244                 break;
245             }
246         }
247     }
248 }
249 
Write(WW8Export & rWrt)250 void WW8_WrtBookmarks::Write( WW8Export& rWrt)
251 {
252     if (aSttCps.empty())
253         return;
254     tools::Long n;
255     std::vector<OUString> aNames;
256     SvMemoryStream aTempStrm1(65535,65535);
257     SvMemoryStream aTempStrm2(65535,65535);
258 
259     BKMKCPs aEndCps;
260     for (const auto& rEntry : aSttCps)
261     {
262         if (rEntry.second)
263         {
264             aEndCps.insert(std::pair<tools::Long,BKMKCP*>(rEntry.second->first, rEntry.second));
265             aNames.push_back(rEntry.second->second.second);
266             SwWW8Writer::WriteLong(aTempStrm1, rEntry.first);
267         }
268     }
269 
270     aTempStrm1.Seek(0);
271     n = 0;
272     for (const auto& rEntry : aEndCps)
273     {
274         if (rEntry.second)
275         {
276             rEntry.second->first = n;
277             SwWW8Writer::WriteLong( aTempStrm2, rEntry.first);
278         }
279         ++n;
280     }
281 
282     aTempStrm2.Seek(0);
283     rWrt.WriteAsStringTable(aNames, rWrt.pFib->m_fcSttbfbkmk,rWrt.pFib->m_lcbSttbfbkmk);
284     SvStream& rStrm = *rWrt.pTableStrm;
285     rWrt.pFib->m_fcPlcfbkf = rStrm.Tell();
286     rStrm.WriteStream( aTempStrm1 );
287     SwWW8Writer::WriteLong(rStrm, rWrt.pFib->m_ccpText + rWrt.pFib->m_ccpTxbx);
288     for (const auto& rEntry : aSttCps)
289     {
290         if (rEntry.second)
291         {
292             SwWW8Writer::WriteLong(rStrm, rEntry.second->first);
293         }
294     }
295     rWrt.pFib->m_lcbPlcfbkf = rStrm.Tell() - rWrt.pFib->m_fcPlcfbkf;
296     rWrt.pFib->m_fcPlcfbkl = rStrm.Tell();
297     rStrm.WriteStream( aTempStrm2 );
298     SwWW8Writer::WriteLong(rStrm, rWrt.pFib->m_ccpText + rWrt.pFib->m_ccpTxbx);
299     rWrt.pFib->m_lcbPlcfbkl = rStrm.Tell() - rWrt.pFib->m_fcPlcfbkl;
300 }
301 
MoveFieldMarks(WW8_CP nFrom,WW8_CP nTo)302 void WW8_WrtBookmarks::MoveFieldMarks(WW8_CP nFrom, WW8_CP nTo)
303 {
304     std::pair<CPItr,CPItr> aRange = aSttCps.equal_range(nFrom);
305     CPItr aItr = aRange.first;
306     while (aItr != aRange.second)
307     {
308         if (aItr->second)
309         {
310             if (aItr->second->first == static_cast<tools::Long>(nFrom))
311             {
312                 aItr->second->second.first = true;
313                 aItr->second->first = nTo;
314             }
315             aSttCps.insert(std::pair<tools::Long,BKMKCP*>(nTo,aItr->second));
316             aItr->second = nullptr;
317             aRange = aSttCps.equal_range(nFrom);
318             aItr = aRange.first;
319             continue;
320         }
321         ++aItr;
322     }
323 }
324 
325 /// Handles export of smart tags.
326 class WW8_WrtFactoids
327 {
328     std::vector<WW8_CP> m_aStartCPs;
329     std::vector<WW8_CP> m_aEndCPs;
330     std::vector< std::map<OUString, OUString> > m_aStatements;
331 
332     WW8_WrtFactoids(WW8_WrtFactoids const&) = delete;
333     WW8_WrtFactoids& operator=(WW8_WrtFactoids const&) = delete;
334 
335 public:
336     WW8_WrtFactoids();
337     void Append(WW8_CP nStartCp, WW8_CP nEndCp, const std::map<OUString, OUString>& rStatements);
338     void Write(WW8Export& rWrt);
339 };
340 
WW8_WrtFactoids()341 WW8_WrtFactoids::WW8_WrtFactoids()
342 {
343 }
344 
Append(WW8_CP nStartCp,WW8_CP nEndCp,const std::map<OUString,OUString> & rStatements)345 void WW8_WrtFactoids::Append(WW8_CP nStartCp, WW8_CP nEndCp, const std::map<OUString, OUString>& rStatements)
346 {
347     m_aStartCPs.push_back(nStartCp);
348     m_aEndCPs.push_back(nEndCp);
349     m_aStatements.push_back(rStatements);
350 }
351 
Write(WW8Export & rExport)352 void WW8_WrtFactoids::Write(WW8Export& rExport)
353 {
354     if (m_aStartCPs.empty())
355         return;
356 
357     // Smart tags are otherwise removed by Word on saving.
358     rExport.pDop->fEmbedFactoids = true;
359 
360     SvStream& rStream = *rExport.pTableStrm;
361 
362     rExport.pFib->m_fcSttbfBkmkFactoid = rStream.Tell();
363     // Write SttbfBkmkFactoid.
364     rStream.WriteUInt16(0xffff); // fExtend
365     rStream.WriteUInt16(m_aStartCPs.size()); // cData
366     rStream.WriteUInt16(0); // cbExtra
367 
368     for (size_t i = 0; i < m_aStartCPs.size(); ++i)
369     {
370         rStream.WriteUInt16(6); // cchData
371         // Write FACTOIDINFO.
372         rStream.WriteUInt32(i); // dwId
373         rStream.WriteUInt16(0); // fSubEntry
374         rStream.WriteUInt16(0); // fto
375         rStream.WriteUInt32(0); // pfpb
376     }
377     rExport.pFib->m_lcbSttbfBkmkFactoid = rStream.Tell() - rExport.pFib->m_fcSttbfBkmkFactoid;
378 
379     rExport.pFib->m_fcPlcfBkfFactoid = rStream.Tell();
380     for (const WW8_CP& rCP : m_aStartCPs)
381         rStream.WriteInt32(rCP);
382     rStream.WriteInt32(rExport.pFib->m_ccpText + rExport.pFib->m_ccpTxbx);
383 
384     // Write FBKFD.
385     for (size_t i = 0; i < m_aStartCPs.size(); ++i)
386     {
387         rStream.WriteInt16(i); // ibkl
388         rStream.WriteInt16(0); // bkc
389         rStream.WriteInt16(1); // cDepth, 1 as start and end is the same.
390     }
391 
392     rExport.pFib->m_lcbPlcfBkfFactoid = rStream.Tell() - rExport.pFib->m_fcPlcfBkfFactoid;
393 
394     rExport.pFib->m_fcPlcfBklFactoid = rStream.Tell();
395     for (const WW8_CP& rCP : m_aEndCPs)
396         rStream.WriteInt32(rCP);
397     rStream.WriteInt32(rExport.pFib->m_ccpText + rExport.pFib->m_ccpTxbx);
398 
399     // Write FBKLD.
400     for (size_t i = 0; i < m_aEndCPs.size(); ++i)
401     {
402         rStream.WriteInt16(i); // ibkf
403         rStream.WriteInt16(0); // cDepth, 0 as does not overlap with any other smart tag.
404     }
405     rExport.pFib->m_lcbPlcfBklFactoid = rStream.Tell() - rExport.pFib->m_fcPlcfBklFactoid;
406 
407     rExport.pFib->m_fcFactoidData = rStream.Tell();
408     // Write SmartTagData.
409     MSOFactoidType aFactoidType;
410     aFactoidType.m_nId = 1;
411     aFactoidType.m_aUri = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
412     aFactoidType.m_aTag = "RDF";
413     WW8SmartTagData aSmartTagData;
414     aSmartTagData.m_aPropBagStore.m_aFactoidTypes.push_back(aFactoidType);
415 
416     std::set<OUString> aSet;
417     for (const std::map<OUString, OUString>& rStatements : m_aStatements)
418     {
419         // Statements for a single text node.
420         for (const auto& rPair : rStatements)
421         {
422             aSet.insert(rPair.first);
423             aSet.insert(rPair.second);
424         }
425     }
426     aSmartTagData.m_aPropBagStore.m_aStringTable.assign(aSet.begin(), aSet.end());
427     for (const std::map<OUString, OUString>& rStatements : m_aStatements)
428     {
429         MSOPropertyBag aPropertyBag;
430         aPropertyBag.m_nId = 1;
431         for (const auto& rPair : rStatements)
432         {
433             MSOProperty aProperty;
434             aProperty.m_nKey = std::distance(aSet.begin(), aSet.find(rPair.first));
435             aProperty.m_nValue = std::distance(aSet.begin(), aSet.find(rPair.second));
436             aPropertyBag.m_aProperties.push_back(aProperty);
437         }
438         aSmartTagData.m_aPropBags.push_back(aPropertyBag);
439     }
440 
441     aSmartTagData.Write(rExport);
442     rExport.pFib->m_lcbFactoidData = rStream.Tell() - rExport.pFib->m_fcFactoidData;
443 }
444 
445 #define DEFAULT_STYLES_COUNT 16
446 
447 // Names of the storage streams
448 constexpr OUStringLiteral sMainStream  = u"WordDocument";
449 #define sCompObj "\1CompObj"
450 
WriteDop(WW8Export & rWrt)451 static void WriteDop( WW8Export& rWrt )
452 {
453     WW8Dop& rDop = *rWrt.pDop;
454 
455     // i#78951#, store the value of unknown compatibility options
456     rDop.SetCompatibilityOptions( rWrt.m_rDoc.getIDocumentSettingAccess().Getn32DummyCompatibilityOptions1());
457     rDop.SetCompatibilityOptions2( rWrt.m_rDoc.getIDocumentSettingAccess().Getn32DummyCompatibilityOptions2());
458 
459     rDop.fNoLeading = !rWrt.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::ADD_EXT_LEADING);
460     rDop.fUsePrinterMetrics = !rWrt.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::USE_VIRTUAL_DEVICE);
461 
462     // write default TabStop
463     const SvxTabStopItem& rTabStop =
464         DefaultItemGet<SvxTabStopItem>(rWrt.m_rDoc, RES_PARATR_TABSTOP);
465     rDop.dxaTab = o3tl::narrowing<sal_uInt16>(rTabStop[0].GetTabPos());
466 
467     // Zoom factor and type
468     SwViewShell *pViewShell(rWrt.m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell());
469     if (pViewShell)
470     {
471         switch ( pViewShell->GetViewOptions()->GetZoomType() )
472         {
473             case SvxZoomType::WHOLEPAGE: rDop.zkSaved = 1; break;
474             case SvxZoomType::PAGEWIDTH: rDop.zkSaved = 2; break;
475             case SvxZoomType::OPTIMAL:   rDop.zkSaved = 3; break;
476             default:                 rDop.zkSaved = 0;
477                 rDop.wScaleSaved = pViewShell->GetViewOptions()->GetZoom();
478                 break;
479         }
480     }
481 
482     // Values from the DocumentStatistics (are definitely needed
483     // for the DocStat fields)
484     rDop.fWCFootnoteEdn = true; // because they are included in StarWriter
485 
486     const SwDocStat& rDStat = rWrt.m_rDoc.getIDocumentStatistics().GetDocStat();
487     rDop.cWords = rDStat.nWord;
488     rDop.cCh = rDStat.nChar;
489     rDop.cPg = static_cast< sal_Int16 >(rDStat.nPage);
490     rDop.cParas = rDStat.nPara;
491     rDop.cLines = rDStat.nPara;
492 
493     SwDocShell *pDocShell(rWrt.m_rDoc.GetDocShell());
494     OSL_ENSURE(pDocShell, "no SwDocShell");
495     uno::Reference<document::XDocumentProperties> xDocProps;
496     uno::Reference<beans::XPropertySet> xProps;
497     if ( pDocShell )
498     {
499         uno::Reference<lang::XComponent> xModelComp = pDocShell->GetModel();
500         xProps.set(xModelComp, uno::UNO_QUERY);
501         uno::Reference<document::XDocumentPropertiesSupplier> xDPS(xModelComp, uno::UNO_QUERY_THROW);
502         xDocProps = xDPS->getDocumentProperties();
503         OSL_ENSURE(xDocProps.is(), "DocumentProperties is null");
504 
505         rDop.lKeyProtDoc = pDocShell->GetModifyPasswordHash();
506     }
507 
508     if ((rWrt.pSepx && rWrt.pSepx->DocumentIsProtected()) ||
509         rWrt.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_FORM ) ||
510         rDop.lKeyProtDoc != 0)
511     {
512         rDop.fProtEnabled =  true;
513         // The password was ignored at import if forms protection was enabled,
514         // so round-trip it since protection is still enabled.
515         if ( rDop.lKeyProtDoc == 0 && xProps.is() )
516         {
517             comphelper::SequenceAsHashMap aPropMap( xProps->getPropertyValue("InteropGrabBag"));
518             aPropMap.getValue("FormPasswordHash") >>= rDop.lKeyProtDoc;
519         }
520     }
521     else
522     {
523         rDop.fProtEnabled = false;
524     }
525 
526     if (rWrt.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::GUTTER_AT_TOP))
527     {
528         rDop.iGutterPos = true;
529     }
530 
531     if (!xDocProps.is())
532     {
533         rDop.dttmCreated = rDop.dttmRevised = rDop.dttmLastPrint = 0x45FBAC69;
534     }
535     else
536     {
537         ::util::DateTime uDT = xDocProps->getCreationDate();
538         rDop.dttmCreated = sw::ms::DateTime2DTTM(DateTime(uDT));
539         uDT = xDocProps->getModificationDate();
540         rDop.dttmRevised = sw::ms::DateTime2DTTM(DateTime(uDT));
541         uDT = xDocProps->getPrintDate();
542         rDop.dttmLastPrint = sw::ms::DateTime2DTTM(DateTime(uDT));
543     }
544 
545     // Also, the DocStat fields in headers, footers are not calculated correctly.
546     // ( we do not have this fields! )
547 
548     // and also for the Headers and Footers
549     rDop.cWordsFootnoteEnd   = rDStat.nWord;
550     rDop.cChFootnoteEdn      = rDStat.nChar;
551     rDop.cPgFootnoteEdn      = static_cast<sal_Int16>(rDStat.nPage);
552     rDop.cParasFootnoteEdn   = rDStat.nPara;
553     rDop.cLinesFootnoteEdn   = rDStat.nPara;
554 
555     rDop.fDontUseHTMLAutoSpacing = rWrt.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::PARA_SPACE_MAX);
556 
557     rDop.fExpShRtn = !rWrt.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK); // #i56856#
558 
559     rDop.Write( *rWrt.pTableStrm, *rWrt.pFib );
560 }
561 
lcl_CmpBeginEndChars(const OUString & rSWStr,const sal_Unicode * pMSStr,int nMSStrByteLen)562 static int lcl_CmpBeginEndChars( const OUString& rSWStr,
563     const sal_Unicode* pMSStr, int nMSStrByteLen )
564 {
565     nMSStrByteLen /= sizeof( sal_Unicode );
566     if( nMSStrByteLen > rSWStr.getLength() )
567         nMSStrByteLen = rSWStr.getLength()+1;
568     nMSStrByteLen *= sizeof( sal_Unicode );
569 
570     return memcmp( rSWStr.getStr(), pMSStr, nMSStrByteLen );
571 }
572 
573 /*
574 Converts the OOo Asian Typography into a best fit match for Microsoft
575 Asian typography. This structure is actually dumped to disk within the
576 Dop Writer. Assumption is that rTypo is cleared to 0 on entry
577 */
ExportDopTypography(WW8DopTypography & rTypo)578 void WW8Export::ExportDopTypography(WW8DopTypography &rTypo)
579 {
580     static const sal_Unicode aLangNotBegin[4][WW8DopTypography::nMaxFollowing]=
581     {
582         //Japanese Level 1
583         {
584             0x0021, 0x0025, 0x0029, 0x002c, 0x002e, 0x003a, 0x003b, 0x003f,
585             0x005d, 0x007d, 0x00a2, 0x00b0, 0x2019, 0x201d, 0x2030, 0x2032,
586             0x2033, 0x2103, 0x3001, 0x3002, 0x3005, 0x3009, 0x300b, 0x300d,
587             0x300f, 0x3011, 0x3015, 0x3041, 0x3043, 0x3045, 0x3047, 0x3049,
588             0x3063, 0x3083, 0x3085, 0x3087, 0x308e, 0x309b, 0x309c, 0x309d,
589             0x309e, 0x30a1, 0x30a3, 0x30a5, 0x30a7, 0x30a9, 0x30c3, 0x30e3,
590             0x30e5, 0x30e7, 0x30ee, 0x30f5, 0x30f6, 0x30fb, 0x30fc, 0x30fd,
591             0x30fe, 0xff01, 0xff05, 0xff09, 0xff0c, 0xff0e, 0xff1a, 0xff1b,
592             0xff1f, 0xff3d, 0xff5d, 0xff61, 0xff63, 0xff64, 0xff65, 0xff67,
593             0xff68, 0xff69, 0xff6a, 0xff6b, 0xff6c, 0xff6d, 0xff6e, 0xff6f,
594             0xff70, 0xff9e, 0xff9f, 0xffe0
595         },
596         //Simplified Chinese
597         {
598             0x0021, 0x0029, 0x002c, 0x002e, 0x003a, 0x003b, 0x003f, 0x005d,
599             0x007d, 0x00a8, 0x00b7, 0x02c7, 0x02c9, 0x2015, 0x2016, 0x2019,
600             0x201d, 0x2026, 0x2236, 0x3001, 0x3002, 0x3003, 0x3005, 0x3009,
601             0x300b, 0x300d, 0x300f, 0x3011, 0x3015, 0x3017, 0xff01, 0xff02,
602             0xff07, 0xff09, 0xff0c, 0xff0e, 0xff1a, 0xff1b, 0xff1f, 0xff3d,
603             0xff40, 0xff5c, 0xff5d, 0xff5e, 0xffe0
604         },
605         //Korean
606         {
607             0x0021, 0x0025, 0x0029, 0x002c, 0x002e, 0x003a, 0x003b, 0x003f,
608             0x005d, 0x007d, 0x00a2, 0x00b0, 0x2019, 0x201d, 0x2032, 0x2033,
609             0x2103, 0x3009, 0x300b, 0x300d, 0x300f, 0x3011, 0x3015, 0xff01,
610             0xff05, 0xff09, 0xff0c, 0xff0e, 0xff1a, 0xff1b, 0xff1f, 0xff3d,
611             0xff5d, 0xffe0
612         },
613         //Traditional Chinese
614         {
615             0x0021, 0x0029, 0x002c, 0x002e, 0x003a, 0x003b, 0x003f, 0x005d,
616             0x007d, 0x00a2, 0x00b7, 0x2013, 0x2014, 0x2019, 0x201d, 0x2022,
617             0x2025, 0x2026, 0x2027, 0x2032, 0x2574, 0x3001, 0x3002, 0x3009,
618             0x300b, 0x300d, 0x300f, 0x3011, 0x3015, 0x301e, 0xfe30, 0xfe31,
619             0xfe33, 0xfe34, 0xfe36, 0xfe38, 0xfe3a, 0xfe3c, 0xfe3e, 0xfe40,
620             0xfe42, 0xfe44, 0xfe4f, 0xfe50, 0xfe51, 0xfe52, 0xfe54, 0xfe55,
621             0xfe56, 0xfe57, 0xfe5a, 0xfe5c, 0xfe5e, 0xff01, 0xff09, 0xff0c,
622             0xff0e, 0xff1a, 0xff1b, 0xff1f, 0xff5c, 0xff5d, 0xff64
623         },
624     };
625 
626     static const sal_Unicode aLangNotEnd[4][WW8DopTypography::nMaxLeading] =
627     {
628         //Japanese Level 1
629         {
630             0x0024, 0x0028, 0x005b, 0x005c, 0x007b, 0x00a3, 0x00a5, 0x2018,
631             0x201c, 0x3008, 0x300a, 0x300c, 0x300e, 0x3010, 0x3014, 0xff04,
632             0xff08, 0xff3b, 0xff5b, 0xff62, 0xffe1, 0xffe5
633         },
634         //Simplified Chinese
635         {
636             0x0028, 0x005b, 0x007b, 0x00b7, 0x2018, 0x201c, 0x3008, 0x300a,
637             0x300c, 0x300e, 0x3010, 0x3014, 0x3016, 0xff08, 0xff0e, 0xff3b,
638             0xff5b, 0xffe1, 0xffe5
639         },
640         //Korean
641         {
642             0x0028, 0x005b, 0x005c, 0x007b, 0x00a3, 0x00a5, 0x2018, 0x201c,
643             0x3008, 0x300a, 0x300c, 0x300e, 0x3010, 0x3014, 0xff04, 0xff08,
644             0xff3b, 0xff5b, 0xffe6
645         },
646         //Traditional Chinese
647         {
648             0x0028, 0x005b, 0x007b, 0x00a3, 0x00a5, 0x2018, 0x201c, 0x2035,
649             0x3008, 0x300a, 0x300c, 0x300e, 0x3010, 0x3014, 0x301d, 0xfe35,
650             0xfe37, 0xfe39, 0xfe3b, 0xfe3d, 0xfe3f, 0xfe41, 0xfe43, 0xfe59,
651             0xfe5b, 0xfe5d, 0xff08, 0xff5b
652         },
653     };
654 
655     const i18n::ForbiddenCharacters *pForbidden = nullptr;
656     const i18n::ForbiddenCharacters *pUseMe = nullptr;
657     sal_uInt8 nUseReserved=0;
658     int nNoNeeded=0;
659     /*
660     Now we have some minor difficult issues, to wit...
661     a. MicroSoft Office can only store one set of begin and end characters in
662     a given document, not one per language.
663     b. StarOffice has only a concept of one set of begin and end characters for
664     a given language, i.e. not the two levels of kinsoku in japanese
665 
666     What is unknown as yet is if our default begin and end chars for
667     japanese, chinese tradition, chinese simplified and korean are different
668     in Word and Writer. I already suspect that they are different between
669     different version of word itself.
670 
671     So what have come up with is to simply see if any of the four languages
672     in OOo have been changed away from OUR defaults, and if one has then
673     export that. If more than one has in the future we may hack in something
674     which examines our document properties to see which language is used the
675     most and choose that, for now we choose the first and throw an ASSERT
676     */
677 
678     /*Our default Japanese Level is 2, this is a special MS hack to set this*/
679     rTypo.m_reserved2 = 1;
680 
681     for (rTypo.m_reserved1=8;rTypo.m_reserved1>0;rTypo.m_reserved1-=2)
682     {
683         pForbidden = m_rDoc.getIDocumentSettingAccess().getForbiddenCharacters(rTypo.GetConvertedLang(),
684             false);
685         if (nullptr != pForbidden)
686         {
687             int nIdx = (rTypo.m_reserved1-2)/2;
688             if( lcl_CmpBeginEndChars( pForbidden->endLine,
689                     aLangNotEnd[ nIdx ], sizeof(aLangNotEnd[ nIdx ]) ) ||
690                 lcl_CmpBeginEndChars( pForbidden->beginLine,
691                     aLangNotBegin[ nIdx ], sizeof(aLangNotBegin[ nIdx ]) ) )
692             {
693                 //One exception for Japanese, if it matches a level 1 we
694                 //can use one extra flag for that, rather than use a custom
695                 if (rTypo.GetConvertedLang() == LANGUAGE_JAPANESE)
696                 {
697                     if (
698                           !lcl_CmpBeginEndChars
699                             (
700                                 pForbidden->endLine,
701                                 OUString(WW8DopTypography::JapanNotEndLevel1).getStr(),
702                                 WW8DopTypography::nMaxLeading * sizeof(sal_Unicode)
703                             )
704                         &&
705                           !lcl_CmpBeginEndChars
706                             (
707                                 pForbidden->beginLine,
708                                 OUString(WW8DopTypography::JapanNotBeginLevel1).getStr(),
709                                 WW8DopTypography::nMaxFollowing * sizeof(sal_Unicode)
710                             )
711                         )
712                     {
713                         rTypo.m_reserved2 = 0;
714                         continue;
715                     }
716                 }
717 
718                 if (!pUseMe)
719                 {
720                     pUseMe = pForbidden;
721                     nUseReserved = rTypo.m_reserved1;
722                     rTypo.m_iLevelOfKinsoku = 2;
723                 }
724                 nNoNeeded++;
725             }
726         }
727     }
728 
729     OSL_ENSURE( nNoNeeded<=1, "Example of unexportable forbidden chars" );
730     rTypo.m_reserved1=nUseReserved;
731     if (rTypo.m_iLevelOfKinsoku && pUseMe)
732     {
733         rTypo.m_cchFollowingPunct = msword_cast<sal_Int16>
734             (pUseMe->beginLine.getLength());
735         if (rTypo.m_cchFollowingPunct > WW8DopTypography::nMaxFollowing - 1)
736             rTypo.m_cchFollowingPunct = WW8DopTypography::nMaxFollowing - 1;
737 
738         rTypo.m_cchLeadingPunct = msword_cast<sal_Int16>
739             (pUseMe->endLine.getLength());
740         if (rTypo.m_cchLeadingPunct > WW8DopTypography::nMaxLeading - 1)
741             rTypo.m_cchLeadingPunct = WW8DopTypography::nMaxLeading -1;
742 
743         memcpy(rTypo.m_rgxchFPunct,pUseMe->beginLine.getStr(),
744             (rTypo.m_cchFollowingPunct+1)*2);
745 
746         memcpy(rTypo.m_rgxchLPunct,pUseMe->endLine.getStr(),
747             (rTypo.m_cchLeadingPunct+1)*2);
748     }
749 
750     const IDocumentSettingAccess& rIDocumentSettingAccess = GetWriter().getIDocumentSettingAccess();
751 
752     rTypo.m_fKerningPunct = sal_uInt16(rIDocumentSettingAccess.get(DocumentSettingId::KERN_ASIAN_PUNCTUATION));
753     rTypo.m_iJustification = sal_uInt16(m_rDoc.getIDocumentSettingAccess().getCharacterCompressionType());
754 }
755 
756 // It can only be found something with this method, if it is used within
757 // WW8_SwAttrIter::OutAttr() and WW8Export::OutputItemSet()
HasItem(sal_uInt16 nWhich) const758 const SfxPoolItem* MSWordExportBase::HasItem( sal_uInt16 nWhich ) const
759 {
760     const SfxPoolItem* pItem=nullptr;
761     if (m_pISet)
762     {
763         // if write an EditEngine text, then the WhichIds are greater than
764         // our own Ids. So the Id have to translate from our into the
765         // EditEngine Range
766         nWhich = sw::hack::GetSetWhichFromSwDocWhich(*m_pISet, m_rDoc, nWhich);
767         if (nWhich && SfxItemState::SET != m_pISet->GetItemState(nWhich, true, &pItem))
768             pItem = nullptr;
769     }
770     else if( m_pChpIter )
771         pItem = m_pChpIter->HasTextItem( nWhich );
772     else
773     {
774         OSL_ENSURE( false, "Where is my ItemSet / pChpIter ?" );
775         pItem = nullptr;
776     }
777     return pItem;
778 }
779 
GetItem(sal_uInt16 nWhich) const780 const SfxPoolItem& MSWordExportBase::GetItem(sal_uInt16 nWhich) const
781 {
782     assert((m_pISet || m_pChpIter) && "Where is my ItemSet / pChpIter ?");
783     if (m_pISet)
784     {
785         // if write an EditEngine text, then the WhichIds are greater than
786         // our own Ids. So the Id have to translate from our into the
787         // EditEngine Range
788         nWhich = sw::hack::GetSetWhichFromSwDocWhich(*m_pISet, m_rDoc, nWhich);
789         OSL_ENSURE(nWhich != 0, "All broken, Impossible");
790         return m_pISet->Get(nWhich);
791     }
792     return m_pChpIter->GetItem( nWhich );
793 }
794 
WW8_WrPlc1(sal_uInt16 nStructSz)795 WW8_WrPlc1::WW8_WrPlc1( sal_uInt16 nStructSz )
796     : pData( new sal_uInt8[ 16 * nStructSz ] ),
797       nDataLen(16 * nStructSz),
798       nStructSiz( nStructSz )
799 {
800 }
801 
~WW8_WrPlc1()802 WW8_WrPlc1::~WW8_WrPlc1()
803 {
804 }
805 
Prev() const806 WW8_CP WW8_WrPlc1::Prev() const
807 {
808     bool b = !aPos.empty();
809     OSL_ENSURE(b,"Prev called on empty list");
810     return b ? aPos.back() : 0;
811 }
812 
Append(WW8_CP nCp,const void * pNewData)813 void WW8_WrPlc1::Append( WW8_CP nCp, const void* pNewData )
814 {
815     sal_uLong nInsPos = aPos.size() * nStructSiz;
816     aPos.push_back( nCp );
817     if( nDataLen < nInsPos + nStructSiz )
818     {
819         sal_uInt8* pNew = new sal_uInt8[ 2 * nDataLen ];
820         memcpy( pNew, pData.get(), nDataLen );
821         pData.reset(pNew);
822         nDataLen *= 2;
823     }
824     memcpy( pData.get() + nInsPos, pNewData, nStructSiz );
825 }
826 
Finish(sal_uLong nLastCp,sal_uLong nSttCp)827 void WW8_WrPlc1::Finish( sal_uLong nLastCp, sal_uLong nSttCp )
828 {
829     if( !aPos.empty() )
830     {
831         aPos.push_back( nLastCp );
832         if( nSttCp )
833             for(WW8_CP & rCp : aPos)
834                 rCp -= nSttCp;
835     }
836 }
837 
Write(SvStream & rStrm)838 void WW8_WrPlc1::Write( SvStream& rStrm )
839 {
840     decltype(aPos)::size_type i;
841     for( i = 0; i < aPos.size(); ++i )
842         SwWW8Writer::WriteLong( rStrm, aPos[i] );
843     if( i )
844         rStrm.WriteBytes(pData.get(), (i-1) * nStructSiz);
845 }
846 
847 // Class WW8_WrPlcField for fields
848 
Write(WW8Export & rWrt)849 void WW8_WrPlcField::Write( WW8Export& rWrt )
850 {
851     if( WW8_WrPlc1::Count() <= 1 )
852         return;
853 
854     WW8_FC *pfc;
855     sal_Int32 *plc;
856     switch (nTextTyp)
857     {
858         case TXT_MAINTEXT:
859             pfc = &rWrt.pFib->m_fcPlcffldMom;
860             plc = &rWrt.pFib->m_lcbPlcffldMom;
861             break;
862         case TXT_HDFT:
863             pfc = &rWrt.pFib->m_fcPlcffldHdr;
864             plc = &rWrt.pFib->m_lcbPlcffldHdr;
865             break;
866 
867         case TXT_FTN:
868             pfc = &rWrt.pFib->m_fcPlcffldFootnote;
869             plc = &rWrt.pFib->m_lcbPlcffldFootnote;
870             break;
871 
872         case TXT_EDN:
873             pfc = &rWrt.pFib->m_fcPlcffldEdn;
874             plc = &rWrt.pFib->m_lcbPlcffldEdn;
875             break;
876 
877         case TXT_ATN:
878             pfc = &rWrt.pFib->m_fcPlcffldAtn;
879             plc = &rWrt.pFib->m_lcbPlcffldAtn;
880             break;
881 
882         case TXT_TXTBOX:
883             pfc = &rWrt.pFib->m_fcPlcffldTxbx;
884             plc = &rWrt.pFib->m_lcbPlcffldTxbx;
885             break;
886 
887         case TXT_HFTXTBOX:
888             pfc = &rWrt.pFib->m_fcPlcffldHdrTxbx;
889             plc = &rWrt.pFib->m_lcbPlcffldHdrTxbx;
890             break;
891 
892         default:
893             pfc = plc = nullptr;
894             break;
895     }
896 
897     if( pfc && plc )
898     {
899         sal_uInt64 nFcStart = rWrt.pTableStrm->Tell();
900         WW8_WrPlc1::Write( *rWrt.pTableStrm );
901         *pfc = nFcStart;
902         *plc = rWrt.pTableStrm->Tell() - nFcStart;
903     }
904 }
905 
Write(WW8Export & rWrt)906 void WW8_WrMagicTable::Write( WW8Export& rWrt )
907 {
908     if( WW8_WrPlc1::Count() <= 1 )
909         return;
910     sal_uLong nFcStart = rWrt.pTableStrm->Tell();
911     WW8_WrPlc1::Write( *rWrt.pTableStrm );
912     rWrt.pFib->m_fcPlcfTch = nFcStart;
913     rWrt.pFib->m_lcbPlcfTch = rWrt.pTableStrm->Tell() - nFcStart;
914 }
915 
Append(WW8_CP nCp,sal_uLong nData)916 void WW8_WrMagicTable::Append( WW8_CP nCp, sal_uLong nData)
917 {
918     /*
919     Tell the undocumented table hack that everything between here and the last
920     table position is non-table text, don't do it if the previous position is
921     the same as this one, as that would be a region of 0 length
922     */
923     if ((!Count()) || (Prev() != nCp))
924     {
925         SVBT32 nLittle;
926         UInt32ToSVBT32(nData,nLittle);
927         WW8_WrPlc1::Append(nCp, nLittle);
928     }
929 }
930 
FillCount(SvStream & rStrm,sal_uLong nCount)931 void SwWW8Writer::FillCount( SvStream& rStrm, sal_uLong nCount )
932 {
933     static const sal_uInt32 aNulls[16] =
934     {
935         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 // 64 Byte
936     };
937 
938     while (nCount > 64)
939     {
940         rStrm.WriteBytes(aNulls, 64); // in steps of 64-Byte
941         nCount -= 64;
942     }
943     rStrm.WriteBytes(aNulls, nCount); // write the rest (0 .. 64 Bytes)
944 }
945 
FillUntil(SvStream & rStrm,sal_uLong nEndPos)946 sal_uLong SwWW8Writer::FillUntil( SvStream& rStrm, sal_uLong nEndPos )
947 {
948     sal_uInt64 nCurPos = rStrm.Tell();
949     if( !nEndPos )                          // nEndPos == 0 -> next Page
950         nEndPos = (nCurPos + 0x1ff) & ~0x1ffUL;
951 
952     if( nEndPos > nCurPos )
953         SwWW8Writer::FillCount( rStrm, nEndPos - nCurPos );
954 #if OSL_DEBUG_LEVEL > 0
955     else
956         OSL_ENSURE( nEndPos == nCurPos, "Wrong FillUntil()" );
957 #endif
958     return rStrm.Tell();
959 }
960 
WW8_WrPlcPn(WW8Export & rWr,ePLCFT ePl,WW8_FC nStartFc)961 WW8_WrPlcPn::WW8_WrPlcPn(WW8Export& rWr, ePLCFT ePl, WW8_FC nStartFc)
962     : rWrt(rWr)
963     , nFkpStartPage(0)
964     , ePlc(ePl)
965 {
966     m_Fkps.push_back(std::make_unique<WW8_WrFkp>(ePlc, nStartFc));
967 }
968 
~WW8_WrPlcPn()969 WW8_WrPlcPn::~WW8_WrPlcPn()
970 {
971 }
972 
CopyLastSprms(sal_uInt8 & rLen)973 sal_uInt8 *WW8_WrPlcPn::CopyLastSprms(sal_uInt8 &rLen)
974 {
975     WW8_WrFkp& rF = *m_Fkps.back();
976     return rF.CopyLastSprms(rLen);
977 }
978 
AppendFkpEntry(WW8_FC nEndFc,short nVarLen,const sal_uInt8 * pSprms)979 void WW8_WrPlcPn::AppendFkpEntry(WW8_FC nEndFc,short nVarLen,const sal_uInt8* pSprms)
980 {
981     WW8_WrFkp* pF = m_Fkps.back().get();
982 
983     // big sprm? build the sprmPHugePapx
984     sal_uInt8* pNewSprms = const_cast<sal_uInt8*>(pSprms);
985     sal_uInt8 aHugePapx[ 8 ];
986     if (PAP == ePlc && 488 <= nVarLen)
987     {
988         sal_uInt8* p = aHugePapx;
989         *p++ = *pSprms++;           // set style Id
990         *p++ = *pSprms++;
991         nVarLen -= 2;
992 
993         tools::Long nDataPos = rWrt.pDataStrm->Tell();
994         SwWW8Writer::WriteShort( *rWrt.pDataStrm, nVarLen );
995         rWrt.pDataStrm->WriteBytes(pSprms, nVarLen);
996 
997         Set_UInt16( p, 0x6646 );    // set SprmCode
998         Set_UInt32( p, nDataPos );  // set startpos (FC) in the datastream
999         nVarLen = static_cast< short >(p - aHugePapx);
1000         pSprms = pNewSprms = aHugePapx;
1001     }
1002     // if append at the same FC-EndPos and there are sprms, then get the old
1003     // sprms and erase it; they will append now with the new sprms
1004     else if( nVarLen && pF->IsEqualPos( nEndFc ))
1005         pF->MergeToNew( nVarLen, pNewSprms );
1006     // has the prev EndFC an empty sprm and the current is empty too, then
1007     // expand only the old EndFc to the new EndFc
1008     else if( !nVarLen && pF->IsEmptySprm() )
1009     {
1010         pF->SetNewEnd( nEndFc );
1011         return ;
1012     }
1013 
1014     bool bOk = pF->Append(nEndFc, nVarLen, pNewSprms);
1015     if( !bOk )
1016     {
1017         pF->Combine();
1018         pF = new WW8_WrFkp(ePlc, pF->GetEndFc()); // Start new Fkp == end of old Fkp
1019 
1020         m_Fkps.push_back(std::unique_ptr<WW8_WrFkp>(pF));
1021         if( !pF->Append( nEndFc, nVarLen, pNewSprms ) )
1022         {
1023             OSL_ENSURE( false, "Unable to insert Sprm" );
1024         }
1025     }
1026     if( pNewSprms != pSprms )   //Merge to new has created a new block
1027         delete[] pNewSprms;
1028 }
1029 
WriteFkps()1030 void WW8_WrPlcPn::WriteFkps()
1031 {
1032     nFkpStartPage = o3tl::narrowing<sal_uInt16>( SwWW8Writer::FillUntil( rWrt.Strm() ) >> 9 );
1033 
1034     for(const std::unique_ptr<WW8_WrFkp> & rp : m_Fkps)
1035     {
1036         rp->Write( rWrt.Strm(), *rWrt.m_pGrf );
1037     }
1038 
1039     if( CHP == ePlc )
1040     {
1041         rWrt.pFib->m_pnChpFirst = nFkpStartPage;
1042         rWrt.pFib->m_cpnBteChp = m_Fkps.size();
1043     }
1044     else
1045     {
1046         rWrt.pFib->m_pnPapFirst = nFkpStartPage;
1047         rWrt.pFib->m_cpnBtePap = m_Fkps.size();
1048     }
1049 }
1050 
WritePlc()1051 void WW8_WrPlcPn::WritePlc()
1052 {
1053     sal_uInt64 nFcStart = rWrt.pTableStrm->Tell();
1054     decltype(m_Fkps)::size_type i;
1055 
1056     for (i = 0; i < m_Fkps.size(); ++i)
1057     {
1058         SwWW8Writer::WriteLong( *rWrt.pTableStrm,
1059                                 m_Fkps[ i ]->GetStartFc() );
1060     }
1061 
1062     SwWW8Writer::WriteLong( *rWrt.pTableStrm,
1063                                 m_Fkps[ i - 1 ]->GetEndFc() );
1064 
1065     // for every FKP output the page
1066     for (i = 0; i < m_Fkps.size(); ++i)
1067     {
1068         SwWW8Writer::WriteLong( *rWrt.pTableStrm, i + nFkpStartPage );
1069     }
1070 
1071     if( CHP == ePlc )
1072     {
1073         rWrt.pFib->m_fcPlcfbteChpx = nFcStart;
1074         rWrt.pFib->m_lcbPlcfbteChpx = rWrt.pTableStrm->Tell() - nFcStart;
1075     }
1076     else
1077     {
1078         rWrt.pFib->m_fcPlcfbtePapx = nFcStart;
1079         rWrt.pFib->m_lcbPlcfbtePapx = rWrt.pTableStrm->Tell() - nFcStart;
1080     }
1081 }
1082 
WW8_WrFkp(ePLCFT ePl,WW8_FC nStartFc)1083 WW8_WrFkp::WW8_WrFkp(ePLCFT ePl, WW8_FC nStartFc)
1084     : ePlc(ePl), nStartGrp(511), nOldStartGrp(511),
1085     nItemSize( ( CHP == ePl ) ? 1 : 13 ),
1086     nIMax(0), nOldVarLen(0), bCombined(false)
1087 {
1088     pFkp = reinterpret_cast<sal_uInt8*>(new sal_Int32[128]);           // 512 Byte
1089     pOfs = reinterpret_cast<sal_uInt8*>(new sal_Int32[128]);           // 512 Byte
1090     memset( pFkp, 0, 4 * 128 );
1091     memset( pOfs, 0, 4 * 128 );
1092     reinterpret_cast<sal_Int32*>(pFkp)[0] = nStartFc;         // 0th entry FC at nStartFc
1093 }
1094 
~WW8_WrFkp()1095 WW8_WrFkp::~WW8_WrFkp()
1096 {
1097     delete[] reinterpret_cast<sal_Int32 *>(pFkp);
1098     delete[] reinterpret_cast<sal_Int32 *>(pOfs);
1099 }
1100 
SearchSameSprm(sal_uInt16 nVarLen,const sal_uInt8 * pSprms)1101 sal_uInt8 WW8_WrFkp::SearchSameSprm( sal_uInt16 nVarLen, const sal_uInt8* pSprms )
1102 {
1103     if( 3 < nVarLen )
1104     {
1105         // if the sprms contained picture-references then never equal!
1106         for( sal_uInt8 n = static_cast< sal_uInt8 >(nVarLen - 1); 3 < n; --n )
1107             if( pSprms[ n ] == GRF_MAGIC_3 &&
1108                 pSprms[ n-1 ] == GRF_MAGIC_2 &&
1109                 pSprms[ n-2 ] == GRF_MAGIC_1 )
1110                     return 0;
1111     }
1112 
1113     short i;
1114     for( i = 0; i < nIMax; i++ )
1115     {
1116         sal_uInt8 nStart = pOfs[i * nItemSize];
1117         if( nStart )
1118         {                               // has Sprms
1119             const sal_uInt8* p = pFkp + ( o3tl::narrowing<sal_uInt16>(nStart) << 1 );
1120             if( ( CHP == ePlc
1121                     ? (*p++ == nVarLen)
1122                     : ((o3tl::narrowing<sal_uInt16>(*p++) << 1 ) == (( nVarLen+1) & 0xfffe)) )
1123                 && !memcmp( p, pSprms, nVarLen ) )
1124                     return nStart;                      // found it
1125         }
1126     }
1127     return 0;           // didn't found it
1128 }
1129 
CopyLastSprms(sal_uInt8 & rLen)1130 sal_uInt8 *WW8_WrFkp::CopyLastSprms(sal_uInt8 &rLen)
1131 {
1132     rLen=0;
1133     sal_uInt8 *pStart=nullptr,*pRet=nullptr;
1134 
1135     if (!bCombined)
1136         pStart = pOfs;
1137     else
1138         pStart = pFkp + ( nIMax + 1 ) * 4;
1139 
1140     sal_uInt8 nStart = *(pStart + (nIMax-1) * nItemSize);
1141 
1142     const sal_uInt8* p = pFkp + ( o3tl::narrowing<sal_uInt16>(nStart) << 1 );
1143 
1144     if (!*p)
1145         p++;
1146 
1147     if (*p)
1148     {
1149         rLen = *p++;
1150         if (PAP == ePlc)
1151             rLen *= 2;
1152         pRet = new sal_uInt8[rLen];
1153         memcpy(pRet,p,rLen);
1154     }
1155     return pRet;
1156 }
1157 
Append(WW8_FC nEndFc,sal_uInt16 nVarLen,const sal_uInt8 * pSprms)1158 bool WW8_WrFkp::Append( WW8_FC nEndFc, sal_uInt16 nVarLen, const sal_uInt8* pSprms )
1159 {
1160     assert((!nVarLen || pSprms) && "Item pointer missing");
1161 
1162     OSL_ENSURE( nVarLen < ( ( ePlc == PAP ) ? 497U : 502U ), "Sprms too long !" );
1163 
1164     if( bCombined )
1165     {
1166         OSL_ENSURE( false, "Fkp::Append: Fkp is already combined" );
1167         return false;
1168     }
1169     sal_Int32 n = reinterpret_cast<sal_Int32*>(pFkp)[nIMax];        // last entry
1170     if( nEndFc <= n )
1171     {
1172         OSL_ENSURE( nEndFc >= n, "+Fkp: FC backwards" );
1173         OSL_ENSURE( !nVarLen || !pSprms || nEndFc != n,
1174                                     "+Fkp: used same FC multiple times" );
1175                         // same FC without Sprm is ignored without grumbling
1176 
1177         return true;    // ignore (do not create a new Fkp)
1178     }
1179 
1180     sal_uInt8 nOldP = nVarLen ? SearchSameSprm( nVarLen, pSprms ) : 0;
1181                                             // Combine equal entries
1182     short nOffset=0, nPos = nStartGrp;
1183     if (nVarLen && !nOldP)
1184     {
1185         nPos = PAP == ePlc
1186                 ? ( 13 == nItemSize     // HACK: PAP and bWrtWW8 !!
1187                      ? (nStartGrp & 0xFFFE ) - nVarLen - 1
1188                      : (nStartGrp - (((nVarLen + 1) & 0xFFFE)+1)) & 0xFFFE )
1189                 : ((nStartGrp - nVarLen - 1) & 0xFFFE);
1190         if( nPos < 0 )
1191             return false;           // doesn't fit at all
1192         nOffset = nPos;             // save offset (can also be uneven!)
1193         nPos &= 0xFFFE;             // Pos for Sprms ( gerade Pos )
1194     }
1195 
1196     if( o3tl::make_unsigned(nPos) <= ( nIMax + 2U ) * 4U + ( nIMax + 1U ) * nItemSize )
1197                                             // does it fits after the CPs and offsets?
1198         return false;                       // no
1199 
1200     reinterpret_cast<sal_Int32*>(pFkp)[nIMax + 1] = nEndFc;     // insert FC
1201 
1202     nOldVarLen = static_cast<sal_uInt8>(nVarLen);
1203     if( nVarLen && !nOldP )
1204     {               // insert it for real
1205         nOldStartGrp = nStartGrp;
1206 
1207         nStartGrp = nPos;
1208         pOfs[nIMax * nItemSize] = static_cast<sal_uInt8>( nStartGrp >> 1 );
1209                                             // insert (start-of-data >> 1)
1210         sal_uInt8 nCnt = static_cast< sal_uInt8 >(CHP == ePlc
1211                         ? ( nVarLen < 256 ) ? static_cast<sal_uInt8>(nVarLen) : 255
1212                         : ( ( nVarLen + 1 ) >> 1 ));
1213 
1214         pFkp[ nOffset ] = nCnt;                     // Enter data length
1215         memcpy( pFkp + nOffset + 1, pSprms, nVarLen );  // store Sprms
1216     }
1217     else
1218     {
1219         // do not enter for real ( no Sprms or recurrence )
1220         // start-of-data 0 ( no data ) or recurrence
1221         pOfs[nIMax * nItemSize] = nOldP;
1222     }
1223     nIMax++;
1224     return true;
1225 }
1226 
Combine()1227 void WW8_WrFkp::Combine()
1228 {
1229     if( bCombined )
1230         return;
1231     if( nIMax )
1232         memcpy( pFkp + ( nIMax + 1 ) * 4, pOfs, nIMax * nItemSize );
1233     delete[] pOfs;
1234     pOfs = nullptr;
1235     pFkp[511] = nIMax;
1236     bCombined = true;
1237 
1238 #if defined OSL_BIGENDIAN           // only the FCs will be rotated here
1239     sal_uInt16 i;                   // the Sprms must be rotated elsewhere
1240 
1241     sal_uInt32* p;
1242     for( i = 0, p = (sal_uInt32*)pFkp; i <= nIMax; i++, p++ )
1243         *p = OSL_SWAPDWORD( *p );
1244 #endif // ifdef OSL_BIGENDIAN
1245 }
1246 
Write(SvStream & rStrm,SwWW8WrGrf & rGrf)1247 void WW8_WrFkp::Write( SvStream& rStrm, SwWW8WrGrf& rGrf )
1248 {
1249     Combine();                      // If not already combined
1250 
1251     sal_uInt8* p;               //  search magic for nPicLocFc
1252     sal_uInt8* pEnd = pFkp + nStartGrp;
1253     for( p = pFkp + 511 - 4; p >= pEnd; p-- )
1254     {
1255         if( *p != GRF_MAGIC_1 )     // search for signature 0x12 0x34 0x56 0xXX
1256             continue;
1257         if( *(p+1) != GRF_MAGIC_2 )
1258             continue;
1259         if( *(p+2) != GRF_MAGIC_3 )
1260             continue;
1261 
1262         SVBT32 nPos;                // signature found
1263         UInt32ToSVBT32( rGrf.GetFPos(), nPos );   // FilePos the graphics
1264         memcpy( p, nPos, 4 );       // patch FilePos over the signature
1265     }
1266     rStrm.WriteBytes(pFkp, 512);
1267 }
1268 
MergeToNew(short & rVarLen,sal_uInt8 * & rpNewSprms)1269 void WW8_WrFkp::MergeToNew( short& rVarLen, sal_uInt8 *& rpNewSprms )
1270 {
1271     sal_uInt8 nStart = pOfs[ (nIMax-1) * nItemSize ];
1272     if( !nStart )
1273         return;
1274 
1275 // has Sprms
1276     sal_uInt8* p = pFkp + ( o3tl::narrowing<sal_uInt16>(nStart) << 1 );
1277 
1278     // old and new equal? Then copy only one into the new sprms
1279     if( nOldVarLen == rVarLen && !memcmp( p+1, rpNewSprms, nOldVarLen ))
1280     {
1281         sal_uInt8* pNew = new sal_uInt8[ nOldVarLen ];
1282         memcpy( pNew, p+1, nOldVarLen );
1283         rpNewSprms = pNew;
1284     }
1285     else
1286     {
1287         sal_uInt8* pNew = new sal_uInt8[ nOldVarLen + rVarLen ];
1288         memcpy( pNew, p+1, nOldVarLen );
1289         memcpy( pNew + nOldVarLen, rpNewSprms, rVarLen );
1290 
1291         rpNewSprms = pNew;
1292         rVarLen = rVarLen + nOldVarLen;
1293     }
1294     --nIMax;
1295     // if this Sprms don't used from others, remove it
1296     bool bFnd = false;
1297     for (sal_uInt16 n = 0; n < nIMax; ++n)
1298     {
1299         if (nStart == pOfs[n * nItemSize])
1300         {
1301             bFnd = true;
1302             break;
1303         }
1304     }
1305     if (!bFnd)
1306     {
1307         nStartGrp = nOldStartGrp;
1308         memset( p, 0, nOldVarLen+1 );
1309     }
1310 }
1311 
GetStartFc() const1312 WW8_FC WW8_WrFkp::GetStartFc() const
1313 {
1314     // when bCombined, then the array beginning with pFkp is already byte-swapped
1315     // to LittleEndian, so to extract the start and end positions they must
1316     // be swapped back.
1317     if( bCombined )
1318         return SVBT32ToUInt32( pFkp );        // 0. Element
1319     return reinterpret_cast<sal_Int32*>(pFkp)[0];
1320 }
1321 
GetEndFc() const1322 WW8_FC WW8_WrFkp::GetEndFc() const
1323 {
1324     if( bCombined )
1325         return SVBT32ToUInt32( &(pFkp[nIMax*4]) );    // nIMax-th SVBT32-Element
1326     return reinterpret_cast<sal_Int32*>(pFkp)[nIMax];
1327 }
1328 
1329 // Method for managing the piece table
WW8_WrPct(WW8_FC nfcMin)1330 WW8_WrPct::WW8_WrPct(WW8_FC nfcMin)
1331     : nOldFc(nfcMin)
1332 {
1333     AppendPc(nOldFc);
1334 }
1335 
~WW8_WrPct()1336 WW8_WrPct::~WW8_WrPct()
1337 {
1338 }
1339 
1340 // Fill the piece and create a new one
AppendPc(WW8_FC nStartFc)1341 void WW8_WrPct::AppendPc(WW8_FC nStartFc)
1342 {
1343     WW8_CP nStartCp = nStartFc - nOldFc;    // subtract the beginning of the text
1344     if ( !nStartCp && !m_Pcts.empty())
1345     {
1346         OSL_ENSURE(1 == m_Pcts.size(), "empty Piece!");
1347         m_Pcts.pop_back();
1348     }
1349 
1350     nOldFc = nStartFc;                      // remember StartFc as old
1351 
1352     nStartCp >>= 1;     // for Unicode: number of characters / 2
1353 
1354     if (!m_Pcts.empty())
1355     {
1356         nStartCp += m_Pcts.back()->GetStartCp();
1357     }
1358 
1359     m_Pcts.push_back(std::make_unique<WW8_WrPc>(nStartFc, nStartCp));
1360 }
1361 
WritePc(WW8Export & rWrt)1362 void WW8_WrPct::WritePc( WW8Export& rWrt )
1363 {
1364     sal_uInt64 nPctStart;
1365     sal_uLong nOldPos, nEndPos;
1366 
1367     nPctStart = rWrt.pTableStrm->Tell();                    // Start piece table
1368     rWrt.pTableStrm->WriteChar( char(0x02) );                       // Status byte PCT
1369     nOldPos = nPctStart + 1;                                // remember Position
1370     SwWW8Writer::WriteLong( *rWrt.pTableStrm, 0 );          // then the length
1371 
1372     for (auto const& it : m_Pcts) // ranges
1373     {
1374         SwWW8Writer::WriteLong( *rWrt.pTableStrm, it->GetStartCp() );
1375     }
1376 
1377     // calculate the last Pos
1378     sal_uLong nStartCp = rWrt.pFib->m_fcMac - nOldFc;
1379     nStartCp >>= 1;             // For Unicode: number of characters / 2
1380     nStartCp += m_Pcts.back()->GetStartCp();
1381     SwWW8Writer::WriteLong( *rWrt.pTableStrm, nStartCp );
1382 
1383     // piece references
1384     for (auto const& it : m_Pcts)
1385     {
1386         SwWW8Writer::WriteShort(*rWrt.pTableStrm, it->GetStatus());
1387         SwWW8Writer::WriteLong(*rWrt.pTableStrm, it->GetStartFc());
1388         SwWW8Writer::WriteShort( *rWrt.pTableStrm, 0);          // PRM=0
1389     }
1390 
1391     // entries in the FIB
1392     rWrt.pFib->m_fcClx = nPctStart;
1393     nEndPos = rWrt.pTableStrm->Tell();
1394     rWrt.pFib->m_lcbClx = nEndPos - nPctStart;
1395 
1396     // and register the length as well
1397     SwWW8Writer::WriteLong( *rWrt.pTableStrm, nOldPos,
1398                             nEndPos - nPctStart-5 );
1399 
1400 }
1401 
SetParaBreak()1402 void WW8_WrPct::SetParaBreak()
1403 {
1404     OSL_ENSURE( !m_Pcts.empty(), "SetParaBreak : m_Pcts.empty()" );
1405     m_Pcts.back()->SetStatus();
1406 }
1407 
Fc2Cp(sal_uLong nFc) const1408 WW8_CP WW8_WrPct::Fc2Cp( sal_uLong nFc ) const
1409 {
1410     OSL_ENSURE( nFc >= o3tl::make_unsigned(nOldFc), "FilePos lies in front of last piece" );
1411     OSL_ENSURE( ! m_Pcts.empty(), "Fc2Cp no piece available" );
1412 
1413     nFc -= nOldFc;
1414     nFc /= 2; // Unicode
1415     return nFc + m_Pcts.back()->GetStartCp();
1416 }
1417 
AppendBookmarks(const SwTextNode & rNd,sal_Int32 nCurrentPos,sal_Int32 nLen)1418 void WW8Export::AppendBookmarks( const SwTextNode& rNd, sal_Int32 nCurrentPos, sal_Int32 nLen )
1419 {
1420     std::vector< const ::sw::mark::IMark* > aArr;
1421     sal_uInt16 nContent;
1422     const sal_Int32 nCurrentEnd = nCurrentPos + nLen;
1423     if( !GetWriter().GetBookmarks( rNd, nCurrentPos, nCurrentEnd, aArr ))
1424         return;
1425 
1426     sal_uLong nNd = rNd.GetIndex(), nSttCP = Fc2Cp( Strm().Tell() );
1427     for(const ::sw::mark::IMark* p : aArr)
1428     {
1429         const ::sw::mark::IMark& rBkmk = *p;
1430         if(dynamic_cast< const ::sw::mark::IFieldmark *>(&rBkmk))
1431             continue;
1432 
1433         const SwPosition* pPos = &rBkmk.GetMarkPos();
1434         const SwPosition* pOPos = nullptr;
1435         if(rBkmk.IsExpanded())
1436             pOPos = &rBkmk.GetOtherMarkPos();
1437         if( pOPos && pOPos->nNode == pPos->nNode &&
1438             pOPos->nContent < pPos->nContent )
1439         {
1440             pPos = pOPos;
1441             pOPos = &rBkmk.GetMarkPos();
1442         }
1443 
1444         if( !pOPos || ( nNd == pPos->nNode.GetIndex() &&
1445             ( nContent = pPos->nContent.GetIndex() ) >= nCurrentPos &&
1446             nContent < nCurrentEnd ) )
1447         {
1448             sal_uLong nCp = nSttCP + pPos->nContent.GetIndex() - nCurrentPos;
1449             m_pBkmks->Append(nCp, BookmarkToWord(rBkmk.GetName()));
1450         }
1451         if( pOPos && nNd == pOPos->nNode.GetIndex() &&
1452             ( nContent = pOPos->nContent.GetIndex() ) >= nCurrentPos &&
1453             nContent < nCurrentEnd )
1454         {
1455             sal_uLong nCp = nSttCP + pOPos->nContent.GetIndex() - nCurrentPos;
1456             m_pBkmks->Append(nCp, BookmarkToWord(rBkmk.GetName()));
1457         }
1458     }
1459 }
1460 
AppendAnnotationMarks(const SwWW8AttrIter & rAttrs,sal_Int32 nCurrentPos,sal_Int32 nLen)1461 void WW8Export::AppendAnnotationMarks(const SwWW8AttrIter& rAttrs, sal_Int32 nCurrentPos, sal_Int32 nLen)
1462 {
1463     IMarkVector aMarks;
1464     if (GetAnnotationMarks(rAttrs, nCurrentPos, nCurrentPos + nLen, aMarks))
1465     {
1466         for (const sw::mark::IMark* pMark : aMarks)
1467         {
1468             const sal_Int32 nStart = pMark->GetMarkStart().nContent.GetIndex();
1469             if (nStart == nCurrentPos)
1470             {
1471                 m_pAtn->AddRangeStartPosition(pMark->GetName(), Fc2Cp(Strm().Tell()),
1472                                               !rAttrs.HasFlysAt(nCurrentPos));
1473             }
1474         }
1475     }
1476 }
1477 
AppendSmartTags(SwTextNode & rTextNode)1478 void WW8Export::AppendSmartTags(SwTextNode& rTextNode)
1479 {
1480     std::map<OUString, OUString> aStatements = SwRDFHelper::getTextNodeStatements("urn:bails", rTextNode);
1481     if (!aStatements.empty())
1482     {
1483         WW8_CP nCP = Fc2Cp(Strm().Tell());
1484         m_pFactoids->Append(nCP, nCP, aStatements);
1485     }
1486 }
1487 
MoveFieldMarks(WW8_CP nFrom,WW8_CP nTo)1488 void WW8Export::MoveFieldMarks(WW8_CP nFrom, WW8_CP nTo)
1489 {
1490     m_pBkmks->MoveFieldMarks(nFrom, nTo);
1491 }
1492 
AppendBookmark(const OUString & rName)1493 void WW8Export::AppendBookmark( const OUString& rName )
1494 {
1495     sal_uLong nSttCP = Fc2Cp( Strm().Tell() );
1496     m_pBkmks->Append( nSttCP, rName );
1497 }
1498 
AppendBookmarkEndWithCorrection(const OUString & rName)1499 void WW8Export::AppendBookmarkEndWithCorrection( const OUString& rName )
1500 {
1501     sal_uLong nEndCP = Fc2Cp( Strm().Tell() );
1502     m_pBkmks->Append( nEndCP - 1, rName );
1503 }
1504 
getBackground()1505 std::unique_ptr<SvxBrushItem> MSWordExportBase::getBackground()
1506 {
1507     const SwFrameFormat &rFormat = m_rDoc.GetPageDesc(0).GetMaster();
1508     std::unique_ptr<SvxBrushItem> aBrush = std::make_unique<SvxBrushItem>(RES_BACKGROUND);
1509     SfxItemState eState = rFormat.GetBackgroundState(aBrush);
1510 
1511     if (SfxItemState::SET == eState)
1512     {
1513         // The 'color' is set for the first page style - take it and use it as the background color of the entire DOCX
1514         if (aBrush->GetColor() != COL_AUTO)
1515             return aBrush;
1516     }
1517     return nullptr;
1518 }
1519 
1520 // #i120928 collect all the graphics of bullets applied to paragraphs
CollectGrfsOfBullets()1521 int MSWordExportBase::CollectGrfsOfBullets()
1522 {
1523     m_vecBulletPic.clear();
1524 
1525     size_t nCountRule = m_rDoc.GetNumRuleTable().size();
1526     for (size_t n = 0; n < nCountRule; ++n)
1527     {
1528         const SwNumRule &rRule = *( m_rDoc.GetNumRuleTable().at(n) );
1529         sal_uInt16 nLevels = rRule.IsContinusNum() ? 1 : 9;
1530         for (sal_uInt16 nLvl = 0; nLvl < nLevels; ++nLvl)
1531         {
1532             const SwNumFormat &rFormat = rRule.Get(nLvl);
1533             if (SVX_NUM_BITMAP != rFormat.GetNumberingType())
1534             {
1535                 continue;
1536             }
1537             const Graphic *pGraf = rFormat.GetBrush()? rFormat.GetBrush()->GetGraphic():nullptr;
1538             if ( pGraf )
1539             {
1540                 bool bHas = false;
1541                 for (const Graphic* p : m_vecBulletPic)
1542                 {
1543                     if (p->GetChecksum() == pGraf->GetChecksum())
1544                     {
1545                         bHas = true;
1546                         break;
1547                     }
1548                 }
1549                 if (!bHas)
1550                 {
1551                     Size aSize(pGraf->GetPrefSize());
1552                     if (0 != aSize.Height() && 0 != aSize.Width())
1553                        m_vecBulletPic.push_back(pGraf);
1554                 }
1555             }
1556         }
1557     }
1558 
1559     return m_vecBulletPic.size();
1560 }
1561 
BulletDefinitions()1562 void MSWordExportBase::BulletDefinitions()
1563 {
1564     for (size_t i = 0; i < m_vecBulletPic.size(); ++i)
1565     {
1566         const MapMode aMapMode(MapUnit::MapTwip);
1567         const Graphic& rGraphic = *m_vecBulletPic[i];
1568         Size aSize(rGraphic.GetPrefSize());
1569         if (MapUnit::MapPixel == rGraphic.GetPrefMapMode().GetMapUnit())
1570             aSize = Application::GetDefaultDevice()->PixelToLogic(aSize, aMapMode);
1571         else
1572             aSize = OutputDevice::LogicToLogic(aSize,rGraphic.GetPrefMapMode(), aMapMode);
1573 
1574         if (0 != aSize.Height() && 0 != aSize.Width())
1575             AttrOutput().BulletDefinition(i, rGraphic, aSize);
1576     }
1577 }
1578 
1579 //Export Graphic of Bullets
ExportGrfBullet(const SwTextNode & rNd)1580 void WW8Export::ExportGrfBullet(const SwTextNode& rNd)
1581 {
1582     int nCount = CollectGrfsOfBullets();
1583     if (nCount > 0)
1584     {
1585         SwPosition aPos(rNd);
1586         OUString aPicBullets("_PictureBullets");
1587         AppendBookmark(aPicBullets);
1588         for (int i = 0; i < nCount; i++)
1589         {
1590             ww8::Frame aFrame(*(m_vecBulletPic[i]), aPos);
1591             OutGrfBullets(aFrame);
1592         }
1593         AppendBookmark(aPicBullets);
1594     }
1595 }
1596 
1597 static sal_uInt8 nAttrMagicIdx = 0;
OutGrfBullets(const ww8::Frame & rFrame)1598 void WW8Export::OutGrfBullets(const ww8::Frame & rFrame)
1599 {
1600     if ( !m_pGrf || !m_pChpPlc || !pO )
1601         return;
1602 
1603     m_pGrf->Insert(rFrame);
1604     m_pChpPlc->AppendFkpEntry( Strm().Tell(), pO->size(), pO->data() );
1605     pO->clear();
1606     // if links...
1607     WriteChar( char(1) );
1608 
1609     sal_uInt8 aArr[ 22 ];
1610     sal_uInt8* pArr = aArr;
1611 
1612     // sprmCFSpec
1613     Set_UInt16( pArr, 0x855 );
1614     Set_UInt8( pArr, 1 );
1615 
1616     Set_UInt16( pArr, 0x083c );
1617     Set_UInt8( pArr, 0x81 );
1618 
1619     // sprmCPicLocation
1620     Set_UInt16( pArr, 0x6a03 );
1621     Set_UInt32( pArr, GRF_MAGIC_321 );
1622 
1623     //extern  nAttrMagicIdx;
1624     --pArr;
1625     Set_UInt8( pArr, nAttrMagicIdx++ );
1626     m_pChpPlc->AppendFkpEntry( Strm().Tell(), static_cast< short >(pArr - aArr), aArr );
1627 }
1628 
GetGrfIndex(const SvxBrushItem & rBrush)1629 int MSWordExportBase::GetGrfIndex(const SvxBrushItem& rBrush)
1630 {
1631     int nIndex = -1;
1632 
1633     const Graphic* pGraphic = rBrush.GetGraphic();
1634     if (pGraphic)
1635     {
1636         for (size_t i = 0; i < m_vecBulletPic.size(); ++i)
1637         {
1638             if (m_vecBulletPic[i]->GetChecksum() == pGraphic->GetChecksum())
1639             {
1640                 nIndex = i;
1641                 break;
1642             }
1643         }
1644     }
1645 
1646     return nIndex;
1647 }
1648 
Write(Writer & rWrt)1649 void WW8_WrtRedlineAuthor::Write( Writer& rWrt )
1650 {
1651     WW8Export & rWW8Wrt = *(static_cast<SwWW8Writer&>(rWrt).m_pExport);
1652     rWW8Wrt.WriteAsStringTable(maAuthors, rWW8Wrt.pFib->m_fcSttbfRMark,
1653         rWW8Wrt.pFib->m_lcbSttbfRMark);
1654 }
1655 
AddRedlineAuthor(std::size_t nId)1656 sal_uInt16 WW8Export::AddRedlineAuthor( std::size_t nId )
1657 {
1658     if( !m_pRedlAuthors )
1659     {
1660         m_pRedlAuthors = new WW8_WrtRedlineAuthor;
1661         m_pRedlAuthors->AddName("Unknown");
1662     }
1663     return m_pRedlAuthors->AddName( SW_MOD()->GetRedlineAuthor( nId ) );
1664 }
1665 
WriteAsStringTable(const std::vector<OUString> & rStrings,sal_Int32 & rfcSttbf,sal_Int32 & rlcbSttbf)1666 void WW8Export::WriteAsStringTable(const std::vector<OUString>& rStrings,
1667     sal_Int32& rfcSttbf, sal_Int32& rlcbSttbf)
1668 {
1669     sal_uInt16 n, nCount = static_cast< sal_uInt16 >(rStrings.size());
1670     if( !nCount )
1671         return;
1672 
1673     // we have some Redlines found in the document -> the
1674     // Author Name Stringtable
1675     SvStream& rStrm = *pTableStrm;
1676     rfcSttbf = rStrm.Tell();
1677     SwWW8Writer::WriteShort( rStrm, -1 );
1678     SwWW8Writer::WriteLong( rStrm, nCount );
1679     for( n = 0; n < nCount; ++n )
1680     {
1681         const OUString& rNm = rStrings[n];
1682         SwWW8Writer::WriteShort( rStrm, rNm.getLength() );
1683         SwWW8Writer::WriteString16(rStrm, rNm, false);
1684     }
1685     rlcbSttbf = rStrm.Tell() - rfcSttbf;
1686 }
1687 
1688 // WriteShort() sets at FilePos nPos the value nVal and seeks to the old
1689 // FilePos. Used to insert lengths after the fact.
WriteShort(SvStream & rStrm,sal_uLong nPos,sal_Int16 nVal)1690 void SwWW8Writer::WriteShort( SvStream& rStrm, sal_uLong nPos, sal_Int16 nVal )
1691 {
1692     sal_uInt64 nOldPos = rStrm.Tell();       // remember Pos
1693     rStrm.Seek( nPos );
1694     SwWW8Writer::WriteShort( rStrm, nVal );
1695     rStrm.Seek( nOldPos );
1696 }
1697 
WriteLong(SvStream & rStrm,sal_uLong nPos,sal_Int32 nVal)1698 void SwWW8Writer::WriteLong( SvStream& rStrm, sal_uLong nPos, sal_Int32 nVal )
1699 {
1700     sal_uInt64 nOldPos = rStrm.Tell();       // remember Pos
1701     rStrm.Seek( nPos );
1702     SwWW8Writer::WriteLong( rStrm, nVal );
1703     rStrm.Seek( nOldPos );
1704 }
1705 
InsUInt16(ww::bytes & rO,sal_uInt16 n)1706 void SwWW8Writer::InsUInt16(ww::bytes &rO, sal_uInt16 n)
1707 {
1708     SVBT16 nL;
1709     ShortToSVBT16( n, nL );
1710     rO.push_back(nL[0]);
1711     rO.push_back(nL[1]);
1712 }
1713 
InsUInt32(ww::bytes & rO,sal_uInt32 n)1714 void SwWW8Writer::InsUInt32(ww::bytes &rO, sal_uInt32 n)
1715 {
1716     SVBT32 nL;
1717     UInt32ToSVBT32( n, nL );
1718     rO.push_back(nL[0]);
1719     rO.push_back(nL[1]);
1720     rO.push_back(nL[2]);
1721     rO.push_back(nL[3]);
1722 }
1723 
InsAsString16(ww::bytes & rO,const OUString & rStr)1724 void SwWW8Writer::InsAsString16(ww::bytes &rO, const OUString& rStr)
1725 {
1726     const sal_Unicode* pStr = rStr.getStr();
1727     for (sal_Int32 n = 0, nLen = rStr.getLength(); n < nLen; ++n, ++pStr)
1728         SwWW8Writer::InsUInt16( rO, *pStr );
1729 }
1730 
InsAsString8(ww::bytes & rO,std::u16string_view rStr,rtl_TextEncoding eCodeSet)1731 void SwWW8Writer::InsAsString8(ww::bytes &rO, std::u16string_view rStr,
1732         rtl_TextEncoding eCodeSet)
1733 {
1734     OString sTmp(OUStringToOString(rStr, eCodeSet));
1735     const char *pStart = sTmp.getStr();
1736     const char *pEnd = pStart + sTmp.getLength();
1737 
1738     rO.insert( rO.end(), pStart, pEnd );
1739 }
1740 
WriteString16(SvStream & rStrm,const OUString & rStr,bool bAddZero)1741 void SwWW8Writer::WriteString16(SvStream& rStrm, const OUString& rStr,
1742     bool bAddZero)
1743 {
1744     ww::bytes aBytes;
1745     SwWW8Writer::InsAsString16(aBytes, rStr);
1746     if (bAddZero)
1747         SwWW8Writer::InsUInt16(aBytes, 0);
1748     //vectors are guaranteed to have contiguous memory, so we can do
1749     //this while migrating away from WW8Bytes. Meyers Effective STL, item 16
1750     if (!aBytes.empty())
1751         rStrm.WriteBytes(aBytes.data(), aBytes.size());
1752 }
1753 
WriteString_xstz(SvStream & rStrm,const OUString & rStr,bool bAddZero)1754 void SwWW8Writer::WriteString_xstz(SvStream& rStrm, const OUString& rStr, bool bAddZero)
1755 {
1756     ww::bytes aBytes;
1757     SwWW8Writer::InsUInt16(aBytes, rStr.getLength());
1758     SwWW8Writer::InsAsString16(aBytes, rStr);
1759     if (bAddZero)
1760         SwWW8Writer::InsUInt16(aBytes, 0);
1761     rStrm.WriteBytes(aBytes.data(), aBytes.size());
1762 }
1763 
WriteString8(SvStream & rStrm,std::u16string_view rStr,bool bAddZero,rtl_TextEncoding eCodeSet)1764 void SwWW8Writer::WriteString8(SvStream& rStrm, std::u16string_view rStr,
1765     bool bAddZero, rtl_TextEncoding eCodeSet)
1766 {
1767     ww::bytes aBytes;
1768     SwWW8Writer::InsAsString8(aBytes, rStr, eCodeSet);
1769     if (bAddZero)
1770         aBytes.push_back(0);
1771     //vectors are guaranteed to have contiguous memory, so we can do
1772     ////this while migrating away from WW8Bytes. Meyers Effective STL, item 16
1773     if (!aBytes.empty())
1774         rStrm.WriteBytes(aBytes.data(), aBytes.size());
1775 }
1776 
WriteStringAsPara(const OUString & rText)1777 void WW8Export::WriteStringAsPara( const OUString& rText )
1778 {
1779     if( !rText.isEmpty() )
1780         OutSwString(rText, 0, rText.getLength());
1781     WriteCR();              // CR thereafter
1782 
1783     ww::bytes aArr;
1784     SwWW8Writer::InsUInt16( aArr, 0/*nStyleId*/ );
1785     if( m_bOutTable )
1786     {                                               // Tab-Attr
1787         // sprmPFInTable
1788         SwWW8Writer::InsUInt16( aArr, NS_sprm::PFInTable::val );
1789         aArr.push_back( 1 );
1790     }
1791 
1792     sal_uInt64 nPos = Strm().Tell();
1793     m_pPapPlc->AppendFkpEntry( nPos, aArr.size(), aArr.data() );
1794     m_pChpPlc->AppendFkpEntry( nPos );
1795 }
1796 
WriteSpecialText(sal_uLong nStart,sal_uLong nEnd,sal_uInt8 nTTyp)1797 void MSWordExportBase::WriteSpecialText( sal_uLong nStart, sal_uLong nEnd, sal_uInt8 nTTyp )
1798 {
1799     sal_uInt8 nOldTyp = m_nTextTyp;
1800     m_nTextTyp = nTTyp;
1801     auto const pOldPam = m_pCurPam;       //!! Simply shifting the PaM without restoring should do the job too
1802     sal_uLong nOldStart = m_nCurStart;
1803     sal_uLong nOldEnd = m_nCurEnd;
1804     SwPaM* pOldEnd = m_pOrigPam;
1805     bool bOldPageDescs = m_bOutPageDescs;
1806     m_bOutPageDescs = false;
1807     if ( nTTyp == TXT_FTN || nTTyp == TXT_EDN )
1808         m_bAddFootnoteTab = true;   // enable one aesthetic tab for this footnote
1809 
1810     SetCurPam(nStart, nEnd);
1811 
1812     // clear linked textboxes since old ones can't be linked to frames in this section
1813     m_aLinkedTextboxesHelper.clear();
1814 
1815     // tdf#106261 Reset table infos, otherwise the depth of the cells will be
1816     // incorrect, in case the header/footer had table(s) and we try to export
1817     // the same table second time.
1818     ww8::WW8TableInfo::Pointer_t pOldTableInfo = m_pTableInfo;
1819     m_pTableInfo = std::make_shared<ww8::WW8TableInfo>();
1820 
1821     WriteText();
1822 
1823     m_pTableInfo = pOldTableInfo;
1824 
1825     m_bOutPageDescs = bOldPageDescs;
1826     m_pCurPam = pOldPam; // delete Pam
1827     m_nCurStart = nOldStart;
1828     m_nCurEnd = nOldEnd;
1829     m_pOrigPam = pOldEnd;
1830     m_nTextTyp = nOldTyp;
1831 }
1832 
OutSwString(const OUString & rStr,sal_Int32 nStt,sal_Int32 const nLen)1833 void WW8Export::OutSwString(const OUString& rStr, sal_Int32 nStt,
1834     sal_Int32 const nLen)
1835 
1836 {
1837     SAL_INFO( "sw.ww8.level2", "<OutSwString>" );
1838 
1839     if( nLen )
1840     {
1841         if( nStt || nLen != rStr.getLength() )
1842         {
1843             OUString sOut( rStr.copy( nStt, nLen ) );
1844 
1845             SAL_INFO( "sw.ww8.level2", sOut );
1846 
1847             SwWW8Writer::WriteString16(Strm(), sOut, false);
1848         }
1849         else
1850         {
1851             SAL_INFO( "sw.ww8.level2", rStr );
1852 
1853             SwWW8Writer::WriteString16(Strm(), rStr, false);
1854         }
1855     }
1856 
1857     SAL_INFO( "sw.ww8.level2", "</OutSwString>" );
1858 }
1859 
WriteCR(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)1860 void WW8Export::WriteCR(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
1861 {
1862     if (pTableTextNodeInfoInner && pTableTextNodeInfoInner->getDepth() == 1 && pTableTextNodeInfoInner->isEndOfCell())
1863         WriteChar('\007');
1864     else
1865         WriteChar( '\015' );
1866 
1867     m_pPiece->SetParaBreak();
1868 }
1869 
WriteChar(sal_Unicode c)1870 void WW8Export::WriteChar( sal_Unicode c )
1871 {
1872     Strm().WriteUInt16( c );
1873 }
1874 
SetCurPam(sal_uLong nStt,sal_uLong nEnd)1875 void MSWordExportBase::SetCurPam(sal_uLong nStt, sal_uLong nEnd)
1876 {
1877     m_nCurStart = nStt;
1878     m_nCurEnd = nEnd;
1879     m_pCurPam = Writer::NewUnoCursor( m_rDoc, nStt, nEnd );
1880 
1881     // Recognize tables in special cases
1882     if ( nStt != m_pCurPam->GetMark()->nNode.GetIndex() &&
1883          m_rDoc.GetNodes()[ nStt ]->IsTableNode() )
1884     {
1885         m_pCurPam->GetMark()->nNode = nStt;
1886     }
1887 
1888     m_pOrigPam = m_pCurPam.get(); // ???
1889     m_pCurPam->Exchange();
1890 }
1891 
SaveData(sal_uLong nStt,sal_uLong nEnd)1892 void MSWordExportBase::SaveData( sal_uLong nStt, sal_uLong nEnd )
1893 {
1894     MSWordSaveData aData;
1895 
1896     // WW8Export only stuff - zeroed here not to issue warnings
1897     aData.pOOld = nullptr;
1898 
1899     // Common stuff
1900     aData.pOldPam = m_pCurPam;
1901     aData.pOldEnd = m_pOrigPam;
1902     aData.pOldFlyFormat = m_pParentFrame;
1903     aData.pOldPageDesc = m_pCurrentPageDesc;
1904 
1905     aData.pOldFlyOffset = m_pFlyOffset;
1906     aData.eOldAnchorType = m_eNewAnchorType;
1907 
1908     aData.bOldOutTable = m_bOutTable;
1909     aData.bOldFlyFrameAttrs = m_bOutFlyFrameAttrs;
1910     aData.bOldStartTOX = m_bStartTOX;
1911     aData.bOldInWriteTOX = m_bInWriteTOX;
1912 
1913     SetCurPam(nStt, nEnd);
1914 
1915     m_bOutTable = false;
1916     // Caution: bIsInTable should not be set here
1917     m_bOutFlyFrameAttrs = false;
1918     m_bStartTOX = false;
1919     m_bInWriteTOX = false;
1920 
1921     m_aSaveData.push( std::move(aData) );
1922 }
1923 
RestoreData()1924 void MSWordExportBase::RestoreData()
1925 {
1926     MSWordSaveData &rData = m_aSaveData.top();
1927 
1928     m_pCurPam = rData.pOldPam;
1929     m_nCurStart = rData.nOldStart;
1930     m_nCurEnd = rData.nOldEnd;
1931     m_pOrigPam = rData.pOldEnd;
1932 
1933     m_bOutTable = rData.bOldOutTable;
1934     m_bOutFlyFrameAttrs = rData.bOldFlyFrameAttrs;
1935     m_bStartTOX = rData.bOldStartTOX;
1936     m_bInWriteTOX = rData.bOldInWriteTOX;
1937 
1938     m_pParentFrame = rData.pOldFlyFormat;
1939     m_pCurrentPageDesc = rData.pOldPageDesc;
1940 
1941     m_eNewAnchorType = rData.eOldAnchorType;
1942     m_pFlyOffset = rData.pOldFlyOffset;
1943 
1944     m_aSaveData.pop();
1945 }
1946 
SaveData(sal_uLong nStt,sal_uLong nEnd)1947 void WW8Export::SaveData( sal_uLong nStt, sal_uLong nEnd )
1948 {
1949     MSWordExportBase::SaveData( nStt, nEnd );
1950 
1951     MSWordSaveData &rData = m_aSaveData.top();
1952 
1953     if ( !pO->empty() )
1954     {
1955         rData.pOOld = std::move(pO);
1956         pO.reset(new ww::bytes);
1957     }
1958     else
1959         rData.pOOld = nullptr; // reuse pO
1960 
1961     rData.bOldWriteAll = GetWriter().m_bWriteAll;
1962     GetWriter().m_bWriteAll = true;
1963 }
1964 
RestoreData()1965 void WW8Export::RestoreData()
1966 {
1967     MSWordSaveData &rData = m_aSaveData.top();
1968 
1969     GetWriter().m_bWriteAll = rData.bOldWriteAll;
1970 
1971     OSL_ENSURE( pO->empty(), "pO is not empty in WW8Export::RestoreData()" );
1972     if ( rData.pOOld )
1973     {
1974         pO = std::move(rData.pOOld);
1975     }
1976 
1977     MSWordExportBase::RestoreData();
1978 }
1979 
TableInfoCell(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)1980 void WW8AttributeOutput::TableInfoCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
1981 {
1982     sal_uInt32 nDepth = pTableTextNodeInfoInner->getDepth();
1983 
1984     if ( nDepth <= 0 )
1985         return;
1986 
1987     /* Cell */
1988     m_rWW8Export.InsUInt16( NS_sprm::PFInTable::val );
1989     m_rWW8Export.pO->push_back( sal_uInt8(0x1) );
1990     m_rWW8Export.InsUInt16( NS_sprm::PItap::val );
1991     m_rWW8Export.InsUInt32( nDepth );
1992 
1993     if ( nDepth > 1 && pTableTextNodeInfoInner->isEndOfCell() )
1994     {
1995         m_rWW8Export.InsUInt16( NS_sprm::PFInnerTableCell::val );
1996         m_rWW8Export.pO->push_back( sal_uInt8(0x1) );
1997     }
1998 }
1999 
TableInfoRow(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)2000 void WW8AttributeOutput::TableInfoRow( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
2001 {
2002     sal_uInt32 nDepth = pTableTextNodeInfoInner->getDepth();
2003 
2004     if ( nDepth <= 0 )
2005         return;
2006 
2007     /* Row */
2008     if ( !pTableTextNodeInfoInner->isEndOfLine() )
2009         return;
2010 
2011     m_rWW8Export.InsUInt16( NS_sprm::PFInTable::val );
2012     m_rWW8Export.pO->push_back( sal_uInt8(0x1) );
2013 
2014     if ( nDepth == 1 )
2015     {
2016         m_rWW8Export.InsUInt16( NS_sprm::PFTtp::val );
2017         m_rWW8Export.pO->push_back( sal_uInt8(0x1) );
2018     }
2019 
2020     m_rWW8Export.InsUInt16( NS_sprm::PItap::val );
2021     m_rWW8Export.InsUInt32( nDepth );
2022 
2023     if ( nDepth > 1 )
2024     {
2025         m_rWW8Export.InsUInt16( NS_sprm::PFInnerTableCell::val );
2026         m_rWW8Export.pO->push_back( sal_uInt8(0x1) );
2027         m_rWW8Export.InsUInt16( NS_sprm::PFInnerTtp::val );
2028         m_rWW8Export.pO->push_back( sal_uInt8(0x1) );
2029     }
2030 
2031     // Most of these are per-row definitions, not per-table.
2032     // WW8 has no explicit table start/end markup,
2033     // simply rows with the same table properties that are grouped together as a table.
2034     TableBidi( pTableTextNodeInfoInner );
2035     TableOrientation( pTableTextNodeInfoInner );
2036     TableSpacing( pTableTextNodeInfoInner );
2037     TableDefinition( pTableTextNodeInfoInner );     //per row definitions
2038     TableHeight( pTableTextNodeInfoInner );         //per row definitions
2039     TableBackgrounds( pTableTextNodeInfoInner );    //per row definitions
2040     // Since this isEndOfLine, cell margin defaults for each row come from last column.
2041     TableDefaultBorders( pTableTextNodeInfoInner ); //per row definitions
2042     TableCanSplit( pTableTextNodeInfoInner );       //per row definitions
2043     TableVerticalCell( pTableTextNodeInfoInner );   //per row definitions
2044     TableCellBorders( pTableTextNodeInfoInner );    //per row definitions
2045 }
2046 
lcl_TCFlags(SwDoc & rDoc,const SwTableBox * pBox,sal_Int32 nRowSpan)2047 static sal_uInt16 lcl_TCFlags(SwDoc &rDoc, const SwTableBox * pBox, sal_Int32 nRowSpan)
2048 {
2049     sal_uInt16 nFlags = 0;
2050 
2051     if (nRowSpan > 1)
2052         nFlags |= (3 << 5);
2053     else if (nRowSpan < 0)
2054         nFlags |= (1 << 5);
2055 
2056     if (pBox != nullptr)
2057     {
2058         const SwFrameFormat * pFormat = pBox->GetFrameFormat();
2059         switch (pFormat->GetVertOrient().GetVertOrient())
2060         {
2061             case text::VertOrientation::CENTER:
2062                 nFlags |= (1 << 7);
2063                 break;
2064             case text::VertOrientation::BOTTOM:
2065                 nFlags |= (2 << 7);
2066                 break;
2067             default:
2068                 break;
2069         }
2070         const SwStartNode * pSttNd = pBox->GetSttNd();
2071         if(pSttNd)
2072         {
2073             SwNodeIndex aIdx( *pSttNd );
2074             const SwContentNode * pCNd = pSttNd->GetNodes().GoNext( &aIdx );
2075             if( pCNd && pCNd->IsTextNode())
2076             {
2077                 SfxItemSet aCoreSet(rDoc.GetAttrPool(), svl::Items<RES_CHRATR_ROTATE, RES_CHRATR_ROTATE>{});
2078                 static_cast<const SwTextNode*>(pCNd)->GetParaAttr(aCoreSet,
2079                     0, static_cast<const SwTextNode*>(pCNd)->GetText().getLength());
2080                 const SfxPoolItem * pRotItem;
2081                 if ( SfxItemState::SET == aCoreSet.GetItemState(RES_CHRATR_ROTATE, true, &pRotItem))
2082                 {
2083                     const SvxCharRotateItem * pRotate = static_cast<const SvxCharRotateItem*>(pRotItem);
2084                     if(pRotate && pRotate->GetValue() == 900_deg10)
2085                     {
2086                         nFlags = nFlags | 0x0004 | 0x0008;
2087                     }
2088                     else if(pRotate && pRotate->GetValue() == 2700_deg10 )
2089                     {
2090                         nFlags = nFlags | 0x0004 | 0x0010;
2091                     }
2092                 }
2093             }
2094         }
2095     }
2096 
2097     return nFlags;
2098 }
2099 
TableVerticalCell(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)2100 void WW8AttributeOutput::TableVerticalCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
2101 {
2102     const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
2103     const SwTableLine * pTabLine = pTabBox->GetUpper();
2104     const SwTableBoxes & rTableBoxes = pTabLine->GetTabBoxes();
2105 
2106     sal_uInt8 nBoxes = rTableBoxes.size();
2107     for ( sal_uInt8 n = 0; n < nBoxes; n++ )
2108     {
2109         const SwTableBox * pTabBox1 = rTableBoxes[n];
2110         const SwFrameFormat * pFrameFormat = pTabBox1->GetFrameFormat();
2111 
2112         // Map from our SvxFrameDirection to WW8 TextFlow.
2113         sal_uInt16 nTextFlow = 0;
2114         switch (m_rWW8Export.TrueFrameDirection(*pFrameFormat))
2115         {
2116             case SvxFrameDirection::Vertical_RL_TB:
2117                 nTextFlow = 5;
2118                 break;
2119             case SvxFrameDirection::Vertical_LR_BT:
2120                 nTextFlow = 3;
2121                 break;
2122             default:
2123                 break;
2124         }
2125 
2126         if (nTextFlow != 0)
2127         {
2128             m_rWW8Export.InsUInt16( NS_sprm::TTextFlow::val );
2129             m_rWW8Export.pO->push_back( n );                   //start range
2130             m_rWW8Export.pO->push_back( sal_uInt8(n + 1) );    //end range
2131             m_rWW8Export.InsUInt16(nTextFlow);
2132         }
2133     }
2134 }
2135 
TableCanSplit(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)2136 void WW8AttributeOutput::TableCanSplit( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
2137 {
2138     const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
2139     const SwTableLine * pTabLine = pTabBox->GetUpper();
2140     const SwFrameFormat * pLineFormat = pTabLine->GetFrameFormat();
2141 
2142     /*
2143      By default the row can be split in word, and now in writer we have a
2144      feature equivalent to this, Word stores 1 for fCantSplit if the row
2145      cannot be split, we set true if we can split it. An example is #i4569#
2146      */
2147 
2148     const SwFormatRowSplit& rSplittable = pLineFormat->GetRowSplit();
2149     sal_uInt8 nCantSplit = (!rSplittable.GetValue()) ? 1 : 0;
2150     m_rWW8Export.InsUInt16( NS_sprm::TFCantSplit::val );
2151     m_rWW8Export.pO->push_back( nCantSplit );
2152     m_rWW8Export.InsUInt16( NS_sprm::TFCantSplit90::val ); // also write fCantSplit90
2153     m_rWW8Export.pO->push_back( nCantSplit );
2154 }
2155 
TableBidi(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)2156 void WW8AttributeOutput::TableBidi( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
2157 {
2158     const SwTable * pTable = pTableTextNodeInfoInner->getTable();
2159     const SwFrameFormat * pFrameFormat = pTable->GetFrameFormat();
2160 
2161     if ( m_rWW8Export.TrueFrameDirection(*pFrameFormat) == SvxFrameDirection::Horizontal_RL_TB )
2162     {
2163         m_rWW8Export.InsUInt16( NS_sprm::TFBiDi::val );
2164         m_rWW8Export.InsUInt16( 1 );
2165     }
2166 }
2167 
TableRowRedline(ww8::WW8TableNodeInfoInner::Pointer_t)2168 void WW8AttributeOutput::TableRowRedline( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ )
2169 {
2170 }
2171 
TableCellRedline(ww8::WW8TableNodeInfoInner::Pointer_t)2172 void WW8AttributeOutput::TableCellRedline( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ )
2173 {
2174 }
2175 
TableHeight(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)2176 void WW8AttributeOutput::TableHeight( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
2177 {
2178     const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
2179     const SwTableLine * pTabLine = pTabBox->GetUpper();
2180     const SwFrameFormat * pLineFormat = pTabLine->GetFrameFormat();
2181 
2182     // output line height   sprmTDyaRowHeight
2183     tools::Long nHeight = 0;
2184     const SwFormatFrameSize& rLSz = pLineFormat->GetFrameSize();
2185     if ( SwFrameSize::Variable != rLSz.GetHeightSizeType() && rLSz.GetHeight() )
2186     {
2187         if ( SwFrameSize::Minimum == rLSz.GetHeightSizeType() )
2188             nHeight = rLSz.GetHeight();
2189         else
2190             nHeight = -rLSz.GetHeight();
2191     }
2192 
2193     if ( nHeight )
2194     {
2195         m_rWW8Export.InsUInt16( NS_sprm::TDyaRowHeight::val );
2196         m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>(nHeight) );
2197     }
2198 
2199 }
2200 
TableOrientation(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)2201 void WW8AttributeOutput::TableOrientation( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
2202 {
2203     const SwTable * pTable = pTableTextNodeInfoInner->getTable();
2204 
2205     const SwFrameFormat *pFormat = pTable->GetFrameFormat();
2206     if ( !pFormat )
2207     {
2208         SAL_WARN( "sw.ww8", "FrameFormat is nil" );
2209         return;
2210     }
2211 
2212     const SwFormatHoriOrient &rHori = pFormat->GetHoriOrient();
2213     const SwFormatVertOrient &rVert = pFormat->GetVertOrient();
2214 
2215     if (
2216         !((text::RelOrientation::PRINT_AREA == rHori.GetRelationOrient() ||
2217          text::RelOrientation::FRAME == rHori.GetRelationOrient())
2218         &&
2219         (text::RelOrientation::PRINT_AREA == rVert.GetRelationOrient() ||
2220          text::RelOrientation::FRAME == rVert.GetRelationOrient()))
2221         )
2222         return;
2223 
2224     const bool bIsRTL = m_rWW8Export.TrueFrameDirection(*pFormat) == SvxFrameDirection::Horizontal_RL_TB;
2225     sal_Int16 eHOri = rHori.GetHoriOrient();
2226     switch (eHOri)
2227     {
2228         case text::HoriOrientation::CENTER:
2229             m_rWW8Export.InsUInt16( NS_sprm::TJc::val ); //logical orientation required for MSO
2230             m_rWW8Export.InsUInt16( 1 );
2231             m_rWW8Export.InsUInt16( NS_sprm::TJc90::val ); //physical orientation required for LO
2232             m_rWW8Export.InsUInt16( 1 );
2233             break;
2234         case text::HoriOrientation::RIGHT:
2235             m_rWW8Export.InsUInt16( NS_sprm::TJc90::val ); //required for LO
2236             m_rWW8Export.InsUInt16( 2 );
2237             if ( !bIsRTL )
2238             {
2239                 m_rWW8Export.InsUInt16( NS_sprm::TJc::val ); //required for MSO
2240                 m_rWW8Export.InsUInt16( 2 );
2241             }
2242             break;
2243         case text::HoriOrientation::LEFT:
2244             if ( bIsRTL )
2245             {
2246                 m_rWW8Export.InsUInt16( NS_sprm::TJc::val ); //required for MSO
2247                 m_rWW8Export.InsUInt16( 2 );
2248             }
2249             break;
2250         case text::HoriOrientation::LEFT_AND_WIDTH:
2251             // Width can only be specified for the LOGICAL left, so in RTL, that is always PHYSICAL right
2252             if ( bIsRTL )
2253             {
2254                 m_rWW8Export.InsUInt16( NS_sprm::TJc90::val ); //required for LO
2255                 m_rWW8Export.InsUInt16( 2 );
2256             }
2257             break;
2258         default:
2259             break;
2260     }
2261 }
2262 
TableSpacing(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)2263 void WW8AttributeOutput::TableSpacing(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
2264 {
2265     const SwTable * pTable = pTableTextNodeInfoInner->getTable();
2266     const SwTableFormat* pTableFormat = pTable->GetFrameFormat();
2267 
2268 
2269     // Writing these SPRM's will make the table a floating one, so only write
2270     // them in case the table is already inside a frame.
2271     if (!(pTableFormat != nullptr && pTable->GetTableNode()->GetFlyFormat()))
2272         return;
2273 
2274     const SvxULSpaceItem & rUL = pTableFormat->GetULSpace();
2275 
2276     if (rUL.GetUpper() > 0)
2277     {
2278         sal_uInt8 const nPadding = 2;
2279         sal_uInt8 const nPcVert = 0;
2280         sal_uInt8 const nPcHorz = 0;
2281 
2282         sal_uInt8 const nTPc = (nPadding << 4) | (nPcVert << 2) | nPcHorz;
2283 
2284         m_rWW8Export.InsUInt16(NS_sprm::TPc::val);
2285         m_rWW8Export.pO->push_back( nTPc );
2286 
2287         m_rWW8Export.InsUInt16(NS_sprm::TDyaAbs::val);
2288         m_rWW8Export.InsUInt16(rUL.GetUpper());
2289 
2290         m_rWW8Export.InsUInt16(NS_sprm::TDyaFromText::val);
2291         m_rWW8Export.InsUInt16(rUL.GetUpper());
2292     }
2293 
2294     if (rUL.GetLower() > 0)
2295     {
2296         m_rWW8Export.InsUInt16(NS_sprm::TDyaFromTextBottom::val);
2297         m_rWW8Export.InsUInt16(rUL.GetLower());
2298     }
2299 }
2300 
TableDefinition(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)2301 void WW8AttributeOutput::TableDefinition( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
2302 {
2303     const SwTable * pTable = pTableTextNodeInfoInner->getTable();
2304 
2305     if ( pTable->GetRowsToRepeat() > pTableTextNodeInfoInner->getRow() )
2306     {
2307         m_rWW8Export.InsUInt16( NS_sprm::TTableHeader::val );
2308         m_rWW8Export.pO->push_back( 1 );
2309     }
2310 
2311     ww8::TableBoxVectorPtr pTableBoxes =
2312         pTableTextNodeInfoInner->getTableBoxesOfRow();
2313     // number of cell written
2314     sal_uInt32 nBoxes = pTableBoxes->size();
2315     assert(nBoxes <= ww8::MAXTABLECELLS);
2316 
2317     // sprm header
2318     m_rWW8Export.InsUInt16( NS_sprm::TDefTable::val );
2319     sal_uInt16 nSprmSize = 2 + (nBoxes + 1) * 2 + nBoxes * 20;
2320     m_rWW8Export.InsUInt16( nSprmSize ); // length
2321 
2322     // number of boxes
2323     m_rWW8Export.pO->push_back( static_cast<sal_uInt8>(nBoxes) );
2324 
2325     /* cells */
2326     /*
2327      ALWAYS relative when text::HoriOrientation::NONE (nPageSize + ( nPageSize / 10 )) < nTableSz,
2328      in that case the cell width's and table width's are not real. The table
2329      width is maxed and cells relative, so we need the frame (generally page)
2330      width that the table is in to work out the true widths.
2331      */
2332     //const bool bNewTableModel = pTable->IsNewModel();
2333     const SwFrameFormat *pFormat = pTable->GetFrameFormat();
2334     if ( !pFormat )
2335     {
2336         SAL_WARN( "sw.ww8", "FrameFormat is nil" );
2337         return;
2338     }
2339 
2340     const SwFormatHoriOrient &rHori = pFormat->GetHoriOrient();
2341     const SwFormatVertOrient &rVert = pFormat->GetVertOrient();
2342 
2343     SwTwips nTableOffset = 0;
2344 
2345     if (
2346         (text::RelOrientation::PRINT_AREA == rHori.GetRelationOrient() ||
2347          text::RelOrientation::FRAME == rHori.GetRelationOrient())
2348         &&
2349         (text::RelOrientation::PRINT_AREA == rVert.GetRelationOrient() ||
2350          text::RelOrientation::FRAME == rVert.GetRelationOrient())
2351         )
2352     {
2353         sal_Int16 eHOri = rHori.GetHoriOrient();
2354         switch ( eHOri )
2355         {
2356             case text::HoriOrientation::CENTER:
2357             case text::HoriOrientation::RIGHT:
2358                 break;
2359 
2360             default:
2361                 nTableOffset = rHori.GetPos();
2362                 const SvxLRSpaceItem& rLRSp = pFormat->GetLRSpace();
2363                 nTableOffset += rLRSp.GetLeft();
2364 
2365                 // convert offset to be measured from right margin in right-to-left tables
2366                 if ( nTableOffset && m_rWW8Export.TrueFrameDirection(*pFormat) == SvxFrameDirection::Horizontal_RL_TB )
2367                 {
2368                     SwTwips nLeftPageMargin, nRightPageMargin;
2369                     const SwTwips nPageSize = m_rWW8Export.CurrentPageWidth(nLeftPageMargin, nRightPageMargin);
2370                     const SwTwips nTableWidth = pFormat->GetFrameSize().GetWidth();
2371                     nTableOffset = nPageSize - nLeftPageMargin - nRightPageMargin - nTableWidth - nTableOffset;
2372                 }
2373                 break;
2374         }
2375     }
2376 
2377     m_rWW8Export.InsInt16( nTableOffset );
2378 
2379     ww8::GridColsPtr pGridCols = GetGridCols( pTableTextNodeInfoInner );
2380     for ( const auto nCol : *pGridCols )
2381      {
2382          m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>(nCol) + nTableOffset );
2383      }
2384 
2385      /* TCs */
2386     ww8::RowSpansPtr pRowSpans = pTableTextNodeInfoInner->getRowSpansOfRow();
2387     ww8::RowSpans::const_iterator aItRowSpans = pRowSpans->begin();
2388 
2389     for (const SwTableBox * pTabBox1 : *pTableBoxes)
2390     {
2391         sal_uInt16 npOCount = m_rWW8Export.pO->size();
2392 
2393         const SwFrameFormat * pBoxFormat = nullptr;
2394         if (pTabBox1 != nullptr)
2395             pBoxFormat = pTabBox1->GetFrameFormat();
2396 
2397         sal_uInt16 nFlags =
2398             lcl_TCFlags(m_rWW8Export.m_rDoc, pTabBox1, *aItRowSpans);
2399         m_rWW8Export.InsUInt16( nFlags );
2400 
2401         static sal_uInt8 aNullBytes[] = { 0x0, 0x0 };
2402 
2403         m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), aNullBytes, aNullBytes+2 );   // dummy
2404         if (pBoxFormat != nullptr)
2405         {
2406             const SvxBoxItem & rBoxItem = pBoxFormat->GetBox();
2407 
2408             WW8Export::Out_SwFormatTableBox( *m_rWW8Export.pO, &rBoxItem ); // 8/16 Byte
2409         }
2410         else
2411             WW8Export::Out_SwFormatTableBox( *m_rWW8Export.pO, nullptr); // 8/16 Byte
2412 
2413         SAL_INFO( "sw.ww8.level2", "<tclength>" << ( m_rWW8Export.pO->size() - npOCount ) << "</tclength>" );
2414         ++aItRowSpans;
2415     }
2416 
2417     int nWidthPercent = pFormat->GetFrameSize().GetWidthPercent();
2418     // Width is in fiftieths of a percent. For sprmTTableWidth, must be non-negative and 600% max
2419     if ( nWidthPercent > 0 && nWidthPercent <= 600 )
2420     {
2421         m_rWW8Export.InsUInt16( NS_sprm::TTableWidth::val );
2422         m_rWW8Export.pO->push_back( sal_uInt8/*ftsPercent*/ (2) );
2423         m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>(nWidthPercent) * 50 );
2424     }
2425 }
2426 
GetGridCols(ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner)2427 ww8::GridColsPtr AttributeOutputBase::GetGridCols( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner )
2428 {
2429     return pTableTextNodeInfoInner->getGridColsOfRow(*this);
2430 }
2431 
GetColumnWidths(ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner)2432 ww8::WidthsPtr AttributeOutputBase::GetColumnWidths( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner )
2433 {
2434     // Get the column widths based on ALL the rows, not just the current row
2435     return pTableTextNodeInfoInner->getGridColsOfRow(*this, true);
2436 }
2437 
GetTablePageSize(ww8::WW8TableNodeInfoInner const * pTableTextNodeInfoInner,tools::Long & rPageSize,bool & rRelBoxSize)2438 void AttributeOutputBase::GetTablePageSize( ww8::WW8TableNodeInfoInner const * pTableTextNodeInfoInner, tools::Long& rPageSize, bool& rRelBoxSize )
2439 {
2440     tools::Long nPageSize = 0;
2441 
2442     const SwNode *pTextNd = pTableTextNodeInfoInner->getNode( );
2443     const SwTable *pTable = pTableTextNodeInfoInner->getTable( );
2444 
2445     const SwFrameFormat *pFormat = pTable->GetFrameFormat();
2446     if ( !pFormat )
2447     {
2448         SAL_WARN( "sw.ww8", "FrameFormat is nil" );
2449         return;
2450     }
2451 
2452     const SwFormatFrameSize &rSize = pFormat->GetFrameSize();
2453     int nWidthPercent = rSize.GetWidthPercent();
2454     bool bManualAligned = pFormat->GetHoriOrient().GetHoriOrient() == text::HoriOrientation::NONE;
2455     if ( (pFormat->GetHoriOrient().GetHoriOrient() == text::HoriOrientation::FULL) || bManualAligned )
2456         nWidthPercent = 100;
2457     bool bRelBoxSize = nWidthPercent != 0;
2458     tools::ULong nTableSz = static_cast<tools::ULong>(rSize.GetWidth());
2459     if (nTableSz > USHRT_MAX/2 && !bRelBoxSize)
2460     {
2461         OSL_ENSURE(bRelBoxSize, "huge table width but not relative, suspicious");
2462         bRelBoxSize = true;
2463     }
2464 
2465     if ( bRelBoxSize )
2466     {
2467         Point aPt;
2468         SwRect aRect( pFormat->FindLayoutRect( false, &aPt ) );
2469         if ( aRect.IsEmpty() )
2470         {
2471             // Then fetch the page width without margins!
2472             const SwFrameFormat* pParentFormat =
2473                 GetExport().m_pParentFrame ?
2474                 &(GetExport().m_pParentFrame->GetFrameFormat()) :
2475                     GetExport().m_rDoc.GetPageDesc(0).GetPageFormatOfNode(*pTextNd, false);
2476             aRect = pParentFormat->FindLayoutRect(true);
2477             nPageSize = aRect.Width();
2478             if ( 0 == nPageSize )
2479             {
2480                 const SvxLRSpaceItem& rLR = pParentFormat->GetLRSpace();
2481                 nPageSize = pParentFormat->GetFrameSize().GetWidth() - rLR.GetLeft()
2482                 - rLR.GetRight();
2483             }
2484         }
2485         else
2486         {
2487             nPageSize = aRect.Width();
2488             if ( bManualAligned )
2489             {
2490                 // #i37571# For manually aligned tables
2491                 const SvxLRSpaceItem &rLR = pFormat->GetLRSpace();
2492                 nPageSize -= (rLR.GetLeft() + rLR.GetRight());
2493             }
2494 
2495         }
2496 
2497         if ( nWidthPercent )
2498         {
2499             nPageSize *= nWidthPercent;
2500             nPageSize /= 100;
2501         }
2502         else
2503             SAL_WARN( "sw.ww8", "nWidthPercent is zero" );
2504     }
2505     else
2506     {
2507         // As the table width is not relative, the TablePageSize equals its width
2508         nPageSize = nTableSz;
2509     }
2510 
2511     rPageSize = nPageSize;
2512     rRelBoxSize = bRelBoxSize;
2513 }
2514 
TableDefaultBorders(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)2515 void WW8AttributeOutput::TableDefaultBorders( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
2516 {
2517     // This function name is misleading because it is not a table default, but a row default,
2518     // and it also only sets default cell margins (aka border padding).
2519     // The specs suggest there is no way to define default border lines/colors.
2520     const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
2521     const SwFrameFormat * pFrameFormat = pTabBox->GetFrameFormat();
2522 
2523     static const SvxBoxItemLine aBorders[] =
2524     {
2525         SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
2526         SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
2527     };
2528 
2529     // Set row default cell margins using this last cell in the row
2530     for ( int i = 0; i < 4; ++i )
2531     {
2532         SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::TCellPaddingDefault::val );
2533         m_rWW8Export.pO->push_back( sal_uInt8(6) );
2534         m_rWW8Export.pO->push_back( sal_uInt8(0) );
2535         m_rWW8Export.pO->push_back( sal_uInt8(1) );
2536         m_rWW8Export.pO->push_back( sal_uInt8(1 << i) );
2537         m_rWW8Export.pO->push_back( sal_uInt8(3) );
2538 
2539         SwWW8Writer::InsUInt16( *m_rWW8Export.pO,
2540                 pFrameFormat->GetBox().GetDistance( aBorders[i] ) );
2541     }
2542 }
2543 
TableCellBorders(ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner)2544 void WW8AttributeOutput::TableCellBorders(
2545     ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner )
2546 {
2547     const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
2548     const SwTableLine * pTabLine = pTabBox->GetUpper();
2549     const SwTableBoxes & rTabBoxes = pTabLine->GetTabBoxes();
2550     sal_uInt8 nBoxes = std::min<size_t>(rTabBoxes.size(), 255);
2551     const SvxBoxItem * pLastBox = nullptr;
2552     sal_uInt8 nSeqStart = 0; // start of sequence of cells with same borders
2553 
2554     static const SvxBoxItemLine aBorders[] =
2555     {
2556         SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
2557         SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
2558     };
2559 
2560     sal_uInt16 nDefaultMargin[4] = {31681, 31681, 31681, 31681};  // outside of documented valid range
2561     // last column in each row defines the row default in TableRowDefaultBorders()
2562     if ( nBoxes && rTabBoxes.size() == nBoxes )
2563     {
2564         const SvxBoxItem& rBox = rTabBoxes[ nBoxes-1 ]->GetFrameFormat()->GetBox();
2565         for ( int i = 0; i < 4; ++i )
2566             nDefaultMargin[i] = rBox.GetDistance( aBorders[i] );
2567     }
2568 
2569     // Detect sequences of cells which have the same borders, and output
2570     // a border description for each such cell range.
2571     for ( unsigned n = 0; n <= nBoxes; ++n )
2572     {
2573         const SvxBoxItem * pBox = (n == nBoxes) ? nullptr :
2574             &rTabBoxes[n]->GetFrameFormat()->GetBox();
2575         if( !pLastBox )
2576             pLastBox = pBox;
2577         else if( !pBox || *pLastBox != *pBox )
2578         {
2579             // This cell has different borders than the previous cell,
2580             // so output the borders for the preceding cell range.
2581             m_rWW8Export.Out_CellRangeBorders(pLastBox, nSeqStart, n);
2582 
2583             // The last column is used as the row default for margins, so we can ignore these matching ones
2584             if ( n == nBoxes )
2585                 break;
2586 
2587             // Output cell margins.
2588             // One CSSA can define up to all four margins if they are the same size value.
2589             sal_uInt16 nMargin[4];
2590             sal_uInt8 nSideBits[4] = {0, 0, 0, 0}; // 0001:top, 0010:left, 0100:bottom, 1000:right
2591             for ( int i = 0; i < 4; ++i )  // sides: top, left, bottom, right
2592             {
2593                 nMargin[i] = std::min(sal_uInt16(31680), pLastBox->GetDistance( aBorders[i] ));
2594                 if ( nMargin[i] == nDefaultMargin[i] )
2595                     continue;
2596 
2597                 // join a previous side's definition if it shares the same value
2598                 for ( int p = 0; p < 4; ++p )
2599                 {
2600                     if ( nMargin[i] == nMargin[p] )
2601                     {
2602                         nSideBits[p] |= 1 << i;
2603                         break;
2604                     }
2605                 }
2606             }
2607 
2608             // write out the cell margins definitions that were used
2609             for ( int i = 0; i < 4; ++i )
2610             {
2611                 if ( nSideBits[i] )
2612                 {
2613                     SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::TCellPadding::val );
2614                     m_rWW8Export.pO->push_back( sal_uInt8(6) );            // 6 bytes
2615                     m_rWW8Export.pO->push_back( sal_uInt8(nSeqStart) );    // first cell: apply margin
2616                     m_rWW8Export.pO->push_back( sal_uInt8(n) );            // end cell: do not apply margin
2617                     m_rWW8Export.pO->push_back( sal_uInt8(nSideBits[i]) );
2618                     m_rWW8Export.pO->push_back( sal_uInt8(3) );            // FtsDxa: size in twips
2619                     SwWW8Writer::InsUInt16( *m_rWW8Export.pO, nMargin[i] );
2620                 }
2621             }
2622 
2623             nSeqStart = n;
2624             pLastBox = pBox;
2625         }
2626     }
2627 }
2628 
TableBackgrounds(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)2629 void WW8AttributeOutput::TableBackgrounds( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
2630 {
2631     const SwTable * pTab = pTableTextNodeInfoInner->getTable();
2632     const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
2633     const SwTableLine * pTabLine = pTabBox->GetUpper();
2634     const SwTableBoxes & rTabBoxes = pTabLine->GetTabBoxes();
2635 
2636     sal_uInt8 nBoxes = rTabBoxes.size();
2637     m_rWW8Export.InsUInt16( NS_sprm::TDefTableShd80::val );
2638     m_rWW8Export.pO->push_back( static_cast<sal_uInt8>(nBoxes * 2) );  // Len
2639 
2640     Color aRowColor = COL_AUTO;
2641     const SvxBrushItem *pTableColorProp = pTab->GetFrameFormat()->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
2642     if ( pTableColorProp )
2643         aRowColor = pTableColorProp->GetColor();
2644 
2645     const SvxBrushItem *pRowColorProp = pTabLine->GetFrameFormat()->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
2646     if ( pRowColorProp && pRowColorProp->GetColor() != COL_AUTO )
2647         aRowColor = pRowColorProp->GetColor();
2648 
2649     for ( sal_uInt8 n = 0; n < nBoxes; n++ )
2650     {
2651         const SwTableBox * pBox1 = rTabBoxes[n];
2652         const SwFrameFormat * pFrameFormat = pBox1->GetFrameFormat();
2653         Color aColor = aRowColor;
2654 
2655         const SvxBrushItem *pCellColorProp = pFrameFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
2656         if ( pCellColorProp && pCellColorProp->GetColor() != COL_AUTO )
2657             aColor = pCellColorProp->GetColor();
2658 
2659         WW8_SHD aShd;
2660         WW8Export::TransBrush( aColor, aShd );
2661         m_rWW8Export.InsUInt16( aShd.GetValue() );
2662     }
2663 
2664 /*sprmTDefTableShdRaw:
2665  * A DefTableShdOperand value that specifies the ... shading for cells 1 up to 22 in the row,
2666  * ... Cells 23 to 44 are shaded by sprmTDefTableShdRaw2nd,
2667  * and cells 45 to 63 are shaded by sprmTDefTableShdRaw3rd.
2668  */
2669     sal_uInt32 const aSprmIds[] { NS_sprm::TDefTableShd::val,
2670                                   NS_sprm::TDefTableShdRaw::val,
2671                                   NS_sprm::TDefTableShdRaw::val,
2672                                   NS_sprm::TDefTableShd2nd::val,
2673                                   NS_sprm::TDefTableShdRaw2nd::val,
2674                                   NS_sprm::TDefTableShd3rd::val,
2675                                   NS_sprm::TDefTableShdRaw3rd::val };
2676     for (sal_uInt32 m : aSprmIds)
2677     {
2678         sal_uInt8 nStart = 0;
2679         sal_uInt8 nStop = rTabBoxes.size();
2680         switch ( m )
2681         {
2682             case NS_sprm::TDefTableShd::val:
2683             case NS_sprm::TDefTableShdRaw::val:
2684                 if ( nStop > 21 )
2685                     nStop = 22;
2686                 break;
2687             case NS_sprm::TDefTableShd2nd::val:
2688             case NS_sprm::TDefTableShdRaw2nd::val:
2689                 nStart = 22;
2690                 if ( nStop > 43 )
2691                     nStop = 44;
2692                 break;
2693             case NS_sprm::TDefTableShd3rd::val:
2694             case NS_sprm::TDefTableShdRaw3rd::val:
2695                 nStart = 44;
2696                 if ( nStop > 62 )
2697                     nStop = 63;
2698                 break;
2699         }
2700         if ( nStart >= nStop )
2701             break;
2702 
2703         m_rWW8Export.InsUInt16( m );
2704         m_rWW8Export.pO->push_back( static_cast<sal_uInt8>((nStop-nStart) * 10) );
2705 
2706         for ( sal_uInt8 n = nStart; n < nStop; n++ )
2707         {
2708             const SwTableBox * pBox1 = rTabBoxes[n];
2709             const SwFrameFormat * pFrameFormat = pBox1->GetFrameFormat();
2710             Color aColor = aRowColor;
2711 
2712             const SvxBrushItem *pCellColorProp = pFrameFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
2713             if ( pCellColorProp && pCellColorProp->GetColor() != COL_AUTO )
2714                 aColor = pCellColorProp->GetColor();
2715 
2716             WW8SHDLong aSHD;
2717             aSHD.setCvFore( 0xFF000000 );
2718 
2719             if ( aColor == COL_AUTO )
2720                 aSHD.setCvBack( 0xFF000000 );
2721             else
2722                 aSHD.setCvBack( wwUtility::RGBToBGR( aColor ) );
2723 
2724             aSHD.Write( m_rWW8Export );
2725         }
2726     }
2727 }
2728 
SectionBreaksAndFrames(const SwTextNode & rNode)2729 void WW8Export::SectionBreaksAndFrames( const SwTextNode& rNode )
2730 {
2731     // output page/section breaks
2732     OutputSectionBreaks( rNode.GetpSwAttrSet(), rNode );
2733 }
2734 
2735 namespace {
2736 
2737 class TrackContentToExport
2738 {
2739 private:
2740     SwPaM *m_pCurPam;
2741     sal_uLong m_nStart, m_nEnd;
2742 public:
TrackContentToExport(SwPaM * pCurPam,sal_uLong nCurStart,sal_uLong nCurEnd)2743     TrackContentToExport(SwPaM *pCurPam, sal_uLong nCurStart, sal_uLong nCurEnd)
2744         : m_pCurPam(pCurPam)
2745         , m_nStart(nCurStart)
2746         , m_nEnd(nCurEnd)
2747     {
2748     }
2749 
contentRemainsToExport(ww8::WW8TableInfo * pTableInfo)2750     bool contentRemainsToExport(ww8::WW8TableInfo *pTableInfo)
2751     {
2752         bool bSimpleContentRemains = m_pCurPam->GetPoint()->nNode < m_pCurPam->GetMark()->nNode ||
2753             (m_pCurPam->GetPoint()->nNode == m_pCurPam->GetMark()->nNode &&
2754               m_pCurPam->GetPoint()->nContent.GetIndex() <= m_pCurPam->GetMark()->nContent.GetIndex());
2755         if (bSimpleContentRemains)
2756             return true;
2757 
2758         if (!pTableInfo)
2759             return false;
2760 
2761         //An old-school table where one cell may points back to a previous node as the next cell
2762         //so if this node is the last node in the range, we may need to jump back to a previously
2763         //skipped cell to output it in a sane sequence. See ooo47778-3.sxw for one of these
2764         //horrors. So if we are at the end of the selection, but this end point is a table
2765         //cell whose next cell is in the selection allow jumping back to it
2766         const SwNode* pCurrentNode = &m_pCurPam->GetPoint()->nNode.GetNode();
2767         const SwNode* pNextNode = pTableInfo->getNextNode(pCurrentNode);
2768 
2769         if (pNextNode && pCurrentNode != pNextNode)
2770         {
2771             return pNextNode->GetIndex() >= m_nStart &&
2772                    pNextNode->GetIndex() < m_nEnd;
2773         }
2774 
2775         return false;
2776     }
2777 };
2778 
2779 }
2780 
WriteText()2781 void MSWordExportBase::WriteText()
2782 {
2783     TrackContentToExport aContentTracking(m_pCurPam.get(), m_nCurStart, m_nCurEnd);
2784     while (aContentTracking.contentRemainsToExport(m_pTableInfo.get()))
2785     {
2786         SwNode& rNd = m_pCurPam->GetNode();
2787 
2788         // no section breaks exported for Endnotes
2789         if ( rNd.IsTextNode() && m_nTextTyp != TXT_EDN && m_nTextTyp != TXT_FTN )
2790         {
2791             SwSoftPageBreakList breakList;
2792             // if paragraph need to be split than handle section break somewhere
2793             // else.
2794             if( !NeedTextNodeSplit( *rNd.GetTextNode(), breakList) )
2795                 SectionBreaksAndFrames( *rNd.GetTextNode() );
2796         }
2797 
2798 
2799         // output the various types of nodes
2800         if ( rNd.IsContentNode() )
2801         {
2802             SwContentNode* pCNd = static_cast<SwContentNode*>(&rNd);
2803 
2804             const SwPageDesc* pTemp = rNd.FindPageDesc();
2805             if ( pTemp )
2806                 m_pCurrentPageDesc = pTemp;
2807 
2808             m_pCurPam->GetPoint()->nContent.Assign( pCNd, 0 );
2809             OutputContentNode( *pCNd );
2810         }
2811         else if ( rNd.IsTableNode() )
2812         {
2813             m_pTableInfo->processSwTable( &rNd.GetTableNode()->GetTable() );
2814         }
2815         else if ( rNd.IsSectionNode() && TXT_MAINTEXT == m_nTextTyp )
2816             OutputSectionNode( *rNd.GetSectionNode() );
2817         else if ( TXT_MAINTEXT == m_nTextTyp && rNd.IsEndNode() &&
2818                   rNd.StartOfSectionNode()->IsSectionNode() )
2819         {
2820             const SwSection& rSect = rNd.StartOfSectionNode()->GetSectionNode()
2821                                         ->GetSection();
2822             if ( m_bStartTOX && SectionType::ToxContent == rSect.GetType() )
2823                 m_bStartTOX = false;
2824 
2825             SwNodeIndex aIdx( rNd, 1 );
2826             if ( aIdx.GetNode().IsEndNode() && aIdx.GetNode().StartOfSectionNode()->IsSectionNode() )
2827                 ;
2828             else if ( aIdx.GetNode().IsSectionNode() )
2829                 ;
2830             else if ( !IsInTable() )    //No sections in table
2831             {
2832                 //#120140# Do not need to insert a page/section break after a section end. Check this case first
2833                 bool bNeedExportBreakHere = true;
2834                 if ( rSect.GetType() == SectionType::ToxContent || rSect.GetType() == SectionType::ToxHeader )
2835                     bNeedExportBreakHere = false;
2836                 else if ( aIdx.GetNode().IsTextNode() )
2837                 {
2838                     SwTextNode *pTempNext = aIdx.GetNode().GetTextNode();
2839                     if ( pTempNext )
2840                     {
2841                         const SfxPoolItem * pTempItem = nullptr;
2842                         if (pTempNext->GetpSwAttrSet() && SfxItemState::SET == pTempNext->GetpSwAttrSet()->GetItemState(RES_PAGEDESC, false, &pTempItem)
2843                             && pTempItem && static_cast<const SwFormatPageDesc*>(pTempItem)->GetRegisteredIn())
2844                         {
2845                             //Next node has a new page style which means this node is a section end. Do not insert another page/section break here
2846                             bNeedExportBreakHere = false;
2847                         }
2848                     }
2849                 }
2850                 else
2851                 {
2852                     /* Do not export Section Break in case DOCX containing MultiColumn and
2853                      * aIdx.GetNode().IsTextNode() is False i.e. Text node is NULL.
2854                      */
2855                     const SwFrameFormat* pPgFormat = rSect.GetFormat();
2856                     const SwFormatCol& rCol = pPgFormat->GetCol();
2857                     sal_uInt16 nColumnCount = rCol.GetNumCols();
2858                     const SwFormatNoBalancedColumns& rNoBalanced = pPgFormat->GetBalancedColumns();
2859                     // Prevent the additional section break only for non-balanced columns.
2860                     if (nColumnCount > 1 && rNoBalanced.GetValue())
2861                     {
2862                         bNeedExportBreakHere = false;
2863                     }
2864                     // No need to create a "fake" section if this is the end of the document,
2865                     // except to emulate balanced columns.
2866                     else if ( nColumnCount < 2 && aIdx == m_rDoc.GetNodes().GetEndOfContent() )
2867                         bNeedExportBreakHere = false;
2868                 }
2869 
2870                 if (bNeedExportBreakHere)  //#120140# End of check
2871                 {
2872                     ReplaceCr( char(0xc) ); // indicator for Page/Section-Break
2873 
2874                     const SwSectionFormat* pParentFormat = rSect.GetFormat()->GetParent();
2875                     if ( !pParentFormat )
2876                         pParentFormat = reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1));
2877 
2878                     sal_uLong nRstLnNum;
2879                     if ( aIdx.GetNode().IsContentNode() )
2880                         nRstLnNum = static_cast<SwContentNode&>(aIdx.GetNode()).GetSwAttrSet().
2881                                                 GetLineNumber().GetStartValue();
2882                     else
2883                         nRstLnNum = 0;
2884 
2885                     AppendSection( m_pCurrentPageDesc, pParentFormat, nRstLnNum );
2886                 }
2887                 else
2888                 {
2889                     OutputEndNode( *rNd.GetEndNode() );
2890                 }
2891             }
2892         }
2893         else if ( rNd.IsStartNode() )
2894         {
2895             OutputStartNode( *rNd.GetStartNode() );
2896         }
2897         else if ( rNd.IsEndNode() )
2898         {
2899             OutputEndNode( *rNd.GetEndNode() );
2900         }
2901 
2902         if ( &rNd == &rNd.GetNodes().GetEndOfContent() )
2903             break;
2904 
2905         const SwNode * pCurrentNode = &m_pCurPam->GetPoint()->nNode.GetNode();
2906         const SwNode * pNextNode = m_pTableInfo->getNextNode(pCurrentNode);
2907 
2908         if (pCurrentNode == pNextNode)
2909         {
2910             SAL_WARN("sw.ww8", "loop in TableInfo");
2911             pNextNode = nullptr;
2912         }
2913 
2914         if (pNextNode != nullptr)
2915             m_pCurPam->GetPoint()->nNode.Assign(*pNextNode);
2916         else
2917             ++m_pCurPam->GetPoint()->nNode;
2918 
2919         sal_uLong nPos = m_pCurPam->GetPoint()->nNode.GetIndex();
2920         ::SetProgressState( nPos, m_pCurPam->GetDoc().GetDocShell() );
2921     }
2922 
2923     SAL_INFO( "sw.ww8.level2", "</WriteText>" );
2924 }
2925 
WriteMainText()2926 void WW8Export::WriteMainText()
2927 {
2928     SAL_INFO( "sw.ww8.level2", "<WriteMainText>" );
2929 
2930     pFib->m_fcMin = Strm().Tell();
2931 
2932     m_pCurPam->GetPoint()->nNode = m_rDoc.GetNodes().GetEndOfContent().StartOfSectionNode()->GetIndex();
2933 
2934     WriteText();
2935 
2936     if( 0 == Strm().Tell() - pFib->m_fcMin )  // no text ?
2937         WriteCR();                  // then CR at the end ( otherwise WW will complain )
2938 
2939     pFib->m_ccpText = Fc2Cp( Strm().Tell() );
2940     m_pFieldMain->Finish( pFib->m_ccpText, 0 );
2941 
2942                     // ccpText includes Footnote and KF-text
2943                     // therefore pFib->ccpText may get updated as well
2944     // save the StyleId of the last paragraph. Because WW97 take the style
2945     // from the last CR, that will be written after footer/Header/footnotes/
2946     // annotation etc.
2947     const SwTextNode* pLastNd = m_pCurPam->GetMark()->nNode.GetNode().GetTextNode();
2948     if( pLastNd )
2949         m_nLastFormatId = GetId( static_cast<SwTextFormatColl&>(pLastNd->GetAnyFormatColl()) );
2950 
2951     SAL_INFO( "sw.ww8.level2", "</WriteMainText>" );
2952 }
2953 
IsInTable() const2954 bool MSWordExportBase::IsInTable() const
2955 {
2956     bool bResult = false;
2957 
2958     if (m_pCurPam != nullptr)
2959     {
2960         SwNode& rNode = m_pCurPam->GetNode();
2961 
2962         if (m_pTableInfo)
2963         {
2964             ww8::WW8TableNodeInfo::Pointer_t pTableNodeInfo = m_pTableInfo->getTableNodeInfo(&rNode);
2965 
2966             if (pTableNodeInfo && pTableNodeInfo->getDepth() > 0)
2967             {
2968                 bResult = true;
2969             }
2970         }
2971     }
2972 
2973     return bResult;
2974 }
2975 
2976 typedef ww8::WW8Sttb< ww8::WW8Struct >  WW8SttbAssoc;
2977 
WriteFkpPlcUsw()2978 void WW8Export::WriteFkpPlcUsw()
2979 {
2980     // Graphics in the data stream
2981     m_pGrf->Write();                          // Graphics
2982 
2983     // output into WordDocument stream
2984     m_pChpPlc->WriteFkps();                   // Fkp.Chpx
2985     m_pPapPlc->WriteFkps();                   // Fkp.Papx
2986     pSepx->WriteSepx( Strm() );             // Sepx
2987 
2988     // output into Table stream
2989     m_pStyles->OutputStylesTable();           // for WW8 StyleTab
2990     pFootnote->WritePlc( *this );                // Footnote-Ref & Text Plc
2991     pEdn->WritePlc( *this );                // Endnote-Ref & Text Plc
2992     m_pTextBxs->WritePlc( *this );             // Textbox Text Plc
2993     m_pHFTextBxs->WritePlc( *this );           // Head/Foot-Textbox Text Plc
2994     m_pAtn->WritePlc( *this );                // Annotation-Ref & Text Plc
2995 
2996     pSepx->WritePlcSed( *this );            // Slcx.PlcSed
2997     pSepx->WritePlcHdd( *this );            // Slcx.PlcHdd
2998 
2999     m_pChpPlc->WritePlc();                    // Plcx.Chpx
3000     m_pPapPlc->WritePlc();                    // Plcx.Papx
3001 
3002     if( m_pRedlAuthors )
3003         m_pRedlAuthors->Write( GetWriter() );       // sttbfRMark (RedlineAuthors)
3004     m_pFieldMain->Write( *this );               // Fields ( Main Text )
3005     m_pFieldHdFt->Write( *this );               // Fields ( Header/Footer )
3006     m_pFieldFootnote->Write( *this );                // Fields ( FootNotes )
3007     m_pFieldEdn->Write( *this );                // Fields ( EndNotes )
3008     m_pFieldAtn->Write( *this );                // Fields ( Annotations )
3009     m_pFieldTextBxs->Write( *this );             // Fields ( Textboxes )
3010     m_pFieldHFTextBxs->Write( *this );           // Fields ( Head/Foot-Textboxes )
3011 
3012     if (m_pEscher || m_rDoc.ContainsMSVBasic())
3013     {
3014         /*
3015          Every time MS 2000 creates an escher stream there is always
3016          an ObjectPool dir (even if empty). It turns out that if a copy of
3017          MS 2000 is used to open a document that contains escher graphics
3018          exported from StarOffice without this empty dir then *if* that
3019          copy of MS Office has never been used to open a MSOffice document
3020          that has escher graphics (and an ObjectPool dir of course) and
3021          that copy of office has not been used to draw escher graphics then
3022          our exported graphics do not appear. Once you do open a ms
3023          document with escher graphics or draw an escher graphic with that
3024          copy of word, then all documents from staroffice that contain
3025          escher work from then on. Tricky to track down, some sort of late
3026          binding trickery in MS where solely for first time initialization
3027          the existence of an ObjectPool dir is necessary for triggering
3028          some magic.
3029         */
3030         // avoid memory leak #i120098#, the unnamed obj will be released in destructor.
3031         xEscherStg = GetWriter().GetStorage().OpenSotStorage(SL::aObjectPool);
3032     }
3033 
3034     // dggInfo - escher stream
3035     WriteEscher();
3036 
3037     m_pSdrObjs->WritePlc( *this );
3038     m_pHFSdrObjs->WritePlc( *this );
3039     // spamom - office drawing table
3040     // spahdr - header office drawing table
3041 
3042     m_pBkmks->Write( *this );                 // Bookmarks - sttbfBkmk/
3043                                             // plcfBkmkf/plcfBkmkl
3044     m_pFactoids->Write(*this);
3045 
3046     WriteNumbering();
3047 
3048     RestoreMacroCmds();
3049 
3050     m_pMagicTable->Write( *this );
3051 
3052     m_pPiece->WritePc( *this );               // Piece-Table
3053     m_aFontHelper.WriteFontTable(pTableStrm, *pFib); // FFNs
3054 
3055     //Convert OOo asian typography into MS typography structure
3056     ExportDopTypography(pDop->doptypography);
3057 
3058     WriteDop( *this );                      // Document-Properties
3059 
3060     // Write SttbfAssoc
3061     WW8SttbAssoc * pSttbfAssoc = dynamic_cast<WW8SttbAssoc *>
3062         (m_rDoc.getIDocumentExternalData().getExternalData(::sw::tExternalDataType::STTBF_ASSOC).get());
3063 
3064     if ( pSttbfAssoc )                      // #i106057#
3065     {
3066         std::vector<OUString> aStrings(pSttbfAssoc->getStrings());
3067         WriteAsStringTable(aStrings, pFib->m_fcSttbfAssoc,
3068                            pFib->m_lcbSttbfAssoc);
3069     }
3070 
3071     Strm().Seek( 0 );
3072 
3073     // Reclaim stored FIB data from document.
3074     ::ww8::WW8FibData * pFibData = dynamic_cast<ww8::WW8FibData *>
3075           (m_rDoc.getIDocumentExternalData().getExternalData(::sw::tExternalDataType::FIB).get());
3076 
3077     if ( pFibData )
3078     {
3079     pFib->m_fReadOnlyRecommended =
3080         pFibData->getReadOnlyRecommended();
3081     pFib->m_fWriteReservation =
3082         pFibData->getWriteReservation();
3083     }
3084 
3085     pFib->Write( Strm() );  // FIB
3086 }
3087 
StoreDoc1()3088 void WW8Export::StoreDoc1()
3089 {
3090     bool bNeedsFinalPara = false;
3091     // Start of Text ( overwrite )
3092     SwWW8Writer::FillUntil( Strm(), pFib->m_fcMin );
3093 
3094     WriteMainText();                    // main text
3095     sal_uInt8 nSprmsLen;
3096     sal_uInt8 *pLastSprms = m_pPapPlc->CopyLastSprms(nSprmsLen);
3097 
3098     bNeedsFinalPara |= pFootnote->WriteText( *this );         // Footnote-Text
3099     bNeedsFinalPara |= pSepx->WriteKFText( *this );          // K/F-Text
3100     bNeedsFinalPara |= m_pAtn->WriteText( *this );         // Annotation-Text
3101     bNeedsFinalPara |= pEdn->WriteText( *this );         // EndNote-Text
3102 
3103     // create the escher streams
3104     CreateEscher();
3105 
3106     bNeedsFinalPara |= m_pTextBxs->WriteText( *this );  //Textbox Text Plc
3107     bNeedsFinalPara |= m_pHFTextBxs->WriteText( *this );//Head/Foot-Textbox Text Plc
3108 
3109     if (bNeedsFinalPara)
3110     {
3111         WriteCR();
3112         m_pPapPlc->AppendFkpEntry(Strm().Tell(), nSprmsLen, pLastSprms);
3113     }
3114     delete[] pLastSprms;
3115 
3116     pSepx->Finish( Fc2Cp( Strm().Tell() ));// Text + Footnote + HdFt as section end
3117     m_pMagicTable->Finish( Fc2Cp( Strm().Tell() ),0);
3118 
3119     pFib->m_fcMac = Strm().Tell();        // End of all texts
3120 
3121     WriteFkpPlcUsw();                   // FKP, PLC, ...
3122 }
3123 
AddLinkTarget(std::u16string_view rURL)3124 void MSWordExportBase::AddLinkTarget(std::u16string_view rURL)
3125 {
3126     if( rURL.empty() || rURL[0] != '#' )
3127         return;
3128 
3129     OUString aURL( BookmarkToWriter( rURL.substr( 1 ) ) );
3130     sal_Int32 nPos = aURL.lastIndexOf( cMarkSeparator );
3131 
3132     if( nPos < 2 )
3133         return;
3134 
3135     OUString sCmp = aURL.copy(nPos+1).replaceAll(" ", "");
3136     if( sCmp.isEmpty() )
3137         return;
3138 
3139     sCmp = sCmp.toAsciiLowerCase();
3140     sal_uLong nIdx = 0;
3141     bool noBookmark = false;
3142 
3143     if( sCmp == "outline" )
3144     {
3145         SwPosition aPos(*m_pCurPam->GetPoint());
3146         OUString aName(BookmarkToWriter(aURL.subView(0, nPos)));
3147         // If we can find the outline this bookmark refers to
3148         // save the name of the bookmark and the
3149         // node index number of where it points to
3150         if( m_rDoc.GotoOutline( aPos, aName ) )
3151         {
3152             nIdx = aPos.nNode.GetIndex();
3153             noBookmark = true;
3154         }
3155     }
3156     else if( sCmp == "graphic" )
3157     {
3158         SwNodeIndex* pIdx;
3159         OUString aName(BookmarkToWriter(aURL.subView(0, nPos)));
3160         const SwFlyFrameFormat* pFormat = m_rDoc.FindFlyByName(aName, SwNodeType::Grf);
3161         if (pFormat && nullptr != (pIdx = const_cast<SwNodeIndex*>(pFormat->GetContent().GetContentIdx())))
3162         {
3163             nIdx = pIdx->GetNext()->GetIndex();
3164             noBookmark = true;
3165         }
3166     }
3167     else if( sCmp == "frame" )
3168     {
3169         SwNodeIndex* pIdx;
3170         OUString aName(BookmarkToWriter(aURL.subView(0, nPos)));
3171         const SwFlyFrameFormat* pFormat = m_rDoc.FindFlyByName(aName, SwNodeType::Text);
3172         if (pFormat && nullptr != (pIdx = const_cast<SwNodeIndex*>(pFormat->GetContent().GetContentIdx())))
3173         {
3174             nIdx = pIdx->GetIndex() + 1;
3175             noBookmark = true;
3176         }
3177     }
3178     else if( sCmp == "ole" )
3179     {
3180         SwNodeIndex* pIdx;
3181         OUString aName(BookmarkToWriter(aURL.subView(0, nPos)));
3182         const SwFlyFrameFormat* pFormat = m_rDoc.FindFlyByName(aName, SwNodeType::Ole);
3183         if (pFormat && nullptr != (pIdx = const_cast<SwNodeIndex*>(pFormat->GetContent().GetContentIdx())))
3184         {
3185             nIdx = pIdx->GetNext()->GetIndex();
3186             noBookmark = true;
3187         }
3188     }
3189     else if( sCmp == "region" )
3190     {
3191         SwNodeIndex* pIdx;
3192         OUString aName(BookmarkToWriter(aURL.subView(0, nPos)));
3193         for (const SwSectionFormat* pFormat : m_rDoc.GetSections())
3194         {
3195             if (aName == pFormat->GetSection()->GetSectionName()
3196                 && nullptr != (pIdx = const_cast<SwNodeIndex*>(pFormat->GetContent().GetContentIdx())))
3197             {
3198                 nIdx = pIdx->GetIndex() + 1;
3199                 noBookmark = true;
3200                 break;
3201             }
3202         }
3203     }
3204     else if( sCmp == "table" )
3205     {
3206         OUString aName(BookmarkToWriter(aURL.subView(0, nPos)));
3207         const SwTable* pTable = SwTable::FindTable(m_rDoc.FindTableFormatByName(aName));
3208         if (pTable)
3209         {
3210             SwTableNode* pTableNode = const_cast<SwTableNode*>(pTable->GetTabSortBoxes()[1]->GetSttNd()->FindTableNode());
3211             if (pTableNode)
3212             {
3213                 nIdx = pTableNode->GetIndex() + 2;
3214                 noBookmark = true;
3215             }
3216         }
3217     }
3218     else if (sCmp == "toxmark")
3219     {
3220         OUString const name(aURL.copy(0, nPos));
3221         OUString const nameDecoded(INetURLObject::decode(name,
3222                                INetURLObject::DecodeMechanism::WithCharset));
3223         std::optional<std::pair<SwTOXMark, sal_Int32>> const tmp(
3224             sw::PrepareJumpToTOXMark(m_rDoc, nameDecoded));
3225         if (tmp)
3226         {
3227             SwTOXMark const* pMark(&tmp->first);
3228             for (sal_Int32 i = 0; i < tmp->second; ++i)
3229             {
3230                 pMark = &m_rDoc.GotoTOXMark(*pMark, TOX_SAME_NXT, true);
3231             }
3232             if (pMark != &tmp->first)
3233             {
3234                 m_TOXMarkBookmarksByURL.emplace(aURL, name);
3235                 m_TOXMarkBookmarksByTOXMark.emplace(pMark, nameDecoded);
3236             }
3237         }
3238     }
3239     if (noBookmark)
3240     {
3241         aBookmarkPair aImplicitBookmark;
3242         aImplicitBookmark.first = aURL;
3243         aImplicitBookmark.second = nIdx;
3244         m_aImplicitBookmarks.push_back(aImplicitBookmark);
3245     }
3246 }
3247 
CollectOutlineBookmarks(const SwDoc & rDoc)3248 void MSWordExportBase::CollectOutlineBookmarks(const SwDoc &rDoc)
3249 {
3250     for (const SfxPoolItem* pItem : rDoc.GetAttrPool().GetItemSurrogates(RES_TXTATR_INETFMT))
3251     {
3252         auto pINetFormat = dynamic_cast<const SwFormatINetFormat*>(pItem);
3253         if (!pINetFormat)
3254             continue;
3255 
3256         const SwTextINetFormat* pTextAttr = pINetFormat->GetTextINetFormat();
3257         if (!pTextAttr)
3258             continue;
3259 
3260         const SwTextNode* pTextNd = pTextAttr->GetpTextNode();
3261         if (!pTextNd)
3262             continue;
3263 
3264         if (!pTextNd->GetNodes().IsDocNodes())
3265             continue;
3266 
3267         AddLinkTarget( pINetFormat->GetValue() );
3268     }
3269 
3270     for (const SfxPoolItem* pItem : rDoc.GetAttrPool().GetItemSurrogates(RES_URL))
3271     {
3272         auto pURL = dynamic_cast<const SwFormatURL*>(pItem);
3273         if (!pURL)
3274             continue;
3275 
3276         AddLinkTarget(pURL->GetURL());
3277         const ImageMap *pIMap = pURL->GetMap();
3278         if (!pIMap)
3279             continue;
3280 
3281         for (size_t i=0; i < pIMap->GetIMapObjectCount(); ++i)
3282         {
3283             const IMapObject* pObj = pIMap->GetIMapObject(i);
3284             if (!pObj)
3285                 continue;
3286             AddLinkTarget( pObj->GetURL() );
3287         }
3288     }
3289 }
3290 
3291 namespace
3292 {
3293     const sal_uLong WW_BLOCKSIZE = 0x200;
3294 
EncryptRC4(msfilter::MSCodec_Std97 & rCtx,SvStream & rIn,SvStream & rOut)3295     ErrCode EncryptRC4(msfilter::MSCodec_Std97& rCtx, SvStream &rIn, SvStream &rOut)
3296     {
3297         sal_uLong nLen = rIn.TellEnd();
3298         rIn.Seek(0);
3299 
3300         sal_uInt8 in[WW_BLOCKSIZE];
3301         for (std::size_t nI = 0, nBlock = 0; nI < nLen; nI += WW_BLOCKSIZE, ++nBlock)
3302         {
3303             std::size_t nBS = std::min(nLen - nI, WW_BLOCKSIZE);
3304             nBS = rIn.ReadBytes(in, nBS);
3305             if (!rCtx.InitCipher(nBlock)) {
3306                 return ERRCODE_IO_NOTSUPPORTED;
3307             }
3308             rCtx.Encode(in, nBS, in, nBS);
3309             rOut.WriteBytes(in, nBS);
3310         }
3311         return ERRCODE_NONE;
3312     }
3313 }
3314 
ExportDocument(bool bWriteAll)3315 ErrCode MSWordExportBase::ExportDocument( bool bWriteAll )
3316 {
3317     m_nCharFormatStart = DEFAULT_STYLES_COUNT;
3318     m_nFormatCollStart = m_nCharFormatStart + m_rDoc.GetCharFormats()->size() - 1;
3319 
3320     m_bStyDef = m_bBreakBefore = m_bOutKF =
3321         m_bOutFlyFrameAttrs = m_bOutPageDescs = m_bOutTable = m_bOutFirstPage =
3322         m_bOutGrf = m_bInWriteEscher = m_bStartTOX =
3323         m_bInWriteTOX = false;
3324 
3325     m_bFootnoteAtTextEnd = m_bEndAtTextEnd = true;
3326 
3327     m_pParentFrame = nullptr;
3328     m_pFlyOffset = nullptr;
3329     m_eNewAnchorType = RndStdIds::FLY_AT_PAGE;
3330     m_nTextTyp = TXT_MAINTEXT;
3331     m_nStyleBeforeFly = m_nLastFormatId = 0;
3332     m_pStyAttr = nullptr;
3333     m_pCurrentStyle = nullptr;
3334     m_pOutFormatNode = nullptr;
3335     m_pEscher = nullptr;
3336     m_pRedlAuthors = nullptr;
3337     m_aTOXArr.clear();
3338 
3339     if ( !m_pOLEExp )
3340     {
3341         sal_uInt32 nSvxMSDffOLEConvFlags = 0;
3342         const SvtFilterOptions& rOpt = SvtFilterOptions::Get();
3343         if ( rOpt.IsMath2MathType() )
3344             nSvxMSDffOLEConvFlags |= OLE_STARMATH_2_MATHTYPE;
3345         if ( rOpt.IsWriter2WinWord() )
3346             nSvxMSDffOLEConvFlags |= OLE_STARWRITER_2_WINWORD;
3347         if ( rOpt.IsCalc2Excel() )
3348             nSvxMSDffOLEConvFlags |= OLE_STARCALC_2_EXCEL;
3349         if ( rOpt.IsImpress2PowerPoint() )
3350             nSvxMSDffOLEConvFlags |= OLE_STARIMPRESS_2_POWERPOINT;
3351 
3352         m_pOLEExp.reset(new SvxMSExportOLEObjects( nSvxMSDffOLEConvFlags ));
3353     }
3354 
3355     if ( !m_pOCXExp && m_rDoc.GetDocShell() )
3356         m_pOCXExp.reset(new SwMSConvertControls(m_rDoc.GetDocShell(), m_pCurPam.get()));
3357 
3358     // #i81405# - Collect anchored objects before changing the redline mode.
3359     m_aFrames = GetFrames( m_rDoc, bWriteAll? nullptr : m_pOrigPam );
3360 
3361     m_nOrigRedlineFlags = m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
3362 
3363     SwRootFrame const*const pLayout(m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout());
3364     m_bOrigShowChanges = pLayout == nullptr || !pLayout->IsHideRedlines();
3365 
3366     if ( !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
3367     {
3368         //restored to original state by SwWriter::Write
3369         m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags(m_nOrigRedlineFlags |
3370                                                          RedlineFlags::ShowDelete |
3371                                                          RedlineFlags::ShowInsert);
3372     }
3373 
3374     // fix the SwPositions in m_aFrames after SetRedlineFlags
3375     UpdateFramePositions(m_aFrames);
3376 
3377     m_aFontHelper.InitFontTable(m_rDoc);
3378     GatherChapterFields();
3379 
3380     CollectOutlineBookmarks(m_rDoc);
3381 
3382     // make unique OrdNums (Z-Order) for all drawing-/fly Objects
3383     if ( m_rDoc.getIDocumentDrawModelAccess().GetDrawModel() )
3384         m_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->GetPage( 0 )->RecalcObjOrdNums();
3385 
3386     ErrCode err = ExportDocument_Impl();
3387 
3388     m_aFrames.clear();
3389 
3390     // park m_pCurPam in a "safe place" now that document is fully exported
3391     // before toggling redline mode to avoid ~SwIndexReg assert e.g. export
3392     // ooo103014-1.odt to .doc
3393     // park m_pOrigPam as well, as needed for exporting abi9915-1.odt to doc
3394     m_pOrigPam->DeleteMark();
3395     *m_pOrigPam->GetPoint() = SwPosition(m_rDoc.GetNodes().GetEndOfContent());
3396     static_cast<SwPaM&>(*m_pCurPam) = *m_pOrigPam;
3397 
3398     m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags(m_nOrigRedlineFlags);
3399 
3400     return err;
3401 }
3402 
InitStd97CodecUpdateMedium(::msfilter::MSCodec_Std97 & rCodec)3403 bool SwWW8Writer::InitStd97CodecUpdateMedium( ::msfilter::MSCodec_Std97& rCodec )
3404 {
3405     uno::Sequence< beans::NamedValue > aEncryptionData;
3406 
3407     if ( mpMedium )
3408     {
3409         const SfxUnoAnyItem* pEncryptionDataItem = SfxItemSet::GetItem<SfxUnoAnyItem>(mpMedium->GetItemSet(), SID_ENCRYPTIONDATA, false);
3410         if ( pEncryptionDataItem && ( pEncryptionDataItem->GetValue() >>= aEncryptionData ) && !rCodec.InitCodec( aEncryptionData ) )
3411         {
3412             OSL_ENSURE( false, "Unexpected EncryptionData!" );
3413             aEncryptionData.realloc( 0 );
3414         }
3415 
3416         if ( !aEncryptionData.hasElements() )
3417         {
3418             // try to generate the encryption data based on password
3419             const SfxStringItem* pPasswordItem = SfxItemSet::GetItem<SfxStringItem>(mpMedium->GetItemSet(), SID_PASSWORD, false);
3420             if ( pPasswordItem && !pPasswordItem->GetValue().isEmpty() && pPasswordItem->GetValue().getLength() <= 15 )
3421             {
3422                 // Generate random number with a seed of time as salt.
3423                 rtlRandomPool aRandomPool = rtl_random_createPool ();
3424                 sal_uInt8 pDocId[ 16 ];
3425                 rtl_random_getBytes( aRandomPool, pDocId, 16 );
3426 
3427                 rtl_random_destroyPool( aRandomPool );
3428 
3429                 sal_uInt16 aPassword[16] = {};
3430 
3431                 const OUString& sPassword(pPasswordItem->GetValue());
3432                 for ( sal_Int32 nChar = 0; nChar < sPassword.getLength(); ++nChar )
3433                     aPassword[nChar] = sPassword[nChar];
3434 
3435                 rCodec.InitKey( aPassword, pDocId );
3436                 aEncryptionData = rCodec.GetEncryptionData();
3437 
3438                 mpMedium->GetItemSet()->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::makeAny( aEncryptionData ) ) );
3439             }
3440         }
3441 
3442         if ( aEncryptionData.hasElements() )
3443             mpMedium->GetItemSet()->ClearItem( SID_PASSWORD );
3444     }
3445 
3446     // nonempty encryption data means here that the codec was successfully initialized
3447     return aEncryptionData.hasElements();
3448 }
3449 
ExportDocument_Impl()3450 ErrCode WW8Export::ExportDocument_Impl()
3451 {
3452     PrepareStorage();
3453 
3454     pFib.reset(new WW8Fib(8, m_bDot));
3455 
3456     tools::SvRef<SotStorageStream> xWwStrm( GetWriter().GetStorage().OpenSotStream( m_aMainStg ) );
3457     tools::SvRef<SotStorageStream> xTableStrm( xWwStrm ), xDataStrm( xWwStrm );
3458     xWwStrm->SetBufferSize( 32768 );
3459 
3460     pFib->m_fWhichTableStm = true;
3461     xTableStrm = GetWriter().GetStorage().OpenSotStream(SL::a1Table, StreamMode::STD_WRITE);
3462     xDataStrm = GetWriter().GetStorage().OpenSotStream(SL::aData, StreamMode::STD_WRITE);
3463 
3464     xDataStrm->SetBufferSize( 32768 );  // for graphics
3465     xTableStrm->SetBufferSize( 16384 ); // for the Font-/Style-Table, etc.
3466 
3467     xTableStrm->SetEndian( SvStreamEndian::LITTLE );
3468     xDataStrm->SetEndian( SvStreamEndian::LITTLE );
3469 
3470     GetWriter().SetStream( xWwStrm.get() );
3471     pTableStrm = xTableStrm.get();
3472     pDataStrm = xDataStrm.get();
3473 
3474     Strm().SetEndian( SvStreamEndian::LITTLE );
3475 
3476     utl::TempFile aTempMain;
3477     aTempMain.EnableKillingFile();
3478     utl::TempFile aTempTable;
3479     aTempTable.EnableKillingFile();
3480     utl::TempFile aTempData;
3481     aTempData.EnableKillingFile();
3482 
3483     msfilter::MSCodec_Std97 aCtx;
3484     bool bEncrypt = GetWriter().InitStd97CodecUpdateMedium(aCtx);
3485     if ( bEncrypt )
3486     {
3487         GetWriter().SetStream(
3488             aTempMain.GetStream( StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE ) );
3489 
3490         pTableStrm = aTempTable.GetStream( StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE );
3491 
3492         pDataStrm = aTempData.GetStream( StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE );
3493 
3494         sal_uInt8 const aRC4EncryptionHeader[ 52 ] = {0};
3495         pTableStrm->WriteBytes(aRC4EncryptionHeader, 52);
3496     }
3497 
3498     // Default: "Standard"
3499     pSepx.reset(new WW8_WrPlcSepx( *this ));                         // Sections/headers/footers
3500 
3501     pFootnote.reset(new WW8_WrPlcFootnoteEdn( TXT_FTN ));                      // Footnotes
3502     pEdn.reset(new WW8_WrPlcFootnoteEdn( TXT_EDN ));                      // Endnotes
3503     m_pAtn = new WW8_WrPlcAnnotations;                                 // PostIts
3504     m_pFactoids.reset(new WW8_WrtFactoids); // Smart tags.
3505     m_pTextBxs = new WW8_WrPlcTextBoxes( TXT_TXTBOX );
3506     m_pHFTextBxs = new WW8_WrPlcTextBoxes( TXT_HFTXTBOX );
3507 
3508     m_pSdrObjs = new MainTextPlcDrawObj;   // Draw-/Fly-Objects for main text
3509     m_pHFSdrObjs = new HdFtPlcDrawObj;    // Draw-/Fly-Objects for header/footer
3510 
3511     m_pBkmks = new WW8_WrtBookmarks;                          // Bookmarks
3512     GetWriter().CreateBookmarkTable();
3513 
3514     m_pPapPlc.reset(new WW8_WrPlcPn( *this, PAP, pFib->m_fcMin ));
3515     m_pChpPlc.reset(new WW8_WrPlcPn( *this, CHP, pFib->m_fcMin ));
3516     pO.reset(new ww::bytes);
3517     m_pStyles.reset(new MSWordStyles( *this ));
3518     m_pFieldMain.reset(new WW8_WrPlcField( 2, TXT_MAINTEXT ));
3519     m_pFieldHdFt.reset(new WW8_WrPlcField( 2, TXT_HDFT ));
3520     m_pFieldFootnote.reset(new WW8_WrPlcField( 2, TXT_FTN ));
3521     m_pFieldEdn.reset(new WW8_WrPlcField( 2, TXT_EDN ));
3522     m_pFieldAtn.reset(new WW8_WrPlcField( 2, TXT_ATN ));
3523     m_pFieldTextBxs.reset(new WW8_WrPlcField( 2, TXT_TXTBOX ));
3524     m_pFieldHFTextBxs.reset(new WW8_WrPlcField( 2, TXT_HFTXTBOX ));
3525 
3526     m_pMagicTable.reset(new WW8_WrMagicTable);
3527 
3528     m_pGrf.reset(new SwWW8WrGrf( *this ));
3529     m_pPiece = new WW8_WrPct( pFib->m_fcMin );
3530     pDop.reset(new WW8Dop);
3531 
3532     pDop->fRevMarking = bool( RedlineFlags::On & m_nOrigRedlineFlags );
3533     SwRootFrame const*const pLayout(m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout());
3534     pDop->fRMView = pLayout == nullptr || !pLayout->IsHideRedlines();
3535     pDop->fRMPrint = pDop->fRMView;
3536 
3537     // set AutoHyphenation flag if found in default para style
3538     const SfxPoolItem* pItem;
3539     SwTextFormatColl* pStdTextFormatColl =
3540         m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD, false);
3541     if (pStdTextFormatColl && SfxItemState::SET == pStdTextFormatColl->GetItemState(
3542         RES_PARATR_HYPHENZONE, false, &pItem))
3543     {
3544         pDop->fAutoHyphen = static_cast<const SvxHyphenZoneItem*>(pItem)->IsHyphen();
3545     }
3546 
3547     StoreDoc1();
3548 
3549     ErrCode err = ERRCODE_NONE;
3550     if ( bEncrypt )
3551     {
3552         SvStream *pStrmTemp, *pTableStrmTemp, *pDataStrmTemp;
3553         pStrmTemp = xWwStrm.get();
3554         pTableStrmTemp = xTableStrm.get();
3555         pDataStrmTemp = xDataStrm.get();
3556 
3557         if ( pDataStrmTemp && pDataStrmTemp != pStrmTemp) {
3558             err = EncryptRC4(aCtx, *pDataStrm, *pDataStrmTemp);
3559             if (err != ERRCODE_NONE) {
3560                 goto done;
3561             }
3562         }
3563 
3564         err = EncryptRC4(aCtx, *pTableStrm, *pTableStrmTemp);
3565         if (err != ERRCODE_NONE) {
3566             goto done;
3567         }
3568 
3569         // Write Unencrypted Header 52 bytes to the start of the table stream
3570         // EncryptionVersionInfo (4 bytes): A Version structure where Version.vMajor MUST be 0x0001, and Version.vMinor MUST be 0x0001.
3571         pTableStrmTemp->Seek( 0 );
3572         pTableStrmTemp->WriteUInt32( 0x10001 ); // nEncType
3573 
3574         sal_uInt8 pDocId[16];
3575         aCtx.GetDocId( pDocId );
3576 
3577         sal_uInt8 pSaltData[16];
3578         sal_uInt8 pSaltDigest[16];
3579         aCtx.GetEncryptKey( pDocId, pSaltData, pSaltDigest );
3580 
3581         pTableStrmTemp->WriteBytes(pDocId, 16);
3582         pTableStrmTemp->WriteBytes(pSaltData, 16);
3583         pTableStrmTemp->WriteBytes(pSaltDigest, 16);
3584 
3585         err = EncryptRC4(aCtx, GetWriter().Strm(), *pStrmTemp);
3586         if (err != ERRCODE_NONE) {
3587             goto done;
3588         }
3589 
3590         // Write Unencrypted Fib 68 bytes to the start of the workdocument stream
3591         pFib->m_fEncrypted = true; // fEncrypted indicates the document is encrypted.
3592         pFib->m_fObfuscated = false; // Must be 0 for RC4.
3593         pFib->m_nHash = 0x34; // encrypt header bytes count of table stream.
3594         pFib->m_nKey = 0; // lkey2 must be 0 for RC4.
3595 
3596         pStrmTemp->Seek( 0 );
3597         pFib->WriteHeader( *pStrmTemp );
3598     done:;
3599     }
3600 
3601     m_pGrf.reset();
3602     m_pMagicTable.reset();
3603     m_pFieldFootnote.reset();
3604     m_pFieldTextBxs.reset();
3605     m_pFieldHFTextBxs.reset();
3606     m_pFieldAtn.reset();
3607     m_pFieldEdn.reset();
3608     m_pFieldHdFt.reset();
3609     m_pFieldMain.reset();
3610     m_pStyles.reset();
3611     pO.reset();
3612     m_pChpPlc.reset();
3613     m_pPapPlc.reset();
3614     pSepx.reset();
3615 
3616     delete m_pRedlAuthors;
3617     delete m_pSdrObjs;
3618     delete m_pHFSdrObjs;
3619     delete m_pTextBxs;
3620     delete m_pHFTextBxs;
3621     delete m_pAtn;
3622     pEdn.reset();
3623     pFootnote.reset();
3624     delete m_pBkmks;
3625     delete m_pPiece;
3626     pDop.reset();
3627     pFib.reset();
3628     GetWriter().SetStream( nullptr );
3629 
3630     xWwStrm->SetBufferSize( 0 );
3631     xTableStrm->SetBufferSize( 0 );
3632     xDataStrm->SetBufferSize( 0 );
3633     if( 0 == pDataStrm->Seek( STREAM_SEEK_TO_END ))
3634     {
3635         xDataStrm.clear();
3636         pDataStrm = nullptr;
3637         GetWriter().GetStorage().Remove(SL::aData);
3638     }
3639 
3640     return err;
3641 }
3642 
PrepareStorage()3643 void WW8Export::PrepareStorage()
3644 {
3645     static const sal_uInt8 pData[] =
3646     {
3647         0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
3648         0xFF, 0xFF, 0xFF, 0xFF, 0x06, 0x09, 0x02, 0x00,
3649         0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00,
3650         0x00, 0x00, 0x00, 0x46,
3651 
3652         0x18, 0x00, 0x00, 0x00,
3653         'M', 'i', 'c', 'r', 'o', 's', 'o', 'f',
3654         't', ' ', 'W', 'o', 'r', 'd', '-', 'D',
3655         'o', 'k', 'u', 'm', 'e', 'n', 't', 0x0,
3656 
3657         0x0A, 0x00, 0x00, 0x00,
3658         'M', 'S', 'W', 'o', 'r', 'd', 'D', 'o',
3659         'c', 0x0,
3660 
3661         0x10, 0x00, 0x00, 0x00,
3662         'W', 'o', 'r', 'd', '.', 'D', 'o', 'c',
3663         'u', 'm', 'e', 'n', 't', '.', '8', 0x0,
3664 
3665         0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
3666         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
3667     };
3668 
3669     SvGlobalName aGName(MSO_WW8_CLASSID);
3670     GetWriter().GetStorage().SetClass(
3671         aGName, SotClipboardFormatId::NONE, "Microsoft Word-Document");
3672     tools::SvRef<SotStorageStream> xStor( GetWriter().GetStorage().OpenSotStream(sCompObj) );
3673     xStor->WriteBytes(pData, sizeof(pData));
3674 
3675     SwDocShell* pDocShell = m_rDoc.GetDocShell ();
3676     OSL_ENSURE(pDocShell, "no SwDocShell");
3677 
3678     if (!pDocShell)        return;
3679 
3680     uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
3681         pDocShell->GetModel(), uno::UNO_QUERY_THROW);
3682     uno::Reference<document::XDocumentProperties> xDocProps(
3683         xDPS->getDocumentProperties());
3684     OSL_ENSURE(xDocProps.is(), "DocumentProperties is null");
3685 
3686     if (!xDocProps.is())
3687         return;
3688 
3689     if ( SvtFilterOptions::Get().IsEnableWordPreview() )
3690     {
3691         std::shared_ptr<GDIMetaFile> xMetaFile =
3692             pDocShell->GetPreviewMetaFile();
3693         uno::Sequence<sal_Int8> metaFile(
3694             sfx2::convertMetaFile(xMetaFile.get()));
3695         sfx2::SaveOlePropertySet(xDocProps, &GetWriter().GetStorage(), &metaFile);
3696     }
3697     else
3698         sfx2::SaveOlePropertySet( xDocProps, &GetWriter().GetStorage() );
3699 }
3700 
WriteStorage()3701 ErrCode SwWW8Writer::WriteStorage()
3702 {
3703     tools::SvRef<SotStorage> pOrigStg;
3704     uno::Reference< packages::XPackageEncryption > xPackageEncryption;
3705     std::shared_ptr<SvStream> pSotStorageStream;
3706     uno::Sequence< beans::NamedValue > aEncryptionData;
3707     if (mpMedium)
3708     {
3709         // Check for specific encryption requests
3710         const SfxUnoAnyItem* pEncryptionDataItem = SfxItemSet::GetItem<SfxUnoAnyItem>(mpMedium->GetItemSet(), SID_ENCRYPTIONDATA, false);
3711         if (pEncryptionDataItem && (pEncryptionDataItem->GetValue() >>= aEncryptionData))
3712         {
3713             ::comphelper::SequenceAsHashMap aHashData(aEncryptionData);
3714             OUString sCryptoType = aHashData.getUnpackedValueOrDefault("CryptoType", OUString());
3715 
3716             if (sCryptoType.getLength())
3717             {
3718                 uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
3719                 uno::Sequence<uno::Any> aArguments{
3720                     uno::makeAny(beans::NamedValue("Binary", uno::makeAny(true))) };
3721                 xPackageEncryption.set(
3722                     xComponentContext->getServiceManager()->createInstanceWithArgumentsAndContext(
3723                         "com.sun.star.comp.oox.crypto." + sCryptoType, aArguments, xComponentContext), uno::UNO_QUERY);
3724 
3725                 if (xPackageEncryption.is())
3726                 {
3727                     // We have an encryptor
3728                     // Create new temporary storage for content
3729                     pOrigStg = m_pStg;
3730                     pSotStorageStream = std::make_shared<SvMemoryStream>();
3731                     m_pStg = new SotStorage(*pSotStorageStream);
3732                 }
3733             }
3734         }
3735     }
3736 
3737     ErrCode nErrorCode = WriteStorageImpl();
3738 
3739     if (xPackageEncryption.is())
3740     {
3741         m_pStg->Commit();
3742         pSotStorageStream->Seek(0);
3743 
3744         // Encrypt data written into temporary storage
3745         xPackageEncryption->setupEncryption(aEncryptionData);
3746 
3747         uno::Reference<io::XInputStream > xInputStream(new utl::OSeekableInputStreamWrapper(pSotStorageStream.get(), false));
3748         uno::Sequence<beans::NamedValue> aStreams = xPackageEncryption->encrypt(xInputStream);
3749 
3750         m_pStg = pOrigStg;
3751         for (const beans::NamedValue & aStreamData : std::as_const(aStreams))
3752         {
3753             // To avoid long paths split and open substorages recursively
3754             // Splitting paths manually, since comphelper::string::split is trimming special characters like \0x01, \0x09
3755             tools::SvRef<SotStorage> pStorage = m_pStg.get();
3756             OUString sFileName;
3757             sal_Int32 idx = 0;
3758             while (pStorage && idx >= 0)
3759             {
3760                 OUString sPathElem = aStreamData.Name.getToken(0, L'/', idx);
3761                 if (!sPathElem.isEmpty())
3762                 {
3763                     if (idx < 0)
3764                     {
3765                         sFileName = sPathElem;
3766                     }
3767                     else
3768                     {
3769                         pStorage = pStorage->OpenSotStorage(sPathElem);
3770                         if (!pStorage)
3771                             break;
3772                     }
3773                 }
3774             };
3775 
3776             if (!pStorage)
3777             {
3778                 nErrorCode = ERRCODE_IO_GENERAL;
3779                 break;
3780             }
3781 
3782             tools::SvRef<SotStorageStream> pStream = pStorage->OpenSotStream(sFileName);
3783             if (!pStream)
3784             {
3785                 nErrorCode = ERRCODE_IO_GENERAL;
3786                 break;
3787             }
3788             uno::Sequence<sal_Int8> aStreamContent;
3789             aStreamData.Value >>= aStreamContent;
3790             size_t nBytesWritten = pStream->WriteBytes(aStreamContent.getArray(), aStreamContent.getLength());
3791             if (nBytesWritten != static_cast<size_t>(aStreamContent.getLength()))
3792             {
3793                 nErrorCode = ERRCODE_IO_CANTWRITE;
3794                 break;
3795             }
3796         }
3797     }
3798 
3799     return nErrorCode;
3800 }
WriteStorageImpl()3801 ErrCode SwWW8Writer::WriteStorageImpl()
3802 {
3803     // #i34818# - update layout (if present), for SwWriteTable
3804     SwViewShell* pViewShell = m_pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
3805     if( pViewShell != nullptr )
3806         pViewShell->CalcLayout();
3807 
3808     tools::Long nMaxNode = m_pDoc->GetNodes().Count();
3809     ::StartProgress( STR_STATSTR_W4WWRITE, 0, nMaxNode, m_pDoc->GetDocShell() );
3810 
3811     // Respect table at the beginning of the document
3812     {
3813         SwTableNode* pTNd = m_pCurrentPam->GetNode().FindTableNode();
3814         if( pTNd && m_bWriteAll )
3815             // start with the table node !!
3816             m_pCurrentPam->GetPoint()->nNode = *pTNd;
3817     }
3818 
3819     // Do the actual export
3820     ErrCode err = ERRCODE_NONE;
3821     {
3822         bool bDot = mpMedium->GetFilter()->GetName().endsWith("Vorlage");
3823         WW8Export aExport(this, *m_pDoc, m_pCurrentPam, m_pOrigPam, bDot);
3824         m_pExport = &aExport;
3825         err = aExport.ExportDocument( m_bWriteAll );
3826         m_pExport = nullptr;
3827     }
3828 
3829     ::EndProgress( m_pDoc->GetDocShell() );
3830     return err;
3831 }
3832 
WriteMedium(SfxMedium &)3833 ErrCode SwWW8Writer::WriteMedium( SfxMedium& )
3834 {
3835     return WriteStorage();
3836 }
3837 
Write(SwPaM & rPaM,SfxMedium & rMed,const OUString * pFileName)3838 ErrCode SwWW8Writer::Write( SwPaM& rPaM, SfxMedium& rMed,
3839                           const OUString* pFileName )
3840 {
3841     mpMedium = &rMed;
3842     ErrCode nRet = StgWriter::Write( rPaM, rMed, pFileName );
3843     mpMedium = nullptr;
3844     return nRet;
3845 }
3846 
MSWordExportBase(SwDoc & rDocument,std::shared_ptr<SwUnoCursor> & pCurrentPam,SwPaM * pOriginalPam)3847 MSWordExportBase::MSWordExportBase( SwDoc& rDocument, std::shared_ptr<SwUnoCursor> & pCurrentPam, SwPaM* pOriginalPam )
3848     : m_aMainStg(sMainStream)
3849     , m_pISet(nullptr)
3850     , m_pPiece(nullptr)
3851     , m_pTopNodeOfHdFtPage(nullptr)
3852     , m_pBkmks(nullptr)
3853     , m_pRedlAuthors(nullptr)
3854     , m_pTableInfo(std::make_shared<ww8::WW8TableInfo>())
3855     , m_nCharFormatStart(0)
3856     , m_nFormatCollStart(0)
3857     , m_nStyleBeforeFly(0)
3858     , m_nLastFormatId(0)
3859     , m_nUniqueList(0)
3860     , m_nHdFtIndex(0)
3861     , m_nOrigRedlineFlags(RedlineFlags::NONE)
3862     , m_bOrigShowChanges(true)
3863     , m_pCurrentPageDesc(nullptr)
3864     , m_bFirstTOCNodeWithSection(false)
3865     , m_pChpIter(nullptr)
3866     , m_pAtn(nullptr)
3867     , m_pTextBxs(nullptr)
3868     , m_pHFTextBxs(nullptr)
3869     , m_pParentFrame(nullptr)
3870     , m_pFlyOffset(nullptr)
3871     , m_eNewAnchorType(RndStdIds::FLY_AS_CHAR)
3872     , m_pStyAttr(nullptr)
3873     , m_pOutFormatNode(nullptr)
3874     , m_pCurrentStyle(nullptr)
3875     , m_pSdrObjs(nullptr)
3876     , m_pHFSdrObjs(nullptr)
3877     , m_pEscher(nullptr)
3878     , m_nTextTyp(0)
3879     , m_bStyDef(false)
3880     , m_bBreakBefore(false)
3881     , m_bOutKF(false)
3882     , m_bOutFlyFrameAttrs(false)
3883     , m_bOutPageDescs(false)
3884     , m_bOutFirstPage(false)
3885     , m_bOutTable(false)
3886     , m_bOutGrf(false)
3887     , m_bInWriteEscher(false)
3888     , m_bStartTOX(false)
3889     , m_bInWriteTOX(false)
3890     , m_bFootnoteAtTextEnd(false)
3891     , m_bEndAtTextEnd(false)
3892     , m_bHasHdr(false)
3893     , m_bHasFtr(false)
3894     , m_bSubstituteBullets(true)
3895     , m_bTabInTOC(false)
3896     , m_bHideTabLeaderAndPageNumbers(false)
3897     , m_bExportModeRTF(false)
3898     , m_bFontSizeWritten(false)
3899     , m_bAddFootnoteTab(false)
3900     , m_rDoc(rDocument)
3901     , m_nCurStart(pCurrentPam->GetPoint()->nNode.GetIndex())
3902     , m_nCurEnd(pCurrentPam->GetMark()->nNode.GetIndex())
3903     , m_pCurPam(pCurrentPam)
3904     , m_pOrigPam(pOriginalPam)
3905 {
3906 }
3907 
~MSWordExportBase()3908 MSWordExportBase::~MSWordExportBase()
3909 {
3910     if (m_pUsedNumTable)           // all used NumRules
3911     {
3912         // clear the part of the list array that was copied from the document
3913         // - it's an auto delete array, so the rest of the array which are
3914         // duplicated lists that were added during the export will be deleted.
3915         m_pUsedNumTable->erase(m_pUsedNumTable->begin(), m_pUsedNumTable->begin() + m_pUsedNumTable->size() - m_nUniqueList);
3916         m_pUsedNumTable.reset();
3917     }
3918     m_pOLEExp.reset();
3919     m_pOCXExp.reset();
3920 }
3921 
WW8Export(SwWW8Writer * pWriter,SwDoc & rDocument,std::shared_ptr<SwUnoCursor> & pCurrentPam,SwPaM * pOriginalPam,bool bDot)3922 WW8Export::WW8Export( SwWW8Writer *pWriter,
3923         SwDoc& rDocument, std::shared_ptr<SwUnoCursor> & pCurrentPam, SwPaM* pOriginalPam,
3924         bool bDot )
3925     : MSWordExportBase( rDocument, pCurrentPam, pOriginalPam )
3926     , pTableStrm(nullptr)
3927     , pDataStrm(nullptr)
3928     , m_bDot(bDot)
3929     , m_pWriter(pWriter)
3930     , m_pAttrOutput(new WW8AttributeOutput(*this))
3931 {
3932 }
3933 
~WW8Export()3934 WW8Export::~WW8Export()
3935 {
3936 }
3937 
AttrOutput() const3938 AttributeOutputBase& WW8Export::AttrOutput() const
3939 {
3940     return *m_pAttrOutput;
3941 }
3942 
Sections() const3943 MSWordSections& WW8Export::Sections() const
3944 {
3945     return *pSepx;
3946 }
3947 
SwWW8Writer(const OUString & rFltName,const OUString & rBaseURL)3948 SwWW8Writer::SwWW8Writer(const OUString& rFltName, const OUString& rBaseURL)
3949     : StgWriter(),
3950       m_pExport( nullptr ),
3951       mpMedium( nullptr )
3952 {
3953     assert(rFltName == FILTER_WW8); // WW6/7 export was removed
3954     (void)rFltName;
3955     SetBaseURL( rBaseURL );
3956 }
3957 
~SwWW8Writer()3958 SwWW8Writer::~SwWW8Writer()
3959 {
3960 }
3961 
SaveOrDelMSVBAStorage_ww8(SfxObjectShell & rDoc,SotStorage & rStor,sal_Bool bSaveInto,const OUString & rStorageName)3962 extern "C" SAL_DLLPUBLIC_EXPORT sal_uInt32 SaveOrDelMSVBAStorage_ww8( SfxObjectShell& rDoc, SotStorage& rStor, sal_Bool bSaveInto, const OUString& rStorageName )
3963 {
3964     SvxImportMSVBasic aTmp( rDoc, rStor );
3965     return sal_uInt32(aTmp.SaveOrDelMSVBAStorage( bSaveInto, rStorageName ));
3966 }
3967 
ExportDOC(const OUString & rFltName,const OUString & rBaseURL,WriterRef & xRet)3968 extern "C" SAL_DLLPUBLIC_EXPORT void ExportDOC( const OUString& rFltName, const OUString& rBaseURL, WriterRef& xRet )
3969 {
3970     xRet = new SwWW8Writer( rFltName, rBaseURL );
3971 }
3972 
GetSaveWarningOfMSVBAStorage_ww8(SfxObjectShell & rDocS)3973 extern "C" SAL_DLLPUBLIC_EXPORT sal_uInt32 GetSaveWarningOfMSVBAStorage_ww8(  SfxObjectShell &rDocS )
3974 {
3975     return sal_uInt32(SvxImportMSVBasic::GetSaveWarningOfMSVBAStorage( rDocS ));
3976 }
3977 
WriteText(WW8Export & rWrt)3978 bool WW8_WrPlcFootnoteEdn::WriteText( WW8Export& rWrt )
3979 {
3980     bool bRet = false;
3981     if (TXT_FTN == nTyp)
3982     {
3983         bRet = WriteGenericText( rWrt, TXT_FTN, rWrt.pFib->m_ccpFootnote );
3984         rWrt.m_pFieldFootnote->Finish( rWrt.Fc2Cp( rWrt.Strm().Tell() ),
3985                             rWrt.pFib->m_ccpText );
3986     }
3987     else
3988     {
3989         bRet = WriteGenericText( rWrt, TXT_EDN, rWrt.pFib->m_ccpEdn );
3990         rWrt.m_pFieldEdn->Finish( rWrt.Fc2Cp( rWrt.Strm().Tell() ),
3991                             rWrt.pFib->m_ccpText + rWrt.pFib->m_ccpFootnote
3992                             + rWrt.pFib->m_ccpHdr + rWrt.pFib->m_ccpAtn );
3993     }
3994     return bRet;
3995 }
3996 
WritePlc(WW8Export & rWrt) const3997 void WW8_WrPlcFootnoteEdn::WritePlc( WW8Export& rWrt ) const
3998 {
3999     if( TXT_FTN == nTyp )
4000     {
4001         WriteGenericPlc( rWrt, TXT_FTN, rWrt.pFib->m_fcPlcffndText,
4002             rWrt.pFib->m_lcbPlcffndText, rWrt.pFib->m_fcPlcffndRef,
4003             rWrt.pFib->m_lcbPlcffndRef );
4004     }
4005     else
4006     {
4007         WriteGenericPlc( rWrt, TXT_EDN, rWrt.pFib->m_fcPlcfendText,
4008             rWrt.pFib->m_lcbPlcfendText, rWrt.pFib->m_fcPlcfendRef,
4009             rWrt.pFib->m_lcbPlcfendRef );
4010     }
4011 }
4012 
WriteText(WW8Export & rWrt)4013 bool WW8_WrPlcAnnotations::WriteText( WW8Export& rWrt )
4014 {
4015     bool bRet = WriteGenericText( rWrt, TXT_ATN, rWrt.pFib->m_ccpAtn );
4016     rWrt.m_pFieldAtn->Finish( rWrt.Fc2Cp( rWrt.Strm().Tell() ),
4017                         rWrt.pFib->m_ccpText + rWrt.pFib->m_ccpFootnote
4018                         + rWrt.pFib->m_ccpHdr );
4019     return bRet;
4020 }
4021 
WritePlc(WW8Export & rWrt) const4022 void WW8_WrPlcAnnotations::WritePlc( WW8Export& rWrt ) const
4023 {
4024     WriteGenericPlc( rWrt, TXT_ATN, rWrt.pFib->m_fcPlcfandText,
4025         rWrt.pFib->m_lcbPlcfandText, rWrt.pFib->m_fcPlcfandRef,
4026         rWrt.pFib->m_lcbPlcfandRef );
4027 }
4028 
WritePlc(WW8Export & rWrt) const4029 void WW8_WrPlcTextBoxes::WritePlc( WW8Export& rWrt ) const
4030 {
4031     if( TXT_TXTBOX == nTyp )
4032     {
4033         WriteGenericPlc( rWrt, nTyp, rWrt.pFib->m_fcPlcftxbxBkd,
4034             rWrt.pFib->m_lcbPlcftxbxBkd, rWrt.pFib->m_fcPlcftxbxText,
4035             rWrt.pFib->m_lcbPlcftxbxText );
4036     }
4037     else
4038     {
4039         WriteGenericPlc( rWrt, nTyp, rWrt.pFib->m_fcPlcfHdrtxbxBkd,
4040             rWrt.pFib->m_lcbPlcfHdrtxbxBkd, rWrt.pFib->m_fcPlcfHdrtxbxText,
4041             rWrt.pFib->m_lcbPlcfHdrtxbxText );
4042     }
4043 }
4044 
RestoreMacroCmds()4045 void WW8Export::RestoreMacroCmds()
4046 {
4047     pFib->m_fcCmds = pTableStrm->Tell();
4048 
4049     uno::Reference < embed::XStorage > xSrcRoot(m_rDoc.GetDocShell()->GetStorage());
4050     try
4051     {
4052         uno::Reference < io::XStream > xSrcStream =
4053                 xSrcRoot->openStreamElement( SL::aMSMacroCmds, embed::ElementModes::READ );
4054         std::unique_ptr<SvStream> pStream = ::utl::UcbStreamHelper::CreateStream( xSrcStream );
4055 
4056         if ( pStream && ERRCODE_NONE == pStream->GetError())
4057         {
4058             pFib->m_lcbCmds = pStream->TellEnd();
4059             pStream->Seek(0);
4060 
4061             std::unique_ptr<sal_uInt8[]> pBuffer( new sal_uInt8[pFib->m_lcbCmds] );
4062             bool bReadOk = checkRead(*pStream, pBuffer.get(), pFib->m_lcbCmds);
4063             if (bReadOk)
4064                 pTableStrm->WriteBytes(pBuffer.get(), pFib->m_lcbCmds);
4065         }
4066     }
4067     catch ( const uno::Exception& )
4068     {
4069     }
4070 
4071     // set len to FIB
4072     pFib->m_lcbCmds = pTableStrm->Tell() - pFib->m_fcCmds;
4073 }
4074 
Write(WW8Export & rExport)4075 void WW8SHDLong::Write( WW8Export& rExport )
4076 {
4077     rExport.InsUInt32( m_cvFore );
4078     rExport.InsUInt32( m_cvBack );
4079     rExport.InsUInt16( 0 ); // ipat
4080 }
4081 
WriteFormData(const::sw::mark::IFieldmark & rFieldmark)4082 void WW8Export::WriteFormData( const ::sw::mark::IFieldmark& rFieldmark )
4083 {
4084     const ::sw::mark::IFieldmark* pFieldmark = &rFieldmark;
4085     const ::sw::mark::ICheckboxFieldmark* pAsCheckbox = dynamic_cast< const ::sw::mark::ICheckboxFieldmark* >( pFieldmark );
4086 
4087     if ( ! ( rFieldmark.GetFieldname() == ODF_FORMTEXT ||
4088                 rFieldmark.GetFieldname() == ODF_FORMDROPDOWN ||
4089                 rFieldmark.GetFieldname() == ODF_FORMCHECKBOX ) )
4090     {
4091         SAL_WARN( "sw.ww8", "unknown field type" );
4092         return;
4093     }
4094 
4095     int type = 0; // TextFieldmark
4096     if ( pAsCheckbox )
4097         type = 1;
4098     if ( rFieldmark.GetFieldname() == ODF_FORMDROPDOWN )
4099         type=2;
4100 
4101     ::sw::mark::IFieldmark::parameter_map_t::const_iterator pParameter = rFieldmark.GetParameters()->find("name");
4102     OUString ffname;
4103     if ( pParameter != rFieldmark.GetParameters()->end() )
4104     {
4105         OUString aName;
4106         pParameter->second >>= aName;
4107         const sal_Int32 nLen = std::min( sal_Int32(20), aName.getLength() );
4108         ffname = aName.copy(0, nLen);
4109     }
4110 
4111     sal_uInt64 nDataStt = pDataStrm->Tell();
4112     m_pChpPlc->AppendFkpEntry(Strm().Tell());
4113 
4114     WriteChar(0x01);
4115     static sal_uInt8 aArr1[] =
4116     {
4117         0x03, 0x6a, 0,0,0,0,    // sprmCPicLocation
4118 
4119         0x06, 0x08, 0x01,       // sprmCFData
4120         0x55, 0x08, 0x01,       // sprmCFSpec
4121         0x02, 0x08, 0x01        // sprmCFFieldVanish
4122     };
4123     sal_uInt8* pDataAdr = aArr1 + 2;
4124     Set_UInt32(pDataAdr, nDataStt);
4125 
4126     m_pChpPlc->AppendFkpEntry( Strm().Tell(), sizeof( aArr1 ), aArr1 );
4127 
4128     struct FFDataHeader
4129     {
4130         sal_uInt32 version;
4131         sal_uInt16 bits;
4132         sal_uInt16 cch;
4133         sal_uInt16 hps;
4134         FFDataHeader() : version( 0xFFFFFFFF ), bits(0), cch(0), hps(0) {}
4135     };
4136 
4137     FFDataHeader aFieldHeader;
4138     aFieldHeader.bits |= (type & 0x03);
4139 
4140     sal_Int32 ffres = 0; // rFieldmark.GetFFRes();
4141     if ( pAsCheckbox && pAsCheckbox->IsChecked() )
4142         ffres = 1;
4143     else if ( type == 2 )
4144     {
4145         ::sw::mark::IFieldmark::parameter_map_t::const_iterator pResParameter = rFieldmark.GetParameters()->find(ODF_FORMDROPDOWN_RESULT);
4146         if(pResParameter != rFieldmark.GetParameters()->end())
4147             pResParameter->second >>= ffres;
4148         else
4149             ffres = 0;
4150     }
4151     aFieldHeader.bits |= ( (ffres<<2) & 0x7C );
4152 
4153     OUString ffdeftext;
4154     OUString ffformat;
4155     OUString ffhelptext = rFieldmark.GetFieldHelptext();
4156     if ( ffhelptext.getLength() > 255 )
4157         ffhelptext = ffhelptext.copy(0, 255);
4158     OUString ffstattext;
4159     OUString ffentrymcr;
4160     OUString ffexitmcr;
4161     if (type == 0) // iTypeText
4162     {
4163         sal_uInt16 nType = 0;
4164         pParameter = rFieldmark.GetParameters()->find("Type");
4165         if ( pParameter != rFieldmark.GetParameters()->end() )
4166         {
4167             OUString aType;
4168             pParameter->second >>= aType;
4169             if ( aType == "number" )            nType = 1;
4170             else if ( aType == "date" )         nType = 2;
4171             else if ( aType == "currentTime" )  nType = 3;
4172             else if ( aType == "currentDate" )  nType = 4;
4173             else if ( aType == "calculated" )   nType = 5;
4174             aFieldHeader.bits |= nType<<11; // FFDataBits-F  00111000 00000000
4175         }
4176 
4177         if ( nType < 3 || nType == 5 )  // not currentTime or currentDate
4178         {
4179             pParameter = rFieldmark.GetParameters()->find("Content");
4180             if ( pParameter != rFieldmark.GetParameters()->end() )
4181             {
4182                 OUString aDefaultText;
4183                 pParameter->second >>= aDefaultText;
4184                 const sal_Int32 nLen = std::min( sal_Int32(255), aDefaultText.getLength() );
4185                 ffdeftext = aDefaultText.copy (0, nLen);
4186             }
4187         }
4188 
4189         pParameter = rFieldmark.GetParameters()->find("MaxLength");
4190         if ( pParameter != rFieldmark.GetParameters()->end() )
4191         {
4192             sal_uInt16 nLength = 0;
4193             pParameter->second >>= nLength;
4194             nLength = std::min( sal_uInt16(32767), nLength );
4195             aFieldHeader.cch = nLength;
4196         }
4197 
4198         pParameter = rFieldmark.GetParameters()->find("Format");
4199         if ( pParameter != rFieldmark.GetParameters()->end() )
4200         {
4201             OUString aFormat;
4202             pParameter->second >>= aFormat;
4203             const sal_Int32 nLen = std::min( sal_Int32(64), aFormat.getLength() );
4204             ffformat = aFormat.copy(0, nLen);
4205         }
4206     }
4207 
4208     pParameter = rFieldmark.GetParameters()->find("Help"); //help
4209     if ( ffhelptext.isEmpty() && pParameter != rFieldmark.GetParameters()->end() )
4210     {
4211         OUString aHelpText;
4212         pParameter->second >>= aHelpText;
4213         const sal_Int32 nLen = std::min( sal_Int32(255), aHelpText.getLength() );
4214         ffhelptext = aHelpText.copy (0, nLen);
4215     }
4216     if ( !ffhelptext.isEmpty() )
4217         aFieldHeader.bits |= 0x1<<7;
4218 
4219     pParameter = rFieldmark.GetParameters()->find("Description"); // doc tooltip
4220     if ( pParameter == rFieldmark.GetParameters()->end() )
4221         pParameter = rFieldmark.GetParameters()->find("Hint"); //docx tooltip
4222     if ( pParameter != rFieldmark.GetParameters()->end() )
4223     {
4224         OUString aStatusText;
4225         pParameter->second >>= aStatusText;
4226         const sal_Int32 nLen = std::min( sal_Int32(138), aStatusText.getLength() );
4227         ffstattext = aStatusText.copy (0, nLen);
4228     }
4229     if ( !ffstattext.isEmpty() )
4230         aFieldHeader.bits |= 0x1<<8;
4231 
4232     pParameter = rFieldmark.GetParameters()->find("EntryMacro");
4233     if ( pParameter != rFieldmark.GetParameters()->end() )
4234     {
4235         OUString aEntryMacro;
4236         pParameter->second >>= aEntryMacro;
4237         const sal_Int32 nLen = std::min( sal_Int32(32), aEntryMacro.getLength() );
4238         ffentrymcr = aEntryMacro.copy (0, nLen);
4239     }
4240 
4241     pParameter = rFieldmark.GetParameters()->find("ExitMacro");
4242     if ( pParameter != rFieldmark.GetParameters()->end() )
4243     {
4244         OUString aExitMacro;
4245         pParameter->second >>= aExitMacro;
4246         const sal_Int32 nLen = std::min( sal_Int32(32), aExitMacro.getLength() );
4247         ffexitmcr = aExitMacro.copy (0, nLen);
4248     }
4249 
4250     std::vector< OUString > aListItems;
4251     if (type==2)
4252     {
4253         aFieldHeader.bits |= 0x8000; // ffhaslistbox
4254         const ::sw::mark::IFieldmark::parameter_map_t* const pParameters = rFieldmark.GetParameters();
4255         ::sw::mark::IFieldmark::parameter_map_t::const_iterator pListEntries = pParameters->find(ODF_FORMDROPDOWN_LISTENTRY);
4256         if(pListEntries != pParameters->end())
4257         {
4258             uno::Sequence< OUString > vListEntries;
4259             pListEntries->second >>= vListEntries;
4260             copy(vListEntries.begin(), vListEntries.end(), back_inserter(aListItems));
4261         }
4262     }
4263 
4264     const sal_uInt8 aFieldData[] =
4265     {
4266         0x44,0,         // the start of "next" data
4267         0,0,0,0,0,0,0,0,0,0,                // PIC-Structure!  /10
4268         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,    //  |              /16
4269         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,    //  |              /16
4270         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,    //  |              /16
4271         0,0,0,0,                            // /               /4
4272     };
4273     sal_uInt32 slen = sizeof(sal_uInt32)
4274         + sizeof(aFieldData)
4275         + sizeof( aFieldHeader.version ) + sizeof( aFieldHeader.bits ) + sizeof( aFieldHeader.cch ) + sizeof( aFieldHeader.hps )
4276         + 2*ffname.getLength() + 4
4277         + 2*ffformat.getLength() + 4
4278         + 2*ffhelptext.getLength() + 4
4279         + 2*ffstattext.getLength() + 4
4280         + 2*ffentrymcr.getLength() + 4
4281         + 2*ffexitmcr.getLength() + 4;
4282     if ( type )
4283         slen += 2; // wDef
4284     else
4285         slen += 2*ffdeftext.getLength() + 4; //xstzTextDef
4286     if ( type==2 ) {
4287         slen += 2; // sttb ( fExtend )
4288         slen += 4; // for num of list items
4289         const int items = aListItems.size();
4290         for( int i = 0; i < items; i++ ) {
4291             OUString item = aListItems[i];
4292             slen += 2 * item.getLength() + 2;
4293         }
4294     }
4295 
4296     pDataStrm->WriteUInt32( slen );
4297 
4298     int len = sizeof( aFieldData );
4299     OSL_ENSURE( len == 0x44-sizeof(sal_uInt32), "SwWW8Writer::WriteFormData(..) - wrong aFieldData length" );
4300     pDataStrm->WriteBytes( aFieldData, len );
4301 
4302     pDataStrm->WriteUInt32( aFieldHeader.version ).WriteUInt16( aFieldHeader.bits ).WriteUInt16( aFieldHeader.cch ).WriteUInt16( aFieldHeader.hps );
4303 
4304     SwWW8Writer::WriteString_xstz( *pDataStrm, ffname, true ); // Form field name
4305 
4306     if ( !type )
4307         SwWW8Writer::WriteString_xstz( *pDataStrm, ffdeftext, true );
4308     if ( type )
4309         pDataStrm->WriteUInt16( 0 );
4310 
4311     SwWW8Writer::WriteString_xstz( *pDataStrm, ffformat, true );
4312     SwWW8Writer::WriteString_xstz( *pDataStrm, ffhelptext, true );
4313     SwWW8Writer::WriteString_xstz( *pDataStrm, ffstattext, true );
4314     SwWW8Writer::WriteString_xstz( *pDataStrm, ffentrymcr, true );
4315     SwWW8Writer::WriteString_xstz( *pDataStrm, ffexitmcr, true );
4316     if (type==2) {
4317         pDataStrm->WriteUInt16( 0xFFFF );
4318         const int items=aListItems.size();
4319         pDataStrm->WriteUInt32( items );
4320         for(int i=0;i<items;i++) {
4321             OUString item=aListItems[i];
4322             SwWW8Writer::WriteString_xstz( *pDataStrm, item, false );
4323         }
4324     }
4325 }
4326 
WriteHyperlinkData(const sw::mark::IFieldmark &)4327 void WW8Export::WriteHyperlinkData( const sw::mark::IFieldmark& /*rFieldmark*/ )
4328 {
4329     //@TODO implement me !!!
4330 }
4331 
TableNodeInfoInner(ww8::WW8TableNodeInfoInner::Pointer_t pNodeInfoInner)4332 void WW8AttributeOutput::TableNodeInfoInner( ww8::WW8TableNodeInfoInner::Pointer_t pNodeInfoInner )
4333 {
4334     SVBT16 nStyle;
4335     ShortToSVBT16( m_rWW8Export.m_nStyleBeforeFly, nStyle );
4336 
4337 #ifdef DBG_UTIL
4338     SAL_INFO( "sw.ww8", "<OutWW8_TableNodeInfoInner>" << pNodeInfoInner->toString());
4339 #endif
4340 
4341     m_rWW8Export.pO->clear();
4342 
4343     sal_uInt32 nShadowsBefore = pNodeInfoInner->getShadowsBefore();
4344     if (nShadowsBefore > 0)
4345     {
4346         ww8::WW8TableNodeInfoInner::Pointer_t
4347             pTmpNodeInfoInner = std::make_shared<ww8::WW8TableNodeInfoInner>(nullptr);
4348 
4349         pTmpNodeInfoInner->setDepth(pNodeInfoInner->getDepth());
4350         pTmpNodeInfoInner->setEndOfCell(true);
4351 
4352         for (sal_uInt32 n = 0; n < nShadowsBefore; ++n)
4353         {
4354             m_rWW8Export.WriteCR(pTmpNodeInfoInner);
4355 
4356             m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), nStyle, nStyle+2 );     // Style #
4357             TableInfoCell(pTmpNodeInfoInner);
4358             m_rWW8Export.m_pPapPlc->AppendFkpEntry
4359                 ( m_rWW8Export.Strm().Tell(), m_rWW8Export.pO->size(), m_rWW8Export.pO->data() );
4360 
4361             m_rWW8Export.pO->clear();
4362         }
4363     }
4364 
4365     if (pNodeInfoInner->isEndOfCell())
4366     {
4367         SAL_INFO( "sw.ww8", "<endOfCell/>" );
4368 
4369         m_rWW8Export.WriteCR(pNodeInfoInner);
4370 
4371         m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), nStyle, nStyle+2 );     // Style #
4372         TableInfoCell(pNodeInfoInner);
4373         m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), m_rWW8Export.pO->size(), m_rWW8Export.pO->data() );
4374 
4375         m_rWW8Export.pO->clear();
4376     }
4377 
4378     sal_uInt32 nShadowsAfter = pNodeInfoInner->getShadowsAfter();
4379     if (nShadowsAfter > 0)
4380     {
4381         ww8::WW8TableNodeInfoInner::Pointer_t
4382             pTmpNodeInfoInner= std::make_shared<ww8::WW8TableNodeInfoInner>(nullptr);
4383 
4384         pTmpNodeInfoInner->setDepth(pNodeInfoInner->getDepth());
4385         pTmpNodeInfoInner->setEndOfCell(true);
4386 
4387         for (sal_uInt32 n = 0; n < nShadowsAfter; ++n)
4388         {
4389             m_rWW8Export.WriteCR(pTmpNodeInfoInner);
4390 
4391             m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), nStyle, nStyle+2 );     // Style #
4392             TableInfoCell(pTmpNodeInfoInner);
4393             m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), m_rWW8Export.pO->size(), m_rWW8Export.pO->data() );
4394 
4395             m_rWW8Export.pO->clear();
4396         }
4397     }
4398 
4399     if (pNodeInfoInner->isEndOfLine())
4400     {
4401         SAL_INFO( "sw.ww8", "<endOfLine/>" );
4402 
4403         TableRowEnd(pNodeInfoInner->getDepth());
4404 
4405         ShortToSVBT16(0, nStyle);
4406         m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), nStyle, nStyle+2 );     // Style #
4407         TableInfoRow(pNodeInfoInner);
4408         m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), m_rWW8Export.pO->size(), m_rWW8Export.pO->data() );
4409 
4410         m_rWW8Export.pO->clear();
4411     }
4412     SAL_INFO( "sw.ww8", "</OutWW8_TableNodeInfoInner>" );
4413 }
4414 
OutputStartNode(const SwStartNode & rNode)4415 void MSWordExportBase::OutputStartNode( const SwStartNode & rNode)
4416 {
4417 
4418     ww8::WW8TableNodeInfo::Pointer_t pNodeInfo =
4419         m_pTableInfo->getTableNodeInfo( &rNode );
4420 
4421     if (pNodeInfo)
4422     {
4423 #ifdef DBG_UTIL
4424         SAL_INFO( "sw.ww8", pNodeInfo->toString());
4425 #endif
4426         const ww8::WW8TableNodeInfo::Inners_t aInners = pNodeInfo->getInners();
4427         ww8::WW8TableNodeInfo::Inners_t::const_reverse_iterator aIt(aInners.rbegin());
4428         ww8::WW8TableNodeInfo::Inners_t::const_reverse_iterator aEnd(aInners.rend());
4429         while (aIt != aEnd)
4430         {
4431             ww8::WW8TableNodeInfoInner::Pointer_t pInner = aIt->second;
4432 
4433             AttrOutput().TableNodeInfoInner(pInner);
4434             ++aIt;
4435         }
4436     }
4437     SAL_INFO( "sw.ww8", "</OutWW8_SwStartNode>" );
4438 }
4439 
OutputEndNode(const SwEndNode & rNode)4440 void MSWordExportBase::OutputEndNode( const SwEndNode &rNode )
4441 {
4442 #ifdef DBG_UTIL
4443     SAL_INFO( "sw.ww8", "<OutWW8_SwEndNode>" << dbg_out(&rNode));
4444 #endif
4445 
4446     ww8::WW8TableNodeInfo::Pointer_t pNodeInfo = m_pTableInfo->getTableNodeInfo( &rNode );
4447 
4448     if (pNodeInfo)
4449     {
4450 #ifdef DBG_UTIL
4451         SAL_INFO( "sw.ww8", pNodeInfo->toString());
4452 #endif
4453         const ww8::WW8TableNodeInfo::Inners_t aInners = pNodeInfo->getInners();
4454         for (const auto& rEntry : aInners)
4455         {
4456             ww8::WW8TableNodeInfoInner::Pointer_t pInner = rEntry.second;
4457             AttrOutput().TableNodeInfoInner(pInner);
4458         }
4459     }
4460     SAL_INFO( "sw.ww8", "</OutWW8_SwEndNode>" );
4461 }
4462 
GetNfKeywordTable()4463 const NfKeywordTable & MSWordExportBase::GetNfKeywordTable()
4464 {
4465     if (m_pKeyMap == nullptr)
4466     {
4467         m_pKeyMap = std::make_shared<NfKeywordTable>();
4468         NfKeywordTable & rKeywordTable = *m_pKeyMap;
4469         rKeywordTable[NF_KEY_D] = "d";
4470         rKeywordTable[NF_KEY_DD] = "dd";
4471         rKeywordTable[NF_KEY_DDD] = "ddd";
4472         rKeywordTable[NF_KEY_DDDD] = "dddd";
4473         rKeywordTable[NF_KEY_M] = "M";
4474         rKeywordTable[NF_KEY_MM] = "MM";
4475         rKeywordTable[NF_KEY_MMM] = "MMM";
4476         rKeywordTable[NF_KEY_MMMM] = "MMMM";
4477         rKeywordTable[NF_KEY_NN] = "ddd";
4478         rKeywordTable[NF_KEY_NNN] = "dddd";
4479         rKeywordTable[NF_KEY_NNNN] = "dddd";
4480         rKeywordTable[NF_KEY_YY] = "yy";
4481         rKeywordTable[NF_KEY_YYYY] = "yyyy";
4482         rKeywordTable[NF_KEY_H] = "H";
4483         rKeywordTable[NF_KEY_HH] = "HH";
4484         rKeywordTable[NF_KEY_MI] = "m";
4485         rKeywordTable[NF_KEY_MMI] = "mm";
4486         rKeywordTable[NF_KEY_S] = "s";
4487         rKeywordTable[NF_KEY_SS] = "ss";
4488         rKeywordTable[NF_KEY_AMPM] = "AM/PM";
4489     }
4490 
4491     return *m_pKeyMap;
4492 }
4493 
4494 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
4495