1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <algorithm>
21 
22 #include <memory>
23 
24 #include <com/sun/star/i18n/ScriptType.hpp>
25 #include <hintids.hxx>
26 #include <editeng/boxitem.hxx>
27 #include <editeng/fontitem.hxx>
28 #include <svx/svdobj.hxx>
29 #include <svx/svdotext.hxx>
30 #include <svx/svdouno.hxx>
31 #include <editeng/lrspitem.hxx>
32 #include <editeng/fhgtitem.hxx>
33 #include <doc.hxx>
34 #include "wrtww8.hxx"
35 #include <docary.hxx>
36 #include <poolfmt.hxx>
37 #include <fmtpdsc.hxx>
38 #include <pagedesc.hxx>
39 #include <ndtxt.hxx>
40 #include <ftninfo.hxx>
41 #include <fmthdft.hxx>
42 #include <section.hxx>
43 #include <fmtcntnt.hxx>
44 #include <fmtftn.hxx>
45 #include <ndindex.hxx>
46 #include <txtftn.hxx>
47 #include <charfmt.hxx>
48 #include <docufld.hxx>
49 #include <dcontact.hxx>
50 #include <fmtcnct.hxx>
51 #include <ftnidx.hxx>
52 #include <fmtclds.hxx>
53 #include <lineinfo.hxx>
54 #include <fmtline.hxx>
55 #include <swtable.hxx>
56 #include <redline.hxx>
57 #include <msfilter.hxx>
58 #include <swmodule.hxx>
59 #include <charatr.hxx>
60 
61 #include "sprmids.hxx"
62 
63 #include "writerhelper.hxx"
64 #include "writerwordglue.hxx"
65 #include <wwstyles.hxx>
66 #include "ww8par.hxx"
67 #include "ww8attributeoutput.hxx"
68 #include "docxattributeoutput.hxx"
69 #include "rtfattributeoutput.hxx"
70 
71 #include <unordered_set>
72 
73 using namespace css;
74 using namespace sw::util;
75 using namespace nsHdFtFlags;
76 
77 /// For the output of sections.
78 struct WW8_PdAttrDesc
79 {
80     std::unique_ptr<sal_uInt8[]> m_pData;
81     sal_uInt16 m_nLen;
82     WW8_FC m_nSepxFcPos;
WW8_PdAttrDescWW8_PdAttrDesc83     WW8_PdAttrDesc() : m_nLen(0), m_nSepxFcPos(0xffffffff) /*default: none*/
84         { }
85 };
86 
87 namespace {
88 
89 struct WW8_SED
90 {
91     SVBT16 aBits1;      // orientation change + internal, Default: 6
92     SVBT32 fcSepx;      //  FC  file offset to beginning of SEPX for section.
93                         //  0xFFFFFFFF for no Sprms
94     SVBT16 fnMpr;       //  used internally by Windows Word, Default: 0
95     SVBT32 fcMpr;       //  FC, points to offset in FC space for MacWord
96                         // Default: 0xffffffff ( nothing )
97                         //  cbSED is 12 (decimal)), C (hex).
98 };
99 
100 }
101 
102 // class WW8_WrPlc0 is only used for header and footer positioning
103 // ie there is no content support structure
104 class WW8_WrPlc0
105 {
106 private:
107     std::vector<sal_uLong> aPos;      // PTRARR of CPs / FCs
108     sal_uLong nOfs;
109 
110     WW8_WrPlc0(WW8_WrPlc0 const&) = delete;
111     WW8_WrPlc0& operator=(WW8_WrPlc0 const&) = delete;
112 
113 public:
114     explicit WW8_WrPlc0( sal_uLong nOffset );
Count() const115     sal_uInt16 Count() const                { return aPos.size(); }
116     void Append( sal_uLong nStartCpOrFc );
117     void Write( SvStream& rStrm );
118 };
119 
120 //  Styles
121 
122 #define WW8_RESERVED_SLOTS 15
123 
124 // GetId( SwCharFormat ) for use in text -> zero is not allowed,
125 // use "Default Char Style" instead
GetId(const SwCharFormat * pFormat) const126 sal_uInt16 MSWordExportBase::GetId( const SwCharFormat* pFormat ) const
127 {
128     sal_uInt16 nRet = m_pStyles->GetSlot( pFormat );
129     return ( nRet != 0x0fff ) ? nRet : 10;      // Default Char Style
130 }
131 
132 // GetId( SwTextFormatColl ) for use in TextNodes -> zero is not allowed,
133 // "Standard" instead
GetId(const SwTextFormatColl & rColl) const134 sal_uInt16 MSWordExportBase::GetId( const SwTextFormatColl& rColl ) const
135 {
136     sal_uInt16 nRet = m_pStyles->GetSlot( &rColl );
137     return ( nRet != 0xfff ) ? nRet : 0;        // Default TextFormatColl
138 }
139 
140 //typedef pFormatT
MSWordStyles(MSWordExportBase & rExport,bool bListStyles)141 MSWordStyles::MSWordStyles( MSWordExportBase& rExport, bool bListStyles )
142     : m_rExport( rExport ),
143     m_bListStyles(bListStyles)
144 {
145     // if exist any Foot-/End-Notes then get from the EndNoteInfo struct
146     // the CharFormats. They will create it!
147     if ( !m_rExport.m_rDoc.GetFootnoteIdxs().empty() )
148     {
149         m_rExport.m_rDoc.GetEndNoteInfo().GetAnchorCharFormat( m_rExport.m_rDoc );
150         m_rExport.m_rDoc.GetEndNoteInfo().GetCharFormat( m_rExport.m_rDoc );
151         m_rExport.m_rDoc.GetFootnoteInfo().GetAnchorCharFormat( m_rExport.m_rDoc );
152         m_rExport.m_rDoc.GetFootnoteInfo().GetCharFormat( m_rExport.m_rDoc );
153     }
154     sal_uInt32 nAlloc = WW8_RESERVED_SLOTS + m_rExport.m_rDoc.GetCharFormats()->size() - 1 +
155                                          m_rExport.m_rDoc.GetTextFormatColls()->size() - 1 +
156                                          (bListStyles ? m_rExport.m_rDoc.GetNumRuleTable().size() - 1 : 0);
157     nAlloc = std::min<sal_uInt32>(nAlloc, MSWORD_MAX_STYLES_LIMIT);
158 
159     // somewhat generous ( free for up to 15 )
160     m_aFormatA.resize(nAlloc, nullptr);
161     memset( m_aHeadingParagraphStyles, -1 , MAXLEVEL * sizeof( sal_uInt16));
162 
163     BuildStylesTable();
164     BuildStyleIds();
165 }
166 
~MSWordStyles()167 MSWordStyles::~MSWordStyles()
168 {
169 }
170 
171 // Sty_SetWWSlot() dependencies for the styles -> zero is allowed
GetSlot(const SwFormat * pFormat) const172 sal_uInt16 MSWordStyles::GetSlot( const SwFormat* pFormat ) const
173 {
174     sal_uInt16 n;
175     for ( n = 0; n < m_nUsedSlots; n++ )
176         if ( m_aFormatA[n] == pFormat )
177             return n;
178     return 0xfff;                   // 0xfff: WW: zero
179 }
180 
BuildGetSlot(const SwFormat & rFormat)181 sal_uInt16 MSWordStyles::BuildGetSlot( const SwFormat& rFormat )
182 {
183     sal_uInt16 nRet = rFormat.GetPoolFormatId();
184     switch ( nRet )
185     {
186         case RES_POOLCOLL_STANDARD:
187             nRet = 0;
188             break;
189 
190         case RES_POOLCOLL_HEADLINE1:
191         case RES_POOLCOLL_HEADLINE2:
192         case RES_POOLCOLL_HEADLINE3:
193         case RES_POOLCOLL_HEADLINE4:
194         case RES_POOLCOLL_HEADLINE5:
195         case RES_POOLCOLL_HEADLINE6:
196         case RES_POOLCOLL_HEADLINE7:
197         case RES_POOLCOLL_HEADLINE8:
198         case RES_POOLCOLL_HEADLINE9:
199             nRet -= RES_POOLCOLL_HEADLINE1-1;
200             break;
201 
202         default:
203             nRet = m_nUsedSlots++;
204             break;
205     }
206     return nRet;
207 }
208 
209 
GetWWId(const SwFormat & rFormat)210 sal_uInt16 MSWordStyles::GetWWId( const SwFormat& rFormat )
211 {
212     sal_uInt16 nRet = ww::stiUser;    // user style as default
213     sal_uInt16 nPoolId = rFormat.GetPoolFormatId();
214     if( nPoolId == RES_POOLCOLL_STANDARD )
215         nRet = 0;
216     else if( nPoolId >= RES_POOLCOLL_HEADLINE1 &&
217              nPoolId <= RES_POOLCOLL_HEADLINE9 )
218         nRet = static_cast< sal_uInt16 >(nPoolId + 1 - RES_POOLCOLL_HEADLINE1);
219     else if( nPoolId >= RES_POOLCOLL_TOX_IDX1 &&
220              nPoolId <= RES_POOLCOLL_TOX_IDX3 )
221         nRet = static_cast< sal_uInt16 >(nPoolId + 10 - RES_POOLCOLL_TOX_IDX1);
222     else if( nPoolId >= RES_POOLCOLL_TOX_CNTNT1 &&
223              nPoolId <= RES_POOLCOLL_TOX_CNTNT5 )
224         nRet = static_cast< sal_uInt16 >(nPoolId + 19 - RES_POOLCOLL_TOX_CNTNT1);
225     else if( nPoolId >= RES_POOLCOLL_TOX_CNTNT6 &&
226              nPoolId <= RES_POOLCOLL_TOX_CNTNT9 )
227         nRet = static_cast< sal_uInt16 >(nPoolId + 24 - RES_POOLCOLL_TOX_CNTNT6);
228     else
229         switch( nPoolId )
230         {
231         case RES_POOLCOLL_FOOTNOTE:         nRet = 29;  break;
232         case RES_POOLCOLL_MARGINAL:         nRet = 30;  break;
233         case RES_POOLCOLL_HEADER:           nRet = 31;  break;
234         case RES_POOLCOLL_FOOTER:           nRet = 32;  break;
235         case RES_POOLCOLL_TOX_IDXH:         nRet = 33;  break;
236         case RES_POOLCOLL_LABEL:            nRet = 34;  break;
237         case RES_POOLCOLL_LABEL_DRAWING:    nRet = 35;  break;
238         case RES_POOLCOLL_ENVELOPE_ADDRESS: nRet = 36;  break;
239         case RES_POOLCOLL_SEND_ADDRESS:     nRet = 37;  break;
240         case RES_POOLCOLL_ENDNOTE:          nRet = 43;  break;
241         case RES_POOLCOLL_TOX_AUTHORITIESH: nRet = 44;  break;
242         case RES_POOLCOLL_TOX_CNTNTH:       nRet = 46;  break;
243         case RES_POOLCOLL_BULLET_LEVEL1:    nRet = 48;  break;
244         case RES_POOLCOLL_LISTS_BEGIN:      nRet = 47;  break;
245         case RES_POOLCOLL_NUM_LEVEL1:       nRet = 49;  break;
246         case RES_POOLCOLL_BULLET_LEVEL2:    nRet = 54;  break;
247         case RES_POOLCOLL_BULLET_LEVEL3:    nRet = 55;  break;
248         case RES_POOLCOLL_BULLET_LEVEL4:    nRet = 56;  break;
249         case RES_POOLCOLL_BULLET_LEVEL5:    nRet = 57;  break;
250         case RES_POOLCOLL_NUM_LEVEL2:       nRet = 58;  break;
251         case RES_POOLCOLL_NUM_LEVEL3:       nRet = 59;  break;
252         case RES_POOLCOLL_NUM_LEVEL4:       nRet = 60;  break;
253         case RES_POOLCOLL_NUM_LEVEL5:       nRet = 61;  break;
254         case RES_POOLCOLL_DOC_TITLE:        nRet = 62;  break;
255         case RES_POOLCOLL_DOC_APPENDIX:     nRet = 63;  break;
256         case RES_POOLCOLL_SIGNATURE:        nRet = 64;  break;
257         case RES_POOLCOLL_TEXT:             nRet = 66;  break;
258         case RES_POOLCOLL_TEXT_MOVE:        nRet = 67;  break;
259         case RES_POOLCOLL_BULLET_NONUM1:    nRet = 68;  break;
260         case RES_POOLCOLL_BULLET_NONUM2:    nRet = 69;  break;
261         case RES_POOLCOLL_BULLET_NONUM3:    nRet = 70;  break;
262         case RES_POOLCOLL_BULLET_NONUM4:    nRet = 71;  break;
263         case RES_POOLCOLL_BULLET_NONUM5:    nRet = 72;  break;
264         case RES_POOLCOLL_DOC_SUBTITLE:     nRet = 74;  break;
265         case RES_POOLCOLL_GREETING:         nRet = 75;  break;
266         case RES_POOLCOLL_TEXT_IDENT:       nRet = 77;  break;
267 
268         case RES_POOLCHR_FOOTNOTE_ANCHOR:   nRet = 38;  break;
269         case RES_POOLCHR_ENDNOTE_ANCHOR:    nRet = 42;  break;
270         case RES_POOLCHR_INET_NORMAL:       nRet = 85;  break;
271         case RES_POOLCHR_INET_VISIT:        nRet = 86;  break;
272         case RES_POOLCHR_HTML_STRONG:       nRet = 87;  break;
273         case RES_POOLCHR_HTML_EMPHASIS:     nRet = 88;  break;
274         case RES_POOLCHR_LINENUM:           nRet = 40;  break;
275         case RES_POOLCHR_PAGENO:            nRet = 41;  break;
276         }
277     return nRet;
278 }
279 
BuildStylesTable()280 void MSWordStyles::BuildStylesTable()
281 {
282     m_nUsedSlots = WW8_RESERVED_SLOTS;  // reserved slots for standard, headingX, and others
283 
284     const SwCharFormats& rArr = *m_rExport.m_rDoc.GetCharFormats();       // first CharFormat
285     // the default character style ( 0 ) will not be outputted !
286     for( size_t n = 1; n < rArr.size() && m_nUsedSlots < MSWORD_MAX_STYLES_LIMIT; n++ )
287     {
288         SwCharFormat* pFormat = rArr[n];
289         m_aFormatA[ BuildGetSlot( *pFormat ) ] = pFormat;
290     }
291 
292     const SwTextFormatColls& rArr2 = *m_rExport.m_rDoc.GetTextFormatColls();   // then TextFormatColls
293     // the default character style ( 0 ) will not be outputted !
294     for( size_t n = 1; n < rArr2.size() && m_nUsedSlots < MSWORD_MAX_STYLES_LIMIT; n++ )
295     {
296         SwTextFormatColl* pFormat = rArr2[n];
297         sal_uInt16 nId = BuildGetSlot( *pFormat ) ;
298         m_aFormatA[ nId ] = pFormat;
299         if ( pFormat->IsAssignedToListLevelOfOutlineStyle() )
300         {
301             int nLvl = pFormat->GetAssignedOutlineStyleLevel() ;
302             if (nLvl >= 0 && nLvl < MAXLEVEL)
303                 m_aHeadingParagraphStyles[nLvl] = nId ;
304         }
305     }
306 
307     if (!m_bListStyles)
308         return;
309 
310     const SwNumRuleTable& rNumRuleTable = m_rExport.m_rDoc.GetNumRuleTable();
311     for (size_t i = 0; i < rNumRuleTable.size() && m_nUsedSlots < MSWORD_MAX_STYLES_LIMIT; ++i)
312     {
313         const SwNumRule* pNumRule = rNumRuleTable[i];
314         if (pNumRule->IsAutoRule() || pNumRule->GetName().startsWith("WWNum"))
315             continue;
316         sal_uInt16 nSlot = BuildGetSlot(*pNumRule);
317         m_aNumRules[nSlot] = pNumRule;
318     }
319 }
320 
CreateStyleId(const OUString & rName)321 OString MSWordStyles::CreateStyleId(const OUString &rName)
322 {
323     OStringBuffer aStyleIdBuf(rName.getLength());
324     for (int i = 0; i < rName.getLength(); ++i)
325     {
326         sal_Unicode nChar = rName[i];
327         if (('0' <= nChar && nChar <= '9') ||
328             ('a' <= nChar && nChar <= 'z') ||
329             ('A' <= nChar && nChar <= 'Z'))
330         {
331             // first letter should be uppercase
332             if (aStyleIdBuf.isEmpty() && 'a' <= nChar && nChar <= 'z')
333                 aStyleIdBuf.append(char(nChar - ('a' - 'A')));
334             else
335                 aStyleIdBuf.append(char(nChar));
336         }
337     }
338     return aStyleIdBuf.makeStringAndClear();
339 }
340 
BuildStyleIds()341 void MSWordStyles::BuildStyleIds()
342 {
343     std::unordered_set<OString> aUsed;
344 
345     m_aStyleIds.emplace_back("Normal");
346     aUsed.insert("normal");
347 
348     for (sal_uInt16 n = 1; n < m_nUsedSlots; ++n)
349     {
350         OUString aName;
351         if (m_aFormatA[n])
352             aName = m_aFormatA[n]->GetName();
353         else if (m_aNumRules.find(n) != m_aNumRules.end())
354             aName = m_aNumRules[n]->GetName();
355 
356         OString aStyleId = CreateStyleId(aName);
357 
358         if (aStyleId.isEmpty())
359             aStyleId = "Style";
360 
361         OString aLower(aStyleId.toAsciiLowerCase());
362 
363         // check for uniqueness & construct something unique if we have to
364         if (aUsed.insert(aLower).second)
365         {
366             m_aStyleIds.push_back(aStyleId);
367         }
368         else
369         {
370             int nFree = 1;
371             while (!aUsed.insert(aLower + OString::number(nFree)).second)
372                 ++nFree;
373 
374             m_aStyleIds.emplace_back(aStyleId + OString::number(nFree));
375         }
376     }
377 }
378 
GetStyleId(sal_uInt16 nId) const379 OString const & MSWordStyles::GetStyleId(sal_uInt16 nId) const
380 {
381     return m_aStyleIds[nId];
382 }
383 
384 /// For WW8 only - extend pO so that the size of pTableStrm is even.
impl_SkipOdd(std::unique_ptr<ww::bytes> const & pO,std::size_t nTableStrmTell)385 static void impl_SkipOdd(std::unique_ptr<ww::bytes> const& pO, std::size_t nTableStrmTell)
386 {
387     if ( ( nTableStrmTell + pO->size() ) & 1 )     // start on even
388         pO->push_back( sal_uInt8(0) );         // Address
389 }
390 
EndStyle()391 void WW8AttributeOutput::EndStyle()
392 {
393     impl_SkipOdd( m_rWW8Export.pO, m_rWW8Export.pTableStrm->Tell() );
394 
395     short nLen = m_rWW8Export.pO->size() - 2;            // length of the style
396     sal_uInt8* p = m_rWW8Export.pO->data() + nPOPosStdLen1;
397     ShortToSVBT16( nLen, p );               // add
398     p = m_rWW8Export.pO->data() + nPOPosStdLen2;
399     ShortToSVBT16( nLen, p );               // also
400 
401     m_rWW8Export.pTableStrm->WriteBytes(m_rWW8Export.pO->data(), m_rWW8Export.pO->size());
402     m_rWW8Export.pO->clear();
403 }
404 
StartStyle(const OUString & rName,StyleType eType,sal_uInt16 nWwBase,sal_uInt16 nWwNext,sal_uInt16 nWwId,sal_uInt16,bool bAutoUpdate)405 void WW8AttributeOutput::StartStyle( const OUString& rName, StyleType eType, sal_uInt16 nWwBase,
406     sal_uInt16 nWwNext, sal_uInt16 nWwId, sal_uInt16 /*nId*/, bool bAutoUpdate )
407 {
408     sal_uInt8 aWW8_STD[ sizeof( WW8_STD ) ] = {};
409     sal_uInt8* pData = aWW8_STD;
410 
411     sal_uInt16 nBit16 = 0x1000;         // fInvalHeight
412     nBit16 |= (ww::stiNil & nWwId);
413     Set_UInt16( pData, nBit16 );
414 
415     nBit16 = nWwBase << 4;          // istdBase
416     nBit16 |= (eType == STYLE_TYPE_PARA ? 1 : 2);      // sgc
417     Set_UInt16( pData, nBit16 );
418 
419     nBit16 = nWwNext << 4;          // istdNext
420     nBit16 |= (eType == STYLE_TYPE_PARA ? 2 : 1);      // cupx
421     Set_UInt16( pData, nBit16 );
422 
423     pData += sizeof( sal_uInt16 );      // bchUpe
424 
425     nBit16 = bAutoUpdate ? 1 : 0;  // fAutoRedef : 1
426     Set_UInt16( pData, nBit16 );
427     // now new:
428     // from Ver8 there are two fields more:
429     // sal_uInt16    fHidden : 1;       /* hidden from UI?
430     // sal_uInt16    : 14;              /* unused bits
431 
432     sal_uInt16 nLen = static_cast< sal_uInt16 >( ( pData - aWW8_STD ) + 1 +
433                 (2 * (rName.getLength() + 1)) );  // temporary
434 
435     nPOPosStdLen1 = m_rWW8Export.pO->size();        // Adr1 for adding the length
436 
437     SwWW8Writer::InsUInt16( *m_rWW8Export.pO, nLen );
438     m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), aWW8_STD, pData );
439 
440     nPOPosStdLen2 = nPOPosStdLen1 + 8;  // Adr2 for adding of "end of upx"
441 
442     // write names
443     SwWW8Writer::InsUInt16( *m_rWW8Export.pO, rName.getLength() ); // length
444     SwWW8Writer::InsAsString16( *m_rWW8Export.pO, rName );
445     m_rWW8Export.pO->push_back( sal_uInt8(0) );             // Despite P-String 0 at the end!
446 }
447 
SetStyleDefaults(const SwFormat & rFormat,bool bPap)448 void MSWordStyles::SetStyleDefaults( const SwFormat& rFormat, bool bPap )
449 {
450     const sw::BroadcastingModify* pOldMod = m_rExport.m_pOutFormatNode;
451     m_rExport.m_pOutFormatNode = &rFormat;
452     bool aFlags[ RES_FRMATR_END - RES_CHRATR_BEGIN ];
453     sal_uInt16 nStt, nEnd, n;
454     if( bPap )
455     {
456        nStt = RES_PARATR_BEGIN;
457        nEnd = RES_FRMATR_END;
458     }
459     else
460     {
461        nStt = RES_CHRATR_BEGIN;
462        nEnd = RES_TXTATR_END;
463     }
464 
465     // dynamic defaults
466     const SfxItemPool& rPool = *rFormat.GetAttrSet().GetPool();
467     for( n = nStt; n < nEnd; ++n )
468         aFlags[ n - RES_CHRATR_BEGIN ] = nullptr != rPool.GetPoolDefaultItem( n )
469             || SfxItemState::SET == m_rExport.m_rDoc.GetDfltTextFormatColl()->GetItemState( n, false );
470 
471     // static defaults, that differs between WinWord and SO
472     if( bPap )
473     {
474         aFlags[ static_cast< sal_uInt16 >(RES_PARATR_WIDOWS) - RES_CHRATR_BEGIN ] = true;
475         aFlags[ static_cast< sal_uInt16 >(RES_PARATR_HYPHENZONE) - RES_CHRATR_BEGIN ] = true;
476         aFlags[ static_cast< sal_uInt16 >(RES_FRAMEDIR) - RES_CHRATR_BEGIN ] = true;
477     }
478     else
479     {
480         aFlags[ RES_CHRATR_FONTSIZE - RES_CHRATR_BEGIN ] = true;
481         aFlags[ RES_CHRATR_LANGUAGE - RES_CHRATR_BEGIN ] = true;
482     }
483 
484     const SfxItemSet* pOldI = m_rExport.GetCurItemSet();
485     m_rExport.SetCurItemSet( &rFormat.GetAttrSet() );
486 
487     const bool* pFlags = aFlags + ( nStt - RES_CHRATR_BEGIN );
488     for ( n = nStt; n < nEnd; ++n, ++pFlags )
489     {
490         if ( *pFlags && !m_rExport.ignoreAttributeForStyleDefaults( n )
491             && SfxItemState::SET != rFormat.GetItemState(n, false))
492         {
493             //If we are a character property then see if it is one of the
494             //western/asian ones that must be collapsed together for export to
495             //word. If so default to the western variant.
496             if ( bPap || m_rExport.CollapseScriptsforWordOk(
497                 i18n::ScriptType::LATIN, n) )
498             {
499                 m_rExport.AttrOutput().OutputItem( rFormat.GetFormatAttr( n ) );
500             }
501         }
502     }
503 
504     m_rExport.SetCurItemSet( pOldI );
505     m_rExport.m_pOutFormatNode = pOldMod;
506 }
507 
StartStyleProperties(bool bParProp,sal_uInt16 nStyle)508 void WW8AttributeOutput::StartStyleProperties( bool bParProp, sal_uInt16 nStyle )
509 {
510     impl_SkipOdd( m_rWW8Export.pO, m_rWW8Export.pTableStrm->Tell() );
511 
512     sal_uInt16 nLen = bParProp ? 2 : 0;         // default length
513     m_nStyleLenPos = m_rWW8Export.pO->size();   // adding length
514                                                 // Don't save pointer, because it
515                                                 // changes by _grow!
516 
517     SwWW8Writer::InsUInt16( *m_rWW8Export.pO, nLen );        // Style-Len
518 
519     m_nStyleStartSize = m_rWW8Export.pO->size();
520 
521     if ( bParProp )
522         SwWW8Writer::InsUInt16( *m_rWW8Export.pO, nStyle );     // Style-Number
523 }
524 
WriteProperties(const SwFormat * pFormat,bool bParProp,sal_uInt16 nPos,bool bInsDefCharSiz)525 void MSWordStyles::WriteProperties( const SwFormat* pFormat, bool bParProp, sal_uInt16 nPos,
526     bool bInsDefCharSiz )
527 {
528     m_rExport.AttrOutput().StartStyleProperties( bParProp, nPos );
529 
530     OSL_ENSURE( m_rExport.m_pCurrentStyle == nullptr, "Current style not NULL" ); // set current style before calling out
531     m_rExport.m_pCurrentStyle = pFormat;
532 
533     m_rExport.OutputFormat( *pFormat, bParProp, !bParProp );
534 
535     OSL_ENSURE( m_rExport.m_pCurrentStyle == pFormat, "current style was changed" );
536     // reset current style...
537     m_rExport.m_pCurrentStyle = nullptr;
538 
539     if ( bInsDefCharSiz  )                   // not derived from other Style
540         SetStyleDefaults( *pFormat, bParProp );
541 
542     m_rExport.AttrOutput().EndStyleProperties( bParProp );
543 }
544 
EndStyleProperties(bool)545 void WW8AttributeOutput::EndStyleProperties( bool /*bParProp*/ )
546 {
547     sal_uInt16 nLen = m_rWW8Export.pO->size() - m_nStyleStartSize;
548     sal_uInt8* pUpxLen = m_rWW8Export.pO->data() + m_nStyleLenPos; // adding length
549     ShortToSVBT16( nLen, pUpxLen );                 // add default length
550 }
551 
GetStyleData(SwFormat * pFormat,bool & bFormatColl,sal_uInt16 & nBase,sal_uInt16 & nNext)552 void MSWordStyles::GetStyleData( SwFormat* pFormat, bool& bFormatColl, sal_uInt16& nBase, sal_uInt16& nNext )
553 {
554     bFormatColl = pFormat->Which() == RES_TXTFMTCOLL || pFormat->Which() == RES_CONDTXTFMTCOLL;
555 
556     // Default: none
557     nBase = 0xfff;
558 
559     // Derived from?
560     if ( !pFormat->IsDefault() )
561         nBase = GetSlot( pFormat->DerivedFrom() );
562 
563     SwFormat* pNext;
564     if ( bFormatColl )
565         pNext = &static_cast<SwTextFormatColl*>(pFormat)->GetNextTextFormatColl();
566     else
567         pNext = pFormat; // CharFormat: next CharFormat == self
568 
569     nNext = GetSlot( pNext );
570 }
571 
DefaultStyle()572 void WW8AttributeOutput::DefaultStyle()
573 {
574     m_rWW8Export.pTableStrm->WriteUInt16(0);   // empty Style
575 }
576 
OutputStyle(const SwNumRule * pNumRule,sal_uInt16 nPos)577 void MSWordStyles::OutputStyle(const SwNumRule* pNumRule, sal_uInt16 nPos)
578 {
579     m_rExport.AttrOutput().StartStyle( pNumRule->GetName(), STYLE_TYPE_LIST,
580             /*nBase =*/ 0, /*nWwNext =*/ 0, /*nWWId =*/ 0, nPos,
581             /*bAutoUpdateFormat =*/ false );
582 
583     m_rExport.AttrOutput().EndStyle();
584 }
585 
586 // OutputStyle applies for TextFormatColls and CharFormats
OutputStyle(SwFormat * pFormat,sal_uInt16 nPos)587 void MSWordStyles::OutputStyle( SwFormat* pFormat, sal_uInt16 nPos )
588 {
589     if ( !pFormat )
590         m_rExport.AttrOutput().DefaultStyle();
591     else
592     {
593         bool bFormatColl;
594         sal_uInt16 nBase, nWwNext;
595 
596         GetStyleData( pFormat, bFormatColl, nBase, nWwNext );
597 
598         OUString aName = pFormat->GetName();
599         // We want to map LO's default style to Word's "Normal" style.
600         // Word looks for this specific style name when reading docx files.
601         // (It must be the English word regardless of language settings)
602         if ( nPos == 0 )
603         {
604             assert( pFormat->GetPoolFormatId() == RES_POOLCOLL_STANDARD );
605             aName = "Normal";
606         }
607         else if (aName.equalsIgnoreAsciiCase("Normal"))
608         {
609             // If LO has a style named "Normal"(!) rename it to something unique
610             const OUString aBaseName = "LO-" + aName;
611             aName = aBaseName;
612             // Check if we still have a clash, in which case we add a suffix
613             for ( int nSuffix = 0; ; ++nSuffix ) {
614                 bool clash=false;
615                 for ( sal_uInt16 n = 1; n < m_nUsedSlots; ++n )
616                     if ( m_aFormatA[n] &&
617                          m_aFormatA[n]->GetName().equalsIgnoreAsciiCase(aName) )
618                     {
619                         clash = true;
620                         break;
621                     }
622                 if (!clash)
623                     break;
624                 // TODO: verify if we really need to increment nSuffix in 2 places
625                 aName = aBaseName + OUString::number(++nSuffix);
626             }
627         }
628         else if (!bFormatColl && m_rExport.GetExportFormat() == MSWordExportBase::DOCX &&
629                         m_rExport.m_pStyles->GetStyleId(nPos).startsWith("ListLabel"))
630         {
631             // tdf#92335 don't export redundant DOCX import style "ListLabel"
632             return;
633         }
634         else if (aName.equalsIgnoreAsciiCase("Internet Link"))
635         {
636             aName = "Hyperlink";
637         }
638         else if (aName.equalsIgnoreAsciiCase("Visited Internet Link"))
639         {
640             aName = "FollowedHyperlink";
641         }
642 
643         m_rExport.AttrOutput().StartStyle( aName, (bFormatColl ? STYLE_TYPE_PARA : STYLE_TYPE_CHAR),
644                 nBase, nWwNext, GetWWId( *pFormat ), nPos,
645                 pFormat->IsAutoUpdateFormat() );
646 
647         if ( bFormatColl )
648             WriteProperties( pFormat, true, nPos, nBase==0xfff );           // UPX.papx
649 
650         WriteProperties( pFormat, false, nPos, bFormatColl && nBase==0xfff );  // UPX.chpx
651 
652         m_rExport.AttrOutput().EndStyle();
653     }
654 }
655 
StartStyles()656 void WW8AttributeOutput::StartStyles()
657 {
658     WW8Fib& rFib = *m_rWW8Export.pFib;
659 
660     sal_uLong nCurPos = m_rWW8Export.pTableStrm->Tell();
661     if ( nCurPos & 1 )                   // start on even
662     {
663         m_rWW8Export.pTableStrm->WriteChar( char(0) );        // Address
664         ++nCurPos;
665     }
666     rFib.m_fcStshfOrig = rFib.m_fcStshf = nCurPos;
667     m_nStyleCountPos = nCurPos + 2;     // count is added later
668 
669     static sal_uInt8 aStShi[] = {
670         0x12, 0x00,
671         0x0F, 0x00, 0x0A, 0x00, 0x01, 0x00, 0x5B, 0x00,
672         0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
673         0x00, 0x00 };
674 
675     m_rWW8Export.pTableStrm->WriteBytes(&aStShi, sizeof(aStShi));
676 }
677 
EndStyles(sal_uInt16 nNumberOfStyles)678 void WW8AttributeOutput::EndStyles( sal_uInt16 nNumberOfStyles )
679 {
680     WW8Fib& rFib = *m_rWW8Export.pFib;
681 
682     rFib.m_lcbStshfOrig = rFib.m_lcbStshf = m_rWW8Export.pTableStrm->Tell() - rFib.m_fcStshf;
683     SwWW8Writer::WriteShort( *m_rWW8Export.pTableStrm, m_nStyleCountPos, nNumberOfStyles );
684 }
685 
OutputStylesTable()686 void MSWordStyles::OutputStylesTable()
687 {
688     m_rExport.m_bStyDef = true;
689 
690     m_rExport.AttrOutput().StartStyles();
691 
692     sal_uInt16 n;
693     // HACK
694     // Ms Office seems to have an internal limitation of 4091 styles
695     // and refuses to load .docx with more, even though the spec seems to allow that;
696     // so simply if there are more styles, don't export those
697     // Implementing check for all exports DOCX, DOC, RTF
698     sal_uInt16 const nLimit = MSWORD_MAX_STYLES_LIMIT;
699     m_nUsedSlots = std::min(nLimit, m_nUsedSlots);
700 
701     for ( n = 0; n < m_nUsedSlots; n++ )
702     {
703         if (m_aNumRules.find(n) != m_aNumRules.end())
704             OutputStyle(m_aNumRules[n], n);
705         else
706             OutputStyle(m_aFormatA[n], n);
707     }
708 
709     m_rExport.AttrOutput().EndStyles( m_nUsedSlots );
710 
711     m_rExport.m_bStyDef = false;
712 }
713 
GetSwNumRule(sal_uInt16 nId) const714 const SwNumRule* MSWordStyles::GetSwNumRule(sal_uInt16 nId) const
715 {
716     std::map<sal_uInt16, const SwNumRule*>::const_iterator it = m_aNumRules.find(nId);
717     assert(it != m_aNumRules.end());
718     return it->second;
719 }
720 
721 //          Fonts
722 
wwFont(const OUString & rFamilyName,FontPitch ePitch,FontFamily eFamily,rtl_TextEncoding eChrSet)723 wwFont::wwFont(const OUString &rFamilyName, FontPitch ePitch, FontFamily eFamily,
724         rtl_TextEncoding eChrSet)
725     : mbAlt(false), mePitch(ePitch), meFamily(eFamily), meChrSet(eChrSet)
726 {
727     FontMapExport aResult(rFamilyName);
728     msFamilyNm = aResult.msPrimary;
729     msAltNm = aResult.msSecondary;
730     if (!msAltNm.isEmpty() && msAltNm != msFamilyNm &&
731         (msFamilyNm.getLength() + msAltNm.getLength() + 2 <= 65) )
732     {
733         //max size of szFfn in 65 chars
734         mbAlt = true;
735     }
736 
737     maWW8_FFN[0] = static_cast<sal_uInt8>( 6 - 1 + 0x22 + ( 2 * ( 1 + msFamilyNm.getLength() ) ));
738     if (mbAlt)
739         maWW8_FFN[0] = static_cast< sal_uInt8 >(maWW8_FFN[0] + 2 * ( 1 + msAltNm.getLength()));
740 
741     sal_uInt8 aB = 0;
742     switch(ePitch)
743     {
744         case PITCH_VARIABLE:
745             aB |= 2;    // aF.prg = 2
746             break;
747         case PITCH_FIXED:
748             aB |= 1;
749             break;
750         default:        // aF.prg = 0 : DEFAULT_PITCH (windows.h)
751             break;
752     }
753     aB |= 1 << 2;   // aF.fTrueType = 1; don't know any better;
754 
755     switch(eFamily)
756     {
757         case FAMILY_ROMAN:
758             aB |= 1 << 4;   // aF.ff = 1;
759             break;
760         case FAMILY_SWISS:
761             aB |= 2 << 4;   // aF.ff = 2;
762             break;
763         case FAMILY_MODERN:
764             aB |= 3 << 4;   // aF.ff = 3;
765             break;
766         case FAMILY_SCRIPT:
767             aB |= 4 << 4;   // aF.ff = 4;
768             break;
769         case FAMILY_DECORATIVE:
770             aB |= 5 << 4;   // aF.ff = 5;
771             break;
772         default:            // aF.ff = 0; FF_DONTCARE (windows.h)
773             break;
774     }
775     maWW8_FFN[1] = aB;
776 
777     ShortToSVBT16( 400, &maWW8_FFN[2] );        // don't know any better
778                                                 // 400 == FW_NORMAL (windows.h)
779 
780     //#i61927# For unicode fonts like Arial Unicode, Word 97+ sets the chs
781     //to SHIFTJIS presumably to capture that it's a multi-byte encoding font
782     //but Word95 doesn't do this, and sets it to 0 (ANSI), so we should do the
783     //same
784     maWW8_FFN[4] = sw::ms::rtl_TextEncodingToWinCharset(eChrSet);
785 
786     if (mbAlt)
787         maWW8_FFN[5] = static_cast< sal_uInt8 >(msFamilyNm.getLength() + 1);
788 }
789 
Write(SvStream * pTableStrm) const790 void wwFont::Write(SvStream *pTableStrm) const
791 {
792     pTableStrm->WriteBytes(maWW8_FFN, sizeof(maWW8_FFN));    // fixed part
793     // from Ver8 following two fields intersected,
794     // we ignore them.
795     //char  panose[ 10 ];       //  0x6   PANOSE
796     //char  fs[ 24     ];       //  0x10  FONTSIGNATURE
797     SwWW8Writer::FillCount(*pTableStrm, 0x22);
798     SwWW8Writer::WriteString16(*pTableStrm, msFamilyNm, true);
799     if (mbAlt)
800         SwWW8Writer::WriteString16(*pTableStrm, msAltNm, true);
801 }
802 
WriteDocx(DocxAttributeOutput * rAttrOutput) const803 void wwFont::WriteDocx( DocxAttributeOutput* rAttrOutput ) const
804 {
805     // no font embedding, panose id, subsetting, ... implemented
806 
807     if (msFamilyNm.isEmpty())
808         return;
809 
810     rAttrOutput->StartFont( msFamilyNm );
811 
812     if ( mbAlt )
813         rAttrOutput->FontAlternateName( msAltNm );
814     rAttrOutput->FontCharset( sw::ms::rtl_TextEncodingToWinCharset( meChrSet ), meChrSet );
815     rAttrOutput->FontFamilyType( meFamily );
816     rAttrOutput->FontPitchType( mePitch );
817     rAttrOutput->EmbedFont( msFamilyNm, meFamily, mePitch );
818 
819     rAttrOutput->EndFont();
820 }
821 
WriteRtf(const RtfAttributeOutput * rAttrOutput) const822 void wwFont::WriteRtf( const RtfAttributeOutput* rAttrOutput ) const
823 {
824     rAttrOutput->FontFamilyType( meFamily, *this );
825     rAttrOutput->FontPitchType( mePitch );
826     rAttrOutput->FontCharset(
827         sw::ms::rtl_TextEncodingToWinCharsetRTF(msFamilyNm, msAltNm, meChrSet));
828     rAttrOutput->StartFont( msFamilyNm );
829     if ( mbAlt )
830         rAttrOutput->FontAlternateName( msAltNm );
831     rAttrOutput->EndFont();
832 }
833 
operator <(const wwFont & r1,const wwFont & r2)834 bool operator<(const wwFont &r1, const wwFont &r2)
835 {
836     int nRet = memcmp(r1.maWW8_FFN, r2.maWW8_FFN, sizeof(r1.maWW8_FFN));
837     if (nRet == 0)
838     {
839         nRet = r1.msFamilyNm.compareTo(r2.msFamilyNm);
840         if (nRet == 0)
841             nRet = r1.msAltNm.compareTo(r2.msAltNm);
842     }
843     return nRet < 0;
844 }
845 
GetId(const wwFont & rFont)846 sal_uInt16 wwFontHelper::GetId(const wwFont &rFont)
847 {
848     sal_uInt16 nRet;
849     std::map<wwFont, sal_uInt16>::const_iterator aIter = maFonts.find(rFont);
850     if (aIter != maFonts.end())
851         nRet = aIter->second;
852     else
853     {
854         nRet = static_cast< sal_uInt16 >(maFonts.size());
855         maFonts[rFont] = nRet;
856     }
857     return nRet;
858 }
859 
InitFontTable(const SwDoc & rDoc)860 void wwFontHelper::InitFontTable(const SwDoc& rDoc)
861 {
862     GetId(wwFont("Times New Roman", PITCH_VARIABLE,
863         FAMILY_ROMAN, RTL_TEXTENCODING_MS_1252));
864 
865     GetId(wwFont("Symbol", PITCH_VARIABLE, FAMILY_ROMAN,
866         RTL_TEXTENCODING_SYMBOL));
867 
868     GetId(wwFont("Arial", PITCH_VARIABLE, FAMILY_SWISS,
869         RTL_TEXTENCODING_MS_1252));
870 
871     const SvxFontItem* pFont = GetDfltAttr(RES_CHRATR_FONT);
872 
873     GetId(wwFont(pFont->GetFamilyName(), pFont->GetPitch(),
874         pFont->GetFamily(), pFont->GetCharSet()));
875 
876     const SfxItemPool& rPool = rDoc.GetAttrPool();
877     pFont = rPool.GetPoolDefaultItem(RES_CHRATR_FONT);
878     if (nullptr != pFont)
879     {
880         GetId(wwFont(pFont->GetFamilyName(), pFont->GetPitch(),
881             pFont->GetFamily(), pFont->GetCharSet()));
882     }
883 
884     if (!bLoadAllFonts)
885         return;
886 
887     const sal_uInt16 aTypes[] = { RES_CHRATR_FONT, RES_CHRATR_CJK_FONT, RES_CHRATR_CTL_FONT, 0 };
888     for (const sal_uInt16* pId = aTypes; *pId; ++pId)
889     {
890         for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(*pId))
891         {
892             pFont = static_cast<const SvxFontItem*>(pItem);
893             GetId(wwFont(pFont->GetFamilyName(), pFont->GetPitch(),
894                          pFont->GetFamily(), pFont->GetCharSet()));
895         }
896     }
897 }
898 
GetId(const SvxFontItem & rFont)899 sal_uInt16 wwFontHelper::GetId(const SvxFontItem& rFont)
900 {
901     wwFont aFont(rFont.GetFamilyName(), rFont.GetPitch(), rFont.GetFamily(),
902         rFont.GetCharSet());
903     return GetId(aFont);
904 }
905 
AsVector() const906 std::vector< const wwFont* > wwFontHelper::AsVector() const
907 {
908     std::vector<const wwFont *> aFontList( maFonts.size() );
909 
910     for ( const auto& aFont : maFonts )
911         aFontList[aFont.second] = &aFont.first;
912 
913     return aFontList;
914 }
915 
WriteFontTable(SvStream * pTableStream,WW8Fib & rFib)916 void wwFontHelper::WriteFontTable(SvStream *pTableStream, WW8Fib& rFib)
917 {
918     rFib.m_fcSttbfffn = pTableStream->Tell();
919     /*
920      * Reserve some space to fill in the len after we know how big it is
921      */
922     SwWW8Writer::WriteLong(*pTableStream, 0);
923 
924     /*
925      * Convert from fast insertion map to linear vector in the order that we
926      * want to write.
927      */
928     std::vector<const wwFont *> aFontList( AsVector() );
929 
930     /*
931      * Write them all to pTableStream
932      */
933     for ( auto aFont : aFontList )
934         aFont->Write(pTableStream);
935 
936     /*
937      * Write the position and len in the FIB
938      */
939     rFib.m_lcbSttbfffn = pTableStream->Tell() - rFib.m_fcSttbfffn;
940     SwWW8Writer::WriteLong( *pTableStream, rFib.m_fcSttbfffn, maFonts.size());
941 }
942 
WriteFontTable(DocxAttributeOutput & rAttrOutput)943 void wwFontHelper::WriteFontTable( DocxAttributeOutput& rAttrOutput )
944 {
945     std::vector<const wwFont *> aFontList( AsVector() );
946 
947     for ( auto aFont : aFontList )
948         aFont->WriteDocx(&rAttrOutput);
949 }
950 
WriteFontTable(const RtfAttributeOutput & rAttrOutput)951 void wwFontHelper::WriteFontTable( const RtfAttributeOutput& rAttrOutput )
952 {
953     std::vector<const wwFont *> aFontList( AsVector() );
954 
955     for ( auto aFont : aFontList )
956         aFont->WriteRtf(&rAttrOutput);
957 }
958 
WW8_WrPlc0(sal_uLong nOffset)959 WW8_WrPlc0::WW8_WrPlc0( sal_uLong nOffset )
960     : nOfs( nOffset )
961 {
962 }
963 
Append(sal_uLong nStartCpOrFc)964 void WW8_WrPlc0::Append( sal_uLong nStartCpOrFc )
965 {
966     aPos.push_back( nStartCpOrFc - nOfs );
967 }
968 
Write(SvStream & rStrm)969 void WW8_WrPlc0::Write( SvStream& rStrm )
970 {
971     for( const auto& rPos : aPos )
972     {
973         rStrm.WriteUInt32(rPos);
974     }
975 }
976 
977 // class MSWordSections : translate PageDescs into Sections
978 //      also deals with header and footer
979 
MSWordSections(MSWordExportBase & rExport)980 MSWordSections::MSWordSections( MSWordExportBase& rExport )
981     : mbDocumentIsProtected( false )
982 {
983     const SwSectionFormat *pFormat = nullptr;
984     rExport.m_pCurrentPageDesc = &rExport.m_rDoc.GetPageDesc( 0 );
985 
986     const SfxPoolItem* pI;
987     const SwNode* pNd = rExport.m_pCurPam->GetContentNode();
988     const SfxItemSet* pSet = pNd ? &static_cast<const SwContentNode*>(pNd)->GetSwAttrSet() : nullptr;
989 
990     sal_uLong nRstLnNum =  pSet ? pSet->Get( RES_LINENUMBER ).GetStartValue() : 0;
991 
992     const SwTableNode* pTableNd = rExport.m_pCurPam->GetNode().FindTableNode();
993     const SwSectionNode* pSectNd = nullptr;
994     if ( pTableNd )
995     {
996         pSet = &pTableNd->GetTable().GetFrameFormat()->GetAttrSet();
997         pNd = pTableNd;
998     }
999     else if (pNd && nullptr != ( pSectNd = pNd->FindSectionNode() ))
1000     {
1001         if ( SectionType::ToxHeader == pSectNd->GetSection().GetType() &&
1002              pSectNd->StartOfSectionNode()->IsSectionNode() )
1003         {
1004             pSectNd = pSectNd->StartOfSectionNode()->GetSectionNode();
1005         }
1006 
1007         if ( SectionType::ToxContent == pSectNd->GetSection().GetType() )
1008         {
1009             pNd = pSectNd;
1010             rExport.m_pCurPam->GetPoint()->nNode = *pNd;
1011         }
1012 
1013         if ( SectionType::Content == pSectNd->GetSection().GetType() )
1014             pFormat = pSectNd->GetSection().GetFormat();
1015     }
1016 
1017     // tdf#118393: FILESAVE: DOCX Export loses header/footer
1018     rExport.m_bFirstTOCNodeWithSection = pSectNd &&
1019         (   SectionType::ToxHeader  == pSectNd->GetSection().GetType() ||
1020             SectionType::ToxContent == pSectNd->GetSection().GetType()  );
1021 
1022     // Try to get page descriptor of the first node
1023     if ( pSet &&
1024          SfxItemState::SET == pSet->GetItemState( RES_PAGEDESC, true, &pI ) &&
1025          static_cast<const SwFormatPageDesc*>(pI)->GetPageDesc() )
1026     {
1027         AppendSection( *static_cast<const SwFormatPageDesc*>(pI), *pNd, pFormat, nRstLnNum );
1028     }
1029     else
1030         AppendSection( rExport.m_pCurrentPageDesc, pFormat, nRstLnNum, /*bIsFirstParagraph=*/true );
1031 }
1032 
WW8_WrPlcSepx(MSWordExportBase & rExport)1033 WW8_WrPlcSepx::WW8_WrPlcSepx( MSWordExportBase& rExport )
1034     : MSWordSections( rExport )
1035     , m_bHeaderFooterWritten( false )
1036 {
1037     // to be in sync with the AppendSection() call in the MSWordSections
1038     // constructor
1039     aCps.push_back( 0 );
1040 }
1041 
~MSWordSections()1042 MSWordSections::~MSWordSections()
1043 {
1044 }
1045 
~WW8_WrPlcSepx()1046 WW8_WrPlcSepx::~WW8_WrPlcSepx()
1047 {
1048 }
1049 
HeaderFooterWritten()1050 bool MSWordSections::HeaderFooterWritten()
1051 {
1052     return false; // only relevant for WW8
1053 }
1054 
HeaderFooterWritten()1055 bool WW8_WrPlcSepx::HeaderFooterWritten()
1056 {
1057     return m_bHeaderFooterWritten;
1058 }
1059 
CurrentNumberOfColumns(const SwDoc & rDoc) const1060 sal_uInt16 MSWordSections::CurrentNumberOfColumns( const SwDoc &rDoc ) const
1061 {
1062     OSL_ENSURE( !aSects.empty(), "no segment inserted yet" );
1063     if ( aSects.empty() )
1064         return 1;
1065 
1066     return NumberOfColumns( rDoc, aSects.back() );
1067 }
1068 
NumberOfColumns(const SwDoc & rDoc,const WW8_SepInfo & rInfo)1069 sal_uInt16 MSWordSections::NumberOfColumns( const SwDoc &rDoc, const WW8_SepInfo& rInfo )
1070 {
1071     const SwPageDesc* pPd = rInfo.pPageDesc;
1072     if ( !pPd )
1073         pPd = &rDoc.GetPageDesc( 0 );
1074 
1075     const SfxItemSet &rSet = pPd->GetMaster().GetAttrSet();
1076     SfxItemSet aSet( *rSet.GetPool(), svl::Items<RES_COL, RES_COL>{} );
1077     aSet.SetParent( &rSet );
1078 
1079     //0xffffffff, what the hell is going on with that!, fixme most terribly
1080     if ( rInfo.pSectionFormat && reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1)) != rInfo.pSectionFormat )
1081         aSet.Put( rInfo.pSectionFormat->GetFormatAttr( RES_COL ) );
1082 
1083     const SwFormatCol& rCol = aSet.Get( RES_COL );
1084     const SwColumns& rColumns = rCol.GetColumns();
1085     return rColumns.size();
1086 }
1087 
CurrentSectionInfo()1088 const WW8_SepInfo* MSWordSections::CurrentSectionInfo()
1089 {
1090     if ( !aSects.empty() )
1091         return &aSects.back();
1092 
1093     return nullptr;
1094 }
1095 
AppendSection(const SwPageDesc * pPd,const SwSectionFormat * pSectionFormat,sal_uLong nLnNumRestartNo,bool bIsFirstParagraph)1096 void MSWordSections::AppendSection( const SwPageDesc* pPd,
1097     const SwSectionFormat* pSectionFormat, sal_uLong nLnNumRestartNo, bool bIsFirstParagraph )
1098 {
1099     if (HeaderFooterWritten()) {
1100         return; // #i117955# prevent new sections in endnotes
1101     }
1102     aSects.emplace_back( pPd, pSectionFormat, nLnNumRestartNo, std::nullopt, nullptr, bIsFirstParagraph );
1103     NeedsDocumentProtected( aSects.back() );
1104 }
1105 
AppendSep(WW8_CP nStartCp,const SwPageDesc * pPd,const SwSectionFormat * pSectionFormat,sal_uLong nLnNumRestartNo)1106 void WW8_WrPlcSepx::AppendSep( WW8_CP nStartCp, const SwPageDesc* pPd,
1107     const SwSectionFormat* pSectionFormat, sal_uLong nLnNumRestartNo )
1108 {
1109     if (HeaderFooterWritten()) {
1110         return; // #i117955# prevent new sections in endnotes
1111     }
1112     aCps.push_back( nStartCp );
1113     AppendSection( pPd, pSectionFormat, nLnNumRestartNo );
1114 }
1115 
AppendSection(const SwFormatPageDesc & rPD,const SwNode & rNd,const SwSectionFormat * pSectionFormat,sal_uLong nLnNumRestartNo)1116 void MSWordSections::AppendSection( const SwFormatPageDesc& rPD,
1117     const SwNode& rNd, const SwSectionFormat* pSectionFormat, sal_uLong nLnNumRestartNo )
1118 {
1119     if (HeaderFooterWritten()) {
1120         return; // #i117955# prevent new sections in endnotes
1121     }
1122 
1123     WW8_SepInfo aI( rPD.GetPageDesc(), pSectionFormat, nLnNumRestartNo, rPD.GetNumOffset(), &rNd );
1124 
1125     aSects.push_back( aI );
1126     NeedsDocumentProtected( aI );
1127 }
1128 
AppendSep(WW8_CP nStartCp,const SwFormatPageDesc & rPD,const SwNode & rNd,const SwSectionFormat * pSectionFormat,sal_uLong nLnNumRestartNo)1129 void WW8_WrPlcSepx::AppendSep( WW8_CP nStartCp, const SwFormatPageDesc& rPD,
1130     const SwNode& rNd, const SwSectionFormat* pSectionFormat, sal_uLong nLnNumRestartNo )
1131 {
1132     if (HeaderFooterWritten()) {
1133         return; // #i117955# prevent new sections in endnotes
1134     }
1135     aCps.push_back( nStartCp );
1136     AppendSection( rPD, rNd, pSectionFormat, nLnNumRestartNo );
1137 }
1138 
WriteFootnoteEndText(WW8Export & rWrt,sal_uLong nCpStt)1139 void WW8_WrPlcSepx::WriteFootnoteEndText( WW8Export& rWrt, sal_uLong nCpStt )
1140 {
1141     sal_uInt8 nInfoFlags = 0;
1142     const SwFootnoteInfo& rInfo = rWrt.m_rDoc.GetFootnoteInfo();
1143     if( !rInfo.m_aErgoSum.isEmpty() )  nInfoFlags |= 0x02;
1144     if( !rInfo.m_aQuoVadis.isEmpty() ) nInfoFlags |= 0x04;
1145 
1146     sal_uInt8 nEmptyStt = 0;
1147     if( nInfoFlags )
1148     {
1149         pTextPos->Append( nCpStt );  // empty footnote separator
1150 
1151         if( 0x02 & nInfoFlags )         // Footnote continuation separator
1152         {
1153             pTextPos->Append( nCpStt );
1154             rWrt.WriteStringAsPara( rInfo.m_aErgoSum );
1155             rWrt.WriteStringAsPara( OUString() );
1156             nCpStt = rWrt.Fc2Cp( rWrt.Strm().Tell() );
1157         }
1158         else
1159             pTextPos->Append( nCpStt );
1160 
1161         if( 0x04 & nInfoFlags )         // Footnote continuation notice
1162         {
1163             pTextPos->Append( nCpStt );
1164             rWrt.WriteStringAsPara( rInfo.m_aQuoVadis );
1165             rWrt.WriteStringAsPara( OUString() );
1166             nCpStt = rWrt.Fc2Cp( rWrt.Strm().Tell() );
1167         }
1168         else
1169             pTextPos->Append( nCpStt );
1170 
1171         nEmptyStt = 3;
1172     }
1173 
1174     while( 6 > nEmptyStt++ )
1175         pTextPos->Append( nCpStt );
1176 
1177     // set the flags at the Dop right away
1178     WW8Dop& rDop = *rWrt.pDop;
1179     // Footnote Info
1180     switch( rInfo.m_eNum )
1181     {
1182     case FTNNUM_PAGE:       rDop.rncFootnote = 2; break;
1183     case FTNNUM_CHAPTER:    rDop.rncFootnote  = 1; break;
1184     default: rDop.rncFootnote  = 0; break;
1185     }                                   // rncFootnote
1186     rDop.nfcFootnoteRef = WW8Export::GetNumId( rInfo.m_aFormat.GetNumberingType() );
1187     rDop.nFootnote = rInfo.m_nFootnoteOffset + 1;
1188     rDop.fpc = rWrt.m_bFootnoteAtTextEnd ? 2 : 1;
1189 
1190     // Endnote Info
1191     rDop.rncEdn = 0;                        // rncEdn: Don't Restart
1192     const SwEndNoteInfo& rEndInfo = rWrt.m_rDoc.GetEndNoteInfo();
1193     rDop.nfcEdnRef = WW8Export::GetNumId( rEndInfo.m_aFormat.GetNumberingType() );
1194     rDop.nEdn = rEndInfo.m_nFootnoteOffset + 1;
1195     rDop.epc = rWrt.m_bEndAtTextEnd ? 3 : 0;
1196 }
1197 
SetHeaderFlag(sal_uInt8 & rHeadFootFlags,const SwFormat & rFormat,sal_uInt8 nFlag)1198 void MSWordSections::SetHeaderFlag( sal_uInt8& rHeadFootFlags, const SwFormat& rFormat,
1199     sal_uInt8 nFlag )
1200 {
1201     const SfxPoolItem* pItem;
1202     if( SfxItemState::SET == rFormat.GetItemState(RES_HEADER, true, &pItem)
1203         && static_cast<const SwFormatHeader*>(pItem)->IsActive() &&
1204         static_cast<const SwFormatHeader*>(pItem)->GetHeaderFormat() )
1205         rHeadFootFlags |= nFlag;
1206 }
1207 
SetFooterFlag(sal_uInt8 & rHeadFootFlags,const SwFormat & rFormat,sal_uInt8 nFlag)1208 void MSWordSections::SetFooterFlag( sal_uInt8& rHeadFootFlags, const SwFormat& rFormat,
1209     sal_uInt8 nFlag )
1210 {
1211     const SfxPoolItem* pItem;
1212     if( SfxItemState::SET == rFormat.GetItemState(RES_FOOTER, true, &pItem)
1213         && static_cast<const SwFormatFooter*>(pItem)->IsActive() &&
1214         static_cast<const SwFormatFooter*>(pItem)->GetFooterFormat() )
1215         rHeadFootFlags |= nFlag;
1216 }
1217 
OutHeaderFooter(WW8Export & rWrt,bool bHeader,const SwFormat & rFormat,sal_uLong & rCpPos,sal_uInt8 nHFFlags,sal_uInt8 nFlag,sal_uInt8 nBreakCode)1218 void WW8_WrPlcSepx::OutHeaderFooter( WW8Export& rWrt, bool bHeader,
1219                      const SwFormat& rFormat, sal_uLong& rCpPos, sal_uInt8 nHFFlags,
1220                      sal_uInt8 nFlag,  sal_uInt8 nBreakCode)
1221 {
1222     if ( nFlag & nHFFlags )
1223     {
1224         pTextPos->Append( rCpPos );
1225         rWrt.WriteHeaderFooterText( rFormat, bHeader);
1226         rWrt.WriteStringAsPara( OUString() ); // CR to the end ( otherwise WW complains )
1227         rCpPos = rWrt.Fc2Cp( rWrt.Strm().Tell() );
1228     }
1229     else
1230     {
1231         pTextPos->Append( rCpPos );
1232         if ((bHeader? rWrt.m_bHasHdr : rWrt.m_bHasFtr) && nBreakCode!=0)
1233         {
1234             rWrt.WriteStringAsPara( OUString() ); // Empty paragraph for empty header/footer
1235             rWrt.WriteStringAsPara( OUString() ); // a CR that WW8 needs for end of the stream
1236             rCpPos = rWrt.Fc2Cp( rWrt.Strm().Tell() );
1237         }
1238     }
1239 }
1240 
NeedsDocumentProtected(const WW8_SepInfo & rInfo)1241 void MSWordSections::NeedsDocumentProtected(const WW8_SepInfo &rInfo)
1242 {
1243     if (rInfo.IsProtected())
1244         mbDocumentIsProtected = true;
1245 }
1246 
IsProtected() const1247 bool WW8_SepInfo::IsProtected() const
1248 {
1249     bool bRet = false;
1250     if (
1251          pSectionFormat &&
1252          (reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1)) != pSectionFormat)
1253        )
1254     {
1255         const SwSection *pSection = pSectionFormat->GetSection();
1256         if (pSection && pSection->IsProtect())
1257         {
1258             bRet = true;
1259         }
1260     }
1261     return bRet;
1262 }
1263 
CheckForFacinPg(const WW8Export & rWrt) const1264 void MSWordSections::CheckForFacinPg( const WW8Export& rWrt ) const
1265 {
1266     // 2 values getting set
1267     //      Dop.fFacingPages            == Header and Footer different
1268     //      Dop.fSwapBordersFacingPgs   == mirrored borders
1269     sal_uInt16 nEnd = 0;
1270     for( const WW8_SepInfo& rSepInfo : aSects )
1271     {
1272         if( !rSepInfo.pSectionFormat )
1273         {
1274             const SwPageDesc* pPd = rSepInfo.pPageDesc;
1275             if( pPd->GetFollow() && pPd != pPd->GetFollow() &&
1276                 pPd->GetFollow()->GetFollow() == pPd->GetFollow() &&
1277                 rSepInfo.pPDNd &&
1278                 pPd->IsFollowNextPageOfNode( *rSepInfo.pPDNd ) )
1279                 // so this is first page and subsequent, so only respect follow
1280                 pPd = pPd->GetFollow();
1281 
1282             // left-/right chain of pagedescs ?
1283             else if( !( 1 & nEnd ) &&
1284                 pPd->GetFollow() && pPd != pPd->GetFollow() &&
1285                 pPd->GetFollow()->GetFollow() == pPd &&
1286                 (( UseOnPage::Left == ( UseOnPage::All & pPd->ReadUseOn() ) &&
1287                    UseOnPage::Right == ( UseOnPage::All & pPd->GetFollow()->ReadUseOn() )) ||
1288                  ( UseOnPage::Right == ( UseOnPage::All & pPd->ReadUseOn() ) &&
1289                    UseOnPage::Left == ( UseOnPage::All & pPd->GetFollow()->ReadUseOn() )) ))
1290             {
1291                 rWrt.pDop->fFacingPages = rWrt.pDop->fMirrorMargins = true;
1292                 nEnd |= 1;
1293             }
1294 
1295             if( !( 1 & nEnd ) &&
1296                 ( !pPd->IsHeaderShared() || !pPd->IsFooterShared() ))
1297             {
1298                 rWrt.pDop->fFacingPages = true;
1299                 nEnd |= 1;
1300             }
1301             if( !( 2 & nEnd ) &&
1302                 UseOnPage::Mirror == ( UseOnPage::Mirror & pPd->ReadUseOn() ))
1303             {
1304                 rWrt.pDop->fSwapBordersFacingPgs =
1305                     rWrt.pDop->fMirrorMargins = true;
1306                 nEnd |= 2;
1307             }
1308 
1309             if( 3 == nEnd )
1310                 break;      // We do not need to go any further
1311         }
1312     }
1313 }
1314 
HasBorderItem(const SwFormat & rFormat)1315 bool MSWordSections::HasBorderItem( const SwFormat& rFormat )
1316 {
1317     const SfxPoolItem* pItem;
1318     return SfxItemState::SET == rFormat.GetItemState(RES_BOX, true, &pItem) &&
1319             (   static_cast<const SvxBoxItem*>(pItem)->GetTop() ||
1320                 static_cast<const SvxBoxItem*>(pItem)->GetBottom()  ||
1321                 static_cast<const SvxBoxItem*>(pItem)->GetLeft()  ||
1322                 static_cast<const SvxBoxItem*>(pItem)->GetRight() );
1323 }
1324 
StartSection()1325 void WW8AttributeOutput::StartSection()
1326 {
1327     m_rWW8Export.pO->clear();
1328 }
1329 
SectFootnoteEndnotePr()1330 void WW8AttributeOutput::SectFootnoteEndnotePr()
1331 {
1332     const SwFootnoteInfo& rInfo = m_rWW8Export.m_rDoc.GetFootnoteInfo();
1333     const SwEndNoteInfo& rEndNoteInfo = m_rWW8Export.m_rDoc.GetEndNoteInfo();
1334     m_rWW8Export.InsUInt16( NS_sprm::SRncFtn::val );
1335     switch( rInfo.m_eNum )
1336     {
1337     case FTNNUM_PAGE:     m_rWW8Export.pO->push_back( sal_uInt8/*rncRstPage*/ (2) ); break;
1338     case FTNNUM_CHAPTER:  m_rWW8Export.pO->push_back( sal_uInt8/*rncRstSect*/ (1) ); break;
1339     default:              m_rWW8Export.pO->push_back( sal_uInt8/*rncCont*/ (0) ); break;
1340     }
1341 
1342     m_rWW8Export.InsUInt16(NS_sprm::SNfcFtnRef::val);
1343     sal_uInt8 nId = WW8Export::GetNumId(rInfo.m_aFormat.GetNumberingType());
1344     SwWW8Writer::InsUInt16(*m_rWW8Export.pO, nId);
1345     m_rWW8Export.InsUInt16(NS_sprm::SNfcEdnRef::val);
1346     nId = WW8Export::GetNumId(rEndNoteInfo.m_aFormat.GetNumberingType());
1347     SwWW8Writer::InsUInt16(*m_rWW8Export.pO, nId);
1348 }
1349 
SectionFormProtection(bool bProtected)1350 void WW8AttributeOutput::SectionFormProtection( bool bProtected )
1351 {
1352     //If the document is to be exported as protected, then if a segment
1353     //is not protected, set the unlocked flag
1354     if ( m_rWW8Export.pSepx->DocumentIsProtected() && !bProtected )
1355     {
1356         SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::SFProtected::val );
1357         m_rWW8Export.pO->push_back( 1 );
1358     }
1359 }
1360 
SectionLineNumbering(sal_uLong nRestartNo,const SwLineNumberInfo & rLnNumInfo)1361 void WW8AttributeOutput::SectionLineNumbering( sal_uLong nRestartNo, const SwLineNumberInfo& rLnNumInfo )
1362 {
1363     // sprmSNLnnMod - activate Line Numbering and define Modulo
1364     SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::SNLnnMod::val );
1365     SwWW8Writer::InsUInt16( *m_rWW8Export.pO, rLnNumInfo.GetCountBy() );
1366 
1367     // sprmSDxaLnn - xPosition of Line Number
1368     SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::SDxaLnn::val );
1369     SwWW8Writer::InsUInt16( *m_rWW8Export.pO, rLnNumInfo.GetPosFromLeft() );
1370 
1371     // sprmSLnc - restart number: 0 per page, 1 per section, 2 never restart
1372     if ( nRestartNo || !rLnNumInfo.IsRestartEachPage() )
1373     {
1374         SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::SLnc::val );
1375         m_rWW8Export.pO->push_back( nRestartNo ? 1 : 2 );
1376     }
1377 
1378     // sprmSLnnMin - Restart the Line Number with given value
1379     if ( nRestartNo )
1380     {
1381         SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::SLnnMin::val );
1382         SwWW8Writer::InsUInt16( *m_rWW8Export.pO, o3tl::narrowing<sal_uInt16>(nRestartNo) - 1 );
1383     }
1384 }
1385 
SectionTitlePage()1386 void WW8AttributeOutput::SectionTitlePage()
1387 {
1388     // sprmSFTitlePage
1389     SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::SFTitlePage::val );
1390     m_rWW8Export.pO->push_back( 1 );
1391 }
1392 
SectionPageBorders(const SwFrameFormat * pPdFormat,const SwFrameFormat * pPdFirstPgFormat)1393 void WW8AttributeOutput::SectionPageBorders( const SwFrameFormat* pPdFormat, const SwFrameFormat* pPdFirstPgFormat )
1394 {
1395     // write border of page
1396     sal_uInt16 nPgBorder = MSWordSections::HasBorderItem( *pPdFormat ) ? 0 : USHRT_MAX;
1397     if ( pPdFormat != pPdFirstPgFormat )
1398     {
1399         if ( MSWordSections::HasBorderItem( *pPdFirstPgFormat ) )
1400         {
1401             if ( USHRT_MAX == nPgBorder )
1402             {
1403                 nPgBorder = 1;
1404                 // only the first page outlined -> Get the BoxItem from the correct format
1405                 m_rWW8Export.m_pISet = &pPdFirstPgFormat->GetAttrSet();
1406                 OutputItem( pPdFirstPgFormat->GetFormatAttr( RES_BOX ) );
1407             }
1408         }
1409         else if ( !nPgBorder )
1410             nPgBorder = 2;
1411     }
1412 
1413     // [MS-DOC] 2.9.255 SPgbPropOperand; 2.9.185 PgbOffsetFrom
1414     if (m_bFromEdge)
1415         nPgBorder |= (1<<5);
1416 
1417     if ( USHRT_MAX != nPgBorder )
1418     {
1419         // write the Flag and Border Attribute
1420         SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::SPgbProp::val );
1421         SwWW8Writer::InsUInt16( *m_rWW8Export.pO, nPgBorder );
1422     }
1423 }
1424 
SectionBiDi(bool bBiDi)1425 void WW8AttributeOutput::SectionBiDi( bool bBiDi )
1426 {
1427     SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::SFBiDi::val );
1428     m_rWW8Export.pO->push_back( bBiDi? 1: 0 );
1429 }
1430 
SectionPageNumbering(sal_uInt16 nNumType,const::std::optional<sal_uInt16> & oPageRestartNumber)1431 void WW8AttributeOutput::SectionPageNumbering( sal_uInt16 nNumType, const ::std::optional<sal_uInt16>& oPageRestartNumber )
1432 {
1433     // sprmSNfcPgn
1434     sal_uInt8 nb = WW8Export::GetNumId( nNumType );
1435     SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::SNfcPgn::val );
1436     m_rWW8Export.pO->push_back( nb );
1437 
1438     if ( oPageRestartNumber )
1439     {
1440         // sprmSFPgnRestart
1441         SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::SFPgnRestart::val );
1442         m_rWW8Export.pO->push_back( 1 );
1443 
1444         // sprmSPgnStart
1445         SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::SPgnStart97::val );
1446         SwWW8Writer::InsUInt16( *m_rWW8Export.pO, *oPageRestartNumber );
1447     }
1448 }
1449 
SectionType(sal_uInt8 nBreakCode)1450 void WW8AttributeOutput::SectionType( sal_uInt8 nBreakCode )
1451 {
1452     if ( 2 != nBreakCode ) // new page is the default
1453     {
1454         SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::SBkc::val );
1455         m_rWW8Export.pO->push_back( nBreakCode );
1456     }
1457 }
1458 
SetupSectionPositions(WW8_PdAttrDesc * pA)1459 void WW8Export::SetupSectionPositions( WW8_PdAttrDesc* pA )
1460 {
1461     if ( !pA )
1462         return;
1463 
1464     if ( !pO->empty() ) // are there attributes ?
1465     {
1466         pA->m_nLen = pO->size();
1467         pA->m_pData.reset(new sal_uInt8 [pO->size()]);
1468         // store for later
1469         memcpy( pA->m_pData.get(), pO->data(), pO->size() );
1470         pO->clear(); // clear HdFt-Text
1471     }
1472     else // no attributes there
1473     {
1474         pA->m_pData.reset();
1475         pA->m_nLen = 0;
1476     }
1477 }
1478 
TextVerticalAdjustment(const drawing::TextVerticalAdjust nVA)1479 void WW8AttributeOutput::TextVerticalAdjustment( const drawing::TextVerticalAdjust nVA )
1480 {
1481     if ( drawing::TextVerticalAdjust_TOP == nVA ) // top alignment is the default
1482         return;
1483 
1484     sal_uInt8 nMSVA = 0;
1485     switch( nVA )
1486     {
1487         case drawing::TextVerticalAdjust_CENTER:
1488             nMSVA = 1;
1489             break;
1490         case drawing::TextVerticalAdjust_BOTTOM:  //Writer = 2, Word = 3
1491             nMSVA = 3;
1492             break;
1493         case drawing::TextVerticalAdjust_BLOCK:   //Writer = 3, Word = 2
1494             nMSVA = 2;
1495             break;
1496         default:
1497             break;
1498     }
1499     SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::SVjc::val );
1500     m_rWW8Export.pO->push_back( nMSVA );
1501 }
1502 
WriteHeadersFooters(sal_uInt8 nHeadFootFlags,const SwFrameFormat & rFormat,const SwFrameFormat & rLeftHeaderFormat,const SwFrameFormat & rLeftFooterFormat,const SwFrameFormat & rFirstPageFormat,sal_uInt8 nBreakCode,bool)1503 void WW8Export::WriteHeadersFooters( sal_uInt8 nHeadFootFlags,
1504         const SwFrameFormat& rFormat, const SwFrameFormat& rLeftHeaderFormat, const SwFrameFormat& rLeftFooterFormat, const SwFrameFormat& rFirstPageFormat, sal_uInt8 nBreakCode, bool /*bEvenAndOddHeaders*/ )
1505 {
1506     sal_uLong nCpPos = Fc2Cp( Strm().Tell() );
1507 
1508     IncrementHdFtIndex();
1509     if ( !(nHeadFootFlags & WW8_HEADER_EVEN) && pDop->fFacingPages )
1510         pSepx->OutHeaderFooter( *this, true, rFormat, nCpPos, nHeadFootFlags, WW8_HEADER_ODD, nBreakCode );
1511     else
1512         pSepx->OutHeaderFooter( *this, true, rLeftHeaderFormat, nCpPos, nHeadFootFlags, WW8_HEADER_EVEN, nBreakCode );
1513     IncrementHdFtIndex();
1514     pSepx->OutHeaderFooter( *this, true, rFormat, nCpPos, nHeadFootFlags, WW8_HEADER_ODD, nBreakCode );
1515 
1516     IncrementHdFtIndex();
1517     if ( !(nHeadFootFlags & WW8_FOOTER_EVEN) && pDop->fFacingPages )
1518         pSepx->OutHeaderFooter( *this, false, rFormat, nCpPos, nHeadFootFlags, WW8_FOOTER_ODD, nBreakCode );
1519     else
1520         pSepx->OutHeaderFooter( *this, false, rLeftFooterFormat, nCpPos, nHeadFootFlags, WW8_FOOTER_EVEN, nBreakCode );
1521     IncrementHdFtIndex();
1522     pSepx->OutHeaderFooter( *this, false, rFormat, nCpPos, nHeadFootFlags, WW8_FOOTER_ODD, nBreakCode );
1523 
1524     //#i24344# Drawing objects cannot be directly shared between main hd/ft
1525     //and title hd/ft so we need to differentiate them
1526     IncrementHdFtIndex();
1527     pSepx->OutHeaderFooter( *this, true, rFirstPageFormat, nCpPos, nHeadFootFlags, WW8_HEADER_FIRST, nBreakCode );
1528     pSepx->OutHeaderFooter( *this, false, rFirstPageFormat, nCpPos, nHeadFootFlags, WW8_FOOTER_FIRST, nBreakCode );
1529 }
1530 
1531 namespace
1532 {
1533 /**
1534  * Determines if the continuous section break we start should use page style properties (header,
1535  * footer, margins) from the next page style of the previous section.
1536  */
UsePrevSectionNextStyle(sal_uInt8 nBreakCode,const SwPageDesc * pPd,const WW8_SepInfo & rSepInfo)1537 bool UsePrevSectionNextStyle(sal_uInt8 nBreakCode, const SwPageDesc* pPd,
1538                              const WW8_SepInfo& rSepInfo)
1539 {
1540     if (nBreakCode != 0)
1541     {
1542         // Not a continuous section break.
1543         return false;
1544     }
1545 
1546     if (!pPd->GetFollow())
1547     {
1548         // Page style has no follow style.
1549         return false;
1550     }
1551 
1552     // We start a continuous section break without headers/footers. Possibly the importer had
1553     // headers/footers for this section break and put them to the closest page break's page style's
1554     // next page style. See "find a node in the section that has a page break" in writerfilter/.
1555     // Try the last-in-practice paragraph of the previous section.
1556     const SwSectionFormat* pSection = rSepInfo.pSectionFormat;
1557     if (pSection == reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1)))
1558     {
1559         return false;
1560     }
1561 
1562     const SwNodeIndex* pSectionStart = pSection->GetContent().GetContentIdx();
1563     if (!pSectionStart)
1564     {
1565         return false;
1566     }
1567 
1568     SwPaM aPaM(*pSectionStart);
1569     aPaM.Move(fnMoveBackward);
1570     if (!aPaM.GetNode().IsTextNode())
1571     {
1572         return false;
1573     }
1574 
1575     SwTextNode* pTextNode = aPaM.GetNode().GetTextNode();
1576     const SwAttrSet* pParaProps = &pTextNode->GetSwAttrSet();
1577     sal_uInt32 nCharHeight = pParaProps->GetSize().GetHeight();
1578     if (nCharHeight > 20)
1579     {
1580         return false;
1581     }
1582 
1583     aPaM.Move(fnMoveBackward);
1584     if (!aPaM.GetNode().IsTextNode())
1585     {
1586         return false;
1587     }
1588 
1589     pTextNode = aPaM.GetNode().GetTextNode();
1590     pParaProps = &pTextNode->GetSwAttrSet();
1591     return pParaProps->HasItem(RES_PAGEDESC);
1592 }
1593 }
1594 
SectionProperties(const WW8_SepInfo & rSepInfo,WW8_PdAttrDesc * pA)1595 void MSWordExportBase::SectionProperties( const WW8_SepInfo& rSepInfo, WW8_PdAttrDesc* pA )
1596 {
1597     const SwPageDesc* pPd = rSepInfo.pPageDesc;
1598 
1599     if ( rSepInfo.pSectionFormat && !pPd )
1600         pPd = &m_rDoc.GetPageDesc( 0 );
1601 
1602     m_pCurrentPageDesc = pPd;
1603 
1604     if ( !pPd )
1605         return;
1606 
1607     bool bOldPg = m_bOutPageDescs;
1608     m_bOutPageDescs = true;
1609 
1610     AttrOutput().StartSection();
1611 
1612     AttrOutput().SectFootnoteEndnotePr();
1613 
1614     // forms
1615     AttrOutput().SectionFormProtection( rSepInfo.IsProtected() );
1616 
1617     // line numbers
1618     const SwLineNumberInfo& rLnNumInfo = m_rDoc.GetLineNumberInfo();
1619     if ( rLnNumInfo.IsPaintLineNumbers() )
1620         AttrOutput().SectionLineNumbering( rSepInfo.nLnNumRestartNo, rLnNumInfo );
1621 
1622     /*  sprmSBkc, break code:   0 No break, 1 New column
1623         2 New page, 3 Even page, 4 Odd page
1624         */
1625     sal_uInt8 nBreakCode = 2;            // default start new page
1626     bool bOutPgDscSet = true, bLeftRightPgChain = false, bOutputStyleItemSet = false;
1627     bool bEnsureHeaderFooterWritten = rSepInfo.pSectionFormat && rSepInfo.bIsFirstParagraph;
1628     const SwFrameFormat* pPdFormat = &pPd->GetMaster();
1629     bool bUsePrevSectionNextStyle = false;
1630     if ( rSepInfo.pSectionFormat )
1631     {
1632         // if pSectionFormat is set, then there is a SectionNode
1633         //  valid pointer -> start Section ,
1634         //  0xfff -> Section terminated
1635         nBreakCode = 0;         // consecutive section
1636 
1637         if ( rSepInfo.pPDNd && rSepInfo.pPDNd->IsContentNode() )
1638         {
1639             if ( !NoPageBreakSection( &rSepInfo.pPDNd->GetContentNode()->GetSwAttrSet() ) )
1640             {
1641                 nBreakCode = 2;
1642             }
1643         }
1644 
1645         if ( reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1)) == rSepInfo.pSectionFormat )
1646             bEnsureHeaderFooterWritten |= !rSepInfo.pPDNd && GetExportFormat() != ExportFormat::RTF;
1647         else
1648         {
1649             if ( nBreakCode == 0 )
1650                 bOutPgDscSet = false;
1651 
1652             // produce Itemset, which inherits PgDesk-Attr-Set:
1653             // as child also the parent is searched if 'deep'-OutputItemSet
1654             const SfxItemSet* pPdSet = &pPdFormat->GetAttrSet();
1655 
1656             bUsePrevSectionNextStyle = GetExportFormat() == ExportFormat::DOCX
1657                                        && UsePrevSectionNextStyle(nBreakCode, pPd, rSepInfo);
1658             if (bUsePrevSectionNextStyle)
1659             {
1660                 // Take page margins from the previous section's next style.
1661                 pPdSet = &pPd->GetFollow()->GetMaster().GetAttrSet();
1662             }
1663 
1664             SfxItemSet aSet( *pPdSet->GetPool(), pPdSet->GetRanges() );
1665             aSet.SetParent( pPdSet );
1666 
1667             // at the child ONLY change column structure according to Sect-Attr.
1668 
1669             const SvxLRSpaceItem &rSectionLR =
1670                 ItemGet<SvxLRSpaceItem>( *(rSepInfo.pSectionFormat), RES_LR_SPACE );
1671             const SvxLRSpaceItem &rPageLR =
1672                 ItemGet<SvxLRSpaceItem>( *pPdFormat, RES_LR_SPACE );
1673 
1674             SvxLRSpaceItem aResultLR( rPageLR.GetLeft() +
1675                     rSectionLR.GetLeft(), rPageLR.GetRight() +
1676                     rSectionLR.GetRight(), 0, 0, RES_LR_SPACE );
1677             //i120133: The Section width should consider section indent value.
1678             if (rSectionLR.GetLeft()+rSectionLR.GetRight()!=0)
1679             {
1680                 const SwFormatCol& rCol = dynamic_cast<const SwFormatCol&>(rSepInfo.pSectionFormat->GetFormatAttr(RES_COL));
1681                 SwFormatCol aCol(rCol);
1682                 aCol.SetAdjustValue(rSectionLR.GetLeft()+rSectionLR.GetRight());
1683                 aSet.Put(aCol);
1684             }
1685             else
1686                 aSet.Put(rSepInfo.pSectionFormat->GetFormatAttr(RES_COL));
1687 
1688             aSet.Put( aResultLR );
1689 
1690             // and write into the WW-File
1691             const SfxItemSet* pOldI = m_pISet;
1692             m_pISet = &aSet;
1693 
1694             // Switch off test on default item values, if page description
1695             // set (value of <bOutPgDscSet>) isn't written.
1696             AttrOutput().OutputStyleItemSet( aSet, bOutPgDscSet );
1697             bOutputStyleItemSet = true;
1698 
1699             //Cannot export as normal page framedir, as continuous sections
1700             //cannot contain any grid settings like proper sections
1701             AttrOutput().SectionBiDi( SvxFrameDirection::Horizontal_RL_TB == TrueFrameDirection( *rSepInfo.pSectionFormat ) );
1702 
1703             m_pISet = pOldI;
1704         }
1705     }
1706 
1707     // Libreoffice 4.0 introduces support for page styles (SwPageDesc) with
1708     // a different header/footer for the first page.  The same effect can be
1709     // achieved by chaining two page styles together (SwPageDesc::GetFollow)
1710     // which are identical except for header/footer.
1711     // The latter method was previously used by the doc/docx import filter.
1712     // In both of these cases, we emit a single Word section with different
1713     // first page header/footer.
1714     const SwFrameFormat* pPdFirstPgFormat = &pPd->GetFirstMaster();
1715     bool titlePage = !pPd->IsFirstShared();
1716     if ( bOutPgDscSet )
1717     {
1718         // if a Follow is set and it does not point to itself,
1719         // then there is a page chain.
1720         // If this emulates a "first page", we can detect it here and write
1721         // it as title page.
1722         // With Left/Right changes it's different - we have to detect where
1723         // the change of pages is, but here it's too late for that!
1724         // tdf#101814 if there is already an explicit first-page, no point
1725         // in checking heuristics here.
1726         if ( !titlePage && pPd->GetFollow() && pPd != pPd->GetFollow() &&
1727              pPd->GetFollow()->GetFollow() == pPd->GetFollow() &&
1728              pPd->IsHeaderShared() && pPd->IsFooterShared() &&
1729              ( !rSepInfo.pPDNd || pPd->IsFollowNextPageOfNode( *rSepInfo.pPDNd ) ) )
1730         {
1731             const SwPageDesc *pFollow = pPd->GetFollow();
1732             const SwFrameFormat& rFollowFormat = pFollow->GetMaster();
1733             if (sw::util::IsPlausableSingleWordSection(*pPdFirstPgFormat, rFollowFormat))
1734             {
1735                 if (rSepInfo.pPDNd)
1736                     pPdFirstPgFormat = pPd->GetPageFormatOfNode( *rSepInfo.pPDNd );
1737                 else
1738                     pPdFirstPgFormat = &pPd->GetMaster();
1739 
1740                 m_pCurrentPageDesc = pPd = pFollow;
1741                 pPdFormat = &rFollowFormat;
1742 
1743                 // has different headers/footers for the title page
1744                 titlePage = true;
1745             }
1746         }
1747 
1748         const SfxItemSet* pOldI = m_pISet;
1749 
1750         const SfxPoolItem* pItem;
1751         if ( titlePage && SfxItemState::SET ==
1752                 pPdFirstPgFormat->GetItemState( RES_PAPER_BIN, true, &pItem ) )
1753         {
1754             m_pISet = &pPdFirstPgFormat->GetAttrSet();
1755             m_bOutFirstPage = true;
1756             AttrOutput().OutputItem( *pItem );
1757             m_bOutFirstPage = false;
1758         }
1759 
1760         // left-/right chain of pagedescs ?
1761         if ( pPd->GetFollow() && pPd != pPd->GetFollow() &&
1762                 pPd->GetFollow()->GetFollow() == pPd &&
1763                 (( UseOnPage::Left == ( UseOnPage::All & pPd->ReadUseOn() ) &&
1764                    UseOnPage::Right == ( UseOnPage::All & pPd->GetFollow()->ReadUseOn() )) ||
1765                  ( UseOnPage::Right == ( UseOnPage::All & pPd->ReadUseOn() ) &&
1766                    UseOnPage::Left == ( UseOnPage::All & pPd->GetFollow()->ReadUseOn() )) ))
1767         {
1768             bLeftRightPgChain = true;
1769 
1770             // which is the reference point? (left or right?)
1771             // assume it is on the right side!
1772             if ( UseOnPage::Left == ( UseOnPage::All & pPd->ReadUseOn() ) )
1773             {
1774                 nBreakCode = 3;
1775                 pPdFormat = &pPd->GetMaster();  //use the current page for settings (margins/width etc)
1776                 pPd = pPd->GetFollow(); //switch to the right page for the right/odd header/footer
1777             }
1778             else
1779                 nBreakCode = 4;
1780         }
1781 
1782         m_pISet = &pPdFormat->GetAttrSet();
1783         if (!bOutputStyleItemSet)
1784         {
1785             if (titlePage)
1786             {
1787                 m_pFirstPageFormat = pPdFirstPgFormat;
1788             }
1789 
1790             AttrOutput().OutputStyleItemSet( pPdFormat->GetAttrSet(), false );
1791 
1792             if (titlePage)
1793             {
1794                 m_pFirstPageFormat = nullptr;
1795             }
1796         }
1797         AttrOutput().SectionPageBorders( pPdFormat, pPdFirstPgFormat );
1798         m_pISet = pOldI;
1799 
1800         // then the rest of the settings from PageDesc
1801         AttrOutput().SectionPageNumbering( pPd->GetNumType().GetNumberingType(), rSepInfo.oPgRestartNo );
1802 
1803         // will it be only left or only right pages?
1804         if ( 2 == nBreakCode )
1805         {
1806             if ( UseOnPage::Left == ( UseOnPage::All & pPd->ReadUseOn() ) )
1807                 nBreakCode = 3;
1808             else if ( UseOnPage::Right == ( UseOnPage::All & pPd->ReadUseOn() ) )
1809                 nBreakCode = 4;
1810         }
1811     }
1812 
1813     if (titlePage)
1814         AttrOutput().SectionTitlePage();
1815 
1816     AttrOutput().SectionType( nBreakCode );
1817 
1818     if( rSepInfo.pPageDesc ) {
1819         AttrOutput().TextVerticalAdjustment( rSepInfo.pPageDesc->GetVerticalAdjustment() );
1820     }
1821 
1822     // Header or Footer
1823     sal_uInt8 nHeadFootFlags = 0;
1824     // Should we output a w:evenAndOddHeaders tag or not?
1825     // N.B.: despite its name this tag affects _both_ headers and footers!
1826     bool bEvenAndOddHeaders = true;
1827     bool bEvenAndOddFooters = true;
1828 
1829     const SwFrameFormat* pPdLeftHeaderFormat = nullptr;
1830     const SwFrameFormat* pPdLeftFooterFormat = nullptr;
1831     if (bLeftRightPgChain)
1832     {
1833         const SwFrameFormat* pHeaderFormat = pPd->GetStashedFrameFormat(true, true, true);
1834         const SwFrameFormat* pFooterFormat = pPd->GetStashedFrameFormat(false, true, true);
1835         if (pHeaderFormat)
1836         {
1837             pPdLeftHeaderFormat = pHeaderFormat;
1838             bEvenAndOddHeaders = false;
1839         }
1840         else
1841         {
1842             pPdLeftHeaderFormat = &pPd->GetFollow()->GetFirstLeft();
1843         }
1844         if (pFooterFormat)
1845         {
1846             pPdLeftFooterFormat = pFooterFormat;
1847             bEvenAndOddFooters = false;
1848         }
1849         else
1850         {
1851             pPdLeftFooterFormat = &pPd->GetFollow()->GetFirstLeft();
1852         }
1853     }
1854     else
1855     {
1856         const SwFrameFormat* pHeaderFormat = pPd->GetStashedFrameFormat(true, true, false);
1857         const SwFrameFormat* pFooterFormat = pPd->GetStashedFrameFormat(false, true, false);
1858         if (pHeaderFormat)
1859         {
1860             pPdLeftHeaderFormat = pHeaderFormat;
1861             bEvenAndOddHeaders = false;
1862         }
1863         else
1864         {
1865             pPdLeftHeaderFormat = &pPd->GetLeft();
1866         }
1867         if (pFooterFormat)
1868         {
1869             pPdLeftFooterFormat = pFooterFormat;
1870             bEvenAndOddFooters = false;
1871         }
1872         else
1873         {
1874             pPdLeftFooterFormat = &pPd->GetLeft();
1875         }
1876     }
1877 
1878     // Ensure that headers are written if section is first paragraph
1879     if ( nBreakCode != 0 || bEnsureHeaderFooterWritten )
1880     {
1881         if ( titlePage )
1882         {
1883             // there is a First Page:
1884             MSWordSections::SetHeaderFlag( nHeadFootFlags, *pPdFirstPgFormat, WW8_HEADER_FIRST );
1885             MSWordSections::SetFooterFlag( nHeadFootFlags, *pPdFirstPgFormat, WW8_FOOTER_FIRST );
1886         }
1887         else
1888         {
1889             if ( pPd->GetStashedFrameFormat(true, true, true) && pPdLeftHeaderFormat && pPdLeftHeaderFormat->GetHeader().GetHeaderFormat() )
1890             {
1891                 MSWordSections::SetHeaderFlag( nHeadFootFlags, *pPdLeftHeaderFormat, WW8_HEADER_FIRST );
1892             }
1893             if ( pPd->GetStashedFrameFormat(false, true, true) && pPdLeftFooterFormat && pPdLeftFooterFormat->GetFooter().GetFooterFormat() )
1894             {
1895                 MSWordSections::SetFooterFlag( nHeadFootFlags, *pPdLeftFooterFormat, WW8_FOOTER_FIRST );
1896             }
1897         }
1898 
1899         MSWordSections::SetHeaderFlag( nHeadFootFlags, *pPdFormat, WW8_HEADER_ODD );
1900         MSWordSections::SetFooterFlag( nHeadFootFlags, *pPdFormat, WW8_FOOTER_ODD );
1901 
1902         if ( !pPd->IsHeaderShared() || bLeftRightPgChain )
1903         {
1904             MSWordSections::SetHeaderFlag( nHeadFootFlags, *pPdLeftHeaderFormat, WW8_HEADER_EVEN );
1905         }
1906         else if ( pPd->IsHeaderShared() && pPd->GetStashedFrameFormat(true, true, false) && pPdLeftHeaderFormat && pPdLeftHeaderFormat->GetHeader().GetHeaderFormat() )
1907         {
1908             MSWordSections::SetHeaderFlag( nHeadFootFlags, *pPdLeftHeaderFormat, WW8_HEADER_EVEN );
1909             bEvenAndOddHeaders = false;
1910         }
1911 
1912         if ( !pPd->IsFooterShared() || bLeftRightPgChain )
1913         {
1914             MSWordSections::SetFooterFlag( nHeadFootFlags, *pPdLeftFooterFormat, WW8_FOOTER_EVEN );
1915         }
1916         else if ( pPd->IsFooterShared() && pPd->GetStashedFrameFormat(false, true, false) && pPdLeftFooterFormat && pPdLeftFooterFormat->GetFooter().GetFooterFormat() )
1917         {
1918             MSWordSections::SetFooterFlag( nHeadFootFlags, *pPdLeftFooterFormat, WW8_FOOTER_EVEN );
1919             bEvenAndOddFooters = false;
1920         }
1921     }
1922 
1923     // binary filters only
1924     SetupSectionPositions( pA );
1925 
1926     /*
1927        !!!!!!!!!!!
1928     // borders at header and footer texts would be done like this:
1929     // This should use something like pOut,
1930     // which is repeated with every special text line.
1931     const SwFrameFormat* pFFormat = rFt.GetFooterFormat();
1932     const SvxBoxItem& rBox = pFFormat->GetBox(false);
1933     OutWW8_SwFormatBox1( m_rWW8Export.pOut, rBox, false);
1934     !!!!!!!!!!!
1935     You can turn this into paragraph attributes, which are then observed in each paragraph.
1936     Applies to background / border.
1937     !!!!!!!!!!!
1938     */
1939 
1940     const SwTextNode *pOldPageRoot = GetHdFtPageRoot();
1941     SetHdFtPageRoot( rSepInfo.pPDNd ? rSepInfo.pPDNd->GetTextNode() : nullptr );
1942 
1943     if (bUsePrevSectionNextStyle && nHeadFootFlags == 0)
1944     {
1945         // Take headers/footers from the previous section's next style.
1946         pPdFormat = &pPd->GetFollow()->GetMaster();
1947         MSWordSections::SetHeaderFlag(nHeadFootFlags, *pPdFormat, WW8_HEADER_ODD);
1948         MSWordSections::SetFooterFlag(nHeadFootFlags, *pPdFormat, WW8_FOOTER_ODD);
1949     }
1950 
1951     WriteHeadersFooters( nHeadFootFlags, *pPdFormat, *pPdLeftHeaderFormat, *pPdLeftFooterFormat, *pPdFirstPgFormat, nBreakCode, bEvenAndOddHeaders && bEvenAndOddFooters );
1952 
1953     SetHdFtPageRoot( pOldPageRoot );
1954 
1955     AttrOutput().EndSection();
1956 
1957     // outside of the section properties again
1958     m_bOutPageDescs = bOldPg;
1959 }
1960 
WriteKFText(WW8Export & rWrt)1961 bool WW8_WrPlcSepx::WriteKFText( WW8Export& rWrt )
1962 {
1963     sal_uLong nCpStart = rWrt.Fc2Cp( rWrt.Strm().Tell() );
1964 
1965     OSL_ENSURE( !pTextPos, "who set the pointer?" );
1966     pTextPos.reset( new WW8_WrPlc0( nCpStart ) );
1967 
1968     WriteFootnoteEndText( rWrt, nCpStart );
1969     CheckForFacinPg( rWrt );
1970 
1971     unsigned int nOldIndex = rWrt.GetHdFtIndex();
1972     rWrt.SetHdFtIndex( 0 );
1973 
1974     for (const WW8_SepInfo & rSepInfo : aSects)
1975     {
1976         auto pAttrDesc = std::make_shared<WW8_PdAttrDesc>();
1977         m_SectionAttributes.push_back(pAttrDesc);
1978 
1979         rWrt.SectionProperties( rSepInfo, pAttrDesc.get() );
1980 
1981         // FIXME: this writes the section properties, but not of all sections;
1982         // it's possible that later in the document (e.g. in endnotes) sections
1983         // are added, but they won't have their properties written here!
1984         m_bHeaderFooterWritten = true;
1985     }
1986     rWrt.SetHdFtIndex( nOldIndex ); //0
1987 
1988     if ( pTextPos->Count() )
1989     {
1990         // HdFt available?
1991         sal_uLong nCpEnd = rWrt.Fc2Cp( rWrt.Strm().Tell() );
1992         pTextPos->Append( nCpEnd );  // End of last Header/Footer for PlcfHdd
1993 
1994         if ( nCpEnd > nCpStart )
1995         {
1996             ++nCpEnd;
1997             pTextPos->Append( nCpEnd + 1 );  // End of last Header/Footer for PlcfHdd
1998 
1999             rWrt.WriteStringAsPara( OUString() ); // CR to the end ( otherwise WW complains )
2000         }
2001         rWrt.m_pFieldHdFt->Finish( nCpEnd, rWrt.pFib->m_ccpText + rWrt.pFib->m_ccpFootnote );
2002         rWrt.pFib->m_ccpHdr = nCpEnd - nCpStart;
2003     }
2004     else
2005     {
2006         pTextPos.reset();
2007     }
2008 
2009     return rWrt.pFib->m_ccpHdr != 0;
2010 }
2011 
WriteSepx(SvStream & rStrm) const2012 void WW8_WrPlcSepx::WriteSepx( SvStream& rStrm ) const
2013 {
2014     OSL_ENSURE(m_SectionAttributes.size() == static_cast<size_t>(aSects.size())
2015         , "WriteSepx(): arrays out of sync!");
2016     for (const auto & rSectionAttribute : m_SectionAttributes) // all sections
2017     {
2018         WW8_PdAttrDesc *const pA = rSectionAttribute.get();
2019         if (pA->m_nLen && pA->m_pData != nullptr)
2020         {
2021             pA->m_nSepxFcPos = rStrm.Tell();
2022             rStrm.WriteUInt16(pA->m_nLen);
2023             rStrm.WriteBytes(pA->m_pData.get(), pA->m_nLen);
2024         }
2025     }
2026 }
2027 
WritePlcSed(WW8Export & rWrt) const2028 void WW8_WrPlcSepx::WritePlcSed( WW8Export& rWrt ) const
2029 {
2030     OSL_ENSURE(m_SectionAttributes.size() == static_cast<size_t>(aSects.size())
2031         , "WritePlcSed(): arrays out of sync!");
2032     OSL_ENSURE( aCps.size() == aSects.size() + 1, "WrPlcSepx: DeSync" );
2033     sal_uLong nFcStart = rWrt.pTableStrm->Tell();
2034 
2035     for( decltype(aSects)::size_type i = 0; i <= aSects.size(); i++ )
2036     {
2037         sal_uInt32 nP = aCps[i];
2038         rWrt.pTableStrm->WriteUInt32(nP);
2039     }
2040 
2041     static WW8_SED aSed = {{4, 0},{0, 0, 0, 0},{0, 0},{0xff, 0xff, 0xff, 0xff}};
2042 
2043     for (const auto & rSectionAttribute : m_SectionAttributes)
2044     {
2045         // Sepx-Pos
2046         UInt32ToSVBT32( rSectionAttribute->m_nSepxFcPos, aSed.fcSepx );
2047         rWrt.pTableStrm->WriteBytes(&aSed, sizeof(aSed));
2048     }
2049     rWrt.pFib->m_fcPlcfsed = nFcStart;
2050     rWrt.pFib->m_lcbPlcfsed = rWrt.pTableStrm->Tell() - nFcStart;
2051 }
2052 
WritePlcHdd(WW8Export & rWrt) const2053 void WW8_WrPlcSepx::WritePlcHdd( WW8Export& rWrt ) const
2054 {
2055     // Don't write out the PlcfHdd if ccpHdd is 0: it's a validation failure case.
2056     if( rWrt.pFib->m_ccpHdr != 0 && pTextPos && pTextPos->Count() )
2057     {
2058         rWrt.pFib->m_fcPlcfhdd = rWrt.pTableStrm->Tell();
2059         pTextPos->Write( *rWrt.pTableStrm );             // Plc0
2060         rWrt.pFib->m_lcbPlcfhdd = rWrt.pTableStrm->Tell() -
2061                                 rWrt.pFib->m_fcPlcfhdd;
2062     }
2063 }
2064 
WriteHeaderFooterText(const SwFormat & rFormat,bool bHeader)2065 void MSWordExportBase::WriteHeaderFooterText( const SwFormat& rFormat, bool bHeader )
2066 {
2067     const SwFormatContent *pContent;
2068     if ( bHeader )
2069     {
2070         m_bHasHdr = true;
2071         const SwFormatHeader& rHd = rFormat.GetHeader();
2072         OSL_ENSURE( rHd.GetHeaderFormat(), "Header text is not here" );
2073         pContent = &rHd.GetHeaderFormat()->GetContent();
2074     }
2075     else
2076     {
2077         m_bHasFtr = true;
2078         const SwFormatFooter& rFt = rFormat.GetFooter();
2079         OSL_ENSURE( rFt.GetFooterFormat(), "Footer text is not here" );
2080         pContent = &rFt.GetFooterFormat()->GetContent();
2081     }
2082 
2083     const SwNodeIndex* pSttIdx = pContent->GetContentIdx();
2084 
2085     if ( pSttIdx )
2086     {
2087         SwNodeIndex aIdx( *pSttIdx, 1 ),
2088         aEnd( *pSttIdx->GetNode().EndOfSectionNode() );
2089         sal_uLong nStart = aIdx.GetIndex();
2090         sal_uLong nEnd = aEnd.GetIndex();
2091 
2092         // range, i.e. valid node
2093         if ( nStart < nEnd )
2094         {
2095             bool bOldKF = m_bOutKF;
2096             m_bOutKF = true;
2097             WriteSpecialText( nStart, nEnd, TXT_HDFT );
2098             m_bOutKF = bOldKF;
2099         }
2100         else
2101             pSttIdx = nullptr;
2102     }
2103 
2104     if ( !pSttIdx )
2105     {
2106         // there is no Header/Footer, but a CR is still necessary
2107         OSL_ENSURE( pSttIdx, "Header/Footer text is not really present" );
2108         AttrOutput().EmptyParagraph();
2109     }
2110 }
2111 
2112 // class WW8_WrPlcFootnoteEdn : Collect the Footnotes and Endnotes and output their text
2113 // and Plcs at the end of the document.
2114 // WW8_WrPlcFootnoteEdn is the class for Footnotes and Endnotes
2115 
WW8_WrPlcSubDoc()2116 WW8_WrPlcSubDoc::WW8_WrPlcSubDoc()
2117 {
2118 }
2119 
~WW8_WrPlcSubDoc()2120 WW8_WrPlcSubDoc::~WW8_WrPlcSubDoc()
2121 {
2122 }
2123 
Append(WW8_CP nCp,const SwFormatFootnote & rFootnote)2124 void WW8_WrPlcFootnoteEdn::Append( WW8_CP nCp, const SwFormatFootnote& rFootnote )
2125 {
2126     aCps.push_back( nCp );
2127     aContent.push_back( &rFootnote );
2128 }
2129 
WW8_Annotation(const SwPostItField * pPostIt,WW8_CP nRangeStart,WW8_CP nRangeEnd)2130 WW8_Annotation::WW8_Annotation(const SwPostItField* pPostIt, WW8_CP nRangeStart, WW8_CP nRangeEnd)
2131     :
2132         maDateTime( DateTime::EMPTY ),
2133         m_nRangeStart(nRangeStart),
2134         m_nRangeEnd(nRangeEnd)
2135 {
2136     mpRichText = pPostIt->GetTextObject();
2137     if (!mpRichText)
2138         msSimpleText = pPostIt->GetText();
2139     msOwner = pPostIt->GetPar1();
2140     m_sInitials = pPostIt->GetInitials();
2141     maDateTime = DateTime(pPostIt->GetDate(), pPostIt->GetTime());
2142 }
2143 
WW8_Annotation(const SwRedlineData * pRedline)2144 WW8_Annotation::WW8_Annotation(const SwRedlineData* pRedline)
2145     :
2146         mpRichText(nullptr),
2147         msSimpleText(pRedline->GetComment()),
2148         msOwner(SW_MOD()->GetRedlineAuthor(pRedline->GetAuthor())),
2149         maDateTime(pRedline->GetTimeStamp()),
2150         m_nRangeStart(0),
2151         m_nRangeEnd(0)
2152 {
2153 }
2154 
HasRange() const2155 bool WW8_Annotation::HasRange() const
2156 {
2157     if (m_nRangeStart != m_nRangeEnd)
2158     {
2159         return true;
2160     }
2161 
2162     return !m_bIgnoreEmpty;
2163 }
2164 
AddRangeStartPosition(const OUString & rName,WW8_CP nStartCp,bool bIgnoreEmpty)2165 void WW8_WrPlcAnnotations::AddRangeStartPosition(const OUString& rName, WW8_CP nStartCp,
2166                                                  bool bIgnoreEmpty)
2167 {
2168     m_aRangeStartPositions[rName] = std::make_pair(nStartCp, bIgnoreEmpty);
2169 }
2170 
Append(WW8_CP nCp,const SwPostItField * pPostIt)2171 void WW8_WrPlcAnnotations::Append( WW8_CP nCp, const SwPostItField *pPostIt )
2172 {
2173     aCps.push_back( nCp );
2174     WW8_Annotation* p;
2175     if( m_aRangeStartPositions.find(pPostIt->GetName()) != m_aRangeStartPositions.end() )
2176     {
2177         auto [nStartCp, bIgnoreEmpty] = m_aRangeStartPositions[pPostIt->GetName()];
2178         p = new WW8_Annotation(pPostIt, nStartCp, nCp);
2179         p->m_bIgnoreEmpty = bIgnoreEmpty;
2180         m_aRangeStartPositions.erase(pPostIt->GetName());
2181     }
2182     else
2183     {
2184         p = new WW8_Annotation(pPostIt, nCp, nCp);
2185     }
2186     aContent.push_back( p );
2187 }
2188 
Append(WW8_CP nCp,const SwRedlineData * pRedline)2189 void WW8_WrPlcAnnotations::Append( WW8_CP nCp, const SwRedlineData *pRedline )
2190 {
2191     maProcessedRedlines.insert(pRedline);
2192     aCps.push_back( nCp );
2193     WW8_Annotation* p = new WW8_Annotation(pRedline);
2194     aContent.push_back( p );
2195 }
2196 
IsNewRedlineComment(const SwRedlineData * pRedline)2197 bool WW8_WrPlcAnnotations::IsNewRedlineComment( const SwRedlineData *pRedline )
2198 {
2199     return maProcessedRedlines.find(pRedline) == maProcessedRedlines.end();
2200 }
2201 
~WW8_WrPlcAnnotations()2202 WW8_WrPlcAnnotations::~WW8_WrPlcAnnotations()
2203 {
2204     for(const void * p : aContent)
2205         delete static_cast<WW8_Annotation const *>(p);
2206 }
2207 
WriteGenericText(WW8Export & rWrt,sal_uInt8 nTTyp,WW8_CP & rCount)2208 bool WW8_WrPlcSubDoc::WriteGenericText( WW8Export& rWrt, sal_uInt8 nTTyp,
2209     WW8_CP& rCount )
2210 {
2211     sal_uInt16 nLen = aContent.size();
2212     if ( !nLen )
2213         return false;
2214 
2215     sal_uLong nCpStart = rWrt.Fc2Cp( rWrt.Strm().Tell() );
2216     pTextPos.reset( new WW8_WrPlc0( nCpStart ) );
2217     sal_uInt16 i;
2218 
2219     switch ( nTTyp )
2220     {
2221         case TXT_ATN:
2222             for ( i = 0; i < nLen; i++ )
2223             {
2224                 // beginning for PlcfAtnText
2225                 pTextPos->Append( rWrt.Fc2Cp( rWrt.Strm().Tell() ));
2226 
2227                 rWrt.WritePostItBegin();
2228                 const WW8_Annotation& rAtn = *static_cast<const WW8_Annotation*>(aContent[i]);
2229                 if (rAtn.mpRichText)
2230                     rWrt.WriteOutliner(*rAtn.mpRichText, nTTyp);
2231                 else
2232                 {
2233                     OUString sText(rAtn.msSimpleText);
2234                     rWrt.WriteStringAsPara(sText.replace(0x0A, 0x0B));
2235                 }
2236             }
2237             break;
2238 
2239         case TXT_TXTBOX:
2240         case TXT_HFTXTBOX:
2241             for ( i = 0; i < nLen; i++ )
2242             {
2243                 // textbox content
2244                 WW8_CP nCP = rWrt.Fc2Cp( rWrt.Strm().Tell() );
2245                 aCps.insert( aCps.begin()+i, nCP );
2246                 pTextPos->Append( nCP );
2247 
2248                 if( aContent[ i ] != nullptr )
2249                 {
2250                     // is it a writer or sdr - textbox?
2251                     const SdrObject& rObj = *static_cast<SdrObject const *>(aContent[ i ]);
2252                     if (rObj.GetObjInventor() == SdrInventor::FmForm)
2253                     {
2254                         sal_uInt8 nOldTyp = rWrt.m_nTextTyp;
2255                         rWrt.m_nTextTyp = nTTyp;
2256                         rWrt.GetOCXExp().ExportControl(rWrt, dynamic_cast<const SdrUnoObj&>(rObj));
2257                         rWrt.m_nTextTyp = nOldTyp;
2258                     }
2259                     else if( dynamic_cast<const SdrTextObj*>( &rObj) !=  nullptr )
2260                         rWrt.WriteSdrTextObj(dynamic_cast<const SdrTextObj&>(rObj), nTTyp);
2261                     else
2262                     {
2263                         const SwFrameFormat* pFormat = ::FindFrameFormat( &rObj );
2264                         OSL_ENSURE( pFormat, "where is the format?" );
2265 
2266                         const SwNodeIndex* pNdIdx = pFormat->GetContent().GetContentIdx();
2267                         OSL_ENSURE( pNdIdx, "where is the StartNode of the Textbox?" );
2268                         rWrt.WriteSpecialText( pNdIdx->GetIndex() + 1,
2269                                                pNdIdx->GetNode().EndOfSectionIndex(),
2270                                                nTTyp );
2271                         {
2272                             SwNodeIndex aContentIdx = *pNdIdx;
2273                             ++aContentIdx;
2274                             if ( aContentIdx.GetNode().IsTableNode() )
2275                             {
2276                                 bool bContainsOnlyTables = true;
2277                                 do {
2278                                     aContentIdx = *(aContentIdx.GetNode().EndOfSectionNode());
2279                                     ++aContentIdx;
2280                                     if ( !aContentIdx.GetNode().IsTableNode() &&
2281                                          aContentIdx.GetIndex() != pNdIdx->GetNode().EndOfSectionIndex() )
2282                                     {
2283                                         bContainsOnlyTables = false;
2284                                     }
2285                                 } while ( aContentIdx.GetNode().IsTableNode() );
2286                                 if ( bContainsOnlyTables )
2287                                 {
2288                                     // Additional paragraph containing a space to
2289                                     // assure that by WW created RTF from written WW8
2290                                     // does not crash WW.
2291                                     rWrt.WriteStringAsPara( " " );
2292                                 }
2293                             }
2294                         }
2295                     }
2296                 }
2297                 else if (i < aSpareFormats.size() && aSpareFormats[i])
2298                 {
2299                     const SwFrameFormat& rFormat = *aSpareFormats[i];
2300                     const SwNodeIndex* pNdIdx = rFormat.GetContent().GetContentIdx();
2301                     rWrt.WriteSpecialText( pNdIdx->GetIndex() + 1,
2302                                pNdIdx->GetNode().EndOfSectionIndex(), nTTyp );
2303                 }
2304 
2305                 // CR at end of one textbox text ( otherwise WW gpft :-( )
2306                 rWrt.WriteStringAsPara( OUString() );
2307             }
2308             break;
2309 
2310         case TXT_EDN:
2311         case TXT_FTN:
2312             for ( i = 0; i < nLen; i++ )
2313             {
2314                 // beginning for PlcfFootnoteText/PlcfEdnText
2315                 pTextPos->Append( rWrt.Fc2Cp( rWrt.Strm().Tell() ));
2316 
2317                 // Note content
2318                 const SwFormatFootnote* pFootnote = static_cast<SwFormatFootnote const *>(aContent[ i ]);
2319                 rWrt.WriteFootnoteBegin( *pFootnote );
2320                 const SwNodeIndex* pIdx = pFootnote->GetTextFootnote()->GetStartNode();
2321                 OSL_ENSURE( pIdx, "Where is the start node of Foot-/Endnote?" );
2322                 rWrt.WriteSpecialText( pIdx->GetIndex() + 1,
2323                                        pIdx->GetNode().EndOfSectionIndex(),
2324                                        nTTyp );
2325             }
2326             break;
2327 
2328         default:
2329             OSL_ENSURE( false, "What kind of SubDocType is that?" );
2330     }
2331 
2332     pTextPos->Append( rWrt.Fc2Cp( rWrt.Strm().Tell() ));
2333     // CR to the end ( otherwise WW complains )
2334     rWrt.WriteStringAsPara( OUString() );
2335 
2336     WW8_CP nCpEnd = rWrt.Fc2Cp( rWrt.Strm().Tell() );
2337     pTextPos->Append( nCpEnd );
2338     rCount = nCpEnd - nCpStart;
2339 
2340     return ( rCount != 0 );
2341 }
2342 
lcl_AuthorComp(const std::pair<OUString,OUString> & aFirst,const std::pair<OUString,OUString> & aSecond)2343 static bool lcl_AuthorComp( const std::pair<OUString,OUString>& aFirst, const std::pair<OUString,OUString>& aSecond)
2344 {
2345     return aFirst.first < aSecond.first;
2346 }
2347 
lcl_PosComp(const std::pair<WW8_CP,int> & aFirst,const std::pair<WW8_CP,int> & aSecond)2348 static bool lcl_PosComp( const std::pair<WW8_CP, int>& aFirst, const std::pair<WW8_CP, int>& aSecond)
2349 {
2350     return aFirst.first < aSecond.first;
2351 }
2352 
WriteGenericPlc(WW8Export & rWrt,sal_uInt8 nTTyp,WW8_FC & rTextStart,sal_Int32 & rTextCount,WW8_FC & rRefStart,sal_Int32 & rRefCount) const2353 void WW8_WrPlcSubDoc::WriteGenericPlc( WW8Export& rWrt, sal_uInt8 nTTyp,
2354     WW8_FC& rTextStart, sal_Int32& rTextCount, WW8_FC& rRefStart, sal_Int32& rRefCount ) const
2355 {
2356 
2357     sal_uLong nFcStart = rWrt.pTableStrm->Tell();
2358     sal_uInt16 nLen = aCps.size();
2359     if ( !nLen )
2360         return;
2361 
2362     OSL_ENSURE( aCps.size() + 2 == pTextPos->Count(), "WritePlc: DeSync" );
2363 
2364     std::vector<std::pair<OUString,OUString> > aStrArr;
2365     WW8Fib& rFib = *rWrt.pFib;              // n+1-th CP-Pos according to the manual
2366     bool bWriteCP = true;
2367 
2368     switch ( nTTyp )
2369     {
2370         case TXT_ATN:
2371             {
2372                 std::vector< std::pair<WW8_CP, int> > aRangeStartPos; // The second of the pair is the original index before sorting.
2373                 std::vector< std::pair<WW8_CP, int> > aRangeEndPos; // Same, so we can map between the indexes before/after sorting.
2374                 std::map<int, int> aAtnStartMap; // Maps from annotation index to start index.
2375                 std::map<int, int> aStartAtnMap; // Maps from start index to annotation index.
2376                 std::map<int, int> aStartEndMap; // Maps from start index to end index.
2377                 // then write first the GrpXstAtnOwners
2378                 int nIdx = 0;
2379                 for ( sal_uInt16 i = 0; i < nLen; ++i )
2380                 {
2381                     const WW8_Annotation& rAtn = *static_cast<const WW8_Annotation*>(aContent[i]);
2382                     aStrArr.emplace_back(rAtn.msOwner,rAtn.m_sInitials);
2383                     // record start and end positions for ranges
2384                     if (rAtn.HasRange())
2385                     {
2386                         aRangeStartPos.emplace_back(rAtn.m_nRangeStart, nIdx);
2387                         aRangeEndPos.emplace_back(rAtn.m_nRangeEnd, nIdx);
2388                         ++nIdx;
2389                     }
2390                 }
2391 
2392                 //sort and remove duplicates
2393                 std::sort(aStrArr.begin(), aStrArr.end(),&lcl_AuthorComp);
2394                 auto aIter = std::unique(aStrArr.begin(), aStrArr.end());
2395                 aStrArr.erase(aIter, aStrArr.end());
2396 
2397                 // Also sort the start and end positions. We need to reference
2398                 // the start index in the annotation table and also need to
2399                 // reference the end index in the start table, so build a map
2400                 // that knows what index to reference, after sorting.
2401                 std::sort(aRangeStartPos.begin(), aRangeStartPos.end(), &lcl_PosComp);
2402                 for (decltype(aRangeStartPos)::size_type i = 0; i < aRangeStartPos.size(); ++i)
2403                 {
2404                     aAtnStartMap[aRangeStartPos[i].second] = i;
2405                     aStartAtnMap[i] = aRangeStartPos[i].second;
2406                 }
2407                 std::sort(aRangeEndPos.begin(), aRangeEndPos.end(), &lcl_PosComp);
2408                 for (decltype(aRangeEndPos)::size_type i = 0; i < aRangeEndPos.size(); ++i)
2409                     aStartEndMap[aAtnStartMap[ aRangeEndPos[i].second ]] = i;
2410 
2411                 for ( decltype(aStrArr)::size_type i = 0; i < aStrArr.size(); ++i )
2412                 {
2413                     const OUString& sAuthor = aStrArr[i].first;
2414                     SwWW8Writer::WriteShort(*rWrt.pTableStrm, sAuthor.getLength());
2415                     SwWW8Writer::WriteString16(*rWrt.pTableStrm, sAuthor,
2416                             false);
2417                 }
2418 
2419                 rFib.m_fcGrpStAtnOwners = nFcStart;
2420                 nFcStart = rWrt.pTableStrm->Tell();
2421                 rFib.m_lcbGrpStAtnOwners = nFcStart - rFib.m_fcGrpStAtnOwners;
2422 
2423                 // Commented text ranges
2424                 if( !aRangeStartPos.empty() )
2425                 {
2426                     // Commented text ranges starting positions (Plcfbkf.aCP)
2427                     rFib.m_fcPlcfAtnbkf = nFcStart;
2428                     for ( decltype(aRangeStartPos)::size_type i = 0; i < aRangeStartPos.size(); ++i )
2429                     {
2430                         SwWW8Writer::WriteLong( *rWrt.pTableStrm, aRangeStartPos[i].first );
2431                     }
2432                     SwWW8Writer::WriteLong( *rWrt.pTableStrm, rFib.m_ccpText + 1);
2433 
2434                     // Commented text ranges additional information (Plcfbkf.aFBKF)
2435                     for ( decltype(aRangeStartPos)::size_type i = 0; i < aRangeStartPos.size(); ++i )
2436                     {
2437                         SwWW8Writer::WriteShort( *rWrt.pTableStrm, aStartEndMap[i] ); // FBKF.ibkl
2438                         SwWW8Writer::WriteShort( *rWrt.pTableStrm, 0 ); // FBKF.bkc
2439                     }
2440 
2441                     nFcStart = rWrt.pTableStrm->Tell();
2442                     rFib.m_lcbPlcfAtnbkf = nFcStart - rFib.m_fcPlcfAtnbkf;
2443 
2444                     // Commented text ranges ending positions (PlcfBkl.aCP)
2445                     rFib.m_fcPlcfAtnbkl = nFcStart;
2446                     for ( decltype(aRangeEndPos)::size_type i = 0; i < aRangeEndPos.size(); ++i )
2447                     {
2448                         SwWW8Writer::WriteLong( *rWrt.pTableStrm, aRangeEndPos[i].first );
2449                     }
2450                     SwWW8Writer::WriteLong( *rWrt.pTableStrm, rFib.m_ccpText + 1);
2451 
2452                     nFcStart = rWrt.pTableStrm->Tell();
2453                     rFib.m_lcbPlcfAtnbkl = nFcStart - rFib.m_fcPlcfAtnbkl;
2454 
2455                     // Commented text ranges as bookmarks (SttbfAtnBkmk)
2456                     rFib.m_fcSttbfAtnbkmk = nFcStart;
2457                     SwWW8Writer::WriteShort( *rWrt.pTableStrm, sal_Int16(sal_uInt16(0xFFFF)) ); // SttbfAtnBkmk.fExtend
2458                     SwWW8Writer::WriteShort( *rWrt.pTableStrm, aRangeStartPos.size() ); // SttbfAtnBkmk.cData
2459                     SwWW8Writer::WriteShort( *rWrt.pTableStrm, 0xA );                   // SttbfAtnBkmk.cbExtra
2460 
2461                     for ( decltype(aRangeStartPos)::size_type i = 0; i < aRangeStartPos.size(); ++i )
2462                     {
2463                         SwWW8Writer::WriteShort( *rWrt.pTableStrm, 0 );         // SttbfAtnBkmk.cchData
2464                         // One ATNBE structure for all text ranges
2465                         SwWW8Writer::WriteShort( *rWrt.pTableStrm, 0x0100 );    // ATNBE.bmc
2466                         SwWW8Writer::WriteLong( *rWrt.pTableStrm, aStartAtnMap[i] );          // ATNBE.lTag
2467                         SwWW8Writer::WriteLong( *rWrt.pTableStrm, -1 );         // ATNBE.lTagOld
2468                     }
2469 
2470                     nFcStart = rWrt.pTableStrm->Tell();
2471                     rFib.m_lcbSttbfAtnbkmk = nFcStart - rFib.m_fcSttbfAtnbkmk;
2472                 }
2473 
2474                 // Write the extended >= Word XP ATRD records
2475                 for( sal_uInt16 i = 0; i < nLen; ++i )
2476                 {
2477                     const WW8_Annotation& rAtn = *static_cast<const WW8_Annotation*>(aContent[i]);
2478 
2479                     sal_uInt32 nDTTM = sw::ms::DateTime2DTTM(rAtn.maDateTime);
2480 
2481                     SwWW8Writer::WriteLong( *rWrt.pTableStrm, nDTTM );
2482                     SwWW8Writer::WriteShort( *rWrt.pTableStrm, 0 );
2483                     SwWW8Writer::WriteLong( *rWrt.pTableStrm, 0 );
2484                     SwWW8Writer::WriteLong( *rWrt.pTableStrm, 0 );
2485                     SwWW8Writer::WriteLong( *rWrt.pTableStrm, 0 );
2486                 }
2487 
2488                 rFib.m_fcAtrdExtra = nFcStart;
2489                 nFcStart = rWrt.pTableStrm->Tell();
2490                 rFib.m_lcbAtrdExtra = nFcStart - rFib.m_fcAtrdExtra;
2491                 rFib.m_fcHplxsdr = 0x01010002;  //WTF, but apparently necessary
2492                 rFib.m_lcbHplxsdr = 0;
2493             }
2494             break;
2495         case TXT_TXTBOX:
2496         case TXT_HFTXTBOX:
2497             {
2498                 pTextPos->Write( *rWrt.pTableStrm );
2499                 const std::vector<sal_uInt32>* pShapeIds = GetShapeIdArr();
2500                 OSL_ENSURE( pShapeIds, "Where are the ShapeIds?" );
2501 
2502                 for ( sal_uInt16 i = 0; i < nLen; ++i )
2503                 {
2504                     // write textbox story - FTXBXS
2505                     // is it a writer or sdr - textbox?
2506                     const SdrObject* pObj = static_cast<SdrObject const *>(aContent[ i ]);
2507                     sal_Int32 nCnt = 1;
2508                     if (dynamic_cast< const SdrTextObj *>( pObj ))
2509                     {
2510                         // find the "highest" SdrObject of this
2511                         const SwFrameFormat& rFormat = *::FindFrameFormat( pObj );
2512 
2513                         const SwFormatChain* pChn = &rFormat.GetChain();
2514                         while ( pChn->GetNext() )
2515                         {
2516                             // has a chain?
2517                             // then calc the cur pos in the chain
2518                             ++nCnt;
2519                             pChn = &pChn->GetNext()->GetChain();
2520                         }
2521                     }
2522                     if( nullptr == pObj )
2523                     {
2524                         if (i < aSpareFormats.size() && aSpareFormats[i])
2525                         {
2526                             const SwFrameFormat& rFormat = *aSpareFormats[i];
2527 
2528                             const SwFormatChain* pChn = &rFormat.GetChain();
2529                             while( pChn->GetNext() )
2530                             {
2531                                 // has a chain?
2532                                 // then calc the cur pos in the chain
2533                                 ++nCnt;
2534                                 pChn = &pChn->GetNext()->GetChain();
2535                             }
2536                         }
2537                     }
2538                     // long cTxbx / iNextReuse
2539                     SwWW8Writer::WriteLong( *rWrt.pTableStrm, nCnt );
2540                     // long cReusable
2541                     SwWW8Writer::WriteLong( *rWrt.pTableStrm, 0 );
2542                     // short fReusable
2543                     SwWW8Writer::WriteShort( *rWrt.pTableStrm, 0 );
2544                     // long reserved
2545                     SwWW8Writer::WriteLong( *rWrt.pTableStrm, -1 );
2546                     // long lid
2547                     SwWW8Writer::WriteLong( *rWrt.pTableStrm,
2548                             (*pShapeIds)[i]);
2549                     // long txidUndo
2550                     SwWW8Writer::WriteLong( *rWrt.pTableStrm, 0 );
2551                 }
2552                 SwWW8Writer::FillCount( *rWrt.pTableStrm, 22 );
2553                 bWriteCP = false;
2554             }
2555             break;
2556     }
2557 
2558     if ( bWriteCP )
2559     {
2560         // write CP Positions
2561         for ( sal_uInt16 i = 0; i < nLen; i++ )
2562             SwWW8Writer::WriteLong( *rWrt.pTableStrm, aCps[ i ] );
2563 
2564         // n+1-th CP-Pos according to the manual
2565         SwWW8Writer::WriteLong( *rWrt.pTableStrm,
2566                 rFib.m_ccpText + rFib.m_ccpFootnote + rFib.m_ccpHdr + rFib.m_ccpEdn +
2567                 rFib.m_ccpTxbx + rFib.m_ccpHdrTxbx + 1 );
2568 
2569         if ( TXT_ATN == nTTyp )
2570         {
2571             sal_uInt16 nlTag = 0;
2572             for ( sal_uInt16 i = 0; i < nLen; ++i )
2573             {
2574                 const WW8_Annotation& rAtn = *static_cast<const WW8_Annotation*>(aContent[i]);
2575 
2576                 //aStrArr is sorted
2577                 auto aIter = std::lower_bound(aStrArr.begin(),
2578                         aStrArr.end(), std::pair<OUString,OUString>(rAtn.msOwner,OUString()),
2579                         &lcl_AuthorComp);
2580                 OSL_ENSURE(aIter != aStrArr.end() && aIter->first == rAtn.msOwner,
2581                         "Impossible");
2582                 sal_uInt16 nFndPos = static_cast< sal_uInt16 >(aIter - aStrArr.begin());
2583                 OUString sInitials( aIter->second );
2584                 sal_uInt8 nInitialsLen = static_cast<sal_uInt8>(sInitials.getLength());
2585                 if ( nInitialsLen > 9 )
2586                 {
2587                     sInitials = sInitials.copy( 0, 9 );
2588                     nInitialsLen = 9;
2589                 }
2590 
2591                 // xstUsrInitl[ 10 ] pascal-style String holding initials
2592                 // of annotation author
2593                 SwWW8Writer::WriteShort(*rWrt.pTableStrm, nInitialsLen);
2594                 SwWW8Writer::WriteString16(*rWrt.pTableStrm, sInitials,
2595                         false);
2596                 SwWW8Writer::FillCount( *rWrt.pTableStrm,
2597                         (9 - nInitialsLen) * 2 );
2598 
2599                 // documents layout of WriteShort's below:
2600 
2601                 // SVBT16 ibst;      // index into GrpXstAtnOwners
2602                 // SVBT16 ak;        // not used
2603                 // SVBT16 grfbmc;    // not used
2604                 // SVBT32 ITagBkmk;  // when not -1, this tag identifies the ATNBE
2605 
2606                 SwWW8Writer::WriteShort( *rWrt.pTableStrm, nFndPos );
2607                 SwWW8Writer::WriteShort( *rWrt.pTableStrm, 0 );
2608                 SwWW8Writer::WriteShort( *rWrt.pTableStrm, 0 );
2609                 if (rAtn.HasRange())
2610                 {
2611                     SwWW8Writer::WriteLong( *rWrt.pTableStrm, nlTag );
2612                     ++nlTag;
2613                 }
2614                 else
2615                     SwWW8Writer::WriteLong( *rWrt.pTableStrm, -1 );
2616             }
2617         }
2618         else
2619         {
2620             sal_uInt16 nNo = 0;
2621             for ( sal_uInt16 i = 0; i < nLen; ++i )             // write Flags
2622             {
2623                 const SwFormatFootnote* pFootnote = static_cast<SwFormatFootnote const *>(aContent[ i ]);
2624                 SwWW8Writer::WriteShort( *rWrt.pTableStrm,
2625                         !pFootnote->GetNumStr().isEmpty() ? 0 : ++nNo );
2626             }
2627         }
2628     }
2629     rRefStart = nFcStart;
2630     nFcStart = rWrt.pTableStrm->Tell();
2631     rRefCount = nFcStart - rRefStart;
2632 
2633     pTextPos->Write( *rWrt.pTableStrm );
2634 
2635     switch ( nTTyp )
2636     {
2637         case TXT_TXTBOX:
2638         case TXT_HFTXTBOX:
2639             for ( sal_uInt16 i = 0; i < nLen; ++i )
2640             {
2641                 // write break descriptor (BKD)
2642                 // short itxbxs
2643                 SwWW8Writer::WriteShort( *rWrt.pTableStrm, i );
2644                 // short dcpDepend
2645                 SwWW8Writer::WriteShort( *rWrt.pTableStrm, 0 );
2646                 // short flags : icol/fTableBreak/fColumnBreak/fMarked/
2647                 //               fUnk/fTextOverflow
2648                 SwWW8Writer::WriteShort( *rWrt.pTableStrm, 0x800 );
2649             }
2650             SwWW8Writer::FillCount( *rWrt.pTableStrm, 6 );
2651             break;
2652     }
2653 
2654     rTextStart = nFcStart;
2655     rTextCount = rWrt.pTableStrm->Tell() - nFcStart;
2656 }
2657 
GetShapeIdArr() const2658 const std::vector<sal_uInt32>* WW8_WrPlcSubDoc::GetShapeIdArr() const
2659 {
2660     return nullptr;
2661 }
2662 
2663 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2664