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