1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
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>
38 #include <poolfmt.hxx>
39 #include <fmtpdsc.hxx>
40 #include <pagedesc.hxx>
42 #include <ftninfo.hxx>
43 #include <fmthdft.hxx>
44 #include <section.hxx>
45 #include <fmtcntnt.hxx>
47 #include <ndindex.hxx>
49 #include <charfmt.hxx>
50 #include <docufld.hxx>
51 #include <dcontact.hxx>
52 #include <fmtcnct.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>
69 #include "ww8attributeoutput.hxx"
70 #include "docxattributeoutput.hxx"
71 #include "rtfattributeoutput.hxx"
73 #include <unordered_set>
76 using namespace sw::util
;
77 using namespace nsHdFtFlags
;
79 /// For the output of sections.
82 std::unique_ptr
<sal_uInt8
[]> m_pData
;
85 WW8_PdAttrDesc() : m_nLen(0), m_nSepxFcPos(0xffffffff) /*default: none*/
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
109 std::vector
<sal_uLong
> m_aPos
; // PTRARR of CPs / FCs
112 WW8_WrPlc0(WW8_WrPlc0
const&) = delete;
113 WW8_WrPlc0
& operator=(WW8_WrPlc0
const&) = delete;
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
);
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)
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
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
));
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
)
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
:
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
);
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
);
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;
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
);
333 m_aStyles
[nSlot
] = { pFormat
};
337 if (m_aStyles
.size() >= MSWORD_MAX_STYLES_LIMIT
)
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
;
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"))
359 m_aStyles
.emplace_back(pNumRule
);
363 // StyleSheetTable::ConvertStyleName appends the suffix do disambiguate conflicting style names
364 static OUString
StripWWSuffix(const OUString
& s
)
367 (void)ret
.endsWith(" (WW)", &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
)
381 while (!aUsed
.insert(lower
+ OUString::number(nFree
)).second
)
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())
415 entry
.ww_name
= StripWWSuffix(entry
.format
->GetName());
416 else if (entry
.num_rule
)
417 entry
.ww_name
= StripWWSuffix(entry
.num_rule
->GetName());
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())
440 OString
aLower(aStyleId
.toAsciiLowerCase());
442 // check for uniqueness & construct something unique if we have to
443 if (!aUsed
.insert(aLower
).second
)
446 while (!aUsed
.insert(aLower
+ OString::number(nFree
)).second
)
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
;
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
);
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"
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
;
543 nStt
= RES_PARATR_BEGIN
;
544 nEnd
= RES_FRMATR_END
;
548 nStt
= RES_CHRATR_BEGIN
;
549 nEnd
= RES_TXTATR_END
;
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
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;
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
604 SwWW8Writer::InsUInt16( *m_rWW8Export
.m_pO
, nLen
); // Style-Len
606 m_nStyleStartSize
= m_rWW8Export
.m_pO
->size();
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
;
647 if ( !pFormat
->IsDefault() )
648 nBase
= GetSlot( pFormat
->DerivedFrom() );
650 const SwFormat
* pNext
;
651 const SwFormat
* pLink
= nullptr;
654 auto pFormatColl
= static_cast<const SwTextFormatColl
*>(pFormat
);
655 pNext
= &pFormatColl
->GetNextTextFormatColl();
656 pLink
= pFormatColl
->GetLinkedCharFormat();
660 pNext
= pFormat
; // CharFormat: next CharFormat == self
661 auto pCharFormat
= static_cast<const SwCharFormat
*>(pFormat
);
662 pLink
= pCharFormat
->GetLinkedParaFormat();
665 nNext
= GetSlot( pNext
);
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
];
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();
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"
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() );
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
732 rFib
.m_fcStshfOrig
= rFib
.m_fcStshf
= nCurPos
;
733 m_nStyleCountPos
= nCurPos
+ 2; // count is added later
735 static sal_uInt8 aStShi
[] = {
737 0x0F, 0x00, 0x0A, 0x00, 0x01, 0x00, 0x5B, 0x00,
738 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 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();
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
)
767 m_rExport
.AttrOutput().EndStyles(m_aStyles
.size());
769 m_rExport
.m_bStyDef
= false;
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
788 maWW8_FFN
[0] = static_cast<sal_uInt8
>( 6 - 1 + 0x22 + ( 2 * ( 1 + msFamilyNm
.getLength() ) ));
790 maWW8_FFN
[0] = static_cast< sal_uInt8
>(maWW8_FFN
[0] + 2 * ( 1 + msAltNm
.getLength()));
796 aB
|= 2; // aF.prg = 2
801 default: // aF.prg = 0 : DEFAULT_PITCH (windows.h)
804 aB
|= 1 << 2; // aF.fTrueType = 1; don't know any better;
809 aB
|= 1 << 4; // aF.ff = 1;
812 aB
|= 2 << 4; // aF.ff = 2;
815 aB
|= 3 << 4; // aF.ff = 3;
818 aB
|= 4 << 4; // aF.ff = 4;
820 case FAMILY_DECORATIVE
:
821 aB
|= 5 << 4; // aF.ff = 5;
823 default: // aF.ff = 0; FF_DONTCARE (windows.h)
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
835 maWW8_FFN
[4] = sw::ms::rtl_TextEncodingToWinCharset(eChrSet
);
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,
846 //char panose[ 10 ]; // 0x6 PANOSE
847 //char fs[ 24 ]; // 0x10 FONTSIGNATURE
848 SwWW8Writer::FillCount(*pTableStrm
, 0x22);
849 SwWW8Writer::WriteString16(*pTableStrm
, msFamilyNm
, true);
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())
861 rAttrOutput
->StartFont( msFamilyNm
);
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
);
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
));
890 nRet
= r1
.msFamilyNm
.compareTo(r2
.msFamilyNm
);
892 nRet
= r1
.msAltNm
.compareTo(r2
.msAltNm
);
897 sal_uInt16
wwFontHelper::GetId(const wwFont
&rFont
)
900 std::map
<wwFont
, sal_uInt16
>::const_iterator aIter
= maFonts
.find(rFont
);
901 if (aIter
!= maFonts
.end())
902 nRet
= aIter
->second
;
905 nRet
= static_cast< sal_uInt16
>(maFonts
.size());
906 maFonts
[rFont
] = 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
)
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(),
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
;
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
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
)
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;
1046 pSet
= &pTableNd
->GetTable().GetFrameFormat()->GetAttrSet();
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() )
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
;
1075 (pDescItem
= pSet
->GetItemIfSet( RES_PAGEDESC
)) &&
1076 pDescItem
->GetPageDesc() )
1078 AppendSection( *pDescItem
, *pNd
, pFormat
, nRstLnNum
);
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
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() )
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
;
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();
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;
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() );
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() );
1218 m_pTextPos
->Append( nCpStt
);
1223 while( 6 > nEmptyStt
++ )
1224 m_pTextPos
->Append( nCpStt
);
1226 // set the flags at the Dop right away
1227 WW8Dop
& rDop
= *rWrt
.m_pDop
;
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;
1235 rDop
.nfcFootnoteRef
= WW8Export::GetNumId( rInfo
.m_aFormat
.GetNumberingType() );
1236 rDop
.nFootnote
= rInfo
.m_nFootnoteOffset
+ 1;
1237 rDop
.fpc
= rWrt
.m_bFootnoteAtTextEnd
? 2 : 1;
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
,
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
,
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() );
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
1297 (reinterpret_cast<SwSectionFormat
*>(sal_IntPtr(-1)) != pSectionFormat
)
1300 const SwSection
*pSection
= pSectionFormat
->GetSection();
1301 if (pSection
&& pSection
->IsProtect())
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() &&
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;
1340 if( !( 1 & nEnd
) &&
1341 ( !pPd
->IsHeaderShared() || !pPd
->IsFooterShared() ))
1343 rWrt
.m_pDop
->fFacingPages
= true;
1346 if( !( 2 & nEnd
) &&
1347 UseOnPage::Mirror
== ( UseOnPage::Mirror
& pPd
->ReadUseOn() ))
1349 rWrt
.m_pDop
->fSwapBordersFacingPgs
=
1350 rWrt
.m_pDop
->fMirrorMargins
= true;
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
);
1364 ( pItem
->GetTop() ||
1365 pItem
->GetBottom() ||
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
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()
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
)
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
)
1458 // [MS-DOC] 2.9.255 SPgbPropOperand; 2.9.185 PgbOffsetFrom
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
)
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
)
1486 SwWW8Writer::InsUInt16( *m_rWW8Export
.m_pO
, NS_sprm::SFPgnRestart::val
);
1487 m_rWW8Export
.m_pO
->push_back( 1 );
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
)
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()]);
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();
1524 void WW8AttributeOutput::TextVerticalAdjustment( const drawing::TextVerticalAdjust nVA
)
1526 if ( drawing::TextVerticalAdjust_TOP
== nVA
) // top alignment is the default
1529 sal_uInt8 nMSVA
= 0;
1532 case drawing::TextVerticalAdjust_CENTER
:
1535 case drawing::TextVerticalAdjust_BOTTOM
: //Writer = 2, Word = 3
1538 case drawing::TextVerticalAdjust_BLOCK
: //Writer = 3, Word = 2
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
);
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
);
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
);
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.
1591 if (!pPd
->GetFollow())
1593 // Page style has no follow style.
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)))
1607 const SwNodeIndex
* pSectionStart
= pSection
->GetContent().GetContentIdx();
1613 SwPaM
aPaM(*pSectionStart
);
1614 aPaM
.Move(fnMoveBackward
);
1615 if (!aPaM
.GetPointNode().IsTextNode())
1620 SwTextNode
* pTextNode
= aPaM
.GetPointNode().GetTextNode();
1621 const SwAttrSet
* pParaProps
= &pTextNode
->GetSwAttrSet();
1622 sal_uInt32 nCharHeight
= pParaProps
->GetSize().GetHeight();
1623 if (nCharHeight
> 20)
1628 aPaM
.Move(fnMoveBackward
);
1629 if (!aPaM
.GetPointNode().IsTextNode())
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
;
1652 bool bOldPg
= m_bOutPageDescs
;
1653 m_bOutPageDescs
= true;
1654 const SwPageDesc
* pSavedPageDesc
= pPd
;
1656 AttrOutput().StartSection();
1658 AttrOutput().SectFootnoteEndnotePr();
1661 AttrOutput().SectionFormProtection( rSepInfo
.IsProtected() );
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
))
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());
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
;
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
) );
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();
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
))
1781 // Do nothing. First format is already set.
1783 else if (rSepInfo
.pPDNd
)
1784 pPdFirstPgFormat
= pPd
->GetPageFormatOfNode( *rSepInfo
.pPDNd
);
1786 pPdFirstPgFormat
= &pPd
->GetMaster();
1788 m_pCurrentPageDesc
= pPd
= pFollow
;
1789 pPdFormat
= &rFollowFormat
;
1791 // has different headers/footers for the title page
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.
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() ) )
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
1835 m_pISet
= &pPdFormat
->GetAttrSet();
1836 if (!bOutputStyleItemSet
)
1840 m_pFirstPageFormat
= pPdFirstPgFormat
;
1843 AttrOutput().OutputStyleItemSet( pPdFormat
->GetAttrSet(), false );
1847 m_pFirstPageFormat
= nullptr;
1850 AttrOutput().SectionPageBorders( pPdFormat
, pPdFirstPgFormat
);
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() ) )
1861 else if ( UseOnPage::Right
== ( UseOnPage::All
& pPd
->ReadUseOn() ) )
1867 AttrOutput().SectionTitlePage();
1869 AttrOutput().SectionType( nBreakCode
);
1871 if( rSepInfo
.pPageDesc
) {
1872 AttrOutput().TextVerticalAdjustment( rSepInfo
.pPageDesc
->GetVerticalAdjustment() );
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);
1890 pPdLeftHeaderFormat
= pHeaderFormat
;
1891 bEvenAndOddHeaders
= false;
1895 pPdLeftHeaderFormat
= &pPd
->GetFollow()->GetFirstLeft();
1899 pPdLeftFooterFormat
= pFooterFormat
;
1900 bEvenAndOddFooters
= false;
1904 pPdLeftFooterFormat
= &pPd
->GetFollow()->GetFirstLeft();
1909 const SwFrameFormat
* pHeaderFormat
= pPd
->GetStashedFrameFormat(true, true, false);
1910 const SwFrameFormat
* pFooterFormat
= pPd
->GetStashedFrameFormat(false, true, false);
1913 pPdLeftHeaderFormat
= pHeaderFormat
;
1914 bEvenAndOddHeaders
= false;
1918 pPdLeftHeaderFormat
= &pPd
->GetLeft();
1922 pPdLeftFooterFormat
= pFooterFormat
;
1923 bEvenAndOddFooters
= false;
1927 pPdLeftFooterFormat
= &pPd
->GetLeft();
1931 // Ensure that headers are written if section is first paragraph
1932 if (nBreakCode
!= 0 || (rSepInfo
.pSectionFormat
&& rSepInfo
.bIsFirstParagraph
))
1936 // there is a First Page:
1937 MSWordSections::SetHeaderFlag( nHeadFootFlags
, *pPdFirstPgFormat
, WW8_HEADER_FIRST
);
1938 MSWordSections::SetFooterFlag( nHeadFootFlags
, *pPdFirstPgFormat
, WW8_FOOTER_FIRST
);
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
);
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);
1988 You can turn this into paragraph attributes, which are then observed in each paragraph.
1989 Applies to background / border.
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() )
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
)
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
;
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
)
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
;
2125 const SwFormatHeader
& rHd
= rFormat
.GetHeader();
2126 OSL_ENSURE( rHd
.GetHeaderFormat(), "Header text is not here" );
2128 if ( !rHd
.GetHeaderFormat() )
2131 pContent
= &rHd
.GetHeaderFormat()->GetContent();
2136 const SwFormatFooter
& rFt
= rFormat
.GetFooter();
2137 OSL_ENSURE( rFt
.GetFooterFormat(), "Footer text is not here" );
2139 if ( !rFt
.GetFooterFormat() )
2142 pContent
= &rFt
.GetFooterFormat()->GetContent();
2145 const SwNodeIndex
* pSttIdx
= pContent
->GetContentIdx();
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
;
2159 WriteSpecialText( nStart
, nEnd
, TXT_HDFT
);
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();
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()),
2217 bool WW8_Annotation::HasRange() const
2219 if (m_nRangeStart
!= m_nRangeEnd
)
2224 return !m_bIgnoreEmpty
;
2227 void WW8_WrPlcAnnotations::AddRangeStartPosition(const OUString
& rName
, WW8_CP nStartCp
,
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
);
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());
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
,
2273 sal_uInt16 nLen
= m_aContent
.size();
2277 sal_uLong nCpStart
= rWrt
.Fc2Cp( rWrt
.Strm().Tell() );
2278 m_pTextPos
.reset( new WW8_WrPlc0( nCpStart
) );
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
);
2295 OUString
sText(rAtn
.msSimpleText
);
2296 rWrt
.WriteStringAsPara(sText
.replace(0x0A, 0x0B));
2303 for ( i
= 0; i
< nLen
; i
++ )
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
);
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(),
2334 SwNodeIndex aContentIdx
= *pNdIdx
;
2336 if ( aContentIdx
.GetNode().IsTableNode() )
2338 bool bContainsOnlyTables
= true;
2340 aContentIdx
= *(aContentIdx
.GetNode().EndOfSectionNode());
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() );
2374 for ( i
= 0; i
< nLen
; i
++ )
2376 // beginning for PlcfFootnoteText/PlcfEdnText
2377 m_pTextPos
->Append( rWrt
.Fc2Cp( rWrt
.Strm().Tell() ));
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(),
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();
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;
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
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
);
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
,
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;
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
]);
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() )
2579 // then calc the cur pos in the chain
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() )
2594 // then calc the cur pos in the chain
2596 pChn
= &pChn
->GetNext()->GetChain();
2600 // long cTxbx / iNextReuse
2601 SwWW8Writer::WriteLong( *rWrt
.m_pTableStrm
, nCnt
);
2603 SwWW8Writer::WriteLong( *rWrt
.m_pTableStrm
, 0 );
2605 SwWW8Writer::WriteShort( *rWrt
.m_pTableStrm
, 0 );
2607 SwWW8Writer::WriteLong( *rWrt
.m_pTableStrm
, -1 );
2609 SwWW8Writer::WriteLong( *rWrt
.m_pTableStrm
,
2612 SwWW8Writer::WriteLong( *rWrt
.m_pTableStrm
, 0 );
2614 SwWW8Writer::FillCount( *rWrt
.m_pTableStrm
, 22 );
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
]);
2639 auto aIter
= std::lower_bound(aStrArr
.begin(),
2640 aStrArr
.end(), std::pair
<OUString
,OUString
>(rAtn
.msOwner
,OUString()),
2642 OSL_ENSURE(aIter
!= aStrArr
.end() && aIter
->first
== rAtn
.msOwner
,
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 );
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
,
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
);
2677 SwWW8Writer::WriteLong( *rWrt
.m_pTableStrm
, -1 );
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
);
2701 for ( sal_uInt16 i
= 0; i
< nLen
; ++i
)
2703 // write break descriptor (BKD)
2705 SwWW8Writer::WriteShort( *rWrt
.m_pTableStrm
, i
);
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 );
2716 rTextStart
= nFcStart
;
2717 rTextCount
= rWrt
.m_pTableStrm
->Tell() - nFcStart
;
2720 const std::vector
<sal_uInt32
>* WW8_WrPlcSubDoc::GetShapeIdArr() const
2725 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */