/* -*- 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/. */ #include "rtfexport.hxx" #include #include SmRtfExport::SmRtfExport(const SmNode* pIn) : SmWordExportBase(pIn) , m_pBuffer(nullptr) , m_nEncoding(RTL_TEXTENCODING_DONTKNOW) { } void SmRtfExport::ConvertFromStarMath(OStringBuffer& rBuffer, rtl_TextEncoding nEncoding) { if (!GetTree()) return; m_pBuffer = &rBuffer; m_nEncoding = nEncoding; m_pBuffer->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE LO_STRING_SVTOOLS_RTF_MOMATH " "); HandleNode(GetTree(), 0); m_pBuffer->append("}"); // moMath } // NOTE: This is still work in progress and unfinished, but it already covers a good // part of the rtf math stuff. void SmRtfExport::HandleVerticalStack(const SmNode* pNode, int nLevel) { m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MEQARR " "); int size = pNode->GetNumSubNodes(); for (int i = 0; i < size; ++i) { m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " "); HandleNode(pNode->GetSubNode(i), nLevel + 1); m_pBuffer->append("}"); // me } m_pBuffer->append("}"); // meqArr } void SmRtfExport::HandleText(const SmNode* pNode, int /*nLevel*/) { m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MR " "); if (pNode->GetToken().eType == TTEXT) // literal text m_pBuffer->append(LO_STRING_SVTOOLS_RTF_MNOR " "); auto pTemp = static_cast(pNode); SAL_INFO("starmath.rtf", "Text: " << pTemp->GetText()); for (sal_Int32 i = 0; i < pTemp->GetText().getLength(); i++) { sal_uInt16 nChar = pTemp->GetText()[i]; OUString aValue(SmTextNode::ConvertSymbolToUnicode(nChar)); m_pBuffer->append(msfilter::rtfutil::OutString(aValue, m_nEncoding)); } m_pBuffer->append("}"); // mr } void SmRtfExport::HandleFractions(const SmNode* pNode, int nLevel, const char* type) { m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MF " "); if (type) { m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MFPR " "); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MTYPE " "); m_pBuffer->append(type); m_pBuffer->append("}"); // mtype m_pBuffer->append("}"); // mfPr } assert(pNode->GetNumSubNodes() == 3); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MNUM " "); HandleNode(pNode->GetSubNode(0), nLevel + 1); m_pBuffer->append("}"); // mnum m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MDEN " "); HandleNode(pNode->GetSubNode(2), nLevel + 1); m_pBuffer->append("}"); // mden m_pBuffer->append("}"); // mf } void SmRtfExport::HandleAttribute(const SmAttributeNode* pNode, int nLevel) { switch (pNode->Attribute()->GetToken().eType) { case TCHECK: case TACUTE: case TGRAVE: case TBREVE: case TCIRCLE: case TVEC: case TTILDE: case THAT: case TDOT: case TDDOT: case TDDDOT: case TWIDETILDE: case TWIDEHAT: case TWIDEHARPOON: case TWIDEVEC: case TBAR: { m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MACC " "); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MACCPR " "); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MCHR " "); OUString aValue(pNode->Attribute()->GetToken().cMathChar); m_pBuffer->append(msfilter::rtfutil::OutString(aValue, m_nEncoding)); m_pBuffer->append("}"); // mchr m_pBuffer->append("}"); // maccPr m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " "); HandleNode(pNode->Body(), nLevel + 1); m_pBuffer->append("}"); // me m_pBuffer->append("}"); // macc break; } case TOVERLINE: case TUNDERLINE: m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MBAR " "); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MBARPR " "); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MPOS " "); m_pBuffer->append((pNode->Attribute()->GetToken().eType == TUNDERLINE) ? "bot" : "top"); m_pBuffer->append("}"); // mpos m_pBuffer->append("}"); // mbarPr m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " "); HandleNode(pNode->Body(), nLevel + 1); m_pBuffer->append("}"); // me m_pBuffer->append("}"); // mbar break; case TOVERSTRIKE: m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MBORDERBOX " "); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MBORDERBOXPR " "); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MHIDETOP " 1}"); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MHIDEBOT " 1}"); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MHIDELEFT " 1}"); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MHIDERIGHT " 1}"); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSTRIKEH " 1}"); m_pBuffer->append("}"); // mborderBoxPr m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " "); HandleNode(pNode->Body(), nLevel + 1); m_pBuffer->append("}"); // me m_pBuffer->append("}"); // mborderBox break; default: HandleAllSubNodes(pNode, nLevel); break; } } void SmRtfExport::HandleRoot(const SmRootNode* pNode, int nLevel) { m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MRAD " "); if (const SmNode* argument = pNode->Argument()) { m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MDEG " "); HandleNode(argument, nLevel + 1); m_pBuffer->append("}"); // mdeg } else { m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MRADPR " "); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MDEGHIDE " 1}"); m_pBuffer->append("}"); // mradPr m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MDEG " }"); // empty but present } m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " "); HandleNode(pNode->Body(), nLevel + 1); m_pBuffer->append("}"); // me m_pBuffer->append("}"); // mrad } namespace { OString mathSymbolToString(const SmNode* node, rtl_TextEncoding nEncoding) { assert(node->GetType() == SmNodeType::Math || node->GetType() == SmNodeType::MathIdent); auto txtnode = static_cast(node); if (txtnode->GetText().isEmpty()) return OString(); assert(txtnode->GetText().getLength() == 1); sal_Unicode chr = SmTextNode::ConvertSymbolToUnicode(txtnode->GetText()[0]); OUString aValue(chr); return msfilter::rtfutil::OutString(aValue, nEncoding); } } void SmRtfExport::HandleOperator(const SmOperNode* pNode, int nLevel) { SAL_INFO("starmath.rtf", "Operator: " << int(pNode->GetToken().eType)); switch (pNode->GetToken().eType) { case TINT: case TINTD: case TIINT: case TIIINT: case TLINT: case TLLINT: case TLLLINT: case TPROD: case TCOPROD: case TSUM: { const SmSubSupNode* subsup = pNode->GetSubNode(0)->GetType() == SmNodeType::SubSup ? static_cast(pNode->GetSubNode(0)) : nullptr; const SmNode* operation = subsup ? subsup->GetBody() : pNode->GetSubNode(0); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MNARY " "); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MNARYPR " "); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MCHR " "); m_pBuffer->append(mathSymbolToString(operation, m_nEncoding)); m_pBuffer->append("}"); // mchr if (!subsup || !subsup->GetSubSup(CSUB)) m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUBHIDE " 1}"); if (!subsup || !subsup->GetSubSup(CSUP)) m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUPHIDE " 1}"); m_pBuffer->append("}"); // mnaryPr if (!subsup || !subsup->GetSubSup(CSUB)) m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUB " }"); else { m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUB " "); HandleNode(subsup->GetSubSup(CSUB), nLevel + 1); m_pBuffer->append("}"); // msub } if (!subsup || !subsup->GetSubSup(CSUP)) m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUP " }"); else { m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUP " "); HandleNode(subsup->GetSubSup(CSUP), nLevel + 1); m_pBuffer->append("}"); // msup } m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " "); HandleNode(pNode->GetSubNode(1), nLevel + 1); // body m_pBuffer->append("}"); // me m_pBuffer->append("}"); // mnary break; } case TLIM: m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MFUNC " "); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MFNAME " "); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIMLOW " "); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " "); HandleNode(pNode->GetSymbol(), nLevel + 1); m_pBuffer->append("}"); // me m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIM " "); if (const SmSubSupNode* subsup = pNode->GetSubNode(0)->GetType() == SmNodeType::SubSup ? static_cast(pNode->GetSubNode(0)) : nullptr) if (subsup->GetSubSup(CSUB)) HandleNode(subsup->GetSubSup(CSUB), nLevel + 1); m_pBuffer->append("}"); // mlim m_pBuffer->append("}"); // mlimLow m_pBuffer->append("}"); // mfName m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " "); HandleNode(pNode->GetSubNode(1), nLevel + 1); // body m_pBuffer->append("}"); // me m_pBuffer->append("}"); // mfunc break; default: SAL_INFO("starmath.rtf", "TODO: " << __func__ << " unhandled oper type"); break; } } void SmRtfExport::HandleSubSupScriptInternal(const SmSubSupNode* pNode, int nLevel, int flags) { // rtf supports only a certain combination of sub/super scripts, but LO can have any, // so try to merge it using several tags if necessary if (flags == 0) // none return; if ((flags & (1 << RSUP | 1 << RSUB)) == (1 << RSUP | 1 << RSUB)) { // m:sSubSup m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSSUBSUP " "); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " "); flags &= ~(1 << RSUP | 1 << RSUB); if (flags == 0) HandleNode(pNode->GetBody(), nLevel + 1); else HandleSubSupScriptInternal(pNode, nLevel, flags); m_pBuffer->append("}"); // me m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUB " "); HandleNode(pNode->GetSubSup(RSUB), nLevel + 1); m_pBuffer->append("}"); // msub m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUP " "); HandleNode(pNode->GetSubSup(RSUP), nLevel + 1); m_pBuffer->append("}"); // msup m_pBuffer->append("}"); // msubSup } else if ((flags & (1 << RSUB)) == 1 << RSUB) { // m:sSub m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSSUB " "); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " "); flags &= ~(1 << RSUB); if (flags == 0) HandleNode(pNode->GetBody(), nLevel + 1); else HandleSubSupScriptInternal(pNode, nLevel, flags); m_pBuffer->append("}"); // me m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUB " "); HandleNode(pNode->GetSubSup(RSUB), nLevel + 1); m_pBuffer->append("}"); // msub m_pBuffer->append("}"); // msSub } else if ((flags & (1 << RSUP)) == 1 << RSUP) { // m:sSup m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSSUP " "); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " "); flags &= ~(1 << RSUP); if (flags == 0) HandleNode(pNode->GetBody(), nLevel + 1); else HandleSubSupScriptInternal(pNode, nLevel, flags); m_pBuffer->append("}"); // me m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUP " "); HandleNode(pNode->GetSubSup(RSUP), nLevel + 1); m_pBuffer->append("}"); // msup m_pBuffer->append("}"); // msSup } else if ((flags & (1 << LSUP | 1 << LSUB)) == (1 << LSUP | 1 << LSUB)) { // m:sPre m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSPRE " "); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUB " "); HandleNode(pNode->GetSubSup(LSUB), nLevel + 1); m_pBuffer->append("}"); // msub m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUP " "); HandleNode(pNode->GetSubSup(LSUP), nLevel + 1); m_pBuffer->append("}"); // msup m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " "); flags &= ~(1 << LSUP | 1 << LSUB); if (flags == 0) HandleNode(pNode->GetBody(), nLevel + 1); else HandleSubSupScriptInternal(pNode, nLevel, flags); m_pBuffer->append("}"); // me m_pBuffer->append("}"); // msPre } else if ((flags & (1 << CSUB)) == (1 << CSUB)) { // m:limLow looks like a good element for central superscript m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIMLOW " "); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " "); flags &= ~(1 << CSUB); if (flags == 0) HandleNode(pNode->GetBody(), nLevel + 1); else HandleSubSupScriptInternal(pNode, nLevel, flags); m_pBuffer->append("}"); // me m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIM " "); HandleNode(pNode->GetSubSup(CSUB), nLevel + 1); m_pBuffer->append("}"); // mlim m_pBuffer->append("}"); // mlimLow } else if ((flags & (1 << CSUP)) == (1 << CSUP)) { // m:limUpp looks like a good element for central superscript m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIMUPP " "); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " "); flags &= ~(1 << CSUP); if (flags == 0) HandleNode(pNode->GetBody(), nLevel + 1); else HandleSubSupScriptInternal(pNode, nLevel, flags); m_pBuffer->append("}"); // me m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIM " "); HandleNode(pNode->GetSubSup(CSUP), nLevel + 1); m_pBuffer->append("}"); // mlim m_pBuffer->append("}"); // mlimUpp } else SAL_INFO("starmath.rtf", "TODO: " << __func__ << " unhandled subsup type"); } void SmRtfExport::HandleMatrix(const SmMatrixNode* pNode, int nLevel) { m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MM " "); for (size_t row = 0; row < pNode->GetNumRows(); ++row) { m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MMR " "); for (size_t col = 0; col < pNode->GetNumCols(); ++col) { m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " "); if (const SmNode* node = pNode->GetSubNode(row * pNode->GetNumCols() + col)) HandleNode(node, nLevel + 1); m_pBuffer->append("}"); // me } m_pBuffer->append("}"); // mmr } m_pBuffer->append("}"); // mm } void SmRtfExport::HandleBrace(const SmBraceNode* pNode, int nLevel) { m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MD " "); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MDPR " "); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MBEGCHR " "); m_pBuffer->append(mathSymbolToString(pNode->OpeningBrace(), m_nEncoding)); m_pBuffer->append("}"); // mbegChr std::vector subnodes; if (pNode->Body()->GetType() == SmNodeType::Bracebody) { auto body = static_cast(pNode->Body()); bool separatorWritten = false; // assume all separators are the same for (size_t i = 0; i < body->GetNumSubNodes(); ++i) { const SmNode* subnode = body->GetSubNode(i); if (subnode->GetType() == SmNodeType::Math || subnode->GetType() == SmNodeType::MathIdent) { // do not write, but write what separator it is auto math = static_cast(subnode); if (!separatorWritten) { m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSEPCHR " "); m_pBuffer->append(mathSymbolToString(math, m_nEncoding)); m_pBuffer->append("}"); // msepChr separatorWritten = true; } } else subnodes.push_back(subnode); } } else subnodes.push_back(pNode->Body()); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MENDCHR " "); m_pBuffer->append(mathSymbolToString(pNode->ClosingBrace(), m_nEncoding)); m_pBuffer->append("}"); // mendChr m_pBuffer->append("}"); // mdPr for (const SmNode* subnode : subnodes) { m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " "); HandleNode(subnode, nLevel + 1); m_pBuffer->append("}"); // me } m_pBuffer->append("}"); // md } void SmRtfExport::HandleVerticalBrace(const SmVerticalBraceNode* pNode, int nLevel) { SAL_INFO("starmath.rtf", "Vertical: " << int(pNode->GetToken().eType)); switch (pNode->GetToken().eType) { case TOVERBRACE: case TUNDERBRACE: { bool top = (pNode->GetToken().eType == TOVERBRACE); if (top) m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIMUPP " "); else m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIMLOW " "); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " "); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MGROUPCHR " "); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MGROUPCHRPR " "); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MCHR " "); m_pBuffer->append(mathSymbolToString(pNode->Brace(), m_nEncoding)); m_pBuffer->append("}"); // mchr // TODO not sure if pos and vertJc are correct m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MPOS " ") .append(top ? "top" : "bot") .append("}"); m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MVERTJC " ") .append(top ? "bot" : "top") .append("}"); m_pBuffer->append("}"); // mgroupChrPr m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " "); HandleNode(pNode->Body(), nLevel + 1); m_pBuffer->append("}"); // me m_pBuffer->append("}"); // mgroupChr m_pBuffer->append("}"); // me m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIM " "); HandleNode(pNode->Script(), nLevel + 1); m_pBuffer->append("}"); // mlim m_pBuffer->append("}"); // mlimUpp or mlimLow break; } default: SAL_INFO("starmath.rtf", "TODO: " << __func__ << " unhandled vertical brace type"); break; } } void SmRtfExport::HandleBlank() { m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MR " "); m_pBuffer->append(" "); m_pBuffer->append("}"); // mr } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */