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