android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / filter / ww8 / wrtw8sty.cxx
blobcf62d9ded167480dff257e230ecff4fc8f4ab375
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <algorithm>
22 #include <memory>
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 <rtl/character.hxx>
35 #include <doc.hxx>
36 #include "wrtww8.hxx"
37 #include <docary.hxx>
38 #include <poolfmt.hxx>
39 #include <fmtpdsc.hxx>
40 #include <pagedesc.hxx>
41 #include <ndtxt.hxx>
42 #include <ftninfo.hxx>
43 #include <fmthdft.hxx>
44 #include <section.hxx>
45 #include <fmtcntnt.hxx>
46 #include <fmtftn.hxx>
47 #include <ndindex.hxx>
48 #include <txtftn.hxx>
49 #include <charfmt.hxx>
50 #include <docufld.hxx>
51 #include <dcontact.hxx>
52 #include <fmtcnct.hxx>
53 #include <ftnidx.hxx>
54 #include <fmtclds.hxx>
55 #include <lineinfo.hxx>
56 #include <fmtline.hxx>
57 #include <swtable.hxx>
58 #include <redline.hxx>
59 #include <msfilter.hxx>
60 #include <swmodule.hxx>
61 #include <charatr.hxx>
63 #include "sprmids.hxx"
65 #include "writerhelper.hxx"
66 #include "writerwordglue.hxx"
67 #include <wwstyles.hxx>
68 #include "ww8par.hxx"
69 #include "ww8attributeoutput.hxx"
70 #include "docxattributeoutput.hxx"
71 #include "rtfattributeoutput.hxx"
73 #include <unordered_set>
75 using namespace css;
76 using namespace sw::util;
77 using namespace nsHdFtFlags;
79 /// For the output of sections.
80 struct WW8_PdAttrDesc
82 std::unique_ptr<sal_uInt8[]> m_pData;
83 sal_uInt16 m_nLen;
84 WW8_FC m_nSepxFcPos;
85 WW8_PdAttrDesc() : m_nLen(0), m_nSepxFcPos(0xffffffff) /*default: none*/
86 { }
89 namespace {
91 struct WW8_SED
93 SVBT16 aBits1; // orientation change + internal, Default: 6
94 SVBT32 fcSepx; // FC file offset to beginning of SEPX for section.
95 // 0xFFFFFFFF for no Sprms
96 SVBT16 fnMpr; // used internally by Windows Word, Default: 0
97 SVBT32 fcMpr; // FC, points to offset in FC space for MacWord
98 // Default: 0xffffffff ( nothing )
99 // cbSED is 12 (decimal)), C (hex).
104 // class WW8_WrPlc0 is only used for header and footer positioning
105 // ie there is no content support structure
106 class WW8_WrPlc0
108 private:
109 std::vector<sal_uLong> m_aPos; // PTRARR of CPs / FCs
110 sal_uLong m_nOfs;
112 WW8_WrPlc0(WW8_WrPlc0 const&) = delete;
113 WW8_WrPlc0& operator=(WW8_WrPlc0 const&) = delete;
115 public:
116 explicit WW8_WrPlc0( sal_uLong nOffset );
117 sal_uInt16 Count() const { return m_aPos.size(); }
118 void Append( sal_uLong nStartCpOrFc );
119 void Write( SvStream& rStrm );
122 // Styles
124 // According to [MS-DOC] v20221115 2.9.271 STSH,
125 // "The beginning of the rglpstd array is reserved for specific "fixed-index" application-defined
126 // styles. A particular fixed-index, application-defined style has the same istd value in every
127 // stylesheet. The rglpstd MUST contain an LPStd for each of these fixed-index styles and the order
128 // MUST match the order in the following table.
130 // istd sti of application-defined style (see sti in StdfBase)
131 // 0 0
132 // 1 1
133 // 2 2
134 // 3 3
135 // 4 4
136 // 5 5
137 // 6 6
138 // 7 7
139 // 8 8
140 // 9 9
141 // 10 65
142 // 11 105
143 // 12 107
144 // 13 Reserved for future use
145 // 14 Reserved for future use"
147 // And [MS-OE376] v20220816 2.1.236 Part 4 Section 2.7.3.9, name (Primary Style Name)
148 // specifies the following mapping:
150 // sti Style name Style type
151 // 0 Normal paragraph
152 // 1 heading 1 paragraph
153 // 2 heading 2 paragraph
154 // 3 heading 3 paragraph
155 // 4 heading 4 paragraph
156 // 5 heading 5 paragraph
157 // 6 heading 6 paragraph
158 // 7 heading 7 paragraph
159 // 8 heading 8 paragraph
160 // 9 heading 9 paragraph
161 // 65 Default Paragraph Font character
162 // 105 Normal Table table
163 // 107 No List numbering
165 #define WW8_RESERVED_SLOTS 15
167 // GetId( SwCharFormat ) for use in text -> zero is not allowed,
168 // use "Default Char Style" instead
169 sal_uInt16 MSWordExportBase::GetId( const SwCharFormat* pFormat ) const
171 sal_uInt16 nRet = m_pStyles->GetSlot( pFormat );
172 return ( nRet != 0x0fff ) ? nRet : 10; // Default Char Style
175 // GetId( SwTextFormatColl ) for use in TextNodes -> zero is not allowed,
176 // "Standard" instead
177 sal_uInt16 MSWordExportBase::GetId( const SwTextFormatColl& rColl ) const
179 sal_uInt16 nRet = m_pStyles->GetSlot( &rColl );
180 return ( nRet != 0xfff ) ? nRet : 0; // Default TextFormatColl
183 //typedef pFormatT
184 MSWordStyles::MSWordStyles( MSWordExportBase& rExport, bool bListStyles )
185 : m_rExport( rExport ),
186 m_bListStyles(bListStyles)
188 // if exist any Foot-/End-Notes then get from the EndNoteInfo struct
189 // the CharFormats. They will create it!
190 if ( !m_rExport.m_rDoc.GetFootnoteIdxs().empty() )
192 m_rExport.m_rDoc.GetEndNoteInfo().GetAnchorCharFormat( m_rExport.m_rDoc );
193 m_rExport.m_rDoc.GetEndNoteInfo().GetCharFormat( m_rExport.m_rDoc );
194 m_rExport.m_rDoc.GetFootnoteInfo().GetAnchorCharFormat( m_rExport.m_rDoc );
195 m_rExport.m_rDoc.GetFootnoteInfo().GetCharFormat( m_rExport.m_rDoc );
198 memset( m_aHeadingParagraphStyles, -1 , MAXLEVEL * sizeof( sal_uInt16));
200 BuildStylesTable();
201 BuildWwNames();
202 BuildStyleIds();
205 MSWordStyles::~MSWordStyles()
209 // Sty_SetWWSlot() dependencies for the styles -> zero is allowed
210 sal_uInt16 MSWordStyles::GetSlot( const SwFormat* pFormat ) const
212 for (size_t slot = 0; slot < m_aStyles.size(); ++slot)
213 if (m_aStyles[slot].format == pFormat)
214 return slot;
215 return 0xfff; // 0xfff: WW: zero
218 /// Get reserved slot number during building the style table.
219 static sal_uInt16 BuildGetSlot(const SwFormat& rFormat)
221 switch (sal_uInt16 nRet = rFormat.GetPoolFormatId())
223 case RES_POOLCOLL_STANDARD:
224 return 0;
226 case RES_POOLCOLL_HEADLINE1:
227 case RES_POOLCOLL_HEADLINE2:
228 case RES_POOLCOLL_HEADLINE3:
229 case RES_POOLCOLL_HEADLINE4:
230 case RES_POOLCOLL_HEADLINE5:
231 case RES_POOLCOLL_HEADLINE6:
232 case RES_POOLCOLL_HEADLINE7:
233 case RES_POOLCOLL_HEADLINE8:
234 case RES_POOLCOLL_HEADLINE9:
235 nRet -= RES_POOLCOLL_HEADLINE1-1;
236 assert(nRet < WW8_RESERVED_SLOTS);
237 return nRet;
239 return 0xfff;
243 // Keep in sync with StyleSheetTable::ConvertStyleName
244 sal_uInt16 MSWordStyles::GetWWId( const SwFormat& rFormat )
246 sal_uInt16 nRet = ww::stiUser; // user style as default
247 sal_uInt16 nPoolId = rFormat.GetPoolFormatId();
248 if( nPoolId == RES_POOLCOLL_STANDARD )
249 nRet = ww::stiNormal;
250 else if( nPoolId >= RES_POOLCOLL_HEADLINE1 &&
251 nPoolId <= RES_POOLCOLL_HEADLINE9 )
252 nRet = static_cast< sal_uInt16 >(nPoolId + ww::stiLevFirst - RES_POOLCOLL_HEADLINE1);
253 else if( nPoolId >= RES_POOLCOLL_TOX_IDX1 &&
254 nPoolId <= RES_POOLCOLL_TOX_IDX3 )
255 nRet = static_cast< sal_uInt16 >(nPoolId + ww::stiIndexFirst - RES_POOLCOLL_TOX_IDX1);
256 else if( nPoolId >= RES_POOLCOLL_TOX_CNTNT1 &&
257 nPoolId <= RES_POOLCOLL_TOX_CNTNT5 )
258 nRet = static_cast< sal_uInt16 >(nPoolId + ww::stiToc1 - RES_POOLCOLL_TOX_CNTNT1);
259 else if( nPoolId >= RES_POOLCOLL_TOX_CNTNT6 &&
260 nPoolId <= RES_POOLCOLL_TOX_CNTNT9 )
261 nRet = static_cast< sal_uInt16 >(nPoolId + ww::stiToc6 - RES_POOLCOLL_TOX_CNTNT6);
262 else
263 switch( nPoolId )
265 case RES_POOLCOLL_FOOTNOTE: nRet = ww::stiFootnoteText; break;
266 case RES_POOLCOLL_MARGINAL: nRet = ww::stiAtnText; break;
267 case RES_POOLCOLL_HEADER: nRet = ww::stiHeader; break;
268 case RES_POOLCOLL_FOOTER: nRet = ww::stiFooter; break;
269 case RES_POOLCOLL_TOX_IDXH: nRet = ww::stiIndexHeading; break;
270 case RES_POOLCOLL_LABEL: nRet = ww::stiCaption; break;
271 case RES_POOLCOLL_TOX_ILLUS1: nRet = ww::stiToCaption; break;
272 case RES_POOLCOLL_ENVELOPE_ADDRESS: nRet = ww::stiEnvAddr; break;
273 case RES_POOLCOLL_SEND_ADDRESS: nRet = ww::stiEnvRet; break;
274 case RES_POOLCHR_FOOTNOTE_ANCHOR: nRet = ww::stiFootnoteRef; break;
275 case RES_POOLCHR_LINENUM: nRet = ww::stiLnn; break;
276 case RES_POOLCHR_PAGENO: nRet = ww::stiPgn; break;
277 case RES_POOLCHR_ENDNOTE_ANCHOR: nRet = ww::stiEdnRef; break;
278 case RES_POOLCOLL_ENDNOTE: nRet = ww::stiEdnText; break;
279 case RES_POOLCOLL_TOX_AUTHORITIESH: nRet = ww::stiToa; break;
280 case RES_POOLCOLL_TOX_CNTNTH: nRet = ww::stiToaHeading; break;
281 case RES_POOLCOLL_LISTS_BEGIN: nRet = ww::stiList; break;
282 case RES_POOLCOLL_BULLET_LEVEL1: nRet = ww::stiListBullet; break;
283 case RES_POOLCOLL_NUM_LEVEL1: nRet = ww::stiListNumber; break;
284 case RES_POOLCOLL_BULLET_LEVEL2: nRet = ww::stiListBullet2; break;
285 case RES_POOLCOLL_BULLET_LEVEL3: nRet = ww::stiListBullet3; break;
286 case RES_POOLCOLL_BULLET_LEVEL4: nRet = ww::stiListBullet4; break;
287 case RES_POOLCOLL_BULLET_LEVEL5: nRet = ww::stiListBullet5; break;
288 case RES_POOLCOLL_NUM_LEVEL2: nRet = ww::stiListNumber2; break;
289 case RES_POOLCOLL_NUM_LEVEL3: nRet = ww::stiListNumber3; break;
290 case RES_POOLCOLL_NUM_LEVEL4: nRet = ww::stiListNumber4; break;
291 case RES_POOLCOLL_NUM_LEVEL5: nRet = ww::stiListNumber5; break;
292 case RES_POOLCOLL_DOC_TITLE: nRet = ww::stiTitle; break;
293 case RES_POOLCOLL_DOC_APPENDIX: nRet = ww::stiClosing; break;
294 case RES_POOLCOLL_SIGNATURE: nRet = ww::stiSignature; break;
295 case RES_POOLCOLL_TEXT: nRet = ww::stiBodyText; break;
296 case RES_POOLCOLL_TEXT_MOVE: nRet = ww::stiBodyTextInd1; break;
297 case RES_POOLCOLL_BULLET_NONUM1: nRet = ww::stiListCont; break;
298 case RES_POOLCOLL_BULLET_NONUM2: nRet = ww::stiListCont2; break;
299 case RES_POOLCOLL_BULLET_NONUM3: nRet = ww::stiListCont3; break;
300 case RES_POOLCOLL_BULLET_NONUM4: nRet = ww::stiListCont4; break;
301 case RES_POOLCOLL_BULLET_NONUM5: nRet = ww::stiListCont5; break;
302 case RES_POOLCOLL_DOC_SUBTITLE: nRet = ww::stiSubtitle; break;
303 case RES_POOLCOLL_GREETING: nRet = ww::stiSalutation; break;
304 case RES_POOLCOLL_TEXT_IDENT: nRet = ww::stiBodyText1I; break;
305 case RES_POOLCHR_INET_NORMAL: nRet = ww::stiHyperlink; break;
306 case RES_POOLCHR_INET_VISIT: nRet = ww::stiHyperlinkFollowed; break;
307 case RES_POOLCHR_HTML_STRONG: nRet = ww::stiStrong; break;
308 case RES_POOLCHR_HTML_EMPHASIS: nRet = ww::stiEmphasis; break;
310 return nRet;
313 void MSWordStyles::BuildStylesTable()
315 assert(m_aStyles.empty());
316 // Put reserved slots first, then character styles, then paragraph styles
317 m_aStyles.resize(WW8_RESERVED_SLOTS);
319 const SwCharFormats& rArr = *m_rExport.m_rDoc.GetCharFormats(); // first CharFormat
320 // the default character style ( 0 ) will not be outputted !
321 for (size_t n = 1; n < rArr.size() && m_aStyles.size() < MSWORD_MAX_STYLES_LIMIT; ++n)
322 m_aStyles.emplace_back(rArr[n]);
324 const SwTextFormatColls& rArr2 = *m_rExport.m_rDoc.GetTextFormatColls(); // then TextFormatColls
325 // the default paragraph style ( 0 ) will not be outputted !
326 for (size_t n = 1; n < rArr2.size(); ++n)
328 SwTextFormatColl* pFormat = rArr2[n];
330 sal_uInt16 nSlot = BuildGetSlot(*pFormat);
331 if (nSlot != 0xfff)
333 m_aStyles[nSlot] = { pFormat };
335 else
337 if (m_aStyles.size() >= MSWORD_MAX_STYLES_LIMIT)
338 continue;
339 m_aStyles.emplace_back(pFormat);
340 nSlot = m_aStyles.size() - 1;
342 if ( pFormat->IsAssignedToListLevelOfOutlineStyle() )
344 int nLvl = pFormat->GetAssignedOutlineStyleLevel() ;
345 if (nLvl >= 0 && nLvl < MAXLEVEL)
346 m_aHeadingParagraphStyles[nLvl] = nSlot;
350 if (!m_bListStyles)
351 return;
353 const SwNumRuleTable& rNumRuleTable = m_rExport.m_rDoc.GetNumRuleTable();
354 for (size_t i = 0; i < rNumRuleTable.size() && m_aStyles.size() < MSWORD_MAX_STYLES_LIMIT; ++i)
356 const SwNumRule* pNumRule = rNumRuleTable[i];
357 if (pNumRule->IsAutoRule() || pNumRule->GetName().startsWith("WWNum"))
358 continue;
359 m_aStyles.emplace_back(pNumRule);
363 // StyleSheetTable::ConvertStyleName appends the suffix do disambiguate conflicting style names
364 static OUString StripWWSuffix(const OUString& s)
366 OUString ret = s;
367 (void)ret.endsWith(" (WW)", &ret);
368 return ret;
371 void MSWordStyles::BuildWwNames()
373 std::unordered_set<OUString> aUsed;
375 auto makeUniqueName = [&aUsed](OUString& name) {
376 // toAsciiLowerCase rules out e.g. user's "normal"; no problem if there are non-ASCII chars
377 OUString lower(name.toAsciiLowerCase());
378 if (!aUsed.insert(lower).second)
380 int nFree = 1;
381 while (!aUsed.insert(lower + OUString::number(nFree)).second)
382 ++nFree;
384 name += OUString::number(nFree);
388 // We want to map LO's default style to Word's "Normal" style.
389 // Word looks for this specific style name when reading docx files.
390 // (It must be the English word regardless of language settings)
391 assert(!m_aStyles.empty());
392 assert(!m_aStyles[0].format || m_aStyles[0].ww_id == ww::stiNormal);
393 m_aStyles[0].ww_name = "Normal";
394 aUsed.insert("normal");
396 // 1. Handle styles having special wwIds, and thus pre-defined names
397 for (auto& entry : m_aStyles)
399 if (!entry.ww_name.isEmpty())
400 continue; // "Normal" is already added
401 if (entry.ww_id >= ww::stiMax)
402 continue; // Not a format with special name
403 assert(entry.format);
405 entry.ww_name = OUString::createFromAscii(ww::GetEnglishNameFromSti(static_cast<ww::sti>(entry.ww_id)));
406 makeUniqueName(entry.ww_name);
409 // 2. Now handle other styles
410 for (auto& entry : m_aStyles)
412 if (!entry.ww_name.isEmpty())
413 continue;
414 if (entry.format)
415 entry.ww_name = StripWWSuffix(entry.format->GetName());
416 else if (entry.num_rule)
417 entry.ww_name = StripWWSuffix(entry.num_rule->GetName());
418 else
419 continue;
420 makeUniqueName(entry.ww_name);
424 OString MSWordStyles::CreateStyleId(std::u16string_view aName)
426 return OUStringToOString(msfilter::util::CreateDOCXStyleId(aName), RTL_TEXTENCODING_UTF8);
429 void MSWordStyles::BuildStyleIds()
431 std::unordered_set<OString> aUsed;
433 for (auto& entry : m_aStyles)
435 OString aStyleId = CreateStyleId(entry.ww_name);
437 if (aStyleId.isEmpty())
438 aStyleId = "Style";
440 OString aLower(aStyleId.toAsciiLowerCase());
442 // check for uniqueness & construct something unique if we have to
443 if (!aUsed.insert(aLower).second)
445 int nFree = 1;
446 while (!aUsed.insert(aLower + OString::number(nFree)).second)
447 ++nFree;
449 aStyleId += OString::number(nFree);
451 entry.style_id = aStyleId;
455 OString const & MSWordStyles::GetStyleId(sal_uInt16 nSlot) const
457 assert(!m_aStyles[nSlot].style_id.isEmpty());
458 return m_aStyles[nSlot].style_id;
461 OUString MSWordStyles::GetStyleWWName(SwFormat const*const pFormat) const
463 if (auto slot = m_rExport.m_pStyles->GetSlot(pFormat); slot != 0xfff)
465 assert(!m_aStyles[slot].ww_name.isEmpty());
466 return m_aStyles[slot].ww_name;
468 return OUString();
471 /// For WW8 only - extend pO so that the size of pTableStrm is even.
472 static void impl_SkipOdd(std::unique_ptr<ww::bytes> const& pO, std::size_t nTableStrmTell)
474 if ( ( nTableStrmTell + pO->size() ) & 1 ) // start on even
475 pO->push_back( sal_uInt8(0) ); // Address
478 void WW8AttributeOutput::EndStyle()
480 impl_SkipOdd( m_rWW8Export.m_pO, m_rWW8Export.m_pTableStrm->Tell() );
482 short nLen = m_rWW8Export.m_pO->size() - 2; // length of the style
483 sal_uInt8* p = m_rWW8Export.m_pO->data() + m_nPOPosStdLen1;
484 ShortToSVBT16( nLen, p ); // add
485 p = m_rWW8Export.m_pO->data() + m_nPOPosStdLen2;
486 ShortToSVBT16( nLen, p ); // also
488 m_rWW8Export.m_pTableStrm->WriteBytes(m_rWW8Export.m_pO->data(), m_rWW8Export.m_pO->size());
489 m_rWW8Export.m_pO->clear();
492 void WW8AttributeOutput::StartStyle( const OUString& rName, StyleType eType, sal_uInt16 nWwBase,
493 sal_uInt16 nWwNext, sal_uInt16 /*nWwLink*/, sal_uInt16 nWwId, sal_uInt16 /*nSlot*/, bool bAutoUpdate )
495 sal_uInt8 aWW8_STD[ sizeof( WW8_STD ) ] = {};
496 sal_uInt8* pData = aWW8_STD;
498 sal_uInt16 nBit16 = 0x1000; // fInvalHeight
499 nBit16 |= (ww::stiNil & nWwId);
500 Set_UInt16( pData, nBit16 );
502 nBit16 = nWwBase << 4; // istdBase
503 nBit16 |= (eType == STYLE_TYPE_PARA ? 1 : 2); // sgc
504 Set_UInt16( pData, nBit16 );
506 nBit16 = nWwNext << 4; // istdNext
507 nBit16 |= (eType == STYLE_TYPE_PARA ? 2 : 1); // cupx
508 Set_UInt16( pData, nBit16 );
510 pData += sizeof( sal_uInt16 ); // bchUpe
512 nBit16 = bAutoUpdate ? 1 : 0; // fAutoRedef : 1
513 Set_UInt16( pData, nBit16 );
514 // now new:
515 // from Ver8 there are two fields more:
516 // sal_uInt16 fHidden : 1; /* hidden from UI?
517 // sal_uInt16 : 14; /* unused bits
519 sal_uInt16 nLen = static_cast< sal_uInt16 >( ( pData - aWW8_STD ) + 1 +
520 (2 * (rName.getLength() + 1)) ); // temporary
522 m_nPOPosStdLen1 = m_rWW8Export.m_pO->size(); // Adr1 for adding the length
524 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, nLen );
525 m_rWW8Export.m_pO->insert( m_rWW8Export.m_pO->end(), aWW8_STD, pData );
527 m_nPOPosStdLen2 = m_nPOPosStdLen1 + 8; // Adr2 for adding of "end of upx"
529 // write names
530 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, rName.getLength() ); // length
531 SwWW8Writer::InsAsString16( *m_rWW8Export.m_pO, rName );
532 m_rWW8Export.m_pO->push_back( sal_uInt8(0) ); // Despite P-String 0 at the end!
535 void MSWordStyles::SetStyleDefaults( const SwFormat& rFormat, bool bPap )
537 const sw::BroadcastingModify* pOldMod = m_rExport.m_pOutFormatNode;
538 m_rExport.m_pOutFormatNode = &rFormat;
539 bool aFlags[ RES_FRMATR_END - RES_CHRATR_BEGIN ];
540 sal_uInt16 nStt, nEnd, n;
541 if( bPap )
543 nStt = RES_PARATR_BEGIN;
544 nEnd = RES_FRMATR_END;
546 else
548 nStt = RES_CHRATR_BEGIN;
549 nEnd = RES_TXTATR_END;
552 // dynamic defaults
553 const SfxItemPool& rPool = *rFormat.GetAttrSet().GetPool();
554 for( n = nStt; n < nEnd; ++n )
555 aFlags[ n - RES_CHRATR_BEGIN ] = nullptr != rPool.GetPoolDefaultItem( n )
556 || SfxItemState::SET == m_rExport.m_rDoc.GetDfltTextFormatColl()->GetItemState( n, false );
558 // static defaults, that differs between WinWord and SO
559 if( bPap )
561 aFlags[ static_cast< sal_uInt16 >(RES_PARATR_WIDOWS) - RES_CHRATR_BEGIN ] = true;
562 aFlags[ static_cast< sal_uInt16 >(RES_PARATR_HYPHENZONE) - RES_CHRATR_BEGIN ] = true;
563 aFlags[ static_cast< sal_uInt16 >(RES_FRAMEDIR) - RES_CHRATR_BEGIN ] = true;
565 else
567 aFlags[ RES_CHRATR_FONTSIZE - RES_CHRATR_BEGIN ] = true;
568 aFlags[ RES_CHRATR_LANGUAGE - RES_CHRATR_BEGIN ] = true;
571 const SfxItemSet* pOldI = m_rExport.GetCurItemSet();
572 m_rExport.SetCurItemSet( &rFormat.GetAttrSet() );
574 const bool* pFlags = aFlags + ( nStt - RES_CHRATR_BEGIN );
575 for ( n = nStt; n < nEnd; ++n, ++pFlags )
577 if ( *pFlags && !m_rExport.ignoreAttributeForStyleDefaults( n )
578 && SfxItemState::SET != rFormat.GetItemState(n, false))
580 //If we are a character property then see if it is one of the
581 //western/asian ones that must be collapsed together for export to
582 //word. If so default to the western variant.
583 if ( bPap || m_rExport.CollapseScriptsforWordOk(
584 i18n::ScriptType::LATIN, n) )
586 m_rExport.AttrOutput().OutputItem( rFormat.GetFormatAttr( n ) );
591 m_rExport.SetCurItemSet( pOldI );
592 m_rExport.m_pOutFormatNode = pOldMod;
595 void WW8AttributeOutput::StartStyleProperties( bool bParProp, sal_uInt16 nStyle )
597 impl_SkipOdd( m_rWW8Export.m_pO, m_rWW8Export.m_pTableStrm->Tell() );
599 sal_uInt16 nLen = bParProp ? 2 : 0; // default length
600 m_nStyleLenPos = m_rWW8Export.m_pO->size(); // adding length
601 // Don't save pointer, because it
602 // changes by _grow!
604 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, nLen ); // Style-Len
606 m_nStyleStartSize = m_rWW8Export.m_pO->size();
608 if ( bParProp )
609 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, nStyle ); // Style-Number
612 void MSWordStyles::WriteProperties( const SwFormat* pFormat, bool bParProp, sal_uInt16 nPos,
613 bool bInsDefCharSiz )
615 m_rExport.AttrOutput().StartStyleProperties( bParProp, nPos );
617 OSL_ENSURE( m_rExport.m_pCurrentStyle == nullptr, "Current style not NULL" ); // set current style before calling out
618 m_rExport.m_pCurrentStyle = pFormat;
620 m_rExport.OutputFormat( *pFormat, bParProp, !bParProp );
622 OSL_ENSURE( m_rExport.m_pCurrentStyle == pFormat, "current style was changed" );
623 // reset current style...
624 m_rExport.m_pCurrentStyle = nullptr;
626 if ( bInsDefCharSiz ) // not derived from other Style
627 SetStyleDefaults( *pFormat, bParProp );
629 m_rExport.AttrOutput().EndStyleProperties( bParProp );
632 void WW8AttributeOutput::EndStyleProperties( bool /*bParProp*/ )
634 sal_uInt16 nLen = m_rWW8Export.m_pO->size() - m_nStyleStartSize;
635 sal_uInt8* pUpxLen = m_rWW8Export.m_pO->data() + m_nStyleLenPos; // adding length
636 ShortToSVBT16( nLen, pUpxLen ); // add default length
639 void MSWordStyles::GetStyleData( const SwFormat* pFormat, bool& bFormatColl, sal_uInt16& nBase, sal_uInt16& nNext, sal_uInt16& nLink )
641 bFormatColl = pFormat->Which() == RES_TXTFMTCOLL || pFormat->Which() == RES_CONDTXTFMTCOLL;
643 // Default: none
644 nBase = 0xfff;
646 // Derived from?
647 if ( !pFormat->IsDefault() )
648 nBase = GetSlot( pFormat->DerivedFrom() );
650 const SwFormat* pNext;
651 const SwFormat* pLink = nullptr;
652 if ( bFormatColl )
654 auto pFormatColl = static_cast<const SwTextFormatColl*>(pFormat);
655 pNext = &pFormatColl->GetNextTextFormatColl();
656 pLink = pFormatColl->GetLinkedCharFormat();
658 else
660 pNext = pFormat; // CharFormat: next CharFormat == self
661 auto pCharFormat = static_cast<const SwCharFormat*>(pFormat);
662 pLink = pCharFormat->GetLinkedParaFormat();
665 nNext = GetSlot( pNext );
667 if (pLink)
669 nLink = GetSlot(pLink);
673 void WW8AttributeOutput::DefaultStyle()
675 m_rWW8Export.m_pTableStrm->WriteUInt16(0); // empty Style
678 void MSWordStyles::OutputStyle(sal_uInt16 nSlot)
680 const auto& entry = m_aStyles[nSlot];
682 if (entry.num_rule)
684 m_rExport.AttrOutput().StartStyle( entry.ww_name, STYLE_TYPE_LIST,
685 /*nBase =*/ 0, /*nWwNext =*/ 0, /*nWwLink =*/ 0, /*nWWId =*/ 0, nSlot,
686 /*bAutoUpdateFormat =*/ false );
688 m_rExport.AttrOutput().EndStyle();
690 else if (!entry.format)
692 m_rExport.AttrOutput().DefaultStyle();
694 else
696 bool bFormatColl;
697 sal_uInt16 nBase, nWwNext;
698 sal_uInt16 nWwLink = 0x0FFF;
700 GetStyleData(entry.format, bFormatColl, nBase, nWwNext, nWwLink);
702 if (!bFormatColl && m_rExport.GetExportFormat() == MSWordExportBase::DOCX &&
703 entry.style_id.startsWith("ListLabel"))
705 // tdf#92335 don't export redundant DOCX import style "ListLabel"
706 return;
709 m_rExport.AttrOutput().StartStyle(entry.ww_name, (bFormatColl ? STYLE_TYPE_PARA : STYLE_TYPE_CHAR),
710 nBase, nWwNext, nWwLink, m_aStyles[nSlot].ww_id, nSlot,
711 entry.format->IsAutoUpdateOnDirectFormat() );
713 if ( bFormatColl )
714 WriteProperties( entry.format, true, nSlot, nBase==0xfff ); // UPX.papx
716 WriteProperties( entry.format, false, nSlot, bFormatColl && nBase==0xfff ); // UPX.chpx
718 m_rExport.AttrOutput().EndStyle();
722 void WW8AttributeOutput::StartStyles()
724 WW8Fib& rFib = *m_rWW8Export.m_pFib;
726 sal_uInt64 nCurPos = m_rWW8Export.m_pTableStrm->Tell();
727 if ( nCurPos & 1 ) // start on even
729 m_rWW8Export.m_pTableStrm->WriteChar( char(0) ); // Address
730 ++nCurPos;
732 rFib.m_fcStshfOrig = rFib.m_fcStshf = nCurPos;
733 m_nStyleCountPos = nCurPos + 2; // count is added later
735 static sal_uInt8 aStShi[] = {
736 0x12, 0x00,
737 0x0F, 0x00, 0x0A, 0x00, 0x01, 0x00, 0x5B, 0x00,
738 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
739 0x00, 0x00 };
741 m_rWW8Export.m_pTableStrm->WriteBytes(&aStShi, sizeof(aStShi));
744 void WW8AttributeOutput::EndStyles( sal_uInt16 nNumberOfStyles )
746 WW8Fib& rFib = *m_rWW8Export.m_pFib;
748 rFib.m_lcbStshfOrig = rFib.m_lcbStshf = m_rWW8Export.m_pTableStrm->Tell() - rFib.m_fcStshf;
749 SwWW8Writer::WriteShort( *m_rWW8Export.m_pTableStrm, m_nStyleCountPos, nNumberOfStyles );
752 void MSWordStyles::OutputStylesTable()
754 m_rExport.m_bStyDef = true;
756 m_rExport.AttrOutput().StartStyles();
758 // HACK
759 // Ms Office seems to have an internal limitation of 4091 styles
760 // and refuses to load .docx with more, even though the spec seems to allow that;
761 // so simply if there are more styles, don't export those
762 // Implementing check for all exports DOCX, DOC, RTF
763 assert(m_aStyles.size() <= MSWORD_MAX_STYLES_LIMIT);
764 for (size_t slot = 0; slot < m_aStyles.size(); ++slot)
765 OutputStyle(slot);
767 m_rExport.AttrOutput().EndStyles(m_aStyles.size());
769 m_rExport.m_bStyDef = false;
772 // Fonts
774 wwFont::wwFont(std::u16string_view rFamilyName, FontPitch ePitch, FontFamily eFamily,
775 rtl_TextEncoding eChrSet)
776 : mbAlt(false), mePitch(ePitch), meFamily(eFamily), meChrSet(eChrSet)
778 FontMapExport aResult(rFamilyName);
779 msFamilyNm = aResult.msPrimary;
780 msAltNm = aResult.msSecondary;
781 if (!msAltNm.isEmpty() && msAltNm != msFamilyNm &&
782 (msFamilyNm.getLength() + msAltNm.getLength() + 2 <= 65) )
784 //max size of szFfn in 65 chars
785 mbAlt = true;
788 maWW8_FFN[0] = static_cast<sal_uInt8>( 6 - 1 + 0x22 + ( 2 * ( 1 + msFamilyNm.getLength() ) ));
789 if (mbAlt)
790 maWW8_FFN[0] = static_cast< sal_uInt8 >(maWW8_FFN[0] + 2 * ( 1 + msAltNm.getLength()));
792 sal_uInt8 aB = 0;
793 switch(ePitch)
795 case PITCH_VARIABLE:
796 aB |= 2; // aF.prg = 2
797 break;
798 case PITCH_FIXED:
799 aB |= 1;
800 break;
801 default: // aF.prg = 0 : DEFAULT_PITCH (windows.h)
802 break;
804 aB |= 1 << 2; // aF.fTrueType = 1; don't know any better;
806 switch(eFamily)
808 case FAMILY_ROMAN:
809 aB |= 1 << 4; // aF.ff = 1;
810 break;
811 case FAMILY_SWISS:
812 aB |= 2 << 4; // aF.ff = 2;
813 break;
814 case FAMILY_MODERN:
815 aB |= 3 << 4; // aF.ff = 3;
816 break;
817 case FAMILY_SCRIPT:
818 aB |= 4 << 4; // aF.ff = 4;
819 break;
820 case FAMILY_DECORATIVE:
821 aB |= 5 << 4; // aF.ff = 5;
822 break;
823 default: // aF.ff = 0; FF_DONTCARE (windows.h)
824 break;
826 maWW8_FFN[1] = aB;
828 ShortToSVBT16( 400, &maWW8_FFN[2] ); // don't know any better
829 // 400 == FW_NORMAL (windows.h)
831 //#i61927# For unicode fonts like Arial Unicode, Word 97+ sets the chs
832 //to SHIFTJIS presumably to capture that it's a multi-byte encoding font
833 //but Word95 doesn't do this, and sets it to 0 (ANSI), so we should do the
834 //same
835 maWW8_FFN[4] = sw::ms::rtl_TextEncodingToWinCharset(eChrSet);
837 if (mbAlt)
838 maWW8_FFN[5] = static_cast< sal_uInt8 >(msFamilyNm.getLength() + 1);
841 void wwFont::Write(SvStream *pTableStrm) const
843 pTableStrm->WriteBytes(maWW8_FFN, sizeof(maWW8_FFN)); // fixed part
844 // from Ver8 following two fields intersected,
845 // we ignore them.
846 //char panose[ 10 ]; // 0x6 PANOSE
847 //char fs[ 24 ]; // 0x10 FONTSIGNATURE
848 SwWW8Writer::FillCount(*pTableStrm, 0x22);
849 SwWW8Writer::WriteString16(*pTableStrm, msFamilyNm, true);
850 if (mbAlt)
851 SwWW8Writer::WriteString16(*pTableStrm, msAltNm, true);
854 void wwFont::WriteDocx( DocxAttributeOutput* rAttrOutput ) const
856 // no font embedding, panose id, subsetting, ... implemented
858 if (msFamilyNm.isEmpty())
859 return;
861 rAttrOutput->StartFont( msFamilyNm );
863 if ( mbAlt )
864 rAttrOutput->FontAlternateName( msAltNm );
865 rAttrOutput->FontCharset( sw::ms::rtl_TextEncodingToWinCharset( meChrSet ), meChrSet );
866 rAttrOutput->FontFamilyType( meFamily );
867 rAttrOutput->FontPitchType( mePitch );
868 rAttrOutput->EmbedFont( msFamilyNm, meFamily, mePitch );
870 rAttrOutput->EndFont();
873 void wwFont::WriteRtf( const RtfAttributeOutput* rAttrOutput ) const
875 rAttrOutput->FontFamilyType( meFamily, *this );
876 rAttrOutput->FontPitchType( mePitch );
877 rAttrOutput->FontCharset(
878 sw::ms::rtl_TextEncodingToWinCharsetRTF(msFamilyNm, msAltNm, meChrSet));
879 rAttrOutput->StartFont( msFamilyNm );
880 if ( mbAlt )
881 rAttrOutput->FontAlternateName( msAltNm );
882 rAttrOutput->EndFont();
885 bool operator<(const wwFont &r1, const wwFont &r2)
887 int nRet = memcmp(r1.maWW8_FFN, r2.maWW8_FFN, sizeof(r1.maWW8_FFN));
888 if (nRet == 0)
890 nRet = r1.msFamilyNm.compareTo(r2.msFamilyNm);
891 if (nRet == 0)
892 nRet = r1.msAltNm.compareTo(r2.msAltNm);
894 return nRet < 0;
897 sal_uInt16 wwFontHelper::GetId(const wwFont &rFont)
899 sal_uInt16 nRet;
900 std::map<wwFont, sal_uInt16>::const_iterator aIter = maFonts.find(rFont);
901 if (aIter != maFonts.end())
902 nRet = aIter->second;
903 else
905 nRet = static_cast< sal_uInt16 >(maFonts.size());
906 maFonts[rFont] = nRet;
908 return nRet;
911 void wwFontHelper::InitFontTable(const SwDoc& rDoc)
913 GetId(wwFont(u"Times New Roman", PITCH_VARIABLE,
914 FAMILY_ROMAN, RTL_TEXTENCODING_MS_1252));
916 GetId(wwFont(u"Symbol", PITCH_VARIABLE, FAMILY_ROMAN,
917 RTL_TEXTENCODING_SYMBOL));
919 GetId(wwFont(u"Arial", PITCH_VARIABLE, FAMILY_SWISS,
920 RTL_TEXTENCODING_MS_1252));
922 const SvxFontItem* pFont = GetDfltAttr(RES_CHRATR_FONT);
924 GetId(wwFont(pFont->GetFamilyName(), pFont->GetPitch(),
925 pFont->GetFamily(), pFont->GetCharSet()));
927 const SfxItemPool& rPool = rDoc.GetAttrPool();
928 pFont = rPool.GetPoolDefaultItem(RES_CHRATR_FONT);
929 if (nullptr != pFont)
931 GetId(wwFont(pFont->GetFamilyName(), pFont->GetPitch(),
932 pFont->GetFamily(), pFont->GetCharSet()));
935 if (!m_bLoadAllFonts)
936 return;
938 const sal_uInt16 aTypes[] = { RES_CHRATR_FONT, RES_CHRATR_CJK_FONT, RES_CHRATR_CTL_FONT, 0 };
939 for (const sal_uInt16* pId = aTypes; *pId; ++pId)
941 for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(*pId))
943 pFont = static_cast<const SvxFontItem*>(pItem);
944 GetId(wwFont(pFont->GetFamilyName(), pFont->GetPitch(),
945 pFont->GetFamily(), pFont->GetCharSet()));
950 sal_uInt16 wwFontHelper::GetId(const SvxFontItem& rFont)
952 wwFont aFont(rFont.GetFamilyName(), rFont.GetPitch(), rFont.GetFamily(),
953 rFont.GetCharSet());
954 return GetId(aFont);
957 std::vector< const wwFont* > wwFontHelper::AsVector() const
959 std::vector<const wwFont *> aFontList( maFonts.size() );
961 for ( const auto& aFont : maFonts )
962 aFontList[aFont.second] = &aFont.first;
964 return aFontList;
967 void wwFontHelper::WriteFontTable(SvStream *pTableStream, WW8Fib& rFib)
969 rFib.m_fcSttbfffn = pTableStream->Tell();
971 * Reserve some space to fill in the len after we know how big it is
973 SwWW8Writer::WriteLong(*pTableStream, 0);
976 * Convert from fast insertion map to linear vector in the order that we
977 * want to write.
979 std::vector<const wwFont *> aFontList( AsVector() );
982 * Write them all to pTableStream
984 for ( auto aFont : aFontList )
985 aFont->Write(pTableStream);
988 * Write the position and len in the FIB
990 rFib.m_lcbSttbfffn = pTableStream->Tell() - rFib.m_fcSttbfffn;
991 SwWW8Writer::WriteLong( *pTableStream, rFib.m_fcSttbfffn, maFonts.size());
994 void wwFontHelper::WriteFontTable( DocxAttributeOutput& rAttrOutput )
996 std::vector<const wwFont *> aFontList( AsVector() );
998 for ( auto aFont : aFontList )
999 aFont->WriteDocx(&rAttrOutput);
1002 void wwFontHelper::WriteFontTable( const RtfAttributeOutput& rAttrOutput )
1004 std::vector<const wwFont *> aFontList( AsVector() );
1006 for ( auto aFont : aFontList )
1007 aFont->WriteRtf(&rAttrOutput);
1010 WW8_WrPlc0::WW8_WrPlc0( sal_uLong nOffset )
1011 : m_nOfs( nOffset )
1015 void WW8_WrPlc0::Append( sal_uLong nStartCpOrFc )
1017 m_aPos.push_back( nStartCpOrFc - m_nOfs );
1020 void WW8_WrPlc0::Write( SvStream& rStrm )
1022 for( const auto& rPos : m_aPos )
1024 rStrm.WriteUInt32(rPos);
1028 // class MSWordSections : translate PageDescs into Sections
1029 // also deals with header and footer
1031 MSWordSections::MSWordSections( MSWordExportBase& rExport )
1032 : mbDocumentIsProtected( false )
1034 const SwSectionFormat *pFormat = nullptr;
1035 rExport.m_pCurrentPageDesc = &rExport.m_rDoc.GetPageDesc( 0 );
1037 const SwNode* pNd = rExport.m_pCurPam->GetPointContentNode();
1038 const SfxItemSet* pSet = pNd ? &static_cast<const SwContentNode*>(pNd)->GetSwAttrSet() : nullptr;
1040 sal_uLong nRstLnNum = pSet ? pSet->Get( RES_LINENUMBER ).GetStartValue() : 0;
1042 const SwTableNode* pTableNd = rExport.m_pCurPam->GetPointNode().FindTableNode();
1043 const SwSectionNode* pSectNd = nullptr;
1044 if ( pTableNd )
1046 pSet = &pTableNd->GetTable().GetFrameFormat()->GetAttrSet();
1047 pNd = pTableNd;
1049 else if (pNd && nullptr != ( pSectNd = pNd->FindSectionNode() ))
1051 if ( SectionType::ToxHeader == pSectNd->GetSection().GetType() &&
1052 pSectNd->StartOfSectionNode()->IsSectionNode() )
1054 pSectNd = pSectNd->StartOfSectionNode()->GetSectionNode();
1057 if ( SectionType::ToxContent == pSectNd->GetSection().GetType() )
1059 pNd = pSectNd;
1060 rExport.m_pCurPam->GetPoint()->Assign(*pNd);
1063 if ( SectionType::Content == pSectNd->GetSection().GetType() )
1064 pFormat = pSectNd->GetSection().GetFormat();
1067 // tdf#118393: FILESAVE: DOCX Export loses header/footer
1068 rExport.m_bFirstTOCNodeWithSection = pSectNd &&
1069 ( SectionType::ToxHeader == pSectNd->GetSection().GetType() ||
1070 SectionType::ToxContent == pSectNd->GetSection().GetType() );
1072 // Try to get page descriptor of the first node
1073 const SwFormatPageDesc* pDescItem;
1074 if ( pSet &&
1075 (pDescItem = pSet->GetItemIfSet( RES_PAGEDESC )) &&
1076 pDescItem->GetPageDesc() )
1078 AppendSection( *pDescItem, *pNd, pFormat, nRstLnNum );
1080 else
1081 AppendSection( rExport.m_pCurrentPageDesc, pFormat, nRstLnNum, /*bIsFirstParagraph=*/true );
1084 WW8_WrPlcSepx::WW8_WrPlcSepx( MSWordExportBase& rExport )
1085 : MSWordSections( rExport )
1086 , m_bHeaderFooterWritten( false )
1088 // to be in sync with the AppendSection() call in the MSWordSections
1089 // constructor
1090 m_aCps.push_back( 0 );
1093 MSWordSections::~MSWordSections()
1097 WW8_WrPlcSepx::~WW8_WrPlcSepx()
1101 bool MSWordSections::HeaderFooterWritten()
1103 return false; // only relevant for WW8
1106 bool WW8_WrPlcSepx::HeaderFooterWritten()
1108 return m_bHeaderFooterWritten;
1111 sal_uInt16 MSWordSections::CurrentNumberOfColumns( const SwDoc &rDoc ) const
1113 OSL_ENSURE( !m_aSects.empty(), "no segment inserted yet" );
1114 if ( m_aSects.empty() )
1115 return 1;
1117 return GetFormatCol(rDoc, m_aSects.back()).GetNumCols();
1120 const SwFormatCol& MSWordSections::GetFormatCol(const SwDoc &rDoc, const WW8_SepInfo& rInfo)
1122 const SwPageDesc* pPd = rInfo.pPageDesc;
1123 if ( !pPd )
1124 pPd = &rDoc.GetPageDesc( 0 );
1126 const SfxItemSet &rSet = pPd->GetMaster().GetAttrSet();
1127 SfxItemSetFixed<RES_COL, RES_COL> aSet( *rSet.GetPool() );
1128 aSet.SetParent( &rSet );
1130 //0xffffffff, what the hell is going on with that!, fixme most terribly
1131 if ( rInfo.pSectionFormat && reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1)) != rInfo.pSectionFormat )
1132 aSet.Put( rInfo.pSectionFormat->GetFormatAttr( RES_COL ) );
1134 return aSet.Get(RES_COL);
1137 const WW8_SepInfo* MSWordSections::CurrentSectionInfo()
1139 if ( !m_aSects.empty() )
1140 return &m_aSects.back();
1142 return nullptr;
1145 void MSWordSections::AppendSection( const SwPageDesc* pPd,
1146 const SwSectionFormat* pSectionFormat, sal_uLong nLnNumRestartNo, bool bIsFirstParagraph )
1148 if (HeaderFooterWritten()) {
1149 return; // #i117955# prevent new sections in endnotes
1151 m_aSects.emplace_back( pPd, pSectionFormat, nLnNumRestartNo, std::nullopt, nullptr, bIsFirstParagraph );
1152 NeedsDocumentProtected( m_aSects.back() );
1155 void WW8_WrPlcSepx::AppendSep( WW8_CP nStartCp, const SwPageDesc* pPd,
1156 const SwSectionFormat* pSectionFormat, sal_uLong nLnNumRestartNo )
1158 if (HeaderFooterWritten()) {
1159 return; // #i117955# prevent new sections in endnotes
1161 m_aCps.push_back( nStartCp );
1162 AppendSection( pPd, pSectionFormat, nLnNumRestartNo );
1165 void MSWordSections::AppendSection( const SwFormatPageDesc& rPD,
1166 const SwNode& rNd, const SwSectionFormat* pSectionFormat, sal_uLong nLnNumRestartNo )
1168 if (HeaderFooterWritten()) {
1169 return; // #i117955# prevent new sections in endnotes
1172 WW8_SepInfo aI( rPD.GetPageDesc(), pSectionFormat, nLnNumRestartNo, rPD.GetNumOffset(), &rNd );
1174 m_aSects.push_back( aI );
1175 NeedsDocumentProtected( aI );
1178 void WW8_WrPlcSepx::AppendSep( WW8_CP nStartCp, const SwFormatPageDesc& rPD,
1179 const SwNode& rNd, const SwSectionFormat* pSectionFormat, sal_uLong nLnNumRestartNo )
1181 if (HeaderFooterWritten()) {
1182 return; // #i117955# prevent new sections in endnotes
1184 m_aCps.push_back( nStartCp );
1185 AppendSection( rPD, rNd, pSectionFormat, nLnNumRestartNo );
1188 void WW8_WrPlcSepx::WriteFootnoteEndText( WW8Export& rWrt, sal_uLong nCpStt )
1190 sal_uInt8 nInfoFlags = 0;
1191 const SwFootnoteInfo& rInfo = rWrt.m_rDoc.GetFootnoteInfo();
1192 if( !rInfo.m_aErgoSum.isEmpty() ) nInfoFlags |= 0x02;
1193 if( !rInfo.m_aQuoVadis.isEmpty() ) nInfoFlags |= 0x04;
1195 sal_uInt8 nEmptyStt = 0;
1196 if( nInfoFlags )
1198 m_pTextPos->Append( nCpStt ); // empty footnote separator
1200 if( 0x02 & nInfoFlags ) // Footnote continuation separator
1202 m_pTextPos->Append( nCpStt );
1203 rWrt.WriteStringAsPara( rInfo.m_aErgoSum );
1204 rWrt.WriteStringAsPara( OUString() );
1205 nCpStt = rWrt.Fc2Cp( rWrt.Strm().Tell() );
1207 else
1208 m_pTextPos->Append( nCpStt );
1210 if( 0x04 & nInfoFlags ) // Footnote continuation notice
1212 m_pTextPos->Append( nCpStt );
1213 rWrt.WriteStringAsPara( rInfo.m_aQuoVadis );
1214 rWrt.WriteStringAsPara( OUString() );
1215 nCpStt = rWrt.Fc2Cp( rWrt.Strm().Tell() );
1217 else
1218 m_pTextPos->Append( nCpStt );
1220 nEmptyStt = 3;
1223 while( 6 > nEmptyStt++ )
1224 m_pTextPos->Append( nCpStt );
1226 // set the flags at the Dop right away
1227 WW8Dop& rDop = *rWrt.m_pDop;
1228 // Footnote Info
1229 switch( rInfo.m_eNum )
1231 case FTNNUM_PAGE: rDop.rncFootnote = 2; break;
1232 case FTNNUM_CHAPTER: rDop.rncFootnote = 1; break;
1233 default: rDop.rncFootnote = 0; break;
1234 } // rncFootnote
1235 rDop.nfcFootnoteRef = WW8Export::GetNumId( rInfo.m_aFormat.GetNumberingType() );
1236 rDop.nFootnote = rInfo.m_nFootnoteOffset + 1;
1237 rDop.fpc = rWrt.m_bFootnoteAtTextEnd ? 2 : 1;
1239 // Endnote Info
1240 rDop.rncEdn = 0; // rncEdn: Don't Restart
1241 const SwEndNoteInfo& rEndInfo = rWrt.m_rDoc.GetEndNoteInfo();
1242 rDop.nfcEdnRef = WW8Export::GetNumId( rEndInfo.m_aFormat.GetNumberingType() );
1243 rDop.nEdn = rEndInfo.m_nFootnoteOffset + 1;
1244 rDop.epc = rWrt.m_bEndAtTextEnd ? 3 : 0;
1247 void MSWordSections::SetHeaderFlag( sal_uInt8& rHeadFootFlags, const SwFormat& rFormat,
1248 sal_uInt8 nFlag )
1250 const SwFormatHeader* pItem = rFormat.GetItemIfSet(RES_HEADER);
1251 if( pItem && pItem->IsActive() && pItem->GetHeaderFormat() )
1252 rHeadFootFlags |= nFlag;
1255 void MSWordSections::SetFooterFlag( sal_uInt8& rHeadFootFlags, const SwFormat& rFormat,
1256 sal_uInt8 nFlag )
1258 const SwFormatFooter* pItem = rFormat.GetItemIfSet(RES_FOOTER);
1259 if( pItem && pItem->IsActive() && pItem->GetFooterFormat() )
1260 rHeadFootFlags |= nFlag;
1263 void WW8_WrPlcSepx::OutHeaderFooter( WW8Export& rWrt, bool bHeader,
1264 const SwFormat& rFormat, sal_uLong& rCpPos, sal_uInt8 nHFFlags,
1265 sal_uInt8 nFlag, sal_uInt8 nBreakCode)
1267 if ( nFlag & nHFFlags )
1269 m_pTextPos->Append( rCpPos );
1270 rWrt.WriteHeaderFooterText( rFormat, bHeader);
1271 rWrt.WriteStringAsPara( OUString() ); // CR to the end ( otherwise WW complains )
1272 rCpPos = rWrt.Fc2Cp( rWrt.Strm().Tell() );
1274 else
1276 m_pTextPos->Append( rCpPos );
1277 if ((bHeader? rWrt.m_bHasHdr : rWrt.m_bHasFtr) && nBreakCode!=0)
1279 rWrt.WriteStringAsPara( OUString() ); // Empty paragraph for empty header/footer
1280 rWrt.WriteStringAsPara( OUString() ); // a CR that WW8 needs for end of the stream
1281 rCpPos = rWrt.Fc2Cp( rWrt.Strm().Tell() );
1286 void MSWordSections::NeedsDocumentProtected(const WW8_SepInfo &rInfo)
1288 if (rInfo.IsProtected())
1289 mbDocumentIsProtected = true;
1292 bool WW8_SepInfo::IsProtected() const
1294 bool bRet = false;
1295 if (
1296 pSectionFormat &&
1297 (reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1)) != pSectionFormat)
1300 const SwSection *pSection = pSectionFormat->GetSection();
1301 if (pSection && pSection->IsProtect())
1303 bRet = true;
1306 return bRet;
1309 void MSWordSections::CheckForFacinPg( const WW8Export& rWrt ) const
1311 // 2 values getting set
1312 // Dop.fFacingPages == Header and Footer different
1313 // Dop.fSwapBordersFacingPgs == mirrored borders
1314 sal_uInt16 nEnd = 0;
1315 for( const WW8_SepInfo& rSepInfo : m_aSects )
1317 if( !rSepInfo.pSectionFormat )
1319 const SwPageDesc* pPd = rSepInfo.pPageDesc;
1320 if( pPd->GetFollow() && pPd != pPd->GetFollow() &&
1321 pPd->GetFollow()->GetFollow() == pPd->GetFollow() &&
1322 rSepInfo.pPDNd &&
1323 pPd->IsFollowNextPageOfNode( *rSepInfo.pPDNd ) )
1324 // so this is first page and subsequent, so only respect follow
1325 pPd = pPd->GetFollow();
1327 // left-/right chain of pagedescs ?
1328 else if( !( 1 & nEnd ) &&
1329 pPd->GetFollow() && pPd != pPd->GetFollow() &&
1330 pPd->GetFollow()->GetFollow() == pPd &&
1331 (( UseOnPage::Left == ( UseOnPage::All & pPd->ReadUseOn() ) &&
1332 UseOnPage::Right == ( UseOnPage::All & pPd->GetFollow()->ReadUseOn() )) ||
1333 ( UseOnPage::Right == ( UseOnPage::All & pPd->ReadUseOn() ) &&
1334 UseOnPage::Left == ( UseOnPage::All & pPd->GetFollow()->ReadUseOn() )) ))
1336 rWrt.m_pDop->fFacingPages = rWrt.m_pDop->fMirrorMargins = true;
1337 nEnd |= 1;
1340 if( !( 1 & nEnd ) &&
1341 ( !pPd->IsHeaderShared() || !pPd->IsFooterShared() ))
1343 rWrt.m_pDop->fFacingPages = true;
1344 nEnd |= 1;
1346 if( !( 2 & nEnd ) &&
1347 UseOnPage::Mirror == ( UseOnPage::Mirror & pPd->ReadUseOn() ))
1349 rWrt.m_pDop->fSwapBordersFacingPgs =
1350 rWrt.m_pDop->fMirrorMargins = true;
1351 nEnd |= 2;
1354 if( 3 == nEnd )
1355 break; // We do not need to go any further
1360 bool MSWordSections::HasBorderItem( const SwFormat& rFormat )
1362 const SvxBoxItem* pItem = rFormat.GetItemIfSet(RES_BOX);
1363 return pItem &&
1364 ( pItem->GetTop() ||
1365 pItem->GetBottom() ||
1366 pItem->GetLeft() ||
1367 pItem->GetRight() );
1370 void WW8AttributeOutput::StartSection()
1372 m_rWW8Export.m_pO->clear();
1375 void WW8AttributeOutput::SectFootnoteEndnotePr()
1377 const SwFootnoteInfo& rInfo = m_rWW8Export.m_rDoc.GetFootnoteInfo();
1378 const SwEndNoteInfo& rEndNoteInfo = m_rWW8Export.m_rDoc.GetEndNoteInfo();
1379 m_rWW8Export.InsUInt16( NS_sprm::SRncFtn::val );
1380 switch( rInfo.m_eNum )
1382 case FTNNUM_PAGE: m_rWW8Export.m_pO->push_back( sal_uInt8/*rncRstPage*/ (2) ); break;
1383 case FTNNUM_CHAPTER: m_rWW8Export.m_pO->push_back( sal_uInt8/*rncRstSect*/ (1) ); break;
1384 default: m_rWW8Export.m_pO->push_back( sal_uInt8/*rncCont*/ (0) ); break;
1387 m_rWW8Export.InsUInt16(NS_sprm::SNfcFtnRef::val);
1388 sal_uInt8 nId = WW8Export::GetNumId(rInfo.m_aFormat.GetNumberingType());
1389 SwWW8Writer::InsUInt16(*m_rWW8Export.m_pO, nId);
1390 m_rWW8Export.InsUInt16(NS_sprm::SNfcEdnRef::val);
1391 nId = WW8Export::GetNumId(rEndNoteInfo.m_aFormat.GetNumberingType());
1392 SwWW8Writer::InsUInt16(*m_rWW8Export.m_pO, nId);
1395 void WW8AttributeOutput::SectionFormProtection( bool bProtected )
1397 //If the document is to be exported as protected, then if a segment
1398 //is not protected, set the unlocked flag
1399 if ( m_rWW8Export.m_pSepx->DocumentIsProtected() && !bProtected )
1401 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SFProtected::val );
1402 m_rWW8Export.m_pO->push_back( 1 );
1406 void WW8AttributeOutput::SectionLineNumbering( sal_uLong nRestartNo, const SwLineNumberInfo& rLnNumInfo )
1408 // sprmSNLnnMod - activate Line Numbering and define Modulo
1409 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SNLnnMod::val );
1410 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, rLnNumInfo.GetCountBy() );
1412 // sprmSDxaLnn - xPosition of Line Number
1413 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SDxaLnn::val );
1414 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, rLnNumInfo.GetPosFromLeft() );
1416 // sprmSLnc - restart number: 0 per page, 1 per section, 2 never restart
1417 if ( nRestartNo || !rLnNumInfo.IsRestartEachPage() )
1419 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SLnc::val );
1420 m_rWW8Export.m_pO->push_back( nRestartNo ? 1 : 2 );
1423 // sprmSLnnMin - Restart the Line Number with given value
1424 if ( nRestartNo )
1426 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SLnnMin::val );
1427 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, o3tl::narrowing<sal_uInt16>(nRestartNo) - 1 );
1431 void WW8AttributeOutput::SectionTitlePage()
1433 // sprmSFTitlePage
1434 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SFTitlePage::val );
1435 m_rWW8Export.m_pO->push_back( 1 );
1438 void WW8AttributeOutput::SectionPageBorders( const SwFrameFormat* pPdFormat, const SwFrameFormat* pPdFirstPgFormat )
1440 // write border of page
1441 sal_uInt16 nPgBorder = MSWordSections::HasBorderItem( *pPdFormat ) ? 0 : USHRT_MAX;
1442 if ( pPdFormat != pPdFirstPgFormat )
1444 if ( MSWordSections::HasBorderItem( *pPdFirstPgFormat ) )
1446 if ( USHRT_MAX == nPgBorder )
1448 nPgBorder = 1;
1449 // only the first page outlined -> Get the BoxItem from the correct format
1450 m_rWW8Export.m_pISet = &pPdFirstPgFormat->GetAttrSet();
1451 OutputItem( pPdFirstPgFormat->GetFormatAttr( RES_BOX ) );
1454 else if ( !nPgBorder )
1455 nPgBorder = 2;
1458 // [MS-DOC] 2.9.255 SPgbPropOperand; 2.9.185 PgbOffsetFrom
1459 if (m_bFromEdge)
1460 nPgBorder |= (1<<5);
1462 if ( USHRT_MAX != nPgBorder )
1464 // write the Flag and Border Attribute
1465 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SPgbProp::val );
1466 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, nPgBorder );
1470 void WW8AttributeOutput::SectionBiDi( bool bBiDi )
1472 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SFBiDi::val );
1473 m_rWW8Export.m_pO->push_back( bBiDi? 1: 0 );
1476 void WW8AttributeOutput::SectionPageNumbering( sal_uInt16 nNumType, const ::std::optional<sal_uInt16>& oPageRestartNumber )
1478 // sprmSNfcPgn
1479 sal_uInt8 nb = WW8Export::GetNumId( nNumType );
1480 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SNfcPgn::val );
1481 m_rWW8Export.m_pO->push_back( nb );
1483 if ( oPageRestartNumber )
1485 // sprmSFPgnRestart
1486 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SFPgnRestart::val );
1487 m_rWW8Export.m_pO->push_back( 1 );
1489 // sprmSPgnStart
1490 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SPgnStart97::val );
1491 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, *oPageRestartNumber );
1495 void WW8AttributeOutput::SectionType( sal_uInt8 nBreakCode )
1497 if ( 2 != nBreakCode ) // new page is the default
1499 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SBkc::val );
1500 m_rWW8Export.m_pO->push_back( nBreakCode );
1504 void WW8Export::SetupSectionPositions( WW8_PdAttrDesc* pA )
1506 if ( !pA )
1507 return;
1509 if ( !m_pO->empty() ) // are there attributes ?
1511 pA->m_nLen = m_pO->size();
1512 pA->m_pData.reset(new sal_uInt8 [m_pO->size()]);
1513 // store for later
1514 memcpy( pA->m_pData.get(), m_pO->data(), m_pO->size() );
1515 m_pO->clear(); // clear HdFt-Text
1517 else // no attributes there
1519 pA->m_pData.reset();
1520 pA->m_nLen = 0;
1524 void WW8AttributeOutput::TextVerticalAdjustment( const drawing::TextVerticalAdjust nVA )
1526 if ( drawing::TextVerticalAdjust_TOP == nVA ) // top alignment is the default
1527 return;
1529 sal_uInt8 nMSVA = 0;
1530 switch( nVA )
1532 case drawing::TextVerticalAdjust_CENTER:
1533 nMSVA = 1;
1534 break;
1535 case drawing::TextVerticalAdjust_BOTTOM: //Writer = 2, Word = 3
1536 nMSVA = 3;
1537 break;
1538 case drawing::TextVerticalAdjust_BLOCK: //Writer = 3, Word = 2
1539 nMSVA = 2;
1540 break;
1541 default:
1542 break;
1544 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::SVjc::val );
1545 m_rWW8Export.m_pO->push_back( nMSVA );
1548 void WW8Export::WriteHeadersFooters( sal_uInt8 nHeadFootFlags,
1549 const SwFrameFormat& rFormat, const SwFrameFormat& rLeftHeaderFormat, const SwFrameFormat& rLeftFooterFormat, const SwFrameFormat& rFirstPageFormat, sal_uInt8 nBreakCode, bool /*bEvenAndOddHeaders*/ )
1551 sal_uLong nCpPos = Fc2Cp( Strm().Tell() );
1553 IncrementHdFtIndex();
1554 if ( !(nHeadFootFlags & WW8_HEADER_EVEN) && m_pDop->fFacingPages )
1555 m_pSepx->OutHeaderFooter( *this, true, rFormat, nCpPos, nHeadFootFlags, WW8_HEADER_ODD, nBreakCode );
1556 else
1557 m_pSepx->OutHeaderFooter( *this, true, rLeftHeaderFormat, nCpPos, nHeadFootFlags, WW8_HEADER_EVEN, nBreakCode );
1558 IncrementHdFtIndex();
1559 m_pSepx->OutHeaderFooter( *this, true, rFormat, nCpPos, nHeadFootFlags, WW8_HEADER_ODD, nBreakCode );
1561 IncrementHdFtIndex();
1562 if ( !(nHeadFootFlags & WW8_FOOTER_EVEN) && m_pDop->fFacingPages )
1563 m_pSepx->OutHeaderFooter( *this, false, rFormat, nCpPos, nHeadFootFlags, WW8_FOOTER_ODD, nBreakCode );
1564 else
1565 m_pSepx->OutHeaderFooter( *this, false, rLeftFooterFormat, nCpPos, nHeadFootFlags, WW8_FOOTER_EVEN, nBreakCode );
1566 IncrementHdFtIndex();
1567 m_pSepx->OutHeaderFooter( *this, false, rFormat, nCpPos, nHeadFootFlags, WW8_FOOTER_ODD, nBreakCode );
1569 //#i24344# Drawing objects cannot be directly shared between main hd/ft
1570 //and title hd/ft so we need to differentiate them
1571 IncrementHdFtIndex();
1572 m_pSepx->OutHeaderFooter( *this, true, rFirstPageFormat, nCpPos, nHeadFootFlags, WW8_HEADER_FIRST, nBreakCode );
1573 m_pSepx->OutHeaderFooter( *this, false, rFirstPageFormat, nCpPos, nHeadFootFlags, WW8_FOOTER_FIRST, nBreakCode );
1576 namespace
1579 * Determines if the continuous section break we start should use page style properties (header,
1580 * footer, margins) from the next page style of the previous section.
1582 bool UsePrevSectionNextStyle(sal_uInt8 nBreakCode, const SwPageDesc* pPd,
1583 const WW8_SepInfo& rSepInfo)
1585 if (nBreakCode != 0)
1587 // Not a continuous section break.
1588 return false;
1591 if (!pPd->GetFollow())
1593 // Page style has no follow style.
1594 return false;
1597 // We start a continuous section break without headers/footers. Possibly the importer had
1598 // headers/footers for this section break and put them to the closest page break's page style's
1599 // next page style. See "find a node in the section that has a page break" in writerfilter/.
1600 // Try the last-in-practice paragraph of the previous section.
1601 const SwSectionFormat* pSection = rSepInfo.pSectionFormat;
1602 if (pSection == reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1)))
1604 return false;
1607 const SwNodeIndex* pSectionStart = pSection->GetContent().GetContentIdx();
1608 if (!pSectionStart)
1610 return false;
1613 SwPaM aPaM(*pSectionStart);
1614 aPaM.Move(fnMoveBackward);
1615 if (!aPaM.GetPointNode().IsTextNode())
1617 return false;
1620 SwTextNode* pTextNode = aPaM.GetPointNode().GetTextNode();
1621 const SwAttrSet* pParaProps = &pTextNode->GetSwAttrSet();
1622 sal_uInt32 nCharHeight = pParaProps->GetSize().GetHeight();
1623 if (nCharHeight > 20)
1625 return false;
1628 aPaM.Move(fnMoveBackward);
1629 if (!aPaM.GetPointNode().IsTextNode())
1631 return false;
1634 pTextNode = aPaM.GetPointNode().GetTextNode();
1635 pParaProps = &pTextNode->GetSwAttrSet();
1636 return pParaProps->HasItem(RES_PAGEDESC);
1640 void MSWordExportBase::SectionProperties( const WW8_SepInfo& rSepInfo, WW8_PdAttrDesc* pA )
1642 const SwPageDesc* pPd = rSepInfo.pPageDesc;
1644 if ( rSepInfo.pSectionFormat && !pPd )
1645 pPd = &m_rDoc.GetPageDesc( 0 );
1647 m_pCurrentPageDesc = pPd;
1649 if ( !pPd )
1650 return;
1652 bool bOldPg = m_bOutPageDescs;
1653 m_bOutPageDescs = true;
1654 const SwPageDesc* pSavedPageDesc = pPd;
1656 AttrOutput().StartSection();
1658 AttrOutput().SectFootnoteEndnotePr();
1660 // forms
1661 AttrOutput().SectionFormProtection( rSepInfo.IsProtected() );
1663 // line numbers
1664 const SwLineNumberInfo& rLnNumInfo = m_rDoc.GetLineNumberInfo();
1665 if ( rLnNumInfo.IsPaintLineNumbers() )
1666 AttrOutput().SectionLineNumbering( rSepInfo.nLnNumRestartNo, rLnNumInfo );
1668 /* sprmSBkc, break code: 0 No break, 1 New column
1669 2 New page, 3 Even page, 4 Odd page
1671 sal_uInt8 nBreakCode = 2; // default start new page
1672 bool bOutPgDscSet = true, bLeftRightPgChain = false, bOutputStyleItemSet = false;
1673 const SwFrameFormat* pPdFormat = &pPd->GetMaster();
1674 bool bUsePrevSectionNextStyle = false;
1675 if ( rSepInfo.pSectionFormat )
1677 // if pSectionFormat is set, then there is a SectionNode
1678 // valid pointer -> start Section ,
1679 // 0xfff -> Section terminated
1680 nBreakCode = 0; // consecutive section
1682 if (rSepInfo.pPDNd && (rSepInfo.pPDNd->IsContentNode() || rSepInfo.pPDNd->IsTableNode()))
1684 const SfxItemSet* pSet
1685 = rSepInfo.pPDNd->IsContentNode()
1686 ? &rSepInfo.pPDNd->GetContentNode()->GetSwAttrSet()
1687 : &rSepInfo.pPDNd->GetTableNode()->GetTable().GetFrameFormat()->GetAttrSet();
1689 if (!NoPageBreakSection(pSet))
1690 nBreakCode = 2;
1693 if (reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1)) != rSepInfo.pSectionFormat)
1695 if ( nBreakCode == 0 )
1696 bOutPgDscSet = false;
1698 // produce Itemset, which inherits PgDesk-Attr-Set:
1699 // as child also the parent is searched if 'deep'-OutputItemSet
1700 const SfxItemSet* pPdSet = &pPdFormat->GetAttrSet();
1702 bUsePrevSectionNextStyle = GetExportFormat() == ExportFormat::DOCX
1703 && UsePrevSectionNextStyle(nBreakCode, pPd, rSepInfo);
1704 if (bUsePrevSectionNextStyle)
1706 // Take page margins from the previous section's next style.
1707 pPdSet = &pPd->GetFollow()->GetMaster().GetAttrSet();
1710 SfxItemSet aSet( *pPdSet->GetPool(), pPdSet->GetRanges() );
1711 aSet.SetParent( pPdSet );
1713 // at the child ONLY change column structure according to Sect-Attr.
1715 const SvxLRSpaceItem &rSectionLR =
1716 rSepInfo.pSectionFormat->GetFormatAttr( RES_LR_SPACE );
1717 const SvxLRSpaceItem &rPageLR =
1718 pPdFormat->GetFormatAttr( RES_LR_SPACE );
1720 SvxLRSpaceItem aResultLR( rPageLR.GetLeft() +
1721 rSectionLR.GetLeft(), rPageLR.GetRight() +
1722 rSectionLR.GetRight(), 0, RES_LR_SPACE );
1723 //i120133: The Section width should consider section indent value.
1724 if (rSectionLR.GetLeft()+rSectionLR.GetRight()!=0)
1726 const SwFormatCol& rCol = rSepInfo.pSectionFormat->GetFormatAttr(RES_COL);
1727 SwFormatCol aCol(rCol);
1728 aCol.SetAdjustValue(rSectionLR.GetLeft()+rSectionLR.GetRight());
1729 aSet.Put(aCol);
1731 else
1732 aSet.Put(rSepInfo.pSectionFormat->GetFormatAttr(RES_COL));
1734 aSet.Put( aResultLR );
1736 // and write into the WW-File
1737 const SfxItemSet* pOldI = m_pISet;
1738 m_pISet = &aSet;
1740 // Switch off test on default item values, if page description
1741 // set (value of <bOutPgDscSet>) isn't written.
1742 AttrOutput().OutputStyleItemSet( aSet, bOutPgDscSet );
1743 bOutputStyleItemSet = true;
1745 //Cannot export as normal page framedir, as continuous sections
1746 //cannot contain any grid settings like proper sections
1747 AttrOutput().SectionBiDi( SvxFrameDirection::Horizontal_RL_TB == TrueFrameDirection( *rSepInfo.pSectionFormat ) );
1749 m_pISet = pOldI;
1753 // Libreoffice 4.0 introduces support for page styles (SwPageDesc) with
1754 // a different header/footer for the first page. The same effect can be
1755 // achieved by chaining two page styles together (SwPageDesc::GetFollow)
1756 // which are identical except for header/footer.
1757 // The latter method was previously used by the doc/docx import filter.
1758 // In both of these cases, we emit a single Word section with different
1759 // first page header/footer.
1760 const SwFrameFormat* pPdFirstPgFormat = &pPd->GetFirstMaster();
1761 bool titlePage = !pPd->IsFirstShared();
1762 if ( bOutPgDscSet )
1764 // if a Follow is set and it does not point to itself,
1765 // then there is a page chain.
1766 // If this emulates a "first page", we can detect it here and write
1767 // it as title page.
1768 // With Left/Right changes it's different - we have to detect where
1769 // the change of pages is, but here it's too late for that!
1770 if ( pPd->GetFollow() && pPd != pPd->GetFollow() &&
1771 pPd->GetFollow()->GetFollow() == pPd->GetFollow() &&
1772 pPd->IsHeaderShared() && pPd->IsFooterShared() &&
1773 ( !rSepInfo.pPDNd || pPd->IsFollowNextPageOfNode( *rSepInfo.pPDNd ) ) )
1775 const SwPageDesc *pFollow = pPd->GetFollow();
1776 const SwFrameFormat& rFollowFormat = pFollow->GetMaster();
1777 if (sw::util::IsPlausableSingleWordSection(*pPdFirstPgFormat, rFollowFormat))
1779 if (titlePage)
1781 // Do nothing. First format is already set.
1783 else if (rSepInfo.pPDNd)
1784 pPdFirstPgFormat = pPd->GetPageFormatOfNode( *rSepInfo.pPDNd );
1785 else
1786 pPdFirstPgFormat = &pPd->GetMaster();
1788 m_pCurrentPageDesc = pPd = pFollow;
1789 pPdFormat = &rFollowFormat;
1791 // has different headers/footers for the title page
1792 titlePage = true;
1795 else if (nBreakCode == 2 && pPd == m_pPreviousSectionPageDesc && pPd->GetFollow() == pPd)
1797 // The first title page has already been displayed in the previous section. Drop it.
1798 titlePage = false;
1801 const SfxItemSet* pOldI = m_pISet;
1803 const SfxPoolItem* pItem;
1804 if ( titlePage && SfxItemState::SET ==
1805 pPdFirstPgFormat->GetItemState( RES_PAPER_BIN, true, &pItem ) )
1807 m_pISet = &pPdFirstPgFormat->GetAttrSet();
1808 m_bOutFirstPage = true;
1809 AttrOutput().OutputItem( *pItem );
1810 m_bOutFirstPage = false;
1813 // left-/right chain of pagedescs ?
1814 if ( pPd->GetFollow() && pPd != pPd->GetFollow() &&
1815 pPd->GetFollow()->GetFollow() == pPd &&
1816 (( UseOnPage::Left == ( UseOnPage::All & pPd->ReadUseOn() ) &&
1817 UseOnPage::Right == ( UseOnPage::All & pPd->GetFollow()->ReadUseOn() )) ||
1818 ( UseOnPage::Right == ( UseOnPage::All & pPd->ReadUseOn() ) &&
1819 UseOnPage::Left == ( UseOnPage::All & pPd->GetFollow()->ReadUseOn() )) ))
1821 bLeftRightPgChain = true;
1823 // which is the reference point? (left or right?)
1824 // assume it is on the right side!
1825 if ( UseOnPage::Left == ( UseOnPage::All & pPd->ReadUseOn() ) )
1827 nBreakCode = 3;
1828 pPdFormat = &pPd->GetMaster(); //use the current page for settings (margins/width etc)
1829 pPd = pPd->GetFollow(); //switch to the right page for the right/odd header/footer
1831 else
1832 nBreakCode = 4;
1835 m_pISet = &pPdFormat->GetAttrSet();
1836 if (!bOutputStyleItemSet)
1838 if (titlePage)
1840 m_pFirstPageFormat = pPdFirstPgFormat;
1843 AttrOutput().OutputStyleItemSet( pPdFormat->GetAttrSet(), false );
1845 if (titlePage)
1847 m_pFirstPageFormat = nullptr;
1850 AttrOutput().SectionPageBorders( pPdFormat, pPdFirstPgFormat );
1851 m_pISet = pOldI;
1853 // then the rest of the settings from PageDesc
1854 AttrOutput().SectionPageNumbering( pPd->GetNumType().GetNumberingType(), rSepInfo.oPgRestartNo );
1856 // will it be only left or only right pages?
1857 if ( 2 == nBreakCode )
1859 if ( UseOnPage::Left == ( UseOnPage::All & pPd->ReadUseOn() ) )
1860 nBreakCode = 3;
1861 else if ( UseOnPage::Right == ( UseOnPage::All & pPd->ReadUseOn() ) )
1862 nBreakCode = 4;
1866 if (titlePage)
1867 AttrOutput().SectionTitlePage();
1869 AttrOutput().SectionType( nBreakCode );
1871 if( rSepInfo.pPageDesc ) {
1872 AttrOutput().TextVerticalAdjustment( rSepInfo.pPageDesc->GetVerticalAdjustment() );
1875 // Header or Footer
1876 sal_uInt8 nHeadFootFlags = 0;
1877 // Should we output a w:evenAndOddHeaders tag or not?
1878 // N.B.: despite its name this tag affects _both_ headers and footers!
1879 bool bEvenAndOddHeaders = true;
1880 bool bEvenAndOddFooters = true;
1882 const SwFrameFormat* pPdLeftHeaderFormat = nullptr;
1883 const SwFrameFormat* pPdLeftFooterFormat = nullptr;
1884 if (bLeftRightPgChain)
1886 const SwFrameFormat* pHeaderFormat = pPd->GetStashedFrameFormat(true, true, true);
1887 const SwFrameFormat* pFooterFormat = pPd->GetStashedFrameFormat(false, true, true);
1888 if (pHeaderFormat)
1890 pPdLeftHeaderFormat = pHeaderFormat;
1891 bEvenAndOddHeaders = false;
1893 else
1895 pPdLeftHeaderFormat = &pPd->GetFollow()->GetFirstLeft();
1897 if (pFooterFormat)
1899 pPdLeftFooterFormat = pFooterFormat;
1900 bEvenAndOddFooters = false;
1902 else
1904 pPdLeftFooterFormat = &pPd->GetFollow()->GetFirstLeft();
1907 else
1909 const SwFrameFormat* pHeaderFormat = pPd->GetStashedFrameFormat(true, true, false);
1910 const SwFrameFormat* pFooterFormat = pPd->GetStashedFrameFormat(false, true, false);
1911 if (pHeaderFormat)
1913 pPdLeftHeaderFormat = pHeaderFormat;
1914 bEvenAndOddHeaders = false;
1916 else
1918 pPdLeftHeaderFormat = &pPd->GetLeft();
1920 if (pFooterFormat)
1922 pPdLeftFooterFormat = pFooterFormat;
1923 bEvenAndOddFooters = false;
1925 else
1927 pPdLeftFooterFormat = &pPd->GetLeft();
1931 // Ensure that headers are written if section is first paragraph
1932 if (nBreakCode != 0 || (rSepInfo.pSectionFormat && rSepInfo.bIsFirstParagraph))
1934 if ( titlePage )
1936 // there is a First Page:
1937 MSWordSections::SetHeaderFlag( nHeadFootFlags, *pPdFirstPgFormat, WW8_HEADER_FIRST );
1938 MSWordSections::SetFooterFlag( nHeadFootFlags, *pPdFirstPgFormat, WW8_FOOTER_FIRST );
1940 else
1942 if ( pPd->GetStashedFrameFormat(true, true, true) && pPdLeftHeaderFormat && pPdLeftHeaderFormat->GetHeader().GetHeaderFormat() )
1944 MSWordSections::SetHeaderFlag( nHeadFootFlags, *pPdLeftHeaderFormat, WW8_HEADER_FIRST );
1946 if ( pPd->GetStashedFrameFormat(false, true, true) && pPdLeftFooterFormat && pPdLeftFooterFormat->GetFooter().GetFooterFormat() )
1948 MSWordSections::SetFooterFlag( nHeadFootFlags, *pPdLeftFooterFormat, WW8_FOOTER_FIRST );
1952 MSWordSections::SetHeaderFlag( nHeadFootFlags, *pPdFormat, WW8_HEADER_ODD );
1953 MSWordSections::SetFooterFlag( nHeadFootFlags, *pPdFormat, WW8_FOOTER_ODD );
1955 if ( !pPd->IsHeaderShared() || bLeftRightPgChain )
1957 MSWordSections::SetHeaderFlag( nHeadFootFlags, *pPdLeftHeaderFormat, WW8_HEADER_EVEN );
1959 else if ( pPd->IsHeaderShared() && pPd->GetStashedFrameFormat(true, true, false) && pPdLeftHeaderFormat && pPdLeftHeaderFormat->GetHeader().GetHeaderFormat() )
1961 MSWordSections::SetHeaderFlag( nHeadFootFlags, *pPdLeftHeaderFormat, WW8_HEADER_EVEN );
1962 bEvenAndOddHeaders = false;
1965 if ( !pPd->IsFooterShared() || bLeftRightPgChain )
1967 MSWordSections::SetFooterFlag( nHeadFootFlags, *pPdLeftFooterFormat, WW8_FOOTER_EVEN );
1969 else if ( pPd->IsFooterShared() && pPd->GetStashedFrameFormat(false, true, false) && pPdLeftFooterFormat && pPdLeftFooterFormat->GetFooter().GetFooterFormat() )
1971 MSWordSections::SetFooterFlag( nHeadFootFlags, *pPdLeftFooterFormat, WW8_FOOTER_EVEN );
1972 bEvenAndOddFooters = false;
1976 // binary filters only
1977 SetupSectionPositions( pA );
1980 !!!!!!!!!!!
1981 // borders at header and footer texts would be done like this:
1982 // This should use something like pOut,
1983 // which is repeated with every special text line.
1984 const SwFrameFormat* pFFormat = rFt.GetFooterFormat();
1985 const SvxBoxItem& rBox = pFFormat->GetBox(false);
1986 OutWW8_SwFormatBox1( m_rWW8Export.pOut, rBox, false);
1987 !!!!!!!!!!!
1988 You can turn this into paragraph attributes, which are then observed in each paragraph.
1989 Applies to background / border.
1990 !!!!!!!!!!!
1993 const SwTextNode *pOldPageRoot = GetHdFtPageRoot();
1994 SetHdFtPageRoot( rSepInfo.pPDNd ? rSepInfo.pPDNd->GetTextNode() : nullptr );
1996 if (bUsePrevSectionNextStyle && nHeadFootFlags == 0)
1998 // Take headers/footers from the previous section's next style.
1999 pPdFormat = &pPd->GetFollow()->GetMaster();
2000 MSWordSections::SetHeaderFlag(nHeadFootFlags, *pPdFormat, WW8_HEADER_ODD);
2001 MSWordSections::SetFooterFlag(nHeadFootFlags, *pPdFormat, WW8_FOOTER_ODD);
2004 WriteHeadersFooters( nHeadFootFlags, *pPdFormat, *pPdLeftHeaderFormat, *pPdLeftFooterFormat, *pPdFirstPgFormat, nBreakCode, bEvenAndOddHeaders && bEvenAndOddFooters );
2006 SetHdFtPageRoot( pOldPageRoot );
2008 AttrOutput().EndSection();
2010 // outside of the section properties again
2011 m_bOutPageDescs = bOldPg;
2012 m_pPreviousSectionPageDesc = pSavedPageDesc;
2015 bool WW8_WrPlcSepx::WriteKFText( WW8Export& rWrt )
2017 sal_uLong nCpStart = rWrt.Fc2Cp( rWrt.Strm().Tell() );
2019 OSL_ENSURE( !m_pTextPos, "who set the pointer?" );
2020 m_pTextPos.reset( new WW8_WrPlc0( nCpStart ) );
2022 WriteFootnoteEndText( rWrt, nCpStart );
2023 CheckForFacinPg( rWrt );
2025 unsigned int nOldIndex = rWrt.GetHdFtIndex();
2026 rWrt.SetHdFtIndex( 0 );
2028 for (const WW8_SepInfo & rSepInfo : m_aSects)
2030 auto pAttrDesc = std::make_shared<WW8_PdAttrDesc>();
2031 m_SectionAttributes.push_back(pAttrDesc);
2033 rWrt.SectionProperties( rSepInfo, pAttrDesc.get() );
2035 // FIXME: this writes the section properties, but not of all sections;
2036 // it's possible that later in the document (e.g. in endnotes) sections
2037 // are added, but they won't have their properties written here!
2038 m_bHeaderFooterWritten = true;
2040 rWrt.SetHdFtIndex( nOldIndex ); //0
2042 if ( m_pTextPos->Count() )
2044 // HdFt available?
2045 sal_uLong nCpEnd = rWrt.Fc2Cp( rWrt.Strm().Tell() );
2046 m_pTextPos->Append( nCpEnd ); // End of last Header/Footer for PlcfHdd
2048 if ( nCpEnd > nCpStart )
2050 ++nCpEnd;
2051 m_pTextPos->Append( nCpEnd + 1 ); // End of last Header/Footer for PlcfHdd
2053 rWrt.WriteStringAsPara( OUString() ); // CR to the end ( otherwise WW complains )
2055 rWrt.m_pFieldHdFt->Finish( nCpEnd, rWrt.m_pFib->m_ccpText + rWrt.m_pFib->m_ccpFootnote );
2056 rWrt.m_pFib->m_ccpHdr = nCpEnd - nCpStart;
2058 else
2060 m_pTextPos.reset();
2063 return rWrt.m_pFib->m_ccpHdr != 0;
2066 void WW8_WrPlcSepx::WriteSepx( SvStream& rStrm ) const
2068 OSL_ENSURE(m_SectionAttributes.size() == static_cast<size_t>(m_aSects.size())
2069 , "WriteSepx(): arrays out of sync!");
2070 for (const auto & rSectionAttribute : m_SectionAttributes) // all sections
2072 WW8_PdAttrDesc *const pA = rSectionAttribute.get();
2073 if (pA->m_nLen && pA->m_pData != nullptr)
2075 pA->m_nSepxFcPos = rStrm.Tell();
2076 rStrm.WriteUInt16(pA->m_nLen);
2077 rStrm.WriteBytes(pA->m_pData.get(), pA->m_nLen);
2082 void WW8_WrPlcSepx::WritePlcSed( WW8Export& rWrt ) const
2084 OSL_ENSURE(m_SectionAttributes.size() == static_cast<size_t>(m_aSects.size())
2085 , "WritePlcSed(): arrays out of sync!");
2086 OSL_ENSURE( m_aCps.size() == m_aSects.size() + 1, "WrPlcSepx: DeSync" );
2087 sal_uInt64 nFcStart = rWrt.m_pTableStrm->Tell();
2089 for( decltype(m_aSects)::size_type i = 0; i <= m_aSects.size(); i++ )
2091 sal_uInt32 nP = m_aCps[i];
2092 rWrt.m_pTableStrm->WriteUInt32(nP);
2095 static WW8_SED aSed = {{4, 0},{0, 0, 0, 0},{0, 0},{0xff, 0xff, 0xff, 0xff}};
2097 for (const auto & rSectionAttribute : m_SectionAttributes)
2099 // Sepx-Pos
2100 UInt32ToSVBT32( rSectionAttribute->m_nSepxFcPos, aSed.fcSepx );
2101 rWrt.m_pTableStrm->WriteBytes(&aSed, sizeof(aSed));
2103 rWrt.m_pFib->m_fcPlcfsed = nFcStart;
2104 rWrt.m_pFib->m_lcbPlcfsed = rWrt.m_pTableStrm->Tell() - nFcStart;
2107 void WW8_WrPlcSepx::WritePlcHdd( WW8Export& rWrt ) const
2109 // Don't write out the PlcfHdd if ccpHdd is 0: it's a validation failure case.
2110 if( rWrt.m_pFib->m_ccpHdr != 0 && m_pTextPos && m_pTextPos->Count() )
2112 rWrt.m_pFib->m_fcPlcfhdd = rWrt.m_pTableStrm->Tell();
2113 m_pTextPos->Write( *rWrt.m_pTableStrm ); // Plc0
2114 rWrt.m_pFib->m_lcbPlcfhdd = rWrt.m_pTableStrm->Tell() -
2115 rWrt.m_pFib->m_fcPlcfhdd;
2119 void MSWordExportBase::WriteHeaderFooterText( const SwFormat& rFormat, bool bHeader )
2121 const SwFormatContent *pContent;
2122 if ( bHeader )
2124 m_bHasHdr = true;
2125 const SwFormatHeader& rHd = rFormat.GetHeader();
2126 OSL_ENSURE( rHd.GetHeaderFormat(), "Header text is not here" );
2128 if ( !rHd.GetHeaderFormat() )
2129 return;
2131 pContent = &rHd.GetHeaderFormat()->GetContent();
2133 else
2135 m_bHasFtr = true;
2136 const SwFormatFooter& rFt = rFormat.GetFooter();
2137 OSL_ENSURE( rFt.GetFooterFormat(), "Footer text is not here" );
2139 if ( !rFt.GetFooterFormat() )
2140 return;
2142 pContent = &rFt.GetFooterFormat()->GetContent();
2145 const SwNodeIndex* pSttIdx = pContent->GetContentIdx();
2147 if ( pSttIdx )
2149 SwNodeIndex aIdx( *pSttIdx, 1 ),
2150 aEnd( *pSttIdx->GetNode().EndOfSectionNode() );
2151 SwNodeOffset nStart = aIdx.GetIndex();
2152 SwNodeOffset nEnd = aEnd.GetIndex();
2154 // range, i.e. valid node
2155 if ( nStart < nEnd )
2157 bool bOldKF = m_bOutKF;
2158 m_bOutKF = true;
2159 WriteSpecialText( nStart, nEnd, TXT_HDFT );
2160 m_bOutKF = bOldKF;
2162 else
2163 pSttIdx = nullptr;
2166 if ( !pSttIdx )
2168 // there is no Header/Footer, but a CR is still necessary
2169 OSL_ENSURE( pSttIdx, "Header/Footer text is not really present" );
2170 AttrOutput().EmptyParagraph();
2174 // class WW8_WrPlcFootnoteEdn : Collect the Footnotes and Endnotes and output their text
2175 // and Plcs at the end of the document.
2176 // WW8_WrPlcFootnoteEdn is the class for Footnotes and Endnotes
2178 WW8_WrPlcSubDoc::WW8_WrPlcSubDoc()
2182 WW8_WrPlcSubDoc::~WW8_WrPlcSubDoc()
2186 void WW8_WrPlcFootnoteEdn::Append( WW8_CP nCp, const SwFormatFootnote& rFootnote )
2188 m_aCps.push_back( nCp );
2189 m_aContent.push_back( &rFootnote );
2192 WW8_Annotation::WW8_Annotation(const SwPostItField* pPostIt, WW8_CP nRangeStart, WW8_CP nRangeEnd)
2194 maDateTime( DateTime::EMPTY ),
2195 m_nRangeStart(nRangeStart),
2196 m_nRangeEnd(nRangeEnd)
2198 mpRichText = pPostIt->GetTextObject();
2199 if (!mpRichText)
2200 msSimpleText = pPostIt->GetText();
2201 msOwner = pPostIt->GetPar1();
2202 m_sInitials = pPostIt->GetInitials();
2203 maDateTime = DateTime(pPostIt->GetDate(), pPostIt->GetTime());
2206 WW8_Annotation::WW8_Annotation(const SwRedlineData* pRedline)
2208 mpRichText(nullptr),
2209 msSimpleText(pRedline->GetComment()),
2210 msOwner(SW_MOD()->GetRedlineAuthor(pRedline->GetAuthor())),
2211 maDateTime(pRedline->GetTimeStamp()),
2212 m_nRangeStart(0),
2213 m_nRangeEnd(0)
2217 bool WW8_Annotation::HasRange() const
2219 if (m_nRangeStart != m_nRangeEnd)
2221 return true;
2224 return !m_bIgnoreEmpty;
2227 void WW8_WrPlcAnnotations::AddRangeStartPosition(const OUString& rName, WW8_CP nStartCp,
2228 bool bIgnoreEmpty)
2230 m_aRangeStartPositions[rName] = std::make_pair(nStartCp, bIgnoreEmpty);
2233 void WW8_WrPlcAnnotations::Append( WW8_CP nCp, const SwPostItField *pPostIt )
2235 m_aCps.push_back( nCp );
2236 WW8_Annotation* p;
2237 if( m_aRangeStartPositions.find(pPostIt->GetName()) != m_aRangeStartPositions.end() )
2239 auto [nStartCp, bIgnoreEmpty] = m_aRangeStartPositions[pPostIt->GetName()];
2240 p = new WW8_Annotation(pPostIt, nStartCp, nCp);
2241 p->m_bIgnoreEmpty = bIgnoreEmpty;
2242 m_aRangeStartPositions.erase(pPostIt->GetName());
2244 else
2246 p = new WW8_Annotation(pPostIt, nCp, nCp);
2248 m_aContent.push_back( p );
2251 void WW8_WrPlcAnnotations::Append( WW8_CP nCp, const SwRedlineData *pRedline )
2253 maProcessedRedlines.insert(pRedline);
2254 m_aCps.push_back( nCp );
2255 WW8_Annotation* p = new WW8_Annotation(pRedline);
2256 m_aContent.push_back( p );
2259 bool WW8_WrPlcAnnotations::IsNewRedlineComment( const SwRedlineData *pRedline )
2261 return maProcessedRedlines.find(pRedline) == maProcessedRedlines.end();
2264 WW8_WrPlcAnnotations::~WW8_WrPlcAnnotations()
2266 for(const void * p : m_aContent)
2267 delete static_cast<WW8_Annotation const *>(p);
2270 bool WW8_WrPlcSubDoc::WriteGenericText( WW8Export& rWrt, sal_uInt8 nTTyp,
2271 WW8_CP& rCount )
2273 sal_uInt16 nLen = m_aContent.size();
2274 if ( !nLen )
2275 return false;
2277 sal_uLong nCpStart = rWrt.Fc2Cp( rWrt.Strm().Tell() );
2278 m_pTextPos.reset( new WW8_WrPlc0( nCpStart ) );
2279 sal_uInt16 i;
2281 switch ( nTTyp )
2283 case TXT_ATN:
2284 for ( i = 0; i < nLen; i++ )
2286 // beginning for PlcfAtnText
2287 m_pTextPos->Append( rWrt.Fc2Cp( rWrt.Strm().Tell() ));
2289 rWrt.WritePostItBegin();
2290 const WW8_Annotation& rAtn = *static_cast<const WW8_Annotation*>(m_aContent[i]);
2291 if (rAtn.mpRichText)
2292 rWrt.WriteOutliner(*rAtn.mpRichText, nTTyp);
2293 else
2295 OUString sText(rAtn.msSimpleText);
2296 rWrt.WriteStringAsPara(sText.replace(0x0A, 0x0B));
2299 break;
2301 case TXT_TXTBOX:
2302 case TXT_HFTXTBOX:
2303 for ( i = 0; i < nLen; i++ )
2305 // textbox content
2306 WW8_CP nCP = rWrt.Fc2Cp( rWrt.Strm().Tell() );
2307 m_aCps.insert( m_aCps.begin()+i, nCP );
2308 m_pTextPos->Append( nCP );
2310 if( m_aContent[ i ] != nullptr )
2312 // is it a writer or sdr - textbox?
2313 const SdrObject& rObj = *static_cast<SdrObject const *>(m_aContent[ i ]);
2314 if (rObj.GetObjInventor() == SdrInventor::FmForm)
2316 sal_uInt8 nOldTyp = rWrt.m_nTextTyp;
2317 rWrt.m_nTextTyp = nTTyp;
2318 rWrt.GetOCXExp().ExportControl(rWrt, dynamic_cast<const SdrUnoObj&>(rObj));
2319 rWrt.m_nTextTyp = nOldTyp;
2321 else if( auto pText = DynCastSdrTextObj(&rObj) )
2322 rWrt.WriteSdrTextObj(*pText, nTTyp);
2323 else
2325 const SwFrameFormat* pFormat = ::FindFrameFormat( &rObj );
2326 OSL_ENSURE( pFormat, "where is the format?" );
2328 const SwNodeIndex* pNdIdx = pFormat->GetContent().GetContentIdx();
2329 OSL_ENSURE( pNdIdx, "where is the StartNode of the Textbox?" );
2330 rWrt.WriteSpecialText( pNdIdx->GetIndex() + 1,
2331 pNdIdx->GetNode().EndOfSectionIndex(),
2332 nTTyp );
2334 SwNodeIndex aContentIdx = *pNdIdx;
2335 ++aContentIdx;
2336 if ( aContentIdx.GetNode().IsTableNode() )
2338 bool bContainsOnlyTables = true;
2339 do {
2340 aContentIdx = *(aContentIdx.GetNode().EndOfSectionNode());
2341 ++aContentIdx;
2342 if ( !aContentIdx.GetNode().IsTableNode() &&
2343 aContentIdx.GetIndex() != pNdIdx->GetNode().EndOfSectionIndex() )
2345 bContainsOnlyTables = false;
2347 } while ( aContentIdx.GetNode().IsTableNode() );
2348 if ( bContainsOnlyTables )
2350 // Additional paragraph containing a space to
2351 // assure that by WW created RTF from written WW8
2352 // does not crash WW.
2353 rWrt.WriteStringAsPara( " " );
2359 else if (i < m_aSpareFormats.size() && m_aSpareFormats[i])
2361 const SwFrameFormat& rFormat = *m_aSpareFormats[i];
2362 const SwNodeIndex* pNdIdx = rFormat.GetContent().GetContentIdx();
2363 rWrt.WriteSpecialText( pNdIdx->GetIndex() + 1,
2364 pNdIdx->GetNode().EndOfSectionIndex(), nTTyp );
2367 // CR at end of one textbox text ( otherwise WW gpft :-( )
2368 rWrt.WriteStringAsPara( OUString() );
2370 break;
2372 case TXT_EDN:
2373 case TXT_FTN:
2374 for ( i = 0; i < nLen; i++ )
2376 // beginning for PlcfFootnoteText/PlcfEdnText
2377 m_pTextPos->Append( rWrt.Fc2Cp( rWrt.Strm().Tell() ));
2379 // Note content
2380 const SwFormatFootnote* pFootnote = static_cast<SwFormatFootnote const *>(m_aContent[ i ]);
2381 rWrt.WriteFootnoteBegin( *pFootnote );
2382 const SwNodeIndex* pIdx = pFootnote->GetTextFootnote()->GetStartNode();
2383 OSL_ENSURE( pIdx, "Where is the start node of Foot-/Endnote?" );
2384 rWrt.WriteSpecialText( pIdx->GetIndex() + 1,
2385 pIdx->GetNode().EndOfSectionIndex(),
2386 nTTyp );
2388 break;
2390 default:
2391 OSL_ENSURE( false, "What kind of SubDocType is that?" );
2394 m_pTextPos->Append( rWrt.Fc2Cp( rWrt.Strm().Tell() ));
2395 // CR to the end ( otherwise WW complains )
2396 rWrt.WriteStringAsPara( OUString() );
2398 WW8_CP nCpEnd = rWrt.Fc2Cp( rWrt.Strm().Tell() );
2399 m_pTextPos->Append( nCpEnd );
2400 rCount = nCpEnd - nCpStart;
2402 return ( rCount != 0 );
2405 static bool lcl_AuthorComp( const std::pair<OUString,OUString>& aFirst, const std::pair<OUString,OUString>& aSecond)
2407 return aFirst.first < aSecond.first;
2410 static bool lcl_PosComp( const std::pair<WW8_CP, int>& aFirst, const std::pair<WW8_CP, int>& aSecond)
2412 return aFirst.first < aSecond.first;
2415 void WW8_WrPlcSubDoc::WriteGenericPlc( WW8Export& rWrt, sal_uInt8 nTTyp,
2416 WW8_FC& rTextStart, sal_Int32& rTextCount, WW8_FC& rRefStart, sal_Int32& rRefCount ) const
2419 sal_uInt64 nFcStart = rWrt.m_pTableStrm->Tell();
2420 sal_uInt16 nLen = m_aCps.size();
2421 if ( !nLen )
2422 return;
2424 OSL_ENSURE( m_aCps.size() + 2 == m_pTextPos->Count(), "WritePlc: DeSync" );
2426 std::vector<std::pair<OUString,OUString> > aStrArr;
2427 WW8Fib& rFib = *rWrt.m_pFib; // n+1-th CP-Pos according to the manual
2428 bool bWriteCP = true;
2430 switch ( nTTyp )
2432 case TXT_ATN:
2434 std::vector< std::pair<WW8_CP, int> > aRangeStartPos; // The second of the pair is the original index before sorting.
2435 std::vector< std::pair<WW8_CP, int> > aRangeEndPos; // Same, so we can map between the indexes before/after sorting.
2436 std::map<int, int> aAtnStartMap; // Maps from annotation index to start index.
2437 std::map<int, int> aStartAtnMap; // Maps from start index to annotation index.
2438 std::map<int, int> aStartEndMap; // Maps from start index to end index.
2439 // then write first the GrpXstAtnOwners
2440 int nIdx = 0;
2441 for ( sal_uInt16 i = 0; i < nLen; ++i )
2443 const WW8_Annotation& rAtn = *static_cast<const WW8_Annotation*>(m_aContent[i]);
2444 aStrArr.emplace_back(rAtn.msOwner,rAtn.m_sInitials);
2445 // record start and end positions for ranges
2446 if (rAtn.HasRange())
2448 aRangeStartPos.emplace_back(rAtn.m_nRangeStart, nIdx);
2449 aRangeEndPos.emplace_back(rAtn.m_nRangeEnd, nIdx);
2450 ++nIdx;
2454 //sort and remove duplicates
2455 std::sort(aStrArr.begin(), aStrArr.end(),&lcl_AuthorComp);
2456 auto aIter = std::unique(aStrArr.begin(), aStrArr.end());
2457 aStrArr.erase(aIter, aStrArr.end());
2459 // Also sort the start and end positions. We need to reference
2460 // the start index in the annotation table and also need to
2461 // reference the end index in the start table, so build a map
2462 // that knows what index to reference, after sorting.
2463 std::sort(aRangeStartPos.begin(), aRangeStartPos.end(), &lcl_PosComp);
2464 for (decltype(aRangeStartPos)::size_type i = 0; i < aRangeStartPos.size(); ++i)
2466 aAtnStartMap[aRangeStartPos[i].second] = i;
2467 aStartAtnMap[i] = aRangeStartPos[i].second;
2469 std::sort(aRangeEndPos.begin(), aRangeEndPos.end(), &lcl_PosComp);
2470 for (decltype(aRangeEndPos)::size_type i = 0; i < aRangeEndPos.size(); ++i)
2471 aStartEndMap[aAtnStartMap[ aRangeEndPos[i].second ]] = i;
2473 for ( decltype(aStrArr)::size_type i = 0; i < aStrArr.size(); ++i )
2475 const OUString& sAuthor = aStrArr[i].first;
2476 SwWW8Writer::WriteShort(*rWrt.m_pTableStrm, sAuthor.getLength());
2477 SwWW8Writer::WriteString16(*rWrt.m_pTableStrm, sAuthor,
2478 false);
2481 rFib.m_fcGrpStAtnOwners = nFcStart;
2482 nFcStart = rWrt.m_pTableStrm->Tell();
2483 rFib.m_lcbGrpStAtnOwners = nFcStart - rFib.m_fcGrpStAtnOwners;
2485 // Commented text ranges
2486 if( !aRangeStartPos.empty() )
2488 // Commented text ranges starting positions (Plcfbkf.aCP)
2489 rFib.m_fcPlcfAtnbkf = nFcStart;
2490 for ( decltype(aRangeStartPos)::size_type i = 0; i < aRangeStartPos.size(); ++i )
2492 SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, aRangeStartPos[i].first );
2494 SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, rFib.m_ccpText + 1);
2496 // Commented text ranges additional information (Plcfbkf.aFBKF)
2497 for ( decltype(aRangeStartPos)::size_type i = 0; i < aRangeStartPos.size(); ++i )
2499 SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, aStartEndMap[i] ); // FBKF.ibkl
2500 SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0 ); // FBKF.bkc
2503 nFcStart = rWrt.m_pTableStrm->Tell();
2504 rFib.m_lcbPlcfAtnbkf = nFcStart - rFib.m_fcPlcfAtnbkf;
2506 // Commented text ranges ending positions (PlcfBkl.aCP)
2507 rFib.m_fcPlcfAtnbkl = nFcStart;
2508 for ( decltype(aRangeEndPos)::size_type i = 0; i < aRangeEndPos.size(); ++i )
2510 SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, aRangeEndPos[i].first );
2512 SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, rFib.m_ccpText + 1);
2514 nFcStart = rWrt.m_pTableStrm->Tell();
2515 rFib.m_lcbPlcfAtnbkl = nFcStart - rFib.m_fcPlcfAtnbkl;
2517 // Commented text ranges as bookmarks (SttbfAtnBkmk)
2518 rFib.m_fcSttbfAtnbkmk = nFcStart;
2519 SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, sal_Int16(sal_uInt16(0xFFFF)) ); // SttbfAtnBkmk.fExtend
2520 SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, aRangeStartPos.size() ); // SttbfAtnBkmk.cData
2521 SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0xA ); // SttbfAtnBkmk.cbExtra
2523 for ( decltype(aRangeStartPos)::size_type i = 0; i < aRangeStartPos.size(); ++i )
2525 SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0 ); // SttbfAtnBkmk.cchData
2526 // One ATNBE structure for all text ranges
2527 SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0x0100 ); // ATNBE.bmc
2528 SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, aStartAtnMap[i] ); // ATNBE.lTag
2529 SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, -1 ); // ATNBE.lTagOld
2532 nFcStart = rWrt.m_pTableStrm->Tell();
2533 rFib.m_lcbSttbfAtnbkmk = nFcStart - rFib.m_fcSttbfAtnbkmk;
2536 // Write the extended >= Word XP ATRD records
2537 for( sal_uInt16 i = 0; i < nLen; ++i )
2539 const WW8_Annotation& rAtn = *static_cast<const WW8_Annotation*>(m_aContent[i]);
2541 sal_uInt32 nDTTM = sw::ms::DateTime2DTTM(rAtn.maDateTime);
2543 SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, nDTTM );
2544 SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0 );
2545 SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, 0 );
2546 SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, 0 );
2547 SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, 0 );
2550 rFib.m_fcAtrdExtra = nFcStart;
2551 nFcStart = rWrt.m_pTableStrm->Tell();
2552 rFib.m_lcbAtrdExtra = nFcStart - rFib.m_fcAtrdExtra;
2553 rFib.m_fcHplxsdr = 0x01010002; //WTF, but apparently necessary
2554 rFib.m_lcbHplxsdr = 0;
2556 break;
2557 case TXT_TXTBOX:
2558 case TXT_HFTXTBOX:
2560 m_pTextPos->Write( *rWrt.m_pTableStrm );
2561 const std::vector<sal_uInt32>* pShapeIds = GetShapeIdArr();
2562 OSL_ENSURE( pShapeIds, "Where are the ShapeIds?" );
2564 for ( sal_uInt16 i = 0; i < nLen; ++i )
2566 // write textbox story - FTXBXS
2567 // is it a writer or sdr - textbox?
2568 const SdrObject* pObj = static_cast<SdrObject const *>(m_aContent[ i ]);
2569 sal_Int32 nCnt = 1;
2570 if (DynCastSdrTextObj( pObj ))
2572 // find the "highest" SdrObject of this
2573 const SwFrameFormat& rFormat = *::FindFrameFormat( pObj );
2575 const SwFormatChain* pChn = &rFormat.GetChain();
2576 while ( pChn->GetNext() )
2578 // has a chain?
2579 // then calc the cur pos in the chain
2580 ++nCnt;
2581 pChn = &pChn->GetNext()->GetChain();
2584 if( nullptr == pObj )
2586 if (i < m_aSpareFormats.size() && m_aSpareFormats[i])
2588 const SwFrameFormat& rFormat = *m_aSpareFormats[i];
2590 const SwFormatChain* pChn = &rFormat.GetChain();
2591 while( pChn->GetNext() )
2593 // has a chain?
2594 // then calc the cur pos in the chain
2595 ++nCnt;
2596 pChn = &pChn->GetNext()->GetChain();
2600 // long cTxbx / iNextReuse
2601 SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, nCnt );
2602 // long cReusable
2603 SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, 0 );
2604 // short fReusable
2605 SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0 );
2606 // long reserved
2607 SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, -1 );
2608 // long lid
2609 SwWW8Writer::WriteLong( *rWrt.m_pTableStrm,
2610 (*pShapeIds)[i]);
2611 // long txidUndo
2612 SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, 0 );
2614 SwWW8Writer::FillCount( *rWrt.m_pTableStrm, 22 );
2615 bWriteCP = false;
2617 break;
2620 if ( bWriteCP )
2622 // write CP Positions
2623 for ( sal_uInt16 i = 0; i < nLen; i++ )
2624 SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, m_aCps[ i ] );
2626 // n+1-th CP-Pos according to the manual
2627 SwWW8Writer::WriteLong( *rWrt.m_pTableStrm,
2628 rFib.m_ccpText + rFib.m_ccpFootnote + rFib.m_ccpHdr + rFib.m_ccpEdn +
2629 rFib.m_ccpTxbx + rFib.m_ccpHdrTxbx + 1 );
2631 if ( TXT_ATN == nTTyp )
2633 sal_uInt16 nlTag = 0;
2634 for ( sal_uInt16 i = 0; i < nLen; ++i )
2636 const WW8_Annotation& rAtn = *static_cast<const WW8_Annotation*>(m_aContent[i]);
2638 //aStrArr is sorted
2639 auto aIter = std::lower_bound(aStrArr.begin(),
2640 aStrArr.end(), std::pair<OUString,OUString>(rAtn.msOwner,OUString()),
2641 &lcl_AuthorComp);
2642 OSL_ENSURE(aIter != aStrArr.end() && aIter->first == rAtn.msOwner,
2643 "Impossible");
2644 sal_uInt16 nFndPos = static_cast< sal_uInt16 >(aIter - aStrArr.begin());
2645 OUString sInitials( aIter->second );
2646 sal_uInt8 nInitialsLen = static_cast<sal_uInt8>(sInitials.getLength());
2647 if ( nInitialsLen > 9 )
2649 sInitials = sInitials.copy( 0, 9 );
2650 nInitialsLen = 9;
2653 // xstUsrInitl[ 10 ] pascal-style String holding initials
2654 // of annotation author
2655 SwWW8Writer::WriteShort(*rWrt.m_pTableStrm, nInitialsLen);
2656 SwWW8Writer::WriteString16(*rWrt.m_pTableStrm, sInitials,
2657 false);
2658 SwWW8Writer::FillCount( *rWrt.m_pTableStrm,
2659 (9 - nInitialsLen) * 2 );
2661 // documents layout of WriteShort's below:
2663 // SVBT16 ibst; // index into GrpXstAtnOwners
2664 // SVBT16 ak; // not used
2665 // SVBT16 grfbmc; // not used
2666 // SVBT32 ITagBkmk; // when not -1, this tag identifies the ATNBE
2668 SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, nFndPos );
2669 SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0 );
2670 SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0 );
2671 if (rAtn.HasRange())
2673 SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, nlTag );
2674 ++nlTag;
2676 else
2677 SwWW8Writer::WriteLong( *rWrt.m_pTableStrm, -1 );
2680 else
2682 sal_uInt16 nNo = 0;
2683 for ( sal_uInt16 i = 0; i < nLen; ++i ) // write Flags
2685 const SwFormatFootnote* pFootnote = static_cast<SwFormatFootnote const *>(m_aContent[ i ]);
2686 SwWW8Writer::WriteShort( *rWrt.m_pTableStrm,
2687 !pFootnote->GetNumStr().isEmpty() ? 0 : ++nNo );
2691 rRefStart = nFcStart;
2692 nFcStart = rWrt.m_pTableStrm->Tell();
2693 rRefCount = nFcStart - rRefStart;
2695 m_pTextPos->Write( *rWrt.m_pTableStrm );
2697 switch ( nTTyp )
2699 case TXT_TXTBOX:
2700 case TXT_HFTXTBOX:
2701 for ( sal_uInt16 i = 0; i < nLen; ++i )
2703 // write break descriptor (BKD)
2704 // short itxbxs
2705 SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, i );
2706 // short dcpDepend
2707 SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0 );
2708 // short flags : icol/fTableBreak/fColumnBreak/fMarked/
2709 // fUnk/fTextOverflow
2710 SwWW8Writer::WriteShort( *rWrt.m_pTableStrm, 0x800 );
2712 SwWW8Writer::FillCount( *rWrt.m_pTableStrm, 6 );
2713 break;
2716 rTextStart = nFcStart;
2717 rTextCount = rWrt.m_pTableStrm->Tell() - nFcStart;
2720 const std::vector<sal_uInt32>* WW8_WrPlcSubDoc::GetShapeIdArr() const
2722 return nullptr;
2725 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */