LibreOffice Module sw (master) 1
ww8atr.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20/*
21 * This file contains methods for the WW8 output
22 * (nodes, attributes, formats and chars).
23 */
24
25
26#include <algorithm>
27#include <hintids.hxx>
28
29#include <o3tl/safeint.hxx>
30#include <vcl/svapp.hxx>
31#include <vcl/settings.hxx>
32#include <sal/log.hxx>
33
34#include <svl/numformat.hxx>
35#include <svl/zformat.hxx>
36#include <svl/itemiter.hxx>
37#include <svl/whiter.hxx>
38#include <svl/grabbagitem.hxx>
39#include <editeng/fontitem.hxx>
40#include <editeng/tstpitem.hxx>
42#include <editeng/spltitem.hxx>
43#include <editeng/widwitem.hxx>
44#include <editeng/lspcitem.hxx>
45#include <editeng/keepitem.hxx>
46#include <editeng/shaditem.hxx>
47#include <editeng/brushitem.hxx>
48#include <editeng/postitem.hxx>
49#include <editeng/wghtitem.hxx>
50#include <editeng/kernitem.hxx>
52#include <editeng/cmapitem.hxx>
53#include <editeng/wrlmitem.hxx>
54#include <editeng/udlnitem.hxx>
55#include <editeng/langitem.hxx>
57#include <editeng/fhgtitem.hxx>
58#include <editeng/colritem.hxx>
61#include <editeng/lrspitem.hxx>
62#include <editeng/ulspitem.hxx>
63#include <editeng/boxitem.hxx>
65#include <editeng/shdditem.hxx>
67#include <editeng/pbinitem.hxx>
74#include <editeng/pgrditem.hxx>
76#include <editeng/blinkitem.hxx>
78#include <editeng/paperinf.hxx>
79#include <svx/xfillit0.hxx>
80#include <svx/xflgrit.hxx>
81#include <o3tl/string_view.hxx>
82#include <fmtfld.hxx>
83#include <fchrfmt.hxx>
84#include <fmtfsize.hxx>
85#include <fmtpdsc.hxx>
86#include <fmtornt.hxx>
87#include <fmtanchr.hxx>
88#include <fmtclds.hxx>
89#include <fmtsrnd.hxx>
90#include <fmtftn.hxx>
91#include <fmtflcnt.hxx>
92#include <frmatr.hxx>
93#include <swtable.hxx>
94#include <fmtinfmt.hxx>
95#include <txtftn.hxx>
96#include <poolfmt.hxx>
97#include <doc.hxx>
102#include <list.hxx>
103#include <docary.hxx>
104#include <pam.hxx>
105#include <paratr.hxx>
106#include <fldbas.hxx>
107#include <docufld.hxx>
108#include <expfld.hxx>
109#include <pagedesc.hxx>
110#include <ndtxt.hxx>
111#include <swrect.hxx>
112#include <redline.hxx>
113#include <reffld.hxx>
114#include <ftninfo.hxx>
115#include <charfmt.hxx>
116#include <section.hxx>
117#include <fmtline.hxx>
118#include <tox.hxx>
119#include <fmtftntx.hxx>
120#include <breakit.hxx>
121#include <com/sun/star/i18n/ScriptType.hpp>
122#include <com/sun/star/i18n/XBreakIterator.hpp>
125#include <tgrditem.hxx>
126#include <flddropdown.hxx>
127#include <chpfld.hxx>
128#include <fmthdft.hxx>
129#include <authfld.hxx>
130#include <dbfld.hxx>
131
132#include "sprmids.hxx"
133
134#include <fmtcntnt.hxx>
135#include "writerhelper.hxx"
136#include "writerwordglue.hxx"
137#include "wrtww8.hxx"
138#include "ww8par.hxx"
139#include "ww8attributeoutput.hxx"
140#include "fields.hxx"
142#include <unotools/fltrcfg.hxx>
143
144
145using ::editeng::SvxBorderLine;
146using namespace ::com::sun::star;
147using namespace nsSwDocInfoSubType;
148using namespace sw::util;
149using namespace sw::types;
150
151bool WW8Export::CollapseScriptsforWordOk( sal_uInt16 nScript, sal_uInt16 nWhich )
152{
153 bool bRet = true;
154 if ( nScript == i18n::ScriptType::ASIAN )
155 {
156 //for asian in ww8, there is only one fontsize
157 //and one fontstyle (posture/weight) for ww6
158 //there is the additional problem that there
159 //is only one font setting for all three scripts
160 switch ( nWhich )
161 {
165 bRet = false;
166 break;
173 default:
174 break;
175 }
176 }
177 else if ( nScript == i18n::ScriptType::COMPLEX )
178 {
179 //Complex is ok in ww8, but for ww6 there is only
180 //one font, one fontsize, one fontsize (weight/posture)
181 //and only one language
182 }
183 else
184 {
185 //for western in ww8, there is only one fontsize
186 //and one fontstyle (posture/weight) for ww6
187 //there is the additional problem that there
188 //is only one font setting for all three scripts
189 switch ( nWhich )
190 {
194 bRet = false;
195 break;
202 default:
203 break;
204 }
205 }
206 return bRet;
207}
208
209
210void MSWordExportBase::ExportPoolItemsToCHP( ww8::PoolItems &rItems, sal_uInt16 nScript, const SvxFontItem *pFont, bool bWriteCombChars )
211{
212 for ( const auto& rItem : rItems )
213 {
214 const SfxPoolItem *pItem = rItem.second;
215 sal_uInt16 nWhich = pItem->Which();
216 if ( ( isCHRATR( nWhich ) || isTXTATR( nWhich ) ) && CollapseScriptsforWordOk( nScript, nWhich ) )
217 {
218 //In the id definition, RES_TXTATR_INETFMT must precede RES_TXTATR_CHARFMT, so that link style can overwrite char style.
219 //and in #i24291# it describes "All we want to do is ensure for now is that if a charfmt exist in the character
220 //properties that it rises to the top and is exported first."
221 //In bug 119649, it is in such situation, so we need to ignore the link style when doing ms word filter exports and
222 //add the second judgement for #i24291# definition.
223 if (nWhich == RES_TXTATR_CHARFMT)
224 {
225 const SfxPoolItem* pINetItem = SearchPoolItems(rItems, RES_TXTATR_INETFMT);
226
227 if (pINetItem)
228 {
229 const SwFormatINetFormat& rINet = pINetItem->StaticWhichCast(RES_TXTATR_INETFMT);
230 const SwCharFormat* pINetFormat = GetSwCharFormat(rINet, m_rDoc);
231 if (!pINetFormat)
232 continue;
233
234 const SwCharFormat* pFormat = pItem->StaticWhichCast(RES_TXTATR_CHARFMT).GetCharFormat();
235 ww8::PoolItems aCharItems, aINetItems;
236 GetPoolItems(pFormat->GetAttrSet(), aCharItems, false);
237 GetPoolItems(pINetFormat->GetAttrSet(), aINetItems, false);
238 for (const auto& rCharItem : aCharItems)
239 {
240 const SfxPoolItem* pCharItem = rCharItem.second;
241 sal_uInt16 nCharWhich = pCharItem->Which();
242 if (!SearchPoolItems(aINetItems, nCharWhich) && !SearchPoolItems(rItems, nCharWhich))
243 AttrOutput().OutputItem(*pCharItem);
244 }
245 continue;
246 }
247 }
248
249 // tdf#38778 Fix output of the font in DOC run for fields
250 if (pFont &&
251 nWhich == RES_TXTATR_FIELD)
252 {
253 AttrOutput().OutputItem( *pFont );
254 }
255
256 // tdf#66401 For Combined Characters in docx, MS Word uses half the normal font-size for the field's
257 // font-size, but only for <w:sz>. Therefore, we check if we are currently writing a field of type
258 // Combined Characters and if so, we half the font size.
259 if (bWriteCombChars &&
260 nWhich == RES_CHRATR_FONTSIZE)
261 {
262 SvxFontHeightItem fontHeight(item_cast<SvxFontHeightItem>( *pItem ));
263 fontHeight.SetHeight( fontHeight.GetHeight() / 2 );
264
265 AttrOutput().OutputItem( fontHeight );
266 }
267 else if (nWhich == RES_CHRATR_COLOR)
268 {
269 const SvxColorItem& rColor = pItem->StaticWhichCast(RES_CHRATR_COLOR);
270 const SfxPoolItem* pBackgroundItem = SearchPoolItems(rItems, RES_CHRATR_BACKGROUND);
271 if (rColor.GetValue() == COL_AUTO && pBackgroundItem)
272 {
273 const SvxBrushItem& rBrushBackground = pBackgroundItem->StaticWhichCast(RES_CHRATR_BACKGROUND);
274 SvxColorItem aForeground(rBrushBackground.GetColor().IsDark() ? COL_WHITE : COL_BLACK, RES_CHRATR_COLOR);
275 AttrOutput().OutputItem(aForeground);
276 }
277 else
278 {
279 // default
280 AttrOutput().OutputItem( *pItem );
281 }
282 }
283 else if (nWhich == RES_CHRATR_HIGHLIGHT)
284 {
285 const SvxBrushItem& rBrush = pItem->StaticWhichCast(RES_CHRATR_HIGHLIGHT);
286 // The UI easily adds unnecessary highlights, so identify and avoid exporting those.
287 // Highlight is not valid in character styles, so must not check there.
288 // Check the (para) style hierarchy to find the nearest defined highlight.
289 const SfxPoolItem* pInherited = nullptr;
290 if ( auto pNd = dynamic_cast< const SwContentNode *>( m_pOutFormatNode ) ) //paragraph
291 pInherited = static_cast<SwTextFormatColl&>( pNd->GetAnyFormatColl() ).GetAttrSet().GetItem(nWhich);
292 else if ( m_bStyDef && m_pCurrentStyle && m_pCurrentStyle->DerivedFrom() ) //style
293 pInherited = &m_pCurrentStyle->DerivedFrom()->GetFormatAttr(nWhich);
294
295 // Ignore highlight if style already sets the same one.
296 // Also ignore a transparent highlight if there is no inherited highlight to cancel
297 if ( (pInherited && *pInherited != *pItem) || (!pInherited && rBrush.GetColor() != COL_TRANSPARENT) )
298 AttrOutput().OutputItem( *pItem );
299 }
300 else
301 {
302 AttrOutput().OutputItem( *pItem );
303 }
304 }
305 }
306}
307
308/*
309 * Output format as follows:
310 * - output the attributes; without parents!
311 */
312
313void MSWordExportBase::OutputItemSet( const SfxItemSet& rSet, bool bPapFormat, bool bChpFormat, sal_uInt16 nScript,
314 bool bExportParentItemSet )
315{
316 if( !(bExportParentItemSet || rSet.Count()) )
317 return;
318
319 m_pISet = &rSet; // for double attributes
320
321 // If frame dir is set, but not adjust, then force adjust as well
322 if ( bPapFormat && SfxItemState::SET == rSet.GetItemState( RES_FRAMEDIR, bExportParentItemSet ) )
323 {
324 // No explicit adjust set ?
325 if ( SfxItemState::SET != rSet.GetItemState( RES_PARATR_ADJUST, bExportParentItemSet ) )
326 {
327 const SvxAdjustItem* pItem = rSet.GetItem( RES_PARATR_ADJUST, bExportParentItemSet );
328 if ( nullptr != pItem )
329 {
330 // then set the adjust used by the parent format
331 AttrOutput().OutputItem( *pItem );
332 }
333 }
334 }
335
336 const SwNumRuleItem* pRuleItem;
337 if ( bPapFormat && (pRuleItem = rSet.GetItemIfSet( RES_PARATR_NUMRULE, bExportParentItemSet )) )
338 {
339 AttrOutput().OutputItem( *pRuleItem );
340
341 // switch off the numbering?
342 const SfxPoolItem* pLRItem;
343 if ( pRuleItem->GetValue().isEmpty() &&
344 SfxItemState::SET != rSet.GetItemState( RES_LR_SPACE, false) &&
345 (pLRItem = rSet.GetItemIfSet( RES_LR_SPACE )) )
346 {
347 // the set the LR-Space of the parentformat!
348 AttrOutput().OutputItem( *pLRItem );
349 }
350 }
351
352 ww8::PoolItems aItems;
353 GetPoolItems( rSet, aItems, bExportParentItemSet );
354 if ( bChpFormat )
355 ExportPoolItemsToCHP(aItems, nScript, nullptr);
356 if ( bPapFormat )
357 {
358 const bool bAlreadyOutputBrushItem = AttrOutput().MaybeOutputBrushItem(rSet);
359
360 for ( const auto& rItem : aItems )
361 {
362 const SfxPoolItem* pItem = rItem.second;
363 sal_uInt16 nWhich = pItem->Which();
364 // Handle fill attributes just like frame attributes for now.
365 if ( (nWhich >= RES_PARATR_BEGIN && nWhich < RES_FRMATR_END && nWhich != RES_PARATR_NUMRULE ) ||
366 (nWhich >= XATTR_FILL_FIRST && nWhich < XATTR_FILL_LAST))
367 AttrOutput().OutputItem( *pItem );
368 }
369
370 // Has to be called after RES_PARATR_GRABBAG is processed.
372 if (!bAlreadyOutputBrushItem && pFill
373 && (pFill->GetValue() == drawing::FillStyle_SOLID || pFill->GetValue() == drawing::FillStyle_NONE)
374 && !rSet.GetItem(RES_BACKGROUND, false))
375 {
376 const bool bFillStyleNone = pFill->GetValue() == drawing::FillStyle_NONE;
377 // No need to write out a NONE background if it can't inherit something else, or if it already inherits a NONE.
378 std::unique_ptr<SvxBrushItem> pInherited;
379 if (bFillStyleNone)
380 {
381 if ( auto pNd = dynamic_cast<const SwContentNode*>(m_pOutFormatNode)) //paragraph
382 pInherited = getSvxBrushItemFromSourceSet(static_cast<SwTextFormatColl&>(pNd->GetAnyFormatColl()).GetAttrSet(), RES_BACKGROUND);
383 else if (m_bStyDef && m_pCurrentStyle && m_pCurrentStyle->DerivedFrom()) //style
385 }
386 // Construct an SvxBrushItem, as expected by the exporters.
387 std::unique_ptr<SvxBrushItem> aBrush(getSvxBrushItemFromSourceSet(rSet, RES_BACKGROUND));
388 if (!bFillStyleNone || (pInherited && *pInherited != *aBrush))
389 AttrOutput().OutputItem(*aBrush);
390 }
391#if 0
392 else
393 {
394 // note: *does not work* due to undocumented Word behavior: must be before a:ln element at least
396 }
397#endif
398 }
399 m_pISet = nullptr; // for double attributes
400}
401
403{
404 //If the header/footer contains a chapter field
407}
408
410{
411 bool bRet = false;
412 if ( const SwNodeIndex* pSttIdx = rContent.GetContentIdx() )
413 {
414 SwNodeIndex aIdx( *pSttIdx, 1 );
415 SwNodeIndex aEnd( *pSttIdx->GetNode().EndOfSectionNode() );
416 SwNodeOffset nStart = aIdx.GetIndex();
417 SwNodeOffset nEnd = aEnd.GetIndex();
418 //If the header/footer contains a chapter field
419 bRet = std::any_of(m_aChapterFieldLocs.cbegin(), m_aChapterFieldLocs.cend(),
420 [nStart, nEnd](SwNodeOffset i) { return ( nStart <= i ) && ( i <= nEnd ); });
421 }
422 return bRet;
423}
424
426{
427 if ( m_aChapterFieldLocs.empty() )
428 return false;
429
430 const SwFrameFormat *pFormat = nullptr;
431
432 pFormat = rFormat.GetHeader().GetHeaderFormat();
433 if ( pFormat && ContentContainsChapterField( pFormat->GetContent() ) )
434 return true;
435
436 pFormat = rFormat.GetFooter().GetFooterFormat();
437 return pFormat && ContentContainsChapterField( pFormat->GetContent() );
438}
439
441{
442 bool bNewPageDesc = false;
443 const SwPageDesc* pCurrent = SwPageDesc::GetPageDescOfNode(rNd);
444 OSL_ENSURE(pCurrent && m_pCurrentPageDesc, "Not possible surely");
445 if (m_pCurrentPageDesc && pCurrent)
446 {
447 if (pCurrent != m_pCurrentPageDesc)
448 {
449 if (m_pCurrentPageDesc->GetFollow() != pCurrent)
450 bNewPageDesc = true;
451 else
452 {
453 const SwFrameFormat& rTitleFormat = m_pCurrentPageDesc->GetFirstMaster();
454 const SwFrameFormat& rFollowFormat = pCurrent->GetMaster();
455
456 bNewPageDesc = !IsPlausableSingleWordSection(rTitleFormat,
457 rFollowFormat);
458 }
459 m_pCurrentPageDesc = pCurrent;
460 }
461 else
462 {
463 const SwFrameFormat &rFormat = pCurrent->GetMaster();
464 bNewPageDesc = FormatHdFtContainsChapterField(rFormat);
465 }
466 }
467 return bNewPageDesc;
468}
469
480void MSWordExportBase::OutputSectionBreaks( const SfxItemSet *pSet, const SwNode& rNd, bool isCellOpen )
481{
483 return;
484
485 m_bBreakBefore = true;
486 bool bNewPageDesc = false;
487 const SwFormatPageDesc *pPgDesc=nullptr;
488 bool bExtraPageBreakBeforeSectionBreak = false;
489
490 //Output a sectionbreak if there's a new pagedescriptor. Otherwise output a
491 //pagebreak if there is a pagebreak here, unless the new page (follow
492 //style) is different to the current one, in which case plump for a
493 //section.
494 bool bBreakSet = false;
495
496 const SwPageDesc * pPageDesc = rNd.FindPageDesc();
497
498 // Even if m_pCurrentPageDesc != pPageDesc ,it might be because of the different header & footer types.
499 if (m_pCurrentPageDesc != pPageDesc)
500 {
501 if (isCellOpen && ( m_pCurrentPageDesc->GetName() != pPageDesc->GetName() ))
502 {
503 /*
504 * If Table cell is open and page header types are different
505 * set pSet to NULL as we don't want to add any section breaks inside a table.
506 */
507 pSet = nullptr;
508 }
510 {
511 bBreakSet = true;
512 bNewPageDesc = true;
513 m_pCurrentPageDesc = pPageDesc;
514 }
515 }
516
517 if ( pSet && pSet->Count() )
518 {
519 const SwFormatPageDesc * pItem = pSet->GetItemIfSet( RES_PAGEDESC, false );
520 if ( pItem && pItem->GetRegisteredIn() != nullptr)
521 {
522 bBreakSet = true;
523 // Avoid unnecessary section breaks if possible. LO can't notice identical
524 // sections during import, so minimize unnecessary duplication
525 // by substituting a simple page break when the resulting section is identical,
526 // unless this is needed to re-number the page.
527 if (!bNewPageDesc && !pItem->GetNumOffset() && m_pCurrentPageDesc
528 && m_pCurrentPageDesc->GetFollow() == pItem->GetPageDesc())
529 {
530 // A section break on the very first paragraph is ignored by LO/Word
531 // and should NOT be turned into a page break.
533 SwNodeIndex aStart(*aDocEnd.GetNode().StartOfSectionNode(), 2);
534 if (rNd.GetIndex() > aStart.GetNode().GetIndex())
535 AttrOutput().OutputItem(SvxFormatBreakItem(SvxBreak::PageBefore, RES_BREAK));
536 }
537 else
538 bNewPageDesc = true;
539
540 pPgDesc = pItem;
541 m_pCurrentPageDesc = pPgDesc->GetPageDesc();
542
543 // tdf#121666: nodes that have pagebreak + sectionbreak may need to export both breaks
544 // tested / implemented with docx format only.
545 // If other formats (rtf /doc) need similar fix, then that may can be done similar way.
546 if (SfxItemState::SET == pSet->GetItemState(RES_BREAK, false))
547 {
548 bExtraPageBreakBeforeSectionBreak = true;
549 }
550 }
551 else if ( const SvxFormatBreakItem* pBreak = pSet->GetItemIfSet( RES_BREAK, false ) )
552 {
553 // Word does not like hard break attributes in some table cells
554 bool bRemoveHardBreakInsideTable = false;
555 if ( m_bOutTable )
556 {
557 const SwTableNode* pTableNode = rNd.FindTableNode();
558 if ( pTableNode )
559 {
560 const SwTableBox* pBox = rNd.GetTableBox();
561 const SwTableLine* pLine = pBox ? pBox->GetUpper() : nullptr;
562 // but only for non-complex tables
563 if ( pLine && !pLine->GetUpper() )
564 {
565 // check if box is not first in that line:
566 if ( 0 < pLine->GetBoxPos( pBox ) && pBox->GetSttNd() )
567 {
568 bRemoveHardBreakInsideTable = true;
569 }
570 }
571 }
572 }
573 bBreakSet = true;
574
575 if ( !bRemoveHardBreakInsideTable )
576 {
577 OSL_ENSURE(m_pCurrentPageDesc, "should not be possible");
578 /*
579 If because of this pagebreak the page desc following the page
580 break is the follow style of the current page desc then output a
581 section break using that style instead. At least in those cases
582 we end up with the same style in word and writer, nothing can be
583 done when it happens when we get a new pagedesc because we
584 overflow from the first page style.
585 */
586 if ( m_pCurrentPageDesc )
587 {
588 // #i76301# - assure that there is a page break before set at the node.
589 if ( pBreak->GetBreak() == SvxBreak::PageBefore )
590 {
591 bNewPageDesc |= SetCurrentPageDescFromNode( rNd );
592 }
593 }
594 if ( !bNewPageDesc )
595 AttrOutput().OutputItem( *pBreak );
596 }
597 }
598 }
599
600 /*
601 #i9301#
602 No explicit page break, lets see if the style had one and we've moved to a
603 new page style because of it, if we have to then we take the opportunity to
604 set the equivalent word section here. We *could* do it for every paragraph
605 that moves onto a new page because of layout, but that would be insane.
606 */
607 bool bHackInBreak = false;
608 if ( !bBreakSet )
609 {
610 if ( const SwContentNode *pNd = rNd.GetContentNode() )
611 {
612 const SvxFormatBreakItem &rBreak = pNd->GetAttr( RES_BREAK );
613 if ( rBreak.GetBreak() == SvxBreak::PageBefore )
614 bHackInBreak = true;
615 else
616 { // Even a pagedesc item is set, the break item can be set 'NONE',
617 // but a pagedesc item is an implicit page break before...
618 const SwFormatPageDesc &rPageDesc = pNd->GetAttr( RES_PAGEDESC );
619 if ( rPageDesc.KnowsPageDesc() )
620 bHackInBreak = true;
621 }
622 }
623 }
624
625 if ( bHackInBreak )
626 {
627 OSL_ENSURE( m_pCurrentPageDesc, "should not be possible" );
628 if ( m_pCurrentPageDesc )
629 bNewPageDesc = SetCurrentPageDescFromNode( rNd );
630 }
631
632 if ( bNewPageDesc && m_pCurrentPageDesc )
633 {
634 PrepareNewPageDesc( pSet, rNd, pPgDesc, m_pCurrentPageDesc, bExtraPageBreakBeforeSectionBreak );
635 }
636 m_bBreakBefore = false;
637}
638
639// #i76300#
641{
642 bool bRet = false;
643
644 if ( pNd &&
647 {
648 PrepareNewPageDesc( pSet, *pNd, nullptr, m_pCurrentPageDesc->GetFollow() );
649 bRet = true;
650 }
651
652 return bRet;
653}
654
656{
657 const SwSectionFormat* pFormat = nullptr;
658 const SwSectionNode* pSect = rNd.FindSectionNode();
659 if ( pSect &&
661 {
662 pFormat = pSect->GetSection().GetFormat();
663 }
664
665 return pFormat;
666}
667
669{
670 const SwFormatLineNumber* pNItem = nullptr;
671 if ( pSet )
672 {
673 pNItem = & pSet->Get( RES_LINENUMBER );
674 }
675 else if ( const SwContentNode *pNd = rNd.GetContentNode() )
676 {
677 pNItem = &pNd->GetAttr( RES_LINENUMBER );
678 }
679
680 return pNItem? pNItem->GetStartValue() : 0;
681}
682
684 const SwNode& rNd,
685 const SwFormatPageDesc* pNewPgDescFormat,
686 const SwPageDesc* pNewPgDesc,
687 bool /*bExtraPageBreak*/ )
688{
689 // The PageDescs will only be inserted in WW8Writer::pSepx with the corresponding
690 // position by the occurrences of PageDesc attributes. The construction and
691 // output of the attributes and header/footer of the PageDesc are done
692 // after the main text and its attributes.
693
694 sal_uLong nFcPos = ReplaceCr( msword::PageBreak ); // Page/Section-Break
695
696 // actually nothing is outputted here, rather the arrays aCps, aSects
697 // accordingly completed
698 if ( !nFcPos )
699 return;
700
701 const SwSectionFormat* pFormat = GetSectionFormat( rNd );
702 const sal_uLong nLnNm = GetSectionLineNo( pSet, rNd );
703
704 OSL_ENSURE( pNewPgDescFormat || pNewPgDesc, "Neither page desc format nor page desc provided." );
705
706 if ( pNewPgDescFormat )
707 {
708 m_pSepx->AppendSep( Fc2Cp( nFcPos ), *pNewPgDescFormat, rNd, pFormat, nLnNm );
709 }
710 else if ( pNewPgDesc )
711 {
712 m_pSepx->AppendSep( Fc2Cp( nFcPos ), pNewPgDesc, rNd, pFormat, nLnNm );
713 }
714}
715
716void MSWordExportBase::CorrectTabStopInSet( SfxItemSet& rSet, sal_Int32 nAbsLeft )
717{
719 if (!pItem)
720 return;
721
722 // then it must be corrected for the output
723 SvxTabStopItem aTStop(*pItem);
724 for ( sal_uInt16 nCnt = 0; nCnt < aTStop.Count(); ++nCnt )
725 {
726 SvxTabStop& rTab = const_cast<SvxTabStop&>(aTStop[ nCnt ]);
727 if ( SvxTabAdjust::Default != rTab.GetAdjustment() &&
728 rTab.GetTabPos() >= nAbsLeft )
729 {
730 rTab.GetTabPos() -= nAbsLeft;
731 }
732 else
733 {
734 aTStop.Remove( nCnt );
735 --nCnt;
736 }
737 }
738 rSet.Put( aTStop );
739}
740
741sal_uInt8 WW8Export::GetNumId( sal_uInt16 eNumType )
742{
743 sal_uInt8 nRet = 0;
744 switch( eNumType )
745 {
747 case SVX_NUM_CHARS_UPPER_LETTER_N: nRet = 3; break;
749 case SVX_NUM_CHARS_LOWER_LETTER_N: nRet = 4; break;
750 case SVX_NUM_ROMAN_UPPER: nRet = 1; break;
751 case SVX_NUM_ROMAN_LOWER: nRet = 2; break;
752 case style::NumberingType::TEXT_NUMBER: nRet = 5; break;
753 case style::NumberingType::TEXT_CARDINAL: nRet = 6; break;
754 case style::NumberingType::TEXT_ORDINAL: nRet = 7; break;
755 case style::NumberingType::AIU_HALFWIDTH_JA: nRet = 12; break;
756 case style::NumberingType::IROHA_HALFWIDTH_JA: nRet = 13; break;
757 case style::NumberingType::FULLWIDTH_ARABIC: nRet = 14; break;
758 case style::NumberingType::NUMBER_TRADITIONAL_JA: nRet = 16; break;
759 case style::NumberingType::CIRCLE_NUMBER: nRet = 18; break;
760 case style::NumberingType::AIU_FULLWIDTH_JA: nRet = 20; break;
761 case style::NumberingType::IROHA_FULLWIDTH_JA: nRet = 21; break;
762
763 case SVX_NUM_BITMAP:
764 case SVX_NUM_CHAR_SPECIAL: nRet = 23; break;
765 case style::NumberingType::HANGUL_SYLLABLE_KO: nRet = 24; break;// ganada
766 case style::NumberingType::HANGUL_JAMO_KO: nRet = 25; break;// chosung
767 case style::NumberingType::HANGUL_CIRCLED_SYLLABLE_KO: nRet = 24; break;
768 case style::NumberingType::HANGUL_CIRCLED_JAMO_KO: nRet = 25; break;
769 case style::NumberingType::TIAN_GAN_ZH: nRet = 30; break;
770 case style::NumberingType::DI_ZI_ZH: nRet = 31; break;
771 case style::NumberingType::NUMBER_UPPER_ZH_TW: nRet = 34;break;
772 case style::NumberingType::NUMBER_UPPER_ZH: nRet = 38; break;
773 case style::NumberingType::NUMBER_DIGITAL_KO: nRet = 41; break;
774 case style::NumberingType::NUMBER_HANGUL_KO: nRet = 42; break;
775 case style::NumberingType::NUMBER_LEGAL_KO: nRet = 43; break;
776 case style::NumberingType::NUMBER_DIGITAL2_KO: nRet = 44; break;
777 case style::NumberingType::NUMBER_HEBREW: nRet = 45; break;
778 case style::NumberingType::CHARS_ARABIC: nRet = 46; break;
779 case style::NumberingType::CHARS_HEBREW: nRet = 47; break;
780 case style::NumberingType::CHARS_ARABIC_ABJAD: nRet = 48; break;
781 case style::NumberingType::CHARS_PERSIAN:
782 case style::NumberingType::CHARS_NEPALI: nRet = 49; break;
783 case style::NumberingType::CHARS_THAI: nRet = 53; break;
784 case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_N_RU:
785 case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_RU: nRet = 58; break;
786 case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_N_RU:
787 case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_RU: nRet = 59; break;
788 // nothing, WW does the same (undocumented)
789 case SVX_NUM_NUMBER_NONE: nRet = 0xff; break;
791 // 0x09, msonfcChiManSty
792 nRet = 9;
793 break;
795 // 0x16, msonfcArabicLZ
796 nRet = 22;
797 break;
798 }
799 return nRet;
800}
801
803{
804 // Handled by ParaOutlineLevel and ParaNumRule
805}
806
807// #i77805#
809{
810 bool bRet( false );
811
812 //If there is no numbering on this fmt, but its parent was outline
813 //numbered, then in writer this is no inheritied, but in word it would
814 //be, so we must export "no numbering" and "body level" to make word
815 //behave like writer (see #i25755)
816 if (SfxItemState::SET != rFormat.GetItemState(RES_PARATR_NUMRULE, false))
817 {
818 if (const SwFormat *pParent = rFormat.DerivedFrom())
819 {
820 if (static_cast<const SwTextFormatColl*>(pParent)->IsAssignedToListLevelOfOutlineStyle())
821 {
823 m_pO->push_back(sal_uInt8(9));
826
827 bRet = true;
828 }
829 }
830 }
831
832 return bRet;
833}
834
835void MSWordExportBase::OutputFormat( const SwFormat& rFormat, bool bPapFormat, bool bChpFormat, bool bFlyFormat )
836{
837 bool bCallOutSet = true;
839 m_pOutFormatNode = &rFormat;
840
841 switch( rFormat.Which() )
842 {
844 case RES_TXTFMTCOLL:
845 if( bPapFormat )
846 {
847 int nLvl = MAXLEVEL;
848
849 if (static_cast<const SwTextFormatColl&>(rFormat).IsAssignedToListLevelOfOutlineStyle())
850 nLvl = static_cast<const SwTextFormatColl&>(rFormat).GetAssignedOutlineStyleLevel();
851
852 if (nLvl >= 0 && nLvl < MAXLEVEL)
853 {
854 //if outline numbered
855 // if Write StyleDefinition then write the OutlineRule
856 const SwNumFormat& rNFormat = m_rDoc.GetOutlineNumRule()->Get( o3tl::narrowing<sal_uInt16>( nLvl ) );
857 if ( m_bStyDef )
858 AttrOutput().OutlineNumbering(static_cast<sal_uInt8>(nLvl));
859
860 if ( rNFormat.GetPositionAndSpaceMode() ==
862 rNFormat.GetAbsLSpace() )
863 {
864 SfxItemSet aSet( rFormat.GetAttrSet() );
866
867 aLR.SetTextLeft( aLR.GetTextLeft() + rNFormat.GetAbsLSpace() );
869
870 aSet.Put( aLR );
871 CorrectTabStopInSet( aSet, rNFormat.GetAbsLSpace() );
872 OutputItemSet( aSet, bPapFormat, bChpFormat,
873 i18n::ScriptType::LATIN, m_bExportModeRTF);
874 bCallOutSet = false;
875 }
876 }
877 else
878 {
879 //otherwise we might have to remove outline numbering from
880 //what gets exported if the parent style was outline numbered
881 // #i77805#
882 // If inherited outline numbering is suppress, the left/right
883 // margins has to be exported explicitly.
885 {
886 SfxItemSet aSet( rFormat.GetAttrSet() );
887 const SvxLRSpaceItem& aLR = aSet.Get(RES_LR_SPACE);
888 aSet.Put( aLR );
889 OutputItemSet( aSet, bPapFormat, bChpFormat,
890 css::i18n::ScriptType::LATIN, m_bExportModeRTF);
891 bCallOutSet = false;
892 }
893 }
894 }
895 break;
896
897 case RES_CHRFMT:
898 break;
899 case RES_FLYFRMFMT:
900 if (bFlyFormat)
901 {
902 OSL_ENSURE(m_pParentFrame, "No parent frame, all broken");
903
904 if (m_pParentFrame)
905 {
906 const SwFrameFormat &rFrameFormat = m_pParentFrame->GetFrameFormat();
907
910 aSet.Set(rFrameFormat.GetAttrSet());
911
912 // Fly as character becomes a paragraph bound
913 // now set the distance to paragraph margin
914 if (m_pFlyOffset)
915 {
916 aSet.Put(SwFormatHoriOrient(m_pFlyOffset->X()));
917 aSet.Put(SwFormatVertOrient(m_pFlyOffset->Y()));
918 SwFormatAnchor aAnchor(rFrameFormat.GetAnchor());
919 aAnchor.SetType(m_eNewAnchorType);
920 aSet.Put(aAnchor);
921 }
922
923 if (SfxItemState::SET != aSet.GetItemState(RES_SURROUND))
924 aSet.Put(SwFormatSurround(css::text::WrapTextMode_NONE));
925
926 const XFillStyleItem* pXFillStyleItem(rFrameFormat.GetAttrSet().GetItem<XFillStyleItem>(XATTR_FILLSTYLE));
927 if (pXFillStyleItem)
928 {
929 switch (pXFillStyleItem->GetValue())
930 {
931 case drawing::FillStyle_NONE:
932 break;
933 case drawing::FillStyle_SOLID:
934 {
935 // Construct an SvxBrushItem, as expected by the exporters.
936 aSet.Put(getSvxBrushItemFromSourceSet(rFrameFormat.GetAttrSet(), RES_BACKGROUND));
937 break;
938 }
939 default:
940 break;
941 }
942 }
943
944 m_bOutFlyFrameAttrs = true;
945 //script doesn't matter if not exporting chp
946 OutputItemSet(aSet, true, false,
947 i18n::ScriptType::LATIN, m_bExportModeRTF);
948 m_bOutFlyFrameAttrs = false;
949
950 bCallOutSet = false;
951 }
952 }
953 break;
954 case RES_FRMFMT:
955 break;
956 default:
957 OSL_ENSURE( false, "Which format is exported here?" );
958 break;
959 }
960
961 if( bCallOutSet )
962 OutputItemSet( rFormat.GetAttrSet(), bPapFormat, bChpFormat,
963 i18n::ScriptType::LATIN, m_bExportModeRTF);
964 m_pOutFormatNode = pOldMod;
965}
966
967bool MSWordExportBase::HasRefToAttr(const OUString& rName)
968{
970 std::vector<SwGetRefField*> vpRFields;
971 pType->GatherRefFields(vpRFields, REF_SETREFATTR);
972 return std::any_of(vpRFields.begin(), vpRFields.end(),
973 [rName](SwGetRefField* pF) { return rName == pF->GetSetRefName(); });
974}
975
976bool MSWordExportBase::HasRefToFootOrEndnote(const bool isEndNote, const sal_uInt16 nSeqNo)
977{
979 std::vector<SwGetRefField*> vpRFields;
980 pType->GatherRefFields(vpRFields, isEndNote ? REF_ENDNOTE : REF_FOOTNOTE);
981 return std::any_of(vpRFields.begin(), vpRFields.end(),
982 [nSeqNo](SwGetRefField* pF) { return nSeqNo == pF->GetSeqNo(); });
983}
984
985OUString MSWordExportBase::GetBookmarkName( sal_uInt16 nTyp, const OUString* pName, sal_uInt16 nSeqNo )
986{
987 OUString sRet;
988 switch ( nTyp )
989 {
990 case REF_SETREFATTR:
991 if ( pName )
992 {
993 sRet = "Ref_" + *pName;
994 }
995 break;
996 case REF_SEQUENCEFLD:
997 {
998 assert(pName);
999 sRet = "Ref_" + *pName;
1000 break;
1001 }
1002 case REF_BOOKMARK:
1003 if ( pName )
1004 sRet = *pName;
1005 break;
1006 case REF_OUTLINE:
1007 break; // ???
1008 case REF_FOOTNOTE:
1009 sRet = "_RefF" + OUString::number( nSeqNo );
1010 break;
1011 case REF_ENDNOTE:
1012 sRet = "_RefE" + OUString::number( nSeqNo );
1013 break;
1014 }
1015 return BookmarkToWord( sRet ); // #i43956# - encode bookmark accordingly
1016}
1017
1018/* File CHRATR.HXX: */
1019void WW8AttributeOutput::RTLAndCJKState( bool bIsRTL, sal_uInt16 nScript )
1020{
1021 if (bIsRTL)
1022 {
1024 {
1026 m_rWW8Export.m_pO->push_back( sal_uInt8(1) );
1027 }
1028 }
1029
1030 // #i46087# patch from james_clark; complex texts needs the undocumented SPRM CComplexScript with param 0x81.
1031 if (nScript == i18n::ScriptType::COMPLEX && !bIsRTL)
1032 {
1034 m_rWW8Export.m_pO->push_back( sal_uInt8(0x81) );
1035 m_rWW8Export.m_pDop->bUseThaiLineBreakingRules = true;
1036 }
1037}
1038
1040{
1041 m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell() - (mbOnTOXEnding?2:0), m_rWW8Export.m_pO->size(), m_rWW8Export.m_pO->data() );
1042 mbOnTOXEnding = false;
1043 m_rWW8Export.m_pO->clear();
1044
1045 if ( pTextNodeInfoInner )
1046 {
1047 if ( pTextNodeInfoInner->isEndOfLine() )
1048 {
1049 TableRowEnd( pTextNodeInfoInner->getDepth() );
1050
1051 SVBT16 nSty;
1052 ShortToSVBT16( 0, nSty );
1053 m_rWW8Export.m_pO->insert( m_rWW8Export.m_pO->end(), nSty, nSty+2 ); // Style #
1054 TableInfoRow( pTextNodeInfoInner );
1055 m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), m_rWW8Export.m_pO->size(), m_rWW8Export.m_pO->data());
1056 m_rWW8Export.m_pO->clear();
1057 //For Bug 119650, should break the properties of CHP PLC after a paragraph end.
1058 m_rWW8Export.m_pChpPlc->AppendFkpEntry(m_rWW8Export.Strm().Tell(), m_rWW8Export.m_pO->size(), m_rWW8Export.m_pO->data());
1059 }
1060 }
1061
1062 // Clear bookmarks of the current paragraph
1065}
1066
1068{
1069 WW8_WrPlcField* pCurrentFields = m_rWW8Export.CurrentFieldPlc();
1070 m_nFieldResults = pCurrentFields ? pCurrentFields->ResultCount() : 0;
1071}
1072
1073void WW8AttributeOutput::StartRun( const SwRedlineData* pRedlineData, sal_Int32 nPos, bool /*bSingleEmptyRun*/ )
1074{
1075 if (pRedlineData)
1076 {
1077 const OUString &rComment = pRedlineData->GetComment();
1078 //Only possible to export to main text
1079 if (!rComment.isEmpty() && (m_rWW8Export.m_nTextTyp == TXT_MAINTEXT))
1080 {
1081 if (m_rWW8Export.m_pAtn->IsNewRedlineComment(pRedlineData))
1082 {
1083 m_rWW8Export.m_pAtn->Append( m_rWW8Export.Fc2Cp( m_rWW8Export.Strm().Tell() ), pRedlineData );
1085 }
1086 }
1087 }
1088
1090 auto aRange = m_aBookmarksOfParagraphStart.equal_range(nPos);
1091 for( auto aIter = aRange.first; aIter != aRange.second; ++aIter)
1092 {
1093 GetExport().AppendBookmark(BookmarkToWord(aIter->second));
1094 }
1095}
1096
1098{
1099 mbOnTOXEnding = true;
1100}
1101
1102void WW8AttributeOutput::EndRun( const SwTextNode* /*pNode*/, sal_Int32 nPos, sal_Int32 /*nLen*/, bool bLastRun )
1103{
1105 auto aRange = m_aBookmarksOfParagraphEnd.equal_range(nPos);
1106 for( auto aIter = aRange.first; aIter != aRange.second; ++aIter)
1107 {
1108 if(bLastRun)
1110 else
1111 GetExport().AppendBookmark(BookmarkToWord(aIter->second));
1112 }
1113}
1114
1116{
1117 Redline( pRedlineData );
1118
1119 WW8_WrPlcField* pCurrentFields = m_rWW8Export.CurrentFieldPlc();
1120 sal_uInt16 nNewFieldResults = pCurrentFields ? pCurrentFields->ResultCount() : 0;
1121
1122 bool bExportedFieldResult = ( m_nFieldResults != nNewFieldResults );
1123
1124 // If we have exported a field result, then we will have been forced to
1125 // split up the text into a 0x13, 0x14, <result> 0x15 sequence with the
1126 // properties forced out at the end of the result, so the 0x15 itself
1127 // should remain clean of all other attributes to avoid #iXXXXX#
1128 if ( !bExportedFieldResult )
1129 {
1130 m_rWW8Export.m_pChpPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(),
1131 m_rWW8Export.m_pO->size(), m_rWW8Export.m_pO->data() );
1132 }
1133 m_rWW8Export.m_pO->clear();
1134}
1135
1136void WW8AttributeOutput::RunText( const OUString& rText, rtl_TextEncoding eCharSet )
1137{
1138 RawText(rText, eCharSet);
1139}
1140
1141void WW8AttributeOutput::RawText(const OUString& rText, rtl_TextEncoding)
1142{
1143 m_rWW8Export.OutSwString(rText, 0, rText.getLength());
1144}
1145
1147{
1148 if (!m_rWW8Export.m_pO->empty() || bForce)
1149 {
1150 m_rWW8Export.m_pChpPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(),
1151 m_rWW8Export.m_pO->size(), m_rWW8Export.m_pO->data() );
1152 m_rWW8Export.m_pO->clear();
1153 }
1154}
1155
1156void WW8AttributeOutput::ParagraphStyle( sal_uInt16 nStyle )
1157{
1158 OSL_ENSURE( m_rWW8Export.m_pO->empty(), " pO is not empty at line end" );
1159
1160 SVBT16 nSty;
1161 ShortToSVBT16( nStyle, nSty );
1162 m_rWW8Export.m_pO->insert( m_rWW8Export.m_pO->end(), nSty, nSty+2 ); // style #
1163}
1164
1166{
1168
1169 m_rWW8Export.m_pO->push_back( bVal ? 1 : 0 );
1170}
1171
1173{
1174 OSL_ENSURE( nId <= 1, "out of range" );
1175 if (nId > 1)
1176 return;
1177
1179 m_rWW8Export.m_pO->push_back( bVal ? 1 : 0 );
1180}
1181
1183{
1184 sal_uInt16 nFontID = m_rWW8Export.GetId( rFont );
1185
1187 m_rWW8Export.InsUInt16( nFontID );
1189
1190 m_rWW8Export.InsUInt16( nFontID );
1191}
1192
1194{
1195 sal_uInt16 nFontID = m_rWW8Export.GetId( rFont );
1197 m_rWW8Export.InsUInt16( nFontID );
1198}
1199
1201{
1202 sal_uInt16 nFontID = m_rWW8Export.GetId( rFont );
1204 m_rWW8Export.InsUInt16( nFontID );
1205}
1206
1208{
1210}
1211
1213{
1214 OutputWW8AttributeCTL( 1, ITALIC_NONE != rPosture.GetPosture() );
1215}
1216
1218{
1219 OutputWW8Attribute( 1, ITALIC_NONE != rPosture.GetPosture() );
1220}
1221
1223{
1224 OutputWW8Attribute( 0, WEIGHT_BOLD == rWeight.GetWeight() );
1225}
1226
1227// Shadowed and Contour are not in WW-UI. JP: ??
1229{
1230 OutputWW8Attribute( 3, rContour.GetValue() );
1231}
1232
1234{
1235 OutputWW8Attribute( 4, rShadow.GetValue() );
1236}
1237
1239{
1241
1242 m_rWW8Export.InsUInt16( rKerning.GetValue() );
1243}
1244
1246{
1248
1249 m_rWW8Export.InsUInt16( rAutoKern.GetValue() ? 2 : 0 );
1250}
1251
1253{
1255 // At the moment the only animated text effect we support is blinking
1256 m_rWW8Export.m_pO->push_back( rBlink.GetValue() ? 2 : 0 );
1257}
1258
1260{
1261 FontStrikeout eSt = rCrossed.GetStrikeout();
1262 if ( STRIKEOUT_DOUBLE == eSt )
1263 {
1264 OutputWW8Attribute( 8, true );
1265 return;
1266 }
1267 if ( STRIKEOUT_NONE != eSt )
1268 {
1269 OutputWW8Attribute( 2, true );
1270 return;
1271 }
1272
1273 // otherwise both off
1274 OutputWW8Attribute( 8, false );
1275 OutputWW8Attribute( 2, false );
1276}
1277
1279{
1280 SvxCaseMap eSt = rCaseMap.GetValue();
1281 switch ( eSt )
1282 {
1283 case SvxCaseMap::SmallCaps:
1284 OutputWW8Attribute( 5, true );
1285 return;
1286 case SvxCaseMap::Uppercase:
1287 OutputWW8Attribute( 6, true );
1288 return;
1289 case SvxCaseMap::Capitalize:
1290 // no such feature in word
1291 break;
1292 default:
1293 // otherwise both off
1294 OutputWW8Attribute( 5, false );
1295 OutputWW8Attribute( 6, false );
1296 return;
1297 }
1298}
1299
1301{
1302 OutputWW8Attribute( 7, rHidden.GetValue() );
1303}
1304
1305void WW8AttributeOutput::CharBorder( const SvxBorderLine* pAllBorder, const sal_uInt16 /*nDist*/, const bool bShadow )
1306{
1308}
1309
1311{
1314 m_rWW8Export.m_pO->push_back( nColor );
1315}
1316
1318{
1320
1321 // FIXME: this should likely be a StaticWhichCast(), but some we put something dirty in RES_CHRATR_WORDLINEMODE apparently
1322 const auto pItem = m_rWW8Export.HasItem(RES_CHRATR_WORDLINEMODE);
1323 bool bWord = false;
1324 if(pItem)
1325 {
1326 const auto pWordline = pItem->DynamicWhichCast(RES_CHRATR_WORDLINEMODE);
1327 if(pWordline)
1328 bWord = pWordline->GetValue();
1329 else
1330 SAL_WARN("sw.ww8", "m_rWW8Export has an RES_CHRATR_WORDLINEMODE item, but it's of the wrong type.");
1331 }
1332
1333 // WW95 - parameters: 0 = none, 1 = single, 2 = by Word,
1334 // 3 = double, 4 = dotted, 5 = hidden
1335 // WW97 - additional parameters:
1336 // 6 = thick, 7 = dash, 8 = dot(not used)
1337 // 9 = dotdash 10 = dotdotdash, 11 = wave
1338 sal_uInt8 b = 0;
1339 switch ( rUnderline.GetLineStyle() )
1340 {
1341 case LINESTYLE_SINGLE:
1342 b = bWord ? 2 : 1;
1343 break;
1344 case LINESTYLE_BOLD:
1345 b = 6;
1346 break;
1347 case LINESTYLE_DOUBLE:
1348 b = 3;
1349 break;
1350 case LINESTYLE_DOTTED:
1351 b = 4;
1352 break;
1353 case LINESTYLE_DASH:
1354 b = 7;
1355 break;
1356 case LINESTYLE_DASHDOT:
1357 b = 9;
1358 break;
1360 b = 10;
1361 break;
1362 case LINESTYLE_WAVE:
1363 b = 11;
1364 break;
1365 // new in WW2000
1367 b = 20;
1368 break;
1369 case LINESTYLE_BOLDDASH:
1370 b = 23;
1371 break;
1372 case LINESTYLE_LONGDASH:
1373 b = 39;
1374 break;
1376 b = 55;
1377 break;
1379 b = 25;
1380 break;
1382 b = 26;
1383 break;
1384 case LINESTYLE_BOLDWAVE:
1385 b = 27;
1386 break;
1388 b = 43;
1389 break;
1390 case LINESTYLE_NONE:
1391 b = 0;
1392 break;
1393 default:
1394 OSL_ENSURE( rUnderline.GetLineStyle() == LINESTYLE_NONE, "Unhandled underline type" );
1395 break;
1396 }
1397
1398 m_rWW8Export.m_pO->push_back( b );
1399 Color aColor = rUnderline.GetColor();
1400 if( aColor != COL_TRANSPARENT )
1401 {
1403
1405 }
1406}
1407
1409{
1410 sal_uInt16 nId = 0;
1411 switch ( rLanguage.Which() )
1412 {
1415 break;
1418 break;
1421 break;
1422 }
1423
1424 if ( !nId )
1425 return;
1426
1427 // use sprmCRgLid0_80 rather than sprmCLid
1429 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rLanguage.GetLanguage()) );
1430
1431 // Word 2000 and above apparently require both old and new versions of
1432 // these sprms to be set, without it spellchecking doesn't work
1434 {
1436 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rLanguage.GetLanguage()) );
1437 }
1438 else if ( nId == NS_sprm::CRgLid1_80::val )
1439 {
1441 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rLanguage.GetLanguage()) );
1442 }
1443}
1444
1446{
1447 sal_uInt8 b = 0xFF;
1448 short nEsc = rEscapement.GetEsc(), nProp = rEscapement.GetProportionalHeight();
1449 if ( !nEsc )
1450 {
1451 b = 0;
1452 nEsc = 0;
1453 nProp = 100;
1454 }
1455 else if ( DFLT_ESC_PROP == nProp || nProp < 1 || nProp > 100 )
1456 {
1457 if ( DFLT_ESC_SUB == nEsc || DFLT_ESC_AUTO_SUB == nEsc )
1458 b = 2;
1459 else if ( DFLT_ESC_SUPER == nEsc || DFLT_ESC_AUTO_SUPER == nEsc )
1460 b = 1;
1461 }
1462 else if ( DFLT_ESC_AUTO_SUPER == nEsc )
1463 {
1464 // Raised by the differences between the ascenders (ascent = baseline to top of highest letter).
1465 // The ascent is generally about 80% of the total font height.
1466 // That is why DFLT_ESC_PROP (58) leads to 33% (DFLT_ESC_SUPER)
1467 nEsc = .8 * (100 - nProp);
1468 }
1469 else if ( DFLT_ESC_AUTO_SUB == nEsc )
1470 {
1471 // Lowered by the differences between the descenders (descent = baseline to bottom of lowest letter).
1472 // The descent is generally about 20% of the total font height.
1473 // That is why DFLT_ESC_PROP (58) leads to 8% (DFLT_ESC_SUB)
1474 nEsc = .2 * -(100 - nProp);
1475 }
1476
1477 if ( 0xFF != b )
1478 {
1480
1481 m_rWW8Export.m_pO->push_back( b );
1482 }
1483
1484 if ( 0 != b && 0xFF != b )
1485 return;
1486
1487 double fHeight = m_rWW8Export.GetItem( RES_CHRATR_FONTSIZE ).GetHeight();
1489
1490 m_rWW8Export.InsUInt16(static_cast<short>( round(fHeight * nEsc / 1000) ));
1491
1492 if( 100 != nProp || !b )
1493 {
1495 m_rWW8Export.InsUInt16(msword_cast<sal_uInt16>( round(fHeight * nProp / 1000) ));
1496 }
1497}
1498
1500{
1501 sal_uInt16 nId = 0;
1502 switch ( rHeight.Which() )
1503 {
1507 break;
1510 break;
1511 }
1512
1513 if ( nId )
1514 {
1516
1517 m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>(( rHeight.GetHeight() + 5 ) / 10 ) );
1518 }
1519}
1520
1522{
1524 m_rWW8Export.InsUInt16( rScaleWidth.GetValue() );
1525}
1526
1528{
1529 sal_uInt16 nId;
1530 switch ( rRelief.GetValue() )
1531 {
1532 case FontRelief::Embossed: nId = NS_sprm::CFEmboss::val; break;
1533 case FontRelief::Engraved: nId = NS_sprm::CFImprint::val; break;
1534 default: nId = 0; break;
1535 }
1536
1537 if( nId )
1538 {
1540 m_rWW8Export.m_pO->push_back( sal_uInt8(0x81) );
1541 }
1542 else
1543 {
1544 // switch both flags off
1546 m_rWW8Export.m_pO->push_back( sal_uInt8(0x0) );
1548 m_rWW8Export.m_pO->push_back( sal_uInt8(0x0) );
1549 }
1550}
1551
1553{
1554 const SfxInt16Item& rAttr = static_cast<const SfxInt16Item&>(rHt);
1555 if( rAttr.GetValue() == 1 )
1556 {
1557 m_rWW8Export.InsUInt16(0x85a);
1558 m_rWW8Export.m_pO->push_back(sal_uInt8(1));
1559 }
1560}
1561
1563{
1564 const SfxInt16Item& rAttr = static_cast<const SfxInt16Item&>(rHt);
1565 m_rWW8Export.InsUInt16(0x286F);
1566 m_rWW8Export.m_pO->push_back(static_cast<sal_uInt8>(rAttr.GetValue()));
1567}
1568
1570{
1571 // #i28331# - check that a Value is set
1572 if ( !rRotate.GetValue() )
1573 return;
1574
1575 if (m_rWW8Export.IsInTable())
1576 return;
1577
1578 // #i36867 In word the text in a table is rotated via the TC or NS_sprm::TTextFlow::val
1579 // This means you can only rotate all or none of the text adding NS_sprm::CFELayout::val
1580 // here corrupts the table, hence !m_rWW8Export.bIsInTable
1581
1583 m_rWW8Export.m_pO->push_back( sal_uInt8(0x06) ); //len 6
1584 m_rWW8Export.m_pO->push_back( sal_uInt8(0x01) );
1585
1586 m_rWW8Export.InsUInt16( rRotate.IsFitToLine() ? 1 : 0 );
1587 static const sal_uInt8 aZeroArr[ 3 ] = { 0, 0, 0 };
1588 m_rWW8Export.m_pO->insert( m_rWW8Export.m_pO->end(), aZeroArr, aZeroArr+3);
1589}
1590
1592{
1593 sal_uInt8 nVal;
1594 const FontEmphasisMark v = rEmphasisMark.GetEmphasisMark();
1595 if (v == FontEmphasisMark::NONE)
1596 nVal = 0;
1597 else if (v == (FontEmphasisMark::Accent | FontEmphasisMark::PosAbove))
1598 nVal = 2;
1599 else if (v == (FontEmphasisMark::Circle | FontEmphasisMark::PosAbove))
1600 nVal = 3;
1601 else if (v == (FontEmphasisMark::Dot | FontEmphasisMark::PosBelow))
1602 nVal = 4;
1603 else
1604 // case 1:
1605 nVal = 1;
1606
1608 m_rWW8Export.m_pO->push_back( nVal );
1609}
1610
1619bool WW8Export::TransBrush(const Color& rCol, WW8_SHD& rShd)
1620{
1621 if( rCol.IsTransparent() )
1622 rShd = WW8_SHD(); // all zeros: transparent
1623 else
1624 {
1625 rShd.SetFore( 0);
1626 rShd.SetBack( msfilter::util::TransColToIco( rCol ) );
1627 rShd.SetStyle( 0 );
1628 }
1629 return !rCol.IsTransparent();
1630}
1631
1632static sal_uInt32 SuitableBGColor(Color nIn)
1633{
1634 if (nIn == COL_AUTO)
1635 return 0xFF000000;
1636 return wwUtility::RGBToBGR(nIn);
1637}
1638
1640{
1642
1644 m_rWW8Export.m_pO->push_back( nColor );
1645
1646 if (nColor)
1647 {
1650 }
1651}
1652
1654{
1655 WW8_SHD aSHD;
1656
1657 WW8Export::TransBrush( rBrush.GetColor(), aSHD );
1658 // sprmCShd80
1661
1662 //Quite a few unknowns, some might be transparency or something
1663 //of that nature...
1665 m_rWW8Export.m_pO->push_back( 10 );
1666 m_rWW8Export.InsUInt32( 0xFF000000 );
1668 m_rWW8Export.InsUInt16( 0x0000);
1669}
1670
1671namespace sw { namespace util {
1672
1674{
1675 if (rINet.GetValue().isEmpty())
1676 return nullptr;
1677
1678 const sal_uInt16 nId = rINet.GetINetFormatId();
1679 const OUString& rStr = rINet.GetINetFormat();
1680 if (rStr.isEmpty())
1681 {
1682 OSL_ENSURE( false, "WW8AttributeOutput::TextINetFormat(..) - missing unvisited character format at hyperlink attribute" );
1683 }
1684
1685 return IsPoolUserFormat( nId )
1686 ? rDoc.FindCharFormatByName( rStr )
1688}
1689
1690} }
1691
1693{
1694 const SwCharFormat* pFormat = GetSwCharFormat(rINet, m_rWW8Export.m_rDoc);
1695 if (!pFormat)
1696 return;
1697
1699
1701}
1702
1703// #i43956# - add optional parameter <pLinkStr>
1704// It's needed to write the hyperlink data for a certain cross-reference
1705// - it contains the name of the link target, which is a bookmark.
1706// add optional parameter <bIncludeEmptyPicLocation>
1707// It is needed to write an empty picture location for page number field separators
1709 OUString const * pLinkStr,
1710 bool bIncludeEmptyPicLocation = false )
1711{
1712 ww::bytes aItems;
1713 rWrt.GetCurrentItems(aItems);
1714
1715 if (c == 0x13)
1716 rWrt.m_pChpPlc->AppendFkpEntry(rWrt.Strm().Tell());
1717 else
1718 rWrt.m_pChpPlc->AppendFkpEntry(rWrt.Strm().Tell(), aItems.size(), aItems.data());
1719
1720 rWrt.WriteChar(c);
1721
1722 // store empty sprmCPicLocation for field separator
1723 if ( bIncludeEmptyPicLocation &&
1724 ( c == 0x13 || c == 0x14 || c == 0x15 ) )
1725 {
1727 SwWW8Writer::InsUInt32( aItems, 0x00000000 );
1728 }
1729
1730 // #i43956# - write hyperlink data and attributes
1731 if ( c == 0x01 && pLinkStr)
1732 {
1733 // write hyperlink data to data stream
1734 SvStream& rStrm = *rWrt.m_pDataStrm;
1735 // position of hyperlink data
1736 const sal_uInt32 nLinkPosInDataStrm = rStrm.Tell();
1737 // write empty header
1738 const sal_uInt16 nEmptyHdrLen = 0x44;
1739 sal_uInt8 aEmptyHeader[ nEmptyHdrLen ] = { 0 };
1740 aEmptyHeader[ 4 ] = 0x44;
1741 rStrm.WriteBytes( aEmptyHeader, nEmptyHdrLen );
1742 // writer fixed header
1743 const sal_uInt16 nFixHdrLen = 0x19;
1744 sal_uInt8 const aFixHeader[ nFixHdrLen ] =
1745 {
1746 0x08, 0xD0, 0xC9, 0xEA, 0x79, 0xF9, 0xBA, 0xCE,
1747 0x11, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9,
1748 0x0B, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
1749 0x00,
1750 };
1751 rStrm.WriteBytes( aFixHeader, nFixHdrLen );
1752 // write reference string including length+1
1753 sal_uInt32 nStrLen( pLinkStr->getLength() + 1 );
1754 SwWW8Writer::WriteLong( rStrm, nStrLen );
1755 SwWW8Writer::WriteString16( rStrm, *pLinkStr, false );
1756 // write additional two NULL Bytes
1758 // write length of hyperlink data
1759 const sal_uInt32 nCurrPos = rStrm.Tell();
1760 rStrm.Seek( nLinkPosInDataStrm );
1761 rStrm.WriteUInt32(nCurrPos - nLinkPosInDataStrm);
1762 rStrm.Seek( nCurrPos );
1763
1764 // write attributes of hyperlink character 0x01
1766 aItems.push_back( sal_uInt8(0x81) );
1768 SwWW8Writer::InsUInt32( aItems, nLinkPosInDataStrm );
1770 aItems.push_back( sal_uInt8(0x01) );
1771 }
1772
1773 //Technically we should probably Remove all attribs
1774 //here for the 0x13, 0x14, 0x15, but our import
1775 //is slightly lacking
1776 //aItems.Remove(0, aItems.Count());
1777 // fSpec-Attribute true
1779 aItems.push_back( 1 );
1780
1781 rWrt.m_pChpPlc->AppendFkpEntry(rWrt.Strm().Tell(), aItems.size(), aItems.data());
1782}
1783
1784static OUString lcl_GetExpandedField(const SwField &rField)
1785{
1786 //replace LF 0x0A with VT 0x0B
1787 return rField.ExpandField(true, nullptr).replace(0x0A, 0x0B);
1788}
1789
1791{
1792 WW8_WrPlcField* pFieldP = nullptr;
1793 switch (m_nTextTyp)
1794 {
1795 case TXT_MAINTEXT:
1796 pFieldP = m_pFieldMain.get();
1797 break;
1798 case TXT_HDFT:
1799 pFieldP = m_pFieldHdFt.get();
1800 break;
1801 case TXT_FTN:
1802 pFieldP = m_pFieldFootnote.get();
1803 break;
1804 case TXT_EDN:
1805 pFieldP = m_pFieldEdn.get();
1806 break;
1807 case TXT_ATN:
1808 pFieldP = m_pFieldAtn.get();
1809 break;
1810 case TXT_TXTBOX:
1811 pFieldP = m_pFieldTextBxs.get();
1812 break;
1813 case TXT_HFTXTBOX:
1814 pFieldP = m_pFieldHFTextBxs.get();
1815 break;
1816 default:
1817 OSL_ENSURE( false, "what type of SubDoc is that?" );
1818 }
1819 return pFieldP;
1820}
1821
1822void WW8Export::OutputField( const SwField* pField, ww::eField eFieldType,
1823 const OUString& rFieldCmd, FieldFlags nMode )
1824{
1825 OUString sFieldCmd(rFieldCmd);
1826 switch (eFieldType)
1827 {
1828 // map fields that are not supported in WW8 as of Word 2003
1829 case ww::eBIBLIOGRAPHY:
1830 eFieldType = ww::eQUOTE;
1831 assert(rFieldCmd == FieldString(ww::eBIBLIOGRAPHY));
1832 sFieldCmd = FieldString(ww::eQUOTE);
1833 break;
1834 case ww::eCITATION:
1835 eFieldType = ww::eQUOTE;
1836 assert(o3tl::starts_with(o3tl::trim(rFieldCmd), u"CITATION"));
1837 sFieldCmd = rFieldCmd.replaceFirst(FieldString(ww::eCITATION),
1839 break;
1840 default:
1841 break;
1842 }
1843
1844 assert(eFieldType <= 0x5F); // 95 is the highest documented one
1845
1846 WW8_WrPlcField* pFieldP = CurrentFieldPlc();
1847
1848 const bool bIncludeEmptyPicLocation = ( eFieldType == ww::ePAGE );
1849 if (FieldFlags::Start & nMode)
1850 {
1851 sal_uInt8 aField13[2] = { 0x13, 0x00 }; // will change
1852 //#i3958#, Needed to make this field work correctly in Word 2000
1853 if (eFieldType == ww::eSHAPE)
1854 aField13[0] |= 0x80;
1855 aField13[1] = static_cast< sal_uInt8 >(eFieldType); // add type
1856 pFieldP->Append( Fc2Cp( Strm().Tell() ), aField13 );
1857 InsertSpecialChar( *this, 0x13, nullptr, bIncludeEmptyPicLocation );
1858 }
1859 if (FieldFlags::CmdStart & nMode)
1860 {
1861 SwWW8Writer::WriteString16(Strm(), sFieldCmd, false);
1862 // #i43956# - write hyperlink character including
1863 // attributes and corresponding binary data for certain reference fields.
1864 bool bHandleBookmark = false;
1865
1866 if (pField)
1867 {
1868 if (pField->GetTyp()->Which() == SwFieldIds::GetRef &&
1869 ( eFieldType == ww::ePAGEREF || eFieldType == ww::eREF ||
1870 eFieldType == ww::eNOTEREF || eFieldType == ww::eFOOTREF ))
1871 bHandleBookmark = true;
1872 }
1873
1874 if ( bHandleBookmark )
1875 {
1876 // retrieve reference destination - the name of the bookmark
1877 OUString aLinkStr;
1878 const sal_uInt16 nSubType = pField->GetSubType();
1879 const SwGetRefField& rRField = *static_cast<const SwGetRefField*>(pField);
1880 if ( nSubType == REF_SETREFATTR ||
1881 nSubType == REF_BOOKMARK )
1882 {
1883 const OUString& aRefName(rRField.GetSetRefName());
1884 aLinkStr = GetBookmarkName( nSubType, &aRefName, 0 );
1885 }
1886 else if ( nSubType == REF_FOOTNOTE ||
1887 nSubType == REF_ENDNOTE )
1888 {
1889 aLinkStr = GetBookmarkName( nSubType, nullptr, rRField.GetSeqNo() );
1890 }
1891 else if ( nSubType == REF_SEQUENCEFLD )
1892 {
1893 aLinkStr = pField->GetPar2();
1894 }
1895 // insert hyperlink character including attributes and data.
1896 InsertSpecialChar( *this, 0x01, &aLinkStr );
1897 }
1898 }
1899 if (FieldFlags::CmdEnd & nMode)
1900 {
1901 static const sal_uInt8 aField14[2] = { 0x14, 0xff };
1902 pFieldP->Append( Fc2Cp( Strm().Tell() ), aField14 );
1903 pFieldP->ResultAdded();
1904 InsertSpecialChar( *this, 0x14, nullptr, bIncludeEmptyPicLocation );
1905 }
1906 if (FieldFlags::End & nMode)
1907 {
1908 OUString sOut;
1909 if( pField )
1910 sOut = lcl_GetExpandedField(*pField);
1911 else
1912 sOut = sFieldCmd;
1913 if( !sOut.isEmpty() )
1914 {
1915 SwWW8Writer::WriteString16(Strm(), sOut, false);
1916
1917 if (pField)
1918 {
1919 if (pField->GetTyp()->Which() == SwFieldIds::Input &&
1920 eFieldType == ww::eFORMTEXT)
1921 {
1922 sal_uInt8 aArr[12];
1923 sal_uInt8 *pArr = aArr;
1924
1926 Set_UInt32( pArr, 0x0 );
1927
1929 Set_UInt8( pArr, 1 );
1930
1932 Set_UInt8( pArr, 1 );
1933
1934 m_pChpPlc->AppendFkpEntry( Strm().Tell(), static_cast< short >(pArr - aArr), aArr );
1935 }
1936 }
1937 }
1938 }
1939 if (!(FieldFlags::Close & nMode))
1940 return;
1941
1942 sal_uInt8 aField15[2] = { 0x15, 0x80 };
1943
1944 if (pField)
1945 {
1946 if (pField->GetTyp()->Which() == SwFieldIds::Input &&
1947 eFieldType == ww::eFORMTEXT)
1948 {
1949 sal_uInt16 nSubType = pField->GetSubType();
1950
1951 if (nSubType == REF_SEQUENCEFLD)
1952 aField15[0] |= (0x4 << 5);
1953 }
1954 // This ought to apply to any field, but just to be safe, start off with DATE/TIME only.
1955 if (pField->GetTyp()->Which() == SwFieldIds::DateTime
1956 && (pField->GetSubType() & FIXEDFLD))
1957 {
1958 //bit 5 - Locked: do not recalculate field
1959 aField15[1] |= 0x10;
1960 }
1961 }
1962
1963 pFieldP->Append( Fc2Cp( Strm().Tell() ), aField15 );
1964 InsertSpecialChar( *this, 0x15, nullptr, bIncludeEmptyPicLocation );
1965}
1966
1967void WW8Export::StartCommentOutput(std::u16string_view rName)
1968{
1969 const OUString sStr{ FieldString(ww::eQUOTE) + "[" + rName + "] " };
1971}
1972
1973void WW8Export::EndCommentOutput(std::u16string_view rName)
1974{
1975 const OUString sStr{ OUString::Concat(" [") + rName + "] " };
1978}
1979
1980sal_uInt16 MSWordExportBase::GetId( const SwTOXType& rTOXType )
1981{
1982 std::vector<const SwTOXType*>::iterator it
1983 = std::find( m_aTOXArr.begin(), m_aTOXArr.end(), &rTOXType );
1984 if ( it != m_aTOXArr.end() )
1985 {
1986 return it - m_aTOXArr.begin();
1987 }
1988 m_aTOXArr.push_back( &rTOXType );
1989 return m_aTOXArr.size() - 1;
1990}
1991
1992// return values: 1 - no PageNum,
1993// 2 - TabStop before PageNum,
1994// 3 - Text before PageNum - rText hold the text
1995// 4 - no Text and no TabStop before PageNum
1996static int lcl_CheckForm( const SwForm& rForm, sal_uInt8 nLvl, OUString& rText )
1997{
1998 int nRet = 4;
1999 rText.clear();
2000
2001 // #i21237#
2002 SwFormTokens aPattern = rForm.GetPattern(nLvl);
2003 SwFormTokens::iterator aIt = aPattern.begin();
2004 FormTokenType eTType;
2005
2006 // #i61362#
2007 if (! aPattern.empty())
2008 {
2009 bool bPgNumFnd = false;
2010
2011 // #i21237#
2012 while( ++aIt != aPattern.end() && !bPgNumFnd )
2013 {
2014 eTType = aIt->eTokenType;
2015
2016 switch( eTType )
2017 {
2018 case TOKEN_PAGE_NUMS:
2019 bPgNumFnd = true;
2020 break;
2021
2022 case TOKEN_TAB_STOP:
2023 nRet = 2;
2024 break;
2025 case TOKEN_TEXT:
2026 {
2027 nRet = 3;
2028 sal_Int32 nCount = std::min<sal_Int32>(5, aIt->sText.getLength());
2029 rText = aIt->sText.copy(0, nCount); // #i21237#
2030 break;
2031 }
2032 case TOKEN_LINK_START:
2033 case TOKEN_LINK_END:
2034 break;
2035
2036 default:
2037 nRet = 4;
2038 break;
2039 }
2040 }
2041
2042 if( !bPgNumFnd )
2043 nRet = 1;
2044 }
2045
2046 return nRet;
2047}
2048
2049static bool lcl_IsHyperlinked(const SwForm& rForm, sal_uInt16 nTOXLvl)
2050{
2051 bool bRes = false;
2052 for (sal_uInt16 nI = 1; nI < nTOXLvl; ++nI)
2053 {
2054 // #i21237#
2055 SwFormTokens aPattern = rForm.GetPattern(nI);
2056
2057 if ( !aPattern.empty() )
2058 {
2059 SwFormTokens::iterator aIt = aPattern.begin();
2060
2061 FormTokenType eTType;
2062
2063 // #i21237#
2064 while ( ++aIt != aPattern.end() )
2065 {
2066 eTType = aIt->eTokenType;
2067 switch (eTType)
2068 {
2069 case TOKEN_LINK_START:
2070 case TOKEN_LINK_END:
2071 bRes = true;
2072 break;
2073 default:
2074 ;
2075 }
2076 }
2077 }
2078 }
2079 return bRes;
2080}
2081
2083{
2084 if(GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::RTF) // Not implemented for RTF
2085 return;
2086
2087 const SwpHints* pTextAttrs = rNode.GetpSwpHints();
2088 if (!pTextAttrs)
2089 return;
2090
2091 for( size_t i = 0; i < pTextAttrs->Count(); ++i )
2092 {
2093 const SwTextAttr* pHt = pTextAttrs->Get(i);
2094 if (pHt->GetAttr().Which() == RES_TXTATR_FIELD)
2095 {
2096 const SwFormatField& rField = static_cast<const SwFormatField&>(pHt->GetAttr());
2097 const SwField* pField = rField.GetField();
2098 // Need to have bookmarks only for sequence fields
2099 if (pField && pField->GetTyp()->Which() == SwFieldIds::SetExp && pField->GetSubType() == nsSwGetSetExpType::GSE_SEQ)
2100 {
2101 const sal_uInt16 nSeqFieldNumber = static_cast<const SwSetExpField*>(pField)->GetSeqNumber();
2102 const OUString sObjectName = static_cast<const SwSetExpFieldType*>(pField->GetTyp())->GetName();
2104 bool bHaveFullBkm = false;
2105 bool bHaveLabelAndNumberBkm = false;
2106 bool bHaveCaptionOnlyBkm = false;
2107 bool bHaveNumberOnlyBkm = false;
2108 bool bRunSplittedAtSep = false;
2109 for( auto const & pFieldType : *pFieldTypes )
2110 {
2111 if( SwFieldIds::GetRef == pFieldType->Which() )
2112 {
2113 SwIterator<SwFormatField,SwFieldType> aIter( *pFieldType );
2114 for( SwFormatField* pFormatField = aIter.First(); pFormatField; pFormatField = aIter.Next() )
2115 {
2116 SwGetRefField* pRefField = static_cast<SwGetRefField*>(pFormatField->GetField());
2117 // If we have a reference to the current sequence field
2118 if(pRefField->GetSeqNo() == nSeqFieldNumber && pRefField->GetSetRefName() == sObjectName)
2119 {
2120 // Need to create a separate run for separator character
2121 SwWW8AttrIter aLocalAttrIter( GetExport(), rNode ); // We need a local iterator having the right number of runs
2122 const OUString& aText = rNode.GetText();
2123 const sal_Int32 nCategoryStart = aText.indexOf(pRefField->GetSetRefName());
2124 const sal_Int32 nPosBeforeSeparator = std::max(nCategoryStart, pHt->GetStart());
2125 bool bCategoryFirst = nCategoryStart < pHt->GetStart();
2126 sal_Int32 nSeparatorPos = 0;
2127 if (bCategoryFirst)
2128 {
2129 nSeparatorPos = aLocalAttrIter.WhereNext();
2130 while (nSeparatorPos <= nPosBeforeSeparator)
2131 {
2132 aLocalAttrIter.NextPos();
2133 nSeparatorPos = aLocalAttrIter.WhereNext();
2134 }
2135 }
2136 else
2137 {
2138 nSeparatorPos = nCategoryStart + pRefField->GetSetRefName().getLength();
2139 }
2140 sal_Int32 nRefTextPos = 0;
2141 if(nSeparatorPos < aText.getLength())
2142 {
2143 nRefTextPos = SwGetExpField::GetReferenceTextPos(pHt->GetFormatField(), GetExport().m_rDoc, nSeparatorPos);
2144 if(nRefTextPos != nSeparatorPos)
2145 {
2146 if(!bRunSplittedAtSep)
2147 {
2148 if(!bCategoryFirst)
2149 rAttrIter.SplitRun(nSeparatorPos);
2150 rAttrIter.SplitRun(nRefTextPos);
2151 bRunSplittedAtSep = true;
2152 }
2153 if(!bCategoryFirst)
2154 aLocalAttrIter.SplitRun(nSeparatorPos);
2155 aLocalAttrIter.SplitRun(nRefTextPos);
2156 }
2157 else if (bCategoryFirst)
2158 {
2159 if(!bRunSplittedAtSep)
2160 {
2161 rAttrIter.SplitRun(nSeparatorPos);
2162 bRunSplittedAtSep = true;
2163 }
2164 aLocalAttrIter.SplitRun(nSeparatorPos);
2165 }
2166 }
2167 // Generate bookmarks on the right position
2168 OUString sName("Ref_" + pRefField->GetSetRefName() + OUString::number(pRefField->GetSeqNo()));
2169 switch (pRefField->GetFormat())
2170 {
2171 case REF_PAGE:
2172 case REF_PAGE_PGDESC:
2173 case REF_CONTENT:
2174 case REF_UPDOWN:
2175 if(!bHaveFullBkm)
2176 {
2177 sal_Int32 nLastAttrStart = 0;
2178 sal_Int32 nActAttr = aLocalAttrIter.WhereNext();
2179 while (nActAttr < rNode.GetText().getLength())
2180 {
2181 nLastAttrStart = nActAttr;
2182 aLocalAttrIter.NextPos();
2183 nActAttr = aLocalAttrIter.WhereNext();
2184 }
2185 WriteBookmarkInActParagraph( sName + "_full", std::min(nCategoryStart, pHt->GetStart()), nLastAttrStart );
2186 bHaveFullBkm = true;
2187 }
2188 break;
2189 case REF_ONLYNUMBER:
2190 {
2191 if(!bHaveLabelAndNumberBkm)
2192 {
2193 sName += "_label_and_number";
2194 if(bCategoryFirst)
2195 WriteBookmarkInActParagraph( sName, std::min(nCategoryStart, pHt->GetStart()), std::max(nCategoryStart, pHt->GetStart()) );
2196 else
2197 {
2198 // Find the last run which contains category text
2199 SwWW8AttrIter aLocalAttrIter2( GetExport(), rNode );
2200 sal_Int32 nCatLastRun = 0;
2201 sal_Int32 nNextAttr = aLocalAttrIter2.WhereNext();
2202 while (nNextAttr < nSeparatorPos)
2203 {
2204 nCatLastRun = nNextAttr;
2205 aLocalAttrIter2.NextPos();
2206 nNextAttr = aLocalAttrIter2.WhereNext();
2207 }
2208 WriteBookmarkInActParagraph( sName, pHt->GetStart(), nCatLastRun );
2209 }
2210 bHaveLabelAndNumberBkm = true;
2211 }
2212 break;
2213 }
2214 case REF_ONLYCAPTION:
2215 {
2216 if(!bHaveCaptionOnlyBkm)
2217 {
2218 // Find last run
2219 sal_Int32 nLastAttrStart = 0;
2220 sal_Int32 nActAttr = aLocalAttrIter.WhereNext();
2221 while (nActAttr < rNode.GetText().getLength())
2222 {
2223 nLastAttrStart = nActAttr;
2224 aLocalAttrIter.NextPos();
2225 nActAttr = aLocalAttrIter.WhereNext();
2226 }
2227 WriteBookmarkInActParagraph( sName + "_caption_only", nRefTextPos, nLastAttrStart );
2228 bHaveCaptionOnlyBkm = true;
2229 }
2230 break;
2231 }
2232 case REF_ONLYSEQNO:
2233 {
2234 if(!bHaveNumberOnlyBkm)
2235 {
2236 WriteBookmarkInActParagraph( sName + "_number_only", pHt->GetStart(), pHt->GetStart() );
2237 bHaveNumberOnlyBkm = true;
2238 }
2239 break;
2240 }
2241 }
2242 }
2243 }
2244 }
2245 }
2246 return;
2247 }
2248 }
2249 }
2250}
2251
2253{
2254 if ( const SwTOXBase* pTOX = rSect.GetTOXBase() )
2255 {
2256 static const char sEntryEnd[] = "\" ";
2257
2258 ww::eField eCode = ww::eTOC;
2259 OUString sStr = pTOX ->GetMSTOCExpression();
2260 if ( sStr.isEmpty() )
2261 {
2262 OUString sUserTypeName;
2263 auto aType = pTOX->GetType();
2264 // user index, it needs INDEX with \f
2265 if ( TOX_USER == aType )
2266 {
2267 sUserTypeName = pTOX->GetTOXType()->GetTypeName();
2268 if ( !sUserTypeName.isEmpty() )
2269 aType = TOX_INDEX;
2270 }
2271 switch (aType)
2272 {
2273 case TOX_INDEX:
2274 eCode = ww::eINDEX;
2275 sStr = FieldString(eCode);
2276
2277 {
2278 const SwFormatCol& rCol = rSect.GetFormat()->GetFormatAttr( RES_COL );
2279 const SwColumns& rColumns = rCol.GetColumns();
2280 sal_Int32 nCol = rColumns.size();
2281
2282 if ( 0 < nCol )
2283 {
2284 // Add a continuous section break
2285 if( GetExport().AddSectionBreaksForTOX() )
2286 {
2287 SwSection *pParent = rSect.GetParent();
2289 pParent ? pParent->GetFormat() : nullptr, 0/*nRstLnNum*/);
2290 GetExport( ).AttrOutput().SectionBreak( msword::PageBreak, false, &rInfo );
2291 }
2292
2293 sStr += "\\c \"" + OUString::number( nCol ) + "\"";
2294 }
2295 }
2296
2297 if (pTOX->GetTOXForm().IsCommaSeparated())
2298 sStr += "\\r ";
2299
2300 if (SwTOIOptions::AlphaDelimiter & pTOX->GetOptions())
2301 sStr += "\\h \"A\" ";
2302
2303 if (!sUserTypeName.isEmpty())
2304 {
2305 sStr += "\\f \"" + sUserTypeName + "\"";
2306 }
2307
2308 if (!pTOX->GetTOXForm().IsCommaSeparated())
2309 {
2310 // In case of Run-in style no separators are added.
2311 OUString aFillText;
2312 for (sal_uInt8 n = 1; n <= 3; ++n)
2313 {
2314 OUString aText;
2315 int nRet = ::lcl_CheckForm(pTOX->GetTOXForm(), n, aText);
2316
2317 if( 3 == nRet )
2318 aFillText = aText;
2319 else if ((4 == nRet) || (2 == nRet))
2320 aFillText = "\t";
2321 else
2322 aFillText.clear();
2323 }
2324 sStr += "\\e \"" + aFillText + sEntryEnd;
2325 }
2326 break;
2327
2328 case TOX_ILLUSTRATIONS:
2329 case TOX_OBJECTS:
2330 case TOX_TABLES:
2331 if (!pTOX->IsFromObjectNames())
2332 {
2333 sStr = FieldString(eCode) + "\\c ";
2334 const OUString& seqName = pTOX->GetSequenceName();
2335 if(!seqName.isEmpty())
2336 {
2337 sStr += "\"" + seqName + sEntryEnd;
2338 }
2339 OUString aText;
2340 int nRet = ::lcl_CheckForm( pTOX->GetTOXForm(), 1, aText );
2341 if (1 == nRet)
2342 sStr += "\\n ";
2343 else if( 3 == nRet || 4 == nRet )
2344 {
2345 sStr += "\\p \"" + aText + sEntryEnd;
2346 }
2347 }
2348 break;
2349
2350 case TOX_AUTHORITIES:
2351 eCode = ww::eBIBLIOGRAPHY;
2352 sStr = FieldString(eCode);
2353 break;
2354 // case TOX_USER:
2355 // case TOX_CONTENT:
2356 default:
2357 {
2358 sStr = FieldString(eCode);
2359
2360 OUString sTOption;
2361 sal_uInt16 n, nTOXLvl = pTOX->GetLevel();
2362 if( !nTOXLvl )
2363 ++nTOXLvl;
2364
2365 if(SwTOXElement::TableLeader & pTOX->GetCreateType())
2366 {
2367 sStr +="\\z " ;
2369 }
2370 if(SwTOXElement::TableInToc & pTOX->GetCreateType())
2371 {
2372 sStr +="\\w " ;
2373 GetExport( ).m_bTabInTOC = true ;
2374 }
2375 if(SwTOXElement::Newline & pTOX->GetCreateType())
2376 {
2377 sStr +="\\x " ;
2378 }
2379 if( SwTOXElement::Mark & pTOX->GetCreateType() )
2380 {
2381 sStr += "\\f ";
2382
2383 if( TOX_USER == pTOX->GetType() )
2384 {
2385 sStr += "\""
2386 + OUStringChar(static_cast<char>( 'A' + GetExport( ).GetId( *pTOX->GetTOXType() ) ))
2387 + sEntryEnd;
2388 }
2389 }
2390 if(SwTOXElement::Bookmark & pTOX->GetCreateType())
2391 {
2392 sStr += "\\b \"" + pTOX->GetBookmarkName() + sEntryEnd;
2393 }
2394
2395 if( SwTOXElement::OutlineLevel & pTOX->GetCreateType() )
2396 {
2397 // Take the TOC value of the max level to evaluate to as
2398 // the starting point for the \o flag, but reduce it to the
2399 // value of the highest outline level filled by a *standard*
2400 // Heading 1 - 9 style because \o "Builds a table of
2401 // contents from paragraphs formatted with built-in heading
2402 // styles". And afterward fill in any outline styles left
2403 // uncovered by that range to the \t flag
2404
2405 // i.e. for
2406 // Heading 1
2407 // Heading 2
2408 // custom-style
2409 // Heading 4
2410 // output
2411 // \o 1-2 \tcustom-style,3,Heading 3,4
2412
2413 // Search over all the outline styles used and figure out
2414 // what is the minimum outline level (if any) filled by a
2415 // non-standard style for that level, i.e. ignore headline
2416 // styles 1-9 and find the lowest valid outline level
2417 sal_uInt8 nPosOfLowestNonStandardLvl = MAXLEVEL;
2419 for( n = rColls.size(); n; )
2420 {
2421 const SwTextFormatColl* pColl = rColls[ --n ];
2422 sal_uInt16 nPoolId = pColl->GetPoolFormatId();
2423 if (
2424 //Is a Non-Standard Outline Style
2425 (RES_POOLCOLL_HEADLINE1 > nPoolId || RES_POOLCOLL_HEADLINE9 < nPoolId) &&
2426 //Has a valid outline level
2428 // Is less than the lowest known non-standard level
2429 (pColl->GetAssignedOutlineStyleLevel() < nPosOfLowestNonStandardLvl)
2430 )
2431 {
2432 nPosOfLowestNonStandardLvl = ::sal::static_int_cast<sal_uInt8>(pColl->GetAssignedOutlineStyleLevel());
2433 }
2434 }
2435
2436 sal_uInt8 nMaxMSAutoEvaluate = nPosOfLowestNonStandardLvl < nTOXLvl ? nPosOfLowestNonStandardLvl : static_cast<sal_uInt8>(nTOXLvl);
2437
2438 //output \o 1-X where X is the highest normal outline style to be included in the toc
2439 if ( nMaxMSAutoEvaluate )
2440 {
2441 if (nMaxMSAutoEvaluate > WW8ListManager::nMaxLevel)
2442 nMaxMSAutoEvaluate = WW8ListManager::nMaxLevel;
2443
2444 sStr += "\\o \"1-" + OUString::number(nMaxMSAutoEvaluate) + sEntryEnd;
2445 }
2446
2447 //collect up any other styles in the writer TOC which will
2448 //not already appear in the MS TOC and place then into the
2449 //\t option
2450 if( nMaxMSAutoEvaluate < nTOXLvl )
2451 {
2452 // collect this templates into the \t option
2453 for( n = rColls.size(); n;)
2454 {
2455 const SwTextFormatColl* pColl = rColls[ --n ];
2457 continue;
2458 sal_uInt8 nTestLvl = ::sal::static_int_cast<sal_uInt8>(pColl->GetAssignedOutlineStyleLevel());
2459 if (nTestLvl < nTOXLvl && nTestLvl >= nMaxMSAutoEvaluate)
2460 {
2461 if (!sTOption.isEmpty())
2462 sTOption += ",";
2463 sTOption += pColl->GetName() + "," + OUString::number( nTestLvl + 1 );
2464 }
2465 }
2466 }
2467 }
2468
2469 if( SwTOXElement::ParagraphOutlineLevel & pTOX->GetCreateType() )
2470 {
2471 sStr +="\\u " ;
2472 }
2473
2474 if( SwTOXElement::Template & pTOX->GetCreateType() )
2475 {
2476 // #i99641# - Consider additional styles regardless of TOX-outlinelevel
2477 for( n = 0; n < MAXLEVEL; ++n )
2478 {
2479 const OUString& rStyles = pTOX->GetStyleNames( n );
2480 if( !rStyles.isEmpty() )
2481 {
2482 sal_Int32 nPos = 0;
2483 const OUString sLvl{ "," + OUString::number( n + 1 ) };
2484 do {
2485 const OUString sStyle( rStyles.getToken( 0, TOX_STYLE_DELIMITER, nPos ));
2486 if( !sStyle.isEmpty() )
2487 {
2489 if (pColl)
2490 {
2491 if (!pColl->IsAssignedToListLevelOfOutlineStyle() || pColl->GetAssignedOutlineStyleLevel() < nTOXLvl)
2492 {
2493 if( !sTOption.isEmpty() )
2494 sTOption += ",";
2495 sTOption += sStyle + sLvl;
2496 }
2497 }
2498 }
2499 } while( -1 != nPos );
2500 }
2501 }
2502 }
2503
2504 // No 'else' branch; why the below snippet is a block I have no idea.
2505 {
2506 OUString aFillText;
2507 sal_uInt8 nNoPgStt = MAXLEVEL, nNoPgEnd = MAXLEVEL;
2508 bool bFirstFillText = true, bOnlyText = true;
2509 for( n = 0; n < nTOXLvl; ++n )
2510 {
2511 OUString aText;
2512 int nRet = ::lcl_CheckForm( pTOX->GetTOXForm(),
2513 static_cast< sal_uInt8 >(n+1), aText );
2514 if( 1 == nRet )
2515 {
2516 bOnlyText = false;
2517 if( MAXLEVEL == nNoPgStt )
2518 nNoPgStt = static_cast< sal_uInt8 >(n+1);
2519 }
2520 else
2521 {
2522 if( MAXLEVEL != nNoPgStt &&
2523 MAXLEVEL == nNoPgEnd )
2524 nNoPgEnd = sal_uInt8(n);
2525
2526 bOnlyText = bOnlyText && 3 == nRet;
2527 if( 3 == nRet || 4 == nRet )
2528 {
2529 if( bFirstFillText )
2530 aFillText = aText;
2531 else if( aFillText != aText )
2532 aFillText.clear();
2533 bFirstFillText = false;
2534 }
2535 }
2536 }
2537 if( MAXLEVEL != nNoPgStt )
2538 {
2539 if (WW8ListManager::nMaxLevel < nNoPgEnd)
2540 nNoPgEnd = WW8ListManager::nMaxLevel;
2541 sStr += "\\n "
2542 + OUString::number( nNoPgStt )
2543 + "-"
2544 + OUString::number( nNoPgEnd )
2545 + " ";
2546 }
2547 if( bOnlyText )
2548 {
2549 sStr += "\\p \"" + aFillText + sEntryEnd;
2550 }
2551 }
2552
2553 if( !sTOption.isEmpty() )
2554 {
2555 sStr += "\\t \"" + sTOption + sEntryEnd;
2556 }
2557
2558 if (lcl_IsHyperlinked(pTOX->GetTOXForm(), nTOXLvl))
2559 sStr += "\\h";
2560 break;
2561 }
2562 }
2563 }
2564
2565 if (!sStr.isEmpty())
2566 {
2567 GetExport( ).m_bInWriteTOX = true;
2568 if (GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::RTF)
2569 { // tdf#129574: required for RTF; doesn't work with DOCX
2570 StartRun(nullptr, -42, true);
2571 }
2572 GetExport( ).OutputField( nullptr, eCode, sStr, FieldFlags::Start | FieldFlags::CmdStart |
2574 if (GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::RTF)
2575 {
2576 EndRun(nullptr, -42, -1, true);
2577 }
2578 }
2579 }
2580
2581 GetExport( ).m_bStartTOX = false;
2582}
2583
2584void AttributeOutputBase::EndTOX( const SwSection& rSect,bool bCareEnd )
2585{
2586 const SwTOXBase* pTOX = rSect.GetTOXBase();
2587 if ( pTOX )
2588 {
2589 ww::eField eCode = TOX_INDEX == pTOX->GetType() ? ww::eINDEX : ww::eTOC;
2590 GetExport( ).OutputField( nullptr, eCode, OUString(), FieldFlags::Close );
2591
2592 if ( pTOX->GetType() == TOX_INDEX && GetExport().AddSectionBreaksForTOX() )
2593 {
2594 const SwFormatCol& rCol = rSect.GetFormat()->GetFormatAttr( RES_COL );
2595 const SwColumns& rColumns = rCol.GetColumns();
2596 sal_Int32 nCol = rColumns.size();
2597
2598 if ( 0 < nCol )
2599 {
2600 WW8_SepInfo rInfo( &GetExport().m_rDoc.GetPageDesc( 0 ), rSect.GetFormat(), 0/*nRstLnNum*/ );
2601 GetExport( ).AttrOutput().SectionBreak( msword::PageBreak, false, &rInfo );
2602 }
2603 }
2604 }
2605 GetExport( ).m_bInWriteTOX = false;
2607 if (bCareEnd)
2608 OnTOXEnding();
2609}
2610
2611bool MSWordExportBase::GetNumberFormat(const SwField& rField, OUString& rStr)
2612{
2613 // Returns a date or time format string by using the US NfKeywordTable
2614 bool bHasFormat = false;
2616 sal_uInt32 nFormatIdx = rField.GetFormat();
2617 const SvNumberformat* pNumFormat = pNFormatr->GetEntry( nFormatIdx );
2618 if( pNumFormat )
2619 {
2620 LanguageType nLng = rField.GetLanguage();
2621 SAL_WARN_IF(nLng == LANGUAGE_DONTKNOW, "sw.ww8", "unexpected LANGUAGE_DONTKNOW");
2622 if (nLng == LANGUAGE_NONE || nLng == LANGUAGE_DONTKNOW)
2623 {
2624 nLng = pNumFormat->GetLanguage();
2625 }
2626 LocaleDataWrapper aLocDat(pNFormatr->GetComponentContext(),
2627 LanguageTag(nLng));
2628
2629 OUString sFormat(pNumFormat->GetMappedFormatstring(GetNfKeywordTable(),
2630 aLocDat));
2631
2632 if (!sFormat.isEmpty())
2633 {
2635
2636 rStr = "\\@\"" + sFormat + "\" " ;
2637 bHasFormat = true;
2638 }
2639 }
2640 return bHasFormat;
2641}
2642
2643void AttributeOutputBase::GetNumberPara( OUString& rStr, const SwField& rField )
2644{
2645 switch(rField.GetFormat())
2646 {
2649 rStr += "\\* ALPHABETIC ";
2650 break;
2653 rStr += "\\* alphabetic ";
2654 break;
2656 rStr += "\\* ROMAN ";
2657 break;
2659 rStr += "\\* roman ";
2660 break;
2662 rStr += "\\* Ordinal ";
2663 break;
2665 rStr += "\\* Ordtext ";
2666 break;
2668 rStr += "\\* Cardtext ";
2669 break;
2670 default:
2671 OSL_ENSURE(rField.GetFormat() == SVX_NUM_ARABIC,
2672 "Unknown numbering type exported as default of Arabic");
2673 [[fallthrough]];
2674 case SVX_NUM_ARABIC:
2675 rStr += "\\* ARABIC ";
2676 break;
2677 case SVX_NUM_PAGEDESC:
2678 //Nothing, use word's default
2679 break;
2680 }
2681}
2682
2684{
2685 sal_uInt8 aArr[ 3 ];
2686 sal_uInt8* pArr = aArr;
2687
2688 // sprmCFSpec true
2690 Set_UInt8( pArr, 1 );
2691
2692 m_pChpPlc->AppendFkpEntry( Strm().Tell() );
2693 WriteChar( 0x05 ); // Annotation reference
2694
2695 if( pOut )
2696 pOut->insert( pOut->end(), aArr, pArr );
2697 else
2698 m_pChpPlc->AppendFkpEntry( Strm().Tell(), static_cast< short >(pArr - aArr), aArr );
2699}
2700
2701OUString FieldString(ww::eField eIndex)
2702{
2703 if (const char *pField = ww::GetEnglishFieldName(eIndex))
2704 return " " + OUString::createFromAscii(pField) + " ";
2705 return " ";
2706}
2707
2709{
2710 //replace LF 0x0A with VT 0x0B
2711 const OUString sExpand(rField.GetPar2().replace(0x0A, 0x0B));
2712
2713 m_rWW8Export.m_pChpPlc->AppendFkpEntry(m_rWW8Export.Strm().Tell());
2714 SwWW8Writer::WriteString16(m_rWW8Export.Strm(), sExpand, false);
2715 static sal_uInt8 aArr[] =
2716 {
2717 0x3C, 0x08, 0x1
2718 };
2719 m_rWW8Export.m_pChpPlc->AppendFkpEntry(m_rWW8Export.Strm().Tell(), sizeof(aArr), aArr);
2720}
2721
2722void WW8AttributeOutput::SetField( const SwField& rField, ww::eField eType, const OUString& rCmd )
2723{
2724 const SwSetExpField* pSet = static_cast<const SwSetExpField*>(&rField);
2725 const OUString &rVar = pSet->GetPar2();
2726
2728
2729 GetExport().OutputField(&rField, eType, rCmd, FieldFlags::Start |
2731
2732 /*
2733 Is there a bookmark at the start position of this field, if so
2734 move it to the 0x14 of the result of the field. This is what word
2735 does. MoveFieldMarks moves any bookmarks at this position to
2736 the beginning of the field result, and marks the bookmark as a
2737 fieldbookmark which is to be ended before the field end mark
2738 instead of after it like a normal bookmark.
2739 */
2741
2742 if (!rVar.isEmpty())
2743 {
2745 }
2746 GetExport().OutputField(&rField, eType, rCmd, FieldFlags::Close);
2747}
2748
2750{
2751 const SwPostItField *pPField = static_cast<const SwPostItField*>(pField);
2752 m_rWW8Export.m_pAtn->Append( m_rWW8Export.Fc2Cp( m_rWW8Export.Strm().Tell() ), pPField );
2754}
2755
2757{
2758 const SwDropDownField& rField2 = *static_cast<const SwDropDownField*>(pField);
2759 uno::Sequence<OUString> aItems =
2760 rField2.GetItemSequence();
2761 GetExport().DoComboBox(rField2.GetName(),
2762 rField2.GetHelp(),
2763 rField2.GetToolTip(),
2764 rField2.GetSelectedItem(), aItems);
2765 return false;
2766}
2767
2769{
2770 return true; // expand to text?
2771}
2772
2773void WW8AttributeOutput::RefField( const SwField &rField, const OUString &rRef)
2774{
2775 const OUString sStr{ FieldString( ww::eREF ) + "\"" + rRef + "\" " };
2778 const OUString sVar = lcl_GetExpandedField( rField );
2779 if ( !sVar.isEmpty() )
2780 {
2782 }
2784}
2785
2787{
2789}
2790
2791namespace
2792{
2793// Escapes a token string for storing in Word formats. Its import counterpart
2794// is lcl_ExtractToken in writerfilter/source/dmapper/DomainMapper_Impl.cxx
2795OUString EscapeToken(const OUString& rCommand)
2796{
2797 bool bWasEscaped = false;
2798
2799 const int nBufferLen = rCommand.getLength()*1.5;
2800 OUStringBuffer sResult(nBufferLen);
2801 sResult.append('"'); // opening quote
2802 for (sal_Int32 i = 0; i < rCommand.getLength(); ++i)
2803 {
2804 sal_Unicode ch = rCommand[i];
2805 switch (ch)
2806 {
2807 case '\\':
2808 case '"':
2809 // Backslashes and doublequotes must be escaped
2810 bWasEscaped = true;
2811 sResult.append('\\');
2812 break;
2813 case ' ':
2814 // Spaces require quotation
2815 bWasEscaped = true;
2816 break;
2817 }
2818 sResult.append(ch);
2819 }
2820
2821 if (bWasEscaped)
2822 {
2823 sResult.append('"'); // closing quote
2824 return sResult.makeStringAndClear();
2825 }
2826 // No escapement/quotation was required
2827 return rCommand;
2828}
2829}
2830
2832{
2833 const SwField* pField = rField.GetField();
2834 bool bWriteExpand = false;
2835 const sal_uInt16 nSubType = pField->GetSubType();
2836
2837 switch (pField->GetTyp()->Which())
2838 {
2839 case SwFieldIds::GetExp:
2840 if (nSubType == nsSwGetSetExpType::GSE_STRING)
2841 {
2842 const SwGetExpField *pGet = static_cast<const SwGetExpField*>(pField);
2843 RefField( *pGet, pGet->GetFormula() );
2844 }
2845 else
2846 bWriteExpand = true;
2847 break;
2848 case SwFieldIds::SetExp:
2849 if (nsSwGetSetExpType::GSE_SEQ == nSubType)
2850 {
2851 OUString sStr;
2852 if (GetExport().FieldsQuoted())
2853 sStr = FieldString(ww::eSEQ) + pField->GetTyp()->GetName() + " ";
2854 else
2855 sStr = FieldString(ww::eSEQ) + "\"" + pField->GetTyp()->GetName() +"\" ";
2856 GetNumberPara( sStr, *pField );
2857 GetExport().OutputField(pField, ww::eSEQ, sStr);
2858 }
2859 else if (nSubType & nsSwGetSetExpType::GSE_STRING)
2860 {
2861 bool bShowAsWell = false;
2862 ww::eField eFieldNo;
2863 const SwSetExpField *pSet = static_cast<const SwSetExpField*>(pField);
2864 const OUString sVar = pSet->GetPar2();
2865 OUString sStr;
2866 if (pSet->GetInputFlag())
2867 {
2868 sStr = FieldString(ww::eASK) + "\""
2869 + pSet->GetPar1() + "\" "
2870 + pSet->GetPromptText() + " \\d "
2871 + sVar;
2872 eFieldNo = ww::eASK;
2873 }
2874 else
2875 {
2876 sStr = FieldString(ww::eSET)
2877 + pSet->GetPar1() + " \""
2878 + sVar + "\" ";
2879 eFieldNo = ww::eSET;
2880 bShowAsWell = (nSubType & nsSwExtendedSubType::SUB_INVISIBLE) == 0;
2881 }
2882
2883 SetField( *pField, eFieldNo, sStr );
2884
2885 if (bShowAsWell)
2886 RefField( *pSet, pSet->GetPar1() );
2887 }
2888 else
2889 bWriteExpand = true;
2890 break;
2892 {
2893 OUString sStr = FieldString(ww::ePAGE);
2894 GetNumberPara(sStr, *pField);
2895 GetExport().OutputField(pField, ww::ePAGE, sStr);
2896 }
2897 break;
2899 {
2900 OUString sStr = FieldString(ww::eFILENAME);
2901 if (pField->GetFormat() == FF_PATHNAME)
2902 sStr += "\\p ";
2903 GetExport().OutputField(pField, ww::eFILENAME, sStr);
2904 }
2905 break;
2907 {
2908 OUString sStr = FieldString(ww::eMERGEFIELD)
2909 + EscapeToken(static_cast<SwDBFieldType *>(pField->GetTyp())->GetColumnName()) + " ";
2910 GetExport().OutputField(pField, ww::eMERGEFIELD, sStr);
2911 }
2912 break;
2914 {
2916 const OUString sStr = FieldString(ww::eDATABASE)
2917 + aData.sDataSource
2918 + OUStringChar(DB_DELIM)
2919 + aData.sCommand;
2920 GetExport().OutputField(pField, ww::eDATABASE, sStr);
2921 }
2922 break;
2923 case SwFieldIds::Author:
2924 {
2928 }
2929 break;
2932 break;
2933 case SwFieldIds::DocInfo: // Last printed, last edited,...
2934 if( DI_SUB_FIXED & nSubType )
2935 bWriteExpand = true;
2936
2937 {
2938 OUString sStr;
2940 switch (0xff & nSubType)
2941 {
2942 case DI_TITLE:
2944 break;
2945 case DI_SUBJECT:
2947 break;
2948 case DI_KEYS:
2950 break;
2951 case DI_COMMENT:
2953 break;
2954 case DI_DOCNO:
2956 break;
2957 case DI_EDIT:
2959 break;
2960 case DI_CREATE:
2961 if (DI_SUB_AUTHOR == (nSubType & DI_SUB_MASK))
2963 else if (GetExport().GetNumberFormat(*pField, sStr) || sStr.isEmpty())
2965
2966 // Create author/time are always imported as fixed. Safe to ignore on export
2967 bWriteExpand = false;
2968 break;
2969
2970 case DI_CHANGE:
2971 if (DI_SUB_AUTHOR == (nSubType & DI_SUB_MASK))
2972 {
2974 bWriteExpand=false;
2975 }
2976 else if (GetExport().GetNumberFormat(*pField, sStr) || sStr.isEmpty())
2978 break;
2979
2980 case DI_PRINT:
2981 if (DI_SUB_AUTHOR != (nSubType & DI_SUB_MASK) &&
2982 (GetExport().GetNumberFormat(*pField, sStr) || sStr.isEmpty()))
2984 break;
2985 case DI_CUSTOM:
2987 {
2988 const SwDocInfoField * pDocInfoField =
2989 dynamic_cast<const SwDocInfoField *> (pField);
2990
2991 if (pDocInfoField != nullptr)
2992 sStr = "\"" + pDocInfoField->GetName() + "\"";
2993
2994 bWriteExpand = false;
2995 }
2996 break;
2997 default:
2998 break;
2999 }
3000
3001 if (!bWriteExpand && eField != ww::eNONE)
3002 {
3003 GetExport().OutputField(pField, eField, FieldString(eField) + sStr);
3004 }
3005 else
3006 bWriteExpand = true;
3007 }
3008 break;
3010 {
3011 OUString sStr;
3012 if (!GetExport().GetNumberFormat(*pField, sStr))
3013 bWriteExpand = true;
3014 else
3015 {
3016 ww::eField eField = (DATEFLD & nSubType) ? ww::eDATE : ww::eTIME;
3017 GetExport().OutputField(pField, eField, FieldString(eField) + sStr);
3018 }
3019 }
3020 break;
3022 {
3024
3025 switch (nSubType)
3026 {
3027 case DS_PAGE:
3029 break;
3030 case DS_WORD:
3032 break;
3033 case DS_CHAR:
3035 break;
3036 }
3037
3038 if (eField != ww::eNONE)
3039 {
3040 OUString sStr = FieldString(eField);
3041 GetNumberPara(sStr, *pField);
3042 GetExport().OutputField(pField, eField, sStr);
3043 }
3044 else
3045 bWriteExpand = true;
3046 }
3047 break;
3049 {
3051 switch (0xFF & nSubType)
3052 {
3053 case EU_FIRSTNAME:
3054 case EU_NAME:
3056 break;
3057 case EU_SHORTCUT:
3059 break;
3060 case EU_STREET:
3061 case EU_COUNTRY:
3062 case EU_ZIP:
3063 case EU_CITY:
3065 break;
3066 }
3067
3068 if (eField != ww::eNONE)
3069 {
3071 }
3072 else
3073 bWriteExpand = true;
3074 }
3075 break;
3077 {
3078 OUString sRet(static_cast<SwAuthorityField const*>(pField)
3079 ->ExpandCitation(AUTH_FIELD_IDENTIFIER, nullptr));
3080 // FIXME: DomainMapper_Impl::CloseFieldCommand() stuffs fully formed
3081 // field instructions in here, but if the field doesn't originate
3082 // from those filters it won't have that
3083 if (!o3tl::starts_with(o3tl::trim(sRet), u"CITATION"))
3084 {
3085 sRet = FieldString(ww::eCITATION) + " \"" + sRet + "\"";
3086 }
3087 GetExport().OutputField( pField, ww::eCITATION, sRet );
3088 }
3089 break;
3090 case SwFieldIds::Postit:
3091 //Sadly only possible for word in main document text
3092 if (GetExport().m_nTextTyp == TXT_MAINTEXT)
3093 {
3094 PostitField( pField );
3095 }
3096 break;
3097 case SwFieldIds::Input:
3098 {
3099 const SwInputField * pInputField = dynamic_cast<const SwInputField *>(pField);
3100
3101 if (pInputField && pInputField->isFormField())
3102 GetExport().DoFormText(pInputField);
3103 else
3104 {
3105 const OUString sStr = FieldString(ww::eFILLIN) + "\""
3106 + pField->GetPar2() + "\"";
3107
3108 GetExport().OutputField(pField, ww::eFILLIN, sStr);
3109 }
3110 }
3111 break;
3112 case SwFieldIds::GetRef:
3113 {
3115 OUString sStr;
3116 const SwGetRefField& rRField = *static_cast<const SwGetRefField*>(pField);
3117 switch (nSubType)
3118 {
3119 case REF_SETREFATTR:
3120 case REF_BOOKMARK:
3121 switch (pField->GetFormat())
3122 {
3123 case REF_PAGE_PGDESC:
3124 case REF_PAGE:
3126 break;
3127 default:
3128 eField = ww::eREF;
3129 break;
3130 }
3131 {
3132 const OUString& aRefName(rRField.GetSetRefName());
3133 sStr = FieldString(eField)
3134 + MSWordExportBase::GetBookmarkName(nSubType, &aRefName, 0);
3135 }
3136 switch (pField->GetFormat())
3137 {
3138 case REF_NUMBER:
3139 sStr += " \\r";
3140 break;
3142 sStr += " \\n";
3143 break;
3145 sStr += " \\w";
3146 break;
3147 }
3148 break;
3149 case REF_SEQUENCEFLD:
3150 {
3151 // Not implemented for RTF
3152 if(GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::RTF)
3153 break;
3154
3155 switch (pField->GetFormat())
3156 {
3157 case REF_PAGE:
3158 case REF_PAGE_PGDESC:
3160 break;
3161 default:
3162 eField = ww::eREF;
3163 break;
3164 }
3165 // Generate a unique bookmark name
3166 {
3167 OUString sName{rRField.GetSetRefName() + OUString::number(rRField.GetSeqNo())};
3168 switch (pField->GetFormat())
3169 {
3170 case REF_PAGE:
3171 case REF_PAGE_PGDESC:
3172 case REF_CONTENT:
3173 case REF_UPDOWN:
3174 sName += "_full";
3175 break;
3176 case REF_ONLYNUMBER:
3177 sName += "_label_and_number";
3178 break;
3179 case REF_ONLYCAPTION:
3180 sName += "_caption_only";
3181 break;
3182 case REF_ONLYSEQNO:
3183 sName += "_number_only";
3184 break;
3185 default: // Ignore other types of reference fields
3186 eField = ww::eNONE;
3187 break;
3188 }
3190 }
3191 switch (pField->GetFormat())
3192 {
3193 case REF_NUMBER:
3194 sStr += " \\r";
3195 break;
3197 sStr += " \\n";
3198 break;
3200 sStr += " \\w";
3201 break;
3202 }
3203 break;
3204 }
3205 case REF_FOOTNOTE:
3206 case REF_ENDNOTE:
3207 switch (pField->GetFormat())
3208 {
3209 case REF_PAGE_PGDESC:
3210 case REF_PAGE:
3212 break;
3213 case REF_UPDOWN:
3214 eField = ww::eREF;
3215 break;
3216 default:
3217 eField =
3218 REF_ENDNOTE == nSubType ? ww::eNOTEREF : ww::eFOOTREF;
3219 break;
3220 }
3221 sStr = FieldString(eField)
3222 + MSWordExportBase::GetBookmarkName(nSubType, nullptr, rRField.GetSeqNo());
3223 break;
3224 }
3225
3226 if (eField != ww::eNONE)
3227 {
3228 switch (pField->GetFormat())
3229 {
3230 case REF_UPDOWN:
3231 sStr += " \\p \\h "; // with hyperlink
3232 break;
3233 case REF_CHAPTER:
3234 sStr += " \\n \\h "; // with hyperlink
3235 break;
3236 default:
3237 sStr += " \\h "; // insert hyperlink
3238 break;
3239 }
3240 GetExport().OutputField(pField, eField, sStr);
3241 }
3242 else
3243 bWriteExpand = true;
3244 }
3245 break;
3247 {
3248 /*
3249 We need a font size to fill in the defaults, if these are overridden
3250 (as they generally are) by character properties then those properties
3251 win.
3252
3253 The fontsize that is used in MS for determining the defaults is always
3254 the CJK fontsize even if the text is not in that language, in OOo the
3255 largest fontsize used in the field is the one we should take, but
3256 whatever we do, word will actually render using the fontsize set for
3257 CJK text. Nevertheless we attempt to guess whether the script is in
3258 asian or western text based up on the first character and use the
3259 font size of that script as our default.
3260 */
3261 assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
3262 sal_uInt16 nScript = g_pBreakIt->GetBreakIter()->getScriptType( pField->GetPar1(), 0);
3263
3265 tools::Long nHeight = GetExport().GetItem(nFontHeightWhich).GetHeight();
3266
3267 nHeight = (nHeight + 10) / 20; //Font Size in points;
3268
3269 /*
3270 Divide the combined char string into its up and down part. Get the
3271 font size and fill in the defaults as up == half the font size and
3272 down == a fifth the font size
3273 */
3274 const sal_Int32 nAbove = (pField->GetPar1().getLength()+1)/2;
3275 const OUString sStr = FieldString(ww::eEQ)
3276 + "\\o (\\s\\up "
3277 + OUString::number(nHeight/2)
3278 + "("
3279 + pField->GetPar1().subView(0, nAbove)
3280 + "), \\s\\do "
3281 + OUString::number(nHeight/5)
3282 + "("
3283 + pField->GetPar1().subView(nAbove)
3284 + "))";
3285 GetExport().OutputField(pField, ww::eEQ, sStr);
3286 }
3287 break;
3289 bWriteExpand = DropdownField( pField );
3290 break;
3292 bWriteExpand = true;
3293 if (GetExport().m_bOutKF && rField.GetTextField())
3294 {
3295 const SwTextNode *pTextNd = GetExport().GetHdFtPageRoot();
3296 if (!pTextNd)
3297 {
3298 pTextNd = GetExport().m_pCurPam->GetPointNode().GetTextNode();
3299 }
3300
3301 if (pTextNd)
3302 {
3303 SwChapterField aCopy(*static_cast<const SwChapterField*>(pField));
3304 aCopy.ChangeExpansion(*pTextNd, false);
3305 const OUString sStr = FieldString(ww::eSTYLEREF)
3306 + " "
3307 + OUString::number(aCopy.GetLevel() + 1)
3308 + " \\* MERGEFORMAT ";
3309 GetExport().OutputField(pField, ww::eSTYLEREF, sStr);
3310 bWriteExpand = false;
3311 }
3312 }
3313 break;
3315 {
3316 OUString sExpand(pField->GetPar2());
3317 if (!sExpand.isEmpty())
3318 {
3319 HiddenField( *pField );
3320 }
3321 }
3322 break;
3324 bWriteExpand = PlaceholderField( pField );
3325 break;
3326 case SwFieldIds::Macro:
3327 {
3328 const OUString sStr = " MACROBUTTON "
3329 + pField->GetPar1().replaceFirst("StarOffice.Standard.Modul1.", "")
3330 + " "
3331 + lcl_GetExpandedField(*pField);
3332 GetExport().OutputField( pField, ww::eMACROBUTTON, sStr );
3333 }
3334 break;
3335 case SwFieldIds::Table:
3336 {
3338 OUString aExpand = OUString::Concat(" =") + o3tl::trim(pField->GetFieldName());
3339 GetExport().OutputField(pField, eField, aExpand);
3340 }
3341 break;
3342 case SwFieldIds::User:
3343 {
3345 OUString aExpand = FieldString(eField) + pField->GetPar1() + " ";
3346 GetExport().OutputField(pField, eField, aExpand);
3347 }
3348 break;
3349 default:
3350 bWriteExpand = true;
3351 break;
3352 }
3353
3354 if (bWriteExpand)
3355 WriteExpand( pField );
3356}
3357
3359{
3360 if ( auto pTextNd = dynamic_cast< const SwContentNode *>( GetExport().m_pOutFormatNode ) )
3361 {
3362 Point const origin;
3363 Point aLayPos = pTextNd->FindLayoutRect( false, &origin ).Pos();
3364
3365 SwPosition aPos( *pTextNd );
3366 ww8::Frame aFrame( *rFlyContent.GetFrameFormat(), std::move(aPos) );
3367
3368 OutputFlyFrame_Impl( aFrame, aLayPos );
3369 }
3370}
3371
3372// TOXMarks are still missing
3373
3374// WW allows detailed settings for hyphenation only for the whole document.
3375// One could implement following mimic: The values of the style "Standard" will
3376// be set in the Document Properties ( DOP ) if they exist.
3377
3378// ACK. This suggestion fits exactly to our implementation of the import,
3379// therefore I'll implement that right now. (KHZ, 07/15/2000)
3381{
3382 // sprmPFNoAutoHyph
3384
3385 m_rWW8Export.m_pO->push_back( rHyphenZone.IsHyphen() ? 0 : 1 );
3386}
3387
3389{
3391 m_rWW8Export.m_pO->push_back( rScriptSpace.GetValue() ? 1 : 0 );
3392}
3393
3395{
3397 m_rWW8Export.m_pO->push_back( rItem.GetValue() ? 1 : 0 );
3398}
3399
3401{
3403 m_rWW8Export.m_pO->push_back( rItem.GetValue() ? 1 : 0 );
3404}
3405
3407{
3408 // sprmPFUsePgsuSettings
3409
3411 m_rWW8Export.m_pO->push_back( rGrid.GetValue() ? 1 : 0 );
3412}
3413
3415{
3416 // sprmPWAlignFont
3417
3419
3420 SvxParaVertAlignItem::Align nAlign = rAlign.GetValue();
3421 sal_uInt16 nVal;
3422 switch ( nAlign )
3423 {
3425 nVal = 2;
3426 break;
3428 nVal = 0;
3429 break;
3431 nVal = 1;
3432 break;
3434 nVal = 3;
3435 break;
3437 nVal = 4;
3438 break;
3439 default:
3440 nVal = 4;
3441 OSL_FAIL( "Unknown vert alignment" );
3442 break;
3443 }
3444 m_rWW8Export.InsUInt16( nVal );
3445}
3446
3447// NoHyphen: I didn't find an equal in the SW UI and WW UI
3448
3449// RefMark, NoLineBreakHere are still missing
3450
3452{
3453 ww::bytes aAttrArr;
3454 const bool bAutoNum = rFootnote.GetNumStr().isEmpty();
3455 if( bAutoNum )
3456 {
3457 static const sal_uInt8 aSpec[] =
3458 {
3459 0x03, 0x6a, 0, 0, 0, 0, // sprmCObjLocation
3460 0x55, 0x08, 1 // sprmCFSpec
3461 };
3462
3463 aAttrArr.insert(aAttrArr.end(), aSpec, aSpec+sizeof(aSpec));
3464 }
3465
3466 // sprmCIstd
3467 const SwEndNoteInfo* pInfo;
3468 if( rFootnote.IsEndNote() )
3469 pInfo = &m_rDoc.GetEndNoteInfo();
3470 else
3471 pInfo = &m_rDoc.GetFootnoteInfo();
3472 const SwCharFormat* pCFormat = pOutArr
3473 ? pInfo->GetAnchorCharFormat( m_rDoc )
3474 : pInfo->GetCharFormat( m_rDoc );
3476 SwWW8Writer::InsUInt16( aAttrArr, GetId( pCFormat ) );
3477
3478 // fSpec-Attribute true
3479 // For Auto-Number a special character must go
3480 // into the text and therefore a fSpec attribute
3481 m_pChpPlc->AppendFkpEntry( Strm().Tell() );
3482 if( bAutoNum )
3483 WriteChar( 0x02 ); // auto number character
3484 else
3485 // user numbering
3486 OutSwString(rFootnote.GetNumStr(), 0, rFootnote.GetNumStr().getLength());
3487
3488 if( pOutArr )
3489 {
3490 // insert at start of array, so the "hard" attribute overrule the
3491 // attributes of the character template
3492 pOutArr->insert( pOutArr->begin(), aAttrArr.begin(), aAttrArr.end() );
3493 }
3494 else
3495 {
3496 std::unique_ptr<ww::bytes> pOwnOutArr(new ww::bytes);
3497
3498 // insert at start of array, so the "hard" attribute overrule the
3499 // attributes of the character template
3500 pOwnOutArr->insert(pOwnOutArr->begin(), aAttrArr.begin(), aAttrArr.end());
3501
3502 // write for the ftn number in the content, the font of the anchor
3503 const SwTextFootnote* pTextFootnote = rFootnote.GetTextFootnote();
3504 if( pTextFootnote )
3505 {
3506 std::unique_ptr<ww::bytes> pOld = std::move(m_pO);
3507 m_pO = std::move(pOwnOutArr);
3509
3510 pCFormat = pInfo->GetCharFormat( m_rDoc );
3511
3512 pTextFootnote->GetTextNode().GetParaAttr(aSet,
3513 pTextFootnote->GetStart(), pTextFootnote->GetStart() + 1, true);
3514 if (aSet.Count())
3515 {
3516 m_pAttrOutput->OutputItem( aSet.Get( RES_CHRATR_FONT ) );
3517 }
3518 else
3519 {
3520 m_pAttrOutput->OutputItem( pCFormat->GetAttrSet().Get(RES_CHRATR_FONT) );
3521 }
3522 pOwnOutArr = std::move(m_pO);
3523 m_pO = std::move(pOld);
3524 }
3525 m_pChpPlc->AppendFkpEntry( Strm().Tell(), pOwnOutArr->size(),
3526 pOwnOutArr->data() );
3527 }
3528}
3529
3530static bool lcl_IsAtTextEnd(const SwFormatFootnote& rFootnote)
3531{
3532 bool bRet = true;
3533 if( rFootnote.GetTextFootnote() )
3534 {
3535 sal_uInt16 nWh = rFootnote.IsEndNote() ? sal_uInt16(RES_END_AT_TXTEND)
3536 : sal_uInt16(RES_FTN_AT_TXTEND);
3537 const SwSectionNode* pSectNd = rFootnote.GetTextFootnote()->GetTextNode().
3538 FindSectionNode();
3539 while( pSectNd && FTNEND_ATPGORDOCEND ==
3540 static_cast<const SwFormatFootnoteEndAtTextEnd&>(pSectNd->GetSection().GetFormat()->
3541 GetFormatAttr( nWh)).GetValue() )
3542 pSectNd = pSectNd->StartOfSectionNode()->FindSectionNode();
3543
3544 if (!pSectNd)
3545 bRet = false; // the is ftn/end collected at Page- or Doc-End
3546 }
3547 return bRet;
3548}
3549
3551{
3552 sal_uInt16 nTyp;
3553 if ( rFootnote.IsEndNote() )
3554 {
3555 nTyp = REF_ENDNOTE;
3556 if ( GetExport().m_bEndAtTextEnd )
3557 GetExport().m_bEndAtTextEnd = lcl_IsAtTextEnd( rFootnote );
3558 }
3559 else
3560 {
3561 nTyp = REF_FOOTNOTE;
3562 if ( GetExport().m_bFootnoteAtTextEnd )
3564 }
3565
3566 // if any reference to this footnote/endnote then insert an internal
3567 // Bookmark.
3568 OUString sBkmkNm;
3569 if ( GetExport().HasRefToFootOrEndnote( rFootnote.IsEndNote(), rFootnote.GetTextFootnote()->GetSeqRefNo()))
3570 {
3571 sBkmkNm = MSWordExportBase::GetBookmarkName( nTyp, nullptr,
3572 rFootnote.GetTextFootnote()->GetSeqRefNo() );
3573 GetExport().AppendBookmark( sBkmkNm );
3574 }
3575
3576 TextFootnote_Impl( rFootnote );
3577
3578 if ( !sBkmkNm.isEmpty() )
3579 GetExport().AppendBookmark( sBkmkNm ); // FIXME: Why is it added twice? Shouldn't this one go to WW8AttributeOutput::TextFootnote_Impl()?
3580}
3581
3583{
3584 WW8_WrPlcFootnoteEdn* pFootnoteEnd;
3585 if ( rFootnote.IsEndNote() || GetExport().m_rDoc.GetFootnoteInfo().m_ePos == FTNPOS_CHAPTER )
3586 pFootnoteEnd = m_rWW8Export.m_pEdn.get();
3587 else
3588 pFootnoteEnd = m_rWW8Export.m_pFootnote.get();
3589
3590 pFootnoteEnd->Append( m_rWW8Export.Fc2Cp( m_rWW8Export.Strm().Tell() ), rFootnote );
3592}
3593
3595{
3596 if( rCharFormat.GetCharFormat() )
3597 {
3599
3601 }
3602}
3603
3604/*
3605 See ww8par6.cxx Read_DoubleLine for some more info
3606 */
3608{
3609 // #i28331# - check that bOn is set
3610 if ( !rTwoLines.GetValue() )
3611 return;
3612
3614 m_rWW8Export.m_pO->push_back( sal_uInt8(0x06) ); //len 6
3615 m_rWW8Export.m_pO->push_back( sal_uInt8(0x02) );
3616
3617 sal_Unicode cStart = rTwoLines.GetStartBracket();
3618 sal_Unicode cEnd = rTwoLines.GetEndBracket();
3619
3620 /*
3621 As per usual we have problems. We can have separate left and right brackets
3622 in OOo, it doesn't appear that you can in word. Also in word there appear
3623 to only be a limited number of possibilities, we can use pretty much
3624 anything.
3625
3626 So if we have none, we export none, if either bracket is set to a known
3627 word type we export both as that type (with the bracket winning out in
3628 the case of a conflict simply being the order of test here.
3629
3630 Upshot being a documented created in word will be reexported with no
3631 ill effects.
3632 */
3633
3634 sal_uInt16 nType;
3635 if (!cStart && !cEnd)
3636 nType = 0;
3637 else if ((cStart == '{') || (cEnd == '}'))
3638 nType = 4;
3639 else if ((cStart == '<') || (cEnd == '>'))
3640 nType = 3;
3641 else if ((cStart == '[') || (cEnd == ']'))
3642 nType = 2;
3643 else
3644 nType = 1;
3646 static const sal_uInt8 aZeroArr[ 3 ] = { 0, 0, 0 };
3647 m_rWW8Export.m_pO->insert( m_rWW8Export.m_pO->end(), aZeroArr, aZeroArr+3);
3648}
3649
3651{
3652 sal_uInt16 nOutLvl = rItem.GetValue();
3653
3654 // Do not write out default level (Body Text) if there is no inheritance, or if the level matches the inherited value
3655 const SfxUInt16Item* pInherited = nullptr;
3656 if (auto pNd = dynamic_cast<const SwContentNode*>(GetExport().m_pOutFormatNode)) //paragraph
3657 pInherited = static_cast<SwTextFormatColl&>(pNd->GetAnyFormatColl()).GetAttrSet().GetItem<SfxUInt16Item>(RES_PARATR_OUTLINELEVEL);
3660 if ((pInherited && pInherited->GetValue() == nOutLvl)
3661 || (!pInherited && !nOutLvl))
3662 return;
3663
3664 ParaOutlineLevel(rItem);
3665}
3666
3668{
3669 const SwTextNode* pTextNd = nullptr;
3670 if (rNumRule.GetValue().isEmpty())
3671 {
3672 ParaNumRule_Impl(pTextNd, 0, 0);
3673 return;
3674 }
3675 const SwNumRule* pRule = GetExport().m_rDoc.FindNumRulePtr(
3676 rNumRule.GetValue() );
3677 if (!pRule)
3678 return;
3679
3680 sal_uInt16 nNumId = GetExport().GetNumberingId(*pRule) + 1;
3681 sal_uInt8 nLvl = 0;
3682
3683 if (!GetExport().m_pOutFormatNode)
3684 {
3685 ParaNumRule_Impl(pTextNd, nLvl, nNumId);
3686 return;
3687 }
3688
3689 if ( dynamic_cast< const SwContentNode *>( GetExport().m_pOutFormatNode ) != nullptr )
3690 {
3691 pTextNd = static_cast<const SwTextNode*>(GetExport().m_pOutFormatNode);
3692
3693 if( pTextNd->IsCountedInList())
3694 {
3695 int nLevel = pTextNd->GetActualListLevel();
3696
3697 if (nLevel < 0)
3698 nLevel = 0;
3699
3700 if (nLevel >= MAXLEVEL)
3701 nLevel = MAXLEVEL - 1;
3702
3703 nLvl = static_cast< sal_uInt8 >(nLevel);
3704
3706 {
3707 // tdf#95848 find the abstract list definition
3708 OUString const listId(pTextNd->GetListId());
3709 if (!listId.isEmpty()
3710 && (listId != pRule->GetDefaultListId() // default list id uses the 1:1 mapping
3711 || pTextNd->IsListRestart()) // or restarting previous list
3712 )
3713 {
3714 SwList const*const pList(
3716 if (pList)
3717 {
3718 SwNumRule const*const pAbstractRule(
3720 pList->GetDefaultListStyleName()));
3721 assert(pAbstractRule);
3722 if (pAbstractRule == pRule && !pTextNd->IsListRestart())
3723 {
3724 // different list, but no override
3725 nNumId = GetExport().DuplicateAbsNum(listId, *pAbstractRule) + 1;
3726 }
3727 else
3728 {
3729 nNumId = GetExport().OverrideNumRule(
3730 *pRule, listId, *pAbstractRule) + 1;
3731
3732 if (pTextNd->IsListRestart())
3733 {
3734 // For restarted lists we should also keep value for
3735 // future w:lvlOverride / w:startOverride
3736 GetExport().AddListLevelOverride(nNumId-1, pTextNd->GetActualListLevel(),
3737 pTextNd->GetActualListStartValue());
3738 }
3739 }
3740 }
3741 }
3742 }
3743 else if (pTextNd->IsListRestart())
3744 {
3745 sal_uInt16 nStartWith = static_cast<sal_uInt16>(pTextNd->GetActualListStartValue());
3746 nNumId = GetExport().DuplicateNumRule(pRule, nLvl, nStartWith);
3747 if (USHRT_MAX != nNumId)
3748 ++nNumId;
3749 }
3750 }
3751 else
3752 {
3753 // #i44815# adjust numbering for numbered paragraphs
3754 // without number. These paragraphs will receive a
3755 // list id 0, which WW interprets as 'no number'.
3756 nNumId = 0;
3757 }
3758 }
3759 else if ( auto pC = dynamic_cast< const SwTextFormatColl *>( GetExport().m_pOutFormatNode ) )
3760 {
3761 if ( pC && pC->IsAssignedToListLevelOfOutlineStyle() )
3762 nLvl = static_cast< sal_uInt8 >( pC->GetAssignedOutlineStyleLevel() );
3763 else
3764 {
3765 const SfxItemSet* pSet = GetExport().m_pISet;
3766 if (pSet && pSet->HasItem(RES_PARATR_LIST_LEVEL))
3767 {
3768 const SfxInt16Item* pItem = pSet->GetItem(RES_PARATR_LIST_LEVEL);
3769 nLvl = pItem->GetValue();
3770 }
3771 }
3772 }
3773
3774 if ( nLvl >= WW8ListManager::nMaxLevel )
3775 nLvl = WW8ListManager::nMaxLevel - 1;
3776
3777 ParaNumRule_Impl( pTextNd, nLvl, nNumId);
3778}
3779
3781 sal_Int32 const nLvl, sal_Int32 const nNumId)
3782{
3783 if (USHRT_MAX == nNumId)
3784 return;
3785
3786 // write sprmPIlvl and sprmPIlfo
3788 m_rWW8Export.m_pO->push_back( ::sal::static_int_cast<sal_uInt8>(nLvl) );
3790 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, ::sal::static_int_cast<sal_uInt16>(nNumId) );
3791}
3792
3793/* File FRMATR.HXX */
3794
3796{
3797 if( m_rWW8Export.m_bOutFlyFrameAttrs ) // Flys
3798 {
3800 return; // Fly around graphic -> Auto-size
3801
3802 //???? What about percentages ???
3803 if ( rSize.GetWidth() && rSize.GetWidthSizeType() == SwFrameSize::Fixed)
3804 {
3805 //"sprmPDxaWidth"
3807 m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>(rSize.GetWidth()) );
3808 }
3809
3810 if ( rSize.GetHeight() )
3811 {
3812 // sprmPWHeightAbs
3814
3815 sal_uInt16 nH = 0;
3816 switch ( rSize.GetHeightSizeType() )
3817 {
3818 case SwFrameSize::Variable: break;
3819 case SwFrameSize::Fixed: nH = o3tl::narrowing<sal_uInt16>(rSize.GetHeight()) & 0x7fff; break;
3820 default: nH = o3tl::narrowing<sal_uInt16>(rSize.GetHeight()) | 0x8000; break;
3821 }
3822 m_rWW8Export.InsUInt16( nH );
3823 }
3824 }
3825 else if( m_rWW8Export.m_bOutPageDescs ) // PageDesc : width + height
3826 {
3828 {
3829 /*sprmSBOrientation*/
3831 m_rWW8Export.m_pO->push_back( 2 );
3832 }
3833
3834 /*sprmSXaPage*/
3837 msword_cast<sal_uInt16>(SvxPaperInfo::GetSloppyPaperDimension(rSize.GetWidth())));
3838
3839 /*sprmSYaPage*/
3842 msword_cast<sal_uInt16>(SvxPaperInfo::GetSloppyPaperDimension(rSize.GetHeight())));
3843 }
3844}
3845
3846// FillOrder is still missing
3847
3856{
3857 OSL_ENSURE( nChar, "replaced with 0 crashes WW97/95" );
3858
3859 bool bReplaced = false;
3860 SvStream& rStrm = Strm();
3861 sal_uInt64 nRetPos = 0, nPos = rStrm.Tell();
3862 //If there is at least two characters already output
3863 if (nPos - 2 >= o3tl::make_unsigned(m_pFib->m_fcMin))
3864 {
3865 sal_uInt16 nUCode=0;
3866
3867 rStrm.SeekRel(-2);
3868 rStrm.ReadUInt16( nUCode );
3869 //If the last char was a cr
3870 if (nUCode == 0x0d) // CR ?
3871 {
3872 if ((nChar == 0x0c) &&
3873 (nPos - 4 >= o3tl::make_unsigned(m_pFib->m_fcMin)))
3874 {
3875 rStrm.SeekRel(-4);
3876 rStrm.ReadUInt16( nUCode );
3877 }
3878 else
3879 {
3880 rStrm.SeekRel(-2);
3881 nUCode = 0x0;
3882 }
3883 //And the para is not of len 0, then replace this cr with the mark
3884 //#120140# If there is a cr before a column break, need replace the cr. So remove the "nChar==0x0e" check.
3885 if( nUCode == 0x0d )
3886 bReplaced = false;
3887 else
3888 {
3889 bReplaced = true;
3890 WriteChar(nChar);
3891 nRetPos = nPos;
3892 }
3893 }
3894 else if ((nUCode == 0x0c) && (nChar == 0x0e))
3895 {
3896 // a column break after a section has no effect in writer
3897 bReplaced = true;
3898 }
3899 rStrm.Seek( nPos );
3900 }
3901 else
3902 bReplaced = true;
3903
3904 if (!bReplaced)
3905 {
3906 // then write as normal char
3907 WriteChar(nChar);
3908 m_pPiece->SetParaBreak();
3909 m_pPapPlc->AppendFkpEntry(rStrm.Tell());
3910 m_pChpPlc->AppendFkpEntry(rStrm.Tell());
3911 nRetPos = rStrm.Tell();
3912 }
3913 return nRetPos;
3914}
3915
3916void WW8AttributeOutput::TableRowEnd(sal_uInt32 nDepth)
3917{
3918 if ( nDepth == 1 )
3919 m_rWW8Export.WriteChar( 0x07 );
3920 else if ( nDepth > 1 )
3921 m_rWW8Export.WriteChar( 0x0d );
3922
3923 //Technically in a word document this is a different value for a row ends
3924 //that are not row ends directly after a cell with a graphic. But it
3925 //doesn't seem to make a difference
3926 //pMagicTable->Append(Fc2Cp(Strm().Tell()),0x1B6);
3927}
3928
3930{
3931 if ( GetExport().m_bStyDef )
3932 if (auto pC = dynamic_cast< const SwTextFormatColl *>( GetExport().m_pOutFormatNode ) )
3933 {
3934 if ( (SfxItemState::SET != pC->GetItemState( RES_BREAK, false ) ) && rPageDesc.KnowsPageDesc() )
3935 FormatBreak( SvxFormatBreakItem( SvxBreak::PageBefore, RES_BREAK ) );
3936 }
3937}
3938
3940{
3941 // sprmPPageBreakBefore/sprmPFPageBreakBefore
3943
3944 m_rWW8Export.m_pO->push_back( bBreak ? 1 : 0 );
3945}
3946
3953{
3954 if ( GetExport().m_bStyDef )
3955 {
3956 switch ( rBreak.GetBreak() )
3957 {
3958 case SvxBreak::NONE:
3959 case SvxBreak::PageBefore:
3960 case SvxBreak::PageBoth:
3961 PageBreakBefore( rBreak.GetValue() != SvxBreak::NONE );
3962 break;
3963 default:
3964 break;
3965 }
3966 }
3967 else if ( !GetExport().m_pParentFrame )
3968 {
3969 sal_uInt8 nC = 0;
3970 bool bBefore = false;
3971 // #i76300# - Note: Can only be <true>, if <bBefore> equals <false>.
3972 bool bCheckForFollowPageDesc = false;
3973
3974 switch ( rBreak.GetBreak() )
3975 {
3976 case SvxBreak::NONE: // disabled
3977 if ( !GetExport().m_bBreakBefore )
3978 PageBreakBefore( false );
3979 return;
3980
3981 case SvxBreak::ColumnBefore: // ColumnBreak
3982 bBefore = true;
3983 [[fallthrough]];
3984 case SvxBreak::ColumnAfter:
3985 case SvxBreak::ColumnBoth:
3987 || GetExport().Sections().CurrentNumberOfColumns( GetExport().m_rDoc ) > 1 )
3988 {
3990 }
3991 break;
3992
3993 case SvxBreak::PageBefore: // PageBreak
3994 // From now on(fix for #i77900#) we prefer to save a page break
3995 // as paragraph attribute (if the exporter is OK with that),
3996 // this has to be done after the export of the paragraph ( =>
3997 // !GetExport().bBreakBefore )
3998 if (GetExport().PreferPageBreakBefore())
3999 {
4000 if (!GetExport().m_bBreakBefore)
4001 PageBreakBefore(true);
4002 }
4003 else
4004 {
4005 bBefore = true;
4006 nC = msword::PageBreak;
4007 }
4008 break;
4009 case SvxBreak::PageAfter:
4010 case SvxBreak::PageBoth:
4011 nC = msword::PageBreak;
4012 // #i76300# - check for follow page description,
4013 // if current writing attributes of a paragraph.
4014 if ( dynamic_cast< const SwTextNode* >( GetExport().m_pOutFormatNode ) &&
4015 GetExport().GetCurItemSet() )
4016 {
4017 bCheckForFollowPageDesc = true;
4018 }
4019 break;
4020
4021 default:
4022 break;
4023 }
4024
4025 if ( ( bBefore == GetExport().m_bBreakBefore ) && nC )
4026 {
4027 // #i76300#
4028 bool bFollowPageDescWritten = false;
4029 if ( bCheckForFollowPageDesc )
4030 {
4031 bFollowPageDescWritten =
4032 GetExport().OutputFollowPageDesc( GetExport().GetCurItemSet(),
4033 dynamic_cast<const SwTextNode*>( GetExport().m_pOutFormatNode ) );
4034 }
4035 if ( !bFollowPageDescWritten )
4036 {
4037 SectionBreak(nC, !bBefore);
4038 }
4039 }
4040 }
4041}
4042
4043void WW8AttributeOutput::SectionBreak( sal_uInt8 nC, bool /*bBreakAfter*/, const WW8_SepInfo* /*pSectionInfo*/, bool /*bExtraPageBreak*/ )
4044{
4045 m_rWW8Export.ReplaceCr( nC );
4046}
4047
4049{
4050 MSWordStyles * pStyles = GetExport().m_pStyles.get();
4051 const SwFormat * pSwFormat = pStyles->GetSwFormat(0);
4052
4053 sal_uInt32 nPageCharSize = 0;
4054
4055 if (pSwFormat != nullptr)
4056 {
4057 nPageCharSize = pSwFormat->GetFormatAttr(RES_CHRATR_FONTSIZE).GetHeight();
4058 }
4059 sal_uInt16 nPitch = rGrid.IsSquaredMode() ? rGrid.GetBaseHeight() :
4060 rGrid.GetBaseWidth( );
4061
4062 sal_Int32 nCharWidth = nPitch - nPageCharSize;
4063 sal_Int32 nFraction = nCharWidth % 20;
4064 if ( nCharWidth < 0 )
4065 nFraction = 20 + nFraction;
4066 nFraction = ( nFraction * 0xFFF ) / 20;
4067 nFraction = ( nFraction & 0x00000FFF );
4068
4069 sal_Int32 nMain = nCharWidth / 20;
4070 if ( nCharWidth < 0 )
4071 nMain -= 1;
4072 nMain = nMain * 0x1000;
4073 nMain = ( nMain & 0xFFFFF000 );
4074
4075 return sal_uInt32( nFraction + nMain );
4076}
4077
4079{
4081 return;
4082
4083 sal_uInt16 nGridType = 0;
4084 switch ( rGrid.GetGridType() )
4085 {
4086 default:
4087 OSL_FAIL("Unknown grid type");
4088 [[fallthrough]];
4089 case GRID_NONE:
4090 nGridType = 0;
4091 break;
4092 case GRID_LINES_ONLY:
4093 nGridType = 2;
4094 break;
4095 case GRID_LINES_CHARS:
4096 if ( rGrid.IsSnapToChars() )
4097 nGridType = 3;
4098 else
4099 nGridType = 1;
4100 break;
4101 }
4103 m_rWW8Export.InsUInt16( nGridType );
4104
4105 sal_uInt16 nHeight = rGrid.GetBaseHeight() + rGrid.GetRubyHeight();
4107 m_rWW8Export.InsUInt16( nHeight );
4108
4111}
4112
4114{
4116 return;
4117
4118 sal_uInt16 nVal;
4119 switch ( rPaperBin.GetValue() )
4120 {
4121 case 0: nVal = 15; break; // Automatically select
4122 case 1: nVal = 1; break; // Upper paper tray
4123 case 2: nVal = 4; break; // Manual paper feed
4124 default: nVal = 0; break;
4125 }
4126
4127 if ( nVal )
4128 {
4131
4132 m_rWW8Export.InsUInt16( nVal );
4133 }
4134}
4135
4137{
4138 // Flys are still missing ( see RTF )
4139
4140 if ( m_rWW8Export.m_bOutFlyFrameAttrs ) // Flys
4141 {
4142 // sprmPDxaFromText10
4144 // use average, since WW only knows one value
4145 m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>( ( rLR.GetLeft() + rLR.GetRight() ) / 2 ) );
4146 }
4147 else if ( m_rWW8Export.m_bOutPageDescs ) // PageDescs
4148 {
4149 m_pageMargins.nLeft = 0;
4151
4152 if ( const SvxBoxItem* pBoxItem = m_rWW8Export.HasItem( RES_BOX ) )
4153 {
4154 m_pageMargins.nLeft = pBoxItem->CalcLineSpace( SvxBoxItemLine::LEFT, /*bEvenIfNoLine*/true );
4155 m_pageMargins.nRight = pBoxItem->CalcLineSpace( SvxBoxItemLine::RIGHT, /*bEvenIfNoLine*/true );
4156 }
4157
4158 m_pageMargins.nLeft += sal::static_int_cast<sal_uInt16>(rLR.GetLeft());
4159 m_pageMargins.nRight += sal::static_int_cast<sal_uInt16>(rLR.GetRight());
4160 sal_uInt16 nGutter = rLR.GetGutterMargin();
4161
4162 // sprmSDxaLeft
4165
4166 // sprmSDxaRight
4169
4170 if (nGutter)
4171 {
4172 // sprmSDzaGutter
4174 m_rWW8Export.InsUInt16(nGutter);
4175 }
4176 }
4177 else
4178 { // normal paragraphs
4179 // sprmPDxaLeft
4180 m_rWW8Export.InsUInt16( 0x845E ); //asian version ?
4181 m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>(rLR.GetTextLeft()) );
4182
4183 // sprmPDxaRight
4184 m_rWW8Export.InsUInt16( 0x845D ); //asian version ?
4185 m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>(rLR.GetRight()) );
4186
4187 // sprmPDxaLeft1
4188 m_rWW8Export.InsUInt16( 0x8460 ); //asian version ?
4190 }
4191}
4192
4194{
4195 if (!rRtlGutter.GetValue())
4196 {
4197 return;
4198 }
4199
4200 // sprmSFRTLGutter
4202 m_rWW8Export.m_pO->push_back(1);
4203}
4204
4206{
4207 // Write the linebreak itself.
4208 m_rWW8Export.WriteChar(0x0b);
4209
4210 // sprmCLbcCRJ
4212 m_rWW8Export.m_pO->push_back(rLineBreak.GetEnumValue());
4213}
4214
4216{
4217 // Flys are still missing ( see RTF )
4218
4219 if ( m_rWW8Export.m_bOutFlyFrameAttrs ) // Flys
4220 {
4221 // sprmPDyaFromText
4223 // use average, since WW only knows one value
4224 m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>( ( rUL.GetUpper() + rUL.GetLower() ) / 2 ) );
4225 }
4226 else if ( m_rWW8Export.m_bOutPageDescs ) // Page-UL
4227 {
4228 OSL_ENSURE( m_rWW8Export.GetCurItemSet(), "Impossible" );
4229 if ( !m_rWW8Export.GetCurItemSet() )
4230 return;
4231
4233
4234 if ( aDistances.HasHeader() )
4235 {
4236 //sprmSDyaHdrTop
4238 m_rWW8Export.InsUInt16( aDistances.m_DyaHdrTop );
4239 }
4240
4241 // sprmSDyaTop
4243 m_rWW8Export.InsUInt16( aDistances.m_DyaTop );
4244 m_pageMargins.nTop = aDistances.m_DyaTop;
4245
4246 if ( aDistances.HasFooter() )
4247 {
4248 //sprmSDyaHdrBottom
4251 }
4252
4253 //sprmSDyaBottom
4255 m_rWW8Export.InsUInt16( aDistances.m_DyaBottom );
4256 m_pageMargins.nBottom = aDistances.m_DyaBottom;
4257 }
4258 else
4259 {
4260 // sprmPDyaBefore
4263 // sprmPDyaAfter
4266 // sprmPFContextualSpacing
4267
4268 // Write out Contextual Spacing = false if it would have inherited a true.
4269 const SvxULSpaceItem* pInherited = nullptr;
4270 if (!rUL.GetContext())
4271 {
4272 if (auto pNd = dynamic_cast<const SwContentNode*>(m_rWW8Export.m_pOutFormatNode)) //paragraph
4273 pInherited = &static_cast<SwTextFormatColl&>(pNd->GetAnyFormatColl()).GetAttrSet().GetULSpace();
4276 }
4277 if (rUL.GetContext() || (pInherited && pInherited->GetContext()))
4278 {
4280 m_rWW8Export.m_pO->push_back( static_cast<sal_uInt8>(rUL.GetContext()) );
4281 }
4282 }
4283}
4284
4285// print, opaque, protect are still missing
4286
4288{
4290 {
4292
4293 m_rWW8Export.m_pO->push_back(
4294 ( css::text::WrapTextMode_NONE != rSurround.GetSurround() ) ? 2 : 1 );
4295 }
4296}
4297
4299{
4300
4303 return;
4304
4305 short nPos;
4306 switch( rFlyVert.GetVertOrient() )
4307 {
4309 nPos = static_cast<short>(rFlyVert.GetPos());
4310 break;
4311 case text::VertOrientation::CENTER:
4312 case text::VertOrientation::LINE_CENTER:
4313 nPos = -8;
4314 break;
4315 case text::VertOrientation::BOTTOM:
4316 case text::VertOrientation::LINE_BOTTOM:
4317 nPos = -12;
4318 break;
4319 case text::VertOrientation::TOP:
4320 case text::VertOrientation::LINE_TOP:
4321 default:
4322 nPos = -4;
4323 break;
4324 }
4325
4326 // sprmPDyaAbs
4329}
4330
4332{
4334 {
4335 OSL_ENSURE( m_rWW8Export.m_pParentFrame, "HoriOrient without mpParentFrame !!" );
4336 return;
4337 }
4338
4341 return;
4342
4343 short nPos;
4344 switch( rFlyHori.GetHoriOrient() )
4345 {
4347 nPos = static_cast<short>(rFlyHori.GetPos());
4348 if( !nPos )
4349 nPos = 1; // WW: 0 is reserved
4350 break;
4351 case text::HoriOrientation::LEFT:
4352 nPos = rFlyHori.IsPosToggle() ? -12 : 0;
4353 break;
4354 case text::HoriOrientation::RIGHT:
4355 nPos = rFlyHori.IsPosToggle() ? -16 : -8;
4356 break;
4357 case text::HoriOrientation::CENTER:
4358 case text::HoriOrientation::FULL: // FULL only for tables
4359 default:
4360 nPos = -4;
4361 break;
4362 }
4363
4364 // sprmPDxaAbs
4367}
4368
4370{
4371 OSL_ENSURE( m_rWW8Export.m_pParentFrame, "Anchor without mpParentFrame !!" );
4372
4374 return;
4375
4376 sal_uInt8 nP = 0;
4377 switch ( rAnchor.GetAnchorId() )
4378 {
4379 case RndStdIds::FLY_AT_PAGE:
4380 // vertical: page | horizontal: page
4381 nP |= (1 << 4) | (2 << 6);
4382 break;
4383 // in case of Fly as characters: set paragraph-bound!!!
4384 case RndStdIds::FLY_AT_FLY:
4385 case RndStdIds::FLY_AT_CHAR:
4386 case RndStdIds::FLY_AT_PARA:
4387 case RndStdIds::FLY_AS_CHAR:
4388 // vertical: page | horizontal: page
4389 nP |= (2 << 4) | (0 << 6);
4390 break;
4391 default:
4392 break;
4393 }
4394
4395 // sprmPPc
4397 m_rWW8Export.m_pO->push_back( nP );
4398}
4399
4401{
4402 // WW cannot have background in a section
4404 return;
4405
4406 WW8_SHD aSHD;
4407 WW8Export::TransBrush( rBrush.GetColor(), aSHD );
4408
4411
4413 m_rWW8Export.m_pO->push_back( 10 ); //size of operand: MUST be 10
4414 m_rWW8Export.InsUInt32( 0xFF000000 ); //cvFore: Foreground BGR = cvAuto
4415 m_rWW8Export.InsUInt32( SuitableBGColor( rBrush.GetColor() ) ); //cvBack
4416 m_rWW8Export.InsUInt16( 0x0000 ); //iPat: specifies the pattern used for shading = clear/100% background
4417}
4418
4420{
4421 // WW cannot have background in a section
4423 return;
4424
4425 // see MSWordExportBase::OutputItemSet for how _SOLID is handled
4426 if ( rFillStyle.GetValue() != drawing::FillStyle_NONE )
4427 return;
4428
4429 //Shd80Nil
4431 m_rWW8Export.InsUInt16( 0xffff );
4432
4433 //cvAuto
4435 m_rWW8Export.m_pO->push_back( 10 );
4436 m_rWW8Export.InsUInt32( 0xFF000000 );
4437 m_rWW8Export.InsUInt32( 0xFF000000 );
4438 m_rWW8Export.InsUInt16( 0x0000 );
4439}
4440
4442{
4443}
4444
4446 sal_uInt16 nDist, bool bShadow)
4447{
4448 sal_uInt32 nColBGR = 0;
4449 sal_uInt16 nWidth = ::editeng::ConvertBorderWidthToWord(
4450 rLine.GetBorderLineStyle(), rLine.GetWidth());
4451 sal_uInt8 brcType = 0;
4452
4453 if( nWidth ) // line ?
4454 {
4455 // BRC.brcType
4456 brcType = 0;
4457 // All the border types values are available on
4458 // http://msdn.microsoft.com/en-us/library/dd908142%28v=office.12%29.aspx
4459 switch (rLine.GetBorderLineStyle())
4460 {
4461 case SvxBorderLineStyle::SOLID:
4462 {
4463 if ( rLine.GetWidth( ) == SvxBorderLineWidth::Hairline )
4464 brcType = 5;
4465 else
4466 brcType = 1;
4467 }
4468 break;
4469 case SvxBorderLineStyle::DOTTED:
4470 brcType = 6;
4471 break;
4472 case SvxBorderLineStyle::DASHED:
4473 brcType = 7;
4474 break;
4475 case SvxBorderLineStyle::DOUBLE:
4476 case SvxBorderLineStyle::DOUBLE_THIN:
4477 brcType = 3;
4478 break;
4479 case SvxBorderLineStyle::THINTHICK_SMALLGAP:
4480 brcType = 11;
4481 break;
4482 case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
4483 brcType = 14;
4484 break;
4485 case SvxBorderLineStyle::THINTHICK_LARGEGAP:
4486 brcType = 17;
4487 break;
4488 case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
4489 brcType = 12;
4490 break;
4491 case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
4492 brcType = 15;
4493 break;
4494 case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
4495 brcType = 18;
4496 break;
4497 case SvxBorderLineStyle::EMBOSSED:
4498 brcType = 24;
4499 break;
4500 case SvxBorderLineStyle::ENGRAVED:
4501 brcType = 25;
4502 break;
4503 case SvxBorderLineStyle::OUTSET:
4504 brcType = 26;
4505 break;
4506 case SvxBorderLineStyle::INSET:
4507 brcType = 27;
4508 break;
4509 case SvxBorderLineStyle::FINE_DASHED:
4510 brcType = 22;
4511 break;
4512 case SvxBorderLineStyle::DASH_DOT:
4513 brcType = 8;
4514 break;
4515 case SvxBorderLineStyle::DASH_DOT_DOT:
4516 brcType = 9;
4517 break;
4518 default:
4519 break;
4520 }
4521
4522 // convert width from twips (1/20 pt) to eighths of a point
4523 nWidth = (( nWidth * 8 ) + 10 ) / 20;
4524 if( 0xff < nWidth )
4525 nWidth = 0xff;
4526
4527 if( 0 == nWidth ) // really thin line
4528 nWidth = 1; // don't omit
4529
4530 // BRC.cv
4531 nColBGR = wwUtility::RGBToBGR(rLine.GetColor().GetRGBColor());
4532 }
4533
4534 // BRC.dptSpace
4535 sal_uInt16 nLDist = rtl::math::round(nDist / 20.0); // unit of measurement: pt
4536 if( nLDist > 0x1f )
4537 nLDist = 0x1f;
4538
4539 return WW8_BRCVer9(nColBGR, sal_uInt8(nWidth), brcType, sal_uInt8(nLDist),
4540 bShadow, false);
4541}
4542
4549void WW8Export::Out_BorderLine(ww::bytes& rO, const SvxBorderLine* pLine,
4550 sal_uInt16 nDist, sal_uInt16 nSprmNo, sal_uInt16 nSprmNoVer9, bool bShadow)
4551{
4552 OSL_ENSURE( ( nSprmNo == 0 ) ||
4553 ( nSprmNo >= 38 && nSprmNo <= 41 ) ||
4554 ( nSprmNo >= NS_sprm::PBrcTop80::val
4555 && nSprmNo <= NS_sprm::PBrcRight80::val ) ||
4556 ( nSprmNo >= NS_sprm::SBrcTop80::val
4557 && nSprmNo <= NS_sprm::SBrcRight80::val ),
4558 "Sprm for border out is of range" );
4559
4560 WW8_BRCVer9 aBrcVer9;
4561 WW8_BRC aBrcVer8;
4562
4563 if( pLine && pLine->GetBorderLineStyle() != SvxBorderLineStyle::NONE )
4564 {
4565 aBrcVer9 = TranslateBorderLine( *pLine, nDist, bShadow );
4567 aBrcVer8 = WW8_BRC( aBrcVer9.dptLineWidth(), aBrcVer9.brcType(), ico,
4568 aBrcVer9.dptSpace(), aBrcVer9.fShadow(), aBrcVer9.fFrame() );
4569 }
4570
4571 // WW97-SprmIds
4572 if ( nSprmNo != 0 )
4573 SwWW8Writer::InsUInt16( rO, nSprmNo );
4574
4575 rO.insert( rO.end(), aBrcVer8.aBits1, aBrcVer8.aBits2+2 );
4576
4577 if ( nSprmNoVer9 != 0 )
4578 {
4579 SwWW8Writer::InsUInt16( rO, nSprmNoVer9 );
4580 rO.push_back(sizeof(WW8_BRCVer9));
4581 rO.insert( rO.end(), aBrcVer9.aBits1, aBrcVer9.aBits2+4);
4582 }
4583}
4584
4591void WW8Export::Out_SwFormatBox(const SvxBoxItem& rBox, bool bShadow)
4592{
4593 static const SvxBoxItemLine aBorders[] =
4594 {
4595 SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
4596 };
4597 static const sal_uInt16 aPBrc[] =
4598 {
4599 // WW8 SPRMs
4602 // WW9 SPRMs
4605 };
4606 static const sal_uInt16 aSBrc[] =
4607 {
4608 // WW8 SPRMs
4611 // WW9 SPRMs
4614 };
4615
4616 const SvxBoxItemLine* pBrd = aBorders;
4617 for( sal_uInt16 i = 0; i < 4; ++i, ++pBrd )
4618 {
4619 const SvxBorderLine* pLn = rBox.GetLine( *pBrd );
4620
4621 sal_uInt16 nSprmNo, nSprmNoVer9 = 0;
4622 if (m_bOutPageDescs)
4623 {
4624 nSprmNo = aSBrc[i];
4625 nSprmNoVer9 = aSBrc[i+4];
4626 }
4627 else
4628 {
4629 nSprmNo = aPBrc[i];
4630 nSprmNoVer9 = aPBrc[i+4];
4631 }
4632
4633 Out_BorderLine( *m_pO, pLn, rBox.GetDistance( *pBrd ), nSprmNo,
4634 nSprmNoVer9, bShadow );
4635 }
4636}
4637
4647{
4648 // possible and maybe better would be 0xffff
4649 static const SvxBoxItemLine aBorders[] =
4650 {
4651 SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
4652 };
4653 static const SvxBorderLine aBorderLine;
4654
4655 for(const SvxBoxItemLine & rBorder : aBorders)
4656 {
4657 const SvxBorderLine* pLn;
4658 if (pBox != nullptr)
4659 pLn = pBox->GetLine( rBorder );
4660 else
4661 pLn = & aBorderLine;
4662
4663 Out_BorderLine(rO, pLn, 0, 0, 0, false);
4664 }
4665}
4666
4668 sal_uInt8 nLimit )
4669{
4670 if ( !pBox )
4671 return;
4672
4673 static const SvxBoxItemLine aBorders[] =
4674 {
4675 SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
4676 };
4677
4678 for( int i = 0; i < 4; ++i )
4679 {
4680 const SvxBorderLine* pLn = pBox->GetLine( aBorders[i] );
4681 if (!pLn)
4682 continue;
4683
4685 m_pO->push_back( 11 );
4686 m_pO->push_back( nStart );
4687 m_pO->push_back( nLimit );
4688 m_pO->push_back( 1<<i );
4689 WW8_BRCVer9 aBrcVer9 = TranslateBorderLine( *pLn, 0, false );
4690 m_pO->insert( m_pO->end(), aBrcVer9.aBits1, aBrcVer9.aBits2+4 );
4691 }
4692}
4693
4695{
4696 // Fly around graphic -> here no border, because the
4697 // graphics header already has the border
4698 if ( m_rWW8Export.m_bOutGrf )
4699 return;
4700
4701 bool bShadow = false;
4702 const SvxShadowItem* pShadowItem = m_rWW8Export.HasItem( RES_SHADOW );
4703 if ( pShadowItem )
4704 {
4705 bShadow = ( pShadowItem->GetLocation() != SvxShadowLocation::NONE )
4706 && ( pShadowItem->GetWidth() != 0 );
4707 }
4708
4709 SvxBoxItem aBox(rBox);
4711 {
4714
4715 aBox.SetDistance(aDistances.nTop, SvxBoxItemLine::TOP);
4716 aBox.SetDistance(aDistances.nLeft, SvxBoxItemLine::LEFT);
4717 aBox.SetDistance(aDistances.nBottom, SvxBoxItemLine::BOTTOM);
4718 aBox.SetDistance(aDistances.nRight, SvxBoxItemLine::RIGHT);
4719
4720 m_bFromEdge = aDistances.bFromEdge;
4721 }
4722
4723 m_rWW8Export.Out_SwFormatBox( aBox, bShadow );
4724}
4725
4727{
4730
4731 const SvxLRSpaceItem& rLR = pFormat->GetLRSpace();
4732 SwTwips nPageSize = pFormat->GetFrameSize().GetWidth();
4733 rLeft = rLR.GetLeft();
4734 rRight = rLR.GetRight();
4735 return nPageSize;
4736}
4737
4738void WW8AttributeOutput::FormatColumns_Impl( sal_uInt16 nCols, const SwFormatCol & rCol, bool bEven, SwTwips nPageSize )
4739{
4740 // CColumns
4742 m_rWW8Export.InsUInt16( nCols - 1 );
4743
4744 // DxaColumns
4746 m_rWW8Export.InsUInt16( rCol.GetGutterWidth( true ) );
4747
4748 // LBetween
4750 m_rWW8Export.m_pO->push_back( COLADJ_NONE == rCol.GetLineAdj( )? 0 : 1 );
4751
4752 const SwColumns & rColumns = rCol.GetColumns( );
4753
4754 // FEvenlySpaced
4756 m_rWW8Export.m_pO->push_back( bEven ? 1 : 0 );
4757
4758 if ( bEven )
4759 return;
4760
4761 for ( sal_uInt16 n = 0; n < nCols; ++n )
4762 {
4763 //sprmSDxaColWidth
4765 m_rWW8Export.m_pO->push_back( static_cast<sal_uInt8>(n) );
4766 m_rWW8Export.InsUInt16( rCol.
4767 CalcPrtColWidth( n,
4768 o3tl::narrowing<sal_uInt16>(nPageSize) ) );
4769
4770 if ( n + 1 != nCols )
4771 {
4772 //sprmSDxaColSpacing
4774 m_rWW8Export.m_pO->push_back( static_cast<sal_uInt8>(n) );
4775 m_rWW8Export.InsUInt16( rColumns[n].GetRight( ) +
4776 rColumns[n + 1].GetLeft( ) );
4777 }
4778 }
4779}
4780
4782{
4783 const SwColumns& rColumns = rCol.GetColumns();
4784
4785 sal_uInt16 nCols = rColumns.size();
4786 if (1 >= nCols || GetExport( ).m_bOutFlyFrameAttrs)
4787 return;
4788
4789 // get the page width without borders !!
4790
4791 const SwFrameFormat* pFormat = GetExport( ).m_pCurrentPageDesc ? &GetExport( ).m_pCurrentPageDesc->GetMaster() : &const_cast<const SwDoc&>(GetExport().m_rDoc).GetPageDesc(0).GetMaster();
4792 const SvxFrameDirectionItem &frameDirection = pFormat->GetFrameDir();
4793 SwTwips nPageSize;
4794 if ( frameDirection.GetValue() == SvxFrameDirection::Vertical_RL_TB || frameDirection.GetValue() == SvxFrameDirection::Vertical_LR_TB )
4795 {
4796 const SvxULSpaceItem &rUL = pFormat->GetULSpace();
4797 nPageSize = pFormat->GetFrameSize().GetHeight();
4798 nPageSize -= rUL.GetUpper() + rUL.GetLower();
4799
4800 const SwFormatHeader *header = pFormat->GetAttrSet().GetItem(RES_HEADER);
4801 if ( header )
4802 {
4803 const SwFrameFormat *headerFormat = header->GetHeaderFormat();
4804 if (headerFormat)
4805 {
4806 nPageSize -= headerFormat->GetFrameSize().GetHeight();
4807 }
4808 }
4809 const SwFormatFooter *footer = pFormat->GetAttrSet().GetItem(RES_FOOTER);
4810 if ( footer )
4811 {
4812 const SwFrameFormat *footerFormat = footer->GetFooterFormat();
4813 if ( footerFormat )
4814 {
4815 nPageSize -= footerFormat->GetFrameSize().GetHeight();
4816 }
4817 }
4818 }
4819 else
4820 {
4821 const SvxLRSpaceItem &rLR = pFormat->GetLRSpace();
4822 nPageSize = pFormat->GetFrameSize().GetWidth();
4823 nPageSize -= rLR.GetLeft() + rLR.GetRight();
4824 //i120133: The Section width should consider page indent value.
4825 nPageSize -= rCol.GetAdjustValue();
4826
4827 }
4828
4829 // look if all columns are equal
4830 bool bEven = rCol.IsOrtho();
4831 if (!bEven)
4832 {
4833 bEven = true;
4834 sal_uInt16