LibreOffice Module sw (master) 1
rtfattributeoutput.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
21#include <memory>
22#include <cstring>
23#include "rtfsdrexport.hxx"
24#include "writerwordglue.hxx"
25#include "ww8par.hxx"
26#include <fmtcntnt.hxx>
27#include <rtl/tencinfo.h>
28#include <sal/log.hxx>
29#include <sot/exchange.hxx>
30#include <svtools/rtfkeywd.hxx>
31#include <editeng/fontitem.hxx>
32#include <editeng/tstpitem.hxx>
34#include <editeng/spltitem.hxx>
35#include <editeng/widwitem.hxx>
36#include <editeng/postitem.hxx>
37#include <editeng/wghtitem.hxx>
38#include <editeng/kernitem.hxx>
40#include <editeng/cmapitem.hxx>
41#include <editeng/wrlmitem.hxx>
42#include <editeng/udlnitem.hxx>
43#include <editeng/langitem.hxx>
45#include <editeng/fhgtitem.hxx>
46#include <editeng/colritem.hxx>
49#include <editeng/shdditem.hxx>
57#include <editeng/blinkitem.hxx>
59#include <editeng/boxitem.hxx>
60#include <editeng/brushitem.hxx>
61#include <editeng/ulspitem.hxx>
62#include <editeng/shaditem.hxx>
63#include <editeng/keepitem.hxx>
65#include <editeng/opaqitem.hxx>
67#include <svx/svdouno.hxx>
69#include <svx/xfillit0.hxx>
70#include <svx/xflgrit.hxx>
71#include <docufld.hxx>
72#include <fmtclds.hxx>
73#include <fmtrowsplt.hxx>
74#include <fmtline.hxx>
75#include <fmtanchr.hxx>
76#include <ftninfo.hxx>
77#include <htmltbl.hxx>
78#include <ndgrf.hxx>
79#include <pagedesc.hxx>
80#include <swmodule.hxx>
81#include <txtftn.hxx>
82#include <txtinet.hxx>
83#include <grfatr.hxx>
84#include <ndole.hxx>
85#include <lineinfo.hxx>
86#include <redline.hxx>
87#include <rtf.hxx>
88#include <vcl/cvtgrf.hxx>
90#include <com/sun/star/i18n/ScriptType.hpp>
91#include <svl/grabbagitem.hxx>
92#include <frmatr.hxx>
93#include <swtable.hxx>
94#include <formatflysplit.hxx>
95#include "rtfexport.hxx"
96
97using namespace ::com::sun::star;
98using namespace sw::util;
99
100static OString OutTBLBorderLine(RtfExport const& rExport, const editeng::SvxBorderLine* pLine,
101 const char* pStr)
102{
103 OStringBuffer aRet;
104 if (pLine && !pLine->isEmpty())
105 {
106 aRet.append(pStr);
107 // single line
108 switch (pLine->GetBorderLineStyle())
109 {
110 case SvxBorderLineStyle::SOLID:
111 {
114 else
115 aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRS);
116 }
117 break;
118 case SvxBorderLineStyle::DOTTED:
120 break;
121 case SvxBorderLineStyle::DASHED:
123 break;
124 case SvxBorderLineStyle::DOUBLE:
125 case SvxBorderLineStyle::DOUBLE_THIN:
127 break;
128 case SvxBorderLineStyle::THINTHICK_SMALLGAP:
130 break;
131 case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
133 break;
134 case SvxBorderLineStyle::THINTHICK_LARGEGAP:
136 break;
137 case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
139 break;
140 case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
142 break;
143 case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
145 break;
146 case SvxBorderLineStyle::EMBOSSED:
148 break;
149 case SvxBorderLineStyle::ENGRAVED:
151 break;
152 case SvxBorderLineStyle::OUTSET:
154 break;
155 case SvxBorderLineStyle::INSET:
157 break;
158 case SvxBorderLineStyle::FINE_DASHED:
160 break;
161 case SvxBorderLineStyle::DASH_DOT:
163 break;
164 case SvxBorderLineStyle::DASH_DOT_DOT:
166 break;
167 case SvxBorderLineStyle::NONE:
168 default:
170 break;
171 }
172
173 double const fConverted(
174 ::editeng::ConvertBorderWidthToWord(pLine->GetBorderLineStyle(), pLine->GetWidth()));
175 if (255 >= pLine->GetWidth()) // That value comes from RTF specs
176 {
178 + OString::number(static_cast<sal_Int32>(fConverted)));
179 }
180 else
181 {
182 // use \brdrth to double the value range...
184 + OString::number(static_cast<sal_Int32>(fConverted) / 2));
185 }
186
188 + OString::number(static_cast<sal_Int32>(rExport.GetColor(pLine->GetColor()))));
189 }
190 else // tdf#129758 "no border" may be needed to override style
191 {
192 aRet.append(OString::Concat(pStr) + OOO_STRING_SVTOOLS_RTF_BRDRNONE);
193 }
194 return aRet.makeStringAndClear();
195}
196
197static OString OutBorderLine(RtfExport const& rExport, const editeng::SvxBorderLine* pLine,
198 const char* pStr, sal_uInt16 nDist,
199 SvxShadowLocation eShadowLocation = SvxShadowLocation::NONE)
200{
201 OStringBuffer aRet(OutTBLBorderLine(rExport, pLine, pStr));
202 if (pLine)
203 {
204 aRet.append(OOO_STRING_SVTOOLS_RTF_BRSP + OString::number(static_cast<sal_Int32>(nDist)));
205 }
206 if (eShadowLocation == SvxShadowLocation::BottomRight)
207 aRet.append(LO_STRING_SVTOOLS_RTF_BRDRSH);
208 return aRet.makeStringAndClear();
209}
210
211void RtfAttributeOutput::RTLAndCJKState(bool bIsRTL, sal_uInt16 nScript)
212{
213 m_bIsRTL = bIsRTL;
214 m_nScript = nScript;
215 m_bControlLtrRtl = true;
216}
217
219 bool /*bGenerateParaId*/)
220{
223
224 // Output table/table row/table cell starts if needed
225 if (pTextNodeInfo)
226 {
227 sal_uInt32 nRow = pTextNodeInfo->getRow();
228 sal_uInt32 nCell = pTextNodeInfo->getCell();
229
230 // New cell/row?
232 {
234 pTextNodeInfo->getInnerForDepth(m_nTableDepth));
235 OSL_ENSURE(pDeepInner, "TableNodeInfoInner not found");
236 // Make sure we always start a row between ending one and starting a cell.
237 // In case of subtables, we may not get the first cell.
238 if (pDeepInner && (pDeepInner->getCell() == 0 || m_bTableRowEnded))
239 {
240 StartTableRow(pDeepInner);
241 }
242
244 }
245
246 // Again, if depth was incremented, start a new table even if we skipped the first cell.
247 if ((nRow == 0 && nCell == 0) || (m_nTableDepth == 0 && pTextNodeInfo->getDepth()))
248 {
249 // Do we have to start the table?
250 // [If we are at the right depth already, it means that we
251 // continue the table cell]
252 sal_uInt32 nCurrentDepth = pTextNodeInfo->getDepth();
253
254 if (nCurrentDepth > m_nTableDepth)
255 {
256 // Start all the tables that begin here
257 for (sal_uInt32 nDepth = m_nTableDepth + 1; nDepth <= pTextNodeInfo->getDepth();
258 ++nDepth)
259 {
261 pTextNodeInfo->getInnerForDepth(nDepth));
262
263 m_bLastTable = (nDepth == pTextNodeInfo->getDepth());
264 StartTable();
265 StartTableRow(pInner);
267 }
268
269 m_nTableDepth = nCurrentDepth;
270 }
271 }
272 }
273
274 OSL_ENSURE(m_aRun.getLength() == 0, "m_aRun is not empty");
275 return 0;
276}
277
279{
280 bool bLastPara = false;
283 {
284 // We're ending a paragraph that is the last paragraph of a footnote or endnote, or of clipboard.
285 bLastPara
287 && m_rExport.GetCurrentNodeIndex() == m_rExport.m_pCurPam->End()->GetNodeIndex();
288 }
289
290 FinishTableRowCell(pTextNodeInfoInner);
291
292 RtfStringBuffer aParagraph;
293
294 aParagraph.appendAndClear(m_aRun);
295 aParagraph->append(m_aAfterRuns);
296 m_aAfterRuns.setLength(0);
298 m_bTableAfterCell = false;
299 else
300 {
301 aParagraph->append(SAL_NEWLINE_STRING);
302 // RTF_PAR at the end of the footnote or clipboard, would cause an additional empty paragraph.
303 if (!bLastPara)
304 {
306 aParagraph->append(' ');
307 }
308 }
310 {
312 m_nColBreakNeeded = false;
313 }
314
316 aParagraph.makeStringAndClear(this);
317 else
318 m_aSectionHeaders.append(aParagraph.makeStringAndClear());
319}
320
322{
326 .WriteChar(' ');
327}
328
330{
331 SwNodeIndex aNextIndex(rNode, 1);
332 if (rNode.IsTextNode())
333 {
334 OSL_ENSURE(m_aStyles.getLength() == 0, "m_aStyles is not empty");
335
336 // output page/section breaks
338 m_aSectionBreaks.setLength(0);
340
341 // output section headers / footers
343 {
345 m_aSectionHeaders.setLength(0);
346 }
347
348 if (aNextIndex.GetNode().IsTextNode())
349 {
350 const SwTextNode* pTextNode = static_cast<SwTextNode*>(&aNextIndex.GetNode());
351 m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode);
352 // Save the current page description for now, so later we will be able to access the previous one.
353 m_pPrevPageDesc = pTextNode->FindPageDesc();
354 }
355 else if (aNextIndex.GetNode().IsTableNode())
356 {
357 const SwTableNode* pTableNode = static_cast<SwTableNode*>(&aNextIndex.GetNode());
358 const SwFrameFormat* pFormat = pTableNode->GetTable().GetFrameFormat();
359 m_rExport.OutputSectionBreaks(&(pFormat->GetAttrSet()), *pTableNode);
360 }
362 }
363 else if (rNode.IsEndNode())
364 {
365 // End of something: make sure that it's the end of a table.
366 assert(rNode.StartOfSectionNode()->IsTableNode());
367 if (aNextIndex.GetNode().IsTextNode())
368 {
369 // Handle section break between a table and a text node following it.
370 const SwTextNode* pTextNode = aNextIndex.GetNode().GetTextNode();
371 m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode);
372 }
373 }
374}
375
377{
378 OStringBuffer aPar;
380 {
382 }
385 else
386 m_aSectionHeaders.append(aPar);
387}
388
390 const SfxItemSet& /*rParagraphMarkerProperties*/, const SwRedlineData* /*pRedlineData*/,
391 const SwRedlineData* /*pRedlineParagraphMarkerDeleted*/,
392 const SwRedlineData* /*pRedlineParagraphMarkerInserted*/)
393{
394 // Do not call MoveCharacterProperties(),
395 // Otherwise associate properties in the paragraph style are ruined.
396 const OString aProperties = m_aStyles.makeStringAndClear();
398}
399
400void RtfAttributeOutput::StartRun(const SwRedlineData* pRedlineData, sal_Int32 /*nPos*/,
401 bool bSingleEmptyRun)
402{
403 SAL_INFO("sw.rtf", __func__ << ", bSingleEmptyRun: " << bSingleEmptyRun);
404
405 m_bInRun = true;
406 m_bSingleEmptyRun = bSingleEmptyRun;
408 m_aRun->append('{');
409
410 // if there is some redlining in the document, output it
411 Redline(pRedlineData);
412
413 OSL_ENSURE(m_aRunText.getLength() == 0, "m_aRunText is not empty");
414}
415
416void RtfAttributeOutput::EndRun(const SwTextNode* /*pNode*/, sal_Int32 /*nPos*/, sal_Int32 /*nLen*/,
417 bool /*bLastRun*/)
418{
421
422 if (m_bInRuby)
423 {
425 m_bInRuby = false;
426 }
427
429 m_aRun->append('}');
430 m_bInRun = false;
431}
432
434{
435 OSL_ENSURE(m_aStyles.getLength() == 0, "m_aStyles is not empty");
436}
437
439{
440 const OString aProperties = MoveCharacterProperties(true);
442}
443
445{
446 const OString aAssocHich = m_aStylesAssocHich.makeStringAndClear();
447 const OString aAssocDbch = m_aStylesAssocDbch.makeStringAndClear();
448 const OString aAssocRtlch = m_aStylesAssocRtlch.makeStringAndClear();
449 const OString aAssocLtrch = m_aStylesAssocLtrch.makeStringAndClear();
450 const OString aNormal = m_aStyles.makeStringAndClear();
451 OStringBuffer aBuf;
452
453 if (aAutoWriteRtlLtr && !m_bControlLtrRtl)
454 {
455 m_bControlLtrRtl = !aAssocRtlch.isEmpty();
456 m_bIsRTL = false;
457 m_nScript = i18n::ScriptType::LATIN;
458 }
459
460 if (m_bIsRTL)
461 {
462 if (!aAssocRtlch.isEmpty())
463 {
464 aBuf.append(OOO_STRING_SVTOOLS_RTF_LTRCH + aAssocLtrch
465 + " " OOO_STRING_SVTOOLS_RTF_RTLCH + aAssocRtlch);
466 }
467 }
468 else
469 {
470 if (!aAssocRtlch.isEmpty())
471 {
472 aBuf.append(OOO_STRING_SVTOOLS_RTF_RTLCH + aAssocRtlch
473 + " " OOO_STRING_SVTOOLS_RTF_LTRCH + aAssocLtrch);
474 }
475 if (!aAssocHich.isEmpty())
476 {
477 aBuf.append(OOO_STRING_SVTOOLS_RTF_HICH + aAssocHich);
478 }
479 if (!aNormal.isEmpty())
480 {
481 aBuf.append(OOO_STRING_SVTOOLS_RTF_LOCH + aNormal);
482 }
483 if (!aAssocDbch.isEmpty())
484 {
485 aBuf.append(OOO_STRING_SVTOOLS_RTF_DBCH + aAssocDbch);
486 }
487 }
488
490 {
491 m_bControlLtrRtl = false;
492
493 switch (m_nScript)
494 {
495 case i18n::ScriptType::LATIN:
497 break;
498 case i18n::ScriptType::ASIAN:
500 break;
501 case i18n::ScriptType::COMPLEX:
502 /* noop */
503 default:
504 /* should not happen? */
505 break;
506 }
507 }
508
509 return aBuf.makeStringAndClear();
510}
511
512void RtfAttributeOutput::RunText(const OUString& rText, rtl_TextEncoding /*eCharSet*/,
513 const OUString& /*rSymbolFont*/)
514{
515 SAL_INFO("sw.rtf", __func__ << ", rText: " << rText);
517}
518
520
521void RtfAttributeOutput::RawText(const OUString& rText, rtl_TextEncoding eCharSet)
522{
524}
525
526void RtfAttributeOutput::StartRuby(const SwTextNode& rNode, sal_Int32 /*nPos*/,
527 const SwFormatRuby& rRuby)
528{
529 WW8Ruby aWW8Ruby(rNode, rRuby, GetExport());
530 OUString aStr = FieldString(ww::eEQ) + "\\* jc" + OUString::number(aWW8Ruby.GetJC())
531 + " \\* \"Font:" + aWW8Ruby.GetFontFamily() + "\" \\* hps"
532 + OUString::number((aWW8Ruby.GetRubyHeight() + 5) / 10) + " \\o";
533 if (aWW8Ruby.GetDirective())
534 {
535 aStr += "\\a" + OUStringChar(aWW8Ruby.GetDirective());
536 }
537 aStr += "(\\s\\up " + OUString::number((aWW8Ruby.GetBaseHeight() + 10) / 20 - 1) + "(";
539 aStr = rRuby.GetText() + "),";
541 m_bInRuby = true;
542}
543
544void RtfAttributeOutput::EndRuby(const SwTextNode& /*rNode*/, sal_Int32 /*nPos*/) {}
545
546bool RtfAttributeOutput::StartURL(const OUString& rUrl, const OUString& rTarget)
547{
548 m_aURLs.push(rUrl);
549 // Ignore hyperlink without a URL.
550 if (!rUrl.isEmpty())
551 {
552 m_aRun->append('{');
554 m_aRun->append('{');
557 m_aRun->append(" HYPERLINK ");
558
559 m_aRun->append("\"");
561 m_aRun->append("\" ");
562
563 if (!rTarget.isEmpty())
564 {
565 m_aRun->append("\\\\t \"");
567 m_aRun->append("\" ");
568 }
569
570 m_aRun->append("}");
572 }
573 return true;
574}
575
576bool RtfAttributeOutput::EndURL(bool const isAtEndOfParagraph)
577{
578 if (m_aURLs.empty())
579 {
580 return true;
581 }
582
583 const OUString& rURL = m_aURLs.top();
584 if (!rURL.isEmpty())
585 {
586 // UGLY: usually EndRun is called earlier, but there is an extra
587 // call to OutAttrWithRange() when at the end of the paragraph,
588 // so in that special case the output needs to be appended to the
589 // new run's text instead of the previous run
590 if (isAtEndOfParagraph)
591 {
592 // close the fldrslt group
593 m_aRunText->append("}}");
594 // close the field group
595 m_aRunText->append('}');
596 }
597 else
598 {
599 // close the fldrslt group
600 m_aRun->append("}}");
601 // close the field group
602 m_aRun->append('}');
603 }
604 }
605 m_aURLs.pop();
606 return true;
607}
608
609void RtfAttributeOutput::FieldVanish(const OUString& /*rText*/, ww::eField /*eType*/,
610 OUString const* /*pBookmarkName*/)
611{
612 SAL_INFO("sw.rtf", "TODO: " << __func__);
613}
614
616{
617 if (!pRedline)
618 return;
619
620 if (pRedline->GetType() == RedlineType::Insert)
621 {
624 m_aRun->append(static_cast<sal_Int32>(
625 m_rExport.GetRedline(SW_MOD()->GetRedlineAuthor(pRedline->GetAuthor()))));
627 }
628 else if (pRedline->GetType() == RedlineType::Delete)
629 {
632 m_aRun->append(static_cast<sal_Int32>(
633 m_rExport.GetRedline(SW_MOD()->GetRedlineAuthor(pRedline->GetAuthor()))));
635 }
636 m_aRun->append(static_cast<sal_Int32>(sw::ms::DateTime2DTTM(pRedline->GetTimeStamp())));
637 m_aRun->append(' ');
638}
639
641 const SwFormatDrop& /*rSwFormatDrop*/, sal_uInt16 /*nStyle*/,
642 ww8::WW8TableNodeInfo::Pointer_t /*pTextNodeInfo*/,
643 ww8::WW8TableNodeInfoInner::Pointer_t /*pTextNodeInfoInner*/)
644{
645 SAL_INFO("sw.rtf", "TODO: " << __func__);
646}
647
649{
650 OString* pStyle = m_rExport.GetStyle(nStyle);
651 OStringBuffer aStyle(OOO_STRING_SVTOOLS_RTF_S
652 + OString::number(static_cast<sal_Int32>(nStyle)));
653 if (pStyle)
654 aStyle.append(*pStyle);
656 m_rExport.Strm().WriteOString(aStyle);
657 else
658 m_aSectionHeaders.append(aStyle);
659}
660
662 ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/)
663{
665 if (m_nTableDepth > 1)
666 {
668 m_aStyles.append(static_cast<sal_Int32>(m_nTableDepth));
669 }
670 m_bWroteCellInfo = true;
671}
672
674{
675 /* noop */
676}
677
679{
680 if (!pFlyFormat || !pFlyFormat->GetFlySplit().GetValue())
681 {
682 return;
683 }
684
685 switch (pFlyFormat->GetVertOrient().GetRelationOrient())
686 {
687 case text::RelOrientation::PAGE_PRINT_AREA:
688 // relative to margin
690 break;
691 case text::RelOrientation::PAGE_FRAME:
692 // relative to page
694 break;
695 default:
696 // text::RelOrientation::FRAME
697 // relative to text
699 break;
700 }
701
702 switch (pFlyFormat->GetHoriOrient().GetRelationOrient())
703 {
704 case text::RelOrientation::FRAME:
705 // relative to column
707 break;
708 case text::RelOrientation::PAGE_PRINT_AREA:
709 // relative to margin
711 break;
712 default:
713 // text::RelOrientation::PAGE_FRAME
714 // relative to page
716 break;
717 }
718
719 // Similar to RtfAttributeOutput::FormatHorizOrientation(), but for tables.
720 switch (pFlyFormat->GetHoriOrient().GetHoriOrient())
721 {
722 case text::HoriOrientation::LEFT:
723 // left
725 break;
726 case text::HoriOrientation::CENTER:
727 // centered
729 break;
730 case text::HoriOrientation::RIGHT:
731 // right
733 break;
734 default:
735 SwTwips nTPosX = pFlyFormat->GetHoriOrient().GetPos();
737 m_aRowDefs.append(static_cast<sal_Int32>(nTPosX));
738 break;
739 }
740
741 // Similar to RtfAttributeOutput::FormatVertOrientation(), but for tables.
742 switch (pFlyFormat->GetVertOrient().GetVertOrient())
743 {
744 case text::VertOrientation::TOP:
745 // up
747 break;
748 case text::VertOrientation::CENTER:
749 // centered
751 break;
752 case text::VertOrientation::BOTTOM:
753 // down
755 break;
756 default:
757 SwTwips nTPosY = pFlyFormat->GetVertOrient().GetPos();
759 m_aRowDefs.append(static_cast<sal_Int32>(nTPosY));
760 break;
761 }
762
763 // Similar to RtfAttributeOutput::FormatULSpace(), but for tables.
764 sal_uInt16 nTdfrmtxtTop = pFlyFormat->GetULSpace().GetUpper();
766 m_aRowDefs.append(static_cast<sal_Int32>(nTdfrmtxtTop));
767 sal_uInt16 nTdfrmtxtBottom = pFlyFormat->GetULSpace().GetLower();
769 m_aRowDefs.append(static_cast<sal_Int32>(nTdfrmtxtBottom));
770
771 // Similar to RtfAttributeOutput::FormatLRSpace(), but for tables.
772 sal_uInt16 nTdfrmtxtLeft = pFlyFormat->GetLRSpace().GetLeft();
774 m_aRowDefs.append(static_cast<sal_Int32>(nTdfrmtxtLeft));
775 sal_uInt16 nTdfrmtxtRight = pFlyFormat->GetLRSpace().GetRight();
777 m_aRowDefs.append(static_cast<sal_Int32>(nTdfrmtxtRight));
778}
779
781 ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
782{
783 InitTableHelper(pTableTextNodeInfoInner);
784
785 const SwTable* pTable = pTableTextNodeInfoInner->getTable();
786 SwFrameFormat* pFormat = pTable->GetFrameFormat();
787
789 TableOrientation(pTableTextNodeInfoInner);
790 TableBidi(pTableTextNodeInfoInner);
791 TableHeight(pTableTextNodeInfoInner);
792 TableCanSplit(pTableTextNodeInfoInner);
793
794 // Write table positioning properties in case this is a floating table.
796
797 // Cell margins
798 const SvxBoxItem& rBox = pFormat->GetBox();
799 static const SvxBoxItemLine aBorders[] = { SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
800 SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT };
801
802 static const char* aRowPadNames[]
805
806 static const char* aRowPadUnits[]
809
810 for (int i = 0; i < 4; ++i)
811 {
812 m_aRowDefs.append(aRowPadUnits[i]);
813 m_aRowDefs.append(sal_Int32(3));
814 m_aRowDefs.append(aRowPadNames[i]);
815 m_aRowDefs.append(static_cast<sal_Int32>(rBox.GetDistance(aBorders[i])));
816 }
817
818 // The cell-dependent properties
819 const double fWidthRatio = m_pTableWrt->GetAbsWidthRatio();
820 const SwWriteTableRows& aRows = m_pTableWrt->GetRows();
821 sal_uInt32 nRow = pTableTextNodeInfoInner->getRow();
822 if (nRow >= aRows.size())
823 {
824 SAL_WARN("sw.ww8", "RtfAttributeOutput::TableDefinition: out of range row: " << nRow);
825 return;
826 }
827 SwWriteTableRow* pRow = aRows[nRow].get();
828 SwTwips nSz = 0;
829
830 // Not using m_nTableDepth, which is not yet incremented here.
831 sal_uInt32 nCurrentDepth = pTableTextNodeInfoInner->getDepth();
832 m_aCells[nCurrentDepth] = pRow->GetCells().size();
833 for (sal_uInt32 i = 0; i < m_aCells[nCurrentDepth]; i++)
834 {
835 const SwWriteTableCell* const pCell = pRow->GetCells()[i].get();
836 const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat();
837
838 pTableTextNodeInfoInner->setCell(i);
839 TableCellProperties(pTableTextNodeInfoInner);
840
841 // Right boundary: this can't be in TableCellProperties as the old
842 // value of nSz is needed.
843 nSz += pCellFormat->GetFrameSize().GetWidth();
845 m_aRowDefs.append(static_cast<sal_Int32>(pFormat->GetLRSpace().GetLeft()
846 + rtl::math::round(nSz * fWidthRatio)));
847 }
848}
849
851 ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
852{
853 /*
854 * The function name is a bit misleading: given that we write borders
855 * before each row, we just have borders, not default ones. Additionally,
856 * this function actually writes borders for a specific cell only and is
857 * called for each cell.
858 */
859
860 const SwWriteTableRows& aRows = m_pTableWrt->GetRows();
861 SwWriteTableRow* pRow = aRows[pTableTextNodeInfoInner->getRow()].get();
862 const SwWriteTableCell* const pCell
863 = pRow->GetCells()[pTableTextNodeInfoInner->getCell()].get();
864 const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat();
865 const SvxBoxItem* pItem = pCellFormat->GetAttrSet().GetItemIfSet(RES_BOX);
866 if (!pItem)
867 return;
868
869 auto& rBox = *pItem;
870 static const SvxBoxItemLine aBorders[] = { SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
871 SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT };
872 static const char* aBorderNames[]
875 //Yes left and top are swapped with each other for cell padding! Because
876 //that's what the thundering annoying rtf export/import word xp does.
877 static const char* aCellPadNames[]
880 static const char* aCellPadUnits[]
883 for (int i = 0; i < 4; ++i)
884 {
885 if (const editeng::SvxBorderLine* pLn = rBox.GetLine(aBorders[i]))
886 m_aRowDefs.append(OutTBLBorderLine(m_rExport, pLn, aBorderNames[i]));
887 if (rBox.GetDistance(aBorders[i]))
888 {
889 m_aRowDefs.append(aCellPadUnits[i]);
890 m_aRowDefs.append(sal_Int32(3));
891 m_aRowDefs.append(aCellPadNames[i]);
892 m_aRowDefs.append(static_cast<sal_Int32>(rBox.GetDistance(aBorders[i])));
893 }
894 }
895}
896
898 ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
899{
900 const SwTable* pTable = pTableTextNodeInfoInner->getTable();
901 const SwTableBox* pTableBox = pTableTextNodeInfoInner->getTableBox();
902 const SwTableLine* pTableLine = pTableBox->GetUpper();
903
904 Color aColor = COL_AUTO;
905 auto pTableColorProp
907 if (pTableColorProp)
908 aColor = pTableColorProp->GetColor();
909
910 auto pRowColorProp
912 if (pRowColorProp && pRowColorProp->GetColor() != COL_AUTO)
913 aColor = pRowColorProp->GetColor();
914
915 const SwWriteTableRows& aRows = m_pTableWrt->GetRows();
916 SwWriteTableRow* pRow = aRows[pTableTextNodeInfoInner->getRow()].get();
917 const SwWriteTableCell* const pCell
918 = pRow->GetCells()[pTableTextNodeInfoInner->getCell()].get();
919 const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat();
920 if (const SvxBrushItem* pBrushItem = pCellFormat->GetAttrSet().GetItemIfSet(RES_BACKGROUND))
921 {
922 if (pBrushItem->GetColor() != COL_AUTO)
923 aColor = pBrushItem->GetColor();
924 }
925
926 if (!aColor.IsTransparent())
927 {
929 m_aRowDefs.append(static_cast<sal_Int32>(m_rExport.GetColor(aColor)));
930 }
931}
932
934 ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/)
935{
936}
937
939 ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/)
940{
941}
942
944{
945 const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
946 const SwTableLine* pTabLine = pTabBox->GetUpper();
947 const SwFrameFormat* pLineFormat = pTabLine->GetFrameFormat();
948 const SwFormatFrameSize& rLSz = pLineFormat->GetFrameSize();
949
950 if (!(SwFrameSize::Variable != rLSz.GetHeightSizeType() && rLSz.GetHeight()))
951 return;
952
953 sal_Int32 nHeight = 0;
954
955 switch (rLSz.GetHeightSizeType())
956 {
958 nHeight = -rLSz.GetHeight();
959 break;
961 nHeight = rLSz.GetHeight();
962 break;
963 default:
964 break;
965 }
966
967 if (nHeight)
968 {
970 m_aRowDefs.append(nHeight);
971 }
972}
973
975 ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
976{
977 const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
978 const SwTableLine* pTabLine = pTabBox->GetUpper();
979 const SwFrameFormat* pLineFormat = pTabLine->GetFrameFormat();
980 const SwFormatRowSplit& rSplittable = pLineFormat->GetRowSplit();
981
982 // The rtf default is to allow a row to break
983 if (!rSplittable.GetValue())
985}
986
988{
989 const SwTable* pTable = pTableTextNodeInfoInner->getTable();
990 const SwFrameFormat* pFrameFormat = pTable->GetFrameFormat();
991
992 if (m_rExport.TrueFrameDirection(*pFrameFormat) != SvxFrameDirection::Horizontal_RL_TB)
994 else
996}
997
999 ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
1000{
1001 const SwWriteTableRows& aRows = m_pTableWrt->GetRows();
1002 SwWriteTableRow* pRow = aRows[pTableTextNodeInfoInner->getRow()].get();
1003 const SwWriteTableCell* const pCell
1004 = pRow->GetCells()[pTableTextNodeInfoInner->getCell()].get();
1005 const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat();
1006
1007 // Text direction.
1008 if (SvxFrameDirection::Vertical_RL_TB == m_rExport.TrueFrameDirection(*pCellFormat))
1010 else if (SvxFrameDirection::Vertical_LR_BT == m_rExport.TrueFrameDirection(*pCellFormat))
1012
1013 // vertical merges
1014 if (pCell->GetRowSpan() > 1)
1016 else if (pCell->GetRowSpan() == 0)
1018
1019 // vertical alignment
1020 const SwFormatVertOrient* pVertOrientItem
1021 = pCellFormat->GetAttrSet().GetItemIfSet(RES_VERT_ORIENT);
1022 if (!pVertOrientItem)
1023 return;
1024
1025 switch (pVertOrientItem->GetVertOrient())
1026 {
1027 case text::VertOrientation::CENTER:
1029 break;
1030 case text::VertOrientation::BOTTOM:
1032 break;
1033 default:
1035 break;
1036 }
1037}
1038
1040{
1041 // This is called when the nested table ends in a cell, and there's no
1042 // paragraph behind that; so we must check for the ends of cell, rows,
1043 // and tables
1044 FinishTableRowCell(pNodeInfoInner);
1045}
1046
1048 ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
1049{
1050 const SwTable* pTable = pTableTextNodeInfoInner->getTable();
1051 SwFrameFormat* pFormat = pTable->GetFrameFormat();
1052
1053 OStringBuffer aTableAdjust(OOO_STRING_SVTOOLS_RTF_TRQL);
1054 switch (pFormat->GetHoriOrient().GetHoriOrient())
1055 {
1056 case text::HoriOrientation::CENTER:
1057 aTableAdjust.setLength(0);
1058 aTableAdjust.append(OOO_STRING_SVTOOLS_RTF_TRQC);
1059 break;
1060 case text::HoriOrientation::RIGHT:
1061 aTableAdjust.setLength(0);
1062 aTableAdjust.append(OOO_STRING_SVTOOLS_RTF_TRQR);
1063 break;
1065 case text::HoriOrientation::LEFT_AND_WIDTH:
1066 aTableAdjust.append(OOO_STRING_SVTOOLS_RTF_TRLEFT);
1067 aTableAdjust.append(static_cast<sal_Int32>(pFormat->GetLRSpace().GetLeft()));
1068 break;
1069 default:
1070 break;
1071 }
1072
1073 m_aRowDefs.append(aTableAdjust);
1074}
1075
1077 ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/)
1078{
1079 SAL_INFO("sw.rtf", "TODO: " << __func__);
1080}
1081
1082void RtfAttributeOutput::TableRowEnd(sal_uInt32 /*nDepth*/) { /* noop, see EndTableRow() */}
1083
1084/*
1085 * Our private table methods.
1086 */
1087
1089 const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
1090{
1091 const SwTable* pTable = pTableTextNodeInfoInner->getTable();
1092 if (m_pTableWrt && pTable == m_pTableWrt->GetTable())
1093 return;
1094
1095 tools::Long nPageSize = 0;
1096 bool bRelBoxSize = false;
1097
1098 // Create the SwWriteTable instance to use col spans
1099 GetTablePageSize(pTableTextNodeInfoInner.get(), nPageSize, bRelBoxSize);
1100
1101 const SwFrameFormat* pFormat = pTable->GetFrameFormat();
1102 const sal_uInt32 nTableSz = pFormat->GetFrameSize().GetWidth();
1103
1104 const SwHTMLTableLayout* pLayout = pTable->GetHTMLTableLayout();
1105 if (pLayout && pLayout->IsExportable())
1106 m_pTableWrt = std::make_unique<SwWriteTable>(pTable, pLayout);
1107 else
1108 m_pTableWrt = std::make_unique<SwWriteTable>(pTable, pTable->GetTabLines(), nPageSize,
1109 nTableSz, false);
1110}
1111
1113{
1114 // To trigger calling InitTableHelper()
1115 m_pTableWrt.reset();
1116}
1117
1119 const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
1120{
1121 sal_uInt32 nCurrentDepth = pTableTextNodeInfoInner->getDepth();
1122 SAL_INFO("sw.rtf", __func__ << ", (depth is " << nCurrentDepth << ")");
1123 m_bTableRowEnded = false;
1124
1125 TableDefinition(pTableTextNodeInfoInner);
1126
1127 if (!m_bLastTable)
1128 m_aTables.push_back(m_aRowDefs.makeStringAndClear());
1129
1130 // We'll write the table definition for nested tables later
1131 if (nCurrentDepth > 1)
1132 return;
1133 // Empty the previous row closing buffer before starting the new one,
1134 // necessary for subtables.
1136 m_aAfterRuns.setLength(0);
1138 m_aRowDefs.setLength(0);
1139}
1140
1142
1144 const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
1145{
1146 TableDefaultBorders(pTableTextNodeInfoInner);
1147 TableBackgrounds(pTableTextNodeInfoInner);
1148 TableVerticalCell(pTableTextNodeInfoInner);
1149}
1150
1152{
1153 SAL_INFO("sw.rtf", __func__ << ", (depth is " << m_nTableDepth << ")");
1154
1155 if (!m_bWroteCellInfo)
1156 {
1159 m_aAfterRuns.append(static_cast<sal_Int32>(m_nTableDepth));
1160 }
1161 if (m_nTableDepth > 1)
1163 else
1165
1166 m_bTableCellOpen = false;
1167 m_bTableAfterCell = true;
1168 m_bWroteCellInfo = false;
1169 if (m_aCells[m_nTableDepth] > 0)
1171}
1172
1174{
1175 SAL_INFO("sw.rtf", __func__ << ", (depth is " << m_nTableDepth << ")");
1176
1177 if (m_nTableDepth > 1)
1178 {
1179 m_aAfterRuns.append(
1181 if (!m_aRowDefs.isEmpty())
1182 {
1183 m_aAfterRuns.append(m_aRowDefs);
1184 m_aRowDefs.setLength(0);
1185 }
1186 else if (!m_aTables.empty())
1187 {
1188 m_aAfterRuns.append(m_aTables.back());
1189 m_aTables.pop_back();
1190 }
1192 "}"
1194 }
1195 else
1196 {
1197 if (!m_aTables.empty())
1198 {
1199 m_aAfterRuns.append(m_aTables.back());
1200 m_aTables.pop_back();
1201 }
1202 // Make sure that the first word of the next paragraph is not merged with the last control
1203 // word of this table row, happens with floating tables.
1205 }
1206 m_bTableRowEnded = true;
1207}
1208
1210{
1211 if (m_nTableDepth > 0)
1212 {
1213 m_nTableDepth--;
1214 m_pTableWrt.reset();
1215 }
1216
1217 // We closed the table; if it is a nested table, the cell that contains it
1218 // still continues
1219 m_bTableCellOpen = true;
1220
1221 // Cleans the table helper
1222 m_pTableWrt.reset();
1223}
1224
1226{
1227 if (!pInner)
1228 return;
1229
1230 // Where are we in the table
1231 sal_uInt32 nRow = pInner->getRow();
1232
1233 const SwTable* pTable = pInner->getTable();
1234 const SwTableLines& rLines = pTable->GetTabLines();
1235 sal_uInt16 nLinesCount = rLines.size();
1236
1237 if (pInner->isEndOfCell())
1238 EndTableCell();
1239
1240 // This is a line end
1241 if (pInner->isEndOfLine())
1242 EndTableRow();
1243
1244 // This is the end of the table
1245 if (pInner->isEndOfLine() && (nRow + 1) == nLinesCount)
1246 EndTable();
1247}
1248
1250{
1251 m_rExport.Strm()
1253 .WriteChar('{')
1256 OSL_ENSURE(m_aStylesheet.getLength() == 0, "m_aStylesheet is not empty");
1258 m_aStylesheet.append('{');
1260}
1261
1262void RtfAttributeOutput::EndStyles(sal_uInt16 /*nNumberOfStyles*/)
1263{
1264 m_rExport.Strm().WriteChar('}');
1266 m_aStylesheet.setLength(0);
1267 m_rExport.Strm().WriteChar('}');
1268}
1269
1270void RtfAttributeOutput::DefaultStyle() { /* noop, the default style is always 0 in RTF */}
1271
1272void RtfAttributeOutput::StartStyle(const OUString& rName, StyleType eType, sal_uInt16 nBase,
1273 sal_uInt16 nNext, sal_uInt16 /*nLink*/, sal_uInt16 /*nWwId*/,
1274 sal_uInt16 nSlot, bool bAutoUpdate)
1275{
1276 SAL_INFO("sw.rtf", __func__ << ", rName = '" << rName << "'");
1277
1278 m_aStylesheet.append('{');
1279 if (eType == STYLE_TYPE_PARA)
1281 else
1283 m_aStylesheet.append(static_cast<sal_Int32>(nSlot));
1284
1285 if (nBase != 0x0FFF)
1286 {
1288 m_aStylesheet.append(static_cast<sal_Int32>(nBase));
1289 }
1290
1292 m_aStylesheet.append(static_cast<sal_Int32>(nNext));
1293
1294 if (bAutoUpdate)
1296
1297 m_rStyleName = rName;
1298 m_nStyleId = nSlot;
1299}
1300
1302{
1303 OString aStyles = MoveCharacterProperties();
1304 m_rExport.InsStyle(m_nStyleId, aStyles);
1305 m_aStylesheet.append(aStyles);
1306 m_aStylesheet.append(' ');
1307 m_aStylesheet.append(
1309 m_aStylesheet.append(";}");
1311}
1312
1313void RtfAttributeOutput::StartStyleProperties(bool /*bParProp*/, sal_uInt16 /*nStyle*/)
1314{
1315 /* noop */
1316}
1317
1318void RtfAttributeOutput::EndStyleProperties(bool /*bParProp*/) { /* noop */}
1319
1321{
1322 if (nLvl >= WW8ListManager::nMaxLevel)
1323 nLvl = WW8ListManager::nMaxLevel - 1;
1324
1326 m_aStyles.append(static_cast<sal_Int32>(nLvl));
1328 m_aStyles.append(static_cast<sal_Int32>(nLvl));
1329}
1330
1332{
1333 if (bBreak)
1334 {
1336 }
1337}
1338
1339void RtfAttributeOutput::SectionBreak(sal_uInt8 nC, bool /*bBreakAfter*/,
1340 const WW8_SepInfo* pSectionInfo, bool /*bExtraPageBreak*/)
1341{
1342 switch (nC)
1343 {
1345 m_nColBreakNeeded = true;
1346 break;
1347 case msword::PageBreak:
1348 if (pSectionInfo)
1349 m_rExport.SectionProperties(*pSectionInfo);
1350 break;
1351 }
1352}
1353
1355{
1357 return;
1358
1361 {
1363 m_aSectionBreaks.setLength(0);
1364 }
1365}
1366
1368{
1369 /*
1370 * noop, \sect must go to StartSection or Word won't notice multiple
1371 * columns...
1372 */
1373}
1374
1376{
1378 m_aSectionBreaks.append(static_cast<sal_Int32>(!bProtected));
1379}
1380
1382 const SwLineNumberInfo& rLnNumInfo)
1383{
1388 if (!rLnNumInfo.IsRestartEachPage())
1390
1391 if (nRestartNo > 0)
1392 {
1394 m_rExport.Strm().WriteNumberAsString(nRestartNo);
1395 }
1396}
1397
1399{
1400 /*
1401 * noop, handled in RtfExport::WriteHeaderFooter()
1402 */
1403}
1404
1406 const SwFrameFormat* /*pFirstPageFormat*/)
1407{
1408 const SvxBoxItem& rBox = pFormat->GetBox();
1411
1412 if (aDistances.bFromEdge)
1413 {
1414 sal_uInt16 nOpt = (1 << 5);
1416 m_aSectionBreaks.append(static_cast<sal_Int32>(nOpt));
1417 }
1418
1419 const editeng::SvxBorderLine* pLine = rBox.GetTop();
1420 if (pLine)
1421 m_aSectionBreaks.append(
1423 pLine = rBox.GetBottom();
1424 if (pLine)
1425 m_aSectionBreaks.append(
1427 pLine = rBox.GetLeft();
1428 if (pLine)
1429 m_aSectionBreaks.append(
1431 pLine = rBox.GetRight();
1432 if (pLine)
1433 m_aSectionBreaks.append(
1435}
1436
1438{
1441}
1442
1444 const ::std::optional<sal_uInt16>& oPageRestartNumber)
1445{
1446 if (oPageRestartNumber)
1447 {
1449 m_aSectionBreaks.append(static_cast<sal_Int32>(*oPageRestartNumber));
1451 }
1452
1453 const char* pStr = nullptr;
1454 switch (nNumType)
1455 {
1459 break;
1463 break;
1466 break;
1469 break;
1470
1471 case SVX_NUM_ARABIC:
1473 break;
1474 }
1475 if (pStr)
1476 m_aSectionBreaks.append(pStr);
1477}
1478
1480{
1481 SAL_INFO("sw.rtf", __func__ << ", nBreakCode = " << int(nBreakCode));
1482
1483 /*
1484 * break code: 0 No break, 1 New column
1485 * 2 New page, 3 Even page, 4 Odd page
1486 */
1487 const char* sType = nullptr;
1488 switch (nBreakCode)
1489 {
1490 case 1:
1492 break;
1493 case 2:
1495 break;
1496 case 3:
1498 break;
1499 case 4:
1501 break;
1502 default:
1504 break;
1505 }
1506 m_aSectionBreaks.append(sType);
1508 {
1510 m_aSectionBreaks.setLength(0);
1511 }
1512}
1513
1515{
1518}
1519
1521{
1522 const char* pOut = nullptr;
1523
1524 if (bFootnote)
1525 {
1526 switch (rInfo.m_aFormat.GetNumberingType())
1527 {
1528 default:
1530 break;
1534 break;
1538 break;
1541 break;
1544 break;
1547 break;
1548 }
1549 }
1550 else
1551 {
1552 switch (rInfo.m_aFormat.GetNumberingType())
1553 {
1554 default:
1556 break;
1560 break;
1564 break;
1567 break;
1570 break;
1573 break;
1574 }
1575 }
1576
1577 m_aSectionBreaks.append(pOut);
1578
1580 {
1582 m_aSectionBreaks.setLength(0);
1583 }
1584}
1585
1586void RtfAttributeOutput::NumberingDefinition(sal_uInt16 nId, const SwNumRule& /*rRule*/)
1587{
1594}
1595
1597{
1598 m_rExport.Strm()
1599 .WriteChar('{')
1603 m_nListId = nId;
1604}
1605
1607{
1610}
1611
1612void RtfAttributeOutput::NumberingLevel(sal_uInt8 nLevel, sal_uInt16 nStart,
1613 sal_uInt16 nNumberingType, SvxAdjust eAdjust,
1614 const sal_uInt8* pNumLvlPos, sal_uInt8 nFollow,
1615 const wwFont* pFont, const SfxItemSet* pOutSet,
1616 sal_Int16 nIndentAt, sal_Int16 nFirstLineIndex,
1617 sal_Int16 /*nListTabPos*/, const OUString& rNumberingString,
1618 const SvxBrushItem* pBrush, bool /*isLegal*/)
1619{
1621 if (nLevel > 8) // RTF knows only 9 levels
1622 m_rExport.Strm()
1625
1627
1628 sal_uInt16 nVal = 0;
1629 switch (nNumberingType)
1630 {
1632 nVal = 1;
1633 break;
1635 nVal = 2;
1636 break;
1639 nVal = 3;
1640 break;
1643 nVal = 4;
1644 break;
1646 nVal = 14;
1647 break;
1649 nVal = 18;
1650 break;
1652 nVal = 35;
1653 if (pOutSet)
1654 {
1655 const SvxLanguageItem& rLang = pOutSet->Get(RES_CHRATR_CJK_LANGUAGE);
1657 {
1658 nVal = 39;
1659 }
1660 }
1661 break;
1663 nVal = 38;
1664 break;
1666 nVal = 34;
1667 break;
1669 nVal = 30;
1670 break;
1671 case SVX_NUM_DI_ZI_ZH:
1672 nVal = 31;
1673 break;
1675 nVal = 16;
1676 break;
1678 nVal = 20;
1679 break;
1681 nVal = 12;
1682 break;
1684 nVal = 21;
1685 break;
1687 nVal = 13;
1688 break;
1689 case style::NumberingType::HANGUL_SYLLABLE_KO:
1690 nVal = 24;
1691 break; // ganada
1692 case style::NumberingType::HANGUL_JAMO_KO:
1693 nVal = 25;
1694 break; // chosung
1695 case style::NumberingType::HANGUL_CIRCLED_SYLLABLE_KO:
1696 nVal = 24;
1697 break;
1698 case style::NumberingType::HANGUL_CIRCLED_JAMO_KO:
1699 nVal = 25;
1700 break;
1701 case style::NumberingType::NUMBER_HANGUL_KO:
1702 nVal = 42;
1703 break; // koreanCounting
1704 case style::NumberingType::NUMBER_DIGITAL_KO:
1705 nVal = 41; // koreanDigital
1706 break;
1707 case style::NumberingType::NUMBER_DIGITAL2_KO:
1708 nVal = 44; // koreanDigital2
1709 break;
1710 case style::NumberingType::NUMBER_LEGAL_KO:
1711 nVal = 43; // koreanLegal
1712 break;
1713
1714 case SVX_NUM_BITMAP:
1716 nVal = 23;
1717 break;
1719 nVal = 255;
1720 break;
1722 nVal = 22;
1723 break;
1724 }
1727
1728 switch (eAdjust)
1729 {
1730 case SvxAdjust::Center:
1731 nVal = 1;
1732 break;
1733 case SvxAdjust::Right:
1734 nVal = 2;
1735 break;
1736 default:
1737 nVal = 0;
1738 break;
1739 }
1742
1743 // bullet
1744 if (nNumberingType == SVX_NUM_BITMAP && pBrush)
1745 {
1746 int nIndex = m_rExport.GetGrfIndex(*pBrush);
1747 if (nIndex != -1)
1748 {
1751 }
1752 }
1753
1756
1759
1760 // leveltext group
1762
1763 if (SVX_NUM_CHAR_SPECIAL == nNumberingType || SVX_NUM_BITMAP == nNumberingType)
1764 {
1765 m_rExport.Strm().WriteOString("\\'01");
1766 sal_Unicode cChar = rNumberingString[0];
1767 m_rExport.Strm().WriteOString("\\u");
1769 m_rExport.Strm().WriteOString(" ?");
1770 }
1771 else
1772 {
1774 msfilter::rtfutil::OutHex(rNumberingString.getLength(), 2));
1777 /*bUnicode =*/false));
1778 }
1779
1780 m_rExport.Strm().WriteOString(";}");
1781
1782 // write the levelnumbers
1784 for (sal_uInt8 i = 0; i <= nLevel && pNumLvlPos[i]; ++i)
1785 {
1787 msfilter::rtfutil::OutHex(pNumLvlPos[i], 2));
1788 }
1789 m_rExport.Strm().WriteOString(";}");
1790
1791 if (pOutSet)
1792 {
1793 if (pFont)
1794 {
1797 }
1798 m_rExport.OutputItemSet(*pOutSet, false, true, i18n::ScriptType::LATIN,
1800 const OString aProperties = MoveCharacterProperties(true);
1802 }
1803
1806 m_rExport.Strm().WriteNumberAsString(nIndentAt);
1807
1808 m_rExport.Strm().WriteChar('}');
1809 if (nLevel > 8)
1810 m_rExport.Strm().WriteChar('}');
1811}
1812
1813void RtfAttributeOutput::WriteField_Impl(const SwField* const pField, ww::eField /*eType*/,
1814 std::u16string_view rFieldCmd, FieldFlags nMode)
1815{
1816 // If there are no field instructions, don't export it as a field.
1817 bool bHasInstructions = !rFieldCmd.empty();
1818 if (FieldFlags::All == nMode)
1819 {
1820 if (bHasInstructions)
1821 {
1823 if (pField && (pField->GetSubType() & FIXEDFLD))
1826 " ");
1830 }
1831 if (pField)
1834 if (bHasInstructions)
1835 m_aRunText->append("}}");
1836 }
1837 else
1838 {
1839 if (nMode & FieldFlags::CmdStart)
1840 {
1843 // paragraph break closes group so open another one "inside" to
1844 " {"); // prevent leaving the field instruction
1845 }
1846 if (bHasInstructions)
1849 if (nMode & FieldFlags::CmdEnd)
1850 {
1852 }
1853 if (nMode & FieldFlags::Close)
1854 {
1855 m_aRunText->append("}}}");
1856 }
1857 }
1858}
1859
1860void RtfAttributeOutput::WriteBookmarks_Impl(std::vector<OUString>& rStarts,
1861 std::vector<OUString>& rEnds)
1862{
1863 for (const auto& rStart : rStarts)
1864 {
1867 m_aRunText->append('}');
1868 }
1869 rStarts.clear();
1870
1871 for (const auto& rEnd : rEnds)
1872 {
1875 m_aRunText->append('}');
1876 }
1877 rEnds.clear();
1878}
1879
1880void RtfAttributeOutput::WriteAnnotationMarks_Impl(std::vector<OUString>& rStarts,
1881 std::vector<OUString>& rEnds)
1882{
1883 for (const auto& rStart : rStarts)
1884 {
1885 OString rName = OUStringToOString(rStart, RTL_TEXTENCODING_UTF8);
1886
1887 // Output the annotation mark
1888 const sal_Int32 nId = m_nNextAnnotationMarkId++;
1891 m_aRun->append(nId);
1892 m_aRun->append('}');
1893 }
1894 rStarts.clear();
1895
1896 for (const auto& rEnd : rEnds)
1897 {
1898 OString rName = OUStringToOString(rEnd, RTL_TEXTENCODING_UTF8);
1899
1900 // Get the id of the annotation mark
1901 auto it = m_rOpenedAnnotationMarksIds.find(rName);
1902 if (it != m_rOpenedAnnotationMarksIds.end())
1903 {
1904 const sal_Int32 nId = it->second;
1906 m_aRun->append(nId);
1907 m_aRun->append('}');
1908 m_rOpenedAnnotationMarksIds.erase(rName);
1909
1910 if (m_aPostitFields.find(nId) != m_aPostitFields.end())
1911 {
1912 m_aRunText->append("{");
1916 m_aRunText->append("}");
1917 }
1918 }
1919 }
1920 rEnds.clear();
1921}
1922
1924 const char* pStr, bool bTitlepg)
1925{
1926 OStringBuffer aSectionBreaks = m_aSectionBreaks;
1927 m_aSectionBreaks.setLength(0);
1928 RtfStringBuffer aRun = m_aRun;
1929 m_aRun.clear();
1930
1933 m_aSectionHeaders.append(
1934 static_cast<sal_Int32>(m_rExport.m_pCurrentPageDesc->GetMaster().GetULSpace().GetUpper()));
1935 if (bTitlepg)
1937 m_aSectionHeaders.append('{');
1938 m_aSectionHeaders.append(pStr);
1940 m_rExport.WriteHeaderFooterText(rFormat, bHeader);
1942 m_aSectionHeaders.append('}');
1943
1944 m_aSectionBreaks = aSectionBreaks;
1945 m_aRun = aRun;
1946}
1947
1948namespace
1949{
1950void lcl_TextFrameShadow(std::vector<std::pair<OString, OString>>& rFlyProperties,
1951 const SwFrameFormat& rFrameFormat)
1952{
1953 const SvxShadowItem& aShadowItem = rFrameFormat.GetShadow();
1954 if (aShadowItem.GetLocation() == SvxShadowLocation::NONE)
1955 return;
1956
1957 rFlyProperties.push_back(std::make_pair<OString, OString>("fShadow", OString::number(1)));
1958
1959 const Color& rColor = aShadowItem.GetColor();
1960 // We in fact need RGB to BGR, but the transformation is symmetric.
1961 rFlyProperties.push_back(std::make_pair<OString, OString>(
1962 "shadowColor", OString::number(wwUtility::RGBToBGR(rColor))));
1963
1964 // Twips -> points -> EMUs -- hacky, the intermediate step hides rounding errors on roundtrip.
1965 OString aShadowWidth = OString::number(sal_Int32(aShadowItem.GetWidth() / 20) * 12700);
1966 OString aOffsetX;
1967 OString aOffsetY;
1968 switch (aShadowItem.GetLocation())
1969 {
1970 case SvxShadowLocation::TopLeft:
1971 aOffsetX = "-" + aShadowWidth;
1972 aOffsetY = "-" + aShadowWidth;
1973 break;
1974 case SvxShadowLocation::TopRight:
1975 aOffsetX = aShadowWidth;
1976 aOffsetY = "-" + aShadowWidth;
1977 break;
1978 case SvxShadowLocation::BottomLeft:
1979 aOffsetX = "-" + aShadowWidth;
1980 aOffsetY = aShadowWidth;
1981 break;
1982 case SvxShadowLocation::BottomRight:
1983 aOffsetX = aShadowWidth;
1984 aOffsetY = aShadowWidth;
1985 break;
1986 case SvxShadowLocation::NONE:
1987 case SvxShadowLocation::End:
1988 break;
1989 }
1990 if (!aOffsetX.isEmpty())
1991 rFlyProperties.emplace_back("shadowOffsetX", aOffsetX);
1992 if (!aOffsetY.isEmpty())
1993 rFlyProperties.emplace_back("shadowOffsetY", aOffsetY);
1994}
1995
1996void lcl_TextFrameRelativeSize(std::vector<std::pair<OString, OString>>& rFlyProperties,
1997 const SwFrameFormat& rFrameFormat)
1998{
1999 const SwFormatFrameSize& rSize = rFrameFormat.GetFrameSize();
2000
2001 // Relative size of the Text Frame.
2002 const sal_uInt8 nWidthPercent = rSize.GetWidthPercent();
2003 if (nWidthPercent && nWidthPercent != SwFormatFrameSize::SYNCED)
2004 {
2005 rFlyProperties.push_back(
2006 std::make_pair<OString, OString>("pctHoriz", OString::number(nWidthPercent * 10)));
2007
2008 OString aRelation;
2009 switch (rSize.GetWidthPercentRelation())
2010 {
2011 case text::RelOrientation::PAGE_FRAME:
2012 aRelation = "1"; // page
2013 break;
2014 default:
2015 aRelation = "0"; // margin
2016 break;
2017 }
2018 rFlyProperties.emplace_back(std::make_pair("sizerelh", aRelation));
2019 }
2020 const sal_uInt8 nHeightPercent = rSize.GetHeightPercent();
2021 if (!(nHeightPercent && nHeightPercent != SwFormatFrameSize::SYNCED))
2022 return;
2023
2024 rFlyProperties.push_back(
2025 std::make_pair<OString, OString>("pctVert", OString::number(nHeightPercent * 10)));
2026
2027 OString aRelation;
2028 switch (rSize.GetHeightPercentRelation())
2029 {
2030 case text::RelOrientation::PAGE_FRAME:
2031 aRelation = "1"; // page
2032 break;
2033 default:
2034 aRelation = "0"; // margin
2035 break;
2036 }
2037 rFlyProperties.emplace_back(std::make_pair("sizerelv", aRelation));
2038}
2039}
2040
2041void RtfAttributeOutput::writeTextFrame(const ww8::Frame& rFrame, bool bTextBox)
2042{
2043 RtfStringBuffer aRunText;
2044 if (bTextBox)
2045 {
2047 aRunText = m_aRunText;
2048 m_aRunText.clear();
2049 }
2050
2052
2053 {
2054 // Save table state, in case the inner text also contains a table.
2056 m_rExport.m_pTableInfo = std::make_shared<ww8::WW8TableInfo>();
2057 std::unique_ptr<SwWriteTable> pTableWrt(std::move(m_pTableWrt));
2058 sal_uInt32 nTableDepth = m_nTableDepth;
2059
2060 m_nTableDepth = 0;
2061 /*
2062 * Save m_aRun as we should not lose the opening brace.
2063 * OTOH, just drop the contents of m_aRunText in case something
2064 * would be there, causing a problem later.
2065 */
2066 OString aSave = m_aRun.makeStringAndClear();
2067 // Also back m_bInRun and m_bSingleEmptyRun up.
2068 bool bInRunOrig = m_bInRun;
2069 m_bInRun = false;
2070 bool bSingleEmptyRunOrig = m_bSingleEmptyRun;
2071 m_bSingleEmptyRun = false;
2073
2074 const SwFrameFormat& rFrameFormat = rFrame.GetFrameFormat();
2075 const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
2076 SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
2077 SwNodeOffset nEnd
2078 = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
2079 m_rExport.SaveData(nStt, nEnd);
2080 m_rExport.m_pParentFrame = &rFrame;
2083
2086 m_aRun->append(aSave);
2087 m_aRunText.clear();
2088 m_bInRun = bInRunOrig;
2089 m_bSingleEmptyRun = bSingleEmptyRunOrig;
2090
2091 // Restore table state.
2092 m_rExport.m_pTableInfo = pTableInfoOrig;
2093 m_pTableWrt = std::move(pTableWrt);
2094 m_nTableDepth = nTableDepth;
2095 }
2096
2097 m_rExport.m_pParentFrame = nullptr;
2098
2099 m_rExport.Strm().WriteChar('}'); // shptxt
2100
2101 if (bTextBox)
2102 {
2103 m_aRunText = aRunText;
2106 }
2107}
2108
2114{
2115private:
2120 bool const m_bInRun;
2121
2122public:
2124 : m_rRtf(rRtf)
2125 , m_Run(std::move(rRtf.m_aRun))
2126 , m_RunText(std::move(rRtf.m_aRunText))
2128 , m_bInRun(rRtf.m_bInRun)
2129 {
2131 }
2133 {
2134 m_rRtf.m_aRun = std::move(m_Run);
2135 m_rRtf.m_aRunText = std::move(m_RunText);
2138
2141 }
2142};
2143
2144void RtfAttributeOutput::OutputFlyFrame_Impl(const ww8::Frame& rFrame, const Point& /*rNdTopLeft*/)
2145{
2146 const SwFrameFormat& rFrameFormat = rFrame.GetFrameFormat();
2147 if (rFrameFormat.GetFlySplit().GetValue())
2148 {
2149 // The frame can split: this was originally from a floating table, write it back as
2150 // such.
2151 SaveRunState aState(*this);
2152 const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
2153 SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
2154 SwNodeOffset nEnd
2155 = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
2156 m_rExport.SaveData(nStt, nEnd);
2157 GetExport().WriteText();
2159 return;
2160 }
2161
2162 const SwNode* pNode = rFrame.GetContent();
2163 const SwGrfNode* pGrfNode = pNode ? pNode->GetGrfNode() : nullptr;
2164
2165 switch (rFrame.GetWriterType())
2166 {
2168 {
2169 // If this is a TextBox of a shape, then ignore: it's handled in RtfSdrExport::StartShape().
2171 break;
2172
2173 SaveRunState const saved(*this);
2174
2175 m_rExport.m_pParentFrame = &rFrame;
2176
2180
2181 // Shape properties.
2182 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
2183 "shapeType", OString::number(ESCHER_ShpInst_TextBox)));
2184
2185 // When a frame has some low height, but automatically expanded due
2186 // to lots of contents, this size contains the real size.
2187 const Size aSize = rFrame.GetSize();
2188 m_pFlyFrameSize = &aSize;
2189
2192 m_rExport.OutputFormat(rFrame.GetFrameFormat(), false, false, true);
2193
2194 // Write ZOrder.
2195 if (const SdrObject* pObject = rFrame.GetFrameFormat().FindRealSdrObject())
2196 {
2198 m_rExport.Strm().WriteNumberAsString(pObject->GetOrdNum());
2199 }
2200
2203 m_aStyles.setLength(0);
2206 m_pFlyFrameSize = nullptr;
2207
2208 lcl_TextFrameShadow(m_aFlyProperties, rFrameFormat);
2209 lcl_TextFrameRelativeSize(m_aFlyProperties, rFrameFormat);
2210
2211 for (const std::pair<OString, OString>& rPair : m_aFlyProperties)
2212 {
2215 m_rExport.Strm().WriteOString(rPair.first);
2217 m_rExport.Strm().WriteOString(rPair.second);
2218 m_rExport.Strm().WriteOString("}}");
2219 }
2220 m_aFlyProperties.clear();
2221
2222 writeTextFrame(rFrame);
2223
2224 m_rExport.Strm().WriteChar('}'); // shpinst
2225 m_rExport.Strm().WriteChar('}'); // shp
2226
2228 }
2229 break;
2231 if (pGrfNode)
2232 {
2233 m_aRunText.append(dynamic_cast<const SwFlyFrameFormat*>(&rFrame.GetFrameFormat()),
2234 pGrfNode);
2235 }
2236 else if (!rFrame.IsInline())
2237 {
2238 m_rExport.m_pParentFrame = &rFrame;
2240 m_rExport.OutputFormat(rFrame.GetFrameFormat(), false, false, true);
2243 m_rExport.OutputFormat(rFrame.GetFrameFormat(), false, false, true);
2244 m_aRunText->append('}');
2245 m_rExport.m_pParentFrame = nullptr;
2246 }
2247 break;
2249 {
2250 const SdrObject* pSdrObj = rFrame.GetFrameFormat().FindRealSdrObject();
2251 if (pSdrObj)
2252 {
2256 m_aRunText->append(" SHAPE ");
2257 m_aRunText->append("}"
2259
2261
2262 m_aRunText->append('}');
2263 m_aRunText->append('}');
2264 }
2265 }
2266 break;
2268 {
2269 const SdrObject* pObject = rFrameFormat.FindRealSdrObject();
2270
2273
2274 if (pObject && pObject->GetObjInventor() == SdrInventor::FmForm)
2275 {
2276 if (auto pFormObj = dynamic_cast<const SdrUnoObj*>(pObject))
2277 {
2278 const uno::Reference<awt::XControlModel>& xControlModel
2279 = pFormObj->GetUnoControlModel();
2280 uno::Reference<lang::XServiceInfo> xInfo(xControlModel, uno::UNO_QUERY);
2281 if (xInfo.is())
2282 {
2283 uno::Reference<beans::XPropertySet> xPropSet(xControlModel, uno::UNO_QUERY);
2284 uno::Reference<beans::XPropertySetInfo> xPropSetInfo
2285 = xPropSet->getPropertySetInfo();
2286 OUString sName;
2287 if (xInfo->supportsService("com.sun.star.form.component.CheckBox"))
2288 {
2291 m_aRun->append(
2293 "{");
2294 m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFTYPE "1"); // 1 = checkbox
2295 // checkbox size in half points, this seems to be always 20
2297
2298 OUString aStr;
2299 sName = "Name";
2300 if (xPropSetInfo->hasPropertyByName(sName))
2301 {
2302 xPropSet->getPropertyValue(sName) >>= aStr;
2303 m_aRun->append(
2305 " ");
2306 m_aRun->append(
2308 m_aRun->append('}');
2309 }
2310
2311 sName = "HelpText";
2312 if (xPropSetInfo->hasPropertyByName(sName))
2313 {
2314 xPropSet->getPropertyValue(sName) >>= aStr;
2318 m_aRun->append(
2320 m_aRun->append('}');
2321 }
2322
2323 sName = "HelpF1Text";
2324 if (xPropSetInfo->hasPropertyByName(sName))
2325 {
2326 xPropSet->getPropertyValue(sName) >>= aStr;
2330 m_aRun->append(
2332 m_aRun->append('}');
2333 }
2334
2335 sal_Int16 nTemp = 0;
2336 xPropSet->getPropertyValue("DefaultState") >>= nTemp;
2338 m_aRun->append(static_cast<sal_Int32>(nTemp));
2339 xPropSet->getPropertyValue("State") >>= nTemp;
2341 m_aRun->append(static_cast<sal_Int32>(nTemp));
2342
2343 m_aRun->append("}}");
2344
2345 // field result is empty, ffres already contains the form result
2347 }
2348 else if (xInfo->supportsService("com.sun.star.form.component.TextField"))
2349 {
2350 OStringBuffer aBuf;
2351 OString aStr;
2352 OUString aTmp;
2353 const char* pStr;
2354
2357 m_aRun->append(
2359 " ");
2360 for (int i = 0; i < 8; i++)
2361 aBuf.append(char(0x00));
2362 xPropSet->getPropertyValue("Name") >>= aTmp;
2364 aBuf.append(OStringChar(static_cast<char>(aStr.getLength())) + aStr
2365 + OStringChar(char(0x00)));
2366 xPropSet->getPropertyValue("DefaultText") >>= aTmp;
2368 aBuf.append(static_cast<char>(aStr.getLength()));
2369 aBuf.append(aStr);
2370 for (int i = 0; i < 11; i++)
2371 aBuf.append(char(0x00));
2372 aStr = aBuf.makeStringAndClear();
2373 pStr = aStr.getStr();
2374 for (int i = 0; i < aStr.getLength(); i++, pStr++)
2376 m_aRun->append('}');
2378 xPropSet->getPropertyValue("Text") >>= aTmp;
2380 m_aRun->append('}');
2381 m_aRun->append(
2383 "{");
2384 sName = "HelpText";
2385 if (xPropSetInfo->hasPropertyByName(sName))
2386 {
2387 xPropSet->getPropertyValue(sName) >>= aTmp;
2391 m_aRun->append(
2393 m_aRun->append('}');
2394 }
2395
2396 sName = "HelpF1Text";
2397 if (xPropSetInfo->hasPropertyByName(sName))
2398 {
2399 xPropSet->getPropertyValue(sName) >>= aTmp;
2403 m_aRun->append(
2405 m_aRun->append('}');
2406 }
2407 m_aRun->append("}");
2408 }
2409 else if (xInfo->supportsService("com.sun.star.form.component.ListBox"))
2410 {
2411 OUString aStr;
2412 uno::Sequence<sal_Int16> aIntSeq;
2413 uno::Sequence<OUString> aStrSeq;
2414
2417 m_aRun->append(
2419 "{");
2422
2423 xPropSet->getPropertyValue("DefaultSelection") >>= aIntSeq;
2424 if (aIntSeq.hasElements())
2425 {
2427 // a dropdown list can have only one 'selected item by default'
2428 m_aRun->append(static_cast<sal_Int32>(aIntSeq[0]));
2429 }
2430
2431 xPropSet->getPropertyValue("SelectedItems") >>= aIntSeq;
2432 if (aIntSeq.hasElements())
2433 {
2435 // a dropdown list can have only one 'currently selected item'
2436 m_aRun->append(static_cast<sal_Int32>(aIntSeq[0]));
2437 }
2438
2439 sName = "Name";
2440 if (xPropSetInfo->hasPropertyByName(sName))
2441 {
2442 xPropSet->getPropertyValue(sName) >>= aStr;
2443 m_aRun->append(
2445 " ");
2446 m_aRun->append(
2448 m_aRun->append('}');
2449 }
2450
2451 sName = "HelpText";
2452 if (xPropSetInfo->hasPropertyByName(sName))
2453 {
2454 xPropSet->getPropertyValue(sName) >>= aStr;
2458 m_aRun->append(
2460 m_aRun->append('}');
2461 }
2462
2463 sName = "HelpF1Text";
2464 if (xPropSetInfo->hasPropertyByName(sName))
2465 {
2466 xPropSet->getPropertyValue(sName) >>= aStr;
2470 m_aRun->append(
2472 m_aRun->append('}');
2473 }
2474
2475 xPropSet->getPropertyValue("StringItemList") >>= aStrSeq;
2476 for (const auto& rStr : std::as_const(aStrSeq))
2477 m_aRun->append(
2480 + "}");
2481
2482 m_aRun->append("}}");
2483
2484 // field result is empty, ffres already contains the form result
2486 }
2487 else
2488 SAL_INFO("sw.rtf", __func__ << " unhandled form control: '"
2489 << xInfo->getImplementationName() << "'");
2490 m_aRun->append('}');
2491 }
2492 }
2493 }
2494
2495 m_aRun->append('}');
2496 }
2497 break;
2498 case ww8::Frame::eOle:
2499 {
2500 const SdrObject* pSdrObj = rFrameFormat.FindRealSdrObject();
2501 if (pSdrObj)
2502 {
2503 SwNodeIndex aIdx(*rFrameFormat.GetContent().GetContentIdx(), 1);
2504 SwOLENode& rOLENd = *aIdx.GetNode().GetOLENode();
2505 FlyFrameOLE(dynamic_cast<const SwFlyFrameFormat*>(&rFrameFormat), rOLENd,
2506 rFrame.GetLayoutSize());
2507 }
2508 }
2509 break;
2510 default:
2511 SAL_INFO("sw.rtf", __func__ << ": unknown type ("
2512 << static_cast<int>(rFrame.GetWriterType()) << ")");
2513 break;
2514 }
2515}
2516
2518{
2519 switch (rCaseMap.GetValue())
2520 {
2521 case SvxCaseMap::SmallCaps:
2523 break;
2524 case SvxCaseMap::Uppercase:
2526 break;
2527 default: // Something that rtf does not support
2529 m_aStyles.append(sal_Int32(0));
2531 m_aStyles.append(sal_Int32(0));
2532 break;
2533 }
2534}
2535
2537{
2538 const Color aColor(rColor.GetValue());
2539
2541 m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(aColor)));
2542}
2543
2545{
2547 if (!rContour.GetValue())
2548 m_aStyles.append(sal_Int32(0));
2549}
2550
2552{
2553 switch (rCrossedOut.GetStrikeout())
2554 {
2555 case STRIKEOUT_NONE:
2557 m_aStyles.append(sal_Int32(0));
2558 break;
2559 case STRIKEOUT_DOUBLE:
2561 m_aStyles.append(sal_Int32(1));
2562 break;
2563 default:
2565 break;
2566 }
2567}
2568
2570{
2571 short nEsc = rEscapement.GetEsc();
2572 short nProp = rEscapement.GetProportionalHeight();
2573 sal_Int32 nProp100 = nProp * 100;
2574 if (DFLT_ESC_PROP == nProp || nProp < 1 || nProp > 100)
2575 {
2576 if (DFLT_ESC_SUB == nEsc || DFLT_ESC_AUTO_SUB == nEsc)
2578 else if (DFLT_ESC_SUPER == nEsc || DFLT_ESC_AUTO_SUPER == nEsc)
2580 return;
2581 }
2582 if (DFLT_ESC_AUTO_SUPER == nEsc)
2583 {
2584 nEsc = .8 * (100 - nProp);
2585 ++nProp100; // A 1 afterwards means 'automatic' according to editeng/rtf/rtfitem.cxx
2586 }
2587 else if (DFLT_ESC_AUTO_SUB == nEsc)
2588 {
2589 nEsc = .2 * -(100 - nProp);
2590 ++nProp100;
2591 }
2592
2593 const char* pUpDn;
2594
2595 double fHeight = m_rExport.GetItem(RES_CHRATR_FONTSIZE).GetHeight();
2596
2597 if (0 < nEsc)
2599 else if (0 > nEsc)
2600 {
2602 fHeight = -fHeight;
2603 }
2604 else
2605 return;
2606
2607 m_aStyles.append('{');
2610 m_aStyles.append(nProp100);
2611 m_aStyles.append('}');
2612 m_aStyles.append(pUpDn);
2613
2614 /*
2615 * Calculate the act. FontSize and the percentage of the displacement;
2616 * RTF file expects half points, while internally it's in twips.
2617 * Formally : (FontSize * 1/20 ) pts x * 2
2618 * ----------------------- = ------------
2619 * 100% Escapement
2620 */
2621 m_aStyles.append(static_cast<sal_Int32>(round(fHeight * nEsc / 1000)));
2622}
2623
2625{
2626 // Insert \loch in MoveCharacterProperties
2628 m_aStyles.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont)));
2629
2630 // Insert \hich in MoveCharacterProperties
2632 m_aStylesAssocHich.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont)));
2633
2634 // FIXME: this may be a tad expensive... but the charset needs to be
2635 // consistent with what wwFont::WriteRtf() does
2638 aTmp.msPrimary, aTmp.msSecondary, rFont.GetCharSet());
2639 m_rExport.SetCurrentEncoding(rtl_getTextEncodingFromWindowsCharset(nWindowsCharset));
2640 if (m_rExport.GetCurrentEncoding() == RTL_TEXTENCODING_DONTKNOW)
2642}
2643
2645{
2646 switch (rFontSize.Which())
2647 {
2650 m_aStyles.append(static_cast<sal_Int32>(rFontSize.GetHeight() / 10));
2651 break;
2654 m_aStylesAssocDbch.append(static_cast<sal_Int32>(rFontSize.GetHeight() / 10));
2655 break;
2658 m_aStylesAssocRtlch.append(static_cast<sal_Int32>(rFontSize.GetHeight() / 10));
2659 break;
2660 }
2661}
2662
2664{
2665 // in quarter points then in twips
2667 m_aStyles.append(static_cast<sal_Int32>(rKerning.GetValue() / 5));
2669 m_aStyles.append(static_cast<sal_Int32>(rKerning.GetValue()));
2670}
2671
2673{
2674 switch (rLanguage.Which())
2675 {
2678 m_aStyles.append(
2679 static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
2681 m_aStylesAssocLtrch.append(
2682 static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
2683 break;
2686 m_aStylesAssocDbch.append(
2687 static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
2689 m_aStylesAssocLtrch.append(
2690 static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
2691 break;
2694 m_aStylesAssocRtlch.append(
2695 static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
2696 break;
2697 }
2698}
2699
2701{
2703 if (rPosture.GetPosture() == ITALIC_NONE)
2704 m_aStyles.append(sal_Int32(0));
2705}
2706
2708{
2710 if (!rShadow.GetValue())
2711 m_aStyles.append(sal_Int32(0));
2712}
2713
2715{
2716 const char* pStr = nullptr;
2718 bool bWord = false;
2719 // No StaticWhichCast(RES_CHRATR_WORDLINEMODE), this may be for a postit, where the which ids
2720 // don't match.
2721 if (pItem)
2722 bWord = static_cast<const SvxWordLineModeItem*>(pItem)->GetValue();
2723 switch (rUnderline.GetLineStyle())
2724 {
2725 case LINESTYLE_SINGLE:
2727 break;
2728 case LINESTYLE_DOUBLE:
2730 break;
2731 case LINESTYLE_NONE:
2733 break;
2734 case LINESTYLE_DOTTED:
2736 break;
2737 case LINESTYLE_DASH:
2739 break;
2740 case LINESTYLE_DASHDOT:
2742 break;
2745 break;
2746 case LINESTYLE_BOLD:
2748 break;
2749 case LINESTYLE_WAVE:
2751 break;
2754 break;
2755 case LINESTYLE_BOLDDASH:
2757 break;
2758 case LINESTYLE_LONGDASH:
2760 break;
2763 break;
2766 break;
2769 break;
2770 case LINESTYLE_BOLDWAVE:
2772 break;
2775 break;
2776 default:
2777 break;
2778 }
2779
2780 if (pStr)
2781 {
2782 m_aStyles.append(pStr);
2783 // NEEDSWORK looks like here rUnderline.GetColor() is always black,
2784 // even if the color in the odt is for example green...
2786 m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(rUnderline.GetColor())));
2787 }
2788}
2789
2791{
2793 if (rWeight.GetWeight() != WEIGHT_BOLD)
2794 m_aStyles.append(sal_Int32(0));
2795}
2796
2798{
2800 m_aStyles.append(static_cast<sal_Int32>(rAutoKern.GetValue() ? 1 : 0));
2801}
2802
2804{
2806 m_aStyles.append(static_cast<sal_Int32>(rBlink.GetValue() ? 2 : 0));
2807}
2808
2810{
2811 if (!rBrush.GetColor().IsTransparent())
2812 {
2814 m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(rBrush.GetColor())));
2815 }
2816}
2817
2819{
2820 // Insert \dbch in MoveCharacterProperties
2822 m_aStylesAssocDbch.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont)));
2823}
2824
2826{
2827 CharFontSize(rFontSize);
2828}
2829
2831{
2832 CharLanguage(rLanguageItem);
2833}
2834
2836{
2838 if (rPosture.GetPosture() == ITALIC_NONE)
2839 m_aStylesAssocDbch.append(sal_Int32(0));
2840}
2841
2843{
2845 if (rWeight.GetWeight() != WEIGHT_BOLD)
2846 m_aStylesAssocDbch.append(sal_Int32(0));
2847}
2848
2850{
2851 // Insert \rtlch in MoveCharacterProperties
2853 m_aStylesAssocRtlch.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont)));
2854}
2855
2857{
2858 CharFontSize(rFontSize);
2859}
2860
2862{
2863 CharLanguage(rLanguageItem);
2864}
2865
2867{
2869 if (rPosture.GetPosture() == ITALIC_NONE)
2870 m_aStylesAssocRtlch.append(sal_Int32(0));
2871}
2872
2874{
2876 if (rWeight.GetWeight() != WEIGHT_BOLD)
2877 m_aStylesAssocRtlch.append(sal_Int32(0));
2878}
2879
2881
2883
2885{
2887 m_aStyles.append(static_cast<sal_Int32>(rRotate.IsFitToLine() ? 1 : 0));
2888}
2889
2891{
2892 FontEmphasisMark v = rEmphasisMark.GetEmphasisMark();
2893 if (v == FontEmphasisMark::NONE)
2895 else if (v == (FontEmphasisMark::Dot | FontEmphasisMark::PosAbove))
2897 else if (v == (FontEmphasisMark::Accent | FontEmphasisMark::PosAbove))
2899 else if (v == (FontEmphasisMark::Circle | FontEmphasisMark::PosAbove))
2901 else if (v == (FontEmphasisMark::Dot | FontEmphasisMark::PosBelow))
2903}
2904
2906{
2907 if (!rTwoLines.GetValue())
2908 return;
2909
2910 sal_Unicode cStart = rTwoLines.GetStartBracket();
2911 sal_Unicode cEnd = rTwoLines.GetEndBracket();
2912
2913 sal_uInt16 nType;
2914 if (!cStart && !cEnd)
2915 nType = 0;
2916 else if ('{' == cStart || '}' == cEnd)
2917 nType = 4;
2918 else if ('<' == cStart || '>' == cEnd)
2919 nType = 3;
2920 else if ('[' == cStart || ']' == cEnd)
2921 nType = 2;
2922 else // all other kind of brackets
2923 nType = 1;
2924
2926 m_aStyles.append(static_cast<sal_Int32>(nType));
2927}
2928
2930{
2932 m_aStyles.append(static_cast<sal_Int32>(rScaleWidth.GetValue()));
2933}
2934
2936{
2937 const char* pStr;
2938 switch (rRelief.GetValue())
2939 {
2940 case FontRelief::Embossed:
2942 break;
2943 case FontRelief::Engraved:
2945 break;
2946 default:
2947 pStr = nullptr;
2948 break;
2949 }
2950
2951 if (pStr)
2952 m_aStyles.append(pStr);
2953}
2954
2956{
2958 if (!rHidden.GetValue())
2959 m_aStyles.append(sal_Int32(0));
2960}
2961
2963 const sal_uInt16 nDist, const bool bShadow)
2964{
2965 m_aStyles.append(
2967 bShadow ? SvxShadowLocation::BottomRight : SvxShadowLocation::NONE));
2968}
2969
2971{
2973 m_aStyles.append(static_cast<sal_Int32>(msfilter::util::TransColToIco(rBrush.GetColor())));
2974}
2975
2977{
2978 if (rURL.GetValue().isEmpty())
2979 return;
2980
2981 const SwCharFormat* pFormat;
2982 const SwTextINetFormat* pTextAtr = rURL.GetTextINetFormat();
2983
2984 if (pTextAtr && nullptr != (pFormat = pTextAtr->GetCharFormat()))
2985 {
2986 sal_uInt16 nStyle = m_rExport.GetId(pFormat);
2987 OString* pString = m_rExport.GetStyle(nStyle);
2988 if (pString)
2989 m_aStyles.append(*pString);
2990 }
2991}
2992
2994{
2995 sal_uInt16 nStyle = m_rExport.GetId(rCharFormat.GetCharFormat());
2997 m_aStyles.append(static_cast<sal_Int32>(nStyle));
2998 OString* pString = m_rExport.GetStyle(nStyle);
2999 if (pString)
3000 m_aStyles.append(*pString);
3001}
3002
3004{
3005 if (rFootnote.GetNumStr().isEmpty())
3007 else
3008 m_aRun->append(
3010}
3011
3013{
3014 SAL_INFO("sw.rtf", __func__ << " start");
3015
3017 EndRunProperties(nullptr);
3018 m_aRun->append(' ');
3019 WriteTextFootnoteNumStr(rFootnote);
3023 m_aRun->append(' ');
3024 WriteTextFootnoteNumStr(rFootnote);
3025
3026 /*
3027 * The footnote contains a whole paragraph, so we have to:
3028 * 1) Reset, then later restore the contents of our run buffer and run state.
3029 * 2) Buffer the output of the whole paragraph, as we do so for section headers already.
3030 */
3031 const SwNodeIndex* pIndex = rFootnote.GetTextFootnote()->GetStartNode();
3032 RtfStringBuffer aRun = m_aRun;
3033 m_aRun.clear();
3034 bool bInRunOrig = m_bInRun;
3035 m_bInRun = false;
3036 bool bSingleEmptyRunOrig = m_bSingleEmptyRun;
3037 m_bSingleEmptyRun = false;
3039 m_rExport.WriteSpecialText(pIndex->GetIndex() + 1, pIndex->GetNode().EndOfSectionIndex(),
3040 !rFootnote.IsEndNote() ? TXT_FTN : TXT_EDN);
3042 m_bInRun = bInRunOrig;
3043 m_bSingleEmptyRun = bSingleEmptyRunOrig;
3044 m_aRun = aRun;
3046 m_aSectionHeaders.setLength(0);
3047
3048 m_aRun->append("}");
3049 m_aRun->append("}");
3050
3051 SAL_INFO("sw.rtf", __func__ << " end");
3052}
3053
3054void RtfAttributeOutput::ParaLineSpacing_Impl(short nSpace, short nMulti)
3055{
3057 m_aStyles.append(static_cast<sal_Int32>(nSpace));
3059 m_aStyles.append(static_cast<sal_Int32>(nMulti));
3060}
3061
3063{
3064 switch (rAdjust.GetAdjust())
3065 {
3066 case SvxAdjust::Left:
3068 break;
3069 case SvxAdjust::Right:
3071 break;
3072 case SvxAdjust::BlockLine:
3073 case SvxAdjust::Block:
3074 if (rAdjust.GetLastBlock() == SvxAdjust::Block)
3076 else
3078 break;
3079 case SvxAdjust::Center:
3081 break;
3082 default:
3083 break;
3084 }
3085}
3086
3088{
3089 if (!rSplit.GetValue())
3091}
3092
3094{
3095 if (rWidows.GetValue())
3097 else
3099}
3100
3102{
3104
3105 for (sal_uInt16 n = 0; n < rTabStop.Count(); n++)
3106 {
3107 const SvxTabStop& rTS = rTabStop[n];
3108 if (SvxTabAdjust::Default != rTS.GetAdjustment())
3109 {
3110 const char* pFill = nullptr;
3111 switch (rTS.GetFill())
3112 {
3113 case cDfltFillChar:
3114 break;
3115
3116 case '.':
3118 break;
3119 case '_':
3121 break;
3122 case '-':
3124 break;
3125 case '=':
3127 break;
3128 default:
3129 break;
3130 }
3131 if (pFill)
3132 m_aStyles.append(pFill);
3133
3134 const char* pAdjStr = nullptr;
3135 switch (rTS.GetAdjustment())
3136 {
3137 case SvxTabAdjust::Right:
3139 break;
3140 case SvxTabAdjust::Decimal:
3142 break;
3143 case SvxTabAdjust::Center:
3145 break;
3146 default:
3147 break;
3148 }
3149 if (pAdjStr)
3150 m_aStyles.append(pAdjStr);
3152 m_aStyles.append(static_cast<sal_Int32>(rTS.GetTabPos() + nOffset));
3153 }
3154 else
3155 {
3157 m_aTabStop.append(rTabStop[0].GetTabPos());
3158 }
3159 }
3160}
3161
3163{
3165 m_aStyles.append(sal_Int32(rHyphenZone.IsHyphen()));
3166}
3167
3168void RtfAttributeOutput::ParaNumRule_Impl(const SwTextNode* pTextNd, sal_Int32 nLvl,
3169 sal_Int32 nNumId)
3170{
3171 if (USHRT_MAX == nNumId || 0 == nNumId || nullptr == pTextNd)
3172 return;
3173
3174 const SwNumRule* pRule = pTextNd->GetNumRule();
3175
3176 if (!pRule || !pTextNd->IsInList())
3177 return;
3178
3179 SAL_WARN_IF(pTextNd->GetActualListLevel() < 0 || pTextNd->GetActualListLevel() >= MAXLEVEL,
3180 "sw.rtf", "text node does not have valid list level");
3181
3182 const SwNumFormat* pFormat = pRule->GetNumFormat(nLvl);
3183 if (!pFormat)
3184 pFormat = &pRule->Get(nLvl);
3185
3186 const SfxItemSet& rNdSet = pTextNd->GetSwAttrSet();
3187
3188 m_aStyles.append('{');
3192 m_aStyles.append(' ');
3193
3195 SvxTextLeftMarginItem leftMargin(rNdSet.Get(RES_MARGIN_TEXTLEFT));
3196 leftMargin.SetTextLeft(leftMargin.GetTextLeft() + pFormat->GetIndentAt());
3197 firstLine.SetTextFirstLineOffset(pFormat->GetFirstLineOffset()); //TODO: overflow
3198
3199 sal_uInt16 nStyle = m_rExport.GetId(pFormat->GetCharFormat());
3200 OString* pString = m_rExport.GetStyle(nStyle);
3201 if (pString)
3202 m_aStyles.append(*pString);
3203
3204 {
3205 OUString sText;
3206 if (SVX_NUM_CHAR_SPECIAL == pFormat->GetNumberingType()
3207 || SVX_NUM_BITMAP == pFormat->GetNumberingType())
3208 {
3209 sal_UCS4 cBullet = pFormat->GetBulletChar();
3210 sText = OUString(&cBullet, 1);
3211 }
3212 else
3213 sText = pTextNd->GetNumString();
3214
3215 if (!sText.isEmpty())
3216 {
3217 m_aStyles.append(' ');
3219 }
3220
3221 if (OUTLINE_RULE != pRule->GetRuleType())
3222 {
3223 if (!sText.isEmpty())
3225 m_aStyles.append('}');
3227 if (nLvl > 8) // RTF knows only 9 levels
3228 {
3229 m_aStyles.append(sal_Int32(8));
3231 m_aStyles.append(nLvl);
3232 m_aStyles.append('}');
3233 }
3234 else
3235 m_aStyles.append(nLvl);
3236 }
3237 else
3240 m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetNumberingId(*pRule)) + 1);
3241 m_aStyles.append(' ');
3242 }
3243 FormatFirstLineIndent(firstLine);
3244 FormatTextLeftMargin(leftMargin);
3245}
3246
3248{
3249 if (!rScriptSpace.GetValue())
3250 return;
3251
3253}
3254
3256{
3257 SAL_INFO("sw.rtf", "TODO: " << __func__);
3258}
3259
3261{
3262 SAL_INFO("sw.rtf", "TODO: " << __func__);
3263}
3264
3266{
3267 const char* pStr;
3268 switch (rAlign.GetValue())
3269 {
3272 break;
3275 break;
3278 break;
3281 break;
3282
3283 default:
3285 break;
3286 }
3287 m_aStyles.append(pStr);
3288}
3289
3291{
3292 SAL_INFO("sw.rtf", "TODO: " << __func__);
3293}
3294
3296{
3298 {
3300 m_aSectionBreaks.append(static_cast<sal_Int32>(rSize.GetWidth()));
3302 m_aSectionBreaks.append(static_cast<sal_Int32>(rSize.GetHeight()));
3304 {
3306 m_aSectionBreaks.setLength(0);
3307 }
3308 }
3309}
3310
3312{
3313 SAL_INFO("sw.rtf", "TODO: " << __func__);
3314}
3315
3317{
3319 m_aStyles.append(static_cast<sal_Int32>(rFirstLine.GetTextFirstLineOffset()));
3320}
3321
3323{
3325 m_aStyles.append(static_cast<sal_Int32>(rTextLeftMargin.GetTextLeft()));
3327 m_aStyles.append(static_cast<sal_Int32>(rTextLeftMargin.GetTextLeft()));
3328}
3329
3331{
3332// (paragraph case, this will be an else branch once others are converted)
3333#if 0
3334 else
3335#endif
3336 {
3338 m_aStyles.append(static_cast<sal_Int32>(rRightMargin.GetRight()));
3340 m_aStyles.append(static_cast<sal_Int32>(rRightMargin.GetRight()));
3341 }
3342}
3343
3345{
3347 {
3349 {
3352
3353 if (const SvxBoxItem* pBoxItem = m_rExport.HasItem(RES_BOX))
3354 {
3356 = pBoxItem->CalcLineSpace(SvxBoxItemLine::LEFT, /*bEvenIfNoLine*/ true);
3358 = pBoxItem->CalcLineSpace(SvxBoxItemLine::RIGHT, /*bEvenIfNoLine*/ true);
3359 }
3360
3361 m_aPageMargins.nLeft += sal::static_int_cast<sal_uInt16>(rLRSpace.GetLeft());
3362 m_aPageMargins.nRight += sal::static_int_cast<sal_uInt16>(rLRSpace.GetRight());
3363
3364 if (rLRSpace.GetLeft())
3365 {
3367 m_aSectionBreaks.append(static_cast<sal_Int32>(m_aPageMargins.nLeft));
3368 }
3369 if (rLRSpace.GetRight())
3370 {
3372 m_aSectionBreaks.append(static_cast<sal_Int32>(m_aPageMargins.nRight));
3373 }
3374 if (rLRSpace.GetGutterMargin())
3375 {
3377 m_aSectionBreaks.append(static_cast<sal_Int32>(rLRSpace.GetGutterMargin()));
3378 }
3380 {
3382 m_aSectionBreaks.setLength(0);
3383 }
3384 }
3385 else
3386 {
3388 m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetTextLeft()));
3390 m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetRight()));
3392 m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetTextLeft()));
3394 m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetRight()));
3396 m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetTextFirstLineOffset()));
3397 }
3398 }
3399 else if (m_rExport.GetRTFFlySyntax())
3400 {
3401 // Wrap: top and bottom spacing, convert from twips to EMUs.
3402 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3403 "dxWrapDistLeft", OString::number(o3tl::convert(rLRSpace.GetLeft(), o3tl::Length::twip,
3405 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3406 "dxWrapDistRight", OString::number(o3tl::convert(
3408 }
3409}
3410
3412{
3414 {
3416 {
3417 OSL_ENSURE(m_rExport.GetCurItemSet(), "Impossible");
3418 if (!m_rExport.GetCurItemSet())
3419 return;
3420
3421 // If we export a follow page format, then our doc model has
3422 // separate header/footer distances for the first page and the
3423 // follow pages, but Word can have only a single distance. In case
3424 // the two values differ, work with the value from the first page
3425 // format to be in sync with the import.
3429
3430 if (aDistances.m_DyaTop)
3431 {
3433 m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.m_DyaTop));
3434 m_aPageMargins.nTop = aDistances.m_DyaTop;
3435 }
3436 if (aDistances.HasHeader())
3437 {
3439 m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.m_DyaHdrTop));
3440 }
3441
3442 if (aDistances.m_DyaBottom)
3443 {
3445 m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.m_DyaBottom));
3446 m_aPageMargins.nBottom = aDistances.m_DyaBottom;
3447 }
3448 if (aDistances.HasFooter())
3449 {
3451 m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.m_DyaHdrBottom));
3452 }
3454 {
3456 m_aSectionBreaks.setLength(0);
3457 }
3458 }
3459 else
3460 {
3461 // Spacing before.
3465 {
3468 m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetUpper()));
3469 }
3470 else
3471 {
3473 m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetUpper()));
3474 }
3476
3477 // Spacing after.
3481 {
3484 m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetLower()));
3485 }
3486 else
3487 {
3489 m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetLower()));
3490 }
3492
3493 // Contextual spacing.
3494 if (rULSpace.GetContext())
3496 }
3497 }
3498 else if (m_rExport.GetRTFFlySyntax())
3499 {
3500 // Wrap: top and bottom spacing, convert from twips to EMUs.
3501 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3502 "dyWrapDistTop", OString::number(o3tl::convert(rULSpace.GetUpper(), o3tl::Length::twip,
3504 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3505 "dyWrapDistBottom", OString::number(o3tl::convert(
3507 }
3508}
3509
3511{
3513 {
3514 css::text::WrapTextMode eSurround = rSurround.GetSurround();
3515 bool bGold = css::text::WrapTextMode_DYNAMIC == eSurround;
3516 if (bGold)
3517 eSurround = css::text::WrapTextMode_PARALLEL;
3518 RTFSurround aMC(bGold, static_cast<sal_uInt8>(eSurround));
3520 m_aRunText->append(static_cast<sal_Int32>(aMC.GetValue()));
3521 }
3523 {
3524 // See DocxSdrExport::startDMLAnchorInline() for SwFormatSurround -> WR / WRK mappings.
3525 sal_Int32 nWr = -1;
3526 std::optional<sal_Int32> oWrk;
3527 switch (rSurround.GetValue())
3528 {
3529 case css::text::WrapTextMode_NONE:
3530 nWr = 1; // top and bottom
3531 break;
3532 case css::text::WrapTextMode_THROUGH:
3533 nWr = 3; // none
3534 break;
3535 case css::text::WrapTextMode_PARALLEL:
3536 nWr = 2; // around
3537 oWrk = 0; // both sides
3538 break;
3539 case css::text::WrapTextMode_DYNAMIC:
3540 default:
3541 nWr = 2; // around
3542 oWrk = 3; // largest
3543 break;
3544 }
3545
3546 if (rSurround.IsContour())
3547 nWr = 4; // tight
3548
3551 if (oWrk)
3552 {
3555 }
3556 }
3557}
3558
3560{
3562 return;
3563
3564 switch (rFlyVert.GetRelationOrient())
3565 {
3566 case text::RelOrientation::PAGE_FRAME:
3567 m_aFlyProperties.push_back(
3568 std::make_pair<OString, OString>("posrelv", OString::number(1)));
3569 break;
3570 default:
3571 m_aFlyProperties.push_back(
3572 std::make_pair<OString, OString>("posrelv", OString::number(2)));
3573 m_rExport.Strm()
3576 break;
3577 }
3578
3579 switch (rFlyVert.GetVertOrient())
3580 {
3581 case text::VertOrientation::TOP:
3582 case text::VertOrientation::LINE_TOP:
3583 m_aFlyProperties.push_back(
3584 std::make_pair<OString, OString>("posv", OString::number(1)));
3585 break;
3586 case text::VertOrientation::BOTTOM:
3587 case text::VertOrientation::LINE_BOTTOM:
3588 m_aFlyProperties.push_back(
3589 std::make_pair<OString, OString>("posv", OString::number(3)));
3590 break;
3591 case text::VertOrientation::CENTER:
3592 case text::VertOrientation::LINE_CENTER:
3593 m_aFlyProperties.push_back(
3594 std::make_pair<OString, OString>("posv", OString::number(2)));
3595 break;
3596 default:
3597 break;
3598 }
3599
3602 if (m_pFlyFrameSize)
3603 {
3606 }
3607}
3608
3610{
3612 return;
3613
3614 switch (rFlyHori.GetRelationOrient())
3615 {
3616 case text::RelOrientation::PAGE_FRAME:
3617 m_aFlyProperties.push_back(
3618 std::make_pair<OString, OString>("posrelh", OString::number(1)));
3619 break;
3620 default:
3621 m_aFlyProperties.push_back(
3622 std::make_pair<OString, OString>("posrelh", OString::number(2)));
3623 m_rExport.Strm()
3626 break;
3627 }
3628
3629 switch (rFlyHori.GetHoriOrient())
3630 {
3631 case text::HoriOrientation::LEFT:
3632 m_aFlyProperties.push_back(
3633 std::make_pair<OString, OString>("posh", OString::number(1)));
3634 break;
3635 case text::HoriOrientation::CENTER:
3636 m_aFlyProperties.push_back(
3637 std::make_pair<OString, OString>("posh", OString::number(2)));
3638 break;
3639 case text::HoriOrientation::RIGHT:
3640 m_aFlyProperties.push_back(
3641 std::make_pair<OString, OString>("posh", OString::number(3)));
3642 break;
3643 default:
3644 break;
3645 }
3646
3649 if (m_pFlyFrameSize)
3650 {
3653 }
3654}
3655
3657{
3659 return;
3660
3661 RndStdIds eId = rAnchor.GetAnchorId();
3663 m_aRunText->append(static_cast<sal_Int32>(eId));
3664 switch (eId)
3665 {
3666 case RndStdIds::FLY_AT_PAGE:
3668 m_aRunText->append(static_cast<sal_Int32>(rAnchor.GetPageNum()));
3669 break;
3670 case RndStdIds::FLY_AT_PARA:
3671 case RndStdIds::FLY_AS_CHAR:
3673 break;
3674 default:
3675 break;
3676 }
3677}
3678
3680{
3682 {
3683 const Color& rColor = rBrush.GetColor();
3684 // We in fact need RGB to BGR, but the transformation is symmetric.
3685 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3686 "fillColor", OString::number(wwUtility::RGBToBGR(rColor))));
3687 }
3688 else if (!rBrush.GetColor().IsTransparent())
3689 {
3691 m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(rBrush.GetColor())));
3692 }
3693}
3694
3696{
3697 m_oFillStyle = rFillStyle.GetValue();
3698}
3699
3701{
3702 if (*m_oFillStyle != drawing::FillStyle_GRADIENT)
3703 return;
3704
3705 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3706 "fillType", OString::number(7))); // Shade using the fillAngle
3707
3708 const basegfx::BGradient& rGradient(rFillGradient.GetGradientValue());
3709 const basegfx::BColorStops& rColorStops(rGradient.GetColorStops());
3710
3711 // MCGR: It would be best to export the full MCGR definition here
3712 // with all ColorStops in rColorStops, but rtf does not support this.
3713 // Best thing to do and to stay compatible is to export front/back
3714 // colors as start/end and - when more than two ColorStops are defined -
3715 // guess that GradientStyle_AXIAL is used and thus create a "fillFocus"
3716 // entry
3717 // NOTE: I also found that loading file from testTextframeGradient
3718 // "textframe-gradient.rtf" and save-as *inverts* the gradient, so I
3719 // exchanged here fillColor/fillBackColor to get the correct order
3720 const Color aStartColor(rColorStops.front().getStopColor());
3721 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3722 "fillColor", OString::number(wwUtility::RGBToBGR(aStartColor))));
3723
3724 if (rColorStops.size() < 3)
3725 {
3726 // two-color version, use back as 2nd color
3727 const Color aEndColor(rColorStops.back().getStopColor());
3728 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3729 "fillBackColor", OString::number(wwUtility::RGBToBGR(aEndColor))));
3730 }
3731 else
3732 {
3733 // assume what was formally GradientStyle_AXIAL, see above and also refer to
3734 // FillModel::pushToPropMap 'fFocus' value and usage.
3735 // The 2nd color is the in-between color, use it
3736 const Color aEndColor(rColorStops[1].getStopColor());
3737 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3738 "fillBackColor", OString::number(wwUtility::RGBToBGR(aEndColor))));
3739 m_aFlyProperties.push_back(
3740 std::make_pair<OString, OString>("fillFocus", OString::number(50)));
3741 }
3742}
3743
3745{
3746 static const SvxBoxItemLine aBorders[] = { SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
3747 SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT };
3748 static const char* aBorderNames[]
3751
3752 sal_uInt16 const nDist = rBox.GetSmallestDistance();
3753
3755 {
3756 // Borders: spacing to contents, convert from twips to EMUs.
3757 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3758 "dxTextLeft", OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::LEFT),
3760 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3761 "dyTextTop", OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::TOP),
3763 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3764 "dxTextRight", OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::RIGHT),
3766 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3767 "dyTextBottom", OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::BOTTOM),
3769
3770 const editeng::SvxBorderLine* pLeft = rBox.GetLine(SvxBoxItemLine::LEFT);
3771 const editeng::SvxBorderLine* pRight = rBox.GetLine(SvxBoxItemLine::RIGHT);
3772 const editeng::SvxBorderLine* pTop = rBox.GetLine(SvxBoxItemLine::TOP);
3773 const editeng::SvxBorderLine* pBottom = rBox.GetLine(SvxBoxItemLine::BOTTOM);
3774
3775 if (!pLeft && !pRight && !pBottom && !pTop)
3776 {
3777 // fLine has default 'true', so need to write it out in case of no border.
3778 m_aFlyProperties.push_back(std::make_pair<OString, OString>("fLine", "0"));
3779 return;
3780 }
3781
3782 // RTF has the flags fTopLine, fBottomLine, fLeftLine and fRightLine to disable single border
3783 // lines. But Word cannot disable single border lines. So we do not use them. In case of
3784 // single border lines it is better to draw all four borders than drawing none. So we look
3785 // whether a border line exists, which is effectively drawn.
3786 const editeng::SvxBorderLine* pBorder = nullptr;
3787 if (pTop && pTop->GetBorderLineStyle() != SvxBorderLineStyle::NONE)
3788 pBorder = pTop;
3789 else if (pBottom && pBottom->GetBorderLineStyle() != SvxBorderLineStyle::NONE)
3790 pBorder = pBottom;
3791 else if (pLeft && pLeft->GetBorderLineStyle() != SvxBorderLineStyle::NONE)
3792 pBorder = pLeft;
3793 else if (pRight && pRight->GetBorderLineStyle() != SvxBorderLineStyle::NONE)
3794 pBorder = pRight;
3795
3796 if (!pBorder)
3797 {
3798 m_aFlyProperties.push_back(std::make_pair<OString, OString>("fLine", "0"));
3799 return;
3800 }
3801
3802 const Color& rColor = pBorder->GetColor();
3803 // We in fact need RGB to BGR, but the transformation is symmetric.
3804 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3805 "lineColor", OString::number(wwUtility::RGBToBGR(rColor))));
3806
3807 double const fConverted(
3809 sal_Int32 nWidth = o3tl::convert(fConverted, o3tl::Length::twip, o3tl::Length::emu);
3810 m_aFlyProperties.push_back(
3811 std::make_pair<OString, OString>("lineWidth", OString::number(nWidth)));
3812
3813 return;
3814 }
3815
3816 if (rBox.GetTop() && rBox.GetBottom() && rBox.GetLeft() && rBox.GetRight()
3817 && *rBox.GetTop() == *rBox.GetBottom() && *rBox.GetTop() == *rBox.GetLeft()
3818 && *rBox.GetTop() == *rBox.GetRight() && nDist == rBox.GetDistance(SvxBoxItemLine::TOP)
3819 && nDist == rBox.GetDistance(SvxBoxItemLine::LEFT)
3820 && nDist == rBox.GetDistance(SvxBoxItemLine::BOTTOM)
3821 && nDist == rBox.GetDistance(SvxBoxItemLine::RIGHT))
3822 m_aSectionBreaks.append(
3824 else
3825 {
3826 SvxShadowLocation eShadowLocation = SvxShadowLocation::NONE;
3827 if (const SvxShadowItem* pItem = GetExport().HasItem(RES_SHADOW))
3828 eShadowLocation = pItem->GetLocation();
3829
3830 const SvxBoxItemLine* pBrd = aBorders;
3831 const char** pBrdNms = aBorderNames;
3832 for (int i = 0; i < 4; ++i, ++pBrd, ++pBrdNms)
3833 {
3834 editeng::SvxBorderLine const* const pLn = rBox.GetLine(*pBrd);
3835 m_aSectionBreaks.append(
3836 OutBorderLine(m_rExport, pLn, *pBrdNms, rBox.GetDistance(*pBrd), eShadowLocation));
3837 }
3838 }
3839
3841 {
3843 m_aSectionBreaks.setLength(0);
3844 }
3845}
3846
3847void RtfAttributeOutput::FormatColumns_Impl(sal_uInt16 nCols, const SwFormatCol& rCol, bool bEven,
3848 SwTwips nPageSize)
3849{
3852
3853 if (rCol.GetLineAdj() != COLADJ_NONE)
3855
3856 if (bEven)
3857 {
3860 }
3861 else
3862 {
3863 const SwColumns& rColumns = rCol.GetColumns();
3864 for (sal_uInt16 n = 0; n < nCols;)
3865 {
3868
3871
3872 if (++n != nCols)
3873 {
3875 m_rExport.Strm().WriteNumberAsString(rColumns[n - 1].GetRight()
3876 + rColumns[n].GetLeft());
3877 }
3878 }
3879 }
3880}
3881
3883{
3884 if (rItem.GetValue())
3886}
3887
3889{
3890 SAL_INFO("sw.rtf", "TODO: " << __func__);
3891}
3892
3894{
3895 if (!rNumbering.IsCount())
3897}
3898
3900{
3901 SvxFrameDirection nDir = rDirection.GetValue();
3902 if (nDir == SvxFrameDirection::Environment)
3904
3906 {
3907 if (nDir == SvxFrameDirection::Vertical_RL_TB)
3908 {
3910 m_aSectionBreaks.append(static_cast<sal_Int32>(1));
3912 {
3914 m_aSectionBreaks.setLength(0);
3915 }
3916 }
3917 return;
3918 }
3919
3921 {
3922 if (nDir == SvxFrameDirection::Vertical_RL_TB)
3923 {
3924 // Top to bottom non-ASCII font
3925 m_aFlyProperties.push_back(std::make_pair<OString, OString>("txflTextFlow", "3"));
3926 }
3927 else if (rDirection.GetValue() == SvxFrameDirection::Vertical_LR_BT)
3928 {
3929 // Bottom to top non-ASCII font
3930 m_aFlyProperties.push_back(std::make_pair<OString, OString>("txflTextFlow", "2"));
3931 }
3932 return;
3933 }
3934
3935 if (nDir == SvxFrameDirection::Horizontal_RL_TB)
3937 else
3939}
3940
3942{
3943 const std::map<OUString, css::uno::Any>& rMap = rItem.GetGrabBag();
3944 for (const auto& rValue : rMap)
3945 {
3946 if (rValue.first == "ParaTopMarginBeforeAutoSpacing")
3947 {
3949 rValue.second >>= m_nParaBeforeSpacing;
3951 }
3952 else if (rValue.first == "ParaBottomMarginAfterAutoSpacing")
3953 {
3955 rValue.second >>= m_nParaAfterSpacing;
3957 }
3958 }
3959}
3960
3962
3964
3966{
3967 OUString sCmd; // for optional Parameters
3968 switch (pField->GetTyp()->Which())
3969 {
3970 //#i119803# Export user field for RTF filter
3971 case SwFieldIds::User:
3972 sCmd = pField->GetTyp()->GetName();
3973 m_rExport.OutputField(pField, ww::eNONE, sCmd);
3974 break;
3975 default:
3976 m_rExport.OutputField(pField, ww::eUNKNOWN, sCmd);
3977 break;
3978 }
3979}
3980
3981void RtfAttributeOutput::RefField(const SwField& /*rField*/, const OUString& /*rRef*/)
3982{
3983 SAL_INFO("sw.rtf", "TODO: " << __func__);
3984}
3985
3987{
3988 SAL_INFO("sw.rtf", "TODO: " << __func__);
3989}
3990
3991void RtfAttributeOutput::SetField(const SwField& /*rField*/, ww::eField /*eType*/,
3992 const OUString& /*rCmd*/)
3993{
3994 SAL_INFO("sw.rtf", "TODO: " << __func__);
3995}
3996
3998{
3999 const SwPostItField& rPField = *static_cast<const SwPostItField*>(pField);
4000
4001 OString aName = OUStringToOString(rPField.GetName(), RTL_TEXTENCODING_UTF8);
4002 auto it = m_rOpenedAnnotationMarksIds.find(aName);
4003 if (it != m_rOpenedAnnotationMarksIds.end())
4004 {
4005 // In case this field is inside annotation marks, we want to write the
4006 // annotation itself after the annotation mark is closed, not here.
4007 m_aPostitFields[it->second] = &rPField;
4008 return;
4009 }
4010
4013 m_aRunText->append("}");
4016 m_aRunText->append("}");
4018
4020
4022 {
4025 m_aRunText->append('}');
4026 }
4028 m_aRunText->append(static_cast<sal_Int32>(sw::ms::DateTime2DTTM(rPField.GetDateTime())));
4029 m_aRunText->append('}');
4030 if (const OutlinerParaObject* pObject = rPField.GetTextObject())
4032 m_aRunText->append('}');
4033}
4034
4036{
4037 // this is handled in OutputFlyFrame_Impl()
4038 return true;
4039}
4040
4042{
4045 " MACROBUTTON None ");
4046 RunText(pField->GetPar1());
4047 m_aRunText->append("}}");
4048 return false; // do not expand
4049}
4050
4052 : AttributeOutputBase("") // ConvertURL isn't used now in RTF output
4053 , m_rExport(rExport)
4054 , m_pPrevPageDesc(nullptr)
4055 , m_nStyleId(0)
4056 , m_nListId(0)
4057 , m_bIsRTL(false)
4058 , m_nScript(i18n::ScriptType::LATIN)
4059 , m_bControlLtrRtl(false)
4060 , m_nNextAnnotationMarkId(0)
4061 , m_nCurrentAnnotationMarkId(-1)
4062 , m_bTableCellOpen(false)
4063 , m_nTableDepth(0)
4064 , m_bTableAfterCell(false)
4065 , m_nColBreakNeeded(false)
4066 , m_bBufferSectionBreaks(false)
4067 , m_bBufferSectionHeaders(false)
4068 , m_bLastTable(true)
4069 , m_bWroteCellInfo(false)
4070 , m_bTableRowEnded(false)
4071 , m_bIsBeforeFirstParagraph(true)
4072 , m_bSingleEmptyRun(false)
4073 , m_bInRun(false)
4074 , m_bInRuby(false)
4075 , m_pFlyFrameSize(nullptr)
4076 , m_bParaBeforeAutoSpacing(false)
4077 , m_nParaBeforeSpacing(0)
4078 , m_bParaAfterAutoSpacing(false)
4079 , m_nParaAfterSpacing(0)
4080{
4081}
4082
4084
4086
4087// These are used by wwFont::WriteRtf()
4088
4090void RtfAttributeOutput::StartFont(std::u16string_view rFamilyName) const
4091{
4092 // write the font name hex-encoded, but without Unicode - Word at least
4093 // cannot read *both* Unicode and fallback as written by OutString
4096}
4097
4100{
4101 m_rExport.Strm().WriteOString(";}");
4103}
4104
4106void RtfAttributeOutput::FontAlternateName(std::u16string_view rName) const
4107{
4108 m_rExport.Strm()
4109 .WriteChar('{')
4112 .WriteChar(' ');
4113 // write the font name hex-encoded, but without Unicode - Word at least
4114 // cannot read *both* Unicode and fallback as written by OutString
4115 m_rExport.Strm()
4117 .WriteChar('}');
4118}
4119
4122{
4125 m_rExport.Strm().WriteChar(' ');
4126 m_rExport.SetCurrentEncoding(rtl_getTextEncodingFromWindowsCharset(nCharSet));
4127}
4128
4131{
4133
4134 const char* pStr = OOO_STRING_SVTOOLS_RTF_FNIL;
4135 switch (eFamily)
4136 {
4137 case FAMILY_ROMAN:
4139 break;
4140 case FAMILY_SWISS:
4142 break;
4143 case FAMILY_MODERN:
4145 break;
4146 case FAMILY_SCRIPT:
4148 break;
4149 case FAMILY_DECORATIVE:
4151 break;
4152 default:
4153 break;
4154 }
4156}
4157
4160{
4162
4163 sal_uInt16 nVal = 0;
4164 switch (ePitch)
4165 {
4166 case PITCH_FIXED:
4167 nVal = 1;
4168 break;
4169 case PITCH_VARIABLE:
4170 nVal = 2;
4171 break;
4172 default:
4173 break;
4174 }
4176}
4177
4178static void lcl_AppendSP(OStringBuffer& rBuffer, std::string_view cName, std::u16string_view rValue,
4179 const RtfExport& rExport)
4180{
4181 rBuffer.append("{" OOO_STRING_SVTOOLS_RTF_SP "{"); // "{\sp{"
4182 rBuffer.append(OOO_STRING_SVTOOLS_RTF_SN " "); //" \sn "
4183 rBuffer.append(cName); //"PropName"
4184 rBuffer.append("}{" OOO_STRING_SVTOOLS_RTF_SV " ");
4185 // "}{ \sv "
4186 rBuffer.append(msfilter::rtfutil::OutString(rValue, rExport.GetCurrentEncoding()));
4187 rBuffer.append("}}");
4188}
4189
4190static OString ExportPICT(const SwFlyFrameFormat* pFlyFrameFormat, const Size& rOrig,
4191 const Size& rRendered, const Size& rMapped, const SwCropGrf& rCr,
4192 const char* pBLIPType, const sal_uInt8* pGraphicAry, sal_uInt64 nSize,
4193 const RtfExport& rExport, SvStream* pStream = nullptr,
4194 bool bWritePicProp = true, const SwAttrSet* pAttrSet = nullptr)
4195{
4196 OStringBuffer aRet;
4197 if (pBLIPType && nSize && pGraphicAry)
4198 {
4199 bool bIsWMF = std::strcmp(pBLIPType, OOO_STRING_SVTOOLS_RTF_WMETAFILE) == 0;
4200
4201 aRet.append("{" OOO_STRING_SVTOOLS_RTF_PICT);
4202
4203 if (pFlyFrameFormat && bWritePicProp)
4204 {
4205 OUString sDescription = pFlyFrameFormat->GetObjDescription();
4206 //write picture properties - wzDescription at first
4207 //looks like: "{\*\picprop{\sp{\sn PropertyName}{\sv PropertyValue}}}"
4208 aRet.append(
4210 lcl_AppendSP(aRet, "wzDescription", sDescription, rExport);
4211 OUString sName = pFlyFrameFormat->GetObjTitle();
4212 lcl_AppendSP(aRet, "wzName", sName, rExport);
4213
4214 if (pAttrSet)
4215 {
4216 MirrorGraph eMirror = pAttrSet->Get(RES_GRFATR_MIRRORGRF).GetValue();
4217 if (eMirror == MirrorGraph::Vertical || eMirror == MirrorGraph::Both)
4218 // Mirror on the vertical axis is a horizontal flip.
4219 lcl_AppendSP(aRet, "fFlipH", u"1", rExport);
4220 }
4221
4222 aRet.append("}"); //"}"
4223 }
4224
4225 tools::Long nXCroppedSize = rOrig.Width() - (rCr.GetLeft() + rCr.GetRight());
4226 tools::Long nYCroppedSize = rOrig.Height() - (rCr.GetTop() + rCr.GetBottom());
4227 /* Graphic with a zero height or width, typically copied from webpages, caused crashes. */
4228 if (!nXCroppedSize)
4229 nXCroppedSize = 100;
4230 if (!nYCroppedSize)
4231 nYCroppedSize = 100;
4232
4233 //Given the original size and taking cropping into account
4234 //first, how much has the original been scaled to get the
4235 //final rendered size
4236 aRet.append(
4238 + OString::number(static_cast<sal_Int32>((100 * rRendered.Width()) / nXCroppedSize))
4240 + OString::number(static_cast<sal_Int32>((100 * rRendered.Height()) / nYCroppedSize))
4241
4242 + OOO_STRING_SVTOOLS_RTF_PICCROPL + OString::number(rCr.GetLeft())
4243 + OOO_STRING_SVTOOLS_RTF_PICCROPR + OString::number(rCr.GetRight())
4244 + OOO_STRING_SVTOOLS_RTF_PICCROPT + OString::number(rCr.GetTop())
4245 + OOO_STRING_SVTOOLS_RTF_PICCROPB + OString::number(rCr.GetBottom())
4246
4247 + OOO_STRING_SVTOOLS_RTF_PICW + OString::number(static_cast<sal_Int32>(rMapped.Width()))
4249 + OString::number(static_cast<sal_Int32>(rMapped.Height()))
4250
4252 + OString::number(static_cast<sal_Int32>(rOrig.Width()))
4254 + OString::number(static_cast<sal_Int32>(rOrig.Height()))
4255
4256 + pBLIPType);
4257 if (bIsWMF)
4258 {
4259 aRet.append(sal_Int32(8));
4260 msfilter::rtfutil::StripMetafileHeader(pGraphicAry, nSize);
4261 }
4262 aRet.append(SAL_NEWLINE_STRING);
4263 if (pStream)
4264 {
4265 pStream->WriteOString(aRet);
4266 aRet.setLength(0);
4267 }
4268 if (pStream)
4269 msfilter::rtfutil::WriteHex(pGraphicAry, nSize, pStream);
4270 else
4271 aRet.append(msfilter::rtfutil::WriteHex(pGraphicAry, nSize));
4272 aRet.append('}');
4273 if (pStream)
4274 {
4275 pStream->WriteOString(aRet);
4276 aRet.setLength(0);
4277 }
4278 }
4279 return aRet.makeStringAndClear();
4280}
4281
4283 SwOLENode& rOLENode, const Size& rSize)
4284{
4286 Size aSize(rOLENode.GetTwipSize());
4287 Size aRendered(aSize);
4288 aRendered.setWidth(rSize.Width());
4289 aRendered.setHeight(rSize.Height());
4290 const Graphic* pGraphic = rOLENode.GetGraphic();
4291 Size aMapped(pGraphic->GetPrefSize());
4292 auto& rCr = rOLENode.GetAttr(RES_GRFATR_CROPGRF);
4293 const char* pBLIPType = OOO_STRING_SVTOOLS_RTF_PNGBLIP;
4294 const sal_uInt8* pGraphicAry = nullptr;
4295 SvMemoryStream aStream;
4296 if (GraphicConverter::Export(aStream, *pGraphic, ConvertDataFormat::PNG) != ERRCODE_NONE)
4297 SAL_WARN("sw.rtf", "failed to export the graphic");
4298 sal_uInt32 nSize = aStream.TellEnd();
4299 pGraphicAry = static_cast<sal_uInt8 const*>(aStream.GetData());
4300 m_aRunText->append(ExportPICT(pFlyFrameFormat, aSize, aRendered, aMapped, rCr, pBLIPType,
4301 pGraphicAry, nSize, m_rExport));
4302 m_aRunText->append("}"); // shppict
4305 SvMemoryStream aWmfStream;
4306 if (GraphicConverter::Export(aWmfStream, *pGraphic, ConvertDataFormat::WMF) != ERRCODE_NONE)
4307 SAL_WARN("sw.rtf", "failed to export the graphic");
4308 nSize = aWmfStream.TellEnd();
4309 pGraphicAry = static_cast<sal_uInt8 const*>(aWmfStream.GetData());
4310 m_aRunText->append(ExportPICT(pFlyFrameFormat, aSize, aRendered, aMapped, rCr, pBLIPType,
4311 pGraphicAry, nSize, m_rExport));
4312 m_aRunText->append("}"); // nonshppict
4313}
4314
4316 SwOLENode& rOLENode, const Size& rSize)
4317{
4318 uno::Reference<embed::XEmbeddedObject> xObj(rOLENode.GetOLEObj().GetOleRef());
4319 sal_Int64 nAspect = rOLENode.GetAspect();
4320 svt::EmbeddedObjectRef aObjRef(xObj, nAspect);
4321 SvGlobalName aObjName(aObjRef->getClassID());
4322
4323 if (!SotExchange::IsMath(aObjName))
4324 return false;
4325
4327 uno::Reference<util::XCloseable> xClosable = xObj->getComponent();
4328 if (!xClosable.is())
4329 return false;
4330 auto pBase = dynamic_cast<oox::FormulaImExportBase*>(xClosable.get());
4331 SAL_WARN_IF(!pBase, "sw.rtf", "Math OLE object cannot write out RTF");
4332 if (pBase)
4333 {
4334 OStringBuffer aBuf;
4335 pBase->writeFormulaRtf(aBuf, m_rExport.GetCurrentEncoding());
4337 }
4338
4339 // Replacement graphic.
4341 FlyFrameOLEReplacement(pFlyFrameFormat, rOLENode, rSize);
4342 m_aRunText->append("}"); // mmathPict
4343 m_aRunText->append("}"); // mmath
4344
4345 return true;
4346}
4347
4348void RtfAttributeOutput::FlyFrameOLE(const SwFlyFrameFormat* pFlyFrameFormat, SwOLENode& rOLENode,
4349