nss: upgrade to release 3.73
[LibreOffice.git] / sw / source / filter / ww8 / wrtww8.cxx
blob5e54f9443c847858a6909b5f43d5019b4f9b3e83
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <memory>
21 #include <iostream>
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>
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"
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>
120 using namespace css;
121 using namespace sw::util;
122 using namespace sw::types;
124 /** FKP - Formatted disK Page
126 class WW8_WrFkp
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
138 sal_uInt8 SearchSameSprm( sal_uInt16 nVarLen, const sal_uInt8* pSprms );
140 WW8_WrFkp(const WW8_WrFkp&) = delete;
141 WW8_WrFkp& operator=(const WW8_WrFkp&) = delete;
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 );
150 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 );
153 bool IsEmptySprm() const
154 { return !bCombined && nIMax && !nOldVarLen; }
155 void SetNewEnd( WW8_FC nEnd )
156 { reinterpret_cast<sal_Int32*>(pFkp)[nIMax] = nEnd; }
158 WW8_FC GetStartFc() const;
159 WW8_FC GetEndFc() const;
161 sal_uInt8 *CopyLastSprms(sal_uInt8 &rLen);
164 // class WW8_WrPc collects all piece entries for one piece
165 class WW8_WrPc
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?
171 public:
172 WW8_WrPc(WW8_FC nSFc, WW8_CP nSCp )
173 : nStartCp( nSCp ), nStartFc( nSFc ), nStatus( 0x0040 )
176 void SetStatus() { nStatus = 0x0050; }
177 sal_uInt16 GetStatus() const { return nStatus; }
178 WW8_CP GetStartCp() const { return nStartCp; }
179 WW8_FC GetStartFc() const { return nStartFc; }
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;
188 class WW8_WrtBookmarks
190 private:
191 /// Structure of one item inside this map: (startPos, (endPos, (a bool value?, bookmarkName)))
192 BKMKCPs aSttCps;
193 BKMKNames maSwBkmkNms;
195 WW8_WrtBookmarks(WW8_WrtBookmarks const&) = delete;
196 WW8_WrtBookmarks& operator=(WW8_WrtBookmarks const&) = delete;
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);
209 WW8_WrtBookmarks::WW8_WrtBookmarks()
212 WW8_WrtBookmarks::~WW8_WrtBookmarks()
214 for (auto& rEntry : aSttCps)
216 if (rEntry.second)
218 delete rEntry.second;
219 rEntry.second = nullptr;
224 void WW8_WrtBookmarks::Append( WW8_CP nStartCp, const OUString& rNm)
226 std::pair<BKMKNames::iterator, bool> aResult = maSwBkmkNms.insert(std::pair<OUString,tools::Long>(rNm,0L));
227 if (aResult.second)
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);
234 else
236 std::pair<CPItr,CPItr> aRange = aSttCps.equal_range(aResult.first->second);
237 for (CPItr aItr = aRange.first;aItr != aRange.second;++aItr)
239 if (aItr->second && aItr->second->second.second == rNm)
241 if (aItr->second->second.first)
242 nStartCp--;
243 aItr->second->first = static_cast<tools::Long>(nStartCp);
244 break;
250 void WW8_WrtBookmarks::Write( WW8Export& rWrt)
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);
259 BKMKCPs aEndCps;
260 for (const auto& rEntry : aSttCps)
262 if (rEntry.second)
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);
270 aTempStrm1.Seek(0);
271 n = 0;
272 for (const auto& rEntry : aEndCps)
274 if (rEntry.second)
276 rEntry.second->first = n;
277 SwWW8Writer::WriteLong( aTempStrm2, rEntry.first);
279 ++n;
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)
290 if (rEntry.second)
292 SwWW8Writer::WriteLong(rStrm, rEntry.second->first);
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;
302 void WW8_WrtBookmarks::MoveFieldMarks(WW8_CP nFrom, WW8_CP nTo)
304 std::pair<CPItr,CPItr> aRange = aSttCps.equal_range(nFrom);
305 CPItr aItr = aRange.first;
306 while (aItr != aRange.second)
308 if (aItr->second)
310 if (aItr->second->first == static_cast<tools::Long>(nFrom))
312 aItr->second->second.first = true;
313 aItr->second->first = nTo;
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;
321 ++aItr;
325 /// Handles export of smart tags.
326 class WW8_WrtFactoids
328 std::vector<WW8_CP> m_aStartCPs;
329 std::vector<WW8_CP> m_aEndCPs;
330 std::vector< std::map<OUString, OUString> > m_aStatements;
332 WW8_WrtFactoids(WW8_WrtFactoids const&) = delete;
333 WW8_WrtFactoids& operator=(WW8_WrtFactoids const&) = delete;
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);
341 WW8_WrtFactoids::WW8_WrtFactoids()
345 void WW8_WrtFactoids::Append(WW8_CP nStartCp, WW8_CP nEndCp, const std::map<OUString, OUString>& rStatements)
347 m_aStartCPs.push_back(nStartCp);
348 m_aEndCPs.push_back(nEndCp);
349 m_aStatements.push_back(rStatements);
352 void WW8_WrtFactoids::Write(WW8Export& rExport)
354 if (m_aStartCPs.empty())
355 return;
357 // Smart tags are otherwise removed by Word on saving.
358 rExport.pDop->fEmbedFactoids = true;
360 SvStream& rStream = *rExport.pTableStrm;
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
368 for (size_t i = 0; i < m_aStartCPs.size(); ++i)
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
377 rExport.pFib->m_lcbSttbfBkmkFactoid = rStream.Tell() - rExport.pFib->m_fcSttbfBkmkFactoid;
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);
384 // Write FBKFD.
385 for (size_t i = 0; i < m_aStartCPs.size(); ++i)
387 rStream.WriteInt16(i); // ibkl
388 rStream.WriteInt16(0); // bkc
389 rStream.WriteInt16(1); // cDepth, 1 as start and end is the same.
392 rExport.pFib->m_lcbPlcfBkfFactoid = rStream.Tell() - rExport.pFib->m_fcPlcfBkfFactoid;
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);
399 // Write FBKLD.
400 for (size_t i = 0; i < m_aEndCPs.size(); ++i)
402 rStream.WriteInt16(i); // ibkf
403 rStream.WriteInt16(0); // cDepth, 0 as does not overlap with any other smart tag.
405 rExport.pFib->m_lcbPlcfBklFactoid = rStream.Tell() - rExport.pFib->m_fcPlcfBklFactoid;
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);
416 std::set<OUString> aSet;
417 for (const std::map<OUString, OUString>& rStatements : m_aStatements)
419 // Statements for a single text node.
420 for (const auto& rPair : rStatements)
422 aSet.insert(rPair.first);
423 aSet.insert(rPair.second);
426 aSmartTagData.m_aPropBagStore.m_aStringTable.assign(aSet.begin(), aSet.end());
427 for (const std::map<OUString, OUString>& rStatements : m_aStatements)
429 MSOPropertyBag aPropertyBag;
430 aPropertyBag.m_nId = 1;
431 for (const auto& rPair : rStatements)
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);
438 aSmartTagData.m_aPropBags.push_back(aPropertyBag);
441 aSmartTagData.Write(rExport);
442 rExport.pFib->m_lcbFactoidData = rStream.Tell() - rExport.pFib->m_fcFactoidData;
445 #define DEFAULT_STYLES_COUNT 16
447 // Names of the storage streams
448 #define sMainStream OUString("WordDocument")
449 #define sCompObj "\1CompObj"
451 static void WriteDop( WW8Export& rWrt )
453 WW8Dop& rDop = *rWrt.pDop;
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());
459 rDop.fNoLeading = !rWrt.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::ADD_EXT_LEADING);
460 rDop.fUsePrinterMetrics = !rWrt.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::USE_VIRTUAL_DEVICE);
462 // write default TabStop
463 const SvxTabStopItem& rTabStop =
464 DefaultItemGet<SvxTabStopItem>(rWrt.m_rDoc, RES_PARATR_TABSTOP);
465 rDop.dxaTab = static_cast<sal_uInt16>(rTabStop[0].GetTabPos());
467 // Zoom factor and type
468 SwViewShell *pViewShell(rWrt.m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell());
469 if (pViewShell)
471 switch ( pViewShell->GetViewOptions()->GetZoomType() )
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;
482 // Values from the DocumentStatistics (are definitely needed
483 // for the DocStat fields)
484 rDop.fWCFootnoteEdn = true; // because they are included in StarWriter
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;
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 )
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");
505 rDop.lKeyProtDoc = pDocShell->GetModifyPasswordHash();
508 if ((rWrt.pSepx && rWrt.pSepx->DocumentIsProtected()) ||
509 rWrt.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_FORM ) ||
510 rDop.lKeyProtDoc != 0)
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() )
517 comphelper::SequenceAsHashMap aPropMap( xProps->getPropertyValue("InteropGrabBag"));
518 aPropMap.getValue("FormPasswordHash") >>= rDop.lKeyProtDoc;
521 else
523 rDop.fProtEnabled = false;
526 if (!xDocProps.is())
528 rDop.dttmCreated = rDop.dttmRevised = rDop.dttmLastPrint = 0x45FBAC69;
530 else
532 ::util::DateTime uDT = xDocProps->getCreationDate();
533 rDop.dttmCreated = sw::ms::DateTime2DTTM(DateTime(uDT));
534 uDT = xDocProps->getModificationDate();
535 rDop.dttmRevised = sw::ms::DateTime2DTTM(DateTime(uDT));
536 uDT = xDocProps->getPrintDate();
537 rDop.dttmLastPrint = sw::ms::DateTime2DTTM(DateTime(uDT));
540 // Also, the DocStat fields in headers, footers are not calculated correctly.
541 // ( we do not have this fields! )
543 // and also for the Headers and Footers
544 rDop.cWordsFootnoteEnd = rDStat.nWord;
545 rDop.cChFootnoteEdn = rDStat.nChar;
546 rDop.cPgFootnoteEdn = static_cast<sal_Int16>(rDStat.nPage);
547 rDop.cParasFootnoteEdn = rDStat.nPara;
548 rDop.cLinesFootnoteEdn = rDStat.nPara;
550 rDop.fDontUseHTMLAutoSpacing = rWrt.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::PARA_SPACE_MAX);
552 rDop.fExpShRtn = !rWrt.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK); // #i56856#
554 rDop.Write( *rWrt.pTableStrm, *rWrt.pFib );
557 static int lcl_CmpBeginEndChars( const OUString& rSWStr,
558 const sal_Unicode* pMSStr, int nMSStrByteLen )
560 nMSStrByteLen /= sizeof( sal_Unicode );
561 if( nMSStrByteLen > rSWStr.getLength() )
562 nMSStrByteLen = rSWStr.getLength()+1;
563 nMSStrByteLen *= sizeof( sal_Unicode );
565 return memcmp( rSWStr.getStr(), pMSStr, nMSStrByteLen );
569 Converts the OOo Asian Typography into a best fit match for Microsoft
570 Asian typography. This structure is actually dumped to disk within the
571 Dop Writer. Assumption is that rTypo is cleared to 0 on entry
573 void WW8Export::ExportDopTypography(WW8DopTypography &rTypo)
575 static const sal_Unicode aLangNotBegin[4][WW8DopTypography::nMaxFollowing]=
577 //Japanese Level 1
579 0x0021, 0x0025, 0x0029, 0x002c, 0x002e, 0x003a, 0x003b, 0x003f,
580 0x005d, 0x007d, 0x00a2, 0x00b0, 0x2019, 0x201d, 0x2030, 0x2032,
581 0x2033, 0x2103, 0x3001, 0x3002, 0x3005, 0x3009, 0x300b, 0x300d,
582 0x300f, 0x3011, 0x3015, 0x3041, 0x3043, 0x3045, 0x3047, 0x3049,
583 0x3063, 0x3083, 0x3085, 0x3087, 0x308e, 0x309b, 0x309c, 0x309d,
584 0x309e, 0x30a1, 0x30a3, 0x30a5, 0x30a7, 0x30a9, 0x30c3, 0x30e3,
585 0x30e5, 0x30e7, 0x30ee, 0x30f5, 0x30f6, 0x30fb, 0x30fc, 0x30fd,
586 0x30fe, 0xff01, 0xff05, 0xff09, 0xff0c, 0xff0e, 0xff1a, 0xff1b,
587 0xff1f, 0xff3d, 0xff5d, 0xff61, 0xff63, 0xff64, 0xff65, 0xff67,
588 0xff68, 0xff69, 0xff6a, 0xff6b, 0xff6c, 0xff6d, 0xff6e, 0xff6f,
589 0xff70, 0xff9e, 0xff9f, 0xffe0
591 //Simplified Chinese
593 0x0021, 0x0029, 0x002c, 0x002e, 0x003a, 0x003b, 0x003f, 0x005d,
594 0x007d, 0x00a8, 0x00b7, 0x02c7, 0x02c9, 0x2015, 0x2016, 0x2019,
595 0x201d, 0x2026, 0x2236, 0x3001, 0x3002, 0x3003, 0x3005, 0x3009,
596 0x300b, 0x300d, 0x300f, 0x3011, 0x3015, 0x3017, 0xff01, 0xff02,
597 0xff07, 0xff09, 0xff0c, 0xff0e, 0xff1a, 0xff1b, 0xff1f, 0xff3d,
598 0xff40, 0xff5c, 0xff5d, 0xff5e, 0xffe0
600 //Korean
602 0x0021, 0x0025, 0x0029, 0x002c, 0x002e, 0x003a, 0x003b, 0x003f,
603 0x005d, 0x007d, 0x00a2, 0x00b0, 0x2019, 0x201d, 0x2032, 0x2033,
604 0x2103, 0x3009, 0x300b, 0x300d, 0x300f, 0x3011, 0x3015, 0xff01,
605 0xff05, 0xff09, 0xff0c, 0xff0e, 0xff1a, 0xff1b, 0xff1f, 0xff3d,
606 0xff5d, 0xffe0
608 //Traditional Chinese
610 0x0021, 0x0029, 0x002c, 0x002e, 0x003a, 0x003b, 0x003f, 0x005d,
611 0x007d, 0x00a2, 0x00b7, 0x2013, 0x2014, 0x2019, 0x201d, 0x2022,
612 0x2025, 0x2026, 0x2027, 0x2032, 0x2574, 0x3001, 0x3002, 0x3009,
613 0x300b, 0x300d, 0x300f, 0x3011, 0x3015, 0x301e, 0xfe30, 0xfe31,
614 0xfe33, 0xfe34, 0xfe36, 0xfe38, 0xfe3a, 0xfe3c, 0xfe3e, 0xfe40,
615 0xfe42, 0xfe44, 0xfe4f, 0xfe50, 0xfe51, 0xfe52, 0xfe54, 0xfe55,
616 0xfe56, 0xfe57, 0xfe5a, 0xfe5c, 0xfe5e, 0xff01, 0xff09, 0xff0c,
617 0xff0e, 0xff1a, 0xff1b, 0xff1f, 0xff5c, 0xff5d, 0xff64
621 static const sal_Unicode aLangNotEnd[4][WW8DopTypography::nMaxLeading] =
623 //Japanese Level 1
625 0x0024, 0x0028, 0x005b, 0x005c, 0x007b, 0x00a3, 0x00a5, 0x2018,
626 0x201c, 0x3008, 0x300a, 0x300c, 0x300e, 0x3010, 0x3014, 0xff04,
627 0xff08, 0xff3b, 0xff5b, 0xff62, 0xffe1, 0xffe5
629 //Simplified Chinese
631 0x0028, 0x005b, 0x007b, 0x00b7, 0x2018, 0x201c, 0x3008, 0x300a,
632 0x300c, 0x300e, 0x3010, 0x3014, 0x3016, 0xff08, 0xff0e, 0xff3b,
633 0xff5b, 0xffe1, 0xffe5
635 //Korean
637 0x0028, 0x005b, 0x005c, 0x007b, 0x00a3, 0x00a5, 0x2018, 0x201c,
638 0x3008, 0x300a, 0x300c, 0x300e, 0x3010, 0x3014, 0xff04, 0xff08,
639 0xff3b, 0xff5b, 0xffe6
641 //Traditional Chinese
643 0x0028, 0x005b, 0x007b, 0x00a3, 0x00a5, 0x2018, 0x201c, 0x2035,
644 0x3008, 0x300a, 0x300c, 0x300e, 0x3010, 0x3014, 0x301d, 0xfe35,
645 0xfe37, 0xfe39, 0xfe3b, 0xfe3d, 0xfe3f, 0xfe41, 0xfe43, 0xfe59,
646 0xfe5b, 0xfe5d, 0xff08, 0xff5b
650 const i18n::ForbiddenCharacters *pForbidden = nullptr;
651 const i18n::ForbiddenCharacters *pUseMe = nullptr;
652 sal_uInt8 nUseReserved=0;
653 int nNoNeeded=0;
655 Now we have some minor difficult issues, to wit...
656 a. MicroSoft Office can only store one set of begin and end characters in
657 a given document, not one per language.
658 b. StarOffice has only a concept of one set of begin and end characters for
659 a given language, i.e. not the two levels of kinsoku in japanese
661 What is unknown as yet is if our default begin and end chars for
662 japanese, chinese tradition, chinese simplified and korean are different
663 in Word and Writer. I already suspect that they are different between
664 different version of word itself.
666 So what have come up with is to simply see if any of the four languages
667 in OOo have been changed away from OUR defaults, and if one has then
668 export that. If more than one has in the future we may hack in something
669 which examines our document properties to see which language is used the
670 most and choose that, for now we choose the first and throw an ASSERT
673 /*Our default Japanese Level is 2, this is a special MS hack to set this*/
674 rTypo.m_reserved2 = 1;
676 for (rTypo.m_reserved1=8;rTypo.m_reserved1>0;rTypo.m_reserved1-=2)
678 pForbidden = m_rDoc.getIDocumentSettingAccess().getForbiddenCharacters(rTypo.GetConvertedLang(),
679 false);
680 if (nullptr != pForbidden)
682 int nIdx = (rTypo.m_reserved1-2)/2;
683 if( lcl_CmpBeginEndChars( pForbidden->endLine,
684 aLangNotEnd[ nIdx ], sizeof(aLangNotEnd[ nIdx ]) ) ||
685 lcl_CmpBeginEndChars( pForbidden->beginLine,
686 aLangNotBegin[ nIdx ], sizeof(aLangNotBegin[ nIdx ]) ) )
688 //One exception for Japanese, if it matches a level 1 we
689 //can use one extra flag for that, rather than use a custom
690 if (rTypo.GetConvertedLang() == LANGUAGE_JAPANESE)
692 if (
693 !lcl_CmpBeginEndChars
695 pForbidden->endLine,
696 WW8DopTypography::JapanNotEndLevel1,
697 WW8DopTypography::nMaxLeading * sizeof(sal_Unicode)
700 !lcl_CmpBeginEndChars
702 pForbidden->beginLine,
703 WW8DopTypography::JapanNotBeginLevel1,
704 WW8DopTypography::nMaxFollowing * sizeof(sal_Unicode)
708 rTypo.m_reserved2 = 0;
709 continue;
713 if (!pUseMe)
715 pUseMe = pForbidden;
716 nUseReserved = rTypo.m_reserved1;
717 rTypo.m_iLevelOfKinsoku = 2;
719 nNoNeeded++;
724 OSL_ENSURE( nNoNeeded<=1, "Example of unexportable forbidden chars" );
725 rTypo.m_reserved1=nUseReserved;
726 if (rTypo.m_iLevelOfKinsoku && pUseMe)
728 rTypo.m_cchFollowingPunct = msword_cast<sal_Int16>
729 (pUseMe->beginLine.getLength());
730 if (rTypo.m_cchFollowingPunct > WW8DopTypography::nMaxFollowing - 1)
731 rTypo.m_cchFollowingPunct = WW8DopTypography::nMaxFollowing - 1;
733 rTypo.m_cchLeadingPunct = msword_cast<sal_Int16>
734 (pUseMe->endLine.getLength());
735 if (rTypo.m_cchLeadingPunct > WW8DopTypography::nMaxLeading - 1)
736 rTypo.m_cchLeadingPunct = WW8DopTypography::nMaxLeading -1;
738 memcpy(rTypo.m_rgxchFPunct,pUseMe->beginLine.getStr(),
739 (rTypo.m_cchFollowingPunct+1)*2);
741 memcpy(rTypo.m_rgxchLPunct,pUseMe->endLine.getStr(),
742 (rTypo.m_cchLeadingPunct+1)*2);
745 const IDocumentSettingAccess& rIDocumentSettingAccess = GetWriter().getIDocumentSettingAccess();
747 rTypo.m_fKerningPunct = sal_uInt16(rIDocumentSettingAccess.get(DocumentSettingId::KERN_ASIAN_PUNCTUATION));
748 rTypo.m_iJustification = sal_uInt16(m_rDoc.getIDocumentSettingAccess().getCharacterCompressionType());
751 // It can only be found something with this method, if it is used within
752 // WW8_SwAttrIter::OutAttr() and WW8Export::OutputItemSet()
753 const SfxPoolItem* MSWordExportBase::HasItem( sal_uInt16 nWhich ) const
755 const SfxPoolItem* pItem=nullptr;
756 if (m_pISet)
758 // if write an EditEngine text, then the WhichIds are greater than
759 // our own Ids. So the Id have to translate from our into the
760 // EditEngine Range
761 nWhich = sw::hack::GetSetWhichFromSwDocWhich(*m_pISet, m_rDoc, nWhich);
762 if (nWhich && SfxItemState::SET != m_pISet->GetItemState(nWhich, true, &pItem))
763 pItem = nullptr;
765 else if( m_pChpIter )
766 pItem = m_pChpIter->HasTextItem( nWhich );
767 else
769 OSL_ENSURE( false, "Where is my ItemSet / pChpIter ?" );
770 pItem = nullptr;
772 return pItem;
775 const SfxPoolItem& MSWordExportBase::GetItem(sal_uInt16 nWhich) const
777 assert((m_pISet || m_pChpIter) && "Where is my ItemSet / pChpIter ?");
778 if (m_pISet)
780 // if write an EditEngine text, then the WhichIds are greater than
781 // our own Ids. So the Id have to translate from our into the
782 // EditEngine Range
783 nWhich = sw::hack::GetSetWhichFromSwDocWhich(*m_pISet, m_rDoc, nWhich);
784 OSL_ENSURE(nWhich != 0, "All broken, Impossible");
785 return m_pISet->Get(nWhich);
787 return m_pChpIter->GetItem( nWhich );
790 WW8_WrPlc1::WW8_WrPlc1( sal_uInt16 nStructSz )
791 : pData( new sal_uInt8[ 16 * nStructSz ] ),
792 nDataLen(16 * nStructSz),
793 nStructSiz( nStructSz )
797 WW8_WrPlc1::~WW8_WrPlc1()
801 WW8_CP WW8_WrPlc1::Prev() const
803 bool b = !aPos.empty();
804 OSL_ENSURE(b,"Prev called on empty list");
805 return b ? aPos.back() : 0;
808 void WW8_WrPlc1::Append( WW8_CP nCp, const void* pNewData )
810 sal_uLong nInsPos = aPos.size() * nStructSiz;
811 aPos.push_back( nCp );
812 if( nDataLen < nInsPos + nStructSiz )
814 sal_uInt8* pNew = new sal_uInt8[ 2 * nDataLen ];
815 memcpy( pNew, pData.get(), nDataLen );
816 pData.reset(pNew);
817 nDataLen *= 2;
819 memcpy( pData.get() + nInsPos, pNewData, nStructSiz );
822 void WW8_WrPlc1::Finish( sal_uLong nLastCp, sal_uLong nSttCp )
824 if( !aPos.empty() )
826 aPos.push_back( nLastCp );
827 if( nSttCp )
828 for(WW8_CP & rCp : aPos)
829 rCp -= nSttCp;
833 void WW8_WrPlc1::Write( SvStream& rStrm )
835 decltype(aPos)::size_type i;
836 for( i = 0; i < aPos.size(); ++i )
837 SwWW8Writer::WriteLong( rStrm, aPos[i] );
838 if( i )
839 rStrm.WriteBytes(pData.get(), (i-1) * nStructSiz);
842 // Class WW8_WrPlcField for fields
844 void WW8_WrPlcField::Write( WW8Export& rWrt )
846 if( WW8_WrPlc1::Count() <= 1 )
847 return;
849 WW8_FC *pfc;
850 sal_Int32 *plc;
851 switch (nTextTyp)
853 case TXT_MAINTEXT:
854 pfc = &rWrt.pFib->m_fcPlcffldMom;
855 plc = &rWrt.pFib->m_lcbPlcffldMom;
856 break;
857 case TXT_HDFT:
858 pfc = &rWrt.pFib->m_fcPlcffldHdr;
859 plc = &rWrt.pFib->m_lcbPlcffldHdr;
860 break;
862 case TXT_FTN:
863 pfc = &rWrt.pFib->m_fcPlcffldFootnote;
864 plc = &rWrt.pFib->m_lcbPlcffldFootnote;
865 break;
867 case TXT_EDN:
868 pfc = &rWrt.pFib->m_fcPlcffldEdn;
869 plc = &rWrt.pFib->m_lcbPlcffldEdn;
870 break;
872 case TXT_ATN:
873 pfc = &rWrt.pFib->m_fcPlcffldAtn;
874 plc = &rWrt.pFib->m_lcbPlcffldAtn;
875 break;
877 case TXT_TXTBOX:
878 pfc = &rWrt.pFib->m_fcPlcffldTxbx;
879 plc = &rWrt.pFib->m_lcbPlcffldTxbx;
880 break;
882 case TXT_HFTXTBOX:
883 pfc = &rWrt.pFib->m_fcPlcffldHdrTxbx;
884 plc = &rWrt.pFib->m_lcbPlcffldHdrTxbx;
885 break;
887 default:
888 pfc = plc = nullptr;
889 break;
892 if( pfc && plc )
894 sal_uInt64 nFcStart = rWrt.pTableStrm->Tell();
895 WW8_WrPlc1::Write( *rWrt.pTableStrm );
896 *pfc = nFcStart;
897 *plc = rWrt.pTableStrm->Tell() - nFcStart;
901 void WW8_WrMagicTable::Write( WW8Export& rWrt )
903 if( WW8_WrPlc1::Count() <= 1 )
904 return;
905 sal_uLong nFcStart = rWrt.pTableStrm->Tell();
906 WW8_WrPlc1::Write( *rWrt.pTableStrm );
907 rWrt.pFib->m_fcPlcfTch = nFcStart;
908 rWrt.pFib->m_lcbPlcfTch = rWrt.pTableStrm->Tell() - nFcStart;
911 void WW8_WrMagicTable::Append( WW8_CP nCp, sal_uLong nData)
914 Tell the undocumented table hack that everything between here and the last
915 table position is non-table text, don't do it if the previous position is
916 the same as this one, as that would be a region of 0 length
918 if ((!Count()) || (Prev() != nCp))
920 SVBT32 nLittle;
921 UInt32ToSVBT32(nData,nLittle);
922 WW8_WrPlc1::Append(nCp, nLittle);
926 void SwWW8Writer::FillCount( SvStream& rStrm, sal_uLong nCount )
928 static const sal_uInt32 aNulls[16] =
930 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 // 64 Byte
933 while (nCount > 64)
935 rStrm.WriteBytes(aNulls, 64); // in steps of 64-Byte
936 nCount -= 64;
938 rStrm.WriteBytes(aNulls, nCount); // write the rest (0 .. 64 Bytes)
941 sal_uLong SwWW8Writer::FillUntil( SvStream& rStrm, sal_uLong nEndPos )
943 sal_uInt64 nCurPos = rStrm.Tell();
944 if( !nEndPos ) // nEndPos == 0 -> next Page
945 nEndPos = (nCurPos + 0x1ff) & ~0x1ffUL;
947 if( nEndPos > nCurPos )
948 SwWW8Writer::FillCount( rStrm, nEndPos - nCurPos );
949 #if OSL_DEBUG_LEVEL > 0
950 else
951 OSL_ENSURE( nEndPos == nCurPos, "Wrong FillUntil()" );
952 #endif
953 return rStrm.Tell();
956 WW8_WrPlcPn::WW8_WrPlcPn(WW8Export& rWr, ePLCFT ePl, WW8_FC nStartFc)
957 : rWrt(rWr)
958 , nFkpStartPage(0)
959 , ePlc(ePl)
961 m_Fkps.push_back(std::make_unique<WW8_WrFkp>(ePlc, nStartFc));
964 WW8_WrPlcPn::~WW8_WrPlcPn()
968 sal_uInt8 *WW8_WrPlcPn::CopyLastSprms(sal_uInt8 &rLen)
970 WW8_WrFkp& rF = *m_Fkps.back();
971 return rF.CopyLastSprms(rLen);
974 void WW8_WrPlcPn::AppendFkpEntry(WW8_FC nEndFc,short nVarLen,const sal_uInt8* pSprms)
976 WW8_WrFkp* pF = m_Fkps.back().get();
978 // big sprm? build the sprmPHugePapx
979 sal_uInt8* pNewSprms = const_cast<sal_uInt8*>(pSprms);
980 sal_uInt8 aHugePapx[ 8 ];
981 if (PAP == ePlc && 488 <= nVarLen)
983 sal_uInt8* p = aHugePapx;
984 *p++ = *pSprms++; // set style Id
985 *p++ = *pSprms++;
986 nVarLen -= 2;
988 tools::Long nDataPos = rWrt.pDataStrm->Tell();
989 SwWW8Writer::WriteShort( *rWrt.pDataStrm, nVarLen );
990 rWrt.pDataStrm->WriteBytes(pSprms, nVarLen);
992 Set_UInt16( p, 0x6646 ); // set SprmCode
993 Set_UInt32( p, nDataPos ); // set startpos (FC) in the datastream
994 nVarLen = static_cast< short >(p - aHugePapx);
995 pSprms = pNewSprms = aHugePapx;
997 // if append at the same FC-EndPos and there are sprms, then get the old
998 // sprms and erase it; they will append now with the new sprms
999 else if( nVarLen && pF->IsEqualPos( nEndFc ))
1000 pF->MergeToNew( nVarLen, pNewSprms );
1001 // has the prev EndFC an empty sprm and the current is empty too, then
1002 // expand only the old EndFc to the new EndFc
1003 else if( !nVarLen && pF->IsEmptySprm() )
1005 pF->SetNewEnd( nEndFc );
1006 return ;
1009 bool bOk = pF->Append(nEndFc, nVarLen, pNewSprms);
1010 if( !bOk )
1012 pF->Combine();
1013 pF = new WW8_WrFkp(ePlc, pF->GetEndFc()); // Start new Fkp == end of old Fkp
1015 m_Fkps.push_back(std::unique_ptr<WW8_WrFkp>(pF));
1016 if( !pF->Append( nEndFc, nVarLen, pNewSprms ) )
1018 OSL_ENSURE( false, "Unable to insert Sprm" );
1021 if( pNewSprms != pSprms ) //Merge to new has created a new block
1022 delete[] pNewSprms;
1025 void WW8_WrPlcPn::WriteFkps()
1027 nFkpStartPage = static_cast<sal_uInt16>( SwWW8Writer::FillUntil( rWrt.Strm() ) >> 9 );
1029 for(const std::unique_ptr<WW8_WrFkp> & rp : m_Fkps)
1031 rp->Write( rWrt.Strm(), *rWrt.m_pGrf );
1034 if( CHP == ePlc )
1036 rWrt.pFib->m_pnChpFirst = nFkpStartPage;
1037 rWrt.pFib->m_cpnBteChp = m_Fkps.size();
1039 else
1041 rWrt.pFib->m_pnPapFirst = nFkpStartPage;
1042 rWrt.pFib->m_cpnBtePap = m_Fkps.size();
1046 void WW8_WrPlcPn::WritePlc()
1048 sal_uInt64 nFcStart = rWrt.pTableStrm->Tell();
1049 decltype(m_Fkps)::size_type i;
1051 for (i = 0; i < m_Fkps.size(); ++i)
1053 SwWW8Writer::WriteLong( *rWrt.pTableStrm,
1054 m_Fkps[ i ]->GetStartFc() );
1057 SwWW8Writer::WriteLong( *rWrt.pTableStrm,
1058 m_Fkps[ i - 1 ]->GetEndFc() );
1060 // for every FKP output the page
1061 for (i = 0; i < m_Fkps.size(); ++i)
1063 SwWW8Writer::WriteLong( *rWrt.pTableStrm, i + nFkpStartPage );
1066 if( CHP == ePlc )
1068 rWrt.pFib->m_fcPlcfbteChpx = nFcStart;
1069 rWrt.pFib->m_lcbPlcfbteChpx = rWrt.pTableStrm->Tell() - nFcStart;
1071 else
1073 rWrt.pFib->m_fcPlcfbtePapx = nFcStart;
1074 rWrt.pFib->m_lcbPlcfbtePapx = rWrt.pTableStrm->Tell() - nFcStart;
1078 WW8_WrFkp::WW8_WrFkp(ePLCFT ePl, WW8_FC nStartFc)
1079 : ePlc(ePl), nStartGrp(511), nOldStartGrp(511),
1080 nItemSize( ( CHP == ePl ) ? 1 : 13 ),
1081 nIMax(0), nOldVarLen(0), bCombined(false)
1083 pFkp = reinterpret_cast<sal_uInt8*>(new sal_Int32[128]); // 512 Byte
1084 pOfs = reinterpret_cast<sal_uInt8*>(new sal_Int32[128]); // 512 Byte
1085 memset( pFkp, 0, 4 * 128 );
1086 memset( pOfs, 0, 4 * 128 );
1087 reinterpret_cast<sal_Int32*>(pFkp)[0] = nStartFc; // 0th entry FC at nStartFc
1090 WW8_WrFkp::~WW8_WrFkp()
1092 delete[] reinterpret_cast<sal_Int32 *>(pFkp);
1093 delete[] reinterpret_cast<sal_Int32 *>(pOfs);
1096 sal_uInt8 WW8_WrFkp::SearchSameSprm( sal_uInt16 nVarLen, const sal_uInt8* pSprms )
1098 if( 3 < nVarLen )
1100 // if the sprms contained picture-references then never equal!
1101 for( sal_uInt8 n = static_cast< sal_uInt8 >(nVarLen - 1); 3 < n; --n )
1102 if( pSprms[ n ] == GRF_MAGIC_3 &&
1103 pSprms[ n-1 ] == GRF_MAGIC_2 &&
1104 pSprms[ n-2 ] == GRF_MAGIC_1 )
1105 return 0;
1108 short i;
1109 for( i = 0; i < nIMax; i++ )
1111 sal_uInt8 nStart = pOfs[i * nItemSize];
1112 if( nStart )
1113 { // has Sprms
1114 const sal_uInt8* p = pFkp + ( static_cast<sal_uInt16>(nStart) << 1 );
1115 if( ( CHP == ePlc
1116 ? (*p++ == nVarLen)
1117 : ((static_cast<sal_uInt16>(*p++) << 1 ) == (( nVarLen+1) & 0xfffe)) )
1118 && !memcmp( p, pSprms, nVarLen ) )
1119 return nStart; // found it
1122 return 0; // didn't found it
1125 sal_uInt8 *WW8_WrFkp::CopyLastSprms(sal_uInt8 &rLen)
1127 rLen=0;
1128 sal_uInt8 *pStart=nullptr,*pRet=nullptr;
1130 if (!bCombined)
1131 pStart = pOfs;
1132 else
1133 pStart = pFkp + ( nIMax + 1 ) * 4;
1135 sal_uInt8 nStart = *(pStart + (nIMax-1) * nItemSize);
1137 const sal_uInt8* p = pFkp + ( static_cast<sal_uInt16>(nStart) << 1 );
1139 if (!*p)
1140 p++;
1142 if (*p)
1144 rLen = *p++;
1145 if (PAP == ePlc)
1146 rLen *= 2;
1147 pRet = new sal_uInt8[rLen];
1148 memcpy(pRet,p,rLen);
1150 return pRet;
1153 bool WW8_WrFkp::Append( WW8_FC nEndFc, sal_uInt16 nVarLen, const sal_uInt8* pSprms )
1155 assert((!nVarLen || pSprms) && "Item pointer missing");
1157 OSL_ENSURE( nVarLen < ( ( ePlc == PAP ) ? 497U : 502U ), "Sprms too long !" );
1159 if( bCombined )
1161 OSL_ENSURE( false, "Fkp::Append: Fkp is already combined" );
1162 return false;
1164 sal_Int32 n = reinterpret_cast<sal_Int32*>(pFkp)[nIMax]; // last entry
1165 if( nEndFc <= n )
1167 OSL_ENSURE( nEndFc >= n, "+Fkp: FC backwards" );
1168 OSL_ENSURE( !nVarLen || !pSprms || nEndFc != n,
1169 "+Fkp: used same FC multiple times" );
1170 // same FC without Sprm is ignored without grumbling
1172 return true; // ignore (do not create a new Fkp)
1175 sal_uInt8 nOldP = nVarLen ? SearchSameSprm( nVarLen, pSprms ) : 0;
1176 // Combine equal entries
1177 short nOffset=0, nPos = nStartGrp;
1178 if (nVarLen && !nOldP)
1180 nPos = PAP == ePlc
1181 ? ( 13 == nItemSize // HACK: PAP and bWrtWW8 !!
1182 ? (nStartGrp & 0xFFFE ) - nVarLen - 1
1183 : (nStartGrp - (((nVarLen + 1) & 0xFFFE)+1)) & 0xFFFE )
1184 : ((nStartGrp - nVarLen - 1) & 0xFFFE);
1185 if( nPos < 0 )
1186 return false; // doesn't fit at all
1187 nOffset = nPos; // save offset (can also be uneven!)
1188 nPos &= 0xFFFE; // Pos for Sprms ( gerade Pos )
1191 if( o3tl::make_unsigned(nPos) <= ( nIMax + 2U ) * 4U + ( nIMax + 1U ) * nItemSize )
1192 // does it fits after the CPs and offsets?
1193 return false; // no
1195 reinterpret_cast<sal_Int32*>(pFkp)[nIMax + 1] = nEndFc; // insert FC
1197 nOldVarLen = static_cast<sal_uInt8>(nVarLen);
1198 if( nVarLen && !nOldP )
1199 { // insert it for real
1200 nOldStartGrp = nStartGrp;
1202 nStartGrp = nPos;
1203 pOfs[nIMax * nItemSize] = static_cast<sal_uInt8>( nStartGrp >> 1 );
1204 // insert (start-of-data >> 1)
1205 sal_uInt8 nCnt = static_cast< sal_uInt8 >(CHP == ePlc
1206 ? ( nVarLen < 256 ) ? static_cast<sal_uInt8>(nVarLen) : 255
1207 : ( ( nVarLen + 1 ) >> 1 ));
1209 pFkp[ nOffset ] = nCnt; // Enter data length
1210 memcpy( pFkp + nOffset + 1, pSprms, nVarLen ); // store Sprms
1212 else
1214 // do not enter for real ( no Sprms or recurrence )
1215 // start-of-data 0 ( no data ) or recurrence
1216 pOfs[nIMax * nItemSize] = nOldP;
1218 nIMax++;
1219 return true;
1222 void WW8_WrFkp::Combine()
1224 if( bCombined )
1225 return;
1226 if( nIMax )
1227 memcpy( pFkp + ( nIMax + 1 ) * 4, pOfs, nIMax * nItemSize );
1228 delete[] pOfs;
1229 pOfs = nullptr;
1230 pFkp[511] = nIMax;
1231 bCombined = true;
1233 #if defined OSL_BIGENDIAN // only the FCs will be rotated here
1234 sal_uInt16 i; // the Sprms must be rotated elsewhere
1236 sal_uInt32* p;
1237 for( i = 0, p = (sal_uInt32*)pFkp; i <= nIMax; i++, p++ )
1238 *p = OSL_SWAPDWORD( *p );
1239 #endif // ifdef OSL_BIGENDIAN
1242 void WW8_WrFkp::Write( SvStream& rStrm, SwWW8WrGrf& rGrf )
1244 Combine(); // If not already combined
1246 sal_uInt8* p; // search magic for nPicLocFc
1247 sal_uInt8* pEnd = pFkp + nStartGrp;
1248 for( p = pFkp + 511 - 4; p >= pEnd; p-- )
1250 if( *p != GRF_MAGIC_1 ) // search for signature 0x12 0x34 0x56 0xXX
1251 continue;
1252 if( *(p+1) != GRF_MAGIC_2 )
1253 continue;
1254 if( *(p+2) != GRF_MAGIC_3 )
1255 continue;
1257 SVBT32 nPos; // signature found
1258 UInt32ToSVBT32( rGrf.GetFPos(), nPos ); // FilePos the graphics
1259 memcpy( p, nPos, 4 ); // patch FilePos over the signature
1261 rStrm.WriteBytes(pFkp, 512);
1264 void WW8_WrFkp::MergeToNew( short& rVarLen, sal_uInt8 *& rpNewSprms )
1266 sal_uInt8 nStart = pOfs[ (nIMax-1) * nItemSize ];
1267 if( !nStart )
1268 return;
1270 // has Sprms
1271 sal_uInt8* p = pFkp + ( static_cast<sal_uInt16>(nStart) << 1 );
1273 // old and new equal? Then copy only one into the new sprms
1274 if( nOldVarLen == rVarLen && !memcmp( p+1, rpNewSprms, nOldVarLen ))
1276 sal_uInt8* pNew = new sal_uInt8[ nOldVarLen ];
1277 memcpy( pNew, p+1, nOldVarLen );
1278 rpNewSprms = pNew;
1280 else
1282 sal_uInt8* pNew = new sal_uInt8[ nOldVarLen + rVarLen ];
1283 memcpy( pNew, p+1, nOldVarLen );
1284 memcpy( pNew + nOldVarLen, rpNewSprms, rVarLen );
1286 rpNewSprms = pNew;
1287 rVarLen = rVarLen + nOldVarLen;
1289 --nIMax;
1290 // if this Sprms don't used from others, remove it
1291 bool bFnd = false;
1292 for (sal_uInt16 n = 0; n < nIMax; ++n)
1294 if (nStart == pOfs[n * nItemSize])
1296 bFnd = true;
1297 break;
1300 if (!bFnd)
1302 nStartGrp = nOldStartGrp;
1303 memset( p, 0, nOldVarLen+1 );
1307 WW8_FC WW8_WrFkp::GetStartFc() const
1309 // when bCombined, then the array beginning with pFkp is already byte-swapped
1310 // to LittleEndian, so to extract the start and end positions they must
1311 // be swapped back.
1312 if( bCombined )
1313 return SVBT32ToUInt32( pFkp ); // 0. Element
1314 return reinterpret_cast<sal_Int32*>(pFkp)[0];
1317 WW8_FC WW8_WrFkp::GetEndFc() const
1319 if( bCombined )
1320 return SVBT32ToUInt32( &(pFkp[nIMax*4]) ); // nIMax-th SVBT32-Element
1321 return reinterpret_cast<sal_Int32*>(pFkp)[nIMax];
1324 // Method for managing the piece table
1325 WW8_WrPct::WW8_WrPct(WW8_FC nfcMin)
1326 : nOldFc(nfcMin)
1328 AppendPc(nOldFc);
1331 WW8_WrPct::~WW8_WrPct()
1335 // Fill the piece and create a new one
1336 void WW8_WrPct::AppendPc(WW8_FC nStartFc)
1338 WW8_CP nStartCp = nStartFc - nOldFc; // subtract the beginning of the text
1339 if ( !nStartCp && !m_Pcts.empty())
1341 OSL_ENSURE(1 == m_Pcts.size(), "empty Piece!");
1342 m_Pcts.pop_back();
1345 nOldFc = nStartFc; // remember StartFc as old
1347 nStartCp >>= 1; // for Unicode: number of characters / 2
1349 if (!m_Pcts.empty())
1351 nStartCp += m_Pcts.back()->GetStartCp();
1354 m_Pcts.push_back(std::make_unique<WW8_WrPc>(nStartFc, nStartCp));
1357 void WW8_WrPct::WritePc( WW8Export& rWrt )
1359 sal_uInt64 nPctStart;
1360 sal_uLong nOldPos, nEndPos;
1362 nPctStart = rWrt.pTableStrm->Tell(); // Start piece table
1363 rWrt.pTableStrm->WriteChar( char(0x02) ); // Status byte PCT
1364 nOldPos = nPctStart + 1; // remember Position
1365 SwWW8Writer::WriteLong( *rWrt.pTableStrm, 0 ); // then the length
1367 for (auto const& it : m_Pcts) // ranges
1369 SwWW8Writer::WriteLong( *rWrt.pTableStrm, it->GetStartCp() );
1372 // calculate the last Pos
1373 sal_uLong nStartCp = rWrt.pFib->m_fcMac - nOldFc;
1374 nStartCp >>= 1; // For Unicode: number of characters / 2
1375 nStartCp += m_Pcts.back()->GetStartCp();
1376 SwWW8Writer::WriteLong( *rWrt.pTableStrm, nStartCp );
1378 // piece references
1379 for (auto const& it : m_Pcts)
1381 SwWW8Writer::WriteShort(*rWrt.pTableStrm, it->GetStatus());
1382 SwWW8Writer::WriteLong(*rWrt.pTableStrm, it->GetStartFc());
1383 SwWW8Writer::WriteShort( *rWrt.pTableStrm, 0); // PRM=0
1386 // entries in the FIB
1387 rWrt.pFib->m_fcClx = nPctStart;
1388 nEndPos = rWrt.pTableStrm->Tell();
1389 rWrt.pFib->m_lcbClx = nEndPos - nPctStart;
1391 // and register the length as well
1392 SwWW8Writer::WriteLong( *rWrt.pTableStrm, nOldPos,
1393 nEndPos - nPctStart-5 );
1397 void WW8_WrPct::SetParaBreak()
1399 OSL_ENSURE( !m_Pcts.empty(), "SetParaBreak : m_Pcts.empty()" );
1400 m_Pcts.back()->SetStatus();
1403 WW8_CP WW8_WrPct::Fc2Cp( sal_uLong nFc ) const
1405 OSL_ENSURE( nFc >= o3tl::make_unsigned(nOldFc), "FilePos lies in front of last piece" );
1406 OSL_ENSURE( ! m_Pcts.empty(), "Fc2Cp no piece available" );
1408 nFc -= nOldFc;
1409 nFc /= 2; // Unicode
1410 return nFc + m_Pcts.back()->GetStartCp();
1413 void WW8Export::AppendBookmarks( const SwTextNode& rNd, sal_Int32 nCurrentPos, sal_Int32 nLen )
1415 std::vector< const ::sw::mark::IMark* > aArr;
1416 sal_uInt16 nContent;
1417 const sal_Int32 nCurrentEnd = nCurrentPos + nLen;
1418 if( !GetWriter().GetBookmarks( rNd, nCurrentPos, nCurrentEnd, aArr ))
1419 return;
1421 sal_uLong nNd = rNd.GetIndex(), nSttCP = Fc2Cp( Strm().Tell() );
1422 for(const ::sw::mark::IMark* p : aArr)
1424 const ::sw::mark::IMark& rBkmk = *p;
1425 if(dynamic_cast< const ::sw::mark::IFieldmark *>(&rBkmk))
1426 continue;
1428 const SwPosition* pPos = &rBkmk.GetMarkPos();
1429 const SwPosition* pOPos = nullptr;
1430 if(rBkmk.IsExpanded())
1431 pOPos = &rBkmk.GetOtherMarkPos();
1432 if( pOPos && pOPos->nNode == pPos->nNode &&
1433 pOPos->nContent < pPos->nContent )
1435 pPos = pOPos;
1436 pOPos = &rBkmk.GetMarkPos();
1439 if( !pOPos || ( nNd == pPos->nNode.GetIndex() &&
1440 ( nContent = pPos->nContent.GetIndex() ) >= nCurrentPos &&
1441 nContent < nCurrentEnd ) )
1443 sal_uLong nCp = nSttCP + pPos->nContent.GetIndex() - nCurrentPos;
1444 m_pBkmks->Append(nCp, BookmarkToWord(rBkmk.GetName()));
1446 if( pOPos && nNd == pOPos->nNode.GetIndex() &&
1447 ( nContent = pOPos->nContent.GetIndex() ) >= nCurrentPos &&
1448 nContent < nCurrentEnd )
1450 sal_uLong nCp = nSttCP + pOPos->nContent.GetIndex() - nCurrentPos;
1451 m_pBkmks->Append(nCp, BookmarkToWord(rBkmk.GetName()));
1456 void WW8Export::AppendAnnotationMarks(const SwWW8AttrIter& rAttrs, sal_Int32 nCurrentPos, sal_Int32 nLen)
1458 IMarkVector aMarks;
1459 if (GetAnnotationMarks(rAttrs, nCurrentPos, nCurrentPos + nLen, aMarks))
1461 for (const sw::mark::IMark* pMark : aMarks)
1463 const sal_Int32 nStart = pMark->GetMarkStart().nContent.GetIndex();
1464 if (nStart == nCurrentPos)
1466 m_pAtn->AddRangeStartPosition(pMark->GetName(), Fc2Cp(Strm().Tell()),
1467 !rAttrs.HasFlysAt(nCurrentPos));
1473 void WW8Export::AppendSmartTags(SwTextNode& rTextNode)
1475 std::map<OUString, OUString> aStatements = SwRDFHelper::getTextNodeStatements("urn:bails", rTextNode);
1476 if (!aStatements.empty())
1478 WW8_CP nCP = Fc2Cp(Strm().Tell());
1479 m_pFactoids->Append(nCP, nCP, aStatements);
1483 void WW8Export::MoveFieldMarks(WW8_CP nFrom, WW8_CP nTo)
1485 m_pBkmks->MoveFieldMarks(nFrom, nTo);
1488 void WW8Export::AppendBookmark( const OUString& rName )
1490 sal_uLong nSttCP = Fc2Cp( Strm().Tell() );
1491 m_pBkmks->Append( nSttCP, rName );
1494 void WW8Export::AppendBookmarkEndWithCorrection( const OUString& rName )
1496 sal_uLong nEndCP = Fc2Cp( Strm().Tell() );
1497 m_pBkmks->Append( nEndCP - 1, rName );
1500 std::unique_ptr<SvxBrushItem> MSWordExportBase::getBackground()
1502 const SwFrameFormat &rFormat = m_rDoc.GetPageDesc(0).GetMaster();
1503 std::unique_ptr<SvxBrushItem> aBrush = std::make_unique<SvxBrushItem>(RES_BACKGROUND);
1504 SfxItemState eState = rFormat.GetBackgroundState(aBrush);
1506 if (SfxItemState::SET == eState)
1508 // The 'color' is set for the first page style - take it and use it as the background color of the entire DOCX
1509 if (aBrush->GetColor() != COL_AUTO)
1510 return aBrush;
1512 return nullptr;
1515 // #i120928 collect all the graphics of bullets applied to paragraphs
1516 int MSWordExportBase::CollectGrfsOfBullets()
1518 m_vecBulletPic.clear();
1520 size_t nCountRule = m_rDoc.GetNumRuleTable().size();
1521 for (size_t n = 0; n < nCountRule; ++n)
1523 const SwNumRule &rRule = *( m_rDoc.GetNumRuleTable().at(n) );
1524 sal_uInt16 nLevels = rRule.IsContinusNum() ? 1 : 9;
1525 for (sal_uInt16 nLvl = 0; nLvl < nLevels; ++nLvl)
1527 const SwNumFormat &rFormat = rRule.Get(nLvl);
1528 if (SVX_NUM_BITMAP != rFormat.GetNumberingType())
1530 continue;
1532 const Graphic *pGraf = rFormat.GetBrush()? rFormat.GetBrush()->GetGraphic():nullptr;
1533 if ( pGraf )
1535 bool bHas = false;
1536 for (const Graphic* p : m_vecBulletPic)
1538 if (p->GetChecksum() == pGraf->GetChecksum())
1540 bHas = true;
1541 break;
1544 if (!bHas)
1546 Size aSize(pGraf->GetPrefSize());
1547 if (0 != aSize.Height() && 0 != aSize.Width())
1548 m_vecBulletPic.push_back(pGraf);
1554 return m_vecBulletPic.size();
1557 void MSWordExportBase::BulletDefinitions()
1559 for (size_t i = 0; i < m_vecBulletPic.size(); ++i)
1561 const MapMode aMapMode(MapUnit::MapTwip);
1562 const Graphic& rGraphic = *m_vecBulletPic[i];
1563 Size aSize(rGraphic.GetPrefSize());
1564 if (MapUnit::MapPixel == rGraphic.GetPrefMapMode().GetMapUnit())
1565 aSize = Application::GetDefaultDevice()->PixelToLogic(aSize, aMapMode);
1566 else
1567 aSize = OutputDevice::LogicToLogic(aSize,rGraphic.GetPrefMapMode(), aMapMode);
1569 if (0 != aSize.Height() && 0 != aSize.Width())
1570 AttrOutput().BulletDefinition(i, rGraphic, aSize);
1574 //Export Graphic of Bullets
1575 void WW8Export::ExportGrfBullet(const SwTextNode& rNd)
1577 int nCount = CollectGrfsOfBullets();
1578 if (nCount > 0)
1580 SwPosition aPos(rNd);
1581 OUString aPicBullets("_PictureBullets");
1582 AppendBookmark(aPicBullets);
1583 for (int i = 0; i < nCount; i++)
1585 ww8::Frame aFrame(*(m_vecBulletPic[i]), aPos);
1586 OutGrfBullets(aFrame);
1588 AppendBookmark(aPicBullets);
1592 static sal_uInt8 nAttrMagicIdx = 0;
1593 void WW8Export::OutGrfBullets(const ww8::Frame & rFrame)
1595 if ( !m_pGrf || !m_pChpPlc || !pO )
1596 return;
1598 m_pGrf->Insert(rFrame);
1599 m_pChpPlc->AppendFkpEntry( Strm().Tell(), pO->size(), pO->data() );
1600 pO->clear();
1601 // if links...
1602 WriteChar( char(1) );
1604 sal_uInt8 aArr[ 22 ];
1605 sal_uInt8* pArr = aArr;
1607 // sprmCFSpec
1608 Set_UInt16( pArr, 0x855 );
1609 Set_UInt8( pArr, 1 );
1611 Set_UInt16( pArr, 0x083c );
1612 Set_UInt8( pArr, 0x81 );
1614 // sprmCPicLocation
1615 Set_UInt16( pArr, 0x6a03 );
1616 Set_UInt32( pArr, GRF_MAGIC_321 );
1618 //extern nAttrMagicIdx;
1619 --pArr;
1620 Set_UInt8( pArr, nAttrMagicIdx++ );
1621 m_pChpPlc->AppendFkpEntry( Strm().Tell(), static_cast< short >(pArr - aArr), aArr );
1624 int MSWordExportBase::GetGrfIndex(const SvxBrushItem& rBrush)
1626 int nIndex = -1;
1628 const Graphic* pGraphic = rBrush.GetGraphic();
1629 if (pGraphic)
1631 for (size_t i = 0; i < m_vecBulletPic.size(); ++i)
1633 if (m_vecBulletPic[i]->GetChecksum() == pGraphic->GetChecksum())
1635 nIndex = i;
1636 break;
1641 return nIndex;
1644 void WW8_WrtRedlineAuthor::Write( Writer& rWrt )
1646 WW8Export & rWW8Wrt = *(static_cast<SwWW8Writer&>(rWrt).m_pExport);
1647 rWW8Wrt.WriteAsStringTable(maAuthors, rWW8Wrt.pFib->m_fcSttbfRMark,
1648 rWW8Wrt.pFib->m_lcbSttbfRMark);
1651 sal_uInt16 WW8Export::AddRedlineAuthor( std::size_t nId )
1653 if( !m_pRedlAuthors )
1655 m_pRedlAuthors = new WW8_WrtRedlineAuthor;
1656 m_pRedlAuthors->AddName("Unknown");
1658 return m_pRedlAuthors->AddName( SW_MOD()->GetRedlineAuthor( nId ) );
1661 void WW8Export::WriteAsStringTable(const std::vector<OUString>& rStrings,
1662 sal_Int32& rfcSttbf, sal_Int32& rlcbSttbf)
1664 sal_uInt16 n, nCount = static_cast< sal_uInt16 >(rStrings.size());
1665 if( !nCount )
1666 return;
1668 // we have some Redlines found in the document -> the
1669 // Author Name Stringtable
1670 SvStream& rStrm = *pTableStrm;
1671 rfcSttbf = rStrm.Tell();
1672 SwWW8Writer::WriteShort( rStrm, -1 );
1673 SwWW8Writer::WriteLong( rStrm, nCount );
1674 for( n = 0; n < nCount; ++n )
1676 const OUString& rNm = rStrings[n];
1677 SwWW8Writer::WriteShort( rStrm, rNm.getLength() );
1678 SwWW8Writer::WriteString16(rStrm, rNm, false);
1680 rlcbSttbf = rStrm.Tell() - rfcSttbf;
1683 // WriteShort() sets at FilePos nPos the value nVal and seeks to the old
1684 // FilePos. Used to insert lengths after the fact.
1685 void SwWW8Writer::WriteShort( SvStream& rStrm, sal_uLong nPos, sal_Int16 nVal )
1687 sal_uInt64 nOldPos = rStrm.Tell(); // remember Pos
1688 rStrm.Seek( nPos );
1689 SwWW8Writer::WriteShort( rStrm, nVal );
1690 rStrm.Seek( nOldPos );
1693 void SwWW8Writer::WriteLong( SvStream& rStrm, sal_uLong nPos, sal_Int32 nVal )
1695 sal_uInt64 nOldPos = rStrm.Tell(); // remember Pos
1696 rStrm.Seek( nPos );
1697 SwWW8Writer::WriteLong( rStrm, nVal );
1698 rStrm.Seek( nOldPos );
1701 void SwWW8Writer::InsUInt16(ww::bytes &rO, sal_uInt16 n)
1703 SVBT16 nL;
1704 ShortToSVBT16( n, nL );
1705 rO.push_back(nL[0]);
1706 rO.push_back(nL[1]);
1709 void SwWW8Writer::InsUInt32(ww::bytes &rO, sal_uInt32 n)
1711 SVBT32 nL;
1712 UInt32ToSVBT32( n, nL );
1713 rO.push_back(nL[0]);
1714 rO.push_back(nL[1]);
1715 rO.push_back(nL[2]);
1716 rO.push_back(nL[3]);
1719 void SwWW8Writer::InsAsString16(ww::bytes &rO, const OUString& rStr)
1721 const sal_Unicode* pStr = rStr.getStr();
1722 for (sal_Int32 n = 0, nLen = rStr.getLength(); n < nLen; ++n, ++pStr)
1723 SwWW8Writer::InsUInt16( rO, *pStr );
1726 void SwWW8Writer::InsAsString8(ww::bytes &rO, const OUString& rStr,
1727 rtl_TextEncoding eCodeSet)
1729 OString sTmp(OUStringToOString(rStr, eCodeSet));
1730 const char *pStart = sTmp.getStr();
1731 const char *pEnd = pStart + sTmp.getLength();
1733 rO.insert( rO.end(), pStart, pEnd );
1736 void SwWW8Writer::WriteString16(SvStream& rStrm, const OUString& rStr,
1737 bool bAddZero)
1739 ww::bytes aBytes;
1740 SwWW8Writer::InsAsString16(aBytes, rStr);
1741 if (bAddZero)
1742 SwWW8Writer::InsUInt16(aBytes, 0);
1743 //vectors are guaranteed to have contiguous memory, so we can do
1744 //this while migrating away from WW8Bytes. Meyers Effective STL, item 16
1745 if (!aBytes.empty())
1746 rStrm.WriteBytes(aBytes.data(), aBytes.size());
1749 void SwWW8Writer::WriteString_xstz(SvStream& rStrm, const OUString& rStr, bool bAddZero)
1751 ww::bytes aBytes;
1752 SwWW8Writer::InsUInt16(aBytes, rStr.getLength());
1753 SwWW8Writer::InsAsString16(aBytes, rStr);
1754 if (bAddZero)
1755 SwWW8Writer::InsUInt16(aBytes, 0);
1756 rStrm.WriteBytes(aBytes.data(), aBytes.size());
1759 void SwWW8Writer::WriteString8(SvStream& rStrm, const OUString& rStr,
1760 bool bAddZero, rtl_TextEncoding eCodeSet)
1762 ww::bytes aBytes;
1763 SwWW8Writer::InsAsString8(aBytes, rStr, eCodeSet);
1764 if (bAddZero)
1765 aBytes.push_back(0);
1766 //vectors are guaranteed to have contiguous memory, so we can do
1767 ////this while migrating away from WW8Bytes. Meyers Effective STL, item 16
1768 if (!aBytes.empty())
1769 rStrm.WriteBytes(aBytes.data(), aBytes.size());
1772 void WW8Export::WriteStringAsPara( const OUString& rText )
1774 if( !rText.isEmpty() )
1775 OutSwString(rText, 0, rText.getLength());
1776 WriteCR(); // CR thereafter
1778 ww::bytes aArr;
1779 SwWW8Writer::InsUInt16( aArr, 0/*nStyleId*/ );
1780 if( m_bOutTable )
1781 { // Tab-Attr
1782 // sprmPFInTable
1783 SwWW8Writer::InsUInt16( aArr, NS_sprm::PFInTable::val );
1784 aArr.push_back( 1 );
1787 sal_uInt64 nPos = Strm().Tell();
1788 m_pPapPlc->AppendFkpEntry( nPos, aArr.size(), aArr.data() );
1789 m_pChpPlc->AppendFkpEntry( nPos );
1792 void MSWordExportBase::WriteSpecialText( sal_uLong nStart, sal_uLong nEnd, sal_uInt8 nTTyp )
1794 sal_uInt8 nOldTyp = m_nTextTyp;
1795 m_nTextTyp = nTTyp;
1796 auto const pOldPam = m_pCurPam; //!! Simply shifting the PaM without restoring should do the job too
1797 sal_uLong nOldStart = m_nCurStart;
1798 sal_uLong nOldEnd = m_nCurEnd;
1799 SwPaM* pOldEnd = m_pOrigPam;
1800 bool bOldPageDescs = m_bOutPageDescs;
1801 m_bOutPageDescs = false;
1802 if ( nTTyp == TXT_FTN || nTTyp == TXT_EDN )
1803 m_bAddFootnoteTab = true; // enable one aesthetic tab for this footnote
1805 SetCurPam(nStart, nEnd);
1807 // clear linked textboxes since old ones can't be linked to frames in this section
1808 m_aLinkedTextboxesHelper.clear();
1810 // tdf#106261 Reset table infos, otherwise the depth of the cells will be
1811 // incorrect, in case the header/footer had table(s) and we try to export
1812 // the same table second time.
1813 ww8::WW8TableInfo::Pointer_t pOldTableInfo = m_pTableInfo;
1814 m_pTableInfo = std::make_shared<ww8::WW8TableInfo>();
1816 WriteText();
1818 m_pTableInfo = pOldTableInfo;
1820 m_bOutPageDescs = bOldPageDescs;
1821 m_pCurPam = pOldPam; // delete Pam
1822 m_nCurStart = nOldStart;
1823 m_nCurEnd = nOldEnd;
1824 m_pOrigPam = pOldEnd;
1825 m_nTextTyp = nOldTyp;
1828 void WW8Export::OutSwString(const OUString& rStr, sal_Int32 nStt,
1829 sal_Int32 const nLen)
1832 SAL_INFO( "sw.ww8.level2", "<OutSwString>" );
1834 if( nLen )
1836 if( nStt || nLen != rStr.getLength() )
1838 OUString sOut( rStr.copy( nStt, nLen ) );
1840 SAL_INFO( "sw.ww8.level2", sOut );
1842 SwWW8Writer::WriteString16(Strm(), sOut, false);
1844 else
1846 SAL_INFO( "sw.ww8.level2", rStr );
1848 SwWW8Writer::WriteString16(Strm(), rStr, false);
1852 SAL_INFO( "sw.ww8.level2", "</OutSwString>" );
1855 void WW8Export::WriteCR(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
1857 if (pTableTextNodeInfoInner && pTableTextNodeInfoInner->getDepth() == 1 && pTableTextNodeInfoInner->isEndOfCell())
1858 WriteChar('\007');
1859 else
1860 WriteChar( '\015' );
1862 m_pPiece->SetParaBreak();
1865 void WW8Export::WriteChar( sal_Unicode c )
1867 Strm().WriteUInt16( c );
1870 void MSWordExportBase::SetCurPam(sal_uLong nStt, sal_uLong nEnd)
1872 m_nCurStart = nStt;
1873 m_nCurEnd = nEnd;
1874 m_pCurPam = Writer::NewUnoCursor( m_rDoc, nStt, nEnd );
1876 // Recognize tables in special cases
1877 if ( nStt != m_pCurPam->GetMark()->nNode.GetIndex() &&
1878 m_rDoc.GetNodes()[ nStt ]->IsTableNode() )
1880 m_pCurPam->GetMark()->nNode = nStt;
1883 m_pOrigPam = m_pCurPam.get(); // ???
1884 m_pCurPam->Exchange();
1887 void MSWordExportBase::SaveData( sal_uLong nStt, sal_uLong nEnd )
1889 MSWordSaveData aData;
1891 // WW8Export only stuff - zeroed here not to issue warnings
1892 aData.pOOld = nullptr;
1894 // Common stuff
1895 aData.pOldPam = m_pCurPam;
1896 aData.pOldEnd = m_pOrigPam;
1897 aData.pOldFlyFormat = m_pParentFrame;
1898 aData.pOldPageDesc = m_pCurrentPageDesc;
1900 aData.pOldFlyOffset = m_pFlyOffset;
1901 aData.eOldAnchorType = m_eNewAnchorType;
1903 aData.bOldOutTable = m_bOutTable;
1904 aData.bOldFlyFrameAttrs = m_bOutFlyFrameAttrs;
1905 aData.bOldStartTOX = m_bStartTOX;
1906 aData.bOldInWriteTOX = m_bInWriteTOX;
1908 SetCurPam(nStt, nEnd);
1910 m_bOutTable = false;
1911 // Caution: bIsInTable should not be set here
1912 m_bOutFlyFrameAttrs = false;
1913 m_bStartTOX = false;
1914 m_bInWriteTOX = false;
1916 m_aSaveData.push( std::move(aData) );
1919 void MSWordExportBase::RestoreData()
1921 MSWordSaveData &rData = m_aSaveData.top();
1923 m_pCurPam = rData.pOldPam;
1924 m_nCurStart = rData.nOldStart;
1925 m_nCurEnd = rData.nOldEnd;
1926 m_pOrigPam = rData.pOldEnd;
1928 m_bOutTable = rData.bOldOutTable;
1929 m_bOutFlyFrameAttrs = rData.bOldFlyFrameAttrs;
1930 m_bStartTOX = rData.bOldStartTOX;
1931 m_bInWriteTOX = rData.bOldInWriteTOX;
1933 m_pParentFrame = rData.pOldFlyFormat;
1934 m_pCurrentPageDesc = rData.pOldPageDesc;
1936 m_eNewAnchorType = rData.eOldAnchorType;
1937 m_pFlyOffset = rData.pOldFlyOffset;
1939 m_aSaveData.pop();
1942 void WW8Export::SaveData( sal_uLong nStt, sal_uLong nEnd )
1944 MSWordExportBase::SaveData( nStt, nEnd );
1946 MSWordSaveData &rData = m_aSaveData.top();
1948 if ( !pO->empty() )
1950 rData.pOOld = std::move(pO);
1951 pO.reset(new ww::bytes);
1953 else
1954 rData.pOOld = nullptr; // reuse pO
1956 rData.bOldWriteAll = GetWriter().m_bWriteAll;
1957 GetWriter().m_bWriteAll = true;
1960 void WW8Export::RestoreData()
1962 MSWordSaveData &rData = m_aSaveData.top();
1964 GetWriter().m_bWriteAll = rData.bOldWriteAll;
1966 OSL_ENSURE( pO->empty(), "pO is not empty in WW8Export::RestoreData()" );
1967 if ( rData.pOOld )
1969 pO = std::move(rData.pOOld);
1972 MSWordExportBase::RestoreData();
1975 void WW8AttributeOutput::TableInfoCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
1977 sal_uInt32 nDepth = pTableTextNodeInfoInner->getDepth();
1979 if ( nDepth <= 0 )
1980 return;
1982 /* Cell */
1983 m_rWW8Export.InsUInt16( NS_sprm::PFInTable::val );
1984 m_rWW8Export.pO->push_back( sal_uInt8(0x1) );
1985 m_rWW8Export.InsUInt16( NS_sprm::PItap::val );
1986 m_rWW8Export.InsUInt32( nDepth );
1988 if ( nDepth > 1 && pTableTextNodeInfoInner->isEndOfCell() )
1990 m_rWW8Export.InsUInt16( NS_sprm::PFInnerTableCell::val );
1991 m_rWW8Export.pO->push_back( sal_uInt8(0x1) );
1995 void WW8AttributeOutput::TableInfoRow( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
1997 sal_uInt32 nDepth = pTableTextNodeInfoInner->getDepth();
1999 if ( nDepth <= 0 )
2000 return;
2002 /* Row */
2003 if ( !pTableTextNodeInfoInner->isEndOfLine() )
2004 return;
2006 m_rWW8Export.InsUInt16( NS_sprm::PFInTable::val );
2007 m_rWW8Export.pO->push_back( sal_uInt8(0x1) );
2009 if ( nDepth == 1 )
2011 m_rWW8Export.InsUInt16( NS_sprm::PFTtp::val );
2012 m_rWW8Export.pO->push_back( sal_uInt8(0x1) );
2015 m_rWW8Export.InsUInt16( NS_sprm::PItap::val );
2016 m_rWW8Export.InsUInt32( nDepth );
2018 if ( nDepth > 1 )
2020 m_rWW8Export.InsUInt16( NS_sprm::PFInnerTableCell::val );
2021 m_rWW8Export.pO->push_back( sal_uInt8(0x1) );
2022 m_rWW8Export.InsUInt16( NS_sprm::PFInnerTtp::val );
2023 m_rWW8Export.pO->push_back( sal_uInt8(0x1) );
2026 // Most of these are per-row definitions, not per-table.
2027 // WW8 has no explicit table start/end markup,
2028 // simply rows with the same table properties that are grouped together as a table.
2029 TableBidi( pTableTextNodeInfoInner );
2030 TableOrientation( pTableTextNodeInfoInner );
2031 TableSpacing( pTableTextNodeInfoInner );
2032 TableDefinition( pTableTextNodeInfoInner ); //per row definitions
2033 TableHeight( pTableTextNodeInfoInner ); //per row definitions
2034 TableBackgrounds( pTableTextNodeInfoInner ); //per row definitions
2035 // Since this isEndOfLine, cell margin defaults for each row come from last column.
2036 TableDefaultBorders( pTableTextNodeInfoInner ); //per row definitions
2037 TableCanSplit( pTableTextNodeInfoInner ); //per row definitions
2038 TableVerticalCell( pTableTextNodeInfoInner ); //per row definitions
2039 TableCellBorders( pTableTextNodeInfoInner ); //per row definitions
2042 static sal_uInt16 lcl_TCFlags(SwDoc &rDoc, const SwTableBox * pBox, sal_Int32 nRowSpan)
2044 sal_uInt16 nFlags = 0;
2046 if (nRowSpan > 1)
2047 nFlags |= (3 << 5);
2048 else if (nRowSpan < 0)
2049 nFlags |= (1 << 5);
2051 if (pBox != nullptr)
2053 const SwFrameFormat * pFormat = pBox->GetFrameFormat();
2054 switch (pFormat->GetVertOrient().GetVertOrient())
2056 case text::VertOrientation::CENTER:
2057 nFlags |= (1 << 7);
2058 break;
2059 case text::VertOrientation::BOTTOM:
2060 nFlags |= (2 << 7);
2061 break;
2062 default:
2063 break;
2065 const SwStartNode * pSttNd = pBox->GetSttNd();
2066 if(pSttNd)
2068 SwNodeIndex aIdx( *pSttNd );
2069 const SwContentNode * pCNd = pSttNd->GetNodes().GoNext( &aIdx );
2070 if( pCNd && pCNd->IsTextNode())
2072 SfxItemSet aCoreSet(rDoc.GetAttrPool(), svl::Items<RES_CHRATR_ROTATE, RES_CHRATR_ROTATE>{});
2073 static_cast<const SwTextNode*>(pCNd)->GetParaAttr(aCoreSet,
2074 0, static_cast<const SwTextNode*>(pCNd)->GetText().getLength());
2075 const SfxPoolItem * pRotItem;
2076 if ( SfxItemState::SET == aCoreSet.GetItemState(RES_CHRATR_ROTATE, true, &pRotItem))
2078 const SvxCharRotateItem * pRotate = static_cast<const SvxCharRotateItem*>(pRotItem);
2079 if(pRotate && pRotate->GetValue() == Degree10(900))
2081 nFlags = nFlags | 0x0004 | 0x0008;
2083 else if(pRotate && pRotate->GetValue() == Degree10(2700) )
2085 nFlags = nFlags | 0x0004 | 0x0010;
2092 return nFlags;
2095 void WW8AttributeOutput::TableVerticalCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
2097 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
2098 const SwTableLine * pTabLine = pTabBox->GetUpper();
2099 const SwTableBoxes & rTableBoxes = pTabLine->GetTabBoxes();
2101 sal_uInt8 nBoxes = rTableBoxes.size();
2102 for ( sal_uInt8 n = 0; n < nBoxes; n++ )
2104 const SwTableBox * pTabBox1 = rTableBoxes[n];
2105 const SwFrameFormat * pFrameFormat = pTabBox1->GetFrameFormat();
2107 // Map from our SvxFrameDirection to WW8 TextFlow.
2108 sal_uInt16 nTextFlow = 0;
2109 switch (m_rWW8Export.TrueFrameDirection(*pFrameFormat))
2111 case SvxFrameDirection::Vertical_RL_TB:
2112 nTextFlow = 5;
2113 break;
2114 case SvxFrameDirection::Vertical_LR_BT:
2115 nTextFlow = 3;
2116 break;
2117 default:
2118 break;
2121 if (nTextFlow != 0)
2123 m_rWW8Export.InsUInt16( NS_sprm::TTextFlow::val );
2124 m_rWW8Export.pO->push_back( n ); //start range
2125 m_rWW8Export.pO->push_back( sal_uInt8(n + 1) ); //end range
2126 m_rWW8Export.InsUInt16(nTextFlow);
2131 void WW8AttributeOutput::TableCanSplit( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
2133 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
2134 const SwTableLine * pTabLine = pTabBox->GetUpper();
2135 const SwFrameFormat * pLineFormat = pTabLine->GetFrameFormat();
2138 By default the row can be split in word, and now in writer we have a
2139 feature equivalent to this, Word stores 1 for fCantSplit if the row
2140 cannot be split, we set true if we can split it. An example is #i4569#
2143 const SwFormatRowSplit& rSplittable = pLineFormat->GetRowSplit();
2144 sal_uInt8 nCantSplit = (!rSplittable.GetValue()) ? 1 : 0;
2145 m_rWW8Export.InsUInt16( NS_sprm::TFCantSplit::val );
2146 m_rWW8Export.pO->push_back( nCantSplit );
2147 m_rWW8Export.InsUInt16( NS_sprm::TFCantSplit90::val ); // also write fCantSplit90
2148 m_rWW8Export.pO->push_back( nCantSplit );
2151 void WW8AttributeOutput::TableBidi( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
2153 const SwTable * pTable = pTableTextNodeInfoInner->getTable();
2154 const SwFrameFormat * pFrameFormat = pTable->GetFrameFormat();
2156 if ( m_rWW8Export.TrueFrameDirection(*pFrameFormat) == SvxFrameDirection::Horizontal_RL_TB )
2158 m_rWW8Export.InsUInt16( NS_sprm::TFBiDi::val );
2159 m_rWW8Export.InsUInt16( 1 );
2163 void WW8AttributeOutput::TableRowRedline( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ )
2167 void WW8AttributeOutput::TableCellRedline( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ )
2171 void WW8AttributeOutput::TableHeight( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
2173 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
2174 const SwTableLine * pTabLine = pTabBox->GetUpper();
2175 const SwFrameFormat * pLineFormat = pTabLine->GetFrameFormat();
2177 // output line height sprmTDyaRowHeight
2178 tools::Long nHeight = 0;
2179 const SwFormatFrameSize& rLSz = pLineFormat->GetFrameSize();
2180 if ( SwFrameSize::Variable != rLSz.GetHeightSizeType() && rLSz.GetHeight() )
2182 if ( SwFrameSize::Minimum == rLSz.GetHeightSizeType() )
2183 nHeight = rLSz.GetHeight();
2184 else
2185 nHeight = -rLSz.GetHeight();
2188 if ( nHeight )
2190 m_rWW8Export.InsUInt16( NS_sprm::TDyaRowHeight::val );
2191 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(nHeight) );
2196 void WW8AttributeOutput::TableOrientation( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
2198 const SwTable * pTable = pTableTextNodeInfoInner->getTable();
2200 const SwFrameFormat *pFormat = pTable->GetFrameFormat();
2201 if ( !pFormat )
2203 SAL_WARN( "sw.ww8", "FrameFormat is nil" );
2204 return;
2207 const SwFormatHoriOrient &rHori = pFormat->GetHoriOrient();
2208 const SwFormatVertOrient &rVert = pFormat->GetVertOrient();
2210 if (
2211 !((text::RelOrientation::PRINT_AREA == rHori.GetRelationOrient() ||
2212 text::RelOrientation::FRAME == rHori.GetRelationOrient())
2214 (text::RelOrientation::PRINT_AREA == rVert.GetRelationOrient() ||
2215 text::RelOrientation::FRAME == rVert.GetRelationOrient()))
2217 return;
2219 const bool bIsRTL = m_rWW8Export.TrueFrameDirection(*pFormat) == SvxFrameDirection::Horizontal_RL_TB;
2220 sal_Int16 eHOri = rHori.GetHoriOrient();
2221 switch (eHOri)
2223 case text::HoriOrientation::CENTER:
2224 m_rWW8Export.InsUInt16( NS_sprm::TJc::val ); //logical orientation required for MSO
2225 m_rWW8Export.InsUInt16( 1 );
2226 m_rWW8Export.InsUInt16( NS_sprm::TJc90::val ); //physical orientation required for LO
2227 m_rWW8Export.InsUInt16( 1 );
2228 break;
2229 case text::HoriOrientation::RIGHT:
2230 m_rWW8Export.InsUInt16( NS_sprm::TJc90::val ); //required for LO
2231 m_rWW8Export.InsUInt16( 2 );
2232 if ( !bIsRTL )
2234 m_rWW8Export.InsUInt16( NS_sprm::TJc::val ); //required for MSO
2235 m_rWW8Export.InsUInt16( 2 );
2237 break;
2238 case text::HoriOrientation::LEFT:
2239 if ( bIsRTL )
2241 m_rWW8Export.InsUInt16( NS_sprm::TJc::val ); //required for MSO
2242 m_rWW8Export.InsUInt16( 2 );
2244 break;
2245 case text::HoriOrientation::LEFT_AND_WIDTH:
2246 // Width can only be specified for the LOGICAL left, so in RTL, that is always PHYSICAL right
2247 if ( bIsRTL )
2249 m_rWW8Export.InsUInt16( NS_sprm::TJc90::val ); //required for LO
2250 m_rWW8Export.InsUInt16( 2 );
2252 break;
2253 default:
2254 break;
2258 void WW8AttributeOutput::TableSpacing(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
2260 const SwTable * pTable = pTableTextNodeInfoInner->getTable();
2261 const SwTableFormat* pTableFormat = pTable->GetFrameFormat();
2264 // Writing these SPRM's will make the table a floating one, so only write
2265 // them in case the table is already inside a frame.
2266 if (!(pTableFormat != nullptr && pTable->GetTableNode()->GetFlyFormat()))
2267 return;
2269 const SvxULSpaceItem & rUL = pTableFormat->GetULSpace();
2271 if (rUL.GetUpper() > 0)
2273 sal_uInt8 const nPadding = 2;
2274 sal_uInt8 const nPcVert = 0;
2275 sal_uInt8 const nPcHorz = 0;
2277 sal_uInt8 const nTPc = (nPadding << 4) | (nPcVert << 2) | nPcHorz;
2279 m_rWW8Export.InsUInt16(NS_sprm::TPc::val);
2280 m_rWW8Export.pO->push_back( nTPc );
2282 m_rWW8Export.InsUInt16(NS_sprm::TDyaAbs::val);
2283 m_rWW8Export.InsUInt16(rUL.GetUpper());
2285 m_rWW8Export.InsUInt16(NS_sprm::TDyaFromText::val);
2286 m_rWW8Export.InsUInt16(rUL.GetUpper());
2289 if (rUL.GetLower() > 0)
2291 m_rWW8Export.InsUInt16(NS_sprm::TDyaFromTextBottom::val);
2292 m_rWW8Export.InsUInt16(rUL.GetLower());
2296 void WW8AttributeOutput::TableDefinition( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
2298 const SwTable * pTable = pTableTextNodeInfoInner->getTable();
2300 if ( pTable->GetRowsToRepeat() > pTableTextNodeInfoInner->getRow() )
2302 m_rWW8Export.InsUInt16( NS_sprm::TTableHeader::val );
2303 m_rWW8Export.pO->push_back( 1 );
2306 ww8::TableBoxVectorPtr pTableBoxes =
2307 pTableTextNodeInfoInner->getTableBoxesOfRow();
2308 // number of cell written
2309 sal_uInt32 nBoxes = pTableBoxes->size();
2310 assert(nBoxes <= ww8::MAXTABLECELLS);
2312 // sprm header
2313 m_rWW8Export.InsUInt16( NS_sprm::TDefTable::val );
2314 sal_uInt16 nSprmSize = 2 + (nBoxes + 1) * 2 + nBoxes * 20;
2315 m_rWW8Export.InsUInt16( nSprmSize ); // length
2317 // number of boxes
2318 m_rWW8Export.pO->push_back( static_cast<sal_uInt8>(nBoxes) );
2320 /* cells */
2322 ALWAYS relative when text::HoriOrientation::NONE (nPageSize + ( nPageSize / 10 )) < nTableSz,
2323 in that case the cell width's and table width's are not real. The table
2324 width is maxed and cells relative, so we need the frame (generally page)
2325 width that the table is in to work out the true widths.
2327 //const bool bNewTableModel = pTable->IsNewModel();
2328 const SwFrameFormat *pFormat = pTable->GetFrameFormat();
2329 if ( !pFormat )
2331 SAL_WARN( "sw.ww8", "FrameFormat is nil" );
2332 return;
2335 const SwFormatHoriOrient &rHori = pFormat->GetHoriOrient();
2336 const SwFormatVertOrient &rVert = pFormat->GetVertOrient();
2338 SwTwips nTableOffset = 0;
2340 if (
2341 (text::RelOrientation::PRINT_AREA == rHori.GetRelationOrient() ||
2342 text::RelOrientation::FRAME == rHori.GetRelationOrient())
2344 (text::RelOrientation::PRINT_AREA == rVert.GetRelationOrient() ||
2345 text::RelOrientation::FRAME == rVert.GetRelationOrient())
2348 sal_Int16 eHOri = rHori.GetHoriOrient();
2349 switch ( eHOri )
2351 case text::HoriOrientation::CENTER:
2352 case text::HoriOrientation::RIGHT:
2353 break;
2355 default:
2356 nTableOffset = rHori.GetPos();
2357 const SvxLRSpaceItem& rLRSp = pFormat->GetLRSpace();
2358 nTableOffset += rLRSp.GetLeft();
2360 // convert offset to be measured from right margin in right-to-left tables
2361 if ( nTableOffset && m_rWW8Export.TrueFrameDirection(*pFormat) == SvxFrameDirection::Horizontal_RL_TB )
2363 SwTwips nLeftPageMargin, nRightPageMargin;
2364 const SwTwips nPageSize = m_rWW8Export.CurrentPageWidth(nLeftPageMargin, nRightPageMargin);
2365 const SwTwips nTableWidth = pFormat->GetFrameSize().GetWidth();
2366 nTableOffset = nPageSize - nLeftPageMargin - nRightPageMargin - nTableWidth - nTableOffset;
2368 break;
2372 m_rWW8Export.InsInt16( nTableOffset );
2374 ww8::GridColsPtr pGridCols = GetGridCols( pTableTextNodeInfoInner );
2375 for ( const auto nCol : *pGridCols )
2377 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(nCol) + nTableOffset );
2380 /* TCs */
2381 ww8::RowSpansPtr pRowSpans = pTableTextNodeInfoInner->getRowSpansOfRow();
2382 ww8::RowSpans::const_iterator aItRowSpans = pRowSpans->begin();
2384 for (const SwTableBox * pTabBox1 : *pTableBoxes)
2386 sal_uInt16 npOCount = m_rWW8Export.pO->size();
2388 const SwFrameFormat * pBoxFormat = nullptr;
2389 if (pTabBox1 != nullptr)
2390 pBoxFormat = pTabBox1->GetFrameFormat();
2392 sal_uInt16 nFlags =
2393 lcl_TCFlags(m_rWW8Export.m_rDoc, pTabBox1, *aItRowSpans);
2394 m_rWW8Export.InsUInt16( nFlags );
2396 static sal_uInt8 aNullBytes[] = { 0x0, 0x0 };
2398 m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), aNullBytes, aNullBytes+2 ); // dummy
2399 if (pBoxFormat != nullptr)
2401 const SvxBoxItem & rBoxItem = pBoxFormat->GetBox();
2403 WW8Export::Out_SwFormatTableBox( *m_rWW8Export.pO, &rBoxItem ); // 8/16 Byte
2405 else
2406 WW8Export::Out_SwFormatTableBox( *m_rWW8Export.pO, nullptr); // 8/16 Byte
2408 SAL_INFO( "sw.ww8.level2", "<tclength>" << ( m_rWW8Export.pO->size() - npOCount ) << "</tclength>" );
2409 ++aItRowSpans;
2412 int nWidthPercent = pFormat->GetFrameSize().GetWidthPercent();
2413 // Width is in fiftieths of a percent. For sprmTTableWidth, must be non-negative and 600% max
2414 if ( nWidthPercent > 0 && nWidthPercent <= 600 )
2416 m_rWW8Export.InsUInt16( NS_sprm::TTableWidth::val );
2417 m_rWW8Export.pO->push_back( sal_uInt8/*ftsPercent*/ (2) );
2418 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(nWidthPercent) * 50 );
2422 ww8::GridColsPtr AttributeOutputBase::GetGridCols( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner )
2424 return pTableTextNodeInfoInner->getGridColsOfRow(*this);
2427 ww8::WidthsPtr AttributeOutputBase::GetColumnWidths( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner )
2429 // Get the column widths based on ALL the rows, not just the current row
2430 return pTableTextNodeInfoInner->getGridColsOfRow(*this, true);
2433 void AttributeOutputBase::GetTablePageSize( ww8::WW8TableNodeInfoInner const * pTableTextNodeInfoInner, tools::Long& rPageSize, bool& rRelBoxSize )
2435 tools::Long nPageSize = 0;
2437 const SwNode *pTextNd = pTableTextNodeInfoInner->getNode( );
2438 const SwTable *pTable = pTableTextNodeInfoInner->getTable( );
2440 const SwFrameFormat *pFormat = pTable->GetFrameFormat();
2441 if ( !pFormat )
2443 SAL_WARN( "sw.ww8", "FrameFormat is nil" );
2444 return;
2447 const SwFormatFrameSize &rSize = pFormat->GetFrameSize();
2448 int nWidthPercent = rSize.GetWidthPercent();
2449 bool bManualAligned = pFormat->GetHoriOrient().GetHoriOrient() == text::HoriOrientation::NONE;
2450 if ( (pFormat->GetHoriOrient().GetHoriOrient() == text::HoriOrientation::FULL) || bManualAligned )
2451 nWidthPercent = 100;
2452 bool bRelBoxSize = nWidthPercent != 0;
2453 tools::ULong nTableSz = static_cast<tools::ULong>(rSize.GetWidth());
2454 if (nTableSz > USHRT_MAX/2 && !bRelBoxSize)
2456 OSL_ENSURE(bRelBoxSize, "huge table width but not relative, suspicious");
2457 bRelBoxSize = true;
2460 if ( bRelBoxSize )
2462 Point aPt;
2463 SwRect aRect( pFormat->FindLayoutRect( false, &aPt ) );
2464 if ( aRect.IsEmpty() )
2466 // Then fetch the page width without margins!
2467 const SwFrameFormat* pParentFormat =
2468 GetExport().m_pParentFrame ?
2469 &(GetExport().m_pParentFrame->GetFrameFormat()) :
2470 GetExport().m_rDoc.GetPageDesc(0).GetPageFormatOfNode(*pTextNd, false);
2471 aRect = pParentFormat->FindLayoutRect(true);
2472 nPageSize = aRect.Width();
2473 if ( 0 == nPageSize )
2475 const SvxLRSpaceItem& rLR = pParentFormat->GetLRSpace();
2476 nPageSize = pParentFormat->GetFrameSize().GetWidth() - rLR.GetLeft()
2477 - rLR.GetRight();
2480 else
2482 nPageSize = aRect.Width();
2483 if ( bManualAligned )
2485 // #i37571# For manually aligned tables
2486 const SvxLRSpaceItem &rLR = pFormat->GetLRSpace();
2487 nPageSize -= (rLR.GetLeft() + rLR.GetRight());
2492 if ( nWidthPercent )
2494 nPageSize *= nWidthPercent;
2495 nPageSize /= 100;
2497 else
2498 SAL_WARN( "sw.ww8", "nWidthPercent is zero" );
2500 else
2502 // As the table width is not relative, the TablePageSize equals its width
2503 nPageSize = nTableSz;
2506 rPageSize = nPageSize;
2507 rRelBoxSize = bRelBoxSize;
2510 void WW8AttributeOutput::TableDefaultBorders( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
2512 // This function name is misleading because it is not a table default, but a row default,
2513 // and it also only sets default cell margins (aka border padding).
2514 // The specs suggest there is no way to define default border lines/colors.
2515 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
2516 const SwFrameFormat * pFrameFormat = pTabBox->GetFrameFormat();
2518 static const SvxBoxItemLine aBorders[] =
2520 SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
2521 SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
2524 // Set row default cell margins using this last cell in the row
2525 for ( int i = 0; i < 4; ++i )
2527 SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::TCellPaddingDefault::val );
2528 m_rWW8Export.pO->push_back( sal_uInt8(6) );
2529 m_rWW8Export.pO->push_back( sal_uInt8(0) );
2530 m_rWW8Export.pO->push_back( sal_uInt8(1) );
2531 m_rWW8Export.pO->push_back( sal_uInt8(1 << i) );
2532 m_rWW8Export.pO->push_back( sal_uInt8(3) );
2534 SwWW8Writer::InsUInt16( *m_rWW8Export.pO,
2535 pFrameFormat->GetBox().GetDistance( aBorders[i] ) );
2539 void WW8AttributeOutput::TableCellBorders(
2540 ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner )
2542 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
2543 const SwTableLine * pTabLine = pTabBox->GetUpper();
2544 const SwTableBoxes & rTabBoxes = pTabLine->GetTabBoxes();
2545 sal_uInt8 nBoxes = std::min<size_t>(rTabBoxes.size(), 255);
2546 const SvxBoxItem * pLastBox = nullptr;
2547 sal_uInt8 nSeqStart = 0; // start of sequence of cells with same borders
2549 static const SvxBoxItemLine aBorders[] =
2551 SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
2552 SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
2555 sal_uInt16 nDefaultMargin[4] = {31681, 31681, 31681, 31681}; // outside of documented valid range
2556 // last column in each row defines the row default in TableRowDefaultBorders()
2557 if ( nBoxes && rTabBoxes.size() == nBoxes )
2559 const SvxBoxItem& rBox = rTabBoxes[ nBoxes-1 ]->GetFrameFormat()->GetBox();
2560 for ( int i = 0; i < 4; ++i )
2561 nDefaultMargin[i] = rBox.GetDistance( aBorders[i] );
2564 // Detect sequences of cells which have the same borders, and output
2565 // a border description for each such cell range.
2566 for ( unsigned n = 0; n <= nBoxes; ++n )
2568 const SvxBoxItem * pBox = (n == nBoxes) ? nullptr :
2569 &rTabBoxes[n]->GetFrameFormat()->GetBox();
2570 if( !pLastBox )
2571 pLastBox = pBox;
2572 else if( !pBox || *pLastBox != *pBox )
2574 // This cell has different borders than the previous cell,
2575 // so output the borders for the preceding cell range.
2576 m_rWW8Export.Out_CellRangeBorders(pLastBox, nSeqStart, n);
2578 // The last column is used as the row default for margins, so we can ignore these matching ones
2579 if ( n == nBoxes )
2580 break;
2582 // Output cell margins.
2583 // One CSSA can define up to all four margins if they are the same size value.
2584 sal_uInt16 nMargin[4];
2585 sal_uInt8 nSideBits[4] = {0, 0, 0, 0}; // 0001:top, 0010:left, 0100:bottom, 1000:right
2586 for ( int i = 0; i < 4; ++i ) // sides: top, left, bottom, right
2588 nMargin[i] = std::min(sal_uInt16(31680), pLastBox->GetDistance( aBorders[i] ));
2589 if ( nMargin[i] == nDefaultMargin[i] )
2590 continue;
2592 // join a previous side's definition if it shares the same value
2593 for ( int p = 0; p < 4; ++p )
2595 if ( nMargin[i] == nMargin[p] )
2597 nSideBits[p] |= 1 << i;
2598 break;
2603 // write out the cell margins definitions that were used
2604 for ( int i = 0; i < 4; ++i )
2606 if ( nSideBits[i] )
2608 SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::TCellPadding::val );
2609 m_rWW8Export.pO->push_back( sal_uInt8(6) ); // 6 bytes
2610 m_rWW8Export.pO->push_back( sal_uInt8(nSeqStart) ); // first cell: apply margin
2611 m_rWW8Export.pO->push_back( sal_uInt8(n) ); // end cell: do not apply margin
2612 m_rWW8Export.pO->push_back( sal_uInt8(nSideBits[i]) );
2613 m_rWW8Export.pO->push_back( sal_uInt8(3) ); // FtsDxa: size in twips
2614 SwWW8Writer::InsUInt16( *m_rWW8Export.pO, nMargin[i] );
2618 nSeqStart = n;
2619 pLastBox = pBox;
2624 void WW8AttributeOutput::TableBackgrounds( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
2626 const SwTable * pTab = pTableTextNodeInfoInner->getTable();
2627 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
2628 const SwTableLine * pTabLine = pTabBox->GetUpper();
2629 const SwTableBoxes & rTabBoxes = pTabLine->GetTabBoxes();
2631 sal_uInt8 nBoxes = rTabBoxes.size();
2632 m_rWW8Export.InsUInt16( NS_sprm::TDefTableShd80::val );
2633 m_rWW8Export.pO->push_back( static_cast<sal_uInt8>(nBoxes * 2) ); // Len
2635 Color aRowColor = COL_AUTO;
2636 const SvxBrushItem *pTableColorProp = pTab->GetFrameFormat()->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
2637 if ( pTableColorProp )
2638 aRowColor = pTableColorProp->GetColor();
2640 const SvxBrushItem *pRowColorProp = pTabLine->GetFrameFormat()->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
2641 if ( pRowColorProp && pRowColorProp->GetColor() != COL_AUTO )
2642 aRowColor = pRowColorProp->GetColor();
2644 for ( sal_uInt8 n = 0; n < nBoxes; n++ )
2646 const SwTableBox * pBox1 = rTabBoxes[n];
2647 const SwFrameFormat * pFrameFormat = pBox1->GetFrameFormat();
2648 Color aColor = aRowColor;
2650 const SvxBrushItem *pCellColorProp = pFrameFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
2651 if ( pCellColorProp && pCellColorProp->GetColor() != COL_AUTO )
2652 aColor = pCellColorProp->GetColor();
2654 WW8_SHD aShd;
2655 WW8Export::TransBrush( aColor, aShd );
2656 m_rWW8Export.InsUInt16( aShd.GetValue() );
2659 /*sprmTDefTableShdRaw:
2660 * A DefTableShdOperand value that specifies the ... shading for cells 1 up to 22 in the row,
2661 * ... Cells 23 to 44 are shaded by sprmTDefTableShdRaw2nd,
2662 * and cells 45 to 63 are shaded by sprmTDefTableShdRaw3rd.
2664 sal_uInt32 const aSprmIds[] { NS_sprm::TDefTableShd::val,
2665 NS_sprm::TDefTableShdRaw::val,
2666 NS_sprm::TDefTableShdRaw::val,
2667 NS_sprm::TDefTableShd2nd::val,
2668 NS_sprm::TDefTableShdRaw2nd::val,
2669 NS_sprm::TDefTableShd3rd::val,
2670 NS_sprm::TDefTableShdRaw3rd::val };
2671 for (sal_uInt32 m : aSprmIds)
2673 sal_uInt8 nStart = 0;
2674 sal_uInt8 nStop = rTabBoxes.size();
2675 switch ( m )
2677 case NS_sprm::TDefTableShd::val:
2678 case NS_sprm::TDefTableShdRaw::val:
2679 if ( nStop > 21 )
2680 nStop = 22;
2681 break;
2682 case NS_sprm::TDefTableShd2nd::val:
2683 case NS_sprm::TDefTableShdRaw2nd::val:
2684 nStart = 22;
2685 if ( nStop > 43 )
2686 nStop = 44;
2687 break;
2688 case NS_sprm::TDefTableShd3rd::val:
2689 case NS_sprm::TDefTableShdRaw3rd::val:
2690 nStart = 44;
2691 if ( nStop > 62 )
2692 nStop = 63;
2693 break;
2695 if ( nStart >= nStop )
2696 break;
2698 m_rWW8Export.InsUInt16( m );
2699 m_rWW8Export.pO->push_back( static_cast<sal_uInt8>((nStop-nStart) * 10) );
2701 for ( sal_uInt8 n = nStart; n < nStop; n++ )
2703 const SwTableBox * pBox1 = rTabBoxes[n];
2704 const SwFrameFormat * pFrameFormat = pBox1->GetFrameFormat();
2705 Color aColor = aRowColor;
2707 const SvxBrushItem *pCellColorProp = pFrameFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
2708 if ( pCellColorProp && pCellColorProp->GetColor() != COL_AUTO )
2709 aColor = pCellColorProp->GetColor();
2711 WW8SHDLong aSHD;
2712 aSHD.setCvFore( 0xFF000000 );
2714 if ( aColor == COL_AUTO )
2715 aSHD.setCvBack( 0xFF000000 );
2716 else
2717 aSHD.setCvBack( wwUtility::RGBToBGR( aColor ) );
2719 aSHD.Write( m_rWW8Export );
2724 void WW8Export::SectionBreaksAndFrames( const SwTextNode& rNode )
2726 // output page/section breaks
2727 OutputSectionBreaks( rNode.GetpSwAttrSet(), rNode );
2730 namespace {
2732 class TrackContentToExport
2734 private:
2735 SwPaM *m_pCurPam;
2736 sal_uLong m_nStart, m_nEnd;
2737 public:
2738 TrackContentToExport(SwPaM *pCurPam, sal_uLong nCurStart, sal_uLong nCurEnd)
2739 : m_pCurPam(pCurPam)
2740 , m_nStart(nCurStart)
2741 , m_nEnd(nCurEnd)
2745 bool contentRemainsToExport(ww8::WW8TableInfo *pTableInfo)
2747 bool bSimpleContentRemains = m_pCurPam->GetPoint()->nNode < m_pCurPam->GetMark()->nNode ||
2748 (m_pCurPam->GetPoint()->nNode == m_pCurPam->GetMark()->nNode &&
2749 m_pCurPam->GetPoint()->nContent.GetIndex() <= m_pCurPam->GetMark()->nContent.GetIndex());
2750 if (bSimpleContentRemains)
2751 return true;
2753 if (!pTableInfo)
2754 return false;
2756 //An old-school table where one cell may points back to a previous node as the next cell
2757 //so if this node is the last node in the range, we may need to jump back to a previously
2758 //skipped cell to output it in a sane sequence. See ooo47778-3.sxw for one of these
2759 //horrors. So if we are at the end of the selection, but this end point is a table
2760 //cell whose next cell is in the selection allow jumping back to it
2761 const SwNode* pCurrentNode = &m_pCurPam->GetPoint()->nNode.GetNode();
2762 const SwNode* pNextNode = pTableInfo->getNextNode(pCurrentNode);
2764 if (pNextNode && pCurrentNode != pNextNode)
2766 return pNextNode->GetIndex() >= m_nStart &&
2767 pNextNode->GetIndex() < m_nEnd;
2770 return false;
2776 void MSWordExportBase::WriteText()
2778 TrackContentToExport aContentTracking(m_pCurPam.get(), m_nCurStart, m_nCurEnd);
2779 while (aContentTracking.contentRemainsToExport(m_pTableInfo.get()))
2781 SwNode& rNd = m_pCurPam->GetNode();
2783 // no section breaks exported for Endnotes
2784 if ( rNd.IsTextNode() && m_nTextTyp != TXT_EDN && m_nTextTyp != TXT_FTN )
2786 SwSoftPageBreakList breakList;
2787 // if paragraph need to be split than handle section break somewhere
2788 // else.
2789 if( !NeedTextNodeSplit( *rNd.GetTextNode(), breakList) )
2790 SectionBreaksAndFrames( *rNd.GetTextNode() );
2794 // output the various types of nodes
2795 if ( rNd.IsContentNode() )
2797 SwContentNode* pCNd = static_cast<SwContentNode*>(&rNd);
2799 const SwPageDesc* pTemp = rNd.FindPageDesc();
2800 if ( pTemp )
2801 m_pCurrentPageDesc = pTemp;
2803 m_pCurPam->GetPoint()->nContent.Assign( pCNd, 0 );
2804 OutputContentNode( *pCNd );
2806 else if ( rNd.IsTableNode() )
2808 m_pTableInfo->processSwTable( &rNd.GetTableNode()->GetTable() );
2810 else if ( rNd.IsSectionNode() && TXT_MAINTEXT == m_nTextTyp )
2811 OutputSectionNode( *rNd.GetSectionNode() );
2812 else if ( TXT_MAINTEXT == m_nTextTyp && rNd.IsEndNode() &&
2813 rNd.StartOfSectionNode()->IsSectionNode() )
2815 const SwSection& rSect = rNd.StartOfSectionNode()->GetSectionNode()
2816 ->GetSection();
2817 if ( m_bStartTOX && SectionType::ToxContent == rSect.GetType() )
2818 m_bStartTOX = false;
2820 SwNodeIndex aIdx( rNd, 1 );
2821 if ( aIdx.GetNode().IsEndNode() && aIdx.GetNode().StartOfSectionNode()->IsSectionNode() )
2823 else if ( aIdx.GetNode().IsSectionNode() )
2825 else if ( !IsInTable() ) //No sections in table
2827 //#120140# Do not need to insert a page/section break after a section end. Check this case first
2828 bool bNeedExportBreakHere = true;
2829 if ( rSect.GetType() == SectionType::ToxContent || rSect.GetType() == SectionType::ToxHeader )
2830 bNeedExportBreakHere = false;
2831 else if ( aIdx.GetNode().IsTextNode() )
2833 SwTextNode *pTempNext = aIdx.GetNode().GetTextNode();
2834 if ( pTempNext )
2836 const SfxPoolItem * pTempItem = nullptr;
2837 if (pTempNext->GetpSwAttrSet() && SfxItemState::SET == pTempNext->GetpSwAttrSet()->GetItemState(RES_PAGEDESC, false, &pTempItem)
2838 && pTempItem && static_cast<const SwFormatPageDesc*>(pTempItem)->GetRegisteredIn())
2840 //Next node has a new page style which means this node is a section end. Do not insert another page/section break here
2841 bNeedExportBreakHere = false;
2845 else
2847 /* Do not export Section Break in case DOCX containing MultiColumn and
2848 * aIdx.GetNode().IsTextNode() is False i.e. Text node is NULL.
2850 const SwFrameFormat* pPgFormat = rSect.GetFormat();
2851 const SwFormatCol& rCol = pPgFormat->GetCol();
2852 sal_uInt16 nColumnCount = rCol.GetNumCols();
2853 const SwFormatNoBalancedColumns& rNoBalanced = pPgFormat->GetBalancedColumns();
2854 // Prevent the additional section break only for non-balanced columns.
2855 if (nColumnCount > 1 && rNoBalanced.GetValue())
2857 bNeedExportBreakHere = false;
2859 // No need to create a "fake" section if this is the end of the document,
2860 // except to emulate balanced columns.
2861 else if ( nColumnCount < 2 && aIdx == m_rDoc.GetNodes().GetEndOfContent() )
2862 bNeedExportBreakHere = false;
2865 if (bNeedExportBreakHere) //#120140# End of check
2867 ReplaceCr( char(0xc) ); // indicator for Page/Section-Break
2869 const SwSectionFormat* pParentFormat = rSect.GetFormat()->GetParent();
2870 if ( !pParentFormat )
2871 pParentFormat = reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1));
2873 sal_uLong nRstLnNum;
2874 if ( aIdx.GetNode().IsContentNode() )
2875 nRstLnNum = static_cast<SwContentNode&>(aIdx.GetNode()).GetSwAttrSet().
2876 GetLineNumber().GetStartValue();
2877 else
2878 nRstLnNum = 0;
2880 AppendSection( m_pCurrentPageDesc, pParentFormat, nRstLnNum );
2882 else
2884 OutputEndNode( *rNd.GetEndNode() );
2888 else if ( rNd.IsStartNode() )
2890 OutputStartNode( *rNd.GetStartNode() );
2892 else if ( rNd.IsEndNode() )
2894 OutputEndNode( *rNd.GetEndNode() );
2897 if ( &rNd == &rNd.GetNodes().GetEndOfContent() )
2898 break;
2900 const SwNode * pCurrentNode = &m_pCurPam->GetPoint()->nNode.GetNode();
2901 const SwNode * pNextNode = m_pTableInfo->getNextNode(pCurrentNode);
2903 if (pCurrentNode == pNextNode)
2905 SAL_WARN("sw.ww8", "loop in TableInfo");
2906 pNextNode = nullptr;
2909 if (pNextNode != nullptr)
2910 m_pCurPam->GetPoint()->nNode.Assign(*pNextNode);
2911 else
2912 ++m_pCurPam->GetPoint()->nNode;
2914 sal_uLong nPos = m_pCurPam->GetPoint()->nNode.GetIndex();
2915 ::SetProgressState( nPos, m_pCurPam->GetDoc().GetDocShell() );
2918 SAL_INFO( "sw.ww8.level2", "</WriteText>" );
2921 void WW8Export::WriteMainText()
2923 SAL_INFO( "sw.ww8.level2", "<WriteMainText>" );
2925 pFib->m_fcMin = Strm().Tell();
2927 m_pCurPam->GetPoint()->nNode = m_rDoc.GetNodes().GetEndOfContent().StartOfSectionNode()->GetIndex();
2929 WriteText();
2931 if( 0 == Strm().Tell() - pFib->m_fcMin ) // no text ?
2932 WriteCR(); // then CR at the end ( otherwise WW will complain )
2934 pFib->m_ccpText = Fc2Cp( Strm().Tell() );
2935 m_pFieldMain->Finish( pFib->m_ccpText, 0 );
2937 // ccpText includes Footnote and KF-text
2938 // therefore pFib->ccpText may get updated as well
2939 // save the StyleId of the last paragraph. Because WW97 take the style
2940 // from the last CR, that will be written after footer/Header/footnotes/
2941 // annotation etc.
2942 const SwTextNode* pLastNd = m_pCurPam->GetMark()->nNode.GetNode().GetTextNode();
2943 if( pLastNd )
2944 m_nLastFormatId = GetId( static_cast<SwTextFormatColl&>(pLastNd->GetAnyFormatColl()) );
2946 SAL_INFO( "sw.ww8.level2", "</WriteMainText>" );
2949 bool MSWordExportBase::IsInTable() const
2951 bool bResult = false;
2953 if (m_pCurPam != nullptr)
2955 SwNode& rNode = m_pCurPam->GetNode();
2957 if (m_pTableInfo)
2959 ww8::WW8TableNodeInfo::Pointer_t pTableNodeInfo = m_pTableInfo->getTableNodeInfo(&rNode);
2961 if (pTableNodeInfo && pTableNodeInfo->getDepth() > 0)
2963 bResult = true;
2968 return bResult;
2971 typedef ww8::WW8Sttb< ww8::WW8Struct > WW8SttbAssoc;
2973 void WW8Export::WriteFkpPlcUsw()
2975 // Graphics in the data stream
2976 m_pGrf->Write(); // Graphics
2978 // output into WordDocument stream
2979 m_pChpPlc->WriteFkps(); // Fkp.Chpx
2980 m_pPapPlc->WriteFkps(); // Fkp.Papx
2981 pSepx->WriteSepx( Strm() ); // Sepx
2983 // output into Table stream
2984 m_pStyles->OutputStylesTable(); // for WW8 StyleTab
2985 pFootnote->WritePlc( *this ); // Footnote-Ref & Text Plc
2986 pEdn->WritePlc( *this ); // Endnote-Ref & Text Plc
2987 m_pTextBxs->WritePlc( *this ); // Textbox Text Plc
2988 m_pHFTextBxs->WritePlc( *this ); // Head/Foot-Textbox Text Plc
2989 m_pAtn->WritePlc( *this ); // Annotation-Ref & Text Plc
2991 pSepx->WritePlcSed( *this ); // Slcx.PlcSed
2992 pSepx->WritePlcHdd( *this ); // Slcx.PlcHdd
2994 m_pChpPlc->WritePlc(); // Plcx.Chpx
2995 m_pPapPlc->WritePlc(); // Plcx.Papx
2997 if( m_pRedlAuthors )
2998 m_pRedlAuthors->Write( GetWriter() ); // sttbfRMark (RedlineAuthors)
2999 m_pFieldMain->Write( *this ); // Fields ( Main Text )
3000 m_pFieldHdFt->Write( *this ); // Fields ( Header/Footer )
3001 m_pFieldFootnote->Write( *this ); // Fields ( FootNotes )
3002 m_pFieldEdn->Write( *this ); // Fields ( EndNotes )
3003 m_pFieldAtn->Write( *this ); // Fields ( Annotations )
3004 m_pFieldTextBxs->Write( *this ); // Fields ( Textboxes )
3005 m_pFieldHFTextBxs->Write( *this ); // Fields ( Head/Foot-Textboxes )
3007 if (m_pEscher || m_rDoc.ContainsMSVBasic())
3010 Every time MS 2000 creates an escher stream there is always
3011 an ObjectPool dir (even if empty). It turns out that if a copy of
3012 MS 2000 is used to open a document that contains escher graphics
3013 exported from StarOffice without this empty dir then *if* that
3014 copy of MS Office has never been used to open a MSOffice document
3015 that has escher graphics (and an ObjectPool dir of course) and
3016 that copy of office has not been used to draw escher graphics then
3017 our exported graphics do not appear. Once you do open a ms
3018 document with escher graphics or draw an escher graphic with that
3019 copy of word, then all documents from staroffice that contain
3020 escher work from then on. Tricky to track down, some sort of late
3021 binding trickery in MS where solely for first time initialization
3022 the existence of an ObjectPool dir is necessary for triggering
3023 some magic.
3025 // avoid memory leak #i120098#, the unnamed obj will be released in destructor.
3026 xEscherStg = GetWriter().GetStorage().OpenSotStorage(SL::aObjectPool);
3029 // dggInfo - escher stream
3030 WriteEscher();
3032 m_pSdrObjs->WritePlc( *this );
3033 m_pHFSdrObjs->WritePlc( *this );
3034 // spamom - office drawing table
3035 // spahdr - header office drawing table
3037 m_pBkmks->Write( *this ); // Bookmarks - sttbfBkmk/
3038 // plcfBkmkf/plcfBkmkl
3039 m_pFactoids->Write(*this);
3041 WriteNumbering();
3043 RestoreMacroCmds();
3045 m_pMagicTable->Write( *this );
3047 m_pPiece->WritePc( *this ); // Piece-Table
3048 m_aFontHelper.WriteFontTable(pTableStrm, *pFib); // FFNs
3050 //Convert OOo asian typography into MS typography structure
3051 ExportDopTypography(pDop->doptypography);
3053 WriteDop( *this ); // Document-Properties
3055 // Write SttbfAssoc
3056 WW8SttbAssoc * pSttbfAssoc = dynamic_cast<WW8SttbAssoc *>
3057 (m_rDoc.getIDocumentExternalData().getExternalData(::sw::tExternalDataType::STTBF_ASSOC).get());
3059 if ( pSttbfAssoc ) // #i106057#
3061 std::vector<OUString> aStrings(pSttbfAssoc->getStrings());
3062 WriteAsStringTable(aStrings, pFib->m_fcSttbfAssoc,
3063 pFib->m_lcbSttbfAssoc);
3066 Strm().Seek( 0 );
3068 // Reclaim stored FIB data from document.
3069 ::ww8::WW8FibData * pFibData = dynamic_cast<ww8::WW8FibData *>
3070 (m_rDoc.getIDocumentExternalData().getExternalData(::sw::tExternalDataType::FIB).get());
3072 if ( pFibData )
3074 pFib->m_fReadOnlyRecommended =
3075 pFibData->getReadOnlyRecommended();
3076 pFib->m_fWriteReservation =
3077 pFibData->getWriteReservation();
3080 pFib->Write( Strm() ); // FIB
3083 void WW8Export::StoreDoc1()
3085 bool bNeedsFinalPara = false;
3086 // Start of Text ( overwrite )
3087 SwWW8Writer::FillUntil( Strm(), pFib->m_fcMin );
3089 WriteMainText(); // main text
3090 sal_uInt8 nSprmsLen;
3091 sal_uInt8 *pLastSprms = m_pPapPlc->CopyLastSprms(nSprmsLen);
3093 bNeedsFinalPara |= pFootnote->WriteText( *this ); // Footnote-Text
3094 bNeedsFinalPara |= pSepx->WriteKFText( *this ); // K/F-Text
3095 bNeedsFinalPara |= m_pAtn->WriteText( *this ); // Annotation-Text
3096 bNeedsFinalPara |= pEdn->WriteText( *this ); // EndNote-Text
3098 // create the escher streams
3099 CreateEscher();
3101 bNeedsFinalPara |= m_pTextBxs->WriteText( *this ); //Textbox Text Plc
3102 bNeedsFinalPara |= m_pHFTextBxs->WriteText( *this );//Head/Foot-Textbox Text Plc
3104 if (bNeedsFinalPara)
3106 WriteCR();
3107 m_pPapPlc->AppendFkpEntry(Strm().Tell(), nSprmsLen, pLastSprms);
3109 delete[] pLastSprms;
3111 pSepx->Finish( Fc2Cp( Strm().Tell() ));// Text + Footnote + HdFt as section end
3112 m_pMagicTable->Finish( Fc2Cp( Strm().Tell() ),0);
3114 pFib->m_fcMac = Strm().Tell(); // End of all texts
3116 WriteFkpPlcUsw(); // FKP, PLC, ...
3119 void MSWordExportBase::AddLinkTarget(const OUString& rURL)
3121 if( rURL.isEmpty() || rURL[0] != '#' )
3122 return;
3124 OUString aURL( BookmarkToWriter( rURL.copy( 1 ) ) );
3125 sal_Int32 nPos = aURL.lastIndexOf( cMarkSeparator );
3127 if( nPos < 2 )
3128 return;
3130 OUString sCmp = aURL.copy(nPos+1).replaceAll(" ", "");
3131 if( sCmp.isEmpty() )
3132 return;
3134 sCmp = sCmp.toAsciiLowerCase();
3135 sal_uLong nIdx = 0;
3136 bool noBookmark = false;
3138 if( sCmp == "outline" )
3140 SwPosition aPos(*m_pCurPam->GetPoint());
3141 OUString aName(BookmarkToWriter(aURL.copy(0, nPos)));
3142 // If we can find the outline this bookmark refers to
3143 // save the name of the bookmark and the
3144 // node index number of where it points to
3145 if( m_rDoc.GotoOutline( aPos, aName ) )
3147 nIdx = aPos.nNode.GetIndex();
3148 noBookmark = true;
3151 else if( sCmp == "graphic" )
3153 SwNodeIndex* pIdx;
3154 OUString aName(BookmarkToWriter(aURL.copy(0, nPos)));
3155 const SwFlyFrameFormat* pFormat = m_rDoc.FindFlyByName(aName, SwNodeType::Grf);
3156 if (pFormat && nullptr != (pIdx = const_cast<SwNodeIndex*>(pFormat->GetContent().GetContentIdx())))
3158 nIdx = pIdx->GetNext()->GetIndex();
3159 noBookmark = true;
3162 else if( sCmp == "frame" )
3164 SwNodeIndex* pIdx;
3165 OUString aName(BookmarkToWriter(aURL.copy(0, nPos)));
3166 const SwFlyFrameFormat* pFormat = m_rDoc.FindFlyByName(aName, SwNodeType::Text);
3167 if (pFormat && nullptr != (pIdx = const_cast<SwNodeIndex*>(pFormat->GetContent().GetContentIdx())))
3169 nIdx = pIdx->GetIndex() + 1;
3170 noBookmark = true;
3173 else if( sCmp == "ole" )
3175 SwNodeIndex* pIdx;
3176 OUString aName(BookmarkToWriter(aURL.copy(0, nPos)));
3177 const SwFlyFrameFormat* pFormat = m_rDoc.FindFlyByName(aName, SwNodeType::Ole);
3178 if (pFormat && nullptr != (pIdx = const_cast<SwNodeIndex*>(pFormat->GetContent().GetContentIdx())))
3180 nIdx = pIdx->GetNext()->GetIndex();
3181 noBookmark = true;
3184 else if( sCmp == "region" )
3186 SwNodeIndex* pIdx;
3187 OUString aName(BookmarkToWriter(aURL.copy(0, nPos)));
3188 for (const SwSectionFormat* pFormat : m_rDoc.GetSections())
3190 if (aName == pFormat->GetSection()->GetSectionName()
3191 && nullptr != (pIdx = const_cast<SwNodeIndex*>(pFormat->GetContent().GetContentIdx())))
3193 nIdx = pIdx->GetIndex() + 1;
3194 noBookmark = true;
3195 break;
3199 else if( sCmp == "table" )
3201 OUString aName(BookmarkToWriter(aURL.copy(0, nPos)));
3202 const SwTable* pTable = SwTable::FindTable(m_rDoc.FindTableFormatByName(aName));
3203 if (pTable)
3205 SwTableNode* pTableNode = const_cast<SwTableNode*>(pTable->GetTabSortBoxes()[1]->GetSttNd()->FindTableNode());
3206 if (pTableNode)
3208 nIdx = pTableNode->GetIndex() + 2;
3209 noBookmark = true;
3213 if (noBookmark)
3215 aBookmarkPair aImplicitBookmark;
3216 aImplicitBookmark.first = aURL;
3217 aImplicitBookmark.second = nIdx;
3218 m_aImplicitBookmarks.push_back(aImplicitBookmark);
3222 void MSWordExportBase::CollectOutlineBookmarks(const SwDoc &rDoc)
3224 for (const SfxPoolItem* pItem : rDoc.GetAttrPool().GetItemSurrogates(RES_TXTATR_INETFMT))
3226 auto pINetFormat = dynamic_cast<const SwFormatINetFormat*>(pItem);
3227 if (!pINetFormat)
3228 continue;
3230 const SwTextINetFormat* pTextAttr = pINetFormat->GetTextINetFormat();
3231 if (!pTextAttr)
3232 continue;
3234 const SwTextNode* pTextNd = pTextAttr->GetpTextNode();
3235 if (!pTextNd)
3236 continue;
3238 if (!pTextNd->GetNodes().IsDocNodes())
3239 continue;
3241 AddLinkTarget( pINetFormat->GetValue() );
3244 for (const SfxPoolItem* pItem : rDoc.GetAttrPool().GetItemSurrogates(RES_URL))
3246 auto pURL = dynamic_cast<const SwFormatURL*>(pItem);
3247 if (!pURL)
3248 continue;
3250 AddLinkTarget(pURL->GetURL());
3251 const ImageMap *pIMap = pURL->GetMap();
3252 if (!pIMap)
3253 continue;
3255 for (size_t i=0; i < pIMap->GetIMapObjectCount(); ++i)
3257 const IMapObject* pObj = pIMap->GetIMapObject(i);
3258 if (!pObj)
3259 continue;
3260 AddLinkTarget( pObj->GetURL() );
3265 namespace
3267 const sal_uLong WW_BLOCKSIZE = 0x200;
3269 ErrCode EncryptRC4(msfilter::MSCodec_Std97& rCtx, SvStream &rIn, SvStream &rOut)
3271 sal_uLong nLen = rIn.TellEnd();
3272 rIn.Seek(0);
3274 sal_uInt8 in[WW_BLOCKSIZE];
3275 for (std::size_t nI = 0, nBlock = 0; nI < nLen; nI += WW_BLOCKSIZE, ++nBlock)
3277 std::size_t nBS = std::min(nLen - nI, WW_BLOCKSIZE);
3278 nBS = rIn.ReadBytes(in, nBS);
3279 if (!rCtx.InitCipher(nBlock)) {
3280 return ERRCODE_IO_NOTSUPPORTED;
3282 rCtx.Encode(in, nBS, in, nBS);
3283 rOut.WriteBytes(in, nBS);
3285 return ERRCODE_NONE;
3289 ErrCode MSWordExportBase::ExportDocument( bool bWriteAll )
3291 m_nCharFormatStart = DEFAULT_STYLES_COUNT;
3292 m_nFormatCollStart = m_nCharFormatStart + m_rDoc.GetCharFormats()->size() - 1;
3294 m_bStyDef = m_bBreakBefore = m_bOutKF =
3295 m_bOutFlyFrameAttrs = m_bOutPageDescs = m_bOutTable = m_bOutFirstPage =
3296 m_bOutGrf = m_bInWriteEscher = m_bStartTOX =
3297 m_bInWriteTOX = false;
3299 m_bFootnoteAtTextEnd = m_bEndAtTextEnd = true;
3301 m_pParentFrame = nullptr;
3302 m_pFlyOffset = nullptr;
3303 m_eNewAnchorType = RndStdIds::FLY_AT_PAGE;
3304 m_nTextTyp = TXT_MAINTEXT;
3305 m_nStyleBeforeFly = m_nLastFormatId = 0;
3306 m_pStyAttr = nullptr;
3307 m_pCurrentStyle = nullptr;
3308 m_pOutFormatNode = nullptr;
3309 m_pEscher = nullptr;
3310 m_pRedlAuthors = nullptr;
3311 m_aTOXArr.clear();
3313 if ( !m_pOLEExp )
3315 sal_uInt32 nSvxMSDffOLEConvFlags = 0;
3316 const SvtFilterOptions& rOpt = SvtFilterOptions::Get();
3317 if ( rOpt.IsMath2MathType() )
3318 nSvxMSDffOLEConvFlags |= OLE_STARMATH_2_MATHTYPE;
3319 if ( rOpt.IsWriter2WinWord() )
3320 nSvxMSDffOLEConvFlags |= OLE_STARWRITER_2_WINWORD;
3321 if ( rOpt.IsCalc2Excel() )
3322 nSvxMSDffOLEConvFlags |= OLE_STARCALC_2_EXCEL;
3323 if ( rOpt.IsImpress2PowerPoint() )
3324 nSvxMSDffOLEConvFlags |= OLE_STARIMPRESS_2_POWERPOINT;
3326 m_pOLEExp.reset(new SvxMSExportOLEObjects( nSvxMSDffOLEConvFlags ));
3329 if ( !m_pOCXExp && m_rDoc.GetDocShell() )
3330 m_pOCXExp.reset(new SwMSConvertControls(m_rDoc.GetDocShell(), m_pCurPam.get()));
3332 // #i81405# - Collect anchored objects before changing the redline mode.
3333 m_aFrames = GetFrames( m_rDoc, bWriteAll? nullptr : m_pOrigPam );
3335 m_nOrigRedlineFlags = m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
3337 SwRootFrame const*const pLayout(m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout());
3338 m_bOrigShowChanges = pLayout == nullptr || !pLayout->IsHideRedlines();
3340 if ( !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
3342 //restored to original state by SwWriter::Write
3343 m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags(m_nOrigRedlineFlags |
3344 RedlineFlags::ShowDelete |
3345 RedlineFlags::ShowInsert);
3348 // fix the SwPositions in m_aFrames after SetRedlineFlags
3349 UpdateFramePositions(m_aFrames);
3351 m_aFontHelper.InitFontTable(m_rDoc);
3352 GatherChapterFields();
3354 CollectOutlineBookmarks(m_rDoc);
3356 // make unique OrdNums (Z-Order) for all drawing-/fly Objects
3357 if ( m_rDoc.getIDocumentDrawModelAccess().GetDrawModel() )
3358 m_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->GetPage( 0 )->RecalcObjOrdNums();
3360 ErrCode err = ExportDocument_Impl();
3362 m_aFrames.clear();
3364 // park m_pCurPam in a "safe place" now that document is fully exported
3365 // before toggling redline mode to avoid ~SwIndexReg assert e.g. export
3366 // ooo103014-1.odt to .doc
3367 // park m_pOrigPam as well, as needed for exporting abi9915-1.odt to doc
3368 m_pOrigPam->DeleteMark();
3369 *m_pOrigPam->GetPoint() = SwPosition(m_rDoc.GetNodes().GetEndOfContent());
3370 static_cast<SwPaM&>(*m_pCurPam) = *m_pOrigPam;
3372 m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags(m_nOrigRedlineFlags);
3374 return err;
3377 bool SwWW8Writer::InitStd97CodecUpdateMedium( ::msfilter::MSCodec_Std97& rCodec )
3379 uno::Sequence< beans::NamedValue > aEncryptionData;
3381 if ( mpMedium )
3383 const SfxUnoAnyItem* pEncryptionDataItem = SfxItemSet::GetItem<SfxUnoAnyItem>(mpMedium->GetItemSet(), SID_ENCRYPTIONDATA, false);
3384 if ( pEncryptionDataItem && ( pEncryptionDataItem->GetValue() >>= aEncryptionData ) && !rCodec.InitCodec( aEncryptionData ) )
3386 OSL_ENSURE( false, "Unexpected EncryptionData!" );
3387 aEncryptionData.realloc( 0 );
3390 if ( !aEncryptionData.hasElements() )
3392 // try to generate the encryption data based on password
3393 const SfxStringItem* pPasswordItem = SfxItemSet::GetItem<SfxStringItem>(mpMedium->GetItemSet(), SID_PASSWORD, false);
3394 if ( pPasswordItem && !pPasswordItem->GetValue().isEmpty() && pPasswordItem->GetValue().getLength() <= 15 )
3396 // Generate random number with a seed of time as salt.
3397 rtlRandomPool aRandomPool = rtl_random_createPool ();
3398 sal_uInt8 pDocId[ 16 ];
3399 rtl_random_getBytes( aRandomPool, pDocId, 16 );
3401 rtl_random_destroyPool( aRandomPool );
3403 sal_uInt16 aPassword[16] = {};
3405 const OUString& sPassword(pPasswordItem->GetValue());
3406 for ( sal_Int32 nChar = 0; nChar < sPassword.getLength(); ++nChar )
3407 aPassword[nChar] = sPassword[nChar];
3409 rCodec.InitKey( aPassword, pDocId );
3410 aEncryptionData = rCodec.GetEncryptionData();
3412 mpMedium->GetItemSet()->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::makeAny( aEncryptionData ) ) );
3416 if ( aEncryptionData.hasElements() )
3417 mpMedium->GetItemSet()->ClearItem( SID_PASSWORD );
3420 // nonempty encryption data means here that the codec was successfully initialized
3421 return aEncryptionData.hasElements();
3424 ErrCode WW8Export::ExportDocument_Impl()
3426 PrepareStorage();
3428 pFib.reset(new WW8Fib(8, m_bDot));
3430 tools::SvRef<SotStorageStream> xWwStrm( GetWriter().GetStorage().OpenSotStream( m_aMainStg ) );
3431 tools::SvRef<SotStorageStream> xTableStrm( xWwStrm ), xDataStrm( xWwStrm );
3432 xWwStrm->SetBufferSize( 32768 );
3434 pFib->m_fWhichTableStm = true;
3435 xTableStrm = GetWriter().GetStorage().OpenSotStream(SL::a1Table, StreamMode::STD_WRITE);
3436 xDataStrm = GetWriter().GetStorage().OpenSotStream(SL::aData, StreamMode::STD_WRITE);
3438 xDataStrm->SetBufferSize( 32768 ); // for graphics
3439 xTableStrm->SetBufferSize( 16384 ); // for the Font-/Style-Table, etc.
3441 xTableStrm->SetEndian( SvStreamEndian::LITTLE );
3442 xDataStrm->SetEndian( SvStreamEndian::LITTLE );
3444 GetWriter().SetStream( xWwStrm.get() );
3445 pTableStrm = xTableStrm.get();
3446 pDataStrm = xDataStrm.get();
3448 Strm().SetEndian( SvStreamEndian::LITTLE );
3450 utl::TempFile aTempMain;
3451 aTempMain.EnableKillingFile();
3452 utl::TempFile aTempTable;
3453 aTempTable.EnableKillingFile();
3454 utl::TempFile aTempData;
3455 aTempData.EnableKillingFile();
3457 msfilter::MSCodec_Std97 aCtx;
3458 bool bEncrypt = GetWriter().InitStd97CodecUpdateMedium(aCtx);
3459 if ( bEncrypt )
3461 GetWriter().SetStream(
3462 aTempMain.GetStream( StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE ) );
3464 pTableStrm = aTempTable.GetStream( StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE );
3466 pDataStrm = aTempData.GetStream( StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE );
3468 sal_uInt8 const aRC4EncryptionHeader[ 52 ] = {0};
3469 pTableStrm->WriteBytes(aRC4EncryptionHeader, 52);
3472 // Default: "Standard"
3473 pSepx.reset(new WW8_WrPlcSepx( *this )); // Sections/headers/footers
3475 pFootnote.reset(new WW8_WrPlcFootnoteEdn( TXT_FTN )); // Footnotes
3476 pEdn.reset(new WW8_WrPlcFootnoteEdn( TXT_EDN )); // Endnotes
3477 m_pAtn = new WW8_WrPlcAnnotations; // PostIts
3478 m_pFactoids.reset(new WW8_WrtFactoids); // Smart tags.
3479 m_pTextBxs = new WW8_WrPlcTextBoxes( TXT_TXTBOX );
3480 m_pHFTextBxs = new WW8_WrPlcTextBoxes( TXT_HFTXTBOX );
3482 m_pSdrObjs = new MainTextPlcDrawObj; // Draw-/Fly-Objects for main text
3483 m_pHFSdrObjs = new HdFtPlcDrawObj; // Draw-/Fly-Objects for header/footer
3485 m_pBkmks = new WW8_WrtBookmarks; // Bookmarks
3486 GetWriter().CreateBookmarkTable();
3488 m_pPapPlc.reset(new WW8_WrPlcPn( *this, PAP, pFib->m_fcMin ));
3489 m_pChpPlc.reset(new WW8_WrPlcPn( *this, CHP, pFib->m_fcMin ));
3490 pO.reset(new ww::bytes);
3491 m_pStyles.reset(new MSWordStyles( *this ));
3492 m_pFieldMain.reset(new WW8_WrPlcField( 2, TXT_MAINTEXT ));
3493 m_pFieldHdFt.reset(new WW8_WrPlcField( 2, TXT_HDFT ));
3494 m_pFieldFootnote.reset(new WW8_WrPlcField( 2, TXT_FTN ));
3495 m_pFieldEdn.reset(new WW8_WrPlcField( 2, TXT_EDN ));
3496 m_pFieldAtn.reset(new WW8_WrPlcField( 2, TXT_ATN ));
3497 m_pFieldTextBxs.reset(new WW8_WrPlcField( 2, TXT_TXTBOX ));
3498 m_pFieldHFTextBxs.reset(new WW8_WrPlcField( 2, TXT_HFTXTBOX ));
3500 m_pMagicTable.reset(new WW8_WrMagicTable);
3502 m_pGrf.reset(new SwWW8WrGrf( *this ));
3503 m_pPiece = new WW8_WrPct( pFib->m_fcMin );
3504 pDop.reset(new WW8Dop);
3506 pDop->fRevMarking = bool( RedlineFlags::On & m_nOrigRedlineFlags );
3507 SwRootFrame const*const pLayout(m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout());
3508 pDop->fRMView = pLayout == nullptr || !pLayout->IsHideRedlines();
3509 pDop->fRMPrint = pDop->fRMView;
3511 // set AutoHyphenation flag if found in default para style
3512 const SfxPoolItem* pItem;
3513 SwTextFormatColl* pStdTextFormatColl =
3514 m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD, false);
3515 if (pStdTextFormatColl && SfxItemState::SET == pStdTextFormatColl->GetItemState(
3516 RES_PARATR_HYPHENZONE, false, &pItem))
3518 pDop->fAutoHyphen = static_cast<const SvxHyphenZoneItem*>(pItem)->IsHyphen();
3521 StoreDoc1();
3523 ErrCode err = ERRCODE_NONE;
3524 if ( bEncrypt )
3526 SvStream *pStrmTemp, *pTableStrmTemp, *pDataStrmTemp;
3527 pStrmTemp = xWwStrm.get();
3528 pTableStrmTemp = xTableStrm.get();
3529 pDataStrmTemp = xDataStrm.get();
3531 if ( pDataStrmTemp && pDataStrmTemp != pStrmTemp) {
3532 err = EncryptRC4(aCtx, *pDataStrm, *pDataStrmTemp);
3533 if (err != ERRCODE_NONE) {
3534 goto done;
3538 err = EncryptRC4(aCtx, *pTableStrm, *pTableStrmTemp);
3539 if (err != ERRCODE_NONE) {
3540 goto done;
3543 // Write Unencrypted Header 52 bytes to the start of the table stream
3544 // EncryptionVersionInfo (4 bytes): A Version structure where Version.vMajor MUST be 0x0001, and Version.vMinor MUST be 0x0001.
3545 pTableStrmTemp->Seek( 0 );
3546 pTableStrmTemp->WriteUInt32( 0x10001 ); // nEncType
3548 sal_uInt8 pDocId[16];
3549 aCtx.GetDocId( pDocId );
3551 sal_uInt8 pSaltData[16];
3552 sal_uInt8 pSaltDigest[16];
3553 aCtx.GetEncryptKey( pDocId, pSaltData, pSaltDigest );
3555 pTableStrmTemp->WriteBytes(pDocId, 16);
3556 pTableStrmTemp->WriteBytes(pSaltData, 16);
3557 pTableStrmTemp->WriteBytes(pSaltDigest, 16);
3559 err = EncryptRC4(aCtx, GetWriter().Strm(), *pStrmTemp);
3560 if (err != ERRCODE_NONE) {
3561 goto done;
3564 // Write Unencrypted Fib 68 bytes to the start of the workdocument stream
3565 pFib->m_fEncrypted = true; // fEncrypted indicates the document is encrypted.
3566 pFib->m_fObfuscated = false; // Must be 0 for RC4.
3567 pFib->m_nHash = 0x34; // encrypt header bytes count of table stream.
3568 pFib->m_nKey = 0; // lkey2 must be 0 for RC4.
3570 pStrmTemp->Seek( 0 );
3571 pFib->WriteHeader( *pStrmTemp );
3572 done:;
3575 m_pGrf.reset();
3576 m_pMagicTable.reset();
3577 m_pFieldFootnote.reset();
3578 m_pFieldTextBxs.reset();
3579 m_pFieldHFTextBxs.reset();
3580 m_pFieldAtn.reset();
3581 m_pFieldEdn.reset();
3582 m_pFieldHdFt.reset();
3583 m_pFieldMain.reset();
3584 m_pStyles.reset();
3585 pO.reset();
3586 m_pChpPlc.reset();
3587 m_pPapPlc.reset();
3588 pSepx.reset();
3590 delete m_pRedlAuthors;
3591 delete m_pSdrObjs;
3592 delete m_pHFSdrObjs;
3593 delete m_pTextBxs;
3594 delete m_pHFTextBxs;
3595 delete m_pAtn;
3596 pEdn.reset();
3597 pFootnote.reset();
3598 delete m_pBkmks;
3599 delete m_pPiece;
3600 pDop.reset();
3601 pFib.reset();
3602 GetWriter().SetStream( nullptr );
3604 xWwStrm->SetBufferSize( 0 );
3605 xTableStrm->SetBufferSize( 0 );
3606 xDataStrm->SetBufferSize( 0 );
3607 if( 0 == pDataStrm->Seek( STREAM_SEEK_TO_END ))
3609 xDataStrm.clear();
3610 pDataStrm = nullptr;
3611 GetWriter().GetStorage().Remove(SL::aData);
3614 return err;
3617 void WW8Export::PrepareStorage()
3619 static const sal_uInt8 pData[] =
3621 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
3622 0xFF, 0xFF, 0xFF, 0xFF, 0x06, 0x09, 0x02, 0x00,
3623 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00,
3624 0x00, 0x00, 0x00, 0x46,
3626 0x18, 0x00, 0x00, 0x00,
3627 'M', 'i', 'c', 'r', 'o', 's', 'o', 'f',
3628 't', ' ', 'W', 'o', 'r', 'd', '-', 'D',
3629 'o', 'k', 'u', 'm', 'e', 'n', 't', 0x0,
3631 0x0A, 0x00, 0x00, 0x00,
3632 'M', 'S', 'W', 'o', 'r', 'd', 'D', 'o',
3633 'c', 0x0,
3635 0x10, 0x00, 0x00, 0x00,
3636 'W', 'o', 'r', 'd', '.', 'D', 'o', 'c',
3637 'u', 'm', 'e', 'n', 't', '.', '8', 0x0,
3639 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
3640 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
3643 SvGlobalName aGName(MSO_WW8_CLASSID);
3644 GetWriter().GetStorage().SetClass(
3645 aGName, SotClipboardFormatId::NONE, "Microsoft Word-Document");
3646 tools::SvRef<SotStorageStream> xStor( GetWriter().GetStorage().OpenSotStream(sCompObj) );
3647 xStor->WriteBytes(pData, sizeof(pData));
3649 SwDocShell* pDocShell = m_rDoc.GetDocShell ();
3650 OSL_ENSURE(pDocShell, "no SwDocShell");
3652 if (!pDocShell) return;
3654 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
3655 pDocShell->GetModel(), uno::UNO_QUERY_THROW);
3656 uno::Reference<document::XDocumentProperties> xDocProps(
3657 xDPS->getDocumentProperties());
3658 OSL_ENSURE(xDocProps.is(), "DocumentProperties is null");
3660 if (!xDocProps.is())
3661 return;
3663 if ( SvtFilterOptions::Get().IsEnableWordPreview() )
3665 std::shared_ptr<GDIMetaFile> xMetaFile =
3666 pDocShell->GetPreviewMetaFile();
3667 uno::Sequence<sal_Int8> metaFile(
3668 sfx2::convertMetaFile(xMetaFile.get()));
3669 sfx2::SaveOlePropertySet(xDocProps, &GetWriter().GetStorage(), &metaFile);
3671 else
3672 sfx2::SaveOlePropertySet( xDocProps, &GetWriter().GetStorage() );
3675 ErrCode SwWW8Writer::WriteStorage()
3677 tools::SvRef<SotStorage> pOrigStg;
3678 uno::Reference< packages::XPackageEncryption > xPackageEncryption;
3679 std::shared_ptr<SvStream> pSotStorageStream;
3680 uno::Sequence< beans::NamedValue > aEncryptionData;
3681 if (mpMedium)
3683 // Check for specific encryption requests
3684 const SfxUnoAnyItem* pEncryptionDataItem = SfxItemSet::GetItem<SfxUnoAnyItem>(mpMedium->GetItemSet(), SID_ENCRYPTIONDATA, false);
3685 if (pEncryptionDataItem && (pEncryptionDataItem->GetValue() >>= aEncryptionData))
3687 ::comphelper::SequenceAsHashMap aHashData(aEncryptionData);
3688 OUString sCryptoType = aHashData.getUnpackedValueOrDefault("CryptoType", OUString());
3690 if (sCryptoType.getLength())
3692 uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
3693 uno::Sequence<uno::Any> aArguments{
3694 uno::makeAny(beans::NamedValue("Binary", uno::makeAny(true))) };
3695 xPackageEncryption.set(
3696 xComponentContext->getServiceManager()->createInstanceWithArgumentsAndContext(
3697 "com.sun.star.comp.oox.crypto." + sCryptoType, aArguments, xComponentContext), uno::UNO_QUERY);
3699 if (xPackageEncryption.is())
3701 // We have an encryptor
3702 // Create new temporary storage for content
3703 pOrigStg = m_pStg;
3704 pSotStorageStream = std::make_shared<SvMemoryStream>();
3705 m_pStg = new SotStorage(*pSotStorageStream);
3711 ErrCode nErrorCode = WriteStorageImpl();
3713 if (xPackageEncryption.is())
3715 m_pStg->Commit();
3716 pSotStorageStream->Seek(0);
3718 // Encrypt data written into temporary storage
3719 xPackageEncryption->setupEncryption(aEncryptionData);
3721 uno::Reference<io::XInputStream > xInputStream(new utl::OSeekableInputStreamWrapper(pSotStorageStream.get(), false));
3722 uno::Sequence<beans::NamedValue> aStreams = xPackageEncryption->encrypt(xInputStream);
3724 m_pStg = pOrigStg;
3725 for (const beans::NamedValue & aStreamData : std::as_const(aStreams))
3727 // To avoid long paths split and open substorages recursively
3728 // Splitting paths manually, since comphelper::string::split is trimming special characters like \0x01, \0x09
3729 tools::SvRef<SotStorage> pStorage = m_pStg.get();
3730 OUString sFileName;
3731 sal_Int32 idx = 0;
3732 while (pStorage && idx >= 0)
3734 OUString sPathElem = aStreamData.Name.getToken(0, L'/', idx);
3735 if (!sPathElem.isEmpty())
3737 if (idx < 0)
3739 sFileName = sPathElem;
3741 else
3743 pStorage = pStorage->OpenSotStorage(sPathElem);
3744 if (!pStorage)
3745 break;
3750 if (!pStorage)
3752 nErrorCode = ERRCODE_IO_GENERAL;
3753 break;
3756 tools::SvRef<SotStorageStream> pStream = pStorage->OpenSotStream(sFileName);
3757 if (!pStream)
3759 nErrorCode = ERRCODE_IO_GENERAL;
3760 break;
3762 uno::Sequence<sal_Int8> aStreamContent;
3763 aStreamData.Value >>= aStreamContent;
3764 size_t nBytesWritten = pStream->WriteBytes(aStreamContent.getArray(), aStreamContent.getLength());
3765 if (nBytesWritten != static_cast<size_t>(aStreamContent.getLength()))
3767 nErrorCode = ERRCODE_IO_CANTWRITE;
3768 break;
3773 return nErrorCode;
3775 ErrCode SwWW8Writer::WriteStorageImpl()
3777 // #i34818# - update layout (if present), for SwWriteTable
3778 SwViewShell* pViewShell = m_pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
3779 if( pViewShell != nullptr )
3780 pViewShell->CalcLayout();
3782 tools::Long nMaxNode = m_pDoc->GetNodes().Count();
3783 ::StartProgress( STR_STATSTR_W4WWRITE, 0, nMaxNode, m_pDoc->GetDocShell() );
3785 // Respect table at the beginning of the document
3787 SwTableNode* pTNd = m_pCurrentPam->GetNode().FindTableNode();
3788 if( pTNd && m_bWriteAll )
3789 // start with the table node !!
3790 m_pCurrentPam->GetPoint()->nNode = *pTNd;
3793 // Do the actual export
3794 ErrCode err = ERRCODE_NONE;
3796 bool bDot = mpMedium->GetFilter()->GetName().endsWith("Vorlage");
3797 WW8Export aExport(this, *m_pDoc, m_pCurrentPam, m_pOrigPam, bDot);
3798 m_pExport = &aExport;
3799 err = aExport.ExportDocument( m_bWriteAll );
3800 m_pExport = nullptr;
3803 ::EndProgress( m_pDoc->GetDocShell() );
3804 return err;
3807 ErrCode SwWW8Writer::WriteMedium( SfxMedium& )
3809 return WriteStorage();
3812 ErrCode SwWW8Writer::Write( SwPaM& rPaM, SfxMedium& rMed,
3813 const OUString* pFileName )
3815 mpMedium = &rMed;
3816 ErrCode nRet = StgWriter::Write( rPaM, rMed, pFileName );
3817 mpMedium = nullptr;
3818 return nRet;
3821 MSWordExportBase::MSWordExportBase( SwDoc& rDocument, std::shared_ptr<SwUnoCursor> & pCurrentPam, SwPaM* pOriginalPam )
3822 : m_aMainStg(sMainStream)
3823 , m_pISet(nullptr)
3824 , m_pPiece(nullptr)
3825 , m_pTopNodeOfHdFtPage(nullptr)
3826 , m_pBkmks(nullptr)
3827 , m_pRedlAuthors(nullptr)
3828 , m_pTableInfo(std::make_shared<ww8::WW8TableInfo>())
3829 , m_nCharFormatStart(0)
3830 , m_nFormatCollStart(0)
3831 , m_nStyleBeforeFly(0)
3832 , m_nLastFormatId(0)
3833 , m_nUniqueList(0)
3834 , m_nHdFtIndex(0)
3835 , m_nOrigRedlineFlags(RedlineFlags::NONE)
3836 , m_bOrigShowChanges(true)
3837 , m_pCurrentPageDesc(nullptr)
3838 , m_bFirstTOCNodeWithSection(false)
3839 , m_pChpIter(nullptr)
3840 , m_pAtn(nullptr)
3841 , m_pTextBxs(nullptr)
3842 , m_pHFTextBxs(nullptr)
3843 , m_pParentFrame(nullptr)
3844 , m_pFlyOffset(nullptr)
3845 , m_eNewAnchorType(RndStdIds::FLY_AS_CHAR)
3846 , m_pStyAttr(nullptr)
3847 , m_pOutFormatNode(nullptr)
3848 , m_pCurrentStyle(nullptr)
3849 , m_pSdrObjs(nullptr)
3850 , m_pHFSdrObjs(nullptr)
3851 , m_pEscher(nullptr)
3852 , m_nTextTyp(0)
3853 , m_bStyDef(false)
3854 , m_bBreakBefore(false)
3855 , m_bOutKF(false)
3856 , m_bOutFlyFrameAttrs(false)
3857 , m_bOutPageDescs(false)
3858 , m_bOutFirstPage(false)
3859 , m_bOutTable(false)
3860 , m_bOutGrf(false)
3861 , m_bInWriteEscher(false)
3862 , m_bStartTOX(false)
3863 , m_bInWriteTOX(false)
3864 , m_bFootnoteAtTextEnd(false)
3865 , m_bEndAtTextEnd(false)
3866 , m_bHasHdr(false)
3867 , m_bHasFtr(false)
3868 , m_bSubstituteBullets(true)
3869 , m_bTabInTOC(false)
3870 , m_bHideTabLeaderAndPageNumbers(false)
3871 , m_bExportModeRTF(false)
3872 , m_bFontSizeWritten(false)
3873 , m_bAddFootnoteTab(false)
3874 , m_rDoc(rDocument)
3875 , m_nCurStart(pCurrentPam->GetPoint()->nNode.GetIndex())
3876 , m_nCurEnd(pCurrentPam->GetMark()->nNode.GetIndex())
3877 , m_pCurPam(pCurrentPam)
3878 , m_pOrigPam(pOriginalPam)
3882 MSWordExportBase::~MSWordExportBase()
3884 if (m_pUsedNumTable) // all used NumRules
3886 // clear the part of the list array that was copied from the document
3887 // - it's an auto delete array, so the rest of the array which are
3888 // duplicated lists that were added during the export will be deleted.
3889 m_pUsedNumTable->erase(m_pUsedNumTable->begin(), m_pUsedNumTable->begin() + m_pUsedNumTable->size() - m_nUniqueList);
3890 m_pUsedNumTable.reset();
3892 m_pOLEExp.reset();
3893 m_pOCXExp.reset();
3896 WW8Export::WW8Export( SwWW8Writer *pWriter,
3897 SwDoc& rDocument, std::shared_ptr<SwUnoCursor> & pCurrentPam, SwPaM* pOriginalPam,
3898 bool bDot )
3899 : MSWordExportBase( rDocument, pCurrentPam, pOriginalPam )
3900 , pTableStrm(nullptr)
3901 , pDataStrm(nullptr)
3902 , m_bDot(bDot)
3903 , m_pWriter(pWriter)
3904 , m_pAttrOutput(new WW8AttributeOutput(*this))
3908 WW8Export::~WW8Export()
3912 AttributeOutputBase& WW8Export::AttrOutput() const
3914 return *m_pAttrOutput;
3917 MSWordSections& WW8Export::Sections() const
3919 return *pSepx;
3922 SwWW8Writer::SwWW8Writer(const OUString& rFltName, const OUString& rBaseURL)
3923 : StgWriter(),
3924 m_pExport( nullptr ),
3925 mpMedium( nullptr )
3927 assert(rFltName == FILTER_WW8); // WW6/7 export was removed
3928 (void)rFltName;
3929 SetBaseURL( rBaseURL );
3932 SwWW8Writer::~SwWW8Writer()
3936 extern "C" SAL_DLLPUBLIC_EXPORT sal_uInt32 SaveOrDelMSVBAStorage_ww8( SfxObjectShell& rDoc, SotStorage& rStor, sal_Bool bSaveInto, const OUString& rStorageName )
3938 SvxImportMSVBasic aTmp( rDoc, rStor );
3939 return sal_uInt32(aTmp.SaveOrDelMSVBAStorage( bSaveInto, rStorageName ));
3942 extern "C" SAL_DLLPUBLIC_EXPORT void ExportDOC( const OUString& rFltName, const OUString& rBaseURL, WriterRef& xRet )
3944 xRet = new SwWW8Writer( rFltName, rBaseURL );
3947 extern "C" SAL_DLLPUBLIC_EXPORT sal_uInt32 GetSaveWarningOfMSVBAStorage_ww8( SfxObjectShell &rDocS )
3949 return sal_uInt32(SvxImportMSVBasic::GetSaveWarningOfMSVBAStorage( rDocS ));
3952 bool WW8_WrPlcFootnoteEdn::WriteText( WW8Export& rWrt )
3954 bool bRet = false;
3955 if (TXT_FTN == nTyp)
3957 bRet = WriteGenericText( rWrt, TXT_FTN, rWrt.pFib->m_ccpFootnote );
3958 rWrt.m_pFieldFootnote->Finish( rWrt.Fc2Cp( rWrt.Strm().Tell() ),
3959 rWrt.pFib->m_ccpText );
3961 else
3963 bRet = WriteGenericText( rWrt, TXT_EDN, rWrt.pFib->m_ccpEdn );
3964 rWrt.m_pFieldEdn->Finish( rWrt.Fc2Cp( rWrt.Strm().Tell() ),
3965 rWrt.pFib->m_ccpText + rWrt.pFib->m_ccpFootnote
3966 + rWrt.pFib->m_ccpHdr + rWrt.pFib->m_ccpAtn );
3968 return bRet;
3971 void WW8_WrPlcFootnoteEdn::WritePlc( WW8Export& rWrt ) const
3973 if( TXT_FTN == nTyp )
3975 WriteGenericPlc( rWrt, TXT_FTN, rWrt.pFib->m_fcPlcffndText,
3976 rWrt.pFib->m_lcbPlcffndText, rWrt.pFib->m_fcPlcffndRef,
3977 rWrt.pFib->m_lcbPlcffndRef );
3979 else
3981 WriteGenericPlc( rWrt, TXT_EDN, rWrt.pFib->m_fcPlcfendText,
3982 rWrt.pFib->m_lcbPlcfendText, rWrt.pFib->m_fcPlcfendRef,
3983 rWrt.pFib->m_lcbPlcfendRef );
3987 bool WW8_WrPlcAnnotations::WriteText( WW8Export& rWrt )
3989 bool bRet = WriteGenericText( rWrt, TXT_ATN, rWrt.pFib->m_ccpAtn );
3990 rWrt.m_pFieldAtn->Finish( rWrt.Fc2Cp( rWrt.Strm().Tell() ),
3991 rWrt.pFib->m_ccpText + rWrt.pFib->m_ccpFootnote
3992 + rWrt.pFib->m_ccpHdr );
3993 return bRet;
3996 void WW8_WrPlcAnnotations::WritePlc( WW8Export& rWrt ) const
3998 WriteGenericPlc( rWrt, TXT_ATN, rWrt.pFib->m_fcPlcfandText,
3999 rWrt.pFib->m_lcbPlcfandText, rWrt.pFib->m_fcPlcfandRef,
4000 rWrt.pFib->m_lcbPlcfandRef );
4003 void WW8_WrPlcTextBoxes::WritePlc( WW8Export& rWrt ) const
4005 if( TXT_TXTBOX == nTyp )
4007 WriteGenericPlc( rWrt, nTyp, rWrt.pFib->m_fcPlcftxbxBkd,
4008 rWrt.pFib->m_lcbPlcftxbxBkd, rWrt.pFib->m_fcPlcftxbxText,
4009 rWrt.pFib->m_lcbPlcftxbxText );
4011 else
4013 WriteGenericPlc( rWrt, nTyp, rWrt.pFib->m_fcPlcfHdrtxbxBkd,
4014 rWrt.pFib->m_lcbPlcfHdrtxbxBkd, rWrt.pFib->m_fcPlcfHdrtxbxText,
4015 rWrt.pFib->m_lcbPlcfHdrtxbxText );
4019 void WW8Export::RestoreMacroCmds()
4021 pFib->m_fcCmds = pTableStrm->Tell();
4023 uno::Reference < embed::XStorage > xSrcRoot(m_rDoc.GetDocShell()->GetStorage());
4026 uno::Reference < io::XStream > xSrcStream =
4027 xSrcRoot->openStreamElement( SL::aMSMacroCmds, embed::ElementModes::READ );
4028 std::unique_ptr<SvStream> pStream = ::utl::UcbStreamHelper::CreateStream( xSrcStream );
4030 if ( pStream && ERRCODE_NONE == pStream->GetError())
4032 pFib->m_lcbCmds = pStream->TellEnd();
4033 pStream->Seek(0);
4035 std::unique_ptr<sal_uInt8[]> pBuffer( new sal_uInt8[pFib->m_lcbCmds] );
4036 bool bReadOk = checkRead(*pStream, pBuffer.get(), pFib->m_lcbCmds);
4037 if (bReadOk)
4038 pTableStrm->WriteBytes(pBuffer.get(), pFib->m_lcbCmds);
4041 catch ( const uno::Exception& )
4045 // set len to FIB
4046 pFib->m_lcbCmds = pTableStrm->Tell() - pFib->m_fcCmds;
4049 void WW8SHDLong::Write( WW8Export& rExport )
4051 rExport.InsUInt32( m_cvFore );
4052 rExport.InsUInt32( m_cvBack );
4053 rExport.InsUInt16( 0 ); // ipat
4056 void WW8Export::WriteFormData( const ::sw::mark::IFieldmark& rFieldmark )
4058 const ::sw::mark::IFieldmark* pFieldmark = &rFieldmark;
4059 const ::sw::mark::ICheckboxFieldmark* pAsCheckbox = dynamic_cast< const ::sw::mark::ICheckboxFieldmark* >( pFieldmark );
4061 if ( ! ( rFieldmark.GetFieldname() == ODF_FORMTEXT ||
4062 rFieldmark.GetFieldname() == ODF_FORMDROPDOWN ||
4063 rFieldmark.GetFieldname() == ODF_FORMCHECKBOX ) )
4065 SAL_WARN( "sw.ww8", "unknown field type" );
4066 return;
4069 int type = 0; // TextFieldmark
4070 if ( pAsCheckbox )
4071 type = 1;
4072 if ( rFieldmark.GetFieldname() == ODF_FORMDROPDOWN )
4073 type=2;
4075 ::sw::mark::IFieldmark::parameter_map_t::const_iterator pParameter = rFieldmark.GetParameters()->find("name");
4076 OUString ffname;
4077 if ( pParameter != rFieldmark.GetParameters()->end() )
4079 OUString aName;
4080 pParameter->second >>= aName;
4081 const sal_Int32 nLen = std::min( sal_Int32(20), aName.getLength() );
4082 ffname = aName.copy(0, nLen);
4085 sal_uInt64 nDataStt = pDataStrm->Tell();
4086 m_pChpPlc->AppendFkpEntry(Strm().Tell());
4088 WriteChar(0x01);
4089 static sal_uInt8 aArr1[] =
4091 0x03, 0x6a, 0,0,0,0, // sprmCPicLocation
4093 0x06, 0x08, 0x01, // sprmCFData
4094 0x55, 0x08, 0x01, // sprmCFSpec
4095 0x02, 0x08, 0x01 // sprmCFFieldVanish
4097 sal_uInt8* pDataAdr = aArr1 + 2;
4098 Set_UInt32(pDataAdr, nDataStt);
4100 m_pChpPlc->AppendFkpEntry( Strm().Tell(), sizeof( aArr1 ), aArr1 );
4102 struct FFDataHeader
4104 sal_uInt32 version;
4105 sal_uInt16 bits;
4106 sal_uInt16 cch;
4107 sal_uInt16 hps;
4108 FFDataHeader() : version( 0xFFFFFFFF ), bits(0), cch(0), hps(0) {}
4111 FFDataHeader aFieldHeader;
4112 aFieldHeader.bits |= (type & 0x03);
4114 sal_Int32 ffres = 0; // rFieldmark.GetFFRes();
4115 if ( pAsCheckbox && pAsCheckbox->IsChecked() )
4116 ffres = 1;
4117 else if ( type == 2 )
4119 ::sw::mark::IFieldmark::parameter_map_t::const_iterator pResParameter = rFieldmark.GetParameters()->find(ODF_FORMDROPDOWN_RESULT);
4120 if(pResParameter != rFieldmark.GetParameters()->end())
4121 pResParameter->second >>= ffres;
4122 else
4123 ffres = 0;
4125 aFieldHeader.bits |= ( (ffres<<2) & 0x7C );
4127 OUString ffdeftext;
4128 OUString ffformat;
4129 OUString ffhelptext = rFieldmark.GetFieldHelptext();
4130 if ( ffhelptext.getLength() > 255 )
4131 ffhelptext = ffhelptext.copy(0, 255);
4132 OUString ffstattext;
4133 OUString ffentrymcr;
4134 OUString ffexitmcr;
4135 if (type == 0) // iTypeText
4137 sal_uInt16 nType = 0;
4138 pParameter = rFieldmark.GetParameters()->find("Type");
4139 if ( pParameter != rFieldmark.GetParameters()->end() )
4141 OUString aType;
4142 pParameter->second >>= aType;
4143 if ( aType == "number" ) nType = 1;
4144 else if ( aType == "date" ) nType = 2;
4145 else if ( aType == "currentTime" ) nType = 3;
4146 else if ( aType == "currentDate" ) nType = 4;
4147 else if ( aType == "calculated" ) nType = 5;
4148 aFieldHeader.bits |= nType<<11; // FFDataBits-F 00111000 00000000
4151 if ( nType < 3 || nType == 5 ) // not currentTime or currentDate
4153 pParameter = rFieldmark.GetParameters()->find("Content");
4154 if ( pParameter != rFieldmark.GetParameters()->end() )
4156 OUString aDefaultText;
4157 pParameter->second >>= aDefaultText;
4158 const sal_Int32 nLen = std::min( sal_Int32(255), aDefaultText.getLength() );
4159 ffdeftext = aDefaultText.copy (0, nLen);
4163 pParameter = rFieldmark.GetParameters()->find("MaxLength");
4164 if ( pParameter != rFieldmark.GetParameters()->end() )
4166 sal_uInt16 nLength = 0;
4167 pParameter->second >>= nLength;
4168 nLength = std::min( sal_uInt16(32767), nLength );
4169 aFieldHeader.cch = nLength;
4172 pParameter = rFieldmark.GetParameters()->find("Format");
4173 if ( pParameter != rFieldmark.GetParameters()->end() )
4175 OUString aFormat;
4176 pParameter->second >>= aFormat;
4177 const sal_Int32 nLen = std::min( sal_Int32(64), aFormat.getLength() );
4178 ffformat = aFormat.copy(0, nLen);
4182 pParameter = rFieldmark.GetParameters()->find("Help"); //help
4183 if ( ffhelptext.isEmpty() && pParameter != rFieldmark.GetParameters()->end() )
4185 OUString aHelpText;
4186 pParameter->second >>= aHelpText;
4187 const sal_Int32 nLen = std::min( sal_Int32(255), aHelpText.getLength() );
4188 ffhelptext = aHelpText.copy (0, nLen);
4190 if ( !ffhelptext.isEmpty() )
4191 aFieldHeader.bits |= 0x1<<7;
4193 pParameter = rFieldmark.GetParameters()->find("Description"); // doc tooltip
4194 if ( pParameter == rFieldmark.GetParameters()->end() )
4195 pParameter = rFieldmark.GetParameters()->find("Hint"); //docx tooltip
4196 if ( pParameter != rFieldmark.GetParameters()->end() )
4198 OUString aStatusText;
4199 pParameter->second >>= aStatusText;
4200 const sal_Int32 nLen = std::min( sal_Int32(138), aStatusText.getLength() );
4201 ffstattext = aStatusText.copy (0, nLen);
4203 if ( !ffstattext.isEmpty() )
4204 aFieldHeader.bits |= 0x1<<8;
4206 pParameter = rFieldmark.GetParameters()->find("EntryMacro");
4207 if ( pParameter != rFieldmark.GetParameters()->end() )
4209 OUString aEntryMacro;
4210 pParameter->second >>= aEntryMacro;
4211 const sal_Int32 nLen = std::min( sal_Int32(32), aEntryMacro.getLength() );
4212 ffentrymcr = aEntryMacro.copy (0, nLen);
4215 pParameter = rFieldmark.GetParameters()->find("ExitMacro");
4216 if ( pParameter != rFieldmark.GetParameters()->end() )
4218 OUString aExitMacro;
4219 pParameter->second >>= aExitMacro;
4220 const sal_Int32 nLen = std::min( sal_Int32(32), aExitMacro.getLength() );
4221 ffexitmcr = aExitMacro.copy (0, nLen);
4224 std::vector< OUString > aListItems;
4225 if (type==2)
4227 aFieldHeader.bits |= 0x8000; // ffhaslistbox
4228 const ::sw::mark::IFieldmark::parameter_map_t* const pParameters = rFieldmark.GetParameters();
4229 ::sw::mark::IFieldmark::parameter_map_t::const_iterator pListEntries = pParameters->find(ODF_FORMDROPDOWN_LISTENTRY);
4230 if(pListEntries != pParameters->end())
4232 uno::Sequence< OUString > vListEntries;
4233 pListEntries->second >>= vListEntries;
4234 copy(vListEntries.begin(), vListEntries.end(), back_inserter(aListItems));
4238 const sal_uInt8 aFieldData[] =
4240 0x44,0, // the start of "next" data
4241 0,0,0,0,0,0,0,0,0,0, // PIC-Structure! /10
4242 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // | /16
4243 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // | /16
4244 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // | /16
4245 0,0,0,0, // / /4
4247 sal_uInt32 slen = sizeof(sal_uInt32)
4248 + sizeof(aFieldData)
4249 + sizeof( aFieldHeader.version ) + sizeof( aFieldHeader.bits ) + sizeof( aFieldHeader.cch ) + sizeof( aFieldHeader.hps )
4250 + 2*ffname.getLength() + 4
4251 + 2*ffformat.getLength() + 4
4252 + 2*ffhelptext.getLength() + 4
4253 + 2*ffstattext.getLength() + 4
4254 + 2*ffentrymcr.getLength() + 4
4255 + 2*ffexitmcr.getLength() + 4;
4256 if ( type )
4257 slen += 2; // wDef
4258 else
4259 slen += 2*ffdeftext.getLength() + 4; //xstzTextDef
4260 if ( type==2 ) {
4261 slen += 2; // sttb ( fExtend )
4262 slen += 4; // for num of list items
4263 const int items = aListItems.size();
4264 for( int i = 0; i < items; i++ ) {
4265 OUString item = aListItems[i];
4266 slen += 2 * item.getLength() + 2;
4270 pDataStrm->WriteUInt32( slen );
4272 int len = sizeof( aFieldData );
4273 OSL_ENSURE( len == 0x44-sizeof(sal_uInt32), "SwWW8Writer::WriteFormData(..) - wrong aFieldData length" );
4274 pDataStrm->WriteBytes( aFieldData, len );
4276 pDataStrm->WriteUInt32( aFieldHeader.version ).WriteUInt16( aFieldHeader.bits ).WriteUInt16( aFieldHeader.cch ).WriteUInt16( aFieldHeader.hps );
4278 SwWW8Writer::WriteString_xstz( *pDataStrm, ffname, true ); // Form field name
4280 if ( !type )
4281 SwWW8Writer::WriteString_xstz( *pDataStrm, ffdeftext, true );
4282 if ( type )
4283 pDataStrm->WriteUInt16( 0 );
4285 SwWW8Writer::WriteString_xstz( *pDataStrm, ffformat, true );
4286 SwWW8Writer::WriteString_xstz( *pDataStrm, ffhelptext, true );
4287 SwWW8Writer::WriteString_xstz( *pDataStrm, ffstattext, true );
4288 SwWW8Writer::WriteString_xstz( *pDataStrm, ffentrymcr, true );
4289 SwWW8Writer::WriteString_xstz( *pDataStrm, ffexitmcr, true );
4290 if (type==2) {
4291 pDataStrm->WriteUInt16( 0xFFFF );
4292 const int items=aListItems.size();
4293 pDataStrm->WriteUInt32( items );
4294 for(int i=0;i<items;i++) {
4295 OUString item=aListItems[i];
4296 SwWW8Writer::WriteString_xstz( *pDataStrm, item, false );
4301 void WW8Export::WriteHyperlinkData( const sw::mark::IFieldmark& /*rFieldmark*/ )
4303 //@TODO implement me !!!
4306 void WW8AttributeOutput::TableNodeInfoInner( ww8::WW8TableNodeInfoInner::Pointer_t pNodeInfoInner )
4308 SVBT16 nStyle;
4309 ShortToSVBT16( m_rWW8Export.m_nStyleBeforeFly, nStyle );
4311 #ifdef DBG_UTIL
4312 SAL_INFO( "sw.ww8", "<OutWW8_TableNodeInfoInner>" << pNodeInfoInner->toString());
4313 #endif
4315 m_rWW8Export.pO->clear();
4317 sal_uInt32 nShadowsBefore = pNodeInfoInner->getShadowsBefore();
4318 if (nShadowsBefore > 0)
4320 ww8::WW8TableNodeInfoInner::Pointer_t
4321 pTmpNodeInfoInner = std::make_shared<ww8::WW8TableNodeInfoInner>(nullptr);
4323 pTmpNodeInfoInner->setDepth(pNodeInfoInner->getDepth());
4324 pTmpNodeInfoInner->setEndOfCell(true);
4326 for (sal_uInt32 n = 0; n < nShadowsBefore; ++n)
4328 m_rWW8Export.WriteCR(pTmpNodeInfoInner);
4330 m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), nStyle, nStyle+2 ); // Style #
4331 TableInfoCell(pTmpNodeInfoInner);
4332 m_rWW8Export.m_pPapPlc->AppendFkpEntry
4333 ( m_rWW8Export.Strm().Tell(), m_rWW8Export.pO->size(), m_rWW8Export.pO->data() );
4335 m_rWW8Export.pO->clear();
4339 if (pNodeInfoInner->isEndOfCell())
4341 SAL_INFO( "sw.ww8", "<endOfCell/>" );
4343 m_rWW8Export.WriteCR(pNodeInfoInner);
4345 m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), nStyle, nStyle+2 ); // Style #
4346 TableInfoCell(pNodeInfoInner);
4347 m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), m_rWW8Export.pO->size(), m_rWW8Export.pO->data() );
4349 m_rWW8Export.pO->clear();
4352 sal_uInt32 nShadowsAfter = pNodeInfoInner->getShadowsAfter();
4353 if (nShadowsAfter > 0)
4355 ww8::WW8TableNodeInfoInner::Pointer_t
4356 pTmpNodeInfoInner= std::make_shared<ww8::WW8TableNodeInfoInner>(nullptr);
4358 pTmpNodeInfoInner->setDepth(pNodeInfoInner->getDepth());
4359 pTmpNodeInfoInner->setEndOfCell(true);
4361 for (sal_uInt32 n = 0; n < nShadowsAfter; ++n)
4363 m_rWW8Export.WriteCR(pTmpNodeInfoInner);
4365 m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), nStyle, nStyle+2 ); // Style #
4366 TableInfoCell(pTmpNodeInfoInner);
4367 m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), m_rWW8Export.pO->size(), m_rWW8Export.pO->data() );
4369 m_rWW8Export.pO->clear();
4373 if (pNodeInfoInner->isEndOfLine())
4375 SAL_INFO( "sw.ww8", "<endOfLine/>" );
4377 TableRowEnd(pNodeInfoInner->getDepth());
4379 ShortToSVBT16(0, nStyle);
4380 m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), nStyle, nStyle+2 ); // Style #
4381 TableInfoRow(pNodeInfoInner);
4382 m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), m_rWW8Export.pO->size(), m_rWW8Export.pO->data() );
4384 m_rWW8Export.pO->clear();
4386 SAL_INFO( "sw.ww8", "</OutWW8_TableNodeInfoInner>" );
4389 void MSWordExportBase::OutputStartNode( const SwStartNode & rNode)
4392 ww8::WW8TableNodeInfo::Pointer_t pNodeInfo =
4393 m_pTableInfo->getTableNodeInfo( &rNode );
4395 if (pNodeInfo)
4397 #ifdef DBG_UTIL
4398 SAL_INFO( "sw.ww8", pNodeInfo->toString());
4399 #endif
4400 const ww8::WW8TableNodeInfo::Inners_t aInners = pNodeInfo->getInners();
4401 ww8::WW8TableNodeInfo::Inners_t::const_reverse_iterator aIt(aInners.rbegin());
4402 ww8::WW8TableNodeInfo::Inners_t::const_reverse_iterator aEnd(aInners.rend());
4403 while (aIt != aEnd)
4405 ww8::WW8TableNodeInfoInner::Pointer_t pInner = aIt->second;
4407 AttrOutput().TableNodeInfoInner(pInner);
4408 ++aIt;
4411 SAL_INFO( "sw.ww8", "</OutWW8_SwStartNode>" );
4414 void MSWordExportBase::OutputEndNode( const SwEndNode &rNode )
4416 #ifdef DBG_UTIL
4417 SAL_INFO( "sw.ww8", "<OutWW8_SwEndNode>" << dbg_out(&rNode));
4418 #endif
4420 ww8::WW8TableNodeInfo::Pointer_t pNodeInfo = m_pTableInfo->getTableNodeInfo( &rNode );
4422 if (pNodeInfo)
4424 #ifdef DBG_UTIL
4425 SAL_INFO( "sw.ww8", pNodeInfo->toString());
4426 #endif
4427 const ww8::WW8TableNodeInfo::Inners_t aInners = pNodeInfo->getInners();
4428 for (const auto& rEntry : aInners)
4430 ww8::WW8TableNodeInfoInner::Pointer_t pInner = rEntry.second;
4431 AttrOutput().TableNodeInfoInner(pInner);
4434 SAL_INFO( "sw.ww8", "</OutWW8_SwEndNode>" );
4437 const NfKeywordTable & MSWordExportBase::GetNfKeywordTable()
4439 if (m_pKeyMap == nullptr)
4441 m_pKeyMap = std::make_shared<NfKeywordTable>();
4442 NfKeywordTable & rKeywordTable = *m_pKeyMap;
4443 rKeywordTable[NF_KEY_D] = "d";
4444 rKeywordTable[NF_KEY_DD] = "dd";
4445 rKeywordTable[NF_KEY_DDD] = "ddd";
4446 rKeywordTable[NF_KEY_DDDD] = "dddd";
4447 rKeywordTable[NF_KEY_M] = "M";
4448 rKeywordTable[NF_KEY_MM] = "MM";
4449 rKeywordTable[NF_KEY_MMM] = "MMM";
4450 rKeywordTable[NF_KEY_MMMM] = "MMMM";
4451 rKeywordTable[NF_KEY_NN] = "ddd";
4452 rKeywordTable[NF_KEY_NNN] = "dddd";
4453 rKeywordTable[NF_KEY_NNNN] = "dddd";
4454 rKeywordTable[NF_KEY_YY] = "yy";
4455 rKeywordTable[NF_KEY_YYYY] = "yyyy";
4456 rKeywordTable[NF_KEY_H] = "H";
4457 rKeywordTable[NF_KEY_HH] = "HH";
4458 rKeywordTable[NF_KEY_MI] = "m";
4459 rKeywordTable[NF_KEY_MMI] = "mm";
4460 rKeywordTable[NF_KEY_S] = "s";
4461 rKeywordTable[NF_KEY_SS] = "ss";
4462 rKeywordTable[NF_KEY_AMPM] = "AM/PM";
4465 return *m_pKeyMap;
4468 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */