LibreOffice Module sw (master) 1
rtfattributeoutput.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
21#include <memory>
22#include <cstring>
23#include "rtfsdrexport.hxx"
24#include "writerwordglue.hxx"
25#include "ww8par.hxx"
26#include <fmtcntnt.hxx>
27#include <rtl/tencinfo.h>
28#include <sal/log.hxx>
29#include <sot/exchange.hxx>
30#include <svtools/rtfkeywd.hxx>
31#include <editeng/fontitem.hxx>
32#include <editeng/tstpitem.hxx>
34#include <editeng/spltitem.hxx>
35#include <editeng/widwitem.hxx>
36#include <editeng/postitem.hxx>
37#include <editeng/wghtitem.hxx>
38#include <editeng/kernitem.hxx>
40#include <editeng/cmapitem.hxx>
41#include <editeng/wrlmitem.hxx>
42#include <editeng/udlnitem.hxx>
43#include <editeng/langitem.hxx>
45#include <editeng/fhgtitem.hxx>
46#include <editeng/colritem.hxx>
49#include <editeng/shdditem.hxx>
57#include <editeng/blinkitem.hxx>
59#include <editeng/boxitem.hxx>
60#include <editeng/brushitem.hxx>
61#include <editeng/ulspitem.hxx>
62#include <editeng/shaditem.hxx>
63#include <editeng/keepitem.hxx>
65#include <editeng/opaqitem.hxx>
67#include <svx/svdouno.hxx>
69#include <sfx2/sfxbasemodel.hxx>
70#include <svx/xfillit0.hxx>
71#include <svx/xflgrit.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>
90#include <vcl/cvtgrf.hxx>
91#include <oox/mathml/export.hxx>
92#include <com/sun/star/i18n/ScriptType.hpp>
93#include <svl/grabbagitem.hxx>
94#include <frmatr.hxx>
95#include <swtable.hxx>
96#include "rtfexport.hxx"
97
98using namespace ::com::sun::star;
99using namespace sw::util;
100
101static OString OutTBLBorderLine(RtfExport const& rExport, const editeng::SvxBorderLine* pLine,
102 const char* pStr)
103{
104 OStringBuffer aRet;
105 if (pLine && !pLine->isEmpty())
106 {
107 aRet.append(pStr);
108 // single line
109 switch (pLine->GetBorderLineStyle())
110 {
111 case SvxBorderLineStyle::SOLID:
112 {
115 else
116 aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRS);
117 }
118 break;
119 case SvxBorderLineStyle::DOTTED:
121 break;
122 case SvxBorderLineStyle::DASHED:
124 break;
125 case SvxBorderLineStyle::DOUBLE:
126 case SvxBorderLineStyle::DOUBLE_THIN:
128 break;
129 case SvxBorderLineStyle::THINTHICK_SMALLGAP:
131 break;
132 case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
134 break;
135 case SvxBorderLineStyle::THINTHICK_LARGEGAP:
137 break;
138 case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
140 break;
141 case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
143 break;
144 case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
146 break;
147 case SvxBorderLineStyle::EMBOSSED:
149 break;
150 case SvxBorderLineStyle::ENGRAVED:
152 break;
153 case SvxBorderLineStyle::OUTSET:
155 break;
156 case SvxBorderLineStyle::INSET:
158 break;
159 case SvxBorderLineStyle::FINE_DASHED:
161 break;
162 case SvxBorderLineStyle::DASH_DOT:
164 break;
165 case SvxBorderLineStyle::DASH_DOT_DOT:
167 break;
168 case SvxBorderLineStyle::NONE:
169 default:
171 break;
172 }
173
174 double const fConverted(
175 ::editeng::ConvertBorderWidthToWord(pLine->GetBorderLineStyle(), pLine->GetWidth()));
176 if (255 >= pLine->GetWidth()) // That value comes from RTF specs
177 {
179 + OString::number(static_cast<sal_Int32>(fConverted)));
180 }
181 else
182 {
183 // use \brdrth to double the value range...
185 aRet.append(static_cast<sal_Int32>(fConverted) / 2);
186 }
187
189 aRet.append(static_cast<sal_Int32>(rExport.GetColor(pLine->GetColor())));
190 }
191 else // tdf#129758 "no border" may be needed to override style
192 {
193 aRet.append(pStr);
195 }
196 return aRet.makeStringAndClear();
197}
198
199static OString OutBorderLine(RtfExport const& rExport, const editeng::SvxBorderLine* pLine,
200 const char* pStr, sal_uInt16 nDist,
201 SvxShadowLocation eShadowLocation = SvxShadowLocation::NONE)
202{
203 OStringBuffer aRet;
204 aRet.append(OutTBLBorderLine(rExport, pLine, pStr));
205 if (pLine)
206 {
207 aRet.append(OOO_STRING_SVTOOLS_RTF_BRSP);
208 aRet.append(static_cast<sal_Int32>(nDist));
209 }
210 if (eShadowLocation == SvxShadowLocation::BottomRight)
211 aRet.append(LO_STRING_SVTOOLS_RTF_BRDRSH);
212 return aRet.makeStringAndClear();
213}
214
215void RtfAttributeOutput::RTLAndCJKState(bool bIsRTL, sal_uInt16 nScript)
216{
217 m_bIsRTL = bIsRTL;
218 m_nScript = nScript;
219 m_bControlLtrRtl = true;
220}
221
223 bool /*bGenerateParaId*/)
224{
227
228 // Output table/table row/table cell starts if needed
229 if (pTextNodeInfo)
230 {
231 sal_uInt32 nRow = pTextNodeInfo->getRow();
232 sal_uInt32 nCell = pTextNodeInfo->getCell();
233
234 // New cell/row?
236 {
238 pTextNodeInfo->getInnerForDepth(m_nTableDepth));
239 OSL_ENSURE(pDeepInner, "TableNodeInfoInner not found");
240 // Make sure we always start a row between ending one and starting a cell.
241 // In case of subtables, we may not get the first cell.
242 if (pDeepInner && (pDeepInner->getCell() == 0 || m_bTableRowEnded))
243 {
244 StartTableRow(pDeepInner);
245 }
246
248 }
249
250 // Again, if depth was incremented, start a new table even if we skipped the first cell.
251 if ((nRow == 0 && nCell == 0) || (m_nTableDepth == 0 && pTextNodeInfo->getDepth()))
252 {
253 // Do we have to start the table?
254 // [If we are at the right depth already, it means that we
255 // continue the table cell]
256 sal_uInt32 nCurrentDepth = pTextNodeInfo->getDepth();
257
258 if (nCurrentDepth > m_nTableDepth)
259 {
260 // Start all the tables that begin here
261 for (sal_uInt32 nDepth = m_nTableDepth + 1; nDepth <= pTextNodeInfo->getDepth();
262 ++nDepth)
263 {
265 pTextNodeInfo->getInnerForDepth(nDepth));
266
267 m_bLastTable = (nDepth == pTextNodeInfo->getDepth());
268 StartTable();
269 StartTableRow(pInner);
271 }
272
273 m_nTableDepth = nCurrentDepth;
274 }
275 }
276 }
277
278 OSL_ENSURE(m_aRun.getLength() == 0, "m_aRun is not empty");
279 return 0;
280}
281
283{
284 bool bLastPara = false;
287 {
288 // We're ending a paragraph that is the last paragraph of a footnote or endnote, or of clipboard.
289 bLastPara
291 && m_rExport.GetCurrentNodeIndex() == m_rExport.m_pCurPam->End()->GetNodeIndex();
292 }
293
294 FinishTableRowCell(pTextNodeInfoInner);
295
296 RtfStringBuffer aParagraph;
297
298 aParagraph.appendAndClear(m_aRun);
299 aParagraph->append(m_aAfterRuns);
300 m_aAfterRuns.setLength(0);
302 m_bTableAfterCell = false;
303 else
304 {
305 aParagraph->append(SAL_NEWLINE_STRING);
306 // RTF_PAR at the end of the footnote or clipboard, would cause an additional empty paragraph.
307 if (!bLastPara)
308 {
310 aParagraph->append(' ');
311 }
312 }
314 {
316 m_nColBreakNeeded = false;
317 }
318
320 aParagraph.makeStringAndClear(this);
321 else
322 m_aSectionHeaders.append(aParagraph.makeStringAndClear());
323}
324
326{
330 .WriteChar(' ');
331}
332
334{
335 SwNodeIndex aNextIndex(rNode, 1);
336 if (rNode.IsTextNode())
337 {
338 OSL_ENSURE(m_aStyles.getLength() == 0, "m_aStyles is not empty");
339
340 // output page/section breaks
342 m_aSectionBreaks.setLength(0);
344
345 // output section headers / footers
347 {
349 m_aSectionHeaders.setLength(0);
350 }
351
352 if (aNextIndex.GetNode().IsTextNode())
353 {
354 const SwTextNode* pTextNode = static_cast<SwTextNode*>(&aNextIndex.GetNode());
355 m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode);
356 // Save the current page description for now, so later we will be able to access the previous one.
357 m_pPrevPageDesc = pTextNode->FindPageDesc();
358 }
359 else if (aNextIndex.GetNode().IsTableNode())
360 {
361 const SwTableNode* pTableNode = static_cast<SwTableNode*>(&aNextIndex.GetNode());
362 const SwFrameFormat* pFormat = pTableNode->GetTable().GetFrameFormat();
363 m_rExport.OutputSectionBreaks(&(pFormat->GetAttrSet()), *pTableNode);
364 }
366 }
367 else if (rNode.IsEndNode())
368 {
369 // End of something: make sure that it's the end of a table.
370 assert(rNode.StartOfSectionNode()->IsTableNode());
371 if (aNextIndex.GetNode().IsTextNode())
372 {
373 // Handle section break between a table and a text node following it.
374 const SwTextNode* pTextNode = aNextIndex.GetNode().GetTextNode();
375 m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode);
376 }
377 }
378}
379
381{
382 OStringBuffer aPar;
384 {
385 aPar.append(OOO_STRING_SVTOOLS_RTF_PARD);
386 aPar.append(OOO_STRING_SVTOOLS_RTF_PLAIN);
387 aPar.append(' ');
388 }
391 else
392 m_aSectionHeaders.append(aPar);
393}
394
396 const SfxItemSet& /*rParagraphMarkerProperties*/, const SwRedlineData* /*pRedlineData*/,
397 const SwRedlineData* /*pRedlineParagraphMarkerDeleted*/,
398 const SwRedlineData* /*pRedlineParagraphMarkerInserted*/)
399{
400 // Do not call MoveCharacterProperties(),
401 // Otherwise associate properties in the paragraph style are ruined.
402 const OString aProperties = m_aStyles.makeStringAndClear();
404}
405
406void RtfAttributeOutput::StartRun(const SwRedlineData* pRedlineData, sal_Int32 /*nPos*/,
407 bool bSingleEmptyRun)
408{
409 SAL_INFO("sw.rtf", __func__ << ", bSingleEmptyRun: " << bSingleEmptyRun);
410
411 m_bInRun = true;
412 m_bSingleEmptyRun = bSingleEmptyRun;
414 m_aRun->append('{');
415
416 // if there is some redlining in the document, output it
417 Redline(pRedlineData);
418
419 OSL_ENSURE(m_aRunText.getLength() == 0, "m_aRunText is not empty");
420}
421
422void RtfAttributeOutput::EndRun(const SwTextNode* /*pNode*/, sal_Int32 /*nPos*/, sal_Int32 /*nLen*/,
423 bool /*bLastRun*/)
424{
427
428 if (m_bInRuby)
429 {
431 m_bInRuby = false;
432 }
433
435 m_aRun->append('}');
436 m_bInRun = false;
437}
438
440{
441 OSL_ENSURE(m_aStyles.getLength() == 0, "m_aStyles is not empty");
442}
443
445{
446 const OString aProperties = MoveCharacterProperties(true);
448}
449
451{
452 const OString aAssocHich = m_aStylesAssocHich.makeStringAndClear();
453 const OString aAssocDbch = m_aStylesAssocDbch.makeStringAndClear();
454 const OString aAssocRtlch = m_aStylesAssocRtlch.makeStringAndClear();
455 const OString aAssocLtrch = m_aStylesAssocLtrch.makeStringAndClear();
456 const OString aNormal = m_aStyles.makeStringAndClear();
457 OStringBuffer aBuf;
458
459 if (aAutoWriteRtlLtr && !m_bControlLtrRtl)
460 {
461 m_bControlLtrRtl = !aAssocRtlch.isEmpty();
462 m_bIsRTL = false;
463 m_nScript = i18n::ScriptType::LATIN;
464 }
465
466 if (m_bIsRTL)
467 {
468 if (!aAssocRtlch.isEmpty())
469 {
470 aBuf.append(OOO_STRING_SVTOOLS_RTF_LTRCH + aAssocLtrch
471 + " " OOO_STRING_SVTOOLS_RTF_RTLCH + aAssocRtlch);
472 }
473 }
474 else
475 {
476 if (!aAssocRtlch.isEmpty())
477 {
478 aBuf.append(OOO_STRING_SVTOOLS_RTF_RTLCH + aAssocRtlch
479 + " " OOO_STRING_SVTOOLS_RTF_LTRCH + aAssocLtrch);
480 }
481 if (!aAssocHich.isEmpty())
482 {
483 aBuf.append(OOO_STRING_SVTOOLS_RTF_HICH + aAssocHich);
484 }
485 if (!aNormal.isEmpty())
486 {
487 aBuf.append(OOO_STRING_SVTOOLS_RTF_LOCH + aNormal);
488 }
489 if (!aAssocDbch.isEmpty())
490 {
491 aBuf.append(OOO_STRING_SVTOOLS_RTF_DBCH + aAssocDbch);
492 }
493 }
494
496 {
497 m_bControlLtrRtl = false;
498
499 switch (m_nScript)
500 {
501 case i18n::ScriptType::LATIN:
503 break;
504 case i18n::ScriptType::ASIAN:
506 break;
507 case i18n::ScriptType::COMPLEX:
508 /* noop */
509 default:
510 /* should not happen? */
511 break;
512 }
513 }
514
515 return aBuf.makeStringAndClear();
516}
517
518void RtfAttributeOutput::RunText(const OUString& rText, rtl_TextEncoding /*eCharSet*/)
519{
520 SAL_INFO("sw.rtf", __func__ << ", rText: " << rText);
522}
523
525
526void RtfAttributeOutput::RawText(const OUString& rText, rtl_TextEncoding eCharSet)
527{
529}
530
531void RtfAttributeOutput::StartRuby(const SwTextNode& rNode, sal_Int32 /*nPos*/,
532 const SwFormatRuby& rRuby)
533{
534 WW8Ruby aWW8Ruby(rNode, rRuby, GetExport());
535 OUString aStr(FieldString(ww::eEQ) + "\\* jc");
536 aStr += OUString::number(aWW8Ruby.GetJC()) + " \\* \"Font:" + aWW8Ruby.GetFontFamily()
537 + "\" \\* hps";
538 aStr += OUString::number((aWW8Ruby.GetRubyHeight() + 5) / 10) + " \\o";
539 if (aWW8Ruby.GetDirective())
540 {
541 aStr += "\\a" + OUStringChar(aWW8Ruby.GetDirective());
542 }
543 aStr += "(\\s\\up " + OUString::number((aWW8Ruby.GetBaseHeight() + 10) / 20 - 1) + "(";
545 aStr = rRuby.GetText() + "),";
547 m_bInRuby = true;
548}
549
550void RtfAttributeOutput::EndRuby(const SwTextNode& /*rNode*/, sal_Int32 /*nPos*/) {}
551
552bool RtfAttributeOutput::StartURL(const OUString& rUrl, const OUString& rTarget)
553{
554 m_aURLs.push(rUrl);
555 // Ignore hyperlink without a URL.
556 if (!rUrl.isEmpty())
557 {
558 m_aRun->append('{');
560 m_aRun->append('{');
563 m_aRun->append(" HYPERLINK ");
564
565 m_aRun->append("\"");
567 m_aRun->append("\" ");
568
569 if (!rTarget.isEmpty())
570 {
571 m_aRun->append("\\\\t \"");
573 m_aRun->append("\" ");
574 }
575
576 m_aRun->append("}");
578 }
579 return true;
580}
581
582bool RtfAttributeOutput::EndURL(bool const isAtEndOfParagraph)
583{
584 if (m_aURLs.empty())
585 {
586 return true;
587 }
588
589 const OUString& rURL = m_aURLs.top();
590 if (!rURL.isEmpty())
591 {
592 // UGLY: usually EndRun is called earlier, but there is an extra
593 // call to OutAttrWithRange() when at the end of the paragraph,
594 // so in that special case the output needs to be appended to the
595 // new run's text instead of the previous run
596 if (isAtEndOfParagraph)
597 {
598 // close the fldrslt group
599 m_aRunText->append("}}");
600 // close the field group
601 m_aRunText->append('}');
602 }
603 else
604 {
605 // close the fldrslt group
606 m_aRun->append("}}");
607 // close the field group
608 m_aRun->append('}');
609 }
610 }
611 m_aURLs.pop();
612 return true;
613}
614
615void RtfAttributeOutput::FieldVanish(const OUString& /*rText*/, ww::eField /*eType*/,
616 OUString const* /*pBookmarkName*/)
617{
618 SAL_INFO("sw.rtf", "TODO: " << __func__);
619}
620
622{
623 if (!pRedline)
624 return;
625
626 if (pRedline->GetType() == RedlineType::Insert)
627 {
630 m_aRun->append(static_cast<sal_Int32>(
631 m_rExport.GetRedline(SW_MOD()->GetRedlineAuthor(pRedline->GetAuthor()))));
633 }
634 else if (pRedline->GetType() == RedlineType::Delete)
635 {
638 m_aRun->append(static_cast<sal_Int32>(
639 m_rExport.GetRedline(SW_MOD()->GetRedlineAuthor(pRedline->GetAuthor()))));
641 }
642 m_aRun->append(static_cast<sal_Int32>(sw::ms::DateTime2DTTM(pRedline->GetTimeStamp())));
643 m_aRun->append(' ');
644}
645
647 const SwFormatDrop& /*rSwFormatDrop*/, sal_uInt16 /*nStyle*/,
648 ww8::WW8TableNodeInfo::Pointer_t /*pTextNodeInfo*/,
649 ww8::WW8TableNodeInfoInner::Pointer_t /*pTextNodeInfoInner*/)
650{
651 SAL_INFO("sw.rtf", "TODO: " << __func__);
652}
653
655{
656 OString* pStyle = m_rExport.GetStyle(nStyle);
657 OStringBuffer aStyle;
658 aStyle.append(OOO_STRING_SVTOOLS_RTF_S);
659 aStyle.append(static_cast<sal_Int32>(nStyle));
660 if (pStyle)
661 aStyle.append(*pStyle);
663 m_rExport.Strm().WriteOString(aStyle);
664 else
665 m_aSectionHeaders.append(aStyle);
666}
667
669 ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/)
670{
672 if (m_nTableDepth > 1)
673 {
675 m_aStyles.append(static_cast<sal_Int32>(m_nTableDepth));
676 }
677 m_bWroteCellInfo = true;
678}
679
681{
682 /* noop */
683}
684
686 ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
687{
688 InitTableHelper(pTableTextNodeInfoInner);
689
690 const SwTable* pTable = pTableTextNodeInfoInner->getTable();
691 SwFrameFormat* pFormat = pTable->GetFrameFormat();
692
694 TableOrientation(pTableTextNodeInfoInner);
695 TableBidi(pTableTextNodeInfoInner);
696 TableHeight(pTableTextNodeInfoInner);
697 TableCanSplit(pTableTextNodeInfoInner);
698
699 // Cell margins
700 const SvxBoxItem& rBox = pFormat->GetBox();
701 static const SvxBoxItemLine aBorders[] = { SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
702 SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT };
703
704 static const char* aRowPadNames[]
707
708 static const char* aRowPadUnits[]
711
712 for (int i = 0; i < 4; ++i)
713 {
714 m_aRowDefs.append(aRowPadUnits[i]);
715 m_aRowDefs.append(sal_Int32(3));
716 m_aRowDefs.append(aRowPadNames[i]);
717 m_aRowDefs.append(static_cast<sal_Int32>(rBox.GetDistance(aBorders[i])));
718 }
719
720 // The cell-dependent properties
721 const double fWidthRatio = m_pTableWrt->GetAbsWidthRatio();
722 const SwWriteTableRows& aRows = m_pTableWrt->GetRows();
723 SwWriteTableRow* pRow = aRows[pTableTextNodeInfoInner->getRow()].get();
724 SwTwips nSz = 0;
725
726 // Not using m_nTableDepth, which is not yet incremented here.
727 sal_uInt32 nCurrentDepth = pTableTextNodeInfoInner->getDepth();
728 m_aCells[nCurrentDepth] = pRow->GetCells().size();
729 for (sal_uInt32 i = 0; i < m_aCells[nCurrentDepth]; i++)
730 {
731 const SwWriteTableCell* const pCell = pRow->GetCells()[i].get();
732 const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat();
733
734 pTableTextNodeInfoInner->setCell(i);
735 TableCellProperties(pTableTextNodeInfoInner);
736
737 // Right boundary: this can't be in TableCellProperties as the old
738 // value of nSz is needed.
739 nSz += pCellFormat->GetFrameSize().GetWidth();
741 m_aRowDefs.append(static_cast<sal_Int32>(pFormat->GetLRSpace().GetLeft()
742 + rtl::math::round(nSz * fWidthRatio)));
743 }
744}
745
747 ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
748{
749 /*
750 * The function name is a bit misleading: given that we write borders
751 * before each row, we just have borders, not default ones. Additionally,
752 * this function actually writes borders for a specific cell only and is
753 * called for each cell.
754 */
755
756 const SwWriteTableRows& aRows = m_pTableWrt->GetRows();
757 SwWriteTableRow* pRow = aRows[pTableTextNodeInfoInner->getRow()].get();
758 const SwWriteTableCell* const pCell
759 = pRow->GetCells()[pTableTextNodeInfoInner->getCell()].get();
760 const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat();
761 const SvxBoxItem* pItem = pCellFormat->GetAttrSet().GetItemIfSet(RES_BOX);
762 if (!pItem)
763 return;
764
765 auto& rBox = *pItem;
766 static const SvxBoxItemLine aBorders[] = { SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
767 SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT };
768 static const char* aBorderNames[]
771 //Yes left and top are swapped with each other for cell padding! Because
772 //that's what the thundering annoying rtf export/import word xp does.
773 static const char* aCellPadNames[]
776 static const char* aCellPadUnits[]
779 for (int i = 0; i < 4; ++i)
780 {
781 if (const editeng::SvxBorderLine* pLn = rBox.GetLine(aBorders[i]))
782 m_aRowDefs.append(OutTBLBorderLine(m_rExport, pLn, aBorderNames[i]));
783 if (rBox.GetDistance(aBorders[i]))
784 {
785 m_aRowDefs.append(aCellPadUnits[i]);
786 m_aRowDefs.append(sal_Int32(3));
787 m_aRowDefs.append(aCellPadNames[i]);
788 m_aRowDefs.append(static_cast<sal_Int32>(rBox.GetDistance(aBorders[i])));
789 }
790 }
791}
792
794 ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
795{
796 const SwTable* pTable = pTableTextNodeInfoInner->getTable();
797 const SwTableBox* pTableBox = pTableTextNodeInfoInner->getTableBox();
798 const SwTableLine* pTableLine = pTableBox->GetUpper();
799
800 Color aColor = COL_AUTO;
801 auto pTableColorProp
803 if (pTableColorProp)
804 aColor = pTableColorProp->GetColor();
805
806 auto pRowColorProp
808 if (pRowColorProp && pRowColorProp->GetColor() != COL_AUTO)
809 aColor = pRowColorProp->GetColor();
810
811 const SwWriteTableRows& aRows = m_pTableWrt->GetRows();
812 SwWriteTableRow* pRow = aRows[pTableTextNodeInfoInner->getRow()].get();
813 const SwWriteTableCell* const pCell
814 = pRow->GetCells()[pTableTextNodeInfoInner->getCell()].get();
815 const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat();
816 if (const SvxBrushItem* pBrushItem = pCellFormat->GetAttrSet().GetItemIfSet(RES_BACKGROUND))
817 {
818 if (pBrushItem->GetColor() != COL_AUTO)
819 aColor = pBrushItem->GetColor();
820 }
821
822 if (!aColor.IsTransparent())
823 {
825 m_aRowDefs.append(static_cast<sal_Int32>(m_rExport.GetColor(aColor)));
826 }
827}
828
830 ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/)
831{
832}
833
835 ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/)
836{
837}
838
840{
841 const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
842 const SwTableLine* pTabLine = pTabBox->GetUpper();
843 const SwFrameFormat* pLineFormat = pTabLine->GetFrameFormat();
844 const SwFormatFrameSize& rLSz = pLineFormat->GetFrameSize();
845
846 if (!(SwFrameSize::Variable != rLSz.GetHeightSizeType() && rLSz.GetHeight()))
847 return;
848
849 sal_Int32 nHeight = 0;
850
851 switch (rLSz.GetHeightSizeType())
852 {
854 nHeight = -rLSz.GetHeight();
855 break;
857 nHeight = rLSz.GetHeight();
858 break;
859 default:
860 break;
861 }
862
863 if (nHeight)
864 {
866 m_aRowDefs.append(nHeight);
867 }
868}
869
871 ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
872{
873 const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
874 const SwTableLine* pTabLine = pTabBox->GetUpper();
875 const SwFrameFormat* pLineFormat = pTabLine->GetFrameFormat();
876 const SwFormatRowSplit& rSplittable = pLineFormat->GetRowSplit();
877
878 // The rtf default is to allow a row to break
879 if (!rSplittable.GetValue())
881}
882
884{
885 const SwTable* pTable = pTableTextNodeInfoInner->getTable();
886 const SwFrameFormat* pFrameFormat = pTable->GetFrameFormat();
887
888 if (m_rExport.TrueFrameDirection(*pFrameFormat) != SvxFrameDirection::Horizontal_RL_TB)
890 else
892}
893
895 ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
896{
897 const SwWriteTableRows& aRows = m_pTableWrt->GetRows();
898 SwWriteTableRow* pRow = aRows[pTableTextNodeInfoInner->getRow()].get();
899 const SwWriteTableCell* const pCell
900 = pRow->GetCells()[pTableTextNodeInfoInner->getCell()].get();
901 const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat();
902
903 // Text direction.
904 if (SvxFrameDirection::Vertical_RL_TB == m_rExport.TrueFrameDirection(*pCellFormat))
906 else if (SvxFrameDirection::Vertical_LR_BT == m_rExport.TrueFrameDirection(*pCellFormat))
908
909 // vertical merges
910 if (pCell->GetRowSpan() > 1)
912 else if (pCell->GetRowSpan() == 0)
914
915 // vertical alignment
916 const SwFormatVertOrient* pVertOrientItem
917 = pCellFormat->GetAttrSet().GetItemIfSet(RES_VERT_ORIENT);
918 if (!pVertOrientItem)
919 return;
920
921 switch (pVertOrientItem->GetVertOrient())
922 {
923 case text::VertOrientation::CENTER:
925 break;
926 case text::VertOrientation::BOTTOM:
928 break;
929 default:
931 break;
932 }
933}
934
936{
937 // This is called when the nested table ends in a cell, and there's no
938 // paragraph behind that; so we must check for the ends of cell, rows,
939 // and tables
940 FinishTableRowCell(pNodeInfoInner);
941}
942
944 ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
945{
946 const SwTable* pTable = pTableTextNodeInfoInner->getTable();
947 SwFrameFormat* pFormat = pTable->GetFrameFormat();
948
949 OStringBuffer aTableAdjust(OOO_STRING_SVTOOLS_RTF_TRQL);
950 switch (pFormat->GetHoriOrient().GetHoriOrient())
951 {
952 case text::HoriOrientation::CENTER:
953 aTableAdjust.setLength(0);
954 aTableAdjust.append(OOO_STRING_SVTOOLS_RTF_TRQC);
955 break;
956 case text::HoriOrientation::RIGHT:
957 aTableAdjust.setLength(0);
958 aTableAdjust.append(OOO_STRING_SVTOOLS_RTF_TRQR);
959 break;
961 case text::HoriOrientation::LEFT_AND_WIDTH:
962 aTableAdjust.append(OOO_STRING_SVTOOLS_RTF_TRLEFT);
963 aTableAdjust.append(static_cast<sal_Int32>(pFormat->GetLRSpace().GetLeft()));
964 break;
965 default:
966 break;
967 }
968
969 m_aRowDefs.append(aTableAdjust);
970}
971
973 ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/)
974{
975 SAL_INFO("sw.rtf", "TODO: " << __func__);
976}
977
978void RtfAttributeOutput::TableRowEnd(sal_uInt32 /*nDepth*/) { /* noop, see EndTableRow() */}
979
980/*
981 * Our private table methods.
982 */
983
985 const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
986{
987 const SwTable* pTable = pTableTextNodeInfoInner->getTable();
988 if (m_pTableWrt && pTable == m_pTableWrt->GetTable())
989 return;
990
991 tools::Long nPageSize = 0;
992 bool bRelBoxSize = false;
993
994 // Create the SwWriteTable instance to use col spans
995 GetTablePageSize(pTableTextNodeInfoInner.get(), nPageSize, bRelBoxSize);
996
997 const SwFrameFormat* pFormat = pTable->GetFrameFormat();
998 const sal_uInt32 nTableSz = pFormat->GetFrameSize().GetWidth();
999
1000 const SwHTMLTableLayout* pLayout = pTable->GetHTMLTableLayout();
1001 if (pLayout && pLayout->IsExportable())
1002 m_pTableWrt = std::make_unique<SwWriteTable>(pTable, pLayout);
1003 else
1004 m_pTableWrt = std::make_unique<SwWriteTable>(pTable, pTable->GetTabLines(), nPageSize,
1005 nTableSz, false);
1006}
1007
1009{
1010 // To trigger calling InitTableHelper()
1011 m_pTableWrt.reset();
1012}
1013
1015 const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
1016{
1017 sal_uInt32 nCurrentDepth = pTableTextNodeInfoInner->getDepth();
1018 SAL_INFO("sw.rtf", __func__ << ", (depth is " << nCurrentDepth << ")");
1019 m_bTableRowEnded = false;
1020
1021 TableDefinition(pTableTextNodeInfoInner);
1022
1023 if (!m_bLastTable)
1024 m_aTables.push_back(m_aRowDefs.makeStringAndClear());
1025
1026 // We'll write the table definition for nested tables later
1027 if (nCurrentDepth > 1)
1028 return;
1029 // Empty the previous row closing buffer before starting the new one,
1030 // necessary for subtables.
1032 m_aAfterRuns.setLength(0);
1034 m_aRowDefs.setLength(0);
1035}
1036
1038
1040 const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
1041{
1042 TableDefaultBorders(pTableTextNodeInfoInner);
1043 TableBackgrounds(pTableTextNodeInfoInner);
1044 TableVerticalCell(pTableTextNodeInfoInner);
1045}
1046
1048{
1049 SAL_INFO("sw.rtf", __func__ << ", (depth is " << m_nTableDepth << ")");
1050
1051 if (!m_bWroteCellInfo)
1052 {
1055 m_aAfterRuns.append(static_cast<sal_Int32>(m_nTableDepth));
1056 }
1057 if (m_nTableDepth > 1)
1059 else
1061
1062 m_bTableCellOpen = false;
1063 m_bTableAfterCell = true;
1064 m_bWroteCellInfo = false;
1065 if (m_aCells[m_nTableDepth] > 0)
1067}
1068
1070{
1071 SAL_INFO("sw.rtf", __func__ << ", (depth is " << m_nTableDepth << ")");
1072
1073 // Trying to end the row without writing the required number of cells? Fill with empty ones.
1074 for (sal_uInt32 i = 0; i < m_aCells[m_nTableDepth]; i++)
1076
1077 if (m_nTableDepth > 1)
1078 {
1079 m_aAfterRuns.append(
1081 if (!m_aRowDefs.isEmpty())
1082 {
1083 m_aAfterRuns.append(m_aRowDefs);
1084 m_aRowDefs.setLength(0);
1085 }
1086 else if (!m_aTables.empty())
1087 {
1088 m_aAfterRuns.append(m_aTables.back());
1089 m_aTables.pop_back();
1090 }
1092 "}"
1094 }
1095 else
1096 {
1097 if (!m_aTables.empty())
1098 {
1099 m_aAfterRuns.append(m_aTables.back());
1100 m_aTables.pop_back();
1101 }
1103 }
1104 m_bTableRowEnded = true;
1105}
1106
1108{
1109 if (m_nTableDepth > 0)
1110 {
1111 m_nTableDepth--;
1112 m_pTableWrt.reset();
1113 }
1114
1115 // We closed the table; if it is a nested table, the cell that contains it
1116 // still continues
1117 m_bTableCellOpen = true;
1118
1119 // Cleans the table helper
1120 m_pTableWrt.reset();
1121}
1122
1124{
1125 if (!pInner)
1126 return;
1127
1128 // Where are we in the table
1129 sal_uInt32 nRow = pInner->getRow();
1130
1131 const SwTable* pTable = pInner->getTable();
1132 const SwTableLines& rLines = pTable->GetTabLines();
1133 sal_uInt16 nLinesCount = rLines.size();
1134
1135 if (pInner->isEndOfCell())
1136 EndTableCell();
1137
1138 // This is a line end
1139 if (pInner->isEndOfLine())
1140 EndTableRow();
1141
1142 // This is the end of the table
1143 if (pInner->isEndOfLine() && (nRow + 1) == nLinesCount)
1144 EndTable();
1145}
1146
1148{
1149 m_rExport.Strm()
1151 .WriteChar('{')
1154 OSL_ENSURE(m_aStylesheet.getLength() == 0, "m_aStylesheet is not empty");
1156 m_aStylesheet.append('{');
1158}
1159
1160void RtfAttributeOutput::EndStyles(sal_uInt16 /*nNumberOfStyles*/)
1161{
1162 m_rExport.Strm().WriteChar('}');
1164 m_aStylesheet.setLength(0);
1165 m_rExport.Strm().WriteChar('}');
1166}
1167
1168void RtfAttributeOutput::DefaultStyle() { /* noop, the default style is always 0 in RTF */}
1169
1170void RtfAttributeOutput::StartStyle(const OUString& rName, StyleType eType, sal_uInt16 nBase,
1171 sal_uInt16 nNext, sal_uInt16 /*nLink*/, sal_uInt16 /*nWwId*/,
1172 sal_uInt16 nId, bool bAutoUpdate)
1173{
1174 SAL_INFO("sw.rtf", __func__ << ", rName = '" << rName << "'");
1175
1176 m_aStylesheet.append('{');
1177 if (eType == STYLE_TYPE_PARA)
1179 else
1181 m_aStylesheet.append(static_cast<sal_Int32>(nId));
1182
1183 if (nBase != 0x0FFF)
1184 {
1186 m_aStylesheet.append(static_cast<sal_Int32>(nBase));
1187 }
1188
1190 m_aStylesheet.append(static_cast<sal_Int32>(nNext));
1191
1192 if (bAutoUpdate)
1194
1195 m_rStyleName = rName;
1196 m_nStyleId = nId;
1197}
1198
1200{
1201 OString aStyles = MoveCharacterProperties();
1202 m_rExport.InsStyle(m_nStyleId, aStyles);
1203 m_aStylesheet.append(aStyles);
1204 m_aStylesheet.append(' ');
1205 m_aStylesheet.append(
1207 m_aStylesheet.append(";}");
1209}
1210
1211void RtfAttributeOutput::StartStyleProperties(bool /*bParProp*/, sal_uInt16 /*nStyle*/)
1212{
1213 /* noop */
1214}
1215
1216void RtfAttributeOutput::EndStyleProperties(bool /*bParProp*/) { /* noop */}
1217
1219{
1220 if (nLvl >= WW8ListManager::nMaxLevel)
1221 nLvl = WW8ListManager::nMaxLevel - 1;
1222
1224 m_aStyles.append(static_cast<sal_Int32>(nLvl));
1226 m_aStyles.append(static_cast<sal_Int32>(nLvl));
1227}
1228
1230{
1231 if (bBreak)
1232 {
1234 }
1235}
1236
1237void RtfAttributeOutput::SectionBreak(sal_uInt8 nC, bool /*bBreakAfter*/,
1238 const WW8_SepInfo* pSectionInfo, bool /*bExtraPageBreak*/)
1239{
1240 switch (nC)
1241 {
1243 m_nColBreakNeeded = true;
1244 break;
1245 case msword::PageBreak:
1246 if (pSectionInfo)
1247 m_rExport.SectionProperties(*pSectionInfo);
1248 break;
1249 }
1250}
1251
1253{
1255 return;
1256
1259 {
1261 m_aSectionBreaks.setLength(0);
1262 }
1263}
1264
1266{
1267 /*
1268 * noop, \sect must go to StartSection or Word won't notice multiple
1269 * columns...
1270 */
1271}
1272
1274{
1276 m_aSectionBreaks.append(static_cast<sal_Int32>(!bProtected));
1277}
1278
1280 const SwLineNumberInfo& rLnNumInfo)
1281{
1283 m_rExport.OutLong(rLnNumInfo.GetCountBy());
1285 m_rExport.OutLong(rLnNumInfo.GetPosFromLeft());
1286 if (!rLnNumInfo.IsRestartEachPage())
1288
1289 if (nRestartNo > 0)
1290 {
1292 m_rExport.OutLong(nRestartNo);
1293 }
1294}
1295
1297{
1298 /*
1299 * noop, handled in RtfExport::WriteHeaderFooter()
1300 */
1301}
1302
1304 const SwFrameFormat* /*pFirstPageFormat*/)
1305{
1306 const SvxBoxItem& rBox = pFormat->GetBox();
1309
1310 if (aDistances.bFromEdge)
1311 {
1312 sal_uInt16 nOpt = (1 << 5);
1314 m_aSectionBreaks.append(static_cast<sal_Int32>(nOpt));
1315 }
1316
1317 const editeng::SvxBorderLine* pLine = rBox.GetTop();
1318 if (pLine)
1319 m_aSectionBreaks.append(
1321 pLine = rBox.GetBottom();
1322 if (pLine)
1323 m_aSectionBreaks.append(
1325 pLine = rBox.GetLeft();
1326 if (pLine)
1327 m_aSectionBreaks.append(
1329 pLine = rBox.GetRight();
1330 if (pLine)
1331 m_aSectionBreaks.append(
1333}
1334
1336{
1339}
1340
1342 const ::std::optional<sal_uInt16>& oPageRestartNumber)
1343{
1344 if (oPageRestartNumber)
1345 {
1347 m_aSectionBreaks.append(static_cast<sal_Int32>(*oPageRestartNumber));
1349 }
1350
1351 const char* pStr = nullptr;
1352 switch (nNumType)
1353 {
1357 break;
1361 break;
1364 break;
1367 break;
1368
1369 case SVX_NUM_ARABIC:
1371 break;
1372 }
1373 if (pStr)
1374 m_aSectionBreaks.append(pStr);
1375}
1376
1378{
1379 SAL_INFO("sw.rtf", __func__ << ", nBreakCode = " << int(nBreakCode));
1380
1381 /*
1382 * break code: 0 No break, 1 New column
1383 * 2 New page, 3 Even page, 4 Odd page
1384 */
1385 const char* sType = nullptr;
1386 switch (nBreakCode)
1387 {
1388 case 1:
1390 break;
1391 case 2:
1393 break;
1394 case 3:
1396 break;
1397 case 4:
1399 break;
1400 default:
1402 break;
1403 }
1404 m_aSectionBreaks.append(sType);
1406 {
1408 m_aSectionBreaks.setLength(0);
1409 }
1410}
1411
1413{
1416}
1417
1419{
1420 const char* pOut = nullptr;
1421
1422 if (bFootnote)
1423 {
1424 switch (rInfo.m_aFormat.GetNumberingType())
1425 {
1426 default:
1428 break;
1432 break;
1436 break;
1439 break;
1442 break;
1445 break;
1446 }
1447 }
1448 else
1449 {
1450 switch (rInfo.m_aFormat.GetNumberingType())
1451 {
1452 default:
1454 break;
1458 break;
1462 break;
1465 break;
1468 break;
1471 break;
1472 }
1473 }
1474
1475 m_aSectionBreaks.append(pOut);
1476
1478 {
1480 m_aSectionBreaks.setLength(0);
1481 }
1482}
1483
1484void RtfAttributeOutput::NumberingDefinition(sal_uInt16 nId, const SwNumRule& /*rRule*/)
1485{
1492}
1493
1495{
1496 m_rExport.Strm()
1497 .WriteChar('{')
1501 m_nListId = nId;
1502}
1503
1505{
1508}
1509
1510void RtfAttributeOutput::NumberingLevel(sal_uInt8 nLevel, sal_uInt16 nStart,
1511 sal_uInt16 nNumberingType, SvxAdjust eAdjust,
1512 const sal_uInt8* pNumLvlPos, sal_uInt8 nFollow,
1513 const wwFont* pFont, const SfxItemSet* pOutSet,
1514 sal_Int16 nIndentAt, sal_Int16 nFirstLineIndex,
1515 sal_Int16 /*nListTabPos*/, const OUString& rNumberingString,
1516 const SvxBrushItem* pBrush)
1517{
1519 if (nLevel > 8) // RTF knows only 9 levels
1520 m_rExport.Strm()
1523
1525
1526 sal_uInt16 nVal = 0;
1527 switch (nNumberingType)
1528 {
1530 nVal = 1;
1531 break;
1533 nVal = 2;
1534 break;
1537 nVal = 3;
1538 break;
1541 nVal = 4;
1542 break;
1544 nVal = 14;
1545 break;
1547 nVal = 18;
1548 break;
1550 nVal = 35;
1551 if (pOutSet)
1552 {
1553 const SvxLanguageItem& rLang = pOutSet->Get(RES_CHRATR_CJK_LANGUAGE);
1555 {
1556 nVal = 39;
1557 }
1558 }
1559 break;
1561 nVal = 38;
1562 break;
1564 nVal = 34;
1565 break;
1567 nVal = 30;
1568 break;
1569 case SVX_NUM_DI_ZI_ZH:
1570 nVal = 31;
1571 break;
1573 nVal = 16;
1574 break;
1576 nVal = 20;
1577 break;
1579 nVal = 12;
1580 break;
1582 nVal = 21;
1583 break;
1585 nVal = 13;
1586 break;
1587 case style::NumberingType::HANGUL_SYLLABLE_KO:
1588 nVal = 24;
1589 break; // ganada
1590 case style::NumberingType::HANGUL_JAMO_KO:
1591 nVal = 25;
1592 break; // chosung
1593 case style::NumberingType::HANGUL_CIRCLED_SYLLABLE_KO:
1594 nVal = 24;
1595 break;
1596 case style::NumberingType::HANGUL_CIRCLED_JAMO_KO:
1597 nVal = 25;
1598 break;
1599 case style::NumberingType::NUMBER_HANGUL_KO:
1600 nVal = 42;
1601 break; // koreanCounting
1602 case style::NumberingType::NUMBER_DIGITAL_KO:
1603 nVal = 41; // koreanDigital
1604 break;
1605 case style::NumberingType::NUMBER_DIGITAL2_KO:
1606 nVal = 44; // koreanDigital2
1607 break;
1608 case style::NumberingType::NUMBER_LEGAL_KO:
1609 nVal = 43; // koreanLegal
1610 break;
1611
1612 case SVX_NUM_BITMAP:
1614 nVal = 23;
1615 break;
1617 nVal = 255;
1618 break;
1620 nVal = 22;
1621 break;
1622 }
1624 m_rExport.OutULong(nVal);
1625
1626 switch (eAdjust)
1627 {
1628 case SvxAdjust::Center:
1629 nVal = 1;
1630 break;
1631 case SvxAdjust::Right:
1632 nVal = 2;
1633 break;
1634 default:
1635 nVal = 0;
1636 break;
1637 }
1639 m_rExport.OutULong(nVal);
1640
1641 // bullet
1642 if (nNumberingType == SVX_NUM_BITMAP && pBrush)
1643 {
1644 int nIndex = m_rExport.GetGrfIndex(*pBrush);
1645 if (nIndex != -1)
1646 {
1649 }
1650 }
1651
1653 m_rExport.OutULong(nStart);
1654
1656 m_rExport.OutULong(nFollow);
1657
1658 // leveltext group
1660
1661 if (SVX_NUM_CHAR_SPECIAL == nNumberingType || SVX_NUM_BITMAP == nNumberingType)
1662 {
1663 m_rExport.Strm().WriteCharPtr("\\'01");
1664 sal_Unicode cChar = rNumberingString[0];
1665 m_rExport.Strm().WriteCharPtr("\\u");
1666 m_rExport.OutULong(cChar);
1667 m_rExport.Strm().WriteCharPtr(" ?");
1668 }
1669 else
1670 {
1672 msfilter::rtfutil::OutHex(rNumberingString.getLength(), 2).getStr());
1675 /*bUnicode =*/false)
1676 .getStr());
1677 }
1678
1679 m_rExport.Strm().WriteCharPtr(";}");
1680
1681 // write the levelnumbers
1683 for (sal_uInt8 i = 0; i <= nLevel && pNumLvlPos[i]; ++i)
1684 {
1686 msfilter::rtfutil::OutHex(pNumLvlPos[i], 2).getStr());
1687 }
1688 m_rExport.Strm().WriteCharPtr(";}");
1689
1690 if (pOutSet)
1691 {
1692 if (pFont)
1693 {
1696 }
1697 m_rExport.OutputItemSet(*pOutSet, false, true, i18n::ScriptType::LATIN,
1699 const OString aProperties = MoveCharacterProperties(true);
1701 }
1702
1705 m_rExport.OutLong(nIndentAt);
1706
1707 m_rExport.Strm().WriteChar('}');
1708 if (nLevel > 8)
1709 m_rExport.Strm().WriteChar('}');
1710}
1711
1712void RtfAttributeOutput::WriteField_Impl(const SwField* const pField, ww::eField /*eType*/,
1713 const OUString& rFieldCmd, FieldFlags nMode)
1714{
1715 // If there are no field instructions, don't export it as a field.
1716 bool bHasInstructions = !rFieldCmd.isEmpty();
1717 if (FieldFlags::All == nMode)
1718 {
1719 if (bHasInstructions)
1720 {
1722 if (pField && (pField->GetSubType() & FIXEDFLD))
1725 " ");
1729 }
1730 if (pField)
1733 if (bHasInstructions)
1734 m_aRunText->append("}}");
1735 }
1736 else
1737 {
1738 if (nMode & FieldFlags::CmdStart)
1739 {
1742 // paragraph break closes group so open another one "inside" to
1743 " {"); // prevent leaving the field instruction
1744 }
1745 if (bHasInstructions)
1748 if (nMode & FieldFlags::CmdEnd)
1749 {
1751 }
1752 if (nMode & FieldFlags::Close)
1753 {
1754 m_aRunText->append("}}}");
1755 }
1756 }
1757}
1758
1759void RtfAttributeOutput::WriteBookmarks_Impl(std::vector<OUString>& rStarts,
1760 std::vector<OUString>& rEnds)
1761{
1762 for (const auto& rStart : rStarts)
1763 {
1766 m_aRunText->append('}');
1767 }
1768 rStarts.clear();
1769
1770 for (const auto& rEnd : rEnds)
1771 {
1774 m_aRunText->append('}');
1775 }
1776 rEnds.clear();
1777}
1778
1779void RtfAttributeOutput::WriteAnnotationMarks_Impl(std::vector<OUString>& rStarts,
1780 std::vector<OUString>& rEnds)
1781{
1782 for (const auto& rStart : rStarts)
1783 {
1784 OString rName = OUStringToOString(rStart, RTL_TEXTENCODING_UTF8);
1785
1786 // Output the annotation mark
1787 const sal_Int32 nId = m_nNextAnnotationMarkId++;
1790 m_aRun->append(nId);
1791 m_aRun->append('}');
1792 }
1793 rStarts.clear();
1794
1795 for (const auto& rEnd : rEnds)
1796 {
1797 OString rName = OUStringToOString(rEnd, RTL_TEXTENCODING_UTF8);
1798
1799 // Get the id of the annotation mark
1800 auto it = m_rOpenedAnnotationMarksIds.find(rName);
1801 if (it != m_rOpenedAnnotationMarksIds.end())
1802 {
1803 const sal_Int32 nId = it->second;
1805 m_aRun->append(nId);
1806 m_aRun->append('}');
1807 m_rOpenedAnnotationMarksIds.erase(rName);
1808
1809 if (m_aPostitFields.find(nId) != m_aPostitFields.end())
1810 {
1811 m_aRunText->append("{");
1815 m_aRunText->append("}");
1816 }
1817 }
1818 }
1819 rEnds.clear();
1820}
1821
1823 const char* pStr, bool bTitlepg)
1824{
1825 OStringBuffer aSectionBreaks = m_aSectionBreaks;
1826 m_aSectionBreaks.setLength(0);
1827 RtfStringBuffer aRun = m_aRun;
1828 m_aRun.clear();
1829
1832 m_aSectionHeaders.append(
1833 static_cast<sal_Int32>(m_rExport.m_pCurrentPageDesc->GetMaster().GetULSpace().GetUpper()));
1834 if (bTitlepg)
1836 m_aSectionHeaders.append('{');
1837 m_aSectionHeaders.append(pStr);
1839 m_rExport.WriteHeaderFooterText(rFormat, bHeader);
1841 m_aSectionHeaders.append('}');
1842
1843 m_aSectionBreaks = aSectionBreaks;
1844 m_aRun = aRun;
1845}
1846
1847namespace
1848{
1849void lcl_TextFrameShadow(std::vector<std::pair<OString, OString>>& rFlyProperties,
1850 const SwFrameFormat& rFrameFormat)
1851{
1852 const SvxShadowItem& aShadowItem = rFrameFormat.GetShadow();
1853 if (aShadowItem.GetLocation() == SvxShadowLocation::NONE)
1854 return;
1855
1856 rFlyProperties.push_back(std::make_pair<OString, OString>("fShadow", OString::number(1)));
1857
1858 const Color& rColor = aShadowItem.GetColor();
1859 // We in fact need RGB to BGR, but the transformation is symmetric.
1860 rFlyProperties.push_back(std::make_pair<OString, OString>(
1861 "shadowColor", OString::number(wwUtility::RGBToBGR(rColor))));
1862
1863 // Twips -> points -> EMUs -- hacky, the intermediate step hides rounding errors on roundtrip.
1864 OString aShadowWidth = OString::number(sal_Int32(aShadowItem.GetWidth() / 20) * 12700);
1865 OString aOffsetX;
1866 OString aOffsetY;
1867 switch (aShadowItem.GetLocation())
1868 {
1869 case SvxShadowLocation::TopLeft:
1870 aOffsetX = "-" + aShadowWidth;
1871 aOffsetY = "-" + aShadowWidth;
1872 break;
1873 case SvxShadowLocation::TopRight:
1874 aOffsetX = aShadowWidth;
1875 aOffsetY = "-" + aShadowWidth;
1876 break;
1877 case SvxShadowLocation::BottomLeft:
1878 aOffsetX = "-" + aShadowWidth;
1879 aOffsetY = aShadowWidth;
1880 break;
1881 case SvxShadowLocation::BottomRight:
1882 aOffsetX = aShadowWidth;
1883 aOffsetY = aShadowWidth;
1884 break;
1885 case SvxShadowLocation::NONE:
1886 case SvxShadowLocation::End:
1887 break;
1888 }
1889 if (!aOffsetX.isEmpty())
1890 rFlyProperties.emplace_back("shadowOffsetX", aOffsetX);
1891 if (!aOffsetY.isEmpty())
1892 rFlyProperties.emplace_back("shadowOffsetY", aOffsetY);
1893}
1894
1895void lcl_TextFrameRelativeSize(std::vector<std::pair<OString, OString>>& rFlyProperties,
1896 const SwFrameFormat& rFrameFormat)
1897{
1898 const SwFormatFrameSize& rSize = rFrameFormat.GetFrameSize();
1899
1900 // Relative size of the Text Frame.
1901 const sal_uInt8 nWidthPercent = rSize.GetWidthPercent();
1902 if (nWidthPercent && nWidthPercent != SwFormatFrameSize::SYNCED)
1903 {
1904 rFlyProperties.push_back(
1905 std::make_pair<OString, OString>("pctHoriz", OString::number(nWidthPercent * 10)));
1906
1907 OString aRelation;
1908 switch (rSize.GetWidthPercentRelation())
1909 {
1910 case text::RelOrientation::PAGE_FRAME:
1911 aRelation = "1"; // page
1912 break;
1913 default:
1914 aRelation = "0"; // margin
1915 break;
1916 }
1917 rFlyProperties.emplace_back(std::make_pair("sizerelh", aRelation));
1918 }
1919 const sal_uInt8 nHeightPercent = rSize.GetHeightPercent();
1920 if (!(nHeightPercent && nHeightPercent != SwFormatFrameSize::SYNCED))
1921 return;
1922
1923 rFlyProperties.push_back(
1924 std::make_pair<OString, OString>("pctVert", OString::number(nHeightPercent * 10)));
1925
1926 OString aRelation;
1927 switch (rSize.GetHeightPercentRelation())
1928 {
1929 case text::RelOrientation::PAGE_FRAME:
1930 aRelation = "1"; // page
1931 break;
1932 default:
1933 aRelation = "0"; // margin
1934 break;
1935 }
1936 rFlyProperties.emplace_back(std::make_pair("sizerelv", aRelation));
1937}
1938}
1939
1940void RtfAttributeOutput::writeTextFrame(const ww8::Frame& rFrame, bool bTextBox)
1941{
1942 RtfStringBuffer aRunText;
1943 if (bTextBox)
1944 {
1946 aRunText = m_aRunText;
1947 m_aRunText.clear();
1948 }
1949
1951
1952 {
1953 // Save table state, in case the inner text also contains a table.
1955 m_rExport.m_pTableInfo = std::make_shared<ww8::WW8TableInfo>();
1956 std::unique_ptr<SwWriteTable> pTableWrt(std::move(m_pTableWrt));
1957 sal_uInt32 nTableDepth = m_nTableDepth;
1958
1959 m_nTableDepth = 0;
1960 /*
1961 * Save m_aRun as we should not lose the opening brace.
1962 * OTOH, just drop the contents of m_aRunText in case something
1963 * would be there, causing a problem later.
1964 */
1965 OString aSave = m_aRun.makeStringAndClear();
1966 // Also back m_bInRun and m_bSingleEmptyRun up.
1967 bool bInRunOrig = m_bInRun;
1968 m_bInRun = false;
1969 bool bSingleEmptyRunOrig = m_bSingleEmptyRun;
1970 m_bSingleEmptyRun = false;
1972
1973 const SwFrameFormat& rFrameFormat = rFrame.GetFrameFormat();
1974 const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
1975 SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
1976 SwNodeOffset nEnd
1977 = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
1978 m_rExport.SaveData(nStt, nEnd);
1979 m_rExport.m_pParentFrame = &rFrame;
1982
1985 m_aRun->append(aSave);
1986 m_aRunText.clear();
1987 m_bInRun = bInRunOrig;
1988 m_bSingleEmptyRun = bSingleEmptyRunOrig;
1989
1990 // Restore table state.
1991 m_rExport.m_pTableInfo = pTableInfoOrig;
1992 m_pTableWrt = std::move(pTableWrt);
1993 m_nTableDepth = nTableDepth;
1994 }
1995
1996 m_rExport.m_pParentFrame = nullptr;
1997
1998 m_rExport.Strm().WriteChar('}'); // shptxt
1999
2000 if (bTextBox)
2001 {
2002 m_aRunText = aRunText;
2005 }
2006}
2007
2013{
2014private:
2019 bool const m_bInRun;
2020
2021public:
2023 : m_rRtf(rRtf)
2024 , m_Run(std::move(rRtf.m_aRun))
2025 , m_RunText(std::move(rRtf.m_aRunText))
2027 , m_bInRun(rRtf.m_bInRun)
2028 {
2030 }
2032 {
2033 m_rRtf.m_aRun = std::move(m_Run);
2034 m_rRtf.m_aRunText = std::move(m_RunText);
2037
2040 }
2041};
2042
2043void RtfAttributeOutput::OutputFlyFrame_Impl(const ww8::Frame& rFrame, const Point& /*rNdTopLeft*/)
2044{
2045 const SwNode* pNode = rFrame.GetContent();
2046 const SwGrfNode* pGrfNode = pNode ? pNode->GetGrfNode() : nullptr;
2047
2048 switch (rFrame.GetWriterType())
2049 {
2051 {
2052 // If this is a TextBox of a shape, then ignore: it's handled in RtfSdrExport::StartShape().
2054 break;
2055
2056 SaveRunState const saved(*this);
2057
2058 m_rExport.m_pParentFrame = &rFrame;
2059
2063
2064 // Shape properties.
2065 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
2066 "shapeType", OString::number(ESCHER_ShpInst_TextBox)));
2067
2068 // When a frame has some low height, but automatically expanded due
2069 // to lots of contents, this size contains the real size.
2070 const Size aSize = rFrame.GetSize();
2071 m_pFlyFrameSize = &aSize;
2072
2075 m_rExport.OutputFormat(rFrame.GetFrameFormat(), false, false, true);
2076
2077 // Write ZOrder.
2078 if (const SdrObject* pObject = rFrame.GetFrameFormat().FindRealSdrObject())
2079 {
2081 m_rExport.OutULong(pObject->GetOrdNum());
2082 }
2083
2086 m_aStyles.setLength(0);
2089 m_pFlyFrameSize = nullptr;
2090
2091 const SwFrameFormat& rFrameFormat = rFrame.GetFrameFormat();
2092 lcl_TextFrameShadow(m_aFlyProperties, rFrameFormat);
2093 lcl_TextFrameRelativeSize(m_aFlyProperties, rFrameFormat);
2094
2095 for (const std::pair<OString, OString>& rPair : m_aFlyProperties)
2096 {
2099 m_rExport.Strm().WriteOString(rPair.first);
2101 m_rExport.Strm().WriteOString(rPair.second);
2102 m_rExport.Strm().WriteCharPtr("}}");
2103 }
2104 m_aFlyProperties.clear();
2105
2106 writeTextFrame(rFrame);
2107
2108 m_rExport.Strm().WriteChar('}'); // shpinst
2109 m_rExport.Strm().WriteChar('}'); // shp
2110
2112 }
2113 break;
2115 if (pGrfNode)
2116 {
2117 m_aRunText.append(dynamic_cast<const SwFlyFrameFormat*>(&rFrame.GetFrameFormat()),
2118 pGrfNode);
2119 }
2120 else if (!rFrame.IsInline())
2121 {
2122 m_rExport.m_pParentFrame = &rFrame;
2124 m_rExport.OutputFormat(rFrame.GetFrameFormat(), false, false, true);
2127 m_rExport.OutputFormat(rFrame.GetFrameFormat(), false, false, true);
2128 m_aRunText->append('}');
2129 m_rExport.m_pParentFrame = nullptr;
2130 }
2131 break;
2133 {
2134 const SdrObject* pSdrObj = rFrame.GetFrameFormat().FindRealSdrObject();
2135 if (pSdrObj)
2136 {
2140 m_aRunText->append(" SHAPE ");
2141 m_aRunText->append("}"
2143
2145
2146 m_aRunText->append('}');
2147 m_aRunText->append('}');
2148 }
2149 }
2150 break;
2152 {
2153 const SwFrameFormat& rFrameFormat = rFrame.GetFrameFormat();
2154 const SdrObject* pObject = rFrameFormat.FindRealSdrObject();
2155
2158
2159 if (pObject && pObject->GetObjInventor() == SdrInventor::FmForm)
2160 {
2161 if (auto pFormObj = dynamic_cast<const SdrUnoObj*>(pObject))
2162 {
2163 const uno::Reference<awt::XControlModel>& xControlModel
2164 = pFormObj->GetUnoControlModel();
2165 uno::Reference<lang::XServiceInfo> xInfo(xControlModel, uno::UNO_QUERY);
2166 if (xInfo.is())
2167 {
2168 uno::Reference<beans::XPropertySet> xPropSet(xControlModel, uno::UNO_QUERY);
2169 uno::Reference<beans::XPropertySetInfo> xPropSetInfo
2170 = xPropSet->getPropertySetInfo();
2171 OUString sName;
2172 if (xInfo->supportsService("com.sun.star.form.component.CheckBox"))
2173 {
2176 m_aRun->append(
2178 "{");
2179 m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFTYPE "1"); // 1 = checkbox
2180 // checkbox size in half points, this seems to be always 20
2182
2183 OUString aStr;
2184 sName = "Name";
2185 if (xPropSetInfo->hasPropertyByName(sName))
2186 {
2187 xPropSet->getPropertyValue(sName) >>= aStr;
2188 m_aRun->append(
2190 " ");
2191 m_aRun->append(
2193 m_aRun->append('}');
2194 }
2195
2196 sName = "HelpText";
2197 if (xPropSetInfo->hasPropertyByName(sName))
2198 {
2199 xPropSet->getPropertyValue(sName) >>= aStr;
2203 m_aRun->append(
2205 m_aRun->append('}');
2206 }
2207
2208 sName = "HelpF1Text";
2209 if (xPropSetInfo->hasPropertyByName(sName))
2210 {
2211 xPropSet->getPropertyValue(sName) >>= aStr;
2215 m_aRun->append(
2217 m_aRun->append('}');
2218 }
2219
2220 sal_Int16 nTemp = 0;
2221 xPropSet->getPropertyValue("DefaultState") >>= nTemp;
2223 m_aRun->append(static_cast<sal_Int32>(nTemp));
2224 xPropSet->getPropertyValue("State") >>= nTemp;
2226 m_aRun->append(static_cast<sal_Int32>(nTemp));
2227
2228 m_aRun->append("}}");
2229
2230 // field result is empty, ffres already contains the form result
2232 }
2233 else if (xInfo->supportsService("com.sun.star.form.component.TextField"))
2234 {
2235 OStringBuffer aBuf;
2236 OString aStr;
2237 OUString aTmp;
2238 const char* pStr;
2239
2242 m_aRun->append(
2244 " ");
2245 for (int i = 0; i < 8; i++)
2246 aBuf.append(char(0x00));
2247 xPropSet->getPropertyValue("Name") >>= aTmp;
2249 aBuf.append(static_cast<char>(aStr.getLength()));
2250 aBuf.append(aStr);
2251 aBuf.append(char(0x00));
2252 xPropSet->getPropertyValue("DefaultText") >>= aTmp;
2254 aBuf.append(static_cast<char>(aStr.getLength()));
2255 aBuf.append(aStr);
2256 for (int i = 0; i < 11; i++)
2257 aBuf.append(char(0x00));
2258 aStr = aBuf.makeStringAndClear();
2259 pStr = aStr.getStr();
2260 for (int i = 0; i < aStr.getLength(); i++, pStr++)
2262 m_aRun->append('}');
2264 xPropSet->getPropertyValue("Text") >>= aTmp;
2266 m_aRun->append('}');
2267 m_aRun->append(
2269 "{");
2270 sName = "HelpText";
2271 if (xPropSetInfo->hasPropertyByName(sName))
2272 {
2273 xPropSet->getPropertyValue(sName) >>= aTmp;
2277 m_aRun->append(
2279 m_aRun->append('}');
2280 }
2281
2282 sName = "HelpF1Text";
2283 if (xPropSetInfo->hasPropertyByName(sName))
2284 {
2285 xPropSet->getPropertyValue(sName) >>= aTmp;
2289 m_aRun->append(
2291 m_aRun->append('}');
2292 }
2293 m_aRun->append("}");
2294 }
2295 else if (xInfo->supportsService("com.sun.star.form.component.ListBox"))
2296 {
2297 OUString aStr;
2298 uno::Sequence<sal_Int16> aIntSeq;
2299 uno::Sequence<OUString> aStrSeq;
2300
2303 m_aRun->append(
2305 "{");
2308
2309 xPropSet->getPropertyValue("DefaultSelection") >>= aIntSeq;
2310 if (aIntSeq.hasElements())
2311 {
2313 // a dropdown list can have only one 'selected item by default'
2314 m_aRun->append(static_cast<sal_Int32>(aIntSeq[0]));
2315 }
2316
2317 xPropSet->getPropertyValue("SelectedItems") >>= aIntSeq;
2318 if (aIntSeq.hasElements())
2319 {
2321 // a dropdown list can have only one 'currently selected item'
2322 m_aRun->append(static_cast<sal_Int32>(aIntSeq[0]));
2323 }
2324
2325 sName = "Name";
2326 if (xPropSetInfo->hasPropertyByName(sName))
2327 {
2328 xPropSet->getPropertyValue(sName) >>= aStr;
2329 m_aRun->append(
2331 " ");
2332 m_aRun->append(
2334 m_aRun->append('}');
2335 }
2336
2337 sName = "HelpText";
2338 if (xPropSetInfo->hasPropertyByName(sName))
2339 {
2340 xPropSet->getPropertyValue(sName) >>= aStr;
2344 m_aRun->append(
2346 m_aRun->append('}');
2347 }
2348
2349 sName = "HelpF1Text";
2350 if (xPropSetInfo->hasPropertyByName(sName))
2351 {
2352 xPropSet->getPropertyValue(sName) >>= aStr;
2356 m_aRun->append(
2358 m_aRun->append('}');
2359 }
2360
2361 xPropSet->getPropertyValue("StringItemList") >>= aStrSeq;
2362 for (const auto& rStr : std::as_const(aStrSeq))
2363 m_aRun->append(
2366 + "}");
2367
2368 m_aRun->append("}}");
2369
2370 // field result is empty, ffres already contains the form result
2372 }
2373 else
2374 SAL_INFO("sw.rtf", __func__ << " unhandled form control: '"
2375 << xInfo->getImplementationName() << "'");
2376 m_aRun->append('}');
2377 }
2378 }
2379 }
2380
2381 m_aRun->append('}');
2382 }
2383 break;
2384 case ww8::Frame::eOle:
2385 {
2386 const SwFrameFormat& rFrameFormat = rFrame.GetFrameFormat();
2387 const SdrObject* pSdrObj = rFrameFormat.FindRealSdrObject();
2388 if (pSdrObj)
2389 {
2390 SwNodeIndex aIdx(*rFrameFormat.GetContent().GetContentIdx(), 1);
2391 SwOLENode& rOLENd = *aIdx.GetNode().GetOLENode();
2392 FlyFrameOLE(dynamic_cast<const SwFlyFrameFormat*>(&rFrameFormat), rOLENd,
2393 rFrame.GetLayoutSize());
2394 }
2395 }
2396 break;
2397 default:
2398 SAL_INFO("sw.rtf", __func__ << ": unknown type ("
2399 << static_cast<int>(rFrame.GetWriterType()) << ")");
2400 break;
2401 }
2402}
2403
2405{
2406 switch (rCaseMap.GetValue())
2407 {
2408 case SvxCaseMap::SmallCaps:
2410 break;
2411 case SvxCaseMap::Uppercase:
2413 break;
2414 default: // Something that rtf does not support
2416 m_aStyles.append(sal_Int32(0));
2418 m_aStyles.append(sal_Int32(0));
2419 break;
2420 }
2421}
2422
2424{
2425 const Color aColor(rColor.GetValue());
2426
2428 m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(aColor)));
2429}
2430
2432{
2434 if (!rContour.GetValue())
2435 m_aStyles.append(sal_Int32(0));
2436}
2437
2439{
2440 switch (rCrossedOut.GetStrikeout())
2441 {
2442 case STRIKEOUT_NONE:
2444 m_aStyles.append(sal_Int32(0));
2445 break;
2446 case STRIKEOUT_DOUBLE:
2448 m_aStyles.append(sal_Int32(1));
2449 break;
2450 default:
2452 break;
2453 }
2454}
2455
2457{
2458 short nEsc = rEscapement.GetEsc();
2459 short nProp = rEscapement.GetProportionalHeight();
2460 sal_Int32 nProp100 = nProp * 100;
2461 if (DFLT_ESC_PROP == nProp || nProp < 1 || nProp > 100)
2462 {
2463 if (DFLT_ESC_SUB == nEsc || DFLT_ESC_AUTO_SUB == nEsc)
2465 else if (DFLT_ESC_SUPER == nEsc || DFLT_ESC_AUTO_SUPER == nEsc)
2467 return;
2468 }
2469 if (DFLT_ESC_AUTO_SUPER == nEsc)
2470 {
2471 nEsc = .8 * (100 - nProp);
2472 ++nProp100; // A 1 afterwards means 'automatic' according to editeng/rtf/rtfitem.cxx
2473 }
2474 else if (DFLT_ESC_AUTO_SUB == nEsc)
2475 {
2476 nEsc = .2 * -(100 - nProp);
2477 ++nProp100;
2478 }
2479
2480 const char* pUpDn;
2481
2482 double fHeight = m_rExport.GetItem(RES_CHRATR_FONTSIZE).GetHeight();
2483
2484 if (0 < nEsc)
2486 else if (0 > nEsc)
2487 {
2489 fHeight = -fHeight;
2490 }
2491 else
2492 return;
2493
2494 m_aStyles.append('{');
2497 m_aStyles.append(nProp100);
2498 m_aStyles.append('}');
2499 m_aStyles.append(pUpDn);
2500
2501 /*
2502 * Calculate the act. FontSize and the percentage of the displacement;
2503 * RTF file expects half points, while internally it's in twips.
2504 * Formally : (FontSize * 1/20 ) pts x * 2
2505 * ----------------------- = ------------
2506 * 100% Escapement
2507 */
2508 m_aStyles.append(static_cast<sal_Int32>(round(fHeight * nEsc / 1000)));
2509}
2510
2512{
2513 // Insert \loch in MoveCharacterProperties
2515 m_aStyles.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont)));
2516
2517 // Insert \hich in MoveCharacterProperties
2519 m_aStylesAssocHich.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont)));
2520
2521 // FIXME: this may be a tad expensive... but the charset needs to be
2522 // consistent with what wwFont::WriteRtf() does
2525 aTmp.msPrimary, aTmp.msSecondary, rFont.GetCharSet());
2526 m_rExport.SetCurrentEncoding(rtl_getTextEncodingFromWindowsCharset(nWindowsCharset));
2527 if (m_rExport.GetCurrentEncoding() == RTL_TEXTENCODING_DONTKNOW)
2529}
2530
2532{
2533 switch (rFontSize.Which())
2534 {
2537 m_aStyles.append(static_cast<sal_Int32>(rFontSize.GetHeight() / 10));
2538 break;
2541 m_aStylesAssocDbch.append(static_cast<sal_Int32>(rFontSize.GetHeight() / 10));
2542 break;
2545 m_aStylesAssocRtlch.append(static_cast<sal_Int32>(rFontSize.GetHeight() / 10));
2546 break;
2547 }
2548}
2549
2551{
2552 // in quarter points then in twips
2554 m_aStyles.append(static_cast<sal_Int32>(rKerning.GetValue() / 5));
2556 m_aStyles.append(static_cast<sal_Int32>(rKerning.GetValue()));
2557}
2558
2560{
2561 switch (rLanguage.Which())
2562 {
2565 m_aStyles.append(
2566 static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
2568 m_aStylesAssocLtrch.append(
2569 static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
2570 break;
2573 m_aStylesAssocDbch.append(
2574 static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
2576 m_aStylesAssocLtrch.append(
2577 static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
2578 break;
2581 m_aStylesAssocRtlch.append(
2582 static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
2583 break;
2584 }
2585}
2586
2588{
2590 if (rPosture.GetPosture() == ITALIC_NONE)
2591 m_aStyles.append(sal_Int32(0));
2592}
2593
2595{
2597 if (!rShadow.GetValue())
2598 m_aStyles.append(sal_Int32(0));
2599}
2600
2602{
2603 const char* pStr = nullptr;
2605 bool bWord = false;
2606 // No StaticWhichCast(RES_CHRATR_WORDLINEMODE), this may be for a postit, where the which ids
2607 // don't match.
2608 if (pItem)
2609 bWord = static_cast<const SvxWordLineModeItem*>(pItem)->GetValue();
2610 switch (rUnderline.GetLineStyle())
2611 {
2612 case LINESTYLE_SINGLE:
2614 break;
2615 case LINESTYLE_DOUBLE:
2617 break;
2618 case LINESTYLE_NONE:
2620 break;
2621 case LINESTYLE_DOTTED:
2623 break;
2624 case LINESTYLE_DASH:
2626 break;
2627 case LINESTYLE_DASHDOT:
2629 break;
2632 break;
2633 case LINESTYLE_BOLD:
2635 break;
2636 case LINESTYLE_WAVE:
2638 break;
2641 break;
2642 case LINESTYLE_BOLDDASH:
2644 break;
2645 case LINESTYLE_LONGDASH:
2647 break;
2650 break;
2653 break;
2656 break;
2657 case LINESTYLE_BOLDWAVE:
2659 break;
2662 break;
2663 default:
2664 break;
2665 }
2666
2667 if (pStr)
2668 {
2669 m_aStyles.append(pStr);
2670 // NEEDSWORK looks like here rUnderline.GetColor() is always black,
2671 // even if the color in the odt is for example green...
2673 m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(rUnderline.GetColor())));
2674 }
2675}
2676
2678{
2680 if (rWeight.GetWeight() != WEIGHT_BOLD)
2681 m_aStyles.append(sal_Int32(0));
2682}
2683
2685{
2687 m_aStyles.append(static_cast<sal_Int32>(rAutoKern.GetValue() ? 1 : 0));
2688}
2689
2691{
2693 m_aStyles.append(static_cast<sal_Int32>(rBlink.GetValue() ? 2 : 0));
2694}
2695
2697{
2698 if (!rBrush.GetColor().IsTransparent())
2699 {
2701 m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(rBrush.GetColor())));
2702 }
2703}
2704
2706{
2707 // Insert \dbch in MoveCharacterProperties
2709 m_aStylesAssocDbch.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont)));
2710}
2711
2713{
2714 CharFontSize(rFontSize);
2715}
2716
2718{
2719 CharLanguage(rLanguageItem);
2720}
2721
2723{
2725 if (rPosture.GetPosture() == ITALIC_NONE)
2726 m_aStylesAssocDbch.append(sal_Int32(0));
2727}
2728
2730{
2732 if (rWeight.GetWeight() != WEIGHT_BOLD)
2733 m_aStylesAssocDbch.append(sal_Int32(0));
2734}
2735
2737{
2738 // Insert \rtlch in MoveCharacterProperties
2740 m_aStylesAssocRtlch.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont)));
2741}
2742
2744{
2745 CharFontSize(rFontSize);
2746}
2747
2749{
2750 CharLanguage(rLanguageItem);
2751}
2752
2754{
2756 if (rPosture.GetPosture() == ITALIC_NONE)
2757 m_aStylesAssocRtlch.append(sal_Int32(0));
2758}
2759
2761{
2763 if (rWeight.GetWeight() != WEIGHT_BOLD)
2764 m_aStylesAssocRtlch.append(sal_Int32(0));
2765}
2766
2768
2770
2772{
2774 m_aStyles.append(static_cast<sal_Int32>(rRotate.IsFitToLine() ? 1 : 0));
2775}
2776
2778{
2779 FontEmphasisMark v = rEmphasisMark.GetEmphasisMark();
2780 if (v == FontEmphasisMark::NONE)
2782 else if (v == (FontEmphasisMark::Dot | FontEmphasisMark::PosAbove))
2784 else if (v == (FontEmphasisMark::Accent | FontEmphasisMark::PosAbove))
2786 else if (v == (FontEmphasisMark::Circle | FontEmphasisMark::PosAbove))
2788 else if (v == (FontEmphasisMark::Dot | FontEmphasisMark::PosBelow))
2790}
2791
2793{
2794 if (!rTwoLines.GetValue())
2795 return;
2796
2797 sal_Unicode cStart = rTwoLines.GetStartBracket();
2798 sal_Unicode cEnd = rTwoLines.GetEndBracket();
2799
2800 sal_uInt16 nType;
2801 if (!cStart && !cEnd)
2802 nType = 0;
2803 else if ('{' == cStart || '}' == cEnd)
2804 nType = 4;
2805 else if ('<' == cStart || '>' == cEnd)
2806 nType = 3;
2807 else if ('[' == cStart || ']' == cEnd)
2808 nType = 2;
2809 else // all other kind of brackets
2810 nType = 1;
2811
2813 m_aStyles.append(static_cast<sal_Int32>(nType));
2814}
2815
2817{
2819 m_aStyles.append(static_cast<sal_Int32>(rScaleWidth.GetValue()));
2820}
2821
2823{
2824 const char* pStr;
2825 switch (rRelief.GetValue())
2826 {
2827 case FontRelief::Embossed:
2829 break;
2830 case FontRelief::Engraved:
2832 break;
2833 default:
2834 pStr = nullptr;
2835 break;
2836 }
2837
2838 if (pStr)
2839 m_aStyles.append(pStr);
2840}
2841
2843{
2845 if (!rHidden.GetValue())
2846 m_aStyles.append(sal_Int32(0));
2847}
2848
2850 const sal_uInt16 nDist, const bool bShadow)
2851{
2852 m_aStyles.append(
2854 bShadow ? SvxShadowLocation::BottomRight : SvxShadowLocation::NONE));
2855}
2856
2858{
2860 m_aStyles.append(static_cast<sal_Int32>(msfilter::util::TransColToIco(rBrush.GetColor())));
2861}
2862
2864{
2865 if (rURL.GetValue().isEmpty())
2866 return;
2867
2868 const SwCharFormat* pFormat;
2869 const SwTextINetFormat* pTextAtr = rURL.GetTextINetFormat();
2870
2871 if (pTextAtr && nullptr != (pFormat = pTextAtr->GetCharFormat()))
2872 {
2873 sal_uInt16 nStyle = m_rExport.GetId(pFormat);
2874 OString* pString = m_rExport.GetStyle(nStyle);
2875 if (pString)
2876 m_aStyles.append(*pString);
2877 }
2878}
2879
2881{
2882 sal_uInt16 nStyle = m_rExport.GetId(rCharFormat.GetCharFormat());
2884 m_aStyles.append(static_cast<sal_Int32>(nStyle));
2885 OString* pString = m_rExport.GetStyle(nStyle);
2886 if (pString)
2887 m_aStyles.append(*pString);
2888}
2889
2891{
2892 if (rFootnote.GetNumStr().isEmpty())
2894 else
2895 m_aRun->append(
2897}
2898
2900{
2901 SAL_INFO("sw.rtf", __func__ << " start");
2902
2904 EndRunProperties(nullptr);
2905 m_aRun->append(' ');
2906 WriteTextFootnoteNumStr(rFootnote);
2910 m_aRun->append(' ');
2911 WriteTextFootnoteNumStr(rFootnote);
2912
2913 /*
2914 * The footnote contains a whole paragraph, so we have to:
2915 * 1) Reset, then later restore the contents of our run buffer and run state.
2916 * 2) Buffer the output of the whole paragraph, as we do so for section headers already.
2917 */
2918 const SwNodeIndex* pIndex = rFootnote.GetTextFootnote()->GetStartNode();
2919 RtfStringBuffer aRun = m_aRun;
2920 m_aRun.clear();
2921 bool bInRunOrig = m_bInRun;
2922 m_bInRun = false;
2923 bool bSingleEmptyRunOrig = m_bSingleEmptyRun;
2924 m_bSingleEmptyRun = false;
2926 m_rExport.WriteSpecialText(pIndex->GetIndex() + 1, pIndex->GetNode().EndOfSectionIndex(),
2927 !rFootnote.IsEndNote() ? TXT_FTN : TXT_EDN);
2929 m_bInRun = bInRunOrig;
2930 m_bSingleEmptyRun = bSingleEmptyRunOrig;
2931 m_aRun = aRun;
2933 m_aSectionHeaders.setLength(0);
2934
2935 m_aRun->append("}");
2936 m_aRun->append("}");
2937
2938 SAL_INFO("sw.rtf", __func__ << " end");
2939}
2940
2941void RtfAttributeOutput::ParaLineSpacing_Impl(short nSpace, short nMulti)
2942{
2944 m_aStyles.append(static_cast<sal_Int32>(nSpace));
2946 m_aStyles.append(static_cast<sal_Int32>(nMulti));
2947}
2948
2950{
2951 switch (rAdjust.GetAdjust())
2952 {
2953 case SvxAdjust::Left:
2955 break;
2956 case SvxAdjust::Right:
2958 break;
2959 case SvxAdjust::BlockLine:
2960 case SvxAdjust::Block:
2961 if (rAdjust.GetLastBlock() == SvxAdjust::Block)
2963 else
2965 break;
2966 case SvxAdjust::Center:
2968 break;
2969 default:
2970 break;
2971 }
2972}
2973
2975{
2976 if (!rSplit.GetValue())
2978}
2979
2981{
2982 if (rWidows.GetValue())
2984 else
2986}
2987
2989{
2990 tools::Long nOffset = 0;
2991 // Tabs are absolute by default.
2994 nOffset = m_rExport.GetItem(RES_LR_SPACE).GetTextLeft();
2995
2996 for (sal_uInt16 n = 0; n < rTabStop.Count(); n++)
2997 {
2998 const SvxTabStop& rTS = rTabStop[n];
2999 if (SvxTabAdjust::Default != rTS.GetAdjustment())
3000 {
3001 const char* pFill = nullptr;
3002 switch (rTS.GetFill())
3003 {
3004 case cDfltFillChar:
3005 break;
3006
3007 case '.':
3009 break;
3010 case '_':
3012 break;
3013 case '-':
3015 break;
3016 case '=':
3018 break;
3019 default:
3020 break;
3021 }
3022 if (pFill)
3023 m_aStyles.append(pFill);
3024
3025 const char* pAdjStr = nullptr;
3026 switch (rTS.GetAdjustment())
3027 {
3028 case SvxTabAdjust::Right:
3030 break;
3031 case SvxTabAdjust::Decimal:
3033 break;
3034 case SvxTabAdjust::Center:
3036 break;
3037 default:
3038 break;
3039 }
3040 if (pAdjStr)
3041 m_aStyles.append(pAdjStr);
3043 m_aStyles.append(static_cast<sal_Int32>(rTS.GetTabPos() + nOffset));
3044 }
3045 else
3046 {
3048 m_aTabStop.append(rTabStop[0].GetTabPos());
3049 }
3050 }
3051}
3052
3054{
3056 m_aStyles.append(sal_Int32(rHyphenZone.IsHyphen()));
3057}
3058
3059void RtfAttributeOutput::ParaNumRule_Impl(const SwTextNode* pTextNd, sal_Int32 nLvl,
3060 sal_Int32 nNumId)
3061{
3062 if (USHRT_MAX == nNumId || 0 == nNumId || nullptr == pTextNd)
3063 return;
3064
3065 const SwNumRule* pRule = pTextNd->GetNumRule();
3066
3067 if (!pRule || !pTextNd->IsInList())
3068 return;
3069
3070 SAL_WARN_IF(pTextNd->GetActualListLevel() < 0 || pTextNd->GetActualListLevel() >= MAXLEVEL,
3071 "sw.rtf", "text node does not have valid list level");
3072
3073 const SwNumFormat* pFormat = pRule->GetNumFormat(nLvl);
3074 if (!pFormat)
3075 pFormat = &pRule->Get(nLvl);
3076
3077 const SfxItemSet& rNdSet = pTextNd->GetSwAttrSet();
3078
3079 m_aStyles.append('{');
3083 m_aStyles.append(' ');
3084
3085 SvxLRSpaceItem aLR(rNdSet.Get(RES_LR_SPACE));
3086 aLR.SetTextLeft(aLR.GetTextLeft() + pFormat->GetIndentAt());
3087 aLR.SetTextFirstLineOffset(pFormat->GetFirstLineOffset()); //TODO: overflow
3088
3089 sal_uInt16 nStyle = m_rExport.GetId(pFormat->GetCharFormat());
3090 OString* pString = m_rExport.GetStyle(nStyle);
3091 if (pString)
3092 m_aStyles.append(*pString);
3093
3094 {
3095 OUString sText;
3096 if (SVX_NUM_CHAR_SPECIAL == pFormat->GetNumberingType()
3097 || SVX_NUM_BITMAP == pFormat->GetNumberingType())
3098 {
3099 sal_UCS4 cBullet = pFormat->GetBulletChar();
3100 sText = OUString(&cBullet, 1);
3101 }
3102 else
3103 sText = pTextNd->GetNumString();
3104
3105 if (!sText.isEmpty())
3106 {
3107 m_aStyles.append(' ');
3109 }
3110
3111 if (OUTLINE_RULE != pRule->GetRuleType())
3112 {
3113 if (!sText.isEmpty())
3115 m_aStyles.append('}');
3117 if (nLvl > 8) // RTF knows only 9 levels
3118 {
3119 m_aStyles.append(sal_Int32(8));
3121 m_aStyles.append(nLvl);
3122 m_aStyles.append('}');
3123 }
3124 else
3125 m_aStyles.append(nLvl);
3126 }
3127 else
3130 m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetNumberingId(*pRule)) + 1);
3131 m_aStyles.append(' ');
3132 }
3133 FormatLRSpace(aLR);
3134}
3135
3137{
3138 if (!rScriptSpace.GetValue())
3139 return;
3140
3142}
3143
3145{
3146 SAL_INFO("sw.rtf", "TODO: " << __func__);
3147}
3148
3150{
3151 SAL_INFO("sw.rtf", "TODO: " << __func__);
3152}
3153
3155{
3156 const char* pStr;
3157 switch (rAlign.GetValue())
3158 {
3161 break;
3164 break;
3167 break;
3170 break;
3171
3172 default:
3174 break;
3175 }
3176 m_aStyles.append(pStr);
3177}
3178
3180{
3181 SAL_INFO("sw.rtf", "TODO: " << __func__);
3182}
3183
3185{
3187 {
3189 m_aSectionBreaks.append(static_cast<sal_Int32>(rSize.GetWidth()));
3191 m_aSectionBreaks.append(static_cast<sal_Int32>(rSize.GetHeight()));
3193 {
3195 m_aSectionBreaks.setLength(0);
3196 }
3197 }
3198}
3199
3201{
3202 SAL_INFO("sw.rtf", "TODO: " << __func__);
3203}
3204
3206{
3208 {
3210 {
3213
3214 if (const SvxBoxItem* pBoxItem = m_rExport.HasItem(RES_BOX))
3215 {
3217 = pBoxItem->CalcLineSpace(SvxBoxItemLine::LEFT, /*bEvenIfNoLine*/ true);
3219 = pBoxItem->CalcLineSpace(SvxBoxItemLine::RIGHT, /*bEvenIfNoLine*/ true);
3220 }
3221
3222 m_aPageMargins.nLeft += sal::static_int_cast<sal_uInt16>(rLRSpace.GetLeft());
3223 m_aPageMargins.nRight += sal::static_int_cast<sal_uInt16>(rLRSpace.GetRight());
3224
3225 if (rLRSpace.GetLeft())
3226 {
3228 m_aSectionBreaks.append(static_cast<sal_Int32>(m_aPageMargins.nLeft));
3229 }
3230 if (rLRSpace.GetRight())
3231 {
3233 m_aSectionBreaks.append(static_cast<sal_Int32>(m_aPageMargins.nRight));
3234 }
3235 if (rLRSpace.GetGutterMargin())
3236 {
3238 m_aSectionBreaks.append(static_cast<sal_Int32>(rLRSpace.GetGutterMargin()));
3239 }
3241 {
3243 m_aSectionBreaks.setLength(0);
3244 }
3245 }
3246 else
3247 {
3249 m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetTextLeft()));
3251 m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetRight()));
3253 m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetTextLeft()));
3255 m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetRight()));
3257 m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetTextFirstLineOffset()));
3258 }
3259 }
3260 else if (m_rExport.GetRTFFlySyntax())
3261 {
3262 // Wrap: top and bottom spacing, convert from twips to EMUs.
3263 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3264 "dxWrapDistLeft", OString::number(o3tl::convert(rLRSpace.GetLeft(), o3tl::Length::twip,
3266 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3267 "dxWrapDistRight", OString::number(o3tl::convert(
3269 }
3270}
3271
3273{
3275 {
3277 {
3278 OSL_ENSURE(m_rExport.GetCurItemSet(), "Impossible");
3279 if (!m_rExport.GetCurItemSet())
3280 return;
3281
3282 // If we export a follow page format, then our doc model has
3283 // separate header/footer distances for the first page and the
3284 // follow pages, but Word can have only a single distance. In case
3285 // the two values differ, work with the value from the first page
3286 // format to be in sync with the import.
3290
3291 if (aDistances.m_DyaTop)
3292 {
3294 m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.m_DyaTop));
3295 m_aPageMargins.nTop = aDistances.m_DyaTop;
3296 }
3297 if (aDistances.HasHeader())
3298 {
3300 m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.m_DyaHdrTop));
3301 }
3302
3303 if (aDistances.m_DyaBottom)
3304 {
3306 m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.m_DyaBottom));
3307 m_aPageMargins.nBottom = aDistances.m_DyaBottom;
3308 }
3309 if (aDistances.HasFooter())
3310 {
3312 m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.m_DyaHdrBottom));
3313 }
3315 {
3317 m_aSectionBreaks.setLength(0);
3318 }
3319 }
3320 else
3321 {
3322 // Spacing before.
3326 {
3329 m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetUpper()));
3330 }
3331 else
3332 {
3334 m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetUpper()));
3335 }
3337
3338 // Spacing after.
3342 {
3345 m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetLower()));
3346 }
3347 else
3348 {
3350 m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetLower()));
3351 }
3353
3354 // Contextual spacing.
3355 if (rULSpace.GetContext())
3357 }
3358 }
3359 else if (m_rExport.GetRTFFlySyntax())
3360 {
3361 // Wrap: top and bottom spacing, convert from twips to EMUs.
3362 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3363 "dyWrapDistTop", OString::number(o3tl::convert(rULSpace.GetUpper(), o3tl::Length::twip,
3365 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3366 "dyWrapDistBottom", OString::number(o3tl::convert(
3368 }
3369}
3370
3372{
3374 {
3375 css::text::WrapTextMode eSurround = rSurround.GetSurround();
3376 bool bGold = css::text::WrapTextMode_DYNAMIC == eSurround;
3377 if (bGold)
3378 eSurround = css::text::WrapTextMode_PARALLEL;
3379 RTFSurround aMC(bGold, static_cast<sal_uInt8>(eSurround));
3381 m_aRunText->append(static_cast<sal_Int32>(aMC.GetValue()));
3382 }
3384 {
3385 // See DocxSdrExport::startDMLAnchorInline() for SwFormatSurround -> WR / WRK mappings.
3386 sal_Int32 nWr = -1;
3387 std::optional<sal_Int32> oWrk;
3388 switch (rSurround.GetValue())
3389 {
3390 case css::text::WrapTextMode_NONE:
3391 nWr = 1; // top and bottom
3392 break;
3393 case css::text::WrapTextMode_THROUGH:
3394 nWr = 3; // none
3395 break;
3396 case css::text::WrapTextMode_PARALLEL:
3397 nWr = 2; // around
3398 oWrk = 0; // both sides
3399 break;
3400 case css::text::WrapTextMode_DYNAMIC:
3401 default:
3402 nWr = 2; // around
3403 oWrk = 3; // largest
3404 break;
3405 }
3406
3407 if (rSurround.IsContour())
3408 nWr = 4; // tight
3409
3411 m_rExport.OutLong(nWr);
3412 if (oWrk)
3413 {
3415 m_rExport.OutLong(*oWrk);
3416 }
3417 }
3418}
3419
3421{
3423 return;
3424
3425 switch (rFlyVert.GetRelationOrient())
3426 {
3427 case text::RelOrientation::PAGE_FRAME:
3428 m_aFlyProperties.push_back(
3429 std::make_pair<OString, OString>("posrelv", OString::number(1)));
3430 break;
3431 default:
3432 m_aFlyProperties.push_back(
3433 std::make_pair<OString, OString>("posrelv", OString::number(2)));
3434 m_rExport.Strm()
3437 break;
3438 }
3439
3440 switch (rFlyVert.GetVertOrient())
3441 {
3442 case text::VertOrientation::TOP:
3443 case text::VertOrientation::LINE_TOP:
3444 m_aFlyProperties.push_back(
3445 std::make_pair<OString, OString>("posv", OString::number(1)));
3446 break;
3447 case text::VertOrientation::BOTTOM:
3448 case text::VertOrientation::LINE_BOTTOM:
3449 m_aFlyProperties.push_back(
3450 std::make_pair<OString, OString>("posv", OString::number(3)));
3451 break;
3452 case text::VertOrientation::CENTER:
3453 case text::VertOrientation::LINE_CENTER:
3454 m_aFlyProperties.push_back(
3455 std::make_pair<OString, OString>("posv", OString::number(2)));
3456 break;
3457 default:
3458 break;
3459 }
3460
3462 m_rExport.OutLong(rFlyVert.GetPos());
3463 if (m_pFlyFrameSize)
3464 {
3467 }
3468}
3469
3471{
3473 return;
3474
3475 switch (rFlyHori.GetRelationOrient())
3476 {
3477 case text::RelOrientation::PAGE_FRAME:
3478 m_aFlyProperties.push_back(
3479 std::make_pair<OString, OString>("posrelh", OString::number(1)));
3480 break;
3481 default:
3482 m_aFlyProperties.push_back(
3483 std::make_pair<OString, OString>("posrelh", OString::number(2)));
3484 m_rExport.Strm()
3487 break;
3488 }
3489
3490 switch (rFlyHori.GetHoriOrient())
3491 {
3492 case text::HoriOrientation::LEFT:
3493 m_aFlyProperties.push_back(
3494 std::make_pair<OString, OString>("posh", OString::number(1)));
3495 break;
3496 case text::HoriOrientation::CENTER:
3497 m_aFlyProperties.push_back(
3498 std::make_pair<OString, OString>("posh", OString::number(2)));
3499 break;
3500 case text::HoriOrientation::RIGHT:
3501 m_aFlyProperties.push_back(
3502 std::make_pair<OString, OString>("posh", OString::number(3)));
3503 break;
3504 default:
3505 break;
3506 }
3507
3509 m_rExport.OutLong(rFlyHori.GetPos());
3510 if (m_pFlyFrameSize)
3511 {
3513 m_rExport.OutLong(rFlyHori.GetPos() + m_pFlyFrameSize->Width());
3514 }
3515}
3516
3518{
3520 return;
3521
3522 RndStdIds eId = rAnchor.GetAnchorId();
3524 m_aRunText->append(static_cast<sal_Int32>(eId));
3525 switch (eId)
3526 {
3527 case RndStdIds::FLY_AT_PAGE:
3529 m_aRunText->append(static_cast<sal_Int32>(rAnchor.GetPageNum()));
3530 break;
3531 case RndStdIds::FLY_AT_PARA:
3532 case RndStdIds::FLY_AS_CHAR:
3534 break;
3535 default:
3536 break;
3537 }
3538}
3539
3541{
3543 {
3544 const Color& rColor = rBrush.GetColor();
3545 // We in fact need RGB to BGR, but the transformation is symmetric.
3546 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3547 "fillColor", OString::number(wwUtility::RGBToBGR(rColor))));
3548 }
3549 else if (!rBrush.GetColor().IsTransparent())
3550 {
3552 m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(rBrush.GetColor())));
3553 }
3554}
3555
3557{
3558 m_oFillStyle = rFillStyle.GetValue();
3559}
3560
3562{
3563 if (*m_oFillStyle != drawing::FillStyle_GRADIENT)
3564 return;
3565
3566 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3567 "fillType", OString::number(7))); // Shade using the fillAngle
3568
3569 const XGradient& rGradient = rFillGradient.GetGradientValue();
3570 const Color& rStartColor = rGradient.GetStartColor();
3571 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3572 "fillBackColor", OString::number(wwUtility::RGBToBGR(rStartColor))));
3573
3574 const Color& rEndColor = rGradient.GetEndColor();
3575 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3576 "fillColor", OString::number(wwUtility::RGBToBGR(rEndColor))));
3577
3578 switch (rGradient.GetGradientStyle())
3579 {
3580 case css::awt::GradientStyle_LINEAR:
3581 break;
3582 case css::awt::GradientStyle_AXIAL:
3583 m_aFlyProperties.push_back(
3584 std::make_pair<OString, OString>("fillFocus", OString::number(50)));
3585 break;
3586 case css::awt::GradientStyle_RADIAL:
3587 case css::awt::GradientStyle_ELLIPTICAL:
3588 case css::awt::GradientStyle_SQUARE:
3589 case css::awt::GradientStyle_RECT:
3590 default:
3591 break;
3592 }
3593}
3594
3596{
3597 static const SvxBoxItemLine aBorders[] = { SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
3598 SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT };
3599 static const char* aBorderNames[]
3602
3603 sal_uInt16 const nDist = rBox.GetSmallestDistance();
3604
3606 {
3607 // Borders: spacing to contents, convert from twips to EMUs.
3608 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3609 "dxTextLeft", OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::LEFT),
3611 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3612 "dyTextTop", OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::TOP),
3614 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3615 "dxTextRight", OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::RIGHT),
3617 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3618 "dyTextBottom", OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::BOTTOM),
3620
3621 const editeng::SvxBorderLine* pLeft = rBox.GetLine(SvxBoxItemLine::LEFT);
3622 const editeng::SvxBorderLine* pRight = rBox.GetLine(SvxBoxItemLine::RIGHT);
3623 const editeng::SvxBorderLine* pTop = rBox.GetLine(SvxBoxItemLine::TOP);
3624 const editeng::SvxBorderLine* pBottom = rBox.GetLine(SvxBoxItemLine::BOTTOM);
3625
3626 if (!pLeft && !pRight && !pBottom && !pTop)
3627 {
3628 // fLine has default 'true', so need to write it out in case of no border.
3629 m_aFlyProperties.push_back(std::make_pair<OString, OString>("fLine", "0"));
3630 return;
3631 }
3632
3633 // RTF has the flags fTopLine, fBottomLine, fLeftLine and fRightLine to disable single border
3634 // lines. But Word cannot disable single border lines. So we do not use them. In case of
3635 // single border lines it is better to draw all four borders than drawing none. So we look
3636 // whether a border line exists, which is effectively drawn.
3637 const editeng::SvxBorderLine* pBorder = nullptr;
3638 if (pTop && pTop->GetBorderLineStyle() != SvxBorderLineStyle::NONE)
3639 pBorder = pTop;
3640 else if (pBottom && pBottom->GetBorderLineStyle() != SvxBorderLineStyle::NONE)
3641 pBorder = pBottom;
3642 else if (pLeft && pLeft->GetBorderLineStyle() != SvxBorderLineStyle::NONE)
3643 pBorder = pLeft;
3644 else if (pRight && pRight->GetBorderLineStyle() != SvxBorderLineStyle::NONE)
3645 pBorder = pRight;
3646
3647 if (!pBorder)
3648 {
3649 m_aFlyProperties.push_back(std::make_pair<OString, OString>("fLine", "0"));
3650 return;
3651 }
3652
3653 const Color& rColor = pBorder->GetColor();
3654 // We in fact need RGB to BGR, but the transformation is symmetric.
3655 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3656 "lineColor", OString::number(wwUtility::RGBToBGR(rColor))));
3657
3658 double const fConverted(
3660 sal_Int32 nWidth = o3tl::convert(fConverted, o3tl::Length::twip, o3tl::Length::emu);
3661 m_aFlyProperties.push_back(
3662 std::make_pair<OString, OString>("lineWidth", OString::number(nWidth)));
3663
3664 return;
3665 }
3666
3667 if (rBox.GetTop() && rBox.GetBottom() && rBox.GetLeft() && rBox.GetRight()
3668 && *rBox.GetTop() == *rBox.GetBottom() && *rBox.GetTop() == *rBox.GetLeft()
3669 && *rBox.GetTop() == *rBox.GetRight() && nDist == rBox.GetDistance(SvxBoxItemLine::TOP)
3670 && nDist == rBox.GetDistance(SvxBoxItemLine::LEFT)
3671 && nDist == rBox.GetDistance(SvxBoxItemLine::BOTTOM)
3672 && nDist == rBox.GetDistance(SvxBoxItemLine::RIGHT))
3673 m_aSectionBreaks.append(
3675 else
3676 {
3677 SvxShadowLocation eShadowLocation = SvxShadowLocation::NONE;
3678 if (const SvxShadowItem* pItem = GetExport().HasItem(RES_SHADOW))
3679 eShadowLocation = pItem->GetLocation();
3680
3681 const SvxBoxItemLine* pBrd = aBorders;
3682 const char** pBrdNms = aBorderNames;
3683 for (int i = 0; i < 4; ++i, ++pBrd, ++pBrdNms)
3684 {
3685 editeng::SvxBorderLine const* const pLn = rBox.GetLine(*pBrd);
3686 m_aSectionBreaks.append(
3687 OutBorderLine(m_rExport, pLn, *pBrdNms, rBox.GetDistance(*pBrd), eShadowLocation));
3688 }
3689 }
3690
3692 {
3694 m_aSectionBreaks.setLength(0);
3695 }
3696}
3697
3698void RtfAttributeOutput::FormatColumns_Impl(sal_uInt16 nCols, const SwFormatCol& rCol, bool bEven,
3699 SwTwips nPageSize)
3700{
3702 m_rExport.OutLong(nCols);
3703
3704 if (rCol.GetLineAdj() != COLADJ_NONE)
3706
3707 if (bEven)
3708 {
3710 m_rExport.OutLong(rCol.GetGutterWidth(true));
3711 }
3712 else
3713 {
3714 const SwColumns& rColumns = rCol.GetColumns();
3715 for (sal_uInt16 n = 0; n < nCols;)
3716 {
3718 m_rExport.OutLong(n + 1);
3719
3721 m_rExport.OutLong(rCol.CalcPrtColWidth(n, nPageSize));
3722
3723 if (++n != nCols)
3724 {
3726 m_rExport.OutLong(rColumns[n - 1].GetRight() + rColumns[n].GetLeft());
3727 }
3728 }
3729 }
3730}
3731
3733{
3734 if (rItem.GetValue())
3736}
3737
3739{
3740 SAL_INFO("sw.rtf", "TODO: " << __func__);
3741}
3742
3744{
3745 if (!rNumbering.IsCount())
3747}
3748
3750{
3751 SvxFrameDirection nDir = rDirection.GetValue();
3752 if (nDir == SvxFrameDirection::Environment)
3754
3756 {
3757 if (nDir == SvxFrameDirection::Vertical_RL_TB)
3758 {
3760 m_aSectionBreaks.append(static_cast<sal_Int32>(1));
3762 {
3764 m_aSectionBreaks.setLength(0);
3765 }
3766 }
3767 return;
3768 }
3769
3771 {
3772 if (nDir == SvxFrameDirection::Vertical_RL_TB)
3773 {
3774 // Top to bottom non-ASCII font
3775 m_aFlyProperties.push_back(std::make_pair<OString, OString>("txflTextFlow", "3"));
3776 }
3777 else if (rDirection.GetValue() == SvxFrameDirection::Vertical_LR_BT)
3778 {
3779 // Bottom to top non-ASCII font
3780 m_aFlyProperties.push_back(std::make_pair<OString, OString>("txflTextFlow", "2"));
3781 }
3782 return;
3783 }
3784
3785 if (nDir == SvxFrameDirection::Horizontal_RL_TB)
3787 else
3789}
3790
3792{
3793 const std::map<OUString, css::uno::Any>& rMap = rItem.GetGrabBag();
3794 for (const auto& rValue : rMap)
3795 {
3796 if (rValue.first == "ParaTopMarginBeforeAutoSpacing")
3797 {
3799 rValue.second >>= m_nParaBeforeSpacing;
3801 }
3802 else if (rValue.first == "ParaBottomMarginAfterAutoSpacing")
3803 {
3805 rValue.second >>= m_nParaAfterSpacing;
3807 }
3808 }
3809}
3810
3812
3814
3816{
3817 OUString sCmd; // for optional Parameters
3818 switch (pField->GetTyp()->Which())
3819 {
3820 //#i119803# Export user field for RTF filter
3821 case SwFieldIds::User:
3822 sCmd = pField->GetTyp()->GetName();
3823 m_rExport.OutputField(pField, ww::eNONE, sCmd);
3824 break;
3825 default:
3826 m_rExport.OutputField(pField, ww::eUNKNOWN, sCmd);
3827 break;
3828 }
3829}
3830
3831void RtfAttributeOutput::RefField(const SwField& /*rField*/, const OUString& /*rRef*/)
3832{
3833 SAL_INFO("sw.rtf", "TODO: " << __func__);
3834}
3835
3837{
3838 SAL_INFO("sw.rtf", "TODO: " << __func__);
3839}
3840
3841void RtfAttributeOutput::SetField(const SwField& /*rField*/, ww::eField /*eType*/,
3842 const OUString& /*rCmd*/)
3843{
3844 SAL_INFO("sw.rtf", "TODO: " << __func__);
3845}
3846
3848{
3849 const SwPostItField& rPField = *static_cast<const SwPostItField*>(pField);
3850
3851 OString aName = OUStringToOString(rPField.GetName(), RTL_TEXTENCODING_UTF8);
3852 auto it = m_rOpenedAnnotationMarksIds.find(aName);
3853 if (it != m_rOpenedAnnotationMarksIds.end())
3854 {
3855 // In case this field is inside annotation marks, we want to write the
3856 // annotation itself after the annotation mark is closed, not here.
3857 m_aPostitFields[it->second] = &rPField;
3858 return;
3859 }
3860
3863 m_aRunText->append("}");
3866 m_aRunText->append("}");
3868
3870
3872 {
3875 m_aRunText->append('}');
3876 }
3878 m_aRunText->append(static_cast<sal_Int32>(sw::ms::DateTime2DTTM(rPField.GetDateTime())));
3879 m_aRunText->append('}');
3880 if (const OutlinerParaObject* pObject = rPField.GetTextObject())
3882 m_aRunText->append('}');
3883}
3884
3886{
3887 // this is handled in OutputFlyFrame_Impl()
3888 return true;
3889}
3890
3892{
3895 " MACROBUTTON None ");
3896 RunText(pField->GetPar1());
3897 m_aRunText->append("}}");
3898 return false; // do not expand
3899}
3900
3902 : AttributeOutputBase("") // ConvertURL isn't used now in RTF output
3903 , m_rExport(rExport)
3904 , m_pPrevPageDesc(nullptr)
3905 , m_nStyleId(0)
3906 , m_nListId(0)
3907 , m_bIsRTL(false)
3908 , m_nScript(i18n::ScriptType::LATIN)
3909 , m_bControlLtrRtl(false)
3910 , m_nNextAnnotationMarkId(0)
3911 , m_nCurrentAnnotationMarkId(-1)
3912 , m_bTableCellOpen(false)
3913 , m_nTableDepth(0)
3914 , m_bTableAfterCell(false)
3915 , m_nColBreakNeeded(false)
3916 , m_bBufferSectionBreaks(false)
3917 , m_bBufferSectionHeaders(false)
3918 , m_bLastTable(true)
3919 , m_bWroteCellInfo(false)
3920 , m_bTableRowEnded(false)
3921 , m_bIsBeforeFirstParagraph(true)
3922 , m_bSingleEmptyRun(false)
3923 , m_bInRun(false)
3924 , m_bInRuby(false)
3925 , m_pFlyFrameSize(nullptr)
3926 , m_bParaBeforeAutoSpacing(false)
3927 , m_nParaBeforeSpacing(0)
3928 , m_bParaAfterAutoSpacing(false)
3929 , m_nParaAfterSpacing(0)
3930{
3931}
3932
3934
3936
3937// These are used by wwFont::WriteRtf()
3938
3940void RtfAttributeOutput::StartFont(const OUString& rFamilyName) const
3941{
3942 // write the font name hex-encoded, but without Unicode - Word at least
3943 // cannot read *both* Unicode and fallback as written by OutString
3945 msfilter::rtfutil::OutString(rFamilyName, m_rExport.GetCurrentEncoding(), false).getStr());
3946}
3947
3950{
3951 m_rExport.Strm().WriteCharPtr(";}");
3953}
3954
3956void RtfAttributeOutput::FontAlternateName(const OUString& rName) const
3957{
3958 m_rExport.Strm()
3959 .WriteChar('{')
3962 .WriteChar(' ');
3963 // write the font name hex-encoded, but without Unicode - Word at least
3964 // cannot read *both* Unicode and fallback as written by OutString
3965 m_rExport.Strm()
3966 .WriteCharPtr(
3968 .WriteChar('}');
3969}
3970
3973{
3975 m_rExport.OutULong(nCharSet);
3976 m_rExport.Strm().WriteChar(' ');
3977 m_rExport.SetCurrentEncoding(rtl_getTextEncodingFromWindowsCharset(nCharSet));
3978}
3979
3982{
3984
3985 const char* pStr = OOO_STRING_SVTOOLS_RTF_FNIL;
3986 switch (eFamily)
3987 {
3988 case FAMILY_ROMAN:
3990 break;
3991 case FAMILY_SWISS:
3993 break;
3994 case FAMILY_MODERN:
3996 break;
3997 case FAMILY_SCRIPT:
3999 break;
4000 case FAMILY_DECORATIVE:
4002 break;
4003 default:
4004 break;
4005 }
4007}
4008
4011{
4013
4014 sal_uInt16 nVal = 0;
4015 switch (ePitch)
4016 {
4017 case PITCH_FIXED:
4018 nVal = 1;
4019 break;
4020 case PITCH_VARIABLE:
4021 nVal = 2;
4022 break;
4023 default:
4024 break;
4025 }
4026 m_rExport.OutULong(nVal);
4027}
4028
4029static void lcl_AppendSP(OStringBuffer& rBuffer, const char cName[], const OUString& rValue,
4030 const RtfExport& rExport)
4031{
4032 rBuffer.append("{" OOO_STRING_SVTOOLS_RTF_SP "{"); // "{\sp{"
4033 rBuffer.append(OOO_STRING_SVTOOLS_RTF_SN " "); //" \sn "
4034 rBuffer.append(cName); //"PropName"
4035 rBuffer.append("}{" OOO_STRING_SVTOOLS_RTF_SV " ");
4036 // "}{ \sv "
4037 rBuffer.append(msfilter::rtfutil::OutString(rValue, rExport.GetCurrentEncoding()));
4038 rBuffer.append("}}");
4039}
4040
4041static OString ExportPICT(const SwFlyFrameFormat* pFlyFrameFormat, const Size& rOrig,
4042 const Size& rRendered, const Size& rMapped, const SwCropGrf& rCr,
4043 const char* pBLIPType, const sal_uInt8* pGraphicAry, sal_uInt64 nSize,
4044 const RtfExport& rExport, SvStream* pStream = nullptr,
4045 bool bWritePicProp = true, const SwAttrSet* pAttrSet = nullptr)
4046{
4047 OStringBuffer aRet;
4048 if (pBLIPType && nSize && pGraphicAry)
4049 {
4050 bool bIsWMF = std::strcmp(pBLIPType, OOO_STRING_SVTOOLS_RTF_WMETAFILE) == 0;
4051
4052 aRet.append("{" OOO_STRING_SVTOOLS_RTF_PICT);
4053
4054 if (pFlyFrameFormat && bWritePicProp)
4055 {
4056 OUString sDescription = pFlyFrameFormat->GetObjDescription();
4057 //write picture properties - wzDescription at first
4058 //looks like: "{\*\picprop{\sp{\sn PropertyName}{\sv PropertyValue}}}"
4059 aRet.append(
4061 lcl_AppendSP(aRet, "wzDescription", sDescription, rExport);
4062 OUString sName = pFlyFrameFormat->GetObjTitle();
4063 lcl_AppendSP(aRet, "wzName", sName, rExport);
4064
4065 if (pAttrSet)
4066 {
4067 MirrorGraph eMirror = pAttrSet->Get(RES_GRFATR_MIRRORGRF).GetValue();
4068 if (eMirror == MirrorGraph::Vertical || eMirror == MirrorGraph::Both)
4069 // Mirror on the vertical axis is a horizontal flip.
4070 lcl_AppendSP(aRet, "fFlipH", "1", rExport);
4071 }
4072
4073 aRet.append("}"); //"}"
4074 }
4075
4076 tools::Long nXCroppedSize = rOrig.Width() - (rCr.GetLeft() + rCr.GetRight());
4077 tools::Long nYCroppedSize = rOrig.Height() - (rCr.GetTop() + rCr.GetBottom());
4078 /* Graphic with a zero height or width, typically copied from webpages, caused crashes. */
4079 if (!nXCroppedSize)
4080 nXCroppedSize = 100;
4081 if (!nYCroppedSize)
4082 nYCroppedSize = 100;
4083
4084 //Given the original size and taking cropping into account
4085 //first, how much has the original been scaled to get the
4086 //final rendered size
4088 aRet.append(static_cast<sal_Int32>((100 * rRendered.Width()) / nXCroppedSize));
4090 aRet.append(static_cast<sal_Int32>((100 * rRendered.Height()) / nYCroppedSize));
4091
4093 aRet.append(rCr.GetLeft());
4095 aRet.append(rCr.GetRight());
4097 aRet.append(rCr.GetTop());
4099 aRet.append(rCr.GetBottom());
4100
4101 aRet.append(OOO_STRING_SVTOOLS_RTF_PICW);
4102 aRet.append(static_cast<sal_Int32>(rMapped.Width()));
4103 aRet.append(OOO_STRING_SVTOOLS_RTF_PICH);
4104 aRet.append(static_cast<sal_Int32>(rMapped.Height()));
4105
4107 aRet.append(static_cast<sal_Int32>(rOrig.Width()));
4109 aRet.append(static_cast<sal_Int32>(rOrig.Height()));
4110
4111 aRet.append(pBLIPType);
4112 if (bIsWMF)
4113 {
4114 aRet.append(sal_Int32(8));
4115 msfilter::rtfutil::StripMetafileHeader(pGraphicAry, nSize);
4116 }
4117 aRet.append(SAL_NEWLINE_STRING);
4118 if (pStream)
4119 {
4120 pStream->WriteOString(aRet);
4121 aRet.setLength(0);
4122 }
4123 if (pStream)
4124 msfilter::rtfutil::WriteHex(pGraphicAry, nSize, pStream);
4125 else
4126 aRet.append(msfilter::rtfutil::WriteHex(pGraphicAry, nSize));
4127 aRet.append('}');
4128 if (pStream)
4129 {
4130 pStream->WriteOString(aRet);
4131 aRet.setLength(0);
4132 }
4133 }
4134 return aRet.makeStringAndClear();
4135}
4136
4138 SwOLENode& rOLENode, const Size& rSize)
4139{
4141 Size aSize(rOLENode.GetTwipSize());
4142 Size aRendered(aSize);
4143 aRendered.setWidth(rSize.Width());
4144 aRendered.setHeight(rSize.Height());
4145 const Graphic* pGraphic = rOLENode.GetGraphic();
4146 Size aMapped(pGraphic->GetPrefSize());
4147 auto& rCr = rOLENode.GetAttr(RES_GRFATR_CROPGRF);
4148 const char* pBLIPType = OOO_STRING_SVTOOLS_RTF_PNGBLIP;
4149 const sal_uInt8* pGraphicAry = nullptr;
4150 SvMemoryStream aStream;
4151 if (GraphicConverter::Export(aStream, *pGraphic, ConvertDataFormat::PNG) != ERRCODE_NONE)
4152 SAL_WARN("sw.rtf", "failed to export the graphic");
4153 sal_uInt32 nSize = aStream.TellEnd();
4154 pGraphicAry = static_cast<sal_uInt8 const*>(aStream.GetData());
4155 m_aRunText->append(ExportPICT(pFlyFrameFormat, aSize, aRendered, aMapped, rCr, pBLIPType,
4156 pGraphicAry, nSize, m_rExport));
4157 m_aRunText->append("}"); // shppict
4160 SvMemoryStream aWmfStream;
4161 if (GraphicConverter::Export(aWmfStream, *pGraphic, ConvertDataFormat::WMF) != ERRCODE_NONE)
4162 SAL_WARN("sw.rtf", "failed to export the graphic");
4163 nSize = aWmfStream.TellEnd();
4164 pGraphicAry = static_cast<sal_uInt8 const*>(aWmfStream.GetData());
4165 m_aRunText->append(ExportPICT(pFlyFrameFormat, aSize, aRendered, aMapped, rCr, pBLIPType,
4166 pGraphicAry, nSize, m_rExport));
4167 m_aRunText->append("}"); // nonshppict
4168}
4169
4171 SwOLENode& rOLENode, const Size& rSize)
4172{
4173 uno::Reference<embed::XEmbeddedObject> xObj(rOLENode.GetOLEObj().GetOleRef());
4174 sal_Int64 nAspect = rOLENode.GetAspect();
4175 svt::EmbeddedObjectRef aObjRef(xObj, nAspect);
4176 SvGlobalName aObjName(aObjRef->getClassID());
4177
4178 if (!SotExchange::IsMath(aObjName))
4179 return false;
4180
4182 uno::Reference<util::XCloseable> xClosable = xObj->getComponent();
4183 if (!xClosable.is())
4184 return false;
4185 // gcc4.4 (and 4.3 and possibly older) have a problem with dynamic_cast directly to the target class,
4186 // so help it with an intermediate cast. I'm not sure what exactly the problem is, seems to be unrelated
4187 // to RTLD_GLOBAL, so most probably a gcc bug.
4188 auto pBase
4189 = dynamic_cast<oox::FormulaExportBase*>(dynamic_cast<SfxBaseModel*>(xClosable.get()));
4190 assert(pBase != nullptr);
4191 OStringBuffer aBuf;
4192 if (pBase)
4193 pBase->writeFormulaRtf(aBuf, m_rExport.GetCurrentEncoding());
4195 // Replacement graphic.
4197 FlyFrameOLEReplacement(pFlyFrameFormat, rOLENode, rSize);
4198 m_aRunText->append("}"); // mmathPict
4199 m_aRunText->append("}"); // mmath
4200
4201 return true;
4202}
4203
4204void RtfAttributeOutput::FlyFrameOLE(const SwFlyFrameFormat* pFlyFrameFormat, SwOLENode& rOLENode,
4205 const Size& rSize)
4206{
4207 if (FlyFrameOLEMath(pFlyFrameFormat, rOLENode, rSize))
4208 return;
4209
4210 FlyFrameOLEReplacement(pFlyFrameFormat, rOLENode, rSize);
4211}
4212
4214 const SwGrfNode* pGrfNode)
4215{
4216 SvMemoryStream aStream;
4217 const sal_uInt8* pGraphicAry = nullptr;
4218 sal_uInt32 nSize = 0;
4219
4220 const Graphic& rGraphic(pGrfNode->GetGrf());
4221
4222 // If there is no graphic there is not much point in parsing it
4223 if (rGraphic.GetType() == GraphicType::NONE)
4224 return;
4225
4226 ConvertDataFormat aConvertDestinationFormat = ConvertDataFormat::WMF;
4227 const char* pConvertDestinationBLIPType = OOO_STRING_SVTOOLS_RTF_WMETAFILE;
4228
4229 GfxLink aGraphicLink;
4230 const char* pBLIPType = nullptr;
4231 if (rGraphic.IsGfxLink())
4232 {
4233 aGraphicLink = rGraphic.GetGfxLink();
4234 nSize = aGraphicLink.GetDataSize();
4235 pGraphicAry = aGraphicLink.GetData();
4236 switch (aGraphicLink.GetType())
4237 {
4238 // #i15508# trying to add BMP type for better exports, need to check if this works
4239 // checked, does not work. Also need to reset pGraphicAry to NULL to force conversion
4240 // to PNG, else the BMP array will be used.
4241 // It may work using direct DIB data, but that needs to be checked eventually
4242 //
4243 // #i15508# before GfxLinkType::NativeBmp was added the graphic data
4244 // (to be hold in pGraphicAry) was not available; thus for now to stay
4245 // compatible, keep it that way by assigning NULL value to pGraphicAry
4246 case GfxLinkType::NativeBmp:
4247 // pBLIPType = OOO_STRING_SVTOOLS_RTF_WBITMAP;
4248 pGraphicAry = nullptr;
4249 break;
4250
4251 case GfxLinkType::NativeJpg:
4253 break;
4254 case GfxLinkType::NativePng:
4256 break;
4257 case GfxLinkType::NativeWmf:
4258 pBLIPType = aGraphicLink.IsEMF() ? OOO_STRING_SVTOOLS_RTF_EMFBLIP
4260 break;
4261 case GfxLinkType::NativeGif:
4262 // GIF is not supported by RTF, but we override default conversion to WMF, PNG seems fits better here.
4263 aConvertDestinationFormat = ConvertDataFormat::PNG;
4264 pConvertDestinationBLIPType = OOO_STRING_SVTOOLS_RTF_PNGBLIP;
4265 break;
4266 default:
4267 break;
4268 }
4269 }
4270
4271 GraphicType eGraphicType = rGraphic.GetType();
4272 if (!pGraphicAry)
4273 {
4274 if (ERRCODE_NONE
4275 == GraphicConverter::Export(aStream, rGraphic,
4276 (eGraphicType == GraphicType::Bitmap)
4277 ? ConvertDataFormat::PNG
4278 : ConvertDataFormat::WMF))
4279 {
4280 pBLIPType = (eGraphicType == GraphicType::Bitmap) ? OOO_STRING_SVTOOLS_RTF_PNGBLIP
4282 nSize = aStream.TellEnd();
4283 pGraphicAry = static_cast<sal_uInt8 const*>(aStream.GetData());
4284 }
4285 }
4286
4287 Size aMapped(eGraphicType == GraphicType::Bitmap ? rGraphic.GetSizePixel()
4288 : rGraphic.GetPrefSize());
4289
4290 auto& rCr = pGrfNode->GetAttr(RES_GRFATR_CROPGRF);
4291
4292 //Get original size in twips
4293 Size aSize(pGrfNode->GetTwipSize());
4294 Size aRendered(aSize);
4295
4296 const SwFormatFrameSize& rS = pFlyFrameFormat->GetFrameSize();
4297 aRendered.setWidth(rS.GetWidth());
4298 aRendered.setHeight(rS.GetHeight());
4299
4300 ww8::Frame* pFrame = nullptr;
4301 for (auto& rFrame : m_rExport.m_aFrames)
4302 {
4303 if (pFlyFrameFormat == &rFrame.GetFrameFormat())
4304 {
4305 pFrame = &rFrame;
4306 break;
4307 }
4308 }
4309
4310 /*
4311 If the graphic is not of type WMF then we will have to store two
4312 graphics, one in the native format wrapped in shppict, and the other in
4313 the wmf format wrapped in nonshppict, so as to keep wordpad happy. If it's
4314 a wmf already then we don't need any such wrapping
4315 */
4316 bool bIsWMF = pBLIPType && std::strcmp(pBLIPType, OOO_STRING_SVTOOLS_RTF_WMETAFILE) == 0;
4317 const SwAttrSet* pAttrSet = pGrfNode->GetpSwAttrSet();
4318 if (!pFrame || pFrame->IsInline())
4319 {
4320 if (!bIsWMF)
4323 }
4324 else
4325 {
4329 m_pFlyFrameSize = &aRendered;
4330 m_rExport.m_pParentFrame = pFrame;
4333 m_rExport.OutputFormat(pFrame->GetFrameFormat(), false, false, true);
4336 m_rExport.m_pParentFrame = nullptr;
4337 m_pFlyFrameSize = nullptr;
4338 std::vector<std::pair<OString, OString>> aFlyProperties{
4339 { "shapeType", OString::number(ESCHER_ShpInst_PictureFrame) },
4340
4341 { "wzDescription", msfilter::rtfutil::OutString(pFlyFrameFormat->GetObjDescription(),
4343 { "wzName", msfilter::rtfutil::OutString(pFlyFrameFormat->GetObjTitle(),
4345 };
4346
4347 // If we have a wrap polygon, then handle that here.
4348 if (pFlyFrameFormat->GetSurround().IsContour())
4349 {
4350 if (const SwNoTextNode* pNd
4351 = sw::util::GetNoTextNodeFromSwFrameFormat(*pFlyFrameFormat))
4352 {
4353 const tools::PolyPolygon* pPolyPoly = pNd->HasContour();
4354 if (pPolyPoly && pPolyPoly->Count())
4355 {
4357 *pPolyPoly, pNd, /*bCorrectCrop=*/true);
4358 OStringBuffer aVerticies;
4359 for (sal_uInt16 i = 0; i < aPoly.GetSize(); ++i)
4360 aVerticies.append(";(" + OString::number(aPoly[i].X()) + ","
4361 + OString::number(aPoly[i].Y()) + ")");
4362 aFlyProperties.push_back(std::make_pair<OString, OString>(
4363 "pWrapPolygonVertices",
4364 "8;" + OString::number(aPoly.GetSize()) + aVerticies));
4365 }
4366 }
4367 }
4368
4369 // Below text, behind document, opaque: they all refer to the same thing.
4370 if (!pFlyFrameFormat->GetOpaque().GetValue())
4371 aFlyProperties.push_back(std::make_pair<OString, OString>("fBehindDocument", "1"));
4372
4373 if (pAttrSet)
4374 {
4375