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