tdf#130857 qt weld: Support mail merge "Server Auth" dialog
[LibreOffice.git] / sw / source / filter / ww8 / wrtww8.cxx
blobcad075e97d8b1ad73fc49e84c455b533b9a69f87
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 <unotools/securityoptions.hxx>
31 #include <algorithm>
32 #include <map>
33 #include <hintids.hxx>
34 #include <string.h>
35 #include <o3tl/safeint.hxx>
36 #include <osl/endian.h>
37 #include <sal/log.hxx>
38 #include <docsh.hxx>
39 #include <drawdoc.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 <formatflysplit.hxx>
91 #include <fmtftntx.hxx>
92 #include "sprmids.hxx"
94 #include <comphelper/sequenceashashmap.hxx>
95 #include <comphelper/processfactory.hxx>
96 #include "writerhelper.hxx"
97 #include "writerwordglue.hxx"
98 #include "ww8attributeoutput.hxx"
99 #include <xmloff/odffields.hxx>
100 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
101 #include <com/sun/star/document/XDocumentProperties.hpp>
102 #include <dbgoutsw.hxx>
103 #include <sfx2/docfile.hxx>
104 #include <sfx2/frame.hxx>
105 #include <svl/stritem.hxx>
106 #include <unotools/tempfile.hxx>
107 #include <filter/msfilter/mscodec.hxx>
108 #include <filter/msfilter/svxmsbas.hxx>
109 #include <rtl/random.h>
110 #include <vcl/svapp.hxx>
111 #include <sfx2/docfilt.hxx>
112 #include "WW8Sttbf.hxx"
113 #include <editeng/charrotateitem.hxx>
114 #include <svx/swframetypes.hxx>
115 #include "WW8FibData.hxx"
116 #include <numrule.hxx>
117 #include <fmtclds.hxx>
118 #include <rdfhelper.hxx>
119 #include <fmtclbl.hxx>
120 #include <iodetect.hxx>
121 #include <fmtwrapinfluenceonobjpos.hxx>
122 #include <officecfg/Office/Common.hxx>
123 #include <fmtanchr.hxx>
125 using namespace css;
126 using namespace sw::util;
127 using namespace sw::types;
129 /** FKP - Formatted disK Page
131 class WW8_WrFkp
133 sal_uInt8* m_pFkp; // Fkp total ( first and only FCs and Sprms )
134 sal_uInt8* m_pOfs; // pointer to the offset area, later copied to pFkp
135 ePLCFT m_ePlc;
136 short m_nStartGrp; // from here on grpprls
137 short m_nOldStartGrp;
138 sal_uInt8 m_nItemSize;
139 sal_uInt8 m_nIMax; // number of entry pairs
140 sal_uInt8 m_nOldVarLen;
141 bool m_bCombined; // true : paste not allowed
143 sal_uInt8 SearchSameSprm( sal_uInt16 nVarLen, const sal_uInt8* pSprms );
145 WW8_WrFkp(const WW8_WrFkp&) = delete;
146 WW8_WrFkp& operator=(const WW8_WrFkp&) = delete;
148 public:
149 WW8_WrFkp(ePLCFT ePl, WW8_FC nStartFc);
150 ~WW8_WrFkp();
151 bool Append( WW8_FC nEndFc, sal_uInt16 nVarLen, const sal_uInt8* pSprms );
152 void Combine();
153 void Write( SvStream& rStrm, SwWW8WrGrf& rGrf );
155 bool IsEqualPos(WW8_FC nEndFc) const
156 { return !m_bCombined && m_nIMax && nEndFc == reinterpret_cast<sal_Int32*>(m_pFkp)[m_nIMax]; }
157 void MergeToNew( short& rVarLen, sal_uInt8 *& pNewSprms );
158 bool IsEmptySprm() const
159 { return !m_bCombined && m_nIMax && !m_nOldVarLen; }
160 void SetNewEnd( WW8_FC nEnd )
161 { reinterpret_cast<sal_Int32*>(m_pFkp)[m_nIMax] = nEnd; }
163 WW8_FC GetStartFc() const;
164 WW8_FC GetEndFc() const;
166 sal_uInt8 *CopyLastSprms(sal_uInt8 &rLen);
169 // class WW8_WrPc collects all piece entries for one piece
170 class WW8_WrPc
172 WW8_CP m_nStartCp; // Starting character position of the text
173 WW8_FC m_nStartFc; // Starting file position of the text
174 sal_uInt16 m_nStatus; // End of paragraph inside the piece?
176 public:
177 WW8_WrPc(WW8_FC nSFc, WW8_CP nSCp )
178 : m_nStartCp( nSCp ), m_nStartFc( nSFc ), m_nStatus( 0x0040 )
181 void SetStatus() { m_nStatus = 0x0050; }
182 sal_uInt16 GetStatus() const { return m_nStatus; }
183 WW8_CP GetStartCp() const { return m_nStartCp; }
184 WW8_FC GetStartFc() const { return m_nStartFc; }
187 typedef std::map<OUString,tools::Long> BKMKNames;
188 typedef std::pair<bool,OUString> BKMK;
189 typedef std::pair<tools::Long,BKMK> BKMKCP;
190 typedef std::multimap<tools::Long,BKMKCP*> BKMKCPs;
191 typedef BKMKCPs::iterator CPItr;
193 class WW8_WrtBookmarks
195 private:
196 /// Structure of one item inside this map: (startPos, (endPos, (a bool value?, bookmarkName)))
197 BKMKCPs maSttCps;
198 BKMKNames maSwBkmkNms;
200 WW8_WrtBookmarks(WW8_WrtBookmarks const&) = delete;
201 WW8_WrtBookmarks& operator=(WW8_WrtBookmarks const&) = delete;
203 public:
204 WW8_WrtBookmarks();
205 ~WW8_WrtBookmarks();
206 //! Add a new bookmark to the list OR add an end position to an existing bookmark.
207 void Append( WW8_CP nStartCp, const OUString& rNm );
208 //! Write out bookmarks to file.
209 void Write( WW8Export& rWrt );
210 //! Move existing field marks from one position to another.
211 void MoveFieldMarks(WW8_CP nFrom, WW8_CP nTo);
214 WW8_WrtBookmarks::WW8_WrtBookmarks()
217 WW8_WrtBookmarks::~WW8_WrtBookmarks()
219 for (auto& rEntry : maSttCps)
221 if (rEntry.second)
223 delete rEntry.second;
224 rEntry.second = nullptr;
229 void WW8_WrtBookmarks::Append( WW8_CP nStartCp, const OUString& rNm)
231 std::pair<BKMKNames::iterator, bool> aResult = maSwBkmkNms.insert(std::pair<OUString,tools::Long>(rNm,0L));
232 if (aResult.second)
234 BKMK aBK(false,rNm);
235 BKMKCP* pBKCP = new BKMKCP(static_cast<tools::Long>(nStartCp),aBK);
236 maSttCps.insert(std::pair<tools::Long,BKMKCP*>(nStartCp,pBKCP));
237 aResult.first->second = static_cast<tools::Long>(nStartCp);
239 else
241 std::pair<CPItr,CPItr> aRange = maSttCps.equal_range(aResult.first->second);
242 for (CPItr aItr = aRange.first;aItr != aRange.second;++aItr)
244 if (aItr->second && aItr->second->second.second == rNm)
246 if (aItr->second->second.first)
247 nStartCp--;
248 aItr->second->first = static_cast<tools::Long>(nStartCp);
249 break;
255 void WW8_WrtBookmarks::Write( WW8Export& rWrt)
257 if (maSttCps.empty())
258 return;
259 tools::Long n;
260 std::vector<OUString> aNames;
261 SvMemoryStream aTempStrm1(65535,65535);
262 SvMemoryStream aTempStrm2(65535,65535);
264 BKMKCPs aEndCps;
265 for (const auto& rEntry : maSttCps)
267 if (rEntry.second)
269 aEndCps.insert(std::pair<tools::Long,BKMKCP*>(rEntry.second->first, rEntry.second));
270 aNames.push_back(rEntry.second->second.second);
271 SwWW8Writer::WriteLong(aTempStrm1, rEntry.first);
275 aTempStrm1.Seek(0);
276 n = 0;
277 for (const auto& rEntry : aEndCps)
279 if (rEntry.second)
281 rEntry.second->first = n;
282 SwWW8Writer::WriteLong( aTempStrm2, rEntry.first);
284 ++n;
287 aTempStrm2.Seek(0);
288 rWrt.WriteAsStringTable(aNames, rWrt.m_pFib->m_fcSttbfbkmk,rWrt.m_pFib->m_lcbSttbfbkmk);
289 SvStream& rStrm = *rWrt.m_pTableStrm;
290 rWrt.m_pFib->m_fcPlcfbkf = rStrm.Tell();
291 rStrm.WriteStream( aTempStrm1 );
292 SwWW8Writer::WriteLong(rStrm, rWrt.m_pFib->m_ccpText + rWrt.m_pFib->m_ccpTxbx);
293 for (const auto& rEntry : maSttCps)
295 if (rEntry.second)
297 SwWW8Writer::WriteLong(rStrm, rEntry.second->first);
300 rWrt.m_pFib->m_lcbPlcfbkf = rStrm.Tell() - rWrt.m_pFib->m_fcPlcfbkf;
301 rWrt.m_pFib->m_fcPlcfbkl = rStrm.Tell();
302 rStrm.WriteStream( aTempStrm2 );
303 SwWW8Writer::WriteLong(rStrm, rWrt.m_pFib->m_ccpText + rWrt.m_pFib->m_ccpTxbx);
304 rWrt.m_pFib->m_lcbPlcfbkl = rStrm.Tell() - rWrt.m_pFib->m_fcPlcfbkl;
307 void WW8_WrtBookmarks::MoveFieldMarks(WW8_CP nFrom, WW8_CP nTo)
309 std::pair<CPItr,CPItr> aRange = maSttCps.equal_range(nFrom);
310 CPItr aItr = aRange.first;
311 while (aItr != aRange.second)
313 if (aItr->second)
315 if (aItr->second->first == static_cast<tools::Long>(nFrom))
317 aItr->second->second.first = true;
318 aItr->second->first = nTo;
320 maSttCps.insert(std::pair<tools::Long,BKMKCP*>(nTo,aItr->second));
321 aItr->second = nullptr;
322 aRange = maSttCps.equal_range(nFrom);
323 aItr = aRange.first;
324 continue;
326 ++aItr;
330 /// Handles export of smart tags.
331 class WW8_WrtFactoids
333 std::vector<WW8_CP> m_aStartCPs;
334 std::vector<WW8_CP> m_aEndCPs;
335 std::vector< std::map<OUString, OUString> > m_aStatements;
337 WW8_WrtFactoids(WW8_WrtFactoids const&) = delete;
338 WW8_WrtFactoids& operator=(WW8_WrtFactoids const&) = delete;
340 public:
341 WW8_WrtFactoids();
342 void Append(WW8_CP nStartCp, WW8_CP nEndCp, const std::map<OUString, OUString>& rStatements);
343 void Write(WW8Export& rWrt);
346 WW8_WrtFactoids::WW8_WrtFactoids()
350 void WW8_WrtFactoids::Append(WW8_CP nStartCp, WW8_CP nEndCp, const std::map<OUString, OUString>& rStatements)
352 m_aStartCPs.push_back(nStartCp);
353 m_aEndCPs.push_back(nEndCp);
354 m_aStatements.push_back(rStatements);
357 void WW8_WrtFactoids::Write(WW8Export& rExport)
359 if (m_aStartCPs.empty())
360 return;
362 // Smart tags are otherwise removed by Word on saving.
363 rExport.m_pDop->fEmbedFactoids = true;
365 SvStream& rStream = *rExport.m_pTableStrm;
367 rExport.m_pFib->m_fcSttbfBkmkFactoid = rStream.Tell();
368 // Write SttbfBkmkFactoid.
369 rStream.WriteUInt16(0xffff); // fExtend
370 rStream.WriteUInt16(m_aStartCPs.size()); // cData
371 rStream.WriteUInt16(0); // cbExtra
373 for (size_t i = 0; i < m_aStartCPs.size(); ++i)
375 rStream.WriteUInt16(6); // cchData
376 // Write FACTOIDINFO.
377 rStream.WriteUInt32(i); // dwId
378 rStream.WriteUInt16(0); // fSubEntry
379 rStream.WriteUInt16(0); // fto
380 rStream.WriteUInt32(0); // pfpb
382 rExport.m_pFib->m_lcbSttbfBkmkFactoid = rStream.Tell() - rExport.m_pFib->m_fcSttbfBkmkFactoid;
384 rExport.m_pFib->m_fcPlcfBkfFactoid = rStream.Tell();
385 for (const WW8_CP& rCP : m_aStartCPs)
386 rStream.WriteInt32(rCP);
387 rStream.WriteInt32(rExport.m_pFib->m_ccpText + rExport.m_pFib->m_ccpTxbx);
389 // Write FBKFD.
390 for (size_t i = 0; i < m_aStartCPs.size(); ++i)
392 rStream.WriteInt16(i); // ibkl
393 rStream.WriteInt16(0); // bkc
394 rStream.WriteInt16(1); // cDepth, 1 as start and end is the same.
397 rExport.m_pFib->m_lcbPlcfBkfFactoid = rStream.Tell() - rExport.m_pFib->m_fcPlcfBkfFactoid;
399 rExport.m_pFib->m_fcPlcfBklFactoid = rStream.Tell();
400 for (const WW8_CP& rCP : m_aEndCPs)
401 rStream.WriteInt32(rCP);
402 rStream.WriteInt32(rExport.m_pFib->m_ccpText + rExport.m_pFib->m_ccpTxbx);
404 // Write FBKLD.
405 for (size_t i = 0; i < m_aEndCPs.size(); ++i)
407 rStream.WriteInt16(i); // ibkf
408 rStream.WriteInt16(0); // cDepth, 0 as does not overlap with any other smart tag.
410 rExport.m_pFib->m_lcbPlcfBklFactoid = rStream.Tell() - rExport.m_pFib->m_fcPlcfBklFactoid;
412 rExport.m_pFib->m_fcFactoidData = rStream.Tell();
413 // Write SmartTagData.
414 MSOFactoidType aFactoidType;
415 aFactoidType.m_nId = 1;
416 aFactoidType.m_aUri = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
417 aFactoidType.m_aTag = "RDF";
418 WW8SmartTagData aSmartTagData;
419 aSmartTagData.m_aPropBagStore.m_aFactoidTypes.push_back(aFactoidType);
421 std::set<OUString> aSet;
422 for (const std::map<OUString, OUString>& rStatements : m_aStatements)
424 // Statements for a single text node.
425 for (const auto& rPair : rStatements)
427 aSet.insert(rPair.first);
428 aSet.insert(rPair.second);
431 aSmartTagData.m_aPropBagStore.m_aStringTable.assign(aSet.begin(), aSet.end());
432 for (const std::map<OUString, OUString>& rStatements : m_aStatements)
434 MSOPropertyBag aPropertyBag;
435 aPropertyBag.m_nId = 1;
436 for (const auto& rPair : rStatements)
438 MSOProperty aProperty;
439 aProperty.m_nKey = std::distance(aSet.begin(), aSet.find(rPair.first));
440 aProperty.m_nValue = std::distance(aSet.begin(), aSet.find(rPair.second));
441 aPropertyBag.m_aProperties.push_back(aProperty);
443 aSmartTagData.m_aPropBags.push_back(aPropertyBag);
446 aSmartTagData.Write(rExport);
447 rExport.m_pFib->m_lcbFactoidData = rStream.Tell() - rExport.m_pFib->m_fcFactoidData;
450 #define DEFAULT_STYLES_COUNT 16
452 // Names of the storage streams
453 constexpr OUStringLiteral sMainStream = u"WordDocument";
454 constexpr OUStringLiteral sCompObj = u"\1CompObj";
456 static void WriteDop( WW8Export& rWrt )
458 WW8Dop& rDop = *rWrt.m_pDop;
460 // i#78951#, store the value of unknown compatibility options
461 rDop.SetCompatibilityOptions( rWrt.m_rDoc.getIDocumentSettingAccess().Getn32DummyCompatibilityOptions1());
462 rDop.SetCompatibilityOptions2( rWrt.m_rDoc.getIDocumentSettingAccess().Getn32DummyCompatibilityOptions2());
464 rDop.fNoLeading = !rWrt.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::ADD_EXT_LEADING);
465 rDop.fUsePrinterMetrics = !rWrt.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::USE_VIRTUAL_DEVICE);
467 // write default TabStop
468 const SvxTabStopItem& rTabStop =
469 rWrt.m_rDoc.GetAttrPool().GetUserOrPoolDefaultItem(RES_PARATR_TABSTOP);
470 rDop.dxaTab = o3tl::narrowing<sal_uInt16>(rTabStop[0].GetTabPos());
472 // Zoom factor and type
473 SwViewShell *pViewShell(rWrt.m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell());
474 if (pViewShell)
476 switch ( pViewShell->GetViewOptions()->GetZoomType() )
478 case SvxZoomType::WHOLEPAGE: rDop.zkSaved = 1; break;
479 case SvxZoomType::PAGEWIDTH: rDop.zkSaved = 2; break;
480 case SvxZoomType::OPTIMAL: rDop.zkSaved = 3; break;
481 default: rDop.zkSaved = 0;
482 rDop.wScaleSaved = pViewShell->GetViewOptions()->GetZoom();
483 break;
487 // Values from the DocumentStatistics (are definitely needed
488 // for the DocStat fields)
489 rDop.fWCFootnoteEdn = true; // because they are included in StarWriter
491 const SwDocStat& rDStat = rWrt.m_rDoc.getIDocumentStatistics().GetDocStat();
492 rDop.cWords = rDStat.nWord;
493 rDop.cCh = rDStat.nChar;
494 rDop.cPg = static_cast< sal_Int16 >(rDStat.nPage);
495 rDop.cParas = rDStat.nPara;
496 rDop.cLines = rDStat.nPara;
498 SwSectionFormats& rSections = rWrt.m_rDoc.GetSections();
499 if (!rSections.empty())
501 SwSectionFormat* pFormat = rSections[0];
502 bool bEndnAtEnd = pFormat->GetEndAtTextEnd().IsAtEnd();
503 if (bEndnAtEnd)
505 // DopBase.epc: this is normally 3 (end of document), change this to end of section.
506 rDop.epc = 0;
510 SwDocShell *pDocShell(rWrt.m_rDoc.GetDocShell());
511 OSL_ENSURE(pDocShell, "no SwDocShell");
512 uno::Reference<document::XDocumentProperties> xDocProps;
513 uno::Reference<beans::XPropertySet> xProps;
514 if ( pDocShell )
516 uno::Reference<lang::XComponent> xModelComp = pDocShell->GetModel();
517 xProps.set(xModelComp, uno::UNO_QUERY);
518 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(xModelComp, uno::UNO_QUERY_THROW);
519 xDocProps = xDPS->getDocumentProperties();
520 OSL_ENSURE(xDocProps.is(), "DocumentProperties is null");
522 rDop.lKeyProtDoc = pDocShell->GetModifyPasswordHash();
525 if ((rWrt.m_pSepx && rWrt.m_pSepx->DocumentIsProtected()) ||
526 rWrt.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_FORM ) ||
527 rDop.lKeyProtDoc != 0)
529 rDop.fProtEnabled = true;
530 // The password was ignored at import if forms protection was enabled,
531 // so round-trip it since protection is still enabled.
532 if ( rDop.lKeyProtDoc == 0 && xProps.is() )
534 comphelper::SequenceAsHashMap aPropMap( xProps->getPropertyValue(u"InteropGrabBag"_ustr));
535 aPropMap.getValue(u"FormPasswordHash"_ustr) >>= rDop.lKeyProtDoc;
538 else
540 rDop.fProtEnabled = false;
543 if (rWrt.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::GUTTER_AT_TOP))
545 rDop.iGutterPos = true;
548 if (!xDocProps.is())
550 rDop.dttmCreated = rDop.dttmRevised = rDop.dttmLastPrint = 0x45FBAC69;
552 else
554 ::util::DateTime uDT = xDocProps->getCreationDate();
555 rDop.dttmCreated = sw::ms::DateTime2DTTM(DateTime(uDT));
556 uDT = xDocProps->getModificationDate();
557 rDop.dttmRevised = sw::ms::DateTime2DTTM(DateTime(uDT));
558 uDT = xDocProps->getPrintDate();
559 rDop.dttmLastPrint = sw::ms::DateTime2DTTM(DateTime(uDT));
562 // Also, the DocStat fields in headers, footers are not calculated correctly.
563 // ( we do not have this fields! )
565 // and also for the Headers and Footers
566 rDop.cWordsFootnoteEnd = rDStat.nWord;
567 rDop.cChFootnoteEdn = rDStat.nChar;
568 rDop.cPgFootnoteEdn = static_cast<sal_Int16>(rDStat.nPage);
569 rDop.cParasFootnoteEdn = rDStat.nPara;
570 rDop.cLinesFootnoteEdn = rDStat.nPara;
572 rDop.fDontUseHTMLAutoSpacing = rWrt.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::PARA_SPACE_MAX);
574 rDop.fExpShRtn = !rWrt.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK); // #i56856#
576 IDocumentSettingAccess& rIDSA = rWrt.m_rDoc.getIDocumentSettingAccess();
577 rDop.fDontBreakWrappedTables = rIDSA.get(DocumentSettingId::DO_NOT_BREAK_WRAPPED_TABLES);
579 rDop.Write( *rWrt.m_pTableStrm, *rWrt.m_pFib );
582 static int lcl_CmpBeginEndChars( const OUString& rSWStr,
583 const sal_Unicode* pMSStr, int nMSStrByteLen )
585 nMSStrByteLen /= sizeof( sal_Unicode );
586 if( nMSStrByteLen > rSWStr.getLength() )
587 nMSStrByteLen = rSWStr.getLength()+1;
588 nMSStrByteLen *= sizeof( sal_Unicode );
590 return memcmp( rSWStr.getStr(), pMSStr, nMSStrByteLen );
594 Converts the OOo Asian Typography into a best fit match for Microsoft
595 Asian typography. This structure is actually dumped to disk within the
596 Dop Writer. Assumption is that rTypo is cleared to 0 on entry
598 void WW8Export::ExportDopTypography(WW8DopTypography &rTypo)
600 static const sal_Unicode aLangNotBegin[4][WW8DopTypography::nMaxFollowing]=
602 //Japanese Level 1
604 0x0021, 0x0025, 0x0029, 0x002c, 0x002e, 0x003a, 0x003b, 0x003f,
605 0x005d, 0x007d, 0x00a2, 0x00b0, 0x2019, 0x201d, 0x2030, 0x2032,
606 0x2033, 0x2103, 0x3001, 0x3002, 0x3005, 0x3009, 0x300b, 0x300d,
607 0x300f, 0x3011, 0x3015, 0x3041, 0x3043, 0x3045, 0x3047, 0x3049,
608 0x3063, 0x3083, 0x3085, 0x3087, 0x308e, 0x309b, 0x309c, 0x309d,
609 0x309e, 0x30a1, 0x30a3, 0x30a5, 0x30a7, 0x30a9, 0x30c3, 0x30e3,
610 0x30e5, 0x30e7, 0x30ee, 0x30f5, 0x30f6, 0x30fb, 0x30fc, 0x30fd,
611 0x30fe, 0xff01, 0xff05, 0xff09, 0xff0c, 0xff0e, 0xff1a, 0xff1b,
612 0xff1f, 0xff3d, 0xff5d, 0xff61, 0xff63, 0xff64, 0xff65, 0xff67,
613 0xff68, 0xff69, 0xff6a, 0xff6b, 0xff6c, 0xff6d, 0xff6e, 0xff6f,
614 0xff70, 0xff9e, 0xff9f, 0xffe0
616 //Simplified Chinese
618 0x0021, 0x0029, 0x002c, 0x002e, 0x003a, 0x003b, 0x003f, 0x005d,
619 0x007d, 0x00a8, 0x00b7, 0x02c7, 0x02c9, 0x2015, 0x2016, 0x2019,
620 0x201d, 0x2026, 0x2236, 0x3001, 0x3002, 0x3003, 0x3005, 0x3009,
621 0x300b, 0x300d, 0x300f, 0x3011, 0x3015, 0x3017, 0xff01, 0xff02,
622 0xff07, 0xff09, 0xff0c, 0xff0e, 0xff1a, 0xff1b, 0xff1f, 0xff3d,
623 0xff40, 0xff5c, 0xff5d, 0xff5e, 0xffe0
625 //Korean
627 0x0021, 0x0025, 0x0029, 0x002c, 0x002e, 0x003a, 0x003b, 0x003f,
628 0x005d, 0x007d, 0x00a2, 0x00b0, 0x2019, 0x201d, 0x2032, 0x2033,
629 0x2103, 0x3009, 0x300b, 0x300d, 0x300f, 0x3011, 0x3015, 0xff01,
630 0xff05, 0xff09, 0xff0c, 0xff0e, 0xff1a, 0xff1b, 0xff1f, 0xff3d,
631 0xff5d, 0xffe0
633 //Traditional Chinese
635 0x0021, 0x0029, 0x002c, 0x002e, 0x003a, 0x003b, 0x003f, 0x005d,
636 0x007d, 0x00a2, 0x00b7, 0x2013, 0x2014, 0x2019, 0x201d, 0x2022,
637 0x2025, 0x2026, 0x2027, 0x2032, 0x2574, 0x3001, 0x3002, 0x3009,
638 0x300b, 0x300d, 0x300f, 0x3011, 0x3015, 0x301e, 0xfe30, 0xfe31,
639 0xfe33, 0xfe34, 0xfe36, 0xfe38, 0xfe3a, 0xfe3c, 0xfe3e, 0xfe40,
640 0xfe42, 0xfe44, 0xfe4f, 0xfe50, 0xfe51, 0xfe52, 0xfe54, 0xfe55,
641 0xfe56, 0xfe57, 0xfe5a, 0xfe5c, 0xfe5e, 0xff01, 0xff09, 0xff0c,
642 0xff0e, 0xff1a, 0xff1b, 0xff1f, 0xff5c, 0xff5d, 0xff64
646 static const sal_Unicode aLangNotEnd[4][WW8DopTypography::nMaxLeading] =
648 //Japanese Level 1
650 0x0024, 0x0028, 0x005b, 0x005c, 0x007b, 0x00a3, 0x00a5, 0x2018,
651 0x201c, 0x3008, 0x300a, 0x300c, 0x300e, 0x3010, 0x3014, 0xff04,
652 0xff08, 0xff3b, 0xff5b, 0xff62, 0xffe1, 0xffe5
654 //Simplified Chinese
656 0x0028, 0x005b, 0x007b, 0x00b7, 0x2018, 0x201c, 0x3008, 0x300a,
657 0x300c, 0x300e, 0x3010, 0x3014, 0x3016, 0xff08, 0xff0e, 0xff3b,
658 0xff5b, 0xffe1, 0xffe5
660 //Korean
662 0x0028, 0x005b, 0x005c, 0x007b, 0x00a3, 0x00a5, 0x2018, 0x201c,
663 0x3008, 0x300a, 0x300c, 0x300e, 0x3010, 0x3014, 0xff04, 0xff08,
664 0xff3b, 0xff5b, 0xffe6
666 //Traditional Chinese
668 0x0028, 0x005b, 0x007b, 0x00a3, 0x00a5, 0x2018, 0x201c, 0x2035,
669 0x3008, 0x300a, 0x300c, 0x300e, 0x3010, 0x3014, 0x301d, 0xfe35,
670 0xfe37, 0xfe39, 0xfe3b, 0xfe3d, 0xfe3f, 0xfe41, 0xfe43, 0xfe59,
671 0xfe5b, 0xfe5d, 0xff08, 0xff5b
675 const i18n::ForbiddenCharacters *pForbidden = nullptr;
676 const i18n::ForbiddenCharacters *pUseMe = nullptr;
677 sal_uInt8 nUseReserved=0;
678 int nNoNeeded=0;
680 Now we have some minor difficult issues, to wit...
681 a. MicroSoft Office can only store one set of begin and end characters in
682 a given document, not one per language.
683 b. StarOffice has only a concept of one set of begin and end characters for
684 a given language, i.e. not the two levels of kinsoku in japanese
686 What is unknown as yet is if our default begin and end chars for
687 japanese, chinese tradition, chinese simplified and korean are different
688 in Word and Writer. I already suspect that they are different between
689 different version of word itself.
691 So what have come up with is to simply see if any of the four languages
692 in OOo have been changed away from OUR defaults, and if one has then
693 export that. If more than one has in the future we may hack in something
694 which examines our document properties to see which language is used the
695 most and choose that, for now we choose the first and throw an ASSERT
698 /*Our default Japanese Level is 2, this is a special MS hack to set this*/
699 rTypo.m_reserved2 = 1;
701 for (rTypo.m_reserved1=8;rTypo.m_reserved1>0;rTypo.m_reserved1-=2)
703 pForbidden = m_rDoc.getIDocumentSettingAccess().getForbiddenCharacters(rTypo.GetConvertedLang(),
704 false);
705 if (nullptr != pForbidden)
707 int nIdx = (rTypo.m_reserved1-2)/2;
708 if( lcl_CmpBeginEndChars( pForbidden->endLine,
709 aLangNotEnd[ nIdx ], sizeof(aLangNotEnd[ nIdx ]) ) ||
710 lcl_CmpBeginEndChars( pForbidden->beginLine,
711 aLangNotBegin[ nIdx ], sizeof(aLangNotBegin[ nIdx ]) ) )
713 //One exception for Japanese, if it matches a level 1 we
714 //can use one extra flag for that, rather than use a custom
715 if (rTypo.GetConvertedLang() == LANGUAGE_JAPANESE)
717 if (
718 !lcl_CmpBeginEndChars
720 pForbidden->endLine,
721 WW8DopTypography::JapanNotEndLevel1.getStr(),
722 WW8DopTypography::nMaxLeading * sizeof(sal_Unicode)
725 !lcl_CmpBeginEndChars
727 pForbidden->beginLine,
728 WW8DopTypography::JapanNotBeginLevel1.getStr(),
729 WW8DopTypography::nMaxFollowing * sizeof(sal_Unicode)
733 rTypo.m_reserved2 = 0;
734 continue;
738 if (!pUseMe)
740 pUseMe = pForbidden;
741 nUseReserved = rTypo.m_reserved1;
742 rTypo.m_iLevelOfKinsoku = 2;
744 nNoNeeded++;
749 OSL_ENSURE( nNoNeeded<=1, "Example of unexportable forbidden chars" );
750 rTypo.m_reserved1=nUseReserved;
751 if (rTypo.m_iLevelOfKinsoku && pUseMe)
753 rTypo.m_cchFollowingPunct = msword_cast<sal_Int16>
754 (pUseMe->beginLine.getLength());
755 if (rTypo.m_cchFollowingPunct > WW8DopTypography::nMaxFollowing - 1)
756 rTypo.m_cchFollowingPunct = WW8DopTypography::nMaxFollowing - 1;
758 rTypo.m_cchLeadingPunct = msword_cast<sal_Int16>
759 (pUseMe->endLine.getLength());
760 if (rTypo.m_cchLeadingPunct > WW8DopTypography::nMaxLeading - 1)
761 rTypo.m_cchLeadingPunct = WW8DopTypography::nMaxLeading -1;
763 memcpy(rTypo.m_rgxchFPunct,pUseMe->beginLine.getStr(),
764 (rTypo.m_cchFollowingPunct+1)*2);
766 memcpy(rTypo.m_rgxchLPunct,pUseMe->endLine.getStr(),
767 (rTypo.m_cchLeadingPunct+1)*2);
770 const IDocumentSettingAccess& rIDocumentSettingAccess = GetWriter().getIDocumentSettingAccess();
772 rTypo.m_fKerningPunct = sal_uInt16(rIDocumentSettingAccess.get(DocumentSettingId::KERN_ASIAN_PUNCTUATION));
773 rTypo.m_iJustification = sal_uInt16(m_rDoc.getIDocumentSettingAccess().getCharacterCompressionType());
776 // It can only be found something with this method, if it is used within
777 // WW8_SwAttrIter::OutAttr() and WW8Export::OutputItemSet()
778 const SfxPoolItem* MSWordExportBase::HasItem( sal_uInt16 nWhich ) const
780 const SfxPoolItem* pItem=nullptr;
781 if (m_pISet)
783 // if write an EditEngine text, then the WhichIds are greater than
784 // our own Ids. So the Id have to translate from our into the
785 // EditEngine Range
786 nWhich = sw::hack::GetSetWhichFromSwDocWhich(*m_pISet, m_rDoc, nWhich);
787 if (nWhich && SfxItemState::SET != m_pISet->GetItemState(nWhich, true, &pItem))
788 pItem = nullptr;
790 else if( m_pChpIter )
791 pItem = m_pChpIter->HasTextItem( nWhich );
792 else
794 OSL_ENSURE( false, "Where is my ItemSet / pChpIter ?" );
795 pItem = nullptr;
797 return pItem;
800 const SfxPoolItem& MSWordExportBase::GetItem(sal_uInt16 nWhich) const
802 assert((m_pISet || m_pChpIter) && "Where is my ItemSet / pChpIter ?");
803 if (m_pISet)
805 // if write an EditEngine text, then the WhichIds are greater than
806 // our own Ids. So the Id have to translate from our into the
807 // EditEngine Range
808 nWhich = sw::hack::GetSetWhichFromSwDocWhich(*m_pISet, m_rDoc, nWhich);
809 OSL_ENSURE(nWhich != 0, "All broken, Impossible");
810 return m_pISet->Get(nWhich);
812 return m_pChpIter->GetItem( nWhich );
815 WW8_WrPlc1::WW8_WrPlc1( sal_uInt16 nStructSz )
816 : m_pData( new sal_uInt8[ 16 * nStructSz ] ),
817 m_nDataLen(16 * nStructSz),
818 m_nStructSiz( nStructSz )
822 WW8_WrPlc1::~WW8_WrPlc1()
826 WW8_CP WW8_WrPlc1::Prev() const
828 bool b = !m_aPos.empty();
829 OSL_ENSURE(b,"Prev called on empty list");
830 return b ? m_aPos.back() : 0;
833 void WW8_WrPlc1::Append( WW8_CP nCp, const void* pNewData )
835 sal_uLong nInsPos = m_aPos.size() * m_nStructSiz;
836 m_aPos.push_back( nCp );
837 if( m_nDataLen < nInsPos + m_nStructSiz )
839 sal_uInt8* pNew = new sal_uInt8[ 2 * m_nDataLen ];
840 memcpy( pNew, m_pData.get(), m_nDataLen );
841 m_pData.reset(pNew);
842 m_nDataLen *= 2;
844 memcpy( m_pData.get() + nInsPos, pNewData, m_nStructSiz );
847 void WW8_WrPlc1::Finish( sal_uLong nLastCp, sal_uLong nSttCp )
849 if( !m_aPos.empty() )
851 m_aPos.push_back( nLastCp );
852 if( nSttCp )
853 for(WW8_CP & rCp : m_aPos)
854 rCp -= nSttCp;
858 void WW8_WrPlc1::Write( SvStream& rStrm )
860 decltype(m_aPos)::size_type i;
861 for( i = 0; i < m_aPos.size(); ++i )
862 SwWW8Writer::WriteLong( rStrm, m_aPos[i] );
863 if( i )
864 rStrm.WriteBytes(m_pData.get(), (i-1) * m_nStructSiz);
867 // Class WW8_WrPlcField for fields
869 void WW8_WrPlcField::Write( WW8Export& rWrt )
871 if( WW8_WrPlc1::Count() <= 1 )
872 return;
874 WW8_FC *pfc;
875 sal_Int32 *plc;
876 switch (m_nTextTyp)
878 case TXT_MAINTEXT:
879 pfc = &rWrt.m_pFib->m_fcPlcffldMom;
880 plc = &rWrt.m_pFib->m_lcbPlcffldMom;
881 break;
882 case TXT_HDFT:
883 pfc = &rWrt.m_pFib->m_fcPlcffldHdr;
884 plc = &rWrt.m_pFib->m_lcbPlcffldHdr;
885 break;
887 case TXT_FTN:
888 pfc = &rWrt.m_pFib->m_fcPlcffldFootnote;
889 plc = &rWrt.m_pFib->m_lcbPlcffldFootnote;
890 break;
892 case TXT_EDN:
893 pfc = &rWrt.m_pFib->m_fcPlcffldEdn;
894 plc = &rWrt.m_pFib->m_lcbPlcffldEdn;
895 break;
897 case TXT_ATN:
898 pfc = &rWrt.m_pFib->m_fcPlcffldAtn;
899 plc = &rWrt.m_pFib->m_lcbPlcffldAtn;
900 break;
902 case TXT_TXTBOX:
903 pfc = &rWrt.m_pFib->m_fcPlcffldTxbx;
904 plc = &rWrt.m_pFib->m_lcbPlcffldTxbx;
905 break;
907 case TXT_HFTXTBOX:
908 pfc = &rWrt.m_pFib->m_fcPlcffldHdrTxbx;
909 plc = &rWrt.m_pFib->m_lcbPlcffldHdrTxbx;
910 break;
912 default:
913 pfc = plc = nullptr;
914 break;
917 if( pfc && plc )
919 sal_uInt64 nFcStart = rWrt.m_pTableStrm->Tell();
920 WW8_WrPlc1::Write( *rWrt.m_pTableStrm );
921 *pfc = nFcStart;
922 *plc = rWrt.m_pTableStrm->Tell() - nFcStart;
926 void WW8_WrMagicTable::Write( WW8Export& rWrt )
928 if( WW8_WrPlc1::Count() <= 1 )
929 return;
930 sal_uInt64 nFcStart = rWrt.m_pTableStrm->Tell();
931 WW8_WrPlc1::Write( *rWrt.m_pTableStrm );
932 rWrt.m_pFib->m_fcPlcfTch = nFcStart;
933 rWrt.m_pFib->m_lcbPlcfTch = rWrt.m_pTableStrm->Tell() - nFcStart;
936 void WW8_WrMagicTable::Append( WW8_CP nCp, sal_uLong nData)
939 Tell the undocumented table hack that everything between here and the last
940 table position is non-table text, don't do it if the previous position is
941 the same as this one, as that would be a region of 0 length
943 if ((!Count()) || (Prev() != nCp))
945 SVBT32 nLittle;
946 UInt32ToSVBT32(nData,nLittle);
947 WW8_WrPlc1::Append(nCp, nLittle);
951 void SwWW8Writer::FillCount( SvStream& rStrm, sal_uLong nCount )
953 static const sal_uInt32 aNulls[16] =
955 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 // 64 Byte
958 while (nCount > 64)
960 rStrm.WriteBytes(aNulls, 64); // in steps of 64-Byte
961 nCount -= 64;
963 rStrm.WriteBytes(aNulls, nCount); // write the rest (0 .. 64 Bytes)
966 sal_uLong SwWW8Writer::FillUntil( SvStream& rStrm, sal_uLong nEndPos )
968 sal_uInt64 nCurPos = rStrm.Tell();
969 if( !nEndPos ) // nEndPos == 0 -> next Page
970 nEndPos = (nCurPos + 0x1ff) & ~0x1ffUL;
972 if( nEndPos > nCurPos )
973 SwWW8Writer::FillCount( rStrm, nEndPos - nCurPos );
974 #if OSL_DEBUG_LEVEL > 0
975 else
976 OSL_ENSURE( nEndPos == nCurPos, "Wrong FillUntil()" );
977 #endif
978 return rStrm.Tell();
981 WW8_WrPlcPn::WW8_WrPlcPn(WW8Export& rWr, ePLCFT ePl, WW8_FC nStartFc)
982 : m_rWrt(rWr)
983 , m_nFkpStartPage(0)
984 , m_ePlc(ePl)
986 m_Fkps.push_back(std::make_unique<WW8_WrFkp>(m_ePlc, nStartFc));
989 WW8_WrPlcPn::~WW8_WrPlcPn()
993 sal_uInt8 *WW8_WrPlcPn::CopyLastSprms(sal_uInt8 &rLen)
995 WW8_WrFkp& rF = *m_Fkps.back();
996 return rF.CopyLastSprms(rLen);
999 void WW8_WrPlcPn::AppendFkpEntry(WW8_FC nEndFc,short nVarLen,const sal_uInt8* pSprms)
1001 WW8_WrFkp* pF = m_Fkps.back().get();
1003 // big sprm? build the sprmPHugePapx
1004 sal_uInt8* pNewSprms = const_cast<sal_uInt8*>(pSprms);
1005 sal_uInt8 aHugePapx[ 8 ];
1006 if (PAP == m_ePlc && 488 <= nVarLen)
1008 sal_uInt8* p = aHugePapx;
1009 *p++ = *pSprms++; // set style Id
1010 *p++ = *pSprms++;
1011 nVarLen -= 2;
1013 sal_uInt64 nDataPos = m_rWrt.m_pDataStrm->Tell();
1014 SwWW8Writer::WriteShort( *m_rWrt.m_pDataStrm, nVarLen );
1015 m_rWrt.m_pDataStrm->WriteBytes(pSprms, nVarLen);
1017 Set_UInt16( p, 0x6646 ); // set SprmCode
1018 Set_UInt32( p, nDataPos ); // set startpos (FC) in the datastream
1019 nVarLen = static_cast< short >(p - aHugePapx);
1020 pSprms = pNewSprms = aHugePapx;
1022 // if append at the same FC-EndPos and there are sprms, then get the old
1023 // sprms and erase it; they will append now with the new sprms
1024 else if( nVarLen && pF->IsEqualPos( nEndFc ))
1025 pF->MergeToNew( nVarLen, pNewSprms );
1026 // has the prev EndFC an empty sprm and the current is empty too, then
1027 // expand only the old EndFc to the new EndFc
1028 else if( !nVarLen && pF->IsEmptySprm() )
1030 pF->SetNewEnd( nEndFc );
1031 return ;
1034 bool bOk = pF->Append(nEndFc, nVarLen, pNewSprms);
1035 if( !bOk )
1037 pF->Combine();
1038 pF = new WW8_WrFkp(m_ePlc, pF->GetEndFc()); // Start new Fkp == end of old Fkp
1040 m_Fkps.push_back(std::unique_ptr<WW8_WrFkp>(pF));
1041 if( !pF->Append( nEndFc, nVarLen, pNewSprms ) )
1043 OSL_ENSURE( false, "Unable to insert Sprm" );
1046 if( pNewSprms != pSprms ) //Merge to new has created a new block
1047 delete[] pNewSprms;
1050 void WW8_WrPlcPn::WriteFkps()
1052 m_nFkpStartPage = o3tl::narrowing<sal_uInt16>( SwWW8Writer::FillUntil( m_rWrt.Strm() ) >> 9 );
1054 for(const std::unique_ptr<WW8_WrFkp> & rp : m_Fkps)
1056 rp->Write( m_rWrt.Strm(), *m_rWrt.m_pGrf );
1059 if( CHP == m_ePlc )
1061 m_rWrt.m_pFib->m_pnChpFirst = m_nFkpStartPage;
1062 m_rWrt.m_pFib->m_cpnBteChp = m_Fkps.size();
1064 else
1066 m_rWrt.m_pFib->m_pnPapFirst = m_nFkpStartPage;
1067 m_rWrt.m_pFib->m_cpnBtePap = m_Fkps.size();
1071 void WW8_WrPlcPn::WritePlc()
1073 sal_uInt64 nFcStart = m_rWrt.m_pTableStrm->Tell();
1074 decltype(m_Fkps)::size_type i;
1076 for (i = 0; i < m_Fkps.size(); ++i)
1078 SwWW8Writer::WriteLong( *m_rWrt.m_pTableStrm,
1079 m_Fkps[ i ]->GetStartFc() );
1082 WW8_FC nEndFc = i > 0 ? m_Fkps[i - 1]->GetEndFc() : 0;
1083 SwWW8Writer::WriteLong(*m_rWrt.m_pTableStrm, nEndFc);
1085 // for every FKP output the page
1086 for (i = 0; i < m_Fkps.size(); ++i)
1088 SwWW8Writer::WriteLong( *m_rWrt.m_pTableStrm, i + m_nFkpStartPage );
1091 if( CHP == m_ePlc )
1093 m_rWrt.m_pFib->m_fcPlcfbteChpx = nFcStart;
1094 m_rWrt.m_pFib->m_lcbPlcfbteChpx = m_rWrt.m_pTableStrm->Tell() - nFcStart;
1096 else
1098 m_rWrt.m_pFib->m_fcPlcfbtePapx = nFcStart;
1099 m_rWrt.m_pFib->m_lcbPlcfbtePapx = m_rWrt.m_pTableStrm->Tell() - nFcStart;
1103 WW8_WrFkp::WW8_WrFkp(ePLCFT ePl, WW8_FC nStartFc)
1104 : m_ePlc(ePl), m_nStartGrp(511), m_nOldStartGrp(511),
1105 m_nItemSize( ( CHP == ePl ) ? 1 : 13 ),
1106 m_nIMax(0), m_nOldVarLen(0), m_bCombined(false)
1108 m_pFkp = reinterpret_cast<sal_uInt8*>(new sal_Int32[128]); // 512 Byte
1109 m_pOfs = reinterpret_cast<sal_uInt8*>(new sal_Int32[128]); // 512 Byte
1110 memset( m_pFkp, 0, 4 * 128 );
1111 memset( m_pOfs, 0, 4 * 128 );
1112 reinterpret_cast<sal_Int32*>(m_pFkp)[0] = nStartFc; // 0th entry FC at nStartFc
1115 WW8_WrFkp::~WW8_WrFkp()
1117 delete[] reinterpret_cast<sal_Int32 *>(m_pFkp);
1118 delete[] reinterpret_cast<sal_Int32 *>(m_pOfs);
1121 sal_uInt8 WW8_WrFkp::SearchSameSprm( sal_uInt16 nVarLen, const sal_uInt8* pSprms )
1123 if( 3 < nVarLen )
1125 // if the sprms contained picture-references then never equal!
1126 for( sal_uInt8 n = static_cast< sal_uInt8 >(nVarLen - 1); 3 < n; --n )
1127 if( pSprms[ n ] == GRF_MAGIC_3 &&
1128 pSprms[ n-1 ] == GRF_MAGIC_2 &&
1129 pSprms[ n-2 ] == GRF_MAGIC_1 )
1130 return 0;
1133 short i;
1134 for( i = 0; i < m_nIMax; i++ )
1136 sal_uInt8 nStart = m_pOfs[i * m_nItemSize];
1137 if( nStart )
1138 { // has Sprms
1139 const sal_uInt8* p = m_pFkp + ( o3tl::narrowing<sal_uInt16>(nStart) << 1 );
1140 if( ( CHP == m_ePlc
1141 ? (*p++ == nVarLen)
1142 : ((o3tl::narrowing<sal_uInt16>(*p++) << 1 ) == (( nVarLen+1) & 0xfffe)) )
1143 && !memcmp( p, pSprms, nVarLen ) )
1144 return nStart; // found it
1147 return 0; // didn't found it
1150 sal_uInt8 *WW8_WrFkp::CopyLastSprms(sal_uInt8 &rLen)
1152 rLen=0;
1153 sal_uInt8 *pStart=nullptr,*pRet=nullptr;
1155 if (!m_bCombined)
1156 pStart = m_pOfs;
1157 else
1158 pStart = m_pFkp + ( m_nIMax + 1 ) * 4;
1160 sal_uInt8 nStart = *(pStart + (m_nIMax-1) * m_nItemSize);
1162 const sal_uInt8* p = m_pFkp + ( o3tl::narrowing<sal_uInt16>(nStart) << 1 );
1164 if (!*p)
1165 p++;
1167 if (*p)
1169 rLen = *p++;
1170 if (PAP == m_ePlc)
1171 rLen *= 2;
1172 pRet = new sal_uInt8[rLen];
1173 memcpy(pRet,p,rLen);
1175 return pRet;
1178 bool WW8_WrFkp::Append( WW8_FC nEndFc, sal_uInt16 nVarLen, const sal_uInt8* pSprms )
1180 assert((!nVarLen || pSprms) && "Item pointer missing");
1182 OSL_ENSURE( nVarLen < ( ( m_ePlc == PAP ) ? 497U : 502U ), "Sprms too long !" );
1184 if( m_bCombined )
1186 OSL_ENSURE( false, "Fkp::Append: Fkp is already combined" );
1187 return false;
1189 sal_Int32 n = reinterpret_cast<sal_Int32*>(m_pFkp)[m_nIMax]; // last entry
1190 if( nEndFc <= n )
1192 OSL_ENSURE( nEndFc >= n, "+Fkp: FC backwards" );
1193 OSL_ENSURE( !nVarLen || !pSprms || nEndFc != n,
1194 "+Fkp: used same FC multiple times" );
1195 // same FC without Sprm is ignored without grumbling
1197 return true; // ignore (do not create a new Fkp)
1200 sal_uInt8 nOldP = nVarLen ? SearchSameSprm( nVarLen, pSprms ) : 0;
1201 // Combine equal entries
1202 short nOffset=0, nPos = m_nStartGrp;
1203 if (nVarLen && !nOldP)
1205 nPos = PAP == m_ePlc
1206 ? ( 13 == m_nItemSize // HACK: PAP and bWrtWW8 !!
1207 ? (m_nStartGrp & 0xFFFE ) - nVarLen - 1
1208 : (m_nStartGrp - (((nVarLen + 1) & 0xFFFE)+1)) & 0xFFFE )
1209 : ((m_nStartGrp - nVarLen - 1) & 0xFFFE);
1210 if( nPos < 0 )
1211 return false; // doesn't fit at all
1212 nOffset = nPos; // save offset (can also be uneven!)
1213 nPos &= 0xFFFE; // Pos for Sprms ( gerade Pos )
1216 if( o3tl::make_unsigned(nPos) <= ( m_nIMax + 2U ) * 4U + ( m_nIMax + 1U ) * m_nItemSize )
1217 // does it fits after the CPs and offsets?
1218 return false; // no
1220 reinterpret_cast<sal_Int32*>(m_pFkp)[m_nIMax + 1] = nEndFc; // insert FC
1222 m_nOldVarLen = static_cast<sal_uInt8>(nVarLen);
1223 if( nVarLen && !nOldP )
1224 { // insert it for real
1225 m_nOldStartGrp = m_nStartGrp;
1227 m_nStartGrp = nPos;
1228 m_pOfs[m_nIMax * m_nItemSize] = static_cast<sal_uInt8>( m_nStartGrp >> 1 );
1229 // insert (start-of-data >> 1)
1230 sal_uInt8 nCnt = static_cast< sal_uInt8 >(CHP == m_ePlc
1231 ? ( nVarLen < 256 ) ? static_cast<sal_uInt8>(nVarLen) : 255
1232 : ( ( nVarLen + 1 ) >> 1 ));
1234 m_pFkp[ nOffset ] = nCnt; // Enter data length
1235 memcpy( m_pFkp + nOffset + 1, pSprms, nVarLen ); // store Sprms
1237 else
1239 // do not enter for real ( no Sprms or recurrence )
1240 // start-of-data 0 ( no data ) or recurrence
1241 m_pOfs[m_nIMax * m_nItemSize] = nOldP;
1243 m_nIMax++;
1244 return true;
1247 void WW8_WrFkp::Combine()
1249 if( m_bCombined )
1250 return;
1251 if( m_nIMax )
1252 memcpy( m_pFkp + ( m_nIMax + 1 ) * 4, m_pOfs, m_nIMax * m_nItemSize );
1253 delete[] m_pOfs;
1254 m_pOfs = nullptr;
1255 m_pFkp[511] = m_nIMax;
1256 m_bCombined = true;
1258 #if defined OSL_BIGENDIAN // only the FCs will be rotated here
1259 sal_uInt16 i; // the Sprms must be rotated elsewhere
1261 sal_uInt32* p;
1262 for( i = 0, p = (sal_uInt32*)m_pFkp; i <= m_nIMax; i++, p++ )
1263 *p = OSL_SWAPDWORD( *p );
1264 #endif // ifdef OSL_BIGENDIAN
1267 void WW8_WrFkp::Write( SvStream& rStrm, SwWW8WrGrf& rGrf )
1269 Combine(); // If not already combined
1271 sal_uInt8* p; // search magic for nPicLocFc
1272 sal_uInt8* pEnd = m_pFkp + m_nStartGrp;
1273 for( p = m_pFkp + 511 - 4; p >= pEnd; p-- )
1275 if( *p != GRF_MAGIC_1 ) // search for signature 0x12 0x34 0x56 0xXX
1276 continue;
1277 if( *(p+1) != GRF_MAGIC_2 )
1278 continue;
1279 if( *(p+2) != GRF_MAGIC_3 )
1280 continue;
1282 SVBT32 nPos; // signature found
1283 UInt32ToSVBT32( rGrf.GetFPos(), nPos ); // FilePos the graphics
1284 memcpy( p, nPos, 4 ); // patch FilePos over the signature
1286 rStrm.WriteBytes(m_pFkp, 512);
1289 void WW8_WrFkp::MergeToNew( short& rVarLen, sal_uInt8 *& rpNewSprms )
1291 sal_uInt8 nStart = m_pOfs[ (m_nIMax-1) * m_nItemSize ];
1292 if( !nStart )
1293 return;
1295 // has Sprms
1296 sal_uInt8* p = m_pFkp + ( o3tl::narrowing<sal_uInt16>(nStart) << 1 );
1298 // old and new equal? Then copy only one into the new sprms
1299 if( m_nOldVarLen == rVarLen && !memcmp( p+1, rpNewSprms, m_nOldVarLen ))
1301 sal_uInt8* pNew = new sal_uInt8[ m_nOldVarLen ];
1302 memcpy( pNew, p+1, m_nOldVarLen );
1303 rpNewSprms = pNew;
1305 else
1307 sal_uInt8* pNew = new sal_uInt8[ m_nOldVarLen + rVarLen ];
1308 memcpy( pNew, p+1, m_nOldVarLen );
1309 memcpy( pNew + m_nOldVarLen, rpNewSprms, rVarLen );
1311 rpNewSprms = pNew;
1312 rVarLen = rVarLen + m_nOldVarLen;
1314 --m_nIMax;
1315 // if this Sprms don't used from others, remove it
1316 bool bFnd = false;
1317 for (sal_uInt16 n = 0; n < m_nIMax; ++n)
1319 if (nStart == m_pOfs[n * m_nItemSize])
1321 bFnd = true;
1322 break;
1325 if (!bFnd)
1327 m_nStartGrp = m_nOldStartGrp;
1328 memset( p, 0, m_nOldVarLen+1 );
1332 WW8_FC WW8_WrFkp::GetStartFc() const
1334 // when bCombined, then the array beginning with pFkp is already byte-swapped
1335 // to LittleEndian, so to extract the start and end positions they must
1336 // be swapped back.
1337 if( m_bCombined )
1338 return SVBT32ToUInt32( m_pFkp ); // 0. Element
1339 return reinterpret_cast<sal_Int32*>(m_pFkp)[0];
1342 WW8_FC WW8_WrFkp::GetEndFc() const
1344 if( m_bCombined )
1345 return SVBT32ToUInt32( &(m_pFkp[m_nIMax*4]) ); // nIMax-th SVBT32-Element
1346 return reinterpret_cast<sal_Int32*>(m_pFkp)[m_nIMax];
1349 // Method for managing the piece table
1350 WW8_WrPct::WW8_WrPct(WW8_FC nfcMin)
1351 : m_nOldFc(nfcMin)
1353 AppendPc(m_nOldFc);
1356 WW8_WrPct::~WW8_WrPct()
1360 // Fill the piece and create a new one
1361 void WW8_WrPct::AppendPc(WW8_FC nStartFc)
1363 WW8_CP nStartCp = nStartFc - m_nOldFc; // subtract the beginning of the text
1364 if ( !nStartCp && !m_Pcts.empty())
1366 OSL_ENSURE(1 == m_Pcts.size(), "empty Piece!");
1367 m_Pcts.pop_back();
1370 m_nOldFc = nStartFc; // remember StartFc as old
1372 nStartCp >>= 1; // for Unicode: number of characters / 2
1374 if (!m_Pcts.empty())
1376 nStartCp += m_Pcts.back()->GetStartCp();
1379 m_Pcts.push_back(std::make_unique<WW8_WrPc>(nStartFc, nStartCp));
1382 void WW8_WrPct::WritePc( WW8Export& rWrt )
1384 sal_uInt64 nPctStart;
1385 sal_uLong nOldPos, nEndPos;
1387 nPctStart = rWrt.m_pTableStrm->Tell(); // Start piece table
1388 rWrt.m_pTableStrm->WriteChar( char(0x02) ); // Status byte PCT
1389 nOldPos = nPctStart + 1; // remember Position
1390 SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, 0 ); // then the length
1392 for (auto const& it : m_Pcts) // ranges
1394 SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, it->GetStartCp() );
1397 // calculate the last Pos
1398 sal_uLong nStartCp = rWrt.m_pFib->m_fcMac - m_nOldFc;
1399 nStartCp >>= 1; // For Unicode: number of characters / 2
1400 nStartCp += m_Pcts.back()->GetStartCp();
1401 SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, nStartCp );
1403 // piece references
1404 for (auto const& it : m_Pcts)
1406 SwWW8Writer::WriteShort(*rWrt.m_pTableStrm, it->GetStatus());
1407 SwWW8Writer::WriteLong(*rWrt.m_pTableStrm, it->GetStartFc());
1408 SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0); // PRM=0
1411 // entries in the FIB
1412 rWrt.m_pFib->m_fcClx = nPctStart;
1413 nEndPos = rWrt.m_pTableStrm->Tell();
1414 rWrt.m_pFib->m_lcbClx = nEndPos - nPctStart;
1416 // and register the length as well
1417 SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, nOldPos,
1418 nEndPos - nPctStart-5 );
1422 void WW8_WrPct::SetParaBreak()
1424 OSL_ENSURE( !m_Pcts.empty(), "SetParaBreak : m_Pcts.empty()" );
1425 m_Pcts.back()->SetStatus();
1428 WW8_CP WW8_WrPct::Fc2Cp( sal_uLong nFc ) const
1430 OSL_ENSURE( nFc >= o3tl::make_unsigned(m_nOldFc), "FilePos lies in front of last piece" );
1431 OSL_ENSURE( ! m_Pcts.empty(), "Fc2Cp no piece available" );
1433 nFc -= m_nOldFc;
1434 nFc /= 2; // Unicode
1435 return nFc + m_Pcts.back()->GetStartCp();
1438 void WW8Export::AppendBookmarks( const SwTextNode& rNd, sal_Int32 nCurrentPos, sal_Int32 nLen, const SwRedlineData* /*pRedlineData*/ )
1440 std::vector< const ::sw::mark::MarkBase* > aArr;
1441 sal_Int32 nContent;
1442 const sal_Int32 nCurrentEnd = nCurrentPos + nLen;
1443 if( !GetWriter().GetBookmarks( rNd, nCurrentPos, nCurrentEnd, aArr ))
1444 return;
1446 SwNodeOffset nNd = rNd.GetIndex();
1447 sal_uLong nSttCP = Fc2Cp( Strm().Tell() );
1448 for(const ::sw::mark::MarkBase* p : aArr)
1450 const ::sw::mark::MarkBase& rBkmk = *p;
1451 if(dynamic_cast< const ::sw::mark::Fieldmark *>(&rBkmk))
1452 continue;
1454 const SwPosition* pPos = &rBkmk.GetMarkPos();
1455 const SwPosition* pOPos = nullptr;
1456 if(rBkmk.IsExpanded())
1457 pOPos = &rBkmk.GetOtherMarkPos();
1458 if( pOPos && pOPos->GetNode() == pPos->GetNode() &&
1459 pOPos->GetContentIndex() < pPos->GetContentIndex() )
1461 pPos = pOPos;
1462 pOPos = &rBkmk.GetMarkPos();
1465 if( !pOPos || ( nNd == pPos->GetNodeIndex() &&
1466 ( nContent = pPos->GetContentIndex() ) >= nCurrentPos &&
1467 nContent < nCurrentEnd ) )
1469 sal_uLong nCp = nSttCP + pPos->GetContentIndex() - nCurrentPos;
1470 m_pBkmks->Append(nCp, BookmarkToWord(rBkmk.GetName()));
1472 if( pOPos && nNd == pOPos->GetNodeIndex() &&
1473 ( nContent = pOPos->GetContentIndex() ) >= nCurrentPos &&
1474 nContent < nCurrentEnd )
1476 sal_uLong nCp = nSttCP + pOPos->GetContentIndex() - nCurrentPos;
1477 m_pBkmks->Append(nCp, BookmarkToWord(rBkmk.GetName()));
1482 void WW8Export::AppendAnnotationMarks(const SwWW8AttrIter& rAttrs, sal_Int32 nCurrentPos, sal_Int32 nLen)
1484 IMarkVector aMarks;
1485 if (GetAnnotationMarks(rAttrs, nCurrentPos, nCurrentPos + nLen, aMarks))
1487 for (const sw::mark::MarkBase* pMark : aMarks)
1489 const sal_Int32 nStart = pMark->GetMarkStart().GetContentIndex();
1490 if (nStart == nCurrentPos)
1492 m_pAtn->AddRangeStartPosition(pMark->GetName(), Fc2Cp(Strm().Tell()),
1493 !rAttrs.HasFlysAt(nCurrentPos));
1499 void WW8Export::AppendSmartTags(SwTextNode& rTextNode)
1501 std::map<OUString, OUString> aStatements = SwRDFHelper::getTextNodeStatements(u"urn:bails"_ustr, rTextNode);
1502 if (!aStatements.empty())
1504 WW8_CP nCP = Fc2Cp(Strm().Tell());
1505 m_pFactoids->Append(nCP, nCP, aStatements);
1509 void WW8Export::MoveFieldMarks(WW8_CP nFrom, WW8_CP nTo)
1511 m_pBkmks->MoveFieldMarks(nFrom, nTo);
1514 void WW8Export::AppendBookmark( const OUString& rName )
1516 sal_uInt64 nSttCP = Fc2Cp( Strm().Tell() );
1517 m_pBkmks->Append( nSttCP, rName );
1520 void WW8Export::AppendBookmarkEndWithCorrection( const OUString& rName )
1522 sal_uInt64 nEndCP = Fc2Cp( Strm().Tell() );
1523 m_pBkmks->Append( nEndCP - 1, rName );
1526 std::unique_ptr<SvxBrushItem> MSWordExportBase::getBackground()
1528 const SwFrameFormat &rFormat = m_rDoc.GetPageDesc(0).GetMaster();
1529 std::unique_ptr<SvxBrushItem> aBrush = std::make_unique<SvxBrushItem>(RES_BACKGROUND);
1530 SfxItemState eState = rFormat.GetBackgroundState(aBrush);
1532 if (SfxItemState::SET == eState)
1534 // The 'color' is set for the first page style - take it and use it as the background color of the entire DOCX
1535 if (aBrush->GetColor() != COL_AUTO || aBrush->GetGraphicObject())
1536 return aBrush;
1538 return nullptr;
1541 // #i120928 collect all the graphics of bullets applied to paragraphs
1542 int MSWordExportBase::CollectGrfsOfBullets()
1544 m_vecBulletPic.clear();
1546 size_t nCountRule = m_rDoc.GetNumRuleTable().size();
1547 for (size_t n = 0; n < nCountRule; ++n)
1549 const SwNumRule &rRule = *( m_rDoc.GetNumRuleTable().at(n) );
1550 sal_uInt16 nLevels = rRule.IsContinusNum() ? 1 : 9;
1551 for (sal_uInt16 nLvl = 0; nLvl < nLevels; ++nLvl)
1553 const SwNumFormat &rFormat = rRule.Get(nLvl);
1554 if (SVX_NUM_BITMAP != rFormat.GetNumberingType())
1556 continue;
1558 const Graphic *pGraf = rFormat.GetBrush()? rFormat.GetBrush()->GetGraphic():nullptr;
1559 if ( pGraf )
1561 bool bHas = false;
1562 for (const Graphic* p : m_vecBulletPic)
1564 if (p->GetChecksum() == pGraf->GetChecksum())
1566 bHas = true;
1567 break;
1570 if (!bHas)
1572 Size aSize(pGraf->GetPrefSize());
1573 if (0 != aSize.Height() && 0 != aSize.Width())
1574 m_vecBulletPic.push_back(pGraf);
1580 return m_vecBulletPic.size();
1583 void MSWordExportBase::BulletDefinitions()
1585 for (size_t i = 0; i < m_vecBulletPic.size(); ++i)
1587 const MapMode aMapMode(MapUnit::MapTwip);
1588 const Graphic& rGraphic = *m_vecBulletPic[i];
1589 Size aSize(rGraphic.GetPrefSize());
1590 if (MapUnit::MapPixel == rGraphic.GetPrefMapMode().GetMapUnit())
1591 aSize = Application::GetDefaultDevice()->PixelToLogic(aSize, aMapMode);
1592 else
1593 aSize = OutputDevice::LogicToLogic(aSize,rGraphic.GetPrefMapMode(), aMapMode);
1595 if (0 != aSize.Height() && 0 != aSize.Width())
1596 AttrOutput().BulletDefinition(i, rGraphic, aSize);
1600 //Export Graphic of Bullets
1601 void WW8Export::ExportGrfBullet(const SwTextNode& rNd)
1603 int nCount = CollectGrfsOfBullets();
1604 if (nCount > 0)
1606 SwPosition aPos(rNd);
1607 OUString aPicBullets(u"_PictureBullets"_ustr);
1608 AppendBookmark(aPicBullets);
1609 for (int i = 0; i < nCount; i++)
1611 ww8::Frame aFrame(*(m_vecBulletPic[i]), aPos);
1612 OutGrfBullets(aFrame);
1614 AppendBookmark(aPicBullets);
1618 static sal_uInt8 nAttrMagicIdx = 0;
1619 void WW8Export::OutGrfBullets(const ww8::Frame & rFrame)
1621 if ( !m_pGrf || !m_pChpPlc || !m_pO )
1622 return;
1624 m_pGrf->Insert(rFrame);
1625 m_pChpPlc->AppendFkpEntry( Strm().Tell(), m_pO->size(), m_pO->data() );
1626 m_pO->clear();
1627 // if links...
1628 WriteChar( char(1) );
1630 sal_uInt8 aArr[ 22 ];
1631 sal_uInt8* pArr = aArr;
1633 // sprmCFSpec
1634 Set_UInt16( pArr, 0x855 );
1635 Set_UInt8( pArr, 1 );
1637 Set_UInt16( pArr, 0x083c );
1638 Set_UInt8( pArr, 0x81 );
1640 // sprmCPicLocation
1641 Set_UInt16( pArr, 0x6a03 );
1642 Set_UInt32( pArr, GRF_MAGIC_321 );
1644 //extern nAttrMagicIdx;
1645 --pArr;
1646 Set_UInt8( pArr, nAttrMagicIdx++ );
1647 m_pChpPlc->AppendFkpEntry( Strm().Tell(), static_cast< short >(pArr - aArr), aArr );
1650 int MSWordExportBase::GetGrfIndex(const SvxBrushItem& rBrush)
1652 int nIndex = -1;
1654 const Graphic* pGraphic = rBrush.GetGraphic();
1655 if (pGraphic)
1657 for (size_t i = 0; i < m_vecBulletPic.size(); ++i)
1659 if (m_vecBulletPic[i]->GetChecksum() == pGraphic->GetChecksum())
1661 nIndex = i;
1662 break;
1667 return nIndex;
1670 void WW8_WrtRedlineAuthor::Write( Writer& rWrt )
1672 WW8Export & rWW8Wrt = *(static_cast<SwWW8Writer&>(rWrt).m_pExport);
1673 rWW8Wrt.WriteAsStringTable(maAuthors, rWW8Wrt.m_pFib->m_fcSttbfRMark,
1674 rWW8Wrt.m_pFib->m_lcbSttbfRMark);
1677 sal_uInt16 WW8Export::AddRedlineAuthor( std::size_t nId )
1679 if( !m_pRedlAuthors )
1681 m_pRedlAuthors.reset(new WW8_WrtRedlineAuthor);
1682 m_pRedlAuthors->AddName(u"Unknown"_ustr);
1684 bool bRemovePersonalInfo
1685 = SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo)
1686 && !SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::DocWarnKeepRedlineInfo);
1687 OUString sName(SwModule::get()->GetRedlineAuthor(nId));
1688 return m_pRedlAuthors->AddName(
1689 bRemovePersonalInfo ? "Author" + OUString::number(mpAuthorIDs->GetInfoID(sName)) : sName);
1692 void WW8Export::WriteAsStringTable(const std::vector<OUString>& rStrings,
1693 sal_Int32& rfcSttbf, sal_Int32& rlcbSttbf)
1695 sal_uInt16 n, nCount = static_cast< sal_uInt16 >(rStrings.size());
1696 if( !nCount )
1697 return;
1699 // we have some Redlines found in the document -> the
1700 // Author Name Stringtable
1701 SvStream& rStrm = *m_pTableStrm;
1702 rfcSttbf = rStrm.Tell();
1703 SwWW8Writer::WriteShort( rStrm, -1 );
1704 SwWW8Writer::WriteLong( rStrm, nCount );
1705 for( n = 0; n < nCount; ++n )
1707 const OUString& rNm = rStrings[n];
1708 SwWW8Writer::WriteShort( rStrm, rNm.getLength() );
1709 SwWW8Writer::WriteString16(rStrm, rNm, false);
1711 rlcbSttbf = rStrm.Tell() - rfcSttbf;
1714 // WriteShort() sets at FilePos nPos the value nVal and seeks to the old
1715 // FilePos. Used to insert lengths after the fact.
1716 void SwWW8Writer::WriteShort( SvStream& rStrm, sal_uLong nPos, sal_Int16 nVal )
1718 sal_uInt64 nOldPos = rStrm.Tell(); // remember Pos
1719 rStrm.Seek( nPos );
1720 SwWW8Writer::WriteShort( rStrm, nVal );
1721 rStrm.Seek( nOldPos );
1724 void SwWW8Writer::WriteLong( SvStream& rStrm, sal_uLong nPos, sal_Int32 nVal )
1726 sal_uInt64 nOldPos = rStrm.Tell(); // remember Pos
1727 rStrm.Seek( nPos );
1728 SwWW8Writer::WriteLong( rStrm, nVal );
1729 rStrm.Seek( nOldPos );
1732 void SwWW8Writer::InsUInt16(ww::bytes &rO, sal_uInt16 n)
1734 SVBT16 nL;
1735 ShortToSVBT16( n, nL );
1736 rO.push_back(nL[0]);
1737 rO.push_back(nL[1]);
1740 void SwWW8Writer::InsUInt32(ww::bytes &rO, sal_uInt32 n)
1742 SVBT32 nL;
1743 UInt32ToSVBT32( n, nL );
1744 rO.push_back(nL[0]);
1745 rO.push_back(nL[1]);
1746 rO.push_back(nL[2]);
1747 rO.push_back(nL[3]);
1750 void SwWW8Writer::InsAsString16(ww::bytes &rO, const OUString& rStr)
1752 const sal_Unicode* pStr = rStr.getStr();
1753 for (sal_Int32 n = 0, nLen = rStr.getLength(); n < nLen; ++n, ++pStr)
1754 SwWW8Writer::InsUInt16( rO, *pStr );
1757 void SwWW8Writer::InsAsString8(ww::bytes &rO, std::u16string_view rStr,
1758 rtl_TextEncoding eCodeSet)
1760 OString sTmp(OUStringToOString(rStr, eCodeSet));
1761 const char *pStart = sTmp.getStr();
1762 const char *pEnd = pStart + sTmp.getLength();
1764 rO.insert( rO.end(), pStart, pEnd );
1767 void SwWW8Writer::WriteString16(SvStream& rStrm, const OUString& rStr,
1768 bool bAddZero)
1770 ww::bytes aBytes;
1771 SwWW8Writer::InsAsString16(aBytes, rStr);
1772 if (bAddZero)
1773 SwWW8Writer::InsUInt16(aBytes, 0);
1774 //vectors are guaranteed to have contiguous memory, so we can do
1775 //this while migrating away from WW8Bytes. Meyers Effective STL, item 16
1776 if (!aBytes.empty())
1777 rStrm.WriteBytes(aBytes.data(), aBytes.size());
1780 void SwWW8Writer::WriteString_xstz(SvStream& rStrm, const OUString& rStr, bool bAddZero)
1782 ww::bytes aBytes;
1783 SwWW8Writer::InsUInt16(aBytes, rStr.getLength());
1784 SwWW8Writer::InsAsString16(aBytes, rStr);
1785 if (bAddZero)
1786 SwWW8Writer::InsUInt16(aBytes, 0);
1787 rStrm.WriteBytes(aBytes.data(), aBytes.size());
1790 void SwWW8Writer::WriteString8(SvStream& rStrm, std::u16string_view rStr,
1791 bool bAddZero, rtl_TextEncoding eCodeSet)
1793 ww::bytes aBytes;
1794 SwWW8Writer::InsAsString8(aBytes, rStr, eCodeSet);
1795 if (bAddZero)
1796 aBytes.push_back(0);
1797 //vectors are guaranteed to have contiguous memory, so we can do
1798 ////this while migrating away from WW8Bytes. Meyers Effective STL, item 16
1799 if (!aBytes.empty())
1800 rStrm.WriteBytes(aBytes.data(), aBytes.size());
1803 void WW8Export::WriteStringAsPara( const OUString& rText )
1805 if( !rText.isEmpty() )
1806 OutSwString(rText, 0, rText.getLength());
1807 WriteCR(); // CR thereafter
1809 ww::bytes aArr;
1810 SwWW8Writer::InsUInt16( aArr, 0/*nStyleId*/ );
1811 if( m_bOutTable )
1812 { // Tab-Attr
1813 // sprmPFInTable
1814 SwWW8Writer::InsUInt16( aArr, NS_sprm::PFInTable::val );
1815 aArr.push_back( 1 );
1818 sal_uInt64 nPos = Strm().Tell();
1819 m_pPapPlc->AppendFkpEntry( nPos, aArr.size(), aArr.data() );
1820 m_pChpPlc->AppendFkpEntry( nPos );
1823 void MSWordExportBase::WriteSpecialText( SwNodeOffset nStart, SwNodeOffset nEnd, sal_uInt8 nTTyp )
1825 sal_uInt8 nOldTyp = m_nTextTyp;
1826 m_nTextTyp = nTTyp;
1827 auto const pOldPam = m_pCurPam; //!! Simply shifting the PaM without restoring should do the job too
1828 SwNodeOffset nOldStart = m_nCurStart;
1829 SwNodeOffset nOldEnd = m_nCurEnd;
1830 SwPaM* pOldEnd = m_pOrigPam;
1831 bool bOldPageDescs = m_bOutPageDescs;
1832 m_bOutPageDescs = false;
1833 if ( nTTyp == TXT_FTN || nTTyp == TXT_EDN )
1834 m_bAddFootnoteTab = true; // enable one aesthetic tab for this footnote
1836 SetCurPam(nStart, nEnd);
1838 // clear linked textboxes since old ones can't be linked to frames in this section
1839 m_aLinkedTextboxesHelper.clear();
1841 // tdf#106261 Reset table infos, otherwise the depth of the cells will be
1842 // incorrect, in case the header/footer had table(s) and we try to export
1843 // the same table second time.
1844 ww8::WW8TableInfo::Pointer_t xOldTableInfo(m_pTableInfo);
1845 m_pTableInfo = std::make_shared<ww8::WW8TableInfo>();
1847 WriteText();
1849 m_pTableInfo = std::move(xOldTableInfo);
1851 m_bOutPageDescs = bOldPageDescs;
1852 m_pCurPam = pOldPam; // delete Pam
1853 m_nCurStart = nOldStart;
1854 m_nCurEnd = nOldEnd;
1855 m_pOrigPam = pOldEnd;
1856 m_nTextTyp = nOldTyp;
1859 void WW8Export::OutSwString(const OUString& rStr, sal_Int32 nStt,
1860 sal_Int32 const nLen)
1863 SAL_INFO( "sw.ww8.level2", "<OutSwString>" );
1865 if( nLen )
1867 if( nStt || nLen != rStr.getLength() )
1869 OUString sOut( rStr.copy( nStt, nLen ) );
1871 SAL_INFO( "sw.ww8.level2", sOut );
1873 SwWW8Writer::WriteString16(Strm(), sOut, false);
1875 else
1877 SAL_INFO( "sw.ww8.level2", rStr );
1879 SwWW8Writer::WriteString16(Strm(), rStr, false);
1883 SAL_INFO( "sw.ww8.level2", "</OutSwString>" );
1886 void WW8Export::WriteCR(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
1888 if (pTableTextNodeInfoInner && pTableTextNodeInfoInner->getDepth() == 1 && pTableTextNodeInfoInner->isEndOfCell())
1889 WriteChar('\007');
1890 else
1891 WriteChar( '\015' );
1893 m_pPiece->SetParaBreak();
1896 void WW8Export::WriteChar( sal_Unicode c )
1898 Strm().WriteUInt16( c );
1901 void MSWordExportBase::SetCurPam(SwNodeOffset nStt, SwNodeOffset nEnd)
1903 m_nCurStart = nStt;
1904 m_nCurEnd = nEnd;
1905 m_pCurPam = Writer::NewUnoCursor( m_rDoc, nStt, nEnd );
1907 // Recognize tables in special cases
1908 if ( nStt != m_pCurPam->GetMark()->GetNodeIndex() &&
1909 m_rDoc.GetNodes()[ nStt ]->IsTableNode() )
1911 m_pCurPam->GetMark()->Assign(nStt);
1914 m_pOrigPam = m_pCurPam.get(); // ???
1915 m_pCurPam->Exchange();
1918 void MSWordExportBase::SaveData( SwNodeOffset nStt, SwNodeOffset nEnd )
1920 MSWordSaveData aData;
1922 // WW8Export only stuff - zeroed here not to issue warnings
1923 aData.pOOld = nullptr;
1925 // Common stuff
1926 aData.pOldPam = m_pCurPam;
1927 aData.pOldEnd = m_pOrigPam;
1928 aData.pOldFlyFormat = m_pParentFrame;
1929 aData.pOldPageDesc = m_pCurrentPageDesc;
1931 aData.pOldFlyOffset = m_pFlyOffset;
1932 aData.eOldAnchorType = m_eNewAnchorType;
1934 aData.bOldWriteAll = false;
1935 aData.bOldOutTable = m_bOutTable;
1936 aData.bOldFlyFrameAttrs = m_bOutFlyFrameAttrs;
1937 aData.bOldStartTOX = m_bStartTOX;
1938 aData.bOldInWriteTOX = m_bInWriteTOX;
1940 SetCurPam(nStt, nEnd);
1942 m_bOutTable = false;
1943 // Caution: bIsInTable should not be set here
1944 m_bOutFlyFrameAttrs = false;
1945 m_bStartTOX = false;
1946 m_bInWriteTOX = false;
1948 m_aSaveData.push( std::move(aData) );
1951 void MSWordExportBase::RestoreData()
1953 MSWordSaveData &rData = m_aSaveData.top();
1955 m_pCurPam = rData.pOldPam;
1956 m_nCurStart = rData.nOldStart;
1957 m_nCurEnd = rData.nOldEnd;
1958 m_pOrigPam = rData.pOldEnd;
1960 m_bOutTable = rData.bOldOutTable;
1961 m_bOutFlyFrameAttrs = rData.bOldFlyFrameAttrs;
1962 m_bStartTOX = rData.bOldStartTOX;
1963 m_bInWriteTOX = rData.bOldInWriteTOX;
1965 m_pParentFrame = rData.pOldFlyFormat;
1966 m_pCurrentPageDesc = rData.pOldPageDesc;
1968 m_eNewAnchorType = rData.eOldAnchorType;
1969 m_pFlyOffset = rData.pOldFlyOffset;
1971 m_aSaveData.pop();
1974 void WW8Export::SaveData( SwNodeOffset nStt, SwNodeOffset nEnd )
1976 MSWordExportBase::SaveData( nStt, nEnd );
1978 MSWordSaveData &rData = m_aSaveData.top();
1980 if ( !m_pO->empty() )
1982 rData.pOOld = std::move(m_pO);
1983 m_pO.reset(new ww::bytes);
1985 else
1986 rData.pOOld = nullptr; // reuse pO
1988 rData.bOldWriteAll = GetWriter().m_bWriteAll;
1989 GetWriter().m_bWriteAll = true;
1992 void WW8Export::RestoreData()
1994 MSWordSaveData &rData = m_aSaveData.top();
1996 GetWriter().m_bWriteAll = rData.bOldWriteAll;
1998 OSL_ENSURE( m_pO->empty(), "pO is not empty in WW8Export::RestoreData()" );
1999 if ( rData.pOOld )
2001 m_pO = std::move(rData.pOOld);
2004 MSWordExportBase::RestoreData();
2007 void WW8AttributeOutput::TableInfoCell(const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
2009 sal_uInt32 nDepth = pTableTextNodeInfoInner->getDepth();
2011 if ( nDepth <= 0 )
2012 return;
2014 /* Cell */
2015 m_rWW8Export.InsUInt16( NS_sprm::PFInTable::val );
2016 m_rWW8Export.m_pO->push_back( sal_uInt8(0x1) );
2017 m_rWW8Export.InsUInt16( NS_sprm::PItap::val );
2018 m_rWW8Export.InsUInt32( nDepth );
2020 if ( nDepth > 1 && pTableTextNodeInfoInner->isEndOfCell() )
2022 m_rWW8Export.InsUInt16( NS_sprm::PFInnerTableCell::val );
2023 m_rWW8Export.m_pO->push_back( sal_uInt8(0x1) );
2027 void WW8AttributeOutput::TableInfoRow(const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
2029 sal_uInt32 nDepth = pTableTextNodeInfoInner->getDepth();
2031 if ( nDepth <= 0 )
2032 return;
2034 /* Row */
2035 if ( !pTableTextNodeInfoInner->isEndOfLine() )
2036 return;
2038 m_rWW8Export.InsUInt16( NS_sprm::PFInTable::val );
2039 m_rWW8Export.m_pO->push_back( sal_uInt8(0x1) );
2041 if ( nDepth == 1 )
2043 m_rWW8Export.InsUInt16( NS_sprm::PFTtp::val );
2044 m_rWW8Export.m_pO->push_back( sal_uInt8(0x1) );
2047 m_rWW8Export.InsUInt16( NS_sprm::PItap::val );
2048 m_rWW8Export.InsUInt32( nDepth );
2050 if ( nDepth > 1 )
2052 m_rWW8Export.InsUInt16( NS_sprm::PFInnerTableCell::val );
2053 m_rWW8Export.m_pO->push_back( sal_uInt8(0x1) );
2054 m_rWW8Export.InsUInt16( NS_sprm::PFInnerTtp::val );
2055 m_rWW8Export.m_pO->push_back( sal_uInt8(0x1) );
2058 // Most of these are per-row definitions, not per-table.
2059 // WW8 has no explicit table start/end markup,
2060 // simply rows with the same table properties that are grouped together as a table.
2061 TableBidi( pTableTextNodeInfoInner );
2062 TableOrientation( pTableTextNodeInfoInner );
2063 TableSpacing( pTableTextNodeInfoInner );
2064 TableDefinition( pTableTextNodeInfoInner ); //per row definitions
2065 TableHeight( pTableTextNodeInfoInner ); //per row definitions
2066 TableBackgrounds( pTableTextNodeInfoInner ); //per row definitions
2067 // Since this isEndOfLine, cell margin defaults for each row come from last column.
2068 TableDefaultBorders( pTableTextNodeInfoInner ); //per row definitions
2069 TableCanSplit( pTableTextNodeInfoInner ); //per row definitions
2070 TableVerticalCell( pTableTextNodeInfoInner ); //per row definitions
2071 TableCellBorders( pTableTextNodeInfoInner ); //per row definitions
2074 static sal_uInt16 lcl_TCFlags(SwDoc &rDoc, const SwTableBox * pBox, sal_Int32 nRowSpan)
2076 sal_uInt16 nFlags = 0;
2078 if (nRowSpan > 1)
2079 nFlags |= (3 << 5);
2080 else if (nRowSpan < 0)
2081 nFlags |= (1 << 5);
2083 if (pBox != nullptr)
2085 const SwFrameFormat * pFormat = pBox->GetFrameFormat();
2086 switch (pFormat->GetVertOrient().GetVertOrient())
2088 case text::VertOrientation::CENTER:
2089 nFlags |= (1 << 7);
2090 break;
2091 case text::VertOrientation::BOTTOM:
2092 nFlags |= (2 << 7);
2093 break;
2094 default:
2095 break;
2097 const SwStartNode * pSttNd = pBox->GetSttNd();
2098 if(pSttNd)
2100 SwNodeIndex aIdx( *pSttNd );
2101 const SwContentNode* pCNd = SwNodes::GoNext(&aIdx);
2102 if( pCNd && pCNd->IsTextNode())
2104 SfxItemSetFixed<RES_CHRATR_ROTATE, RES_CHRATR_ROTATE> aCoreSet(rDoc.GetAttrPool());
2105 static_cast<const SwTextNode*>(pCNd)->GetParaAttr(aCoreSet,
2106 0, static_cast<const SwTextNode*>(pCNd)->GetText().getLength());
2107 if ( const SvxCharRotateItem * pRotate = aCoreSet.GetItemIfSet(RES_CHRATR_ROTATE))
2109 if(pRotate->GetValue() == 900_deg10)
2111 nFlags = nFlags | 0x0004 | 0x0008;
2113 else if(pRotate->GetValue() == 2700_deg10 )
2115 nFlags = nFlags | 0x0004 | 0x0010;
2122 return nFlags;
2125 void WW8AttributeOutput::TableVerticalCell(const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
2127 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
2128 const SwTableLine * pTabLine = pTabBox->GetUpper();
2129 const SwTableBoxes & rTableBoxes = pTabLine->GetTabBoxes();
2131 sal_uInt8 nBoxes = rTableBoxes.size();
2132 for ( sal_uInt8 n = 0; n < nBoxes; n++ )
2134 const SwTableBox * pTabBox1 = rTableBoxes[n];
2135 const SwFrameFormat * pFrameFormat = pTabBox1->GetFrameFormat();
2137 // Map from our SvxFrameDirection to WW8 TextFlow.
2138 sal_uInt16 nTextFlow = 0;
2139 switch (m_rWW8Export.TrueFrameDirection(*pFrameFormat))
2141 case SvxFrameDirection::Vertical_RL_TB:
2142 nTextFlow = 5;
2143 break;
2144 case SvxFrameDirection::Vertical_LR_BT:
2145 nTextFlow = 3;
2146 break;
2147 default:
2148 break;
2151 if (nTextFlow != 0)
2153 m_rWW8Export.InsUInt16( NS_sprm::TTextFlow::val );
2154 m_rWW8Export.m_pO->push_back( n ); //start range
2155 m_rWW8Export.m_pO->push_back( sal_uInt8(n + 1) ); //end range
2156 m_rWW8Export.InsUInt16(nTextFlow);
2161 void WW8AttributeOutput::TableCanSplit(const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner )
2163 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
2164 const SwTableLine * pTabLine = pTabBox->GetUpper();
2165 const SwFrameFormat * pLineFormat = pTabLine->GetFrameFormat();
2168 By default the row can be split in word, and now in writer we have a
2169 feature equivalent to this, Word stores 1 for fCantSplit if the row
2170 cannot be split, we set true if we can split it. An example is #i4569#
2173 const SwFormatRowSplit& rSplittable = pLineFormat->GetRowSplit();
2174 sal_uInt8 nCantSplit = (!rSplittable.GetValue()) ? 1 : 0;
2175 m_rWW8Export.InsUInt16( NS_sprm::TFCantSplit::val );
2176 m_rWW8Export.m_pO->push_back( nCantSplit );
2177 m_rWW8Export.InsUInt16( NS_sprm::TFCantSplit90::val ); // also write fCantSplit90
2178 m_rWW8Export.m_pO->push_back( nCantSplit );
2181 void WW8AttributeOutput::TableBidi(const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
2183 const SwTable * pTable = pTableTextNodeInfoInner->getTable();
2184 const SwFrameFormat * pFrameFormat = pTable->GetFrameFormat();
2186 if ( m_rWW8Export.TrueFrameDirection(*pFrameFormat) == SvxFrameDirection::Horizontal_RL_TB )
2188 m_rWW8Export.InsUInt16( NS_sprm::TFBiDi::val );
2189 m_rWW8Export.InsUInt16( 1 );
2193 void WW8AttributeOutput::TableRowRedline(const ww8::WW8TableNodeInfoInner::Pointer_t& /*pTableTextNodeInfoInner*/)
2197 void WW8AttributeOutput::TableCellRedline(const ww8::WW8TableNodeInfoInner::Pointer_t& /*pTableTextNodeInfoInner*/)
2201 void WW8AttributeOutput::TableHeight(const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
2203 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
2204 const SwTableLine * pTabLine = pTabBox->GetUpper();
2205 const SwFrameFormat * pLineFormat = pTabLine->GetFrameFormat();
2207 // output line height sprmTDyaRowHeight
2208 tools::Long nHeight = 0;
2209 const SwFormatFrameSize& rLSz = pLineFormat->GetFrameSize();
2210 if ( SwFrameSize::Variable != rLSz.GetHeightSizeType() && rLSz.GetHeight() )
2212 if ( SwFrameSize::Minimum == rLSz.GetHeightSizeType() )
2213 nHeight = rLSz.GetHeight();
2214 else
2215 nHeight = -rLSz.GetHeight();
2218 if ( nHeight )
2220 m_rWW8Export.InsUInt16( NS_sprm::TDyaRowHeight::val );
2221 m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>(nHeight) );
2226 void WW8AttributeOutput::TableOrientation(const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
2228 const SwTable * pTable = pTableTextNodeInfoInner->getTable();
2230 const SwFrameFormat *pFormat = pTable->GetFrameFormat();
2231 if ( !pFormat )
2233 SAL_WARN( "sw.ww8", "FrameFormat is nil" );
2234 return;
2237 const SwFormatHoriOrient &rHori = pFormat->GetHoriOrient();
2238 const SwFormatVertOrient &rVert = pFormat->GetVertOrient();
2240 if (
2241 !((text::RelOrientation::PRINT_AREA == rHori.GetRelationOrient() ||
2242 text::RelOrientation::FRAME == rHori.GetRelationOrient())
2244 (text::RelOrientation::PRINT_AREA == rVert.GetRelationOrient() ||
2245 text::RelOrientation::FRAME == rVert.GetRelationOrient()))
2247 return;
2249 const bool bIsRTL = m_rWW8Export.TrueFrameDirection(*pFormat) == SvxFrameDirection::Horizontal_RL_TB;
2250 sal_Int16 eHOri = rHori.GetHoriOrient();
2251 switch (eHOri)
2253 case text::HoriOrientation::CENTER:
2254 m_rWW8Export.InsUInt16( NS_sprm::TJc::val ); //logical orientation required for MSO
2255 m_rWW8Export.InsUInt16( 1 );
2256 m_rWW8Export.InsUInt16( NS_sprm::TJc90::val ); //physical orientation required for LO
2257 m_rWW8Export.InsUInt16( 1 );
2258 break;
2259 case text::HoriOrientation::RIGHT:
2260 m_rWW8Export.InsUInt16( NS_sprm::TJc90::val ); //required for LO
2261 m_rWW8Export.InsUInt16( 2 );
2262 if ( !bIsRTL )
2264 m_rWW8Export.InsUInt16( NS_sprm::TJc::val ); //required for MSO
2265 m_rWW8Export.InsUInt16( 2 );
2267 break;
2268 case text::HoriOrientation::LEFT:
2269 if ( bIsRTL )
2271 m_rWW8Export.InsUInt16( NS_sprm::TJc::val ); //required for MSO
2272 m_rWW8Export.InsUInt16( 2 );
2274 break;
2275 case text::HoriOrientation::LEFT_AND_WIDTH:
2276 // Width can only be specified for the LOGICAL left, so in RTL, that is always PHYSICAL right
2277 if ( bIsRTL )
2279 m_rWW8Export.InsUInt16( NS_sprm::TJc90::val ); //required for LO
2280 m_rWW8Export.InsUInt16( 2 );
2282 break;
2283 default:
2284 break;
2288 void WW8AttributeOutput::TableSpacing(const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
2290 const SwTable * pTable = pTableTextNodeInfoInner->getTable();
2291 const SwTableFormat* pTableFormat = pTable->GetFrameFormat();
2294 // Writing these SPRM's will make the table a floating one, so only write
2295 // them in case the table is already inside a frame.
2296 if (!(pTableFormat != nullptr && pTable->GetTableNode()->GetFlyFormat()))
2297 return;
2299 const SvxULSpaceItem & rUL = pTableFormat->GetULSpace();
2301 if (rUL.GetUpper() > 0)
2303 sal_uInt8 const nPadding = 2;
2304 sal_uInt8 const nPcVert = 0;
2305 sal_uInt8 const nPcHorz = 0;
2307 sal_uInt8 const nTPc = (nPadding << 4) | (nPcVert << 2) | nPcHorz;
2309 m_rWW8Export.InsUInt16(NS_sprm::TPc::val);
2310 m_rWW8Export.m_pO->push_back( nTPc );
2312 m_rWW8Export.InsUInt16(NS_sprm::TDyaAbs::val);
2313 m_rWW8Export.InsUInt16(rUL.GetUpper());
2315 m_rWW8Export.InsUInt16(NS_sprm::TDyaFromText::val);
2316 m_rWW8Export.InsUInt16(rUL.GetUpper());
2319 if (rUL.GetLower() > 0)
2321 m_rWW8Export.InsUInt16(NS_sprm::TDyaFromTextBottom::val);
2322 m_rWW8Export.InsUInt16(rUL.GetLower());
2326 void WW8AttributeOutput::TablePositioning(SwFrameFormat* pFlyFormat)
2328 if (!pFlyFormat || !pFlyFormat->GetFlySplit().GetValue())
2330 return;
2333 sal_uInt8 nPcVert = 0;
2334 switch (pFlyFormat->GetVertOrient().GetRelationOrient())
2336 case text::RelOrientation::PAGE_PRINT_AREA:
2337 // relative to margin
2338 nPcVert = 0;
2339 break;
2340 case text::RelOrientation::PAGE_FRAME:
2341 // relative to page
2342 nPcVert = 1;
2343 break;
2344 default:
2345 // text::RelOrientation::FRAME
2346 // relative to text
2347 nPcVert = 2;
2348 break;
2350 sal_uInt8 nPcHorz = 0;
2351 switch (pFlyFormat->GetHoriOrient().GetRelationOrient())
2353 case text::RelOrientation::FRAME:
2354 // relative to column
2355 nPcHorz = 0;
2356 break;
2357 case text::RelOrientation::PAGE_PRINT_AREA:
2358 // relative to margin
2359 nPcHorz = 1;
2360 break;
2361 default:
2362 // text::RelOrientation::PAGE_FRAME
2363 // relative to page
2364 nPcHorz = 2;
2365 break;
2367 sal_uInt8 nTPc = (nPcVert << 4) | (nPcHorz << 6);
2368 m_rWW8Export.InsUInt16(NS_sprm::TPc::val);
2369 m_rWW8Export.m_pO->push_back(nTPc);
2371 // Similar to WW8AttributeOutput::FormatHorizOrientation(), but for tables.
2372 sal_Int16 nTDxaAbs = 0;
2373 switch (pFlyFormat->GetHoriOrient().GetHoriOrient())
2375 case text::HoriOrientation::LEFT:
2376 // left
2377 nTDxaAbs = 0;
2378 break;
2379 case text::HoriOrientation::CENTER:
2380 // centered
2381 nTDxaAbs = -4;
2382 break;
2383 case text::HoriOrientation::RIGHT:
2384 // right
2385 nTDxaAbs = -8;
2386 break;
2387 default:
2388 nTDxaAbs = pFlyFormat->GetHoriOrient().GetPos();
2389 break;
2391 m_rWW8Export.InsUInt16(NS_sprm::TDxaAbs::val);
2392 m_rWW8Export.InsInt16(nTDxaAbs);
2394 // Similar to WW8AttributeOutput::FormatVertOrientation(), but for tables.
2395 sal_Int16 nTDyaAbs = 0;
2396 switch (pFlyFormat->GetVertOrient().GetVertOrient())
2398 case text::VertOrientation::TOP:
2399 // up
2400 nTDyaAbs = -4;
2401 break;
2402 case text::VertOrientation::CENTER:
2403 // centered
2404 nTDyaAbs = -8;
2405 break;
2406 case text::VertOrientation::BOTTOM:
2407 // down
2408 nTDyaAbs = -12;
2409 break;
2410 default:
2411 nTDyaAbs = pFlyFormat->GetVertOrient().GetPos();
2412 break;
2414 m_rWW8Export.InsUInt16(NS_sprm::TDyaAbs::val);
2415 m_rWW8Export.InsInt16(nTDyaAbs);
2417 // Similar to WW8AttributeOutput::FormatULSpace(), but for tables.
2418 sal_uInt16 nDyaFromText = pFlyFormat->GetULSpace().GetUpper();
2419 m_rWW8Export.InsUInt16(NS_sprm::TDyaFromText::val);
2420 m_rWW8Export.InsUInt16(nDyaFromText);
2421 sal_uInt16 nDyaFromTextBottom = pFlyFormat->GetULSpace().GetLower();
2422 m_rWW8Export.InsUInt16(NS_sprm::TDyaFromTextBottom::val);
2423 m_rWW8Export.InsUInt16(nDyaFromTextBottom);
2425 // Similar to WW8AttributeOutput::FormatLRSpace(), but for tables.
2426 sal_uInt16 nDxaFromText = pFlyFormat->GetLRSpace().ResolveLeft({});
2427 m_rWW8Export.InsUInt16(NS_sprm::TDxaFromText::val);
2428 m_rWW8Export.InsUInt16(nDxaFromText);
2429 sal_uInt16 nDxaFromTextRight = pFlyFormat->GetLRSpace().ResolveRight({});
2430 m_rWW8Export.InsUInt16(NS_sprm::TDxaFromTextRight::val);
2431 m_rWW8Export.InsUInt16(nDxaFromTextRight);
2433 if (!pFlyFormat->GetWrapInfluenceOnObjPos().GetAllowOverlap())
2435 // Allowing overlap is the default in both Writer and in WW8.
2436 m_rWW8Export.InsUInt16(NS_sprm::TFNoAllowOverlap::val);
2437 m_rWW8Export.m_pO->push_back(1);
2441 void WW8AttributeOutput::TableDefinition(const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
2443 const SwTable * pTable = pTableTextNodeInfoInner->getTable();
2445 if ( pTable->GetRowsToRepeat() > pTableTextNodeInfoInner->getRow() )
2447 m_rWW8Export.InsUInt16( NS_sprm::TTableHeader::val );
2448 m_rWW8Export.m_pO->push_back( 1 );
2451 ww8::TableBoxVectorPtr pTableBoxes =
2452 pTableTextNodeInfoInner->getTableBoxesOfRow();
2453 // number of cell written
2454 sal_uInt32 nBoxes = pTableBoxes->size();
2455 assert(nBoxes <= ww8::MAXTABLECELLS);
2457 // sprm header
2458 m_rWW8Export.InsUInt16( NS_sprm::TDefTable::val );
2459 sal_uInt16 nSprmSize = 2 + (nBoxes + 1) * 2 + nBoxes * 20;
2460 m_rWW8Export.InsUInt16( nSprmSize ); // length
2462 // number of boxes
2463 m_rWW8Export.m_pO->push_back( static_cast<sal_uInt8>(nBoxes) );
2465 /* cells */
2467 ALWAYS relative when text::HoriOrientation::NONE (nPageSize + ( nPageSize / 10 )) < nTableSz,
2468 in that case the cell width's and table width's are not real. The table
2469 width is maxed and cells relative, so we need the frame (generally page)
2470 width that the table is in to work out the true widths.
2472 //const bool bNewTableModel = pTable->IsNewModel();
2473 const SwFrameFormat *pFormat = pTable->GetFrameFormat();
2474 if ( !pFormat )
2476 SAL_WARN( "sw.ww8", "FrameFormat is nil" );
2477 return;
2480 const SwFormatHoriOrient &rHori = pFormat->GetHoriOrient();
2481 const SwFormatVertOrient &rVert = pFormat->GetVertOrient();
2483 SwTwips nTableOffset = 0;
2485 if (
2486 (text::RelOrientation::PRINT_AREA == rHori.GetRelationOrient() ||
2487 text::RelOrientation::FRAME == rHori.GetRelationOrient())
2489 (text::RelOrientation::PRINT_AREA == rVert.GetRelationOrient() ||
2490 text::RelOrientation::FRAME == rVert.GetRelationOrient())
2493 sal_Int16 eHOri = rHori.GetHoriOrient();
2494 switch ( eHOri )
2496 case text::HoriOrientation::CENTER:
2497 case text::HoriOrientation::RIGHT:
2498 break;
2500 default:
2501 nTableOffset = rHori.GetPos();
2502 const SvxLRSpaceItem& rLRSp = pFormat->GetLRSpace();
2503 nTableOffset += rLRSp.ResolveLeft({});
2505 // convert offset to be measured from right margin in right-to-left tables
2506 if ( nTableOffset && m_rWW8Export.TrueFrameDirection(*pFormat) == SvxFrameDirection::Horizontal_RL_TB )
2508 SwTwips nLeftPageMargin, nRightPageMargin;
2509 const SwTwips nPageSize = m_rWW8Export.CurrentPageWidth(nLeftPageMargin, nRightPageMargin);
2510 const SwTwips nTableWidth = pFormat->GetFrameSize().GetWidth();
2511 nTableOffset = nPageSize - nLeftPageMargin - nRightPageMargin - nTableWidth - nTableOffset;
2513 break;
2517 m_rWW8Export.InsInt16( nTableOffset );
2519 ww8::GridColsPtr pGridCols = GetGridCols( pTableTextNodeInfoInner );
2520 for ( const auto nCol : *pGridCols )
2522 m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>(nCol) + nTableOffset );
2525 /* TCs */
2526 ww8::RowSpansPtr pRowSpans = pTableTextNodeInfoInner->getRowSpansOfRow();
2527 ww8::RowSpans::const_iterator aItRowSpans = pRowSpans->begin();
2529 for (const SwTableBox * pTabBox1 : *pTableBoxes)
2531 sal_uInt16 npOCount = m_rWW8Export.m_pO->size();
2533 const SwFrameFormat * pBoxFormat = nullptr;
2534 if (pTabBox1 != nullptr)
2535 pBoxFormat = pTabBox1->GetFrameFormat();
2537 sal_uInt16 nFlags =
2538 lcl_TCFlags(m_rWW8Export.m_rDoc, pTabBox1, *aItRowSpans);
2539 m_rWW8Export.InsUInt16( nFlags );
2541 static sal_uInt8 aNullBytes[] = { 0x0, 0x0 };
2543 m_rWW8Export.m_pO->insert( m_rWW8Export.m_pO->end(), aNullBytes, aNullBytes+2 ); // dummy
2544 if (pBoxFormat != nullptr)
2546 const SvxBoxItem & rBoxItem = pBoxFormat->GetBox();
2548 WW8Export::Out_SwFormatTableBox( *m_rWW8Export.m_pO, &rBoxItem ); // 8/16 Byte
2550 else
2551 WW8Export::Out_SwFormatTableBox( *m_rWW8Export.m_pO, nullptr); // 8/16 Byte
2553 SAL_INFO( "sw.ww8.level2", "<tclength>" << ( m_rWW8Export.m_pO->size() - npOCount ) << "</tclength>" );
2554 ++aItRowSpans;
2557 int nWidthPercent = pFormat->GetFrameSize().GetWidthPercent();
2559 // The best fit for "automatic" table placement is relative 100%
2560 if (!nWidthPercent && rHori.GetHoriOrient() == text::HoriOrientation::FULL)
2561 nWidthPercent = 100;
2563 // Width is in fiftieths of a percent. For sprmTTableWidth, must be non-negative and 600% max
2564 if ( nWidthPercent > 0 && nWidthPercent <= 600 )
2566 m_rWW8Export.InsUInt16( NS_sprm::TTableWidth::val );
2567 m_rWW8Export.m_pO->push_back( sal_uInt8/*ftsPercent*/ (2) );
2568 m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>(nWidthPercent) * 50 );
2571 // Write table positioning properties in case this is a floating table.
2572 TablePositioning(pTable->GetTableNode()->GetFlyFormat());
2575 ww8::GridColsPtr AttributeOutputBase::GetGridCols( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner )
2577 return pTableTextNodeInfoInner->getGridColsOfRow(*this);
2580 ww8::WidthsPtr AttributeOutputBase::GetColumnWidths( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner )
2582 // Get the column widths based on ALL the rows, not just the current row
2583 return pTableTextNodeInfoInner->getGridColsOfRow(*this, true);
2586 void AttributeOutputBase::GetTablePageSize( ww8::WW8TableNodeInfoInner const * pTableTextNodeInfoInner, tools::Long& rPageSize, bool& rRelBoxSize )
2588 tools::Long nPageSize = 0;
2590 const SwNode *pTextNd = pTableTextNodeInfoInner->getNode( );
2591 const SwTable *pTable = pTableTextNodeInfoInner->getTable( );
2593 const SwFrameFormat *pFormat = pTable->GetFrameFormat();
2594 if ( !pFormat )
2596 SAL_WARN( "sw.ww8", "FrameFormat is nil" );
2597 return;
2600 const SwFormatFrameSize &rSize = pFormat->GetFrameSize();
2601 int nWidthPercent = rSize.GetWidthPercent();
2602 bool bManualAligned = pFormat->GetHoriOrient().GetHoriOrient() == text::HoriOrientation::NONE;
2603 if ( (pFormat->GetHoriOrient().GetHoriOrient() == text::HoriOrientation::FULL) || bManualAligned )
2604 nWidthPercent = 100;
2605 bool bRelBoxSize = nWidthPercent != 0;
2606 tools::ULong nTableSz = static_cast<tools::ULong>(rSize.GetWidth());
2607 if (nTableSz > USHRT_MAX/2 && !bRelBoxSize)
2609 OSL_ENSURE(bRelBoxSize, "huge table width but not relative, suspicious");
2610 bRelBoxSize = true;
2613 if ( bRelBoxSize )
2615 Point aPt;
2616 SwRect aRect( pFormat->FindLayoutRect( false, &aPt ) );
2617 if ( aRect.IsEmpty() )
2619 // Then fetch the page width without margins!
2620 const SwFrameFormat* pParentFormat =
2621 GetExport().m_pParentFrame ?
2622 &(GetExport().m_pParentFrame->GetFrameFormat()) :
2623 GetExport().m_rDoc.GetPageDesc(0).GetPageFormatOfNode(*pTextNd, false);
2624 aRect = pParentFormat->FindLayoutRect(true);
2625 nPageSize = aRect.Width();
2626 if ( 0 == nPageSize )
2628 const SvxLRSpaceItem& rLR = pParentFormat->GetLRSpace();
2629 nPageSize = pParentFormat->GetFrameSize().GetWidth() - rLR.ResolveLeft({})
2630 - rLR.ResolveRight({});
2633 else
2635 nPageSize = aRect.Width();
2636 if ( bManualAligned )
2638 // #i37571# For manually aligned tables
2639 const SvxLRSpaceItem &rLR = pFormat->GetLRSpace();
2640 nPageSize -= (rLR.ResolveLeft({}) + rLR.ResolveRight({}));
2645 if ( nWidthPercent )
2647 nPageSize *= nWidthPercent;
2648 nPageSize /= 100;
2650 else
2651 SAL_WARN( "sw.ww8", "nWidthPercent is zero" );
2653 else
2655 // As the table width is not relative, the TablePageSize equals its width
2656 nPageSize = nTableSz;
2659 rPageSize = nPageSize;
2660 rRelBoxSize = bRelBoxSize;
2663 void WW8AttributeOutput::TableDefaultBorders( const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner )
2665 // This function name is misleading because it is not a table default, but a row default,
2666 // and it also only sets default cell margins (aka border padding).
2667 // The specs suggest there is no way to define default border lines/colors.
2668 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
2669 const SwFrameFormat * pFrameFormat = pTabBox->GetFrameFormat();
2671 static const SvxBoxItemLine aBorders[] =
2673 SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
2674 SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
2677 // Set row default cell margins using this last cell in the row
2678 for ( int i = 0; i < 4; ++i )
2680 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::TCellPaddingDefault::val );
2681 m_rWW8Export.m_pO->push_back( sal_uInt8(6) );
2682 m_rWW8Export.m_pO->push_back( sal_uInt8(0) );
2683 m_rWW8Export.m_pO->push_back( sal_uInt8(1) );
2684 m_rWW8Export.m_pO->push_back( sal_uInt8(1 << i) );
2685 m_rWW8Export.m_pO->push_back( sal_uInt8(3) );
2687 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO,
2688 pFrameFormat->GetBox().GetDistance( aBorders[i] ) );
2692 void WW8AttributeOutput::TableCellBorders(
2693 ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner )
2695 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
2696 const SwTableLine * pTabLine = pTabBox->GetUpper();
2697 const SwTableBoxes & rTabBoxes = pTabLine->GetTabBoxes();
2698 sal_uInt8 nBoxes = std::min<size_t>(rTabBoxes.size(), 255);
2699 const SvxBoxItem * pLastBox = nullptr;
2700 sal_uInt8 nSeqStart = 0; // start of sequence of cells with same borders
2702 static const SvxBoxItemLine aBorders[] =
2704 SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
2705 SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
2708 sal_uInt16 nDefaultMargin[4] = {31681, 31681, 31681, 31681}; // outside of documented valid range
2709 // last column in each row defines the row default in TableRowDefaultBorders()
2710 if ( nBoxes && rTabBoxes.size() == nBoxes )
2712 const SvxBoxItem& rBox = rTabBoxes[ nBoxes-1 ]->GetFrameFormat()->GetBox();
2713 for ( int i = 0; i < 4; ++i )
2714 nDefaultMargin[i] = rBox.GetDistance( aBorders[i] );
2717 // Detect sequences of cells which have the same borders, and output
2718 // a border description for each such cell range.
2719 for ( unsigned n = 0; n <= nBoxes; ++n )
2721 const SvxBoxItem * pBox = (n == nBoxes) ? nullptr :
2722 &rTabBoxes[n]->GetFrameFormat()->GetBox();
2723 if( !pLastBox )
2724 pLastBox = pBox;
2725 else if( !pBox || *pLastBox != *pBox )
2727 // This cell has different borders than the previous cell,
2728 // so output the borders for the preceding cell range.
2729 m_rWW8Export.Out_CellRangeBorders(pLastBox, nSeqStart, n);
2731 // The last column is used as the row default for margins, so we can ignore these matching ones
2732 if ( n == nBoxes )
2733 break;
2735 // Output cell margins.
2736 // One CSSA can define up to all four margins if they are the same size value.
2737 sal_uInt16 nMargin[4];
2738 sal_uInt8 nSideBits[4] = {0, 0, 0, 0}; // 0001:top, 0010:left, 0100:bottom, 1000:right
2739 for ( int i = 0; i < 4; ++i ) // sides: top, left, bottom, right
2741 nMargin[i] = std::min(sal_Int16(31680), pLastBox->GetDistance( aBorders[i] ));
2742 if ( nMargin[i] == nDefaultMargin[i] )
2743 continue;
2745 // join a previous side's definition if it shares the same value
2746 for ( int p = 0; p < 4; ++p )
2748 if ( nMargin[i] == nMargin[p] )
2750 nSideBits[p] |= 1 << i;
2751 break;
2756 // write out the cell margins definitions that were used
2757 for ( int i = 0; i < 4; ++i )
2759 if ( nSideBits[i] )
2761 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::TCellPadding::val );
2762 m_rWW8Export.m_pO->push_back( sal_uInt8(6) ); // 6 bytes
2763 m_rWW8Export.m_pO->push_back( sal_uInt8(nSeqStart) ); // first cell: apply margin
2764 m_rWW8Export.m_pO->push_back( sal_uInt8(n) ); // end cell: do not apply margin
2765 m_rWW8Export.m_pO->push_back( sal_uInt8(nSideBits[i]) );
2766 m_rWW8Export.m_pO->push_back( sal_uInt8(3) ); // FtsDxa: size in twips
2767 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, nMargin[i] );
2771 nSeqStart = n;
2772 pLastBox = pBox;
2777 void WW8AttributeOutput::TableBackgrounds(const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
2779 const SwTable * pTab = pTableTextNodeInfoInner->getTable();
2780 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
2781 const SwTableLine * pTabLine = pTabBox->GetUpper();
2782 const SwTableBoxes & rTabBoxes = pTabLine->GetTabBoxes();
2784 sal_uInt8 nBoxes = rTabBoxes.size();
2785 m_rWW8Export.InsUInt16( NS_sprm::TDefTableShd80::val );
2786 m_rWW8Export.m_pO->push_back( static_cast<sal_uInt8>(nBoxes * 2) ); // Len
2788 Color aRowColor = COL_AUTO;
2789 const SvxBrushItem *pTableColorProp = pTab->GetFrameFormat()->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
2790 if ( pTableColorProp )
2791 aRowColor = pTableColorProp->GetColor();
2793 const SvxBrushItem *pRowColorProp = pTabLine->GetFrameFormat()->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
2794 if ( pRowColorProp && pRowColorProp->GetColor() != COL_AUTO )
2795 aRowColor = pRowColorProp->GetColor();
2797 for ( sal_uInt8 n = 0; n < nBoxes; n++ )
2799 const SwTableBox * pBox1 = rTabBoxes[n];
2800 const SwFrameFormat * pFrameFormat = pBox1->GetFrameFormat();
2801 Color aColor = aRowColor;
2803 const SvxBrushItem *pCellColorProp = pFrameFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
2804 if ( pCellColorProp && pCellColorProp->GetColor() != COL_AUTO )
2805 aColor = pCellColorProp->GetColor();
2807 WW8_SHD aShd;
2808 WW8Export::TransBrush( aColor, aShd );
2809 m_rWW8Export.InsUInt16( aShd.GetValue() );
2812 /*sprmTDefTableShdRaw:
2813 * A DefTableShdOperand value that specifies the ... shading for cells 1 up to 22 in the row,
2814 * ... Cells 23 to 44 are shaded by sprmTDefTableShdRaw2nd,
2815 * and cells 45 to 63 are shaded by sprmTDefTableShdRaw3rd.
2817 sal_uInt32 const aSprmIds[] { NS_sprm::TDefTableShd::val,
2818 NS_sprm::TDefTableShdRaw::val,
2819 NS_sprm::TDefTableShdRaw::val,
2820 NS_sprm::TDefTableShd2nd::val,
2821 NS_sprm::TDefTableShdRaw2nd::val,
2822 NS_sprm::TDefTableShd3rd::val,
2823 NS_sprm::TDefTableShdRaw3rd::val };
2824 for (sal_uInt32 m : aSprmIds)
2826 sal_uInt8 nStart = 0;
2827 sal_uInt8 nStop = rTabBoxes.size();
2828 switch ( m )
2830 case NS_sprm::TDefTableShd::val:
2831 case NS_sprm::TDefTableShdRaw::val:
2832 if ( nStop > 21 )
2833 nStop = 22;
2834 break;
2835 case NS_sprm::TDefTableShd2nd::val:
2836 case NS_sprm::TDefTableShdRaw2nd::val:
2837 nStart = 22;
2838 if ( nStop > 43 )
2839 nStop = 44;
2840 break;
2841 case NS_sprm::TDefTableShd3rd::val:
2842 case NS_sprm::TDefTableShdRaw3rd::val:
2843 nStart = 44;
2844 if ( nStop > 62 )
2845 nStop = 63;
2846 break;
2848 if ( nStart >= nStop )
2849 break;
2851 m_rWW8Export.InsUInt16( m );
2852 m_rWW8Export.m_pO->push_back( static_cast<sal_uInt8>((nStop-nStart) * 10) );
2854 for ( sal_uInt8 n = nStart; n < nStop; n++ )
2856 const SwTableBox * pBox1 = rTabBoxes[n];
2857 const SwFrameFormat * pFrameFormat = pBox1->GetFrameFormat();
2858 Color aColor = aRowColor;
2860 const SvxBrushItem *pCellColorProp = pFrameFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
2861 if ( pCellColorProp && pCellColorProp->GetColor() != COL_AUTO )
2862 aColor = pCellColorProp->GetColor();
2864 WW8SHDLong aSHD;
2865 aSHD.setCvFore( 0xFF000000 );
2867 if ( aColor == COL_AUTO )
2868 aSHD.setCvBack( 0xFF000000 );
2869 else
2870 aSHD.setCvBack( wwUtility::RGBToBGR( aColor ) );
2872 aSHD.Write( m_rWW8Export );
2877 void WW8Export::SectionBreaksAndFrames( const SwTextNode& rNode )
2879 // output page/section breaks
2880 OutputSectionBreaks( rNode.GetpSwAttrSet(), rNode );
2883 namespace {
2885 class TrackContentToExport
2887 private:
2888 SwPaM *m_pCurPam;
2889 SwNodeOffset m_nStart, m_nEnd;
2890 public:
2891 TrackContentToExport(SwPaM *pCurPam, SwNodeOffset nCurStart, SwNodeOffset nCurEnd)
2892 : m_pCurPam(pCurPam)
2893 , m_nStart(nCurStart)
2894 , m_nEnd(nCurEnd)
2898 bool contentRemainsToExport(ww8::WW8TableInfo *pTableInfo)
2900 bool bSimpleContentRemains = m_pCurPam->GetPoint()->GetNode() < m_pCurPam->GetMark()->GetNode() ||
2901 (m_pCurPam->GetPoint()->GetNode() == m_pCurPam->GetMark()->GetNode() &&
2902 m_pCurPam->GetPoint()->GetContentIndex() <= m_pCurPam->GetMark()->GetContentIndex());
2903 if (bSimpleContentRemains)
2904 return true;
2906 if (!pTableInfo)
2907 return false;
2909 //An old-school table where one cell may points back to a previous node as the next cell
2910 //so if this node is the last node in the range, we may need to jump back to a previously
2911 //skipped cell to output it in a sane sequence. See ooo47778-3.sxw for one of these
2912 //horrors. So if we are at the end of the selection, but this end point is a table
2913 //cell whose next cell is in the selection allow jumping back to it
2914 const SwNode* pCurrentNode = &m_pCurPam->GetPoint()->GetNode();
2915 const SwNode* pNextNode = pTableInfo->getNextNode(pCurrentNode);
2917 if (pNextNode && pCurrentNode != pNextNode)
2919 return pNextNode->GetIndex() >= m_nStart &&
2920 pNextNode->GetIndex() < m_nEnd;
2923 return false;
2929 void MSWordExportBase::WriteText()
2931 TrackContentToExport aContentTracking(m_pCurPam.get(), m_nCurStart, m_nCurEnd);
2932 while (aContentTracking.contentRemainsToExport(m_pTableInfo.get()))
2934 SwNode& rNd = m_pCurPam->GetPointNode();
2936 // no section breaks exported for Endnotes
2937 if ( rNd.IsTextNode() && m_nTextTyp != TXT_EDN && m_nTextTyp != TXT_FTN )
2939 SwSoftPageBreakList breakList;
2940 // if paragraph need to be split than handle section break somewhere
2941 // else.
2942 if( !NeedTextNodeSplit( *rNd.GetTextNode(), breakList) )
2943 SectionBreaksAndFrames( *rNd.GetTextNode() );
2947 // output the various types of nodes
2948 if ( rNd.IsContentNode() )
2950 SwContentNode* pCNd = static_cast<SwContentNode*>(&rNd);
2952 const SwPageDesc* pTemp = rNd.FindPageDesc();
2953 if ( pTemp )
2954 m_pCurrentPageDesc = pTemp;
2956 m_pCurPam->GetPoint()->SetContent( 0 );
2957 OutputContentNode( *pCNd );
2959 else if ( rNd.IsTableNode() )
2961 m_pTableInfo->processSwTable( &rNd.GetTableNode()->GetTable() );
2963 else if ( rNd.IsSectionNode() && TXT_MAINTEXT == m_nTextTyp )
2964 OutputSectionNode( *rNd.GetSectionNode() );
2965 else if ( TXT_MAINTEXT == m_nTextTyp && rNd.IsEndNode() &&
2966 rNd.StartOfSectionNode()->IsSectionNode() )
2968 const SwSection& rSect = rNd.StartOfSectionNode()->GetSectionNode()
2969 ->GetSection();
2970 if ( m_bStartTOX && SectionType::ToxContent == rSect.GetType() )
2971 m_bStartTOX = false;
2973 SwNodeIndex aIdx( rNd, 1 );
2974 if ( aIdx.GetNode().IsEndNode() && aIdx.GetNode().StartOfSectionNode()->IsSectionNode() )
2976 else if ( aIdx.GetNode().IsSectionNode() )
2978 else if ( !IsInTable() ) //No sections in table
2980 //#120140# Do not need to insert a page/section break after a section end. Check this case first
2981 bool bNeedExportBreakHere = true;
2982 if ( rSect.GetType() == SectionType::ToxContent || rSect.GetType() == SectionType::ToxHeader )
2983 bNeedExportBreakHere = false;
2984 else if ( aIdx.GetNode().IsTextNode() )
2986 SwTextNode *pTempNext = aIdx.GetNode().GetTextNode();
2987 if ( pTempNext )
2989 const SwFormatPageDesc * pTempItem = nullptr;
2990 if (pTempNext->GetpSwAttrSet()
2991 && (pTempItem = pTempNext->GetpSwAttrSet()->GetItemIfSet(RES_PAGEDESC, false))
2992 && pTempItem->GetRegisteredIn())
2994 //Next node has a new page style which means this node is a section end. Do not insert another page/section break here
2995 bNeedExportBreakHere = false;
2999 else
3001 /* Do not export Section Break in case DOCX containing MultiColumn and
3002 * aIdx.GetNode().IsTextNode() is False i.e. Text node is NULL.
3004 const SwFrameFormat* pPgFormat = rSect.GetFormat();
3005 const SwFormatCol& rCol = pPgFormat->GetCol();
3006 sal_uInt16 nColumnCount = rCol.GetNumCols();
3007 const SwFormatNoBalancedColumns& rNoBalanced = pPgFormat->GetBalancedColumns();
3008 // Prevent the additional section break only for non-balanced columns.
3009 if (nColumnCount > 1 && rNoBalanced.GetValue())
3011 bNeedExportBreakHere = false;
3013 // No need to create a "fake" section if this is the end of the document,
3014 // except to emulate balanced columns.
3015 else if ( nColumnCount < 2 && aIdx == m_rDoc.GetNodes().GetEndOfContent() )
3016 bNeedExportBreakHere = false;
3019 if (bNeedExportBreakHere) //#120140# End of check
3021 ReplaceCr( char(0xc) ); // indicator for Page/Section-Break
3023 const SwSectionFormat* pParentFormat = rSect.GetFormat()->GetParent();
3024 if ( !pParentFormat )
3025 pParentFormat = reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1));
3027 sal_uLong nRstLnNum;
3028 if ( aIdx.GetNode().IsContentNode() )
3029 nRstLnNum = static_cast<SwContentNode&>(aIdx.GetNode()).GetSwAttrSet().
3030 GetLineNumber().GetStartValue();
3031 else
3032 nRstLnNum = 0;
3034 AppendSection( m_pCurrentPageDesc, pParentFormat, nRstLnNum );
3036 else
3038 OutputEndNode( *rNd.GetEndNode() );
3042 else if ( rNd.IsStartNode() )
3044 OutputStartNode( *rNd.GetStartNode() );
3046 else if ( rNd.IsEndNode() )
3048 OutputEndNode( *rNd.GetEndNode() );
3051 if ( &rNd == &rNd.GetNodes().GetEndOfContent() )
3052 break;
3054 const SwNode * pCurrentNode = &m_pCurPam->GetPoint()->GetNode();
3055 const SwNode * pNextNode = m_pTableInfo->getNextNode(pCurrentNode);
3057 if (pCurrentNode == pNextNode)
3059 SAL_WARN("sw.ww8", "loop in TableInfo");
3060 pNextNode = nullptr;
3063 if (pNextNode != nullptr)
3064 m_pCurPam->GetPoint()->Assign(*pNextNode);
3065 else
3066 m_pCurPam->GetPoint()->Adjust(SwNodeOffset(1));
3068 SwNodeOffset nPos = m_pCurPam->GetPoint()->GetNodeIndex();
3069 ::SetProgressState( sal_Int32(nPos), m_pCurPam->GetDoc().GetDocShell() );
3072 SAL_INFO( "sw.ww8.level2", "</WriteText>" );
3075 void WW8Export::WriteMainText()
3077 SAL_INFO( "sw.ww8.level2", "<WriteMainText>" );
3079 m_pFib->m_fcMin = Strm().Tell();
3081 m_pCurPam->GetPoint()->Assign(*m_rDoc.GetNodes().GetEndOfContent().StartOfSectionNode());
3083 WriteText();
3085 if( 0 == Strm().Tell() - m_pFib->m_fcMin ) // no text ?
3086 WriteCR(); // then CR at the end ( otherwise WW will complain )
3088 m_pFib->m_ccpText = Fc2Cp( Strm().Tell() );
3089 m_pFieldMain->Finish( m_pFib->m_ccpText, 0 );
3091 // ccpText includes Footnote and KF-text
3092 // therefore pFib->ccpText may get updated as well
3093 // save the StyleId of the last paragraph. Because WW97 take the style
3094 // from the last CR, that will be written after footer/Header/footnotes/
3095 // annotation etc.
3096 const SwTextNode* pLastNd = m_pCurPam->GetMark()->GetNode().GetTextNode();
3097 if( pLastNd )
3098 m_nLastFormatId = GetId( static_cast<SwTextFormatColl&>(pLastNd->GetAnyFormatColl()) );
3100 SAL_INFO( "sw.ww8.level2", "</WriteMainText>" );
3103 bool MSWordExportBase::IsInTable() const
3105 bool bResult = false;
3107 if (m_pCurPam != nullptr)
3109 SwNode& rNode = m_pCurPam->GetPointNode();
3111 if (m_pTableInfo)
3113 ww8::WW8TableNodeInfo::Pointer_t pTableNodeInfo = m_pTableInfo->getTableNodeInfo(&rNode);
3115 if (pTableNodeInfo && pTableNodeInfo->getDepth() > 0)
3117 bResult = true;
3122 return bResult;
3125 typedef ww8::WW8Sttb< ww8::WW8Struct > WW8SttbAssoc;
3127 void WW8Export::WriteFkpPlcUsw()
3129 // Graphics in the data stream
3130 m_pGrf->Write(); // Graphics
3132 // output into WordDocument stream
3133 m_pChpPlc->WriteFkps(); // Fkp.Chpx
3134 m_pPapPlc->WriteFkps(); // Fkp.Papx
3135 m_pSepx->WriteSepx( Strm() ); // Sepx
3137 // output into Table stream
3138 m_pStyles->OutputStylesTable(); // for WW8 StyleTab
3139 m_pFootnote->WritePlc( *this ); // Footnote-Ref & Text Plc
3140 m_pEdn->WritePlc( *this ); // Endnote-Ref & Text Plc
3141 m_pTextBxs->WritePlc( *this ); // Textbox Text Plc
3142 m_pHFTextBxs->WritePlc( *this ); // Head/Foot-Textbox Text Plc
3143 m_pAtn->WritePlc( *this ); // Annotation-Ref & Text Plc
3145 m_pSepx->WritePlcSed( *this ); // Slcx.PlcSed
3146 m_pSepx->WritePlcHdd( *this ); // Slcx.PlcHdd
3148 m_pChpPlc->WritePlc(); // Plcx.Chpx
3149 m_pPapPlc->WritePlc(); // Plcx.Papx
3151 if( m_pRedlAuthors )
3152 m_pRedlAuthors->Write( GetWriter() ); // sttbfRMark (RedlineAuthors)
3153 m_pFieldMain->Write( *this ); // Fields ( Main Text )
3154 m_pFieldHdFt->Write( *this ); // Fields ( Header/Footer )
3155 m_pFieldFootnote->Write( *this ); // Fields ( FootNotes )
3156 m_pFieldEdn->Write( *this ); // Fields ( EndNotes )
3157 m_pFieldAtn->Write( *this ); // Fields ( Annotations )
3158 m_pFieldTextBxs->Write( *this ); // Fields ( Textboxes )
3159 m_pFieldHFTextBxs->Write( *this ); // Fields ( Head/Foot-Textboxes )
3161 if (m_pEscher || m_rDoc.ContainsMSVBasic())
3164 Every time MS 2000 creates an escher stream there is always
3165 an ObjectPool dir (even if empty). It turns out that if a copy of
3166 MS 2000 is used to open a document that contains escher graphics
3167 exported from StarOffice without this empty dir then *if* that
3168 copy of MS Office has never been used to open a MSOffice document
3169 that has escher graphics (and an ObjectPool dir of course) and
3170 that copy of office has not been used to draw escher graphics then
3171 our exported graphics do not appear. Once you do open a ms
3172 document with escher graphics or draw an escher graphic with that
3173 copy of word, then all documents from staroffice that contain
3174 escher work from then on. Tricky to track down, some sort of late
3175 binding trickery in MS where solely for first time initialization
3176 the existence of an ObjectPool dir is necessary for triggering
3177 some magic.
3179 // avoid memory leak #i120098#, the unnamed obj will be released in destructor.
3180 m_xEscherStg = GetWriter().GetStorage().OpenSotStorage(SL::aObjectPool);
3183 // dggInfo - escher stream
3184 WriteEscher();
3186 m_pSdrObjs->WritePlc( *this );
3187 m_pHFSdrObjs->WritePlc( *this );
3188 // spamom - office drawing table
3189 // spahdr - header office drawing table
3191 m_pBkmks->Write( *this ); // Bookmarks - sttbfBkmk/
3192 // plcfBkmkf/plcfBkmkl
3193 m_pFactoids->Write(*this);
3195 WriteNumbering();
3197 RestoreMacroCmds();
3199 m_pMagicTable->Write( *this );
3201 m_pPiece->WritePc( *this ); // Piece-Table
3202 m_aFontHelper.WriteFontTable(m_pTableStrm, *m_pFib); // FFNs
3204 //Convert OOo asian typography into MS typography structure
3205 ExportDopTypography(m_pDop->doptypography);
3207 WriteDop( *this ); // Document-Properties
3209 // Write SttbfAssoc
3210 WW8SttbAssoc * pSttbfAssoc = dynamic_cast<WW8SttbAssoc *>
3211 (m_rDoc.getIDocumentExternalData().getExternalData(::sw::tExternalDataType::STTBF_ASSOC).get());
3213 if ( pSttbfAssoc ) // #i106057#
3215 std::vector<OUString> aStrings(pSttbfAssoc->getStrings());
3216 WriteAsStringTable(aStrings, m_pFib->m_fcSttbfAssoc,
3217 m_pFib->m_lcbSttbfAssoc);
3220 Strm().Seek( 0 );
3222 // Reclaim stored FIB data from document.
3223 ::ww8::WW8FibData * pFibData = dynamic_cast<ww8::WW8FibData *>
3224 (m_rDoc.getIDocumentExternalData().getExternalData(::sw::tExternalDataType::FIB).get());
3226 if ( pFibData )
3228 m_pFib->m_fReadOnlyRecommended =
3229 pFibData->getReadOnlyRecommended();
3230 m_pFib->m_fWriteReservation =
3231 pFibData->getWriteReservation();
3234 m_pFib->Write( Strm() ); // FIB
3237 void WW8Export::StoreDoc1()
3239 bool bNeedsFinalPara = false;
3240 // Start of Text ( overwrite )
3241 SwWW8Writer::FillUntil( Strm(), m_pFib->m_fcMin );
3243 WriteMainText(); // main text
3244 sal_uInt8 nSprmsLen;
3245 sal_uInt8 *pLastSprms = m_pPapPlc->CopyLastSprms(nSprmsLen);
3247 bNeedsFinalPara |= m_pFootnote->WriteText( *this ); // Footnote-Text
3248 bNeedsFinalPara |= m_pSepx->WriteKFText( *this ); // K/F-Text
3249 bNeedsFinalPara |= m_pAtn->WriteText( *this ); // Annotation-Text
3250 bNeedsFinalPara |= m_pEdn->WriteText( *this ); // EndNote-Text
3252 // create the escher streams
3253 CreateEscher();
3255 bNeedsFinalPara |= m_pTextBxs->WriteText( *this ); //Textbox Text Plc
3256 bNeedsFinalPara |= m_pHFTextBxs->WriteText( *this );//Head/Foot-Textbox Text Plc
3258 if (bNeedsFinalPara)
3260 WriteCR();
3261 m_pPapPlc->AppendFkpEntry(Strm().Tell(), nSprmsLen, pLastSprms);
3263 delete[] pLastSprms;
3265 m_pSepx->Finish( Fc2Cp( Strm().Tell() ));// Text + Footnote + HdFt as section end
3266 m_pMagicTable->Finish( Fc2Cp( Strm().Tell() ),0);
3268 m_pFib->m_fcMac = Strm().Tell(); // End of all texts
3270 WriteFkpPlcUsw(); // FKP, PLC, ...
3273 void MSWordExportBase::AddLinkTarget(std::u16string_view rURL)
3275 if( rURL.empty() || rURL[0] != '#' )
3276 return;
3278 OUString aURL( BookmarkToWriter( rURL.substr( 1 ) ) );
3279 sal_Int32 nPos = aURL.lastIndexOf( cMarkSeparator );
3281 if( nPos < 2 )
3282 return;
3284 OUString sCmp = aURL.copy(nPos+1).replaceAll(" ", "");
3285 if( sCmp.isEmpty() )
3286 return;
3288 sCmp = sCmp.toAsciiLowerCase();
3289 if( sCmp == "outline" )
3291 SwPosition aPos(*m_pCurPam->GetPoint());
3292 OUString aName(BookmarkToWriter(aURL.subView(0, nPos)));
3293 // If we can find the outline this bookmark refers to
3294 // save the name of the bookmark and the
3295 // node index number of where it points to
3296 if( m_rDoc.GotoOutline( aPos, aName ) )
3297 m_aImplicitBookmarks.emplace_back(aURL, aPos.GetNodeIndex());
3299 else if( sCmp == "graphic" )
3301 OUString aName(BookmarkToWriter(aURL.subView(0, nPos)));
3302 if (const SwFlyFrameFormat* pFormat = m_rDoc.FindFlyByName(aName, SwNodeType::Grf))
3304 const SwFormatAnchor& rFormatAnchor = pFormat->GetAnchor();
3305 if (SwNode* pAnchorNode = rFormatAnchor.GetAnchorNode())
3306 m_aImplicitBookmarks.emplace_back(aURL, pAnchorNode->GetIndex());
3309 else if( sCmp == "frame" )
3311 OUString aName(BookmarkToWriter(aURL.subView(0, nPos)));
3312 if (const SwFlyFrameFormat* pFormat = m_rDoc.FindFlyByName(aName, SwNodeType::Text))
3313 if (const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx())
3314 m_aImplicitBookmarks.emplace_back(aURL, pIdx->GetIndex() + 1);
3316 else if( sCmp == "ole" )
3318 OUString aName(BookmarkToWriter(aURL.subView(0, nPos)));
3319 if (const SwFlyFrameFormat* pFormat = m_rDoc.FindFlyByName(aName, SwNodeType::Ole))
3321 const SwFormatAnchor& rFormatAnchor = pFormat->GetAnchor();
3322 if (SwNode* pAnchorNode = rFormatAnchor.GetAnchorNode())
3323 m_aImplicitBookmarks.emplace_back(aURL, pAnchorNode->GetIndex());
3326 else if( sCmp == "region" )
3328 OUString aName(BookmarkToWriter(aURL.subView(0, nPos)));
3329 for (const SwSectionFormat* pFormat : m_rDoc.GetSections())
3331 if (aName == pFormat->GetSection()->GetSectionName())
3333 if (const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx())
3335 m_aImplicitBookmarks.emplace_back(aURL, pIdx->GetIndex() + 1);
3336 break;
3341 else if( sCmp == "table" )
3343 OUString aName(BookmarkToWriter(aURL.subView(0, nPos)));
3344 if (const SwTable* pTable = SwTable::FindTable(m_rDoc.FindTableFormatByName(aName)))
3345 if (const SwTableNode* pTableNode = pTable->GetTabSortBoxes()[1]->GetSttNd()->FindTableNode())
3346 m_aImplicitBookmarks.emplace_back(aURL, pTableNode->GetIndex() + 2);
3348 else if (sCmp == "toxmark")
3350 OUString const name(aURL.copy(0, nPos));
3351 OUString const nameDecoded(INetURLObject::decode(name,
3352 INetURLObject::DecodeMechanism::WithCharset));
3353 if (const auto tmp = sw::PrepareJumpToTOXMark(m_rDoc, nameDecoded))
3355 SwTOXMark const* pMark(&tmp->first);
3356 for (sal_Int32 i = 0; i < tmp->second; ++i)
3358 pMark = &m_rDoc.GotoTOXMark(*pMark, TOX_SAME_NXT, true);
3360 if (!SfxPoolItem::areSame(pMark, &tmp->first))
3362 m_TOXMarkBookmarksByURL.emplace(aURL, name);
3363 m_TOXMarkBookmarksByTOXMark.emplace(pMark, nameDecoded);
3369 void MSWordExportBase::CollectOutlineBookmarks(const SwDoc &rDoc)
3371 rDoc.ForEachINetFormat(
3372 [this] (const SwFormatINetFormat& rINetFormat) -> bool
3374 AddLinkTarget( rINetFormat.GetValue() );
3375 return true;
3378 rDoc.ForEachFormatURL(
3379 [this] (const SwFormatURL& rURL) -> bool
3381 AddLinkTarget(rURL.GetURL());
3382 const ImageMap *pIMap = rURL.GetMap();
3383 if (!pIMap)
3384 return true;
3386 for (size_t i=0; i < pIMap->GetIMapObjectCount(); ++i)
3388 const IMapObject* pObj = pIMap->GetIMapObject(i);
3389 if (!pObj)
3390 continue;
3391 AddLinkTarget( pObj->GetURL() );
3393 return true;
3397 namespace
3399 const sal_uInt64 WW_BLOCKSIZE = 0x200;
3401 ErrCode EncryptRC4(msfilter::MSCodec_Std97& rCtx, SvStream &rIn, SvStream &rOut)
3403 sal_uInt64 nLen = rIn.TellEnd();
3404 rIn.Seek(0);
3406 sal_uInt8 in[WW_BLOCKSIZE];
3407 for (std::size_t nI = 0, nBlock = 0; nI < nLen; nI += WW_BLOCKSIZE, ++nBlock)
3409 std::size_t nBS = std::min(nLen - nI, WW_BLOCKSIZE);
3410 nBS = rIn.ReadBytes(in, nBS);
3411 if (!rCtx.InitCipher(nBlock)) {
3412 return ERRCODE_IO_NOTSUPPORTED;
3414 rCtx.Encode(in, nBS, in, nBS);
3415 rOut.WriteBytes(in, nBS);
3417 return ERRCODE_NONE;
3421 ErrCode MSWordExportBase::ExportDocument( bool bWriteAll )
3423 m_nCharFormatStart = DEFAULT_STYLES_COUNT;
3424 m_nFormatCollStart = m_nCharFormatStart + m_rDoc.GetCharFormats()->size() - 1;
3426 m_bStyDef = m_bBreakBefore = m_bOutKF =
3427 m_bOutFlyFrameAttrs = m_bOutPageDescs = m_bOutTable = m_bOutFirstPage =
3428 m_bOutGrf = m_bInWriteEscher = m_bStartTOX =
3429 m_bInWriteTOX = false;
3431 m_bFootnoteAtTextEnd = m_bEndAtTextEnd = true;
3433 m_pParentFrame = nullptr;
3434 m_bParaInlineHeading = false;
3435 m_pFlyOffset = nullptr;
3436 m_eNewAnchorType = RndStdIds::FLY_AT_PAGE;
3437 m_nTextTyp = TXT_MAINTEXT;
3438 m_nStyleBeforeFly = m_nLastFormatId = 0;
3439 m_pStyAttr = nullptr;
3440 m_pCurrentStyle = nullptr;
3441 m_pOutFormatNode = nullptr;
3442 m_pEscher = nullptr;
3443 m_pRedlAuthors = nullptr;
3444 m_aTOXArr.clear();
3446 if ( !m_oOLEExp )
3448 sal_uInt32 nSvxMSDffOLEConvFlags = 0;
3449 if (officecfg::Office::Common::Filter::Microsoft::Export::MathToMathType::get())
3450 nSvxMSDffOLEConvFlags |= OLE_STARMATH_2_MATHTYPE;
3451 if (officecfg::Office::Common::Filter::Microsoft::Export::WriterToWinWord::get())
3452 nSvxMSDffOLEConvFlags |= OLE_STARWRITER_2_WINWORD;
3453 if (officecfg::Office::Common::Filter::Microsoft::Export::CalcToExcel::get())
3454 nSvxMSDffOLEConvFlags |= OLE_STARCALC_2_EXCEL;
3455 if (officecfg::Office::Common::Filter::Microsoft::Export::ImpressToPowerPoint::get())
3456 nSvxMSDffOLEConvFlags |= OLE_STARIMPRESS_2_POWERPOINT;
3458 m_oOLEExp.emplace( nSvxMSDffOLEConvFlags );
3461 if ( !m_pOCXExp && m_rDoc.GetDocShell() )
3462 m_pOCXExp.reset(new SwMSConvertControls(m_rDoc.GetDocShell(), m_pCurPam.get()));
3464 // #i81405# - Collect anchored objects before changing the redline mode.
3465 m_aFrames = GetFrames( m_rDoc, bWriteAll? nullptr : m_pOrigPam );
3467 m_nOrigRedlineFlags = m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
3469 SwRootFrame const*const pLayout(m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout());
3470 m_bOrigShowChanges = pLayout == nullptr || !pLayout->IsHideRedlines();
3472 if ( !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
3474 //restored to original state by SwWriter::Write
3475 m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags(m_nOrigRedlineFlags |
3476 RedlineFlags::ShowDelete |
3477 RedlineFlags::ShowInsert);
3480 // fix the SwPositions in m_aFrames after SetRedlineFlags
3481 UpdateFramePositions(m_aFrames);
3483 m_aFontHelper.InitFontTable(m_rDoc);
3484 GatherChapterFields();
3486 CollectOutlineBookmarks(m_rDoc);
3488 // make unique OrdNums (Z-Order) for all drawing-/fly Objects
3489 if ( m_rDoc.getIDocumentDrawModelAccess().GetDrawModel() )
3490 m_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->GetPage( 0 )->RecalcObjOrdNums();
3492 ErrCode err = ExportDocument_Impl();
3494 m_aFrames.clear();
3496 // park m_pCurPam in a "safe place" now that document is fully exported
3497 // before toggling redline mode to avoid ~SwContentIndexReg assert e.g. export
3498 // ooo103014-1.odt to .doc
3499 // park m_pOrigPam as well, as needed for exporting abi9915-1.odt to doc
3500 m_pOrigPam->DeleteMark();
3501 m_pOrigPam->GetPoint()->Assign(m_rDoc.GetNodes().GetEndOfContent());
3502 static_cast<SwPaM&>(*m_pCurPam) = *m_pOrigPam;
3504 m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags(m_nOrigRedlineFlags);
3506 return err;
3509 bool SwWW8Writer::InitStd97CodecUpdateMedium( ::msfilter::MSCodec_Std97& rCodec )
3511 uno::Sequence< beans::NamedValue > aEncryptionData;
3513 if ( mpMedium )
3515 const SfxUnoAnyItem* pEncryptionDataItem = mpMedium->GetItemSet().GetItem(SID_ENCRYPTIONDATA, false);
3516 if ( pEncryptionDataItem && ( pEncryptionDataItem->GetValue() >>= aEncryptionData ) && !rCodec.InitCodec( aEncryptionData ) )
3518 OSL_ENSURE( false, "Unexpected EncryptionData!" );
3519 aEncryptionData.realloc( 0 );
3522 if ( !aEncryptionData.hasElements() )
3524 // try to generate the encryption data based on password
3525 const SfxStringItem* pPasswordItem = mpMedium->GetItemSet().GetItem(SID_PASSWORD, false);
3526 if ( pPasswordItem && !pPasswordItem->GetValue().isEmpty() && pPasswordItem->GetValue().getLength() <= 15 )
3528 // Generate random number with a seed of time as salt.
3529 sal_uInt8 pDocId[ 16 ];
3530 if (rtl_random_getBytes(nullptr, pDocId, 16) != rtl_Random_E_None)
3532 throw uno::RuntimeException(u"rtl_random_getBytes failed"_ustr);
3535 sal_uInt16 aPassword[16] = {};
3537 const OUString& sPassword(pPasswordItem->GetValue());
3538 for ( sal_Int32 nChar = 0; nChar < sPassword.getLength(); ++nChar )
3539 aPassword[nChar] = sPassword[nChar];
3541 rCodec.InitKey( aPassword, pDocId );
3542 aEncryptionData = rCodec.GetEncryptionData();
3544 mpMedium->GetItemSet().Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::Any( aEncryptionData ) ) );
3548 if ( aEncryptionData.hasElements() )
3549 mpMedium->GetItemSet().ClearItem( SID_PASSWORD );
3552 // nonempty encryption data means here that the codec was successfully initialized
3553 return aEncryptionData.hasElements();
3556 ErrCode WW8Export::ExportDocument_Impl()
3558 PrepareStorage();
3560 m_pFib.reset(new WW8Fib(8, m_bDot));
3562 rtl::Reference<SotStorageStream> xWwStrm(GetWriter().GetStorage().OpenSotStream(m_aMainStg));
3563 if (!xWwStrm->IsWritable())
3564 return ERRCODE_IO_ACCESSDENIED;
3566 rtl::Reference<SotStorageStream> xTableStrm(xWwStrm), xDataStrm(xWwStrm);
3567 xWwStrm->SetBufferSize( 32768 );
3569 m_pFib->m_fWhichTableStm = true;
3570 xTableStrm = GetWriter().GetStorage().OpenSotStream(SL::a1Table, StreamMode::STD_WRITE);
3571 xDataStrm = GetWriter().GetStorage().OpenSotStream(SL::aData, StreamMode::STD_WRITE);
3573 xDataStrm->SetBufferSize( 32768 ); // for graphics
3574 xTableStrm->SetBufferSize( 16384 ); // for the Font-/Style-Table, etc.
3576 xTableStrm->SetEndian( SvStreamEndian::LITTLE );
3577 xDataStrm->SetEndian( SvStreamEndian::LITTLE );
3579 GetWriter().SetStream( xWwStrm.get() );
3580 m_pTableStrm = xTableStrm.get();
3581 m_pDataStrm = xDataStrm.get();
3583 Strm().SetEndian( SvStreamEndian::LITTLE );
3585 utl::TempFileFast aTempMain;
3586 utl::TempFileFast aTempTable;
3587 utl::TempFileFast aTempData;
3589 msfilter::MSCodec_Std97 aCtx;
3590 bool bEncrypt = GetWriter().InitStd97CodecUpdateMedium(aCtx);
3591 if ( bEncrypt )
3593 GetWriter().SetStream(
3594 aTempMain.GetStream( StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE ) );
3596 m_pTableStrm = aTempTable.GetStream( StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE );
3598 m_pDataStrm = aTempData.GetStream( StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE );
3600 sal_uInt8 const aRC4EncryptionHeader[ 52 ] = {0};
3601 m_pTableStrm->WriteBytes(aRC4EncryptionHeader, 52);
3604 // Default: "Standard"
3605 m_pSepx.reset(new WW8_WrPlcSepx( *this )); // Sections/headers/footers
3607 m_pFootnote.reset(new WW8_WrPlcFootnoteEdn( TXT_FTN )); // Footnotes
3608 m_pEdn.reset(new WW8_WrPlcFootnoteEdn( TXT_EDN )); // Endnotes
3609 m_pAtn.reset(new WW8_WrPlcAnnotations); // PostIts
3610 m_pFactoids.reset(new WW8_WrtFactoids); // Smart tags.
3611 m_pTextBxs.reset(new WW8_WrPlcTextBoxes( TXT_TXTBOX ));
3612 m_pHFTextBxs.reset(new WW8_WrPlcTextBoxes( TXT_HFTXTBOX ));
3614 m_pSdrObjs.reset(new MainTextPlcDrawObj); // Draw-/Fly-Objects for main text
3615 m_pHFSdrObjs.reset(new HdFtPlcDrawObj); // Draw-/Fly-Objects for header/footer
3617 m_pBkmks.reset(new WW8_WrtBookmarks); // Bookmarks
3618 GetWriter().CreateBookmarkTable();
3620 m_pPapPlc.reset(new WW8_WrPlcPn( *this, PAP, m_pFib->m_fcMin ));
3621 m_pChpPlc.reset(new WW8_WrPlcPn( *this, CHP, m_pFib->m_fcMin ));
3622 m_pO.reset(new ww::bytes);
3623 m_pStyles.reset(new MSWordStyles( *this ));
3624 m_pFieldMain.reset(new WW8_WrPlcField( 2, TXT_MAINTEXT ));
3625 m_pFieldHdFt.reset(new WW8_WrPlcField( 2, TXT_HDFT ));
3626 m_pFieldFootnote.reset(new WW8_WrPlcField( 2, TXT_FTN ));
3627 m_pFieldEdn.reset(new WW8_WrPlcField( 2, TXT_EDN ));
3628 m_pFieldAtn.reset(new WW8_WrPlcField( 2, TXT_ATN ));
3629 m_pFieldTextBxs.reset(new WW8_WrPlcField( 2, TXT_TXTBOX ));
3630 m_pFieldHFTextBxs.reset(new WW8_WrPlcField( 2, TXT_HFTXTBOX ));
3632 m_pMagicTable.reset(new WW8_WrMagicTable);
3634 m_pGrf.reset(new SwWW8WrGrf( *this ));
3635 m_pPiece.reset(new WW8_WrPct( m_pFib->m_fcMin ));
3636 m_pDop.reset(new WW8Dop);
3638 m_pDop->fRevMarking = bool( RedlineFlags::On & m_nOrigRedlineFlags );
3639 SwRootFrame const*const pLayout(m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout());
3640 m_pDop->fRMView = pLayout == nullptr || !pLayout->IsHideRedlines();
3641 m_pDop->fRMPrint = m_pDop->fRMView;
3643 // set AutoHyphenation flag if found in default para style
3644 const SvxHyphenZoneItem* pItem;
3645 SwTextFormatColl* pStdTextFormatColl =
3646 m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD, false);
3647 if (pStdTextFormatColl && (pItem = pStdTextFormatColl->GetItemIfSet(
3648 RES_PARATR_HYPHENZONE, false)))
3650 m_pDop->fAutoHyphen = pItem->IsHyphen();
3653 StoreDoc1();
3655 ErrCode err = ERRCODE_NONE;
3656 if ( bEncrypt )
3658 SvStream *pStrmTemp, *pTableStrmTemp, *pDataStrmTemp;
3659 pStrmTemp = xWwStrm.get();
3660 pTableStrmTemp = xTableStrm.get();
3661 pDataStrmTemp = xDataStrm.get();
3663 if ( pDataStrmTemp && pDataStrmTemp != pStrmTemp) {
3664 err = EncryptRC4(aCtx, *m_pDataStrm, *pDataStrmTemp);
3665 if (err != ERRCODE_NONE) {
3666 goto done;
3670 err = EncryptRC4(aCtx, *m_pTableStrm, *pTableStrmTemp);
3671 if (err != ERRCODE_NONE) {
3672 goto done;
3675 // Write Unencrypted Header 52 bytes to the start of the table stream
3676 // EncryptionVersionInfo (4 bytes): A Version structure where Version.vMajor MUST be 0x0001, and Version.vMinor MUST be 0x0001.
3677 pTableStrmTemp->Seek( 0 );
3678 pTableStrmTemp->WriteUInt32( 0x10001 ); // nEncType
3680 sal_uInt8 pDocId[16];
3681 aCtx.GetDocId( pDocId );
3683 sal_uInt8 pSaltData[16];
3684 sal_uInt8 pSaltDigest[16];
3685 aCtx.GetEncryptKey( pDocId, pSaltData, pSaltDigest );
3687 pTableStrmTemp->WriteBytes(pDocId, 16);
3688 pTableStrmTemp->WriteBytes(pSaltData, 16);
3689 pTableStrmTemp->WriteBytes(pSaltDigest, 16);
3691 err = EncryptRC4(aCtx, GetWriter().Strm(), *pStrmTemp);
3692 if (err != ERRCODE_NONE) {
3693 goto done;
3696 // Write Unencrypted Fib 68 bytes to the start of the workdocument stream
3697 m_pFib->m_fEncrypted = true; // fEncrypted indicates the document is encrypted.
3698 m_pFib->m_fObfuscated = false; // Must be 0 for RC4.
3699 m_pFib->m_nHash = 0x34; // encrypt header bytes count of table stream.
3700 m_pFib->m_nKey = 0; // lkey2 must be 0 for RC4.
3702 pStrmTemp->Seek( 0 );
3703 m_pFib->WriteHeader( *pStrmTemp );
3704 done:;
3707 m_pGrf.reset();
3708 m_pMagicTable.reset();
3709 m_pFieldFootnote.reset();
3710 m_pFieldTextBxs.reset();
3711 m_pFieldHFTextBxs.reset();
3712 m_pFieldAtn.reset();
3713 m_pFieldEdn.reset();
3714 m_pFieldHdFt.reset();
3715 m_pFieldMain.reset();
3716 m_pStyles.reset();
3717 m_pO.reset();
3718 m_pChpPlc.reset();
3719 m_pPapPlc.reset();
3720 m_pSepx.reset();
3722 m_pRedlAuthors.reset();
3723 m_pSdrObjs.reset();
3724 m_pHFSdrObjs.reset();
3725 m_pTextBxs.reset();
3726 m_pHFTextBxs.reset();
3727 m_pAtn.reset();
3728 m_pEdn.reset();
3729 m_pFootnote.reset();
3730 m_pBkmks.reset();
3731 m_pPiece.reset();
3732 m_pDop.reset();
3733 m_pFib.reset();
3734 GetWriter().SetStream( nullptr );
3736 xWwStrm->SetBufferSize( 0 );
3737 xTableStrm->SetBufferSize( 0 );
3738 xDataStrm->SetBufferSize( 0 );
3739 if( 0 == m_pDataStrm->Seek( STREAM_SEEK_TO_END ))
3741 xDataStrm.clear();
3742 m_pDataStrm = nullptr;
3743 GetWriter().GetStorage().Remove(SL::aData);
3746 return err;
3749 void WW8Export::PrepareStorage()
3751 static const sal_uInt8 pData[] =
3753 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
3754 0xFF, 0xFF, 0xFF, 0xFF, 0x06, 0x09, 0x02, 0x00,
3755 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00,
3756 0x00, 0x00, 0x00, 0x46,
3758 0x18, 0x00, 0x00, 0x00,
3759 'M', 'i', 'c', 'r', 'o', 's', 'o', 'f',
3760 't', ' ', 'W', 'o', 'r', 'd', '-', 'D',
3761 'o', 'k', 'u', 'm', 'e', 'n', 't', 0x0,
3763 0x0A, 0x00, 0x00, 0x00,
3764 'M', 'S', 'W', 'o', 'r', 'd', 'D', 'o',
3765 'c', 0x0,
3767 0x10, 0x00, 0x00, 0x00,
3768 'W', 'o', 'r', 'd', '.', 'D', 'o', 'c',
3769 'u', 'm', 'e', 'n', 't', '.', '8', 0x0,
3771 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
3772 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
3775 SvGlobalName aGName(MSO_WW8_CLASSID);
3776 GetWriter().GetStorage().SetClass(
3777 aGName, SotClipboardFormatId::NONE, u"Microsoft Word-Document"_ustr);
3778 rtl::Reference<SotStorageStream> xStor(GetWriter().GetStorage().OpenSotStream(sCompObj));
3779 xStor->WriteBytes(pData, sizeof(pData));
3781 SwDocShell* pDocShell = m_rDoc.GetDocShell ();
3782 OSL_ENSURE(pDocShell, "no SwDocShell");
3784 if (!pDocShell) return;
3786 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
3787 pDocShell->GetModel(), uno::UNO_QUERY_THROW);
3788 uno::Reference<document::XDocumentProperties> xDocProps(
3789 xDPS->getDocumentProperties());
3790 OSL_ENSURE(xDocProps.is(), "DocumentProperties is null");
3792 if (!xDocProps.is())
3793 return;
3795 if (officecfg::Office::Common::Filter::Microsoft::Export::EnableWordPreview::get())
3797 std::shared_ptr<GDIMetaFile> xMetaFile =
3798 pDocShell->GetPreviewMetaFile();
3799 uno::Sequence<sal_Int8> metaFile(
3800 sfx2::convertMetaFile(xMetaFile.get()));
3801 sfx2::SaveOlePropertySet(xDocProps, &GetWriter().GetStorage(), &metaFile);
3803 else
3804 sfx2::SaveOlePropertySet( xDocProps, &GetWriter().GetStorage() );
3807 ErrCodeMsg SwWW8Writer::WriteStorage()
3809 rtl::Reference<SotStorage> xOrigStg;
3810 uno::Reference< packages::XPackageEncryption > xPackageEncryption;
3811 std::shared_ptr<SvStream> pSotStorageStream;
3812 uno::Sequence< beans::NamedValue > aEncryptionData;
3813 if (mpMedium)
3815 // Check for specific encryption requests
3816 const SfxUnoAnyItem* pEncryptionDataItem = mpMedium->GetItemSet().GetItem(SID_ENCRYPTIONDATA, false);
3817 if (pEncryptionDataItem && (pEncryptionDataItem->GetValue() >>= aEncryptionData))
3819 ::comphelper::SequenceAsHashMap aHashData(aEncryptionData);
3820 OUString sCryptoType = aHashData.getUnpackedValueOrDefault(u"CryptoType"_ustr, OUString());
3822 if (sCryptoType.getLength())
3824 const uno::Reference<uno::XComponentContext>& xComponentContext(comphelper::getProcessComponentContext());
3825 uno::Sequence<uno::Any> aArguments{
3826 uno::Any(beans::NamedValue(u"Binary"_ustr, uno::Any(true))) };
3827 xPackageEncryption.set(
3828 xComponentContext->getServiceManager()->createInstanceWithArgumentsAndContext(
3829 "com.sun.star.comp.oox.crypto." + sCryptoType, aArguments, xComponentContext), uno::UNO_QUERY);
3831 if (xPackageEncryption)
3833 // We have an encryptor
3834 // Create new temporary storage for content
3835 xOrigStg = m_pStg;
3836 pSotStorageStream = std::make_shared<SvMemoryStream>();
3837 m_pStg = new SotStorage(*pSotStorageStream);
3843 ErrCode nErrorCode = WriteStorageImpl();
3845 if (xPackageEncryption)
3847 assert(pSotStorageStream && m_pStg && "because always set if xPackageEncryption was set");
3849 m_pStg->Commit();
3850 pSotStorageStream->Seek(0);
3852 // Encrypt data written into temporary storage
3853 xPackageEncryption->setupEncryption(aEncryptionData);
3855 uno::Reference<io::XInputStream > xInputStream(new utl::OSeekableInputStreamWrapper(pSotStorageStream.get(), false));
3856 uno::Sequence<beans::NamedValue> aStreams = xPackageEncryption->encrypt(xInputStream);
3858 m_pStg = std::move(xOrigStg);
3859 for (const beans::NamedValue& aStreamData : aStreams)
3861 // To avoid long paths split and open substorages recursively
3862 // Splitting paths manually, since comphelper::string::split is trimming special characters like \0x01, \0x09
3863 rtl::Reference<SotStorage> pStorage = m_pStg;
3864 OUString sFileName;
3865 sal_Int32 idx = 0;
3866 while (pStorage && idx >= 0)
3868 OUString sPathElem = aStreamData.Name.getToken(0, L'/', idx);
3869 if (!sPathElem.isEmpty())
3871 if (idx < 0)
3873 sFileName = sPathElem;
3875 else
3877 pStorage = pStorage->OpenSotStorage(sPathElem);
3878 if (!pStorage)
3879 break;
3884 if (!pStorage)
3886 nErrorCode = ERRCODE_IO_GENERAL;
3887 break;
3890 rtl::Reference<SotStorageStream> pStream = pStorage->OpenSotStream(sFileName);
3891 if (!pStream)
3893 nErrorCode = ERRCODE_IO_GENERAL;
3894 break;
3896 uno::Sequence<sal_Int8> aStreamContent;
3897 aStreamData.Value >>= aStreamContent;
3898 size_t nBytesWritten = pStream->WriteBytes(aStreamContent.getArray(), aStreamContent.getLength());
3899 if (nBytesWritten != static_cast<size_t>(aStreamContent.getLength()))
3901 nErrorCode = ERRCODE_IO_CANTWRITE;
3902 break;
3907 return nErrorCode;
3909 ErrCode SwWW8Writer::WriteStorageImpl()
3911 // #i34818# - update layout (if present), for SwWriteTable
3912 SwViewShell* pViewShell = m_pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
3913 if( pViewShell != nullptr )
3914 pViewShell->CalcLayout();
3916 SwNodeOffset nMaxNode = m_pDoc->GetNodes().Count();
3917 ::StartProgress( STR_STATSTR_W4WWRITE, 0, sal_Int32(nMaxNode), m_pDoc->GetDocShell() );
3919 // Respect table at the beginning of the document
3921 SwTableNode* pTNd = m_pCurrentPam->GetPointNode().FindTableNode();
3922 if( pTNd && m_bWriteAll )
3923 // start with the table node !!
3924 m_pCurrentPam->GetPoint()->Assign(*pTNd);
3927 // Do the actual export
3928 ErrCode err = ERRCODE_NONE;
3930 bool bDot = mpMedium->GetFilter()->GetName().endsWith("Vorlage");
3931 WW8Export aExport(this, *m_pDoc, m_pCurrentPam, m_pOrigPam, bDot);
3932 m_pExport = &aExport;
3933 err = aExport.ExportDocument( m_bWriteAll );
3934 m_pExport = nullptr;
3937 ::EndProgress( m_pDoc->GetDocShell() );
3938 return err;
3941 ErrCodeMsg SwWW8Writer::WriteMedium( SfxMedium& )
3943 return WriteStorage();
3946 ErrCodeMsg SwWW8Writer::Write( SwPaM& rPaM, SfxMedium& rMed,
3947 const OUString* pFileName )
3949 mpMedium = &rMed;
3950 ErrCodeMsg nRet = StgWriter::Write( rPaM, rMed, pFileName );
3951 mpMedium = nullptr;
3952 return nRet;
3955 MSWordExportBase::MSWordExportBase( SwDoc& rDocument, std::shared_ptr<SwUnoCursor> & pCurrentPam, SwPaM* pOriginalPam )
3956 : m_aMainStg(sMainStream)
3957 , m_pISet(nullptr)
3958 , m_pTopNodeOfHdFtPage(nullptr)
3959 , m_pTableInfo(std::make_shared<ww8::WW8TableInfo>())
3960 , m_nCharFormatStart(0)
3961 , m_nFormatCollStart(0)
3962 , m_nStyleBeforeFly(0)
3963 , m_nLastFormatId(0)
3964 , m_nUniqueList(0)
3965 , m_nHdFtIndex(0)
3966 , m_nOrigRedlineFlags(RedlineFlags::NONE)
3967 , m_bOrigShowChanges(true)
3968 , m_pCurrentPageDesc(nullptr)
3969 , m_pPreviousSectionPageDesc(nullptr)
3970 , m_bFirstTOCNodeWithSection(false)
3971 , m_pChpIter(nullptr)
3972 , m_pParentFrame(nullptr)
3973 , m_bParaInlineHeading(false)
3974 , m_pFlyOffset(nullptr)
3975 , m_eNewAnchorType(RndStdIds::FLY_AS_CHAR)
3976 , m_pStyAttr(nullptr)
3977 , m_pOutFormatNode(nullptr)
3978 , m_pCurrentStyle(nullptr)
3979 , m_pEscher(nullptr)
3980 , m_nTextTyp(0)
3981 , m_bStyDef(false)
3982 , m_bBreakBefore(false)
3983 , m_bOutKF(false)
3984 , m_bOutFlyFrameAttrs(false)
3985 , m_bOutPageDescs(false)
3986 , m_bOutFirstPage(false)
3987 , m_bOutTable(false)
3988 , m_bOutGrf(false)
3989 , m_bInWriteEscher(false)
3990 , m_bStartTOX(false)
3991 , m_bInWriteTOX(false)
3992 , m_bFootnoteAtTextEnd(false)
3993 , m_bEndAtTextEnd(false)
3994 , m_bHasHdr(false)
3995 , m_bHasFtr(false)
3996 , m_bSubstituteBullets(true)
3997 , m_bTabInTOC(false)
3998 , m_bHideTabLeaderAndPageNumbers(false)
3999 , m_bExportModeRTF(false)
4000 , m_bFontSizeWritten(false)
4001 , m_bAddFootnoteTab(false)
4002 , m_rDoc(rDocument)
4003 , m_nCurStart(pCurrentPam->GetPoint()->GetNodeIndex())
4004 , m_nCurEnd(pCurrentPam->GetMark()->GetNodeIndex())
4005 , m_pCurPam(pCurrentPam)
4006 , m_pOrigPam(pOriginalPam)
4010 MSWordExportBase::~MSWordExportBase()
4012 if (m_pUsedNumTable) // all used NumRules
4014 // clear the part of the list array that was copied from the document
4015 // - it's an auto delete array, so the rest of the array which are
4016 // duplicated lists that were added during the export will be deleted.
4017 m_pUsedNumTable->erase(m_pUsedNumTable->begin(), m_pUsedNumTable->begin() + m_pUsedNumTable->size() - m_nUniqueList);
4018 m_pUsedNumTable.reset();
4020 m_oOLEExp.reset();
4021 m_pOCXExp.reset();
4024 WW8Export::WW8Export( SwWW8Writer *pWriter,
4025 SwDoc& rDocument, std::shared_ptr<SwUnoCursor> & pCurrentPam, SwPaM* pOriginalPam,
4026 bool bDot )
4027 : MSWordExportBase( rDocument, pCurrentPam, pOriginalPam )
4028 , m_pTableStrm(nullptr)
4029 , m_pDataStrm(nullptr)
4030 , m_bDot(bDot)
4031 , m_pWriter(pWriter)
4032 , m_pAttrOutput(new WW8AttributeOutput(*this))
4033 , mpAuthorIDs(new SvtSecurityMapPersonalInfo)
4037 WW8Export::~WW8Export()
4041 AttributeOutputBase& WW8Export::AttrOutput() const
4043 return *m_pAttrOutput;
4046 MSWordSections& WW8Export::Sections() const
4048 return *m_pSepx;
4051 SwWW8Writer::SwWW8Writer(std::u16string_view rFltName, const OUString& rBaseURL)
4052 : m_pExport( nullptr ),
4053 mpMedium( nullptr )
4055 assert(rFltName == FILTER_WW8); // WW6/7 export was removed
4056 (void)rFltName;
4057 SetBaseURL( rBaseURL );
4060 SwWW8Writer::~SwWW8Writer()
4064 extern "C" SAL_DLLPUBLIC_EXPORT sal_uInt32 SaveOrDelMSVBAStorage_ww8( SfxObjectShell& rDoc, SotStorage& rStor, sal_Bool bSaveInto, const OUString& rStorageName )
4066 SvxImportMSVBasic aTmp( rDoc, rStor );
4067 return sal_uInt32(aTmp.SaveOrDelMSVBAStorage( bSaveInto, rStorageName ));
4070 extern "C" SAL_DLLPUBLIC_EXPORT void ExportDOC( std::u16string_view rFltName, const OUString& rBaseURL, WriterRef& xRet )
4072 xRet = new SwWW8Writer( rFltName, rBaseURL );
4075 extern "C" SAL_DLLPUBLIC_EXPORT sal_uInt32 GetSaveWarningOfMSVBAStorage_ww8( SfxObjectShell &rDocS )
4077 return sal_uInt32(SvxImportMSVBasic::GetSaveWarningOfMSVBAStorage( rDocS ));
4080 bool WW8_WrPlcFootnoteEdn::WriteText( WW8Export& rWrt )
4082 bool bRet = false;
4083 if (TXT_FTN == m_nTyp)
4085 bRet = WriteGenericText( rWrt, TXT_FTN, rWrt.m_pFib->m_ccpFootnote );
4086 rWrt.m_pFieldFootnote->Finish( rWrt.Fc2Cp( rWrt.Strm().Tell() ),
4087 rWrt.m_pFib->m_ccpText );
4089 else
4091 bRet = WriteGenericText( rWrt, TXT_EDN, rWrt.m_pFib->m_ccpEdn );
4092 rWrt.m_pFieldEdn->Finish( rWrt.Fc2Cp( rWrt.Strm().Tell() ),
4093 rWrt.m_pFib->m_ccpText + rWrt.m_pFib->m_ccpFootnote
4094 + rWrt.m_pFib->m_ccpHdr + rWrt.m_pFib->m_ccpAtn );
4096 return bRet;
4099 void WW8_WrPlcFootnoteEdn::WritePlc( WW8Export& rWrt ) const
4101 if( TXT_FTN == m_nTyp )
4103 WriteGenericPlc( rWrt, TXT_FTN, rWrt.m_pFib->m_fcPlcffndText,
4104 rWrt.m_pFib->m_lcbPlcffndText, rWrt.m_pFib->m_fcPlcffndRef,
4105 rWrt.m_pFib->m_lcbPlcffndRef );
4107 else
4109 WriteGenericPlc( rWrt, TXT_EDN, rWrt.m_pFib->m_fcPlcfendText,
4110 rWrt.m_pFib->m_lcbPlcfendText, rWrt.m_pFib->m_fcPlcfendRef,
4111 rWrt.m_pFib->m_lcbPlcfendRef );
4115 bool WW8_WrPlcAnnotations::WriteText( WW8Export& rWrt )
4117 bool bRet = WriteGenericText( rWrt, TXT_ATN, rWrt.m_pFib->m_ccpAtn );
4118 rWrt.m_pFieldAtn->Finish( rWrt.Fc2Cp( rWrt.Strm().Tell() ),
4119 rWrt.m_pFib->m_ccpText + rWrt.m_pFib->m_ccpFootnote
4120 + rWrt.m_pFib->m_ccpHdr );
4121 return bRet;
4124 void WW8_WrPlcAnnotations::WritePlc( WW8Export& rWrt ) const
4126 WriteGenericPlc( rWrt, TXT_ATN, rWrt.m_pFib->m_fcPlcfandText,
4127 rWrt.m_pFib->m_lcbPlcfandText, rWrt.m_pFib->m_fcPlcfandRef,
4128 rWrt.m_pFib->m_lcbPlcfandRef );
4131 void WW8_WrPlcTextBoxes::WritePlc( WW8Export& rWrt ) const
4133 if( TXT_TXTBOX == m_nTyp )
4135 WriteGenericPlc( rWrt, m_nTyp, rWrt.m_pFib->m_fcPlcftxbxBkd,
4136 rWrt.m_pFib->m_lcbPlcftxbxBkd, rWrt.m_pFib->m_fcPlcftxbxText,
4137 rWrt.m_pFib->m_lcbPlcftxbxText );
4139 else
4141 WriteGenericPlc( rWrt, m_nTyp, rWrt.m_pFib->m_fcPlcfHdrtxbxBkd,
4142 rWrt.m_pFib->m_lcbPlcfHdrtxbxBkd, rWrt.m_pFib->m_fcPlcfHdrtxbxText,
4143 rWrt.m_pFib->m_lcbPlcfHdrtxbxText );
4147 void WW8Export::RestoreMacroCmds()
4149 m_pFib->m_fcCmds = m_pTableStrm->Tell();
4151 uno::Reference < embed::XStorage > xSrcRoot(m_rDoc.GetDocShell()->GetStorage());
4154 uno::Reference < io::XStream > xSrcStream =
4155 xSrcRoot->openStreamElement( SL::aMSMacroCmds, embed::ElementModes::READ );
4156 std::unique_ptr<SvStream> pStream = ::utl::UcbStreamHelper::CreateStream( xSrcStream );
4158 if ( pStream && ERRCODE_NONE == pStream->GetError())
4160 m_pFib->m_lcbCmds = pStream->TellEnd();
4161 pStream->Seek(0);
4163 std::unique_ptr<sal_uInt8[]> pBuffer( new sal_uInt8[m_pFib->m_lcbCmds] );
4164 bool bReadOk = checkRead(*pStream, pBuffer.get(), m_pFib->m_lcbCmds);
4165 if (bReadOk)
4166 m_pTableStrm->WriteBytes(pBuffer.get(), m_pFib->m_lcbCmds);
4169 catch ( const uno::Exception& )
4173 // set len to FIB
4174 m_pFib->m_lcbCmds = m_pTableStrm->Tell() - m_pFib->m_fcCmds;
4177 void WW8SHDLong::Write( WW8Export& rExport )
4179 rExport.InsUInt32( m_cvFore );
4180 rExport.InsUInt32( m_cvBack );
4181 rExport.InsUInt16( 0 ); // ipat
4184 void WW8Export::WriteFormData( const ::sw::mark::Fieldmark& rFieldmark )
4186 const ::sw::mark::Fieldmark* pFieldmark = &rFieldmark;
4187 const ::sw::mark::CheckboxFieldmark* pAsCheckbox = dynamic_cast< const ::sw::mark::CheckboxFieldmark* >( pFieldmark );
4189 if ( ! ( rFieldmark.GetFieldname() == ODF_FORMTEXT ||
4190 rFieldmark.GetFieldname() == ODF_FORMDROPDOWN ||
4191 rFieldmark.GetFieldname() == ODF_FORMCHECKBOX ) )
4193 SAL_WARN( "sw.ww8", "unknown field type" );
4194 return;
4197 int type = 0; // TextFieldmark
4198 if ( pAsCheckbox )
4199 type = 1;
4200 if ( rFieldmark.GetFieldname() == ODF_FORMDROPDOWN )
4201 type=2;
4203 OUString ffname = rFieldmark.GetName();
4204 if (ffname.getLength() > 20)
4205 ffname = ffname.copy(0, 20);
4207 sal_uInt64 nDataStt = m_pDataStrm->Tell();
4208 m_pChpPlc->AppendFkpEntry(Strm().Tell());
4210 WriteChar(0x01);
4211 static sal_uInt8 aArr1[] =
4213 0x03, 0x6a, 0,0,0,0, // sprmCPicLocation
4215 0x06, 0x08, 0x01, // sprmCFData
4216 0x55, 0x08, 0x01, // sprmCFSpec
4217 0x02, 0x08, 0x01 // sprmCFFieldVanish
4219 sal_uInt8* pDataAdr = aArr1 + 2;
4220 Set_UInt32(pDataAdr, nDataStt);
4222 m_pChpPlc->AppendFkpEntry( Strm().Tell(), sizeof( aArr1 ), aArr1 );
4224 struct FFDataHeader
4226 sal_uInt32 version;
4227 sal_uInt16 bits;
4228 sal_uInt16 cch;
4229 sal_uInt16 hps;
4230 FFDataHeader() : version( 0xFFFFFFFF ), bits(0), cch(0), hps(0) {}
4233 FFDataHeader aFieldHeader;
4234 aFieldHeader.bits |= (type & 0x03);
4236 sal_Int32 ffres = 0; // rFieldmark.GetFFRes();
4237 if ( pAsCheckbox && pAsCheckbox->IsChecked() )
4238 ffres = 1;
4239 else if ( type == 2 )
4241 ::sw::mark::Fieldmark::parameter_map_t::const_iterator pResParameter = rFieldmark.GetParameters()->find(ODF_FORMDROPDOWN_RESULT);
4242 if(pResParameter != rFieldmark.GetParameters()->end())
4243 pResParameter->second >>= ffres;
4244 else
4245 ffres = 0;
4247 aFieldHeader.bits |= ( (ffres<<2) & 0x7C );
4249 OUString ffdeftext;
4250 OUString ffformat;
4251 OUString ffhelptext = rFieldmark.GetFieldHelptext();
4252 if ( ffhelptext.getLength() > 255 )
4253 ffhelptext = ffhelptext.copy(0, 255);
4254 OUString ffstattext;
4255 OUString ffentrymcr;
4256 OUString ffexitmcr;
4258 ::sw::mark::Fieldmark::parameter_map_t::const_iterator pParameter
4259 = rFieldmark.GetParameters()->find(u"Type"_ustr);
4260 if (type == 0) // iTypeText
4262 sal_uInt16 nType = 0;
4263 if ( pParameter != rFieldmark.GetParameters()->end() )
4265 OUString aType;
4266 pParameter->second >>= aType;
4267 if ( aType == "number" ) nType = 1;
4268 else if ( aType == "date" ) nType = 2;
4269 else if ( aType == "currentTime" ) nType = 3;
4270 else if ( aType == "currentDate" ) nType = 4;
4271 else if ( aType == "calculated" ) nType = 5;
4272 aFieldHeader.bits |= nType<<11; // FFDataBits-F 00111000 00000000
4275 if ( nType < 3 || nType == 5 ) // not currentTime or currentDate
4277 pParameter = rFieldmark.GetParameters()->find(u"Content"_ustr);
4278 if ( pParameter != rFieldmark.GetParameters()->end() )
4280 OUString aDefaultText;
4281 pParameter->second >>= aDefaultText;
4282 const sal_Int32 nLen = std::min( sal_Int32(255), aDefaultText.getLength() );
4283 ffdeftext = aDefaultText.copy (0, nLen);
4287 pParameter = rFieldmark.GetParameters()->find(u"MaxLength"_ustr);
4288 if ( pParameter != rFieldmark.GetParameters()->end() )
4290 sal_uInt16 nLength = 0;
4291 pParameter->second >>= nLength;
4292 nLength = std::min( sal_uInt16(32767), nLength );
4293 aFieldHeader.cch = nLength;
4296 pParameter = rFieldmark.GetParameters()->find(u"Format"_ustr);
4297 if ( pParameter != rFieldmark.GetParameters()->end() )
4299 OUString aFormat;
4300 pParameter->second >>= aFormat;
4301 const sal_Int32 nLen = std::min( sal_Int32(64), aFormat.getLength() );
4302 ffformat = aFormat.copy(0, nLen);
4306 pParameter = rFieldmark.GetParameters()->find(u"Help"_ustr); //help
4307 if ( ffhelptext.isEmpty() && pParameter != rFieldmark.GetParameters()->end() )
4309 OUString aHelpText;
4310 pParameter->second >>= aHelpText;
4311 const sal_Int32 nLen = std::min( sal_Int32(255), aHelpText.getLength() );
4312 ffhelptext = aHelpText.copy (0, nLen);
4314 if ( !ffhelptext.isEmpty() )
4315 aFieldHeader.bits |= 0x1<<7;
4317 pParameter = rFieldmark.GetParameters()->find(u"Description"_ustr); // doc tooltip
4318 if ( pParameter == rFieldmark.GetParameters()->end() )
4319 pParameter = rFieldmark.GetParameters()->find(u"Hint"_ustr); //docx tooltip
4320 if ( pParameter != rFieldmark.GetParameters()->end() )
4322 OUString aStatusText;
4323 pParameter->second >>= aStatusText;
4324 const sal_Int32 nLen = std::min( sal_Int32(138), aStatusText.getLength() );
4325 ffstattext = aStatusText.copy (0, nLen);
4327 if ( !ffstattext.isEmpty() )
4328 aFieldHeader.bits |= 0x1<<8;
4330 pParameter = rFieldmark.GetParameters()->find(u"EntryMacro"_ustr);
4331 if ( pParameter != rFieldmark.GetParameters()->end() )
4333 OUString aEntryMacro;
4334 pParameter->second >>= aEntryMacro;
4335 const sal_Int32 nLen = std::min( sal_Int32(32), aEntryMacro.getLength() );
4336 ffentrymcr = aEntryMacro.copy (0, nLen);
4339 pParameter = rFieldmark.GetParameters()->find(u"ExitMacro"_ustr);
4340 if ( pParameter != rFieldmark.GetParameters()->end() )
4342 OUString aExitMacro;
4343 pParameter->second >>= aExitMacro;
4344 const sal_Int32 nLen = std::min( sal_Int32(32), aExitMacro.getLength() );
4345 ffexitmcr = aExitMacro.copy (0, nLen);
4348 std::vector< OUString > aListItems;
4349 if (type==2)
4351 aFieldHeader.bits |= 0x8000; // ffhaslistbox
4352 const ::sw::mark::Fieldmark::parameter_map_t* const pParameters = rFieldmark.GetParameters();
4353 ::sw::mark::Fieldmark::parameter_map_t::const_iterator pListEntries = pParameters->find(ODF_FORMDROPDOWN_LISTENTRY);
4354 if(pListEntries != pParameters->end())
4356 uno::Sequence< OUString > vListEntries;
4357 pListEntries->second >>= vListEntries;
4358 aListItems.reserve(vListEntries.getLength());
4359 copy(std::cbegin(vListEntries), std::cend(vListEntries), back_inserter(aListItems));
4363 const sal_uInt8 aFieldData[] =
4365 0x44,0, // the start of "next" data
4366 0,0,0,0,0,0,0,0,0,0, // PIC-Structure! /10
4367 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // | /16
4368 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // | /16
4369 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // | /16
4370 0,0,0,0, // / /4
4372 sal_uInt32 slen = sizeof(sal_uInt32)
4373 + sizeof(aFieldData)
4374 + sizeof( aFieldHeader.version ) + sizeof( aFieldHeader.bits ) + sizeof( aFieldHeader.cch ) + sizeof( aFieldHeader.hps )
4375 + 2*ffname.getLength() + 4
4376 + 2*ffformat.getLength() + 4
4377 + 2*ffhelptext.getLength() + 4
4378 + 2*ffstattext.getLength() + 4
4379 + 2*ffentrymcr.getLength() + 4
4380 + 2*ffexitmcr.getLength() + 4;
4381 if ( type )
4382 slen += 2; // wDef
4383 else
4384 slen += 2*ffdeftext.getLength() + 4; //xstzTextDef
4385 if ( type==2 ) {
4386 slen += 2; // sttb ( fExtend )
4387 slen += 4; // for num of list items
4388 const int items = aListItems.size();
4389 for( int i = 0; i < items; i++ ) {
4390 const OUString& item = aListItems[i];
4391 slen += 2 * item.getLength() + 2;
4395 m_pDataStrm->WriteUInt32( slen );
4397 int len = sizeof( aFieldData );
4398 OSL_ENSURE( len == 0x44-sizeof(sal_uInt32), "SwWW8Writer::WriteFormData(..) - wrong aFieldData length" );
4399 m_pDataStrm->WriteBytes( aFieldData, len );
4401 m_pDataStrm->WriteUInt32( aFieldHeader.version ).WriteUInt16( aFieldHeader.bits ).WriteUInt16( aFieldHeader.cch ).WriteUInt16( aFieldHeader.hps );
4403 SwWW8Writer::WriteString_xstz( *m_pDataStrm, ffname, true ); // Form field name
4405 if ( !type )
4406 SwWW8Writer::WriteString_xstz( *m_pDataStrm, ffdeftext, true );
4407 if ( type )
4408 m_pDataStrm->WriteUInt16( 0 );
4410 SwWW8Writer::WriteString_xstz( *m_pDataStrm, ffformat, true );
4411 SwWW8Writer::WriteString_xstz( *m_pDataStrm, ffhelptext, true );
4412 SwWW8Writer::WriteString_xstz( *m_pDataStrm, ffstattext, true );
4413 SwWW8Writer::WriteString_xstz( *m_pDataStrm, ffentrymcr, true );
4414 SwWW8Writer::WriteString_xstz( *m_pDataStrm, ffexitmcr, true );
4415 if (type==2) {
4416 m_pDataStrm->WriteUInt16( 0xFFFF );
4417 const int items=aListItems.size();
4418 m_pDataStrm->WriteUInt32( items );
4419 for(int i=0;i<items;i++) {
4420 const OUString& item=aListItems[i];
4421 SwWW8Writer::WriteString_xstz( *m_pDataStrm, item, false );
4426 void WW8Export::WriteHyperlinkData( const sw::mark::Fieldmark& /*rFieldmark*/ )
4428 //@TODO implement me !!!
4431 void WW8AttributeOutput::TableNodeInfoInner(const ww8::WW8TableNodeInfoInner::Pointer_t& pNodeInfoInner)
4433 SVBT16 nStyle;
4434 ShortToSVBT16( m_rWW8Export.m_nStyleBeforeFly, nStyle );
4436 #ifdef DBG_UTIL
4437 SAL_INFO( "sw.ww8", "<OutWW8_TableNodeInfoInner>" << pNodeInfoInner->toString());
4438 #endif
4440 m_rWW8Export.m_pO->clear();
4442 sal_uInt32 nShadowsBefore = pNodeInfoInner->getShadowsBefore();
4443 if (nShadowsBefore > 0)
4445 ww8::WW8TableNodeInfoInner::Pointer_t
4446 pTmpNodeInfoInner = std::make_shared<ww8::WW8TableNodeInfoInner>(nullptr);
4448 pTmpNodeInfoInner->setDepth(pNodeInfoInner->getDepth());
4449 pTmpNodeInfoInner->setEndOfCell(true);
4451 for (sal_uInt32 n = 0; n < nShadowsBefore; ++n)
4453 m_rWW8Export.WriteCR(pTmpNodeInfoInner);
4455 m_rWW8Export.m_pO->insert( m_rWW8Export.m_pO->end(), nStyle, nStyle+2 ); // Style #
4456 TableInfoCell(pTmpNodeInfoInner);
4457 m_rWW8Export.m_pPapPlc->AppendFkpEntry
4458 ( m_rWW8Export.Strm().Tell(), m_rWW8Export.m_pO->size(), m_rWW8Export.m_pO->data() );
4460 m_rWW8Export.m_pO->clear();
4464 if (pNodeInfoInner->isEndOfCell())
4466 SAL_INFO( "sw.ww8", "<endOfCell/>" );
4468 m_rWW8Export.WriteCR(pNodeInfoInner);
4470 m_rWW8Export.m_pO->insert( m_rWW8Export.m_pO->end(), nStyle, nStyle+2 ); // Style #
4471 TableInfoCell(pNodeInfoInner);
4472 m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), m_rWW8Export.m_pO->size(), m_rWW8Export.m_pO->data() );
4474 m_rWW8Export.m_pO->clear();
4477 sal_uInt32 nShadowsAfter = pNodeInfoInner->getShadowsAfter();
4478 if (nShadowsAfter > 0)
4480 ww8::WW8TableNodeInfoInner::Pointer_t
4481 pTmpNodeInfoInner= std::make_shared<ww8::WW8TableNodeInfoInner>(nullptr);
4483 pTmpNodeInfoInner->setDepth(pNodeInfoInner->getDepth());
4484 pTmpNodeInfoInner->setEndOfCell(true);
4486 for (sal_uInt32 n = 0; n < nShadowsAfter; ++n)
4488 m_rWW8Export.WriteCR(pTmpNodeInfoInner);
4490 m_rWW8Export.m_pO->insert( m_rWW8Export.m_pO->end(), nStyle, nStyle+2 ); // Style #
4491 TableInfoCell(pTmpNodeInfoInner);
4492 m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), m_rWW8Export.m_pO->size(), m_rWW8Export.m_pO->data() );
4494 m_rWW8Export.m_pO->clear();
4498 if (pNodeInfoInner->isEndOfLine())
4500 SAL_INFO( "sw.ww8", "<endOfLine/>" );
4502 TableRowEnd(pNodeInfoInner->getDepth());
4504 ShortToSVBT16(0, nStyle);
4505 m_rWW8Export.m_pO->insert( m_rWW8Export.m_pO->end(), nStyle, nStyle+2 ); // Style #
4506 TableInfoRow(pNodeInfoInner);
4507 m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), m_rWW8Export.m_pO->size(), m_rWW8Export.m_pO->data() );
4509 m_rWW8Export.m_pO->clear();
4511 SAL_INFO( "sw.ww8", "</OutWW8_TableNodeInfoInner>" );
4514 void MSWordExportBase::OutputStartNode( const SwStartNode & rNode)
4517 ww8::WW8TableNodeInfo::Pointer_t pNodeInfo =
4518 m_pTableInfo->getTableNodeInfo( &rNode );
4520 if (pNodeInfo)
4522 #ifdef DBG_UTIL
4523 SAL_INFO( "sw.ww8", pNodeInfo->toString());
4524 #endif
4525 const ww8::WW8TableNodeInfo::Inners_t aInners = pNodeInfo->getInners();
4526 ww8::WW8TableNodeInfo::Inners_t::const_reverse_iterator aIt(aInners.rbegin());
4527 ww8::WW8TableNodeInfo::Inners_t::const_reverse_iterator aEnd(aInners.rend());
4528 while (aIt != aEnd)
4530 ww8::WW8TableNodeInfoInner::Pointer_t pInner = aIt->second;
4532 AttrOutput().TableNodeInfoInner(pInner);
4533 ++aIt;
4536 SAL_INFO( "sw.ww8", "</OutWW8_SwStartNode>" );
4539 void MSWordExportBase::OutputEndNode( const SwEndNode &rNode )
4541 #ifdef DBG_UTIL
4542 SAL_INFO( "sw.ww8", "<OutWW8_SwEndNode>" << dbg_out(&rNode));
4543 #endif
4545 ww8::WW8TableNodeInfo::Pointer_t pNodeInfo = m_pTableInfo->getTableNodeInfo( &rNode );
4547 if (pNodeInfo)
4549 #ifdef DBG_UTIL
4550 SAL_INFO( "sw.ww8", pNodeInfo->toString());
4551 #endif
4552 const ww8::WW8TableNodeInfo::Inners_t aInners = pNodeInfo->getInners();
4553 for (const auto& rEntry : aInners)
4555 ww8::WW8TableNodeInfoInner::Pointer_t pInner = rEntry.second;
4556 AttrOutput().TableNodeInfoInner(pInner);
4559 SAL_INFO( "sw.ww8", "</OutWW8_SwEndNode>" );
4562 const NfKeywordTable & MSWordExportBase::GetNfKeywordTable()
4564 if (m_pKeyMap == nullptr)
4566 m_pKeyMap = std::make_shared<NfKeywordTable>();
4567 NfKeywordTable & rKeywordTable = *m_pKeyMap;
4568 rKeywordTable[NF_KEY_D] = "d";
4569 rKeywordTable[NF_KEY_DD] = "dd";
4570 rKeywordTable[NF_KEY_DDD] = "ddd";
4571 rKeywordTable[NF_KEY_DDDD] = "dddd";
4572 rKeywordTable[NF_KEY_M] = "M";
4573 rKeywordTable[NF_KEY_MM] = "MM";
4574 rKeywordTable[NF_KEY_MMM] = "MMM";
4575 rKeywordTable[NF_KEY_MMMM] = "MMMM";
4576 rKeywordTable[NF_KEY_NN] = "ddd";
4577 rKeywordTable[NF_KEY_NNN] = "dddd";
4578 rKeywordTable[NF_KEY_NNNN] = "dddd";
4579 rKeywordTable[NF_KEY_YY] = "yy";
4580 rKeywordTable[NF_KEY_YYYY] = "yyyy";
4581 rKeywordTable[NF_KEY_H] = "H";
4582 rKeywordTable[NF_KEY_HH] = "HH";
4583 rKeywordTable[NF_KEY_MI] = "m";
4584 rKeywordTable[NF_KEY_MMI] = "mm";
4585 rKeywordTable[NF_KEY_S] = "s";
4586 rKeywordTable[NF_KEY_SS] = "ss";
4587 rKeywordTable[NF_KEY_AMPM] = "AM/PM";
4590 return *m_pKeyMap;
4593 OUString MSWordExportBase::BookmarkToWord(const OUString& rBookmark, bool* pIsMove, bool* pIsFrom)
4595 OUString sLookup = rBookmark;
4596 if (pIsMove)
4598 static constexpr OUStringLiteral MoveFrom_Bookmark_NamePrefix = u"__RefMoveFrom__";
4599 static constexpr OUStringLiteral MoveTo_Bookmark_NamePrefix = u"__RefMoveTo__";
4600 if (rBookmark.startsWith(MoveFrom_Bookmark_NamePrefix, &sLookup))
4602 *pIsMove = true;
4603 *pIsFrom = true;
4605 else if (rBookmark.startsWith(MoveTo_Bookmark_NamePrefix, &sLookup))
4607 *pIsMove = true;
4608 *pIsFrom = false;
4611 if (auto it = m_aBookmarkToWord.find(sLookup); it != m_aBookmarkToWord.end())
4612 return it->second;
4614 OUString sRet
4615 = INetURLObject::encode(sLookup.replace(' ', '_'), // Spaces are prohibited in bookmark name
4616 INetURLObject::PART_REL_SEGMENT_EXTRA,
4617 INetURLObject::EncodeMechanism::All, RTL_TEXTENCODING_ASCII_US);
4618 // Unicode letters are allowed
4619 sRet = INetURLObject::decode(sRet, INetURLObject::DecodeMechanism::Unambiguous,
4620 RTL_TEXTENCODING_UTF8);
4622 /*#i15387#*/
4623 // Word has 40 character limit for bookmarks: [MS-OE376] Part 4 Sect. 2.13.6.2, bookmarkStart
4624 if (sRet.getLength() > 40)
4626 // Generate a unique bookmark name
4627 sRet = sRet.copy(0, 40);
4628 for (sal_uInt32 n = 1; n; ++n)
4630 if (m_aWordBookmarks.find(sRet) == m_aWordBookmarks.end())
4631 break;
4632 auto num = OUString::number(n, 36);
4633 sRet = sRet.subView(0, 40 - num.length) + num;
4637 m_aBookmarkToWord[sLookup] = sRet;
4638 m_aWordBookmarks.insert(sRet);
4639 return sRet;
4642 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */