summaryrefslogtreecommitdiffstats
path: root/sw/source/filter/ww8/rtfexport.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/filter/ww8/rtfexport.cxx')
-rw-r--r--sw/source/filter/ww8/rtfexport.cxx1585
1 files changed, 1585 insertions, 0 deletions
diff --git a/sw/source/filter/ww8/rtfexport.cxx b/sw/source/filter/ww8/rtfexport.cxx
new file mode 100644
index 000000000..5e908c852
--- /dev/null
+++ b/sw/source/filter/ww8/rtfexport.cxx
@@ -0,0 +1,1585 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include "rtfexport.hxx"
+
+#include <string_view>
+
+#include "rtfexportfilter.hxx"
+#include "rtfsdrexport.hxx"
+#include "rtfattributeoutput.hxx"
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/text/XTextFieldsSupplier.hpp>
+#include <docsh.hxx>
+#include <viewsh.hxx>
+#include <viewopt.hxx>
+#include <fmtpdsc.hxx>
+#include <ftninfo.hxx>
+#include <fmthdft.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/paperinf.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/protitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <lineinfo.hxx>
+#include <redline.hxx>
+#include <swmodule.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <comphelper/string.hxx>
+#include <svtools/rtfkeywd.hxx>
+#include <filter/msfilter/rtfutil.hxx>
+#include <unotools/docinfohelper.hxx>
+#include <xmloff/odffields.hxx>
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+#include <rtl/tencinfo.h>
+#include <sal/log.hxx>
+#include <svx/xflclit.hxx>
+#include <fmtmeta.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <fmtfsize.hxx>
+#include <ndtxt.hxx>
+#include <numrule.hxx>
+#include <frmatr.hxx>
+#include <swtable.hxx>
+#include <IMark.hxx>
+
+using namespace ::com::sun::star;
+
+// the default text encoding for the export, if it doesn't fit unicode will
+// be used
+#define DEF_ENCODING RTL_TEXTENCODING_ASCII_US
+
+AttributeOutputBase& RtfExport::AttrOutput() const { return *m_pAttrOutput; }
+
+MSWordSections& RtfExport::Sections() const { return *m_pSections; }
+
+RtfSdrExport& RtfExport::SdrExporter() const { return *m_pSdrExport; }
+
+bool RtfExport::CollapseScriptsforWordOk(sal_uInt16 nScript, sal_uInt16 nWhich)
+{
+ // FIXME is this actually true for rtf? - this is copied from DOCX
+ if (nScript == i18n::ScriptType::ASIAN)
+ {
+ // for asian in ww8, there is only one fontsize
+ // and one fontstyle (posture/weight)
+ switch (nWhich)
+ {
+ case RES_CHRATR_FONTSIZE:
+ case RES_CHRATR_POSTURE:
+ case RES_CHRATR_WEIGHT:
+ return false;
+ default:
+ break;
+ }
+ }
+ else if (nScript != i18n::ScriptType::COMPLEX)
+ {
+ // for western in ww8, there is only one fontsize
+ // and one fontstyle (posture/weight)
+ switch (nWhich)
+ {
+ case RES_CHRATR_CJK_FONTSIZE:
+ case RES_CHRATR_CJK_POSTURE:
+ case RES_CHRATR_CJK_WEIGHT:
+ return false;
+ default:
+ break;
+ }
+ }
+ return true;
+}
+
+void RtfExport::AppendBookmarks(const SwTextNode& rNode, sal_Int32 nCurrentPos, sal_Int32 nLen,
+ const SwRedlineData* /*pRedlineData*/)
+{
+ std::vector<OUString> aStarts;
+ std::vector<OUString> aEnds;
+
+ IMarkVector aMarks;
+ if (GetBookmarks(rNode, nCurrentPos, nCurrentPos + nLen, aMarks))
+ {
+ for (const auto& pMark : aMarks)
+ {
+ const sal_Int32 nStart = pMark->GetMarkStart().nContent.GetIndex();
+ const sal_Int32 nEnd = pMark->GetMarkEnd().nContent.GetIndex();
+
+ if (nStart == nCurrentPos)
+ aStarts.push_back(pMark->GetName());
+
+ if (nEnd == nCurrentPos)
+ aEnds.push_back(pMark->GetName());
+ }
+ }
+
+ m_pAttrOutput->WriteBookmarks_Impl(aStarts, aEnds);
+}
+
+void RtfExport::AppendBookmark(const OUString& rName)
+{
+ std::vector<OUString> aStarts{ rName };
+ std::vector<OUString> aEnds{ rName };
+
+ m_pAttrOutput->WriteBookmarks_Impl(aStarts, aEnds);
+}
+
+void RtfExport::AppendAnnotationMarks(const SwWW8AttrIter& rAttrs, sal_Int32 nCurrentPos,
+ sal_Int32 nLen)
+{
+ std::vector<OUString> aStarts;
+ std::vector<OUString> aEnds;
+
+ IMarkVector aMarks;
+ if (GetAnnotationMarks(rAttrs, nCurrentPos, nCurrentPos + nLen, aMarks))
+ {
+ for (const auto& pMark : aMarks)
+ {
+ const sal_Int32 nStart = pMark->GetMarkStart().nContent.GetIndex();
+ const sal_Int32 nEnd = pMark->GetMarkEnd().nContent.GetIndex();
+
+ if (nStart == nCurrentPos)
+ aStarts.push_back(pMark->GetName());
+
+ if (nEnd == nCurrentPos)
+ aEnds.push_back(pMark->GetName());
+ }
+ }
+
+ m_pAttrOutput->WriteAnnotationMarks_Impl(aStarts, aEnds);
+}
+
+//For i120928,to export graphic of bullet for RTF filter
+void RtfExport::ExportGrfBullet(const SwTextNode& /*rNd*/)
+{
+ // Noop, would be too late, see WriteNumbering() instead.
+}
+
+void RtfExport::WriteChar(sal_Unicode /*c*/) { /* WriteChar() has nothing to do for rtf. */}
+
+static bool IsExportNumRule(const SwNumRule& rRule)
+{
+ sal_uInt8 nEnd = MAXLEVEL;
+ while (nEnd-- && !rRule.GetNumFormat(nEnd))
+ ;
+ ++nEnd;
+
+ sal_uInt8 nLvl;
+
+ for (nLvl = 0; nLvl < nEnd; ++nLvl)
+ {
+ const SwNumFormat* pNFormat = &rRule.Get(nLvl);
+ if (SVX_NUM_NUMBER_NONE != pNFormat->GetNumberingType() || !pNFormat->GetPrefix().isEmpty()
+ || (!pNFormat->GetSuffix().isEmpty() && pNFormat->GetSuffix() != "."))
+ break;
+ }
+
+ return nLvl != nEnd;
+}
+
+void RtfExport::BuildNumbering()
+{
+ const SwNumRuleTable& rListTable = m_rDoc.GetNumRuleTable();
+
+ SwNumRule* pOutlineRule = m_rDoc.GetOutlineNumRule();
+ if (IsExportNumRule(*pOutlineRule))
+ GetNumberingId(*pOutlineRule);
+
+ for (auto n = rListTable.size(); n;)
+ {
+ SwNumRule* pRule = rListTable[--n];
+ if (!m_rDoc.IsUsed(*pRule))
+ continue;
+
+ if (IsExportNumRule(*pRule))
+ GetNumberingId(*pRule);
+ }
+}
+
+void RtfExport::WriteNumbering()
+{
+ SAL_INFO("sw.rtf", __func__ << " start");
+
+ if (!m_pUsedNumTable)
+ return; // no numbering is used
+
+ Strm()
+ .WriteChar('{')
+ .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_IGNORE)
+ .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LISTTABLE);
+
+ CollectGrfsOfBullets();
+ if (!m_vecBulletPic.empty())
+ Strm()
+ .WriteChar('{')
+ .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_IGNORE)
+ .WriteCharPtr(LO_STRING_SVTOOLS_RTF_LISTPICTURE);
+ BulletDefinitions();
+ if (!m_vecBulletPic.empty())
+ Strm().WriteChar('}');
+
+ AbstractNumberingDefinitions();
+ Strm().WriteChar('}');
+
+ Strm().WriteChar('{').WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LISTOVERRIDETABLE);
+ NumberingDefinitions();
+ Strm().WriteChar('}');
+
+ SAL_INFO("sw.rtf", __func__ << " end");
+}
+
+void RtfExport::WriteRevTab()
+{
+ int nRevAuthors = m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size();
+
+ if (nRevAuthors < 1)
+ return;
+
+ // RTF always seems to use Unknown as the default first entry
+ GetRedline("Unknown");
+
+ for (SwRangeRedline* pRedl : m_rDoc.getIDocumentRedlineAccess().GetRedlineTable())
+ {
+ GetRedline(SW_MOD()->GetRedlineAuthor(pRedl->GetAuthor()));
+ }
+
+ // Now write the table
+ Strm()
+ .WriteChar('{')
+ .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_IGNORE)
+ .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_REVTBL)
+ .WriteChar(' ');
+ for (std::size_t i = 0; i < m_aRedlineTable.size(); ++i)
+ {
+ const OUString* pAuthor = GetRedline(i);
+ Strm().WriteChar('{');
+ if (pAuthor)
+ Strm().WriteCharPtr(
+ msfilter::rtfutil::OutString(*pAuthor, m_eDefaultEncoding).getStr());
+ Strm().WriteCharPtr(";}");
+ }
+ Strm().WriteChar('}').WriteCharPtr(SAL_NEWLINE_STRING);
+}
+
+void RtfExport::WriteHeadersFooters(sal_uInt8 nHeadFootFlags, const SwFrameFormat& rFormat,
+ const SwFrameFormat& rLeftHeaderFormat,
+ const SwFrameFormat& rLeftFooterFormat,
+ const SwFrameFormat& rFirstPageFormat, sal_uInt8 /*nBreakCode*/,
+ bool /*bEvenAndOddHeaders*/)
+{
+ // headers
+ if (nHeadFootFlags & nsHdFtFlags::WW8_HEADER_EVEN)
+ WriteHeaderFooter(rLeftHeaderFormat, true, OOO_STRING_SVTOOLS_RTF_HEADERL);
+
+ if (nHeadFootFlags & nsHdFtFlags::WW8_HEADER_ODD)
+ WriteHeaderFooter(rFormat, true, OOO_STRING_SVTOOLS_RTF_HEADER);
+
+ if (nHeadFootFlags & nsHdFtFlags::WW8_HEADER_FIRST)
+ WriteHeaderFooter(rFirstPageFormat, true, OOO_STRING_SVTOOLS_RTF_HEADERF, true);
+
+ // footers
+ if (nHeadFootFlags & nsHdFtFlags::WW8_FOOTER_EVEN)
+ WriteHeaderFooter(rLeftFooterFormat, false, OOO_STRING_SVTOOLS_RTF_FOOTERL);
+
+ if (nHeadFootFlags & nsHdFtFlags::WW8_FOOTER_ODD)
+ WriteHeaderFooter(rFormat, false, OOO_STRING_SVTOOLS_RTF_FOOTER);
+
+ if (nHeadFootFlags & nsHdFtFlags::WW8_FOOTER_FIRST)
+ WriteHeaderFooter(rFirstPageFormat, false, OOO_STRING_SVTOOLS_RTF_FOOTERF, true);
+}
+
+void RtfExport::OutputField(const SwField* pField, ww::eField eFieldType, const OUString& rFieldCmd,
+ FieldFlags nMode)
+{
+ m_pAttrOutput->WriteField_Impl(pField, eFieldType, rFieldCmd, nMode);
+}
+
+void RtfExport::WriteFormData(const ::sw::mark::IFieldmark& rFieldmark)
+{
+ sal_Int32 nType;
+ if (rFieldmark.GetFieldname() == ODF_FORMDROPDOWN)
+ {
+ nType = 2;
+ }
+ /* TODO
+ else if (rFieldmark.GetFieldname() == ODF_FORMCHECKBOX)
+ {
+ nType = 1;
+ }
+ else if (rFieldmark.GetFieldname() == ODF_FORMTEXT)
+ {
+ nType = 0;
+ }
+*/
+ else
+ {
+ SAL_INFO("sw.rtf", "unknown field type");
+ return;
+ }
+ m_pAttrOutput->RunText().append(
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FORMFIELD
+ "{" OOO_STRING_SVTOOLS_RTF_FFTYPE);
+ m_pAttrOutput->RunText().append(nType);
+ if (rFieldmark.GetFieldname() == ODF_FORMDROPDOWN)
+ {
+ m_pAttrOutput->RunText().append(OOO_STRING_SVTOOLS_RTF_FFHASLISTBOX "1");
+ uno::Sequence<OUString> entries;
+ if (auto const it = rFieldmark.GetParameters()->find(ODF_FORMDROPDOWN_LISTENTRY);
+ it != rFieldmark.GetParameters()->end())
+ {
+ it->second >>= entries;
+ }
+ if (auto const it = rFieldmark.GetParameters()->find(ODF_FORMDROPDOWN_RESULT);
+ it != rFieldmark.GetParameters()->end())
+ {
+ sal_Int32 result(-1);
+ it->second >>= result;
+ if (0 <= result && result < entries.getLength())
+ {
+ m_pAttrOutput->RunText().append(OOO_STRING_SVTOOLS_RTF_FFRES);
+ m_pAttrOutput->RunText().append(result);
+ }
+ }
+ for (OUString const& rEntry : entries)
+ {
+ m_pAttrOutput->RunText().append(
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFL " ");
+ m_pAttrOutput->RunText().append(
+ msfilter::rtfutil::OutString(rEntry, m_eDefaultEncoding));
+ m_pAttrOutput->RunText().append("}");
+ }
+ }
+ m_pAttrOutput->RunText().append("}}"); // close FORMFIELD destination
+}
+
+void RtfExport::WriteHyperlinkData(const ::sw::mark::IFieldmark& /*rFieldmark*/)
+{
+ SAL_INFO("sw.rtf", "TODO: " << __func__);
+}
+
+void RtfExport::DoComboBox(const OUString& /*rName*/, const OUString& /*rHelp*/,
+ const OUString& /*rToolTip*/, const OUString& /*rSelected*/,
+ const uno::Sequence<OUString>& /*rListItems*/)
+{
+ // this is handled in RtfAttributeOutput::OutputFlyFrame_Impl
+}
+
+void RtfExport::DoFormText(const SwInputField* pField)
+{
+ OUString sResult = pField->ExpandField(true, nullptr);
+ const OUString& rHelp = pField->GetHelp();
+ OUString sName = pField->GetPar2();
+ const OUString& rStatus = pField->GetToolTip();
+ m_pAttrOutput->RunText().append("{" OOO_STRING_SVTOOLS_RTF_FIELD
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FLDINST
+ "{ FORMTEXT }");
+ m_pAttrOutput->RunText().append(
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FORMFIELD
+ " {" OOO_STRING_SVTOOLS_RTF_FFTYPE "0");
+ if (!rHelp.isEmpty())
+ m_pAttrOutput->RunText().append(OOO_STRING_SVTOOLS_RTF_FFOWNHELP);
+ if (!rStatus.isEmpty())
+ m_pAttrOutput->RunText().append(OOO_STRING_SVTOOLS_RTF_FFOWNSTAT);
+ m_pAttrOutput->RunText().append(OOO_STRING_SVTOOLS_RTF_FFTYPETXT "0");
+
+ if (!sName.isEmpty())
+ m_pAttrOutput->RunText().append(
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFNAME " "
+ + msfilter::rtfutil::OutString(sName, m_eDefaultEncoding) + "}");
+ if (!rHelp.isEmpty())
+ m_pAttrOutput->RunText().append(
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFHELPTEXT " "
+ + msfilter::rtfutil::OutString(rHelp, m_eDefaultEncoding) + "}");
+ m_pAttrOutput->RunText().append(
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFDEFTEXT " "
+ + msfilter::rtfutil::OutString(sResult, m_eDefaultEncoding) + "}");
+ if (!rStatus.isEmpty())
+ m_pAttrOutput->RunText().append(
+ "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFSTATTEXT " "
+ + msfilter::rtfutil::OutString(rStatus, m_eDefaultEncoding) + "}");
+ m_pAttrOutput->RunText().append("}}}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " ");
+ m_pAttrOutput->RunText().append(msfilter::rtfutil::OutString(sResult, m_eDefaultEncoding)
+ + "}}");
+}
+
+sal_uInt64 RtfExport::ReplaceCr(sal_uInt8 /*nChar*/)
+{
+ // Completely unused for Rtf export... only here for code sharing
+ // purpose with binary export
+
+ return 0;
+}
+
+void RtfExport::WriteFonts()
+{
+ Strm()
+ .WriteCharPtr(SAL_NEWLINE_STRING)
+ .WriteChar('{')
+ .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_FONTTBL);
+ m_aFontHelper.WriteFontTable(*m_pAttrOutput);
+ Strm().WriteChar('}');
+}
+
+void RtfExport::WriteStyles()
+{
+ SAL_INFO("sw.rtf", __func__ << " start");
+ m_pStyles->OutputStylesTable();
+ SAL_INFO("sw.rtf", __func__ << " end");
+}
+
+void RtfExport::WriteFootnoteSettings()
+{
+ const SwPageFootnoteInfo& rFootnoteInfo = m_rDoc.GetPageDesc(0).GetFootnoteInfo();
+ // Request a separator only in case the width is larger than zero.
+ bool bSeparator = double(rFootnoteInfo.GetWidth()) > 0;
+
+ Strm()
+ .WriteChar('{')
+ .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_IGNORE)
+ .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_FTNSEP);
+ if (bSeparator)
+ Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_CHFTNSEP);
+ Strm().WriteChar('}');
+}
+
+void RtfExport::WriteMainText()
+{
+ SAL_INFO("sw.rtf", __func__ << " start");
+
+ if (std::unique_ptr<SvxBrushItem> oBrush = getBackground(); oBrush)
+ {
+ Strm().WriteCharPtr(LO_STRING_SVTOOLS_RTF_VIEWBKSP).WriteChar('1');
+ Strm().WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_BACKGROUND);
+ Strm().WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_SHP);
+ Strm().WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_SHPINST);
+
+ std::vector<std::pair<OString, OString>> aProperties{
+ { "shapeType", "1" },
+ { "fillColor", OString::number(wwUtility::RGBToBGR(oBrush->GetColor())) }
+ };
+ for (const std::pair<OString, OString>& rPair : aProperties)
+ {
+ Strm().WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_SP "{");
+ Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_SN " ");
+ Strm().WriteOString(rPair.first);
+ Strm().WriteCharPtr("}{" OOO_STRING_SVTOOLS_RTF_SV " ");
+ Strm().WriteOString(rPair.second);
+ Strm().WriteCharPtr("}}");
+ }
+ Strm().WriteChar('}'); // shpinst
+ Strm().WriteChar('}'); // shp
+ Strm().WriteChar('}'); // background
+ }
+
+ SwTableNode* pTableNode = m_pCurPam->GetNode().FindTableNode();
+ if (m_pWriter && m_pWriter->m_bWriteOnlyFirstTable && pTableNode != nullptr)
+ {
+ m_pCurPam->GetPoint()->nNode = *pTableNode;
+ m_pCurPam->GetMark()->nNode = *(pTableNode->EndOfSectionNode());
+ }
+ else
+ {
+ m_pCurPam->GetPoint()->nNode
+ = m_rDoc.GetNodes().GetEndOfContent().StartOfSectionNode()->GetIndex();
+ }
+
+ WriteText();
+
+ SAL_INFO("sw.rtf", __func__ << " end");
+}
+
+void RtfExport::WriteInfo()
+{
+ OString aGenerator
+ = OUStringToOString(utl::DocInfoHelper::GetGeneratorString(), RTL_TEXTENCODING_UTF8);
+ Strm()
+ .WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_IGNORE LO_STRING_SVTOOLS_RTF_GENERATOR " ")
+ .WriteOString(aGenerator)
+ .WriteChar('}');
+ Strm().WriteChar('{').WriteCharPtr(OOO_STRING_SVTOOLS_RTF_INFO);
+
+ SwDocShell* pDocShell(m_rDoc.GetDocShell());
+ uno::Reference<document::XDocumentProperties> xDocProps;
+ if (pDocShell)
+ {
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(pDocShell->GetModel(),
+ uno::UNO_QUERY);
+ xDocProps.set(xDPS->getDocumentProperties());
+ }
+
+ if (xDocProps.is())
+ {
+ // Handle user-defined properties.
+ uno::Reference<beans::XPropertyContainer> xUserDefinedProperties
+ = xDocProps->getUserDefinedProperties();
+ if (xUserDefinedProperties.is())
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(xUserDefinedProperties,
+ uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySetInfo> xPropertySetInfo
+ = xPropertySet->getPropertySetInfo();
+ // Do we have explicit markup in RTF for this property name?
+ if (xPropertySetInfo->hasPropertyByName("Company"))
+ {
+ OUString aValue;
+ xPropertySet->getPropertyValue("Company") >>= aValue;
+ OutUnicode(OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_COMPANY, aValue);
+ }
+ }
+
+ OutUnicode(OOO_STRING_SVTOOLS_RTF_TITLE, xDocProps->getTitle(), true);
+ OutUnicode(OOO_STRING_SVTOOLS_RTF_SUBJECT, xDocProps->getSubject());
+
+ OutUnicode(OOO_STRING_SVTOOLS_RTF_KEYWORDS,
+ ::comphelper::string::convertCommaSeparated(xDocProps->getKeywords()));
+ OutUnicode(OOO_STRING_SVTOOLS_RTF_DOCCOMM, xDocProps->getDescription());
+
+ OutUnicode(OOO_STRING_SVTOOLS_RTF_AUTHOR, xDocProps->getAuthor());
+ OutDateTime(OOO_STRING_SVTOOLS_RTF_CREATIM, xDocProps->getCreationDate());
+
+ OutUnicode(OOO_STRING_SVTOOLS_RTF_AUTHOR, xDocProps->getModifiedBy());
+ OutDateTime(OOO_STRING_SVTOOLS_RTF_REVTIM, xDocProps->getModificationDate());
+
+ OutDateTime(OOO_STRING_SVTOOLS_RTF_PRINTIM, xDocProps->getPrintDate());
+ }
+
+ Strm().WriteChar('}');
+}
+
+void RtfExport::WriteUserPropType(int nType)
+{
+ Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PROPTYPE);
+ OutULong(nType);
+}
+
+void RtfExport::WriteUserPropValue(const OUString& rValue)
+{
+ Strm().WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_STATICVAL " ");
+ Strm().WriteOString(msfilter::rtfutil::OutString(rValue, m_eDefaultEncoding));
+ Strm().WriteChar('}');
+}
+
+void RtfExport::WriteUserProps()
+{
+ Strm().WriteChar('{').WriteCharPtr(
+ OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_USERPROPS);
+
+ SwDocShell* pDocShell(m_rDoc.GetDocShell());
+ uno::Reference<document::XDocumentProperties> xDocProps;
+ if (pDocShell)
+ {
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(pDocShell->GetModel(),
+ uno::UNO_QUERY);
+ xDocProps.set(xDPS->getDocumentProperties());
+ }
+ else
+ {
+ // Clipboard document, read metadata from the meta field manager.
+ sw::MetaFieldManager& rManager = m_rDoc.GetMetaFieldManager();
+ xDocProps.set(rManager.getDocumentProperties());
+ }
+
+ if (xDocProps.is())
+ {
+ // Handle user-defined properties.
+ uno::Reference<beans::XPropertyContainer> xUserDefinedProperties
+ = xDocProps->getUserDefinedProperties();
+ if (xUserDefinedProperties.is())
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(xUserDefinedProperties,
+ uno::UNO_QUERY);
+ const uno::Sequence<beans::Property> aProperties
+ = xPropertySet->getPropertySetInfo()->getProperties();
+
+ for (const beans::Property& rProperty : aProperties)
+ {
+ if (rProperty.Name.startsWith("Company"))
+ // We have explicit markup in RTF for this property.
+ continue;
+
+ // Property name.
+ Strm().WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_PROPNAME " ");
+ Strm().WriteCharPtr(
+ msfilter::rtfutil::OutString(rProperty.Name, m_eDefaultEncoding).getStr());
+ Strm().WriteChar('}');
+
+ // Property value.
+ OUString aValue;
+ double fValue;
+ bool bValue;
+ util::DateTime aDate;
+ uno::Any aAny = xPropertySet->getPropertyValue(rProperty.Name);
+ if (aAny >>= bValue)
+ {
+ WriteUserPropType(11);
+ WriteUserPropValue(OUString::number(static_cast<int>(bValue)));
+ }
+ else if (aAny >>= aValue)
+ {
+ WriteUserPropType(30);
+ WriteUserPropValue(aValue);
+ }
+ else if (aAny >>= fValue)
+ {
+ aValue = OUString::number(fValue);
+ if (aValue.indexOf('.') == -1)
+ {
+ // Integer.
+ WriteUserPropType(3);
+ WriteUserPropValue(aValue);
+ }
+ else
+ {
+ // Real number.
+ WriteUserPropType(5);
+ WriteUserPropValue(aValue);
+ }
+ }
+ else if (aAny >>= aDate)
+ {
+ WriteUserPropType(64);
+ // Format is 'YYYY. MM. DD.'.
+ aValue += OUString::number(aDate.Year) + ". ";
+ if (aDate.Month < 10)
+ aValue += "0";
+ aValue += OUString::number(aDate.Month) + ". ";
+ if (aDate.Day < 10)
+ aValue += "0";
+ aValue += OUString::number(aDate.Day) + ".";
+ WriteUserPropValue(aValue);
+ }
+ }
+ }
+ }
+
+ Strm().WriteChar('}');
+}
+
+void RtfExport::WriteDocVars()
+{
+ SwDocShell* pDocShell(m_rDoc.GetDocShell());
+ if (!pDocShell)
+ return;
+
+ uno::Reference<text::XTextFieldsSupplier> xModel(pDocShell->GetModel(), uno::UNO_QUERY);
+ uno::Reference<container::XNameAccess> xTextFieldMasters = xModel->getTextFieldMasters();
+ uno::Sequence<rtl::OUString> aMasterNames = xTextFieldMasters->getElementNames();
+ if (!aMasterNames.hasElements())
+ {
+ return;
+ }
+
+ // Only write docVars if there will be at least a single docVar.
+ constexpr OUStringLiteral aPrefix(u"com.sun.star.text.fieldmaster.User.");
+ for (const auto& rMasterName : std::as_const(aMasterNames))
+ {
+ if (!rMasterName.startsWith(aPrefix))
+ {
+ // Not a user field.
+ continue;
+ }
+
+ uno::Reference<beans::XPropertySet> xField;
+ xTextFieldMasters->getByName(rMasterName) >>= xField;
+ if (!xField.is())
+ {
+ continue;
+ }
+
+ OUString aKey = rMasterName.copy(aPrefix.getLength());
+ OUString aValue;
+ xField->getPropertyValue("Content") >>= aValue;
+
+ Strm().WriteChar('{').WriteCharPtr(
+ OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_DOCVAR);
+ Strm().WriteChar(' ');
+
+ Strm().WriteChar('{');
+ Strm().WriteOString(msfilter::rtfutil::OutString(aKey, m_eDefaultEncoding));
+ Strm().WriteChar('}');
+
+ Strm().WriteChar('{');
+ Strm().WriteOString(msfilter::rtfutil::OutString(aValue, m_eDefaultEncoding));
+ Strm().WriteChar('}');
+
+ Strm().WriteChar('}');
+ }
+}
+
+void RtfExport::WritePageDescTable()
+{
+ // Write page descriptions (page styles)
+ std::size_t nSize = m_rDoc.GetPageDescCnt();
+ if (!nSize)
+ return;
+
+ Strm().WriteCharPtr(SAL_NEWLINE_STRING);
+ m_bOutPageDescs = true;
+ Strm()
+ .WriteChar('{')
+ .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_IGNORE)
+ .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PGDSCTBL);
+ for (std::size_t n = 0; n < nSize; ++n)
+ {
+ const SwPageDesc& rPageDesc = m_rDoc.GetPageDesc(n);
+
+ Strm()
+ .WriteCharPtr(SAL_NEWLINE_STRING)
+ .WriteChar('{')
+ .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PGDSC);
+ OutULong(n).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PGDSCUSE);
+ OutULong(static_cast<sal_uLong>(rPageDesc.ReadUseOn()));
+
+ OutPageDescription(rPageDesc, false);
+
+ // search for the next page description
+ std::size_t i = nSize;
+ while (i)
+ if (rPageDesc.GetFollow() == &m_rDoc.GetPageDesc(--i))
+ break;
+ Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PGDSCNXT);
+ OutULong(i).WriteChar(' ');
+ Strm()
+ .WriteCharPtr(
+ msfilter::rtfutil::OutString(rPageDesc.GetName(), m_eDefaultEncoding).getStr())
+ .WriteCharPtr(";}");
+ }
+ Strm().WriteChar('}').WriteCharPtr(SAL_NEWLINE_STRING);
+ m_bOutPageDescs = false;
+
+ // reset table infos, otherwise the depth of the cells will be incorrect,
+ // in case the page style (header or footer) had tables
+ m_pTableInfo = std::make_shared<ww8::WW8TableInfo>();
+}
+
+ErrCode RtfExport::ExportDocument_Impl()
+{
+ // Make the header
+ Strm()
+ .WriteChar('{')
+ .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_RTF)
+ .WriteChar('1')
+ .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_ANSI);
+ Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_DEFF);
+ OutULong(m_aFontHelper.GetId(m_rDoc.GetAttrPool().GetDefaultItem(RES_CHRATR_FONT)));
+ // If this not exist, MS don't understand our ansi characters (0x80-0xff).
+ Strm().WriteCharPtr("\\adeflang1025");
+
+ // Font table
+ WriteFonts();
+
+ m_pStyles = std::make_unique<MSWordStyles>(*this);
+ // Color and stylesheet table
+ WriteStyles();
+
+ // List table
+ BuildNumbering();
+ WriteNumbering();
+
+ WriteRevTab();
+
+ WriteInfo();
+ WriteUserProps();
+ WriteDocVars();
+
+ // Default TabSize
+ Strm()
+ .WriteOString(m_pAttrOutput->GetTabStop().makeStringAndClear())
+ .WriteCharPtr(SAL_NEWLINE_STRING);
+
+ // Automatic hyphenation: it's a global setting in Word, it's a paragraph setting in Writer.
+ // Set it's value to "auto" and disable on paragraph level, if no hyphenation is used there.
+ Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_HYPHAUTO);
+ OutULong(1);
+
+ // Zoom
+ SwViewShell* pViewShell(m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell());
+ if (pViewShell && pViewShell->GetViewOptions()->GetZoomType() == SvxZoomType::PERCENT)
+ {
+ Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_VIEWSCALE);
+ OutULong(pViewShell->GetViewOptions()->GetZoom());
+ }
+ // Record changes?
+ if (RedlineFlags::On & m_nOrigRedlineFlags)
+ Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_REVISIONS);
+ // Mirror margins?
+ if ((UseOnPage::Mirror & m_rDoc.GetPageDesc(0).ReadUseOn()) == UseOnPage::Mirror)
+ Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_MARGMIRROR);
+
+ // Gutter at top?
+ if (m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::GUTTER_AT_TOP))
+ {
+ Strm().WriteCharPtr(LO_STRING_SVTOOLS_RTF_GUTTERPRL);
+ }
+
+ // Init sections
+ m_pSections = std::make_unique<MSWordSections>(*this);
+
+ // Page description
+ WritePageDescTable();
+
+ // Enable form protection by default if needed, as there is no switch to
+ // enable it on a per-section basis. OTOH don't always enable it as it
+ // breaks moving of drawings - so write it only in case there is really a
+ // protected section in the document.
+ for (auto const& pSectionFormat : m_rDoc.GetSections())
+ {
+ if (!pSectionFormat->IsInUndo() && pSectionFormat->GetProtect().IsContentProtected())
+ {
+ Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_FORMPROT);
+ break;
+ }
+ }
+
+ // enable form field shading
+ Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_FORMSHADE);
+
+ // size and empty margins of the page
+ if (m_rDoc.GetPageDescCnt())
+ {
+ // Seeking the first SwFormatPageDesc. If no set, the default is valid
+ const SwFormatPageDesc* pSttPgDsc = nullptr;
+ {
+ const SwNode& rSttNd
+ = *m_rDoc.GetNodes()[m_rDoc.GetNodes().GetEndOfExtras().GetIndex() + 2];
+ const SfxItemSet* pSet = nullptr;
+
+ if (rSttNd.IsContentNode())
+ pSet = &rSttNd.GetContentNode()->GetSwAttrSet();
+ else if (rSttNd.IsTableNode())
+ pSet = &rSttNd.GetTableNode()->GetTable().GetFrameFormat()->GetAttrSet();
+
+ else if (rSttNd.IsSectionNode())
+ pSet = &rSttNd.GetSectionNode()->GetSection().GetFormat()->GetAttrSet();
+
+ if (pSet)
+ {
+ std::size_t nPosInDoc;
+ pSttPgDsc = &pSet->Get(RES_PAGEDESC);
+ if (!pSttPgDsc->GetPageDesc())
+ pSttPgDsc = nullptr;
+ else if (m_rDoc.FindPageDesc(pSttPgDsc->GetPageDesc()->GetName(), &nPosInDoc))
+ {
+ Strm()
+ .WriteChar('{')
+ .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_IGNORE)
+ .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PGDSCNO);
+ OutULong(nPosInDoc).WriteChar('}');
+ }
+ }
+ }
+ const SwPageDesc& rPageDesc = pSttPgDsc ? *pSttPgDsc->GetPageDesc() : m_rDoc.GetPageDesc(0);
+ const SwFrameFormat& rFormatPage = rPageDesc.GetMaster();
+
+ {
+ if (rPageDesc.GetLandscape())
+ Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LANDSCAPE);
+
+ const SwFormatFrameSize& rSz = rFormatPage.GetFrameSize();
+ // Clipboard document is always created without a printer, then
+ // the size will be always LONG_MAX! Solution then is to use A4
+ if (LONG_MAX == rSz.GetHeight() || LONG_MAX == rSz.GetWidth())
+ {
+ Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PAPERH);
+ Size a4 = SvxPaperInfo::GetPaperSize(PAPER_A4);
+ OutULong(a4.Height()).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PAPERW);
+ OutULong(a4.Width());
+ }
+ else
+ {
+ Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PAPERH);
+ OutULong(rSz.GetHeight()).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PAPERW);
+ OutULong(rSz.GetWidth());
+ }
+ }
+
+ {
+ const SvxLRSpaceItem& rLR = rFormatPage.GetLRSpace();
+ Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_MARGL);
+ OutLong(rLR.GetLeft()).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_MARGR);
+ OutLong(rLR.GetRight());
+ }
+
+ {
+ const SvxULSpaceItem& rUL = rFormatPage.GetULSpace();
+ Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_MARGT);
+ OutLong(rUL.GetUpper()).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_MARGB);
+ OutLong(rUL.GetLower());
+ }
+
+ Strm()
+ .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_SECTD)
+ .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_SBKNONE);
+ m_pAttrOutput->SectFootnoteEndnotePr();
+ // All sections are unlocked by default
+ Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_SECTUNLOCKED);
+ OutLong(1);
+ OutPageDescription(rPageDesc, true); // Changed bCheckForFirstPage to true so headers
+ // following title page are correctly added - i13107
+ if (pSttPgDsc)
+ {
+ m_pCurrentPageDesc = &rPageDesc;
+ }
+ }
+
+ // line numbering
+ const SwLineNumberInfo& rLnNumInfo = m_rDoc.GetLineNumberInfo();
+ if (rLnNumInfo.IsPaintLineNumbers())
+ {
+ sal_uLong nLnNumRestartNo = 0;
+ if (const WW8_SepInfo* pSectionInfo = m_pSections->CurrentSectionInfo())
+ nLnNumRestartNo = pSectionInfo->nLnNumRestartNo;
+
+ AttrOutput().SectionLineNumbering(nLnNumRestartNo, rLnNumInfo);
+ }
+
+ {
+ // write the footnotes and endnotes-out Info
+ const SwFootnoteInfo& rFootnoteInfo = m_rDoc.GetFootnoteInfo();
+
+ const char* pOut = FTNPOS_CHAPTER == rFootnoteInfo.m_ePos ? OOO_STRING_SVTOOLS_RTF_ENDDOC
+ : OOO_STRING_SVTOOLS_RTF_FTNBJ;
+ Strm().WriteCharPtr(pOut).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_FTNSTART);
+ OutLong(rFootnoteInfo.m_nFootnoteOffset + 1);
+
+ switch (rFootnoteInfo.m_eNum)
+ {
+ case FTNNUM_PAGE:
+ pOut = OOO_STRING_SVTOOLS_RTF_FTNRSTPG;
+ break;
+ case FTNNUM_DOC:
+ pOut = OOO_STRING_SVTOOLS_RTF_FTNRSTCONT;
+ break;
+ default:
+ pOut = OOO_STRING_SVTOOLS_RTF_FTNRESTART;
+ break;
+ }
+ Strm().WriteCharPtr(pOut);
+
+ switch (rFootnoteInfo.m_aFormat.GetNumberingType())
+ {
+ case SVX_NUM_CHARS_LOWER_LETTER:
+ case SVX_NUM_CHARS_LOWER_LETTER_N:
+ pOut = OOO_STRING_SVTOOLS_RTF_FTNNALC;
+ break;
+ case SVX_NUM_CHARS_UPPER_LETTER:
+ case SVX_NUM_CHARS_UPPER_LETTER_N:
+ pOut = OOO_STRING_SVTOOLS_RTF_FTNNAUC;
+ break;
+ case SVX_NUM_ROMAN_LOWER:
+ pOut = OOO_STRING_SVTOOLS_RTF_FTNNRLC;
+ break;
+ case SVX_NUM_ROMAN_UPPER:
+ pOut = OOO_STRING_SVTOOLS_RTF_FTNNRUC;
+ break;
+ case SVX_NUM_SYMBOL_CHICAGO:
+ pOut = OOO_STRING_SVTOOLS_RTF_FTNNCHI;
+ break;
+ default:
+ pOut = OOO_STRING_SVTOOLS_RTF_FTNNAR;
+ break;
+ }
+ Strm().WriteCharPtr(pOut);
+
+ const SwEndNoteInfo& rEndNoteInfo = m_rDoc.GetEndNoteInfo();
+
+ Strm()
+ .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_AENDDOC)
+ .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_AFTNRSTCONT)
+ .WriteCharPtr(OOO_STRING_SVTOOLS_RTF_AFTNSTART);
+ OutLong(rEndNoteInfo.m_nFootnoteOffset + 1);
+
+ switch (rEndNoteInfo.m_aFormat.GetNumberingType())
+ {
+ case SVX_NUM_CHARS_LOWER_LETTER:
+ case SVX_NUM_CHARS_LOWER_LETTER_N:
+ pOut = OOO_STRING_SVTOOLS_RTF_AFTNNALC;
+ break;
+ case SVX_NUM_CHARS_UPPER_LETTER:
+ case SVX_NUM_CHARS_UPPER_LETTER_N:
+ pOut = OOO_STRING_SVTOOLS_RTF_AFTNNAUC;
+ break;
+ case SVX_NUM_ROMAN_LOWER:
+ pOut = OOO_STRING_SVTOOLS_RTF_AFTNNRLC;
+ break;
+ case SVX_NUM_ROMAN_UPPER:
+ pOut = OOO_STRING_SVTOOLS_RTF_AFTNNRUC;
+ break;
+ case SVX_NUM_SYMBOL_CHICAGO:
+ pOut = OOO_STRING_SVTOOLS_RTF_AFTNNCHI;
+ break;
+ default:
+ pOut = OOO_STRING_SVTOOLS_RTF_AFTNNAR;
+ break;
+ }
+ Strm().WriteCharPtr(pOut);
+ }
+
+ if (!m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::PARA_SPACE_MAX))
+ // RTF default is true, so write compat flag if this should be false.
+ Strm().WriteCharPtr(LO_STRING_SVTOOLS_RTF_HTMAUTSP);
+
+ Strm().WriteCharPtr(SAL_NEWLINE_STRING);
+
+ WriteFootnoteSettings();
+
+ WriteMainText();
+
+ Strm().WriteChar('}');
+
+ return ERRCODE_NONE;
+}
+
+void RtfExport::PrepareNewPageDesc(const SfxItemSet* pSet, const SwNode& rNd,
+ const SwFormatPageDesc* pNewPgDescFormat,
+ const SwPageDesc* pNewPgDesc, bool bExtraPageBreak)
+{
+ const SwSectionFormat* pFormat = GetSectionFormat(rNd);
+ const sal_uLong nLnNm = GetSectionLineNo(pSet, rNd);
+
+ OSL_ENSURE(pNewPgDescFormat || pNewPgDesc, "Neither page desc format nor page desc provided.");
+
+ if (pNewPgDescFormat)
+ m_pSections->AppendSection(*pNewPgDescFormat, rNd, pFormat, nLnNm);
+ else if (pNewPgDesc)
+ m_pSections->AppendSection(pNewPgDesc, rNd, pFormat, nLnNm);
+
+ // Don't insert a page break, when we're changing page style just because the next page has to be a different one.
+ if (!m_pAttrOutput->GetPrevPageDesc()
+ || m_pAttrOutput->GetPrevPageDesc()->GetFollow() != pNewPgDesc)
+ AttrOutput().SectionBreak(msword::PageBreak, false, m_pSections->CurrentSectionInfo(),
+ bExtraPageBreak);
+}
+
+bool RtfExport::DisallowInheritingOutlineNumbering(const SwFormat& rFormat)
+{
+ bool bRet(false);
+
+ if (SfxItemState::SET != rFormat.GetItemState(RES_PARATR_NUMRULE, false))
+ {
+ if (const SwFormat* pParent = rFormat.DerivedFrom())
+ {
+ if (static_cast<const SwTextFormatColl*>(pParent)
+ ->IsAssignedToListLevelOfOutlineStyle())
+ {
+ // Level 9 disables the outline
+ Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LEVEL).WriteInt32(9);
+
+ bRet = true;
+ }
+ }
+ }
+
+ return bRet;
+}
+
+void RtfExport::OutputEndNode(const SwEndNode& rEndNode)
+{
+ if (TXT_MAINTEXT == m_nTextTyp && rEndNode.StartOfSectionNode()->IsTableNode())
+ // End node of a table: see if a section break should be written after the table.
+ AttrOutput().SectionBreaks(rEndNode);
+}
+
+void RtfExport::OutputGrfNode(const SwGrfNode& /*rGrfNode*/)
+{
+ /* noop, see RtfAttributeOutput::FlyFrameGraphic */
+}
+
+void RtfExport::OutputOLENode(const SwOLENode& /*rOLENode*/)
+{
+ /* noop, see RtfAttributeOutput::FlyFrameOLE */
+}
+
+void RtfExport::OutputLinkedOLE(const OUString& /*rLinked*/) {}
+
+void RtfExport::OutputTextNode(SwTextNode& rNode)
+{
+ m_nCurrentNodeIndex = rNode.GetIndex();
+ if (!m_bOutOutlineOnly || rNode.IsOutline())
+ MSWordExportBase::OutputTextNode(rNode);
+ m_nCurrentNodeIndex = SwNodeOffset(0);
+}
+
+void RtfExport::AppendSection(const SwPageDesc* pPageDesc, const SwSectionFormat* pFormat,
+ sal_uLong nLnNum)
+{
+ m_pSections->AppendSection(pPageDesc, pFormat, nLnNum);
+ AttrOutput().SectionBreak(msword::PageBreak, false, m_pSections->CurrentSectionInfo());
+}
+
+RtfExport::RtfExport(RtfExportFilter* pFilter, SwDoc& rDocument,
+ std::shared_ptr<SwUnoCursor>& pCurrentPam, SwPaM& rOriginalPam,
+ Writer* pWriter, bool bOutOutlineOnly)
+ : MSWordExportBase(rDocument, pCurrentPam, &rOriginalPam)
+ , m_pFilter(pFilter)
+ , m_pWriter(pWriter)
+ , m_bOutOutlineOnly(bOutOutlineOnly)
+ , m_eDefaultEncoding(
+ rtl_getTextEncodingFromWindowsCharset(sw::ms::rtl_TextEncodingToWinCharset(DEF_ENCODING)))
+ , m_eCurrentEncoding(m_eDefaultEncoding)
+ , m_bRTFFlySyntax(false)
+ , m_nCurrentNodeIndex(0)
+{
+ m_bExportModeRTF = true;
+ // the attribute output for the document
+ m_pAttrOutput = std::make_unique<RtfAttributeOutput>(*this);
+ // that just causes problems for RTF
+ m_bSubstituteBullets = false;
+ // needed to have a complete font table
+ m_aFontHelper.m_bLoadAllFonts = true;
+ // the related SdrExport
+ m_pSdrExport = std::make_unique<RtfSdrExport>(*this);
+
+ if (!m_pWriter)
+ m_pWriter = &m_pFilter->GetWriter();
+}
+
+RtfExport::~RtfExport() = default;
+
+SvStream& RtfExport::Strm()
+{
+ if (m_pStream)
+ return *m_pStream;
+
+ return m_pWriter->Strm();
+}
+
+void RtfExport::setStream() { m_pStream = std::make_unique<SvMemoryStream>(); }
+
+OString RtfExport::getStream()
+{
+ OString aRet;
+
+ if (m_pStream)
+ aRet = OString(static_cast<const char*>(m_pStream->GetData()), m_pStream->Tell());
+
+ return aRet;
+}
+
+void RtfExport::resetStream() { m_pStream.reset(); }
+
+SvStream& RtfExport::OutULong(sal_uLong nVal) { return Writer::OutULong(Strm(), nVal); }
+
+SvStream& RtfExport::OutLong(tools::Long nVal) { return Writer::OutLong(Strm(), nVal); }
+
+void RtfExport::OutUnicode(const char* pToken, const OUString& rContent, bool bUpr)
+{
+ if (rContent.isEmpty())
+ return;
+
+ if (!bUpr)
+ {
+ Strm().WriteChar('{').WriteCharPtr(pToken).WriteChar(' ');
+ Strm().WriteCharPtr(msfilter::rtfutil::OutString(rContent, m_eCurrentEncoding).getStr());
+ Strm().WriteChar('}');
+ }
+ else
+ Strm().WriteCharPtr(
+ msfilter::rtfutil::OutStringUpr(pToken, rContent, m_eCurrentEncoding).getStr());
+}
+
+void RtfExport::OutDateTime(const char* pStr, const util::DateTime& rDT)
+{
+ Strm().WriteChar('{').WriteCharPtr(pStr).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_YR);
+ OutULong(rDT.Year).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_MO);
+ OutULong(rDT.Month).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_DY);
+ OutULong(rDT.Day).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_HR);
+ OutULong(rDT.Hours).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_MIN);
+ OutULong(rDT.Minutes).WriteChar('}');
+}
+
+sal_uInt16 RtfExport::GetColor(const Color& rColor) const
+{
+ for (const auto& rEntry : m_aColTable)
+ if (rEntry.second == rColor)
+ {
+ SAL_INFO("sw.rtf", __func__ << " returning " << rEntry.first << " (" << rColor.GetRed()
+ << "," << rColor.GetGreen() << "," << rColor.GetBlue()
+ << ")");
+ return rEntry.first;
+ }
+ OSL_FAIL("No such Color in m_aColTable!");
+ return 0;
+}
+
+void RtfExport::InsColor(const Color& rCol)
+{
+ sal_uInt16 n;
+ bool bAutoColorInTable = false;
+ for (const auto& rEntry : m_aColTable)
+ {
+ if (rEntry.second == rCol)
+ return; // Already in the table
+ if (rEntry.second == COL_AUTO)
+ bAutoColorInTable = true;
+ }
+ if (rCol == COL_AUTO)
+ // COL_AUTO gets value 0
+ n = 0;
+ else
+ {
+ // other colors get values >0
+ n = m_aColTable.size();
+ if (!bAutoColorInTable)
+ // reserve value "0" for COL_AUTO (if COL_AUTO wasn't inserted until now)
+ n++;
+ }
+ m_aColTable.insert(std::pair<sal_uInt16, Color>(n, rCol));
+}
+
+void RtfExport::InsColorLine(const SvxBoxItem& rBox)
+{
+ const editeng::SvxBorderLine* pLine = nullptr;
+
+ if (rBox.GetTop())
+ {
+ pLine = rBox.GetTop();
+ InsColor(pLine->GetColor());
+ }
+ if (rBox.GetBottom() && pLine != rBox.GetBottom())
+ {
+ pLine = rBox.GetBottom();
+ InsColor(pLine->GetColor());
+ }
+ if (rBox.GetLeft() && pLine != rBox.GetLeft())
+ {
+ pLine = rBox.GetLeft();
+ InsColor(pLine->GetColor());
+ }
+ if (rBox.GetRight() && pLine != rBox.GetRight())
+ InsColor(rBox.GetRight()->GetColor());
+}
+
+void RtfExport::OutColorTable()
+{
+ // Build the table from rPool since the colors provided to
+ // RtfAttributeOutput callbacks are too late.
+ const SfxItemPool& rPool = m_rDoc.GetAttrPool();
+
+ // MSO Word uses a default color table with 16 colors (which is used e.g. for highlighting)
+ InsColor(COL_BLACK);
+ InsColor(COL_LIGHTBLUE);
+ InsColor(COL_LIGHTCYAN);
+ InsColor(COL_LIGHTGREEN);
+ InsColor(COL_LIGHTMAGENTA);
+ InsColor(COL_LIGHTRED);
+ InsColor(COL_YELLOW);
+ InsColor(COL_WHITE);
+ InsColor(COL_BLUE);
+ InsColor(COL_CYAN);
+ InsColor(COL_GREEN);
+ InsColor(COL_MAGENTA);
+ InsColor(COL_RED);
+ InsColor(COL_BROWN);
+ InsColor(COL_GRAY);
+ InsColor(COL_LIGHTGRAY);
+
+ // char color
+ {
+ auto pCol = GetDfltAttr(RES_CHRATR_COLOR);
+ InsColor(pCol->GetValue());
+ pCol = rPool.GetPoolDefaultItem(RES_CHRATR_COLOR);
+ if (pCol)
+ InsColor(pCol->GetValue());
+ for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_CHRATR_COLOR))
+ {
+ pCol = dynamic_cast<const SvxColorItem*>(pItem);
+ if (pCol)
+ InsColor(pCol->GetValue());
+ }
+
+ auto pUnder = GetDfltAttr(RES_CHRATR_UNDERLINE);
+ InsColor(pUnder->GetColor());
+ for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_CHRATR_UNDERLINE))
+ {
+ pUnder = dynamic_cast<const SvxUnderlineItem*>(pItem);
+ if (pUnder)
+ InsColor(pUnder->GetColor());
+ }
+
+ auto pOver = GetDfltAttr(RES_CHRATR_OVERLINE);
+ InsColor(pOver->GetColor());
+ for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_CHRATR_OVERLINE))
+ {
+ pOver = dynamic_cast<const SvxOverlineItem*>(pItem);
+ if (pOver)
+ InsColor(pOver->GetColor());
+ }
+ }
+
+ // background color
+ static const sal_uInt16 aBrushIds[] = { RES_BACKGROUND, RES_CHRATR_BACKGROUND, 0 };
+
+ for (const sal_uInt16* pIds = aBrushIds; *pIds; ++pIds)
+ {
+ auto pBackground = static_cast<const SvxBrushItem*>(GetDfltAttr(*pIds));
+ InsColor(pBackground->GetColor());
+ pBackground = static_cast<const SvxBrushItem*>(rPool.GetPoolDefaultItem(*pIds));
+ if (pBackground)
+ {
+ InsColor(pBackground->GetColor());
+ }
+ for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(*pIds))
+ {
+ pBackground = static_cast<const SvxBrushItem*>(pItem);
+ if (pBackground)
+ {
+ InsColor(pBackground->GetColor());
+ }
+ }
+ }
+
+ // shadow color
+ {
+ auto pShadow = GetDfltAttr(RES_SHADOW);
+ InsColor(pShadow->GetColor());
+ pShadow = rPool.GetPoolDefaultItem(RES_SHADOW);
+ if (nullptr != pShadow)
+ {
+ InsColor(pShadow->GetColor());
+ }
+ for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_SHADOW))
+ {
+ pShadow = dynamic_cast<const SvxShadowItem*>(pItem);
+ if (pShadow)
+ {
+ InsColor(pShadow->GetColor());
+ }
+ }
+ }
+
+ // frame border color
+ {
+ const SvxBoxItem* pBox = rPool.GetPoolDefaultItem(RES_BOX);
+ if (nullptr != pBox)
+ InsColorLine(*pBox);
+ for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_BOX))
+ {
+ pBox = dynamic_cast<const SvxBoxItem*>(pItem);
+ if (pBox)
+ InsColorLine(*pBox);
+ }
+ }
+
+ {
+ const SvxBoxItem* pCharBox = rPool.GetPoolDefaultItem(RES_CHRATR_BOX);
+ if (pCharBox)
+ InsColorLine(*pCharBox);
+ for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_CHRATR_BOX))
+ {
+ pCharBox = dynamic_cast<const SvxBoxItem*>(pItem);
+ if (pCharBox)
+ InsColorLine(*pCharBox);
+ }
+ }
+
+ // TextFrame or paragraph background solid fill.
+ for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(XATTR_FILLCOLOR))
+ {
+ if (auto pColorItem = dynamic_cast<const XFillColorItem*>(pItem))
+ InsColor(pColorItem->GetColorValue());
+ }
+
+ for (std::size_t n = 0; n < m_aColTable.size(); ++n)
+ {
+ const Color& rCol = m_aColTable[n];
+ if (n || COL_AUTO != rCol)
+ {
+ Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_RED);
+ OutULong(rCol.GetRed()).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_GREEN);
+ OutULong(rCol.GetGreen()).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_BLUE);
+ OutULong(rCol.GetBlue());
+ }
+ Strm().WriteChar(';');
+ }
+}
+
+void RtfExport::InsStyle(sal_uInt16 nId, const OString& rStyle)
+{
+ m_aStyTable.insert(std::pair<sal_uInt16, OString>(nId, rStyle));
+}
+
+OString* RtfExport::GetStyle(sal_uInt16 nId)
+{
+ auto it = m_aStyTable.find(nId);
+ if (it != m_aStyTable.end())
+ return &it->second;
+ return nullptr;
+}
+
+sal_uInt16 RtfExport::GetRedline(const OUString& rAuthor)
+{
+ auto it = m_aRedlineTable.find(rAuthor);
+ if (it != m_aRedlineTable.end())
+ return it->second;
+
+ const sal_uInt16 nId = m_aRedlineTable.size();
+ m_aRedlineTable.insert(std::pair<OUString, sal_uInt16>(rAuthor, nId));
+ return nId;
+}
+
+const OUString* RtfExport::GetRedline(sal_uInt16 nId)
+{
+ for (const auto& rEntry : m_aRedlineTable)
+ if (rEntry.second == nId)
+ return &rEntry.first;
+ return nullptr;
+}
+
+void RtfExport::OutPageDescription(const SwPageDesc& rPgDsc, bool bCheckForFirstPage)
+{
+ SAL_INFO("sw.rtf", __func__ << " start");
+ const SwPageDesc* pSave = m_pCurrentPageDesc;
+
+ m_pCurrentPageDesc = &rPgDsc;
+ if (bCheckForFirstPage && m_pCurrentPageDesc->GetFollow()
+ && m_pCurrentPageDesc->GetFollow() != m_pCurrentPageDesc)
+ m_pCurrentPageDesc = m_pCurrentPageDesc->GetFollow();
+
+ if (m_pCurrentPageDesc->GetLandscape())
+ Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_LNDSCPSXN);
+
+ const SwFormat* pFormat = &m_pCurrentPageDesc->GetMaster(); //GetLeft();
+ m_bOutPageDescs = true;
+ if (m_pCurrentPageDesc != &rPgDsc)
+ m_pFirstPageItemSet = &rPgDsc.GetMaster().GetAttrSet();
+ OutputFormat(*pFormat, true, false);
+ m_pFirstPageItemSet = nullptr;
+ m_bOutPageDescs = false;
+
+ // normal header / footer (without a style)
+ const SfxPoolItem* pItem;
+ if (m_pCurrentPageDesc->GetLeft().GetAttrSet().GetItemState(RES_HEADER, false, &pItem)
+ == SfxItemState::SET)
+ WriteHeaderFooter(*pItem, true);
+ if (m_pCurrentPageDesc->GetLeft().GetAttrSet().GetItemState(RES_FOOTER, false, &pItem)
+ == SfxItemState::SET)
+ WriteHeaderFooter(*pItem, false);
+
+ // title page
+ if (m_pCurrentPageDesc != &rPgDsc)
+ {
+ Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_TITLEPG);
+ m_pCurrentPageDesc = &rPgDsc;
+ if (m_pCurrentPageDesc->GetMaster().GetAttrSet().GetItemState(RES_HEADER, false, &pItem)
+ == SfxItemState::SET)
+ WriteHeaderFooter(*pItem, true);
+ if (m_pCurrentPageDesc->GetMaster().GetAttrSet().GetItemState(RES_FOOTER, false, &pItem)
+ == SfxItemState::SET)
+ WriteHeaderFooter(*pItem, false);
+ }
+
+ // numbering type
+ AttrOutput().SectionPageNumbering(m_pCurrentPageDesc->GetNumType().GetNumberingType(),
+ std::nullopt);
+
+ m_pCurrentPageDesc = pSave;
+ SAL_INFO("sw.rtf", __func__ << " end");
+}
+
+void RtfExport::WriteHeaderFooter(const SfxPoolItem& rItem, bool bHeader)
+{
+ if (bHeader)
+ {
+ const auto& rHeader = static_cast<const SwFormatHeader&>(rItem);
+ if (!rHeader.IsActive())
+ return;
+ }
+ else
+ {
+ const auto& rFooter = static_cast<const SwFormatFooter&>(rItem);
+ if (!rFooter.IsActive())
+ return;
+ }
+
+ SAL_INFO("sw.rtf", __func__ << " start");
+
+ const char* pStr = (bHeader ? OOO_STRING_SVTOOLS_RTF_HEADER : OOO_STRING_SVTOOLS_RTF_FOOTER);
+ /* is this a title page? */
+ if (m_pCurrentPageDesc->GetFollow() && m_pCurrentPageDesc->GetFollow() != m_pCurrentPageDesc)
+ {
+ Strm().WriteCharPtr(OOO_STRING_SVTOOLS_RTF_TITLEPG);
+ pStr = (bHeader ? OOO_STRING_SVTOOLS_RTF_HEADERF : OOO_STRING_SVTOOLS_RTF_FOOTERF);
+ }
+ Strm().WriteChar('{').WriteCharPtr(pStr);
+ WriteHeaderFooterText(m_pCurrentPageDesc->GetMaster(), bHeader);
+ Strm().WriteChar('}');
+
+ SAL_INFO("sw.rtf", __func__ << " end");
+}
+
+void RtfExport::WriteHeaderFooter(const SwFrameFormat& rFormat, bool bHeader, const char* pStr,
+ bool bTitlepg)
+{
+ SAL_INFO("sw.rtf", __func__ << " start");
+
+ m_pAttrOutput->WriteHeaderFooter_Impl(rFormat, bHeader, pStr, bTitlepg);
+
+ SAL_INFO("sw.rtf", __func__ << " end");
+}
+
+namespace
+{
+/// Glue class to call RtfExport as an internal filter, needed by copy&paste support.
+class SwRTFWriter : public Writer
+{
+private:
+ bool m_bOutOutlineOnly;
+
+public:
+ SwRTFWriter(std::u16string_view rFilterName, const OUString& rBaseURL);
+
+ ErrCode WriteStream() override;
+};
+}
+
+SwRTFWriter::SwRTFWriter(std::u16string_view rFilterName, const OUString& rBaseURL)
+{
+ SetBaseURL(rBaseURL);
+ // export outline nodes, only (send outline to clipboard/presentation)
+ m_bOutOutlineOnly = o3tl::starts_with(rFilterName, u"O");
+}
+
+ErrCode SwRTFWriter::WriteStream()
+{
+ std::shared_ptr<SwUnoCursor> pCurPam(m_pDoc->CreateUnoCursor(*m_pCurrentPam->End(), false));
+ pCurPam->SetMark();
+ *pCurPam->GetPoint() = *m_pCurrentPam->Start();
+ RtfExport aExport(nullptr, *m_pDoc, pCurPam, *m_pCurrentPam, this, m_bOutOutlineOnly);
+ aExport.ExportDocument(true);
+ return ERRCODE_NONE;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT void ExportRTF(std::u16string_view rFltName,
+ const OUString& rBaseURL, WriterRef& xRet)
+{
+ xRet = new SwRTFWriter(rFltName, rBaseURL);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */