LibreOffice Module sw (master) 1
rtfattributeoutput.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
21#include <memory>
22#include <cstring>
23#include "rtfsdrexport.hxx"
24#include "writerwordglue.hxx"
25#include "ww8par.hxx"
26#include <fmtcntnt.hxx>
27#include <rtl/tencinfo.h>
28#include <sal/log.hxx>
29#include <sot/exchange.hxx>
30#include <svtools/rtfkeywd.hxx>
31#include <editeng/fontitem.hxx>
32#include <editeng/tstpitem.hxx>
34#include <editeng/spltitem.hxx>
35#include <editeng/widwitem.hxx>
36#include <editeng/postitem.hxx>
37#include <editeng/wghtitem.hxx>
38#include <editeng/kernitem.hxx>
40#include <editeng/cmapitem.hxx>
41#include <editeng/wrlmitem.hxx>
42#include <editeng/udlnitem.hxx>
43#include <editeng/langitem.hxx>
45#include <editeng/fhgtitem.hxx>
46#include <editeng/colritem.hxx>
49#include <editeng/shdditem.hxx>
57#include <editeng/blinkitem.hxx>
59#include <editeng/boxitem.hxx>
60#include <editeng/brushitem.hxx>
61#include <editeng/ulspitem.hxx>
62#include <editeng/shaditem.hxx>
63#include <editeng/keepitem.hxx>
65#include <editeng/opaqitem.hxx>
67#include <svx/svdouno.hxx>
69#include <svx/xfillit0.hxx>
70#include <svx/xflgrit.hxx>
71#include <docufld.hxx>
72#include <fmtclds.hxx>
73#include <fmtrowsplt.hxx>
74#include <fmtline.hxx>
75#include <fmtanchr.hxx>
76#include <ftninfo.hxx>
77#include <htmltbl.hxx>
78#include <ndgrf.hxx>
79#include <pagedesc.hxx>
80#include <swmodule.hxx>
81#include <txtftn.hxx>
82#include <txtinet.hxx>
83#include <grfatr.hxx>
84#include <ndole.hxx>
85#include <lineinfo.hxx>
86#include <redline.hxx>
87#include <rtf.hxx>
89#include <vcl/cvtgrf.hxx>
91#include <com/sun/star/i18n/ScriptType.hpp>
92#include <svl/grabbagitem.hxx>
93#include <frmatr.hxx>
94#include <swtable.hxx>
95#include "rtfexport.hxx"
96
97using namespace ::com::sun::star;
98using namespace sw::util;
99
100static OString OutTBLBorderLine(RtfExport const& rExport, const editeng::SvxBorderLine* pLine,
101 const char* pStr)
102{
103 OStringBuffer aRet;
104 if (pLine && !pLine->isEmpty())
105 {
106 aRet.append(pStr);
107 // single line
108 switch (pLine->GetBorderLineStyle())
109 {
110 case SvxBorderLineStyle::SOLID:
111 {
114 else
115 aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRS);
116 }
117 break;
118 case SvxBorderLineStyle::DOTTED:
120 break;
121 case SvxBorderLineStyle::DASHED:
123 break;
124 case SvxBorderLineStyle::DOUBLE:
125 case SvxBorderLineStyle::DOUBLE_THIN:
127 break;
128 case SvxBorderLineStyle::THINTHICK_SMALLGAP:
130 break;
131 case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
133 break;
134 case SvxBorderLineStyle::THINTHICK_LARGEGAP:
136 break;
137 case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
139 break;
140 case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
142 break;
143 case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
145 break;
146 case SvxBorderLineStyle::EMBOSSED:
148 break;
149 case SvxBorderLineStyle::ENGRAVED:
151 break;
152 case SvxBorderLineStyle::OUTSET:
154 break;
155 case SvxBorderLineStyle::INSET:
157 break;
158 case SvxBorderLineStyle::FINE_DASHED:
160 break;
161 case SvxBorderLineStyle::DASH_DOT:
163 break;
164 case SvxBorderLineStyle::DASH_DOT_DOT:
166 break;
167 case SvxBorderLineStyle::NONE:
168 default:
170 break;
171 }
172
173 double const fConverted(
174 ::editeng::ConvertBorderWidthToWord(pLine->GetBorderLineStyle(), pLine->GetWidth()));
175 if (255 >= pLine->GetWidth()) // That value comes from RTF specs
176 {
178 + OString::number(static_cast<sal_Int32>(fConverted)));
179 }
180 else
181 {
182 // use \brdrth to double the value range...
184 aRet.append(static_cast<sal_Int32>(fConverted) / 2);
185 }
186
188 aRet.append(static_cast<sal_Int32>(rExport.GetColor(pLine->GetColor())));
189 }
190 else // tdf#129758 "no border" may be needed to override style
191 {
192 aRet.append(pStr);
194 }
195 return aRet.makeStringAndClear();
196}
197
198static OString OutBorderLine(RtfExport const& rExport, const editeng::SvxBorderLine* pLine,
199 const char* pStr, sal_uInt16 nDist,
200 SvxShadowLocation eShadowLocation = SvxShadowLocation::NONE)
201{
202 OStringBuffer aRet;
203 aRet.append(OutTBLBorderLine(rExport, pLine, pStr));
204 if (pLine)
205 {
206 aRet.append(OOO_STRING_SVTOOLS_RTF_BRSP);
207 aRet.append(static_cast<sal_Int32>(nDist));
208 }
209 if (eShadowLocation == SvxShadowLocation::BottomRight)
210 aRet.append(LO_STRING_SVTOOLS_RTF_BRDRSH);
211 return aRet.makeStringAndClear();
212}
213
214void RtfAttributeOutput::RTLAndCJKState(bool bIsRTL, sal_uInt16 nScript)
215{
216 m_bIsRTL = bIsRTL;
217 m_nScript = nScript;
218 m_bControlLtrRtl = true;
219}
220
222 bool /*bGenerateParaId*/)
223{
226
227 // Output table/table row/table cell starts if needed
228 if (pTextNodeInfo)
229 {
230 sal_uInt32 nRow = pTextNodeInfo->getRow();
231 sal_uInt32 nCell = pTextNodeInfo->getCell();
232
233 // New cell/row?
235 {
237 pTextNodeInfo->getInnerForDepth(m_nTableDepth));
238 OSL_ENSURE(pDeepInner, "TableNodeInfoInner not found");
239 // Make sure we always start a row between ending one and starting a cell.
240 // In case of subtables, we may not get the first cell.
241 if (pDeepInner && (pDeepInner->getCell() == 0 || m_bTableRowEnded))
242 {
243 StartTableRow(pDeepInner);
244 }
245
247 }
248
249 // Again, if depth was incremented, start a new table even if we skipped the first cell.
250 if ((nRow == 0 && nCell == 0) || (m_nTableDepth == 0 && pTextNodeInfo->getDepth()))
251 {
252 // Do we have to start the table?
253 // [If we are at the right depth already, it means that we
254 // continue the table cell]
255 sal_uInt32 nCurrentDepth = pTextNodeInfo->getDepth();
256
257 if (nCurrentDepth > m_nTableDepth)
258 {
259 // Start all the tables that begin here
260 for (sal_uInt32 nDepth = m_nTableDepth + 1; nDepth <= pTextNodeInfo->getDepth();
261 ++nDepth)
262 {
264 pTextNodeInfo->getInnerForDepth(nDepth));
265
266 m_bLastTable = (nDepth == pTextNodeInfo->getDepth());
267 StartTable();
268 StartTableRow(pInner);
270 }
271
272 m_nTableDepth = nCurrentDepth;
273 }
274 }
275 }
276
277 OSL_ENSURE(m_aRun.getLength() == 0, "m_aRun is not empty");
278 return 0;
279}
280
282{
283 bool bLastPara = false;
286 {
287 // We're ending a paragraph that is the last paragraph of a footnote or endnote, or of clipboard.
288 bLastPara
290 && m_rExport.GetCurrentNodeIndex() == m_rExport.m_pCurPam->End()->GetNodeIndex();
291 }
292
293 FinishTableRowCell(pTextNodeInfoInner);
294
295 RtfStringBuffer aParagraph;
296
297 aParagraph.appendAndClear(m_aRun);
298 aParagraph->append(m_aAfterRuns);
299 m_aAfterRuns.setLength(0);
301 m_bTableAfterCell = false;
302 else
303 {
304 aParagraph->append(SAL_NEWLINE_STRING);
305 // RTF_PAR at the end of the footnote or clipboard, would cause an additional empty paragraph.
306 if (!bLastPara)
307 {
309 aParagraph->append(' ');
310 }
311 }
313 {
315 m_nColBreakNeeded = false;
316 }
317
319 aParagraph.makeStringAndClear(this);
320 else
321 m_aSectionHeaders.append(aParagraph.makeStringAndClear());
322}
323
325{
329 .WriteChar(' ');
330}
331
333{
334 SwNodeIndex aNextIndex(rNode, 1);
335 if (rNode.IsTextNode())
336 {
337 OSL_ENSURE(m_aStyles.getLength() == 0, "m_aStyles is not empty");
338
339 // output page/section breaks
341 m_aSectionBreaks.setLength(0);
343
344 // output section headers / footers
346 {
348 m_aSectionHeaders.setLength(0);
349 }
350
351 if (aNextIndex.GetNode().IsTextNode())
352 {
353 const SwTextNode* pTextNode = static_cast<SwTextNode*>(&aNextIndex.GetNode());
354 m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode);
355 // Save the current page description for now, so later we will be able to access the previous one.
356 m_pPrevPageDesc = pTextNode->FindPageDesc();
357 }
358 else if (aNextIndex.GetNode().IsTableNode())
359 {
360 const SwTableNode* pTableNode = static_cast<SwTableNode*>(&aNextIndex.GetNode());
361 const SwFrameFormat* pFormat = pTableNode->GetTable().GetFrameFormat();
362 m_rExport.OutputSectionBreaks(&(pFormat->GetAttrSet()), *pTableNode);
363 }
365 }
366 else if (rNode.IsEndNode())
367 {
368 // End of something: make sure that it's the end of a table.
369 assert(rNode.StartOfSectionNode()->IsTableNode());
370 if (aNextIndex.GetNode().IsTextNode())
371 {
372 // Handle section break between a table and a text node following it.
373 const SwTextNode* pTextNode = aNextIndex.GetNode().GetTextNode();
374 m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode);
375 }
376 }
377}
378
380{
381 OStringBuffer aPar;
383 {
384 aPar.append(OOO_STRING_SVTOOLS_RTF_PARD);
385 aPar.append(OOO_STRING_SVTOOLS_RTF_PLAIN);
386 aPar.append(' ');
387 }
390 else
391 m_aSectionHeaders.append(aPar);
392}
393
395 const SfxItemSet& /*rParagraphMarkerProperties*/, const SwRedlineData* /*pRedlineData*/,
396 const SwRedlineData* /*pRedlineParagraphMarkerDeleted*/,
397 const SwRedlineData* /*pRedlineParagraphMarkerInserted*/)
398{
399 // Do not call MoveCharacterProperties(),
400 // Otherwise associate properties in the paragraph style are ruined.
401 const OString aProperties = m_aStyles.makeStringAndClear();
403}
404
405void RtfAttributeOutput::StartRun(const SwRedlineData* pRedlineData, sal_Int32 /*nPos*/,
406 bool bSingleEmptyRun)
407{
408 SAL_INFO("sw.rtf", __func__ << ", bSingleEmptyRun: " << bSingleEmptyRun);
409
410 m_bInRun = true;
411 m_bSingleEmptyRun = bSingleEmptyRun;
413 m_aRun->append('{');
414
415 // if there is some redlining in the document, output it
416 Redline(pRedlineData);
417
418 OSL_ENSURE(m_aRunText.getLength() == 0, "m_aRunText is not empty");
419}
420
421void RtfAttributeOutput::EndRun(const SwTextNode* /*pNode*/, sal_Int32 /*nPos*/, sal_Int32 /*nLen*/,
422 bool /*bLastRun*/)
423{
426
427 if (m_bInRuby)
428 {
430 m_bInRuby = false;
431 }
432
434 m_aRun->append('}');
435 m_bInRun = false;
436}
437
439{
440 OSL_ENSURE(m_aStyles.getLength() == 0, "m_aStyles is not empty");
441}
442
444{
445 const OString aProperties = MoveCharacterProperties(true);
447}
448
450{
451 const OString aAssocHich = m_aStylesAssocHich.makeStringAndClear();
452 const OString aAssocDbch = m_aStylesAssocDbch.makeStringAndClear();
453 const OString aAssocRtlch = m_aStylesAssocRtlch.makeStringAndClear();
454 const OString aAssocLtrch = m_aStylesAssocLtrch.makeStringAndClear();
455 const OString aNormal = m_aStyles.makeStringAndClear();
456 OStringBuffer aBuf;
457
458 if (aAutoWriteRtlLtr && !m_bControlLtrRtl)
459 {
460 m_bControlLtrRtl = !aAssocRtlch.isEmpty();
461 m_bIsRTL = false;
462 m_nScript = i18n::ScriptType::LATIN;
463 }
464
465 if (m_bIsRTL)
466 {
467 if (!aAssocRtlch.isEmpty())
468 {
469 aBuf.append(OOO_STRING_SVTOOLS_RTF_LTRCH + aAssocLtrch
470 + " " OOO_STRING_SVTOOLS_RTF_RTLCH + aAssocRtlch);
471 }
472 }
473 else
474 {
475 if (!aAssocRtlch.isEmpty())
476 {
477 aBuf.append(OOO_STRING_SVTOOLS_RTF_RTLCH + aAssocRtlch
478 + " " OOO_STRING_SVTOOLS_RTF_LTRCH + aAssocLtrch);
479 }
480 if (!aAssocHich.isEmpty())
481 {
482 aBuf.append(OOO_STRING_SVTOOLS_RTF_HICH + aAssocHich);
483 }
484 if (!aNormal.isEmpty())
485 {
486 aBuf.append(OOO_STRING_SVTOOLS_RTF_LOCH + aNormal);
487 }
488 if (!aAssocDbch.isEmpty())
489 {
490 aBuf.append(OOO_STRING_SVTOOLS_RTF_DBCH + aAssocDbch);
491 }
492 }
493
495 {
496 m_bControlLtrRtl = false;
497
498 switch (m_nScript)
499 {
500 case i18n::ScriptType::LATIN:
502 break;
503 case i18n::ScriptType::ASIAN:
505 break;
506 case i18n::ScriptType::COMPLEX:
507 /* noop */
508 default:
509 /* should not happen? */
510 break;
511 }
512 }
513
514 return aBuf.makeStringAndClear();
515}
516
517void RtfAttributeOutput::RunText(const OUString& rText, rtl_TextEncoding /*eCharSet*/,
518 const OUString& /*rSymbolFont*/)
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 sal_uInt32 nRow = pTableTextNodeInfoInner->getRow();
724 if (nRow >= aRows.size())
725 {
726 SAL_WARN("sw.ww8", "RtfAttributeOutput::TableDefinition: out of range row: " << nRow);
727 return;
728 }
729 SwWriteTableRow* pRow = aRows[nRow].get();
730 SwTwips nSz = 0;
731
732 // Not using m_nTableDepth, which is not yet incremented here.
733 sal_uInt32 nCurrentDepth = pTableTextNodeInfoInner->getDepth();
734 m_aCells[nCurrentDepth] = pRow->GetCells().size();
735 for (sal_uInt32 i = 0; i < m_aCells[nCurrentDepth]; i++)
736 {
737 const SwWriteTableCell* const pCell = pRow->GetCells()[i].get();
738 const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat();
739
740 pTableTextNodeInfoInner->setCell(i);
741 TableCellProperties(pTableTextNodeInfoInner);
742
743 // Right boundary: this can't be in TableCellProperties as the old
744 // value of nSz is needed.
745 nSz += pCellFormat->GetFrameSize().GetWidth();
747 m_aRowDefs.append(static_cast<sal_Int32>(pFormat->GetLRSpace().GetLeft()
748 + rtl::math::round(nSz * fWidthRatio)));
749 }
750}
751
753 ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
754{
755 /*
756 * The function name is a bit misleading: given that we write borders
757 * before each row, we just have borders, not default ones. Additionally,
758 * this function actually writes borders for a specific cell only and is
759 * called for each cell.
760 */
761
762 const SwWriteTableRows& aRows = m_pTableWrt->GetRows();
763 SwWriteTableRow* pRow = aRows[pTableTextNodeInfoInner->getRow()].get();
764 const SwWriteTableCell* const pCell
765 = pRow->GetCells()[pTableTextNodeInfoInner->getCell()].get();
766 const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat();
767 const SvxBoxItem* pItem = pCellFormat->GetAttrSet().GetItemIfSet(RES_BOX);
768 if (!pItem)
769 return;
770
771 auto& rBox = *pItem;
772 static const SvxBoxItemLine aBorders[] = { SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
773 SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT };
774 static const char* aBorderNames[]
777 //Yes left and top are swapped with each other for cell padding! Because
778 //that's what the thundering annoying rtf export/import word xp does.
779 static const char* aCellPadNames[]
782 static const char* aCellPadUnits[]
785 for (int i = 0; i < 4; ++i)
786 {
787 if (const editeng::SvxBorderLine* pLn = rBox.GetLine(aBorders[i]))
788 m_aRowDefs.append(OutTBLBorderLine(m_rExport, pLn, aBorderNames[i]));
789 if (rBox.GetDistance(aBorders[i]))
790 {
791 m_aRowDefs.append(aCellPadUnits[i]);
792 m_aRowDefs.append(sal_Int32(3));
793 m_aRowDefs.append(aCellPadNames[i]);
794 m_aRowDefs.append(static_cast<sal_Int32>(rBox.GetDistance(aBorders[i])));
795 }
796 }
797}
798
800 ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
801{
802 const SwTable* pTable = pTableTextNodeInfoInner->getTable();
803 const SwTableBox* pTableBox = pTableTextNodeInfoInner->getTableBox();
804 const SwTableLine* pTableLine = pTableBox->GetUpper();
805
806 Color aColor = COL_AUTO;
807 auto pTableColorProp
809 if (pTableColorProp)
810 aColor = pTableColorProp->GetColor();
811
812 auto pRowColorProp
814 if (pRowColorProp && pRowColorProp->GetColor() != COL_AUTO)
815 aColor = pRowColorProp->GetColor();
816
817 const SwWriteTableRows& aRows = m_pTableWrt->GetRows();
818 SwWriteTableRow* pRow = aRows[pTableTextNodeInfoInner->getRow()].get();
819 const SwWriteTableCell* const pCell
820 = pRow->GetCells()[pTableTextNodeInfoInner->getCell()].get();
821 const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat();
822 if (const SvxBrushItem* pBrushItem = pCellFormat->GetAttrSet().GetItemIfSet(RES_BACKGROUND))
823 {
824 if (pBrushItem->GetColor() != COL_AUTO)
825 aColor = pBrushItem->GetColor();
826 }
827
828 if (!aColor.IsTransparent())
829 {
831 m_aRowDefs.append(static_cast<sal_Int32>(m_rExport.GetColor(aColor)));
832 }
833}
834
836 ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/)
837{
838}
839
841 ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/)
842{
843}
844
846{
847 const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
848 const SwTableLine* pTabLine = pTabBox->GetUpper();
849 const SwFrameFormat* pLineFormat = pTabLine->GetFrameFormat();
850 const SwFormatFrameSize& rLSz = pLineFormat->GetFrameSize();
851
852 if (!(SwFrameSize::Variable != rLSz.GetHeightSizeType() && rLSz.GetHeight()))
853 return;
854
855 sal_Int32 nHeight = 0;
856
857 switch (rLSz.GetHeightSizeType())
858 {
860 nHeight = -rLSz.GetHeight();
861 break;
863 nHeight = rLSz.GetHeight();
864 break;
865 default:
866 break;
867 }
868
869 if (nHeight)
870 {
872 m_aRowDefs.append(nHeight);
873 }
874}
875
877 ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
878{
879 const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
880 const SwTableLine* pTabLine = pTabBox->GetUpper();
881 const SwFrameFormat* pLineFormat = pTabLine->GetFrameFormat();
882 const SwFormatRowSplit& rSplittable = pLineFormat->GetRowSplit();
883
884 // The rtf default is to allow a row to break
885 if (!rSplittable.GetValue())
887}
888
890{
891 const SwTable* pTable = pTableTextNodeInfoInner->getTable();
892 const SwFrameFormat* pFrameFormat = pTable->GetFrameFormat();
893
894 if (m_rExport.TrueFrameDirection(*pFrameFormat) != SvxFrameDirection::Horizontal_RL_TB)
896 else
898}
899
901 ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
902{
903 const SwWriteTableRows& aRows = m_pTableWrt->GetRows();
904 SwWriteTableRow* pRow = aRows[pTableTextNodeInfoInner->getRow()].get();
905 const SwWriteTableCell* const pCell
906 = pRow->GetCells()[pTableTextNodeInfoInner->getCell()].get();
907 const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat();
908
909 // Text direction.
910 if (SvxFrameDirection::Vertical_RL_TB == m_rExport.TrueFrameDirection(*pCellFormat))
912 else if (SvxFrameDirection::Vertical_LR_BT == m_rExport.TrueFrameDirection(*pCellFormat))
914
915 // vertical merges
916 if (pCell->GetRowSpan() > 1)
918 else if (pCell->GetRowSpan() == 0)
920
921 // vertical alignment
922 const SwFormatVertOrient* pVertOrientItem
923 = pCellFormat->GetAttrSet().GetItemIfSet(RES_VERT_ORIENT);
924 if (!pVertOrientItem)
925 return;
926
927 switch (pVertOrientItem->GetVertOrient())
928 {
929 case text::VertOrientation::CENTER:
931 break;
932 case text::VertOrientation::BOTTOM:
934 break;
935 default:
937 break;
938 }
939}
940
942{
943 // This is called when the nested table ends in a cell, and there's no
944 // paragraph behind that; so we must check for the ends of cell, rows,
945 // and tables
946 FinishTableRowCell(pNodeInfoInner);
947}
948
950 ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)
951{
952 const SwTable* pTable = pTableTextNodeInfoInner->getTable();
953 SwFrameFormat* pFormat = pTable->GetFrameFormat();
954
955 OStringBuffer aTableAdjust(OOO_STRING_SVTOOLS_RTF_TRQL);
956 switch (pFormat->GetHoriOrient().GetHoriOrient())
957 {
958 case text::HoriOrientation::CENTER:
959 aTableAdjust.setLength(0);
960 aTableAdjust.append(OOO_STRING_SVTOOLS_RTF_TRQC);
961 break;
962 case text::HoriOrientation::RIGHT:
963 aTableAdjust.setLength(0);
964 aTableAdjust.append(OOO_STRING_SVTOOLS_RTF_TRQR);
965 break;
967 case text::HoriOrientation::LEFT_AND_WIDTH:
968 aTableAdjust.append(OOO_STRING_SVTOOLS_RTF_TRLEFT);
969 aTableAdjust.append(static_cast<sal_Int32>(pFormat->GetLRSpace().GetLeft()));
970 break;
971 default:
972 break;
973 }
974
975 m_aRowDefs.append(aTableAdjust);
976}
977
979 ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/)
980{
981 SAL_INFO("sw.rtf", "TODO: " << __func__);
982}
983
984void RtfAttributeOutput::TableRowEnd(sal_uInt32 /*nDepth*/) { /* noop, see EndTableRow() */}
985
986/*
987 * Our private table methods.
988 */
989
991 const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
992{
993 const SwTable* pTable = pTableTextNodeInfoInner->getTable();
994 if (m_pTableWrt && pTable == m_pTableWrt->GetTable())
995 return;
996
997 tools::Long nPageSize = 0;
998 bool bRelBoxSize = false;
999
1000 // Create the SwWriteTable instance to use col spans
1001 GetTablePageSize(pTableTextNodeInfoInner.get(), nPageSize, bRelBoxSize);
1002
1003 const SwFrameFormat* pFormat = pTable->GetFrameFormat();
1004 const sal_uInt32 nTableSz = pFormat->GetFrameSize().GetWidth();
1005
1006 const SwHTMLTableLayout* pLayout = pTable->GetHTMLTableLayout();
1007 if (pLayout && pLayout->IsExportable())
1008 m_pTableWrt = std::make_unique<SwWriteTable>(pTable, pLayout);
1009 else
1010 m_pTableWrt = std::make_unique<SwWriteTable>(pTable, pTable->GetTabLines(), nPageSize,
1011 nTableSz, false);
1012}
1013
1015{
1016 // To trigger calling InitTableHelper()
1017 m_pTableWrt.reset();
1018}
1019
1021 const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
1022{
1023 sal_uInt32 nCurrentDepth = pTableTextNodeInfoInner->getDepth();
1024 SAL_INFO("sw.rtf", __func__ << ", (depth is " << nCurrentDepth << ")");
1025 m_bTableRowEnded = false;
1026
1027 TableDefinition(pTableTextNodeInfoInner);
1028
1029 if (!m_bLastTable)
1030 m_aTables.push_back(m_aRowDefs.makeStringAndClear());
1031
1032 // We'll write the table definition for nested tables later
1033 if (nCurrentDepth > 1)
1034 return;
1035 // Empty the previous row closing buffer before starting the new one,
1036 // necessary for subtables.
1038 m_aAfterRuns.setLength(0);
1040 m_aRowDefs.setLength(0);
1041}
1042
1044
1046 const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
1047{
1048 TableDefaultBorders(pTableTextNodeInfoInner);
1049 TableBackgrounds(pTableTextNodeInfoInner);
1050 TableVerticalCell(pTableTextNodeInfoInner);
1051}
1052
1054{
1055 SAL_INFO("sw.rtf", __func__ << ", (depth is " << m_nTableDepth << ")");
1056
1057 if (!m_bWroteCellInfo)
1058 {
1061 m_aAfterRuns.append(static_cast<sal_Int32>(m_nTableDepth));
1062 }
1063 if (m_nTableDepth > 1)
1065 else
1067
1068 m_bTableCellOpen = false;
1069 m_bTableAfterCell = true;
1070 m_bWroteCellInfo = false;
1071 if (m_aCells[m_nTableDepth] > 0)
1073}
1074
1076{
1077 SAL_INFO("sw.rtf", __func__ << ", (depth is " << m_nTableDepth << ")");
1078
1079 // Trying to end the row without writing the required number of cells? Fill with empty ones.
1080 for (sal_uInt32 i = 0; i < m_aCells[m_nTableDepth]; i++)
1082
1083 if (m_nTableDepth > 1)
1084 {
1085 m_aAfterRuns.append(
1087 if (!m_aRowDefs.isEmpty())
1088 {
1089 m_aAfterRuns.append(m_aRowDefs);
1090 m_aRowDefs.setLength(0);
1091 }
1092 else if (!m_aTables.empty())
1093 {
1094 m_aAfterRuns.append(m_aTables.back());
1095 m_aTables.pop_back();
1096 }
1098 "}"
1100 }
1101 else
1102 {
1103 if (!m_aTables.empty())
1104 {
1105 m_aAfterRuns.append(m_aTables.back());
1106 m_aTables.pop_back();
1107 }
1109 }
1110 m_bTableRowEnded = true;
1111}
1112
1114{
1115 if (m_nTableDepth > 0)
1116 {
1117 m_nTableDepth--;
1118 m_pTableWrt.reset();
1119 }
1120
1121 // We closed the table; if it is a nested table, the cell that contains it
1122 // still continues
1123 m_bTableCellOpen = true;
1124
1125 // Cleans the table helper
1126 m_pTableWrt.reset();
1127}
1128
1130{
1131 if (!pInner)
1132 return;
1133
1134 // Where are we in the table
1135 sal_uInt32 nRow = pInner->getRow();
1136
1137 const SwTable* pTable = pInner->getTable();
1138 const SwTableLines& rLines = pTable->GetTabLines();
1139 sal_uInt16 nLinesCount = rLines.size();
1140
1141 if (pInner->isEndOfCell())
1142 EndTableCell();
1143
1144 // This is a line end
1145 if (pInner->isEndOfLine())
1146 EndTableRow();
1147
1148 // This is the end of the table
1149 if (pInner->isEndOfLine() && (nRow + 1) == nLinesCount)
1150 EndTable();
1151}
1152
1154{
1155 m_rExport.Strm()
1157 .WriteChar('{')
1160 OSL_ENSURE(m_aStylesheet.getLength() == 0, "m_aStylesheet is not empty");
1162 m_aStylesheet.append('{');
1164}
1165
1166void RtfAttributeOutput::EndStyles(sal_uInt16 /*nNumberOfStyles*/)
1167{
1168 m_rExport.Strm().WriteChar('}');
1170 m_aStylesheet.setLength(0);
1171 m_rExport.Strm().WriteChar('}');
1172}
1173
1174void RtfAttributeOutput::DefaultStyle() { /* noop, the default style is always 0 in RTF */}
1175
1176void RtfAttributeOutput::StartStyle(const OUString& rName, StyleType eType, sal_uInt16 nBase,
1177 sal_uInt16 nNext, sal_uInt16 /*nLink*/, sal_uInt16 /*nWwId*/,
1178 sal_uInt16 nSlot, bool bAutoUpdate)
1179{
1180 SAL_INFO("sw.rtf", __func__ << ", rName = '" << rName << "'");
1181
1182 m_aStylesheet.append('{');
1183 if (eType == STYLE_TYPE_PARA)
1185 else
1187 m_aStylesheet.append(static_cast<sal_Int32>(nSlot));
1188
1189 if (nBase != 0x0FFF)
1190 {
1192 m_aStylesheet.append(static_cast<sal_Int32>(nBase));
1193 }
1194
1196 m_aStylesheet.append(static_cast<sal_Int32>(nNext));
1197
1198 if (bAutoUpdate)
1200
1201 m_rStyleName = rName;
1202 m_nStyleId = nSlot;
1203}
1204
1206{
1207 OString aStyles = MoveCharacterProperties();
1208 m_rExport.InsStyle(m_nStyleId, aStyles);
1209 m_aStylesheet.append(aStyles);
1210 m_aStylesheet.append(' ');
1211 m_aStylesheet.append(
1213 m_aStylesheet.append(";}");
1215}
1216
1217void RtfAttributeOutput::StartStyleProperties(bool /*bParProp*/, sal_uInt16 /*nStyle*/)
1218{
1219 /* noop */
1220}
1221
1222void RtfAttributeOutput::EndStyleProperties(bool /*bParProp*/) { /* noop */}
1223
1225{
1226 if (nLvl >= WW8ListManager::nMaxLevel)
1227 nLvl = WW8ListManager::nMaxLevel - 1;
1228
1230 m_aStyles.append(static_cast<sal_Int32>(nLvl));
1232 m_aStyles.append(static_cast<sal_Int32>(nLvl));
1233}
1234
1236{
1237 if (bBreak)
1238 {
1240 }
1241}
1242
1243void RtfAttributeOutput::SectionBreak(sal_uInt8 nC, bool /*bBreakAfter*/,
1244 const WW8_SepInfo* pSectionInfo, bool /*bExtraPageBreak*/)
1245{
1246 switch (nC)
1247 {
1249 m_nColBreakNeeded = true;
1250 break;
1251 case msword::PageBreak:
1252 if (pSectionInfo)
1253 m_rExport.SectionProperties(*pSectionInfo);
1254 break;
1255 }
1256}
1257
1259{
1261 return;
1262
1265 {
1267 m_aSectionBreaks.setLength(0);
1268 }
1269}
1270
1272{
1273 /*
1274 * noop, \sect must go to StartSection or Word won't notice multiple
1275 * columns...
1276 */
1277}
1278
1280{
1282 m_aSectionBreaks.append(static_cast<sal_Int32>(!bProtected));
1283}
1284
1286 const SwLineNumberInfo& rLnNumInfo)
1287{
1289 m_rExport.OutLong(rLnNumInfo.GetCountBy());
1291 m_rExport.OutLong(rLnNumInfo.GetPosFromLeft());
1292 if (!rLnNumInfo.IsRestartEachPage())
1294
1295 if (nRestartNo > 0)
1296 {
1298 m_rExport.OutLong(nRestartNo);
1299 }
1300}
1301
1303{
1304 /*
1305 * noop, handled in RtfExport::WriteHeaderFooter()
1306 */
1307}
1308
1310 const SwFrameFormat* /*pFirstPageFormat*/)
1311{
1312 const SvxBoxItem& rBox = pFormat->GetBox();
1315
1316 if (aDistances.bFromEdge)
1317 {
1318 sal_uInt16 nOpt = (1 << 5);
1320 m_aSectionBreaks.append(static_cast<sal_Int32>(nOpt));
1321 }
1322
1323 const editeng::SvxBorderLine* pLine = rBox.GetTop();
1324 if (pLine)
1325 m_aSectionBreaks.append(
1327 pLine = rBox.GetBottom();
1328 if (pLine)
1329 m_aSectionBreaks.append(
1331 pLine = rBox.GetLeft();
1332 if (pLine)
1333 m_aSectionBreaks.append(
1335 pLine = rBox.GetRight();
1336 if (pLine)
1337 m_aSectionBreaks.append(
1339}
1340
1342{
1345}
1346
1348 const ::std::optional<sal_uInt16>& oPageRestartNumber)
1349{
1350 if (oPageRestartNumber)
1351 {
1353 m_aSectionBreaks.append(static_cast<sal_Int32>(*oPageRestartNumber));
1355 }
1356
1357 const char* pStr = nullptr;
1358 switch (nNumType)
1359 {
1363 break;
1367 break;
1370 break;
1373 break;
1374
1375 case SVX_NUM_ARABIC:
1377 break;
1378 }
1379 if (pStr)
1380 m_aSectionBreaks.append(pStr);
1381}
1382
1384{
1385 SAL_INFO("sw.rtf", __func__ << ", nBreakCode = " << int(nBreakCode));
1386
1387 /*
1388 * break code: 0 No break, 1 New column
1389 * 2 New page, 3 Even page, 4 Odd page
1390 */
1391 const char* sType = nullptr;
1392 switch (nBreakCode)
1393 {
1394 case 1:
1396 break;
1397 case 2:
1399 break;
1400 case 3:
1402 break;
1403 case 4:
1405 break;
1406 default:
1408 break;
1409 }
1410 m_aSectionBreaks.append(sType);
1412 {
1414 m_aSectionBreaks.setLength(0);
1415 }
1416}
1417
1419{
1422}
1423
1425{
1426 const char* pOut = nullptr;
1427
1428 if (bFootnote)
1429 {
1430 switch (rInfo.m_aFormat.GetNumberingType())
1431 {
1432 default:
1434 break;
1438 break;
1442 break;
1445 break;
1448 break;
1451 break;
1452 }
1453 }
1454 else
1455 {
1456 switch (rInfo.m_aFormat.GetNumberingType())
1457 {
1458 default:
1460 break;
1464 break;
1468 break;
1471 break;
1474 break;
1477 break;
1478 }
1479 }
1480
1481 m_aSectionBreaks.append(pOut);
1482
1484 {
1486 m_aSectionBreaks.setLength(0);
1487 }
1488}
1489
1490void RtfAttributeOutput::NumberingDefinition(sal_uInt16 nId, const SwNumRule& /*rRule*/)
1491{
1498}
1499
1501{
1502 m_rExport.Strm()
1503 .WriteChar('{')
1507 m_nListId = nId;
1508}
1509
1511{
1514}
1515
1516void RtfAttributeOutput::NumberingLevel(sal_uInt8 nLevel, sal_uInt16 nStart,
1517 sal_uInt16 nNumberingType, SvxAdjust eAdjust,
1518 const sal_uInt8* pNumLvlPos, sal_uInt8 nFollow,
1519 const wwFont* pFont, const SfxItemSet* pOutSet,
1520 sal_Int16 nIndentAt, sal_Int16 nFirstLineIndex,
1521 sal_Int16 /*nListTabPos*/, const OUString& rNumberingString,
1522 const SvxBrushItem* pBrush)
1523{
1525 if (nLevel > 8) // RTF knows only 9 levels
1526 m_rExport.Strm()
1529
1531
1532 sal_uInt16 nVal = 0;
1533 switch (nNumberingType)
1534 {
1536 nVal = 1;
1537 break;
1539 nVal = 2;
1540 break;
1543 nVal = 3;
1544 break;
1547 nVal = 4;
1548 break;
1550 nVal = 14;
1551 break;
1553 nVal = 18;
1554 break;
1556 nVal = 35;
1557 if (pOutSet)
1558 {
1559 const SvxLanguageItem& rLang = pOutSet->Get(RES_CHRATR_CJK_LANGUAGE);
1561 {
1562 nVal = 39;
1563 }
1564 }
1565 break;
1567 nVal = 38;
1568 break;
1570 nVal = 34;
1571 break;
1573 nVal = 30;
1574 break;
1575 case SVX_NUM_DI_ZI_ZH:
1576 nVal = 31;
1577 break;
1579 nVal = 16;
1580 break;
1582 nVal = 20;
1583 break;
1585 nVal = 12;
1586 break;
1588 nVal = 21;
1589 break;
1591 nVal = 13;
1592 break;
1593 case style::NumberingType::HANGUL_SYLLABLE_KO:
1594 nVal = 24;
1595 break; // ganada
1596 case style::NumberingType::HANGUL_JAMO_KO:
1597 nVal = 25;
1598 break; // chosung
1599 case style::NumberingType::HANGUL_CIRCLED_SYLLABLE_KO:
1600 nVal = 24;
1601 break;
1602 case style::NumberingType::HANGUL_CIRCLED_JAMO_KO:
1603 nVal = 25;
1604 break;
1605 case style::NumberingType::NUMBER_HANGUL_KO:
1606 nVal = 42;
1607 break; // koreanCounting
1608 case style::NumberingType::NUMBER_DIGITAL_KO:
1609 nVal = 41; // koreanDigital
1610 break;
1611 case style::NumberingType::NUMBER_DIGITAL2_KO:
1612 nVal = 44; // koreanDigital2
1613 break;
1614 case style::NumberingType::NUMBER_LEGAL_KO:
1615 nVal = 43; // koreanLegal
1616 break;
1617
1618 case SVX_NUM_BITMAP:
1620 nVal = 23;
1621 break;
1623 nVal = 255;
1624 break;
1626 nVal = 22;
1627 break;
1628 }
1630 m_rExport.OutULong(nVal);
1631
1632 switch (eAdjust)
1633 {
1634 case SvxAdjust::Center:
1635 nVal = 1;
1636 break;
1637 case SvxAdjust::Right:
1638 nVal = 2;
1639 break;
1640 default:
1641 nVal = 0;
1642 break;
1643 }
1645 m_rExport.OutULong(nVal);
1646
1647 // bullet
1648 if (nNumberingType == SVX_NUM_BITMAP && pBrush)
1649 {
1650 int nIndex = m_rExport.GetGrfIndex(*pBrush);
1651 if (nIndex != -1)
1652 {
1655 }
1656 }
1657
1659 m_rExport.OutULong(nStart);
1660
1662 m_rExport.OutULong(nFollow);
1663
1664 // leveltext group
1666
1667 if (SVX_NUM_CHAR_SPECIAL == nNumberingType || SVX_NUM_BITMAP == nNumberingType)
1668 {
1669 m_rExport.Strm().WriteCharPtr("\\'01");
1670 sal_Unicode cChar = rNumberingString[0];
1671 m_rExport.Strm().WriteCharPtr("\\u");
1672 m_rExport.OutULong(cChar);
1673 m_rExport.Strm().WriteCharPtr(" ?");
1674 }
1675 else
1676 {
1678 msfilter::rtfutil::OutHex(rNumberingString.getLength(), 2).getStr());
1681 /*bUnicode =*/false)
1682 .getStr());
1683 }
1684
1685 m_rExport.Strm().WriteCharPtr(";}");
1686
1687 // write the levelnumbers
1689 for (sal_uInt8 i = 0; i <= nLevel && pNumLvlPos[i]; ++i)
1690 {
1692 msfilter::rtfutil::OutHex(pNumLvlPos[i], 2).getStr());
1693 }
1694 m_rExport.Strm().WriteCharPtr(";}");
1695
1696 if (pOutSet)
1697 {
1698 if (pFont)
1699 {
1702 }
1703 m_rExport.OutputItemSet(*pOutSet, false, true, i18n::ScriptType::LATIN,
1705 const OString aProperties = MoveCharacterProperties(true);
1707 }
1708
1711 m_rExport.OutLong(nIndentAt);
1712
1713 m_rExport.Strm().WriteChar('}');
1714 if (nLevel > 8)
1715 m_rExport.Strm().WriteChar('}');
1716}
1717
1718void RtfAttributeOutput::WriteField_Impl(const SwField* const pField, ww::eField /*eType*/,
1719 const OUString& rFieldCmd, FieldFlags nMode)
1720{
1721 // If there are no field instructions, don't export it as a field.
1722 bool bHasInstructions = !rFieldCmd.isEmpty();
1723 if (FieldFlags::All == nMode)
1724 {
1725 if (bHasInstructions)
1726 {
1728 if (pField && (pField->GetSubType() & FIXEDFLD))
1731 " ");
1735 }
1736 if (pField)
1739 if (bHasInstructions)
1740 m_aRunText->append("}}");
1741 }
1742 else
1743 {
1744 if (nMode & FieldFlags::CmdStart)
1745 {
1748 // paragraph break closes group so open another one "inside" to
1749 " {"); // prevent leaving the field instruction
1750 }
1751 if (bHasInstructions)
1754 if (nMode & FieldFlags::CmdEnd)
1755 {
1757 }
1758 if (nMode & FieldFlags::Close)
1759 {
1760 m_aRunText->append("}}}");
1761 }
1762 }
1763}
1764
1765void RtfAttributeOutput::WriteBookmarks_Impl(std::vector<OUString>& rStarts,
1766 std::vector<OUString>& rEnds)
1767{
1768 for (const auto& rStart : rStarts)
1769 {
1772 m_aRunText->append('}');
1773 }
1774 rStarts.clear();
1775
1776 for (const auto& rEnd : rEnds)
1777 {
1780 m_aRunText->append('}');
1781 }
1782 rEnds.clear();
1783}
1784
1785void RtfAttributeOutput::WriteAnnotationMarks_Impl(std::vector<OUString>& rStarts,
1786 std::vector<OUString>& rEnds)
1787{
1788 for (const auto& rStart : rStarts)
1789 {
1790 OString rName = OUStringToOString(rStart, RTL_TEXTENCODING_UTF8);
1791
1792 // Output the annotation mark
1793 const sal_Int32 nId = m_nNextAnnotationMarkId++;
1796 m_aRun->append(nId);
1797 m_aRun->append('}');
1798 }
1799 rStarts.clear();
1800
1801 for (const auto& rEnd : rEnds)
1802 {
1803 OString rName = OUStringToOString(rEnd, RTL_TEXTENCODING_UTF8);
1804
1805 // Get the id of the annotation mark
1806 auto it = m_rOpenedAnnotationMarksIds.find(rName);
1807 if (it != m_rOpenedAnnotationMarksIds.end())
1808 {
1809 const sal_Int32 nId = it->second;
1811 m_aRun->append(nId);
1812 m_aRun->append('}');
1813 m_rOpenedAnnotationMarksIds.erase(rName);
1814
1815 if (m_aPostitFields.find(nId) != m_aPostitFields.end())
1816 {
1817 m_aRunText->append("{");
1821 m_aRunText->append("}");
1822 }
1823 }
1824 }
1825 rEnds.clear();
1826}
1827
1829 const char* pStr, bool bTitlepg)
1830{
1831 OStringBuffer aSectionBreaks = m_aSectionBreaks;
1832 m_aSectionBreaks.setLength(0);
1833 RtfStringBuffer aRun = m_aRun;
1834 m_aRun.clear();
1835
1838 m_aSectionHeaders.append(
1839 static_cast<sal_Int32>(m_rExport.m_pCurrentPageDesc->GetMaster().GetULSpace().GetUpper()));
1840 if (bTitlepg)
1842 m_aSectionHeaders.append('{');
1843 m_aSectionHeaders.append(pStr);
1845 m_rExport.WriteHeaderFooterText(rFormat, bHeader);
1847 m_aSectionHeaders.append('}');
1848
1849 m_aSectionBreaks = aSectionBreaks;
1850 m_aRun = aRun;
1851}
1852
1853namespace
1854{
1855void lcl_TextFrameShadow(std::vector<std::pair<OString, OString>>& rFlyProperties,
1856 const SwFrameFormat& rFrameFormat)
1857{
1858 const SvxShadowItem& aShadowItem = rFrameFormat.GetShadow();
1859 if (aShadowItem.GetLocation() == SvxShadowLocation::NONE)
1860 return;
1861
1862 rFlyProperties.push_back(std::make_pair<OString, OString>("fShadow", OString::number(1)));
1863
1864 const Color& rColor = aShadowItem.GetColor();
1865 // We in fact need RGB to BGR, but the transformation is symmetric.
1866 rFlyProperties.push_back(std::make_pair<OString, OString>(
1867 "shadowColor", OString::number(wwUtility::RGBToBGR(rColor))));
1868
1869 // Twips -> points -> EMUs -- hacky, the intermediate step hides rounding errors on roundtrip.
1870 OString aShadowWidth = OString::number(sal_Int32(aShadowItem.GetWidth() / 20) * 12700);
1871 OString aOffsetX;
1872 OString aOffsetY;
1873 switch (aShadowItem.GetLocation())
1874 {
1875 case SvxShadowLocation::TopLeft:
1876 aOffsetX = "-" + aShadowWidth;
1877 aOffsetY = "-" + aShadowWidth;
1878 break;
1879 case SvxShadowLocation::TopRight:
1880 aOffsetX = aShadowWidth;
1881 aOffsetY = "-" + aShadowWidth;
1882 break;
1883 case SvxShadowLocation::BottomLeft:
1884 aOffsetX = "-" + aShadowWidth;
1885 aOffsetY = aShadowWidth;
1886 break;
1887 case SvxShadowLocation::BottomRight:
1888 aOffsetX = aShadowWidth;
1889 aOffsetY = aShadowWidth;
1890 break;
1891 case SvxShadowLocation::NONE:
1892 case SvxShadowLocation::End:
1893 break;
1894 }
1895 if (!aOffsetX.isEmpty())
1896 rFlyProperties.emplace_back("shadowOffsetX", aOffsetX);
1897 if (!aOffsetY.isEmpty())
1898 rFlyProperties.emplace_back("shadowOffsetY", aOffsetY);
1899}
1900
1901void lcl_TextFrameRelativeSize(std::vector<std::pair<OString, OString>>& rFlyProperties,
1902 const SwFrameFormat& rFrameFormat)
1903{
1904 const SwFormatFrameSize& rSize = rFrameFormat.GetFrameSize();
1905
1906 // Relative size of the Text Frame.
1907 const sal_uInt8 nWidthPercent = rSize.GetWidthPercent();
1908 if (nWidthPercent && nWidthPercent != SwFormatFrameSize::SYNCED)
1909 {
1910 rFlyProperties.push_back(
1911 std::make_pair<OString, OString>("pctHoriz", OString::number(nWidthPercent * 10)));
1912
1913 OString aRelation;
1914 switch (rSize.GetWidthPercentRelation())
1915 {
1916 case text::RelOrientation::PAGE_FRAME:
1917 aRelation = "1"; // page
1918 break;
1919 default:
1920 aRelation = "0"; // margin
1921 break;
1922 }
1923 rFlyProperties.emplace_back(std::make_pair("sizerelh", aRelation));
1924 }
1925 const sal_uInt8 nHeightPercent = rSize.GetHeightPercent();
1926 if (!(nHeightPercent && nHeightPercent != SwFormatFrameSize::SYNCED))
1927 return;
1928
1929 rFlyProperties.push_back(
1930 std::make_pair<OString, OString>("pctVert", OString::number(nHeightPercent * 10)));
1931
1932 OString aRelation;
1933 switch (rSize.GetHeightPercentRelation())
1934 {
1935 case text::RelOrientation::PAGE_FRAME:
1936 aRelation = "1"; // page
1937 break;
1938 default:
1939 aRelation = "0"; // margin
1940 break;
1941 }
1942 rFlyProperties.emplace_back(std::make_pair("sizerelv", aRelation));
1943}
1944}
1945
1946void RtfAttributeOutput::writeTextFrame(const ww8::Frame& rFrame, bool bTextBox)
1947{
1948 RtfStringBuffer aRunText;
1949 if (bTextBox)
1950 {
1952 aRunText = m_aRunText;
1953 m_aRunText.clear();
1954 }
1955
1957
1958 {
1959 // Save table state, in case the inner text also contains a table.
1961 m_rExport.m_pTableInfo = std::make_shared<ww8::WW8TableInfo>();
1962 std::unique_ptr<SwWriteTable> pTableWrt(std::move(m_pTableWrt));
1963 sal_uInt32 nTableDepth = m_nTableDepth;
1964
1965 m_nTableDepth = 0;
1966 /*
1967 * Save m_aRun as we should not lose the opening brace.
1968 * OTOH, just drop the contents of m_aRunText in case something
1969 * would be there, causing a problem later.
1970 */
1971 OString aSave = m_aRun.makeStringAndClear();
1972 // Also back m_bInRun and m_bSingleEmptyRun up.
1973 bool bInRunOrig = m_bInRun;
1974 m_bInRun = false;
1975 bool bSingleEmptyRunOrig = m_bSingleEmptyRun;
1976 m_bSingleEmptyRun = false;
1978
1979 const SwFrameFormat& rFrameFormat = rFrame.GetFrameFormat();
1980 const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
1981 SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
1982 SwNodeOffset nEnd
1983 = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
1984 m_rExport.SaveData(nStt, nEnd);
1985 m_rExport.m_pParentFrame = &rFrame;
1988
1991 m_aRun->append(aSave);
1992 m_aRunText.clear();
1993 m_bInRun = bInRunOrig;
1994 m_bSingleEmptyRun = bSingleEmptyRunOrig;
1995
1996 // Restore table state.
1997 m_rExport.m_pTableInfo = pTableInfoOrig;
1998 m_pTableWrt = std::move(pTableWrt);
1999 m_nTableDepth = nTableDepth;
2000 }
2001
2002 m_rExport.m_pParentFrame = nullptr;
2003
2004 m_rExport.Strm().WriteChar('}'); // shptxt
2005
2006 if (bTextBox)
2007 {
2008 m_aRunText = aRunText;
2011 }
2012}
2013
2019{
2020private:
2025 bool const m_bInRun;
2026
2027public:
2029 : m_rRtf(rRtf)
2030 , m_Run(std::move(rRtf.m_aRun))
2031 , m_RunText(std::move(rRtf.m_aRunText))
2033 , m_bInRun(rRtf.m_bInRun)
2034 {
2036 }
2038 {
2039 m_rRtf.m_aRun = std::move(m_Run);
2040 m_rRtf.m_aRunText = std::move(m_RunText);
2043
2046 }
2047};
2048
2049void RtfAttributeOutput::OutputFlyFrame_Impl(const ww8::Frame& rFrame, const Point& /*rNdTopLeft*/)
2050{
2051 const SwNode* pNode = rFrame.GetContent();
2052 const SwGrfNode* pGrfNode = pNode ? pNode->GetGrfNode() : nullptr;
2053
2054 switch (rFrame.GetWriterType())
2055 {
2057 {
2058 // If this is a TextBox of a shape, then ignore: it's handled in RtfSdrExport::StartShape().
2060 break;
2061
2062 SaveRunState const saved(*this);
2063
2064 m_rExport.m_pParentFrame = &rFrame;
2065
2069
2070 // Shape properties.
2071 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
2072 "shapeType", OString::number(ESCHER_ShpInst_TextBox)));
2073
2074 // When a frame has some low height, but automatically expanded due
2075 // to lots of contents, this size contains the real size.
2076 const Size aSize = rFrame.GetSize();
2077 m_pFlyFrameSize = &aSize;
2078
2081 m_rExport.OutputFormat(rFrame.GetFrameFormat(), false, false, true);
2082
2083 // Write ZOrder.
2084 if (const SdrObject* pObject = rFrame.GetFrameFormat().FindRealSdrObject())
2085 {
2087 m_rExport.OutULong(pObject->GetOrdNum());
2088 }
2089
2092 m_aStyles.setLength(0);
2095 m_pFlyFrameSize = nullptr;
2096
2097 const SwFrameFormat& rFrameFormat = rFrame.GetFrameFormat();
2098 lcl_TextFrameShadow(m_aFlyProperties, rFrameFormat);
2099 lcl_TextFrameRelativeSize(m_aFlyProperties, rFrameFormat);
2100
2101 for (const std::pair<OString, OString>& rPair : m_aFlyProperties)
2102 {
2105 m_rExport.Strm().WriteOString(rPair.first);
2107 m_rExport.Strm().WriteOString(rPair.second);
2108 m_rExport.Strm().WriteCharPtr("}}");
2109 }
2110 m_aFlyProperties.clear();
2111
2112 writeTextFrame(rFrame);
2113
2114 m_rExport.Strm().WriteChar('}'); // shpinst
2115 m_rExport.Strm().WriteChar('}'); // shp
2116
2118 }
2119 break;
2121 if (pGrfNode)
2122 {
2123 m_aRunText.append(dynamic_cast<const SwFlyFrameFormat*>(&rFrame.GetFrameFormat()),
2124 pGrfNode);
2125 }
2126 else if (!rFrame.IsInline())
2127 {
2128 m_rExport.m_pParentFrame = &rFrame;
2130 m_rExport.OutputFormat(rFrame.GetFrameFormat(), false, false, true);
2133 m_rExport.OutputFormat(rFrame.GetFrameFormat(), false, false, true);
2134 m_aRunText->append('}');
2135 m_rExport.m_pParentFrame = nullptr;
2136 }
2137 break;
2139 {
2140 const SdrObject* pSdrObj = rFrame.GetFrameFormat().FindRealSdrObject();
2141 if (pSdrObj)
2142 {
2146 m_aRunText->append(" SHAPE ");
2147 m_aRunText->append("}"
2149
2151
2152 m_aRunText->append('}');
2153 m_aRunText->append('}');
2154 }
2155 }
2156 break;
2158 {
2159 const SwFrameFormat& rFrameFormat = rFrame.GetFrameFormat();
2160 const SdrObject* pObject = rFrameFormat.FindRealSdrObject();
2161
2164
2165 if (pObject && pObject->GetObjInventor() == SdrInventor::FmForm)
2166 {
2167 if (auto pFormObj = dynamic_cast<const SdrUnoObj*>(pObject))
2168 {
2169 const uno::Reference<awt::XControlModel>& xControlModel
2170 = pFormObj->GetUnoControlModel();
2171 uno::Reference<lang::XServiceInfo> xInfo(xControlModel, uno::UNO_QUERY);
2172 if (xInfo.is())
2173 {
2174 uno::Reference<beans::XPropertySet> xPropSet(xControlModel, uno::UNO_QUERY);
2175 uno::Reference<beans::XPropertySetInfo> xPropSetInfo
2176 = xPropSet->getPropertySetInfo();
2177 OUString sName;
2178 if (xInfo->supportsService("com.sun.star.form.component.CheckBox"))
2179 {
2182 m_aRun->append(
2184 "{");
2185 m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFTYPE "1"); // 1 = checkbox
2186 // checkbox size in half points, this seems to be always 20
2188
2189 OUString aStr;
2190 sName = "Name";
2191 if (xPropSetInfo->hasPropertyByName(sName))
2192 {
2193 xPropSet->getPropertyValue(sName) >>= aStr;
2194 m_aRun->append(
2196 " ");
2197 m_aRun->append(
2199 m_aRun->append('}');
2200 }
2201
2202 sName = "HelpText";
2203 if (xPropSetInfo->hasPropertyByName(sName))
2204 {
2205 xPropSet->getPropertyValue(sName) >>= aStr;
2209 m_aRun->append(
2211 m_aRun->append('}');
2212 }
2213
2214 sName = "HelpF1Text";
2215 if (xPropSetInfo->hasPropertyByName(sName))
2216 {
2217 xPropSet->getPropertyValue(sName) >>= aStr;
2221 m_aRun->append(
2223 m_aRun->append('}');
2224 }
2225
2226 sal_Int16 nTemp = 0;
2227 xPropSet->getPropertyValue("DefaultState") >>= nTemp;
2229 m_aRun->append(static_cast<sal_Int32>(nTemp));
2230 xPropSet->getPropertyValue("State") >>= nTemp;
2232 m_aRun->append(static_cast<sal_Int32>(nTemp));
2233
2234 m_aRun->append("}}");
2235
2236 // field result is empty, ffres already contains the form result
2238 }
2239 else if (xInfo->supportsService("com.sun.star.form.component.TextField"))
2240 {
2241 OStringBuffer aBuf;
2242 OString aStr;
2243 OUString aTmp;
2244 const char* pStr;
2245
2248 m_aRun->append(
2250 " ");
2251 for (int i = 0; i < 8; i++)
2252 aBuf.append(char(0x00));
2253 xPropSet->getPropertyValue("Name") >>= aTmp;
2255 aBuf.append(static_cast<char>(aStr.getLength()));
2256 aBuf.append(aStr);
2257 aBuf.append(char(0x00));
2258 xPropSet->getPropertyValue("DefaultText") >>= aTmp;
2260 aBuf.append(static_cast<char>(aStr.getLength()));
2261 aBuf.append(aStr);
2262 for (int i = 0; i < 11; i++)
2263 aBuf.append(char(0x00));
2264 aStr = aBuf.makeStringAndClear();
2265 pStr = aStr.getStr();
2266 for (int i = 0; i < aStr.getLength(); i++, pStr++)
2268 m_aRun->append('}');
2270 xPropSet->getPropertyValue("Text") >>= aTmp;
2272 m_aRun->append('}');
2273 m_aRun->append(
2275 "{");
2276 sName = "HelpText";
2277 if (xPropSetInfo->hasPropertyByName(sName))
2278 {
2279 xPropSet->getPropertyValue(sName) >>= aTmp;
2283 m_aRun->append(
2285 m_aRun->append('}');
2286 }
2287
2288 sName = "HelpF1Text";
2289 if (xPropSetInfo->hasPropertyByName(sName))
2290 {
2291 xPropSet->getPropertyValue(sName) >>= aTmp;
2295 m_aRun->append(
2297 m_aRun->append('}');
2298 }
2299 m_aRun->append("}");
2300 }
2301 else if (xInfo->supportsService("com.sun.star.form.component.ListBox"))
2302 {
2303 OUString aStr;
2304 uno::Sequence<sal_Int16> aIntSeq;
2305 uno::Sequence<OUString> aStrSeq;
2306
2309 m_aRun->append(
2311 "{");
2314
2315 xPropSet->getPropertyValue("DefaultSelection") >>= aIntSeq;
2316 if (aIntSeq.hasElements())
2317 {
2319 // a dropdown list can have only one 'selected item by default'
2320 m_aRun->append(static_cast<sal_Int32>(aIntSeq[0]));
2321 }
2322
2323 xPropSet->getPropertyValue("SelectedItems") >>= aIntSeq;
2324 if (aIntSeq.hasElements())
2325 {
2327 // a dropdown list can have only one 'currently selected item'
2328 m_aRun->append(static_cast<sal_Int32>(aIntSeq[0]));
2329 }
2330
2331 sName = "Name";
2332 if (xPropSetInfo->hasPropertyByName(sName))
2333 {
2334 xPropSet->getPropertyValue(sName) >>= aStr;
2335 m_aRun->append(
2337 " ");
2338 m_aRun->append(
2340 m_aRun->append('}');
2341 }
2342
2343 sName = "HelpText";
2344 if (xPropSetInfo->hasPropertyByName(sName))
2345 {
2346 xPropSet->getPropertyValue(sName) >>= aStr;
2350 m_aRun->append(
2352 m_aRun->append('}');
2353 }
2354
2355 sName = "HelpF1Text";
2356 if (xPropSetInfo->hasPropertyByName(sName))
2357 {
2358 xPropSet->getPropertyValue(sName) >>= aStr;
2362 m_aRun->append(
2364 m_aRun->append('}');
2365 }
2366
2367 xPropSet->getPropertyValue("StringItemList") >>= aStrSeq;
2368 for (const auto& rStr : std::as_const(aStrSeq))
2369 m_aRun->append(
2372 + "}");
2373
2374 m_aRun->append("}}");
2375
2376 // field result is empty, ffres already contains the form result
2378 }
2379 else
2380 SAL_INFO("sw.rtf", __func__ << " unhandled form control: '"
2381 << xInfo->getImplementationName() << "'");
2382 m_aRun->append('}');
2383 }
2384 }
2385 }
2386
2387 m_aRun->append('}');
2388 }
2389 break;
2390 case ww8::Frame::eOle:
2391 {
2392 const SwFrameFormat& rFrameFormat = rFrame.GetFrameFormat();
2393 const SdrObject* pSdrObj = rFrameFormat.FindRealSdrObject();
2394 if (pSdrObj)
2395 {
2396 SwNodeIndex aIdx(*rFrameFormat.GetContent().GetContentIdx(), 1);
2397 SwOLENode& rOLENd = *aIdx.GetNode().GetOLENode();
2398 FlyFrameOLE(dynamic_cast<const SwFlyFrameFormat*>(&rFrameFormat), rOLENd,
2399 rFrame.GetLayoutSize());
2400 }
2401 }
2402 break;
2403 default:
2404 SAL_INFO("sw.rtf", __func__ << ": unknown type ("
2405 << static_cast<int>(rFrame.GetWriterType()) << ")");
2406 break;
2407 }
2408}
2409
2411{
2412 switch (rCaseMap.GetValue())
2413 {
2414 case SvxCaseMap::SmallCaps:
2416 break;
2417 case SvxCaseMap::Uppercase:
2419 break;
2420 default: // Something that rtf does not support
2422 m_aStyles.append(sal_Int32(0));
2424 m_aStyles.append(sal_Int32(0));
2425 break;
2426 }
2427}
2428
2430{
2431 const Color aColor(rColor.GetValue());
2432
2434 m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(aColor)));
2435}
2436
2438{
2440 if (!rContour.GetValue())
2441 m_aStyles.append(sal_Int32(0));
2442}
2443
2445{
2446 switch (rCrossedOut.GetStrikeout())
2447 {
2448 case STRIKEOUT_NONE:
2450 m_aStyles.append(sal_Int32(0));
2451 break;
2452 case STRIKEOUT_DOUBLE:
2454 m_aStyles.append(sal_Int32(1));
2455 break;
2456 default:
2458 break;
2459 }
2460}
2461
2463{
2464 short nEsc = rEscapement.GetEsc();
2465 short nProp = rEscapement.GetProportionalHeight();
2466 sal_Int32 nProp100 = nProp * 100;
2467 if (DFLT_ESC_PROP == nProp || nProp < 1 || nProp > 100)
2468 {
2469 if (DFLT_ESC_SUB == nEsc || DFLT_ESC_AUTO_SUB == nEsc)
2471 else if (DFLT_ESC_SUPER == nEsc || DFLT_ESC_AUTO_SUPER == nEsc)
2473 return;
2474 }
2475 if (DFLT_ESC_AUTO_SUPER == nEsc)
2476 {
2477 nEsc = .8 * (100 - nProp);
2478 ++nProp100; // A 1 afterwards means 'automatic' according to editeng/rtf/rtfitem.cxx
2479 }
2480 else if (DFLT_ESC_AUTO_SUB == nEsc)
2481 {
2482 nEsc = .2 * -(100 - nProp);
2483 ++nProp100;
2484 }
2485
2486 const char* pUpDn;
2487
2488 double fHeight = m_rExport.GetItem(RES_CHRATR_FONTSIZE).GetHeight();
2489
2490 if (0 < nEsc)
2492 else if (0 > nEsc)
2493 {
2495 fHeight = -fHeight;
2496 }
2497 else
2498 return;
2499
2500 m_aStyles.append('{');
2503 m_aStyles.append(nProp100);
2504 m_aStyles.append('}');
2505 m_aStyles.append(pUpDn);
2506
2507 /*
2508 * Calculate the act. FontSize and the percentage of the displacement;
2509 * RTF file expects half points, while internally it's in twips.
2510 * Formally : (FontSize * 1/20 ) pts x * 2
2511 * ----------------------- = ------------
2512 * 100% Escapement
2513 */
2514 m_aStyles.append(static_cast<sal_Int32>(round(fHeight * nEsc / 1000)));
2515}
2516
2518{
2519 // Insert \loch in MoveCharacterProperties
2521 m_aStyles.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont)));
2522
2523 // Insert \hich in MoveCharacterProperties
2525 m_aStylesAssocHich.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont)));
2526
2527 // FIXME: this may be a tad expensive... but the charset needs to be
2528 // consistent with what wwFont::WriteRtf() does
2531 aTmp.msPrimary, aTmp.msSecondary, rFont.GetCharSet());
2532 m_rExport.SetCurrentEncoding(rtl_getTextEncodingFromWindowsCharset(nWindowsCharset));
2533 if (m_rExport.GetCurrentEncoding() == RTL_TEXTENCODING_DONTKNOW)
2535}
2536
2538{
2539 switch (rFontSize.Which())
2540 {
2543 m_aStyles.append(static_cast<sal_Int32>(rFontSize.GetHeight() / 10));
2544 break;
2547 m_aStylesAssocDbch.append(static_cast<sal_Int32>(rFontSize.GetHeight() / 10));
2548 break;
2551 m_aStylesAssocRtlch.append(static_cast<sal_Int32>(rFontSize.GetHeight() / 10));
2552 break;
2553 }
2554}
2555
2557{
2558 // in quarter points then in twips
2560 m_aStyles.append(static_cast<sal_Int32>(rKerning.GetValue() / 5));
2562 m_aStyles.append(static_cast<sal_Int32>(rKerning.GetValue()));
2563}
2564
2566{
2567 switch (rLanguage.Which())
2568 {
2571 m_aStyles.append(
2572 static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
2574 m_aStylesAssocLtrch.append(
2575 static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
2576 break;
2579 m_aStylesAssocDbch.append(
2580 static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
2582 m_aStylesAssocLtrch.append(
2583 static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
2584 break;
2587 m_aStylesAssocRtlch.append(
2588 static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
2589 break;
2590 }
2591}
2592
2594{
2596 if (rPosture.GetPosture() == ITALIC_NONE)
2597 m_aStyles.append(sal_Int32(0));
2598}
2599
2601{
2603 if (!rShadow.GetValue())
2604 m_aStyles.append(sal_Int32(0));
2605}
2606
2608{
2609 const char* pStr = nullptr;
2611 bool bWord = false;
2612 // No StaticWhichCast(RES_CHRATR_WORDLINEMODE), this may be for a postit, where the which ids
2613 // don't match.
2614 if (pItem)
2615 bWord = static_cast<const SvxWordLineModeItem*>(pItem)->GetValue();
2616 switch (rUnderline.GetLineStyle())
2617 {
2618 case LINESTYLE_SINGLE:
2620 break;
2621 case LINESTYLE_DOUBLE:
2623 break;
2624 case LINESTYLE_NONE:
2626 break;
2627 case LINESTYLE_DOTTED:
2629 break;
2630 case LINESTYLE_DASH:
2632 break;
2633 case LINESTYLE_DASHDOT:
2635 break;
2638 break;
2639 case LINESTYLE_BOLD:
2641 break;
2642 case LINESTYLE_WAVE:
2644 break;
2647 break;
2648 case LINESTYLE_BOLDDASH:
2650 break;
2651 case LINESTYLE_LONGDASH:
2653 break;
2656 break;
2659 break;
2662 break;
2663 case LINESTYLE_BOLDWAVE:
2665 break;
2668 break;
2669 default:
2670 break;
2671 }
2672
2673 if (pStr)
2674 {
2675 m_aStyles.append(pStr);
2676 // NEEDSWORK looks like here rUnderline.GetColor() is always black,
2677 // even if the color in the odt is for example green...
2679 m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(rUnderline.GetColor())));
2680 }
2681}
2682
2684{
2686 if (rWeight.GetWeight() != WEIGHT_BOLD)
2687 m_aStyles.append(sal_Int32(0));
2688}
2689
2691{
2693 m_aStyles.append(static_cast<sal_Int32>(rAutoKern.GetValue() ? 1 : 0));
2694}
2695
2697{
2699 m_aStyles.append(static_cast<sal_Int32>(rBlink.GetValue() ? 2 : 0));
2700}
2701
2703{
2704 if (!rBrush.GetColor().IsTransparent())
2705 {
2707 m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(rBrush.GetColor())));
2708 }
2709}
2710
2712{
2713 // Insert \dbch in MoveCharacterProperties
2715 m_aStylesAssocDbch.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont)));
2716}
2717
2719{
2720 CharFontSize(rFontSize);
2721}
2722
2724{
2725 CharLanguage(rLanguageItem);
2726}
2727
2729{
2731 if (rPosture.GetPosture() == ITALIC_NONE)
2732 m_aStylesAssocDbch.append(sal_Int32(0));
2733}
2734
2736{
2738 if (rWeight.GetWeight() != WEIGHT_BOLD)
2739 m_aStylesAssocDbch.append(sal_Int32(0));
2740}
2741
2743{
2744 // Insert \rtlch in MoveCharacterProperties
2746 m_aStylesAssocRtlch.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont)));
2747}
2748
2750{
2751 CharFontSize(rFontSize);
2752}
2753
2755{
2756 CharLanguage(rLanguageItem);
2757}
2758
2760{
2762 if (rPosture.GetPosture() == ITALIC_NONE)
2763 m_aStylesAssocRtlch.append(sal_Int32(0));
2764}
2765
2767{
2769 if (rWeight.GetWeight() != WEIGHT_BOLD)
2770 m_aStylesAssocRtlch.append(sal_Int32(0));
2771}
2772
2774
2776
2778{
2780 m_aStyles.append(static_cast<sal_Int32>(rRotate.IsFitToLine() ? 1 : 0));
2781}
2782
2784{
2785 FontEmphasisMark v = rEmphasisMark.GetEmphasisMark();
2786 if (v == FontEmphasisMark::NONE)
2788 else if (v == (FontEmphasisMark::Dot | FontEmphasisMark::PosAbove))
2790 else if (v == (FontEmphasisMark::Accent | FontEmphasisMark::PosAbove))
2792 else if (v == (FontEmphasisMark::Circle | FontEmphasisMark::PosAbove))
2794 else if (v == (FontEmphasisMark::Dot | FontEmphasisMark::PosBelow))
2796}
2797
2799{
2800 if (!rTwoLines.GetValue())
2801 return;
2802
2803 sal_Unicode cStart = rTwoLines.GetStartBracket();
2804 sal_Unicode cEnd = rTwoLines.GetEndBracket();
2805
2806 sal_uInt16 nType;
2807 if (!cStart && !cEnd)
2808 nType = 0;
2809 else if ('{' == cStart || '}' == cEnd)
2810 nType = 4;
2811 else if ('<' == cStart || '>' == cEnd)
2812 nType = 3;
2813 else if ('[' == cStart || ']' == cEnd)
2814 nType = 2;
2815 else // all other kind of brackets
2816 nType = 1;
2817
2819 m_aStyles.append(static_cast<sal_Int32>(nType));
2820}
2821
2823{
2825 m_aStyles.append(static_cast<sal_Int32>(rScaleWidth.GetValue()));
2826}
2827
2829{
2830 const char* pStr;
2831 switch (rRelief.GetValue())
2832 {
2833 case FontRelief::Embossed:
2835 break;
2836 case FontRelief::Engraved:
2838 break;
2839 default:
2840 pStr = nullptr;
2841 break;
2842 }
2843
2844 if (pStr)
2845 m_aStyles.append(pStr);
2846}
2847
2849{
2851 if (!rHidden.GetValue())
2852 m_aStyles.append(sal_Int32(0));
2853}
2854
2856 const sal_uInt16 nDist, const bool bShadow)
2857{
2858 m_aStyles.append(
2860 bShadow ? SvxShadowLocation::BottomRight : SvxShadowLocation::NONE));
2861}
2862
2864{
2866 m_aStyles.append(static_cast<sal_Int32>(msfilter::util::TransColToIco(rBrush.GetColor())));
2867}
2868
2870{
2871 if (rURL.GetValue().isEmpty())
2872 return;
2873
2874 const SwCharFormat* pFormat;
2875 const SwTextINetFormat* pTextAtr = rURL.GetTextINetFormat();
2876
2877 if (pTextAtr && nullptr != (pFormat = pTextAtr->GetCharFormat()))
2878 {
2879 sal_uInt16 nStyle = m_rExport.GetId(pFormat);
2880 OString* pString = m_rExport.GetStyle(nStyle);
2881 if (pString)
2882 m_aStyles.append(*pString);
2883 }
2884}
2885
2887{
2888 sal_uInt16 nStyle = m_rExport.GetId(rCharFormat.GetCharFormat());
2890 m_aStyles.append(static_cast<sal_Int32>(nStyle));
2891 OString* pString = m_rExport.GetStyle(nStyle);
2892 if (pString)
2893 m_aStyles.append(*pString);
2894}
2895
2897{
2898 if (rFootnote.GetNumStr().isEmpty())
2900 else
2901 m_aRun->append(
2903}
2904
2906{
2907 SAL_INFO("sw.rtf", __func__ << " start");
2908
2910 EndRunProperties(nullptr);
2911 m_aRun->append(' ');
2912 WriteTextFootnoteNumStr(rFootnote);
2916 m_aRun->append(' ');
2917 WriteTextFootnoteNumStr(rFootnote);
2918
2919 /*
2920 * The footnote contains a whole paragraph, so we have to:
2921 * 1) Reset, then later restore the contents of our run buffer and run state.
2922 * 2) Buffer the output of the whole paragraph, as we do so for section headers already.
2923 */
2924 const SwNodeIndex* pIndex = rFootnote.GetTextFootnote()->GetStartNode();
2925 RtfStringBuffer aRun = m_aRun;
2926 m_aRun.clear();
2927 bool bInRunOrig = m_bInRun;
2928 m_bInRun = false;
2929 bool bSingleEmptyRunOrig = m_bSingleEmptyRun;
2930 m_bSingleEmptyRun = false;
2932 m_rExport.WriteSpecialText(pIndex->GetIndex() + 1, pIndex->GetNode().EndOfSectionIndex(),
2933 !rFootnote.IsEndNote() ? TXT_FTN : TXT_EDN);
2935 m_bInRun = bInRunOrig;
2936 m_bSingleEmptyRun = bSingleEmptyRunOrig;
2937 m_aRun = aRun;
2939 m_aSectionHeaders.setLength(0);
2940
2941 m_aRun->append("}");
2942 m_aRun->append("}");
2943
2944 SAL_INFO("sw.rtf", __func__ << " end");
2945}
2946
2947void RtfAttributeOutput::ParaLineSpacing_Impl(short nSpace, short nMulti)
2948{
2950 m_aStyles.append(static_cast<sal_Int32>(nSpace));
2952 m_aStyles.append(static_cast<sal_Int32>(nMulti));
2953}
2954
2956{
2957 switch (rAdjust.GetAdjust())
2958 {
2959 case SvxAdjust::Left:
2961 break;
2962 case SvxAdjust::Right:
2964 break;
2965 case SvxAdjust::BlockLine:
2966 case SvxAdjust::Block:
2967 if (rAdjust.GetLastBlock() == SvxAdjust::Block)
2969 else
2971 break;
2972 case SvxAdjust::Center:
2974 break;
2975 default:
2976 break;
2977 }
2978}
2979
2981{
2982 if (!rSplit.GetValue())
2984}
2985
2987{
2988 if (rWidows.GetValue())
2990 else
2992}
2993
2995{
2996 tools::Long nOffset = 0;
2997 // Tabs are absolute by default.
3000 nOffset = m_rExport.GetItem(RES_LR_SPACE).GetTextLeft();
3001
3002 for (sal_uInt16 n = 0; n < rTabStop.Count(); n++)
3003 {
3004 const SvxTabStop& rTS = rTabStop[n];
3005 if (SvxTabAdjust::Default != rTS.GetAdjustment())
3006 {
3007 const char* pFill = nullptr;
3008 switch (rTS.GetFill())
3009 {
3010 case cDfltFillChar:
3011 break;
3012
3013 case '.':
3015 break;
3016 case '_':
3018 break;
3019 case '-':
3021 break;
3022 case '=':
3024 break;
3025 default:
3026 break;
3027 }
3028 if (pFill)
3029 m_aStyles.append(pFill);
3030
3031 const char* pAdjStr = nullptr;
3032 switch (rTS.GetAdjustment())
3033 {
3034 case SvxTabAdjust::Right:
3036 break;
3037 case SvxTabAdjust::Decimal:
3039 break;
3040 case SvxTabAdjust::Center:
3042 break;
3043 default:
3044 break;
3045 }
3046 if (pAdjStr)
3047 m_aStyles.append(pAdjStr);
3049 m_aStyles.append(static_cast<sal_Int32>(rTS.GetTabPos() + nOffset));
3050 }
3051 else
3052 {
3054 m_aTabStop.append(rTabStop[0].GetTabPos());
3055 }
3056 }
3057}
3058
3060{
3062 m_aStyles.append(sal_Int32(rHyphenZone.IsHyphen()));
3063}
3064
3065void RtfAttributeOutput::ParaNumRule_Impl(const SwTextNode* pTextNd, sal_Int32 nLvl,
3066 sal_Int32 nNumId)
3067{
3068 if (USHRT_MAX == nNumId || 0 == nNumId || nullptr == pTextNd)
3069 return;
3070
3071 const SwNumRule* pRule = pTextNd->GetNumRule();
3072
3073 if (!pRule || !pTextNd->IsInList())
3074 return;
3075
3076 SAL_WARN_IF(pTextNd->GetActualListLevel() < 0 || pTextNd->GetActualListLevel() >= MAXLEVEL,
3077 "sw.rtf", "text node does not have valid list level");
3078
3079 const SwNumFormat* pFormat = pRule->GetNumFormat(nLvl);
3080 if (!pFormat)
3081 pFormat = &pRule->Get(nLvl);
3082
3083 const SfxItemSet& rNdSet = pTextNd->GetSwAttrSet();
3084
3085 m_aStyles.append('{');
3089 m_aStyles.append(' ');
3090
3091 SvxLRSpaceItem aLR(rNdSet.Get(RES_LR_SPACE));
3092 aLR.SetTextLeft(aLR.GetTextLeft() + pFormat->GetIndentAt());
3093 aLR.SetTextFirstLineOffset(pFormat->GetFirstLineOffset()); //TODO: overflow
3094
3095 sal_uInt16 nStyle = m_rExport.GetId(pFormat->GetCharFormat());
3096 OString* pString = m_rExport.GetStyle(nStyle);
3097 if (pString)
3098 m_aStyles.append(*pString);
3099
3100 {
3101 OUString sText;
3102 if (SVX_NUM_CHAR_SPECIAL == pFormat->GetNumberingType()
3103 || SVX_NUM_BITMAP == pFormat->GetNumberingType())
3104 {
3105 sal_UCS4 cBullet = pFormat->GetBulletChar();
3106 sText = OUString(&cBullet, 1);
3107 }
3108 else
3109 sText = pTextNd->GetNumString();
3110
3111 if (!sText.isEmpty())
3112 {
3113 m_aStyles.append(' ');
3115 }
3116
3117 if (OUTLINE_RULE != pRule->GetRuleType())
3118 {
3119 if (!sText.isEmpty())
3121 m_aStyles.append('}');
3123 if (nLvl > 8) // RTF knows only 9 levels
3124 {
3125 m_aStyles.append(sal_Int32(8));
3127 m_aStyles.append(nLvl);
3128 m_aStyles.append('}');
3129 }
3130 else
3131 m_aStyles.append(nLvl);
3132 }
3133 else
3136 m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetNumberingId(*pRule)) + 1);
3137 m_aStyles.append(' ');
3138 }
3139 FormatLRSpace(aLR);
3140}
3141
3143{
3144 if (!rScriptSpace.GetValue())
3145 return;
3146
3148}
3149
3151{
3152 SAL_INFO("sw.rtf", "TODO: " << __func__);
3153}
3154
3156{
3157 SAL_INFO("sw.rtf", "TODO: " << __func__);
3158}
3159
3161{
3162 const char* pStr;
3163 switch (rAlign.GetValue())
3164 {
3167 break;
3170 break;
3173 break;
3176 break;
3177
3178 default:
3180 break;
3181 }
3182 m_aStyles.append(pStr);
3183}
3184
3186{
3187 SAL_INFO("sw.rtf", "TODO: " << __func__);
3188}
3189
3191{
3193 {
3195 m_aSectionBreaks.append(static_cast<sal_Int32>(rSize.GetWidth()));
3197 m_aSectionBreaks.append(static_cast<sal_Int32>(rSize.GetHeight()));
3199 {
3201 m_aSectionBreaks.setLength(0);
3202 }
3203 }
3204}
3205
3207{
3208 SAL_INFO("sw.rtf", "TODO: " << __func__);
3209}
3210
3212{
3214 {
3216 {
3219
3220 if (const SvxBoxItem* pBoxItem = m_rExport.HasItem(RES_BOX))
3221 {
3223 = pBoxItem->CalcLineSpace(SvxBoxItemLine::LEFT, /*bEvenIfNoLine*/ true);
3225 = pBoxItem->CalcLineSpace(SvxBoxItemLine::RIGHT, /*bEvenIfNoLine*/ true);
3226 }
3227
3228 m_aPageMargins.nLeft += sal::static_int_cast<sal_uInt16>(rLRSpace.GetLeft());
3229 m_aPageMargins.nRight += sal::static_int_cast<sal_uInt16>(rLRSpace.GetRight());
3230
3231 if (rLRSpace.GetLeft())
3232 {
3234 m_aSectionBreaks.append(static_cast<sal_Int32>(m_aPageMargins.nLeft));
3235 }
3236 if (rLRSpace.GetRight())
3237 {
3239 m_aSectionBreaks.append(static_cast<sal_Int32>(m_aPageMargins.nRight));
3240 }
3241 if (rLRSpace.GetGutterMargin())
3242 {
3244 m_aSectionBreaks.append(static_cast<sal_Int32>(rLRSpace.GetGutterMargin()));
3245 }
3247 {
3249 m_aSectionBreaks.setLength(0);
3250 }
3251 }
3252 else
3253 {
3255 m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetTextLeft()));
3257 m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetRight()));
3259 m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetTextLeft()));
3261 m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetRight()));
3263 m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetTextFirstLineOffset()));
3264 }
3265 }
3266 else if (m_rExport.GetRTFFlySyntax())
3267 {
3268 // Wrap: top and bottom spacing, convert from twips to EMUs.
3269 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3270 "dxWrapDistLeft", OString::number(o3tl::convert(rLRSpace.GetLeft(), o3tl::Length::twip,
3272 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3273 "dxWrapDistRight", OString::number(o3tl::convert(
3275 }
3276}
3277
3279{
3281 {
3283 {
3284 OSL_ENSURE(m_rExport.GetCurItemSet(), "Impossible");
3285 if (!m_rExport.GetCurItemSet())
3286 return;
3287
3288 // If we export a follow page format, then our doc model has
3289 // separate header/footer distances for the first page and the
3290 // follow pages, but Word can have only a single distance. In case
3291 // the two values differ, work with the value from the first page
3292 // format to be in sync with the import.
3296
3297 if (aDistances.m_DyaTop)
3298 {
3300 m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.m_DyaTop));
3301 m_aPageMargins.nTop = aDistances.m_DyaTop;
3302 }
3303 if (aDistances.HasHeader())
3304 {
3306 m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.m_DyaHdrTop));
3307 }
3308
3309 if (aDistances.m_DyaBottom)
3310 {
3312 m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.m_DyaBottom));
3313 m_aPageMargins.nBottom = aDistances.m_DyaBottom;
3314 }
3315 if (aDistances.HasFooter())
3316 {
3318 m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.m_DyaHdrBottom));
3319 }
3321 {
3323 m_aSectionBreaks.setLength(0);
3324 }
3325 }
3326 else
3327 {
3328 // Spacing before.
3332 {
3335 m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetUpper()));
3336 }
3337 else
3338 {
3340 m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetUpper()));
3341 }
3343
3344 // Spacing after.
3348 {
3351 m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetLower()));
3352 }
3353 else
3354 {
3356 m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetLower()));
3357 }
3359
3360 // Contextual spacing.
3361 if (rULSpace.GetContext())
3363 }
3364 }
3365 else if (m_rExport.GetRTFFlySyntax())
3366 {
3367 // Wrap: top and bottom spacing, convert from twips to EMUs.
3368 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3369 "dyWrapDistTop", OString::number(o3tl::convert(rULSpace.GetUpper(), o3tl::Length::twip,
3371 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3372 "dyWrapDistBottom", OString::number(o3tl::convert(
3374 }
3375}
3376
3378{
3380 {
3381 css::text::WrapTextMode eSurround = rSurround.GetSurround();
3382 bool bGold = css::text::WrapTextMode_DYNAMIC == eSurround;
3383 if (bGold)
3384 eSurround = css::text::WrapTextMode_PARALLEL;
3385 RTFSurround aMC(bGold, static_cast<sal_uInt8>(eSurround));
3387 m_aRunText->append(static_cast<sal_Int32>(aMC.GetValue()));
3388 }
3390 {
3391 // See DocxSdrExport::startDMLAnchorInline() for SwFormatSurround -> WR / WRK mappings.
3392 sal_Int32 nWr = -1;
3393 std::optional<sal_Int32> oWrk;
3394 switch (rSurround.GetValue())
3395 {
3396 case css::text::WrapTextMode_NONE:
3397 nWr = 1; // top and bottom
3398 break;
3399 case css::text::WrapTextMode_THROUGH:
3400 nWr = 3; // none
3401 break;
3402 case css::text::WrapTextMode_PARALLEL:
3403 nWr = 2; // around
3404 oWrk = 0; // both sides
3405 break;
3406 case css::text::WrapTextMode_DYNAMIC:
3407 default:
3408 nWr = 2; // around
3409 oWrk = 3; // largest
3410 break;
3411 }
3412
3413 if (rSurround.IsContour())
3414 nWr = 4; // tight
3415
3417 m_rExport.OutLong(nWr);
3418 if (oWrk)
3419 {
3421 m_rExport.OutLong(*oWrk);
3422 }
3423 }
3424}
3425
3427{
3429 return;
3430
3431 switch (rFlyVert.GetRelationOrient())
3432 {
3433 case text::RelOrientation::PAGE_FRAME:
3434 m_aFlyProperties.push_back(
3435 std::make_pair<OString, OString>("posrelv", OString::number(1)));
3436 break;
3437 default:
3438 m_aFlyProperties.push_back(
3439 std::make_pair<OString, OString>("posrelv", OString::number(2)));
3440 m_rExport.Strm()
3443 break;
3444 }
3445
3446 switch (rFlyVert.GetVertOrient())
3447 {
3448 case text::VertOrientation::TOP:
3449 case text::VertOrientation::LINE_TOP:
3450 m_aFlyProperties.push_back(
3451 std::make_pair<OString, OString>("posv", OString::number(1)));
3452 break;
3453 case text::VertOrientation::BOTTOM:
3454 case text::VertOrientation::LINE_BOTTOM:
3455 m_aFlyProperties.push_back(
3456 std::make_pair<OString, OString>("posv", OString::number(3)));
3457 break;
3458 case text::VertOrientation::CENTER:
3459 case text::VertOrientation::LINE_CENTER:
3460 m_aFlyProperties.push_back(
3461 std::make_pair<OString, OString>("posv", OString::number(2)));
3462 break;
3463 default:
3464 break;
3465 }
3466
3468 m_rExport.OutLong(rFlyVert.GetPos());
3469 if (m_pFlyFrameSize)
3470 {
3473 }
3474}
3475
3477{
3479 return;
3480
3481 switch (rFlyHori.GetRelationOrient())
3482 {
3483 case text::RelOrientation::PAGE_FRAME:
3484 m_aFlyProperties.push_back(
3485 std::make_pair<OString, OString>("posrelh", OString::number(1)));
3486 break;
3487 default:
3488 m_aFlyProperties.push_back(
3489 std::make_pair<OString, OString>("posrelh", OString::number(2)));
3490 m_rExport.Strm()
3493 break;
3494 }
3495
3496 switch (rFlyHori.GetHoriOrient())
3497 {
3498 case text::HoriOrientation::LEFT:
3499 m_aFlyProperties.push_back(
3500 std::make_pair<OString, OString>("posh", OString::number(1)));
3501 break;
3502 case text::HoriOrientation::CENTER:
3503 m_aFlyProperties.push_back(
3504 std::make_pair<OString, OString>("posh", OString::number(2)));
3505 break;
3506 case text::HoriOrientation::RIGHT:
3507 m_aFlyProperties.push_back(
3508 std::make_pair<OString, OString>("posh", OString::number(3)));
3509 break;
3510 default:
3511 break;
3512 }
3513
3515 m_rExport.OutLong(rFlyHori.GetPos());
3516 if (m_pFlyFrameSize)
3517 {
3519 m_rExport.OutLong(rFlyHori.GetPos() + m_pFlyFrameSize->Width());
3520 }
3521}
3522
3524{
3526 return;
3527
3528 RndStdIds eId = rAnchor.GetAnchorId();
3530 m_aRunText->append(static_cast<sal_Int32>(eId));
3531 switch (eId)
3532 {
3533 case RndStdIds::FLY_AT_PAGE:
3535 m_aRunText->append(static_cast<sal_Int32>(rAnchor.GetPageNum()));
3536 break;
3537 case RndStdIds::FLY_AT_PARA:
3538 case RndStdIds::FLY_AS_CHAR:
3540 break;
3541 default:
3542 break;
3543 }
3544}
3545
3547{
3549 {
3550 const Color& rColor = rBrush.GetColor();
3551 // We in fact need RGB to BGR, but the transformation is symmetric.
3552 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3553 "fillColor", OString::number(wwUtility::RGBToBGR(rColor))));
3554 }
3555 else if (!rBrush.GetColor().IsTransparent())
3556 {
3558 m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(rBrush.GetColor())));
3559 }
3560}
3561
3563{
3564 m_oFillStyle = rFillStyle.GetValue();
3565}
3566
3568{
3569 if (*m_oFillStyle != drawing::FillStyle_GRADIENT)
3570 return;
3571
3572 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3573 "fillType", OString::number(7))); // Shade using the fillAngle
3574
3575 const XGradient& rGradient = rFillGradient.GetGradientValue();
3576 const Color& rStartColor = rGradient.GetStartColor();
3577 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3578 "fillBackColor", OString::number(wwUtility::RGBToBGR(rStartColor))));
3579
3580 const Color& rEndColor = rGradient.GetEndColor();
3581 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3582 "fillColor", OString::number(wwUtility::RGBToBGR(rEndColor))));
3583
3584 switch (rGradient.GetGradientStyle())
3585 {
3586 case css::awt::GradientStyle_LINEAR:
3587 break;
3588 case css::awt::GradientStyle_AXIAL:
3589 m_aFlyProperties.push_back(
3590 std::make_pair<OString, OString>("fillFocus", OString::number(50)));
3591 break;
3592 case css::awt::GradientStyle_RADIAL:
3593 case css::awt::GradientStyle_ELLIPTICAL:
3594 case css::awt::GradientStyle_SQUARE:
3595 case css::awt::GradientStyle_RECT:
3596 default:
3597 break;
3598 }
3599}
3600
3602{
3603 static const SvxBoxItemLine aBorders[] = { SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
3604 SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT };
3605 static const char* aBorderNames[]
3608
3609 sal_uInt16 const nDist = rBox.GetSmallestDistance();
3610
3612 {
3613 // Borders: spacing to contents, convert from twips to EMUs.
3614 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3615 "dxTextLeft", OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::LEFT),
3617 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3618 "dyTextTop", OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::TOP),
3620 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3621 "dxTextRight", OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::RIGHT),
3623 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3624 "dyTextBottom", OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::BOTTOM),
3626
3627 const editeng::SvxBorderLine* pLeft = rBox.GetLine(SvxBoxItemLine::LEFT);
3628 const editeng::SvxBorderLine* pRight = rBox.GetLine(SvxBoxItemLine::RIGHT);
3629 const editeng::SvxBorderLine* pTop = rBox.GetLine(SvxBoxItemLine::TOP);
3630 const editeng::SvxBorderLine* pBottom = rBox.GetLine(SvxBoxItemLine::BOTTOM);
3631
3632 if (!pLeft && !pRight && !pBottom && !pTop)
3633 {
3634 // fLine has default 'true', so need to write it out in case of no border.
3635 m_aFlyProperties.push_back(std::make_pair<OString, OString>("fLine", "0"));
3636 return;
3637 }
3638
3639 // RTF has the flags fTopLine, fBottomLine, fLeftLine and fRightLine to disable single border
3640 // lines. But Word cannot disable single border lines. So we do not use them. In case of
3641 // single border lines it is better to draw all four borders than drawing none. So we look
3642 // whether a border line exists, which is effectively drawn.
3643 const editeng::SvxBorderLine* pBorder = nullptr;
3644 if (pTop && pTop->GetBorderLineStyle() != SvxBorderLineStyle::NONE)
3645 pBorder = pTop;
3646 else if (pBottom && pBottom->GetBorderLineStyle() != SvxBorderLineStyle::NONE)
3647 pBorder = pBottom;
3648 else if (pLeft && pLeft->GetBorderLineStyle() != SvxBorderLineStyle::NONE)
3649 pBorder = pLeft;
3650 else if (pRight && pRight->GetBorderLineStyle() != SvxBorderLineStyle::NONE)
3651 pBorder = pRight;
3652
3653 if (!pBorder)
3654 {
3655 m_aFlyProperties.push_back(std::make_pair<OString, OString>("fLine", "0"));
3656 return;
3657 }
3658
3659 const Color& rColor = pBorder->GetColor();
3660 // We in fact need RGB to BGR, but the transformation is symmetric.
3661 m_aFlyProperties.push_back(std::make_pair<OString, OString>(
3662 "lineColor", OString::number(wwUtility::RGBToBGR(rColor))));
3663
3664 double const fConverted(
3666 sal_Int32 nWidth = o3tl::convert(fConverted, o3tl::Length::twip, o3tl::Length::emu);
3667 m_aFlyProperties.push_back(
3668 std::make_pair<OString, OString>("lineWidth", OString::number(nWidth)));
3669
3670 return;
3671 }
3672
3673 if (rBox.GetTop() && rBox.GetBottom() && rBox.GetLeft() && rBox.GetRight()
3674 && *rBox.GetTop() == *rBox.GetBottom() && *rBox.GetTop() == *rBox.GetLeft()
3675 && *rBox.GetTop() == *rBox.GetRight() && nDist == rBox.GetDistance(SvxBoxItemLine::TOP)
3676 && nDist == rBox.GetDistance(SvxBoxItemLine::LEFT)
3677 && nDist == rBox.GetDistance(SvxBoxItemLine::BOTTOM)
3678 && nDist == rBox.GetDistance(SvxBoxItemLine::RIGHT))
3679 m_aSectionBreaks.append(
3681 else
3682 {
3683 SvxShadowLocation eShadowLocation = SvxShadowLocation::NONE;
3684 if (const SvxShadowItem* pItem = GetExport().HasItem(RES_SHADOW))
3685 eShadowLocation = pItem->GetLocation();
3686
3687 const SvxBoxItemLine* pBrd = aBorders;
3688 const char** pBrdNms = aBorderNames;
3689 for (int i = 0; i < 4; ++i, ++pBrd, ++pBrdNms)
3690 {
3691 editeng::SvxBorderLine const* const pLn = rBox.GetLine(*pBrd);
3692 m_aSectionBreaks.append(
3693 OutBorderLine(m_rExport, pLn, *pBrdNms, rBox.GetDistance(*pBrd), eShadowLocation));
3694 }
3695 }
3696
3698 {
3700 m_aSectionBreaks.setLength(0);
3701 }
3702}
3703
3704void RtfAttributeOutput::FormatColumns_Impl(sal_uInt16 nCols, const SwFormatCol& rCol, bool bEven,
3705 SwTwips nPageSize)
3706{
3708 m_rExport.OutLong(nCols);
3709
3710 if (rCol.GetLineAdj() != COLADJ_NONE)
3712
3713 if (bEven)
3714 {
3716 m_rExport.OutLong(rCol.GetGutterWidth(true));
3717 }
3718 else
3719 {
3720 const SwColumns& rColumns = rCol.GetColumns();
3721 for (sal_uInt16 n = 0; n < nCols;)
3722 {
3724 m_rExport.OutLong(n + 1);
3725
3727 m_rExport.OutLong(rCol.CalcPrtColWidth(n, nPageSize));
3728
3729 if (++n != nCols)
3730 {
3732 m_rExport.OutLong(rColumns[n - 1].GetRight() + rColumns[n].GetLeft());
3733 }
3734 }
3735 }
3736}
3737
3739{
3740 if (rItem.GetValue())
3742}
3743
3745{
3746 SAL_INFO("sw.rtf", "TODO: " << __func__);
3747}
3748
3750{
3751 if (!rNumbering.IsCount())
3753}
3754
3756{
3757 SvxFrameDirection nDir = rDirection.GetValue();
3758 if (nDir == SvxFrameDirection::Environment)
3760
3762 {
3763 if (nDir == SvxFrameDirection::Vertical_RL_TB)
3764 {
3766 m_aSectionBreaks.append(static_cast<sal_Int32>(1));
3768 {
3770 m_aSectionBreaks.setLength(0);
3771 }
3772 }
3773 return;
3774 }
3775
3777 {
3778 if (nDir == SvxFrameDirection::Vertical_RL_TB)
3779 {
3780 // Top to bottom non-ASCII font
3781 m_aFlyProperties.push_back(std::make_pair<OString, OString>("txflTextFlow", "3"));
3782 }
3783 else if (rDirection.GetValue() == SvxFrameDirection::Vertical_LR_BT)
3784 {
3785 // Bottom to top non-ASCII font
3786 m_aFlyProperties.push_back(std::make_pair<OString, OString>("txflTextFlow", "2"));
3787 }
3788 return;
3789 }
3790
3791 if (nDir == SvxFrameDirection::Horizontal_RL_TB)
3793 else
3795}
3796
3798{
3799 const std::map<OUString, css::uno::Any>& rMap = rItem.GetGrabBag();
3800 for (const auto& rValue : rMap)
3801 {
3802 if (rValue.first == "ParaTopMarginBeforeAutoSpacing")
3803 {
3805 rValue.second >>= m_nParaBeforeSpacing;
3807 }
3808 else if (rValue.first == "ParaBottomMarginAfterAutoSpacing")
3809 {
3811 rValue.second >>= m_nParaAfterSpacing;
3813 }
3814 }
3815}
3816
3818
3820
3822{
3823 OUString sCmd; // for optional Parameters
3824 switch (pField->GetTyp()->Which())
3825 {
3826 //#i119803# Export user field for RTF filter
3827 case SwFieldIds::User:
3828 sCmd = pField->GetTyp()->GetName();
3829 m_rExport.OutputField(pField, ww::eNONE, sCmd);
3830 break;
3831 default:
3832 m_rExport.OutputField(pField, ww::eUNKNOWN, sCmd);
3833 break;
3834 }
3835}
3836
3837void RtfAttributeOutput::RefField(const SwField& /*rField*/, const OUString& /*rRef*/)
3838{
3839 SAL_INFO("sw.rtf", "TODO: " << __func__);
3840}
3841
3843{
3844 SAL_INFO("sw.rtf", "TODO: " << __func__);
3845}
3846
3847void RtfAttributeOutput::SetField(const SwField& /*rField*/, ww::eField /*eType*/,
3848 const OUString& /*rCmd*/)
3849{
3850 SAL_INFO("sw.rtf", "TODO: " << __func__);
3851}
3852
3854{
3855 const SwPostItField& rPField = *static_cast<const SwPostItField*>(pField);
3856
3857 OString aName = OUStringToOString(rPField.GetName(), RTL_TEXTENCODING_UTF8);
3858 auto it = m_rOpenedAnnotationMarksIds.find(aName);
3859 if (it != m_rOpenedAnnotationMarksIds.end())
3860 {
3861 // In case this field is inside annotation marks, we want to write the
3862 // annotation itself after the annotation mark is closed, not here.
3863 m_aPostitFields[it->second] = &rPField;
3864 return;
3865 }
3866
3869 m_aRunText->append("}");
3872 m_aRunText->append("}");
3874
3876
3878 {
3881 m_aRunText->append('}');
3882 }
3884 m_aRunText->append(static_cast<sal_Int32>(sw::ms::DateTime2DTTM(rPField.GetDateTime())));
3885 m_aRunText->append('}');
3886 if (const OutlinerParaObject* pObject = rPField.GetTextObject())
3888 m_aRunText->append('}');
3889}
3890
3892{
3893 // this is handled in OutputFlyFrame_Impl()
3894 return true;
3895}
3896
3898{
3901 " MACROBUTTON None ");
3902 RunText(pField->GetPar1());
3903 m_aRunText->append("}}");
3904 return false; // do not expand
3905}
3906
3908 : AttributeOutputBase("") // ConvertURL isn't used now in RTF output
3909 , m_rExport(rExport)
3910 , m_pPrevPageDesc(nullptr)
3911 , m_nStyleId(0)
3912 , m_nListId(0)
3913 , m_bIsRTL(false)
3914 , m_nScript(i18n::ScriptType::LATIN)
3915 , m_bControlLtrRtl(false)
3916 , m_nNextAnnotationMarkId(0)
3917 , m_nCurrentAnnotationMarkId(-1)
3918 , m_bTableCellOpen(false)
3919 , m_nTableDepth(0)
3920 , m_bTableAfterCell(false)
3921 , m_nColBreakNeeded(false)
3922 , m_bBufferSectionBreaks(false)
3923 , m_bBufferSectionHeaders(false)
3924 , m_bLastTable(true)
3925 , m_bWroteCellInfo(false)
3926 , m_bTableRowEnded(false)
3927 , m_bIsBeforeFirstParagraph(true)
3928 , m_bSingleEmptyRun(false)
3929 , m_bInRun(false)
3930 , m_bInRuby(false)
3931 , m_pFlyFrameSize(nullptr)
3932 , m_bParaBeforeAutoSpacing(false)
3933 , m_nParaBeforeSpacing(0)
3934 , m_bParaAfterAutoSpacing(false)
3935 , m_nParaAfterSpacing(0)
3936{
3937}
3938
3940
3942
3943// These are used by wwFont::WriteRtf()
3944
3946void RtfAttributeOutput::StartFont(const OUString& rFamilyName) const
3947{
3948 // write the font name hex-encoded, but without Unicode - Word at least
3949 // cannot read *both* Unicode and fallback as written by OutString
3951 msfilter::rtfutil::OutString(rFamilyName, m_rExport.GetCurrentEncoding(), false).getStr());
3952}
3953
3956{
3957 m_rExport.Strm().WriteCharPtr(";}");
3959}
3960
3962void RtfAttributeOutput::FontAlternateName(const OUString& rName) const
3963{
3964 m_rExport.Strm()
3965 .WriteChar('{')
3968 .WriteChar(' ');
3969 // write the font name hex-encoded, but without Unicode - Word at least
3970 // cannot read *both* Unicode and fallback as written by OutString
3971 m_rExport.Strm()
3972 .WriteCharPtr(
3974 .WriteChar('}');
3975}
3976
3979{
3981 m_rExport.OutULong(nCharSet);
3982 m_rExport.Strm().WriteChar(' ');
3983 m_rExport.SetCurrentEncoding(rtl_getTextEncodingFromWindowsCharset(nCharSet));
3984}
3985
3988{
3990
3991 const char* pStr = OOO_STRING_SVTOOLS_RTF_FNIL;
3992 switch (eFamily)
3993 {
3994 case FAMILY_ROMAN:
3996 break;
3997 case FAMILY_SWISS:
3999 break;
4000 case FAMILY_MODERN:
4002 break;
4003 case FAMILY_SCRIPT:
4005 break;
4006 case FAMILY_DECORATIVE:
4008 break;
4009 default:
4010 break;
4011 }
4013}
4014
4017{
4019
4020 sal_uInt16 nVal = 0;
4021 switch (ePitch)
4022 {
4023 case PITCH_FIXED:
4024 nVal = 1;
4025 break;
4026 case PITCH_VARIABLE:
4027 nVal = 2;
4028 break;
4029 default:
4030 break;
4031 }
4032 m_rExport.OutULong(nVal);
4033}
4034
4035static void lcl_AppendSP(OStringBuffer& rBuffer, const char cName[], const OUString& rValue,
4036 const RtfExport& rExport)
4037{
4038 rBuffer.append("{" OOO_STRING_SVTOOLS_RTF_SP "{"); // "{\sp{"
4039 rBuffer.append(OOO_STRING_SVTOOLS_RTF_SN " "); //" \sn "
4040 rBuffer.append(cName); //"PropName"
4041 rBuffer.append("}{" OOO_STRING_SVTOOLS_RTF_SV " ");
4042 // "}{ \sv "
4043 rBuffer.append(msfilter::rtfutil::OutString(rValue, rExport.GetCurrentEncoding()));
4044 rBuffer.append("}}");
4045}
4046
4047static OString ExportPICT(const SwFlyFrameFormat* pFlyFrameFormat, const Size& rOrig,
4048 const Size& rRendered, const Size& rMapped, const SwCropGrf& rCr,
4049 const char* pBLIPType, const sal_uInt8* pGraphicAry, sal_uInt64 nSize,
4050 const RtfExport& rExport, SvStream* pStream = nullptr,
4051 bool bWritePicProp = true, const SwAttrSet* pAttrSet = nullptr)
4052{
4053 OStringBuffer aRet;
4054 if (pBLIPType && nSize && pGraphicAry)
4055 {
4056 bool bIsWMF = std::strcmp(pBLIPType, OOO_STRING_SVTOOLS_RTF_WMETAFILE) == 0;
4057
4058 aRet.append("{" OOO_STRING_SVTOOLS_RTF_PICT);
4059
4060 if (pFlyFrameFormat && bWritePicProp)
4061 {
4062 OUString sDescription = pFlyFrameFormat->GetObjDescription();
4063 //write picture properties - wzDescription at first
4064 //looks like: "{\*\picprop{\sp{\sn PropertyName}{\sv PropertyValue}}}"
4065 aRet.append(
4067 lcl_AppendSP(aRet, "wzDescription", sDescription, rExport);
4068 OUString sName = pFlyFrameFormat->GetObjTitle();
4069 lcl_AppendSP(aRet, "wzName", sName, rExport);
4070
4071 if (pAttrSet)
4072 {
4073 MirrorGraph eMirror = pAttrSet->Get(RES_GRFATR_MIRRORGRF).GetValue();
4074 if (eMirror == MirrorGraph::Vertical || eMirror == MirrorGraph::Both)
4075 // Mirror on the vertical axis is a horizontal flip.
4076 lcl_AppendSP(aRet, "fFlipH", "1", rExport);
4077 }
4078
4079 aRet.append("}"); //"}"
4080 }
4081
4082 tools::Long nXCroppedSize = rOrig.Width() - (rCr.GetLeft() + rCr.GetRight());
4083 tools::Long nYCroppedSize = rOrig.Height() - (rCr.GetTop() + rCr.GetBottom());
4084 /* Graphic with a zero height or width, typically copied from webpages, caused crashes. */
4085 if (!nXCroppedSize)
4086 nXCroppedSize = 100;
4087 if (!nYCroppedSize)
4088 nYCroppedSize = 100;
4089
4090 //Given the original size and taking cropping into account
4091 //first, how much has the original been scaled to get the
4092 //final rendered size
4094 aRet.append(static_cast<sal_Int32>((100 * rRendered.Width()) / nXCroppedSize));
4096 aRet.append(static_cast<sal_Int32>((100 * rRendered.Height()) / nYCroppedSize));
4097
4099 aRet.append(rCr.GetLeft());
4101 aRet.append(rCr.GetRight());
4103 aRet.append(rCr.GetTop());
4105 aRet.append(rCr.GetBottom());
4106
4107 aRet.append(OOO_STRING_SVTOOLS_RTF_PICW);
4108 aRet.append(static_cast<sal_Int32>(rMapped.Width()));
4109 aRet.append(OOO_STRING_SVTOOLS_RTF_PICH);
4110 aRet.append(static_cast<sal_Int32>(rMapped.Height()));
4111
4113 aRet.append(static_cast<sal_Int32>(rOrig.Width()));
4115 aRet.append(static_cast<sal_Int32>(rOrig.Height()));
4116
4117 aRet.append(pBLIPType);
4118 if (bIsWMF)
4119 {
4120 aRet.append(sal_Int32(8));
4121 msfilter::rtfutil::StripMetafileHeader(pGraphicAry, nSize);
4122 }
4123 aRet.append(SAL_NEWLINE_STRING);
4124 if (pStream)
4125 {
4126 pStream->WriteOString(aRet);
4127 aRet.setLength(0);
4128 }
4129 if (pStream)
4130 msfilter::rtfutil::WriteHex(pGraphicAry, nSize, pStream);
4131 else
4132 aRet.append(msfilter::rtfutil::WriteHex(pGraphicAry, nSize));
4133 aRet.append('}');
4134 if (pStream)
4135 {
4136 pStream->WriteOString(aRet);
4137 aRet.setLength(0);
4138 }
4139 }
4140 return aRet.makeStringAndClear();
4141}
4142
4144 SwOLENode& rOLENode, const Size& rSize)
4145{
4147 Size aSize(rOLENode.GetTwipSize());
4148 Size aRendered(aSize);
4149 aRendered.setWidth(rSize.Width());
4150 aRendered.setHeight(rSize.Height());
4151 const Graphic* pGraphic = rOLENode.GetGraphic();
4152 Size aMapped(pGraphic->GetPrefSize());
4153 auto& rCr = rOLENode.GetAttr(RES_GRFATR_CROPGRF);
4154 const char* pBLIPType = OOO_STRING_SVTOOLS_RTF_PNGBLIP;
4155 const sal_uInt8* pGraphicAry = nullptr;
4156 SvMemoryStream aStream;
4157 if (GraphicConverter::Export(aStream, *pGraphic, ConvertDataFormat::PNG) != ERRCODE_NONE)
4158 SAL_WARN("sw.rtf", "failed to export the graphic");
4159 sal_uInt32 nSize = aStream.TellEnd();
4160 pGraphicAry = static_cast<sal_uInt8 const*>(aStream.GetData());
4161 m_aRunText->append(ExportPICT(pFlyFrameFormat, aSize, aRendered, aMapped, rCr, pBLIPType,
4162 pGraphicAry, nSize, m_rExport));
4163 m_aRunText->append("}"); // shppict
4166 SvMemoryStream aWmfStream;
4167 if (GraphicConverter::Export(aWmfStream, *pGraphic, ConvertDataFormat::WMF) != ERRCODE_NONE)
4168 SAL_WARN("sw.rtf", "failed to export the graphic");
4169 nSize = aWmfStream.TellEnd();
4170 pGraphicAry = static_cast<sal_uInt8 const*>(aWmfStream.GetData());
4171 m_aRunText->append(ExportPICT(pFlyFrameFormat, aSize, aRendered, aMapped, rCr, pBLIPType,
4172 pGraphicAry, nSize, m_rExport));
4173 m_aRunText->append("}"); // nonshppict
4174}
4175
4177 SwOLENode& rOLENode, const Size& rSize)
4178{
4179 uno::Reference<embed::XEmbeddedObject> xObj(rOLENode.GetOLEObj().GetOleRef());
4180 sal_Int64 nAspect = rOLENode.GetAspect();
4181 svt::EmbeddedObjectRef aObjRef(xObj, nAspect);
4182 SvGlobalName aObjName(aObjRef->getClassID());
4183
4184 if (!SotExchange::IsMath(aObjName))
4185 return false;
4186
4188 uno::Reference<util::XCloseable> xClosable = xObj->getComponent();
4189 if (!xClosable.is())
4190 return false;
4191 auto pBase = dynamic_cast<oox::FormulaImExportBase*>(xClosable.get());
4192 SAL_WARN_IF(!pBase, "sw.rtf", "Math OLE object cannot write out RTF");
4193 if (pBase)
4194 {
4195 OStringBuffer aBuf;
4196 pBase->writeFormulaRtf(aBuf, m_rExport.GetCurrentEncoding());
4198 }
4199
4200 // Replacement graphic.
4202 FlyFrameOLEReplacement(pFlyFrameFormat, rOLENode, rSize);
4203 m_aRunText->append("}"); // mmathPict
4204 m_aRunText->append("}"); // mmath
4205
4206 return true;
4207}
4208
4209void RtfAttributeOutput::FlyFrameOLE(const SwFlyFrameFormat* pFlyFrameFormat, SwOLENode& rOLENode,
4210 const Size& rSize)
4211{
4212 if (FlyFrameOLEMath(pFlyFrameFormat, rOLENode, rSize))
4213 return;
4214
4215 FlyFrameOLEReplacement(pFlyFrameFormat, rOLENode, rSize);
4216}
4217
4219 const SwGrfNode* pGrfNode)
4220{
4221 SvMemoryStream aStream;
4222 const sal_uInt8* pGraphicAry = nullptr;
4223 sal_uInt32 nSize = 0;
4224
4225 const Graphic& rGraphic(pGrfNode->GetGrf());
4226
4227 // If there is no graphic there is not much point in parsing it
4228 if (rGraphic.GetType() == GraphicType::NONE)
4229 return;
4230
4231 ConvertDataFormat aConvertDestinationFormat = ConvertDataFormat::WMF;
4232 const char* pConvertDestinationBLIPType = OOO_STRING_SVTOOLS_RTF_WMETAFILE;
4233
4234 GfxLink aGraphicLink;
4235 const char* pBLIPType = nullptr;
4236 if (rGraphic.IsGfxLink())
4237 {
4238 aGraphicLink = rGraphic.GetGfxLink();
4239 nSize = aGraphicLink.GetDataSize();
4240 pGraphicAry = aGraphicLink.GetData();
4241 switch (aGraphicLink.GetType())
4242 {
4243 // #i15508# trying to add BMP type for better exports, need to check if this works
4244 // checked, does not work. Also need to reset pGraphicAry to NULL to force conversion
4245 // to PNG, else the BMP array will be used.
4246 // It may work using direct DIB data, but that needs to be checked eventually
4247 //
4248 // #i15508# before GfxLinkType::NativeBmp was added the graphic data
4249 // (to be hold in pGraphicAry) was not available; thus for now to stay
4250 // compatible, keep it that way by assigning NULL value to pGraphicAry
4251 case GfxLinkType::NativeBmp:
4252 // pBLIPType = OOO_STRING_SVTOOLS_RTF_WBITMAP;
4253 pGraphicAry = nullptr;
4254 break;
4255
4256 case GfxLinkType::NativeJpg:
4258 break;
4259 case GfxLinkType::NativePng:
4261 break;
4262 case GfxLinkType::NativeWmf:
4263 pBLIPType = aGraphicLink.IsEMF() ? OOO_STRING_SVTOOLS_RTF_EMFBLIP
4265 break;
4266 case GfxLinkType::NativeGif:
4267 // GIF is not supported by RTF, but we override default conversion to WMF, PNG seems fits better here.
4268 aConvertDestinationFormat = ConvertDataFormat::PNG;
4269 pConvertDestinationBLIPType = OOO_STRING_SVTOOLS_RTF_PNGBLIP;
4270 break;
4271 default:
4272 break;
4273 }
4274 }
4275
4276 GraphicType eGraphicType = rGraphic.GetType();
4277 if (!pGraphicAry)
4278 {
4279 if (ERRCODE_NONE
4280 == GraphicConverter::Export(aStream, rGraphic,
4281 (eGraphicType == GraphicType::Bitmap)
4282 ? ConvertDataFormat::PNG
4283 : ConvertDataFormat::WMF))
4284 {
4285 pBLIPType = (eGraphicType == GraphicType::Bitmap) ? OOO_STRING_SVTOOLS_RTF_PNGBLIP
4287 nSize = aStream.TellEnd();
4288 pGraphicAry = static_cast<sal_uInt8 const*>(aStream.GetData());
4289 }
4290 }
4291
4292 Size aMapped(eGraphicType == GraphicType::Bitmap ? rGraphic.GetSizePixel()
4293 : rGraphic.GetPrefSize());
4294
4295 auto& rCr = pGrfNode->GetAttr(RES_GRFATR_CROPGRF);
4296
4297 //Get original size in twips
4298 Size aSize(pGrfNode->GetTwipSize());
4299 Size aRendered(aSize);
4300
4301 const SwFormatFrameSize& rS = pFlyFrameFormat->GetFrameSize();
4302 aRendered.setWidth(rS.GetWidth());
4303 aRendered.setHeight(rS.GetHeight());
4304
4305 ww8::Frame* pFrame = nullptr;
4306 for (auto& rFrame : m_rExport.m_aFrames)
4307 {
4308 if (pFlyFrameFormat == &rFrame.GetFrameFormat())
4309 {
4310 pFrame = &rFrame;
4311 break;
4312 }
4313 }
4314
4315 /*
4316 If the graphic is not of type WMF then we will have to store two
4317 graphics, one in the native format wrapped in shppict, and the other in
4318 the wmf format wrapped in nonshppict, so as to keep wordpad happy. If it's
4319 a wmf already then we don't need any such wrapping
4320 */
4321 bool bIsWMF = pBLIPType && std::strcmp(pBLIPType, OOO_STRING_SVTOOLS_RTF_WMETAFILE) == 0;
4322 const SwAttrSet* pAttrSet = pGrfNode->GetpSwAttrSet();
4323 if (!pFrame || pFrame->IsInline())
4324 {
4325 if (!bIsWMF)
4328 }
4329 else
4330 {
4334 m_pFlyFrameSize = &aRendered;
4335 m_rExport.m_pParentFrame = pFrame;
4338 m_rExport.OutputFormat(pFrame->GetFrameFormat(), false, false, true);
4341 m_rExport.m_pParentFrame = nullptr;
4342 m_pFlyFrameSize = nullptr;
4343 std::vector<std::pair<OString, OString>> aFlyProperties{
4344 { "shapeType", OString::number(ESCHER_ShpInst_PictureFrame) },
4345
4346 { "wzDescription", msfilter::rtfutil::OutString(pFlyFrameFormat->GetObjDescription(),
4348 { "wzName", msfilter::rtfutil::OutString(pFlyFrameFormat->GetObjTitle(),
4350 };
4351
4352 // If we have a wrap polygon, then handle that here.
4353 if (pFlyFrameFormat->GetSurround().IsContour())
4354 {
4355 if (const SwNoTextNode* pNd
4356 = sw::util::GetNoTextNodeFromSwFrameFormat(*pFlyFrameFormat))
4357 {
4358 const tools::PolyPolygon* pPolyPoly = pNd->HasContour();
4359 if (pPolyPoly && pPolyPoly->Count())
4360 {
4362 *pPolyPoly, pNd, /*bCorrectCrop=*/true);
4363 OStringBuffer aVerticies;
4364 for (sal_uInt16 i = 0; i < aPoly.GetSize(); ++i)
4365 aVerticies.append(";(" + OString::number(aPoly[i].X()) + ","
4366 + OString::number(aPoly[i].Y()) + ")");
4367 aFlyProperties.push_back(std::make_pair<OString, OString>(
4368 "pWrapPolygonVertices",
4369 "8;" + OString::number(aPoly.GetSize()) + aVerticies));
4370 }
4371 }
4372 }
4373
4374 // Below text, behind document, opaque: they all refer to the same thing.
4375 if (!pFlyFrameFormat->GetOpaque().GetValue())
4376 aFlyProperties.push_back(std::make_pair<OString, OString>("fBehindDocument", "1"));