/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * Version: MPL 1.1 / GPLv3+ / LGPLv3+ * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License or as specified alternatively below. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * Major Contributor(s): * Copyright (C) 2012 Miklos Vajna (SUSE, Inc.) * * All Rights Reserved. * * For minor contributions see the git repository. * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 3 or later (the "GPLv3+"), or * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"), * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable * instead of those above. */ #include "rtfexport.hxx" #include #include #include SmRtfExport::SmRtfExport(const SmNode* pIn) : m_pTree(pIn) , m_pBuffer(0) { } bool SmRtfExport::ConvertFromStarMath(OStringBuffer& rBuffer) { if (!m_pTree) return false; m_pBuffer = &rBuffer; m_pBuffer->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE "\\moMath"); HandleNode(m_pTree, 0); m_pBuffer->append("}"); // moMath return true; } // NOTE: This is still work in progress and unfinished, but it already covers a good // part of the rtf math stuff. void SmRtfExport::HandleNode(const SmNode* pNode, int nLevel) { SAL_INFO("starmath.rtf", "Node: " << nLevel << " " << int(pNode->GetType()) << " " << pNode->GetNumSubNodes()); switch(pNode->GetType()) { case NATTRIBUT: HandleAttribute( static_cast< const SmAttributNode* >( pNode ), nLevel ); break; case NTEXT: HandleText(pNode,nLevel); break; case NVERTICAL_BRACE: HandleVerticalBrace(static_cast(pNode), nLevel); break; case NBRACE: HandleBrace( static_cast< const SmBraceNode* >( pNode ), nLevel ); break; case NOPER: HandleOperator(static_cast(pNode), nLevel); break; case NUNHOR: HandleUnaryOperation(static_cast(pNode), nLevel); break; case NBINHOR: HandleBinaryOperation(static_cast(pNode), nLevel); break; case NBINVER: HandleFractions(pNode, nLevel); break; case NROOT: HandleRoot(static_cast(pNode), nLevel); break; case NMATH: HandleMath(pNode, nLevel); break; case NSUBSUP: HandleSubSupScript(static_cast(pNode), nLevel); break; case NEXPRESSION: HandleAllSubNodes(pNode, nLevel); break; case NTABLE: //Root Node, PILE equivalent, i.e. vertical stack HandleTable(pNode,nLevel); break; case NMATRIX: HandleMatrix(static_cast(pNode), nLevel); break; case NLINE: HandleAllSubNodes(pNode, nLevel); break; case NPLACE: // explicitly do nothing, MSOffice treats that as a placeholder if item is missing break; default: SAL_INFO("starmath.rtf", "TODO: " << OSL_THIS_FUNC << " unhandled node type"); break; } } //Root Node, PILE equivalent, i.e. vertical stack void SmRtfExport::HandleTable(const SmNode* pNode, int nLevel) { if (nLevel || pNode->GetNumSubNodes() > 1) HandleVerticalStack(pNode, nLevel); else HandleAllSubNodes(pNode, nLevel); } void SmRtfExport::HandleAllSubNodes(const SmNode* pNode, int nLevel) { int size = pNode->GetNumSubNodes(); for (int i = 0; i < size; ++i) { if (!pNode->GetSubNode(i)) { OSL_FAIL("Subnode is NULL, parent node not handled properly"); continue; } HandleNode(pNode->GetSubNode(i), nLevel + 1); } } void SmRtfExport::HandleVerticalStack(const SmNode* pNode, int nLevel) { m_pBuffer->append("{\\meqArr "); int size = pNode->GetNumSubNodes(); for (int i = 0; i < size; ++i) { m_pBuffer->append("{\\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("{\\mr "); SmTextNode* pTemp=(SmTextNode* )pNode; SAL_INFO("starmath.rtf", "Text: " << pTemp->GetText()); for (xub_StrLen i = 0; i < pTemp->GetText().Len(); i++) { sal_uInt16 nChar = pTemp->GetText().GetChar(i); OUString aValue(SmTextNode::ConvertSymbolToUnicode(nChar)); m_pBuffer->append(msfilter::rtfutil::OutString(aValue, RTL_TEXTENCODING_MS_1252)); } m_pBuffer->append("}"); // mr } void SmRtfExport::HandleFractions(const SmNode* pNode, int nLevel, const char* type) { m_pBuffer->append("{\\mf "); if (type) { m_pBuffer->append("{\\mfPr "); m_pBuffer->append("{\\mtype "); m_pBuffer->append(type); m_pBuffer->append("}"); // mtype m_pBuffer->append("}"); // mfPr } OSL_ASSERT(pNode->GetNumSubNodes() == 3); m_pBuffer->append("{\\mnum "); HandleNode(pNode->GetSubNode(0), nLevel + 1); m_pBuffer->append("}"); // mnum m_pBuffer->append("{\\mden "); HandleNode(pNode->GetSubNode(2), nLevel + 1); m_pBuffer->append("}"); // mden m_pBuffer->append("}"); // mf } void SmRtfExport::HandleUnaryOperation(const SmUnHorNode* pNode, int nLevel) { HandleAllSubNodes(pNode, nLevel); } void SmRtfExport::HandleBinaryOperation(const SmBinHorNode* pNode, int nLevel) { SAL_INFO("starmath.rtf", "Binary: " << int(pNode->Symbol()->GetToken().eType)); // update HandleMath() when adding new items switch (pNode->Symbol()->GetToken().eType) { case TDIVIDEBY: return HandleFractions(pNode, nLevel, "lin"); default: HandleAllSubNodes(pNode, nLevel); break; } } void SmRtfExport::HandleAttribute(const SmAttributNode* 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 TWIDEVEC: case TBAR: { m_pBuffer->append("{\\macc "); m_pBuffer->append("{\\maccPr "); m_pBuffer->append("{\\mchr "); OUString aValue(pNode->Attribute()->GetToken().cMathChar); m_pBuffer->append(msfilter::rtfutil::OutString(aValue, RTL_TEXTENCODING_MS_1252)); m_pBuffer->append("}"); // mchr m_pBuffer->append("}"); // maccPr m_pBuffer->append("{\\me "); HandleNode( pNode->Body(), nLevel + 1 ); m_pBuffer->append("}"); // me m_pBuffer->append("}"); // macc break; } case TOVERLINE: case TUNDERLINE: m_pBuffer->append("{\\mbar "); m_pBuffer->append("{\\mbarPr "); m_pBuffer->append("{\\mpos "); m_pBuffer->append((pNode->Attribute()->GetToken().eType == TUNDERLINE ) ? "bot" : "top"); m_pBuffer->append("}"); // mpos m_pBuffer->append("}"); // mbarPr m_pBuffer->append("{\\me "); HandleNode( pNode->Body(), nLevel + 1 ); m_pBuffer->append("}"); // me m_pBuffer->append("}"); // mbar break; case TOVERSTRIKE: m_pBuffer->append("{\\mborderBox "); m_pBuffer->append("{\\mborderBoxPr "); m_pBuffer->append("{\\mhideTop 1}"); m_pBuffer->append("{\\mhideBot 1}"); m_pBuffer->append("{\\mhideLeft 1}"); m_pBuffer->append("{\\mhideRight 1}"); m_pBuffer->append("{\\mstrikeH 1}"); m_pBuffer->append("}"); // mborderBoxPr m_pBuffer->append("{\\me "); HandleNode( pNode->Body(), nLevel + 1 ); m_pBuffer->append("}"); // me m_pBuffer->append("}"); // mborderBox break; default: HandleAllSubNodes( pNode, nLevel ); break; } } void SmRtfExport::HandleMath(const SmNode* pNode, int nLevel) { SAL_INFO("starmath.rtf", "Math: " << int(pNode->GetToken().eType)); switch (pNode->GetToken().eType) { case TDIVIDEBY: case TACUTE: // these are handled elsewhere, e.g. when handling BINHOR OSL_ASSERT(false); default: HandleText(pNode, nLevel); break; } } void SmRtfExport::HandleRoot(const SmRootNode* pNode, int nLevel) { m_pBuffer->append("{\\mrad "); if (const SmNode* argument = pNode->Argument()) { m_pBuffer->append("{\\mdeg "); HandleNode(argument, nLevel + 1); m_pBuffer->append("}"); // mdeg } else { m_pBuffer->append("{\\mradPr "); m_pBuffer->append("{\\mdegHide 1}"); m_pBuffer->append("}"); // mradPr m_pBuffer->append("{\\mdeg }"); // empty but present } m_pBuffer->append("{\\me "); HandleNode(pNode->Body(), nLevel + 1); m_pBuffer->append("}"); // me m_pBuffer->append("}"); // mrad } namespace { OString mathSymbolToString(const SmNode* node) { assert(node->GetType() == NMATH); const SmTextNode* txtnode = static_cast(node); if (txtnode->GetText().Len() == 0) return OString(); assert(txtnode->GetText().Len() == 1); sal_Unicode chr = SmTextNode::ConvertSymbolToUnicode(txtnode->GetText().GetChar(0)); OUString aValue(chr); return msfilter::rtfutil::OutString(aValue, RTL_TEXTENCODING_MS_1252); } } void SmRtfExport::HandleOperator(const SmOperNode* pNode, int nLevel) { SAL_INFO("starmath.rtf", "Operator: " << int(pNode->GetToken().eType)); switch (pNode->GetToken().eType) { case TINT: case TIINT: case TIIINT: case TLINT: case TLLINT: case TLLLINT: case TPROD: case TCOPROD: case TSUM: { const SmSubSupNode* subsup = pNode->GetSubNode(0)->GetType() == NSUBSUP ? static_cast(pNode->GetSubNode(0)) : 0; const SmNode* operation = subsup ? subsup->GetBody() : pNode->GetSubNode(0); m_pBuffer->append("{\\mnary "); m_pBuffer->append("{\\mnaryPr "); m_pBuffer->append("{\\mchr "); m_pBuffer->append(mathSymbolToString(operation)); m_pBuffer->append("}"); // mchr if (!subsup || !subsup->GetSubSup(CSUB)) m_pBuffer->append("{\\msubHide 1}"); if (!subsup || !subsup->GetSubSup(CSUP)) m_pBuffer->append("{\\msupHide 1}"); m_pBuffer->append("}"); // mnaryPr if (!subsup || !subsup->GetSubSup(CSUB)) m_pBuffer->append("{\\msub }"); else { m_pBuffer->append("{\\msub "); HandleNode(subsup->GetSubSup(CSUB), nLevel + 1); m_pBuffer->append("}"); // msub } if (!subsup || !subsup->GetSubSup( CSUP )) m_pBuffer->append("{\\msup }"); else { m_pBuffer->append("{\\msup "); HandleNode(subsup->GetSubSup(CSUP), nLevel + 1); m_pBuffer->append("}"); // msup } m_pBuffer->append("{\\me "); HandleNode(pNode->GetSubNode(1), nLevel + 1); // body m_pBuffer->append("}"); // me m_pBuffer->append("}"); // mnary break; } case TLIM: m_pBuffer->append("{\\mfunc "); m_pBuffer->append("{\\mfName "); m_pBuffer->append("{\\mlimLow "); m_pBuffer->append("{\\me "); HandleNode(pNode->GetSymbol(), nLevel + 1); m_pBuffer->append("}"); // me m_pBuffer->append("{\\mlim "); if (const SmSubSupNode* subsup = pNode->GetSubNode(0)->GetType() == NSUBSUP ? static_cast( pNode->GetSubNode(0)) : 0) 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("{\\me "); HandleNode(pNode->GetSubNode(1), nLevel + 1); // body m_pBuffer->append("}"); // me m_pBuffer->append("}"); // mfunc break; default: SAL_INFO("starmath.rtf", "TODO: " << OSL_THIS_FUNC << " unhandled oper type"); break; } } void SmRtfExport::HandleSubSupScript(const SmSubSupNode* pNode, int nLevel) { // set flags to a bitfield of which sub/sup items exists int flags = (pNode->GetSubSup(CSUB) ? (1 << CSUB) : 0) | (pNode->GetSubSup(CSUP) ? (1 << CSUP) : 0) | (pNode->GetSubSup(RSUB) ? (1 << RSUB) : 0) | (pNode->GetSubSup(RSUP) ? (1 << RSUP) : 0) | (pNode->GetSubSup(LSUB) ? (1 << LSUB) : 0) | (pNode->GetSubSup(LSUP) ? (1 << LSUP) : 0); HandleSubSupScriptInternal(pNode, nLevel, flags); } 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("{\\msSubSup "); m_pBuffer->append("{\\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("{\\msub "); HandleNode(pNode->GetSubSup(RSUB), nLevel + 1); m_pBuffer->append("}"); // msub m_pBuffer->append("{\\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("{\\msSub "); m_pBuffer->append("{\\me "); flags &= ~(1 << RSUB); if (flags == 0) HandleNode(pNode->GetBody(), nLevel + 1); else HandleSubSupScriptInternal(pNode, nLevel, flags); m_pBuffer->append("}"); // me m_pBuffer->append("{\\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("{\\msSup "); m_pBuffer->append("{\\me "); flags &= ~(1 << RSUP); if (flags == 0) HandleNode(pNode->GetBody(), nLevel + 1); else HandleSubSupScriptInternal(pNode, nLevel, flags); m_pBuffer->append("}"); // me m_pBuffer->append("{\\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("{\\msPre "); m_pBuffer->append("{\\msub "); HandleNode(pNode->GetSubSup(LSUB ), nLevel + 1); m_pBuffer->append("}"); // msub m_pBuffer->append("{\\msup "); HandleNode(pNode->GetSubSup(LSUP), nLevel + 1); m_pBuffer->append("}"); // msup m_pBuffer->append("{\\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("{\\mlimLow "); m_pBuffer->append("{\\me "); flags &= ~(1 << CSUB); if (flags == 0) HandleNode(pNode->GetBody(), nLevel + 1); else HandleSubSupScriptInternal(pNode, nLevel, flags); m_pBuffer->append("}"); // me m_pBuffer->append("{\\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("{\\mlimUpp "); m_pBuffer->append("{\\me "); flags &= ~(1 << CSUP); if (flags == 0) HandleNode(pNode->GetBody(), nLevel + 1); else HandleSubSupScriptInternal(pNode, nLevel, flags); m_pBuffer->append("}"); // me m_pBuffer->append("{\\mlim "); HandleNode(pNode->GetSubSup(CSUP), nLevel + 1); m_pBuffer->append("}"); // mlim m_pBuffer->append("}"); // mlimUpp } else SAL_INFO("starmath.rtf", "TODO: " << OSL_THIS_FUNC << " unhandled subsup type"); } void SmRtfExport::HandleMatrix(const SmMatrixNode* pNode, int nLevel) { m_pBuffer->append("{\\mm "); for (int row = 0; row < pNode->GetNumRows(); ++row ) { m_pBuffer->append("{\\mmr "); for (int col = 0; col < pNode->GetNumCols(); ++col ) { m_pBuffer->append("{\\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("{\\md "); m_pBuffer->append("{\\mdPr "); m_pBuffer->append("{\\mbegChr "); m_pBuffer->append(mathSymbolToString(pNode->OpeningBrace())); m_pBuffer->append("}"); // mbegChr std::vector< const SmNode* > subnodes; if (pNode->Body()->GetType() == NBRACEBODY) { const SmBracebodyNode* body = static_cast( pNode->Body()); bool separatorWritten = false; // assume all separators are the same for (int i = 0; i < body->GetNumSubNodes(); ++i) { const SmNode* subnode = body->GetSubNode(i); if (subnode->GetType() == NMATH) { // do not write, but write what separator it is const SmMathSymbolNode* math = static_cast(subnode); if(!separatorWritten) { m_pBuffer->append("{\\msepChr "); m_pBuffer->append(mathSymbolToString(math)); m_pBuffer->append("}"); // msepChr separatorWritten = true; } } else subnodes.push_back(subnode); } } else subnodes.push_back(pNode->Body()); m_pBuffer->append("{\\mendChr "); m_pBuffer->append(mathSymbolToString(pNode->ClosingBrace())); m_pBuffer->append("}"); // mendChr m_pBuffer->append("}"); // mdPr for (unsigned int i = 0; i < subnodes.size(); ++i) { m_pBuffer->append("{\\me "); HandleNode(subnodes[ i ], 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("{\\mlimUpp "); else m_pBuffer->append("{\\mlimLow "); m_pBuffer->append("{\\me "); m_pBuffer->append("{\\mgroupChr "); m_pBuffer->append("{\\mgroupChrPr "); m_pBuffer->append("{\\mchr "); m_pBuffer->append(mathSymbolToString(pNode->Brace())); m_pBuffer->append("}"); // mchr // TODO not sure if pos and vertJc are correct m_pBuffer->append("{\\mpos ").append(top ? "top" : "bot").append("}"); m_pBuffer->append("{\\mvertJc ").append(top ? "bot" : "top").append("}"); m_pBuffer->append("}"); // mgroupChrPr m_pBuffer->append("{\\me "); HandleNode(pNode->Body(), nLevel + 1); m_pBuffer->append("}"); // me m_pBuffer->append("}"); // mgroupChr m_pBuffer->append("}"); // me m_pBuffer->append("{\\mlim "); HandleNode(pNode->Script(), nLevel + 1); m_pBuffer->append("}"); // mlim m_pBuffer->append("}"); // mlimUpp or mlimLow break; } default: SAL_INFO("starmath.rtf", "TODO: " << OSL_THIS_FUNC << " unhandled vertical brace type"); break; } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */