blob: b996446b01eb4032b183937f63b440893a714955 [file] [log] [blame]
/* -*- 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 "rtfattributeoutput.hxx"
#include <memory>
#include <cstring>
#include "rtfsdrexport.hxx"
#include "writerwordglue.hxx"
#include "ww8par.hxx"
#include <fmtcntnt.hxx>
#include <rtl/tencinfo.h>
#include <sal/log.hxx>
#include <sot/exchange.hxx>
#include <svtools/rtfkeywd.hxx>
#include <editeng/fontitem.hxx>
#include <editeng/tstpitem.hxx>
#include <editeng/adjustitem.hxx>
#include <editeng/spltitem.hxx>
#include <editeng/widwitem.hxx>
#include <editeng/postitem.hxx>
#include <editeng/wghtitem.hxx>
#include <editeng/kernitem.hxx>
#include <editeng/crossedoutitem.hxx>
#include <editeng/cmapitem.hxx>
#include <editeng/wrlmitem.hxx>
#include <editeng/udlnitem.hxx>
#include <editeng/langitem.hxx>
#include <editeng/escapementitem.hxx>
#include <editeng/fhgtitem.hxx>
#include <editeng/colritem.hxx>
#include <editeng/hyphenzoneitem.hxx>
#include <editeng/contouritem.hxx>
#include <editeng/shdditem.hxx>
#include <editeng/autokernitem.hxx>
#include <editeng/emphasismarkitem.hxx>
#include <editeng/twolinesitem.hxx>
#include <editeng/charscaleitem.hxx>
#include <editeng/charrotateitem.hxx>
#include <editeng/charreliefitem.hxx>
#include <editeng/paravertalignitem.hxx>
#include <editeng/blinkitem.hxx>
#include <editeng/charhiddenitem.hxx>
#include <editeng/boxitem.hxx>
#include <editeng/brushitem.hxx>
#include <editeng/ulspitem.hxx>
#include <editeng/shaditem.hxx>
#include <editeng/keepitem.hxx>
#include <editeng/frmdiritem.hxx>
#include <editeng/opaqitem.hxx>
#include <o3tl/unit_conversion.hxx>
#include <svx/svdouno.hxx>
#include <filter/msfilter/rtfutil.hxx>
#include <svx/xfillit0.hxx>
#include <svx/xflgrit.hxx>
#include <docufld.hxx>
#include <fmtclds.hxx>
#include <fmtrowsplt.hxx>
#include <fmtline.hxx>
#include <fmtanchr.hxx>
#include <ftninfo.hxx>
#include <htmltbl.hxx>
#include <ndgrf.hxx>
#include <pagedesc.hxx>
#include <swmodule.hxx>
#include <txtftn.hxx>
#include <txtinet.hxx>
#include <grfatr.hxx>
#include <ndole.hxx>
#include <lineinfo.hxx>
#include <redline.hxx>
#include <rtf.hxx>
#include <vcl/cvtgrf.hxx>
#include <oox/drawingml/drawingmltypes.hxx>
#include <oox/mathml/imexport.hxx>
#include <com/sun/star/i18n/ScriptType.hpp>
#include <svl/grabbagitem.hxx>
#include <frmatr.hxx>
#include <swtable.hxx>
#include <formatflysplit.hxx>
#include <fmtwrapinfluenceonobjpos.hxx>
#include "rtfexport.hxx"
#include <IDocumentDeviceAccess.hxx>
#include <sfx2/printer.hxx>
using namespace ::com::sun::star;
using namespace sw::util;
static OString OutTBLBorderLine(RtfExport const& rExport, const editeng::SvxBorderLine* pLine,
const char* pStr)
{
OStringBuffer aRet;
if (pLine && !pLine->isEmpty())
{
aRet.append(pStr);
// single line
switch (pLine->GetBorderLineStyle())
{
case SvxBorderLineStyle::SOLID:
{
if (SvxBorderLineWidth::Hairline == pLine->GetWidth())
aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRHAIR);
else
aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRS);
}
break;
case SvxBorderLineStyle::DOTTED:
aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRDOT);
break;
case SvxBorderLineStyle::DASHED:
aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRDASH);
break;
case SvxBorderLineStyle::DOUBLE:
case SvxBorderLineStyle::DOUBLE_THIN:
aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRDB);
break;
case SvxBorderLineStyle::THINTHICK_SMALLGAP:
aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTNTHSG);
break;
case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTNTHMG);
break;
case SvxBorderLineStyle::THINTHICK_LARGEGAP:
aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTNTHLG);
break;
case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTHTNSG);
break;
case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTHTNMG);
break;
case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTHTNLG);
break;
case SvxBorderLineStyle::EMBOSSED:
aRet.append(OOO_STRING_SVTOOLS_RTF_BRDREMBOSS);
break;
case SvxBorderLineStyle::ENGRAVED:
aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRENGRAVE);
break;
case SvxBorderLineStyle::OUTSET:
aRet.append(OOO_STRING_SVTOOLS_RTF_BRDROUTSET);
break;
case SvxBorderLineStyle::INSET:
aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRINSET);
break;
case SvxBorderLineStyle::FINE_DASHED:
aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRDASHSM);
break;
case SvxBorderLineStyle::DASH_DOT:
aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRDASHD);
break;
case SvxBorderLineStyle::DASH_DOT_DOT:
aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRDASHDD);
break;
case SvxBorderLineStyle::NONE:
default:
aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRNONE);
break;
}
double const fConverted(
::editeng::ConvertBorderWidthToWord(pLine->GetBorderLineStyle(), pLine->GetWidth()));
if (255 >= pLine->GetWidth()) // That value comes from RTF specs
{
aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRW
+ OString::number(static_cast<sal_Int32>(fConverted)));
}
else
{
// use \brdrth to double the value range...
aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRTH OOO_STRING_SVTOOLS_RTF_BRDRW
+ OString::number(static_cast<sal_Int32>(fConverted) / 2));
}
aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRCF
+ OString::number(static_cast<sal_Int32>(rExport.GetColor(pLine->GetColor()))));
}
else // tdf#129758 "no border" may be needed to override style
{
aRet.append(OString::Concat(pStr) + OOO_STRING_SVTOOLS_RTF_BRDRNONE);
}
return aRet.makeStringAndClear();
}
static OString OutBorderLine(RtfExport const& rExport, const editeng::SvxBorderLine* pLine,
const char* pStr, sal_uInt16 nDist,
SvxShadowLocation eShadowLocation = SvxShadowLocation::NONE)
{
OStringBuffer aRet(OutTBLBorderLine(rExport, pLine, pStr));
if (pLine)
{
aRet.append(OOO_STRING_SVTOOLS_RTF_BRSP + OString::number(static_cast<sal_Int32>(nDist)));
}
if (eShadowLocation == SvxShadowLocation::BottomRight)
aRet.append(LO_STRING_SVTOOLS_RTF_BRDRSH);
return aRet.makeStringAndClear();
}
void RtfAttributeOutput::RTLAndCJKState(bool bIsRTL, sal_uInt16 nScript)
{
m_bIsRTL = bIsRTL;
m_nScript = nScript;
m_bControlLtrRtl = true;
}
sal_Int32 RtfAttributeOutput::StartParagraph(ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo,
bool /*bGenerateParaId*/)
{
if (m_bIsBeforeFirstParagraph && m_rExport.m_nTextTyp != TXT_HDFT)
m_bIsBeforeFirstParagraph = false;
// Output table/table row/table cell starts if needed
if (pTextNodeInfo)
{
sal_uInt32 nRow = pTextNodeInfo->getRow();
sal_uInt32 nCell = pTextNodeInfo->getCell();
// New cell/row?
if (m_nTableDepth > 0 && !m_bTableCellOpen)
{
ww8::WW8TableNodeInfoInner::Pointer_t pDeepInner(
pTextNodeInfo->getInnerForDepth(m_nTableDepth));
OSL_ENSURE(pDeepInner, "TableNodeInfoInner not found");
// Make sure we always start a row between ending one and starting a cell.
// In case of subtables, we may not get the first cell.
if (pDeepInner && (pDeepInner->getCell() == 0 || m_bTableRowEnded))
{
StartTableRow(pDeepInner);
}
StartTableCell();
}
// Again, if depth was incremented, start a new table even if we skipped the first cell.
if ((nRow == 0 && nCell == 0) || (m_nTableDepth == 0 && pTextNodeInfo->getDepth()))
{
// Do we have to start the table?
// [If we are at the right depth already, it means that we
// continue the table cell]
sal_uInt32 nCurrentDepth = pTextNodeInfo->getDepth();
if (nCurrentDepth > m_nTableDepth)
{
// Start all the tables that begin here
for (sal_uInt32 nDepth = m_nTableDepth + 1; nDepth <= pTextNodeInfo->getDepth();
++nDepth)
{
ww8::WW8TableNodeInfoInner::Pointer_t pInner(
pTextNodeInfo->getInnerForDepth(nDepth));
m_bLastTable = (nDepth == pTextNodeInfo->getDepth());
StartTable();
StartTableRow(pInner);
StartTableCell();
}
m_nTableDepth = nCurrentDepth;
}
}
}
OSL_ENSURE(m_aRun.getLength() == 0, "m_aRun is not empty");
return 0;
}
void RtfAttributeOutput::EndParagraph(ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner)
{
bool bLastPara = false;
if (m_rExport.m_nTextTyp == TXT_FTN || m_rExport.m_nTextTyp == TXT_EDN
|| m_rExport.m_rDoc.IsClipBoard())
{
// We're ending a paragraph that is the last paragraph of a footnote or endnote, or of clipboard.
bLastPara
= m_rExport.GetCurrentNodeIndex()
&& m_rExport.GetCurrentNodeIndex() == m_rExport.m_pCurPam->End()->GetNodeIndex();
}
FinishTableRowCell(pTextNodeInfoInner);
RtfStringBuffer aParagraph;
aParagraph.appendAndClear(m_aRun);
aParagraph->append(m_aAfterRuns);
m_aAfterRuns.setLength(0);
if (m_bTableAfterCell)
m_bTableAfterCell = false;
else
{
aParagraph->append(SAL_NEWLINE_STRING);
// RTF_PAR at the end of the footnote or clipboard, would cause an additional empty paragraph.
if (!bLastPara)
{
aParagraph->append(OOO_STRING_SVTOOLS_RTF_PAR);
aParagraph->append(' ');
}
}
if (m_nColBreakNeeded)
{
aParagraph->append(OOO_STRING_SVTOOLS_RTF_COLUMN);
m_nColBreakNeeded = false;
}
if (!m_bBufferSectionHeaders)
aParagraph.makeStringAndClear(this);
else
m_aSectionHeaders.append(aParagraph.makeStringAndClear());
}
void RtfAttributeOutput::EmptyParagraph()
{
m_rExport.Strm()
.WriteOString(SAL_NEWLINE_STRING)
.WriteOString(OOO_STRING_SVTOOLS_RTF_PAR)
.WriteChar(' ');
}
void RtfAttributeOutput::SectionBreaks(const SwNode& rNode)
{
SwNodeIndex aNextIndex(rNode, 1);
if (rNode.IsTextNode())
{
OSL_ENSURE(m_aStyles.getLength() == 0, "m_aStyles is not empty");
// output page/section breaks
m_rExport.Strm().WriteOString(m_aSectionBreaks);
m_aSectionBreaks.setLength(0);
m_bBufferSectionBreaks = true;
// output section headers / footers
if (!m_bBufferSectionHeaders)
{
m_rExport.Strm().WriteOString(m_aSectionHeaders);
m_aSectionHeaders.setLength(0);
}
if (aNextIndex.GetNode().IsTextNode())
{
const SwTextNode* pTextNode = static_cast<SwTextNode*>(&aNextIndex.GetNode());
m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode);
// Save the current page description for now, so later we will be able to access the previous one.
m_pPrevPageDesc = pTextNode->FindPageDesc();
}
else if (aNextIndex.GetNode().IsTableNode())
{
const SwTableNode* pTableNode = static_cast<SwTableNode*>(&aNextIndex.GetNode());
const SwFrameFormat* pFormat = pTableNode->GetTable().GetFrameFormat();
m_rExport.OutputSectionBreaks(&(pFormat->GetAttrSet()), *pTableNode);
}
m_bBufferSectionBreaks = false;
}
else if (rNode.IsEndNode())
{
// End of something: make sure that it's the end of a table.
assert(rNode.StartOfSectionNode()->IsTableNode());
if (aNextIndex.GetNode().IsTextNode())
{
// Handle section break between a table and a text node following it.
const SwTextNode* pTextNode = aNextIndex.GetNode().GetTextNode();
m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode);
}
}
}
void RtfAttributeOutput::StartParagraphProperties()
{
OStringBuffer aPar;
if (!m_rExport.GetRTFFlySyntax())
{
aPar.append(OOO_STRING_SVTOOLS_RTF_PARD OOO_STRING_SVTOOLS_RTF_PLAIN " ");
}
if (!m_bBufferSectionHeaders)
m_rExport.Strm().WriteOString(aPar);
else
m_aSectionHeaders.append(aPar);
}
void RtfAttributeOutput::EndParagraphProperties(
const SfxItemSet& /*rParagraphMarkerProperties*/, const SwRedlineData* /*pRedlineData*/,
const SwRedlineData* /*pRedlineParagraphMarkerDeleted*/,
const SwRedlineData* /*pRedlineParagraphMarkerInserted*/)
{
// Do not call MoveCharacterProperties(),
// Otherwise associate properties in the paragraph style are ruined.
const OString aProperties = m_aStyles.makeStringAndClear();
m_rExport.Strm().WriteOString(aProperties);
}
void RtfAttributeOutput::StartRun(const SwRedlineData* pRedlineData, sal_Int32 /*nPos*/,
bool bSingleEmptyRun)
{
SAL_INFO("sw.rtf", __func__ << ", bSingleEmptyRun: " << bSingleEmptyRun);
m_bInRun = true;
m_bSingleEmptyRun = bSingleEmptyRun;
if (!m_bSingleEmptyRun)
m_aRun->append('{');
// if there is some redlining in the document, output it
Redline(pRedlineData);
OSL_ENSURE(m_aRunText.getLength() == 0, "m_aRunText is not empty");
}
void RtfAttributeOutput::EndRun(const SwTextNode* /*pNode*/, sal_Int32 /*nPos*/, sal_Int32 /*nLen*/,
bool /*bLastRun*/)
{
m_aRun->append(SAL_NEWLINE_STRING);
m_aRun.appendAndClear(m_aRunText);
if (m_bInRuby)
{
m_aRun->append(")}}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " {}}}");
m_bInRuby = false;
}
if (!m_bSingleEmptyRun && m_bInRun)
m_aRun->append('}');
m_bInRun = false;
}
void RtfAttributeOutput::StartRunProperties()
{
OSL_ENSURE(m_aStyles.getLength() == 0, "m_aStyles is not empty");
}
void RtfAttributeOutput::EndRunProperties(const SwRedlineData* /*pRedlineData*/)
{
const OString aProperties = MoveCharacterProperties(true);
m_aRun->append(aProperties);
}
OString RtfAttributeOutput::MoveCharacterProperties(bool aAutoWriteRtlLtr)
{
const OString aAssocHich = m_aStylesAssocHich.makeStringAndClear();
const OString aAssocDbch = m_aStylesAssocDbch.makeStringAndClear();
const OString aAssocRtlch = m_aStylesAssocRtlch.makeStringAndClear();
const OString aAssocLtrch = m_aStylesAssocLtrch.makeStringAndClear();
const OString aNormal = m_aStyles.makeStringAndClear();
OStringBuffer aBuf;
if (aAutoWriteRtlLtr && !m_bControlLtrRtl)
{
m_bControlLtrRtl = !aAssocRtlch.isEmpty();
m_bIsRTL = false;
m_nScript = i18n::ScriptType::LATIN;
}
if (m_bIsRTL)
{
if (!aAssocRtlch.isEmpty())
{
aBuf.append(OOO_STRING_SVTOOLS_RTF_LTRCH + aAssocLtrch
+ " " OOO_STRING_SVTOOLS_RTF_RTLCH + aAssocRtlch);
}
}
else
{
if (!aAssocRtlch.isEmpty())
{
aBuf.append(OOO_STRING_SVTOOLS_RTF_RTLCH + aAssocRtlch
+ " " OOO_STRING_SVTOOLS_RTF_LTRCH + aAssocLtrch);
}
if (!aAssocHich.isEmpty())
{
aBuf.append(OOO_STRING_SVTOOLS_RTF_HICH + aAssocHich);
}
if (!aNormal.isEmpty())
{
aBuf.append(OOO_STRING_SVTOOLS_RTF_LOCH + aNormal);
}
if (!aAssocDbch.isEmpty())
{
aBuf.append(OOO_STRING_SVTOOLS_RTF_DBCH + aAssocDbch);
}
}
if (m_bControlLtrRtl)
{
m_bControlLtrRtl = false;
switch (m_nScript)
{
case i18n::ScriptType::LATIN:
aBuf.append(OOO_STRING_SVTOOLS_RTF_LOCH);
break;
case i18n::ScriptType::ASIAN:
aBuf.append(OOO_STRING_SVTOOLS_RTF_DBCH);
break;
case i18n::ScriptType::COMPLEX:
/* noop */
default:
/* should not happen? */
break;
}
}
return aBuf.makeStringAndClear();
}
void RtfAttributeOutput::RunText(const OUString& rText, rtl_TextEncoding /*eCharSet*/,
const OUString& /*rSymbolFont*/)
{
SAL_INFO("sw.rtf", __func__ << ", rText: " << rText);
RawText(rText, m_rExport.GetCurrentEncoding());
}
OStringBuffer& RtfAttributeOutput::RunText() { return m_aRunText.getLastBuffer(); }
void RtfAttributeOutput::RawText(const OUString& rText, rtl_TextEncoding eCharSet)
{
m_aRunText->append(msfilter::rtfutil::OutString(rText, eCharSet));
}
void RtfAttributeOutput::StartRuby(const SwTextNode& rNode, sal_Int32 /*nPos*/,
const SwFormatRuby& rRuby)
{
WW8Ruby aWW8Ruby(rNode, rRuby, GetExport());
OUString aStr = FieldString(ww::eEQ) + "\\* jc" + OUString::number(aWW8Ruby.GetJC())
+ " \\* \"Font:" + aWW8Ruby.GetFontFamily() + "\" \\* hps"
+ OUString::number((aWW8Ruby.GetRubyHeight() + 5) / 10) + " \\o";
if (aWW8Ruby.GetDirective())
{
aStr += "\\a" + OUStringChar(aWW8Ruby.GetDirective());
}
aStr += "(\\s\\up " + OUString::number((aWW8Ruby.GetBaseHeight() + 10) / 20 - 1) + "(";
m_rExport.OutputField(nullptr, ww::eEQ, aStr, FieldFlags::Start | FieldFlags::CmdStart);
aStr = rRuby.GetText() + "),";
m_rExport.OutputField(nullptr, ww::eEQ, aStr, FieldFlags::NONE);
m_bInRuby = true;
}
void RtfAttributeOutput::EndRuby(const SwTextNode& /*rNode*/, sal_Int32 /*nPos*/) {}
bool RtfAttributeOutput::StartURL(const OUString& rUrl, const OUString& rTarget)
{
m_aURLs.push(rUrl);
// Ignore hyperlink without a URL.
if (!rUrl.isEmpty())
{
m_aRun->append('{');
m_aRun->append(OOO_STRING_SVTOOLS_RTF_FIELD);
m_aRun->append('{');
m_aRun->append(OOO_STRING_SVTOOLS_RTF_IGNORE);
m_aRun->append(OOO_STRING_SVTOOLS_RTF_FLDINST);
m_aRun->append(" HYPERLINK ");
m_aRun->append("\"");
m_aRun->append(msfilter::rtfutil::OutString(rUrl, m_rExport.GetCurrentEncoding()));
m_aRun->append("\" ");
// Adding the target is likely a LO embellishment.
// Don't export it to clipboard, since editeng and other RTF readers won't understand it.
if (!rTarget.isEmpty() && !m_rExport.m_rDoc.IsClipBoard())
{
m_aRun->append("\\\\t \"");
m_aRun->append(msfilter::rtfutil::OutString(rTarget, m_rExport.GetCurrentEncoding()));
m_aRun->append("\" ");
}
m_aRun->append("}");
m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " {");
}
return true;
}
bool RtfAttributeOutput::EndURL(bool const isAtEndOfParagraph)
{
if (m_aURLs.empty())
{
return true;
}
const OUString& rURL = m_aURLs.top();
if (!rURL.isEmpty())
{
// UGLY: usually EndRun is called earlier, but there is an extra
// call to OutAttrWithRange() when at the end of the paragraph,
// so in that special case the output needs to be appended to the
// new run's text instead of the previous run
if (isAtEndOfParagraph)
{
// close the fldrslt group
m_aRunText->append("}}");
// close the field group
m_aRunText->append('}');
}
else
{
// close the fldrslt group
m_aRun->append("}}");
// close the field group
m_aRun->append('}');
}
}
m_aURLs.pop();
return true;
}
void RtfAttributeOutput::FieldVanish(const OUString& /*rText*/, ww::eField /*eType*/,
OUString const* /*pBookmarkName*/)
{
SAL_INFO("sw.rtf", "TODO: " << __func__);
}
void RtfAttributeOutput::Redline(const SwRedlineData* pRedline)
{
if (!pRedline)
return;
if (pRedline->GetType() == RedlineType::Insert)
{
m_aRun->append(OOO_STRING_SVTOOLS_RTF_REVISED);
m_aRun->append(OOO_STRING_SVTOOLS_RTF_REVAUTH);
m_aRun->append(static_cast<sal_Int32>(
m_rExport.GetRedline(SW_MOD()->GetRedlineAuthor(pRedline->GetAuthor()))));
m_aRun->append(OOO_STRING_SVTOOLS_RTF_REVDTTM);
}
else if (pRedline->GetType() == RedlineType::Delete)
{
m_aRun->append(OOO_STRING_SVTOOLS_RTF_DELETED);
m_aRun->append(OOO_STRING_SVTOOLS_RTF_REVAUTHDEL);
m_aRun->append(static_cast<sal_Int32>(
m_rExport.GetRedline(SW_MOD()->GetRedlineAuthor(pRedline->GetAuthor()))));
m_aRun->append(OOO_STRING_SVTOOLS_RTF_REVDTTMDEL);
}
m_aRun->append(static_cast<sal_Int32>(sw::ms::DateTime2DTTM(pRedline->GetTimeStamp())));
m_aRun->append(' ');
}
void RtfAttributeOutput::FormatDrop(const SwTextNode& /*rNode*/,
const SwFormatDrop& /*rSwFormatDrop*/, sal_uInt16 /*nStyle*/,
ww8::WW8TableNodeInfo::Pointer_t /*pTextNodeInfo*/,
ww8::WW8TableNodeInfoInner::Pointer_t /*pTextNodeInfoInner*/)
{
SAL_INFO("sw.rtf", "TODO: " << __func__);
}
void RtfAttributeOutput::ParagraphStyle(sal_uInt16 nStyle)
{
OString* pStyle = m_rExport.GetStyle(nStyle);
OStringBuffer aStyle(OOO_STRING_SVTOOLS_RTF_S
+ OString::number(static_cast<sal_Int32>(nStyle)));
if (pStyle)
aStyle.append(*pStyle);
if (!m_bBufferSectionHeaders)
m_rExport.Strm().WriteOString(aStyle);
else
m_aSectionHeaders.append(aStyle);
}
void RtfAttributeOutput::TableInfoCell(
const ww8::WW8TableNodeInfoInner::Pointer_t& /*pTableTextNodeInfoInner*/)
{
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_INTBL);
if (m_nTableDepth > 1)
{
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ITAP);
m_aStyles.append(static_cast<sal_Int32>(m_nTableDepth));
}
m_bWroteCellInfo = true;
}
void RtfAttributeOutput::TableInfoRow(
const ww8::WW8TableNodeInfoInner::Pointer_t& /*pTableTextNodeInfo*/)
{
/* noop */
}
void RtfAttributeOutput::TablePositioning(SwFrameFormat* pFlyFormat)
{
if (!pFlyFormat || !pFlyFormat->GetFlySplit().GetValue())
{
return;
}
switch (pFlyFormat->GetVertOrient().GetRelationOrient())
{
case text::RelOrientation::PAGE_PRINT_AREA:
// relative to margin
m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPVMRG);
break;
case text::RelOrientation::PAGE_FRAME:
// relative to page
m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPVPG);
break;
default:
// text::RelOrientation::FRAME
// relative to text
m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPVPARA);
break;
}
switch (pFlyFormat->GetHoriOrient().GetRelationOrient())
{
case text::RelOrientation::FRAME:
// relative to column
m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPHCOL);
break;
case text::RelOrientation::PAGE_PRINT_AREA:
// relative to margin
m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPHMRG);
break;
default:
// text::RelOrientation::PAGE_FRAME
// relative to page
m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPHPG);
break;
}
// Similar to RtfAttributeOutput::FormatHorizOrientation(), but for tables.
switch (pFlyFormat->GetHoriOrient().GetHoriOrient())
{
case text::HoriOrientation::LEFT:
// left
m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPOSXL);
break;
case text::HoriOrientation::CENTER:
// centered
m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPOSXC);
break;
case text::HoriOrientation::RIGHT:
// right
m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPOSXR);
break;
default:
SwTwips nTPosX = pFlyFormat->GetHoriOrient().GetPos();
m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPOSX);
m_aRowDefs.append(static_cast<sal_Int32>(nTPosX));
break;
}
// Similar to RtfAttributeOutput::FormatVertOrientation(), but for tables.
switch (pFlyFormat->GetVertOrient().GetVertOrient())
{
case text::VertOrientation::TOP:
// up
m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPOSYT);
break;
case text::VertOrientation::CENTER:
// centered
m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPOSYC);
break;
case text::VertOrientation::BOTTOM:
// down
m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPOSYB);
break;
default:
SwTwips nTPosY = pFlyFormat->GetVertOrient().GetPos();
m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TPOSY);
m_aRowDefs.append(static_cast<sal_Int32>(nTPosY));
break;
}
// Similar to RtfAttributeOutput::FormatULSpace(), but for tables.
sal_uInt16 nTdfrmtxtTop = pFlyFormat->GetULSpace().GetUpper();
m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TDFRMTXTTOP);
m_aRowDefs.append(static_cast<sal_Int32>(nTdfrmtxtTop));
sal_uInt16 nTdfrmtxtBottom = pFlyFormat->GetULSpace().GetLower();
m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TDFRMTXTBOTTOM);
m_aRowDefs.append(static_cast<sal_Int32>(nTdfrmtxtBottom));
// Similar to RtfAttributeOutput::FormatLRSpace(), but for tables.
sal_uInt16 nTdfrmtxtLeft = pFlyFormat->GetLRSpace().GetLeft();
m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TDFRMTXTLEFT);
m_aRowDefs.append(static_cast<sal_Int32>(nTdfrmtxtLeft));
sal_uInt16 nTdfrmtxtRight = pFlyFormat->GetLRSpace().GetRight();
m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TDFRMTXTRIGHT);
m_aRowDefs.append(static_cast<sal_Int32>(nTdfrmtxtRight));
if (!pFlyFormat->GetWrapInfluenceOnObjPos().GetAllowOverlap())
{
// Allowing overlap is the default in both Writer and in RTF.
m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TABSNOOVRLP);
m_aRowDefs.append(static_cast<sal_Int32>(1));
}
}
void RtfAttributeOutput::TableDefinition(
const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
{
InitTableHelper(pTableTextNodeInfoInner);
const SwTable* pTable = pTableTextNodeInfoInner->getTable();
SwFrameFormat* pFormat = pTable->GetFrameFormat();
m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_TROWD);
TableOrientation(pTableTextNodeInfoInner);
TableBidi(pTableTextNodeInfoInner);
TableHeight(pTableTextNodeInfoInner);
TableCanSplit(pTableTextNodeInfoInner);
// Write table positioning properties in case this is a floating table.
TablePositioning(pTable->GetTableNode()->GetFlyFormat());
// Cell margins
const SvxBoxItem& rBox = pFormat->GetBox();
static const SvxBoxItemLine aBorders[] = { SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT };
static const char* aRowPadNames[]
= { OOO_STRING_SVTOOLS_RTF_TRPADDT, OOO_STRING_SVTOOLS_RTF_TRPADDL,
OOO_STRING_SVTOOLS_RTF_TRPADDB, OOO_STRING_SVTOOLS_RTF_TRPADDR };
static const char* aRowPadUnits[]
= { OOO_STRING_SVTOOLS_RTF_TRPADDFT, OOO_STRING_SVTOOLS_RTF_TRPADDFL,
OOO_STRING_SVTOOLS_RTF_TRPADDFB, OOO_STRING_SVTOOLS_RTF_TRPADDFR };
for (int i = 0; i < 4; ++i)
{
m_aRowDefs.append(aRowPadUnits[i]);
m_aRowDefs.append(sal_Int32(3));
m_aRowDefs.append(aRowPadNames[i]);
m_aRowDefs.append(static_cast<sal_Int32>(rBox.GetDistance(aBorders[i])));
}
// The cell-dependent properties
const double fWidthRatio = m_pTableWrt->GetAbsWidthRatio();
const SwWriteTableRows& aRows = m_pTableWrt->GetRows();
sal_uInt32 nRow = pTableTextNodeInfoInner->getRow();
if (nRow >= aRows.size())
{
SAL_WARN("sw.ww8", "RtfAttributeOutput::TableDefinition: out of range row: " << nRow);
return;
}
SwWriteTableRow* pRow = aRows[nRow].get();
SwTwips nSz = 0;
// Not using m_nTableDepth, which is not yet incremented here.
sal_uInt32 nCurrentDepth = pTableTextNodeInfoInner->getDepth();
m_aCells[nCurrentDepth] = pRow->GetCells().size();
for (sal_uInt32 i = 0; i < m_aCells[nCurrentDepth]; i++)
{
const SwWriteTableCell* const pCell = pRow->GetCells()[i].get();
const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat();
pTableTextNodeInfoInner->setCell(i);
TableCellProperties(pTableTextNodeInfoInner);
// Right boundary: this can't be in TableCellProperties as the old
// value of nSz is needed.
nSz += pCellFormat->GetFrameSize().GetWidth();
m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CELLX);
m_aRowDefs.append(static_cast<sal_Int32>(pFormat->GetLRSpace().GetLeft()
+ rtl::math::round(nSz * fWidthRatio)));
}
}
void RtfAttributeOutput::TableDefaultBorders(
const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
{
/*
* The function name is a bit misleading: given that we write borders
* before each row, we just have borders, not default ones. Additionally,
* this function actually writes borders for a specific cell only and is
* called for each cell.
*/
const SwWriteTableRows& aRows = m_pTableWrt->GetRows();
SwWriteTableRow* pRow = aRows[pTableTextNodeInfoInner->getRow()].get();
const SwWriteTableCell* const pCell
= pRow->GetCells()[pTableTextNodeInfoInner->getCell()].get();
const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat();
const SvxBoxItem* pItem = pCellFormat->GetAttrSet().GetItemIfSet(RES_BOX);
if (!pItem)
return;
auto& rBox = *pItem;
static const SvxBoxItemLine aBorders[] = { SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT };
static const char* aBorderNames[]
= { OOO_STRING_SVTOOLS_RTF_CLBRDRT, OOO_STRING_SVTOOLS_RTF_CLBRDRL,
OOO_STRING_SVTOOLS_RTF_CLBRDRB, OOO_STRING_SVTOOLS_RTF_CLBRDRR };
//Yes left and top are swapped with each other for cell padding! Because
//that's what the thundering annoying rtf export/import word xp does.
static const char* aCellPadNames[]
= { OOO_STRING_SVTOOLS_RTF_CLPADL, OOO_STRING_SVTOOLS_RTF_CLPADT,
OOO_STRING_SVTOOLS_RTF_CLPADB, OOO_STRING_SVTOOLS_RTF_CLPADR };
static const char* aCellPadUnits[]
= { OOO_STRING_SVTOOLS_RTF_CLPADFL, OOO_STRING_SVTOOLS_RTF_CLPADFT,
OOO_STRING_SVTOOLS_RTF_CLPADFB, OOO_STRING_SVTOOLS_RTF_CLPADFR };
for (int i = 0; i < 4; ++i)
{
if (const editeng::SvxBorderLine* pLn = rBox.GetLine(aBorders[i]))
m_aRowDefs.append(OutTBLBorderLine(m_rExport, pLn, aBorderNames[i]));
if (rBox.GetDistance(aBorders[i]))
{
m_aRowDefs.append(aCellPadUnits[i]);
m_aRowDefs.append(sal_Int32(3));
m_aRowDefs.append(aCellPadNames[i]);
m_aRowDefs.append(static_cast<sal_Int32>(rBox.GetDistance(aBorders[i])));
}
}
}
void RtfAttributeOutput::TableBackgrounds(
const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
{
const SwTable* pTable = pTableTextNodeInfoInner->getTable();
const SwTableBox* pTableBox = pTableTextNodeInfoInner->getTableBox();
const SwTableLine* pTableLine = pTableBox->GetUpper();
Color aColor = COL_AUTO;
auto pTableColorProp
= pTable->GetFrameFormat()->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
if (pTableColorProp)
aColor = pTableColorProp->GetColor();
auto pRowColorProp
= pTableLine->GetFrameFormat()->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
if (pRowColorProp && pRowColorProp->GetColor() != COL_AUTO)
aColor = pRowColorProp->GetColor();
const SwWriteTableRows& aRows = m_pTableWrt->GetRows();
SwWriteTableRow* pRow = aRows[pTableTextNodeInfoInner->getRow()].get();
const SwWriteTableCell* const pCell
= pRow->GetCells()[pTableTextNodeInfoInner->getCell()].get();
const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat();
if (const SvxBrushItem* pBrushItem = pCellFormat->GetAttrSet().GetItemIfSet(RES_BACKGROUND))
{
if (pBrushItem->GetColor() != COL_AUTO)
aColor = pBrushItem->GetColor();
}
if (!aColor.IsTransparent())
{
m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLCBPAT);
m_aRowDefs.append(static_cast<sal_Int32>(m_rExport.GetColor(aColor)));
}
}
void RtfAttributeOutput::TableRowRedline(
const ww8::WW8TableNodeInfoInner::Pointer_t& /*pTableTextNodeInfoInner*/)
{
}
void RtfAttributeOutput::TableCellRedline(
const ww8::WW8TableNodeInfoInner::Pointer_t& /*pTableTextNodeInfoInner*/)
{
}
void RtfAttributeOutput::TableHeight(
const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
{
const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
const SwTableLine* pTabLine = pTabBox->GetUpper();
const SwFrameFormat* pLineFormat = pTabLine->GetFrameFormat();
const SwFormatFrameSize& rLSz = pLineFormat->GetFrameSize();
if (!(SwFrameSize::Variable != rLSz.GetHeightSizeType() && rLSz.GetHeight()))
return;
sal_Int32 nHeight = 0;
switch (rLSz.GetHeightSizeType())
{
case SwFrameSize::Fixed:
nHeight = -rLSz.GetHeight();
break;
case SwFrameSize::Minimum:
nHeight = rLSz.GetHeight();
break;
default:
break;
}
if (nHeight)
{
m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_TRRH);
m_aRowDefs.append(nHeight);
}
}
void RtfAttributeOutput::TableCanSplit(
const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
{
const SwTableBox* pTabBox = pTableTextNodeInfoInner->getTableBox();
const SwTableLine* pTabLine = pTabBox->GetUpper();
const SwFrameFormat* pLineFormat = pTabLine->GetFrameFormat();
const SwFormatRowSplit& rSplittable = pLineFormat->GetRowSplit();
// The rtf default is to allow a row to break
if (!rSplittable.GetValue())
m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_TRKEEP);
}
void RtfAttributeOutput::TableBidi(
const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
{
const SwTable* pTable = pTableTextNodeInfoInner->getTable();
const SwFrameFormat* pFrameFormat = pTable->GetFrameFormat();
if (m_rExport.TrueFrameDirection(*pFrameFormat) != SvxFrameDirection::Horizontal_RL_TB)
m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_LTRROW);
else
m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_RTLROW);
}
void RtfAttributeOutput::TableVerticalCell(
const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
{
const SwWriteTableRows& aRows = m_pTableWrt->GetRows();
SwWriteTableRow* pRow = aRows[pTableTextNodeInfoInner->getRow()].get();
const SwWriteTableCell* const pCell
= pRow->GetCells()[pTableTextNodeInfoInner->getCell()].get();
const SwFrameFormat* pCellFormat = pCell->GetBox()->GetFrameFormat();
// Text direction.
if (SvxFrameDirection::Vertical_RL_TB == m_rExport.TrueFrameDirection(*pCellFormat))
m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLTXTBRL);
else if (SvxFrameDirection::Vertical_LR_BT == m_rExport.TrueFrameDirection(*pCellFormat))
m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLTXBTLR);
// vertical merges
if (pCell->GetRowSpan() > 1)
m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLVMGF);
else if (pCell->GetRowSpan() == 0)
m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLVMRG);
// vertical alignment
const SwFormatVertOrient* pVertOrientItem
= pCellFormat->GetAttrSet().GetItemIfSet(RES_VERT_ORIENT);
if (!pVertOrientItem)
return;
switch (pVertOrientItem->GetVertOrient())
{
case text::VertOrientation::CENTER:
m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLVERTALC);
break;
case text::VertOrientation::BOTTOM:
m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLVERTALB);
break;
default:
m_aRowDefs.append(OOO_STRING_SVTOOLS_RTF_CLVERTALT);
break;
}
}
void RtfAttributeOutput::TableNodeInfoInner(
const ww8::WW8TableNodeInfoInner::Pointer_t& pNodeInfoInner)
{
// This is called when the nested table ends in a cell, and there's no
// paragraph behind that; so we must check for the ends of cell, rows,
// and tables
FinishTableRowCell(pNodeInfoInner);
}
void RtfAttributeOutput::TableOrientation(
const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
{
const SwTable* pTable = pTableTextNodeInfoInner->getTable();
SwFrameFormat* pFormat = pTable->GetFrameFormat();
OStringBuffer aTableAdjust(OOO_STRING_SVTOOLS_RTF_TRQL);
switch (pFormat->GetHoriOrient().GetHoriOrient())
{
case text::HoriOrientation::CENTER:
aTableAdjust.setLength(0);
aTableAdjust.append(OOO_STRING_SVTOOLS_RTF_TRQC);
break;
case text::HoriOrientation::RIGHT:
aTableAdjust.setLength(0);
aTableAdjust.append(OOO_STRING_SVTOOLS_RTF_TRQR);
break;
case text::HoriOrientation::NONE:
case text::HoriOrientation::LEFT_AND_WIDTH:
aTableAdjust.append(OOO_STRING_SVTOOLS_RTF_TRLEFT);
aTableAdjust.append(static_cast<sal_Int32>(pFormat->GetLRSpace().GetLeft()));
break;
default:
break;
}
m_aRowDefs.append(aTableAdjust);
}
void RtfAttributeOutput::TableSpacing(
const ww8::WW8TableNodeInfoInner::Pointer_t& /*pTableTextNodeInfoInner*/)
{
SAL_INFO("sw.rtf", "TODO: " << __func__);
}
void RtfAttributeOutput::TableRowEnd(sal_uInt32 /*nDepth*/) { /* noop, see EndTableRow() */}
/*
* Our private table methods.
*/
void RtfAttributeOutput::InitTableHelper(
const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
{
const SwTable* pTable = pTableTextNodeInfoInner->getTable();
if (m_pTableWrt && pTable == m_pTableWrt->GetTable())
return;
tools::Long nPageSize = 0;
bool bRelBoxSize = false;
// Create the SwWriteTable instance to use col spans
GetTablePageSize(pTableTextNodeInfoInner.get(), nPageSize, bRelBoxSize);
const SwFrameFormat* pFormat = pTable->GetFrameFormat();
const sal_uInt32 nTableSz = pFormat->GetFrameSize().GetWidth();
const SwHTMLTableLayout* pLayout = pTable->GetHTMLTableLayout();
if (pLayout && pLayout->IsExportable())
m_pTableWrt = std::make_unique<SwWriteTable>(pTable, pLayout);
else
m_pTableWrt = std::make_unique<SwWriteTable>(pTable, pTable->GetTabLines(), nPageSize,
nTableSz, false);
}
void RtfAttributeOutput::StartTable()
{
// To trigger calling InitTableHelper()
m_pTableWrt.reset();
}
void RtfAttributeOutput::StartTableRow(
const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
{
sal_uInt32 nCurrentDepth = pTableTextNodeInfoInner->getDepth();
SAL_INFO("sw.rtf", __func__ << ", (depth is " << nCurrentDepth << ")");
m_bTableRowEnded = false;
TableDefinition(pTableTextNodeInfoInner);
if (!m_bLastTable)
m_aTables.push_back(m_aRowDefs.makeStringAndClear());
// We'll write the table definition for nested tables later
if (nCurrentDepth > 1)
return;
// Empty the previous row closing buffer before starting the new one,
// necessary for subtables.
m_rExport.Strm().WriteOString(m_aAfterRuns);
m_aAfterRuns.setLength(0);
m_rExport.Strm().WriteOString(m_aRowDefs);
m_aRowDefs.setLength(0);
}
void RtfAttributeOutput::StartTableCell() { m_bTableCellOpen = true; }
void RtfAttributeOutput::TableCellProperties(
const ww8::WW8TableNodeInfoInner::Pointer_t& pTableTextNodeInfoInner)
{
TableDefaultBorders(pTableTextNodeInfoInner);
TableBackgrounds(pTableTextNodeInfoInner);
TableVerticalCell(pTableTextNodeInfoInner);
}
void RtfAttributeOutput::EndTableCell()
{
SAL_INFO("sw.rtf", __func__ << ", (depth is " << m_nTableDepth << ")");
if (!m_bWroteCellInfo)
{
m_aAfterRuns.append(OOO_STRING_SVTOOLS_RTF_INTBL);
m_aAfterRuns.append(OOO_STRING_SVTOOLS_RTF_ITAP);
m_aAfterRuns.append(static_cast<sal_Int32>(m_nTableDepth));
}
if (m_nTableDepth > 1)
m_aAfterRuns.append(OOO_STRING_SVTOOLS_RTF_NESTCELL);
else
m_aAfterRuns.append(OOO_STRING_SVTOOLS_RTF_CELL);
m_bTableCellOpen = false;
m_bTableAfterCell = true;
m_bWroteCellInfo = false;
if (m_aCells[m_nTableDepth] > 0)
m_aCells[m_nTableDepth]--;
}
void RtfAttributeOutput::EndTableRow()
{
SAL_INFO("sw.rtf", __func__ << ", (depth is " << m_nTableDepth << ")");
if (m_nTableDepth > 1)
{
m_aAfterRuns.append(
"{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_NESTTABLEPROPRS);
if (!m_aRowDefs.isEmpty())
{
m_aAfterRuns.append(m_aRowDefs);
m_aRowDefs.setLength(0);
}
else if (!m_aTables.empty())
{
m_aAfterRuns.append(m_aTables.back());
m_aTables.pop_back();
}
m_aAfterRuns.append(OOO_STRING_SVTOOLS_RTF_NESTROW
"}"
"{" OOO_STRING_SVTOOLS_RTF_NONESTTABLES OOO_STRING_SVTOOLS_RTF_PAR "}");
}
else
{
if (!m_aTables.empty())
{
m_aAfterRuns.append(m_aTables.back());
m_aTables.pop_back();
}
// Make sure that the first word of the next paragraph is not merged with the last control
// word of this table row, happens with floating tables.
m_aAfterRuns.append(OOO_STRING_SVTOOLS_RTF_ROW OOO_STRING_SVTOOLS_RTF_PARD " ");
}
m_bTableRowEnded = true;
}
void RtfAttributeOutput::EndTable()
{
if (m_nTableDepth > 0)
{
m_nTableDepth--;
m_pTableWrt.reset();
}
// We closed the table; if it is a nested table, the cell that contains it
// still continues
m_bTableCellOpen = true;
// Cleans the table helper
m_pTableWrt.reset();
}
void RtfAttributeOutput::FinishTableRowCell(const ww8::WW8TableNodeInfoInner::Pointer_t& pInner)
{
if (!pInner)
return;
// Where are we in the table
sal_uInt32 nRow = pInner->getRow();
const SwTable* pTable = pInner->getTable();
const SwTableLines& rLines = pTable->GetTabLines();
sal_uInt16 nLinesCount = rLines.size();
if (pInner->isEndOfCell())
EndTableCell();
// This is a line end
if (pInner->isEndOfLine())
EndTableRow();
// This is the end of the table
if (pInner->isEndOfLine() && (nRow + 1) == nLinesCount)
EndTable();
}
void RtfAttributeOutput::StartStyles()
{
m_rExport.Strm()
.WriteOString(SAL_NEWLINE_STRING)
.WriteChar('{')
.WriteOString(OOO_STRING_SVTOOLS_RTF_COLORTBL);
m_rExport.OutColorTable();
OSL_ENSURE(m_aStylesheet.getLength() == 0, "m_aStylesheet is not empty");
m_aStylesheet.append(SAL_NEWLINE_STRING);
m_aStylesheet.append('{');
m_aStylesheet.append(OOO_STRING_SVTOOLS_RTF_STYLESHEET);
}
void RtfAttributeOutput::EndStyles(sal_uInt16 /*nNumberOfStyles*/)
{
m_rExport.Strm().WriteChar('}');
m_rExport.Strm().WriteOString(m_aStylesheet);
m_aStylesheet.setLength(0);
m_rExport.Strm().WriteChar('}');
}
void RtfAttributeOutput::DefaultStyle() { /* noop, the default style is always 0 in RTF */}
void RtfAttributeOutput::StartStyle(const OUString& rName, StyleType eType, sal_uInt16 nBase,
sal_uInt16 nNext, sal_uInt16 /*nLink*/, sal_uInt16 /*nWwId*/,
sal_uInt16 nSlot, bool bAutoUpdate)
{
SAL_INFO("sw.rtf", __func__ << ", rName = '" << rName << "'");
m_aStylesheet.append('{');
if (eType == STYLE_TYPE_PARA)
m_aStylesheet.append(OOO_STRING_SVTOOLS_RTF_S);
else
m_aStylesheet.append(OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_CS);
m_aStylesheet.append(static_cast<sal_Int32>(nSlot));
if (nBase != 0x0FFF)
{
m_aStylesheet.append(OOO_STRING_SVTOOLS_RTF_SBASEDON);
m_aStylesheet.append(static_cast<sal_Int32>(nBase));
}
m_aStylesheet.append(OOO_STRING_SVTOOLS_RTF_SNEXT);
m_aStylesheet.append(static_cast<sal_Int32>(nNext));
if (bAutoUpdate)
m_aStylesheet.append(OOO_STRING_SVTOOLS_RTF_SAUTOUPD);
m_rStyleName = rName;
m_nStyleId = nSlot;
}
void RtfAttributeOutput::EndStyle()
{
OString aStyles = MoveCharacterProperties();
m_rExport.InsStyle(m_nStyleId, aStyles);
m_aStylesheet.append(aStyles);
m_aStylesheet.append(' ');
m_aStylesheet.append(
msfilter::rtfutil::OutString(m_rStyleName, m_rExport.GetCurrentEncoding()));
m_aStylesheet.append(";}");
m_aStylesheet.append(SAL_NEWLINE_STRING);
}
void RtfAttributeOutput::StartStyleProperties(bool /*bParProp*/, sal_uInt16 /*nStyle*/)
{
/* noop */
}
void RtfAttributeOutput::EndStyleProperties(bool /*bParProp*/) { /* noop */}
void RtfAttributeOutput::OutlineNumbering(sal_uInt8 nLvl)
{
if (nLvl >= WW8ListManager::nMaxLevel)
nLvl = WW8ListManager::nMaxLevel - 1;
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ILVL);
m_aStyles.append(static_cast<sal_Int32>(nLvl));
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_OUTLINELEVEL);
m_aStyles.append(static_cast<sal_Int32>(nLvl));
}
void RtfAttributeOutput::PageBreakBefore(bool bBreak)
{
if (bBreak)
{
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_PAGEBB);
}
}
void RtfAttributeOutput::SectionBreak(sal_uInt8 nC, bool /*bBreakAfter*/,
const WW8_SepInfo* pSectionInfo, bool /*bExtraPageBreak*/)
{
switch (nC)
{
case msword::ColumnBreak:
m_nColBreakNeeded = true;
break;
case msword::PageBreak:
if (pSectionInfo)
m_rExport.SectionProperties(*pSectionInfo);
break;
}
}
void RtfAttributeOutput::StartSection()
{
if (m_bIsBeforeFirstParagraph)
return;
m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_SECT OOO_STRING_SVTOOLS_RTF_SECTD);
if (!m_bBufferSectionBreaks)
{
m_rExport.Strm().WriteOString(m_aSectionBreaks);
m_aSectionBreaks.setLength(0);
}
}
void RtfAttributeOutput::EndSection()
{
/*
* noop, \sect must go to StartSection or Word won't notice multiple
* columns...
*/
}
void RtfAttributeOutput::SectionFormProtection(bool bProtected)
{
m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_SECTUNLOCKED);
m_aSectionBreaks.append(static_cast<sal_Int32>(!bProtected));
}
void RtfAttributeOutput::SectionLineNumbering(sal_uLong nRestartNo,
const SwLineNumberInfo& rLnNumInfo)
{
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LINEMOD);
m_rExport.Strm().WriteNumberAsString(rLnNumInfo.GetCountBy());
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LINEX);
m_rExport.Strm().WriteNumberAsString(rLnNumInfo.GetPosFromLeft());
if (!rLnNumInfo.IsRestartEachPage())
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LINECONT);
if (nRestartNo > 0)
{
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LINESTARTS);
m_rExport.Strm().WriteNumberAsString(nRestartNo);
}
}
void RtfAttributeOutput::SectionTitlePage()
{
/*
* noop, handled in RtfExport::WriteHeaderFooter()
*/
}
void RtfAttributeOutput::SectionPageBorders(const SwFrameFormat* pFormat,
const SwFrameFormat* /*pFirstPageFormat*/)
{
const SvxBoxItem& rBox = pFormat->GetBox();
editeng::WordBorderDistances aDistances;
editeng::BorderDistancesToWord(rBox, m_aPageMargins, aDistances);
if (aDistances.bFromEdge)
{
sal_uInt16 nOpt = (1 << 5);
m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_PGBRDROPT);
m_aSectionBreaks.append(static_cast<sal_Int32>(nOpt));
}
const editeng::SvxBorderLine* pLine = rBox.GetTop();
if (pLine)
m_aSectionBreaks.append(
OutBorderLine(m_rExport, pLine, OOO_STRING_SVTOOLS_RTF_PGBRDRT, aDistances.nTop));
pLine = rBox.GetBottom();
if (pLine)
m_aSectionBreaks.append(
OutBorderLine(m_rExport, pLine, OOO_STRING_SVTOOLS_RTF_PGBRDRB, aDistances.nBottom));
pLine = rBox.GetLeft();
if (pLine)
m_aSectionBreaks.append(
OutBorderLine(m_rExport, pLine, OOO_STRING_SVTOOLS_RTF_PGBRDRL, aDistances.nLeft));
pLine = rBox.GetRight();
if (pLine)
m_aSectionBreaks.append(
OutBorderLine(m_rExport, pLine, OOO_STRING_SVTOOLS_RTF_PGBRDRR, aDistances.nRight));
}
void RtfAttributeOutput::SectionBiDi(bool bBiDi)
{
m_rExport.Strm().WriteOString(bBiDi ? OOO_STRING_SVTOOLS_RTF_RTLSECT
: OOO_STRING_SVTOOLS_RTF_LTRSECT);
}
void RtfAttributeOutput::SectionPageNumbering(sal_uInt16 nNumType,
const ::std::optional<sal_uInt16>& oPageRestartNumber)
{
if (oPageRestartNumber)
{
m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_PGNSTARTS);
m_aSectionBreaks.append(static_cast<sal_Int32>(*oPageRestartNumber));
m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_PGNRESTART);
}
const char* pStr = nullptr;
switch (nNumType)
{
case SVX_NUM_CHARS_UPPER_LETTER:
case SVX_NUM_CHARS_UPPER_LETTER_N:
pStr = OOO_STRING_SVTOOLS_RTF_PGNUCLTR;
break;
case SVX_NUM_CHARS_LOWER_LETTER:
case SVX_NUM_CHARS_LOWER_LETTER_N:
pStr = OOO_STRING_SVTOOLS_RTF_PGNLCLTR;
break;
case SVX_NUM_ROMAN_UPPER:
pStr = OOO_STRING_SVTOOLS_RTF_PGNUCRM;
break;
case SVX_NUM_ROMAN_LOWER:
pStr = OOO_STRING_SVTOOLS_RTF_PGNLCRM;
break;
case SVX_NUM_ARABIC:
pStr = OOO_STRING_SVTOOLS_RTF_PGNDEC;
break;
}
if (pStr)
m_aSectionBreaks.append(pStr);
}
void RtfAttributeOutput::SectionType(sal_uInt8 nBreakCode)
{
SAL_INFO("sw.rtf", __func__ << ", nBreakCode = " << int(nBreakCode));
/*
* break code: 0 No break, 1 New column
* 2 New page, 3 Even page, 4 Odd page
*/
const char* sType = nullptr;
switch (nBreakCode)
{
case 1:
sType = OOO_STRING_SVTOOLS_RTF_SBKCOL;
break;
case 2:
sType = OOO_STRING_SVTOOLS_RTF_SBKPAGE;
break;
case 3:
sType = OOO_STRING_SVTOOLS_RTF_SBKEVEN;
break;
case 4:
sType = OOO_STRING_SVTOOLS_RTF_SBKODD;
break;
default:
sType = OOO_STRING_SVTOOLS_RTF_SBKNONE;
break;
}
m_aSectionBreaks.append(sType);
if (!m_bBufferSectionBreaks)
{
m_rExport.Strm().WriteOString(m_aSectionBreaks);
m_aSectionBreaks.setLength(0);
}
}
void RtfAttributeOutput::SectFootnoteEndnotePr()
{
WriteFootnoteEndnotePr(true, m_rExport.m_rDoc.GetFootnoteInfo());
WriteFootnoteEndnotePr(false, m_rExport.m_rDoc.GetEndNoteInfo());
}
void RtfAttributeOutput::WriteFootnoteEndnotePr(bool bFootnote, const SwEndNoteInfo& rInfo)
{
const char* pOut = nullptr;
if (bFootnote)
{
switch (rInfo.m_aFormat.GetNumberingType())
{
default:
pOut = OOO_STRING_SVTOOLS_RTF_SFTNNAR;
break;
case SVX_NUM_CHARS_LOWER_LETTER:
case SVX_NUM_CHARS_LOWER_LETTER_N:
pOut = OOO_STRING_SVTOOLS_RTF_SFTNNALC;
break;
case SVX_NUM_CHARS_UPPER_LETTER:
case SVX_NUM_CHARS_UPPER_LETTER_N:
pOut = OOO_STRING_SVTOOLS_RTF_SFTNNAUC;
break;
case SVX_NUM_ROMAN_LOWER:
pOut = OOO_STRING_SVTOOLS_RTF_SFTNNRLC;
break;
case SVX_NUM_ROMAN_UPPER:
pOut = OOO_STRING_SVTOOLS_RTF_SFTNNRUC;
break;
case SVX_NUM_SYMBOL_CHICAGO:
pOut = OOO_STRING_SVTOOLS_RTF_SFTNNCHI;
break;
}
}
else
{
switch (rInfo.m_aFormat.GetNumberingType())
{
default:
pOut = OOO_STRING_SVTOOLS_RTF_SAFTNNAR;
break;
case SVX_NUM_CHARS_LOWER_LETTER:
case SVX_NUM_CHARS_LOWER_LETTER_N:
pOut = OOO_STRING_SVTOOLS_RTF_SAFTNNALC;
break;
case SVX_NUM_CHARS_UPPER_LETTER:
case SVX_NUM_CHARS_UPPER_LETTER_N:
pOut = OOO_STRING_SVTOOLS_RTF_SAFTNNAUC;
break;
case SVX_NUM_ROMAN_LOWER:
pOut = OOO_STRING_SVTOOLS_RTF_SAFTNNRLC;
break;
case SVX_NUM_ROMAN_UPPER:
pOut = OOO_STRING_SVTOOLS_RTF_SAFTNNRUC;
break;
case SVX_NUM_SYMBOL_CHICAGO:
pOut = OOO_STRING_SVTOOLS_RTF_SAFTNNCHI;
break;
}
}
m_aSectionBreaks.append(pOut);
if (!m_bBufferSectionBreaks)
{
m_rExport.Strm().WriteOString(m_aSectionBreaks);
m_aSectionBreaks.setLength(0);
}
}
void RtfAttributeOutput::NumberingDefinition(sal_uInt16 nId, const SwNumRule& /*rRule*/)
{
m_rExport.Strm().WriteChar('{').WriteOString(OOO_STRING_SVTOOLS_RTF_LISTOVERRIDE);
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LISTID);
m_rExport.Strm().WriteNumberAsString(nId);
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LISTOVERRIDECOUNT).WriteChar('0');
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LS);
m_rExport.Strm().WriteNumberAsString(nId).WriteChar('}');
}
void RtfAttributeOutput::StartAbstractNumbering(sal_uInt16 nId)
{
m_rExport.Strm()
.WriteChar('{')
.WriteOString(OOO_STRING_SVTOOLS_RTF_LIST)
.WriteOString(OOO_STRING_SVTOOLS_RTF_LISTTEMPLATEID);
m_rExport.Strm().WriteNumberAsString(nId);
m_nListId = nId;
}
void RtfAttributeOutput::EndAbstractNumbering()
{
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LISTID);
m_rExport.Strm().WriteNumberAsString(m_nListId).WriteChar('}').WriteOString(SAL_NEWLINE_STRING);
}
void RtfAttributeOutput::NumberingLevel(sal_uInt8 nLevel, sal_uInt16 nStart,
sal_uInt16 nNumberingType, SvxAdjust eAdjust,
const sal_uInt8* pNumLvlPos, sal_uInt8 nFollow,
const wwFont* pFont, const SfxItemSet* pOutSet,
sal_Int16 nIndentAt, sal_Int16 nFirstLineIndex,
sal_Int16 /*nListTabPos*/, const OUString& rNumberingString,
const SvxBrushItem* pBrush, bool isLegal)
{
m_rExport.Strm().WriteOString(SAL_NEWLINE_STRING);
if (nLevel > 8) // RTF knows only 9 levels
m_rExport.Strm()
.WriteOString(OOO_STRING_SVTOOLS_RTF_IGNORE)
.WriteOString(OOO_STRING_SVTOOLS_RTF_SOUTLVL);
m_rExport.Strm().WriteChar('{').WriteOString(OOO_STRING_SVTOOLS_RTF_LISTLEVEL);
sal_uInt16 nVal = 0;
switch (nNumberingType)
{
case SVX_NUM_ROMAN_UPPER:
nVal = 1;
break;
case SVX_NUM_ROMAN_LOWER:
nVal = 2;
break;
case SVX_NUM_CHARS_UPPER_LETTER:
case SVX_NUM_CHARS_UPPER_LETTER_N:
nVal = 3;
break;
case SVX_NUM_CHARS_LOWER_LETTER:
case SVX_NUM_CHARS_LOWER_LETTER_N:
nVal = 4;
break;
case SVX_NUM_FULL_WIDTH_ARABIC:
nVal = 14;
break;
case SVX_NUM_CIRCLE_NUMBER:
nVal = 18;
break;
case SVX_NUM_NUMBER_LOWER_ZH:
nVal = 35;
if (pOutSet)
{
const SvxLanguageItem& rLang = pOutSet->Get(RES_CHRATR_CJK_LANGUAGE);
if (rLang.GetLanguage() == LANGUAGE_CHINESE_SIMPLIFIED)
{
nVal = 39;
}
}
break;
case SVX_NUM_NUMBER_UPPER_ZH:
nVal = 38;
break;
case SVX_NUM_NUMBER_UPPER_ZH_TW:
nVal = 34;
break;
case SVX_NUM_TIAN_GAN_ZH:
nVal = 30;
break;
case SVX_NUM_DI_ZI_ZH:
nVal = 31;
break;
case SVX_NUM_NUMBER_TRADITIONAL_JA:
nVal = 16;
break;
case SVX_NUM_AIU_FULLWIDTH_JA:
nVal = 20;
break;
case SVX_NUM_AIU_HALFWIDTH_JA:
nVal = 12;
break;
case SVX_NUM_IROHA_FULLWIDTH_JA:
nVal = 21;
break;
case SVX_NUM_IROHA_HALFWIDTH_JA:
nVal = 13;
break;
case style::NumberingType::HANGUL_SYLLABLE_KO:
nVal = 24;
break; // ganada
case style::NumberingType::HANGUL_JAMO_KO:
nVal = 25;
break; // chosung
case style::NumberingType::HANGUL_CIRCLED_SYLLABLE_KO:
nVal = 24;
break;
case style::NumberingType::HANGUL_CIRCLED_JAMO_KO:
nVal = 25;
break;
case style::NumberingType::NUMBER_HANGUL_KO:
nVal = 42;
break; // koreanCounting
case style::NumberingType::NUMBER_DIGITAL_KO:
nVal = 41; // koreanDigital
break;
case style::NumberingType::NUMBER_DIGITAL2_KO:
nVal = 44; // koreanDigital2
break;
case style::NumberingType::NUMBER_LEGAL_KO:
nVal = 43; // koreanLegal
break;
case SVX_NUM_BITMAP:
case SVX_NUM_CHAR_SPECIAL:
nVal = 23;
break;
case SVX_NUM_NUMBER_NONE:
nVal = 255;
break;
case SVX_NUM_ARABIC_ZERO:
nVal = 22;
break;
}
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LEVELNFC);
m_rExport.Strm().WriteNumberAsString(nVal);
switch (eAdjust)
{
case SvxAdjust::Center:
nVal = 1;
break;
case SvxAdjust::Right:
nVal = 2;
break;
default:
nVal = 0;
break;
}
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LEVELJC);
m_rExport.Strm().WriteNumberAsString(nVal);
// bullet
if (nNumberingType == SVX_NUM_BITMAP && pBrush)
{
int nIndex = m_rExport.GetGrfIndex(*pBrush);
if (nIndex != -1)
{
m_rExport.Strm().WriteOString(LO_STRING_SVTOOLS_RTF_LEVELPICTURE);
m_rExport.Strm().WriteNumberAsString(nIndex);
}
}
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LEVELSTARTAT);
m_rExport.Strm().WriteNumberAsString(nStart);
if (isLegal)
{
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LEVELLEGAL);
}
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LEVELFOLLOW);
m_rExport.Strm().WriteNumberAsString(nFollow);
// leveltext group
m_rExport.Strm().WriteChar('{').WriteOString(OOO_STRING_SVTOOLS_RTF_LEVELTEXT).WriteChar(' ');
if (SVX_NUM_CHAR_SPECIAL == nNumberingType || SVX_NUM_BITMAP == nNumberingType)
{
m_rExport.Strm().WriteOString("\\'01");
sal_Unicode cChar = rNumberingString[0];
m_rExport.Strm().WriteOString("\\u");
m_rExport.Strm().WriteNumberAsString(cChar);
m_rExport.Strm().WriteOString(" ?");
}
else
{
m_rExport.Strm().WriteOString("\\'").WriteOString(
msfilter::rtfutil::OutHex(rNumberingString.getLength(), 2));
m_rExport.Strm().WriteOString(msfilter::rtfutil::OutString(rNumberingString,
m_rExport.GetDefaultEncoding(),
/*bUnicode =*/false));
}
m_rExport.Strm().WriteOString(";}");
// write the levelnumbers
m_rExport.Strm().WriteOString("{").WriteOString(OOO_STRING_SVTOOLS_RTF_LEVELNUMBERS);
for (sal_uInt8 i = 0; i <= nLevel && pNumLvlPos[i]; ++i)
{
m_rExport.Strm().WriteOString("\\'").WriteOString(
msfilter::rtfutil::OutHex(pNumLvlPos[i], 2));
}
m_rExport.Strm().WriteOString(";}");
if (pOutSet)
{
if (pFont)
{
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_F);
m_rExport.Strm().WriteNumberAsString(m_rExport.m_aFontHelper.GetId(*pFont));
}
m_rExport.OutputItemSet(*pOutSet, false, true, i18n::ScriptType::LATIN,
m_rExport.m_bExportModeRTF);
const OString aProperties = MoveCharacterProperties(true);
m_rExport.Strm().WriteOString(aProperties);
}
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_FI);
m_rExport.Strm().WriteNumberAsString(nFirstLineIndex).WriteOString(OOO_STRING_SVTOOLS_RTF_LI);
m_rExport.Strm().WriteNumberAsString(nIndentAt);
m_rExport.Strm().WriteChar('}');
if (nLevel > 8)
m_rExport.Strm().WriteChar('}');
}
void RtfAttributeOutput::WriteField_Impl(const SwField* const pField, ww::eField /*eType*/,
std::u16string_view rFieldCmd, FieldFlags nMode)
{
// If there are no field instructions, don't export it as a field.
bool bHasInstructions = !rFieldCmd.empty();
if (FieldFlags::All == nMode)
{
if (bHasInstructions)
{
m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_FIELD);
if (pField && (pField->GetSubType() & FIXEDFLD))
m_aRunText->append(OOO_STRING_SVTOOLS_RTF_FLDLOCK);
m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FLDINST
" ");
m_aRunText->append(
msfilter::rtfutil::OutString(rFieldCmd, m_rExport.GetCurrentEncoding()));
m_aRunText->append("}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " ");
}
if (pField)
m_aRunText->append(msfilter::rtfutil::OutString(pField->ExpandField(true, nullptr),
m_rExport.GetDefaultEncoding()));
if (bHasInstructions)
m_aRunText->append("}}");
}
else
{
if (nMode & FieldFlags::CmdStart)
{
m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_FIELD);
m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FLDINST
// paragraph break closes group so open another one "inside" to
" {"); // prevent leaving the field instruction
}
if (bHasInstructions)
m_aRunText->append(
msfilter::rtfutil::OutString(rFieldCmd, m_rExport.GetCurrentEncoding()));
if (nMode & FieldFlags::CmdEnd)
{
m_aRunText->append("}}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT);
// The fldrslt contains its own full copy of character formatting,
// but if the result is empty (nMode & FieldFlags::End) or field export is condensed
// in any way (multiple flags) then avoid spamming an unnecessary plain character reset.
if (nMode == FieldFlags::CmdEnd)
m_aRunText->append(OOO_STRING_SVTOOLS_RTF_PLAIN);
m_aRunText->append(" {");
}
if (nMode & FieldFlags::Close)
{
m_aRunText->append("}}}");
}
}
}
void RtfAttributeOutput::WriteBookmarks_Impl(std::vector<OUString>& rStarts,
std::vector<OUString>& rEnds)
{
for (const auto& rStart : rStarts)
{
m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_BKMKSTART " ");
m_aRunText->append(msfilter::rtfutil::OutString(rStart, m_rExport.GetCurrentEncoding()));
m_aRunText->append('}');
}
rStarts.clear();
for (const auto& rEnd : rEnds)
{
m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_BKMKEND " ");
m_aRunText->append(msfilter::rtfutil::OutString(rEnd, m_rExport.GetCurrentEncoding()));
m_aRunText->append('}');
}
rEnds.clear();
}
void RtfAttributeOutput::WriteAnnotationMarks_Impl(std::vector<OUString>& rStarts,
std::vector<OUString>& rEnds)
{
for (const auto& rStart : rStarts)
{
OString rName = OUStringToOString(rStart, RTL_TEXTENCODING_UTF8);
// Output the annotation mark
const sal_Int32 nId = m_nNextAnnotationMarkId++;
m_rOpenedAnnotationMarksIds[rName] = nId;
m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ATRFSTART " ");
m_aRun->append(nId);
m_aRun->append('}');
}
rStarts.clear();
for (const auto& rEnd : rEnds)
{
OString rName = OUStringToOString(rEnd, RTL_TEXTENCODING_UTF8);
// Get the id of the annotation mark
auto it = m_rOpenedAnnotationMarksIds.find(rName);
if (it != m_rOpenedAnnotationMarksIds.end())
{
const sal_Int32 nId = it->second;
m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ATRFEND " ");
m_aRun->append(nId);
m_aRun->append('}');
m_rOpenedAnnotationMarksIds.erase(rName);
if (m_aPostitFields.find(nId) != m_aPostitFields.end())
{
m_aRunText->append("{");
m_nCurrentAnnotationMarkId = nId;
PostitField(m_aPostitFields[nId]);
m_nCurrentAnnotationMarkId = -1;
m_aRunText->append("}");
}
}
}
rEnds.clear();
}
void RtfAttributeOutput::WriteHeaderFooter_Impl(const SwFrameFormat& rFormat, bool bHeader,
const char* pStr, bool bTitlepg)
{
OStringBuffer aSectionBreaks = m_aSectionBreaks;
m_aSectionBreaks.setLength(0);
RtfStringBuffer aRun = m_aRun;
m_aRun.clear();
m_aSectionHeaders.append(bHeader ? OOO_STRING_SVTOOLS_RTF_HEADERY
: OOO_STRING_SVTOOLS_RTF_FOOTERY);
m_aSectionHeaders.append(
static_cast<sal_Int32>(m_rExport.m_pCurrentPageDesc->GetMaster().GetULSpace().GetUpper()));
if (bTitlepg)
m_aSectionHeaders.append(OOO_STRING_SVTOOLS_RTF_TITLEPG);
m_aSectionHeaders.append('{');
m_aSectionHeaders.append(pStr);
m_bBufferSectionHeaders = true;
m_rExport.WriteHeaderFooterText(rFormat, bHeader);
m_bBufferSectionHeaders = false;
m_aSectionHeaders.append('}');
m_aSectionBreaks = aSectionBreaks;
m_aRun = std::move(aRun);
}
namespace
{
void lcl_TextFrameShadow(std::vector<std::pair<OString, OString>>& rFlyProperties,
const SwFrameFormat& rFrameFormat)
{
const SvxShadowItem& aShadowItem = rFrameFormat.GetShadow();
if (aShadowItem.GetLocation() == SvxShadowLocation::NONE)
return;
rFlyProperties.push_back(std::make_pair<OString, OString>("fShadow"_ostr, OString::number(1)));
const Color& rColor = aShadowItem.GetColor();
// We in fact need RGB to BGR, but the transformation is symmetric.
rFlyProperties.push_back(std::make_pair<OString, OString>(
"shadowColor"_ostr, OString::number(wwUtility::RGBToBGR(rColor))));
// Twips -> points -> EMUs -- hacky, the intermediate step hides rounding errors on roundtrip.
OString aShadowWidth = OString::number(sal_Int32(aShadowItem.GetWidth() / 20) * 12700);
OString aOffsetX;
OString aOffsetY;
switch (aShadowItem.GetLocation())
{
case SvxShadowLocation::TopLeft:
aOffsetX = "-" + aShadowWidth;
aOffsetY = "-" + aShadowWidth;
break;
case SvxShadowLocation::TopRight:
aOffsetX = aShadowWidth;
aOffsetY = "-" + aShadowWidth;
break;
case SvxShadowLocation::BottomLeft:
aOffsetX = "-" + aShadowWidth;
aOffsetY = aShadowWidth;
break;
case SvxShadowLocation::BottomRight:
aOffsetX = aShadowWidth;
aOffsetY = aShadowWidth;
break;
case SvxShadowLocation::NONE:
case SvxShadowLocation::End:
break;
}
if (!aOffsetX.isEmpty())
rFlyProperties.emplace_back("shadowOffsetX", aOffsetX);
if (!aOffsetY.isEmpty())
rFlyProperties.emplace_back("shadowOffsetY", aOffsetY);
}
void lcl_TextFrameRelativeSize(std::vector<std::pair<OString, OString>>& rFlyProperties,
const SwFrameFormat& rFrameFormat)
{
const SwFormatFrameSize& rSize = rFrameFormat.GetFrameSize();
// Relative size of the Text Frame.
const sal_uInt8 nWidthPercent = rSize.GetWidthPercent();
if (nWidthPercent && nWidthPercent != SwFormatFrameSize::SYNCED)
{
rFlyProperties.push_back(
std::make_pair<OString, OString>("pctHoriz"_ostr, OString::number(nWidthPercent * 10)));
OString aRelation;
switch (rSize.GetWidthPercentRelation())
{
case text::RelOrientation::PAGE_FRAME:
aRelation = "1"_ostr; // page
break;
default:
aRelation = "0"_ostr; // margin
break;
}
rFlyProperties.emplace_back(std::make_pair("sizerelh", aRelation));
}
const sal_uInt8 nHeightPercent = rSize.GetHeightPercent();
if (!(nHeightPercent && nHeightPercent != SwFormatFrameSize::SYNCED))
return;
rFlyProperties.push_back(
std::make_pair<OString, OString>("pctVert"_ostr, OString::number(nHeightPercent * 10)));
OString aRelation;
switch (rSize.GetHeightPercentRelation())
{
case text::RelOrientation::PAGE_FRAME:
aRelation = "1"_ostr; // page
break;
default:
aRelation = "0"_ostr; // margin
break;
}
rFlyProperties.emplace_back(std::make_pair("sizerelv", aRelation));
}
}
void RtfAttributeOutput::writeTextFrame(const ww8::Frame& rFrame, bool bTextBox)
{
RtfStringBuffer aRunText;
if (bTextBox)
{
m_rExport.setStream();
aRunText = m_aRunText;
m_aRunText.clear();
}
m_rExport.Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_SHPTXT);
{
// Save table state, in case the inner text also contains a table.
ww8::WW8TableInfo::Pointer_t pTableInfoOrig = m_rExport.m_pTableInfo;
m_rExport.m_pTableInfo = std::make_shared<ww8::WW8TableInfo>();
std::unique_ptr<SwWriteTable> pTableWrt(std::move(m_pTableWrt));
sal_uInt32 nTableDepth = m_nTableDepth;
m_nTableDepth = 0;
/*
* Save m_aRun as we should not lose the opening brace.
* OTOH, just drop the contents of m_aRunText in case something
* would be there, causing a problem later.
*/
OString aSave = m_aRun.makeStringAndClear();
// Also back m_bInRun and m_bSingleEmptyRun up.
bool bInRunOrig = m_bInRun;
m_bInRun = false;
bool bSingleEmptyRunOrig = m_bSingleEmptyRun;
m_bSingleEmptyRun = false;
m_rExport.SetRTFFlySyntax(true);
const SwFrameFormat& rFrameFormat = rFrame.GetFrameFormat();
const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
SwNodeOffset nEnd
= pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
m_rExport.SaveData(nStt, nEnd);
m_rExport.m_pParentFrame = &rFrame;
m_rExport.WriteText();
m_rExport.RestoreData();
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_PARD);
m_rExport.SetRTFFlySyntax(false);
m_aRun->append(aSave);
m_aRunText.clear();
m_bInRun = bInRunOrig;
m_bSingleEmptyRun = bSingleEmptyRunOrig;
// Restore table state.
m_rExport.m_pTableInfo = std::move(pTableInfoOrig);
m_pTableWrt = std::move(pTableWrt);
m_nTableDepth = nTableDepth;
}
m_rExport.m_pParentFrame = nullptr;
m_rExport.Strm().WriteChar('}'); // shptxt
if (bTextBox)
{
m_aRunText = aRunText;
m_aRunText->append(m_rExport.getStream());
m_rExport.resetStream();
}
}
/** save the current run state around exporting things that contain paragraphs
themselves like text frames.
TODO: probably more things need to be saved?
*/
class SaveRunState
{
private:
RtfAttributeOutput& m_rRtf;
RtfStringBuffer m_Run;
RtfStringBuffer m_RunText;
bool const m_bSingleEmptyRun;
bool const m_bInRun;
public:
explicit SaveRunState(RtfAttributeOutput& rRtf)
: m_rRtf(rRtf)
, m_Run(std::move(rRtf.m_aRun))
, m_RunText(std::move(rRtf.m_aRunText))
, m_bSingleEmptyRun(rRtf.m_bSingleEmptyRun)
, m_bInRun(rRtf.m_bInRun)
{
m_rRtf.m_rExport.setStream();
}
~SaveRunState()
{
m_rRtf.m_aRun = std::move(m_Run);
m_rRtf.m_aRunText = std::move(m_RunText);
m_rRtf.m_bSingleEmptyRun = m_bSingleEmptyRun;
m_rRtf.m_bInRun = m_bInRun;
m_rRtf.m_aRunText->append(m_rRtf.m_rExport.getStream());
m_rRtf.m_rExport.resetStream();
}
};
void RtfAttributeOutput::OutputFlyFrame_Impl(const ww8::Frame& rFrame, const Point& /*rNdTopLeft*/)
{
const SwFrameFormat& rFrameFormat = rFrame.GetFrameFormat();
if (rFrameFormat.GetFlySplit().GetValue())
{
// The frame can split: this was originally from a floating table, write it back as
// such.
SaveRunState aState(*this);
const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
SwNodeOffset nEnd
= pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
m_rExport.SaveData(nStt, nEnd);
GetExport().WriteText();
m_rExport.RestoreData();
return;
}
const SwNode* pNode = rFrame.GetContent();
const SwGrfNode* pGrfNode = pNode ? pNode->GetGrfNode() : nullptr;
switch (rFrame.GetWriterType())
{
case ww8::Frame::eTextBox:
{
// If this is a TextBox of a shape, then ignore: it's handled in RtfSdrExport::StartShape().
if (RtfSdrExport::isTextBox(rFrame.GetFrameFormat()))
break;
SaveRunState const saved(*this);
m_rExport.m_pParentFrame = &rFrame;
m_rExport.Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_SHP);
m_rExport.Strm().WriteOString(
"{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_SHPINST);
// Shape properties.
m_aFlyProperties.push_back(std::make_pair<OString, OString>(
"shapeType"_ostr, OString::number(ESCHER_ShpInst_TextBox)));
// When a frame has some low height, but automatically expanded due
// to lots of contents, this size contains the real size.
const Size aSize = rFrame.GetSize();
m_pFlyFrameSize = &aSize;
m_rExport.m_bOutFlyFrameAttrs = true;
m_rExport.SetRTFFlySyntax(true);
m_rExport.OutputFormat(rFrame.GetFrameFormat(), false, false, true);
// Write ZOrder.
if (const SdrObject* pObject = rFrame.GetFrameFormat().FindRealSdrObject())
{
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SHPZ);
m_rExport.Strm().WriteNumberAsString(pObject->GetOrdNum());
}
m_rExport.Strm().WriteOString(m_aRunText.makeStringAndClear());
m_rExport.Strm().WriteOString(m_aStyles);
m_aStyles.setLength(0);
m_rExport.m_bOutFlyFrameAttrs = false;
m_rExport.SetRTFFlySyntax(false);
m_pFlyFrameSize = nullptr;
lcl_TextFrameShadow(m_aFlyProperties, rFrameFormat);
lcl_TextFrameRelativeSize(m_aFlyProperties, rFrameFormat);
for (const std::pair<OString, OString>& rPair : m_aFlyProperties)
{
m_rExport.Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_SP "{");
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SN " ");
m_rExport.Strm().WriteOString(rPair.first);
m_rExport.Strm().WriteOString("}{" OOO_STRING_SVTOOLS_RTF_SV " ");
m_rExport.Strm().WriteOString(rPair.second);
m_rExport.Strm().WriteOString("}}");
}
m_aFlyProperties.clear();
writeTextFrame(rFrame);
m_rExport.Strm().WriteChar('}'); // shpinst
m_rExport.Strm().WriteChar('}'); // shp
m_rExport.Strm().WriteOString(SAL_NEWLINE_STRING);
}
break;
case ww8::Frame::eGraphic:
if (pGrfNode)
{
m_aRunText.append(dynamic_cast<const SwFlyFrameFormat*>(&rFrame.GetFrameFormat()),
pGrfNode);
}
else if (!rFrame.IsInline())
{
m_rExport.m_pParentFrame = &rFrame;
m_rExport.SetRTFFlySyntax(true);
m_rExport.OutputFormat(rFrame.GetFrameFormat(), false, false, true);
m_rExport.SetRTFFlySyntax(false);
m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE);
m_rExport.OutputFormat(rFrame.GetFrameFormat(), false, false, true);
m_aRunText->append('}');
m_rExport.m_pParentFrame = nullptr;
}
break;
case ww8::Frame::eDrawing:
{
const SdrObject* pSdrObj = rFrame.GetFrameFormat().FindRealSdrObject();
if (pSdrObj)
{
m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_FIELD "{");
m_aRunText->append(OOO_STRING_SVTOOLS_RTF_IGNORE);
m_aRunText->append(OOO_STRING_SVTOOLS_RTF_FLDINST);
m_aRunText->append(" SHAPE ");
m_aRunText->append("}"
"{" OOO_STRING_SVTOOLS_RTF_FLDRSLT);
m_rExport.SdrExporter().AddSdrObject(*pSdrObj);
m_aRunText->append('}');
m_aRunText->append('}');
}
}
break;
case ww8::Frame::eFormControl:
{
const SdrObject* pObject = rFrameFormat.FindRealSdrObject();
m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_FIELD);
m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FLDINST);
if (pObject && pObject->GetObjInventor() == SdrInventor::FmForm)
{
if (auto pFormObj = dynamic_cast<const SdrUnoObj*>(pObject))
{
const uno::Reference<awt::XControlModel>& xControlModel
= pFormObj->GetUnoControlModel();
uno::Reference<lang::XServiceInfo> xInfo(xControlModel, uno::UNO_QUERY);
if (xInfo.is())
{
uno::Reference<beans::XPropertySet> xPropSet(xControlModel, uno::UNO_QUERY);
uno::Reference<beans::XPropertySetInfo> xPropSetInfo
= xPropSet->getPropertySetInfo();
OUString sName;
if (xInfo->supportsService("com.sun.star.form.component.CheckBox"))
{
m_aRun->append(OUStringToOString(FieldString(ww::eFORMCHECKBOX),
m_rExport.GetCurrentEncoding()));
m_aRun->append(
"{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FORMFIELD
"{");
m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFTYPE "1"); // 1 = checkbox
// checkbox size in half points, this seems to be always 20
m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFHPS "20");
OUString aStr;
sName = "Name";
if (xPropSetInfo->hasPropertyByName(sName))
{
xPropSet->getPropertyValue(sName) >>= aStr;
m_aRun->append(
"{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFNAME
" ");
m_aRun->append(
OUStringToOString(aStr, m_rExport.GetCurrentEncoding()));
m_aRun->append('}');
}
sName = "HelpText";
if (xPropSetInfo->hasPropertyByName(sName))
{
xPropSet->getPropertyValue(sName) >>= aStr;
m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFOWNHELP);
m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE
OOO_STRING_SVTOOLS_RTF_FFHELPTEXT " ");
m_aRun->append(
OUStringToOString(aStr, m_rExport.GetCurrentEncoding()));
m_aRun->append('}');
}
sName = "HelpF1Text";
if (xPropSetInfo->hasPropertyByName(sName))
{
xPropSet->getPropertyValue(sName) >>= aStr;
m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFOWNSTAT);
m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE
OOO_STRING_SVTOOLS_RTF_FFSTATTEXT " ");
m_aRun->append(
OUStringToOString(aStr, m_rExport.GetCurrentEncoding()));
m_aRun->append('}');
}
sal_Int16 nTemp = 0;
xPropSet->getPropertyValue("DefaultState") >>= nTemp;
m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFDEFRES);
m_aRun->append(static_cast<sal_Int32>(nTemp));
xPropSet->getPropertyValue("State") >>= nTemp;
m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFRES);
m_aRun->append(static_cast<sal_Int32>(nTemp));
m_aRun->append("}}");
// field result is empty, ffres already contains the form result
m_aRun->append("}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " ");
}
else if (xInfo->supportsService("com.sun.star.form.component.TextField"))
{
OStringBuffer aBuf;
OString aStr;
OUString aTmp;
const char* pStr;
m_aRun->append(OUStringToOString(FieldString(ww::eFORMTEXT),
m_rExport.GetCurrentEncoding()));
m_aRun->append(
"{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_DATAFIELD
" ");
for (int i = 0; i < 8; i++)
aBuf.append(char(0x00));
xPropSet->getPropertyValue("Name") >>= aTmp;
aStr = OUStringToOString(aTmp, m_rExport.GetCurrentEncoding());
aBuf.append(OStringChar(static_cast<char>(aStr.getLength())) + aStr
+ OStringChar(char(0x00)));
xPropSet->getPropertyValue("DefaultText") >>= aTmp;
aStr = OUStringToOString(aTmp, m_rExport.GetCurrentEncoding());
aBuf.append(static_cast<char>(aStr.getLength()));
aBuf.append(aStr);
for (int i = 0; i < 11; i++)
aBuf.append(char(0x00));
aStr = aBuf.makeStringAndClear();
pStr = aStr.getStr();
for (int i = 0; i < aStr.getLength(); i++, pStr++)
m_aRun->append(msfilter::rtfutil::OutHex(*pStr, 2));
m_aRun->append('}');
m_aRun->append("}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " ");
xPropSet->getPropertyValue("Text") >>= aTmp;
m_aRun->append(OUStringToOString(aTmp, m_rExport.GetCurrentEncoding()));
m_aRun->append('}');
m_aRun->append(
"{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FORMFIELD
"{");
sName = "HelpText";
if (xPropSetInfo->hasPropertyByName(sName))
{
xPropSet->getPropertyValue(sName) >>= aTmp;
m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFOWNHELP);
m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE
OOO_STRING_SVTOOLS_RTF_FFHELPTEXT " ");
m_aRun->append(
OUStringToOString(aTmp, m_rExport.GetCurrentEncoding()));
m_aRun->append('}');
}
sName = "HelpF1Text";
if (xPropSetInfo->hasPropertyByName(sName))
{
xPropSet->getPropertyValue(sName) >>= aTmp;
m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFOWNSTAT);
m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE
OOO_STRING_SVTOOLS_RTF_FFSTATTEXT " ");
m_aRun->append(
OUStringToOString(aTmp, m_rExport.GetCurrentEncoding()));
m_aRun->append('}');
}
m_aRun->append("}");
}
else if (xInfo->supportsService("com.sun.star.form.component.ListBox"))
{
OUString aStr;
uno::Sequence<sal_Int16> aIntSeq;
uno::Sequence<OUString> aStrSeq;
m_aRun->append(OUStringToOString(FieldString(ww::eFORMDROPDOWN),
m_rExport.GetCurrentEncoding()));
m_aRun->append(
"{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FORMFIELD
"{");
m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFTYPE "2"); // 2 = list
m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFHASLISTBOX);
xPropSet->getPropertyValue("DefaultSelection") >>= aIntSeq;
if (aIntSeq.hasElements())
{
m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFDEFRES);
// a dropdown list can have only one 'selected item by default'
m_aRun->append(static_cast<sal_Int32>(aIntSeq[0]));
}
xPropSet->getPropertyValue("SelectedItems") >>= aIntSeq;
if (aIntSeq.hasElements())
{
m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFRES);
// a dropdown list can have only one 'currently selected item'
m_aRun->append(static_cast<sal_Int32>(aIntSeq[0]));
}
sName = "Name";
if (xPropSetInfo->hasPropertyByName(sName))
{
xPropSet->getPropertyValue(sName) >>= aStr;
m_aRun->append(
"{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFNAME
" ");
m_aRun->append(
OUStringToOString(aStr, m_rExport.GetCurrentEncoding()));
m_aRun->append('}');
}
sName = "HelpText";
if (xPropSetInfo->hasPropertyByName(sName))
{
xPropSet->getPropertyValue(sName) >>= aStr;
m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFOWNHELP);
m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE
OOO_STRING_SVTOOLS_RTF_FFHELPTEXT " ");
m_aRun->append(
OUStringToOString(aStr, m_rExport.GetCurrentEncoding()));
m_aRun->append('}');
}
sName = "HelpF1Text";
if (xPropSetInfo->hasPropertyByName(sName))
{
xPropSet->getPropertyValue(sName) >>= aStr;
m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFOWNSTAT);
m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE
OOO_STRING_SVTOOLS_RTF_FFSTATTEXT " ");
m_aRun->append(
OUStringToOString(aStr, m_rExport.GetCurrentEncoding()));
m_aRun->append('}');
}
xPropSet->getPropertyValue("StringItemList") >>= aStrSeq;
for (const auto& rStr : aStrSeq)
m_aRun->append(
"{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FFL " "
+ OUStringToOString(rStr, m_rExport.GetCurrentEncoding())
+ "}");
m_aRun->append("}}");
// field result is empty, ffres already contains the form result
m_aRun->append("}{" OOO_STRING_SVTOOLS_RTF_FLDRSLT " ");
}
else
SAL_INFO("sw.rtf", __func__ << " unhandled form control: '"
<< xInfo->getImplementationName() << "'");
m_aRun->append('}');
}
}
}
m_aRun->append('}');
}
break;
case ww8::Frame::eOle:
{
const SdrObject* pSdrObj = rFrameFormat.FindRealSdrObject();
if (pSdrObj)
{
SwNodeIndex aIdx(*rFrameFormat.GetContent().GetContentIdx(), 1);
SwOLENode& rOLENd = *aIdx.GetNode().GetOLENode();
FlyFrameOLE(dynamic_cast<const SwFlyFrameFormat*>(&rFrameFormat), rOLENd,
rFrame.GetLayoutSize());
}
}
break;
default:
SAL_INFO("sw.rtf", __func__ << ": unknown type ("
<< static_cast<int>(rFrame.GetWriterType()) << ")");
break;
}
}
void RtfAttributeOutput::CharCaseMap(const SvxCaseMapItem& rCaseMap)
{
switch (rCaseMap.GetValue())
{
case SvxCaseMap::SmallCaps:
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SCAPS);
break;
case SvxCaseMap::Uppercase:
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CAPS);
break;
default: // Something that rtf does not support
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SCAPS);
m_aStyles.append(sal_Int32(0));
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CAPS);
m_aStyles.append(sal_Int32(0));
break;
}
}
void RtfAttributeOutput::CharColor(const SvxColorItem& rColor)
{
const Color aColor(rColor.GetValue());
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CF);
m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(aColor)));
}
void RtfAttributeOutput::CharContour(const SvxContourItem& rContour)
{
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_OUTL);
if (!rContour.GetValue())
m_aStyles.append(sal_Int32(0));
}
void RtfAttributeOutput::CharCrossedOut(const SvxCrossedOutItem& rCrossedOut)
{
switch (rCrossedOut.GetStrikeout())
{
case STRIKEOUT_NONE:
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_STRIKE);
m_aStyles.append(sal_Int32(0));
break;
case STRIKEOUT_DOUBLE:
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_STRIKED);
m_aStyles.append(sal_Int32(1));
break;
default:
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_STRIKE);
break;
}
}
void RtfAttributeOutput::CharEscapement(const SvxEscapementItem& rEscapement)
{
short nEsc = rEscapement.GetEsc();
short nProp = rEscapement.GetProportionalHeight();
sal_Int32 nProp100 = nProp * 100;
if (DFLT_ESC_PROP == nProp || nProp < 1 || nProp > 100)
{
if (DFLT_ESC_SUB == nEsc || DFLT_ESC_AUTO_SUB == nEsc)
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SUB);
else if (DFLT_ESC_SUPER == nEsc || DFLT_ESC_AUTO_SUPER == nEsc)
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SUPER);
return;
}
if (DFLT_ESC_AUTO_SUPER == nEsc)
{
nEsc = .8 * (100 - nProp);
++nProp100; // A 1 afterwards means 'automatic' according to editeng/rtf/rtfitem.cxx
}
else if (DFLT_ESC_AUTO_SUB == nEsc)
{
nEsc = .2 * -(100 - nProp);
++nProp100;
}
const char* pUpDn;
double fHeight = m_rExport.GetItem(RES_CHRATR_FONTSIZE).GetHeight();
if (0 < nEsc)
pUpDn = OOO_STRING_SVTOOLS_RTF_UP;
else if (0 > nEsc)
{
pUpDn = OOO_STRING_SVTOOLS_RTF_DN;
fHeight = -fHeight;
}
else
return;
m_aStyles.append('{');
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_IGNORE);
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_UPDNPROP);
m_aStyles.append(nProp100);
m_aStyles.append('}');
m_aStyles.append(pUpDn);
/*
* Calculate the act. FontSize and the percentage of the displacement;
* RTF file expects half points, while internally it's in twips.
* Formally : (FontSize * 1/20 ) pts x * 2
* ----------------------- = ------------
* 100% Escapement
*/
m_aStyles.append(static_cast<sal_Int32>(round(fHeight * nEsc / 1000)));
}
void RtfAttributeOutput::CharFont(const SvxFontItem& rFont)
{
// Insert \loch in MoveCharacterProperties
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_F);
m_aStyles.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont)));
// Insert \hich in MoveCharacterProperties
m_aStylesAssocHich.append(OOO_STRING_SVTOOLS_RTF_AF);
m_aStylesAssocHich.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont)));
// FIXME: this may be a tad expensive... but the charset needs to be
// consistent with what wwFont::WriteRtf() does
sw::util::FontMapExport aTmp(rFont.GetFamilyName());
sal_uInt8 nWindowsCharset = sw::ms::rtl_TextEncodingToWinCharsetRTF(
aTmp.msPrimary, aTmp.msSecondary, rFont.GetCharSet());
m_rExport.SetCurrentEncoding(rtl_getTextEncodingFromWindowsCharset(nWindowsCharset));
if (m_rExport.GetCurrentEncoding() == RTL_TEXTENCODING_DONTKNOW)
m_rExport.SetCurrentEncoding(m_rExport.GetDefaultEncoding());
}
void RtfAttributeOutput::CharFontSize(const SvxFontHeightItem& rFontSize)
{
switch (rFontSize.Which())
{
case RES_CHRATR_FONTSIZE:
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_FS);
m_aStyles.append(static_cast<sal_Int32>(rFontSize.GetHeight() / 10));
break;
case RES_CHRATR_CJK_FONTSIZE:
m_aStylesAssocDbch.append(OOO_STRING_SVTOOLS_RTF_AFS);
m_aStylesAssocDbch.append(static_cast<sal_Int32>(rFontSize.GetHeight() / 10));
break;
case RES_CHRATR_CTL_FONTSIZE:
m_aStylesAssocRtlch.append(OOO_STRING_SVTOOLS_RTF_AFS);
m_aStylesAssocRtlch.append(static_cast<sal_Int32>(rFontSize.GetHeight() / 10));
break;
}
}
void RtfAttributeOutput::CharKerning(const SvxKerningItem& rKerning)
{
// in quarter points then in twips
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_EXPND);
m_aStyles.append(static_cast<sal_Int32>(rKerning.GetValue() / 5));
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_EXPNDTW);
m_aStyles.append(static_cast<sal_Int32>(rKerning.GetValue()));
}
void RtfAttributeOutput::CharLanguage(const SvxLanguageItem& rLanguage)
{
switch (rLanguage.Which())
{
case RES_CHRATR_LANGUAGE:
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LANG);
m_aStyles.append(
static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
m_aStylesAssocLtrch.append(OOO_STRING_SVTOOLS_RTF_LANG);
m_aStylesAssocLtrch.append(
static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
break;
case RES_CHRATR_CJK_LANGUAGE:
m_aStylesAssocDbch.append(OOO_STRING_SVTOOLS_RTF_LANGFE);
m_aStylesAssocDbch.append(
static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
m_aStylesAssocLtrch.append(OOO_STRING_SVTOOLS_RTF_LANGFE);
m_aStylesAssocLtrch.append(
static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
break;
case RES_CHRATR_CTL_LANGUAGE:
m_aStylesAssocRtlch.append(OOO_STRING_SVTOOLS_RTF_ALANG);
m_aStylesAssocRtlch.append(
static_cast<sal_Int32>(static_cast<sal_uInt16>(rLanguage.GetLanguage())));
break;
}
}
void RtfAttributeOutput::CharPosture(const SvxPostureItem& rPosture)
{
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_I);
if (rPosture.GetPosture() == ITALIC_NONE)
m_aStyles.append(sal_Int32(0));
}
void RtfAttributeOutput::CharShadow(const SvxShadowedItem& rShadow)
{
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SHAD);
if (!rShadow.GetValue())
m_aStyles.append(sal_Int32(0));
}
void RtfAttributeOutput::CharUnderline(const SvxUnderlineItem& rUnderline)
{
const char* pStr = nullptr;
const SfxPoolItem* pItem = m_rExport.HasItem(RES_CHRATR_WORDLINEMODE);
bool bWord = false;
// No StaticWhichCast(RES_CHRATR_WORDLINEMODE), this may be for a postit, where the which ids
// don't match.
if (pItem)
bWord = static_cast<const SvxWordLineModeItem*>(pItem)->GetValue();
switch (rUnderline.GetLineStyle())
{
case LINESTYLE_SINGLE:
pStr = bWord ? OOO_STRING_SVTOOLS_RTF_ULW : OOO_STRING_SVTOOLS_RTF_UL;
break;
case LINESTYLE_DOUBLE:
pStr = OOO_STRING_SVTOOLS_RTF_ULDB;
break;
case LINESTYLE_NONE:
pStr = OOO_STRING_SVTOOLS_RTF_ULNONE;
break;
case LINESTYLE_DOTTED:
pStr = OOO_STRING_SVTOOLS_RTF_ULD;
break;
case LINESTYLE_DASH:
pStr = OOO_STRING_SVTOOLS_RTF_ULDASH;
break;
case LINESTYLE_DASHDOT:
pStr = OOO_STRING_SVTOOLS_RTF_ULDASHD;
break;
case LINESTYLE_DASHDOTDOT:
pStr = OOO_STRING_SVTOOLS_RTF_ULDASHDD;
break;
case LINESTYLE_BOLD:
pStr = OOO_STRING_SVTOOLS_RTF_ULTH;
break;
case LINESTYLE_WAVE:
pStr = OOO_STRING_SVTOOLS_RTF_ULWAVE;
break;
case LINESTYLE_BOLDDOTTED:
pStr = OOO_STRING_SVTOOLS_RTF_ULTHD;
break;
case LINESTYLE_BOLDDASH:
pStr = OOO_STRING_SVTOOLS_RTF_ULTHDASH;
break;
case LINESTYLE_LONGDASH:
pStr = OOO_STRING_SVTOOLS_RTF_ULLDASH;
break;
case LINESTYLE_BOLDLONGDASH:
pStr = OOO_STRING_SVTOOLS_RTF_ULTHLDASH;
break;
case LINESTYLE_BOLDDASHDOT:
pStr = OOO_STRING_SVTOOLS_RTF_ULTHDASHD;
break;
case LINESTYLE_BOLDDASHDOTDOT:
pStr = OOO_STRING_SVTOOLS_RTF_ULTHDASHDD;
break;
case LINESTYLE_BOLDWAVE:
pStr = OOO_STRING_SVTOOLS_RTF_ULHWAVE;
break;
case LINESTYLE_DOUBLEWAVE:
pStr = OOO_STRING_SVTOOLS_RTF_ULULDBWAVE;
break;
default:
break;
}
if (pStr)
{
m_aStyles.append(pStr);
// NEEDSWORK looks like here rUnderline.GetColor() is always black,
// even if the color in the odt is for example green...
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ULC);
m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(rUnderline.GetColor())));
}
}
void RtfAttributeOutput::CharWeight(const SvxWeightItem& rWeight)
{
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_B);
if (rWeight.GetWeight() != WEIGHT_BOLD)
m_aStyles.append(sal_Int32(0));
}
void RtfAttributeOutput::CharAutoKern(const SvxAutoKernItem& rAutoKern)
{
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_KERNING);
m_aStyles.append(static_cast<sal_Int32>(rAutoKern.GetValue() ? 1 : 0));
}
void RtfAttributeOutput::CharAnimatedText(const SvxBlinkItem& rBlink)
{
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ANIMTEXT);
m_aStyles.append(static_cast<sal_Int32>(rBlink.GetValue() ? 2 : 0));
}
void RtfAttributeOutput::CharBackground(const SvxBrushItem& rBrush)
{
if (!rBrush.GetColor().IsTransparent())
{
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CHCBPAT);
m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(rBrush.GetColor())));
}
}
void RtfAttributeOutput::CharFontCJK(const SvxFontItem& rFont)
{
// Insert \dbch in MoveCharacterProperties
m_aStylesAssocDbch.append(OOO_STRING_SVTOOLS_RTF_AF);
m_aStylesAssocDbch.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont)));
}
void RtfAttributeOutput::CharFontSizeCJK(const SvxFontHeightItem& rFontSize)
{
CharFontSize(rFontSize);
}
void RtfAttributeOutput::CharLanguageCJK(const SvxLanguageItem& rLanguageItem)
{
CharLanguage(rLanguageItem);
}
void RtfAttributeOutput::CharPostureCJK(const SvxPostureItem& rPosture)
{
m_aStylesAssocDbch.append(OOO_STRING_SVTOOLS_RTF_I);
if (rPosture.GetPosture() == ITALIC_NONE)
m_aStylesAssocDbch.append(sal_Int32(0));
}
void RtfAttributeOutput::CharWeightCJK(const SvxWeightItem& rWeight)
{
m_aStylesAssocDbch.append(OOO_STRING_SVTOOLS_RTF_AB);
if (rWeight.GetWeight() != WEIGHT_BOLD)
m_aStylesAssocDbch.append(sal_Int32(0));
}
void RtfAttributeOutput::CharFontCTL(const SvxFontItem& rFont)
{
// Insert \rtlch in MoveCharacterProperties
m_aStylesAssocRtlch.append(OOO_STRING_SVTOOLS_RTF_AF);
m_aStylesAssocRtlch.append(static_cast<sal_Int32>(m_rExport.m_aFontHelper.GetId(rFont)));
}
void RtfAttributeOutput::CharFontSizeCTL(const SvxFontHeightItem& rFontSize)
{
CharFontSize(rFontSize);
}
void RtfAttributeOutput::CharLanguageCTL(const SvxLanguageItem& rLanguageItem)
{
CharLanguage(rLanguageItem);
}
void RtfAttributeOutput::CharPostureCTL(const SvxPostureItem& rPosture)
{
m_aStylesAssocRtlch.append(OOO_STRING_SVTOOLS_RTF_AI);
if (rPosture.GetPosture() == ITALIC_NONE)
m_aStylesAssocRtlch.append(sal_Int32(0));
}
void RtfAttributeOutput::CharWeightCTL(const SvxWeightItem& rWeight)
{
m_aStylesAssocRtlch.append(OOO_STRING_SVTOOLS_RTF_AB);
if (rWeight.GetWeight() != WEIGHT_BOLD)
m_aStylesAssocRtlch.append(sal_Int32(0));
}
void RtfAttributeOutput::CharBidiRTL(const SfxPoolItem& /*rItem*/) {}
void RtfAttributeOutput::CharIdctHint(const SfxPoolItem& /*rItem*/) {}
void RtfAttributeOutput::CharRotate(const SvxCharRotateItem& rRotate)
{
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_HORZVERT);
m_aStyles.append(static_cast<sal_Int32>(rRotate.IsFitToLine() ? 1 : 0));
}
void RtfAttributeOutput::CharEmphasisMark(const SvxEmphasisMarkItem& rEmphasisMark)
{
FontEmphasisMark v = rEmphasisMark.GetEmphasisMark();
if (v == FontEmphasisMark::NONE)
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ACCNONE);
else if (v == (FontEmphasisMark::Dot | FontEmphasisMark::PosAbove))
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ACCDOT);
else if (v == (FontEmphasisMark::Accent | FontEmphasisMark::PosAbove))
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ACCCOMMA);
else if (v == (FontEmphasisMark::Circle | FontEmphasisMark::PosAbove))
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ACCCIRCLE);
else if (v == (FontEmphasisMark::Dot | FontEmphasisMark::PosBelow))
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ACCUNDERDOT);
}
void RtfAttributeOutput::CharTwoLines(const SvxTwoLinesItem& rTwoLines)
{
if (!rTwoLines.GetValue())
return;
sal_Unicode cStart = rTwoLines.GetStartBracket();
sal_Unicode cEnd = rTwoLines.GetEndBracket();
sal_uInt16 nType;
if (!cStart && !cEnd)
nType = 0;
else if ('{' == cStart || '}' == cEnd)
nType = 4;
else if ('<' == cStart || '>' == cEnd)
nType = 3;
else if ('[' == cStart || ']' == cEnd)
nType = 2;
else // all other kind of brackets
nType = 1;
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_TWOINONE);
m_aStyles.append(static_cast<sal_Int32>(nType));
}
void RtfAttributeOutput::CharScaleWidth(const SvxCharScaleWidthItem& rScaleWidth)
{
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CHARSCALEX);
m_aStyles.append(static_cast<sal_Int32>(rScaleWidth.GetValue()));
}
void RtfAttributeOutput::CharRelief(const SvxCharReliefItem& rRelief)
{
const char* pStr;
switch (rRelief.GetValue())
{
case FontRelief::Embossed:
pStr = OOO_STRING_SVTOOLS_RTF_EMBO;
break;
case FontRelief::Engraved:
pStr = OOO_STRING_SVTOOLS_RTF_IMPR;
break;
default:
pStr = nullptr;
break;
}
if (pStr)
m_aStyles.append(pStr);
}
void RtfAttributeOutput::CharHidden(const SvxCharHiddenItem& rHidden)
{
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_V);
if (!rHidden.GetValue())
m_aStyles.append(sal_Int32(0));
}
void RtfAttributeOutput::CharBorder(const editeng::SvxBorderLine* pAllBorder,
const sal_uInt16 nDist, const bool bShadow)
{
m_aStyles.append(
OutBorderLine(m_rExport, pAllBorder, OOO_STRING_SVTOOLS_RTF_CHBRDR, nDist,
bShadow ? SvxShadowLocation::BottomRight : SvxShadowLocation::NONE));
}
void RtfAttributeOutput::CharHighlight(const SvxBrushItem& rBrush)
{
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_HIGHLIGHT);
m_aStyles.append(static_cast<sal_Int32>(msfilter::util::TransColToIco(rBrush.GetColor())));
}
void RtfAttributeOutput::TextINetFormat(const SwFormatINetFormat& rURL)
{
if (rURL.GetValue().isEmpty())
return;
const SwCharFormat* pFormat;
const SwTextINetFormat* pTextAtr = rURL.GetTextINetFormat();
if (pTextAtr && nullptr != (pFormat = pTextAtr->GetCharFormat()))
{
sal_uInt16 nStyle = m_rExport.GetId(pFormat);
OString* pString = m_rExport.GetStyle(nStyle);
if (pString)
m_aStyles.append(*pString);
}
}
void RtfAttributeOutput::TextCharFormat(const SwFormatCharFormat& rCharFormat)
{
sal_uInt16 nStyle = m_rExport.GetId(rCharFormat.GetCharFormat());
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CS);
m_aStyles.append(static_cast<sal_Int32>(nStyle));
OString* pString = m_rExport.GetStyle(nStyle);
if (pString)
m_aStyles.append(*pString);
}
void RtfAttributeOutput::WriteTextFootnoteNumStr(const SwFormatFootnote& rFootnote)
{
if (rFootnote.GetNumStr().isEmpty())
m_aRun->append(OOO_STRING_SVTOOLS_RTF_CHFTN);
else
m_aRun->append(
msfilter::rtfutil::OutString(rFootnote.GetNumStr(), m_rExport.GetCurrentEncoding()));
}
void RtfAttributeOutput::TextFootnote_Impl(const SwFormatFootnote& rFootnote)
{
SAL_INFO("sw.rtf", __func__ << " start");
m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_SUPER " ");
EndRunProperties(nullptr);
m_aRun->append(' ');
WriteTextFootnoteNumStr(rFootnote);
m_aRun->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FOOTNOTE);
if (rFootnote.IsEndNote() || m_rExport.m_rDoc.GetFootnoteInfo().m_ePos == FTNPOS_CHAPTER)
m_aRun->append(OOO_STRING_SVTOOLS_RTF_FTNALT);
m_aRun->append(' ');
WriteTextFootnoteNumStr(rFootnote);
/*
* The footnote contains a whole paragraph, so we have to:
* 1) Reset, then later restore the contents of our run buffer and run state.
* 2) Buffer the output of the whole paragraph, as we do so for section headers already.
*/
const SwNodeIndex* pIndex = rFootnote.GetTextFootnote()->GetStartNode();
RtfStringBuffer aRun = m_aRun;
m_aRun.clear();
bool bInRunOrig = m_bInRun;
m_bInRun = false;
bool bSingleEmptyRunOrig = m_bSingleEmptyRun;
m_bSingleEmptyRun = false;
m_bBufferSectionHeaders = true;
m_rExport.WriteSpecialText(pIndex->GetIndex() + 1, pIndex->GetNode().EndOfSectionIndex(),
!rFootnote.IsEndNote() ? TXT_FTN : TXT_EDN);
m_bBufferSectionHeaders = false;
m_bInRun = bInRunOrig;
m_bSingleEmptyRun = bSingleEmptyRunOrig;
m_aRun = std::move(aRun);
m_aRun->append(m_aSectionHeaders);
m_aSectionHeaders.setLength(0);
m_aRun->append("}");
m_aRun->append("}");
SAL_INFO("sw.rtf", __func__ << " end");
}
void RtfAttributeOutput::ParaLineSpacing_Impl(short nSpace, short nMulti)
{
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SL);
m_aStyles.append(static_cast<sal_Int32>(nSpace));
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SLMULT);
m_aStyles.append(static_cast<sal_Int32>(nMulti));
}
void RtfAttributeOutput::ParaAdjust(const SvxAdjustItem& rAdjust)
{
switch (rAdjust.GetAdjust())
{
case SvxAdjust::Left:
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_QL);
break;
case SvxAdjust::Right:
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_QR);
break;
case SvxAdjust::BlockLine:
case SvxAdjust::Block:
if (rAdjust.GetLastBlock() == SvxAdjust::Block)
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_QD);
else
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_QJ);
break;
case SvxAdjust::Center:
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_QC);
break;
default:
break;
}
}
void RtfAttributeOutput::ParaSplit(const SvxFormatSplitItem& rSplit)
{
if (!rSplit.GetValue())
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_KEEP);
}
void RtfAttributeOutput::ParaWidows(const SvxWidowsItem& rWidows)
{
if (rWidows.GetValue())
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_WIDCTLPAR);
else
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_NOWIDCTLPAR);
}
void RtfAttributeOutput::ParaTabStop(const SvxTabStopItem& rTabStop)
{
tools::Long nOffset = m_rExport.GetParaTabStopOffset();
for (sal_uInt16 n = 0; n < rTabStop.Count(); n++)
{
const SvxTabStop& rTS = rTabStop[n];
if (SvxTabAdjust::Default != rTS.GetAdjustment())
{
const char* pFill = nullptr;
switch (rTS.GetFill())
{
case cDfltFillChar:
break;
case '.':
pFill = OOO_STRING_SVTOOLS_RTF_TLDOT;
break;
case '_':
pFill = OOO_STRING_SVTOOLS_RTF_TLUL;
break;
case '-':
pFill = OOO_STRING_SVTOOLS_RTF_TLTH;
break;
case '=':
pFill = OOO_STRING_SVTOOLS_RTF_TLEQ;
break;
default:
break;
}
if (pFill)
m_aStyles.append(pFill);
const char* pAdjStr = nullptr;
switch (rTS.GetAdjustment())
{
case SvxTabAdjust::Right:
pAdjStr = OOO_STRING_SVTOOLS_RTF_TQR;
break;
case SvxTabAdjust::Decimal:
pAdjStr = OOO_STRING_SVTOOLS_RTF_TQDEC;
break;
case SvxTabAdjust::Center:
pAdjStr = OOO_STRING_SVTOOLS_RTF_TQC;
break;
default:
break;
}
if (pAdjStr)
m_aStyles.append(pAdjStr);
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_TX);
m_aStyles.append(static_cast<sal_Int32>(rTS.GetTabPos() + nOffset));
}
else
{
m_aTabStop.append(OOO_STRING_SVTOOLS_RTF_DEFTAB);
m_aTabStop.append(rTabStop[0].GetTabPos());
}
}
}
void RtfAttributeOutput::ParaHyphenZone(const SvxHyphenZoneItem& rHyphenZone)
{
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_HYPHPAR);
m_aStyles.append(sal_Int32(rHyphenZone.IsHyphen()));
}
void RtfAttributeOutput::ParaNumRule_Impl(const SwTextNode* pTextNd, sal_Int32 nLvl,
sal_Int32 nNumId)
{
if (USHRT_MAX == nNumId || 0 == nNumId || nullptr == pTextNd)
return;
const SwNumRule* pRule = pTextNd->GetNumRule();
if (!pRule || !pTextNd->IsInList())
return;
SAL_WARN_IF(pTextNd->GetActualListLevel() < 0 || pTextNd->GetActualListLevel() >= MAXLEVEL,
"sw.rtf", "text node does not have valid list level");
const SwNumFormat* pFormat = pRule->GetNumFormat(nLvl);
if (!pFormat)
pFormat = &pRule->Get(nLvl);
const SfxItemSet& rNdSet = pTextNd->GetSwAttrSet();
m_aStyles.append('{');
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LISTTEXT);
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_PARD);
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_PLAIN);
m_aStyles.append(' ');
SvxFirstLineIndentItem firstLine(rNdSet.Get(RES_MARGIN_FIRSTLINE));
SvxTextLeftMarginItem leftMargin(rNdSet.Get(RES_MARGIN_TEXTLEFT));
leftMargin.SetTextLeft(leftMargin.GetTextLeft() + pFormat->GetIndentAt());
firstLine.SetTextFirstLineOffset(pFormat->GetFirstLineOffset()); //TODO: overflow
sal_uInt16 nStyle = m_rExport.GetId(pFormat->GetCharFormat());
OString* pString = m_rExport.GetStyle(nStyle);
if (pString)
m_aStyles.append(*pString);
{
OUString sText;
if (SVX_NUM_CHAR_SPECIAL == pFormat->GetNumberingType()
|| SVX_NUM_BITMAP == pFormat->GetNumberingType())
{
sal_UCS4 cBullet = pFormat->GetBulletChar();
sText = OUString(&cBullet, 1);
}
else
sText = pTextNd->GetNumString();
if (!sText.isEmpty())
{
m_aStyles.append(' ');
m_aStyles.append(msfilter::rtfutil::OutString(sText, m_rExport.GetDefaultEncoding()));
}
if (OUTLINE_RULE != pRule->GetRuleType())
{
if (!sText.isEmpty())
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_TAB);
m_aStyles.append('}');
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ILVL);
if (nLvl > 8) // RTF knows only 9 levels
{
m_aStyles.append(sal_Int32(8));
m_aStyles.append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_SOUTLVL);
m_aStyles.append(nLvl);
m_aStyles.append('}');
}
else
m_aStyles.append(nLvl);
}
else
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_TAB "}");
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LS);
m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetNumberingId(*pRule)) + 1);
m_aStyles.append(' ');
}
FormatFirstLineIndent(firstLine);
FormatTextLeftMargin(leftMargin);
}
void RtfAttributeOutput::ParaScriptSpace(const SfxBoolItem& rScriptSpace)
{
if (!rScriptSpace.GetValue())
return;
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ASPALPHA);
}
void RtfAttributeOutput::ParaHangingPunctuation(const SfxBoolItem& /*rItem*/)
{
SAL_INFO("sw.rtf", "TODO: " << __func__);
}
void RtfAttributeOutput::ParaForbiddenRules(const SfxBoolItem& /*rItem*/)
{
SAL_INFO("sw.rtf", "TODO: " << __func__);
}
void RtfAttributeOutput::ParaVerticalAlign(const SvxParaVertAlignItem& rAlign)
{
const char* pStr;
switch (rAlign.GetValue())
{
case SvxParaVertAlignItem::Align::Top:
pStr = OOO_STRING_SVTOOLS_RTF_FAHANG;
break;
case SvxParaVertAlignItem::Align::Bottom:
pStr = OOO_STRING_SVTOOLS_RTF_FAVAR;
break;
case SvxParaVertAlignItem::Align::Center:
pStr = OOO_STRING_SVTOOLS_RTF_FACENTER;
break;
case SvxParaVertAlignItem::Align::Baseline:
pStr = OOO_STRING_SVTOOLS_RTF_FAROMAN;
break;
default:
pStr = OOO_STRING_SVTOOLS_RTF_FAAUTO;
break;
}
m_aStyles.append(pStr);
}
void RtfAttributeOutput::ParaSnapToGrid(const SvxParaGridItem& /*rGrid*/)
{
SAL_INFO("sw.rtf", "TODO: " << __func__);
}
void RtfAttributeOutput::FormatFrameSize(const SwFormatFrameSize& rSize)
{
if (m_rExport.m_bOutPageDescs)
{
m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_PGWSXN);
m_aSectionBreaks.append(static_cast<sal_Int32>(rSize.GetWidth()));
m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_PGHSXN);
m_aSectionBreaks.append(static_cast<sal_Int32>(rSize.GetHeight()));
if (!m_bBufferSectionBreaks)
{
m_rExport.Strm().WriteOString(m_aSectionBreaks);
m_aSectionBreaks.setLength(0);
}
}
}
void RtfAttributeOutput::FormatPaperBin(const SvxPaperBinItem& rItem)
{
SfxPrinter* pPrinter = m_rExport.m_rDoc.getIDocumentDeviceAccess().getPrinter(true);
sal_Int16 nPaperSource = pPrinter->GetSourceIndexByPaperBin(rItem.GetValue());
m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_BINFSXN);
m_aSectionBreaks.append(static_cast<sal_Int32>(nPaperSource));
m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_BINSXN);
m_aSectionBreaks.append(static_cast<sal_Int32>(nPaperSource));
}
void RtfAttributeOutput::FormatFirstLineIndent(SvxFirstLineIndentItem const& rFirstLine)
{
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_FI);
m_aStyles.append(static_cast<sal_Int32>(rFirstLine.GetTextFirstLineOffset()));
}
void RtfAttributeOutput::FormatTextLeftMargin(SvxTextLeftMarginItem const& rTextLeftMargin)
{
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LI);
m_aStyles.append(static_cast<sal_Int32>(rTextLeftMargin.GetTextLeft()));
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LIN);
m_aStyles.append(static_cast<sal_Int32>(rTextLeftMargin.GetTextLeft()));
}
void RtfAttributeOutput::FormatRightMargin(SvxRightMarginItem const& rRightMargin)
{
// (paragraph case, this will be an else branch once others are converted)
#if 0
else
#endif
{
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_RI);
m_aStyles.append(static_cast<sal_Int32>(rRightMargin.GetRight()));
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_RIN);
m_aStyles.append(static_cast<sal_Int32>(rRightMargin.GetRight()));
}
}
void RtfAttributeOutput::FormatLRSpace(const SvxLRSpaceItem& rLRSpace)
{
if (!m_rExport.m_bOutFlyFrameAttrs)
{
if (m_rExport.m_bOutPageDescs)
{
m_aPageMargins.nLeft = 0;
m_aPageMargins.nRight = 0;
if (const SvxBoxItem* pBoxItem = m_rExport.HasItem(RES_BOX))
{
m_aPageMargins.nLeft
= pBoxItem->CalcLineSpace(SvxBoxItemLine::LEFT, /*bEvenIfNoLine*/ true);
m_aPageMargins.nRight
= pBoxItem->CalcLineSpace(SvxBoxItemLine::RIGHT, /*bEvenIfNoLine*/ true);
}
m_aPageMargins.nLeft += sal::static_int_cast<sal_uInt16>(rLRSpace.GetLeft());
m_aPageMargins.nRight += sal::static_int_cast<sal_uInt16>(rLRSpace.GetRight());
if (rLRSpace.GetLeft())
{
m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_MARGLSXN);
m_aSectionBreaks.append(static_cast<sal_Int32>(m_aPageMargins.nLeft));
}
if (rLRSpace.GetRight())
{
m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_MARGRSXN);
m_aSectionBreaks.append(static_cast<sal_Int32>(m_aPageMargins.nRight));
}
if (rLRSpace.GetGutterMargin())
{
m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_GUTTER);
m_aSectionBreaks.append(static_cast<sal_Int32>(rLRSpace.GetGutterMargin()));
}
if (!m_bBufferSectionBreaks)
{
m_rExport.Strm().WriteOString(m_aSectionBreaks);
m_aSectionBreaks.setLength(0);
}
}
else
{
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LI);
m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetTextLeft()));
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_RI);
m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetRight()));
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LIN);
m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetTextLeft()));
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_RIN);
m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetRight()));
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_FI);
m_aStyles.append(static_cast<sal_Int32>(rLRSpace.GetTextFirstLineOffset()));
}
}
else if (m_rExport.GetRTFFlySyntax())
{
// Wrap: top and bottom spacing, convert from twips to EMUs.
m_aFlyProperties.push_back(std::make_pair<OString, OString>(
"dxWrapDistLeft"_ostr,
OString::number(
o3tl::convert(rLRSpace.GetLeft(), o3tl::Length::twip, o3tl::Length::emu))));
m_aFlyProperties.push_back(std::make_pair<OString, OString>(
"dxWrapDistRight"_ostr,
OString::number(
o3tl::convert(rLRSpace.GetRight(), o3tl::Length::twip, o3tl::Length::emu))));
}
}
void RtfAttributeOutput::FormatULSpace(const SvxULSpaceItem& rULSpace)
{
if (!m_rExport.m_bOutFlyFrameAttrs)
{
if (m_rExport.m_bOutPageDescs)
{
OSL_ENSURE(m_rExport.GetCurItemSet(), "Impossible");
if (!m_rExport.GetCurItemSet())
return;
// If we export a follow page format, then our doc model has
// separate header/footer distances for the first page and the
// follow pages, but Word can have only a single distance. In case
// the two values differ, work with the value from the first page
// format to be in sync with the import.
sw::util::HdFtDistanceGlue aDistances(m_rExport.GetFirstPageItemSet()
? *m_rExport.GetFirstPageItemSet()
: *m_rExport.GetCurItemSet());
if (aDistances.m_DyaTop)
{
m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_MARGTSXN);
m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.m_DyaTop));
m_aPageMargins.nTop = aDistances.m_DyaTop;
}
if (aDistances.HasHeader())
{
m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_HEADERY);
m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.m_DyaHdrTop));
}
if (aDistances.m_DyaBottom)
{
m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_MARGBSXN);
m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.m_DyaBottom));
m_aPageMargins.nBottom = aDistances.m_DyaBottom;
}
if (aDistances.HasFooter())
{
m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_FOOTERY);
m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.m_DyaHdrBottom));
}
if (!m_bBufferSectionBreaks)
{
m_rExport.Strm().WriteOString(m_aSectionBreaks);
m_aSectionBreaks.setLength(0);
}
}
else
{
// Spacing before.
if (m_bParaBeforeAutoSpacing && m_nParaBeforeSpacing == rULSpace.GetUpper())
m_aStyles.append(LO_STRING_SVTOOLS_RTF_SBAUTO "1");
else if (m_bParaBeforeAutoSpacing && m_nParaBeforeSpacing == -1)
{
m_aStyles.append(LO_STRING_SVTOOLS_RTF_SBAUTO "0");
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SB);
m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetUpper()));
}
else
{
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SB);
m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetUpper()));
}
m_bParaBeforeAutoSpacing = false;
// Spacing after.
if (m_bParaAfterAutoSpacing && m_nParaAfterSpacing == rULSpace.GetLower())
m_aStyles.append(LO_STRING_SVTOOLS_RTF_SAAUTO "1");
else if (m_bParaAfterAutoSpacing && m_nParaAfterSpacing == -1)
{
m_aStyles.append(LO_STRING_SVTOOLS_RTF_SAAUTO "0");
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SA);
m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetLower()));
}
else
{
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SA);
m_aStyles.append(static_cast<sal_Int32>(rULSpace.GetLower()));
}
m_bParaAfterAutoSpacing = false;
// Contextual spacing.
if (rULSpace.GetContext())
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CONTEXTUALSPACE);
}
}
else if (m_rExport.GetRTFFlySyntax())
{
// Wrap: top and bottom spacing, convert from twips to EMUs.
m_aFlyProperties.push_back(std::make_pair<OString, OString>(
"dyWrapDistTop"_ostr,
OString::number(
o3tl::convert(rULSpace.GetUpper(), o3tl::Length::twip, o3tl::Length::emu))));
m_aFlyProperties.push_back(std::make_pair<OString, OString>(
"dyWrapDistBottom"_ostr,
OString::number(
o3tl::convert(rULSpace.GetLower(), o3tl::Length::twip, o3tl::Length::emu))));
}
}
void RtfAttributeOutput::FormatSurround(const SwFormatSurround& rSurround)
{
if (m_rExport.m_bOutFlyFrameAttrs && !m_rExport.GetRTFFlySyntax())
{
css::text::WrapTextMode eSurround = rSurround.GetSurround();
bool bGold = css::text::WrapTextMode_DYNAMIC == eSurround;
if (bGold)
eSurround = css::text::WrapTextMode_PARALLEL;
RTFSurround aMC(bGold, static_cast<sal_uInt8>(eSurround));
m_aRunText->append(OOO_STRING_SVTOOLS_RTF_FLYMAINCNT);
m_aRunText->append(static_cast<sal_Int32>(aMC.GetValue()));
}
else if (m_rExport.m_bOutFlyFrameAttrs && m_rExport.GetRTFFlySyntax())
{
// See DocxSdrExport::startDMLAnchorInline() for SwFormatSurround -> WR / WRK mappings.
sal_Int32 nWr = -1;
std::optional<sal_Int32> oWrk;
switch (rSurround.GetValue())
{
case css::text::WrapTextMode_NONE:
nWr = 1; // top and bottom
break;
case css::text::WrapTextMode_THROUGH:
nWr = 3; // none
break;
case css::text::WrapTextMode_PARALLEL:
nWr = 2; // around
oWrk = 0; // both sides
break;
case css::text::WrapTextMode_DYNAMIC:
default:
nWr = 2; // around
oWrk = 3; // largest
break;
}
if (rSurround.IsContour())
nWr = 4; // tight
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SHPWR);
m_rExport.Strm().WriteNumberAsString(nWr);
if (oWrk)
{
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SHPWRK);
m_rExport.Strm().WriteNumberAsString(*oWrk);
}
}
}
void RtfAttributeOutput::FormatVertOrientation(const SwFormatVertOrient& rFlyVert)
{
if (!(m_rExport.m_bOutFlyFrameAttrs && m_rExport.GetRTFFlySyntax()))
return;
switch (rFlyVert.GetRelationOrient())
{
case text::RelOrientation::PAGE_FRAME:
m_aFlyProperties.push_back(
std::make_pair<OString, OString>("posrelv"_ostr, OString::number(1)));
break;
default:
m_aFlyProperties.push_back(
std::make_pair<OString, OString>("posrelv"_ostr, OString::number(2)));
m_rExport.Strm()
.WriteOString(OOO_STRING_SVTOOLS_RTF_SHPBYPARA)
.WriteOString(OOO_STRING_SVTOOLS_RTF_SHPBYIGNORE);
break;
}
switch (rFlyVert.GetVertOrient())
{
case text::VertOrientation::TOP:
case text::VertOrientation::LINE_TOP:
m_aFlyProperties.push_back(
std::make_pair<OString, OString>("posv"_ostr, OString::number(1)));
break;
case text::VertOrientation::BOTTOM:
case text::VertOrientation::LINE_BOTTOM:
m_aFlyProperties.push_back(
std::make_pair<OString, OString>("posv"_ostr, OString::number(3)));
break;
case text::VertOrientation::CENTER:
case text::VertOrientation::LINE_CENTER:
m_aFlyProperties.push_back(
std::make_pair<OString, OString>("posv"_ostr, OString::number(2)));
break;
default:
break;
}
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SHPTOP);
m_rExport.Strm().WriteNumberAsString(rFlyVert.GetPos());
if (m_pFlyFrameSize)
{
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SHPBOTTOM);
m_rExport.Strm().WriteNumberAsString(rFlyVert.GetPos() + m_pFlyFrameSize->Height());
}
}
void RtfAttributeOutput::FormatHorizOrientation(const SwFormatHoriOrient& rFlyHori)
{
if (!(m_rExport.m_bOutFlyFrameAttrs && m_rExport.GetRTFFlySyntax()))
return;
switch (rFlyHori.GetRelationOrient())
{
case text::RelOrientation::PAGE_FRAME:
m_aFlyProperties.push_back(
std::make_pair<OString, OString>("posrelh"_ostr, OString::number(1)));
break;
default:
m_aFlyProperties.push_back(
std::make_pair<OString, OString>("posrelh"_ostr, OString::number(2)));
m_rExport.Strm()
.WriteOString(OOO_STRING_SVTOOLS_RTF_SHPBXCOLUMN)
.WriteOString(OOO_STRING_SVTOOLS_RTF_SHPBXIGNORE);
break;
}
switch (rFlyHori.GetHoriOrient())
{
case text::HoriOrientation::LEFT:
m_aFlyProperties.push_back(
std::make_pair<OString, OString>("posh"_ostr, OString::number(1)));
break;
case text::HoriOrientation::CENTER:
m_aFlyProperties.push_back(
std::make_pair<OString, OString>("posh"_ostr, OString::number(2)));
break;
case text::HoriOrientation::RIGHT:
m_aFlyProperties.push_back(
std::make_pair<OString, OString>("posh"_ostr, OString::number(3)));
break;
default:
break;
}
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SHPLEFT);
m_rExport.Strm().WriteNumberAsString(rFlyHori.GetPos());
if (m_pFlyFrameSize)
{
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SHPRIGHT);
m_rExport.Strm().WriteNumberAsString(rFlyHori.GetPos() + m_pFlyFrameSize->Width());
}
}
void RtfAttributeOutput::FormatAnchor(const SwFormatAnchor& rAnchor)
{
if (m_rExport.GetRTFFlySyntax())
return;
RndStdIds eId = rAnchor.GetAnchorId();
m_aRunText->append(OOO_STRING_SVTOOLS_RTF_FLYANCHOR);
m_aRunText->append(static_cast<sal_Int32>(eId));
switch (eId)
{
case RndStdIds::FLY_AT_PAGE:
m_aRunText->append(OOO_STRING_SVTOOLS_RTF_FLYPAGE);
m_aRunText->append(static_cast<sal_Int32>(rAnchor.GetPageNum()));
break;
case RndStdIds::FLY_AT_PARA:
case RndStdIds::FLY_AS_CHAR:
m_aRunText->append(OOO_STRING_SVTOOLS_RTF_FLYCNTNT);
break;
default:
break;
}
}
void RtfAttributeOutput::FormatBackground(const SvxBrushItem& rBrush)
{
if (m_rExport.GetRTFFlySyntax())
{
const Color& rColor = rBrush.GetColor();
// We in fact need RGB to BGR, but the transformation is symmetric.
m_aFlyProperties.push_back(std::make_pair<OString, OString>(
"fillColor"_ostr, OString::number(wwUtility::RGBToBGR(rColor))));
}
else if (!rBrush.GetColor().IsTransparent())
{
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CBPAT);
m_aStyles.append(static_cast<sal_Int32>(m_rExport.GetColor(rBrush.GetColor())));
}
}
void RtfAttributeOutput::FormatFillStyle(const XFillStyleItem& rFillStyle)
{
m_oFillStyle = rFillStyle.GetValue();
}
void RtfAttributeOutput::FormatFillGradient(const XFillGradientItem& rFillGradient)
{
if (*m_oFillStyle != drawing::FillStyle_GRADIENT)
return;
m_aFlyProperties.push_back(std::make_pair<OString, OString>(
"fillType"_ostr, OString::number(7))); // Shade using the fillAngle
const basegfx::BGradient& rGradient(rFillGradient.GetGradientValue());
const basegfx::BColorStops& rColorStops(rGradient.GetColorStops());
// MCGR: It would be best to export the full MCGR definition here
// with all ColorStops in rColorStops, but rtf does not support this.
// Best thing to do and to stay compatible is to export front/back
// colors as start/end and - when more than two ColorStops are defined -
// guess that GradientStyle_AXIAL is used and thus create a "fillFocus"
// entry
// LO does linear gradients top to bottom, while MSO does bottom to top.
// LO does axial gradients inner to outer, while MSO does outer to inner.
// Conclusion: swap start and end colors (and stop emulating this with 180deg rotations).
const Color aMSOStartColor(rColorStops.back().getStopColor());
Color aMSOEndColor(rColorStops.front().getStopColor());
const sal_Int32 nAngle = toDegrees(rGradient.GetAngle()) * oox::drawingml::PER_DEGREE;
if (nAngle != 0)
{
m_aFlyProperties.push_back(
std::make_pair<OString, OString>("fillAngle"_ostr, OString::number(nAngle)));
}
bool bIsSymmetrical = true;
if (rColorStops.size() < 3)
{
if (rGradient.GetGradientStyle() != awt::GradientStyle_AXIAL)
bIsSymmetrical = false;
}
else
{
// assume what was formally GradientStyle_AXIAL, see above and also refer to
// FillModel::pushToPropMap 'fFocus' value and usage.
// The 2nd color is the in-between color, use it
aMSOEndColor = Color(rColorStops[1].getStopColor());
}
m_aFlyProperties.push_back(std::make_pair<OString, OString>(
"fillColor"_ostr, OString::number(wwUtility::RGBToBGR(aMSOStartColor))));
m_aFlyProperties.push_back(std::make_pair<OString, OString>(
"fillBackColor"_ostr, OString::number(wwUtility::RGBToBGR(aMSOEndColor))));
if (bIsSymmetrical)
{
m_aFlyProperties.push_back(
std::make_pair<OString, OString>("fillFocus"_ostr, OString::number(50)));
}
}
void RtfAttributeOutput::FormatBox(const SvxBoxItem& rBox)
{
static const SvxBoxItemLine aBorders[] = { SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT,
SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT };
static const char* aBorderNames[]
= { OOO_STRING_SVTOOLS_RTF_BRDRT, OOO_STRING_SVTOOLS_RTF_BRDRL,
OOO_STRING_SVTOOLS_RTF_BRDRB, OOO_STRING_SVTOOLS_RTF_BRDRR };
sal_uInt16 const nDist = rBox.GetSmallestDistance();
if (m_rExport.GetRTFFlySyntax())
{
// Borders: spacing to contents, convert from twips to EMUs.
m_aFlyProperties.push_back(std::make_pair<OString, OString>(
"dxTextLeft"_ostr,
OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::LEFT),
o3tl::Length::twip, o3tl::Length::emu))));
m_aFlyProperties.push_back(std::make_pair<OString, OString>(
"dyTextTop"_ostr,
OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::TOP), o3tl::Length::twip,
o3tl::Length::emu))));
m_aFlyProperties.push_back(std::make_pair<OString, OString>(
"dxTextRight"_ostr,
OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::RIGHT),
o3tl::Length::twip, o3tl::Length::emu))));
m_aFlyProperties.push_back(std::make_pair<OString, OString>(
"dyTextBottom"_ostr,
OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::BOTTOM),
o3tl::Length::twip, o3tl::Length::emu))));
const editeng::SvxBorderLine* pLeft = rBox.GetLine(SvxBoxItemLine::LEFT);
const editeng::SvxBorderLine* pRight = rBox.GetLine(SvxBoxItemLine::RIGHT);
const editeng::SvxBorderLine* pTop = rBox.GetLine(SvxBoxItemLine::TOP);
const editeng::SvxBorderLine* pBottom = rBox.GetLine(SvxBoxItemLine::BOTTOM);
if (!pLeft && !pRight && !pBottom && !pTop)
{
// fLine has default 'true', so need to write it out in case of no border.
m_aFlyProperties.push_back(std::make_pair<OString, OString>("fLine"_ostr, "0"_ostr));
return;
}
// RTF has the flags fTopLine, fBottomLine, fLeftLine and fRightLine to disable single border
// lines. But Word cannot disable single border lines. So we do not use them. In case of
// single border lines it is better to draw all four borders than drawing none. So we look
// whether a border line exists, which is effectively drawn.
const editeng::SvxBorderLine* pBorder = nullptr;
if (pTop && pTop->GetBorderLineStyle() != SvxBorderLineStyle::NONE)
pBorder = pTop;
else if (pBottom && pBottom->GetBorderLineStyle() != SvxBorderLineStyle::NONE)
pBorder = pBottom;
else if (pLeft && pLeft->GetBorderLineStyle() != SvxBorderLineStyle::NONE)
pBorder = pLeft;
else if (pRight && pRight->GetBorderLineStyle() != SvxBorderLineStyle::NONE)
pBorder = pRight;
if (!pBorder)
{
m_aFlyProperties.push_back(std::make_pair<OString, OString>("fLine"_ostr, "0"_ostr));
return;
}
const Color& rColor = pBorder->GetColor();
// We in fact need RGB to BGR, but the transformation is symmetric.
m_aFlyProperties.push_back(std::make_pair<OString, OString>(
"lineColor"_ostr, OString::number(wwUtility::RGBToBGR(rColor))));
double const fConverted(
editeng::ConvertBorderWidthToWord(pBorder->GetBorderLineStyle(), pBorder->GetWidth()));
sal_Int32 nWidth = o3tl::convert(fConverted, o3tl::Length::twip, o3tl::Length::emu);
m_aFlyProperties.push_back(
std::make_pair<OString, OString>("lineWidth"_ostr, OString::number(nWidth)));
return;
}
if (rBox.GetTop() && rBox.GetBottom() && rBox.GetLeft() && rBox.GetRight()
&& *rBox.GetTop() == *rBox.GetBottom() && *rBox.GetTop() == *rBox.GetLeft()
&& *rBox.GetTop() == *rBox.GetRight() && nDist == rBox.GetDistance(SvxBoxItemLine::TOP)
&& nDist == rBox.GetDistance(SvxBoxItemLine::LEFT)
&& nDist == rBox.GetDistance(SvxBoxItemLine::BOTTOM)
&& nDist == rBox.GetDistance(SvxBoxItemLine::RIGHT))
m_aSectionBreaks.append(
OutBorderLine(m_rExport, rBox.GetTop(), OOO_STRING_SVTOOLS_RTF_BOX, nDist));
else
{
SvxShadowLocation eShadowLocation = SvxShadowLocation::NONE;
if (const SvxShadowItem* pItem = GetExport().HasItem(RES_SHADOW))
eShadowLocation = pItem->GetLocation();
const SvxBoxItemLine* pBrd = aBorders;
const char** pBrdNms = aBorderNames;
for (int i = 0; i < 4; ++i, ++pBrd, ++pBrdNms)
{
editeng::SvxBorderLine const* const pLn = rBox.GetLine(*pBrd);
m_aSectionBreaks.append(
OutBorderLine(m_rExport, pLn, *pBrdNms, rBox.GetDistance(*pBrd), eShadowLocation));
}
}
if (!m_bBufferSectionBreaks)
{
m_aStyles.append(m_aSectionBreaks);
m_aSectionBreaks.setLength(0);
}
}
void RtfAttributeOutput::FormatColumns_Impl(sal_uInt16 nCols, const SwFormatCol& rCol, bool bEven,
SwTwips nPageSize)
{
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_COLS);
m_rExport.Strm().WriteNumberAsString(nCols);
if (rCol.GetLineAdj() != COLADJ_NONE)
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_LINEBETCOL);
if (bEven)
{
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_COLSX);
m_rExport.Strm().WriteNumberAsString(rCol.GetGutterWidth(true));
}
else
{
const SwColumns& rColumns = rCol.GetColumns();
for (sal_uInt16 n = 0; n < nCols;)
{
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_COLNO);
m_rExport.Strm().WriteNumberAsString(n + 1);
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_COLW);
m_rExport.Strm().WriteNumberAsString(rCol.CalcPrtColWidth(n, nPageSize));
if (++n != nCols)
{
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_COLSR);
m_rExport.Strm().WriteNumberAsString(rColumns[n - 1].GetRight()
+ rColumns[n].GetLeft());
}
}
}
}
void RtfAttributeOutput::FormatKeep(const SvxFormatKeepItem& rItem)
{
if (rItem.GetValue())
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_KEEPN);
}
void RtfAttributeOutput::FormatTextGrid(const SwTextGridItem& /*rGrid*/)
{
SAL_INFO("sw.rtf", "TODO: " << __func__);
}
void RtfAttributeOutput::FormatLineNumbering(const SwFormatLineNumber& rNumbering)
{
if (!rNumbering.IsCount())
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_NOLINE);
}
void RtfAttributeOutput::FormatFrameDirection(const SvxFrameDirectionItem& rDirection)
{
SvxFrameDirection nDir = rDirection.GetValue();
if (nDir == SvxFrameDirection::Environment)
nDir = GetExport().GetDefaultFrameDirection();
if (m_rExport.m_bOutPageDescs)
{
if (nDir == SvxFrameDirection::Vertical_RL_TB)
{
m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_STEXTFLOW);
m_aSectionBreaks.append(static_cast<sal_Int32>(1));
if (!m_bBufferSectionBreaks)
{
m_rExport.Strm().WriteOString(m_aSectionBreaks);
m_aSectionBreaks.setLength(0);
}
}
return;
}
if (m_rExport.GetRTFFlySyntax())
{
if (nDir == SvxFrameDirection::Vertical_RL_TB)
{
// Top to bottom non-ASCII font
m_aFlyProperties.push_back(
std::make_pair<OString, OString>("txflTextFlow"_ostr, "3"_ostr));
}
else if (rDirection.GetValue() == SvxFrameDirection::Vertical_LR_BT)
{
// Bottom to top non-ASCII font
m_aFlyProperties.push_back(
std::make_pair<OString, OString>("txflTextFlow"_ostr, "2"_ostr));
}
return;
}
if (nDir == SvxFrameDirection::Horizontal_RL_TB)
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_RTLPAR);
else
m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LTRPAR);
}
void RtfAttributeOutput::ParaGrabBag(const SfxGrabBagItem& rItem)
{
const std::map<OUString, css::uno::Any>& rMap = rItem.GetGrabBag();
for (const auto& rValue : rMap)
{
if (rValue.first == "ParaTopMarginBeforeAutoSpacing")
{
m_bParaBeforeAutoSpacing = true;
rValue.second >>= m_nParaBeforeSpacing;
m_nParaBeforeSpacing = o3tl::toTwips(m_nParaBeforeSpacing, o3tl::Length::mm100);
}
else if (rValue.first == "ParaBottomMarginAfterAutoSpacing")
{
m_bParaAfterAutoSpacing = true;
rValue.second >>= m_nParaAfterSpacing;
m_nParaAfterSpacing = o3tl::toTwips(m_nParaAfterSpacing, o3tl::Length::mm100);
}
}
}
void RtfAttributeOutput::CharGrabBag(const SfxGrabBagItem& /*rItem*/) {}
void RtfAttributeOutput::ParaOutlineLevel(const SfxUInt16Item& /*rItem*/) {}
void RtfAttributeOutput::WriteExpand(const SwField* pField)
{
OUString sCmd; // for optional Parameters
switch (pField->GetTyp()->Which())
{
//#i119803# Export user field for RTF filter
case SwFieldIds::User:
sCmd = pField->GetTyp()->GetName();
m_rExport.OutputField(pField, ww::eNONE, sCmd);
break;
default:
m_rExport.OutputField(pField, ww::eUNKNOWN, sCmd);
break;
}
}
void RtfAttributeOutput::RefField(const SwField& /*rField*/, const OUString& /*rRef*/)
{
SAL_INFO("sw.rtf", "TODO: " << __func__);
}
void RtfAttributeOutput::HiddenField(const SwField& /*rField*/)
{
SAL_INFO("sw.rtf", "TODO: " << __func__);
}
void RtfAttributeOutput::SetField(const SwField& /*rField*/, ww::eField /*eType*/,
const OUString& /*rCmd*/)
{
SAL_INFO("sw.rtf", "TODO: " << __func__);
}
void RtfAttributeOutput::PostitField(const SwField* pField)
{
const SwPostItField& rPField = *static_cast<const SwPostItField*>(pField);
OString aName = OUStringToOString(rPField.GetName(), RTL_TEXTENCODING_UTF8);
auto it = m_rOpenedAnnotationMarksIds.find(aName);
if (it != m_rOpenedAnnotationMarksIds.end())
{
// In case this field is inside annotation marks, we want to write the
// annotation itself after the annotation mark is closed, not here.
m_aPostitFields[it->second] = &rPField;
return;
}
m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ATNID " ");
m_aRunText->append(OUStringToOString(rPField.GetInitials(), m_rExport.GetCurrentEncoding()));
m_aRunText->append("}");
m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ATNAUTHOR " ");
m_aRunText->append(OUStringToOString(rPField.GetPar1(), m_rExport.GetCurrentEncoding()));
m_aRunText->append("}");
m_aRunText->append(OOO_STRING_SVTOOLS_RTF_CHATN);
m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ANNOTATION);
if (m_nCurrentAnnotationMarkId != -1)
{
m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ATNREF " ");
m_aRunText->append(m_nCurrentAnnotationMarkId);
m_aRunText->append('}');
}
m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_ATNDATE " ");
m_aRunText->append(static_cast<sal_Int32>(sw::ms::DateTime2DTTM(rPField.GetDateTime())));
m_aRunText->append('}');
if (const OutlinerParaObject* pObject = rPField.GetTextObject())
m_rExport.SdrExporter().WriteOutliner(*pObject, TXT_ATN);
m_aRunText->append('}');
}
bool RtfAttributeOutput::DropdownField(const SwField* /*pField*/)
{
// this is handled in OutputFlyFrame_Impl()
return true;
}
bool RtfAttributeOutput::PlaceholderField(const SwField* pField)
{
m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_FIELD
"{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_FLDINST
" MACROBUTTON None ");
RunText(pField->GetPar1());
m_aRunText->append("}}");
return false; // do not expand
}
RtfAttributeOutput::RtfAttributeOutput(RtfExport& rExport)
: AttributeOutputBase("") // ConvertURL isn't used now in RTF output
, m_rExport(rExport)
, m_pPrevPageDesc(nullptr)
, m_nStyleId(0)
, m_nListId(0)
, m_bIsRTL(false)
, m_nScript(i18n::ScriptType::LATIN)
, m_bControlLtrRtl(false)
, m_nNextAnnotationMarkId(0)
, m_nCurrentAnnotationMarkId(-1)
, m_bTableCellOpen(false)
, m_nTableDepth(0)
, m_bTableAfterCell(false)
, m_nColBreakNeeded(false)
, m_bBufferSectionBreaks(false)
, m_bBufferSectionHeaders(false)
, m_bLastTable(true)
, m_bWroteCellInfo(false)
, m_bTableRowEnded(false)
, m_bIsBeforeFirstParagraph(true)
, m_bSingleEmptyRun(false)
, m_bInRun(false)
, m_bInRuby(false)
, m_pFlyFrameSize(nullptr)
, m_bParaBeforeAutoSpacing(false)
, m_nParaBeforeSpacing(0)
, m_bParaAfterAutoSpacing(false)
, m_nParaAfterSpacing(0)
{
}
RtfAttributeOutput::~RtfAttributeOutput() = default;
MSWordExportBase& RtfAttributeOutput::GetExport() { return m_rExport; }
// These are used by wwFont::WriteRtf()
/// Start the font.
void RtfAttributeOutput::StartFont(std::u16string_view rFamilyName) const
{
// write the font name hex-encoded, but without Unicode - Word at least
// cannot read *both* Unicode and fallback as written by OutString
m_rExport.Strm().WriteOString(
msfilter::rtfutil::OutString(rFamilyName, m_rExport.GetCurrentEncoding(), false));
}
/// End the font.
void RtfAttributeOutput::EndFont() const
{
m_rExport.Strm().WriteOString(";}");
m_rExport.SetCurrentEncoding(m_rExport.GetDefaultEncoding());
}
/// Alternate name for the font.
void RtfAttributeOutput::FontAlternateName(std::u16string_view rName) const
{
m_rExport.Strm()
.WriteChar('{')
.WriteOString(OOO_STRING_SVTOOLS_RTF_IGNORE)
.WriteOString(OOO_STRING_SVTOOLS_RTF_FALT)
.WriteChar(' ');
// write the font name hex-encoded, but without Unicode - Word at least
// cannot read *both* Unicode and fallback as written by OutString
m_rExport.Strm()
.WriteOString(msfilter::rtfutil::OutString(rName, m_rExport.GetCurrentEncoding(), false))
.WriteChar('}');
}
/// Font charset.
void RtfAttributeOutput::FontCharset(sal_uInt8 nCharSet) const
{
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_FCHARSET);
m_rExport.Strm().WriteNumberAsString(nCharSet);
m_rExport.Strm().WriteChar(' ');
m_rExport.SetCurrentEncoding(rtl_getTextEncodingFromWindowsCharset(nCharSet));
}
/// Font family.
void RtfAttributeOutput::FontFamilyType(FontFamily eFamily, const wwFont& rFont) const
{
m_rExport.Strm().WriteChar('{').WriteOString(OOO_STRING_SVTOOLS_RTF_F);
const char* pStr = OOO_STRING_SVTOOLS_RTF_FNIL;
switch (eFamily)
{
case FAMILY_ROMAN:
pStr = OOO_STRING_SVTOOLS_RTF_FROMAN;
break;
case FAMILY_SWISS:
pStr = OOO_STRING_SVTOOLS_RTF_FSWISS;
break;
case FAMILY_MODERN:
pStr = OOO_STRING_SVTOOLS_RTF_FMODERN;
break;
case FAMILY_SCRIPT:
pStr = OOO_STRING_SVTOOLS_RTF_FSCRIPT;
break;
case FAMILY_DECORATIVE:
pStr = OOO_STRING_SVTOOLS_RTF_FDECOR;
break;
default:
break;
}
m_rExport.Strm().WriteNumberAsString(m_rExport.m_aFontHelper.GetId(rFont)).WriteOString(pStr);
}
/// Font pitch.
void RtfAttributeOutput::FontPitchType(FontPitch ePitch) const
{
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_FPRQ);
sal_uInt16 nVal = 0;
switch (ePitch)
{
case PITCH_FIXED:
nVal = 1;
break;
case PITCH_VARIABLE:
nVal = 2;
break;
default:
break;
}
m_rExport.Strm().WriteNumberAsString(nVal);
}
static void lcl_AppendSP(OStringBuffer& rBuffer, std::string_view cName, std::u16string_view rValue,
const RtfExport& rExport)
{
rBuffer.append("{" OOO_STRING_SVTOOLS_RTF_SP "{"); // "{\sp{"
rBuffer.append(OOO_STRING_SVTOOLS_RTF_SN " "); //" \sn "
rBuffer.append(cName); //"PropName"
rBuffer.append("}{" OOO_STRING_SVTOOLS_RTF_SV " ");
// "}{ \sv "
rBuffer.append(msfilter::rtfutil::OutString(rValue, rExport.GetCurrentEncoding()));
rBuffer.append("}}");
}
static OString ExportPICT(const SwFlyFrameFormat* pFlyFrameFormat, const Size& rOrig,
const Size& rRendered, const Size& rMapped, const SwCropGrf& rCr,
const char* pBLIPType, const sal_uInt8* pGraphicAry, sal_uInt64 nSize,
const RtfExport& rExport, SvStream* pStream = nullptr,
bool bWritePicProp = true, const SwAttrSet* pAttrSet = nullptr)
{
OStringBuffer aRet;
if (pBLIPType && nSize && pGraphicAry)
{
bool bIsWMF = std::strcmp(pBLIPType, OOO_STRING_SVTOOLS_RTF_WMETAFILE) == 0;
aRet.append("{" OOO_STRING_SVTOOLS_RTF_PICT);
if (pFlyFrameFormat && bWritePicProp)
{
OUString sDescription = pFlyFrameFormat->GetObjDescription();
//write picture properties - wzDescription at first
//looks like: "{\*\picprop{\sp{\sn PropertyName}{\sv PropertyValue}}}"
aRet.append(
"{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_PICPROP); //"{\*\picprop
lcl_AppendSP(aRet, "wzDescription", sDescription, rExport);
OUString sName = pFlyFrameFormat->GetObjTitle();
lcl_AppendSP(aRet, "wzName", sName, rExport);
if (pAttrSet)
{
MirrorGraph eMirror = pAttrSet->Get(RES_GRFATR_MIRRORGRF).GetValue();
if (eMirror == MirrorGraph::Vertical || eMirror == MirrorGraph::Both)
// Mirror on the vertical axis is a horizontal flip.
lcl_AppendSP(aRet, "fFlipH", u"1", rExport);
}
aRet.append("}"); //"}"
}
tools::Long nXCroppedSize = rOrig.Width() - (rCr.GetLeft() + rCr.GetRight());
tools::Long nYCroppedSize = rOrig.Height() - (rCr.GetTop() + rCr.GetBottom());
/* Graphic with a zero height or width, typically copied from webpages, caused crashes. */
if (!nXCroppedSize)
nXCroppedSize = 100;
if (!nYCroppedSize)
nYCroppedSize = 100;
//Given the original size and taking cropping into account
//first, how much has the original been scaled to get the
//final rendered size
aRet.append(
OOO_STRING_SVTOOLS_RTF_PICSCALEX
+ OString::number(static_cast<sal_Int32>((100 * rRendered.Width()) / nXCroppedSize))
+ OOO_STRING_SVTOOLS_RTF_PICSCALEY
+ OString::number(static_cast<sal_Int32>((100 * rRendered.Height()) / nYCroppedSize))
+ OOO_STRING_SVTOOLS_RTF_PICCROPL + OString::number(rCr.GetLeft())
+ OOO_STRING_SVTOOLS_RTF_PICCROPR + OString::number(rCr.GetRight())
+ OOO_STRING_SVTOOLS_RTF_PICCROPT + OString::number(rCr.GetTop())
+ OOO_STRING_SVTOOLS_RTF_PICCROPB + OString::number(rCr.GetBottom())
+ OOO_STRING_SVTOOLS_RTF_PICW + OString::number(static_cast<sal_Int32>(rMapped.Width()))
+ OOO_STRING_SVTOOLS_RTF_PICH
+ OString::number(static_cast<sal_Int32>(rMapped.Height()))
+ OOO_STRING_SVTOOLS_RTF_PICWGOAL
+ OString::number(static_cast<sal_Int32>(rOrig.Width()))
+ OOO_STRING_SVTOOLS_RTF_PICHGOAL
+ OString::number(static_cast<sal_Int32>(rOrig.Height()))
+ pBLIPType);
if (bIsWMF)
{
aRet.append(sal_Int32(8));
msfilter::rtfutil::StripMetafileHeader(pGraphicAry, nSize);
}
aRet.append(SAL_NEWLINE_STRING);
if (pStream)
{
pStream->WriteOString(aRet);
aRet.setLength(0);
}
if (pStream)
msfilter::rtfutil::WriteHex(pGraphicAry, nSize, pStream);
else
aRet.append(msfilter::rtfutil::WriteHex(pGraphicAry, nSize));
aRet.append('}');
if (pStream)
{
pStream->WriteOString(aRet);
aRet.setLength(0);
}
}
return aRet.makeStringAndClear();
}
void RtfAttributeOutput::FlyFrameOLEReplacement(const SwFlyFrameFormat* pFlyFrameFormat,
SwOLENode& rOLENode, const Size& rSize)
{
m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_SHPPICT);
Size aSize(rOLENode.GetTwipSize());
Size aRendered(aSize);
aRendered.setWidth(rSize.Width());
aRendered.setHeight(rSize.Height());
const Graphic* pGraphic = rOLENode.GetGraphic();
Size aMapped(pGraphic->GetPrefSize());
auto& rCr = rOLENode.GetAttr(RES_GRFATR_CROPGRF);
const char* pBLIPType = OOO_STRING_SVTOOLS_RTF_PNGBLIP;
const sal_uInt8* pGraphicAry = nullptr;
SvMemoryStream aStream;
if (GraphicConverter::Export(aStream, *pGraphic, ConvertDataFormat::PNG) != ERRCODE_NONE)
SAL_WARN("sw.rtf", "failed to export the graphic");
sal_uInt64 nSize = aStream.TellEnd();
pGraphicAry = static_cast<sal_uInt8 const*>(aStream.GetData());
m_aRunText->append(ExportPICT(pFlyFrameFormat, aSize, aRendered, aMapped, rCr, pBLIPType,
pGraphicAry, nSize, m_rExport));
m_aRunText->append("}"); // shppict
m_aRunText->append("{" OOO_STRING_SVTOOLS_RTF_NONSHPPICT);
pBLIPType = OOO_STRING_SVTOOLS_RTF_WMETAFILE;
SvMemoryStream aWmfStream;
if (GraphicConverter::Export(aWmfStream, *pGraphic, ConvertDataFormat::WMF) != ERRCODE_NONE)
SAL_WARN("sw.rtf", "failed to export the graphic");
nSize = aWmfStream.TellEnd();
pGraphicAry = static_cast<sal_uInt8 const*>(aWmfStream.GetData());
m_aRunText->append(ExportPICT(pFlyFrameFormat, aSize, aRendered, aMapped, rCr, pBLIPType,
pGraphicAry, nSize, m_rExport));
m_aRunText->append("}"); // nonshppict
}
bool RtfAttributeOutput::FlyFrameOLEMath(const SwFlyFrameFormat* pFlyFrameFormat,
SwOLENode& rOLENode, const Size& rSize)
{
uno::Reference<embed::XEmbeddedObject> xObj(rOLENode.GetOLEObj().GetOleRef());
sal_Int64 nAspect = rOLENode.GetAspect();
svt::EmbeddedObjectRef aObjRef(xObj, nAspect);
SvGlobalName aObjName(aObjRef->getClassID());
if (!SotExchange::IsMath(aObjName))
return false;
m_aRunText->append("{" LO_STRING_SVTOOLS_RTF_MMATH " ");
uno::Reference<util::XCloseable> xClosable = xObj->getComponent();
if (!xClosable.is())
return false;
auto pBase = dynamic_cast<oox::FormulaImExportBase*>(xClosable.get());
SAL_WARN_IF(!pBase, "sw.rtf", "Math OLE object cannot write out RTF");
if (pBase)
{
OStringBuffer aBuf;
pBase->writeFormulaRtf(aBuf, m_rExport.GetCurrentEncoding());
m_aRunText->append(aBuf);
}
// Replacement graphic.
m_aRunText->append("{" LO_STRING_SVTOOLS_RTF_MMATHPICT " ");
FlyFrameOLEReplacement(pFlyFrameFormat, rOLENode, rSize);
m_aRunText->append("}"); // mmathPict
m_aRunText->append("}"); // mmath
return true;
}
void RtfAttributeOutput::FlyFrameOLE(const SwFlyFrameFormat* pFlyFrameFormat, SwOLENode& rOLENode,
const Size& rSize)
{
if (FlyFrameOLEMath(pFlyFrameFormat, rOLENode, rSize))
return;
FlyFrameOLEReplacement(pFlyFrameFormat, rOLENode, rSize);
}
void RtfAttributeOutput::FlyFrameGraphic(const SwFlyFrameFormat* pFlyFrameFormat,
const SwGrfNode* pGrfNode)
{
SvMemoryStream aStream;
const sal_uInt8* pGraphicAry = nullptr;
sal_uInt32 nSize = 0;
const Graphic& rGraphic(pGrfNode->GetGrf());
// If there is no graphic there is not much point in parsing it
if (rGraphic.GetType() == GraphicType::NONE)
return;
ConvertDataFormat aConvertDestinationFormat = ConvertDataFormat::WMF;
const char* pConvertDestinationBLIPType = OOO_STRING_SVTOOLS_RTF_WMETAFILE;
GfxLink aGraphicLink;
const char* pBLIPType = nullptr;
if (rGraphic.IsGfxLink())
{
aGraphicLink = rGraphic.GetGfxLink();
nSize = aGraphicLink.GetDataSize();
pGraphicAry = aGraphicLink.GetData();
switch (aGraphicLink.GetType())
{
// #i15508# trying to add BMP type for better exports, need to check if this works
// checked, does not work. Also need to reset pGraphicAry to NULL to force conversion
// to PNG, else the BMP array will be used.
// It may work using direct DIB data, but that needs to be checked eventually
//
// #i15508# before GfxLinkType::NativeBmp was added the graphic data
// (to be hold in pGraphicAry) was not available; thus for now to stay
// compatible, keep it that way by assigning NULL value to pGraphicAry
case GfxLinkType::NativeBmp:
// pBLIPType = OOO_STRING_SVTOOLS_RTF_WBITMAP;
pGraphicAry = nullptr;
break;
case GfxLinkType::NativeJpg:
pBLIPType = OOO_STRING_SVTOOLS_RTF_JPEGBLIP;
break;
case GfxLinkType::NativePng:
pBLIPType = OOO_STRING_SVTOOLS_RTF_PNGBLIP;
break;
case GfxLinkType::NativeWmf:
pBLIPType = aGraphicLink.IsEMF() ? OOO_STRING_SVTOOLS_RTF_EMFBLIP
: OOO_STRING_SVTOOLS_RTF_WMETAFILE;
break;
case GfxLinkType::NativeGif:
// GIF is not supported by RTF, but we override default conversion to WMF, PNG seems fits better here.
aConvertDestinationFormat = ConvertDataFormat::PNG;
pConvertDestinationBLIPType = OOO_STRING_SVTOOLS_RTF_PNGBLIP;
break;
default:
break;
}
}
GraphicType eGraphicType = rGraphic.GetType();
if (!pGraphicAry)
{
if (ERRCODE_NONE
== GraphicConverter::Export(aStream, rGraphic,
(eGraphicType == GraphicType::Bitmap)
? ConvertDataFormat::PNG
: ConvertDataFormat::WMF))
{
pBLIPType = (eGraphicType == GraphicType::Bitmap) ? OOO_STRING_SVTOOLS_RTF_PNGBLIP
: OOO_STRING_SVTOOLS_RTF_WMETAFILE;
nSize = aStream.TellEnd();
pGraphicAry = static_cast<sal_uInt8 const*>(aStream.GetData());
}
}
Size aMapped(eGraphicType == GraphicType::Bitmap ? rGraphic.GetSizePixel()
: rGraphic.GetPrefSize());
auto& rCr = pGrfNode->GetAttr(RES_GRFATR_CROPGRF);
//Get original size in twips
Size aSize(pGrfNode->GetTwipSize());
Size aRendered(aSize);
const SwFormatFrameSize& rS = pFlyFrameFormat->GetFrameSize();
aRendered.setWidth(rS.GetWidth());
aRendered.setHeight(rS.GetHeight());
ww8::Frame* pFrame = nullptr;
for (auto& rFrame : m_rExport.m_aFrames)
{
if (pFlyFrameFormat == &rFrame.GetFrameFormat())
{
pFrame = &rFrame;
break;
}
}
const SwAttrSet* pAttrSet = pGrfNode->GetpSwAttrSet();
if (pFrame && !pFrame->IsInline())
{
m_rExport.Strm().WriteOString(
"{" OOO_STRING_SVTOOLS_RTF_SHP
"{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_SHPINST);
m_pFlyFrameSize = &aRendered;
m_rExport.m_pParentFrame = pFrame;
m_rExport.m_bOutFlyFrameAttrs = true;
m_rExport.SetRTFFlySyntax(true);
m_rExport.OutputFormat(pFrame->GetFrameFormat(), false, false, true);
m_rExport.m_bOutFlyFrameAttrs = false;
m_rExport.SetRTFFlySyntax(false);
m_rExport.m_pParentFrame = nullptr;
m_pFlyFrameSize = nullptr;
std::vector<std::pair<OString, OString>> aFlyProperties{
{ "shapeType", OString::number(ESCHER_ShpInst_PictureFrame) },
{ "wzDescription", msfilter::rtfutil::OutString(pFlyFrameFormat->GetObjDescription(),
m_rExport.GetCurrentEncoding()) },
{ "wzName", msfilter::rtfutil::OutString(pFlyFrameFormat->GetObjTitle(),
m_rExport.GetCurrentEncoding()) }
};
// If we have a wrap polygon, then handle that here.
if (pFlyFrameFormat->GetSurround().IsContour())
{
if (const SwNoTextNode* pNd
= sw::util::GetNoTextNodeFromSwFrameFormat(*pFlyFrameFormat))
{
const tools::PolyPolygon* pPolyPoly = pNd->HasContour();
if (pPolyPoly && pPolyPoly->Count())
{
tools::Polygon aPoly = sw::util::CorrectWordWrapPolygonForExport(
*pPolyPoly, pNd, /*bCorrectCrop=*/true);
OStringBuffer aVerticies;
for (sal_uInt16 i = 0; i < aPoly.GetSize(); ++i)
aVerticies.append(";(" + OString::number(aPoly[i].X()) + ","
+ OString::number(aPoly[i].Y()) + ")");
aFlyProperties.push_back(std::make_pair<OString, OString>(
"pWrapPolygonVertices"_ostr,
"8;" + OString::number(aPoly.GetSize()) + aVerticies));
}
}
}
// Below text, behind document, opaque: they all refer to the same thing.
if (!pFlyFrameFormat->GetOpaque().GetValue())
aFlyProperties.push_back(
std::make_pair<OString, OString>("fBehindDocument"_ostr, "1"_ostr));
if (pAttrSet)
{
if (Degree10 nRot10 = pAttrSet->Get(RES_GRFATR_ROTATION).GetValue())
{
// See writerfilter::rtftok::RTFSdrImport::applyProperty(),
// positive rotation angles are clockwise in RTF, we have them
// as counter-clockwise.
// Additionally, RTF type is 0..360*2^16, our is 0..360*10.
sal_Int32 nRot = nRot10.get() * -1 * RTF_MULTIPLIER / 10;
aFlyProperties.emplace_back("rotation", OString::number(nRot));
}
}
for (const std::pair<OString, OString>& rPair : aFlyProperties)
{
m_rExport.Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_SP "{");
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_SN " ");
m_rExport.Strm().WriteOString(rPair.first);
m_rExport.Strm().WriteOString("}{" OOO_STRING_SVTOOLS_RTF_SV " ");
m_rExport.Strm().WriteOString(rPair.second);
m_rExport.Strm().WriteOString("}}");
}
m_rExport.Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_SP "{" OOO_STRING_SVTOOLS_RTF_SN
" pib"
"}{" OOO_STRING_SVTOOLS_RTF_SV " ");
}
bool bWritePicProp = !pFrame || pFrame->IsInline();
if (pBLIPType)
ExportPICT(pFlyFrameFormat, aSize, aRendered, aMapped, rCr, pBLIPType, pGraphicAry, nSize,
m_rExport, &m_rExport.Strm(), bWritePicProp, pAttrSet);
else
{
aStream.Seek(0);
if (GraphicConverter::Export(aStream, rGraphic, aConvertDestinationFormat) != ERRCODE_NONE)
SAL_WARN("sw.rtf", "failed to export the graphic");
pBLIPType = pConvertDestinationBLIPType;
nSize = aStream.TellEnd();
pGraphicAry = static_cast<sal_uInt8 const*>(aStream.GetData());
ExportPICT(pFlyFrameFormat, aSize, aRendered, aMapped, rCr, pBLIPType, pGraphicAry, nSize,
m_rExport, &m_rExport.Strm(), bWritePicProp, pAttrSet);
}
if (pFrame && !pFrame->IsInline())
m_rExport.Strm().WriteOString("}}}}"); // Close SV, SP, SHPINST and SHP.
m_rExport.Strm().WriteOString(SAL_NEWLINE_STRING);
}
void RtfAttributeOutput::BulletDefinition(int /*nId*/, const Graphic& rGraphic, Size aSize)
{
m_rExport.Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_SHPPICT);
m_rExport.Strm().WriteOString("{" OOO_STRING_SVTOOLS_RTF_PICT OOO_STRING_SVTOOLS_RTF_PNGBLIP);
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_PICWGOAL);
m_rExport.Strm().WriteNumberAsString(aSize.Width());
m_rExport.Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_PICHGOAL);
m_rExport.Strm().WriteNumberAsString(aSize.Height());
m_rExport.Strm().WriteOString(SAL_NEWLINE_STRING);
const sal_uInt8* pGraphicAry = nullptr;
SvMemoryStream aStream;
if (GraphicConverter::Export(aStream, rGraphic, ConvertDataFormat::PNG) != ERRCODE_NONE)
SAL_WARN("sw.rtf", "failed to export the numbering picture bullet");
sal_uInt64 nSize = aStream.TellEnd();
pGraphicAry = static_cast<sal_uInt8 const*>(aStream.GetData());
msfilter::rtfutil::WriteHex(pGraphicAry, nSize, &m_rExport.Strm());
m_rExport.Strm().WriteOString("}}"); // pict, shppict
}
void RtfAttributeOutput::SectionRtlGutter(const SfxBoolItem& rRtlGutter)
{
if (!rRtlGutter.GetValue())
{
return;
}
m_rExport.Strm().WriteOString(LO_STRING_SVTOOLS_RTF_RTLGUTTER);
}
void RtfAttributeOutput::TextLineBreak(const SwFormatLineBreak& rLineBreak)
{
// Text wrapping break of type:
m_aStyles.append(LO_STRING_SVTOOLS_RTF_LBR);
m_aStyles.append(static_cast<sal_Int32>(rLineBreak.GetEnumValue()));
// Write the linebreak itself.
RunText("\x0b");
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */