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