/* -*- 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 <unotools/securityoptions.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>
#include <fmtftntx.hxx>
#include <ftnidx.hxx>
#include <txtftn.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().GetContentIndex();
const sal_Int32 nEnd = pMark->GetMarkEnd().GetContentIndex();
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().GetContentIndex();
const sal_Int32 nEnd = pMark->GetMarkEnd().GetContentIndex();
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('{')
.WriteOString(OOO_STRING_SVTOOLS_RTF_IGNORE)
.WriteOString(OOO_STRING_SVTOOLS_RTF_LISTTABLE);
CollectGrfsOfBullets();
if (!m_vecBulletPic.empty())
Strm()
.WriteChar('{')
.WriteOString(OOO_STRING_SVTOOLS_RTF_IGNORE)
.WriteOString(LO_STRING_SVTOOLS_RTF_LISTPICTURE);
BulletDefinitions();
if (!m_vecBulletPic.empty())
Strm().WriteChar('}');
AbstractNumberingDefinitions();
Strm().WriteChar('}');
Strm().WriteChar('{').WriteOString(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(u"Unknown"_ustr);
for (SwRangeRedline* pRedl : m_rDoc.getIDocumentRedlineAccess().GetRedlineTable())
{
GetRedline(SW_MOD()->GetRedlineAuthor(pRedl->GetAuthor()));
}
bool bRemoveChangesInfo
= SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo)
&& !SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::DocWarnKeepRedlineInfo);
// Now write the table
Strm()
.WriteChar('{')
.WriteOString(OOO_STRING_SVTOOLS_RTF_IGNORE)
.WriteOString(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)
{
OUString sAuthor(bRemoveChangesInfo ? "Author" + OUString::number(GetInfoID(*pAuthor))
: *pAuthor);
Strm().WriteOString(msfilter::rtfutil::OutString(sAuthor, m_eDefaultEncoding));
}
Strm().WriteOString(";}");
}
Strm().WriteChar('}').WriteOString(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::Fieldmark& 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::Fieldmark& /*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()
.WriteOString(SAL_NEWLINE_STRING)
.WriteChar('{')
.WriteOString(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('{')
.WriteOString(OOO_STRING_SVTOOLS_RTF_IGNORE)
.WriteOString(OOO_STRING_SVTOOLS_RTF_FTNSEP);
if (bSeparator)
Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_CHFTNSEP);
Strm().WriteChar('}');
}
void RtfExport::WriteMainText()
{
SAL_INFO("sw.rtf", __func__ << " start");
const std::unique_ptr<SvxBrushItem> oBrush = getBackground();
if (oBrush && oBrush->GetColor() != COL_AUTO)
{
Strm().WriteOString(LO_STRING_SVTOOLS_RTF_VIEWBKSP).WriteChar('1');
Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_BACKGROUND);
Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_SHP);
Strm().WriteOString("{" 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().WriteOString("{" OOO_STRING_SVTOOLS_RTF_SP "{");
Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SN " ");
Strm().WriteOString(rPair.first);
Strm().WriteOString("}{" OOO_STRING_SVTOOLS_RTF_SV " ");
Strm().WriteOString(rPair.second);
Strm().WriteOString("}}");
}
Strm().WriteChar('}'); // shpinst
Strm().WriteChar('}'); // shp
Strm().WriteChar('}'); // background
}
SwTableNode* pTableNode = m_pCurPam->GetPointNode().FindTableNode();
if (m_pWriter && m_pWriter->m_bWriteOnlyFirstTable && pTableNode != nullptr)
{
m_pCurPam->GetPoint()->Assign(*pTableNode);
m_pCurPam->GetMark()->Assign(*pTableNode->EndOfSectionNode());
}
else
{
m_pCurPam->GetPoint()->Assign(*m_rDoc.GetNodes().GetEndOfContent().StartOfSectionNode());
}
WriteText();
SAL_INFO("sw.rtf", __func__ << " end");
}
void RtfExport::WriteInfo()
{
OString aGenerator
= OUStringToOString(utl::DocInfoHelper::GetGeneratorString(), RTL_TEXTENCODING_UTF8);
Strm()
.WriteOString("{" OOO_STRING_SVTOOLS_RTF_IGNORE LO_STRING_SVTOOLS_RTF_GENERATOR " ")
.WriteOString(aGenerator)
.WriteChar('}');
Strm().WriteChar('{').WriteOString(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(u"Company"_ustr))
{
OUString aValue;
xPropertySet->getPropertyValue(u"Company"_ustr) >>= 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().WriteOString(OOO_STRING_SVTOOLS_RTF_PROPTYPE).WriteNumberAsString(nType);
}
void RtfExport::WriteUserPropValue(std::u16string_view rValue)
{
Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_STATICVAL " ");
Strm().WriteOString(msfilter::rtfutil::OutString(rValue, m_eDefaultEncoding));
Strm().WriteChar('}');
}
void RtfExport::WriteUserProps()
{
Strm().WriteChar('{').WriteOString(
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().WriteOString("{" OOO_STRING_SVTOOLS_RTF_PROPNAME " ");
Strm().WriteOString(
msfilter::rtfutil::OutString(rProperty.Name, m_eDefaultEncoding));
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.
static constexpr OUString aPrefix(u"com.sun.star.text.fieldmaster.User."_ustr);
for (const auto& rMasterName : 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(u"Content"_ustr) >>= aValue;
Strm().WriteChar('{').WriteOString(
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('}');
}
}
ErrCode RtfExport::ExportDocument_Impl()
{
// Make the header
Strm()
.WriteChar('{')
.WriteOString(OOO_STRING_SVTOOLS_RTF_RTF)
.WriteChar('1')
.WriteOString(OOO_STRING_SVTOOLS_RTF_ANSI);
Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_DEFF);
Strm().WriteNumberAsString(
m_aFontHelper.GetId(m_rDoc.GetAttrPool().GetUserOrPoolDefaultItem(RES_CHRATR_FONT)));
// If this not exist, MS don't understand our ansi characters (0x80-0xff).
Strm().WriteOString("\\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()).WriteOString(SAL_NEWLINE_STRING);
m_pAttrOutput->GetTabStop().setLength(0);
// 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().WriteOString(OOO_STRING_SVTOOLS_RTF_HYPHAUTO);
Strm().WriteOString("1");
// Zoom
SwViewShell* pViewShell(m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell());
if (pViewShell && pViewShell->GetViewOptions()->GetZoomType() == SvxZoomType::PERCENT)
{
Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_VIEWSCALE);
Strm().WriteNumberAsString(pViewShell->GetViewOptions()->GetZoom());
}
// Record changes?
if (RedlineFlags::On & m_nOrigRedlineFlags)
Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_REVISIONS);
// Mirror margins?
if ((UseOnPage::Mirror & m_rDoc.GetPageDesc(0).ReadUseOn()) == UseOnPage::Mirror)
Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_MARGMIRROR);
// Gutter at top?
IDocumentSettingAccess& rIDSA = m_rDoc.getIDocumentSettingAccess();
if (rIDSA.get(DocumentSettingId::GUTTER_AT_TOP))
{
Strm().WriteOString(LO_STRING_SVTOOLS_RTF_GUTTERPRL);
}
// Init sections
m_pSections = std::make_unique<MSWordSections>(*this);
// 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.
SwSectionFormats& rSections = m_rDoc.GetSections();
for (auto const& pSectionFormat : rSections)
{
if (!pSectionFormat->IsInUndo() && pSectionFormat->GetProtect().IsContentProtected())
{
Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_FORMPROT);
break;
}
}
// enable form field shading
Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_FORMSHADE);
// Enable breaking wrapped tables across pages: the "no" in the control word's name is
// confusing.
if (!rIDSA.get(DocumentSettingId::DO_NOT_BREAK_WRAPPED_TABLES))
{
Strm().WriteOString(LO_STRING_SVTOOLS_RTF_NOBRKWRPTBL);
}
// 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('{')
.WriteOString(OOO_STRING_SVTOOLS_RTF_IGNORE)
.WriteOString(OOO_STRING_SVTOOLS_RTF_PGDSCNO);
Strm().WriteNumberAsString(nPosInDoc).WriteChar('}');
}
}
}
const SwPageDesc& rPageDesc = pSttPgDsc ? *pSttPgDsc->GetPageDesc() : m_rDoc.GetPageDesc(0);
const SwFrameFormat& rFormatPage = rPageDesc.GetMaster();
{
if (rPageDesc.GetLandscape())
Strm().WriteOString(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().WriteOString(OOO_STRING_SVTOOLS_RTF_PAPERH);
Size a4 = SvxPaperInfo::GetPaperSize(PAPER_A4);
Strm().WriteNumberAsString(a4.Height()).WriteOString(OOO_STRING_SVTOOLS_RTF_PAPERW);
Strm().WriteNumberAsString(a4.Width());
}
else
{
Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_PAPERH);
Strm()
.WriteNumberAsString(rSz.GetHeight())
.WriteOString(OOO_STRING_SVTOOLS_RTF_PAPERW);
Strm().WriteNumberAsString(rSz.GetWidth());
}
}
{
const SvxLRSpaceItem& rLR = rFormatPage.GetLRSpace();
Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_MARGL);
Strm().WriteNumberAsString(rLR.GetLeft()).WriteOString(OOO_STRING_SVTOOLS_RTF_MARGR);
Strm().WriteNumberAsString(rLR.GetRight());
}
{
const SvxULSpaceItem& rUL = rFormatPage.GetULSpace();
Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_MARGT);
Strm().WriteNumberAsString(rUL.GetUpper()).WriteOString(OOO_STRING_SVTOOLS_RTF_MARGB);
Strm().WriteNumberAsString(rUL.GetLower());
}
Strm()
.WriteOString(OOO_STRING_SVTOOLS_RTF_SECTD)
.WriteOString(OOO_STRING_SVTOOLS_RTF_SBKNONE);
m_pAttrOutput->SectFootnoteEndnotePr();
// All sections are unlocked by default
Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SECTUNLOCKED);
Strm().WriteOString("1");
OutPageDescription(rPageDesc); // 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().WriteOString(pOut).WriteOString(OOO_STRING_SVTOOLS_RTF_FTNSTART);
Strm().WriteNumberAsString(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().WriteOString(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().WriteOString(pOut);
const SwEndNoteInfo& rEndNoteInfo = m_rDoc.GetEndNoteInfo();
if (!rSections.empty())
{
SwSectionFormat* pFormat = rSections[0];
bool bEndnAtEnd = pFormat->GetEndAtTextEnd().IsAtEnd();
if (bEndnAtEnd)
{
// Endnotes at end of section.
Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_AENDNOTES);
}
else
{
// Endnotes at end of document.
Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_AENDDOC);
}
}
// Types of notes that are present in the document:
Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_FET);
SwFootnoteIdxs& rFootnotes = m_rDoc.GetFootnoteIdxs();
bool bHasFootnote = false;
bool bHasEndnote = false;
for (const auto& pFootnote : rFootnotes)
{
if (pFootnote->GetFootnote().IsEndNote())
{
bHasEndnote = true;
}
else
{
bHasFootnote = true;
}
if (bHasFootnote && bHasEndnote)
{
break;
}
}
if (bHasFootnote && bHasEndnote)
{
// Both footnotes and endnotes.
Strm().WriteOString("2");
}
else if (bHasEndnote)
{
// Endnotes only.
Strm().WriteOString("1");
}
Strm()
.WriteOString(OOO_STRING_SVTOOLS_RTF_AFTNRSTCONT)
.WriteOString(OOO_STRING_SVTOOLS_RTF_AFTNSTART);
Strm().WriteNumberAsString(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().WriteOString(pOut);
}
if (!m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::PARA_SPACE_MAX))
// RTF default is true, so write compat flag if this should be false.
Strm().WriteOString(LO_STRING_SVTOOLS_RTF_HTMAUTSP);
Strm().WriteOString(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(SwFormatPageDesc(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().WriteOString(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)
, mpAuthorIDs(new SvtSecurityMapPersonalInfo)
{
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(); }
void RtfExport::OutUnicode(std::string_view pToken, std::u16string_view rContent, bool bUpr)
{
if (rContent.empty())
return;
if (!bUpr)
{
Strm().WriteChar('{').WriteOString(pToken).WriteChar(' ');
Strm().WriteOString(msfilter::rtfutil::OutString(rContent, m_eCurrentEncoding));
Strm().WriteChar('}');
}
else
Strm().WriteOString(msfilter::rtfutil::OutStringUpr(pToken, rContent, m_eCurrentEncoding));
}
void RtfExport::OutDateTime(std::string_view pStr, const util::DateTime& rDT)
{
Strm().WriteChar('{').WriteOString(pStr).WriteOString(OOO_STRING_SVTOOLS_RTF_YR);
Strm().WriteNumberAsString(rDT.Year).WriteOString(OOO_STRING_SVTOOLS_RTF_MO);
Strm().WriteNumberAsString(rDT.Month).WriteOString(OOO_STRING_SVTOOLS_RTF_DY);
Strm().WriteNumberAsString(rDT.Day).WriteOString(OOO_STRING_SVTOOLS_RTF_HR);
Strm().WriteNumberAsString(rDT.Hours).WriteOString(OOO_STRING_SVTOOLS_RTF_MIN);
Strm().WriteNumberAsString(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.GetUserDefaultItem(RES_CHRATR_COLOR);
if (pCol)
InsColor(pCol->GetValue());
m_rDoc.ForEachCharacterColorItem([this](const SvxColorItem& rColorItem) -> bool {
InsColor(rColorItem.GetValue());
return true;
});
auto pUnder = GetDfltAttr(RES_CHRATR_UNDERLINE);
InsColor(pUnder->GetColor());
m_rDoc.ForEachCharacterUnderlineItem([this](const SvxUnderlineItem& rUnder) -> bool {
InsColor(rUnder.GetColor());
return true;
});
auto pOver = GetDfltAttr(RES_CHRATR_OVERLINE);
InsColor(pOver->GetColor());
m_rDoc.ForEachOverlineItem([this](const SvxOverlineItem& rOver) -> bool {
InsColor(rOver.GetColor());
return true;
});
}
// background color
{
const SvxBrushItem* pBackground = GetDfltAttr(RES_BACKGROUND);
InsColor(pBackground->GetColor());
pBackground = rPool.GetUserDefaultItem(RES_BACKGROUND);
if (pBackground)
{
InsColor(pBackground->GetColor());
}
m_rDoc.ForEachBackgroundBrushItem([this](const SvxBrushItem& rBrush) -> bool {
InsColor(rBrush.GetColor());
return true;
});
}
{
const SvxBrushItem* pBackground = GetDfltAttr(RES_CHRATR_BACKGROUND);
InsColor(pBackground->GetColor());
pBackground = rPool.GetUserDefaultItem(RES_CHRATR_BACKGROUND);
if (pBackground)
{
InsColor(pBackground->GetColor());
}
m_rDoc.ForEachCharacterBrushItem([this](const SvxBrushItem& rBrush) -> bool {
InsColor(rBrush.GetColor());
return true;
});
}
// shadow color
{
auto pShadow = GetDfltAttr(RES_SHADOW);
InsColor(pShadow->GetColor());
pShadow = rPool.GetUserDefaultItem(RES_SHADOW);
if (nullptr != pShadow)
{
InsColor(pShadow->GetColor());
}
m_rDoc.ForEachShadowItem([this](const SvxShadowItem& rShadow) -> bool {
InsColor(rShadow.GetColor());
return true;
});
}
// frame border color
{
const SvxBoxItem* pBox = rPool.GetUserDefaultItem(RES_BOX);
if (nullptr != pBox)
InsColorLine(*pBox);
m_rDoc.ForEachBoxItem([this](const SvxBoxItem& rBox) -> bool {
InsColorLine(rBox);
return true;
});
}
{
const SvxBoxItem* pCharBox = rPool.GetUserDefaultItem(RES_CHRATR_BOX);
if (pCharBox)
InsColorLine(*pCharBox);
m_rDoc.ForEachCharacterBoxItem([this](const SvxBoxItem& rCharBox) -> bool {
InsColorLine(rCharBox);
return true;
});
}
// TextFrame or paragraph background solid fill.
ItemSurrogates aSurrogates;
rPool.GetItemSurrogatesForItem(aSurrogates, SfxItemType::XFillColorItemType);
for (const SfxPoolItem* pItem : aSurrogates)
{
const auto& rColorItem = static_cast<const XFillColorItem&>(*pItem);
InsColor(rColorItem.GetColorValue());
}
for (std::size_t n = 0; n < m_aColTable.size(); ++n)
{
const Color& rCol = m_aColTable[n];
if (n || COL_AUTO != rCol)
{
Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_RED);
Strm().WriteNumberAsString(rCol.GetRed()).WriteOString(OOO_STRING_SVTOOLS_RTF_GREEN);
Strm().WriteNumberAsString(rCol.GetGreen()).WriteOString(OOO_STRING_SVTOOLS_RTF_BLUE);
Strm().WriteNumberAsString(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)
{
const sal_uInt16 nId = m_aRedlineTable.size();
// insert if we don't already have one
auto[it, inserted] = m_aRedlineTable.insert(std::pair<OUString, sal_uInt16>(rAuthor, nId));
(void)inserted;
return it->second;
}
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)
{
SAL_INFO("sw.rtf", __func__ << " start");
const SwPageDesc* pSave = m_pCurrentPageDesc;
m_pCurrentPageDesc = &rPgDsc;
if (m_pCurrentPageDesc->GetFollow() && m_pCurrentPageDesc->GetFollow() != m_pCurrentPageDesc)
m_pCurrentPageDesc = m_pCurrentPageDesc->GetFollow();
if (m_pCurrentPageDesc->GetLandscape())
Strm().WriteOString(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;
const bool bFakeFirst = m_pCurrentPageDesc != &rPgDsc;
if (bFakeFirst || !m_pCurrentPageDesc->IsFirstShared())
Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_TITLEPG);
// normal header / footer (without a style)
const SfxPoolItem* pItem;
if (m_pCurrentPageDesc->GetLeft().GetAttrSet().GetItemState(RES_HEADER, false, &pItem)
== SfxItemState::SET)
WriteHeaderFooter(*pItem, /*Header*/ true, /*AsTitlePg*/ false, /*WriteFirst*/ !bFakeFirst);
if (m_pCurrentPageDesc->GetLeft().GetAttrSet().GetItemState(RES_FOOTER, false, &pItem)
== SfxItemState::SET)
WriteHeaderFooter(*pItem, false, /*AsTitlePg*/ false, /*WriteFirst*/ !bFakeFirst);
// title page
if (bFakeFirst)
{
m_pCurrentPageDesc = &rPgDsc;
if (m_pCurrentPageDesc->GetMaster().GetAttrSet().GetItemState(RES_HEADER, false, &pItem)
== SfxItemState::SET)
WriteHeaderFooter(*pItem, /*Header*/ true, /*AsTitlePg*/ true, /*WriteFirst*/ false);
if (m_pCurrentPageDesc->GetMaster().GetAttrSet().GetItemState(RES_FOOTER, false, &pItem)
== SfxItemState::SET)
WriteHeaderFooter(*pItem, /*Header*/ false, /*AsTitlePg*/ true, /*WriteFirst*/ false);
}
// numbering type
AttrOutput().SectionPageNumbering(m_pCurrentPageDesc->GetNumType().GetNumberingType(),
std::nullopt);
m_pCurrentPageDesc = pSave;
SAL_INFO("sw.rtf", __func__ << " end");
}
/** WriteHeaderFooter: used to write the initial header and footers
* @param bHeader: true for a header, false for a footer.
* @param bAsTitlePg: used to emulate a first-follow page style linking.
* Set to true to only write this header as if it were a "first header".
* @param bWriteFirst: used to determine whether to write a non-shared first header as the header.
* Set to false if bAsTitlePg will be used to define the "first header".
*/
void RtfExport::WriteHeaderFooter(const SfxPoolItem& rItem, bool bHeader, bool bAsTitlePg,
bool bWriteFirst)
{
assert(!bAsTitlePg || !bWriteFirst);
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");
/* is this a title page? */
if (bAsTitlePg || (bWriteFirst && !m_pCurrentPageDesc->IsFirstShared()))
{
auto pStr = bHeader ? OOO_STRING_SVTOOLS_RTF_HEADERF : OOO_STRING_SVTOOLS_RTF_FOOTERF;
Strm().WriteChar('{').WriteOString(pStr);
WriteHeaderFooterText(m_pCurrentPageDesc->IsFirstShared()
? m_pCurrentPageDesc->GetMaster()
: m_pCurrentPageDesc->GetFirstMaster(),
bHeader);
Strm().WriteChar('}');
}
if (!bAsTitlePg)
{
auto pStr = bHeader ? OOO_STRING_SVTOOLS_RTF_HEADER : OOO_STRING_SVTOOLS_RTF_FOOTER;
Strm().WriteChar('{').WriteOString(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: */
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'ExportDocument' is required to be utilized.
↑ V560 A part of conditional expression is always false: 0 <= result.
↑ V1029 Numeric Truncation Error. Return value of the 'size' function is written to the 16-bit variable.
↑ V1029 Numeric Truncation Error. Return value of the 'size' function is written to the 16-bit variable.