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