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