crashtesting: assert on reimport of docx export of ooo102874-2.doc
[LibreOffice.git] / sw / source / filter / ww8 / rtfattributeoutput.cxx
blobcbb9fefb9b6f2ce7747d7346e93e0276b9ed426a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "rtfattributeoutput.hxx"
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>
33 #include <editeng/adjustitem.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>
39 #include <editeng/crossedoutitem.hxx>
40 #include <editeng/cmapitem.hxx>
41 #include <editeng/wrlmitem.hxx>
42 #include <editeng/udlnitem.hxx>
43 #include <editeng/langitem.hxx>
44 #include <editeng/escapementitem.hxx>
45 #include <editeng/fhgtitem.hxx>
46 #include <editeng/colritem.hxx>
47 #include <editeng/hyphenzoneitem.hxx>
48 #include <editeng/contouritem.hxx>
49 #include <editeng/shdditem.hxx>
50 #include <editeng/autokernitem.hxx>
51 #include <editeng/emphasismarkitem.hxx>
52 #include <editeng/twolinesitem.hxx>
53 #include <editeng/charscaleitem.hxx>
54 #include <editeng/charrotateitem.hxx>
55 #include <editeng/charreliefitem.hxx>
56 #include <editeng/paravertalignitem.hxx>
57 #include <editeng/blinkitem.hxx>
58 #include <editeng/charhiddenitem.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>
64 #include <editeng/frmdiritem.hxx>
65 #include <editeng/opaqitem.hxx>
66 #include <o3tl/unit_conversion.hxx>
67 #include <svx/svdouno.hxx>
68 #include <filter/msfilter/rtfutil.hxx>
69 #include <svx/xfillit0.hxx>
70 #include <svx/xflgrit.hxx>
71 #include <unotools/securityoptions.hxx>
72 #include <docufld.hxx>
73 #include <fmtclds.hxx>
74 #include <fmtrowsplt.hxx>
75 #include <fmtline.hxx>
76 #include <fmtanchr.hxx>
77 #include <ftninfo.hxx>
78 #include <htmltbl.hxx>
79 #include <ndgrf.hxx>
80 #include <pagedesc.hxx>
81 #include <swmodule.hxx>
82 #include <txtftn.hxx>
83 #include <txtinet.hxx>
84 #include <grfatr.hxx>
85 #include <ndole.hxx>
86 #include <lineinfo.hxx>
87 #include <redline.hxx>
88 #include <rtf.hxx>
89 #include <vcl/cvtgrf.hxx>
90 #include <oox/drawingml/drawingmltypes.hxx>
91 #include <oox/mathml/imexport.hxx>
92 #include <com/sun/star/i18n/ScriptType.hpp>
93 #include <svl/grabbagitem.hxx>
94 #include <frmatr.hxx>
95 #include <swtable.hxx>
96 #include <formatflysplit.hxx>
97 #include <fmtwrapinfluenceonobjpos.hxx>
98 #include "rtfexport.hxx"
99 #include <IDocumentDeviceAccess.hxx>
100 #include <sfx2/printer.hxx>
101 #include <fmtftntx.hxx>
103 using namespace ::com::sun::star;
104 using namespace sw::util;
106 static OString OutTBLBorderLine(RtfExport const& rExport, const editeng::SvxBorderLine* pLine,
107 const char* pStr)
109 OStringBuffer aRet;
110 if (pLine && !pLine->isEmpty())
112 aRet.append(pStr);
113 // single line
114 switch (pLine->GetBorderLineStyle())
116 case SvxBorderLineStyle::SOLID:
118 if (SvxBorderLineWidth::Hairline == pLine->GetWidth())
119 aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRHAIR);
120 else
121 aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRS);
123 break;
124 case SvxBorderLineStyle::DOTTED:
125 aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRDOT);
126 break;
127 case SvxBorderLineStyle::DASHED:
128 aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRDASH);
129 break;
130 case SvxBorderLineStyle::DOUBLE:
131 case SvxBorderLineStyle::DOUBLE_THIN:
132 aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRDB);
133 break;
134 case SvxBorderLineStyle::THINTHICK_SMALLGAP:
135 aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTNTHSG);
136 break;
137 case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
138 aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTNTHMG);
139 break;
140 case SvxBorderLineStyle::THINTHICK_LARGEGAP:
141 aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTNTHLG);
142 break;
143 case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
144 aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTHTNSG);
145 break;
146 case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
147 aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTHTNMG);
148 break;
149 case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
150 aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTHTNLG);
151 break;
152 case SvxBorderLineStyle::EMBOSSED:
153 aRet.append(OOO_STRING_SVTOOLS_RTF_BRDREMBOSS);
154 break;
155 case SvxBorderLineStyle::ENGRAVED:
156 aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRENGRAVE);
157 break;
158 case SvxBorderLineStyle::OUTSET:
159 aRet.append(OOO_STRING_SVTOOLS_RTF_BRDROUTSET);
160 break;
161 case SvxBorderLineStyle::INSET:
162 aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRINSET);
163 break;
164 case SvxBorderLineStyle::FINE_DASHED:
165 aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRDASHSM);
166 break;
167 case SvxBorderLineStyle::DASH_DOT:
168 aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRDASHD);
169 break;
170 case SvxBorderLineStyle::DASH_DOT_DOT:
171 aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRDASHDD);
172 break;
173 case SvxBorderLineStyle::NONE:
174 default:
175 aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRNONE);
176 break;
179 double const fConverted(
180 ::editeng::ConvertBorderWidthToWord(pLine->GetBorderLineStyle(), pLine->GetWidth()));
181 if (255 >= pLine->GetWidth()) // That value comes from RTF specs
183 aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRW
184 + OString::number(static_cast<sal_Int32>(fConverted)));
186 else
188 // use \brdrth to double the value range...
189 aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTH OOO_STRING_SVTOOLS_RTF_BRDRW
190 + OString::number(static_cast<sal_Int32>(fConverted) / 2));
193 aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRCF
194 + OString::number(static_cast<sal_Int32>(rExport.GetColor(pLine->GetColor()))));
196 else // tdf#129758 "no border" may be needed to override style
198 aRet.append(OString::Concat(pStr) + OOO_STRING_SVTOOLS_RTF_BRDRNONE);
200 return aRet.makeStringAndClear();
203 static OString OutBorderLine(RtfExport const& rExport, const editeng::SvxBorderLine* pLine,
204 const char* pStr, sal_uInt16 nDist,
205 SvxShadowLocation eShadowLocation = SvxShadowLocation::NONE)
207 OStringBuffer aRet(OutTBLBorderLine(rExport, pLine, pStr));
208 if (pLine)
210 aRet.append(OOO_STRING_SVTOOLS_RTF_BRSP + OString::number(static_cast<sal_Int32>(nDist)));
212 if (eShadowLocation == SvxShadowLocation::BottomRight)
213 aRet.append(LO_STRING_SVTOOLS_RTF_BRDRSH);
214 return aRet.makeStringAndClear();
217 void RtfAttributeOutput::RTLAndCJKState(bool bIsRTL, sal_uInt16 nScript)
219 m_bIsRTL = bIsRTL;
220 m_nScript = nScript;
221 m_bControlLtrRtl = true;
224 sal_Int32 RtfAttributeOutput::StartParagraph(const ww8::WW8TableNodeInfo::Pointer_t& pTextNodeInfo,
225 bool /*bGenerateParaId*/)
227 if (m_bIsBeforeFirstParagraph && m_rExport.m_nTextTyp != TXT_HDFT)
228 m_bIsBeforeFirstParagraph = false;
230 // Output table/table row/table cell starts if needed
231 if (pTextNodeInfo)
233 sal_uInt32 nRow = pTextNodeInfo->getRow();
234 sal_uInt32 nCell = pTextNodeInfo->getCell();
236 // New cell/row?
237 if (m_nTableDepth > 0 && !m_bTableCellOpen)
239 ww8::WW8TableNodeInfoInner::Pointer_t pDeepInner(
240 pTextNodeInfo->getInnerForDepth(m_nTableDepth));
241 OSL_ENSURE(pDeepInner, "TableNodeInfoInner not found");
242 // Make sure we always start a row between ending one and starting a cell.
243 // In case of subtables, we may not get the first cell.
244 if (pDeepInner && (pDeepInner->getCell() == 0 || m_bTableRowEnded))
246 StartTableRow(pDeepInner);
249 StartTableCell();
252 // Again, if depth was incremented, start a new table even if we skipped the first cell.
253 if ((nRow == 0 && nCell == 0) || (m_nTableDepth == 0 && pTextNodeInfo->getDepth()))
255 // Do we have to start the table?
256 // [If we are at the right depth already, it means that we
257 // continue the table cell]
258 sal_uInt32 nCurrentDepth = pTextNodeInfo->getDepth();
260 if (nCurrentDepth > m_nTableDepth)
262 // Start all the tables that begin here
263 for (sal_uInt32 nDepth = m_nTableDepth + 1; nDepth <= pTextNodeInfo->getDepth();
264 ++nDepth)
266 ww8::WW8TableNodeInfoInner::Pointer_t pInner(
267 pTextNodeInfo->getInnerForDepth(nDepth));
269 m_bLastTable = (nDepth == pTextNodeInfo->getDepth());
270 StartTable();
271 StartTableRow(pInner);
272 StartTableCell();
275 m_nTableDepth = nCurrentDepth;
280 OSL_ENSURE(m_aRun.getLength() == 0, "m_aRun is not empty");
281 return 0;
284 void RtfAttributeOutput::EndParagraph(
285 const ww8::WW8TableNodeInfoInner::Pointer_t& pTextNodeInfoInner)
287 bool bLastPara = false;
288 if (m_rExport.m_nTextTyp == TXT_FTN || m_rExport.m_nTextTyp == TXT_EDN
289 || m_rExport.m_rDoc.IsClipBoard())
291 // We're ending a paragraph that is the last paragraph of a footnote or endnote, or of clipboard.
292 bLastPara
293 = m_rExport.GetCurrentNodeIndex()
294 && m_rExport.GetCurrentNodeIndex() == m_rExport.m_pCurPam->End()->GetNodeIndex();
297 FinishTableRowCell(pTextNodeInfoInner);
299 RtfStringBuffer aParagraph;
301 aParagraph.appendAndClear(m_aRun);
302 aParagraph->append(m_aAfterRuns);
303 m_aAfterRuns.setLength(0);
304 if (m_bTableAfterCell)
305 m_bTableAfterCell = false;
306 else
308 aParagraph->append(SAL_NEWLINE_STRING);
309 // RTF_PAR at the end of the footnote or clipboard, would cause an additional empty paragraph.
310 if (!bLastPara)
312 aParagraph->append(OOO_STRING_SVTOOLS_RTF_PAR);
313 aParagraph->append(' ');
316 if (m_nColBreakNeeded)
318 aParagraph->append(OOO_STRING_SVTOOLS_RTF_COLUMN);
319 m_nColBreakNeeded = false;
322 if (!m_bBufferSectionHeaders)
323 aParagraph.makeStringAndClear(this);
324 else
325 m_aSectionHeaders.append(aParagraph.makeStringAndClear());
328 void RtfAttributeOutput::EmptyParagraph()
330 m_rExport.Strm()
331 .WriteOString(SAL_NEWLINE_STRING)
332 .WriteOString(OOO_STRING_SVTOOLS_RTF_PAR)
333 .WriteChar(' ');
336 void RtfAttributeOutput::SectionBreaks(const SwNode& rNode)
338 SwNodeIndex aNextIndex(rNode, 1);
339 if (rNode.IsTextNode())
341 OSL_ENSURE(m_aStyles.getLength() == 0, "m_aStyles is not empty");
343 // output page/section breaks
344 m_rExport.Strm().WriteOString(m_aSectionBreaks);
345 m_aSectionBreaks.setLength(0);
346 m_bBufferSectionBreaks = true;
348 // output section headers / footers
349 if (!m_bBufferSectionHeaders)
351 m_rExport.Strm().WriteOString(m_aSectionHeaders);
352 m_aSectionHeaders.setLength(0);
355 if (aNextIndex.GetNode().IsTextNode())
357 const SwTextNode* pTextNode = static_cast<SwTextNode*>(&aNextIndex.GetNode());
358 m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode);
359 // Save the current page description for now, so later we will be able to access the previous one.
360 m_pPrevPageDesc = pTextNode->FindPageDesc();
362 else if (aNextIndex.GetNode().IsTableNode())
364 const SwTableNode* pTableNode = static_cast<SwTableNode*>(&aNextIndex.GetNode());
365 const SwFrameFormat* pFormat = pTableNode->GetTable().GetFrameFormat();
366 m_rExport.OutputSectionBreaks(&(pFormat->GetAttrSet()), *pTableNode);
368 m_bBufferSectionBreaks = false;
370 else if (rNode.IsEndNode())
372 // End of something: make sure that it's the end of a table.
373 assert(rNode.StartOfSectionNode()->IsTableNode());
374 if (aNextIndex.GetNode().IsTextNode())
376 // Handle section break between a table and a text node following it.
377 const SwTextNode* pTextNode = aNextIndex.GetNode().GetTextNode();
378 m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode);
383 void RtfAttributeOutput::StartParagraphProperties()
385 OStringBuffer aPar;
386 if (!m_rExport.GetRTFFlySyntax())
388 aPar.append(OOO_STRING_SVTOOLS_RTF_PARD OOO_STRING_SVTOOLS_RTF_PLAIN " ");
390 if (!m_bBufferSectionHeaders)
391 m_rExport.Strm().WriteOString(aPar);
392 else
393 m_aSectionHeaders.append(aPar);
396 void RtfAttributeOutput::EndParagraphProperties(
397 const SfxItemSet& /*rParagraphMarkerProperties*/, const SwRedlineData* /*pRedlineData*/,
398 const SwRedlineData* /*pRedlineParagraphMarkerDeleted*/,
399 const SwRedlineData* /*pRedlineParagraphMarkerInserted*/)
401 // Do not call MoveCharacterProperties(),
402 // Otherwise associate properties in the paragraph style are ruined.
403 const OString aProperties = m_aStyles.makeStringAndClear();
404 m_rExport.Strm().WriteOString(aProperties);
407 void RtfAttributeOutput::StartRun(const SwRedlineData* pRedlineData, sal_Int32 /*nPos*/,
408 bool bSingleEmptyRun)
410 SAL_INFO("sw.rtf", __func__ << ", bSingleEmptyRun: " << bSingleEmptyRun);
412 m_bInRun = true;
413 m_bSingleEmptyRun = bSingleEmptyRun;
414 if (!m_bSingleEmptyRun)
415 m_aRun->append('{');
417 // if there is some redlining in the document, output it
418 Redline(pRedlineData);
420 OSL_ENSURE(m_aRunText.getLength() == 0, "m_aRunText is not empty");
423 void RtfAttributeOutput::EndRun(const SwTextNode* /*pNode*/, sal_Int32 /*nPos*/, sal_Int32 /*nLen*/,
424 bool /*bLastRun*/)
426 m_aRun->append(SAL_NEWLINE_STRING);
427 m_aRun.appendAndClear(m_aRunText);
429 if (m_bInRuby)
431 m_aRun->append(")}}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " {}}}");
432 m_bInRuby = false;
435 if (!m_bSingleEmptyRun && m_bInRun)
436 m_aRun->append('}');
437 m_bInRun = false;
440 void RtfAttributeOutput::StartRunProperties()
442 OSL_ENSURE(m_aStyles.getLength() == 0, "m_aStyles is not empty");
445 void RtfAttributeOutput::EndRunProperties(const SwRedlineData* /*pRedlineData*/)
447 const OString aProperties = MoveCharacterProperties(true);
448 m_aRun->append(aProperties);
451 OString RtfAttributeOutput::MoveCharacterProperties(bool aAutoWriteRtlLtr)
453 const OString aAssocHich = m_aStylesAssocHich.makeStringAndClear();
454 const OString aAssocDbch = m_aStylesAssocDbch.makeStringAndClear();
455 const OString aAssocRtlch = m_aStylesAssocRtlch.makeStringAndClear();
456 const OString aAssocLtrch = m_aStylesAssocLtrch.makeStringAndClear();
457 const OString aNormal = m_aStyles.makeStringAndClear();
458 OStringBuffer aBuf;
460 if (aAutoWriteRtlLtr && !m_bControlLtrRtl)
462 m_bControlLtrRtl = !aAssocRtlch.isEmpty();
463 m_bIsRTL = false;
464 m_nScript = i18n::ScriptType::LATIN;
467 if (m_bIsRTL)
469 if (!aAssocRtlch.isEmpty())
471 aBuf.append(OOO_STRING_SVTOOLS_RTF_LTRCH + aAssocLtrch
472 + " " OOO_STRING_SVTOOLS_RTF_RTLCH + aAssocRtlch);
475 else
477 if (!aAssocRtlch.isEmpty())
479 aBuf.append(OOO_STRING_SVTOOLS_RTF_RTLCH + aAssocRtlch
480 + " " OOO_STRING_SVTOOLS_RTF_LTRCH + aAssocLtrch);
482 if (!aAssocHich.isEmpty())
484 aBuf.append(OOO_STRING_SVTOOLS_RTF_HICH + aAssocHich);
486 if (!aNormal.isEmpty())
488 aBuf.append(OOO_STRING_SVTOOLS_RTF_LOCH + aNormal);
490 if (!aAssocDbch.isEmpty())
492 aBuf.append(OOO_STRING_SVTOOLS_RTF_DBCH + aAssocDbch);
496 if (m_bControlLtrRtl)
498 m_bControlLtrRtl = false;
500 switch (m_nScript)
502 case i18n::ScriptType::LATIN:
503 aBuf.append(OOO_STRING_SVTOOLS_RTF_LOCH);
504 break;
505 case i18n::ScriptType::ASIAN:
506 aBuf.append(OOO_STRING_SVTOOLS_RTF_DBCH);
507 break;
508 case i18n::ScriptType::COMPLEX:
509 /* noop */
510 default:
511 /* should not happen? */
512 break;
516 return aBuf.makeStringAndClear();
519 void RtfAttributeOutput::RunText(const OUString& rText, rtl_TextEncoding /*eCharSet*/,
520 const OUString& /*rSymbolFont*/)
522 SAL_INFO("sw.rtf", __func__ << ", rText: " << rText);
523 RawText(rText, m_rExport.GetCurrentEncoding());
526 OStringBuffer& RtfAttributeOutput::RunText() { return m_aRunText.getLastBuffer(); }
528 void RtfAttributeOutput::RawText(const OUString& rText, rtl_TextEncoding eCharSet)
530 m_aRunText->append(msfilter::rtfutil::OutString(rText, eCharSet));
533 void RtfAttributeOutput::StartRuby(const SwTextNode& rNode, sal_Int32 /*nPos*/,
534 const SwFormatRuby& rRuby)
536 WW8Ruby aWW8Ruby(rNode, rRuby, GetExport());
537 OUString aStr = FieldString(ww::eEQ) + "\\* jc" + OUString::number(aWW8Ruby.GetJC())
538 + " \\* \"Font:" + aWW8Ruby.GetFontFamily() + "\" \\* hps"
539 + OUString::number((aWW8Ruby.GetRubyHeight() + 5) / 10) + " \\o";
540 if (aWW8Ruby.GetDirective())
542 aStr += "\\a" + OUStringChar(aWW8Ruby.GetDirective());
544 aStr += "(\\s\\up " + OUString::number((aWW8Ruby.GetBaseHeight() + 10) / 20 - 1) + "(";
545 m_rExport.OutputField(nullptr, ww::eEQ, aStr, FieldFlags::Start | FieldFlags::CmdStart);
546 aStr = rRuby.GetText() + "),";
547 m_rExport.OutputField(nullptr, ww::eEQ, aStr, FieldFlags::NONE);
548 m_bInRuby = true;
551 void RtfAttributeOutput::EndRuby(const SwTextNode& /*rNode*/, sal_Int32 /*nPos*/) {}
553 bool RtfAttributeOutput::StartURL(const OUString& rUrl, const OUString& rTarget,
554 const OUString& /*rName*/)
556 m_aURLs.push(rUrl);
557 // Ignore hyperlink without a URL.
558 if (!rUrl.isEmpty())
560 m_aRun->append('{');
561 m_aRun->append(OOO_STRING_SVTOOLS_RTF_FIELD);
562 m_aRun->append('{');
563 m_aRun->append(OOO_STRING_SVTOOLS_RTF_IGNORE);
564 m_aRun->append(OOO_STRING_SVTOOLS_RTF_FLDINST);
565 m_aRun->append(" HYPERLINK ");
567 m_aRun->append("\"");
568 m_aRun->append(msfilter::rtfutil::OutString(rUrl, m_rExport.GetCurrentEncoding()));
569 m_aRun->append("\" ");
571 // Adding the target is likely a LO embellishment.
572 // Don't export it to clipboard, since editeng and other RTF readers won't understand it.
573 if (!rTarget.isEmpty() && !m_rExport.m_rDoc.IsClipBoard())
575 m_aRun->append("\\\\t \"");
576 m_aRun->append(msfilter::rtfutil::OutString(rTarget, m_rExport.GetCurrentEncoding()));
577 m_aRun->append("\" ");
580 m_aRun->append("}");
581 m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " {");
583 return true;
586 bool RtfAttributeOutput::EndURL(bool const isAtEndOfParagraph)
588 if (m_aURLs.empty())
590 return true;
593 const OUString& rURL = m_aURLs.top();
594 if (!rURL.isEmpty())
596 // UGLY: usually EndRun is called earlier, but there is an extra
597 // call to OutAttrWithRange() when at the end of the paragraph,
598 // so in that special case the output needs to be appended to the
599 // new run's text instead of the previous run
600 if (isAtEndOfParagraph)
602 // close the fldrslt group
603 m_aRunText->append("}}");
604 // close the field group
605 m_aRunText->append('}');
607 else
609 // close the fldrslt group
610 m_aRun->append("}}");
611 // close the field group
612 m_aRun->append('}');
615 m_aURLs.pop();
616 return true;
619 void RtfAttributeOutput::FieldVanish(const OUString& /*rText*/, ww::eField /*eType*/,
620 OUString const* /*pBookmarkName*/)
622 SAL_INFO("sw.rtf", "TODO: " << __func__);
625 void RtfAttributeOutput::Redline(const SwRedlineData* pRedline)
627 if (!pRedline)
628 return;
630 bool bRemoveCommentAuthorDates
631 = SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo)
632 && !SvtSecurityOptions::IsOptionSet(
633 SvtSecurityOptions::EOption::DocWarnKeepNoteAuthorDateInfo);
635 if (pRedline->GetType() == RedlineType::Insert)
637 m_aRun->append(OOO_STRING_SVTOOLS_RTF_REVISED);
638 m_aRun->append(OOO_STRING_SVTOOLS_RTF_REVAUTH);
639 m_aRun->append(static_cast<sal_Int32>(
640 m_rExport.GetRedline(SwModule::get()->GetRedlineAuthor(pRedline->GetAuthor()))));
641 if (!bRemoveCommentAuthorDates)
642 m_aRun->append(OOO_STRING_SVTOOLS_RTF_REVDTTM);
644 else if (pRedline->GetType() == RedlineType::Delete)
646 m_aRun->append(OOO_STRING_SVTOOLS_RTF_DELETED);
647 m_aRun->append(OOO_STRING_SVTOOLS_RTF_REVAUTHDEL);
648 m_aRun->append(static_cast<sal_Int32>(
649 m_rExport.GetRedline(SwModule::get()->GetRedlineAuthor(pRedline->GetAuthor()))));
650 if (!bRemoveCommentAuthorDates)
651 m_aRun->append(OOO_STRING_SVTOOLS_RTF_REVDTTMDEL);
653 if (!bRemoveCommentAuthorDates)
654 m_aRun->append(static_cast<sal_Int32>(sw::ms::DateTime2DTTM(pRedline->GetTimeStamp())));
655 m_aRun->append(' ');
658 void RtfAttributeOutput::FormatDrop(const SwTextNode& /*rNode*/,
659 const SwFormatDrop& /*rSwFormatDrop*/, sal_uInt16 /*nStyle*/,
660 ww8::WW8TableNodeInfo::Pointer_t /*pTextNodeInfo*/,
661 ww8::WW8TableNodeInfoInner::Pointer_t /*pTextNodeInfoInner*/)
663 SAL_INFO("sw.rtf", "TODO: " << __func__);
666 void RtfAttributeOutput::ParagraphStyle(sal_uInt16 nStyle)
668 OString* pStyle = m_rExport.GetStyle(nStyle);
669 OStringBuffer aStyle(OOO_STRING_SVTOOLS_RTF_S
670 + OString::number(static_cast<sal_Int32>(nStyle)));
671 if (pStyle)
672 aStyle.append(*pStyle);
673 if (!m_bBufferSectionHeaders)
674 m_rExport.Strm().WriteOString(aStyle);
675 else
676 m_aSectionHeaders.append(aStyle);
679 void RtfAttributeOutput::TableInfoCell(
680 const ww8::WW8TableNodeInfoInner::Pointer_t& /*pTableTextNodeInfoInner*/)
682 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_INTBL);
683 if (m_nTableDepth > 1)
685 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ITAP);
686 m_aStyles.append(static_cast<sal_Int32>(m_nTableDepth));
688 m_bWroteCellInfo = true;
691 void RtfAttributeOutput::TableInfoRow(
692 const ww8::WW8TableNodeInfoInner::Pointer_t& /*pTableTextNodeInfo*/)
694 /* noop */
697 void RtfAttributeOutput::TablePositioning(SwFrameFormat* pFlyFormat)
699 if (!pFlyFormat || !pFlyFormat->GetFlySplit().GetValue())
701 return;
704 switch (pFlyFormat->GetVertOrient().GetRelationOrient())
706 case text::RelOrientation::PAGE_PRINT_AREA:
707 // relative to margin
708 m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPVMRG);
709 break;
710 case text::RelOrientation::PAGE_FRAME:
711 // relative to page
712 m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPVPG);
713 break;
714 default:
715 // text::RelOrientation::FRAME
716 // relative to text
717 m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPVPARA);
718 break;
721 switch (pFlyFormat->GetHoriOrient().GetRelationOrient())
723 case text::RelOrientation::FRAME:
724 // relative to column
725 m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPHCOL);
726 break;
727 case text::RelOrientation::PAGE_PRINT_AREA:
728 // relative to margin
729 m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPHMRG);
730 break;
731 default:
732 // text::RelOrientation::PAGE_FRAME
733 // relative to page
734 m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPHPG);
735 break;
738 // Similar to RtfAttributeOutput::FormatHorizOrientation(), but for tables.
739 switch (pFlyFormat->GetHoriOrient().GetHoriOrient())
741 case text::HoriOrientation::LEFT:
742 // left
743 m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPOSXL);
744 break;
745 case text::HoriOrientation::CENTER:
746 // centered
747 m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPOSXC);
748 break;
749 case text::HoriOrientation::RIGHT:
750 // right
751 m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPOSXR);
752 break;
753 default:
754 SwTwips nTPosX = pFlyFormat->GetHoriOrient().GetPos();
755 m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPOSX);
756 m_aRowDefs.append(static_cast<sal_Int32>(nTPosX));
757 break;
760 // Similar to RtfAttributeOutput::FormatVertOrientation(), but for tables.
761 switch (pFlyFormat->GetVertOrient().GetVertOrient())
763 case text::VertOrientation::TOP:
764 // up
765 m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPOSYT);
766 break;
767 case text::VertOrientation::CENTER:
768 // centered
769 m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPOSYC);
770 break;
771 case text::VertOrientation::BOTTOM:
772 // down
773 m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPOSYB);
774 break;
775 default:
776 SwTwips nTPosY = pFlyFormat->GetVertOrient().GetPos();
777 m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPOSY);
778 m_aRowDefs.append(static_cast<sal_Int32>(nTPosY));
779 break;
782 // Similar to RtfAttributeOutput::FormatULSpace(), but for tables.
783 sal_uInt16 nTdfrmtxtTop = pFlyFormat->GetULSpace().GetUpper();
784 m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TDFRMTXTTOP);
785 m_aRowDefs.append(static_cast<sal_Int32>(nTdfrmtxtTop));
786 sal_uInt16 nTdfrmtxtBottom = pFlyFormat->GetULSpace().GetLower();
787 m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TDFRMTXTBOTTOM);
788 m_aRowDefs.append(static_cast<sal_Int32>(nTdfrmtxtBottom));
790 // Similar to RtfAttributeOutput::FormatLRSpace(), but for tables.
791 sal_uInt16 nTdfrmtxtLeft = pFlyFormat->GetLRSpace().ResolveLeft({});
792 m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TDFRMTXTLEFT);
793 m_aRowDefs.append(static_cast<sal_Int32>(nTdfrmtxtLeft));
794 sal_uInt16 nTdfrmtxtRight = pFlyFormat->GetLRSpace().ResolveRight({});
795 m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TDFRMTXTRIGHT);
796 m_aRowDefs.append(static_cast<sal_Int32>(nTdfrmtxtRight));
798 if (!pFlyFormat->GetWrapInfluenceOnObjPos().GetAllowOverlap())
800 // Allowing overlap is the default in both Writer and in RTF.
801 m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TABSNOOVRLP);
802 m_aRowDefs.append(static_cast<sal_Int32>(1));
806 void RtfAttributeOutput::TableDefinition(
807 const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
809 InitTableHelper(pTableTextNodeInfoInner);
811 const SwTable* pTable = pTableTextNodeInfoInner->getTable();
812 SwFrameFormat* pFormat = pTable->GetFrameFormat();
814 m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_TROWD);
815 TableOrientation(pTableTextNodeInfoInner);
816 TableBidi(pTableTextNodeInfoInner);
817 TableHeight(pTableTextNodeInfoInner);
818 TableCanSplit(pTableTextNodeInfoInner);
820 // Write table positioning properties in case this is a floating table.
821 TablePositioning(pTable->GetTableNode()->GetFlyFormat());
823 // Cell margins
824 const SvxBoxItem& rBox = pFormat->GetBox();
825 static const SvxBoxItemLine aBorders[] = { SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
826 SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT };
828 static const char* aRowPadNames[]
829 = { OOO_STRING_SVTOOLS_RTF_TRPADDT, OOO_STRING_SVTOOLS_RTF_TRPADDL,
830 OOO_STRING_SVTOOLS_RTF_TRPADDB, OOO_STRING_SVTOOLS_RTF_TRPADDR };
832 static const char* aRowPadUnits[]
833 = { OOO_STRING_SVTOOLS_RTF_TRPADDFT, OOO_STRING_SVTOOLS_RTF_TRPADDFL,
834 OOO_STRING_SVTOOLS_RTF_TRPADDFB, OOO_STRING_SVTOOLS_RTF_TRPADDFR };
836 for (int i = 0; i < 4; ++i)
838 m_aRowDefs.append(aRowPadUnits[i]);
839 m_aRowDefs.append(sal_Int32(3));
840 m_aRowDefs.append(aRowPadNames[i]);
841 m_aRowDefs.append(static_cast<sal_Int32>(rBox.GetDistance(aBorders[i])));
844 // The cell-dependent properties
845 const double fWidthRatio = m_pTableWrt->GetAbsWidthRatio();
846 const SwWriteTableRows& aRows = m_pTableWrt->GetRows();
847 sal_uInt32 nRow = pTableTextNodeInfoInner->getRow();
848 if (nRow >= aRows.size())
850 SAL_WARN("sw.ww8", "RtfAttributeOutput::TableDefinition: out of range row: " << nRow);
851 return;
853 SwWriteTableRow* pRow = aRows[nRow].get();
854 SwTwips nSz = 0;
856 // Not using m_nTableDepth, which is not yet incremented here.
857 sal_uInt32 nCurrentDepth = pTableTextNodeInfoInner->getDepth();
858 m_aCells[nCurrentDepth] = pRow->GetCells().size();
859 for (sal_uInt32 i = 0; i < m_aCells[nCurrentDepth]; i++)
861 const SwWriteTableCell* const pCell = pRow->GetCells()[i].get();
862 const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat();
864 pTableTextNodeInfoInner->setCell(i);
865 TableCellProperties(pTableTextNodeInfoInner);
867 // Right boundary: this can't be in TableCellProperties as the old
868 // value of nSz is needed.
869 nSz += pCellFormat->GetFrameSize().GetWidth();
870 m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CELLX);
871 m_aRowDefs.append(static_cast<sal_Int32>(pFormat->GetLRSpace().ResolveLeft({})
872 + rtl::math::round(nSz * fWidthRatio)));
876 void RtfAttributeOutput::TableDefaultBorders(
877 const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
880 * The function name is a bit misleading: given that we write borders
881 * before each row, we just have borders, not default ones. Additionally,
882 * this function actually writes borders for a specific cell only and is
883 * called for each cell.
886 const SwWriteTableRows& aRows = m_pTableWrt->GetRows();
887 SwWriteTableRow* pRow = aRows[pTableTextNodeInfoInner->getRow()].get();
888 const SwWriteTableCell* const pCell
889 = pRow->GetCells()[pTableTextNodeInfoInner->getCell()].get();
890 const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat();
891 const SvxBoxItem* pItem = pCellFormat->GetAttrSet().GetItemIfSet(RES_BOX);
892 if (!pItem)
893 return;
895 auto& rBox = *pItem;
896 static const SvxBoxItemLine aBorders[] = { SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
897 SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT };
898 static const char* aBorderNames[]
899 = { OOO_STRING_SVTOOLS_RTF_CLBRDRT, OOO_STRING_SVTOOLS_RTF_CLBRDRL,
900 OOO_STRING_SVTOOLS_RTF_CLBRDRB, OOO_STRING_SVTOOLS_RTF_CLBRDRR };
901 //Yes left and top are swapped with each other for cell padding! Because
902 //that's what the thundering annoying rtf export/import word xp does.
903 static const char* aCellPadNames[]
904 = { OOO_STRING_SVTOOLS_RTF_CLPADL, OOO_STRING_SVTOOLS_RTF_CLPADT,
905 OOO_STRING_SVTOOLS_RTF_CLPADB, OOO_STRING_SVTOOLS_RTF_CLPADR };
906 static const char* aCellPadUnits[]
907 = { OOO_STRING_SVTOOLS_RTF_CLPADFL, OOO_STRING_SVTOOLS_RTF_CLPADFT,
908 OOO_STRING_SVTOOLS_RTF_CLPADFB, OOO_STRING_SVTOOLS_RTF_CLPADFR };
909 for (int i = 0; i < 4; ++i)
911 if (const editeng::SvxBorderLine* pLn = rBox.GetLine(aBorders[i]))
912 m_aRowDefs.append(OutTBLBorderLine(m_rExport, pLn, aBorderNames[i]));
913 if (rBox.GetDistance(aBorders[i]))
915 m_aRowDefs.append(aCellPadUnits[i]);
916 m_aRowDefs.append(sal_Int32(3));
917 m_aRowDefs.append(aCellPadNames[i]);
918 m_aRowDefs.append(static_cast<sal_Int32>(rBox.GetDistance(aBorders[i])));
923 void RtfAttributeOutput::TableBackgrounds(
924 const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
926 const SwTable* pTable = pTableTextNodeInfoInner->getTable();
927 const SwTableBox* pTableBox = pTableTextNodeInfoInner->getTableBox();
928 const SwTableLine* pTableLine = pTableBox->GetUpper();
930 Color aColor = COL_AUTO;
931 auto pTableColorProp
932 = pTable->GetFrameFormat()->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
933 if (pTableColorProp)
934 aColor = pTableColorProp->GetColor();
936 auto pRowColorProp
937 = pTableLine->GetFrameFormat()->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
938 if (pRowColorProp && pRowColorProp->GetColor() != COL_AUTO)
939 aColor = pRowColorProp->GetColor();
941 const SwWriteTableRows& aRows = m_pTableWrt->GetRows();
942 SwWriteTableRow* pRow = aRows[pTableTextNodeInfoInner->getRow()].get();
943 const SwWriteTableCell* const pCell
944 = pRow->GetCells()[pTableTextNodeInfoInner->getCell()].get();
945 const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat();
946 if (const SvxBrushItem* pBrushItem = pCellFormat->GetAttrSet().GetItemIfSet(RES_BACKGROUND))
948 if (pBrushItem->GetColor() != COL_AUTO)
949 aColor = pBrushItem->GetColor();
952 if (!aColor.IsTransparent())
954 m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLCBPAT);
955 m_aRowDefs.append(static_cast<sal_Int32>(m_rExport.GetColor(aColor)));
959 void RtfAttributeOutput::TableRowRedline(
960 const ww8::WW8TableNodeInfoInner::Pointer_t& /*pTableTextNodeInfoInner*/)
964 void RtfAttributeOutput::TableCellRedline(
965 const ww8::WW8TableNodeInfoInner::Pointer_t& /*pTableTextNodeInfoInner*/)
969 void RtfAttributeOutput::TableHeight(
970 const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
972 const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
973 const SwTableLine* pTabLine = pTabBox->GetUpper();
974 const SwFrameFormat* pLineFormat = pTabLine->GetFrameFormat();
975 const SwFormatFrameSize& rLSz = pLineFormat->GetFrameSize();
977 if (!(SwFrameSize::Variable != rLSz.GetHeightSizeType() && rLSz.GetHeight()))
978 return;
980 sal_Int32 nHeight = 0;
982 switch (rLSz.GetHeightSizeType())
984 case SwFrameSize::Fixed:
985 nHeight = -rLSz.GetHeight();
986 break;
987 case SwFrameSize::Minimum:
988 nHeight = rLSz.GetHeight();
989 break;
990 default:
991 break;
994 if (nHeight)
996 m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_TRRH);
997 m_aRowDefs.append(nHeight);
1001 void RtfAttributeOutput::TableCanSplit(
1002 const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
1004 const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
1005 const SwTableLine* pTabLine = pTabBox->GetUpper();
1006 const SwFrameFormat* pLineFormat = pTabLine->GetFrameFormat();
1007 const SwFormatRowSplit& rSplittable = pLineFormat->GetRowSplit();
1009 // The rtf default is to allow a row to break
1010 if (!rSplittable.GetValue())
1011 m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_TRKEEP);
1014 void RtfAttributeOutput::TableBidi(
1015 const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
1017 const SwTable* pTable = pTableTextNodeInfoInner->getTable();
1018 const SwFrameFormat* pFrameFormat = pTable->GetFrameFormat();
1020 if (m_rExport.TrueFrameDirection(*pFrameFormat) != SvxFrameDirection::Horizontal_RL_TB)
1021 m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_LTRROW);
1022 else
1023 m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_RTLROW);
1026 void RtfAttributeOutput::TableVerticalCell(
1027 const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
1029 const SwWriteTableRows& aRows = m_pTableWrt->GetRows();
1030 SwWriteTableRow* pRow = aRows[pTableTextNodeInfoInner->getRow()].get();
1031 const SwWriteTableCell* const pCell
1032 = pRow->GetCells()[pTableTextNodeInfoInner->getCell()].get();
1033 const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat();
1035 // Text direction.
1036 if (SvxFrameDirection::Vertical_RL_TB == m_rExport.TrueFrameDirection(*pCellFormat))
1037 m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLTXTBRL);
1038 else if (SvxFrameDirection::Vertical_LR_BT == m_rExport.TrueFrameDirection(*pCellFormat))
1039 m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLTXBTLR);
1041 // vertical merges
1042 if (pCell->GetRowSpan() > 1)
1043 m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLVMGF);
1044 else if (pCell->GetRowSpan() == 0)
1045 m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLVMRG);
1047 // vertical alignment
1048 const SwFormatVertOrient* pVertOrientItem
1049 = pCellFormat->GetAttrSet().GetItemIfSet(RES_VERT_ORIENT);
1050 if (!pVertOrientItem)
1051 return;
1053 switch (pVertOrientItem->GetVertOrient())
1055 case text::VertOrientation::CENTER:
1056 m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLVERTALC);
1057 break;
1058 case text::VertOrientation::BOTTOM:
1059 m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLVERTALB);
1060 break;
1061 default:
1062 m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLVERTALT);
1063 break;
1067 void RtfAttributeOutput::TableNodeInfoInner(
1068 const ww8::WW8TableNodeInfoInner::Pointer_t& pNodeInfoInner)
1070 // This is called when the nested table ends in a cell, and there's no
1071 // paragraph behind that; so we must check for the ends of cell, rows,
1072 // and tables
1073 FinishTableRowCell(pNodeInfoInner);
1076 void RtfAttributeOutput::TableOrientation(
1077 const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
1079 const SwTable* pTable = pTableTextNodeInfoInner->getTable();
1080 SwFrameFormat* pFormat = pTable->GetFrameFormat();
1082 OStringBuffer aTableAdjust(OOO_STRING_SVTOOLS_RTF_TRQL);
1083 switch (pFormat->GetHoriOrient().GetHoriOrient())
1085 case text::HoriOrientation::CENTER:
1086 aTableAdjust.setLength(0);
1087 aTableAdjust.append(OOO_STRING_SVTOOLS_RTF_TRQC);
1088 break;
1089 case text::HoriOrientation::RIGHT:
1090 aTableAdjust.setLength(0);
1091 aTableAdjust.append(OOO_STRING_SVTOOLS_RTF_TRQR);
1092 break;
1093 case text::HoriOrientation::NONE:
1094 case text::HoriOrientation::LEFT_AND_WIDTH:
1095 aTableAdjust.append(OOO_STRING_SVTOOLS_RTF_TRLEFT);
1096 aTableAdjust.append(pFormat->GetLRSpace().ResolveLeft({}));
1097 break;
1098 default:
1099 break;
1102 m_aRowDefs.append(aTableAdjust);
1105 void RtfAttributeOutput::TableSpacing(
1106 const ww8::WW8TableNodeInfoInner::Pointer_t& /*pTableTextNodeInfoInner*/)
1108 SAL_INFO("sw.rtf", "TODO: " << __func__);
1111 void RtfAttributeOutput::TableRowEnd(sal_uInt32 /*nDepth*/) { /* noop, see EndTableRow() */}
1114 * Our private table methods.
1117 void RtfAttributeOutput::InitTableHelper(
1118 const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
1120 const SwTable* pTable = pTableTextNodeInfoInner->getTable();
1121 if (m_pTableWrt && pTable == m_pTableWrt->GetTable())
1122 return;
1124 tools::Long nPageSize = 0;
1125 bool bRelBoxSize = false;
1127 // Create the SwWriteTable instance to use col spans
1128 GetTablePageSize(pTableTextNodeInfoInner.get(), nPageSize, bRelBoxSize);
1130 const SwFrameFormat* pFormat = pTable->GetFrameFormat();
1131 const sal_uInt32 nTableSz = pFormat->GetFrameSize().GetWidth();
1133 const SwHTMLTableLayout* pLayout = pTable->GetHTMLTableLayout();
1134 if (pLayout && pLayout->IsExportable())
1135 m_pTableWrt = std::make_unique<SwWriteTable>(pTable, pLayout);
1136 else
1137 m_pTableWrt = std::make_unique<SwWriteTable>(pTable, pTable->GetTabLines(), nPageSize,
1138 nTableSz, false);
1141 void RtfAttributeOutput::StartTable()
1143 // To trigger calling InitTableHelper()
1144 m_pTableWrt.reset();
1147 void RtfAttributeOutput::StartTableRow(
1148 const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
1150 sal_uInt32 nCurrentDepth = pTableTextNodeInfoInner->getDepth();
1151 SAL_INFO("sw.rtf", __func__ << ", (depth is " << nCurrentDepth << ")");
1152 m_bTableRowEnded = false;
1154 TableDefinition(pTableTextNodeInfoInner);
1156 if (!m_bLastTable)
1157 m_aTables.push_back(m_aRowDefs.makeStringAndClear());
1159 // We'll write the table definition for nested tables later
1160 if (nCurrentDepth > 1)
1161 return;
1162 // Empty the previous row closing buffer before starting the new one,
1163 // necessary for subtables.
1164 m_rExport.Strm().WriteOString(m_aAfterRuns);
1165 m_aAfterRuns.setLength(0);
1166 m_rExport.Strm().WriteOString(m_aRowDefs);
1167 m_aRowDefs.setLength(0);
1170 void RtfAttributeOutput::StartTableCell() { m_bTableCellOpen = true; }
1172 void RtfAttributeOutput::TableCellProperties(
1173 const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
1175 TableDefaultBorders(pTableTextNodeInfoInner);
1176 TableBackgrounds(pTableTextNodeInfoInner);
1177 TableVerticalCell(pTableTextNodeInfoInner);
1180 void RtfAttributeOutput::EndTableCell()
1182 SAL_INFO("sw.rtf", __func__ << ", (depth is " << m_nTableDepth << ")");
1184 if (!m_bWroteCellInfo)
1186 m_aAfterRuns.append(OOO_STRING_SVTOOLS_RTF_INTBL);
1187 m_aAfterRuns.append(OOO_STRING_SVTOOLS_RTF_ITAP);
1188 m_aAfterRuns.append(static_cast<sal_Int32>(m_nTableDepth));
1190 if (m_nTableDepth > 1)
1191 m_aAfterRuns.append(OOO_STRING_SVTOOLS_RTF_NESTCELL);
1192 else
1193 m_aAfterRuns.append(OOO_STRING_SVTOOLS_RTF_CELL);
1195 m_bTableCellOpen = false;
1196 m_bTableAfterCell = true;
1197 m_bWroteCellInfo = false;
1198 if (m_aCells[m_nTableDepth] > 0)
1199 m_aCells[m_nTableDepth]--;
1202 void RtfAttributeOutput::EndTableRow()
1204 SAL_INFO("sw.rtf", __func__ << ", (depth is " << m_nTableDepth << ")");
1206 if (m_nTableDepth > 1)
1208 m_aAfterRuns.append(
1209 "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_NESTTABLEPROPRS);
1210 if (!m_aRowDefs.isEmpty())
1212 m_aAfterRuns.append(m_aRowDefs);
1213 m_aRowDefs.setLength(0);
1215 else if (!m_aTables.empty())
1217 m_aAfterRuns.append(m_aTables.back());
1218 m_aTables.pop_back();
1220 m_aAfterRuns.append(OOO_STRING_SVTOOLS_RTF_NESTROW
1222 "{" OOO_STRING_SVTOOLS_RTF_NONESTTABLES OOO_STRING_SVTOOLS_RTF_PAR "}");
1224 else
1226 if (!m_aTables.empty())
1228 m_aAfterRuns.append(m_aTables.back());
1229 m_aTables.pop_back();
1231 // Make sure that the first word of the next paragraph is not merged with the last control
1232 // word of this table row, happens with floating tables.
1233 m_aAfterRuns.append(OOO_STRING_SVTOOLS_RTF_ROW OOO_STRING_SVTOOLS_RTF_PARD " ");
1235 m_bTableRowEnded = true;
1238 void RtfAttributeOutput::EndTable()
1240 if (m_nTableDepth > 0)
1242 m_nTableDepth--;
1243 m_pTableWrt.reset();
1246 // We closed the table; if it is a nested table, the cell that contains it
1247 // still continues
1248 m_bTableCellOpen = true;
1250 // Cleans the table helper
1251 m_pTableWrt.reset();
1254 void RtfAttributeOutput::FinishTableRowCell(const ww8::WW8TableNodeInfoInner::Pointer_t& pInner)
1256 if (!pInner)
1257 return;
1259 // Where are we in the table
1260 sal_uInt32 nRow = pInner->getRow();
1262 const SwTable* pTable = pInner->getTable();
1263 const SwTableLines& rLines = pTable->GetTabLines();
1264 sal_uInt16 nLinesCount = rLines.size();
1266 if (pInner->isEndOfCell())
1267 EndTableCell();
1269 // This is a line end
1270 if (pInner->isEndOfLine())
1271 EndTableRow();
1273 // This is the end of the table
1274 if (pInner->isEndOfLine() && (nRow + 1) == nLinesCount)
1275 EndTable();
1278 void RtfAttributeOutput::StartStyles()
1280 m_rExport.Strm()
1281 .WriteOString(SAL_NEWLINE_STRING)
1282 .WriteChar('{')
1283 .WriteOString(OOO_STRING_SVTOOLS_RTF_COLORTBL);
1284 m_rExport.OutColorTable();
1285 OSL_ENSURE(m_aStylesheet.getLength() == 0, "m_aStylesheet is not empty");
1286 m_aStylesheet.append(SAL_NEWLINE_STRING);
1287 m_aStylesheet.append('{');
1288 m_aStylesheet.append(OOO_STRING_SVTOOLS_RTF_STYLESHEET);
1291 void RtfAttributeOutput::EndStyles(sal_uInt16 /*nNumberOfStyles*/)
1293 m_rExport.Strm().WriteChar('}');
1294 m_rExport.Strm().WriteOString(m_aStylesheet);
1295 m_aStylesheet.setLength(0);
1296 m_rExport.Strm().WriteChar('}');
1299 void RtfAttributeOutput::DefaultStyle() { /* noop, the default style is always 0 in RTF */}
1301 void RtfAttributeOutput::StartStyle(const OUString& rName, StyleType eType, sal_uInt16 nBase,
1302 sal_uInt16 nNext, sal_uInt16 /*nLink*/, sal_uInt16 /*nWwId*/,
1303 sal_uInt16 nSlot, bool bAutoUpdate)
1305 SAL_INFO("sw.rtf", __func__ << ", rName = '" << rName << "'");
1307 m_aStylesheet.append('{');
1308 if (eType == STYLE_TYPE_PARA)
1309 m_aStylesheet.append(OOO_STRING_SVTOOLS_RTF_S);
1310 else
1311 m_aStylesheet.append(OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_CS);
1312 m_aStylesheet.append(static_cast<sal_Int32>(nSlot));
1314 if (nBase != 0x0FFF)
1316 m_aStylesheet.append(OOO_STRING_SVTOOLS_RTF_SBASEDON);
1317 m_aStylesheet.append(static_cast<sal_Int32>(nBase));
1320 m_aStylesheet.append(OOO_STRING_SVTOOLS_RTF_SNEXT);
1321 m_aStylesheet.append(static_cast<sal_Int32>(nNext));
1323 if (bAutoUpdate)
1324 m_aStylesheet.append(OOO_STRING_SVTOOLS_RTF_SAUTOUPD);
1326 m_rStyleName = rName;
1327 m_nStyleId = nSlot;
1330 void RtfAttributeOutput::EndStyle()
1332 OString aStyles = MoveCharacterProperties();
1333 m_rExport.InsStyle(m_nStyleId, aStyles);
1334 m_aStylesheet.append(aStyles);
1335 m_aStylesheet.append(' ');
1336 m_aStylesheet.append(
1337 msfilter::rtfutil::OutString(m_rStyleName, m_rExport.GetCurrentEncoding()));
1338 m_aStylesheet.append(";}");
1339 m_aStylesheet.append(SAL_NEWLINE_STRING);
1342 void RtfAttributeOutput::StartStyleProperties(bool /*bParProp*/, sal_uInt16 /*nStyle*/)
1344 /* noop */
1347 void RtfAttributeOutput::EndStyleProperties(bool /*bParProp*/) { /* noop */}
1349 void RtfAttributeOutput::OutlineNumbering(sal_uInt8 nLvl)
1351 if (nLvl >= WW8ListManager::nMaxLevel)
1352 nLvl = WW8ListManager::nMaxLevel - 1;
1354 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ILVL);
1355 m_aStyles.append(static_cast<sal_Int32>(nLvl));
1356 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_OUTLINELEVEL);
1357 m_aStyles.append(static_cast<sal_Int32>(nLvl));
1360 void RtfAttributeOutput::PageBreakBefore(bool bBreak)
1362 if (bBreak)
1364 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_PAGEBB);
1368 void RtfAttributeOutput::SectionBreak(sal_uInt8 nC, bool /*bBreakAfter*/,
1369 const WW8_SepInfo* pSectionInfo, bool /*bExtraPageBreak*/)
1371 switch (nC)
1373 case msword::ColumnBreak:
1374 m_nColBreakNeeded = true;
1375 break;
1376 case msword::PageBreak:
1377 if (pSectionInfo)
1378 m_rExport.SectionProperties(*pSectionInfo);
1379 break;
1382 // Endnotes included in the section:
1383 if (!pSectionInfo)
1385 return;
1387 const SwSectionFormat* pSectionFormat = pSectionInfo->pSectionFormat;
1388 if (!pSectionFormat || pSectionFormat == reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1)))
1390 // MSWordExportBase::WriteText() can set the section format to -1, ignore.
1391 return;
1393 if (!pSectionFormat->GetEndAtTextEnd().IsAtEnd())
1395 return;
1397 m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_ENDNHERE);
1400 void RtfAttributeOutput::StartSection()
1402 if (m_bIsBeforeFirstParagraph)
1403 return;
1405 m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_SECT OOO_STRING_SVTOOLS_RTF_SECTD);
1406 if (!m_bBufferSectionBreaks)
1408 m_rExport.Strm().WriteOString(m_aSectionBreaks);
1409 m_aSectionBreaks.setLength(0);
1413 void RtfAttributeOutput::EndSection()
1416 * noop, \sect must go to StartSection or Word won't notice multiple
1417 * columns...
1421 void RtfAttributeOutput::SectionFormProtection(bool bProtected)
1423 m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_SECTUNLOCKED);
1424 m_aSectionBreaks.append(static_cast<sal_Int32>(!bProtected));
1427 void RtfAttributeOutput::SectionLineNumbering(sal_uLong nRestartNo,
1428 const SwLineNumberInfo& rLnNumInfo)
1430 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LINEMOD);
1431 m_rExport.Strm().WriteNumberAsString(rLnNumInfo.GetCountBy());
1432 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LINEX);
1433 m_rExport.Strm().WriteNumberAsString(rLnNumInfo.GetPosFromLeft());
1434 if (!rLnNumInfo.IsRestartEachPage())
1435 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LINECONT);
1437 if (nRestartNo > 0)
1439 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LINESTARTS);
1440 m_rExport.Strm().WriteNumberAsString(nRestartNo);
1444 void RtfAttributeOutput::SectionTitlePage()
1447 * noop, handled in RtfExport::WriteHeaderFooter()
1451 void RtfAttributeOutput::SectionPageBorders(const SwFrameFormat* pFormat,
1452 const SwFrameFormat* /*pFirstPageFormat*/)
1454 const SvxBoxItem& rBox = pFormat->GetBox();
1455 editeng::WordBorderDistances aDistances;
1456 editeng::BorderDistancesToWord(rBox, m_aPageMargins, aDistances);
1458 if (aDistances.bFromEdge)
1460 sal_uInt16 nOpt = (1 << 5);
1461 m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_PGBRDROPT);
1462 m_aSectionBreaks.append(static_cast<sal_Int32>(nOpt));
1465 const editeng::SvxBorderLine* pLine = rBox.GetTop();
1466 if (pLine)
1467 m_aSectionBreaks.append(
1468 OutBorderLine(m_rExport, pLine, OOO_STRING_SVTOOLS_RTF_PGBRDRT, aDistances.nTop));
1469 pLine = rBox.GetBottom();
1470 if (pLine)
1471 m_aSectionBreaks.append(
1472 OutBorderLine(m_rExport, pLine, OOO_STRING_SVTOOLS_RTF_PGBRDRB, aDistances.nBottom));
1473 pLine = rBox.GetLeft();
1474 if (pLine)
1475 m_aSectionBreaks.append(
1476 OutBorderLine(m_rExport, pLine, OOO_STRING_SVTOOLS_RTF_PGBRDRL, aDistances.nLeft));
1477 pLine = rBox.GetRight();
1478 if (pLine)
1479 m_aSectionBreaks.append(
1480 OutBorderLine(m_rExport, pLine, OOO_STRING_SVTOOLS_RTF_PGBRDRR, aDistances.nRight));
1483 void RtfAttributeOutput::SectionBiDi(bool bBiDi)
1485 m_rExport.Strm().WriteOString(bBiDi ? OOO_STRING_SVTOOLS_RTF_RTLSECT
1486 : OOO_STRING_SVTOOLS_RTF_LTRSECT);
1489 void RtfAttributeOutput::SectionPageNumbering(sal_uInt16 nNumType,
1490 const ::std::optional<sal_uInt16>& oPageRestartNumber)
1492 if (oPageRestartNumber)
1494 m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_PGNSTARTS);
1495 m_aSectionBreaks.append(static_cast<sal_Int32>(*oPageRestartNumber));
1496 m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_PGNRESTART);
1499 const char* pStr = nullptr;
1500 switch (nNumType)
1502 case SVX_NUM_CHARS_UPPER_LETTER:
1503 case SVX_NUM_CHARS_UPPER_LETTER_N:
1504 pStr = OOO_STRING_SVTOOLS_RTF_PGNUCLTR;
1505 break;
1506 case SVX_NUM_CHARS_LOWER_LETTER:
1507 case SVX_NUM_CHARS_LOWER_LETTER_N:
1508 pStr = OOO_STRING_SVTOOLS_RTF_PGNLCLTR;
1509 break;
1510 case SVX_NUM_ROMAN_UPPER:
1511 pStr = OOO_STRING_SVTOOLS_RTF_PGNUCRM;
1512 break;
1513 case SVX_NUM_ROMAN_LOWER:
1514 pStr = OOO_STRING_SVTOOLS_RTF_PGNLCRM;
1515 break;
1517 case SVX_NUM_ARABIC:
1518 pStr = OOO_STRING_SVTOOLS_RTF_PGNDEC;
1519 break;
1521 if (pStr)
1522 m_aSectionBreaks.append(pStr);
1525 void RtfAttributeOutput::SectionType(sal_uInt8 nBreakCode)
1527 SAL_INFO("sw.rtf", __func__ << ", nBreakCode = " << int(nBreakCode));
1530 * break code: 0 No break, 1 New column
1531 * 2 New page, 3 Even page, 4 Odd page
1533 const char* sType = nullptr;
1534 switch (nBreakCode)
1536 case 1:
1537 sType = OOO_STRING_SVTOOLS_RTF_SBKCOL;
1538 break;
1539 case 2:
1540 sType = OOO_STRING_SVTOOLS_RTF_SBKPAGE;
1541 break;
1542 case 3:
1543 sType = OOO_STRING_SVTOOLS_RTF_SBKEVEN;
1544 break;
1545 case 4:
1546 sType = OOO_STRING_SVTOOLS_RTF_SBKODD;
1547 break;
1548 default:
1549 sType = OOO_STRING_SVTOOLS_RTF_SBKNONE;
1550 break;
1552 m_aSectionBreaks.append(sType);
1553 if (!m_bBufferSectionBreaks)
1555 m_rExport.Strm().WriteOString(m_aSectionBreaks);
1556 m_aSectionBreaks.setLength(0);
1560 void RtfAttributeOutput::SectFootnoteEndnotePr()
1562 WriteFootnoteEndnotePr(true, m_rExport.m_rDoc.GetFootnoteInfo());
1563 WriteFootnoteEndnotePr(false, m_rExport.m_rDoc.GetEndNoteInfo());
1566 void RtfAttributeOutput::WriteFootnoteEndnotePr(bool bFootnote, const SwEndNoteInfo& rInfo)
1568 const char* pOut = nullptr;
1570 if (bFootnote)
1572 switch (rInfo.m_aFormat.GetNumberingType())
1574 default:
1575 pOut = OOO_STRING_SVTOOLS_RTF_SFTNNAR;
1576 break;
1577 case SVX_NUM_CHARS_LOWER_LETTER:
1578 case SVX_NUM_CHARS_LOWER_LETTER_N:
1579 pOut = OOO_STRING_SVTOOLS_RTF_SFTNNALC;
1580 break;
1581 case SVX_NUM_CHARS_UPPER_LETTER:
1582 case SVX_NUM_CHARS_UPPER_LETTER_N:
1583 pOut = OOO_STRING_SVTOOLS_RTF_SFTNNAUC;
1584 break;
1585 case SVX_NUM_ROMAN_LOWER:
1586 pOut = OOO_STRING_SVTOOLS_RTF_SFTNNRLC;
1587 break;
1588 case SVX_NUM_ROMAN_UPPER:
1589 pOut = OOO_STRING_SVTOOLS_RTF_SFTNNRUC;
1590 break;
1591 case SVX_NUM_SYMBOL_CHICAGO:
1592 pOut = OOO_STRING_SVTOOLS_RTF_SFTNNCHI;
1593 break;
1596 else
1598 switch (rInfo.m_aFormat.GetNumberingType())
1600 default:
1601 pOut = OOO_STRING_SVTOOLS_RTF_SAFTNNAR;
1602 break;
1603 case SVX_NUM_CHARS_LOWER_LETTER:
1604 case SVX_NUM_CHARS_LOWER_LETTER_N:
1605 pOut = OOO_STRING_SVTOOLS_RTF_SAFTNNALC;
1606 break;
1607 case SVX_NUM_CHARS_UPPER_LETTER:
1608 case SVX_NUM_CHARS_UPPER_LETTER_N:
1609 pOut = OOO_STRING_SVTOOLS_RTF_SAFTNNAUC;
1610 break;
1611 case SVX_NUM_ROMAN_LOWER:
1612 pOut = OOO_STRING_SVTOOLS_RTF_SAFTNNRLC;
1613 break;
1614 case SVX_NUM_ROMAN_UPPER:
1615 pOut = OOO_STRING_SVTOOLS_RTF_SAFTNNRUC;
1616 break;
1617 case SVX_NUM_SYMBOL_CHICAGO:
1618 pOut = OOO_STRING_SVTOOLS_RTF_SAFTNNCHI;
1619 break;
1623 m_aSectionBreaks.append(pOut);
1625 if (!m_bBufferSectionBreaks)
1627 m_rExport.Strm().WriteOString(m_aSectionBreaks);
1628 m_aSectionBreaks.setLength(0);
1632 void RtfAttributeOutput::NumberingDefinition(sal_uInt16 nId, const SwNumRule& /*rRule*/)
1634 m_rExport.Strm().WriteChar('{').WriteOString(OOO_STRING_SVTOOLS_RTF_LISTOVERRIDE);
1635 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LISTID);
1636 m_rExport.Strm().WriteNumberAsString(nId);
1637 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LISTOVERRIDECOUNT).WriteChar('0');
1638 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LS);
1639 m_rExport.Strm().WriteNumberAsString(nId).WriteChar('}');
1642 void RtfAttributeOutput::StartAbstractNumbering(sal_uInt16 nId)
1644 m_rExport.Strm()
1645 .WriteChar('{')
1646 .WriteOString(OOO_STRING_SVTOOLS_RTF_LIST)
1647 .WriteOString(OOO_STRING_SVTOOLS_RTF_LISTTEMPLATEID);
1648 m_rExport.Strm().WriteNumberAsString(nId);
1649 m_nListId = nId;
1652 void RtfAttributeOutput::EndAbstractNumbering()
1654 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LISTID);
1655 m_rExport.Strm().WriteNumberAsString(m_nListId).WriteChar('}').WriteOString(SAL_NEWLINE_STRING);
1658 void RtfAttributeOutput::NumberingLevel(sal_uInt8 nLevel, sal_uInt16 nStart,
1659 sal_uInt16 nNumberingType, SvxAdjust eAdjust,
1660 const sal_uInt8* pNumLvlPos, sal_uInt8 nFollow,
1661 const wwFont* pFont, const SfxItemSet* pOutSet,
1662 sal_Int16 nIndentAt, sal_Int16 nFirstLineIndex,
1663 sal_Int16 /*nListTabPos*/, const OUString& rNumberingString,
1664 const SvxBrushItem* pBrush, bool isLegal)
1666 m_rExport.Strm().WriteOString(SAL_NEWLINE_STRING);
1667 if (nLevel > 8) // RTF knows only 9 levels
1668 m_rExport.Strm()
1669 .WriteOString(OOO_STRING_SVTOOLS_RTF_IGNORE)
1670 .WriteOString(OOO_STRING_SVTOOLS_RTF_SOUTLVL);
1672 m_rExport.Strm().WriteChar('{').WriteOString(OOO_STRING_SVTOOLS_RTF_LISTLEVEL);
1674 sal_uInt16 nVal = 0;
1675 switch (nNumberingType)
1677 case SVX_NUM_ROMAN_UPPER:
1678 nVal = 1;
1679 break;
1680 case SVX_NUM_ROMAN_LOWER:
1681 nVal = 2;
1682 break;
1683 case SVX_NUM_CHARS_UPPER_LETTER:
1684 case SVX_NUM_CHARS_UPPER_LETTER_N:
1685 nVal = 3;
1686 break;
1687 case SVX_NUM_CHARS_LOWER_LETTER:
1688 case SVX_NUM_CHARS_LOWER_LETTER_N:
1689 nVal = 4;
1690 break;
1691 case SVX_NUM_FULL_WIDTH_ARABIC:
1692 nVal = 14;
1693 break;
1694 case SVX_NUM_CIRCLE_NUMBER:
1695 nVal = 18;
1696 break;
1697 case SVX_NUM_NUMBER_LOWER_ZH:
1698 nVal = 35;
1699 if (pOutSet)
1701 const SvxLanguageItem& rLang = pOutSet->Get(RES_CHRATR_CJK_LANGUAGE);
1702 if (rLang.GetLanguage() == LANGUAGE_CHINESE_SIMPLIFIED)
1704 nVal = 39;
1707 break;
1708 case SVX_NUM_NUMBER_UPPER_ZH:
1709 nVal = 38;
1710 break;
1711 case SVX_NUM_NUMBER_UPPER_ZH_TW:
1712 nVal = 34;
1713 break;
1714 case SVX_NUM_TIAN_GAN_ZH:
1715 nVal = 30;
1716 break;
1717 case SVX_NUM_DI_ZI_ZH:
1718 nVal = 31;
1719 break;
1720 case SVX_NUM_NUMBER_TRADITIONAL_JA:
1721 nVal = 16;
1722 break;
1723 case SVX_NUM_AIU_FULLWIDTH_JA:
1724 nVal = 20;
1725 break;
1726 case SVX_NUM_AIU_HALFWIDTH_JA:
1727 nVal = 12;
1728 break;
1729 case SVX_NUM_IROHA_FULLWIDTH_JA:
1730 nVal = 21;
1731 break;
1732 case SVX_NUM_IROHA_HALFWIDTH_JA:
1733 nVal = 13;
1734 break;
1735 case style::NumberingType::HANGUL_SYLLABLE_KO:
1736 nVal = 24;
1737 break; // ganada
1738 case style::NumberingType::HANGUL_JAMO_KO:
1739 nVal = 25;
1740 break; // chosung
1741 case style::NumberingType::HANGUL_CIRCLED_SYLLABLE_KO:
1742 nVal = 24;
1743 break;
1744 case style::NumberingType::HANGUL_CIRCLED_JAMO_KO:
1745 nVal = 25;
1746 break;
1747 case style::NumberingType::NUMBER_HANGUL_KO:
1748 nVal = 42;
1749 break; // koreanCounting
1750 case style::NumberingType::NUMBER_DIGITAL_KO:
1751 nVal = 41; // koreanDigital
1752 break;
1753 case style::NumberingType::NUMBER_DIGITAL2_KO:
1754 nVal = 44; // koreanDigital2
1755 break;
1756 case style::NumberingType::NUMBER_LEGAL_KO:
1757 nVal = 43; // koreanLegal
1758 break;
1760 case SVX_NUM_BITMAP:
1761 case SVX_NUM_CHAR_SPECIAL:
1762 nVal = 23;
1763 break;
1764 case SVX_NUM_NUMBER_NONE:
1765 nVal = 255;
1766 break;
1767 case SVX_NUM_ARABIC_ZERO:
1768 nVal = 22;
1769 break;
1771 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LEVELNFC);
1772 m_rExport.Strm().WriteNumberAsString(nVal);
1774 switch (eAdjust)
1776 case SvxAdjust::Center:
1777 nVal = 1;
1778 break;
1779 case SvxAdjust::Right:
1780 nVal = 2;
1781 break;
1782 default:
1783 nVal = 0;
1784 break;
1786 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LEVELJC);
1787 m_rExport.Strm().WriteNumberAsString(nVal);
1789 // bullet
1790 if (nNumberingType == SVX_NUM_BITMAP && pBrush)
1792 int nIndex = m_rExport.GetGrfIndex(*pBrush);
1793 if (nIndex != -1)
1795 m_rExport.Strm().WriteOString(LO_STRING_SVTOOLS_RTF_LEVELPICTURE);
1796 m_rExport.Strm().WriteNumberAsString(nIndex);
1800 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LEVELSTARTAT);
1801 m_rExport.Strm().WriteNumberAsString(nStart);
1803 if (isLegal)
1805 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LEVELLEGAL);
1808 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LEVELFOLLOW);
1809 m_rExport.Strm().WriteNumberAsString(nFollow);
1811 // leveltext group
1812 m_rExport.Strm().WriteChar('{').WriteOString(OOO_STRING_SVTOOLS_RTF_LEVELTEXT).WriteChar(' ');
1814 if (SVX_NUM_CHAR_SPECIAL == nNumberingType || SVX_NUM_BITMAP == nNumberingType)
1816 m_rExport.Strm().WriteOString("\\'01");
1817 sal_Unicode cChar = rNumberingString[0];
1818 m_rExport.Strm().WriteOString("\\u");
1819 m_rExport.Strm().WriteNumberAsString(cChar);
1820 m_rExport.Strm().WriteOString(" ?");
1822 else
1824 m_rExport.Strm().WriteOString("\\'").WriteOString(
1825 msfilter::rtfutil::OutHex(rNumberingString.getLength(), 2));
1826 m_rExport.Strm().WriteOString(msfilter::rtfutil::OutString(rNumberingString,
1827 m_rExport.GetDefaultEncoding(),
1828 /*bUnicode =*/false));
1831 m_rExport.Strm().WriteOString(";}");
1833 // write the levelnumbers
1834 m_rExport.Strm().WriteOString("{").WriteOString(OOO_STRING_SVTOOLS_RTF_LEVELNUMBERS);
1835 for (sal_uInt8 i = 0; i <= nLevel && pNumLvlPos[i]; ++i)
1837 m_rExport.Strm().WriteOString("\\'").WriteOString(
1838 msfilter::rtfutil::OutHex(pNumLvlPos[i], 2));
1840 m_rExport.Strm().WriteOString(";}");
1842 if (pOutSet)
1844 if (pFont)
1846 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_F);
1847 m_rExport.Strm().WriteNumberAsString(m_rExport.m_aFontHelper.GetId(*pFont));
1849 m_rExport.OutputItemSet(*pOutSet, false, true, i18n::ScriptType::LATIN,
1850 m_rExport.m_bExportModeRTF);
1851 const OString aProperties = MoveCharacterProperties(true);
1852 m_rExport.Strm().WriteOString(aProperties);
1855 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_FI);
1856 m_rExport.Strm().WriteNumberAsString(nFirstLineIndex).WriteOString(OOO_STRING_SVTOOLS_RTF_LI);
1857 m_rExport.Strm().WriteNumberAsString(nIndentAt);
1859 m_rExport.Strm().WriteChar('}');
1860 if (nLevel > 8)
1861 m_rExport.Strm().WriteChar('}');
1864 void RtfAttributeOutput::WriteField_Impl(const SwField* const pField, ww::eField /*eType*/,
1865 std::u16string_view rFieldCmd, FieldFlags nMode)
1867 // If there are no field instructions, don't export it as a field.
1868 bool bHasInstructions = !rFieldCmd.empty();
1869 if (FieldFlags::All == nMode)
1871 if (bHasInstructions)
1873 m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_FIELD);
1874 if (pField && (pField->GetSubType() & FIXEDFLD))
1875 m_aRunText->append(OOO_STRING_SVTOOLS_RTF_FLDLOCK);
1876 m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FLDINST
1877 " ");
1878 m_aRunText->append(
1879 msfilter::rtfutil::OutString(rFieldCmd, m_rExport.GetCurrentEncoding()));
1880 m_aRunText->append("}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " ");
1882 if (pField)
1883 m_aRunText->append(msfilter::rtfutil::OutString(pField->ExpandField(true, nullptr),
1884 m_rExport.GetDefaultEncoding()));
1885 if (bHasInstructions)
1886 m_aRunText->append("}}");
1888 else
1890 if (nMode & FieldFlags::CmdStart)
1892 m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_FIELD);
1893 m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FLDINST
1894 // paragraph break closes group so open another one "inside" to
1895 " {"); // prevent leaving the field instruction
1897 if (bHasInstructions)
1898 m_aRunText->append(
1899 msfilter::rtfutil::OutString(rFieldCmd, m_rExport.GetCurrentEncoding()));
1900 if (nMode & FieldFlags::CmdEnd)
1902 m_aRunText->append("}}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT);
1903 // The fldrslt contains its own full copy of character formatting,
1904 // but if the result is empty (nMode & FieldFlags::End) or field export is condensed
1905 // in any way (multiple flags) then avoid spamming an unnecessary plain character reset.
1906 if (nMode == FieldFlags::CmdEnd)
1907 m_aRunText->append(OOO_STRING_SVTOOLS_RTF_PLAIN);
1908 m_aRunText->append(" {");
1910 if (nMode & FieldFlags::Close)
1912 m_aRunText->append("}}}");
1917 void RtfAttributeOutput::WriteBookmarks_Impl(std::vector<OUString>& rStarts,
1918 std::vector<OUString>& rEnds)
1920 for (const auto& rStart : rStarts)
1922 m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_BKMKSTART " ");
1923 m_aRunText->append(msfilter::rtfutil::OutString(rStart, m_rExport.GetCurrentEncoding()));
1924 m_aRunText->append('}');
1926 rStarts.clear();
1928 for (const auto& rEnd : rEnds)
1930 m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_BKMKEND " ");
1931 m_aRunText->append(msfilter::rtfutil::OutString(rEnd, m_rExport.GetCurrentEncoding()));
1932 m_aRunText->append('}');
1934 rEnds.clear();
1937 void RtfAttributeOutput::WriteAnnotationMarks_Impl(std::vector<OUString>& rStarts,
1938 std::vector<OUString>& rEnds)
1940 for (const auto& rStart : rStarts)
1942 OString rName = OUStringToOString(rStart, RTL_TEXTENCODING_UTF8);
1944 // Output the annotation mark
1945 const sal_Int32 nId = m_nNextAnnotationMarkId++;
1946 m_rOpenedAnnotationMarksIds[rName] = nId;
1947 m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ATRFSTART " ");
1948 m_aRun->append(nId);
1949 m_aRun->append('}');
1951 rStarts.clear();
1953 for (const auto& rEnd : rEnds)
1955 OString rName = OUStringToOString(rEnd, RTL_TEXTENCODING_UTF8);
1957 // Get the id of the annotation mark
1958 auto it = m_rOpenedAnnotationMarksIds.find(rName);
1959 if (it != m_rOpenedAnnotationMarksIds.end())
1961 const sal_Int32 nId = it->second;
1962 m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ATRFEND " ");
1963 m_aRun->append(nId);
1964 m_aRun->append('}');
1965 m_rOpenedAnnotationMarksIds.erase(rName);
1967 if (m_aPostitFields.find(nId) != m_aPostitFields.end())
1969 m_aRunText->append("{");
1970 m_nCurrentAnnotationMarkId = nId;
1971 PostitField(m_aPostitFields[nId]);
1972 m_nCurrentAnnotationMarkId = -1;
1973 m_aRunText->append("}");
1977 rEnds.clear();
1980 void RtfAttributeOutput::WriteHeaderFooter_Impl(const SwFrameFormat& rFormat, bool bHeader,
1981 const char* pStr, bool bTitlepg)
1983 OStringBuffer aSectionBreaks = m_aSectionBreaks;
1984 m_aSectionBreaks.setLength(0);
1985 RtfStringBuffer aRun = m_aRun;
1986 m_aRun.clear();
1988 m_aSectionHeaders.append(bHeader ? OOO_STRING_SVTOOLS_RTF_HEADERY
1989 : OOO_STRING_SVTOOLS_RTF_FOOTERY);
1990 m_aSectionHeaders.append(
1991 static_cast<sal_Int32>(m_rExport.m_pCurrentPageDesc->GetMaster().GetULSpace().GetUpper()));
1992 if (bTitlepg)
1993 m_aSectionHeaders.append(OOO_STRING_SVTOOLS_RTF_TITLEPG);
1994 m_aSectionHeaders.append('{');
1995 m_aSectionHeaders.append(pStr);
1996 m_bBufferSectionHeaders = true;
1997 m_rExport.WriteHeaderFooterText(rFormat, bHeader);
1998 m_bBufferSectionHeaders = false;
1999 m_aSectionHeaders.append('}');
2001 m_aSectionBreaks = aSectionBreaks;
2002 m_aRun = std::move(aRun);
2005 namespace
2007 void lcl_TextFrameShadow(std::vector<std::pair<OString, OString>>& rFlyProperties,
2008 const SwFrameFormat& rFrameFormat)
2010 const SvxShadowItem& aShadowItem = rFrameFormat.GetShadow();
2011 if (aShadowItem.GetLocation() == SvxShadowLocation::NONE)
2012 return;
2014 rFlyProperties.push_back(std::make_pair<OString, OString>("fShadow"_ostr, OString::number(1)));
2016 const Color& rColor = aShadowItem.GetColor();
2017 // We in fact need RGB to BGR, but the transformation is symmetric.
2018 rFlyProperties.push_back(std::make_pair<OString, OString>(
2019 "shadowColor"_ostr, OString::number(wwUtility::RGBToBGR(rColor))));
2021 // Twips -> points -> EMUs -- hacky, the intermediate step hides rounding errors on roundtrip.
2022 OString aShadowWidth = OString::number(sal_Int32(aShadowItem.GetWidth() / 20) * 12700);
2023 OString aOffsetX;
2024 OString aOffsetY;
2025 switch (aShadowItem.GetLocation())
2027 case SvxShadowLocation::TopLeft:
2028 aOffsetX = "-" + aShadowWidth;
2029 aOffsetY = "-" + aShadowWidth;
2030 break;
2031 case SvxShadowLocation::TopRight:
2032 aOffsetX = aShadowWidth;
2033 aOffsetY = "-" + aShadowWidth;
2034 break;
2035 case SvxShadowLocation::BottomLeft:
2036 aOffsetX = "-" + aShadowWidth;
2037 aOffsetY = aShadowWidth;
2038 break;
2039 case SvxShadowLocation::BottomRight:
2040 aOffsetX = aShadowWidth;
2041 aOffsetY = aShadowWidth;
2042 break;
2043 case SvxShadowLocation::NONE:
2044 case SvxShadowLocation::End:
2045 break;
2047 if (!aOffsetX.isEmpty())
2048 rFlyProperties.emplace_back("shadowOffsetX", aOffsetX);
2049 if (!aOffsetY.isEmpty())
2050 rFlyProperties.emplace_back("shadowOffsetY", aOffsetY);
2053 void lcl_TextFrameRelativeSize(std::vector<std::pair<OString, OString>>& rFlyProperties,
2054 const SwFrameFormat& rFrameFormat)
2056 const SwFormatFrameSize& rSize = rFrameFormat.GetFrameSize();
2058 // Relative size of the Text Frame.
2059 const sal_uInt8 nWidthPercent = rSize.GetWidthPercent();
2060 if (nWidthPercent && nWidthPercent != SwFormatFrameSize::SYNCED)
2062 rFlyProperties.push_back(
2063 std::make_pair<OString, OString>("pctHoriz"_ostr, OString::number(nWidthPercent * 10)));
2065 OString aRelation;
2066 switch (rSize.GetWidthPercentRelation())
2068 case text::RelOrientation::PAGE_FRAME:
2069 aRelation = "1"_ostr; // page
2070 break;
2071 default:
2072 aRelation = "0"_ostr; // margin
2073 break;
2075 rFlyProperties.emplace_back(std::make_pair("sizerelh", aRelation));
2077 const sal_uInt8 nHeightPercent = rSize.GetHeightPercent();
2078 if (!(nHeightPercent && nHeightPercent != SwFormatFrameSize::SYNCED))
2079 return;
2081 rFlyProperties.push_back(
2082 std::make_pair<OString, OString>("pctVert"_ostr, OString::number(nHeightPercent * 10)));
2084 OString aRelation;
2085 switch (rSize.GetHeightPercentRelation())
2087 case text::RelOrientation::PAGE_FRAME:
2088 aRelation = "1"_ostr; // page
2089 break;
2090 default:
2091 aRelation = "0"_ostr; // margin
2092 break;
2094 rFlyProperties.emplace_back(std::make_pair("sizerelv", aRelation));
2098 void RtfAttributeOutput::writeTextFrame(const ww8::Frame& rFrame, bool bTextBox)
2100 RtfStringBuffer aRunText;
2101 if (bTextBox)
2103 m_rExport.setStream();
2104 aRunText = m_aRunText;
2105 m_aRunText.clear();
2108 m_rExport.Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_SHPTXT);
2111 // Save table state, in case the inner text also contains a table.
2112 ww8::WW8TableInfo::Pointer_t pTableInfoOrig = m_rExport.m_pTableInfo;
2113 m_rExport.m_pTableInfo = std::make_shared<ww8::WW8TableInfo>();
2114 std::unique_ptr<SwWriteTable> pTableWrt(std::move(m_pTableWrt));
2115 sal_uInt32 nTableDepth = m_nTableDepth;
2117 m_nTableDepth = 0;
2119 * Save m_aRun as we should not lose the opening brace.
2120 * OTOH, just drop the contents of m_aRunText in case something
2121 * would be there, causing a problem later.
2123 OString aSave = m_aRun.makeStringAndClear();
2124 // Also back m_bInRun and m_bSingleEmptyRun up.
2125 bool bInRunOrig = m_bInRun;
2126 m_bInRun = false;
2127 bool bSingleEmptyRunOrig = m_bSingleEmptyRun;
2128 m_bSingleEmptyRun = false;
2129 m_rExport.SetRTFFlySyntax(true);
2131 const SwFrameFormat& rFrameFormat = rFrame.GetFrameFormat();
2132 const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
2133 SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
2134 SwNodeOffset nEnd
2135 = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
2136 m_rExport.SaveData(nStt, nEnd);
2137 m_rExport.m_pParentFrame = &rFrame;
2138 m_rExport.WriteText();
2139 m_rExport.RestoreData();
2141 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_PARD);
2142 m_rExport.SetRTFFlySyntax(false);
2143 m_aRun->append(aSave);
2144 m_aRunText.clear();
2145 m_bInRun = bInRunOrig;
2146 m_bSingleEmptyRun = bSingleEmptyRunOrig;
2148 // Restore table state.
2149 m_rExport.m_pTableInfo = std::move(pTableInfoOrig);
2150 m_pTableWrt = std::move(pTableWrt);
2151 m_nTableDepth = nTableDepth;
2154 m_rExport.m_pParentFrame = nullptr;
2156 m_rExport.Strm().WriteChar('}'); // shptxt
2158 if (bTextBox)
2160 m_aRunText = std::move(aRunText);
2161 m_aRunText->append(m_rExport.getStream());
2162 m_rExport.resetStream();
2166 /** save the current run state around exporting things that contain paragraphs
2167 themselves like text frames.
2168 TODO: probably more things need to be saved?
2170 class SaveRunState
2172 private:
2173 RtfAttributeOutput& m_rRtf;
2174 RtfStringBuffer m_Run;
2175 RtfStringBuffer m_RunText;
2176 bool const m_bSingleEmptyRun;
2177 bool const m_bInRun;
2179 public:
2180 explicit SaveRunState(RtfAttributeOutput& rRtf)
2181 : m_rRtf(rRtf)
2182 , m_Run(std::move(rRtf.m_aRun))
2183 , m_RunText(std::move(rRtf.m_aRunText))
2184 , m_bSingleEmptyRun(rRtf.m_bSingleEmptyRun)
2185 , m_bInRun(rRtf.m_bInRun)
2187 m_rRtf.m_rExport.setStream();
2189 ~SaveRunState()
2191 m_rRtf.m_aRun = std::move(m_Run);
2192 m_rRtf.m_aRunText = std::move(m_RunText);
2193 m_rRtf.m_bSingleEmptyRun = m_bSingleEmptyRun;
2194 m_rRtf.m_bInRun = m_bInRun;
2196 m_rRtf.m_aRunText->append(m_rRtf.m_rExport.getStream());
2197 m_rRtf.m_rExport.resetStream();
2201 void RtfAttributeOutput::OutputFlyFrame_Impl(const ww8::Frame& rFrame, const Point& /*rNdTopLeft*/)
2203 const SwFrameFormat& rFrameFormat = rFrame.GetFrameFormat();
2204 if (rFrameFormat.GetFlySplit().GetValue())
2206 // The frame can split: this was originally from a floating table, write it back as
2207 // such.
2208 SaveRunState aState(*this);
2209 const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
2210 SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
2211 SwNodeOffset nEnd
2212 = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
2213 m_rExport.SaveData(nStt, nEnd);
2214 GetExport().WriteText();
2215 m_rExport.RestoreData();
2216 return;
2219 const SwNode* pNode = rFrame.GetContent();
2220 const SwGrfNode* pGrfNode = pNode ? pNode->GetGrfNode() : nullptr;
2222 switch (rFrame.GetWriterType())
2224 case ww8::Frame::eTextBox:
2226 // If this is a TextBox of a shape, then ignore: it's handled in RtfSdrExport::StartShape().
2227 if (RtfSdrExport::isTextBox(rFrame.GetFrameFormat()))
2228 break;
2230 SaveRunState const saved(*this);
2232 m_rExport.m_pParentFrame = &rFrame;
2234 m_rExport.Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_SHP);
2235 m_rExport.Strm().WriteOString(
2236 "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_SHPINST);
2238 // Shape properties.
2239 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
2240 "shapeType"_ostr, OString::number(ESCHER_ShpInst_TextBox)));
2242 // When a frame has some low height, but automatically expanded due
2243 // to lots of contents, this size contains the real size.
2244 const Size aSize = rFrame.GetSize();
2245 m_pFlyFrameSize = &aSize;
2247 m_rExport.m_bOutFlyFrameAttrs = true;
2248 m_rExport.SetRTFFlySyntax(true);
2249 m_rExport.OutputFormat(rFrame.GetFrameFormat(), false, false, true);
2251 // Write ZOrder.
2252 if (const SdrObject* pObject = rFrame.GetFrameFormat().FindRealSdrObject())
2254 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SHPZ);
2255 m_rExport.Strm().WriteNumberAsString(pObject->GetOrdNum());
2258 m_rExport.Strm().WriteOString(m_aRunText.makeStringAndClear());
2259 m_rExport.Strm().WriteOString(m_aStyles);
2260 m_aStyles.setLength(0);
2261 m_rExport.m_bOutFlyFrameAttrs = false;
2262 m_rExport.SetRTFFlySyntax(false);
2263 m_pFlyFrameSize = nullptr;
2265 lcl_TextFrameShadow(m_aFlyProperties, rFrameFormat);
2266 lcl_TextFrameRelativeSize(m_aFlyProperties, rFrameFormat);
2268 for (const std::pair<OString, OString>& rPair : m_aFlyProperties)
2270 m_rExport.Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_SP "{");
2271 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SN " ");
2272 m_rExport.Strm().WriteOString(rPair.first);
2273 m_rExport.Strm().WriteOString("}{" OOO_STRING_SVTOOLS_RTF_SV " ");
2274 m_rExport.Strm().WriteOString(rPair.second);
2275 m_rExport.Strm().WriteOString("}}");
2277 m_aFlyProperties.clear();
2279 writeTextFrame(rFrame);
2281 m_rExport.Strm().WriteChar('}'); // shpinst
2282 m_rExport.Strm().WriteChar('}'); // shp
2284 m_rExport.Strm().WriteOString(SAL_NEWLINE_STRING);
2286 break;
2287 case ww8::Frame::eGraphic:
2288 if (pGrfNode)
2290 m_aRunText.append(dynamic_cast<const SwFlyFrameFormat*>(&rFrame.GetFrameFormat()),
2291 pGrfNode);
2293 else if (!rFrame.IsInline())
2295 m_rExport.m_pParentFrame = &rFrame;
2296 m_rExport.SetRTFFlySyntax(true);
2297 m_rExport.OutputFormat(rFrame.GetFrameFormat(), false, false, true);
2298 m_rExport.SetRTFFlySyntax(false);
2299 m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE);
2300 m_rExport.OutputFormat(rFrame.GetFrameFormat(), false, false, true);
2301 m_aRunText->append('}');
2302 m_rExport.m_pParentFrame = nullptr;
2304 break;
2305 case ww8::Frame::eDrawing:
2307 const SdrObject* pSdrObj = rFrame.GetFrameFormat().FindRealSdrObject();
2308 if (pSdrObj)
2310 m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_FIELD "{");
2311 m_aRunText->append(OOO_STRING_SVTOOLS_RTF_IGNORE);
2312 m_aRunText->append(OOO_STRING_SVTOOLS_RTF_FLDINST);
2313 m_aRunText->append(" SHAPE ");
2314 m_aRunText->append("}"
2315 "{" OOO_STRING_SVTOOLS_RTF_FLDRSLT);
2317 m_rExport.SdrExporter().AddSdrObject(*pSdrObj);
2319 m_aRunText->append('}');
2320 m_aRunText->append('}');
2323 break;
2324 case ww8::Frame::eFormControl:
2326 const SdrObject* pObject = rFrameFormat.FindRealSdrObject();
2328 m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_FIELD);
2329 m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FLDINST);
2331 if (pObject && pObject->GetObjInventor() == SdrInventor::FmForm)
2333 if (auto pFormObj = dynamic_cast<const SdrUnoObj*>(pObject))
2335 const uno::Reference<awt::XControlModel>& xControlModel
2336 = pFormObj->GetUnoControlModel();
2337 uno::Reference<lang::XServiceInfo> xInfo(xControlModel, uno::UNO_QUERY);
2338 if (xInfo.is())
2340 uno::Reference<beans::XPropertySet> xPropSet(xControlModel, uno::UNO_QUERY);
2341 uno::Reference<beans::XPropertySetInfo> xPropSetInfo
2342 = xPropSet->getPropertySetInfo();
2343 OUString sName;
2344 if (xInfo->supportsService(u"com.sun.star.form.component.CheckBox"_ustr))
2346 m_aRun->append(OUStringToOString(FieldString(ww::eFORMCHECKBOX),
2347 m_rExport.GetCurrentEncoding()));
2348 m_aRun->append(
2349 "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FORMFIELD
2350 "{");
2351 m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFTYPE "1"); // 1 = checkbox
2352 // checkbox size in half points, this seems to be always 20
2353 m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFHPS "20");
2355 OUString aStr;
2356 sName = "Name";
2357 if (xPropSetInfo->hasPropertyByName(sName))
2359 xPropSet->getPropertyValue(sName) >>= aStr;
2360 m_aRun->append(
2361 "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFNAME
2362 " ");
2363 m_aRun->append(
2364 OUStringToOString(aStr, m_rExport.GetCurrentEncoding()));
2365 m_aRun->append('}');
2368 sName = "HelpText";
2369 if (xPropSetInfo->hasPropertyByName(sName))
2371 xPropSet->getPropertyValue(sName) >>= aStr;
2372 m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFOWNHELP);
2373 m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE
2374 OOO_STRING_SVTOOLS_RTF_FFHELPTEXT " ");
2375 m_aRun->append(
2376 OUStringToOString(aStr, m_rExport.GetCurrentEncoding()));
2377 m_aRun->append('}');
2380 sName = "HelpF1Text";
2381 if (xPropSetInfo->hasPropertyByName(sName))
2383 xPropSet->getPropertyValue(sName) >>= aStr;
2384 m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFOWNSTAT);
2385 m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE
2386 OOO_STRING_SVTOOLS_RTF_FFSTATTEXT " ");
2387 m_aRun->append(
2388 OUStringToOString(aStr, m_rExport.GetCurrentEncoding()));
2389 m_aRun->append('}');
2392 sal_Int16 nTemp = 0;
2393 xPropSet->getPropertyValue(u"DefaultState"_ustr) >>= nTemp;
2394 m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFDEFRES);
2395 m_aRun->append(static_cast<sal_Int32>(nTemp));
2396 xPropSet->getPropertyValue(u"State"_ustr) >>= nTemp;
2397 m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFRES);
2398 m_aRun->append(static_cast<sal_Int32>(nTemp));
2400 m_aRun->append("}}");
2402 // field result is empty, ffres already contains the form result
2403 m_aRun->append("}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " ");
2405 else if (xInfo->supportsService(
2406 u"com.sun.star.form.component.TextField"_ustr))
2408 OStringBuffer aBuf;
2409 OString aStr;
2410 OUString aTmp;
2411 const char* pStr;
2413 m_aRun->append(OUStringToOString(FieldString(ww::eFORMTEXT),
2414 m_rExport.GetCurrentEncoding()));
2415 m_aRun->append(
2416 "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_DATAFIELD
2417 " ");
2418 for (int i = 0; i < 8; i++)
2419 aBuf.append(char(0x00));
2420 xPropSet->getPropertyValue(u"Name"_ustr) >>= aTmp;
2421 aStr = OUStringToOString(aTmp, m_rExport.GetCurrentEncoding());
2422 aBuf.append(OStringChar(static_cast<char>(aStr.getLength())) + aStr
2423 + OStringChar(char(0x00)));
2424 xPropSet->getPropertyValue(u"DefaultText"_ustr) >>= aTmp;
2425 aStr = OUStringToOString(aTmp, m_rExport.GetCurrentEncoding());
2426 aBuf.append(static_cast<char>(aStr.getLength()));
2427 aBuf.append(aStr);
2428 for (int i = 0; i < 11; i++)
2429 aBuf.append(char(0x00));
2430 aStr = aBuf.makeStringAndClear();
2431 pStr = aStr.getStr();
2432 for (int i = 0; i < aStr.getLength(); i++, pStr++)
2433 m_aRun->append(msfilter::rtfutil::OutHex(*pStr, 2));
2434 m_aRun->append('}');
2435 m_aRun->append("}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " ");
2436 xPropSet->getPropertyValue(u"Text"_ustr) >>= aTmp;
2437 m_aRun->append(
2438 msfilter::rtfutil::OutString(aTmp, m_rExport.GetCurrentEncoding()));
2439 m_aRun->append('}');
2440 m_aRun->append(
2441 "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FORMFIELD
2442 "{");
2443 sName = "HelpText";
2444 if (xPropSetInfo->hasPropertyByName(sName))
2446 xPropSet->getPropertyValue(sName) >>= aTmp;
2447 m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFOWNHELP);
2448 m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE
2449 OOO_STRING_SVTOOLS_RTF_FFHELPTEXT " ");
2450 m_aRun->append(msfilter::rtfutil::OutString(
2451 aTmp, m_rExport.GetCurrentEncoding()));
2452 m_aRun->append('}');
2455 sName = "HelpF1Text";
2456 if (xPropSetInfo->hasPropertyByName(sName))
2458 xPropSet->getPropertyValue(sName) >>= aTmp;
2459 m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFOWNSTAT);
2460 m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE
2461 OOO_STRING_SVTOOLS_RTF_FFSTATTEXT " ");
2462 m_aRun->append(msfilter::rtfutil::OutString(
2463 aTmp, m_rExport.GetCurrentEncoding()));
2464 m_aRun->append('}');
2466 m_aRun->append("}");
2468 else if (xInfo->supportsService(
2469 u"com.sun.star.form.component.ListBox"_ustr))
2471 OUString aStr;
2472 uno::Sequence<sal_Int16> aIntSeq;
2473 uno::Sequence<OUString> aStrSeq;
2475 m_aRun->append(OUStringToOString(FieldString(ww::eFORMDROPDOWN),
2476 m_rExport.GetCurrentEncoding()));
2477 m_aRun->append(
2478 "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FORMFIELD
2479 "{");
2480 m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFTYPE "2"); // 2 = list
2481 m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFHASLISTBOX);
2483 xPropSet->getPropertyValue(u"DefaultSelection"_ustr) >>= aIntSeq;
2484 if (aIntSeq.hasElements())
2486 m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFDEFRES);
2487 // a dropdown list can have only one 'selected item by default'
2488 m_aRun->append(static_cast<sal_Int32>(aIntSeq[0]));
2491 xPropSet->getPropertyValue(u"SelectedItems"_ustr) >>= aIntSeq;
2492 if (aIntSeq.hasElements())
2494 m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFRES);
2495 // a dropdown list can have only one 'currently selected item'
2496 m_aRun->append(static_cast<sal_Int32>(aIntSeq[0]));
2499 sName = "Name";
2500 if (xPropSetInfo->hasPropertyByName(sName))
2502 xPropSet->getPropertyValue(sName) >>= aStr;
2503 m_aRun->append(
2504 "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFNAME
2505 " ");
2506 m_aRun->append(
2507 OUStringToOString(aStr, m_rExport.GetCurrentEncoding()));
2508 m_aRun->append('}');
2511 sName = "HelpText";
2512 if (xPropSetInfo->hasPropertyByName(sName))
2514 xPropSet->getPropertyValue(sName) >>= aStr;
2515 m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFOWNHELP);
2516 m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE
2517 OOO_STRING_SVTOOLS_RTF_FFHELPTEXT " ");
2518 m_aRun->append(
2519 OUStringToOString(aStr, m_rExport.GetCurrentEncoding()));
2520 m_aRun->append('}');
2523 sName = "HelpF1Text";
2524 if (xPropSetInfo->hasPropertyByName(sName))
2526 xPropSet->getPropertyValue(sName) >>= aStr;
2527 m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFOWNSTAT);
2528 m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE
2529 OOO_STRING_SVTOOLS_RTF_FFSTATTEXT " ");
2530 m_aRun->append(
2531 OUStringToOString(aStr, m_rExport.GetCurrentEncoding()));
2532 m_aRun->append('}');
2535 xPropSet->getPropertyValue(u"StringItemList"_ustr) >>= aStrSeq;
2536 for (const auto& rStr : aStrSeq)
2537 m_aRun->append(
2538 "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFL " "
2539 + OUStringToOString(rStr, m_rExport.GetCurrentEncoding())
2540 + "}");
2542 m_aRun->append("}}");
2544 // field result is empty, ffres already contains the form result
2545 m_aRun->append("}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " ");
2547 else
2548 SAL_INFO("sw.rtf", __func__ << " unhandled form control: '"
2549 << xInfo->getImplementationName() << "'");
2554 m_aRun->append('}');
2555 m_aRun->append('}');
2557 break;
2558 case ww8::Frame::eOle:
2560 const SdrObject* pSdrObj = rFrameFormat.FindRealSdrObject();
2561 if (pSdrObj)
2563 SwNodeIndex aIdx(*rFrameFormat.GetContent().GetContentIdx(), 1);
2564 SwOLENode& rOLENd = *aIdx.GetNode().GetOLENode();
2565 FlyFrameOLE(dynamic_cast<const SwFlyFrameFormat*>(&rFrameFormat), rOLENd,
2566 rFrame.GetLayoutSize());
2569 break;
2570 default:
2571 SAL_INFO("sw.rtf", __func__ << ": unknown type ("
2572 << static_cast<int>(rFrame.GetWriterType()) << ")");
2573 break;
2577 void RtfAttributeOutput::CharCaseMap(const SvxCaseMapItem& rCaseMap)
2579 switch (rCaseMap.GetValue())
2581 case SvxCaseMap::SmallCaps:
2582 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SCAPS);
2583 break;
2584 case SvxCaseMap::Uppercase:
2585 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CAPS);
2586 break;
2587 default: // Something that rtf does not support
2588 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SCAPS);
2589 m_aStyles.append(sal_Int32(0));
2590 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CAPS);
2591 m_aStyles.append(sal_Int32(0));
2592 break;
2596 void RtfAttributeOutput::CharColor(const SvxColorItem& rColor)
2598 const Color aColor(rColor.GetValue());
2600 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CF);
2601 m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(aColor)));
2604 void RtfAttributeOutput::CharContour(const SvxContourItem& rContour)
2606 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_OUTL);
2607 if (!rContour.GetValue())
2608 m_aStyles.append(sal_Int32(0));
2611 void RtfAttributeOutput::CharCrossedOut(const SvxCrossedOutItem& rCrossedOut)
2613 switch (rCrossedOut.GetStrikeout())
2615 case STRIKEOUT_NONE:
2616 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_STRIKE);
2617 m_aStyles.append(sal_Int32(0));
2618 break;
2619 case STRIKEOUT_DOUBLE:
2620 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_STRIKED);
2621 m_aStyles.append(sal_Int32(1));
2622 break;
2623 default:
2624 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_STRIKE);
2625 break;
2629 void RtfAttributeOutput::CharEscapement(const SvxEscapementItem& rEscapement)
2631 short nEsc = rEscapement.GetEsc();
2632 short nProp = rEscapement.GetProportionalHeight();
2633 sal_Int32 nProp100 = nProp * 100;
2634 if (DFLT_ESC_PROP == nProp || nProp < 1 || nProp > 100)
2636 if (DFLT_ESC_SUB == nEsc || DFLT_ESC_AUTO_SUB == nEsc)
2637 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SUB);
2638 else if (DFLT_ESC_SUPER == nEsc || DFLT_ESC_AUTO_SUPER == nEsc)
2639 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SUPER);
2640 return;
2642 if (DFLT_ESC_AUTO_SUPER == nEsc)
2644 nEsc = .8 * (100 - nProp);
2645 ++nProp100; // A 1 afterwards means 'automatic' according to editeng/rtf/rtfitem.cxx
2647 else if (DFLT_ESC_AUTO_SUB == nEsc)
2649 nEsc = .2 * -(100 - nProp);
2650 ++nProp100;
2653 const char* pUpDn;
2655 double fHeight = m_rExport.GetItem(RES_CHRATR_FONTSIZE).GetHeight();
2657 if (0 < nEsc)
2658 pUpDn = OOO_STRING_SVTOOLS_RTF_UP;
2659 else if (0 > nEsc)
2661 pUpDn = OOO_STRING_SVTOOLS_RTF_DN;
2662 fHeight = -fHeight;
2664 else
2665 return;
2667 m_aStyles.append('{');
2668 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_IGNORE);
2669 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_UPDNPROP);
2670 m_aStyles.append(nProp100);
2671 m_aStyles.append('}');
2672 m_aStyles.append(pUpDn);
2675 * Calculate the act. FontSize and the percentage of the displacement;
2676 * RTF file expects half points, while internally it's in twips.
2677 * Formally : (FontSize * 1/20 ) pts x * 2
2678 * ----------------------- = ------------
2679 * 100% Escapement
2681 m_aStyles.append(static_cast<sal_Int32>(round(fHeight * nEsc / 1000)));
2684 void RtfAttributeOutput::CharFont(const SvxFontItem& rFont)
2686 // Insert \loch in MoveCharacterProperties
2687 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_F);
2688 m_aStyles.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont)));
2690 // Insert \hich in MoveCharacterProperties
2691 m_aStylesAssocHich.append(OOO_STRING_SVTOOLS_RTF_AF);
2692 m_aStylesAssocHich.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont)));
2694 // FIXME: this may be a tad expensive... but the charset needs to be
2695 // consistent with what wwFont::WriteRtf() does
2696 sw::util::FontMapExport aTmp(rFont.GetFamilyName());
2697 sal_uInt8 nWindowsCharset = sw::ms::rtl_TextEncodingToWinCharsetRTF(
2698 aTmp.msPrimary, aTmp.msSecondary, rFont.GetCharSet());
2699 m_rExport.SetCurrentEncoding(rtl_getTextEncodingFromWindowsCharset(nWindowsCharset));
2700 if (m_rExport.GetCurrentEncoding() == RTL_TEXTENCODING_DONTKNOW)
2701 m_rExport.SetCurrentEncoding(m_rExport.GetDefaultEncoding());
2704 void RtfAttributeOutput::CharFontSize(const SvxFontHeightItem& rFontSize)
2706 switch (rFontSize.Which())
2708 case RES_CHRATR_FONTSIZE:
2709 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_FS);
2710 m_aStyles.append(static_cast<sal_Int32>(rFontSize.GetHeight() / 10));
2711 break;
2712 case RES_CHRATR_CJK_FONTSIZE:
2713 m_aStylesAssocDbch.append(OOO_STRING_SVTOOLS_RTF_AFS);
2714 m_aStylesAssocDbch.append(static_cast<sal_Int32>(rFontSize.GetHeight() / 10));
2715 break;
2716 case RES_CHRATR_CTL_FONTSIZE:
2717 m_aStylesAssocRtlch.append(OOO_STRING_SVTOOLS_RTF_AFS);
2718 m_aStylesAssocRtlch.append(static_cast<sal_Int32>(rFontSize.GetHeight() / 10));
2719 break;
2723 void RtfAttributeOutput::CharKerning(const SvxKerningItem& rKerning)
2725 // in quarter points then in twips
2726 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_EXPND);
2727 m_aStyles.append(static_cast<sal_Int32>(rKerning.GetValue() / 5));
2728 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_EXPNDTW);
2729 m_aStyles.append(static_cast<sal_Int32>(rKerning.GetValue()));
2732 void RtfAttributeOutput::CharLanguage(const SvxLanguageItem& rLanguage)
2734 switch (rLanguage.Which())
2736 case RES_CHRATR_LANGUAGE:
2737 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LANG);
2738 m_aStyles.append(
2739 static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
2740 m_aStylesAssocLtrch.append(OOO_STRING_SVTOOLS_RTF_LANG);
2741 m_aStylesAssocLtrch.append(
2742 static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
2743 break;
2744 case RES_CHRATR_CJK_LANGUAGE:
2745 m_aStylesAssocDbch.append(OOO_STRING_SVTOOLS_RTF_LANGFE);
2746 m_aStylesAssocDbch.append(
2747 static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
2748 m_aStylesAssocLtrch.append(OOO_STRING_SVTOOLS_RTF_LANGFE);
2749 m_aStylesAssocLtrch.append(
2750 static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
2751 break;
2752 case RES_CHRATR_CTL_LANGUAGE:
2753 m_aStylesAssocRtlch.append(OOO_STRING_SVTOOLS_RTF_ALANG);
2754 m_aStylesAssocRtlch.append(
2755 static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
2756 break;
2760 void RtfAttributeOutput::CharPosture(const SvxPostureItem& rPosture)
2762 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_I);
2763 if (rPosture.GetPosture() == ITALIC_NONE)
2764 m_aStyles.append(sal_Int32(0));
2767 void RtfAttributeOutput::CharShadow(const SvxShadowedItem& rShadow)
2769 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SHAD);
2770 if (!rShadow.GetValue())
2771 m_aStyles.append(sal_Int32(0));
2774 void RtfAttributeOutput::CharUnderline(const SvxUnderlineItem& rUnderline)
2776 const char* pStr = nullptr;
2777 const SfxPoolItem* pItem = m_rExport.HasItem(RES_CHRATR_WORDLINEMODE);
2778 bool bWord = false;
2779 // No StaticWhichCast(RES_CHRATR_WORDLINEMODE), this may be for a postit, where the which ids
2780 // don't match.
2781 if (pItem)
2782 bWord = static_cast<const SvxWordLineModeItem*>(pItem)->GetValue();
2783 switch (rUnderline.GetLineStyle())
2785 case LINESTYLE_SINGLE:
2786 pStr = bWord ? OOO_STRING_SVTOOLS_RTF_ULW : OOO_STRING_SVTOOLS_RTF_UL;
2787 break;
2788 case LINESTYLE_DOUBLE:
2789 pStr = OOO_STRING_SVTOOLS_RTF_ULDB;
2790 break;
2791 case LINESTYLE_NONE:
2792 pStr = OOO_STRING_SVTOOLS_RTF_ULNONE;
2793 break;
2794 case LINESTYLE_DOTTED:
2795 pStr = OOO_STRING_SVTOOLS_RTF_ULD;
2796 break;
2797 case LINESTYLE_DASH:
2798 pStr = OOO_STRING_SVTOOLS_RTF_ULDASH;
2799 break;
2800 case LINESTYLE_DASHDOT:
2801 pStr = OOO_STRING_SVTOOLS_RTF_ULDASHD;
2802 break;
2803 case LINESTYLE_DASHDOTDOT:
2804 pStr = OOO_STRING_SVTOOLS_RTF_ULDASHDD;
2805 break;
2806 case LINESTYLE_BOLD:
2807 pStr = OOO_STRING_SVTOOLS_RTF_ULTH;
2808 break;
2809 case LINESTYLE_WAVE:
2810 pStr = OOO_STRING_SVTOOLS_RTF_ULWAVE;
2811 break;
2812 case LINESTYLE_BOLDDOTTED:
2813 pStr = OOO_STRING_SVTOOLS_RTF_ULTHD;
2814 break;
2815 case LINESTYLE_BOLDDASH:
2816 pStr = OOO_STRING_SVTOOLS_RTF_ULTHDASH;
2817 break;
2818 case LINESTYLE_LONGDASH:
2819 pStr = OOO_STRING_SVTOOLS_RTF_ULLDASH;
2820 break;
2821 case LINESTYLE_BOLDLONGDASH:
2822 pStr = OOO_STRING_SVTOOLS_RTF_ULTHLDASH;
2823 break;
2824 case LINESTYLE_BOLDDASHDOT:
2825 pStr = OOO_STRING_SVTOOLS_RTF_ULTHDASHD;
2826 break;
2827 case LINESTYLE_BOLDDASHDOTDOT:
2828 pStr = OOO_STRING_SVTOOLS_RTF_ULTHDASHDD;
2829 break;
2830 case LINESTYLE_BOLDWAVE:
2831 pStr = OOO_STRING_SVTOOLS_RTF_ULHWAVE;
2832 break;
2833 case LINESTYLE_DOUBLEWAVE:
2834 pStr = OOO_STRING_SVTOOLS_RTF_ULULDBWAVE;
2835 break;
2836 default:
2837 break;
2840 if (pStr)
2842 m_aStyles.append(pStr);
2843 // NEEDSWORK looks like here rUnderline.GetColor() is always black,
2844 // even if the color in the odt is for example green...
2845 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ULC);
2846 m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(rUnderline.GetColor())));
2850 void RtfAttributeOutput::CharWeight(const SvxWeightItem& rWeight)
2852 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_B);
2853 if (rWeight.GetWeight() != WEIGHT_BOLD)
2854 m_aStyles.append(sal_Int32(0));
2857 void RtfAttributeOutput::CharAutoKern(const SvxAutoKernItem& rAutoKern)
2859 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_KERNING);
2860 m_aStyles.append(static_cast<sal_Int32>(rAutoKern.GetValue() ? 1 : 0));
2863 void RtfAttributeOutput::CharAnimatedText(const SvxBlinkItem& rBlink)
2865 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ANIMTEXT);
2866 m_aStyles.append(static_cast<sal_Int32>(rBlink.GetValue() ? 2 : 0));
2869 void RtfAttributeOutput::CharBackground(const SvxBrushItem& rBrush)
2871 if (!rBrush.GetColor().IsTransparent())
2873 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CHCBPAT);
2874 m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(rBrush.GetColor())));
2878 void RtfAttributeOutput::CharFontCJK(const SvxFontItem& rFont)
2880 // Insert \dbch in MoveCharacterProperties
2881 m_aStylesAssocDbch.append(OOO_STRING_SVTOOLS_RTF_AF);
2882 m_aStylesAssocDbch.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont)));
2885 void RtfAttributeOutput::CharFontSizeCJK(const SvxFontHeightItem& rFontSize)
2887 CharFontSize(rFontSize);
2890 void RtfAttributeOutput::CharLanguageCJK(const SvxLanguageItem& rLanguageItem)
2892 CharLanguage(rLanguageItem);
2895 void RtfAttributeOutput::CharPostureCJK(const SvxPostureItem& rPosture)
2897 m_aStylesAssocDbch.append(OOO_STRING_SVTOOLS_RTF_I);
2898 if (rPosture.GetPosture() == ITALIC_NONE)
2899 m_aStylesAssocDbch.append(sal_Int32(0));
2902 void RtfAttributeOutput::CharWeightCJK(const SvxWeightItem& rWeight)
2904 m_aStylesAssocDbch.append(OOO_STRING_SVTOOLS_RTF_AB);
2905 if (rWeight.GetWeight() != WEIGHT_BOLD)
2906 m_aStylesAssocDbch.append(sal_Int32(0));
2909 void RtfAttributeOutput::CharFontCTL(const SvxFontItem& rFont)
2911 // Insert \rtlch in MoveCharacterProperties
2912 m_aStylesAssocRtlch.append(OOO_STRING_SVTOOLS_RTF_AF);
2913 m_aStylesAssocRtlch.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont)));
2916 void RtfAttributeOutput::CharFontSizeCTL(const SvxFontHeightItem& rFontSize)
2918 CharFontSize(rFontSize);
2921 void RtfAttributeOutput::CharLanguageCTL(const SvxLanguageItem& rLanguageItem)
2923 CharLanguage(rLanguageItem);
2926 void RtfAttributeOutput::CharPostureCTL(const SvxPostureItem& rPosture)
2928 m_aStylesAssocRtlch.append(OOO_STRING_SVTOOLS_RTF_AI);
2929 if (rPosture.GetPosture() == ITALIC_NONE)
2930 m_aStylesAssocRtlch.append(sal_Int32(0));
2933 void RtfAttributeOutput::CharWeightCTL(const SvxWeightItem& rWeight)
2935 m_aStylesAssocRtlch.append(OOO_STRING_SVTOOLS_RTF_AB);
2936 if (rWeight.GetWeight() != WEIGHT_BOLD)
2937 m_aStylesAssocRtlch.append(sal_Int32(0));
2940 void RtfAttributeOutput::CharBidiRTL(const SfxPoolItem& /*rItem*/) {}
2942 void RtfAttributeOutput::CharIdctHint(const SfxPoolItem& /*rItem*/) {}
2944 void RtfAttributeOutput::CharRotate(const SvxCharRotateItem& rRotate)
2946 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_HORZVERT);
2947 m_aStyles.append(static_cast<sal_Int32>(rRotate.IsFitToLine() ? 1 : 0));
2950 void RtfAttributeOutput::CharEmphasisMark(const SvxEmphasisMarkItem& rEmphasisMark)
2952 FontEmphasisMark v = rEmphasisMark.GetEmphasisMark();
2953 if (v == FontEmphasisMark::NONE)
2954 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ACCNONE);
2955 else if (v == (FontEmphasisMark::Dot | FontEmphasisMark::PosAbove))
2956 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ACCDOT);
2957 else if (v == (FontEmphasisMark::Accent | FontEmphasisMark::PosAbove))
2958 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ACCCOMMA);
2959 else if (v == (FontEmphasisMark::Circle | FontEmphasisMark::PosAbove))
2960 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ACCCIRCLE);
2961 else if (v == (FontEmphasisMark::Dot | FontEmphasisMark::PosBelow))
2962 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ACCUNDERDOT);
2965 void RtfAttributeOutput::CharTwoLines(const SvxTwoLinesItem& rTwoLines)
2967 if (!rTwoLines.GetValue())
2968 return;
2970 sal_Unicode cStart = rTwoLines.GetStartBracket();
2971 sal_Unicode cEnd = rTwoLines.GetEndBracket();
2973 sal_uInt16 nType;
2974 if (!cStart && !cEnd)
2975 nType = 0;
2976 else if ('{' == cStart || '}' == cEnd)
2977 nType = 4;
2978 else if ('<' == cStart || '>' == cEnd)
2979 nType = 3;
2980 else if ('[' == cStart || ']' == cEnd)
2981 nType = 2;
2982 else // all other kind of brackets
2983 nType = 1;
2985 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_TWOINONE);
2986 m_aStyles.append(static_cast<sal_Int32>(nType));
2989 void RtfAttributeOutput::CharScaleWidth(const SvxCharScaleWidthItem& rScaleWidth)
2991 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CHARSCALEX);
2992 m_aStyles.append(static_cast<sal_Int32>(rScaleWidth.GetValue()));
2995 void RtfAttributeOutput::CharRelief(const SvxCharReliefItem& rRelief)
2997 const char* pStr;
2998 switch (rRelief.GetValue())
3000 case FontRelief::Embossed:
3001 pStr = OOO_STRING_SVTOOLS_RTF_EMBO;
3002 break;
3003 case FontRelief::Engraved:
3004 pStr = OOO_STRING_SVTOOLS_RTF_IMPR;
3005 break;
3006 default:
3007 pStr = nullptr;
3008 break;
3011 if (pStr)
3012 m_aStyles.append(pStr);
3015 void RtfAttributeOutput::CharHidden(const SvxCharHiddenItem& rHidden)
3017 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_V);
3018 if (!rHidden.GetValue())
3019 m_aStyles.append(sal_Int32(0));
3022 void RtfAttributeOutput::CharBorder(const editeng::SvxBorderLine* pAllBorder,
3023 const sal_uInt16 nDist, const bool bShadow)
3025 m_aStyles.append(
3026 OutBorderLine(m_rExport, pAllBorder, OOO_STRING_SVTOOLS_RTF_CHBRDR, nDist,
3027 bShadow ? SvxShadowLocation::BottomRight : SvxShadowLocation::NONE));
3030 void RtfAttributeOutput::CharHighlight(const SvxBrushItem& rBrush)
3032 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_HIGHLIGHT);
3033 m_aStyles.append(static_cast<sal_Int32>(msfilter::util::TransColToIco(rBrush.GetColor())));
3036 void RtfAttributeOutput::TextINetFormat(const SwFormatINetFormat& rURL)
3038 if (rURL.GetValue().isEmpty())
3039 return;
3041 const SwCharFormat* pFormat;
3042 const SwTextINetFormat* pTextAtr = rURL.GetTextINetFormat();
3044 if (pTextAtr && nullptr != (pFormat = pTextAtr->GetCharFormat()))
3046 sal_uInt16 nStyle = m_rExport.GetId(pFormat);
3047 OString* pString = m_rExport.GetStyle(nStyle);
3048 if (pString)
3049 m_aStyles.append(*pString);
3053 void RtfAttributeOutput::TextCharFormat(const SwFormatCharFormat& rCharFormat)
3055 sal_uInt16 nStyle = m_rExport.GetId(rCharFormat.GetCharFormat());
3056 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CS);
3057 m_aStyles.append(static_cast<sal_Int32>(nStyle));
3058 OString* pString = m_rExport.GetStyle(nStyle);
3059 if (pString)
3060 m_aStyles.append(*pString);
3063 void RtfAttributeOutput::WriteTextFootnoteNumStr(const SwFormatFootnote& rFootnote)
3065 if (rFootnote.GetNumStr().isEmpty())
3066 m_aRun->append(OOO_STRING_SVTOOLS_RTF_CHFTN);
3067 else
3068 m_aRun->append(
3069 msfilter::rtfutil::OutString(rFootnote.GetNumStr(), m_rExport.GetCurrentEncoding()));
3072 void RtfAttributeOutput::TextFootnote_Impl(const SwFormatFootnote& rFootnote)
3074 SAL_INFO("sw.rtf", __func__ << " start");
3076 m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_SUPER " ");
3077 EndRunProperties(nullptr);
3078 m_aRun->append(' ');
3079 WriteTextFootnoteNumStr(rFootnote);
3080 m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FOOTNOTE);
3081 if (rFootnote.IsEndNote() || m_rExport.m_rDoc.GetFootnoteInfo().m_ePos == FTNPOS_CHAPTER)
3082 m_aRun->append(OOO_STRING_SVTOOLS_RTF_FTNALT);
3083 m_aRun->append(' ');
3084 WriteTextFootnoteNumStr(rFootnote);
3087 * The footnote contains a whole paragraph, so we have to:
3088 * 1) Reset, then later restore the contents of our run buffer and run state.
3089 * 2) Buffer the output of the whole paragraph, as we do so for section headers already.
3091 const SwNodeIndex* pIndex = rFootnote.GetTextFootnote()->GetStartNode();
3092 RtfStringBuffer aRun = m_aRun;
3093 m_aRun.clear();
3094 bool bInRunOrig = m_bInRun;
3095 m_bInRun = false;
3096 bool bSingleEmptyRunOrig = m_bSingleEmptyRun;
3097 m_bSingleEmptyRun = false;
3098 m_bBufferSectionHeaders = true;
3099 m_rExport.WriteSpecialText(pIndex->GetIndex() + 1, pIndex->GetNode().EndOfSectionIndex(),
3100 !rFootnote.IsEndNote() ? TXT_FTN : TXT_EDN);
3101 m_bBufferSectionHeaders = false;
3102 m_bInRun = bInRunOrig;
3103 m_bSingleEmptyRun = bSingleEmptyRunOrig;
3104 m_aRun = std::move(aRun);
3105 m_aRun->append(m_aSectionHeaders);
3106 m_aSectionHeaders.setLength(0);
3108 m_aRun->append("}");
3109 m_aRun->append("}");
3111 SAL_INFO("sw.rtf", __func__ << " end");
3114 void RtfAttributeOutput::ParaLineSpacing_Impl(short nSpace, short nMulti)
3116 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SL);
3117 m_aStyles.append(static_cast<sal_Int32>(nSpace));
3118 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SLMULT);
3119 m_aStyles.append(static_cast<sal_Int32>(nMulti));
3122 void RtfAttributeOutput::ParaAdjust(const SvxAdjustItem& rAdjust)
3124 switch (rAdjust.GetAdjust())
3126 case SvxAdjust::Left:
3127 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_QL);
3128 break;
3129 case SvxAdjust::Right:
3130 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_QR);
3131 break;
3132 case SvxAdjust::BlockLine:
3133 case SvxAdjust::Block:
3134 if (rAdjust.GetLastBlock() == SvxAdjust::Block)
3135 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_QD);
3136 else
3137 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_QJ);
3138 break;
3139 case SvxAdjust::Center:
3140 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_QC);
3141 break;
3142 default:
3143 break;
3147 void RtfAttributeOutput::ParaSplit(const SvxFormatSplitItem& rSplit)
3149 if (!rSplit.GetValue())
3150 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_KEEP);
3153 void RtfAttributeOutput::ParaWidows(const SvxWidowsItem& rWidows)
3155 if (rWidows.GetValue())
3156 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_WIDCTLPAR);
3157 else
3158 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_NOWIDCTLPAR);
3161 void RtfAttributeOutput::ParaTabStop(const SvxTabStopItem& rTabStop)
3163 tools::Long nOffset = m_rExport.GetParaTabStopOffset();
3165 for (sal_uInt16 n = 0; n < rTabStop.Count(); n++)
3167 const SvxTabStop& rTS = rTabStop[n];
3168 if (SvxTabAdjust::Default != rTS.GetAdjustment())
3170 const char* pFill = nullptr;
3171 switch (rTS.GetFill())
3173 case cDfltFillChar:
3174 break;
3176 case '.':
3177 pFill = OOO_STRING_SVTOOLS_RTF_TLDOT;
3178 break;
3179 case '_':
3180 pFill = OOO_STRING_SVTOOLS_RTF_TLUL;
3181 break;
3182 case '-':
3183 pFill = OOO_STRING_SVTOOLS_RTF_TLTH;
3184 break;
3185 case '=':
3186 pFill = OOO_STRING_SVTOOLS_RTF_TLEQ;
3187 break;
3188 default:
3189 break;
3191 if (pFill)
3192 m_aStyles.append(pFill);
3194 const char* pAdjStr = nullptr;
3195 switch (rTS.GetAdjustment())
3197 case SvxTabAdjust::Right:
3198 pAdjStr = OOO_STRING_SVTOOLS_RTF_TQR;
3199 break;
3200 case SvxTabAdjust::Decimal:
3201 pAdjStr = OOO_STRING_SVTOOLS_RTF_TQDEC;
3202 break;
3203 case SvxTabAdjust::Center:
3204 pAdjStr = OOO_STRING_SVTOOLS_RTF_TQC;
3205 break;
3206 default:
3207 break;
3209 if (pAdjStr)
3210 m_aStyles.append(pAdjStr);
3211 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_TX);
3212 m_aStyles.append(static_cast<sal_Int32>(rTS.GetTabPos() + nOffset));
3214 else
3216 m_aTabStop.append(OOO_STRING_SVTOOLS_RTF_DEFTAB);
3217 m_aTabStop.append(rTabStop[0].GetTabPos());
3222 void RtfAttributeOutput::ParaHyphenZone(const SvxHyphenZoneItem& rHyphenZone)
3224 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_HYPHPAR);
3225 m_aStyles.append(sal_Int32(rHyphenZone.IsHyphen()));
3228 void RtfAttributeOutput::ParaNumRule_Impl(const SwTextNode* pTextNd, sal_Int32 nLvl,
3229 sal_Int32 nNumId)
3231 if (USHRT_MAX == nNumId || 0 == nNumId || nullptr == pTextNd)
3232 return;
3234 const SwNumRule* pRule = pTextNd->GetNumRule();
3236 if (!pRule || !pTextNd->IsInList())
3237 return;
3239 SAL_WARN_IF(pTextNd->GetActualListLevel() < 0 || pTextNd->GetActualListLevel() >= MAXLEVEL,
3240 "sw.rtf", "text node does not have valid list level");
3242 const SwNumFormat* pFormat = pRule->GetNumFormat(nLvl);
3243 if (!pFormat)
3244 pFormat = &pRule->Get(nLvl);
3246 const SfxItemSet& rNdSet = pTextNd->GetSwAttrSet();
3248 m_aStyles.append('{');
3249 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LISTTEXT);
3250 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_PARD);
3251 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_PLAIN);
3252 m_aStyles.append(' ');
3254 SvxFirstLineIndentItem firstLine(rNdSet.Get(RES_MARGIN_FIRSTLINE));
3255 SvxTextLeftMarginItem leftMargin(rNdSet.Get(RES_MARGIN_TEXTLEFT));
3256 leftMargin.SetTextLeft(
3257 SvxIndentValue::twips(leftMargin.ResolveTextLeft({}) + pFormat->GetIndentAt()));
3259 firstLine.SetTextFirstLineOffset(SvxIndentValue{
3260 static_cast<double>(pFormat->GetFirstLineOffset()), pFormat->GetFirstLineOffsetUnit() });
3262 sal_uInt16 nStyle = m_rExport.GetId(pFormat->GetCharFormat());
3263 OString* pString = m_rExport.GetStyle(nStyle);
3264 if (pString)
3265 m_aStyles.append(*pString);
3268 OUString sText;
3269 if (SVX_NUM_CHAR_SPECIAL == pFormat->GetNumberingType()
3270 || SVX_NUM_BITMAP == pFormat->GetNumberingType())
3272 sal_UCS4 cBullet = pFormat->GetBulletChar();
3273 sText = OUString(&cBullet, 1);
3275 else
3276 sText = pTextNd->GetNumString();
3278 if (!sText.isEmpty())
3280 m_aStyles.append(' ');
3281 m_aStyles.append(msfilter::rtfutil::OutString(sText, m_rExport.GetDefaultEncoding()));
3284 if (OUTLINE_RULE != pRule->GetRuleType())
3286 if (!sText.isEmpty())
3287 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_TAB);
3288 m_aStyles.append('}');
3289 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ILVL);
3290 if (nLvl > 8) // RTF knows only 9 levels
3292 m_aStyles.append(sal_Int32(8));
3293 m_aStyles.append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_SOUTLVL);
3294 m_aStyles.append(nLvl);
3295 m_aStyles.append('}');
3297 else
3298 m_aStyles.append(nLvl);
3300 else
3301 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_TAB "}");
3302 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LS);
3303 m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetNumberingId(*pRule)) + 1);
3304 m_aStyles.append(' ');
3306 FormatFirstLineIndent(firstLine);
3307 FormatTextLeftMargin(leftMargin);
3310 void RtfAttributeOutput::ParaScriptSpace(const SfxBoolItem& rScriptSpace)
3312 if (!rScriptSpace.GetValue())
3313 return;
3315 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ASPALPHA);
3318 void RtfAttributeOutput::ParaHangingPunctuation(const SfxBoolItem& /*rItem*/)
3320 SAL_INFO("sw.rtf", "TODO: " << __func__);
3323 void RtfAttributeOutput::ParaForbiddenRules(const SfxBoolItem& /*rItem*/)
3325 SAL_INFO("sw.rtf", "TODO: " << __func__);
3328 void RtfAttributeOutput::ParaVerticalAlign(const SvxParaVertAlignItem& rAlign)
3330 const char* pStr;
3331 switch (rAlign.GetValue())
3333 case SvxParaVertAlignItem::Align::Top:
3334 pStr = OOO_STRING_SVTOOLS_RTF_FAHANG;
3335 break;
3336 case SvxParaVertAlignItem::Align::Bottom:
3337 pStr = OOO_STRING_SVTOOLS_RTF_FAVAR;
3338 break;
3339 case SvxParaVertAlignItem::Align::Center:
3340 pStr = OOO_STRING_SVTOOLS_RTF_FACENTER;
3341 break;
3342 case SvxParaVertAlignItem::Align::Baseline:
3343 pStr = OOO_STRING_SVTOOLS_RTF_FAROMAN;
3344 break;
3346 default:
3347 pStr = OOO_STRING_SVTOOLS_RTF_FAAUTO;
3348 break;
3350 m_aStyles.append(pStr);
3353 void RtfAttributeOutput::ParaSnapToGrid(const SvxParaGridItem& /*rGrid*/)
3355 SAL_INFO("sw.rtf", "TODO: " << __func__);
3358 void RtfAttributeOutput::FormatFrameSize(const SwFormatFrameSize& rSize)
3360 if (m_rExport.m_bOutPageDescs)
3362 m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_PGWSXN);
3363 m_aSectionBreaks.append(static_cast<sal_Int32>(rSize.GetWidth()));
3364 m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_PGHSXN);
3365 m_aSectionBreaks.append(static_cast<sal_Int32>(rSize.GetHeight()));
3366 if (!m_bBufferSectionBreaks)
3368 m_rExport.Strm().WriteOString(m_aSectionBreaks);
3369 m_aSectionBreaks.setLength(0);
3374 void RtfAttributeOutput::FormatPaperBin(const SvxPaperBinItem& rItem)
3376 SfxPrinter* pPrinter = m_rExport.m_rDoc.getIDocumentDeviceAccess().getPrinter(true);
3377 sal_Int16 nPaperSource = pPrinter->GetSourceIndexByPaperBin(rItem.GetValue());
3378 m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_BINFSXN);
3379 m_aSectionBreaks.append(static_cast<sal_Int32>(nPaperSource));
3380 m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_BINSXN);
3381 m_aSectionBreaks.append(static_cast<sal_Int32>(nPaperSource));
3384 void RtfAttributeOutput::FormatFirstLineIndent(SvxFirstLineIndentItem const& rFirstLine)
3386 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_FI);
3387 m_aStyles.append(rFirstLine.ResolveTextFirstLineOffset({}));
3390 void RtfAttributeOutput::FormatTextLeftMargin(SvxTextLeftMarginItem const& rTextLeftMargin)
3392 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LI);
3393 m_aStyles.append(rTextLeftMargin.ResolveTextLeft({}));
3394 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LIN);
3395 m_aStyles.append(rTextLeftMargin.ResolveTextLeft({}));
3398 void RtfAttributeOutput::FormatRightMargin(SvxRightMarginItem const& rRightMargin)
3400 // (paragraph case, this will be an else branch once others are converted)
3401 #if 0
3402 else
3403 #endif
3405 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_RI);
3406 m_aStyles.append(rRightMargin.ResolveRight({}));
3407 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_RIN);
3408 m_aStyles.append(rRightMargin.ResolveRight({}));
3412 void RtfAttributeOutput::FormatLRSpace(const SvxLRSpaceItem& rLRSpace)
3414 if (!m_rExport.m_bOutFlyFrameAttrs)
3416 if (m_rExport.m_bOutPageDescs)
3418 m_aPageMargins.nLeft = 0;
3419 m_aPageMargins.nRight = 0;
3421 if (const SvxBoxItem* pBoxItem = m_rExport.HasItem(RES_BOX))
3423 m_aPageMargins.nLeft
3424 = pBoxItem->CalcLineSpace(SvxBoxItemLine::LEFT, /*bEvenIfNoLine*/ true);
3425 m_aPageMargins.nRight
3426 = pBoxItem->CalcLineSpace(SvxBoxItemLine::RIGHT, /*bEvenIfNoLine*/ true);
3429 m_aPageMargins.nLeft += sal::static_int_cast<sal_uInt16>(rLRSpace.ResolveLeft({}));
3430 m_aPageMargins.nRight += sal::static_int_cast<sal_uInt16>(rLRSpace.ResolveRight({}));
3432 if (rLRSpace.ResolveLeft({}))
3434 m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_MARGLSXN);
3435 m_aSectionBreaks.append(static_cast<sal_Int32>(m_aPageMargins.nLeft));
3437 if (rLRSpace.ResolveRight({}))
3439 m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_MARGRSXN);
3440 m_aSectionBreaks.append(static_cast<sal_Int32>(m_aPageMargins.nRight));
3442 if (rLRSpace.GetGutterMargin())
3444 m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_GUTTER);
3445 m_aSectionBreaks.append(static_cast<sal_Int32>(rLRSpace.GetGutterMargin()));
3447 if (!m_bBufferSectionBreaks)
3449 m_rExport.Strm().WriteOString(m_aSectionBreaks);
3450 m_aSectionBreaks.setLength(0);
3453 else
3455 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LI);
3456 m_aStyles.append(rLRSpace.ResolveTextLeft({}));
3457 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_RI);
3458 m_aStyles.append(rLRSpace.ResolveRight({}));
3459 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LIN);
3460 m_aStyles.append(rLRSpace.ResolveTextLeft({}));
3461 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_RIN);
3462 m_aStyles.append(rLRSpace.ResolveRight({}));
3463 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_FI);
3464 m_aStyles.append(rLRSpace.ResolveTextFirstLineOffset({}));
3467 else if (m_rExport.GetRTFFlySyntax())
3469 // Wrap: top and bottom spacing, convert from twips to EMUs.
3470 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3471 "dxWrapDistLeft"_ostr,
3472 OString::number(
3473 o3tl::convert(rLRSpace.ResolveLeft({}), o3tl::Length::twip, o3tl::Length::emu))));
3474 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3475 "dxWrapDistRight"_ostr,
3476 OString::number(
3477 o3tl::convert(rLRSpace.ResolveRight({}), o3tl::Length::twip, o3tl::Length::emu))));
3481 void RtfAttributeOutput::FormatULSpace(const SvxULSpaceItem& rULSpace)
3483 if (!m_rExport.m_bOutFlyFrameAttrs)
3485 if (m_rExport.m_bOutPageDescs)
3487 OSL_ENSURE(m_rExport.GetCurItemSet(), "Impossible");
3488 if (!m_rExport.GetCurItemSet())
3489 return;
3491 // If we export a follow page format, then our doc model has
3492 // separate header/footer distances for the first page and the
3493 // follow pages, but Word can have only a single distance. In case
3494 // the two values differ, work with the value from the first page
3495 // format to be in sync with the import.
3496 sw::util::HdFtDistanceGlue aDistances(m_rExport.GetFirstPageItemSet()
3497 ? *m_rExport.GetFirstPageItemSet()
3498 : *m_rExport.GetCurItemSet());
3500 if (aDistances.m_DyaTop)
3502 m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_MARGTSXN);
3503 m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.m_DyaTop));
3504 m_aPageMargins.nTop = aDistances.m_DyaTop;
3506 if (aDistances.HasHeader())
3508 m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_HEADERY);
3509 m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.m_DyaHdrTop));
3512 if (aDistances.m_DyaBottom)
3514 m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_MARGBSXN);
3515 m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.m_DyaBottom));
3516 m_aPageMargins.nBottom = aDistances.m_DyaBottom;
3518 if (aDistances.HasFooter())
3520 m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_FOOTERY);
3521 m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.m_DyaHdrBottom));
3523 if (!m_bBufferSectionBreaks)
3525 m_rExport.Strm().WriteOString(m_aSectionBreaks);
3526 m_aSectionBreaks.setLength(0);
3529 else
3531 // Spacing before.
3532 if (m_bParaBeforeAutoSpacing && m_nParaBeforeSpacing == rULSpace.GetUpper())
3533 m_aStyles.append(LO_STRING_SVTOOLS_RTF_SBAUTO "1");
3534 else if (m_bParaBeforeAutoSpacing && m_nParaBeforeSpacing == -1)
3536 m_aStyles.append(LO_STRING_SVTOOLS_RTF_SBAUTO "0");
3537 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SB);
3538 m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetUpper()));
3540 else
3542 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SB);
3543 m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetUpper()));
3545 m_bParaBeforeAutoSpacing = false;
3547 // Spacing after.
3548 if (m_bParaAfterAutoSpacing && m_nParaAfterSpacing == rULSpace.GetLower())
3549 m_aStyles.append(LO_STRING_SVTOOLS_RTF_SAAUTO "1");
3550 else if (m_bParaAfterAutoSpacing && m_nParaAfterSpacing == -1)
3552 m_aStyles.append(LO_STRING_SVTOOLS_RTF_SAAUTO "0");
3553 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SA);
3554 m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetLower()));
3556 else
3558 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SA);
3559 m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetLower()));
3561 m_bParaAfterAutoSpacing = false;
3563 // Contextual spacing.
3564 if (rULSpace.GetContext())
3565 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CONTEXTUALSPACE);
3568 else if (m_rExport.GetRTFFlySyntax())
3570 // Wrap: top and bottom spacing, convert from twips to EMUs.
3571 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3572 "dyWrapDistTop"_ostr,
3573 OString::number(
3574 o3tl::convert(rULSpace.GetUpper(), o3tl::Length::twip, o3tl::Length::emu))));
3575 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3576 "dyWrapDistBottom"_ostr,
3577 OString::number(
3578 o3tl::convert(rULSpace.GetLower(), o3tl::Length::twip, o3tl::Length::emu))));
3582 void RtfAttributeOutput::FormatSurround(const SwFormatSurround& rSurround)
3584 if (m_rExport.m_bOutFlyFrameAttrs && !m_rExport.GetRTFFlySyntax())
3586 css::text::WrapTextMode eSurround = rSurround.GetSurround();
3587 bool bGold = css::text::WrapTextMode_DYNAMIC == eSurround;
3588 if (bGold)
3589 eSurround = css::text::WrapTextMode_PARALLEL;
3590 RTFSurround aMC(bGold, static_cast<sal_uInt8>(eSurround));
3591 m_aRunText->append(OOO_STRING_SVTOOLS_RTF_FLYMAINCNT);
3592 m_aRunText->append(static_cast<sal_Int32>(aMC.GetValue()));
3594 else if (m_rExport.m_bOutFlyFrameAttrs && m_rExport.GetRTFFlySyntax())
3596 // See DocxSdrExport::startDMLAnchorInline() for SwFormatSurround -> WR / WRK mappings.
3597 sal_Int32 nWr = -1;
3598 std::optional<sal_Int32> oWrk;
3599 switch (rSurround.GetValue())
3601 case css::text::WrapTextMode_NONE:
3602 nWr = 1; // top and bottom
3603 break;
3604 case css::text::WrapTextMode_THROUGH:
3605 nWr = 3; // none
3606 break;
3607 case css::text::WrapTextMode_PARALLEL:
3608 nWr = 2; // around
3609 oWrk = 0; // both sides
3610 break;
3611 case css::text::WrapTextMode_DYNAMIC:
3612 default:
3613 nWr = 2; // around
3614 oWrk = 3; // largest
3615 break;
3618 if (rSurround.IsContour())
3619 nWr = 4; // tight
3621 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SHPWR);
3622 m_rExport.Strm().WriteNumberAsString(nWr);
3623 if (oWrk)
3625 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SHPWRK);
3626 m_rExport.Strm().WriteNumberAsString(*oWrk);
3631 void RtfAttributeOutput::FormatVertOrientation(const SwFormatVertOrient& rFlyVert)
3633 if (!(m_rExport.m_bOutFlyFrameAttrs && m_rExport.GetRTFFlySyntax()))
3634 return;
3636 switch (rFlyVert.GetRelationOrient())
3638 case text::RelOrientation::PAGE_FRAME:
3639 m_aFlyProperties.push_back(
3640 std::make_pair<OString, OString>("posrelv"_ostr, OString::number(1)));
3641 break;
3642 default:
3643 m_aFlyProperties.push_back(
3644 std::make_pair<OString, OString>("posrelv"_ostr, OString::number(2)));
3645 m_rExport.Strm()
3646 .WriteOString(OOO_STRING_SVTOOLS_RTF_SHPBYPARA)
3647 .WriteOString(OOO_STRING_SVTOOLS_RTF_SHPBYIGNORE);
3648 break;
3651 switch (rFlyVert.GetVertOrient())
3653 case text::VertOrientation::TOP:
3654 case text::VertOrientation::LINE_TOP:
3655 m_aFlyProperties.push_back(
3656 std::make_pair<OString, OString>("posv"_ostr, OString::number(1)));
3657 break;
3658 case text::VertOrientation::BOTTOM:
3659 case text::VertOrientation::LINE_BOTTOM:
3660 m_aFlyProperties.push_back(
3661 std::make_pair<OString, OString>("posv"_ostr, OString::number(3)));
3662 break;
3663 case text::VertOrientation::CENTER:
3664 case text::VertOrientation::LINE_CENTER:
3665 m_aFlyProperties.push_back(
3666 std::make_pair<OString, OString>("posv"_ostr, OString::number(2)));
3667 break;
3668 default:
3669 break;
3672 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SHPTOP);
3673 m_rExport.Strm().WriteNumberAsString(rFlyVert.GetPos());
3674 if (m_pFlyFrameSize)
3676 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SHPBOTTOM);
3677 m_rExport.Strm().WriteNumberAsString(rFlyVert.GetPos() + m_pFlyFrameSize->Height());
3681 void RtfAttributeOutput::FormatHorizOrientation(const SwFormatHoriOrient& rFlyHori)
3683 if (!(m_rExport.m_bOutFlyFrameAttrs && m_rExport.GetRTFFlySyntax()))
3684 return;
3686 switch (rFlyHori.GetRelationOrient())
3688 case text::RelOrientation::PAGE_FRAME:
3689 m_aFlyProperties.push_back(
3690 std::make_pair<OString, OString>("posrelh"_ostr, OString::number(1)));
3691 break;
3692 default:
3693 m_aFlyProperties.push_back(
3694 std::make_pair<OString, OString>("posrelh"_ostr, OString::number(2)));
3695 m_rExport.Strm()
3696 .WriteOString(OOO_STRING_SVTOOLS_RTF_SHPBXCOLUMN)
3697 .WriteOString(OOO_STRING_SVTOOLS_RTF_SHPBXIGNORE);
3698 break;
3701 switch (rFlyHori.GetHoriOrient())
3703 case text::HoriOrientation::LEFT:
3704 m_aFlyProperties.push_back(
3705 std::make_pair<OString, OString>("posh"_ostr, OString::number(1)));
3706 break;
3707 case text::HoriOrientation::CENTER:
3708 m_aFlyProperties.push_back(
3709 std::make_pair<OString, OString>("posh"_ostr, OString::number(2)));
3710 break;
3711 case text::HoriOrientation::RIGHT:
3712 m_aFlyProperties.push_back(
3713 std::make_pair<OString, OString>("posh"_ostr, OString::number(3)));
3714 break;
3715 default:
3716 break;
3719 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SHPLEFT);
3720 m_rExport.Strm().WriteNumberAsString(rFlyHori.GetPos());
3721 if (m_pFlyFrameSize)
3723 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SHPRIGHT);
3724 m_rExport.Strm().WriteNumberAsString(rFlyHori.GetPos() + m_pFlyFrameSize->Width());
3728 void RtfAttributeOutput::FormatAnchor(const SwFormatAnchor& rAnchor)
3730 if (m_rExport.GetRTFFlySyntax())
3731 return;
3733 RndStdIds eId = rAnchor.GetAnchorId();
3734 m_aRunText->append(OOO_STRING_SVTOOLS_RTF_FLYANCHOR);
3735 m_aRunText->append(static_cast<sal_Int32>(eId));
3736 switch (eId)
3738 case RndStdIds::FLY_AT_PAGE:
3739 m_aRunText->append(OOO_STRING_SVTOOLS_RTF_FLYPAGE);
3740 m_aRunText->append(static_cast<sal_Int32>(rAnchor.GetPageNum()));
3741 break;
3742 case RndStdIds::FLY_AT_PARA:
3743 case RndStdIds::FLY_AS_CHAR:
3744 m_aRunText->append(OOO_STRING_SVTOOLS_RTF_FLYCNTNT);
3745 break;
3746 default:
3747 break;
3751 void RtfAttributeOutput::FormatBackground(const SvxBrushItem& rBrush)
3753 if (m_rExport.GetRTFFlySyntax())
3755 const Color& rColor = rBrush.GetColor();
3756 // We in fact need RGB to BGR, but the transformation is symmetric.
3757 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3758 "fillColor"_ostr, OString::number(wwUtility::RGBToBGR(rColor))));
3760 else if (!rBrush.GetColor().IsTransparent())
3762 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CBPAT);
3763 m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(rBrush.GetColor())));
3767 void RtfAttributeOutput::FormatFillStyle(const XFillStyleItem& rFillStyle)
3769 m_oFillStyle = rFillStyle.GetValue();
3772 void RtfAttributeOutput::FormatFillGradient(const XFillGradientItem& rFillGradient)
3774 assert(m_oFillStyle && "ITEM: FormatFillStyle *has* to be called before FormatFillGradient(!)");
3775 if (*m_oFillStyle != drawing::FillStyle_GRADIENT)
3776 return;
3778 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3779 "fillType"_ostr, OString::number(7))); // Shade using the fillAngle
3781 const basegfx::BGradient& rGradient(rFillGradient.GetGradientValue());
3782 const basegfx::BColorStops& rColorStops(rGradient.GetColorStops());
3784 // MCGR: It would be best to export the full MCGR definition here
3785 // with all ColorStops in rColorStops, but rtf does not support this.
3786 // Best thing to do and to stay compatible is to export front/back
3787 // colors as start/end and - when more than two ColorStops are defined -
3788 // guess that GradientStyle_AXIAL is used and thus create a "fillFocus"
3789 // entry
3791 // LO does linear gradients top to bottom, while MSO does bottom to top.
3792 // LO does axial gradients inner to outer, while MSO does outer to inner.
3793 // Conclusion: swap start and end colors (and stop emulating this with 180deg rotations).
3794 const Color aMSOStartColor(rColorStops.back().getStopColor());
3795 Color aMSOEndColor(rColorStops.front().getStopColor());
3797 const sal_Int32 nAngle = toDegrees(rGradient.GetAngle()) * oox::drawingml::PER_DEGREE;
3798 if (nAngle != 0)
3800 m_aFlyProperties.push_back(
3801 std::make_pair<OString, OString>("fillAngle"_ostr, OString::number(nAngle)));
3804 bool bIsSymmetrical = true;
3805 if (rColorStops.size() < 3)
3807 if (rGradient.GetGradientStyle() != awt::GradientStyle_AXIAL)
3808 bIsSymmetrical = false;
3810 else
3812 // assume what was formally GradientStyle_AXIAL, see above and also refer to
3813 // FillModel::pushToPropMap 'fFocus' value and usage.
3814 // The 2nd color is the in-between color, use it
3815 aMSOEndColor = Color(rColorStops[1].getStopColor());
3818 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3819 "fillColor"_ostr, OString::number(wwUtility::RGBToBGR(aMSOStartColor))));
3820 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3821 "fillBackColor"_ostr, OString::number(wwUtility::RGBToBGR(aMSOEndColor))));
3823 if (bIsSymmetrical)
3825 m_aFlyProperties.push_back(
3826 std::make_pair<OString, OString>("fillFocus"_ostr, OString::number(50)));
3830 void RtfAttributeOutput::FormatBox(const SvxBoxItem& rBox)
3832 static const SvxBoxItemLine aBorders[] = { SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
3833 SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT };
3834 static const char* aBorderNames[]
3835 = { OOO_STRING_SVTOOLS_RTF_BRDRT, OOO_STRING_SVTOOLS_RTF_BRDRL,
3836 OOO_STRING_SVTOOLS_RTF_BRDRB, OOO_STRING_SVTOOLS_RTF_BRDRR };
3838 sal_uInt16 const nDist = rBox.GetSmallestDistance();
3840 if (m_rExport.GetRTFFlySyntax())
3842 // Borders: spacing to contents, convert from twips to EMUs.
3843 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3844 "dxTextLeft"_ostr,
3845 OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::LEFT),
3846 o3tl::Length::twip, o3tl::Length::emu))));
3847 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3848 "dyTextTop"_ostr,
3849 OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::TOP), o3tl::Length::twip,
3850 o3tl::Length::emu))));
3851 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3852 "dxTextRight"_ostr,
3853 OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::RIGHT),
3854 o3tl::Length::twip, o3tl::Length::emu))));
3855 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3856 "dyTextBottom"_ostr,
3857 OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::BOTTOM),
3858 o3tl::Length::twip, o3tl::Length::emu))));
3860 const editeng::SvxBorderLine* pLeft = rBox.GetLine(SvxBoxItemLine::LEFT);
3861 const editeng::SvxBorderLine* pRight = rBox.GetLine(SvxBoxItemLine::RIGHT);
3862 const editeng::SvxBorderLine* pTop = rBox.GetLine(SvxBoxItemLine::TOP);
3863 const editeng::SvxBorderLine* pBottom = rBox.GetLine(SvxBoxItemLine::BOTTOM);
3865 if (!pLeft && !pRight && !pBottom && !pTop)
3867 // fLine has default 'true', so need to write it out in case of no border.
3868 m_aFlyProperties.push_back(std::make_pair<OString, OString>("fLine"_ostr, "0"_ostr));
3869 return;
3872 // RTF has the flags fTopLine, fBottomLine, fLeftLine and fRightLine to disable single border
3873 // lines. But Word cannot disable single border lines. So we do not use them. In case of
3874 // single border lines it is better to draw all four borders than drawing none. So we look
3875 // whether a border line exists, which is effectively drawn.
3876 const editeng::SvxBorderLine* pBorder = nullptr;
3877 if (pTop && pTop->GetBorderLineStyle() != SvxBorderLineStyle::NONE)
3878 pBorder = pTop;
3879 else if (pBottom && pBottom->GetBorderLineStyle() != SvxBorderLineStyle::NONE)
3880 pBorder = pBottom;
3881 else if (pLeft && pLeft->GetBorderLineStyle() != SvxBorderLineStyle::NONE)
3882 pBorder = pLeft;
3883 else if (pRight && pRight->GetBorderLineStyle() != SvxBorderLineStyle::NONE)
3884 pBorder = pRight;
3886 if (!pBorder)
3888 m_aFlyProperties.push_back(std::make_pair<OString, OString>("fLine"_ostr, "0"_ostr));
3889 return;
3892 const Color& rColor = pBorder->GetColor();
3893 // We in fact need RGB to BGR, but the transformation is symmetric.
3894 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3895 "lineColor"_ostr, OString::number(wwUtility::RGBToBGR(rColor))));
3897 double const fConverted(
3898 editeng::ConvertBorderWidthToWord(pBorder->GetBorderLineStyle(), pBorder->GetWidth()));
3899 sal_Int32 nWidth = o3tl::convert(fConverted, o3tl::Length::twip, o3tl::Length::emu);
3900 m_aFlyProperties.push_back(
3901 std::make_pair<OString, OString>("lineWidth"_ostr, OString::number(nWidth)));
3903 return;
3906 if (rBox.GetTop() && rBox.GetBottom() && rBox.GetLeft() && rBox.GetRight()
3907 && *rBox.GetTop() == *rBox.GetBottom() && *rBox.GetTop() == *rBox.GetLeft()
3908 && *rBox.GetTop() == *rBox.GetRight() && nDist == rBox.GetDistance(SvxBoxItemLine::TOP)
3909 && nDist == rBox.GetDistance(SvxBoxItemLine::LEFT)
3910 && nDist == rBox.GetDistance(SvxBoxItemLine::BOTTOM)
3911 && nDist == rBox.GetDistance(SvxBoxItemLine::RIGHT))
3912 m_aSectionBreaks.append(
3913 OutBorderLine(m_rExport, rBox.GetTop(), OOO_STRING_SVTOOLS_RTF_BOX, nDist));
3914 else
3916 SvxShadowLocation eShadowLocation = SvxShadowLocation::NONE;
3917 if (const SvxShadowItem* pItem = GetExport().HasItem(RES_SHADOW))
3918 eShadowLocation = pItem->GetLocation();
3920 const SvxBoxItemLine* pBrd = aBorders;
3921 const char** pBrdNms = aBorderNames;
3922 for (int i = 0; i < 4; ++i, ++pBrd, ++pBrdNms)
3924 editeng::SvxBorderLine const* const pLn = rBox.GetLine(*pBrd);
3925 m_aSectionBreaks.append(
3926 OutBorderLine(m_rExport, pLn, *pBrdNms, rBox.GetDistance(*pBrd), eShadowLocation));
3930 if (!m_bBufferSectionBreaks)
3932 m_aStyles.append(m_aSectionBreaks);
3933 m_aSectionBreaks.setLength(0);
3937 void RtfAttributeOutput::FormatColumns_Impl(sal_uInt16 nCols, const SwFormatCol& rCol, bool bEven,
3938 SwTwips nPageSize)
3940 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_COLS);
3941 m_rExport.Strm().WriteNumberAsString(nCols);
3943 if (rCol.GetLineAdj() != COLADJ_NONE)
3944 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LINEBETCOL);
3946 if (bEven)
3948 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_COLSX);
3949 m_rExport.Strm().WriteNumberAsString(rCol.GetGutterWidth(true));
3951 else
3953 const SwColumns& rColumns = rCol.GetColumns();
3954 for (sal_uInt16 n = 0; n < nCols;)
3956 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_COLNO);
3957 m_rExport.Strm().WriteNumberAsString(n + 1);
3959 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_COLW);
3960 m_rExport.Strm().WriteNumberAsString(rCol.CalcPrtColWidth(n, nPageSize));
3962 if (++n != nCols)
3964 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_COLSR);
3965 m_rExport.Strm().WriteNumberAsString(rColumns[n - 1].GetRight()
3966 + rColumns[n].GetLeft());
3972 void RtfAttributeOutput::FormatKeep(const SvxFormatKeepItem& rItem)
3974 if (rItem.GetValue())
3975 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_KEEPN);
3978 void RtfAttributeOutput::FormatTextGrid(const SwTextGridItem& /*rGrid*/)
3980 SAL_INFO("sw.rtf", "TODO: " << __func__);
3983 void RtfAttributeOutput::FormatLineNumbering(const SwFormatLineNumber& rNumbering)
3985 if (!rNumbering.IsCount())
3986 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_NOLINE);
3989 void RtfAttributeOutput::FormatFrameDirection(const SvxFrameDirectionItem& rDirection)
3991 SvxFrameDirection nDir = rDirection.GetValue();
3992 if (nDir == SvxFrameDirection::Environment)
3993 nDir = GetExport().GetDefaultFrameDirection();
3995 if (m_rExport.m_bOutPageDescs)
3997 if (nDir == SvxFrameDirection::Vertical_RL_TB)
3999 m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_STEXTFLOW);
4000 m_aSectionBreaks.append(static_cast<sal_Int32>(1));
4001 if (!m_bBufferSectionBreaks)
4003 m_rExport.Strm().WriteOString(m_aSectionBreaks);
4004 m_aSectionBreaks.setLength(0);
4007 return;
4010 if (m_rExport.GetRTFFlySyntax())
4012 if (nDir == SvxFrameDirection::Vertical_RL_TB)
4014 // Top to bottom non-ASCII font
4015 m_aFlyProperties.push_back(
4016 std::make_pair<OString, OString>("txflTextFlow"_ostr, "3"_ostr));
4018 else if (rDirection.GetValue() == SvxFrameDirection::Vertical_LR_BT)
4020 // Bottom to top non-ASCII font
4021 m_aFlyProperties.push_back(
4022 std::make_pair<OString, OString>("txflTextFlow"_ostr, "2"_ostr));
4024 return;
4027 if (nDir == SvxFrameDirection::Horizontal_RL_TB)
4028 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_RTLPAR);
4029 else
4030 m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LTRPAR);
4033 void RtfAttributeOutput::ParaGrabBag(const SfxGrabBagItem& rItem)
4035 const std::map<OUString, css::uno::Any>& rMap = rItem.GetGrabBag();
4036 for (const auto& rValue : rMap)
4038 if (rValue.first == "ParaTopMarginBeforeAutoSpacing")
4040 m_bParaBeforeAutoSpacing = true;
4041 rValue.second >>= m_nParaBeforeSpacing;
4042 m_nParaBeforeSpacing = o3tl::toTwips(m_nParaBeforeSpacing, o3tl::Length::mm100);
4044 else if (rValue.first == "ParaBottomMarginAfterAutoSpacing")
4046 m_bParaAfterAutoSpacing = true;
4047 rValue.second >>= m_nParaAfterSpacing;
4048 m_nParaAfterSpacing = o3tl::toTwips(m_nParaAfterSpacing, o3tl::Length::mm100);
4053 void RtfAttributeOutput::CharGrabBag(const SfxGrabBagItem& /*rItem*/) {}
4055 void RtfAttributeOutput::ParaOutlineLevel(const SfxUInt16Item& /*rItem*/) {}
4057 void RtfAttributeOutput::WriteExpand(const SwField* pField)
4059 OUString sCmd; // for optional Parameters
4060 switch (pField->GetTyp()->Which())
4062 //#i119803# Export user field for RTF filter
4063 case SwFieldIds::User:
4064 sCmd = pField->GetTyp()->GetName();
4065 m_rExport.OutputField(pField, ww::eNONE, sCmd);
4066 break;
4067 default:
4068 m_rExport.OutputField(pField, ww::eUNKNOWN, sCmd);
4069 break;
4073 void RtfAttributeOutput::RefField(const SwField& /*rField*/, const OUString& /*rRef*/)
4075 SAL_INFO("sw.rtf", "TODO: " << __func__);
4078 void RtfAttributeOutput::HiddenField(const SwField& /*rField*/)
4080 SAL_INFO("sw.rtf", "TODO: " << __func__);
4083 void RtfAttributeOutput::SetField(const SwField& /*rField*/, ww::eField /*eType*/,
4084 const OUString& /*rCmd*/)
4086 SAL_INFO("sw.rtf", "TODO: " << __func__);
4089 void RtfAttributeOutput::PostitField(const SwField* pField)
4091 bool bRemoveCommentAuthorDates
4092 = SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo)
4093 && !SvtSecurityOptions::IsOptionSet(
4094 SvtSecurityOptions::EOption::DocWarnKeepNoteAuthorDateInfo);
4096 const SwPostItField& rPField = *static_cast<const SwPostItField*>(pField);
4098 OString aName = OUStringToOString(rPField.GetName(), RTL_TEXTENCODING_UTF8);
4099 auto it = m_rOpenedAnnotationMarksIds.find(aName);
4100 if (it != m_rOpenedAnnotationMarksIds.end())
4102 // In case this field is inside annotation marks, we want to write the
4103 // annotation itself after the annotation mark is closed, not here.
4104 m_aPostitFields[it->second] = &rPField;
4105 return;
4107 OUString sAuthor(bRemoveCommentAuthorDates
4108 ? "Author" + OUString::number(m_rExport.GetInfoID(rPField.GetPar1()))
4109 : rPField.GetPar1());
4110 OUString sInitials(bRemoveCommentAuthorDates
4111 ? "A" + OUString::number(m_rExport.GetInfoID(rPField.GetPar1()))
4112 : rPField.GetInitials());
4113 m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ATNID " ");
4114 m_aRunText->append(OUStringToOString(sInitials, m_rExport.GetCurrentEncoding()));
4115 m_aRunText->append("}");
4116 m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ATNAUTHOR " ");
4117 m_aRunText->append(OUStringToOString(sAuthor, m_rExport.GetCurrentEncoding()));
4118 m_aRunText->append("}");
4119 m_aRunText->append(OOO_STRING_SVTOOLS_RTF_CHATN);
4121 m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ANNOTATION);
4123 if (m_nCurrentAnnotationMarkId != -1)
4125 m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ATNREF " ");
4126 m_aRunText->append(m_nCurrentAnnotationMarkId);
4127 m_aRunText->append('}');
4129 if (!bRemoveCommentAuthorDates)
4131 m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ATNDATE " ");
4132 m_aRunText->append(static_cast<sal_Int32>(sw::ms::DateTime2DTTM(rPField.GetDateTime())));
4133 m_aRunText->append('}');
4135 if (const OutlinerParaObject* pObject = rPField.GetTextObject())
4136 m_rExport.SdrExporter().WriteOutliner(*pObject, TXT_ATN);
4137 m_aRunText->append('}');
4140 bool RtfAttributeOutput::DropdownField(const SwField* /*pField*/)
4142 // this is handled in OutputFlyFrame_Impl()
4143 return true;
4146 bool RtfAttributeOutput::PlaceholderField(const SwField* pField)
4148 m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_FIELD
4149 "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FLDINST
4150 " MACROBUTTON None ");
4151 RunText(pField->GetPar1());
4152 m_aRunText->append("}}");
4153 return false; // do not expand
4156 RtfAttributeOutput::RtfAttributeOutput(RtfExport& rExport)
4157 : AttributeOutputBase(u""_ustr) // ConvertURL isn't used now in RTF output
4158 , m_rExport(rExport)
4159 , m_pPrevPageDesc(nullptr)
4160 , m_nStyleId(0)
4161 , m_nListId(0)
4162 , m_bIsRTL(false)
4163 , m_nScript(i18n::ScriptType::LATIN)
4164 , m_bControlLtrRtl(false)
4165 , m_nNextAnnotationMarkId(0)
4166 , m_nCurrentAnnotationMarkId(-1)
4167 , m_bTableCellOpen(false)
4168 , m_nTableDepth(0)
4169 , m_bTableAfterCell(false)
4170 , m_nColBreakNeeded(false)
4171 , m_bBufferSectionBreaks(false)
4172 , m_bBufferSectionHeaders(false)
4173 , m_bLastTable(true)
4174 , m_bWroteCellInfo(false)
4175 , m_bTableRowEnded(false)
4176 , m_bIsBeforeFirstParagraph(true)
4177 , m_bSingleEmptyRun(false)
4178 , m_bInRun(false)
4179 , m_bInRuby(false)
4180 , m_pFlyFrameSize(nullptr)
4181 , m_bParaBeforeAutoSpacing(false)
4182 , m_nParaBeforeSpacing(0)
4183 , m_bParaAfterAutoSpacing(false)
4184 , m_nParaAfterSpacing(0)
4188 RtfAttributeOutput::~RtfAttributeOutput() = default;
4190 MSWordExportBase& RtfAttributeOutput::GetExport() { return m_rExport; }
4192 // These are used by wwFont::WriteRtf()
4194 /// Start the font.
4195 void RtfAttributeOutput::StartFont(std::u16string_view rFamilyName) const
4197 // write the font name hex-encoded, but without Unicode - Word at least
4198 // cannot read *both* Unicode and fallback as written by OutString
4199 m_rExport.Strm().WriteOString(
4200 msfilter::rtfutil::OutString(rFamilyName, m_rExport.GetCurrentEncoding(), false));
4203 /// End the font.
4204 void RtfAttributeOutput::EndFont() const
4206 m_rExport.Strm().WriteOString(";}");
4207 m_rExport.SetCurrentEncoding(m_rExport.GetDefaultEncoding());
4210 /// Alternate name for the font.
4211 void RtfAttributeOutput::FontAlternateName(std::u16string_view rName) const
4213 m_rExport.Strm()
4214 .WriteChar('{')
4215 .WriteOString(OOO_STRING_SVTOOLS_RTF_IGNORE)
4216 .WriteOString(OOO_STRING_SVTOOLS_RTF_FALT)
4217 .WriteChar(' ');
4218 // write the font name hex-encoded, but without Unicode - Word at least
4219 // cannot read *both* Unicode and fallback as written by OutString
4220 m_rExport.Strm()
4221 .WriteOString(msfilter::rtfutil::OutString(rName, m_rExport.GetCurrentEncoding(), false))
4222 .WriteChar('}');
4225 /// Font charset.
4226 void RtfAttributeOutput::FontCharset(sal_uInt8 nCharSet) const
4228 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_FCHARSET);
4229 m_rExport.Strm().WriteNumberAsString(nCharSet);
4230 m_rExport.Strm().WriteChar(' ');
4231 m_rExport.SetCurrentEncoding(rtl_getTextEncodingFromWindowsCharset(nCharSet));
4234 /// Font family.
4235 void RtfAttributeOutput::FontFamilyType(FontFamily eFamily, const wwFont& rFont) const
4237 m_rExport.Strm().WriteChar('{').WriteOString(OOO_STRING_SVTOOLS_RTF_F);
4239 const char* pStr = OOO_STRING_SVTOOLS_RTF_FNIL;
4240 switch (eFamily)
4242 case FAMILY_ROMAN:
4243 pStr = OOO_STRING_SVTOOLS_RTF_FROMAN;
4244 break;
4245 case FAMILY_SWISS:
4246 pStr = OOO_STRING_SVTOOLS_RTF_FSWISS;
4247 break;
4248 case FAMILY_MODERN:
4249 pStr = OOO_STRING_SVTOOLS_RTF_FMODERN;
4250 break;
4251 case FAMILY_SCRIPT:
4252 pStr = OOO_STRING_SVTOOLS_RTF_FSCRIPT;
4253 break;
4254 case FAMILY_DECORATIVE:
4255 pStr = OOO_STRING_SVTOOLS_RTF_FDECOR;
4256 break;
4257 default:
4258 break;
4260 m_rExport.Strm().WriteNumberAsString(m_rExport.m_aFontHelper.GetId(rFont)).WriteOString(pStr);
4263 /// Font pitch.
4264 void RtfAttributeOutput::FontPitchType(FontPitch ePitch) const
4266 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_FPRQ);
4268 sal_uInt16 nVal = 0;
4269 switch (ePitch)
4271 case PITCH_FIXED:
4272 nVal = 1;
4273 break;
4274 case PITCH_VARIABLE:
4275 nVal = 2;
4276 break;
4277 default:
4278 break;
4280 m_rExport.Strm().WriteNumberAsString(nVal);
4283 static void lcl_AppendSP(OStringBuffer& rBuffer, std::string_view cName, std::u16string_view rValue,
4284 const RtfExport& rExport)
4286 rBuffer.append("{" OOO_STRING_SVTOOLS_RTF_SP "{"); // "{\sp{"
4287 rBuffer.append(OOO_STRING_SVTOOLS_RTF_SN " "); //" \sn "
4288 rBuffer.append(cName); //"PropName"
4289 rBuffer.append("}{" OOO_STRING_SVTOOLS_RTF_SV " ");
4290 // "}{ \sv "
4291 rBuffer.append(msfilter::rtfutil::OutString(rValue, rExport.GetCurrentEncoding()));
4292 rBuffer.append("}}");
4295 static OString ExportPICT(const SwFlyFrameFormat* pFlyFrameFormat, const Size& rOrig,
4296 const Size& rRendered, const Size& rMapped, const SwCropGrf& rCr,
4297 const char* pBLIPType, const sal_uInt8* pGraphicAry, sal_uInt64 nSize,
4298 const RtfExport& rExport, SvStream* pStream = nullptr,
4299 bool bWritePicProp = true, const SwAttrSet* pAttrSet = nullptr)
4301 OStringBuffer aRet;
4302 if (pBLIPType && nSize && pGraphicAry)
4304 bool bIsWMF = std::strcmp(pBLIPType, OOO_STRING_SVTOOLS_RTF_WMETAFILE) == 0;
4306 aRet.append("{" OOO_STRING_SVTOOLS_RTF_PICT);
4308 if (pFlyFrameFormat && bWritePicProp)
4310 OUString sDescription = pFlyFrameFormat->GetObjDescription();
4311 //write picture properties - wzDescription at first
4312 //looks like: "{\*\picprop{\sp{\sn PropertyName}{\sv PropertyValue}}}"
4313 aRet.append(
4314 "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_PICPROP); //"{\*\picprop
4315 lcl_AppendSP(aRet, "wzDescription", sDescription, rExport);
4316 OUString sName = pFlyFrameFormat->GetObjTitle();
4317 lcl_AppendSP(aRet, "wzName", sName, rExport);
4319 if (pAttrSet)
4321 MirrorGraph eMirror = pAttrSet->Get(RES_GRFATR_MIRRORGRF).GetValue();
4322 if (eMirror == MirrorGraph::Vertical || eMirror == MirrorGraph::Both)
4323 // Mirror on the vertical axis is a horizontal flip.
4324 lcl_AppendSP(aRet, "fFlipH", u"1", rExport);
4327 aRet.append("}"); //"}"
4330 tools::Long nXCroppedSize = rOrig.Width() - (rCr.GetLeft() + rCr.GetRight());
4331 tools::Long nYCroppedSize = rOrig.Height() - (rCr.GetTop() + rCr.GetBottom());
4332 /* Graphic with a zero height or width, typically copied from webpages, caused crashes. */
4333 if (!nXCroppedSize)
4334 nXCroppedSize = 100;
4335 if (!nYCroppedSize)
4336 nYCroppedSize = 100;
4338 //Given the original size and taking cropping into account
4339 //first, how much has the original been scaled to get the
4340 //final rendered size
4341 aRet.append(
4342 OOO_STRING_SVTOOLS_RTF_PICSCALEX
4343 + OString::number(static_cast<sal_Int32>((100 * rRendered.Width()) / nXCroppedSize))
4344 + OOO_STRING_SVTOOLS_RTF_PICSCALEY
4345 + OString::number(static_cast<sal_Int32>((100 * rRendered.Height()) / nYCroppedSize))
4347 + OOO_STRING_SVTOOLS_RTF_PICCROPL + OString::number(rCr.GetLeft())
4348 + OOO_STRING_SVTOOLS_RTF_PICCROPR + OString::number(rCr.GetRight())
4349 + OOO_STRING_SVTOOLS_RTF_PICCROPT + OString::number(rCr.GetTop())
4350 + OOO_STRING_SVTOOLS_RTF_PICCROPB + OString::number(rCr.GetBottom())
4352 + OOO_STRING_SVTOOLS_RTF_PICW + OString::number(static_cast<sal_Int32>(rMapped.Width()))
4353 + OOO_STRING_SVTOOLS_RTF_PICH
4354 + OString::number(static_cast<sal_Int32>(rMapped.Height()))
4356 + OOO_STRING_SVTOOLS_RTF_PICWGOAL
4357 + OString::number(static_cast<sal_Int32>(rOrig.Width()))
4358 + OOO_STRING_SVTOOLS_RTF_PICHGOAL
4359 + OString::number(static_cast<sal_Int32>(rOrig.Height()))
4361 + pBLIPType);
4362 if (bIsWMF)
4364 aRet.append(sal_Int32(8));
4365 msfilter::rtfutil::StripMetafileHeader(pGraphicAry, nSize);
4367 aRet.append(SAL_NEWLINE_STRING);
4368 if (pStream)
4370 pStream->WriteOString(aRet);
4371 aRet.setLength(0);
4373 if (pStream)
4374 msfilter::rtfutil::WriteHex(pGraphicAry, nSize, pStream);
4375 else
4376 aRet.append(msfilter::rtfutil::WriteHex(pGraphicAry, nSize));
4377 aRet.append('}');
4378 if (pStream)
4380 pStream->WriteOString(aRet);
4381 aRet.setLength(0);
4384 return aRet.makeStringAndClear();
4387 void RtfAttributeOutput::FlyFrameOLEReplacement(const SwFlyFrameFormat* pFlyFrameFormat,
4388 SwOLENode& rOLENode, const Size& rSize)
4390 m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_SHPPICT);
4391 Size aSize(rOLENode.GetTwipSize());
4392 Size aRendered(aSize);
4393 aRendered.setWidth(rSize.Width());
4394 aRendered.setHeight(rSize.Height());
4395 const Graphic* pGraphic = rOLENode.GetGraphic();
4396 Graphic aGraphic = pGraphic ? *pGraphic : Graphic();
4397 Size aMapped(aGraphic.GetPrefSize());
4398 auto& rCr = rOLENode.GetAttr(RES_GRFATR_CROPGRF);
4399 const char* pBLIPType = OOO_STRING_SVTOOLS_RTF_PNGBLIP;
4400 const sal_uInt8* pGraphicAry = nullptr;
4401 SvMemoryStream aStream;
4402 if (GraphicConverter::Export(aStream, aGraphic, ConvertDataFormat::PNG) != ERRCODE_NONE)
4403 SAL_WARN("sw.rtf", "failed to export the graphic");
4404 sal_uInt64 nSize = aStream.TellEnd();
4405 pGraphicAry = static_cast<sal_uInt8 const*>(aStream.GetData());
4406 m_aRunText->append(ExportPICT(pFlyFrameFormat, aSize, aRendered, aMapped, rCr, pBLIPType,
4407 pGraphicAry, nSize, m_rExport));
4408 m_aRunText->append("}"); // shppict
4409 m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_NONSHPPICT);
4410 pBLIPType = OOO_STRING_SVTOOLS_RTF_WMETAFILE;
4411 SvMemoryStream aWmfStream;
4412 if (GraphicConverter::Export(aWmfStream, aGraphic, ConvertDataFormat::WMF) != ERRCODE_NONE)
4413 SAL_WARN("sw.rtf", "failed to export the graphic");
4414 nSize = aWmfStream.TellEnd();
4415 pGraphicAry = static_cast<sal_uInt8 const*>(aWmfStream.GetData());
4416 m_aRunText->append(ExportPICT(pFlyFrameFormat, aSize, aRendered, aMapped, rCr, pBLIPType,
4417 pGraphicAry, nSize, m_rExport));
4418 m_aRunText->append("}"); // nonshppict
4421 bool RtfAttributeOutput::FlyFrameOLEMath(const SwFlyFrameFormat* pFlyFrameFormat,
4422 SwOLENode& rOLENode, const Size& rSize)
4424 uno::Reference<embed::XEmbeddedObject> xObj(rOLENode.GetOLEObj().GetOleRef());
4425 sal_Int64 nAspect = rOLENode.GetAspect();
4426 svt::EmbeddedObjectRef aObjRef(xObj, nAspect);
4427 SvGlobalName aObjName(aObjRef->getClassID());
4429 if (!SotExchange::IsMath(aObjName))
4430 return false;
4432 m_aRunText->append("{" LO_STRING_SVTOOLS_RTF_MMATH " ");
4433 uno::Reference<util::XCloseable> xClosable = xObj->getComponent();
4434 auto pBase = dynamic_cast<oox::FormulaImExportBase*>(xClosable.get());
4435 SAL_WARN_IF(!pBase, "sw.rtf", "Math OLE object cannot write out RTF");
4436 if (pBase)
4438 OStringBuffer aBuf;
4439 pBase->writeFormulaRtf(aBuf, m_rExport.GetCurrentEncoding());
4440 m_aRunText->append(aBuf);
4443 // Replacement graphic.
4444 m_aRunText->append("{" LO_STRING_SVTOOLS_RTF_MMATHPICT " ");
4445 FlyFrameOLEReplacement(pFlyFrameFormat, rOLENode, rSize);
4446 m_aRunText->append("}"); // mmathPict
4447 m_aRunText->append("}"); // mmath
4449 return true;
4452 void RtfAttributeOutput::FlyFrameOLE(const SwFlyFrameFormat* pFlyFrameFormat, SwOLENode& rOLENode,
4453 const Size& rSize)
4455 if (FlyFrameOLEMath(pFlyFrameFormat, rOLENode, rSize))
4456 return;
4458 FlyFrameOLEReplacement(pFlyFrameFormat, rOLENode, rSize);
4461 void RtfAttributeOutput::FlyFrameGraphic(const SwFlyFrameFormat* pFlyFrameFormat,
4462 const SwGrfNode* pGrfNode)
4464 SvMemoryStream aStream;
4465 const sal_uInt8* pGraphicAry = nullptr;
4466 sal_uInt32 nSize = 0;
4468 const Graphic& rGraphic(pGrfNode->GetGrf());
4470 // If there is no graphic there is not much point in parsing it
4471 if (rGraphic.GetType() == GraphicType::NONE)
4472 return;
4474 ConvertDataFormat aConvertDestinationFormat = ConvertDataFormat::WMF;
4475 const char* pConvertDestinationBLIPType = OOO_STRING_SVTOOLS_RTF_WMETAFILE;
4477 GfxLink aGraphicLink;
4478 const char* pBLIPType = nullptr;
4479 if (rGraphic.IsGfxLink())
4481 aGraphicLink = rGraphic.GetGfxLink();
4482 nSize = aGraphicLink.GetDataSize();
4483 pGraphicAry = aGraphicLink.GetData();
4484 switch (aGraphicLink.GetType())
4486 // #i15508# trying to add BMP type for better exports, need to check if this works
4487 // checked, does not work. Also need to reset pGraphicAry to NULL to force conversion
4488 // to PNG, else the BMP array will be used.
4489 // It may work using direct DIB data, but that needs to be checked eventually
4491 // #i15508# before GfxLinkType::NativeBmp was added the graphic data
4492 // (to be hold in pGraphicAry) was not available; thus for now to stay
4493 // compatible, keep it that way by assigning NULL value to pGraphicAry
4494 case GfxLinkType::NativeBmp:
4495 // pBLIPType = OOO_STRING_SVTOOLS_RTF_WBITMAP;
4496 pGraphicAry = nullptr;
4497 break;
4499 case GfxLinkType::NativeJpg:
4500 pBLIPType = OOO_STRING_SVTOOLS_RTF_JPEGBLIP;
4501 break;
4502 case GfxLinkType::NativePng:
4503 pBLIPType = OOO_STRING_SVTOOLS_RTF_PNGBLIP;
4504 break;
4505 case GfxLinkType::NativeWmf:
4506 pBLIPType = aGraphicLink.IsEMF() ? OOO_STRING_SVTOOLS_RTF_EMFBLIP
4507 : OOO_STRING_SVTOOLS_RTF_WMETAFILE;
4508 break;
4509 case GfxLinkType::NativeGif:
4510 // GIF is not supported by RTF, but we override default conversion to WMF, PNG seems fits better here.
4511 aConvertDestinationFormat = ConvertDataFormat::PNG;
4512 pConvertDestinationBLIPType = OOO_STRING_SVTOOLS_RTF_PNGBLIP;
4513 break;
4514 default:
4515 break;
4519 GraphicType eGraphicType = rGraphic.GetType();
4520 if (!pGraphicAry)
4522 if (ERRCODE_NONE
4523 == GraphicConverter::Export(aStream, rGraphic,
4524 (eGraphicType == GraphicType::Bitmap)
4525 ? ConvertDataFormat::PNG
4526 : ConvertDataFormat::WMF))
4528 pBLIPType = (eGraphicType == GraphicType::Bitmap) ? OOO_STRING_SVTOOLS_RTF_PNGBLIP
4529 : OOO_STRING_SVTOOLS_RTF_WMETAFILE;
4530 nSize = aStream.TellEnd();
4531 pGraphicAry = static_cast<sal_uInt8 const*>(aStream.GetData());
4535 Size aMapped(eGraphicType == GraphicType::Bitmap ? rGraphic.GetSizePixel()
4536 : rGraphic.GetPrefSize());
4538 auto& rCr = pGrfNode->GetAttr(RES_GRFATR_CROPGRF);
4540 //Get original size in twips
4541 Size aSize(pGrfNode->GetTwipSize());
4542 Size aRendered(aSize);
4544 const SwFormatFrameSize& rS = pFlyFrameFormat->GetFrameSize();
4545 aRendered.setWidth(rS.GetWidth());
4546 aRendered.setHeight(rS.GetHeight());
4548 ww8::Frame* pFrame = nullptr;
4549 for (auto& rFrame : m_rExport.m_aFrames)
4551 if (pFlyFrameFormat == &rFrame.GetFrameFormat())
4553 pFrame = &rFrame;
4554 break;
4558 const SwAttrSet* pAttrSet = pGrfNode->GetpSwAttrSet();
4559 if (pFrame && !pFrame->IsInline())
4561 m_rExport.Strm().WriteOString(
4562 "{" OOO_STRING_SVTOOLS_RTF_SHP
4563 "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_SHPINST);
4564 m_pFlyFrameSize = &aRendered;
4565 m_rExport.m_pParentFrame = pFrame;
4566 m_rExport.m_bOutFlyFrameAttrs = true;
4567 m_rExport.SetRTFFlySyntax(true);
4568 m_rExport.OutputFormat(pFrame->GetFrameFormat(), false, false, true);
4569 m_rExport.m_bOutFlyFrameAttrs = false;
4570 m_rExport.SetRTFFlySyntax(false);
4571 m_rExport.m_pParentFrame = nullptr;
4572 m_pFlyFrameSize = nullptr;
4573 std::vector<std::pair<OString, OString>> aFlyProperties{
4574 { "shapeType", OString::number(ESCHER_ShpInst_PictureFrame) },
4576 { "wzDescription", msfilter::rtfutil::OutString(pFlyFrameFormat->GetObjDescription(),
4577 m_rExport.GetCurrentEncoding()) },
4578 { "wzName", msfilter::rtfutil::OutString(pFlyFrameFormat->GetObjTitle(),
4579 m_rExport.GetCurrentEncoding()) }
4582 // If we have a wrap polygon, then handle that here.
4583 if (pFlyFrameFormat->GetSurround().IsContour())
4585 if (const SwNoTextNode* pNd
4586 = sw::util::GetNoTextNodeFromSwFrameFormat(*pFlyFrameFormat))
4588 const tools::PolyPolygon* pPolyPoly = pNd->HasContour();
4589 if (pPolyPoly && pPolyPoly->Count())
4591 tools::Polygon aPoly = sw::util::CorrectWordWrapPolygonForExport(
4592 *pPolyPoly, pNd, /*bCorrectCrop=*/true);
4593 OStringBuffer aVerticies;
4594 for (sal_uInt16 i = 0; i < aPoly.GetSize(); ++i)
4595 aVerticies.append(";(" + OString::number(aPoly[i].X()) + ","
4596 + OString::number(aPoly[i].Y()) + ")");
4597 aFlyProperties.push_back(std::make_pair<OString, OString>(
4598 "pWrapPolygonVertices"_ostr,
4599 "8;" + OString::number(aPoly.GetSize()) + aVerticies));
4604 // Below text, behind document, opaque: they all refer to the same thing.
4605 if (!pFlyFrameFormat->GetOpaque().GetValue())
4606 aFlyProperties.push_back(
4607 std::make_pair<OString, OString>("fBehindDocument"_ostr, "1"_ostr));
4609 if (pAttrSet)
4611 if (Degree10 nRot10 = pAttrSet->Get(RES_GRFATR_ROTATION).GetValue())
4613 // See writerfilter::rtftok::RTFSdrImport::applyProperty(),
4614 // positive rotation angles are clockwise in RTF, we have them
4615 // as counter-clockwise.
4616 // Additionally, RTF type is 0..360*2^16, our is 0..360*10.
4617 sal_Int32 nRot = nRot10.get() * -1 * RTF_MULTIPLIER / 10;
4618 aFlyProperties.emplace_back("rotation", OString::number(nRot));
4622 for (const std::pair<OString, OString>& rPair : aFlyProperties)
4624 m_rExport.Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_SP "{");
4625 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SN " ");
4626 m_rExport.Strm().WriteOString(rPair.first);
4627 m_rExport.Strm().WriteOString("}{" OOO_STRING_SVTOOLS_RTF_SV " ");
4628 m_rExport.Strm().WriteOString(rPair.second);
4629 m_rExport.Strm().WriteOString("}}");
4631 m_rExport.Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_SP "{" OOO_STRING_SVTOOLS_RTF_SN
4632 " pib"
4633 "}{" OOO_STRING_SVTOOLS_RTF_SV " ");
4636 bool bWritePicProp = !pFrame || pFrame->IsInline();
4637 if (pBLIPType)
4638 ExportPICT(pFlyFrameFormat, aSize, aRendered, aMapped, rCr, pBLIPType, pGraphicAry, nSize,
4639 m_rExport, &m_rExport.Strm(), bWritePicProp, pAttrSet);
4640 else
4642 aStream.Seek(0);
4643 if (GraphicConverter::Export(aStream, rGraphic, aConvertDestinationFormat) != ERRCODE_NONE)
4644 SAL_WARN("sw.rtf", "failed to export the graphic");
4645 pBLIPType = pConvertDestinationBLIPType;
4646 nSize = aStream.TellEnd();
4647 pGraphicAry = static_cast<sal_uInt8 const*>(aStream.GetData());
4649 ExportPICT(pFlyFrameFormat, aSize, aRendered, aMapped, rCr, pBLIPType, pGraphicAry, nSize,
4650 m_rExport, &m_rExport.Strm(), bWritePicProp, pAttrSet);
4653 if (pFrame && !pFrame->IsInline())
4654 m_rExport.Strm().WriteOString("}}}}"); // Close SV, SP, SHPINST and SHP.
4656 m_rExport.Strm().WriteOString(SAL_NEWLINE_STRING);
4659 void RtfAttributeOutput::BulletDefinition(int /*nId*/, const Graphic& rGraphic, Size aSize)
4661 m_rExport.Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_SHPPICT);
4662 m_rExport.Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_PICT OOO_STRING_SVTOOLS_RTF_PNGBLIP);
4664 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_PICWGOAL);
4665 m_rExport.Strm().WriteNumberAsString(aSize.Width());
4666 m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_PICHGOAL);
4667 m_rExport.Strm().WriteNumberAsString(aSize.Height());
4669 m_rExport.Strm().WriteOString(SAL_NEWLINE_STRING);
4670 const sal_uInt8* pGraphicAry = nullptr;
4671 SvMemoryStream aStream;
4672 if (GraphicConverter::Export(aStream, rGraphic, ConvertDataFormat::PNG) != ERRCODE_NONE)
4673 SAL_WARN("sw.rtf", "failed to export the numbering picture bullet");
4674 sal_uInt64 nSize = aStream.TellEnd();
4675 pGraphicAry = static_cast<sal_uInt8 const*>(aStream.GetData());
4676 msfilter::rtfutil::WriteHex(pGraphicAry, nSize, &m_rExport.Strm());
4677 m_rExport.Strm().WriteOString("}}"); // pict, shppict
4680 void RtfAttributeOutput::SectionRtlGutter(const SfxBoolItem& rRtlGutter)
4682 if (!rRtlGutter.GetValue())
4684 return;
4687 m_rExport.Strm().WriteOString(LO_STRING_SVTOOLS_RTF_RTLGUTTER);
4690 void RtfAttributeOutput::TextLineBreak(const SwFormatLineBreak& rLineBreak)
4692 // Text wrapping break of type:
4693 m_aStyles.append(LO_STRING_SVTOOLS_RTF_LBR);
4694 m_aStyles.append(static_cast<sal_Int32>(rLineBreak.GetEnumValue()));
4696 // Write the linebreak itself.
4697 RunText(u"\x0b"_ustr);
4700 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */