diff options
Diffstat (limited to 'starmath/source/mathtype.cxx')
-rw-r--r-- | starmath/source/mathtype.cxx | 3354 |
1 files changed, 3354 insertions, 0 deletions
diff --git a/starmath/source/mathtype.cxx b/starmath/source/mathtype.cxx new file mode 100644 index 000000000..518f29fc5 --- /dev/null +++ b/starmath/source/mathtype.cxx @@ -0,0 +1,3354 @@ +/* -*- 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 "mathtype.hxx" +#include "eqnolefilehdr.hxx" + +#include <filter/msfilter/classids.hxx> +#include <osl/diagnose.h> +#include <sfx2/docfile.hxx> +#include <sot/storage.hxx> +#include <array> + +//These are the default MathType sizes +constexpr std::array<sal_Int16, 7> aSizeTable { + 12, + 8, + 6, + 24, + 10, + 12, + 12, +}; + +void MathType::Init() +{ + /* + These are the default MathType italic/bold settings If mathtype is changed + from its defaults, there is nothing we can do, as this information is not + stored in the document + */ + MathTypeFont aFont; + aUserStyles.reserve(11); + for(sal_uInt8 i=1;i<=11;i++) + { + aFont.nTface = i+128; + switch (i) + { + default: + aFont.nStyle=0; + break; + case 3: + case 4: + aFont.nStyle=1; + break; + case 7: + aFont.nStyle=2; + break; + } + aUserStyles.insert(aFont); + } +} + + +/*ToDo replace with table rather than switch, returns + sal_True in the case that the char is just a char, and + sal_False if the character is an operator which must not be + placed inside the quote sequence designed to protect + against being parsed as a keyword + + General solution required to force starmath to handle + unicode math chars the way it handles its own math + chars rather than handle them as text as it will do + for the default case below, i.e. incorrect spacing + between math symbols and ordinary text e.g. 1=2 rather + than 1 = 2 + */ +bool MathType::LookupChar(sal_Unicode nChar,OUStringBuffer &rRet,sal_uInt8 nVersion, + sal_uInt8 nTypeFace) +{ + bool bRet=false; + const char *pC = nullptr; + switch(nChar) + { + case 0x0000: + pC = " none "; + break; + case 0x00ac: + pC = " neg "; + break; + case 0x00b1: + pC = " +- "; + break; + case '(': + pC = " \\( "; + break; + case ')': + pC = " \\) "; + break; + case '[': + pC = " \\[ "; + break; + case ']': + pC = " \\] "; + break; + case '.': + pC = " \".\" "; + break; + case 0xae: + if ((nVersion < 3) && (nTypeFace == 0x86)) + pC = " rightarrow "; + else + { + rRet.append(OUStringChar(nChar)); + bRet=true; + } + break; + case 0x00fb: + if ((nVersion < 3) && (nTypeFace == 0x81)) + nChar = 0xDF; + rRet.append(OUStringChar(nChar)); + bRet=true; + break; + case 'a': + if ((nVersion < 3) && (nTypeFace == 0x84)) + nChar = 0x3b1; + rRet.append(OUStringChar(nChar)); + bRet=true; + break; + case 'b': + if ((nVersion < 3) && (nTypeFace == 0x84)) + nChar = 0x3b2; + rRet.append(OUStringChar(nChar)); + bRet=true; + break; + case 'l': + if ((nVersion < 3) && (nTypeFace == 0x84)) + nChar = 0x3bb; + rRet.append(OUStringChar(nChar)); + bRet=true; + break; + case 'n': + if ((nVersion < 3) && (nTypeFace == 0x84)) + nChar = 0x3bd; + rRet.append(OUStringChar(nChar)); + bRet=true; + break; + case 'r': + if ((nVersion < 3) && (nTypeFace == 0x84)) + nChar = 0x3c1; + rRet.append(OUStringChar(nChar)); + bRet=true; + break; + case 'D': + if ((nVersion < 3) && (nTypeFace == 0x84)) + nChar = 0x394; + rRet.append(OUStringChar(nChar)); + bRet=true; + break; + case 0xa9: + if ((nVersion < 3) && (nTypeFace == 0x82)) + nChar = '\''; + rRet.append(OUStringChar(nChar)); + bRet=true; + break; + case 0x00f1: + if ((nVersion < 3) && (nTypeFace == 0x86)) + pC = " \\rangle "; + else + { + rRet.append(OUStringChar(nChar)); + bRet=true; + } + break; + case 0x00a3: + if ((nVersion < 3) && (nTypeFace == 0x86)) + pC = " <= "; + else + { + rRet.append(OUStringChar(nChar)); + bRet=true; + } + break; + case 0x00de: + if ((nVersion < 3) && (nTypeFace == 0x86)) + pC = " drarrow "; + else + { + rRet.append(OUStringChar(nChar)); + bRet=true; + } + break; + case 0x0057: + if ((nVersion < 3) && (nTypeFace == 0x85)) + pC = " %OMEGA "; + else + { + rRet.append(OUStringChar(nChar)); + bRet=true; + } + break; + case 0x007b: + pC = " lbrace "; + break; + case 0x007c: + pC = " \\lline "; + break; + case 0x007d: + pC = " rbrace "; + break; + case 0x007e: + pC = " \"~\" "; + break; + case 0x2224: + pC = " ndivides "; + break; + case 0x2225: + pC = " parallel "; + break; + case 0x00d7: + if (nVersion < 3) + pC = " cdot "; + else + pC = " times "; + break; + case 0x00f7: + pC = " div "; + break; + case 0x019b: + pC = " lambdabar "; + break; + case 0x2026: + pC = " dotslow "; + break; + case 0x2022: + pC = " cdot "; + break; + case 0x2102: + pC = " setC "; + break; + case 0x210f: + pC = " hbar "; + break; + case 0x2111: + pC = " Im "; + break; + case 0x2115: + pC = " setN "; + break; + case 0x2118: + pC = " wp "; + break; + case 0x211a: + pC = " setQ "; + break; + case 0x211c: + pC = " Re "; + break; + case 0x211d: + pC = " setR "; + break; + case 0x2124: + pC = " setZ "; + break; + case 0x2135: + pC = " aleph "; + break; + case 0x2190: + pC = " leftarrow "; + break; + case 0x2191: + pC = " uparrow "; + break; + case 0x2192: + pC = " rightarrow "; + break; + case 0x0362: + pC = " widevec "; + break; + case 0x2193: + pC = " downarrow "; + break; + case 0x21d0: + pC = " dlarrow "; + break; + case 0x21d2: + pC = " drarrow "; + break; + case 0x21d4: + pC = " dlrarrow "; + break; + case 0x2200: + pC = " forall "; + break; + case 0x2202: + pC = " partial "; + break; + case 0x2203: + pC = " exists "; + break; + case 0x2204: + pC = " notexists "; + break; + case 0x2205: + pC = " emptyset "; + break; + case 0x2207: + pC = " nabla "; + break; + case 0x2112: + pC = " laplace "; + break; + case 0x03F6: + pC = " backepsilon "; + break; + case 0x2208: // in + case 0x2209: // notin + rRet.append(" func " + OUStringChar(nChar) + " "); + break; + case 0x220d: // owns + rRet.append(u" func \u220b "); + break; + case 0x220f: + pC = " prod "; + break; + case 0x2210: + pC = " coprod "; + break; + case 0x2211: + pC = " sum "; + break; + case 0x2212: + pC = " - "; + break; + case 0x2213: + pC = " -+ "; + break; + case 0x2217: + pC = " * "; + break; + case 0x2218: + pC = " circ "; + break; + case 0x221d: + pC = " prop "; + break; + case 0x221e: + pC = " infinity "; + break; + case 0x2227: + pC = " and "; + break; + case 0x2228: + pC = " or "; + break; + case 0x2229: + pC = " intersection "; + break; + case 0x222a: + pC = " union "; + break; + case 0x222b: + pC = " int "; + break; + case 0x222c: + pC = " iint "; + break; + case 0x222d: + pC = " iiint "; + break; + case 0x222e: + pC = " lint "; + break; + case 0x222f: + pC = " llint "; + break; + case 0x2230: + pC = " lllint "; + break; + case 0x2245: + pC = " simeq "; + break; + case 0x2248: + pC = " approx "; + break; + case 0x2260: + pC = " <> "; + break; + case 0x2261: + pC = " equiv "; + break; + case 0x2264: + pC = " <= "; + break; + case 0x2265: + pC = " >= "; + break; + + case 0x227A: + pC = " prec "; + break; + case 0x227B: + pC = " succ "; + break; + case 0x227C: + pC = " preccurlyeq "; + break; + case 0x227D: + pC = " succcurlyeq "; + break; + case 0x227E: + pC = " precsim "; + break; + case 0x227F: + pC = " succsim "; + break; + case 0x2280: + pC = " nprec "; + break; + case 0x2281: + pC = " nsucc "; + break; + + case 0x2282: // subset + case 0x2283: // supset + case 0x2284: // nsubset + case 0x2285: // nsupset + case 0x2286: // subseteq + case 0x2287: // supseteq + case 0x2288: // nsubseteq + case 0x2289: // nsupseteq + case 0x22b2: // NORMAL SUBGROUP OF + case 0x22b3: // CONTAINS AS NORMAL SUBGROUP + rRet.append(" func " + OUStringChar(nChar) + " "); + break; + case 0x22a5: + pC = " ortho "; + break; + case 0x22c5: + pC = " cdot "; + break; + case 0x22ee: + pC = " dotsvert "; + break; + case 0x22ef: + pC = " dotsaxis "; + break; + case 0x22f0: + pC = " dotsup "; + break; + case 0x22f1: + pC = " dotsdown "; + break; + case MS_LANGLE: + case MS_LMATHANGLE: + pC = " langle "; + break; + case MS_RANGLE: + case MS_RMATHANGLE: + pC = " rangle "; + break; + case 0x301a: + pC = " ldbracket "; + break; + case 0x301b: + pC = " rdbracket "; + break; + case 0xe083: + rRet.append("+"); + bRet=true; + break; + case '^': + case 0xe091: + pC = " widehat "; + break; + case 0xe096: + pC = " widetilde "; + break; + case 0xe098: + pC = " widevec "; + break; + case 0xE421: + pC = " geslant "; + break; + case 0xE425: + pC = " leslant "; + break; + case 0xeb01: //no space + case 0xeb08: //normal space + bRet=true; + break; + case 0xef04: //tiny space + case 0xef05: //tiny space + case 0xeb02: //small space + case 0xeb04: //medium space + rRet.append("`"); + break; + case 0xeb05: //large space + rRet.append("~"); + break; + case 0x3a9: + pC = " %OMEGA "; + break; + default: + rRet.append(OUStringChar(nChar)); + bRet=true; + break; + } + if (pC) + rRet.appendAscii(pC); + return bRet; +} + +void MathTypeFont::AppendStyleToText(OUString &rRet) +{ + const char *pC = nullptr; + switch (nStyle) + { + default: + case 0: + break; + case 1: + pC = " ital "; + break; + case 2: + pC = " bold "; + break; + case 3: + pC = " bold italic"; + break; + } + if (pC) + rRet += OUString::createFromAscii( pC ); +} + +void MathType::TypeFaceToString(OUString &rTxt,sal_uInt8 nFace) +{ + MathTypeFont aFont(nFace); + auto aItr = aUserStyles.find(aFont); + if (aItr != aUserStyles.end()) + aFont.nStyle = aItr->nStyle; + aFont.AppendStyleToText(rTxt); +} + +bool MathType::Parse(SotStorage *pStor) +{ + tools::SvRef<SotStorageStream> xSrc = pStor->OpenSotStream( + "Equation Native", + StreamMode::STD_READ); + if ( (!xSrc.is()) || (ERRCODE_NONE != xSrc->GetError())) + return false; + return Parse(xSrc.get()); +} + +bool MathType::Parse(SvStream* pStream) +{ + pS = pStream; + pS->SetEndian( SvStreamEndian::LITTLE ); + + EQNOLEFILEHDR aHdr; + aHdr.Read(pS); + sal_uInt8 nProdVersion; + sal_uInt8 nProdSubVersion; + sal_uInt8 nPlatform; + sal_uInt8 nProduct; + pS->ReadUChar( nVersion ); + pS->ReadUChar( nPlatform ); + pS->ReadUChar( nProduct ); + pS->ReadUChar( nProdVersion ); + pS->ReadUChar( nProdSubVersion ); + + if (!pS->good() || nVersion > 3) // allow only supported versions of MathType to be parsed + return false; + + bool bRet = HandleRecords(0); + //little crude hack to close occasionally open expressions + //a sophisticated system to determine what expressions are + //opened is required, but this is as much work as rewriting + //starmaths internals. + rRet.append("{}"); + + return bRet; +} + +static void lcl_PrependDummyTerm(OUStringBuffer &rRet, sal_Int32 &rTextStart) +{ + if ((rTextStart < rRet.getLength()) && + (rRet[rTextStart] == '=') && + ((rTextStart == 0) || (rRet[ rTextStart-1 ] == '{')) + ) + { + rRet.insert(rTextStart, " {}"); + rTextStart+=3; + } +} + +static void lcl_AppendDummyTerm(OUStringBuffer &rRet) +{ + bool bOk=false; + for(int nI=rRet.getLength()-1;nI >= 0; nI--) + { + sal_Int32 nIdx = sal::static_int_cast< sal_Int32 >(nI); + sal_Unicode nChar = rRet[nIdx]; + if (nChar == ' ') + continue; + if (rRet[nIdx] != '{') + bOk=true; + break; + } + if (!bOk) //No term, use dummy + rRet.append(" {}"); +} + +void MathType::HandleNudge() +{ + sal_uInt8 nXNudge(0); + pS->ReadUChar(nXNudge); + sal_uInt8 nYNudge(0); + pS->ReadUChar(nYNudge); + if (nXNudge == 128 && nYNudge == 128) + { + sal_uInt16 nXLongNudge(0); + sal_uInt16 nYLongNudge(0); + pS->ReadUInt16(nXLongNudge); + pS->ReadUInt16(nYLongNudge); + } +} + +/* Fabulously complicated as many tokens have to be reordered and generally + * moved around from mathtypes paradigm to starmaths. */ +bool MathType::HandleRecords(int nLevel, sal_uInt8 nSelector, + sal_uInt8 nVariation, int nMatrixRows, int nMatrixCols) +{ + //depth-protect + if (nLevel > 1024) + return false; + + sal_uInt8 nTag,nRecord; + sal_uInt8 nTabType; + sal_uInt16 nTabOffset; + int i, newline=0; + bool bSilent=false; + int nPart=0; + OUString sPush,sMainTerm; + int nSetSize=0,nSetAlign=0; + int nCurRow=0,nCurCol=0; + bool bOpenString=false; + sal_Int32 nTextStart = 0; + sal_Int32 nSubSupStartPos = 0; + sal_Int32 nLastTemplateBracket=-1; + bool bRet = true; + + do + { + nTag = 0; + pS->ReadUChar( nTag ); + nRecord = nTag&0x0F; + + /*MathType strings can of course include words which + *are StarMath keywords, the simplest solution is + to escape strings of greater than len 1 with double + quotes to avoid scanning the TokenTable for matches + + Unfortunately it may turn out that the string gets + split during the handling of a character emblishment + so this special case must be handled in the + character handler case 2: + */ + if ((nRecord == CHAR) && (!bOpenString)) + { + bOpenString=true; + nTextStart = rRet.getLength(); + } + else if ((nRecord != CHAR) && bOpenString) + { + bOpenString=false; + if ((rRet.getLength() - nTextStart) > 1) + { + OUString aStr; + TypeFaceToString(aStr,nTypeFace); + aStr += "\""; + rRet.insert(nTextStart,aStr); + rRet.append("\""); + } + else if (nRecord == END && !rRet.isEmpty()) + { + sal_Unicode cChar = 0; + sal_Int32 nI = rRet.getLength()-1; + while (nI) + { + cChar = rRet[nI]; + if (cChar != ' ') + break; + --nI; + } + if ((cChar == '=') || (cChar == '+') || (cChar == '-')) + rRet.append("{}"); + } + } + + switch(nRecord) + { + case LINE: + { + if (xfLMOVE(nTag)) + HandleNudge(); + + if (newline>0) + rRet.append("\nnewline\n"); + if (!(xfNULL(nTag))) + { + switch (nSelector) + { + case tmANGLE: + if (nVariation==0) + rRet.append(" langle "); + else if (nVariation==1) + rRet.append(" \\langle "); + break; + case tmPAREN: + if (nVariation==0) + rRet.append(" left ("); + else if (nVariation==1) + rRet.append("\\("); + break; + case tmBRACE: + if ((nVariation==0) || (nVariation==1)) + rRet.append(" left lbrace "); + else + rRet.append(" left none "); + break; + case tmBRACK: + if (nVariation==0) + rRet.append(" left ["); + else if (nVariation==1) + rRet.append("\\["); + break; + case tmLBLB: + case tmLBRP: + rRet.append(" \\["); + break; + case tmBAR: + if (nVariation==0) + rRet.append(" lline "); + else if (nVariation==1) + rRet.append(" \\lline "); + break; + case tmDBAR: + if (nVariation==0) + rRet.append(" ldline "); + else if (nVariation==1) + rRet.append(" \\ldline "); + break; + case tmFLOOR: + if (nVariation == 0 || nVariation & 0x01) // tvFENCE_L + rRet.append(" left lfloor "); + else + rRet.append(" left none "); + break; + case tmCEILING: + if (nVariation==0) + rRet.append(" lceil "); + else if (nVariation==1) + rRet.append(" \\lceil "); + break; + case tmRBRB: + case tmRBLB: + rRet.append(" \\]"); + break; + case tmLPRB: + rRet.append(" \\("); + break; + case tmROOT: + if (nPart == 0) + { + if (nVariation == 0) + rRet.append(" sqrt"); + else + { + rRet.append(" nroot"); + sPush = rRet.makeStringAndClear(); + } + } + rRet.append(" {"); + break; + case tmFRACT: + if (nPart == 0) + rRet.append(" { "); + + + if (nPart == 1) + rRet.append(" over "); + rRet.append(" {"); + break; + case tmSCRIPT: + nSubSupStartPos = rRet.getLength(); + if ((nVariation == 0) || + ((nVariation == 2) && (nPart==1))) + { + lcl_AppendDummyTerm(rRet); + rRet.append(" rSup"); + } + else if ((nVariation == 1) || + ((nVariation == 2) && (nPart==0))) + { + lcl_AppendDummyTerm(rRet); + rRet.append(" rSub"); + } + rRet.append(" {"); + break; + case tmUBAR: + if (nVariation == 0) + rRet.append(" {underline "); + else if (nVariation == 1) + rRet.append(" {underline underline "); + rRet.append(" {"); + break; + case tmOBAR: + if (nVariation == 0) + rRet.append(" {overline "); + else if (nVariation == 1) + rRet.append(" {overline overline "); + rRet.append(" {"); + break; + case tmLARROW: + if (nPart == 0) + { + if (nVariation == 0) + rRet.append(" widevec ");//left arrow above + else if (nVariation == 1) + rRet.append(" widevec ");//left arrow below + rRet.append(" {"); + } + break; + case tmRARROW: + if (nPart == 0) + { + if (nVariation == 0) + rRet.append(" widevec ");//right arrow above + else if (nVariation == 1) + rRet.append(" widevec ");//right arrow below + rRet.append(" {"); + } + break; + case tmBARROW: + if (nPart == 0) + { + if (nVariation == 0) + rRet.append(" widevec ");//double arrow above + else if (nVariation == 1) + rRet.append(" widevec ");//double arrow below + rRet.append(" {"); + } + break; + case tmSINT: + if (nPart == 0) + { + if ((nVariation == 3) || (nVariation == 4)) + rRet.append(" lInt"); + else + rRet.append(" Int"); + if ( (nVariation != 0) && (nVariation != 3)) + { + sPush = rRet.makeStringAndClear(); + } + } + if (((nVariation == 1) || + (nVariation == 4)) && (nPart==1)) + rRet.append(" rSub"); + else if ((nVariation == 2) && (nPart==2)) + rRet.append(" rSup"); + else if ((nVariation == 2) && (nPart==1)) + rRet.append(" rSub"); + rRet.append(" {"); + break; + case tmDINT: + if (nPart == 0) + { + if ((nVariation == 2) || (nVariation == 3)) + rRet.append(" llInt"); + else + rRet.append(" iInt"); + if ( (nVariation != 0) && (nVariation != 2)) + { + sPush = rRet.makeStringAndClear(); + } + } + if (((nVariation == 1) || + (nVariation == 3)) && (nPart==1)) + rRet.append(" rSub"); + rRet.append(" {"); + break; + case tmTINT: + if (nPart == 0) + { + if ((nVariation == 2) || (nVariation == 3)) + rRet.append(" lllInt"); + else + rRet.append(" iiInt"); + if ( (nVariation != 0) && (nVariation != 2)) + { + sPush = rRet.makeStringAndClear(); + } + } + if (((nVariation == 1) || + (nVariation == 3)) && (nPart==1)) + rRet.append(" rSub"); + rRet.append(" {"); + break; + case tmSSINT: + if (nPart == 0) + { + if (nVariation == 2) + rRet.append(" lInt"); + else + rRet.append(" Int"); + sPush = rRet.makeStringAndClear(); + } + if (((nVariation == 1) || + (nVariation == 2)) && (nPart==1)) + rRet.append(" cSub"); + else if ((nVariation == 0) && (nPart==2)) + rRet.append(" cSup"); + else if ((nVariation == 0) && (nPart==1)) + rRet.append(" cSub"); + rRet.append(" {"); + break; + case tmDSINT: + if (nPart == 0) + { + if (nVariation == 0) + rRet.append(" llInt"); + else + rRet.append(" iInt"); + sPush = rRet.makeStringAndClear(); + } + if (nPart==1) + rRet.append(" cSub"); + rRet.append(" {"); + break; + case tmTSINT: + if (nPart == 0) + { + if (nVariation == 0) + rRet.append(" lllInt"); + else + rRet.append(" iiInt"); + sPush = rRet.makeStringAndClear(); + } + if (nPart==1) + rRet.append(" cSub"); + rRet.append(" {"); + break; + case tmUHBRACE: + case tmLHBRACE: + rRet.append(" {"); + break; + case tmSUM: + if (nPart == 0) + { + rRet.append(" Sum"); + if (nVariation != 2) + { + sPush = rRet.makeStringAndClear(); + } + } + if ((nVariation == 0) && (nPart==1)) + rRet.append(" cSub"); + else if ((nVariation == 1) && (nPart==2)) + rRet.append(" cSup"); + else if ((nVariation == 1) && (nPart==1)) + rRet.append(" cSub"); + rRet.append(" {"); + break; + case tmISUM: + if (nPart == 0) + { + rRet.append(" Sum"); + sPush = rRet.makeStringAndClear(); + } + if ((nVariation == 0) && (nPart==1)) + rRet.append(" rSub"); + else if ((nVariation == 1) && (nPart==2)) + rRet.append(" rSup"); + else if ((nVariation == 1) && (nPart==1)) + rRet.append(" rSub"); + rRet.append(" {"); + break; + case tmPROD: + if (nPart == 0) + { + rRet.append(" Prod"); + if (nVariation != 2) + { + sPush = rRet.makeStringAndClear(); + } + } + if ((nVariation == 0) && (nPart==1)) + rRet.append(" cSub"); + else if ((nVariation == 1) && (nPart==2)) + rRet.append(" cSup"); + else if ((nVariation == 1) && (nPart==1)) + rRet.append(" cSub"); + rRet.append(" {"); + break; + case tmIPROD: + if (nPart == 0) + { + rRet.append(" Prod"); + sPush = rRet.makeStringAndClear(); + } + if ((nVariation == 0) && (nPart==1)) + rRet.append(" rSub"); + else if ((nVariation == 1) && (nPart==2)) + rRet.append(" rSup"); + else if ((nVariation == 1) && (nPart==1)) + rRet.append(" rSub"); + rRet.append(" {"); + break; + case tmCOPROD: + if (nPart == 0) + { + rRet.append(" coProd"); + if (nVariation != 2) + { + sPush = rRet.makeStringAndClear(); + } + } + if ((nVariation == 0) && (nPart==1)) + rRet.append(" cSub"); + else if ((nVariation == 1) && (nPart==2)) + rRet.append(" cSup"); + else if ((nVariation == 1) && (nPart==1)) + rRet.append(" cSub"); + rRet.append(" {"); + break; + case tmICOPROD: + if (nPart == 0) + { + rRet.append(" coProd"); + sPush = rRet.makeStringAndClear(); + } + if ((nVariation == 0) && (nPart==1)) + rRet.append(" rSub"); + else if ((nVariation == 1) && (nPart==2)) + rRet.append(" rSup"); + else if ((nVariation == 1) && (nPart==1)) + rRet.append(" rSub"); + rRet.append(" {"); + break; + case tmUNION: + if (nPart == 0) + { + rRet.append(" union"); //union + if (nVariation != 2) + { + sPush = rRet.makeStringAndClear(); + } + } + if ((nVariation == 0) && (nPart==1)) + rRet.append(" cSub"); + else if ((nVariation == 1) && (nPart==2)) + rRet.append(" cSup"); + else if ((nVariation == 1) && (nPart==1)) + rRet.append(" cSub"); + rRet.append(" {"); + break; + case tmIUNION: + if (nPart == 0) + { + rRet.append(" union"); //union + sPush = rRet.makeStringAndClear(); + } + if ((nVariation == 0) && (nPart==1)) + rRet.append(" rSub"); + else if ((nVariation == 1) && (nPart==2)) + rRet.append(" rSup"); + else if ((nVariation == 1) && (nPart==1)) + rRet.append(" rSub"); + rRet.append(" {"); + break; + case tmINTER: + if (nPart == 0) + { + rRet.append(" intersect"); //intersect + if (nVariation != 2) + { + sPush = rRet.makeStringAndClear(); + } + } + if ((nVariation == 0) && (nPart==1)) + rRet.append(" cSub"); + else if ((nVariation == 1) && (nPart==2)) + rRet.append(" cSup"); + else if ((nVariation == 1) && (nPart==1)) + rRet.append(" cSub"); + rRet.append(" {"); + break; + case tmIINTER: + if (nPart == 0) + { + rRet.append(" intersect"); //intersect + sPush = rRet.makeStringAndClear(); + } + if ((nVariation == 0) && (nPart==1)) + rRet.append(" rSub"); + else if ((nVariation == 1) && (nPart==2)) + rRet.append(" rSup"); + else if ((nVariation == 1) && (nPart==1)) + rRet.append(" rSub"); + rRet.append(" {"); + break; + case tmLIM: + if ((nVariation == 0) && (nPart==1)) + rRet.append(" cSup"); + else if ((nVariation == 1) && (nPart==1)) + rRet.append(" cSub"); + else if ((nVariation == 2) && (nPart==1)) + rRet.append(" cSub"); + else if ((nVariation == 2) && (nPart==2)) + rRet.append(" cSup"); + rRet.append(" {"); + break; + case tmLDIV: + if (nVariation == 0) + { + if (nPart == 0) + { + sPush = rRet.makeStringAndClear(); + } + } + rRet.append(" {"); + if (nVariation == 0) + { + if (nPart == 1) + rRet.append("alignr "); + } + if (nPart == 0) + rRet.append("\\lline "); + if (nVariation == 1) + rRet.append("overline "); + break; + case tmSLFRACT: + rRet.append(" {"); + break; + case tmINTOP: + if (nPart == 0) + { + sPush = rRet.makeStringAndClear(); + } + if ((nVariation == 0) && (nPart==0)) + rRet.append(" rSup"); + else if ((nVariation == 2) && (nPart==1)) + rRet.append(" rSup"); + else if ((nVariation == 1) && (nPart==0)) + rRet.append(" rSub"); + else if ((nVariation == 2) && (nPart==0)) + rRet.append(" rSub"); + rRet.append(" {"); + break; + case tmSUMOP: + if (nPart == 0) + { + sPush = rRet.makeStringAndClear(); + } + if ((nVariation == 0) && (nPart==0)) + rRet.append(" cSup"); + else if ((nVariation == 2) && (nPart==1)) + rRet.append(" cSup"); + else if ((nVariation == 1) && (nPart==0)) + rRet.append(" cSub"); + else if ((nVariation == 2) && (nPart==0)) + rRet.append(" cSub"); + rRet.append(" {"); + break; + case tmLSCRIPT: + if (nPart == 0) + rRet.append("\"\""); + if ((nVariation == 0) + || ((nVariation == 2) && (nPart==1))) + rRet.append(" lSup"); + else if ((nVariation == 1) + || ((nVariation == 2) && (nPart==0))) + rRet.append(" lSub"); + rRet.append(" {"); + break; + case tmDIRAC: + if (nVariation==0) + { + if (nPart == 0) + rRet.append(" langle "); + } + else if (nVariation==1) + { + rRet.append(" \\langle "); + newline--; + } + else if (nVariation==2) + { + rRet.append(" \\lline "); + newline--; + } + break; + case tmUARROW: + if (nVariation == 0) + rRet.append(" widevec ");//left below + else if (nVariation == 1) + rRet.append(" widevec ");//right below + else if (nVariation == 2) + rRet.append(" widevec ");//double headed below + rRet.append(" {"); + break; + case tmOARROW: + if (nVariation == 0) + rRet.append(" widevec ");//left above + else if (nVariation == 1) + rRet.append(" widevec ");//right above + else if (nVariation == 2) + rRet.append(" widevec ");//double headed above + rRet.append(" {"); + break; + default: + break; + } + sal_Int16 nOldCurSize=nCurSize; + sal_Int32 nSizeStartPos = rRet.getLength(); + HandleSize( nLSize, nDSize, nSetSize ); + bRet = HandleRecords( nLevel+1 ); + while (nSetSize) + { + bool bOk=false; + sal_Int32 nI = rRet.lastIndexOf('{'); + if (nI != -1) + { + for(nI=nI+1;nI<rRet.getLength();nI++) + if (rRet[nI] != ' ') + { + bOk=true; + break; + } + } + else + bOk=true; + + if (bOk) + rRet.append("} "); + else if (rRet.getLength() > nSizeStartPos) + rRet = rRet.truncate(nSizeStartPos); + nSetSize--; + nCurSize=nOldCurSize; + } + + + HandleMatrixSeparator(nMatrixRows,nMatrixCols, + nCurCol,nCurRow); + + switch (nSelector) + { + case tmANGLE: + if (nVariation==0) + rRet.append(" rangle "); + else if (nVariation==2) + rRet.append(" \\rangle "); + break; + case tmPAREN: + if (nVariation==0) + rRet.append(" right )"); + else if (nVariation==2) + rRet.append("\\)"); + break; + case tmBRACE: + if ((nVariation==0) || (nVariation==2)) + rRet.append(" right rbrace "); + else + rRet.append(" right none "); + break; + case tmBRACK: + if (nVariation==0) + rRet.append(" right ]"); + else if (nVariation==2) + rRet.append("\\]"); + break; + case tmBAR: + if (nVariation==0) + rRet.append(" rline "); + else if (nVariation==2) + rRet.append(" \\rline "); + break; + case tmDBAR: + if (nVariation==0) + rRet.append(" rdline "); + else if (nVariation==2) + rRet.append(" \\rdline "); + break; + case tmFLOOR: + if (nVariation == 0 || nVariation & 0x02) // tvFENCE_R + rRet.append(" right rfloor "); + else + rRet.append(" right none "); + break; + case tmCEILING: + if (nVariation==0) + rRet.append(" rceil "); + else if (nVariation==2) + rRet.append(" \\rceil "); + break; + case tmLBLB: + case tmRBLB: + rRet.append("\\["); + break; + case tmRBRB: + case tmLPRB: + rRet.append("\\]"); + break; + case tmROOT: + rRet.append("} "); + if (nVariation == 1) + { + if (nPart == 0) + { + newline--; + sMainTerm = rRet.makeStringAndClear(); + } + else if (nPart == 1) + { + rRet.insert(0, sPush); + rRet.append(sMainTerm); + sPush.clear(); + sMainTerm.clear(); + } + } + else + { + if (nPart == 0) + newline--; + } + nPart++; + break; + case tmLBRP: + rRet.append("\\)"); + break; + case tmFRACT: + rRet.append("} "); + if (nPart == 0) + newline--; + else + rRet.append("} "); + nPart++; + break; + case tmSCRIPT: + { + if ((nPart == 0) && + ((nVariation == 2) || (nVariation == 1))) + newline--; + + bool bOk=false; + sal_Int32 nI = rRet.lastIndexOf('{'); + if (nI != -1) + { + for(nI=nI+1;nI<rRet.getLength();nI++) + if (rRet[nI] != ' ') + { + bOk=true; + break; + } + } + else + bOk=true; + + if (bOk) + rRet.append("} "); + else if (rRet.getLength() > nSubSupStartPos) + rRet = rRet.truncate(nSubSupStartPos); + nPart++; + } + break; + case tmLSCRIPT: + if ((nPart == 0) && + ((nVariation == 2) || (nVariation == 1))) + newline--; + rRet.append("} "); + nPart++; + break; + case tmUARROW: + case tmOARROW: + rRet.append("} "); + break; + case tmUBAR: + case tmOBAR: + rRet.append("}} "); + break; + case tmLARROW: + case tmRARROW: + case tmBARROW: + if (nPart == 0) + { + newline--; + rRet.append("} "); + } + nPart++; + break; + case tmUHBRACE: + rRet.append("} "); + if (nPart == 0) + { + newline--; + rRet.append("overbrace"); + } + nPart++; + break; + case tmLHBRACE: + rRet.append("} "); + if (nPart == 0) + { + newline--; + rRet.append("underbrace"); + } + nPart++; + break; + case tmLIM: + if (nPart==0) + newline--; + else if ((nPart==1) && + ((nVariation == 2) || (nVariation == 1))) + newline--; + rRet.append("} "); + nPart++; + break; + case tmLDIV: + rRet.append("} "); + if (nVariation == 0) + { + if (nPart == 0) + { + sMainTerm = rRet.makeStringAndClear(); + } + else if (nPart == 1) + { + rRet.insert(0, sPush); + rRet.append(" over " + sMainTerm); + sPush.clear(); + sMainTerm.clear(); + } + } + if (nPart == 0) + newline--; + nPart++; + break; + case tmSLFRACT: + rRet.append("} "); + if (nPart == 0) + { + newline--; + switch (nVariation) + { + case 1: + rRet.append("slash"); + break; + default: + rRet.append("wideslash"); + break; + } + } + nPart++; + break; + case tmSUM: + case tmISUM: + case tmPROD: + case tmIPROD: + case tmCOPROD: + case tmICOPROD: + case tmUNION: + case tmIUNION: + case tmINTER: + case tmIINTER: + rRet.append("} "); + if (nPart == 0) + { + if (nVariation != 2) + { + sMainTerm = rRet.makeStringAndClear(); + } + newline--; + } + else if ((nPart == 1) && (nVariation == 0)) + { + rRet.insert(0, sPush); + rRet.append(sMainTerm); + sPush.clear(); + sMainTerm.clear(); + newline--; + } + else if ((nPart == 1) && (nVariation == 1)) + newline--; + else if ((nPart == 2) && (nVariation == 1)) + { + rRet.insert(0, sPush); + rRet.append(sMainTerm); + sPush.clear(); + sMainTerm.clear(); + newline--; + } + nPart++; + break; + case tmSINT: + rRet.append("} "); + if (nPart == 0) + { + if ((nVariation != 0) && (nVariation != 3)) + { + sMainTerm = rRet.makeStringAndClear(); + } + newline--; + } + else if ((nPart == 1) && + ((nVariation == 1) || (nVariation==4))) + { + rRet.insert(0, sPush); + rRet.append(sMainTerm); + sPush.clear(); + sMainTerm.clear(); + newline--; + } + else if ((nPart == 1) && (nVariation == 2)) + newline--; + else if ((nPart == 2) && (nVariation == 2)) + { + rRet.insert(0, sPush); + rRet.append(sMainTerm); + sPush.clear(); + sMainTerm.clear(); + newline--; + } + nPart++; + break; + case tmDINT: + case tmTINT: + rRet.append("} "); + if (nPart == 0) + { + if ((nVariation != 0) && (nVariation != 2)) + { + sMainTerm = rRet.makeStringAndClear(); + } + newline--; + } + else if ((nPart == 1) && + ((nVariation == 1) || (nVariation==3))) + { + rRet.insert(0, sPush); + rRet.append(sMainTerm); + sPush.clear(); + sMainTerm.clear(); + newline--; + } + nPart++; + break; + case tmSSINT: + rRet.append("} "); + if (nPart == 0) + { + sMainTerm = rRet.makeStringAndClear(); + newline--; + } + else if ((nPart == 1) && + ((nVariation == 1) || (nVariation==2))) + { + rRet.insert(0, sPush); + rRet.append(sMainTerm); + sPush.clear(); + sMainTerm.clear(); + newline--; + } + else if ((nPart == 1) && (nVariation == 0)) + newline--; + else if ((nPart == 2) && (nVariation == 0)) + { + rRet.insert(0, sPush); + rRet.append(sMainTerm); + sPush.clear(); + sMainTerm.clear(); + newline--; + } + nPart++; + break; + case tmDSINT: + case tmTSINT: + rRet.append("} "); + if (nPart == 0) + { + sMainTerm = rRet.makeStringAndClear(); + newline--; + } + else if (nPart == 1) + { + rRet.insert(0, sPush); + rRet.append(sMainTerm); + sPush.clear(); + sMainTerm.clear(); + newline--; + } + nPart++; + break; + case tmINTOP: + case tmSUMOP: + rRet.append("} "); + + if ((nPart == 0) && + ((nVariation == 0) || (nVariation == 1))) + { + sMainTerm = rRet.makeStringAndClear(); + newline--; + } + else if ((nPart == 0) && (nVariation == 2)) + newline--; + else if ((nPart == 1) && (nVariation == 2)) + { + sMainTerm = rRet.makeStringAndClear(); + newline--; + } + else if ((nPart == 2) || ((nPart == 1) && + (nVariation == 0 || nVariation == 1))) + { + rRet.insert(0, sPush); + rRet.append(sMainTerm); + sPush.clear(); + sMainTerm.clear(); + } + nPart++; + break; + case tmDIRAC: + if (nVariation==0) + { + if (nPart == 0) + { + newline--; //there is another term to arrive + rRet.append(" mline "); + } + else + rRet.append(" rangle "); + } + else if (nVariation==1) + rRet.append(" \\lline "); + else if (nVariation==2) + rRet.append(" \\rangle "); + nPart++; + break; + default: + break; + } + bSilent = true; //Skip the optional brackets and/or + //symbols that follow some of these + //records. Foo Data. + + /*In matrices and piles we cannot separate equation + *lines with the newline keyword*/ + if (nMatrixCols==0) + newline++; + } + } + break; + case CHAR: + if (xfLMOVE(nTag)) + HandleNudge(); + bRet = HandleChar( nTextStart, nSetSize, nLevel, nTag, nSelector, nVariation, bSilent ); + break; + case TMPL: + if (xfLMOVE(nTag)) + HandleNudge(); + bRet = HandleTemplate( nLevel, nSelector, nVariation, nLastTemplateBracket ); + break; + case PILE: + if (xfLMOVE(nTag)) + HandleNudge(); + bRet = HandlePile( nSetAlign, nLevel, nSelector, nVariation ); + HandleMatrixSeparator( nMatrixRows, nMatrixCols, nCurCol, nCurRow ); + break; + case MATRIX: + if (xfLMOVE(nTag)) + HandleNudge(); + bRet = HandleMatrix( nLevel, nSelector, nVariation ); + HandleMatrixSeparator( nMatrixRows, nMatrixCols, nCurCol, nCurRow ); + break; + case EMBEL: + if (xfLMOVE(nTag)) + HandleNudge(); + HandleEmblishments(); + break; + case RULER: + { + sal_uInt8 nTabStops(0); + pS->ReadUChar( nTabStops ); + for (i=0;i<nTabStops;i++) + { + pS->ReadUChar( nTabType ); + pS->ReadUInt16( nTabOffset ); + } + SAL_WARN("starmath", "Not seen in the wild Equation Ruler Field"); + break; + } + case FONT: + { + MathTypeFont aFont; + pS->ReadUChar( aFont.nTface ); + /* + The typeface number is the negative (which makes it + positive) of the typeface value (unbiased) that appears in + CHAR records that might follow a given FONT record + */ + aFont.nTface = 128-aFont.nTface; + pS->ReadUChar( aFont.nStyle ); + aUserStyles.insert(aFont); + // read font name + while(true) + { + char nChar8(0); + pS->ReadChar( nChar8 ); + if (nChar8 == 0) + break; + } + } + break; + case SIZE: + HandleSetSize(); + break; + case 10: + case 11: + case 12: + case 13: + case 14: + nLSize=nRecord-10; + break; + case END: + default: + break; + } + } + while (nRecord != END && !pS->eof()); + while (nSetSize) + { + rRet.append("}"); + nSetSize--; + } + return bRet; +} + +/*Simply determine if we are at the end of a record or the end of a line, + *with fiddly logic to see if we are in a matrix or a pile or neither + + Note we cannot tell until after the event that this is the last entry + of a pile, so we must strip the last separator of a pile after this + is detected in the PILE handler + */ +void MathType::HandleMatrixSeparator(int nMatrixRows,int nMatrixCols, + int &rCurCol,int &rCurRow) +{ + if (nMatrixRows==0) + return; + + if (rCurCol == nMatrixCols-1) + { + if (rCurRow != nMatrixRows-1) + rRet.append(" {} ##\n"); + if (nMatrixRows!=-1) + { + rCurCol=0; + rCurRow++; + } + } + else + { + rRet.append(" {} # "); + if (nMatrixRows!=-1) + rCurCol++; + else + rRet.append("\n"); + } +} + +/* set the alignment of the following term, but starmath currently + * cannot handle vertical alignment */ +void MathType::HandleAlign(sal_uInt8 nHorAlign, int &rSetAlign) +{ + switch(nHorAlign) + { + case 1: + default: + rRet.append("alignl {"); + break; + case 2: + rRet.append("alignc {"); + break; + case 3: + rRet.append("alignr {"); + break; + } + rSetAlign++; +} + +/* set size of text, complexity due to overuse of signedness as a flag + * indicator by mathtype file format*/ +bool MathType::HandleSize(sal_Int16 nLstSize,sal_Int16 nDefSize, int &rSetSize) +{ + bool bRet=false; + if (nLstSize < 0) + { + const sal_Int16 nDefaultSize = 12; + if ((-nLstSize/32 != nDefaultSize) && (-nLstSize/32 != nCurSize)) + { + if (rSetSize) + { + rSetSize--; + rRet.append("}"); + bRet=true; + } + if (-nLstSize/32 != nLastSize) + { + nLastSize = nCurSize; + rRet.append(" size "); + rRet.append(static_cast<sal_Int32>(-nLstSize/32)); + rRet.append("{"); + bRet=true; + rSetSize++; + } + nCurSize = -nLstSize/32; + } + } + else + { + /*sizetable should theoretically be filled with the default sizes + *of the various font groupings matching starmaths equivalents + in aTypeFaces, and a test would be done to see if the new font + size would be the same as what starmath would have chosen for + itself anyway in which case the size setting could be ignored*/ + nLstSize = aSizeTable.at(nLstSize); + nLstSize = nLstSize + nDefSize; + if (nLstSize != nCurSize) + { + if (rSetSize) + { + rSetSize--; + rRet.append("}"); + bRet=true; + } + if (nLstSize != nLastSize) + { + nLastSize = nCurSize; + rRet.append(" size "); + rRet.append(static_cast<sal_Int32>(nLstSize)); + rRet.append("{"); + bRet=true; + rSetSize++; + } + nCurSize = nLstSize; + } + } + return bRet; +} + +bool MathType::ConvertFromStarMath( SfxMedium& rMedium ) +{ + if (!pTree) + return false; + + SvStream *pStream = rMedium.GetOutStream(); + if ( pStream ) + { + tools::SvRef<SotStorage> pStor = new SotStorage( pStream, false ); + + SvGlobalName aGName(MSO_EQUATION3_CLASSID); + pStor->SetClass( aGName, SotClipboardFormatId::NONE, "Microsoft Equation 3.0"); + + static sal_uInt8 const aCompObj[] = { + 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0xCE, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x46, 0x17, 0x00, 0x00, 0x00, + 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, + 0x74, 0x20, 0x45, 0x71, 0x75, 0x61, 0x74, 0x69, + 0x6F, 0x6E, 0x20, 0x33, 0x2E, 0x30, 0x00, 0x0C, + 0x00, 0x00, 0x00, 0x44, 0x53, 0x20, 0x45, 0x71, + 0x75, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x00, 0x0B, + 0x00, 0x00, 0x00, 0x45, 0x71, 0x75, 0x61, 0x74, + 0x69, 0x6F, 0x6E, 0x2E, 0x33, 0x00, 0xF4, 0x39, + 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + tools::SvRef<SotStorageStream> xStor( pStor->OpenSotStream("\1CompObj")); + xStor->WriteBytes(aCompObj, sizeof(aCompObj)); + + static sal_uInt8 const aOle[] = { + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + tools::SvRef<SotStorageStream> xStor2( pStor->OpenSotStream("\1Ole")); + xStor2->WriteBytes(aOle, sizeof(aOle)); + xStor.clear(); + xStor2.clear(); + + tools::SvRef<SotStorageStream> xSrc = pStor->OpenSotStream("Equation Native"); + if ( (!xSrc.is()) || (ERRCODE_NONE != xSrc->GetError())) + return false; + + pS = xSrc.get(); + pS->SetEndian( SvStreamEndian::LITTLE ); + + pS->SeekRel(EQNOLEFILEHDR_SIZE); //Skip 28byte Header and fill it in later + pS->WriteUChar( 0x03 ); + pS->WriteUChar( 0x01 ); + pS->WriteUChar( 0x01 ); + pS->WriteUChar( 0x03 ); + pS->WriteUChar( 0x00 ); + sal_uInt32 nSize = pS->Tell(); + nPendingAttributes=0; + + HandleNodes(pTree, 0); + pS->WriteUChar( END ); + + nSize = pS->Tell()-nSize; + pS->Seek(0); + EQNOLEFILEHDR aHdr(nSize+4+1); + aHdr.Write(pS); + + pStor->Commit(); + } + + return true; +} + + +void MathType::HandleNodes(SmNode *pNode,int nLevel) +{ + switch(pNode->GetType()) + { + case SmNodeType::Attribute: + HandleAttributes(pNode,nLevel); + break; + case SmNodeType::Text: + HandleText(pNode); + break; + case SmNodeType::VerticalBrace: + HandleVerticalBrace(pNode,nLevel); + break; + case SmNodeType::Brace: + HandleBrace(pNode,nLevel); + break; + case SmNodeType::Oper: + HandleOperator(pNode,nLevel); + break; + case SmNodeType::BinVer: + HandleFractions(pNode,nLevel); + break; + case SmNodeType::Root: + HandleRoot(pNode,nLevel); + break; + case SmNodeType::Special: + { + SmTextNode *pText = static_cast<SmTextNode *>(pNode); + //if the token str and the result text are the same then this + //is to be seen as text, else assume it's a mathchar + if (pText->GetText() == pText->GetToken().aText) + HandleText(pText); + else + HandleMath(pText); + } + break; + case SmNodeType::Math: + case SmNodeType::MathIdent: + HandleMath(pNode); + break; + case SmNodeType::SubSup: + HandleSubSupScript(pNode,nLevel); + break; + case SmNodeType::Expression: + { + size_t nSize = pNode->GetNumSubNodes(); + for (size_t i = 0; i < nSize; ++i) + { + if (SmNode *pTemp = pNode->GetSubNode(i)) + HandleNodes(pTemp,nLevel+1); + } + break; + } + case SmNodeType::Table: + //Root Node, PILE equivalent, i.e. vertical stack + HandleTable(pNode,nLevel); + break; + case SmNodeType::Matrix: + HandleSmMatrix(static_cast<SmMatrixNode *>(pNode),nLevel); + break; + case SmNodeType::Line: + { + pS->WriteUChar( 0x0a ); + pS->WriteUChar( LINE ); + size_t nSize = pNode->GetNumSubNodes(); + for (size_t i = 0; i < nSize; ++i) + { + if (SmNode *pTemp = pNode->GetSubNode(i)) + HandleNodes(pTemp,nLevel+1); + } + pS->WriteUChar( END ); + break; + } + case SmNodeType::Align: + HandleMAlign(pNode,nLevel); + break; + case SmNodeType::Blank: + pS->WriteUChar( CHAR ); + pS->WriteUChar( 0x98 ); + if (pNode->GetToken().eType == TSBLANK) + pS->WriteUInt16( 0xEB04 ); + else + pS->WriteUInt16( 0xEB05 ); + break; + default: + { + size_t nSize = pNode->GetNumSubNodes(); + for (size_t i = 0; i < nSize; ++i) + { + if (SmNode *pTemp = pNode->GetSubNode(i)) + HandleNodes(pTemp,nLevel+1); + } + break; + } + } +} + + +int MathType::StartTemplate(sal_uInt16 nSelector,sal_uInt16 nVariation) +{ + int nOldPending=nPendingAttributes; + pS->WriteUChar( TMPL ); //Template + pS->WriteUChar( nSelector ); //selector + pS->WriteUChar( nVariation ); //variation + pS->WriteUChar( 0x00 ); //options + pS->WriteUChar( LINE ); + //there's just no way we can now handle any character + //attributes (from mathtypes perspective) centered + //over an expression but above template attribute + //such as widevec and similar constructs + //we have to drop them + nPendingAttributes=0; + return nOldPending; +} + +void MathType::EndTemplate(int nOldPendingAttributes) +{ + pS->WriteUChar( END ); //end line + pS->WriteUChar( END ); //end template + nPendingAttributes=nOldPendingAttributes; +} + + +void MathType::HandleSmMatrix(SmMatrixNode *pMatrix,int nLevel) +{ + pS->WriteUChar( MATRIX ); + pS->WriteUChar( 0x00 ); //vAlign ? + pS->WriteUChar( 0x00 ); //h_just + pS->WriteUChar( 0x00 ); //v_just + pS->WriteUChar( pMatrix->GetNumRows() ); //v_just + pS->WriteUChar( pMatrix->GetNumCols() ); //v_just + int nBytes=(pMatrix->GetNumRows()+1)*2/8; + if (((pMatrix->GetNumRows()+1)*2)%8) + nBytes++; + for (int j = 0; j < nBytes; j++) + pS->WriteUChar( 0x00 ); //row_parts + nBytes=(pMatrix->GetNumCols()+1)*2/8; + if (((pMatrix->GetNumCols()+1)*2)%8) + nBytes++; + for (int k = 0; k < nBytes; k++) + pS->WriteUChar( 0x00 ); //col_parts + size_t nSize = pMatrix->GetNumSubNodes(); + for (size_t i = 0; i < nSize; ++i) + { + if (SmNode *pTemp = pMatrix->GetSubNode(i)) + { + pS->WriteUChar( LINE ); //line + HandleNodes(pTemp,nLevel+1); + pS->WriteUChar( END ); //end line + } + } + pS->WriteUChar( END ); +} + + +//Root Node, PILE equivalent, i.e. vertical stack +void MathType::HandleTable(SmNode *pNode,int nLevel) +{ + size_t nSize = pNode->GetNumSubNodes(); + //The root of the starmath is a table, if + //we convert this them each iteration of + //conversion from starmath to mathtype will + //add an extra unnecessary level to the + //mathtype output stack which would grow + //without bound in a multi step conversion + + if (nLevel == 0) + pS->WriteUChar( 0x0A ); //initial size + + if ( nLevel || (nSize >1)) + { + pS->WriteUChar( PILE ); + pS->WriteUChar( nHAlign ); //vAlign ? + pS->WriteUChar( 0x01 ); //hAlign + } + + for (size_t i = 0; i < nSize; ++i) + { + if (SmNode *pTemp = pNode->GetSubNode(i)) + { + pS->WriteUChar( LINE ); + HandleNodes(pTemp,nLevel+1); + pS->WriteUChar( END ); + } + } + if (nLevel || (nSize>1)) + pS->WriteUChar( END ); +} + + +void MathType::HandleRoot(SmNode *pNode,int nLevel) +{ + SmNode *pTemp; + pS->WriteUChar( TMPL ); //Template + pS->WriteUChar( 0x0D ); //selector + if (pNode->GetSubNode(0)) + pS->WriteUChar( 0x01 ); //variation + else + pS->WriteUChar( 0x00 ); //variation + pS->WriteUChar( 0x00 ); //options + + if (nullptr != (pTemp = pNode->GetSubNode(2))) + { + pS->WriteUChar( LINE ); //line + HandleNodes(pTemp,nLevel+1); + pS->WriteUChar( END ); + } + + if (nullptr != (pTemp = pNode->GetSubNode(0))) + { + pS->WriteUChar( LINE ); //line + HandleNodes(pTemp,nLevel+1); + pS->WriteUChar( END ); + } + else + pS->WriteUChar( LINE|0x10 ); //dummy line + + + pS->WriteUChar( END ); +} + +sal_uInt8 MathType::HandleCScript(SmNode *pNode,SmNode *pContent,int nLevel, + sal_uInt64 *pPos,bool bTest) +{ + sal_uInt8 nVariation2=0xff; + + if (bTest && pNode->GetSubNode(CSUP+1)) + { + nVariation2=0; + if (pNode->GetSubNode(CSUB+1)) + nVariation2=2; + } + else if (pNode->GetSubNode(CSUB+1)) + nVariation2=1; + + if (nVariation2!=0xff) + { + if (pPos) + *pPos = pS->Tell(); + pS->WriteUChar( TMPL ); //Template + pS->WriteUChar( 0x2B ); //selector + pS->WriteUChar( nVariation2 ); + pS->WriteUChar( 0x00 ); //options + + if (pContent) + { + pS->WriteUChar( LINE ); //line + HandleNodes(pContent,nLevel+1); + pS->WriteUChar( END ); //line + } + else + pS->WriteUChar( LINE|0x10 ); + + pS->WriteUChar( 0x0B ); + + SmNode *pTemp; + if (nullptr != (pTemp = pNode->GetSubNode(CSUB+1))) + { + pS->WriteUChar( LINE ); //line + HandleNodes(pTemp,nLevel+1); + pS->WriteUChar( END ); //line + } + else + pS->WriteUChar( LINE|0x10 ); + if (bTest && nullptr != (pTemp = pNode->GetSubNode(CSUP+1))) + { + pS->WriteUChar( LINE ); //line + HandleNodes(pTemp,nLevel+1); + pS->WriteUChar( END ); //line + } + else + pS->WriteUChar( LINE|0x10 ); + } + return nVariation2; +} + + +/* + Sub and Sup scripts and another problem area, StarMath + can have all possible options used at the same time, whereas + Mathtype cannot. The ordering of the nodes for each system + is quite different as well leading to some complexity + */ +void MathType::HandleSubSupScript(SmNode *pNode,int nLevel) +{ + sal_uInt8 nVariation=0xff; + if (pNode->GetSubNode(LSUP+1)) + { + nVariation=0; + if (pNode->GetSubNode(LSUB+1)) + nVariation=2; + } + else if ( nullptr != pNode->GetSubNode(LSUB+1) ) + nVariation=1; + + SmNode *pTemp; + if (nVariation!=0xff) + { + pS->WriteUChar( TMPL ); //Template + pS->WriteUChar( 0x2c ); //selector + pS->WriteUChar( nVariation ); + pS->WriteUChar( 0x00 ); //options + pS->WriteUChar( 0x0B ); + + if (nullptr != (pTemp = pNode->GetSubNode(LSUB+1))) + { + pS->WriteUChar( LINE ); //line + HandleNodes(pTemp,nLevel+1); + pS->WriteUChar( END ); //line + } + else + pS->WriteUChar( LINE|0x10 ); + if (nullptr != (pTemp = pNode->GetSubNode(LSUP+1))) + { + pS->WriteUChar( LINE ); //line + HandleNodes(pTemp,nLevel+1); + pS->WriteUChar( END ); //line + } + else + pS->WriteUChar( LINE|0x10 ); + pS->WriteUChar( END ); + nVariation=0xff; + } + + + sal_uInt8 nVariation2=HandleCScript(pNode,nullptr,nLevel); + + if (nullptr != (pTemp = pNode->GetSubNode(0))) + { + HandleNodes(pTemp,nLevel+1); + } + + if (nVariation2 != 0xff) + pS->WriteUChar( END ); + + if (nullptr != (pNode->GetSubNode(RSUP+1))) + { + nVariation=0; + if (pNode->GetSubNode(RSUB+1)) + nVariation=2; + } + else if (nullptr != pNode->GetSubNode(RSUB+1)) + nVariation=1; + + if (nVariation!=0xff) + { + pS->WriteUChar( TMPL ); //Template + pS->WriteUChar( 0x0F ); //selector + pS->WriteUChar( nVariation ); + pS->WriteUChar( 0x00 ); //options + pS->WriteUChar( 0x0B ); + + if (nullptr != (pTemp = pNode->GetSubNode(RSUB+1))) + { + pS->WriteUChar( LINE ); //line + HandleNodes(pTemp,nLevel+1); + pS->WriteUChar( END ); //line + } + else + pS->WriteUChar( LINE|0x10 ); + if (nullptr != (pTemp = pNode->GetSubNode(RSUP+1))) + { + pS->WriteUChar( LINE ); //line + HandleNodes(pTemp,nLevel+1); + pS->WriteUChar( END ); //line + } + else + pS->WriteUChar( LINE|0x10 ); + pS->WriteUChar( END ); //line + } + + //After subscript mathtype will keep the size of + //normal text at the subscript size, sigh. + pS->WriteUChar( 0x0A ); +} + + +void MathType::HandleFractions(SmNode *pNode,int nLevel) +{ + SmNode *pTemp; + pS->WriteUChar( TMPL ); //Template + pS->WriteUChar( 0x0E ); //selector + pS->WriteUChar( 0x00 ); //variation + pS->WriteUChar( 0x00 ); //options + + pS->WriteUChar( 0x0A ); + pS->WriteUChar( LINE ); //line + if (nullptr != (pTemp = pNode->GetSubNode(0))) + HandleNodes(pTemp,nLevel+1); + pS->WriteUChar( END ); + + pS->WriteUChar( 0x0A ); + pS->WriteUChar( LINE ); //line + if (nullptr != (pTemp = pNode->GetSubNode(2))) + HandleNodes(pTemp,nLevel+1); + pS->WriteUChar( END ); + + pS->WriteUChar( END ); +} + + +void MathType::HandleBrace(SmNode *pNode,int nLevel) +{ + SmNode *pTemp; + SmNode *pLeft=pNode->GetSubNode(0); + SmNode *pRight=pNode->GetSubNode(2); + + pS->WriteUChar( TMPL ); //Template + bIsReInterpBrace=false; + sal_uInt8 nBSpec=0x10; + auto nLoc = pS->Tell(); + if (pLeft) + { + switch (pLeft->GetToken().eType) + { + case TLANGLE: + pS->WriteUChar( tmANGLE ); //selector + pS->WriteUChar( 0 ); //variation + pS->WriteUChar( 0 ); //options + break; + case TLBRACE: + pS->WriteUChar( tmBRACE ); //selector + pS->WriteUChar( 0 ); //variation + pS->WriteUChar( 0 ); //options + nBSpec+=3; + break; + case TLBRACKET: + pS->WriteUChar( tmBRACK ); //selector + pS->WriteUChar( 0 ); //variation + pS->WriteUChar( 0 ); //options + nBSpec+=3; + break; + case TLFLOOR: + pS->WriteUChar( tmFLOOR ); //selector + pS->WriteUChar( 0 ); //variation + pS->WriteUChar( 0 ); //options + break; + case TLLINE: + pS->WriteUChar( tmBAR ); //selector + pS->WriteUChar( 0 ); //variation + pS->WriteUChar( 0 ); //options + nBSpec+=3; + break; + case TLDLINE: + pS->WriteUChar( tmDBAR ); //selector + pS->WriteUChar( 0 ); //variation + pS->WriteUChar( 0 ); //options + break; + default: + pS->WriteUChar( tmPAREN ); //selector + pS->WriteUChar( 0 ); //variation + pS->WriteUChar( 0 ); //options + nBSpec+=3; + break; + } + } + + if (nullptr != (pTemp = pNode->GetSubNode(1))) + { + pS->WriteUChar( LINE ); //line + HandleNodes(pTemp,nLevel+1); + pS->WriteUChar( END ); //options + } + nSpec=nBSpec; + if (pLeft) + HandleNodes(pLeft,nLevel+1); + if (bIsReInterpBrace) + { + auto nLoc2 = pS->Tell(); + pS->Seek(nLoc); + pS->WriteUChar( 0x2D ); + pS->Seek(nLoc2); + pS->WriteUChar( CHAR ); + pS->WriteUChar( 0x96 ); + pS->WriteUInt16( 0xEC07 ); + bIsReInterpBrace=false; + } + if (pRight) + HandleNodes(pRight,nLevel+1); + nSpec=0x0; + pS->WriteUChar( END ); +} + + +void MathType::HandleVerticalBrace(SmNode *pNode,int nLevel) +{ + SmNode *pTemp; + pS->WriteUChar( TMPL ); //Template + if (pNode->GetToken().eType == TUNDERBRACE) + pS->WriteUChar( tmLHBRACE ); //selector + else + pS->WriteUChar( tmUHBRACE ); //selector + pS->WriteUChar( 0 ); //variation + pS->WriteUChar( 0 ); //options + + if (nullptr != (pTemp = pNode->GetSubNode(0))) + { + pS->WriteUChar( LINE ); //line + HandleNodes(pTemp,nLevel+1); + pS->WriteUChar( END ); //options + } + + if (nullptr != (pTemp = pNode->GetSubNode(2))) + { + pS->WriteUChar( LINE ); //line + HandleNodes(pTemp,nLevel+1); + pS->WriteUChar( END ); //options + } + pS->WriteUChar( END ); +} + +void MathType::HandleOperator(SmNode *pNode,int nLevel) +{ + if (HandleLim(pNode,nLevel)) + return; + + sal_uInt64 nPos; + sal_uInt8 nVariation; + + switch (pNode->GetToken().eType) + { + case TIINT: + case TIIINT: + case TLINT: + case TLLINT: + case TLLLINT: + nVariation=HandleCScript(pNode->GetSubNode(0), + pNode->GetSubNode(1),nLevel,&nPos,false); + break; + default: + nVariation=HandleCScript(pNode->GetSubNode(0), + pNode->GetSubNode(1),nLevel,&nPos); + break; + } + + sal_uInt8 nOldVariation=nVariation; + sal_uInt8 nIntVariation=nVariation; + + sal_uInt64 nPos2=0; + if (nVariation != 0xff) + { + nPos2 = pS->Tell(); + pS->Seek(nPos); + if (nVariation == 2) + { + nIntVariation=0; + nVariation = 1; + } + else if (nVariation == 0) + nVariation = 1; + else if (nVariation == 1) + nVariation = 0; + } + else + { + nVariation = 2; + nIntVariation=0; + } + pS->WriteUChar( TMPL ); + switch(pNode->GetToken().eType) + { + case TINT: + case TINTD: + if (nOldVariation != 0xff) + pS->WriteUChar( 0x18 ); //selector + else + pS->WriteUChar( 0x15 ); //selector + pS->WriteUChar( nIntVariation ); //variation + break; + case TIINT: + if (nOldVariation != 0xff) + { + pS->WriteUChar( 0x19 ); + pS->WriteUChar( 0x01 ); + } + else + { + pS->WriteUChar( 0x16 ); + pS->WriteUChar( 0x00 ); + } + break; + case TIIINT: + if (nOldVariation != 0xff) + { + pS->WriteUChar( 0x1a ); + pS->WriteUChar( 0x01 ); + } + else + { + pS->WriteUChar( 0x17 ); + pS->WriteUChar( 0x00 ); + } + break; + case TLINT: + if (nOldVariation != 0xff) + { + pS->WriteUChar( 0x18 ); + pS->WriteUChar( 0x02 ); + } + else + { + pS->WriteUChar( 0x15 ); + pS->WriteUChar( 0x03 ); + } + break; + case TLLINT: + if (nOldVariation != 0xff) + { + pS->WriteUChar( 0x19 ); + pS->WriteUChar( 0x00 ); + } + else + { + pS->WriteUChar( 0x16 ); + pS->WriteUChar( 0x02 ); + } + break; + case TLLLINT: + if (nOldVariation != 0xff) + { + pS->WriteUChar( 0x1a ); + pS->WriteUChar( 0x00 ); + } + else + { + pS->WriteUChar( 0x17 ); + pS->WriteUChar( 0x02 ); + } + break; + case TSUM: + default: + pS->WriteUChar( 0x1d ); + pS->WriteUChar( nVariation ); + break; + case TPROD: + pS->WriteUChar( 0x1f ); + pS->WriteUChar( nVariation ); + break; + case TCOPROD: + pS->WriteUChar( 0x21 ); + pS->WriteUChar( nVariation ); + break; + } + pS->WriteUChar( 0 ); //options + + if (nPos2) + pS->Seek(nPos2); + else + { + pS->WriteUChar( LINE ); //line + HandleNodes(pNode->GetSubNode(1),nLevel+1); + pS->WriteUChar( END ); //line + pS->WriteUChar( LINE|0x10 ); + pS->WriteUChar( LINE|0x10 ); + } + + pS->WriteUChar( 0x0D ); + switch(pNode->GetToken().eType) + { + case TSUM: + default: + pS->WriteUChar( CHAR ); + pS->WriteUChar( 0x86 ); + pS->WriteUInt16( 0x2211 ); + break; + case TPROD: + pS->WriteUChar( CHAR ); + pS->WriteUChar( 0x86 ); + pS->WriteUInt16( 0x220F ); + break; + case TCOPROD: + pS->WriteUChar( CHAR ); + pS->WriteUChar( 0x8B ); + pS->WriteUInt16( 0x2210 ); + break; + case TIIINT: + case TLLLINT: + pS->WriteUChar( CHAR ); + pS->WriteUChar( 0x86 ); + pS->WriteUInt16( 0x222B ); + [[fallthrough]]; + case TIINT: + case TLLINT: + pS->WriteUChar( CHAR ); + pS->WriteUChar( 0x86 ); + pS->WriteUInt16( 0x222B ); + [[fallthrough]]; + case TINT: + case TINTD: + case TLINT: + pS->WriteUChar( CHAR ); + pS->WriteUChar( 0x86 ); + pS->WriteUInt16( 0x222B ); + break; + } + pS->WriteUChar( END ); + pS->WriteUChar( 0x0A ); +} + + +bool MathType::HandlePile(int &rSetAlign, int nLevel, sal_uInt8 nSelector, sal_uInt8 nVariation) +{ + sal_uInt8 nVAlign; + pS->ReadUChar( nHAlign ); + pS->ReadUChar( nVAlign ); + + HandleAlign(nHAlign, rSetAlign); + + rRet.append(" stack {\n"); + bool bRet = HandleRecords( nLevel+1, nSelector, nVariation, -1, -1 ); + int nRemoveFrom = rRet.getLength() >= 3 ? rRet.getLength() - 3 : 0; + rRet.remove(nRemoveFrom, 2); + rRet.append("} "); + + while (rSetAlign) + { + rRet.append("} "); + rSetAlign--; + } + return bRet; +} + +bool MathType::HandleMatrix(int nLevel, sal_uInt8 nSelector, sal_uInt8 nVariation) +{ + sal_uInt8 nH_just,nV_just,nRows,nCols,nVAlign; + pS->ReadUChar( nVAlign ); + pS->ReadUChar( nH_just ); + pS->ReadUChar( nV_just ); + pS->ReadUChar( nRows ); + pS->ReadUChar( nCols ); + if (!pS->good()) + return false; + int nBytes = ((nRows+1)*2)/8; + if (((nRows+1)*2)%8) + nBytes++; + pS->SeekRel(nBytes); + nBytes = ((nCols+1)*2)/8; + if (((nCols+1)*2)%8) + nBytes++; + pS->SeekRel(nBytes); + rRet.append(" matrix {\n"); + bool bRet = HandleRecords( nLevel+1, nSelector, nVariation, nRows, nCols ); + + sal_Int32 nI = rRet.lastIndexOf('#'); + if (nI > 0) + if (rRet[nI-1] != '#') //missing column + rRet.append("{}"); + + rRet.append("\n} "); + return bRet; +} + +bool MathType::HandleTemplate(int nLevel, sal_uInt8 &rSelector, + sal_uInt8 &rVariation, sal_Int32 &rLastTemplateBracket) +{ + sal_uInt8 nOption; //This appears utterly unused + pS->ReadUChar( rSelector ); + pS->ReadUChar( rVariation ); + pS->ReadUChar( nOption ); + OSL_ENSURE(rSelector < 48,"Selector out of range"); + if ((rSelector >= 21) && (rSelector <=26)) + { + OSL_ENSURE(nOption < 2,"Option out of range"); + } + else if (rSelector <= 12) + { + OSL_ENSURE(nOption < 3,"Option out of range"); + } + + //For the (broken) case where one subscript template ends, and there is + //another one after it, mathtype handles it as if the second one was + //inside the first one and renders it as sub of sub + bool bRemove=false; + if ( (rSelector == 0xf) && (rLastTemplateBracket != -1) ) + { + bRemove=true; + for (sal_Int32 nI = rLastTemplateBracket+1; nI < rRet.getLength(); nI++ ) + if (rRet[nI] != ' ') + { + bRemove=false; + break; + } + } + + //suborderlist + bool bRet = HandleRecords( nLevel+1, rSelector, rVariation ); + + if (bRemove) + { + if (rLastTemplateBracket < rRet.getLength()) + rRet.remove(rLastTemplateBracket, 1); + rRet.append("} "); + rLastTemplateBracket = -1; + } + if (rSelector == 0xf) + rLastTemplateBracket = rRet.lastIndexOf('}'); + else + rLastTemplateBracket = -1; + + rSelector = sal::static_int_cast< sal_uInt8 >(-1); + return bRet; +} + +void MathType::HandleEmblishments() +{ + sal_uInt8 nEmbel; + do + { + pS->ReadUChar( nEmbel ); + if (!pS->good()) + break; + switch (nEmbel) + { + case 0x02: + rRet.append(" dot "); + break; + case 0x03: + rRet.append(" ddot "); + break; + case 0x04: + rRet.append(" dddot "); + break; + case 0x05: + if (!nPostSup) + { + sPost.append(" sup {}"); + nPostSup = sPost.getLength(); + } + sPost.insert(nPostSup-1," ' "); + nPostSup += 3; + break; + case 0x06: + if (!nPostSup) + { + sPost.append(" sup {}"); + nPostSup = sPost.getLength(); + } + sPost.insert(nPostSup-1," '' "); + nPostSup += 4; + break; + case 0x07: + if (!nPostlSup) + { + sPost.append(" lsup {}"); + nPostlSup = sPost.getLength(); + } + sPost.insert(nPostlSup-1," ' "); + nPostlSup += 3; + break; + case 0x08: + rRet.append(" tilde "); + break; + case 0x09: + rRet.append(" hat "); + break; + case 0x0b: + rRet.append(" vec "); + break; + case 0x10: + rRet.append(" overstrike "); + break; + case 0x11: + rRet.append(" bar "); + break; + case 0x12: + if (!nPostSup) + { + sPost.append(" sup {}"); + nPostSup = sPost.getLength(); + } + sPost.insert(nPostSup-1," ''' "); + nPostSup += 5; + break; + case 0x14: + rRet.append(" breve "); + break; + default: + OSL_ENSURE(nEmbel < 21,"Embel out of range"); + break; + } + if (nVersion < 3) + break; + }while (nEmbel); +} + +void MathType::HandleSetSize() +{ + sal_uInt8 nTemp(0); + pS->ReadUChar(nTemp); + switch (nTemp) + { + case 101: + pS->ReadInt16( nLSize ); + nLSize = -nLSize; + break; + case 100: + pS->ReadUChar( nTemp ); + nLSize = nTemp; + pS->ReadInt16( nDSize ); + break; + default: + nLSize = nTemp; + pS->ReadUChar( nTemp ); + nDSize = nTemp-128; + break; + } +} + +bool MathType::HandleChar(sal_Int32 &rTextStart, int &rSetSize, int nLevel, + sal_uInt8 nTag, sal_uInt8 nSelector, sal_uInt8 nVariation, bool bSilent) +{ + sal_Unicode nChar(0); + bool bRet = true; + + if (xfAUTO(nTag)) + { + //This is a candidate for function recognition, whatever + //that is! + } + + sal_uInt8 nOldTypeFace = nTypeFace; + pS->ReadUChar( nTypeFace ); + if (nVersion < 3) + { + sal_uInt8 nChar8(0); + pS->ReadUChar( nChar8 ); + nChar = nChar8; + } + else + pS->ReadUtf16( nChar ); + + /* + bad character, old mathtype < 3 has these + */ + if (nChar < 0x20) + return bRet; + + if (xfEMBELL(nTag)) + { + //A bit tricky, the character emblishments for + //mathtype can all be listed after each other, in + //starmath some must go before the character and some + //must go after. In addition some of the emblishments + //may repeated and in starmath some of these groups + //must be gathered together. sPost is the portion that + //follows the char and nPostSup and nPostlSup are the + //indexes at which this class of emblishment is + //collated together + sPost = ""; + nPostSup = nPostlSup = 0; + int nOriglen=rRet.getLength()-rTextStart; + rRet.append(" {"); // #i24340# make what would be "vec {A}_n" become "{vec {A}}_n" + if ((!bSilent) && (nOriglen > 1)) + rRet.append("\""); + bRet = HandleRecords( nLevel+1, nSelector, nVariation ); + if (!bSilent) + { + if (nOriglen > 1) + { + OUString aStr; + TypeFaceToString(aStr,nOldTypeFace); + aStr += "\""; + rRet.insert(std::min(rTextStart, rRet.getLength()), aStr); + + aStr.clear(); + TypeFaceToString(aStr,nTypeFace); + rRet.append(aStr + "{"); + } + else + rRet.append(" {"); + rTextStart = rRet.getLength(); + } + } + + if (!bSilent) + { + sal_Int32 nOldLen = rRet.getLength(); + if ( + HandleSize(nLSize,nDSize,rSetSize) || + (nOldTypeFace != nTypeFace) + ) + { + if ((nOldLen - rTextStart) > 1) + { + rRet.insert(nOldLen, "\""); + OUString aStr; + TypeFaceToString(aStr,nOldTypeFace); + aStr += "\""; + rRet.insert(rTextStart,aStr); + } + rTextStart = rRet.getLength(); + } + nOldLen = rRet.getLength(); + if (!LookupChar(nChar,rRet,nVersion,nTypeFace)) + { + if (nOldLen - rTextStart > 1) + { + rRet.insert(nOldLen, "\""); + OUString aStr; + TypeFaceToString(aStr,nOldTypeFace); + aStr += "\""; + rRet.insert(rTextStart, aStr); + } + rTextStart = rRet.getLength(); + } + lcl_PrependDummyTerm(rRet, rTextStart); + } + + if ((xfEMBELL(nTag)) && (!bSilent)) + { + rRet.append("}}" + sPost); // #i24340# make what would be "vec {A}_n" become "{vec {A}}_n" + rTextStart = rRet.getLength(); + } + return bRet; +} + +bool MathType::HandleLim(SmNode *pNode,int nLevel) +{ + bool bRet=false; + //Special case for the "lim" option in StarMath + if ((pNode->GetToken().eType == TLIM) + || (pNode->GetToken().eType == TLIMSUP) + || (pNode->GetToken().eType == TLIMINF) + ) + { + if (pNode->GetSubNode(1)) + { + sal_uInt8 nVariation2=HandleCScript(pNode->GetSubNode(0),nullptr, + nLevel); + + pS->WriteUChar( 0x0A ); + pS->WriteUChar( LINE ); //line + pS->WriteUChar( CHAR|0x10 ); + pS->WriteUChar( 0x82 ); + pS->WriteUInt16( 'l' ); + pS->WriteUChar( CHAR|0x10 ); + pS->WriteUChar( 0x82 ); + pS->WriteUInt16( 'i' ); + pS->WriteUChar( CHAR|0x10 ); + pS->WriteUChar( 0x82 ); + pS->WriteUInt16( 'm' ); + + if (pNode->GetToken().eType == TLIMSUP) + { + pS->WriteUChar( CHAR ); //some space + pS->WriteUChar( 0x98 ); + pS->WriteUInt16( 0xEB04 ); + + pS->WriteUChar( CHAR|0x10 ); + pS->WriteUChar( 0x82 ); + pS->WriteUInt16( 's' ); + pS->WriteUChar( CHAR|0x10 ); + pS->WriteUChar( 0x82 ); + pS->WriteUInt16( 'u' ); + pS->WriteUChar( CHAR|0x10 ); + pS->WriteUChar( 0x82 ); + pS->WriteUInt16( 'p' ); + } + else if (pNode->GetToken().eType == TLIMINF) + { + pS->WriteUChar( CHAR ); //some space + pS->WriteUChar( 0x98 ); + pS->WriteUInt16( 0xEB04 ); + + pS->WriteUChar( CHAR|0x10 ); + pS->WriteUChar( 0x82 ); + pS->WriteUInt16( 'i' ); + pS->WriteUChar( CHAR|0x10 ); + pS->WriteUChar( 0x82 ); + pS->WriteUInt16( 'n' ); + pS->WriteUChar( CHAR|0x10 ); + pS->WriteUChar( 0x82 ); + pS->WriteUInt16( 'f' ); + } + + + pS->WriteUChar( CHAR ); //some space + pS->WriteUChar( 0x98 ); + pS->WriteUInt16( 0xEB04 ); + + if (nVariation2 != 0xff) + { + pS->WriteUChar( END ); + pS->WriteUChar( END ); + } + HandleNodes(pNode->GetSubNode(1),nLevel+1); + bRet = true; + } + } + return bRet; +} + +void MathType::HandleMAlign(SmNode *pNode,int nLevel) +{ + sal_uInt8 nPushedHAlign=nHAlign; + switch(pNode->GetToken().eType) + { + case TALIGNC: + nHAlign=2; + break; + case TALIGNR: + nHAlign=3; + break; + default: + nHAlign=1; + break; + } + size_t nSize = pNode->GetNumSubNodes(); + for (size_t i = 0; i < nSize; ++i) + { + if (SmNode *pTemp = pNode->GetSubNode(i)) + HandleNodes(pTemp,nLevel+1); + } + nHAlign=nPushedHAlign; +} + +void MathType::HandleMath(SmNode *pNode) +{ + if (pNode->GetToken().eType == TMLINE) + { + pS->WriteUChar( END ); + pS->WriteUChar( LINE ); + bIsReInterpBrace=true; + return; + } + SmMathSymbolNode *pTemp = static_cast<SmMathSymbolNode *>(pNode); + for(sal_Int32 i=0;i<pTemp->GetText().getLength();i++) + { + sal_Unicode nArse = SmTextNode::ConvertSymbolToUnicode(pTemp->GetText()[i]); + if ((nArse == 0x2224) || (nArse == 0x2288) || (nArse == 0x2285) || + (nArse == 0x2289)) + { + pS->WriteUChar( CHAR|0x20 ); + } + else if (nPendingAttributes && + (i == ((pTemp->GetText().getLength()+1)/2)-1)) + { + pS->WriteUChar( 0x22 ); + } + else + pS->WriteUChar( CHAR ); //char without formula recognition + //The typeface seems to be MTEXTRA for unicode characters, + //though how to determine when mathtype chooses one over + //the other is unknown. This should do the trick + //nevertheless. + sal_uInt8 nBias; + if ( (nArse == 0x2213) || (nArse == 0x2218) || + (nArse == 0x210F) || ( + (nArse >= 0x22EE) && (nArse <= 0x22FF) + )) + { + nBias = 0xB; //typeface + } + else if ((nArse == 0x2F) || (nArse == 0x2225)) + nBias = 0x2; //typeface + else if ((nArse > 0x2000) || (nArse == 0x00D7)) + nBias = 0x6; //typeface + else if (nArse == 0x3d1) + nBias = 0x4; + else if ((nArse > 0xFF) && ((nArse < 0x393) || (nArse > 0x3c9))) + nBias = 0xB; //typeface + else + nBias = 0x3; //typeface + + pS->WriteUChar( nSpec+nBias+128 ); //typeface + + if (nArse == 0x2224) + { + pS->WriteUInt16( 0x7C ); + pS->WriteUChar( EMBEL ); + pS->WriteUChar( 0x0A ); + pS->WriteUChar( END ); //end embel + pS->WriteUChar( END ); //end embel + } + else if (nArse == 0x2225) + pS->WriteUInt16( 0xEC09 ); + else if (nArse == 0xE421) + pS->WriteUInt16( 0x2265 ); + else if (nArse == 0x230A) + pS->WriteUInt16( 0xF8F0 ); + else if (nArse == 0x230B) + pS->WriteUInt16( 0xF8FB ); + else if (nArse == 0xE425) + pS->WriteUInt16( 0x2264 ); + else if (nArse == 0x226A) + { + pS->WriteUInt16( 0x3C ); + pS->WriteUChar( CHAR ); + pS->WriteUChar( 0x98 ); + pS->WriteUInt16( 0xEB01 ); + pS->WriteUChar( CHAR ); + pS->WriteUChar( 0x86 ); + pS->WriteUInt16( 0x3c ); + } + else if (nArse == 0x2288) + { + pS->WriteUInt16( 0x2286 ); + pS->WriteUChar( EMBEL ); + pS->WriteUChar( 0x0A ); + pS->WriteUChar( END ); //end embel + pS->WriteUChar( END ); //end embel + } + else if (nArse == 0x2289) + { + pS->WriteUInt16( 0x2287 ); + pS->WriteUChar( EMBEL ); + pS->WriteUChar( 0x0A ); + pS->WriteUChar( END ); //end embel + pS->WriteUChar( END ); //end embel + } + else if (nArse == 0x2285) + { + pS->WriteUInt16( 0x2283 ); + pS->WriteUChar( EMBEL ); + pS->WriteUChar( 0x0A ); + pS->WriteUChar( END ); //end embel + pS->WriteUChar( END ); //end embel + } + else + pS->WriteUInt16( nArse ); + } + nPendingAttributes = 0; +} + +void MathType::HandleAttributes(SmNode *pNode,int nLevel) +{ + int nOldPending = 0; + SmNode *pTemp = nullptr; + SmTextNode *pIsText = nullptr; + + if (nullptr != (pTemp = pNode->GetSubNode(0))) + { + pIsText = static_cast<SmTextNode *>(pNode->GetSubNode(1)); + + switch (pTemp->GetToken().eType) + { + case TWIDEVEC: + //there's just no way we can now handle any character + //attributes (from mathtypes perspective) centered + //over an expression but above template attributes + //such as widevec and similar constructs + //we have to drop them + nOldPending = StartTemplate(0x2f,0x01); + break; + case TCHECK: //Not Exportable + case TACUTE: //Not Exportable + case TGRAVE: //Not Exportable + case TCIRCLE: //Not Exportable + case TWIDEHARPOON: //Not Exportable + case TWIDETILDE: //Not Exportable + case TWIDEHAT: //Not Exportable + break; + case TUNDERLINE: + nOldPending = StartTemplate(0x10); + break; + case TOVERLINE: //If the next node is not text + //or text with more than one char + if ((pIsText->GetToken().eType != TTEXT) || + (pIsText->GetText().getLength() > 1)) + nOldPending = StartTemplate(0x11); + break; + default: + nPendingAttributes++; + break; + } + } + + if (pIsText) + HandleNodes(pIsText,nLevel+1); + + switch (pTemp->GetToken().eType) + { + case TWIDEVEC: + case TUNDERLINE: + EndTemplate(nOldPending); + break; + case TOVERLINE: + if ((pIsText->GetToken().eType != TTEXT) || + (pIsText->GetText().getLength() > 1)) + EndTemplate(nOldPending); + break; + default: + break; + } + + //if there was no suitable place to put the attribute, + //then we have to just give up on it + if (nPendingAttributes) + nPendingAttributes--; + else + { + if ((nInsertion != 0) && nullptr != (pTemp = pNode->GetSubNode(0))) + { + auto nPos = pS->Tell(); + nInsertion--; + pS->Seek(nInsertion); + switch(pTemp->GetToken().eType) + { + case TACUTE: //Not Exportable + case TGRAVE: //Not Exportable + case TCIRCLE: //Not Exportable + break; + case TCDOT: + pS->WriteUChar( 2 ); + break; + case TDDOT: + pS->WriteUChar( 3 ); + break; + case TDDDOT: + pS->WriteUChar( 4 ); + break; + case TTILDE: + pS->WriteUChar( 8 ); + break; + case THAT: + pS->WriteUChar( 9 ); + break; + case TVEC: + pS->WriteUChar( 11 ); + break; + case TOVERSTRIKE: + pS->WriteUChar( 16 ); + break; + case TOVERLINE: + if ((pIsText->GetToken().eType == TTEXT) && + (pIsText->GetText().getLength() == 1)) + pS->WriteUChar( 17 ); + break; + case TBREVE: + pS->WriteUChar( 20 ); + break; + case TWIDEVEC: + case TWIDEHARPOON: + case TUNDERLINE: + case TWIDETILDE: + case TWIDEHAT: + break; + case TBAR: + pS->WriteUChar( 17 ); + break; + default: + pS->WriteUChar( 2 ); + break; + } + pS->Seek(nPos); + } + } +} + +void MathType::HandleText(SmNode *pNode) +{ + SmTextNode *pTemp = static_cast<SmTextNode *>(pNode); + for(sal_Int32 i=0;i<pTemp->GetText().getLength();i++) + { + if (nPendingAttributes && + (i == ((pTemp->GetText().getLength()+1)/2)-1)) + { + pS->WriteUChar( 0x22 ); //char, with attributes right + //after the character + } + else + pS->WriteUChar( CHAR ); + + sal_uInt8 nFace = 0x1; + if (pNode->GetFont().GetItalic() == ITALIC_NORMAL) + nFace = 0x3; + else if (pNode->GetFont().GetWeight() == WEIGHT_BOLD) + nFace = 0x7; + pS->WriteUChar( nFace+128 ); //typeface + sal_uInt16 nChar = pTemp->GetText()[i]; + pS->WriteUInt16( SmTextNode::ConvertSymbolToUnicode(nChar) ); + + //Mathtype can only have these sort of character + //attributes on a single character, starmath can put them + //anywhere, when the entity involved is a text run this is + //a large effort to place the character attribute on the + //central mathtype character so that it does pretty much + //what the user probably has in mind. The attributes + //filled in here are dummy ones which are replaced in the + //ATTRIBUTE handler if a suitable location for the + //attributes was found here. Unfortunately it is + //possible for starmath to place character attributes on + //entities which cannot occur in mathtype e.g. a Summation + //symbol so these attributes may be lost + if (nPendingAttributes && + (i == ((pTemp->GetText().getLength()+1)/2)-1)) + { + pS->WriteUChar( EMBEL ); + while (nPendingAttributes) + { + pS->WriteUChar( 2 ); + //wedge the attributes in here and clear + //the pending stack + nPendingAttributes--; + } + nInsertion=pS->Tell(); + pS->WriteUChar( END ); //end embel + pS->WriteUChar( END ); //end embel + } + } +} + +extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportMathType(SvStream &rStream) +{ + OUStringBuffer sText; + MathType aEquation(sText); + bool bRet = false; + try + { + bRet = aEquation.Parse(&rStream); + } + catch (const std::out_of_range&) + { + } + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |