xref: /core/sw/source/filter/ww8/rtfexport.cxx (revision 7c70a59c)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include "rtfexport.hxx"
21 
22 #include "rtfexportfilter.hxx"
23 #include "rtfsdrexport.hxx"
24 #include "rtfattributeoutput.hxx"
25 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
26 #include <com/sun/star/frame/XModel.hpp>
27 #include <com/sun/star/i18n/ScriptType.hpp>
28 #include <com/sun/star/beans/XPropertySet.hpp>
29 #include <com/sun/star/beans/XPropertySetInfo.hpp>
30 #include <docsh.hxx>
31 #include <viewsh.hxx>
32 #include <viewopt.hxx>
33 #include <fmtpdsc.hxx>
34 #include <ftninfo.hxx>
35 #include <fmthdft.hxx>
36 #include <editeng/colritem.hxx>
37 #include <editeng/udlnitem.hxx>
38 #include <editeng/fontitem.hxx>
39 #include <editeng/paperinf.hxx>
40 #include <editeng/brushitem.hxx>
41 #include <editeng/protitem.hxx>
42 #include <editeng/lrspitem.hxx>
43 #include <editeng/ulspitem.hxx>
44 #include <editeng/boxitem.hxx>
45 #include <editeng/shaditem.hxx>
46 #include <lineinfo.hxx>
47 #include <redline.hxx>
48 #include <swmodule.hxx>
49 #include <IDocumentLayoutAccess.hxx>
50 #include <comphelper/string.hxx>
51 #include <svtools/rtfkeywd.hxx>
52 #include <filter/msfilter/rtfutil.hxx>
53 #include <unotools/docinfohelper.hxx>
54 #include <osl/diagnose.h>
55 #include <rtl/tencinfo.h>
56 #include <sal/log.hxx>
57 #if OSL_DEBUG_LEVEL > 1
58 #include <iostream>
59 #endif
60 #include <svx/xflclit.hxx>
61 #include <fmtmeta.hxx>
62 #include <IDocumentSettingAccess.hxx>
63 #include <fmtfsize.hxx>
64 #include <ndtxt.hxx>
65 #include <numrule.hxx>
66 #include <frmatr.hxx>
67 #include <swtable.hxx>
68 #include <IMark.hxx>
69 
70 using namespace ::com::sun::star;
71 
72 // the default text encoding for the export, if it doesn't fit unicode will
73 // be used
74 #define DEF_ENCODING RTL_TEXTENCODING_ASCII_US
75 
76 AttributeOutputBase& RtfExport::AttrOutput() const { return *m_pAttrOutput; }
77 
78 MSWordSections& RtfExport::Sections() const { return *m_pSections; }
79 
80 RtfSdrExport& RtfExport::SdrExporter() const { return *m_pSdrExport; }
81 
82 bool RtfExport::CollapseScriptsforWordOk(sal_uInt16 nScript, sal_uInt16 nWhich)
83 {
84     // FIXME is this actually true for rtf? - this is copied from DOCX
85     if (nScript == i18n::ScriptType::ASIAN)
86     {
87         // for asian in ww8, there is only one fontsize
88         // and one fontstyle (posture/weight)
89         switch (nWhich)
90         {
91             case RES_CHRATR_FONTSIZE:
92             case RES_CHRATR_POSTURE:
93             case RES_CHRATR_WEIGHT:
94                 return false;
95             default:
96                 break;
97         }
98     }
99     else if (nScript != i18n::ScriptType::COMPLEX)
100     {
101         // for western in ww8, there is only one fontsize
102         // and one fontstyle (posture/weight)
103         switch (nWhich)
104         {
105             case RES_CHRATR_CJK_FONTSIZE:
106             case RES_CHRATR_CJK_POSTURE:
107             case RES_CHRATR_CJK_WEIGHT:
108                 return false;
109             default:
110                 break;
111         }
112     }
113     return true;
114 }
115 
116 void RtfExport::AppendBookmarks(const SwTextNode& rNode, sal_Int32 nCurrentPos, sal_Int32 nLen)
117 {
118     std::vector<OUString> aStarts;
119     std::vector<OUString> aEnds;
120 
121     IMarkVector aMarks;
122     if (GetBookmarks(rNode, nCurrentPos, nCurrentPos + nLen, aMarks))
123     {
124         for (const auto& pMark : aMarks)
125         {
126             const sal_Int32 nStart = pMark->GetMarkStart().nContent.GetIndex();
127             const sal_Int32 nEnd = pMark->GetMarkEnd().nContent.GetIndex();
128 
129             if (nStart == nCurrentPos)
130                 aStarts.push_back(pMark->GetName());
131 
132             if (nEnd == nCurrentPos)
133                 aEnds.push_back(pMark->GetName());
134         }
135     }
136 
137     m_pAttrOutput->WriteBookmarks_Impl(aStarts, aEnds);
138 }
139 
140 void RtfExport::AppendBookmark(const OUString& rName)
141 {
142     std::vector<OUString> aStarts;
143     std::vector<OUString> aEnds;
144 
145     aStarts.push_back(rName);
146     aEnds.push_back(rName);
147 
148     m_pAttrOutput->WriteBookmarks_Impl(aStarts, aEnds);
149 }
150 
151 void RtfExport::AppendAnnotationMarks(const SwWW8AttrIter& rAttrs, sal_Int32 nCurrentPos,
152                                       sal_Int32 nLen)
153 {
154     std::vector<OUString> aStarts;
155     std::vector<OUString> aEnds;
156 
157     IMarkVector aMarks;
158     if (GetAnnotationMarks(rAttrs, nCurrentPos, nCurrentPos + nLen, aMarks))
159     {
160         for (const auto& pMark : aMarks)
161         {
162             const sal_Int32 nStart = pMark->GetMarkStart().nContent.GetIndex();
163             const sal_Int32 nEnd = pMark->GetMarkEnd().nContent.GetIndex();
164 
165             if (nStart == nCurrentPos)
166                 aStarts.push_back(pMark->GetName());
167 
168             if (nEnd == nCurrentPos)
169                 aEnds.push_back(pMark->GetName());
170         }
171     }
172 
173     m_pAttrOutput->WriteAnnotationMarks_Impl(aStarts, aEnds);
174 }
175 
176 //For i120928,to export graphic of bullet for RTF filter
177 void RtfExport::ExportGrfBullet(const SwTextNode& /*rNd*/)
178 {
179     // Noop, would be too late, see WriteNumbering() instead.
180 }
181 
182 void RtfExport::WriteChar(sal_Unicode /*c*/) { /* WriteChar() has nothing to do for rtf. */}
183 
184 static bool IsExportNumRule(const SwNumRule& rRule)
185 {
186     sal_uInt8 nEnd = MAXLEVEL;
187     while (nEnd-- && !rRule.GetNumFormat(nEnd))
188         ;
189     ++nEnd;
190 
191     sal_uInt8 nLvl;
192 
193     for (nLvl = 0; nLvl < nEnd; ++nLvl)
194     {
195         const SwNumFormat* pNFormat = &rRule.Get(nLvl);
196         if (SVX_NUM_NUMBER_NONE != pNFormat->GetNumberingType() || !pNFormat->GetPrefix().isEmpty()
197             || (!pNFormat->GetSuffix().isEmpty() && pNFormat->GetSuffix() != "."))
198             break;
199     }
200 
201     return nLvl != nEnd;
202 }
203 
204 void RtfExport::BuildNumbering()
205 {
206     const SwNumRuleTable& rListTable = m_rDoc.GetNumRuleTable();
207 
208     SwNumRule* pOutlineRule = m_rDoc.GetOutlineNumRule();
209     if (IsExportNumRule(*pOutlineRule))
210         GetNumberingId(*pOutlineRule);
211 
212     for (auto n = rListTable.size(); n;)
213     {
214         SwNumRule* pRule = rListTable[--n];
215         if (!SwDoc::IsUsed(*pRule))
216             continue;
217 
218         if (IsExportNumRule(*pRule))
219             GetNumberingId(*pRule);
220     }
221 }
222 
223 void RtfExport::WriteNumbering()
224 {
225     SAL_INFO("sw.rtf", __func__ << " start");
226 
227     if (!m_pUsedNumTable)
228         return; // no numbering is used
229 
230     Strm()
231         .WriteChar('{')
232         .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_IGNORE)
233         .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LISTTABLE);
234 
235     CollectGrfsOfBullets();
236     if (!m_vecBulletPic.empty())
237         Strm()
238             .WriteChar('{')
239             .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_IGNORE)
240             .WriteCharPtr(LO_STRING_SVTOOLS_RTF_LISTPICTURE);
241     BulletDefinitions();
242     if (!m_vecBulletPic.empty())
243         Strm().WriteChar('}');
244 
245     AbstractNumberingDefinitions();
246     Strm().WriteChar('}');
247 
248     Strm().WriteChar('{').WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LISTOVERRIDETABLE);
249     NumberingDefinitions();
250     Strm().WriteChar('}');
251 
252     SAL_INFO("sw.rtf", __func__ << " end");
253 }
254 
255 void RtfExport::WriteRevTab()
256 {
257     int nRevAuthors = m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size();
258 
259     if (nRevAuthors < 1)
260         return;
261 
262     // RTF always seems to use Unknown as the default first entry
263     GetRedline("Unknown");
264 
265     for (SwRangeRedline* pRedl : m_rDoc.getIDocumentRedlineAccess().GetRedlineTable())
266     {
267         GetRedline(SW_MOD()->GetRedlineAuthor(pRedl->GetAuthor()));
268     }
269 
270     // Now write the table
271     Strm()
272         .WriteChar('{')
273         .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_IGNORE)
274         .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_REVTBL)
275         .WriteChar(' ');
276     for (std::size_t i = 0; i < m_aRedlineTable.size(); ++i)
277     {
278         const OUString* pAuthor = GetRedline(i);
279         Strm().WriteChar('{');
280         if (pAuthor)
281             Strm().WriteCharPtr(
282                 msfilter::rtfutil::OutString(*pAuthor, m_eDefaultEncoding).getStr());
283         Strm().WriteCharPtr(";}");
284     }
285     Strm().WriteChar('}').WriteCharPtr(SAL_NEWLINE_STRING);
286 }
287 
288 void RtfExport::WriteHeadersFooters(sal_uInt8 nHeadFootFlags, const SwFrameFormat& rFormat,
289                                     const SwFrameFormat& rLeftFormat,
290                                     const SwFrameFormat& rFirstPageFormat, sal_uInt8 /*nBreakCode*/)
291 {
292     // headers
293     if (nHeadFootFlags & nsHdFtFlags::WW8_HEADER_EVEN)
294         WriteHeaderFooter(rLeftFormat, true, OOO_STRING_SVTOOLS_RTF_HEADERL);
295 
296     if (nHeadFootFlags & nsHdFtFlags::WW8_HEADER_ODD)
297         WriteHeaderFooter(rFormat, true, OOO_STRING_SVTOOLS_RTF_HEADER);
298 
299     if (nHeadFootFlags & nsHdFtFlags::WW8_HEADER_FIRST)
300         WriteHeaderFooter(rFirstPageFormat, true, OOO_STRING_SVTOOLS_RTF_HEADERF, true);
301 
302     // footers
303     if (nHeadFootFlags & nsHdFtFlags::WW8_FOOTER_EVEN)
304         WriteHeaderFooter(rLeftFormat, false, OOO_STRING_SVTOOLS_RTF_FOOTERL);
305 
306     if (nHeadFootFlags & nsHdFtFlags::WW8_FOOTER_ODD)
307         WriteHeaderFooter(rFormat, false, OOO_STRING_SVTOOLS_RTF_FOOTER);
308 
309     if (nHeadFootFlags & nsHdFtFlags::WW8_FOOTER_FIRST)
310         WriteHeaderFooter(rFirstPageFormat, false, OOO_STRING_SVTOOLS_RTF_FOOTERF, true);
311 }
312 
313 void RtfExport::OutputField(const SwField* pField, ww::eField eFieldType, const OUString& rFieldCmd,
314                             FieldFlags nMode)
315 {
316     m_pAttrOutput->WriteField_Impl(pField, eFieldType, rFieldCmd, nMode);
317 }
318 
319 void RtfExport::WriteFormData(const ::sw::mark::IFieldmark& /*rFieldmark*/)
320 {
321     SAL_INFO("sw.rtf", "TODO: " << __func__);
322 }
323 
324 void RtfExport::WriteHyperlinkData(const ::sw::mark::IFieldmark& /*rFieldmark*/)
325 {
326     SAL_INFO("sw.rtf", "TODO: " << __func__);
327 }
328 
329 void RtfExport::DoComboBox(const OUString& /*rName*/, const OUString& /*rHelp*/,
330                            const OUString& /*rToolTip*/, const OUString& /*rSelected*/,
331                            const uno::Sequence<OUString>& /*rListItems*/)
332 {
333     // this is handled in RtfAttributeOutput::OutputFlyFrame_Impl
334 }
335 
336 void RtfExport::DoFormText(const SwInputField* pField)
337 {
338     OUString sResult = pField->ExpandField(true, nullptr);
339     const OUString& rHelp = pField->GetHelp();
340     OUString sName = pField->GetPar2();
341     const OUString& rStatus = pField->GetToolTip();
342     m_pAttrOutput->RunText().append("{" OOO_STRING_SVTOOLS_RTF_FIELD
343                                     "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FLDINST
344                                     "{ FORMTEXT }");
345     m_pAttrOutput->RunText().append(
346         "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FORMFIELD
347         " {" OOO_STRING_SVTOOLS_RTF_FFTYPE "0");
348     if (!rHelp.isEmpty())
349         m_pAttrOutput->RunText().append(OOO_STRING_SVTOOLS_RTF_FFOWNHELP);
350     if (!rStatus.isEmpty())
351         m_pAttrOutput->RunText().append(OOO_STRING_SVTOOLS_RTF_FFOWNSTAT);
352     m_pAttrOutput->RunText().append(OOO_STRING_SVTOOLS_RTF_FFTYPETXT "0");
353 
354     if (!sName.isEmpty())
355         m_pAttrOutput->RunText()
356             .append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFNAME " ")
357             .append(msfilter::rtfutil::OutString(sName, m_eDefaultEncoding))
358             .append("}");
359     if (!rHelp.isEmpty())
360         m_pAttrOutput->RunText()
361             .append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFHELPTEXT " ")
362             .append(msfilter::rtfutil::OutString(rHelp, m_eDefaultEncoding))
363             .append("}");
364     m_pAttrOutput->RunText()
365         .append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFDEFTEXT " ")
366         .append(msfilter::rtfutil::OutString(sResult, m_eDefaultEncoding))
367         .append("}");
368     if (!rStatus.isEmpty())
369         m_pAttrOutput->RunText()
370             .append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFSTATTEXT " ")
371             .append(msfilter::rtfutil::OutString(rStatus, m_eDefaultEncoding))
372             .append("}");
373     m_pAttrOutput->RunText().append("}}}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " ");
374     m_pAttrOutput->RunText()
375         .append(msfilter::rtfutil::OutString(sResult, m_eDefaultEncoding))
376         .append("}}");
377 }
378 
379 sal_uLong RtfExport::ReplaceCr(sal_uInt8 /*nChar*/)
380 {
381     // Completely unused for Rtf export... only here for code sharing
382     // purpose with binary export
383 
384     return 0;
385 }
386 
387 void RtfExport::WriteFonts()
388 {
389     Strm()
390         .WriteCharPtr(SAL_NEWLINE_STRING)
391         .WriteChar('{')
392         .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_FONTTBL);
393     m_aFontHelper.WriteFontTable(*m_pAttrOutput);
394     Strm().WriteChar('}');
395 }
396 
397 void RtfExport::WriteStyles()
398 {
399     SAL_INFO("sw.rtf", __func__ << " start");
400     m_pStyles->OutputStylesTable();
401     SAL_INFO("sw.rtf", __func__ << " end");
402 }
403 
404 void RtfExport::WriteFootnoteSettings()
405 {
406     const SwPageFootnoteInfo& rFootnoteInfo = m_rDoc.GetPageDesc(0).GetFootnoteInfo();
407     // Request a separator only in case the width is larger than zero.
408     bool bSeparator = double(rFootnoteInfo.GetWidth()) > 0;
409 
410     Strm()
411         .WriteChar('{')
412         .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_IGNORE)
413         .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_FTNSEP);
414     if (bSeparator)
415         Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_CHFTNSEP);
416     Strm().WriteChar('}');
417 }
418 
419 void RtfExport::WriteMainText()
420 {
421     SAL_INFO("sw.rtf", __func__ << " start");
422 
423     if (std::unique_ptr<SvxBrushItem> oBrush = getBackground(); oBrush)
424     {
425         Strm().WriteCharPtr(LO_STRING_SVTOOLS_RTF_VIEWBKSP).WriteChar('1');
426         Strm().WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_BACKGROUND);
427         Strm().WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_SHP);
428         Strm().WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_SHPINST);
429 
430         std::vector<std::pair<OString, OString>> aProperties;
431         aProperties.push_back(std::make_pair<OString, OString>("shapeType", "1"));
432         aProperties.push_back(std::make_pair<OString, OString>(
433             "fillColor", OString::number(wwUtility::RGBToBGR(oBrush->GetColor()))));
434         for (const std::pair<OString, OString>& rPair : aProperties)
435         {
436             Strm().WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_SP "{");
437             Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_SN " ");
438             Strm().WriteOString(rPair.first);
439             Strm().WriteCharPtr("}{" OOO_STRING_SVTOOLS_RTF_SV " ");
440             Strm().WriteOString(rPair.second);
441             Strm().WriteCharPtr("}}");
442         }
443         Strm().WriteChar('}'); // shpinst
444         Strm().WriteChar('}'); // shp
445         Strm().WriteChar('}'); // background
446     }
447 
448     SwTableNode* pTableNode = m_pCurPam->GetNode().FindTableNode();
449     if (m_pWriter && m_pWriter->m_bWriteOnlyFirstTable && pTableNode != nullptr)
450     {
451         m_pCurPam->GetPoint()->nNode = *pTableNode;
452         m_pCurPam->GetMark()->nNode = *(pTableNode->EndOfSectionNode());
453     }
454     else
455     {
456         m_pCurPam->GetPoint()->nNode
457             = m_rDoc.GetNodes().GetEndOfContent().StartOfSectionNode()->GetIndex();
458     }
459 
460     WriteText();
461 
462     SAL_INFO("sw.rtf", __func__ << " end");
463 }
464 
465 void RtfExport::WriteInfo()
466 {
467     OString aGenerator
468         = OUStringToOString(utl::DocInfoHelper::GetGeneratorString(), RTL_TEXTENCODING_UTF8);
469     Strm()
470         .WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_IGNORE LO_STRING_SVTOOLS_RTF_GENERATOR " ")
471         .WriteOString(aGenerator)
472         .WriteChar('}');
473     Strm().WriteChar('{').WriteCharPtr(OOO_STRING_SVTOOLS_RTF_INFO);
474 
475     SwDocShell* pDocShell(m_rDoc.GetDocShell());
476     uno::Reference<document::XDocumentProperties> xDocProps;
477     if (pDocShell)
478     {
479         uno::Reference<document::XDocumentPropertiesSupplier> xDPS(pDocShell->GetModel(),
480                                                                    uno::UNO_QUERY);
481         xDocProps.set(xDPS->getDocumentProperties());
482     }
483 
484     if (xDocProps.is())
485     {
486         // Handle user-defined properties.
487         uno::Reference<beans::XPropertyContainer> xUserDefinedProperties
488             = xDocProps->getUserDefinedProperties();
489         if (xUserDefinedProperties.is())
490         {
491             uno::Reference<beans::XPropertySet> xPropertySet(xUserDefinedProperties,
492                                                              uno::UNO_QUERY);
493             uno::Reference<beans::XPropertySetInfo> xPropertySetInfo
494                 = xPropertySet->getPropertySetInfo();
495             // Do we have explicit markup in RTF for this property name?
496             if (xPropertySetInfo->hasPropertyByName("Company"))
497             {
498                 OUString aValue;
499                 xPropertySet->getPropertyValue("Company") >>= aValue;
500                 OutUnicode(OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_COMPANY, aValue);
501             }
502         }
503 
504         OutUnicode(OOO_STRING_SVTOOLS_RTF_TITLE, xDocProps->getTitle(), true);
505         OutUnicode(OOO_STRING_SVTOOLS_RTF_SUBJECT, xDocProps->getSubject());
506 
507         OutUnicode(OOO_STRING_SVTOOLS_RTF_KEYWORDS,
508                    ::comphelper::string::convertCommaSeparated(xDocProps->getKeywords()));
509         OutUnicode(OOO_STRING_SVTOOLS_RTF_DOCCOMM, xDocProps->getDescription());
510 
511         OutUnicode(OOO_STRING_SVTOOLS_RTF_AUTHOR, xDocProps->getAuthor());
512         OutDateTime(OOO_STRING_SVTOOLS_RTF_CREATIM, xDocProps->getCreationDate());
513 
514         OutUnicode(OOO_STRING_SVTOOLS_RTF_AUTHOR, xDocProps->getModifiedBy());
515         OutDateTime(OOO_STRING_SVTOOLS_RTF_REVTIM, xDocProps->getModificationDate());
516 
517         OutDateTime(OOO_STRING_SVTOOLS_RTF_PRINTIM, xDocProps->getPrintDate());
518     }
519 
520     Strm().WriteChar('}');
521 }
522 
523 void RtfExport::WriteUserPropType(int nType)
524 {
525     Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PROPTYPE);
526     OutULong(nType);
527 }
528 
529 void RtfExport::WriteUserPropValue(const OUString& rValue)
530 {
531     Strm().WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_STATICVAL " ");
532     Strm().WriteOString(msfilter::rtfutil::OutString(rValue, m_eDefaultEncoding));
533     Strm().WriteChar('}');
534 }
535 
536 void RtfExport::WriteUserProps()
537 {
538     Strm().WriteChar('{').WriteCharPtr(
539         OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_USERPROPS);
540 
541     SwDocShell* pDocShell(m_rDoc.GetDocShell());
542     uno::Reference<document::XDocumentProperties> xDocProps;
543     if (pDocShell)
544     {
545         uno::Reference<document::XDocumentPropertiesSupplier> xDPS(pDocShell->GetModel(),
546                                                                    uno::UNO_QUERY);
547         xDocProps.set(xDPS->getDocumentProperties());
548     }
549     else
550     {
551         // Clipboard document, read metadata from the meta field manager.
552         sw::MetaFieldManager& rManager = m_rDoc.GetMetaFieldManager();
553         xDocProps.set(rManager.getDocumentProperties());
554     }
555 
556     if (xDocProps.is())
557     {
558         // Handle user-defined properties.
559         uno::Reference<beans::XPropertyContainer> xUserDefinedProperties
560             = xDocProps->getUserDefinedProperties();
561         if (xUserDefinedProperties.is())
562         {
563             uno::Reference<beans::XPropertySet> xPropertySet(xUserDefinedProperties,
564                                                              uno::UNO_QUERY);
565             const uno::Sequence<beans::Property> aProperties
566                 = xPropertySet->getPropertySetInfo()->getProperties();
567 
568             for (const beans::Property& rProperty : aProperties)
569             {
570                 if (rProperty.Name.startsWith("Company"))
571                     // We have explicit markup in RTF for this property.
572                     continue;
573 
574                 // Property name.
575                 Strm().WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_PROPNAME " ");
576                 Strm().WriteCharPtr(
577                     msfilter::rtfutil::OutString(rProperty.Name, m_eDefaultEncoding).getStr());
578                 Strm().WriteChar('}');
579 
580                 // Property value.
581                 OUString aValue;
582                 double fValue;
583                 bool bValue;
584                 util::DateTime aDate;
585                 uno::Any aAny = xPropertySet->getPropertyValue(rProperty.Name);
586                 if (aAny >>= bValue)
587                 {
588                     WriteUserPropType(11);
589                     WriteUserPropValue(OUString::number(static_cast<int>(bValue)));
590                 }
591                 else if (aAny >>= aValue)
592                 {
593                     WriteUserPropType(30);
594                     WriteUserPropValue(aValue);
595                 }
596                 else if (aAny >>= fValue)
597                 {
598                     aValue = OUString::number(fValue);
599                     if (aValue.indexOf('.') == -1)
600                     {
601                         // Integer.
602                         WriteUserPropType(3);
603                         WriteUserPropValue(aValue);
604                     }
605                     else
606                     {
607                         // Real number.
608                         WriteUserPropType(5);
609                         WriteUserPropValue(aValue);
610                     }
611                 }
612                 else if (aAny >>= aDate)
613                 {
614                     WriteUserPropType(64);
615                     // Format is 'YYYY. MM. DD.'.
616                     aValue += OUString::number(aDate.Year) + ". ";
617                     if (aDate.Month < 10)
618                         aValue += "0";
619                     aValue += OUString::number(aDate.Month) + ". ";
620                     if (aDate.Day < 10)
621                         aValue += "0";
622                     aValue += OUString::number(aDate.Day) + ".";
623                     WriteUserPropValue(aValue);
624                 }
625             }
626         }
627     }
628 
629     Strm().WriteChar('}');
630 }
631 
632 void RtfExport::WritePageDescTable()
633 {
634     // Write page descriptions (page styles)
635     std::size_t nSize = m_rDoc.GetPageDescCnt();
636     if (!nSize)
637         return;
638 
639     Strm().WriteCharPtr(SAL_NEWLINE_STRING);
640     m_bOutPageDescs = true;
641     Strm()
642         .WriteChar('{')
643         .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_IGNORE)
644         .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PGDSCTBL);
645     for (std::size_t n = 0; n < nSize; ++n)
646     {
647         const SwPageDesc& rPageDesc = m_rDoc.GetPageDesc(n);
648 
649         Strm()
650             .WriteCharPtr(SAL_NEWLINE_STRING)
651             .WriteChar('{')
652             .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PGDSC);
653         OutULong(n).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PGDSCUSE);
654         OutULong(static_cast<sal_uLong>(rPageDesc.ReadUseOn()));
655 
656         OutPageDescription(rPageDesc, false);
657 
658         // search for the next page description
659         std::size_t i = nSize;
660         while (i)
661             if (rPageDesc.GetFollow() == &m_rDoc.GetPageDesc(--i))
662                 break;
663         Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PGDSCNXT);
664         OutULong(i).WriteChar(' ');
665         Strm()
666             .WriteCharPtr(
667                 msfilter::rtfutil::OutString(rPageDesc.GetName(), m_eDefaultEncoding).getStr())
668             .WriteCharPtr(";}");
669     }
670     Strm().WriteChar('}').WriteCharPtr(SAL_NEWLINE_STRING);
671     m_bOutPageDescs = false;
672 
673     // reset table infos, otherwise the depth of the cells will be incorrect,
674     // in case the page style (header or footer) had tables
675     m_pTableInfo = std::make_shared<ww8::WW8TableInfo>();
676 }
677 
678 ErrCode RtfExport::ExportDocument_Impl()
679 {
680     // Make the header
681     Strm()
682         .WriteChar('{')
683         .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_RTF)
684         .WriteChar('1')
685         .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_ANSI);
686     Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_DEFF);
687     OutULong(m_aFontHelper.GetId(m_rDoc.GetAttrPool().GetDefaultItem(RES_CHRATR_FONT)));
688     // If this not exist, MS don't understand our ansi characters (0x80-0xff).
689     Strm().WriteCharPtr("\\adeflang1025");
690 
691     // Font table
692     WriteFonts();
693 
694     m_pStyles = std::make_unique<MSWordStyles>(*this);
695     // Color and stylesheet table
696     WriteStyles();
697 
698     // List table
699     BuildNumbering();
700     WriteNumbering();
701 
702     WriteRevTab();
703 
704     WriteInfo();
705     WriteUserProps();
706     // Default TabSize
707     Strm()
708         .WriteOString(m_pAttrOutput->GetTabStop().makeStringAndClear())
709         .WriteCharPtr(SAL_NEWLINE_STRING);
710 
711     // Automatic hyphenation: it's a global setting in Word, it's a paragraph setting in Writer.
712     // Set it's value to "auto" and disable on paragraph level, if no hyphenation is used there.
713     Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_HYPHAUTO);
714     OutULong(1);
715 
716     // Zoom
717     SwViewShell* pViewShell(m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell());
718     if (pViewShell && pViewShell->GetViewOptions()->GetZoomType() == SvxZoomType::PERCENT)
719     {
720         Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_VIEWSCALE);
721         OutULong(pViewShell->GetViewOptions()->GetZoom());
722     }
723     // Record changes?
724     if (RedlineFlags::On & m_nOrigRedlineFlags)
725         Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_REVISIONS);
726     // Mirror margins?
727     if ((UseOnPage::Mirror & m_rDoc.GetPageDesc(0).ReadUseOn()) == UseOnPage::Mirror)
728         Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_MARGMIRROR);
729     // Init sections
730     m_pSections = new MSWordSections(*this);
731 
732     // Page description
733     WritePageDescTable();
734 
735     // Enable form protection by default if needed, as there is no switch to
736     // enable it on a per-section basis. OTOH don't always enable it as it
737     // breaks moving of drawings - so write it only in case there is really a
738     // protected section in the document.
739     for (auto const& pSectionFormat : m_rDoc.GetSections())
740     {
741         if (!pSectionFormat->IsInUndo() && pSectionFormat->GetProtect().IsContentProtected())
742         {
743             Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_FORMPROT);
744             break;
745         }
746     }
747 
748     // enable form field shading
749     Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_FORMSHADE);
750 
751     // size and empty margins of the page
752     if (m_rDoc.GetPageDescCnt())
753     {
754         // Seeking the first SwFormatPageDesc. If no set, the default is valid
755         const SwFormatPageDesc* pSttPgDsc = nullptr;
756         {
757             const SwNode& rSttNd
758                 = *m_rDoc.GetNodes()[m_rDoc.GetNodes().GetEndOfExtras().GetIndex() + 2];
759             const SfxItemSet* pSet = nullptr;
760 
761             if (rSttNd.IsContentNode())
762                 pSet = &rSttNd.GetContentNode()->GetSwAttrSet();
763             else if (rSttNd.IsTableNode())
764                 pSet = &rSttNd.GetTableNode()->GetTable().GetFrameFormat()->GetAttrSet();
765 
766             else if (rSttNd.IsSectionNode())
767                 pSet = &rSttNd.GetSectionNode()->GetSection().GetFormat()->GetAttrSet();
768 
769             if (pSet)
770             {
771                 std::size_t nPosInDoc;
772                 pSttPgDsc = &pSet->Get(RES_PAGEDESC);
773                 if (!pSttPgDsc->GetPageDesc())
774                     pSttPgDsc = nullptr;
775                 else if (m_rDoc.FindPageDesc(pSttPgDsc->GetPageDesc()->GetName(), &nPosInDoc))
776                 {
777                     Strm()
778                         .WriteChar('{')
779                         .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_IGNORE)
780                         .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PGDSCNO);
781                     OutULong(nPosInDoc).WriteChar('}');
782                 }
783             }
784         }
785         const SwPageDesc& rPageDesc = pSttPgDsc ? *pSttPgDsc->GetPageDesc() : m_rDoc.GetPageDesc(0);
786         const SwFrameFormat& rFormatPage = rPageDesc.GetMaster();
787 
788         {
789             if (rPageDesc.GetLandscape())
790                 Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LANDSCAPE);
791 
792             const SwFormatFrameSize& rSz = rFormatPage.GetFrameSize();
793             // Clipboard document is always created without a printer, then
794             // the size will be always LONG_MAX! Solution then is to use A4
795             if (LONG_MAX == rSz.GetHeight() || LONG_MAX == rSz.GetWidth())
796             {
797                 Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PAPERH);
798                 Size a4 = SvxPaperInfo::GetPaperSize(PAPER_A4);
799                 OutULong(a4.Height()).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PAPERW);
800                 OutULong(a4.Width());
801             }
802             else
803             {
804                 Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PAPERH);
805                 OutULong(rSz.GetHeight()).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PAPERW);
806                 OutULong(rSz.GetWidth());
807             }
808         }
809 
810         {
811             const SvxLRSpaceItem& rLR = rFormatPage.GetLRSpace();
812             Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_MARGL);
813             OutLong(rLR.GetLeft()).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_MARGR);
814             OutLong(rLR.GetRight());
815         }
816 
817         {
818             const SvxULSpaceItem& rUL = rFormatPage.GetULSpace();
819             Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_MARGT);
820             OutLong(rUL.GetUpper()).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_MARGB);
821             OutLong(rUL.GetLower());
822         }
823 
824         Strm()
825             .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_SECTD)
826             .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_SBKNONE);
827         m_pAttrOutput->SectFootnoteEndnotePr();
828         // All sections are unlocked by default
829         Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_SECTUNLOCKED);
830         OutLong(1);
831         OutPageDescription(rPageDesc, true); // Changed bCheckForFirstPage to true so headers
832         // following title page are correctly added - i13107
833         if (pSttPgDsc)
834         {
835             m_pCurrentPageDesc = &rPageDesc;
836         }
837     }
838 
839     // line numbering
840     const SwLineNumberInfo& rLnNumInfo = m_rDoc.GetLineNumberInfo();
841     if (rLnNumInfo.IsPaintLineNumbers())
842     {
843         sal_uLong nLnNumRestartNo = 0;
844         if (const WW8_SepInfo* pSectionInfo = m_pSections->CurrentSectionInfo())
845             nLnNumRestartNo = pSectionInfo->nLnNumRestartNo;
846 
847         AttrOutput().SectionLineNumbering(nLnNumRestartNo, rLnNumInfo);
848     }
849 
850     {
851         // write the footnotes and endnotes-out Info
852         const SwFootnoteInfo& rFootnoteInfo = m_rDoc.GetFootnoteInfo();
853 
854         const char* pOut = FTNPOS_CHAPTER == rFootnoteInfo.m_ePos ? OOO_STRING_SVTOOLS_RTF_ENDDOC
855                                                                   : OOO_STRING_SVTOOLS_RTF_FTNBJ;
856         Strm().WriteCharPtr(pOut).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_FTNSTART);
857         OutLong(rFootnoteInfo.m_nFootnoteOffset + 1);
858 
859         switch (rFootnoteInfo.m_eNum)
860         {
861             case FTNNUM_PAGE:
862                 pOut = OOO_STRING_SVTOOLS_RTF_FTNRSTPG;
863                 break;
864             case FTNNUM_DOC:
865                 pOut = OOO_STRING_SVTOOLS_RTF_FTNRSTCONT;
866                 break;
867             default:
868                 pOut = OOO_STRING_SVTOOLS_RTF_FTNRESTART;
869                 break;
870         }
871         Strm().WriteCharPtr(pOut);
872 
873         switch (rFootnoteInfo.m_aFormat.GetNumberingType())
874         {
875             case SVX_NUM_CHARS_LOWER_LETTER:
876             case SVX_NUM_CHARS_LOWER_LETTER_N:
877                 pOut = OOO_STRING_SVTOOLS_RTF_FTNNALC;
878                 break;
879             case SVX_NUM_CHARS_UPPER_LETTER:
880             case SVX_NUM_CHARS_UPPER_LETTER_N:
881                 pOut = OOO_STRING_SVTOOLS_RTF_FTNNAUC;
882                 break;
883             case SVX_NUM_ROMAN_LOWER:
884                 pOut = OOO_STRING_SVTOOLS_RTF_FTNNRLC;
885                 break;
886             case SVX_NUM_ROMAN_UPPER:
887                 pOut = OOO_STRING_SVTOOLS_RTF_FTNNRUC;
888                 break;
889             case SVX_NUM_SYMBOL_CHICAGO:
890                 pOut = OOO_STRING_SVTOOLS_RTF_FTNNCHI;
891                 break;
892             default:
893                 pOut = OOO_STRING_SVTOOLS_RTF_FTNNAR;
894                 break;
895         }
896         Strm().WriteCharPtr(pOut);
897 
898         const SwEndNoteInfo& rEndNoteInfo = m_rDoc.GetEndNoteInfo();
899 
900         Strm()
901             .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_AENDDOC)
902             .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_AFTNRSTCONT)
903             .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_AFTNSTART);
904         OutLong(rEndNoteInfo.m_nFootnoteOffset + 1);
905 
906         switch (rEndNoteInfo.m_aFormat.GetNumberingType())
907         {
908             case SVX_NUM_CHARS_LOWER_LETTER:
909             case SVX_NUM_CHARS_LOWER_LETTER_N:
910                 pOut = OOO_STRING_SVTOOLS_RTF_AFTNNALC;
911                 break;
912             case SVX_NUM_CHARS_UPPER_LETTER:
913             case SVX_NUM_CHARS_UPPER_LETTER_N:
914                 pOut = OOO_STRING_SVTOOLS_RTF_AFTNNAUC;
915                 break;
916             case SVX_NUM_ROMAN_LOWER:
917                 pOut = OOO_STRING_SVTOOLS_RTF_AFTNNRLC;
918                 break;
919             case SVX_NUM_ROMAN_UPPER:
920                 pOut = OOO_STRING_SVTOOLS_RTF_AFTNNRUC;
921                 break;
922             case SVX_NUM_SYMBOL_CHICAGO:
923                 pOut = OOO_STRING_SVTOOLS_RTF_AFTNNCHI;
924                 break;
925             default:
926                 pOut = OOO_STRING_SVTOOLS_RTF_AFTNNAR;
927                 break;
928         }
929         Strm().WriteCharPtr(pOut);
930     }
931 
932     if (!m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::PARA_SPACE_MAX))
933         // RTF default is true, so write compat flag if this should be false.
934         Strm().WriteCharPtr(LO_STRING_SVTOOLS_RTF_HTMAUTSP);
935 
936     Strm().WriteCharPtr(SAL_NEWLINE_STRING);
937 
938     WriteFootnoteSettings();
939 
940     WriteMainText();
941 
942     Strm().WriteChar('}');
943 
944     return ERRCODE_NONE;
945 }
946 
947 void RtfExport::PrepareNewPageDesc(const SfxItemSet* pSet, const SwNode& rNd,
948                                    const SwFormatPageDesc* pNewPgDescFormat,
949                                    const SwPageDesc* pNewPgDesc)
950 {
951     const SwSectionFormat* pFormat = GetSectionFormat(rNd);
952     const sal_uLong nLnNm = GetSectionLineNo(pSet, rNd);
953 
954     OSL_ENSURE(pNewPgDescFormat || pNewPgDesc, "Neither page desc format nor page desc provided.");
955 
956     if (pNewPgDescFormat)
957         m_pSections->AppendSection(*pNewPgDescFormat, rNd, pFormat, nLnNm);
958     else if (pNewPgDesc)
959         m_pSections->AppendSection(pNewPgDesc, rNd, pFormat, nLnNm);
960 
961     // Don't insert a page break, when we're changing page style just because the next page has to be a different one.
962     if (!m_pAttrOutput->GetPrevPageDesc()
963         || m_pAttrOutput->GetPrevPageDesc()->GetFollow() != pNewPgDesc)
964         AttrOutput().SectionBreak(msword::PageBreak, false, m_pSections->CurrentSectionInfo());
965 }
966 
967 bool RtfExport::DisallowInheritingOutlineNumbering(const SwFormat& rFormat)
968 {
969     bool bRet(false);
970 
971     if (SfxItemState::SET != rFormat.GetItemState(RES_PARATR_NUMRULE, false))
972     {
973         if (const SwFormat* pParent = rFormat.DerivedFrom())
974         {
975             if (static_cast<const SwTextFormatColl*>(pParent)
976                     ->IsAssignedToListLevelOfOutlineStyle())
977             {
978                 // Level 9 disables the outline
979                 Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LEVEL).WriteInt32(9);
980 
981                 bRet = true;
982             }
983         }
984     }
985 
986     return bRet;
987 }
988 
989 void RtfExport::OutputEndNode(const SwEndNode& rEndNode)
990 {
991     if (TXT_MAINTEXT == m_nTextTyp && rEndNode.StartOfSectionNode()->IsTableNode())
992         // End node of a table: see if a section break should be written after the table.
993         AttrOutput().SectionBreaks(rEndNode);
994 }
995 
996 void RtfExport::OutputGrfNode(const SwGrfNode& /*rGrfNode*/)
997 {
998     /* noop, see RtfAttributeOutput::FlyFrameGraphic */
999 }
1000 
1001 void RtfExport::OutputOLENode(const SwOLENode& /*rOLENode*/)
1002 {
1003     /* noop, see RtfAttributeOutput::FlyFrameOLE */
1004 }
1005 
1006 void RtfExport::OutputLinkedOLE(const OUString& /*rLinked*/) {}
1007 
1008 void RtfExport::OutputTextNode(SwTextNode& rNode)
1009 {
1010     m_nCurrentNodeIndex = rNode.GetIndex();
1011     if (!m_bOutOutlineOnly || rNode.IsOutline())
1012         MSWordExportBase::OutputTextNode(rNode);
1013     m_nCurrentNodeIndex = 0;
1014 }
1015 
1016 void RtfExport::AppendSection(const SwPageDesc* pPageDesc, const SwSectionFormat* pFormat,
1017                               sal_uLong nLnNum)
1018 {
1019     m_pSections->AppendSection(pPageDesc, pFormat, nLnNum);
1020     AttrOutput().SectionBreak(msword::PageBreak, false, m_pSections->CurrentSectionInfo());
1021 }
1022 
1023 RtfExport::RtfExport(RtfExportFilter* pFilter, SwDoc& rDocument,
1024                      std::shared_ptr<SwUnoCursor>& pCurrentPam, SwPaM& rOriginalPam,
1025                      Writer* pWriter, bool bOutOutlineOnly)
1026     : MSWordExportBase(rDocument, pCurrentPam, &rOriginalPam)
1027     , m_pFilter(pFilter)
1028     , m_pWriter(pWriter)
1029     , m_pSections(nullptr)
1030     , m_bOutOutlineOnly(bOutOutlineOnly)
1031     , m_eDefaultEncoding(
1032           rtl_getTextEncodingFromWindowsCharset(sw::ms::rtl_TextEncodingToWinCharset(DEF_ENCODING)))
1033     , m_eCurrentEncoding(m_eDefaultEncoding)
1034     , m_bRTFFlySyntax(false)
1035     , m_nCurrentNodeIndex(0)
1036 {
1037     m_bExportModeRTF = true;
1038     // the attribute output for the document
1039     m_pAttrOutput = std::make_unique<RtfAttributeOutput>(*this);
1040     // that just causes problems for RTF
1041     m_bSubstituteBullets = false;
1042     // needed to have a complete font table
1043     m_aFontHelper.bLoadAllFonts = true;
1044     // the related SdrExport
1045     m_pSdrExport = std::make_unique<RtfSdrExport>(*this);
1046 
1047     if (!m_pWriter)
1048         m_pWriter = &m_pFilter->GetWriter();
1049 }
1050 
1051 RtfExport::~RtfExport() = default;
1052 
1053 SvStream& RtfExport::Strm()
1054 {
1055     if (m_pStream)
1056         return *m_pStream;
1057 
1058     return m_pWriter->Strm();
1059 }
1060 
1061 void RtfExport::setStream() { m_pStream = std::make_unique<SvMemoryStream>(); }
1062 
1063 OString RtfExport::getStream()
1064 {
1065     OString aRet;
1066 
1067     if (m_pStream)
1068         aRet = OString(static_cast<const char*>(m_pStream->GetData()), m_pStream->Tell());
1069 
1070     return aRet;
1071 }
1072 
1073 void RtfExport::resetStream() { m_pStream.reset(); }
1074 
1075 SvStream& RtfExport::OutULong(sal_uLong nVal) { return Writer::OutULong(Strm(), nVal); }
1076 
1077 SvStream& RtfExport::OutLong(long nVal) { return Writer::OutLong(Strm(), nVal); }
1078 
1079 void RtfExport::OutUnicode(const char* pToken, const OUString& rContent, bool bUpr)
1080 {
1081     if (rContent.isEmpty())
1082         return;
1083 
1084     if (!bUpr)
1085     {
1086         Strm().WriteChar('{').WriteCharPtr(pToken).WriteChar(' ');
1087         Strm().WriteCharPtr(msfilter::rtfutil::OutString(rContent, m_eCurrentEncoding).getStr());
1088         Strm().WriteChar('}');
1089     }
1090     else
1091         Strm().WriteCharPtr(
1092             msfilter::rtfutil::OutStringUpr(pToken, rContent, m_eCurrentEncoding).getStr());
1093 }
1094 
1095 void RtfExport::OutDateTime(const char* pStr, const util::DateTime& rDT)
1096 {
1097     Strm().WriteChar('{').WriteCharPtr(pStr).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_YR);
1098     OutULong(rDT.Year).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_MO);
1099     OutULong(rDT.Month).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_DY);
1100     OutULong(rDT.Day).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_HR);
1101     OutULong(rDT.Hours).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_MIN);
1102     OutULong(rDT.Minutes).WriteChar('}');
1103 }
1104 
1105 sal_uInt16 RtfExport::GetColor(const Color& rColor) const
1106 {
1107     for (const auto& rEntry : m_aColTable)
1108         if (rEntry.second == rColor)
1109         {
1110             SAL_INFO("sw.rtf", __func__ << " returning " << rEntry.first << " (" << rColor.GetRed()
1111                                         << "," << rColor.GetGreen() << "," << rColor.GetBlue()
1112                                         << ")");
1113             return rEntry.first;
1114         }
1115     OSL_FAIL("No such Color in m_aColTable!");
1116     return 0;
1117 }
1118 
1119 void RtfExport::InsColor(const Color& rCol)
1120 {
1121     sal_uInt16 n;
1122     bool bAutoColorInTable = false;
1123     for (const auto& rEntry : m_aColTable)
1124     {
1125         if (rEntry.second == rCol)
1126             return; // Already in the table
1127         if (rEntry.second == COL_AUTO)
1128             bAutoColorInTable = true;
1129     }
1130     if (rCol == COL_AUTO)
1131         // COL_AUTO gets value 0
1132         n = 0;
1133     else
1134     {
1135         // other colors get values >0
1136         n = m_aColTable.size();
1137         if (!bAutoColorInTable)
1138             // reserve value "0" for COL_AUTO (if COL_AUTO wasn't inserted until now)
1139             n++;
1140     }
1141     m_aColTable.insert(std::pair<sal_uInt16, Color>(n, rCol));
1142 }
1143 
1144 void RtfExport::InsColorLine(const SvxBoxItem& rBox)
1145 {
1146     const editeng::SvxBorderLine* pLine = nullptr;
1147 
1148     if (rBox.GetTop())
1149     {
1150         pLine = rBox.GetTop();
1151         InsColor(pLine->GetColor());
1152     }
1153     if (rBox.GetBottom() && pLine != rBox.GetBottom())
1154     {
1155         pLine = rBox.GetBottom();
1156         InsColor(pLine->GetColor());
1157     }
1158     if (rBox.GetLeft() && pLine != rBox.GetLeft())
1159     {
1160         pLine = rBox.GetLeft();
1161         InsColor(pLine->GetColor());
1162     }
1163     if (rBox.GetRight() && pLine != rBox.GetRight())
1164         InsColor(rBox.GetRight()->GetColor());
1165 }
1166 
1167 void RtfExport::OutColorTable()
1168 {
1169     // Build the table from rPool since the colors provided to
1170     // RtfAttributeOutput callbacks are too late.
1171     const SfxItemPool& rPool = m_rDoc.GetAttrPool();
1172 
1173     // MSO Word uses a default color table with 16 colors (which is used e.g. for highlighting)
1174     InsColor(COL_BLACK);
1175     InsColor(COL_LIGHTBLUE);
1176     InsColor(COL_LIGHTCYAN);
1177     InsColor(COL_LIGHTGREEN);
1178     InsColor(COL_LIGHTMAGENTA);
1179     InsColor(COL_LIGHTRED);
1180     InsColor(COL_YELLOW);
1181     InsColor(COL_WHITE);
1182     InsColor(COL_BLUE);
1183     InsColor(COL_CYAN);
1184     InsColor(COL_GREEN);
1185     InsColor(COL_MAGENTA);
1186     InsColor(COL_RED);
1187     InsColor(COL_BROWN);
1188     InsColor(COL_GRAY);
1189     InsColor(COL_LIGHTGRAY);
1190 
1191     // char color
1192     {
1193         auto pCol = GetDfltAttr(RES_CHRATR_COLOR);
1194         InsColor(pCol->GetValue());
1195         pCol = rPool.GetPoolDefaultItem(RES_CHRATR_COLOR);
1196         if (pCol)
1197             InsColor(pCol->GetValue());
1198         for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_CHRATR_COLOR))
1199         {
1200             pCol = dynamic_cast<const SvxColorItem*>(pItem);
1201             if (pCol)
1202                 InsColor(pCol->GetValue());
1203         }
1204 
1205         auto pUnder = GetDfltAttr(RES_CHRATR_UNDERLINE);
1206         InsColor(pUnder->GetColor());
1207         for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_CHRATR_UNDERLINE))
1208         {
1209             pUnder = dynamic_cast<const SvxUnderlineItem*>(pItem);
1210             if (pUnder)
1211                 InsColor(pUnder->GetColor());
1212         }
1213 
1214         auto pOver = GetDfltAttr(RES_CHRATR_OVERLINE);
1215         InsColor(pOver->GetColor());
1216         for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_CHRATR_OVERLINE))
1217         {
1218             pOver = dynamic_cast<const SvxOverlineItem*>(pItem);
1219             if (pOver)
1220                 InsColor(pOver->GetColor());
1221         }
1222     }
1223 
1224     // background color
1225     static const sal_uInt16 aBrushIds[] = { RES_BACKGROUND, RES_CHRATR_BACKGROUND, 0 };
1226 
1227     for (const sal_uInt16* pIds = aBrushIds; *pIds; ++pIds)
1228     {
1229         auto pBackground = static_cast<const SvxBrushItem*>(GetDfltAttr(*pIds));
1230         InsColor(pBackground->GetColor());
1231         pBackground = static_cast<const SvxBrushItem*>(rPool.GetPoolDefaultItem(*pIds));
1232         if (pBackground)
1233         {
1234             InsColor(pBackground->GetColor());
1235         }
1236         for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(*pIds))
1237         {
1238             pBackground = static_cast<const SvxBrushItem*>(pItem);
1239             if (pBackground)
1240             {
1241                 InsColor(pBackground->GetColor());
1242             }
1243         }
1244     }
1245 
1246     // shadow color
1247     {
1248         auto pShadow = GetDfltAttr(RES_SHADOW);
1249         InsColor(pShadow->GetColor());
1250         pShadow = rPool.GetPoolDefaultItem(RES_SHADOW);
1251         if (nullptr != pShadow)
1252         {
1253             InsColor(pShadow->GetColor());
1254         }
1255         for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_SHADOW))
1256         {
1257             pShadow = dynamic_cast<const SvxShadowItem*>(pItem);
1258             if (pShadow)
1259             {
1260                 InsColor(pShadow->GetColor());
1261             }
1262         }
1263     }
1264 
1265     // frame border color
1266     {
1267         const SvxBoxItem* pBox = rPool.GetPoolDefaultItem(RES_BOX);
1268         if (nullptr != pBox)
1269             InsColorLine(*pBox);
1270         for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_BOX))
1271         {
1272             pBox = dynamic_cast<const SvxBoxItem*>(pItem);
1273             if (pBox)
1274                 InsColorLine(*pBox);
1275         }
1276     }
1277 
1278     {
1279         const SvxBoxItem* pCharBox = rPool.GetPoolDefaultItem(RES_CHRATR_BOX);
1280         if (pCharBox)
1281             InsColorLine(*pCharBox);
1282         for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_CHRATR_BOX))
1283         {
1284             pCharBox = dynamic_cast<const SvxBoxItem*>(pItem);
1285             if (pCharBox)
1286                 InsColorLine(*pCharBox);
1287         }
1288     }
1289 
1290     // TextFrame or paragraph background solid fill.
1291     for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(XATTR_FILLCOLOR))
1292     {
1293         if (auto pColorItem = dynamic_cast<const XFillColorItem*>(pItem))
1294             InsColor(pColorItem->GetColorValue());
1295     }
1296 
1297     for (std::size_t n = 0; n < m_aColTable.size(); ++n)
1298     {
1299         const Color& rCol = m_aColTable[n];
1300         if (n || COL_AUTO != rCol)
1301         {
1302             Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_RED);
1303             OutULong(rCol.GetRed()).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_GREEN);
1304             OutULong(rCol.GetGreen()).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_BLUE);
1305             OutULong(rCol.GetBlue());
1306         }
1307         Strm().WriteChar(';');
1308     }
1309 }
1310 
1311 void RtfExport::InsStyle(sal_uInt16 nId, const OString& rStyle)
1312 {
1313     m_aStyTable.insert(std::pair<sal_uInt16, OString>(nId, rStyle));
1314 }
1315 
1316 OString* RtfExport::GetStyle(sal_uInt16 nId)
1317 {
1318     auto it = m_aStyTable.find(nId);
1319     if (it != m_aStyTable.end())
1320         return &it->second;
1321     return nullptr;
1322 }
1323 
1324 sal_uInt16 RtfExport::GetRedline(const OUString& rAuthor)
1325 {
1326     auto it = m_aRedlineTable.find(rAuthor);
1327     if (it != m_aRedlineTable.end())
1328         return it->second;
1329 
1330     const sal_uInt16 nId = m_aRedlineTable.size();
1331     m_aRedlineTable.insert(std::pair<OUString, sal_uInt16>(rAuthor, nId));
1332     return nId;
1333 }
1334 
1335 const OUString* RtfExport::GetRedline(sal_uInt16 nId)
1336 {
1337     for (const auto& rEntry : m_aRedlineTable)
1338         if (rEntry.second == nId)
1339             return &rEntry.first;
1340     return nullptr;
1341 }
1342 
1343 void RtfExport::OutPageDescription(const SwPageDesc& rPgDsc, bool bCheckForFirstPage)
1344 {
1345     SAL_INFO("sw.rtf", __func__ << " start");
1346     const SwPageDesc* pSave = m_pCurrentPageDesc;
1347 
1348     m_pCurrentPageDesc = &rPgDsc;
1349     if (bCheckForFirstPage && m_pCurrentPageDesc->GetFollow()
1350         && m_pCurrentPageDesc->GetFollow() != m_pCurrentPageDesc)
1351         m_pCurrentPageDesc = m_pCurrentPageDesc->GetFollow();
1352 
1353     if (m_pCurrentPageDesc->GetLandscape())
1354         Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LNDSCPSXN);
1355 
1356     const SwFormat* pFormat = &m_pCurrentPageDesc->GetMaster(); //GetLeft();
1357     m_bOutPageDescs = true;
1358     if (m_pCurrentPageDesc != &rPgDsc)
1359         m_pFirstPageItemSet = &rPgDsc.GetMaster().GetAttrSet();
1360     OutputFormat(*pFormat, true, false);
1361     m_pFirstPageItemSet = nullptr;
1362     m_bOutPageDescs = false;
1363 
1364     // normal header / footer (without a style)
1365     const SfxPoolItem* pItem;
1366     if (m_pCurrentPageDesc->GetLeft().GetAttrSet().GetItemState(RES_HEADER, false, &pItem)
1367         == SfxItemState::SET)
1368         WriteHeaderFooter(*pItem, true);
1369     if (m_pCurrentPageDesc->GetLeft().GetAttrSet().GetItemState(RES_FOOTER, false, &pItem)
1370         == SfxItemState::SET)
1371         WriteHeaderFooter(*pItem, false);
1372 
1373     // title page
1374     if (m_pCurrentPageDesc != &rPgDsc)
1375     {
1376         Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_TITLEPG);
1377         m_pCurrentPageDesc = &rPgDsc;
1378         if (m_pCurrentPageDesc->GetMaster().GetAttrSet().GetItemState(RES_HEADER, false, &pItem)
1379             == SfxItemState::SET)
1380             WriteHeaderFooter(*pItem, true);
1381         if (m_pCurrentPageDesc->GetMaster().GetAttrSet().GetItemState(RES_FOOTER, false, &pItem)
1382             == SfxItemState::SET)
1383             WriteHeaderFooter(*pItem, false);
1384     }
1385 
1386     // numbering type
1387     AttrOutput().SectionPageNumbering(m_pCurrentPageDesc->GetNumType().GetNumberingType(),
1388                                       std::nullopt);
1389 
1390     m_pCurrentPageDesc = pSave;
1391     SAL_INFO("sw.rtf", __func__ << " end");
1392 }
1393 
1394 void RtfExport::WriteHeaderFooter(const SfxPoolItem& rItem, bool bHeader)
1395 {
1396     if (bHeader)
1397     {
1398         const auto& rHeader = static_cast<const SwFormatHeader&>(rItem);
1399         if (!rHeader.IsActive())
1400             return;
1401     }
1402     else
1403     {
1404         const auto& rFooter = static_cast<const SwFormatFooter&>(rItem);
1405         if (!rFooter.IsActive())
1406             return;
1407     }
1408 
1409     SAL_INFO("sw.rtf", __func__ << " start");
1410 
1411     const char* pStr = (bHeader ? OOO_STRING_SVTOOLS_RTF_HEADER : OOO_STRING_SVTOOLS_RTF_FOOTER);
1412     /* is this a title page? */
1413     if (m_pCurrentPageDesc->GetFollow() && m_pCurrentPageDesc->GetFollow() != m_pCurrentPageDesc)
1414     {
1415         Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_TITLEPG);
1416         pStr = (bHeader ? OOO_STRING_SVTOOLS_RTF_HEADERF : OOO_STRING_SVTOOLS_RTF_FOOTERF);
1417     }
1418     Strm().WriteChar('{').WriteCharPtr(pStr);
1419     WriteHeaderFooterText(m_pCurrentPageDesc->GetMaster(), bHeader);
1420     Strm().WriteChar('}');
1421 
1422     SAL_INFO("sw.rtf", __func__ << " end");
1423 }
1424 
1425 void RtfExport::WriteHeaderFooter(const SwFrameFormat& rFormat, bool bHeader, const char* pStr,
1426                                   bool bTitlepg)
1427 {
1428     SAL_INFO("sw.rtf", __func__ << " start");
1429 
1430     m_pAttrOutput->WriteHeaderFooter_Impl(rFormat, bHeader, pStr, bTitlepg);
1431 
1432     SAL_INFO("sw.rtf", __func__ << " end");
1433 }
1434 
1435 namespace
1436 {
1437 /// Glue class to call RtfExport as an internal filter, needed by copy&paste support.
1438 class SwRTFWriter : public Writer
1439 {
1440 private:
1441     bool m_bOutOutlineOnly;
1442 
1443 public:
1444     SwRTFWriter(const OUString& rFilterName, const OUString& rBaseURL);
1445 
1446     ErrCode WriteStream() override;
1447 };
1448 }
1449 
1450 SwRTFWriter::SwRTFWriter(const OUString& rFilterName, const OUString& rBaseURL)
1451 {
1452     SetBaseURL(rBaseURL);
1453     // export outline nodes, only (send outline to clipboard/presentation)
1454     m_bOutOutlineOnly = rFilterName.startsWith("O");
1455 }
1456 
1457 ErrCode SwRTFWriter::WriteStream()
1458 {
1459     std::shared_ptr<SwUnoCursor> pCurPam(m_pDoc->CreateUnoCursor(*m_pCurrentPam->End(), false));
1460     pCurPam->SetMark();
1461     *pCurPam->GetPoint() = *m_pCurrentPam->Start();
1462     RtfExport aExport(nullptr, *m_pDoc, pCurPam, *m_pCurrentPam, this, m_bOutOutlineOnly);
1463     aExport.ExportDocument(true);
1464     return ERRCODE_NONE;
1465 }
1466 
1467 extern "C" SAL_DLLPUBLIC_EXPORT void ExportRTF(const OUString& rFltName, const OUString& rBaseURL,
1468                                                WriterRef& xRet)
1469 {
1470     xRet = new SwRTFWriter(rFltName, rBaseURL);
1471 }
1472 
1473 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1474