/* -*- 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 #include #include "rtfsdrexport.hxx" #include "writerwordglue.hxx" #include "ww8par.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rtfexport.hxx" #include #include 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(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(fConverted) / 2)); } aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRCF + OString::number(static_cast(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(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(&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(&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( 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( m_rExport.GetRedline(SW_MOD()->GetRedlineAuthor(pRedline->GetAuthor())))); m_aRun->append(OOO_STRING_SVTOOLS_RTF_REVDTTMDEL); } m_aRun->append(static_cast(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(nStyle))); if (pStyle) aStyle.append(*pStyle); if (!m_bBufferSectionHeaders) m_rExport.Strm().WriteOString(aStyle); else m_aSectionHeaders.append(aStyle); } void RtfAttributeOutput::TableInfoCell( 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(m_nTableDepth)); } m_bWroteCellInfo = true; } void RtfAttributeOutput::TableInfoRow(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(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(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(nTdfrmtxtTop)); sal_uInt16 nTdfrmtxtBottom = pFlyFormat->GetULSpace().GetLower(); m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TDFRMTXTBOTTOM); m_aRowDefs.append(static_cast(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(nTdfrmtxtLeft)); sal_uInt16 nTdfrmtxtRight = pFlyFormat->GetLRSpace().GetRight(); m_aRowDefs.append(LO_STRING_SVTOOLS_RTF_TDFRMTXTRIGHT); m_aRowDefs.append(static_cast(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(1)); } } void RtfAttributeOutput::TableDefinition( 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(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(pFormat->GetLRSpace().GetLeft() + rtl::math::round(nSz * fWidthRatio))); } } void RtfAttributeOutput::TableDefaultBorders( 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(rBox.GetDistance(aBorders[i]))); } } } void RtfAttributeOutput::TableBackgrounds( 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(RES_BACKGROUND); if (pTableColorProp) aColor = pTableColorProp->GetColor(); auto pRowColorProp = pTableLine->GetFrameFormat()->GetAttrSet().GetItem(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(m_rExport.GetColor(aColor))); } } void RtfAttributeOutput::TableRowRedline( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/) { } void RtfAttributeOutput::TableCellRedline( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/) { } void RtfAttributeOutput::TableHeight(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( 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(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( 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(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( 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(pFormat->GetLRSpace().GetLeft())); break; default: break; } m_aRowDefs.append(aTableAdjust); } void RtfAttributeOutput::TableSpacing( 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(pTable, pLayout); else m_pTableWrt = std::make_unique(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(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(nSlot)); if (nBase != 0x0FFF) { m_aStylesheet.append(OOO_STRING_SVTOOLS_RTF_SBASEDON); m_aStylesheet.append(static_cast(nBase)); } m_aStylesheet.append(OOO_STRING_SVTOOLS_RTF_SNEXT); m_aStylesheet.append(static_cast(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(nLvl)); m_aStyles.append(OOO_STRING_SVTOOLS_RTF_OUTLINELEVEL); m_aStyles.append(static_cast(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(!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(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& oPageRestartNumber) { if (oPageRestartNumber) { m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_PGNSTARTS); m_aSectionBreaks.append(static_cast(*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); 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& rStarts, std::vector& 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& rStarts, std::vector& 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(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 = aRun; } namespace { void lcl_TextFrameShadow(std::vector>& rFlyProperties, const SwFrameFormat& rFrameFormat) { const SvxShadowItem& aShadowItem = rFrameFormat.GetShadow(); if (aShadowItem.GetLocation() == SvxShadowLocation::NONE) return; rFlyProperties.push_back(std::make_pair("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( "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>& 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("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("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(); std::unique_ptr 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 = 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( "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& 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(&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(pObject)) { const uno::Reference& xControlModel = pFormObj->GetUnoControlModel(); uno::Reference xInfo(xControlModel, uno::UNO_QUERY); if (xInfo.is()) { uno::Reference xPropSet(xControlModel, uno::UNO_QUERY); uno::Reference 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(nTemp)); xPropSet->getPropertyValue("State") >>= nTemp; m_aRun->append(OOO_STRING_SVTOOLS_RTF_FFRES); m_aRun->append(static_cast(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(aStr.getLength())) + aStr + OStringChar(char(0x00))); xPropSet->getPropertyValue("DefaultText") >>= aTmp; aStr = OUStringToOString(aTmp, m_rExport.GetCurrentEncoding()); aBuf.append(static_cast(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 aIntSeq; uno::Sequence 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(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(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 : std::as_const(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(&rFrameFormat), rOLENd, rFrame.GetLayoutSize()); } } break; default: SAL_INFO("sw.rtf", __func__ << ": unknown type (" << static_cast(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(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(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(m_rExport.m_aFontHelper.GetId(rFont))); // Insert \hich in MoveCharacterProperties m_aStylesAssocHich.append(OOO_STRING_SVTOOLS_RTF_AF); m_aStylesAssocHich.append(static_cast(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(rFontSize.GetHeight() / 10)); break; case RES_CHRATR_CJK_FONTSIZE: m_aStylesAssocDbch.append(OOO_STRING_SVTOOLS_RTF_AFS); m_aStylesAssocDbch.append(static_cast(rFontSize.GetHeight() / 10)); break; case RES_CHRATR_CTL_FONTSIZE: m_aStylesAssocRtlch.append(OOO_STRING_SVTOOLS_RTF_AFS); m_aStylesAssocRtlch.append(static_cast(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(rKerning.GetValue() / 5)); m_aStyles.append(OOO_STRING_SVTOOLS_RTF_EXPNDTW); m_aStyles.append(static_cast(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(static_cast(rLanguage.GetLanguage()))); m_aStylesAssocLtrch.append(OOO_STRING_SVTOOLS_RTF_LANG); m_aStylesAssocLtrch.append( static_cast(static_cast(rLanguage.GetLanguage()))); break; case RES_CHRATR_CJK_LANGUAGE: m_aStylesAssocDbch.append(OOO_STRING_SVTOOLS_RTF_LANGFE); m_aStylesAssocDbch.append( static_cast(static_cast(rLanguage.GetLanguage()))); m_aStylesAssocLtrch.append(OOO_STRING_SVTOOLS_RTF_LANGFE); m_aStylesAssocLtrch.append( static_cast(static_cast(rLanguage.GetLanguage()))); break; case RES_CHRATR_CTL_LANGUAGE: m_aStylesAssocRtlch.append(OOO_STRING_SVTOOLS_RTF_ALANG); m_aStylesAssocRtlch.append( static_cast(static_cast(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(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(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(rAutoKern.GetValue() ? 1 : 0)); } void RtfAttributeOutput::CharAnimatedText(const SvxBlinkItem& rBlink) { m_aStyles.append(OOO_STRING_SVTOOLS_RTF_ANIMTEXT); m_aStyles.append(static_cast(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(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(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(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(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(nType)); } void RtfAttributeOutput::CharScaleWidth(const SvxCharScaleWidthItem& rScaleWidth) { m_aStyles.append(OOO_STRING_SVTOOLS_RTF_CHARSCALEX); m_aStyles.append(static_cast(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(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(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 = 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(nSpace)); m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SLMULT); m_aStyles.append(static_cast(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(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(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(rSize.GetWidth())); m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_PGHSXN); m_aSectionBreaks.append(static_cast(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(nPaperSource)); m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_BINSXN); m_aSectionBreaks.append(static_cast(nPaperSource)); } void RtfAttributeOutput::FormatFirstLineIndent(SvxFirstLineIndentItem const& rFirstLine) { m_aStyles.append(OOO_STRING_SVTOOLS_RTF_FI); m_aStyles.append(static_cast(rFirstLine.GetTextFirstLineOffset())); } void RtfAttributeOutput::FormatTextLeftMargin(SvxTextLeftMarginItem const& rTextLeftMargin) { m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LI); m_aStyles.append(static_cast(rTextLeftMargin.GetTextLeft())); m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LIN); m_aStyles.append(static_cast(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(rRightMargin.GetRight())); m_aStyles.append(OOO_STRING_SVTOOLS_RTF_RIN); m_aStyles.append(static_cast(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(rLRSpace.GetLeft()); m_aPageMargins.nRight += sal::static_int_cast(rLRSpace.GetRight()); if (rLRSpace.GetLeft()) { m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_MARGLSXN); m_aSectionBreaks.append(static_cast(m_aPageMargins.nLeft)); } if (rLRSpace.GetRight()) { m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_MARGRSXN); m_aSectionBreaks.append(static_cast(m_aPageMargins.nRight)); } if (rLRSpace.GetGutterMargin()) { m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_GUTTER); m_aSectionBreaks.append(static_cast(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(rLRSpace.GetTextLeft())); m_aStyles.append(OOO_STRING_SVTOOLS_RTF_RI); m_aStyles.append(static_cast(rLRSpace.GetRight())); m_aStyles.append(OOO_STRING_SVTOOLS_RTF_LIN); m_aStyles.append(static_cast(rLRSpace.GetTextLeft())); m_aStyles.append(OOO_STRING_SVTOOLS_RTF_RIN); m_aStyles.append(static_cast(rLRSpace.GetRight())); m_aStyles.append(OOO_STRING_SVTOOLS_RTF_FI); m_aStyles.append(static_cast(rLRSpace.GetTextFirstLineOffset())); } } else if (m_rExport.GetRTFFlySyntax()) { // Wrap: top and bottom spacing, convert from twips to EMUs. m_aFlyProperties.push_back(std::make_pair( "dxWrapDistLeft"_ostr, OString::number( o3tl::convert(rLRSpace.GetLeft(), o3tl::Length::twip, o3tl::Length::emu)))); m_aFlyProperties.push_back(std::make_pair( "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(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(aDistances.m_DyaHdrTop)); } if (aDistances.m_DyaBottom) { m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_MARGBSXN); m_aSectionBreaks.append(static_cast(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(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(rULSpace.GetUpper())); } else { m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SB); m_aStyles.append(static_cast(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(rULSpace.GetLower())); } else { m_aStyles.append(OOO_STRING_SVTOOLS_RTF_SA); m_aStyles.append(static_cast(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( "dyWrapDistTop"_ostr, OString::number( o3tl::convert(rULSpace.GetUpper(), o3tl::Length::twip, o3tl::Length::emu)))); m_aFlyProperties.push_back(std::make_pair( "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(eSurround)); m_aRunText->append(OOO_STRING_SVTOOLS_RTF_FLYMAINCNT); m_aRunText->append(static_cast(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 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("posrelv"_ostr, OString::number(1))); break; default: m_aFlyProperties.push_back( std::make_pair("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("posv"_ostr, OString::number(1))); break; case text::VertOrientation::BOTTOM: case text::VertOrientation::LINE_BOTTOM: m_aFlyProperties.push_back( std::make_pair("posv"_ostr, OString::number(3))); break; case text::VertOrientation::CENTER: case text::VertOrientation::LINE_CENTER: m_aFlyProperties.push_back( std::make_pair("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("posrelh"_ostr, OString::number(1))); break; default: m_aFlyProperties.push_back( std::make_pair("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("posh"_ostr, OString::number(1))); break; case text::HoriOrientation::CENTER: m_aFlyProperties.push_back( std::make_pair("posh"_ostr, OString::number(2))); break; case text::HoriOrientation::RIGHT: m_aFlyProperties.push_back( std::make_pair("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(eId)); switch (eId) { case RndStdIds::FLY_AT_PAGE: m_aRunText->append(OOO_STRING_SVTOOLS_RTF_FLYPAGE); m_aRunText->append(static_cast(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( "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(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( "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 // NOTE: I also found that loading file from testTextframeGradient // "textframe-gradient.rtf" and save-as *inverts* the gradient, so I // exchanged here fillColor/fillBackColor to get the correct order const Color aStartColor(rColorStops.front().getStopColor()); m_aFlyProperties.push_back(std::make_pair( "fillColor"_ostr, OString::number(wwUtility::RGBToBGR(aStartColor)))); if (rColorStops.size() < 3) { // two-color version, use back as 2nd color const Color aEndColor(rColorStops.back().getStopColor()); m_aFlyProperties.push_back(std::make_pair( "fillBackColor"_ostr, OString::number(wwUtility::RGBToBGR(aEndColor)))); if (rGradient.GetGradientStyle() == awt::GradientStyle_AXIAL) { m_aFlyProperties.push_back( std::make_pair("fillFocus"_ostr, OString::number(50))); } } 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 const Color aEndColor(rColorStops[1].getStopColor()); m_aFlyProperties.push_back(std::make_pair( "fillBackColor"_ostr, OString::number(wwUtility::RGBToBGR(aEndColor)))); m_aFlyProperties.push_back( std::make_pair("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( "dxTextLeft"_ostr, OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::LEFT), o3tl::Length::twip, o3tl::Length::emu)))); m_aFlyProperties.push_back(std::make_pair( "dyTextTop"_ostr, OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::TOP), o3tl::Length::twip, o3tl::Length::emu)))); m_aFlyProperties.push_back(std::make_pair( "dxTextRight"_ostr, OString::number(o3tl::convert(rBox.GetDistance(SvxBoxItemLine::RIGHT), o3tl::Length::twip, o3tl::Length::emu)))); m_aFlyProperties.push_back(std::make_pair( "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("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("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( "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("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(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("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("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& 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(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(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((100 * rRendered.Width()) / nXCroppedSize)) + OOO_STRING_SVTOOLS_RTF_PICSCALEY + OString::number(static_cast((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(rMapped.Width())) + OOO_STRING_SVTOOLS_RTF_PICH + OString::number(static_cast(rMapped.Height())) + OOO_STRING_SVTOOLS_RTF_PICWGOAL + OString::number(static_cast(rOrig.Width())) + OOO_STRING_SVTOOLS_RTF_PICHGOAL + OString::number(static_cast(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(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(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 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 xClosable = xObj->getComponent(); if (!xClosable.is()) return false; auto pBase = dynamic_cast(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(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> 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( "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("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& 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(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(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(rLineBreak.GetEnumValue())); // Write the linebreak itself. RunText("\x0b"); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */