cURL: follow redirects
[LibreOffice.git] / starmath / source / mathtype.cxx
blob1e107158b9df2e668ced298b5bc5f3f3696c7167
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <mathtype.hxx>
22 #include <filter/msfilter/classids.hxx>
23 #include <osl/diagnose.h>
24 #include <sfx2/docfile.hxx>
26 void MathType::Init()
28 //These are the default MathType sizes
29 aSizeTable.push_back(12);
30 aSizeTable.push_back(8);
31 aSizeTable.push_back(6);
32 aSizeTable.push_back(24);
33 aSizeTable.push_back(10);
34 aSizeTable.push_back(12);
35 aSizeTable.push_back(12);
38 These are the default MathType italic/bold settings If mathtype is changed
39 from its defaults, there is nothing we can do, as this information is not
40 stored in the document
42 MathTypeFont aFont;
43 for(sal_uInt8 i=1;i<=11;i++)
45 aFont.nTface = i+128;
46 switch (i)
48 default:
49 aFont.nStyle=0;
50 break;
51 case 3:
52 case 4:
53 aFont.nStyle=1;
54 break;
55 case 7:
56 aFont.nStyle=2;
57 break;
59 aUserStyles.insert(aFont);
64 /*ToDo replace with table rather than switch, returns
65 sal_True in the case that the char is just a char, and
66 sal_False if the character is an operator which must not be
67 placed inside the quote sequence designed to protect
68 against being parsed as a keyword
70 General solution required to force starmath to handle
71 unicode math chars the way it handles its own math
72 chars rather than handle them as text as it will do
73 for the default case below, i.e. incorrect spacing
74 between math symbols and ordinary text e.g. 1=2 rather
75 than 1 = 2
77 bool MathType::LookupChar(sal_Unicode nChar,OUString &rRet,sal_uInt8 nVersion,
78 sal_uInt8 nTypeFace)
80 bool bRet=false;
81 const char *pC = nullptr;
82 switch(nChar)
84 case 0x0000:
85 pC = " none ";
86 break;
87 case 0x00ac:
88 pC = " neg ";
89 break;
90 case 0x00b1:
91 pC = " +- ";
92 break;
93 case '(':
94 pC = " \\( ";
95 break;
96 case ')':
97 pC = " \\) ";
98 break;
99 case '[':
100 pC = " \\[ ";
101 break;
102 case ']':
103 pC = " \\] ";
104 break;
105 case '.':
106 pC = " \".\" ";
107 break;
108 case 0xae:
109 if ((nVersion < 3) && (nTypeFace == 0x86))
110 pC = " rightarrow ";
111 else
113 rRet += OUStringLiteral1( nChar );
114 bRet=true;
116 break;
117 case 0x00fb:
118 if ((nVersion < 3) && (nTypeFace == 0x81))
119 nChar = 0xDF;
120 rRet += OUStringLiteral1( nChar );
121 bRet=true;
122 break;
123 case 'a':
124 if ((nVersion < 3) && (nTypeFace == 0x84))
125 nChar = 0x3b1;
126 rRet += OUStringLiteral1( nChar );
127 bRet=true;
128 break;
129 case 'b':
130 if ((nVersion < 3) && (nTypeFace == 0x84))
131 nChar = 0x3b2;
132 rRet += OUStringLiteral1( nChar );
133 bRet=true;
134 break;
135 case 'l':
136 if ((nVersion < 3) && (nTypeFace == 0x84))
137 nChar = 0x3bb;
138 rRet += OUStringLiteral1( nChar );
139 bRet=true;
140 break;
141 case 'n':
142 if ((nVersion < 3) && (nTypeFace == 0x84))
143 nChar = 0x3bd;
144 rRet += OUStringLiteral1( nChar );
145 bRet=true;
146 break;
147 case 'r':
148 if ((nVersion < 3) && (nTypeFace == 0x84))
149 nChar = 0x3c1;
150 rRet += OUStringLiteral1( nChar );
151 bRet=true;
152 break;
153 case 'D':
154 if ((nVersion < 3) && (nTypeFace == 0x84))
155 nChar = 0x394;
156 rRet += OUStringLiteral1( nChar );
157 bRet=true;
158 break;
159 case 0xa9:
160 if ((nVersion < 3) && (nTypeFace == 0x82))
161 nChar = '\'';
162 rRet += OUStringLiteral1( nChar );
163 bRet=true;
164 break;
165 case 0x00f1:
166 if ((nVersion < 3) && (nTypeFace == 0x86))
167 pC = " \\rangle ";
168 else
170 rRet += OUStringLiteral1( nChar );
171 bRet=true;
173 break;
174 case 0x00a3:
175 if ((nVersion < 3) && (nTypeFace == 0x86))
176 pC = " <= ";
177 else
179 rRet += OUStringLiteral1( nChar );
180 bRet=true;
182 break;
183 case 0x00de:
184 if ((nVersion < 3) && (nTypeFace == 0x86))
185 pC = " drarrow ";
186 else
188 rRet += OUStringLiteral1( nChar );
189 bRet=true;
191 break;
192 case 0x0057:
193 if ((nVersion < 3) && (nTypeFace == 0x85))
194 pC = " %OMEGA ";
195 else
197 rRet += OUStringLiteral1( nChar );
198 bRet=true;
200 break;
201 case 0x007b:
202 pC = " lbrace ";
203 break;
204 case 0x007c:
205 pC = " \\lline ";
206 break;
207 case 0x007d:
208 pC = " rbrace ";
209 break;
210 case 0x007e:
211 pC = " \"~\" ";
212 break;
213 case 0x2224:
214 pC = " ndivides ";
215 break;
216 case 0x2225:
217 pC = " parallel ";
218 break;
219 case 0x00d7:
220 if (nVersion < 3)
221 pC = " cdot ";
222 else
223 pC = " times ";
224 break;
225 case 0x00f7:
226 pC = " div ";
227 break;
228 case 0x019b:
229 pC = " lambdabar ";
230 break;
231 case 0x2026:
232 pC = " dotslow ";
233 break;
234 case 0x2022:
235 pC = " cdot ";
236 break;
237 case 0x2102:
238 pC = " setC ";
239 break;
240 case 0x210f:
241 pC = " hbar ";
242 break;
243 case 0x2111:
244 pC = " Im ";
245 break;
246 case 0x2115:
247 pC = " setN ";
248 break;
249 case 0x2118:
250 pC = " wp ";
251 break;
252 case 0x211a:
253 pC = " setQ ";
254 break;
255 case 0x211c:
256 pC = " Re ";
257 break;
258 case 0x211d:
259 pC = " setR ";
260 break;
261 case 0x2124:
262 pC = " setZ ";
263 break;
264 case 0x2135:
265 pC = " aleph ";
266 break;
267 case 0x2190:
268 pC = " leftarrow ";
269 break;
270 case 0x2191:
271 pC = " uparrow ";
272 break;
273 case 0x2192:
274 pC = " rightarrow ";
275 break;
276 case 0x0362:
277 pC = " widevec ";
278 break;
279 case 0x2193:
280 pC = " downarrow ";
281 break;
282 case 0x21d0:
283 pC = " dlarrow ";
284 break;
285 case 0x21d2:
286 pC = " drarrow ";
287 break;
288 case 0x21d4:
289 pC = " dlrarrow ";
290 break;
291 case 0x2200:
292 pC = " forall ";
293 break;
294 case 0x2202:
295 pC = " partial ";
296 break;
297 case 0x2203:
298 pC = " exists ";
299 break;
300 case 0x2204:
301 pC = " notexists ";
302 break;
303 case 0x2205:
304 pC = " emptyset ";
305 break;
306 case 0x2207:
307 pC = " nabla ";
308 break;
309 case 0x2208:
310 pC = " in ";
311 break;
312 case 0x2209:
313 pC = " notin ";
314 break;
315 case 0x220d:
316 pC = " owns ";
317 break;
318 case 0x220f:
319 pC = " prod ";
320 break;
321 case 0x2210:
322 pC = " coprod ";
323 break;
324 case 0x2211:
325 pC = " sum ";
326 break;
327 case 0x2212:
328 pC = " - ";
329 break;
330 case 0x2213:
331 pC = " -+ ";
332 break;
333 case 0x2217:
334 pC = " * ";
335 break;
336 case 0x2218:
337 pC = " circ ";
338 break;
339 case 0x221d:
340 pC = " prop ";
341 break;
342 case 0x221e:
343 pC = " infinity ";
344 break;
345 case 0x2227:
346 pC = " and ";
347 break;
348 case 0x2228:
349 pC = " or ";
350 break;
351 case 0x2229:
352 pC = " intersection ";
353 break;
354 case 0x222a:
355 pC = " union ";
356 break;
357 case 0x222b:
358 pC = " int ";
359 break;
360 case 0x222c:
361 pC = " iint ";
362 break;
363 case 0x222d:
364 pC = " iiint ";
365 break;
366 case 0x222e:
367 pC = " lint ";
368 break;
369 case 0x222f:
370 pC = " llint ";
371 break;
372 case 0x2230:
373 pC = " lllint ";
374 break;
375 case 0x2245:
376 pC = " simeq ";
377 break;
378 case 0x2248:
379 pC = " approx ";
380 break;
381 case 0x2260:
382 pC = " <> ";
383 break;
384 case 0x2261:
385 pC = " equiv ";
386 break;
387 case 0x2264:
388 pC = " <= ";
389 break;
390 case 0x2265:
391 pC = " >= ";
392 break;
394 case 0x227A:
395 pC = " prec ";
396 break;
397 case 0x227B:
398 pC = " succ ";
399 break;
400 case 0x227C:
401 pC = " preccurlyeq ";
402 break;
403 case 0x227D:
404 pC = " succcurlyeq ";
405 break;
406 case 0x227E:
407 pC = " precsim ";
408 break;
409 case 0x227F:
410 pC = " succsim ";
411 break;
412 case 0x2280:
413 pC = " nprec ";
414 break;
415 case 0x2281:
416 pC = " nsucc ";
417 break;
419 case 0x2282:
420 pC = " subset ";
421 break;
422 case 0x2283:
423 pC = " supset ";
424 break;
425 case 0x2284:
426 pC = " nsubset ";
427 break;
428 case 0x2285:
429 pC = " nsupset ";
430 break;
431 case 0x2286:
432 pC = " subseteq ";
433 break;
434 case 0x2287:
435 pC = " supseteq ";
436 break;
437 case 0x2288:
438 pC = " nsubseteq ";
439 break;
440 case 0x2289:
441 pC = " nsupseteq ";
442 break;
443 case 0x22b2:
444 case 0x22b3:
445 rRet += " " + OUStringLiteral1( nChar ) + " ";
446 break;
447 case 0x22a5:
448 pC = " ortho ";
449 break;
450 case 0x22c5:
451 pC = " cdot ";
452 break;
453 case 0x22ee:
454 pC = " dotsvert ";
455 break;
456 case 0x22ef:
457 pC = " dotsaxis ";
458 break;
459 case 0x22f0:
460 pC = " dotsup ";
461 break;
462 case 0x22f1:
463 pC = " dotsdown ";
464 break;
465 case MS_LANGLE:
466 case MS_LMATHANGLE:
467 pC = " langle ";
468 break;
469 case MS_RANGLE:
470 case MS_RMATHANGLE:
471 pC = " rangle ";
472 break;
473 case 0x301a:
474 pC = " ldbracket ";
475 break;
476 case 0x301b:
477 pC = " rdbracket ";
478 break;
479 case 0xe083:
480 rRet += "+";
481 bRet=true;
482 break;
483 case '^':
484 case 0xe091:
485 pC = " widehat ";
486 break;
487 case 0xe096:
488 pC = " widetilde ";
489 break;
490 case 0xe098:
491 pC = " widevec ";
492 break;
493 case 0xE421:
494 pC = " geslant ";
495 break;
496 case 0xE425:
497 pC = " leslant ";
498 break;
499 case 0xeb01: //no space
500 case 0xeb08: //normal space
501 bRet=true;
502 break;
503 case 0xef04: //tiny space
504 case 0xef05: //tiny space
505 case 0xeb02: //small space
506 case 0xeb04: //medium space
507 rRet += "`";
508 break;
509 case 0xeb05: //large space
510 rRet += "~";
511 break;
512 case 0x3a9:
513 pC = " %OMEGA ";
514 break;
515 default:
516 rRet += OUStringLiteral1( nChar );
517 bRet=true;
518 break;
520 if (pC)
521 rRet += OUString::createFromAscii( pC );
522 return bRet;
525 void MathTypeFont::AppendStyleToText(OUString &rRet)
527 const char *pC = nullptr;
528 switch (nStyle)
530 default:
531 case 0:
532 break;
533 case 1:
534 pC = " ital ";
535 break;
536 case 2:
537 pC = " bold ";
538 break;
539 case 3:
540 pC = " bold italic";
541 break;
543 if (pC)
544 rRet += OUString::createFromAscii( pC );
547 void MathType::TypeFaceToString(OUString &rTxt,sal_uInt8 nFace)
549 MathTypeFont aFont(nFace);
550 MathTypeFontSet::iterator aItr = aUserStyles.find(aFont);
551 if (aItr != aUserStyles.end())
552 aFont.nStyle = aItr->nStyle;
553 aFont.AppendStyleToText(rTxt);
556 bool MathType::Parse(SotStorage *pStor)
558 tools::SvRef<SotStorageStream> xSrc = pStor->OpenSotStream(
559 "Equation Native",
560 StreamMode::STD_READ);
561 if ( (!xSrc.Is()) || (SVSTREAM_OK != xSrc->GetError()))
562 return false;
563 pS = xSrc.get();
564 pS->SetEndian( SvStreamEndian::LITTLE );
566 EQNOLEFILEHDR aHdr;
567 aHdr.Read(pS);
568 pS->ReadUChar( nVersion );
569 pS->ReadUChar( nPlatform );
570 pS->ReadUChar( nProduct );
571 pS->ReadUChar( nProdVersion );
572 pS->ReadUChar( nProdSubVersion );
574 if (nVersion > 3) // allow only supported versions of MathType to be parsed
575 return false;
577 bool bRet = HandleRecords(0);
578 //little crude hack to close occasionally open expressions
579 //a sophisticated system to determine what expressions are
580 //opened is required, but this is as much work as rewriting
581 //starmaths internals.
582 rRet += "{}";
584 #ifdef CAOLAN
585 //sanity check
587 //sigh, theres no point! MathType (in some bizarre subvarient) pads
588 //the end of the formula with ENDs (0)'s
589 sal_uLong nEnd = pS->Tell();
590 SAL_WARN_IF(nEnd == pS->Seek(STREAM_SEEK_TO_END), "starmath", "Possibly unfully parsed formula");
591 #endif
592 return bRet;
595 static void lcl_PrependDummyTerm(OUString &rRet, sal_Int32 &rTextStart)
597 if ((rTextStart < rRet.getLength()) &&
598 (rRet[rTextStart] == '=') &&
599 ((rTextStart == 0) || (rRet[ rTextStart-1 ] == '{'))
602 rRet = rRet.replaceAt(rTextStart,0," {}");
603 rTextStart+=3;
607 static void lcl_AppendDummyTerm(OUString &rRet)
609 bool bOk=false;
610 for(int nI=rRet.getLength()-1;nI >= 0; nI--)
612 sal_Int32 nIdx = sal::static_int_cast< sal_Int32 >(nI);
613 sal_Unicode nChar = rRet[nIdx];
614 if (nChar == ' ')
615 continue;
616 if (rRet[nIdx] != '{')
617 bOk=true;
618 break;
620 if (!bOk) //No term, use dummy
621 rRet += " {}";
624 void MathType::HandleNudge()
626 sal_uInt8 nXNudge;
627 pS->ReadUChar( nXNudge );
628 sal_uInt8 nYNudge;
629 pS->ReadUChar( nYNudge );
630 if (nXNudge == 128 && nYNudge == 128)
632 sal_uInt16 nXLongNudge;
633 sal_uInt16 nYLongNudge;
634 pS->ReadUInt16( nXLongNudge );
635 pS->ReadUInt16( nYLongNudge );
639 /* Fabulously complicated as many tokens have to be reordered and generally
640 * moved around from mathtypes paradigm to starmaths. */
641 bool MathType::HandleRecords(int nLevel, sal_uInt8 nSelector,
642 sal_uInt8 nVariation, int nMatrixRows, int nMatrixCols)
644 sal_uInt8 nTag,nRecord;
645 sal_uInt8 nTabType,nTabStops;
646 sal_uInt16 nTabOffset;
647 int i, newline=0;
648 bool bSilent=false;
649 int nPart=0;
650 OUString sPush,sMainTerm;
651 int nSetSize=0,nSetAlign=0;
652 int nCurRow=0,nCurCol=0;
653 bool bOpenString=false;
654 sal_Int32 nTextStart = 0;
655 sal_Int32 nSubSupStartPos = 0;
656 sal_Int32 nLastTemplateBracket=-1;
657 bool bRet = true;
661 nTag = 0;
662 pS->ReadUChar( nTag );
663 nRecord = nTag&0x0F;
665 /*MathType strings can of course include words which
666 *are StarMath keywords, the simplest solution is
667 to escape strings of greater than len 1 with double
668 quotes to avoid scanning the TokenTable for matches
670 Unfortunately it may turn out that the string gets
671 split during the handling of a character emblishment
672 so this special case must be handled in the
673 character handler case 2:
675 if ((nRecord == CHAR) && (!bOpenString))
677 bOpenString=true;
678 nTextStart = rRet.getLength();
680 else if ((nRecord != CHAR) && (bOpenString))
682 bOpenString=false;
683 if ((rRet.getLength() - nTextStart) > 1)
685 OUString aStr;
686 TypeFaceToString(aStr,nTypeFace);
687 aStr += "\"";
688 rRet = rRet.replaceAt(nTextStart,0,aStr);
689 rRet += "\"";
691 else if (nRecord == END && !rRet.isEmpty())
693 sal_Unicode cChar = 0;
694 sal_Int32 nI = rRet.getLength()-1;
695 while (nI && ((cChar = rRet[nI]) == ' '))
696 --nI;
697 if ((cChar == '=') || (cChar == '+') || (cChar == '-'))
698 rRet += "{}";
702 switch(nRecord)
704 case LINE:
706 if (xfLMOVE(nTag))
707 HandleNudge();
709 if (newline>0)
710 rRet += "\nnewline\n";
711 if (!(xfNULL(nTag)))
713 switch (nSelector)
715 case 0x0:
716 if (nVariation==0)
717 rRet += " langle ";
718 else if (nVariation==1)
719 rRet += " \\langle ";
720 break;
721 case 0x1:
722 if (nVariation==0)
723 rRet += " left (";
724 else if (nVariation==1)
725 rRet += "\\(";
726 break;
727 case 0x2:
728 if ((nVariation==0) || (nVariation==1))
729 rRet += " left lbrace ";
730 else
731 rRet += " left none ";
732 break;
733 case 0x3:
734 if (nVariation==0)
735 rRet += " left [";
736 else if (nVariation==1)
737 rRet += "\\[";
738 break;
739 case 0x8:
740 case 0xb:
741 rRet += " \\[";
742 break;
743 case 0x4:
744 if (nVariation==0)
745 rRet += " lline ";
746 else if (nVariation==1)
747 rRet += " \\lline ";
748 break;
749 case 0x5:
750 if (nVariation==0)
751 rRet += " ldline ";
752 else if (nVariation==1)
753 rRet += " \\ldline ";
754 break;
755 case 0x6:
756 if (nVariation == 0 || nVariation == 1)
757 rRet += " left lfloor ";
758 else if (nVariation==1)
759 rRet += " left none ";
760 break;
761 case 0x7:
762 if (nVariation==0)
763 rRet += " lceil ";
764 else if (nVariation==1)
765 rRet += " \\lceil ";
766 break;
767 case 0x9:
768 case 0xa:
769 rRet += " \\]";
770 break;
771 case 0xc:
772 rRet += " \\(";
773 break;
774 case 0xd:
775 if (nPart == 0)
777 if (nVariation == 0)
778 rRet += " sqrt";
779 else
781 rRet += " nroot";
782 sPush = rRet;
783 rRet.clear();
786 rRet += " {";
787 break;
788 case 0xe:
789 if (nPart == 0)
790 rRet += " { ";
793 if (nPart == 1)
794 rRet += " over ";
795 rRet += " {";
796 break;
797 case 0xf:
798 nSubSupStartPos = rRet.getLength();
799 if ((nVariation == 0) ||
800 ((nVariation == 2) && (nPart==1)))
802 lcl_AppendDummyTerm(rRet);
803 rRet += " rSup";
805 else if ((nVariation == 1) ||
806 ((nVariation == 2) && (nPart==0)))
808 lcl_AppendDummyTerm(rRet);
809 rRet += " rSub";
811 rRet += " {";
812 break;
813 case 0x10:
814 if (nVariation == 0)
815 rRet += " {underline ";
816 else if (nVariation == 1)
817 rRet += " {underline underline ";
818 rRet += " {";
819 break;
820 case 0x11:
821 if (nVariation == 0)
822 rRet += " {overline ";
823 else if (nVariation == 1)
824 rRet += " {overline overline ";
825 rRet += " {";
826 break;
827 case 0x12:
828 if (nPart == 0)
830 if (nVariation == 0)
831 rRet += " widevec ";//left arrow above
832 else if (nVariation == 1)
833 rRet += " widevec ";//left arrow below
834 rRet += " {";
836 break;
837 case 0x13:
838 if (nPart == 0)
840 if (nVariation == 0)
841 rRet += " widevec ";//right arrow above
842 else if (nVariation == 1)
843 rRet += " widevec ";//right arrow below
844 rRet += " {";
846 break;
847 case 0x14:
848 if (nPart == 0)
850 if (nVariation == 0)
851 rRet += " widevec ";//double arrow above
852 else if (nVariation == 1)
853 rRet += " widevec ";//double arrow below
854 rRet += " {";
856 break;
857 case 0x15:
858 if (nPart == 0)
860 if ((nVariation == 3) || (nVariation == 4))
861 rRet += " lInt";
862 else
863 rRet += " Int";
864 if ( (nVariation != 0) && (nVariation != 3))
866 sPush = rRet;
867 rRet.clear();
870 if (((nVariation == 1) ||
871 (nVariation == 4)) && (nPart==1))
872 rRet += " rSub";
873 else if ((nVariation == 2) && (nPart==2))
874 rRet += " rSup";
875 else if ((nVariation == 2) && (nPart==1))
876 rRet += " rSub";
877 rRet += " {";
878 break;
879 case 0x16:
880 if (nPart == 0)
882 if ((nVariation == 2) || (nVariation == 3))
883 rRet += " llInt";
884 else
885 rRet += " iInt";
886 if ( (nVariation != 0) && (nVariation != 2))
888 sPush = rRet;
889 rRet.clear();
892 if (((nVariation == 1) ||
893 (nVariation == 3)) && (nPart==1))
894 rRet += " rSub";
895 rRet += " {";
896 break;
897 case 0x17:
898 if (nPart == 0)
900 if ((nVariation == 2) || (nVariation == 3))
901 rRet += " lllInt";
902 else
903 rRet += " iiInt";
904 if ( (nVariation != 0) && (nVariation != 2))
906 sPush = rRet;
907 rRet.clear();
910 if (((nVariation == 1) ||
911 (nVariation == 3)) && (nPart==1))
912 rRet += " rSub";
913 rRet += " {";
914 break;
915 case 0x18:
916 if (nPart == 0)
918 if (nVariation == 2)
919 rRet += " lInt";
920 else
921 rRet += " Int";
922 sPush = rRet;
923 rRet.clear();
925 if (((nVariation == 1) ||
926 (nVariation == 2)) && (nPart==1))
927 rRet += " cSub";
928 else if ((nVariation == 0) && (nPart==2))
929 rRet += " cSup";
930 else if ((nVariation == 0) && (nPart==1))
931 rRet += " cSub";
932 rRet += " {";
933 break;
934 case 0x19:
935 if (nPart == 0)
937 if (nVariation == 0)
938 rRet += " llInt";
939 else
940 rRet += " iInt";
941 sPush = rRet;
942 rRet.clear();
944 if (nPart==1)
945 rRet += " cSub";
946 rRet += " {";
947 break;
948 case 0x1a:
949 if (nPart == 0)
951 if (nVariation == 0)
952 rRet += " lllInt";
953 else
954 rRet += " iiInt";
955 sPush = rRet;
956 rRet.clear();
958 if (nPart==1)
959 rRet += " cSub";
960 rRet += " {";
961 break;
962 case 0x1b:
963 case 0x1c:
964 rRet += " {";
965 break;
966 case 0x1d:
967 if (nPart == 0)
969 rRet += " Sum";
970 if (nVariation != 2)
972 sPush = rRet;
973 rRet.clear();
976 if ((nVariation == 0) && (nPart==1))
977 rRet += " cSub";
978 else if ((nVariation == 1) && (nPart==2))
979 rRet += " cSup";
980 else if ((nVariation == 1) && (nPart==1))
981 rRet += " cSub";
982 rRet += " {";
983 break;
984 case 0x1e:
985 if (nPart == 0)
987 rRet += " Sum";
988 sPush = rRet;
989 rRet.clear();
991 if ((nVariation == 0) && (nPart==1))
992 rRet += " rSub";
993 else if ((nVariation == 1) && (nPart==2))
994 rRet += " rSup";
995 else if ((nVariation == 1) && (nPart==1))
996 rRet += " rSub";
997 rRet += " {";
998 break;
999 case 0x1f:
1000 if (nPart == 0)
1002 rRet += " Prod";
1003 if (nVariation != 2)
1005 sPush = rRet;
1006 rRet.clear();
1009 if ((nVariation == 0) && (nPart==1))
1010 rRet += " cSub";
1011 else if ((nVariation == 1) && (nPart==2))
1012 rRet += " cSup";
1013 else if ((nVariation == 1) && (nPart==1))
1014 rRet += " cSub";
1015 rRet += " {";
1016 break;
1017 case 0x20:
1018 if (nPart == 0)
1020 rRet += " Prod";
1021 sPush = rRet;
1022 rRet.clear();
1024 if ((nVariation == 0) && (nPart==1))
1025 rRet += " rSub";
1026 else if ((nVariation == 1) && (nPart==2))
1027 rRet += " rSup";
1028 else if ((nVariation == 1) && (nPart==1))
1029 rRet += " rSub";
1030 rRet += " {";
1031 break;
1032 case 0x21:
1033 if (nPart == 0)
1035 rRet += " coProd";
1036 if (nVariation != 2)
1038 sPush = rRet;
1039 rRet.clear();
1042 if ((nVariation == 0) && (nPart==1))
1043 rRet += " cSub";
1044 else if ((nVariation == 1) && (nPart==2))
1045 rRet += " cSup";
1046 else if ((nVariation == 1) && (nPart==1))
1047 rRet += " cSub";
1048 rRet += " {";
1049 break;
1050 case 0x22:
1051 if (nPart == 0)
1053 rRet += " coProd";
1054 sPush = rRet;
1055 rRet.clear();
1057 if ((nVariation == 0) && (nPart==1))
1058 rRet += " rSub";
1059 else if ((nVariation == 1) && (nPart==2))
1060 rRet += " rSup";
1061 else if ((nVariation == 1) && (nPart==1))
1062 rRet += " rSub";
1063 rRet += " {";
1064 break;
1065 case 0x23:
1066 if (nPart == 0)
1068 rRet += " union"; //union
1069 if (nVariation != 2)
1071 sPush = rRet;
1072 rRet.clear();
1075 if ((nVariation == 0) && (nPart==1))
1076 rRet += " cSub";
1077 else if ((nVariation == 1) && (nPart==2))
1078 rRet += " cSup";
1079 else if ((nVariation == 1) && (nPart==1))
1080 rRet += " cSub";
1081 rRet += " {";
1082 break;
1083 case 0x24:
1084 if (nPart == 0)
1086 rRet += " union"; //union
1087 sPush = rRet;
1088 rRet.clear();
1090 if ((nVariation == 0) && (nPart==1))
1091 rRet += " rSub";
1092 else if ((nVariation == 1) && (nPart==2))
1093 rRet += " rSup";
1094 else if ((nVariation == 1) && (nPart==1))
1095 rRet += " rSub";
1096 rRet += " {";
1097 break;
1098 case 0x25:
1099 if (nPart == 0)
1101 rRet += " intersect"; //intersect
1102 if (nVariation != 2)
1104 sPush = rRet;
1105 rRet.clear();
1108 if ((nVariation == 0) && (nPart==1))
1109 rRet += " cSub";
1110 else if ((nVariation == 1) && (nPart==2))
1111 rRet += " cSup";
1112 else if ((nVariation == 1) && (nPart==1))
1113 rRet += " cSub";
1114 rRet += " {";
1115 break;
1116 case 0x26:
1117 if (nPart == 0)
1119 rRet += " intersect"; //intersect
1120 sPush = rRet;
1121 rRet.clear();
1123 if ((nVariation == 0) && (nPart==1))
1124 rRet += " rSub";
1125 else if ((nVariation == 1) && (nPart==2))
1126 rRet += " rSup";
1127 else if ((nVariation == 1) && (nPart==1))
1128 rRet += " rSub";
1129 rRet += " {";
1130 break;
1131 case 0x27:
1132 if ((nVariation == 0) && (nPart==1))
1133 rRet += " cSup";
1134 else if ((nVariation == 1) && (nPart==1))
1135 rRet += " cSub";
1136 else if ((nVariation == 2) && (nPart==1))
1137 rRet += " cSub";
1138 else if ((nVariation == 2) && (nPart==2))
1139 rRet += " cSup";
1140 rRet += " {";
1141 break;
1142 case 0x28:
1143 if (nVariation == 0)
1145 if (nPart == 0)
1147 sPush = rRet;
1148 rRet.clear();
1151 rRet += " {";
1152 if (nVariation == 0)
1154 if (nPart == 1)
1155 rRet += "alignr ";
1157 if (nPart == 0)
1158 rRet += "\\lline ";
1159 if (nVariation == 1)
1160 rRet += "overline ";
1161 break;
1162 case 0x29:
1163 rRet += " {";
1164 break;
1165 case 0x2a:
1166 if (nPart == 0)
1168 sPush = rRet;
1169 rRet.clear();
1171 if ((nVariation == 0) && (nPart==0))
1172 rRet += " rSup";
1173 else if ((nVariation == 2) && (nPart==1))
1174 rRet += " rSup";
1175 else if ((nVariation == 1) && (nPart==0))
1176 rRet += " rSub";
1177 else if ((nVariation == 2) && (nPart==0))
1178 rRet += " rSub";
1179 rRet += " {";
1180 break;
1181 case 0x2b:
1182 if (nPart == 0)
1184 sPush = rRet;
1185 rRet.clear();
1187 if ((nVariation == 0) && (nPart==0))
1188 rRet += " cSup";
1189 else if ((nVariation == 2) && (nPart==1))
1190 rRet += " cSup";
1191 else if ((nVariation == 1) && (nPart==0))
1192 rRet += " cSub";
1193 else if ((nVariation == 2) && (nPart==0))
1194 rRet += " cSub";
1195 rRet += " {";
1196 break;
1197 case 0x2c:
1198 if (nPart == 0)
1199 rRet += "\"\"";
1200 if ((nVariation == 0)
1201 || ((nVariation == 2) && (nPart==1)))
1202 rRet += " lSup";
1203 else if ((nVariation == 1)
1204 || ((nVariation == 2) && (nPart==0)))
1205 rRet += " lSub";
1206 rRet += " {";
1207 break;
1208 case 0x2d:
1209 if (nVariation==0)
1211 if (nPart == 0)
1212 rRet += " langle ";
1214 else if (nVariation==1)
1216 rRet += " \\langle ";
1217 newline--;
1219 else if (nVariation==2)
1221 rRet += " \\lline ";
1222 newline--;
1224 break;
1225 case 0x2e:
1226 if (nVariation == 0)
1227 rRet += " widevec ";//left below
1228 else if (nVariation == 1)
1229 rRet += " widevec ";//right below
1230 else if (nVariation == 2)
1231 rRet += " widevec ";//double headed below
1232 rRet += " {";
1233 break;
1234 case 0x2f:
1235 if (nVariation == 0)
1236 rRet += " widevec ";//left above
1237 else if (nVariation == 1)
1238 rRet += " widevec ";//right above
1239 else if (nVariation == 2)
1240 rRet += " widevec ";//double headed above
1241 rRet += " {";
1242 break;
1243 default:
1244 break;
1246 sal_Int16 nOldCurSize=nCurSize;
1247 sal_Int32 nSizeStartPos = rRet.getLength();
1248 HandleSize( nLSize, nDSize, nSetSize );
1249 bRet = HandleRecords( nLevel+1 );
1250 while (nSetSize)
1252 bool bOk=false;
1253 sal_Int32 nI = rRet.lastIndexOf('{');
1254 if (nI != -1)
1256 for(nI=nI+1;nI<rRet.getLength();nI++)
1257 if (rRet[nI] != ' ')
1259 bOk=true;
1260 break;
1263 else
1264 bOk=true;
1266 if (bOk)
1267 rRet += "} ";
1268 else
1269 rRet = rRet.replaceAt( nSizeStartPos, rRet.getLength(), "" );
1270 nSetSize--;
1271 nCurSize=nOldCurSize;
1275 HandleMatrixSeparator(nMatrixRows,nMatrixCols,
1276 nCurCol,nCurRow);
1278 switch (nSelector)
1280 case 0x0:
1281 if (nVariation==0)
1282 rRet += " rangle ";
1283 else if (nVariation==2)
1284 rRet += " \\rangle ";
1285 break;
1286 case 0x1:
1287 if (nVariation==0)
1288 rRet += " right )";
1289 else if (nVariation==2)
1290 rRet += "\\)";
1291 break;
1292 case 0x2:
1293 if ((nVariation==0) || (nVariation==2))
1294 rRet += " right rbrace ";
1295 else
1296 rRet += " right none ";
1297 break;
1298 case 0x3:
1299 if (nVariation==0)
1300 rRet += " right ]";
1301 else if (nVariation==2)
1302 rRet += "\\]";
1303 break;
1304 case 0x4:
1305 if (nVariation==0)
1306 rRet += " rline ";
1307 else if (nVariation==2)
1308 rRet += " \\rline ";
1309 break;
1310 case 0x5:
1311 if (nVariation==0)
1312 rRet += " rdline ";
1313 else if (nVariation==2)
1314 rRet += " \\rdline ";
1315 break;
1316 case 0x6:
1317 if (nVariation == 0 || nVariation == 2)
1318 rRet += " right rfloor ";
1319 else if (nVariation==2)
1320 rRet += " right none ";
1321 break;
1322 case 0x7:
1323 if (nVariation==0)
1324 rRet += " rceil ";
1325 else if (nVariation==2)
1326 rRet += " \\rceil ";
1327 break;
1328 case 0x8:
1329 case 0xa:
1330 rRet += "\\[";
1331 break;
1332 case 0x9:
1333 case 0xc:
1334 rRet += "\\]";
1335 break;
1336 case 0xd:
1337 rRet += "} ";
1338 if (nVariation == 1)
1340 if (nPart == 0)
1342 newline--;
1343 sMainTerm = rRet;
1344 rRet.clear();
1346 else
1348 sPush += rRet;
1349 rRet = sPush;
1350 rRet += sMainTerm;
1353 else
1355 if (nPart == 0)
1356 newline--;
1358 nPart++;
1359 break;
1360 case 0xb:
1361 rRet += "\\)";
1362 break;
1363 case 0xe:
1364 rRet += "} ";
1365 if (nPart == 0)
1366 newline--;
1367 else
1368 rRet += "} ";
1369 nPart++;
1370 break;
1371 case 0xf:
1373 if ((nPart == 0) &&
1374 ((nVariation == 2) || (nVariation == 1)))
1375 newline--;
1377 bool bOk=false;
1378 sal_Int32 nI = rRet.lastIndexOf('{');
1379 if (nI != -1)
1381 for(nI=nI+1;nI<rRet.getLength();nI++)
1382 if (rRet[nI] != ' ')
1384 bOk=true;
1385 break;
1388 else
1389 bOk=true;
1391 if (bOk)
1392 rRet += "} ";
1393 else
1394 rRet = rRet.replaceAt(nSubSupStartPos, rRet.getLength(), "");
1395 nPart++;
1397 break;
1398 case 0x2c:
1399 if ((nPart == 0) &&
1400 ((nVariation == 2) || (nVariation == 1)))
1401 newline--;
1402 rRet += "} ";
1403 nPart++;
1404 break;
1405 case 0x2e:
1406 case 0x2f:
1407 rRet += "} ";
1408 break;
1409 case 0x10:
1410 case 0x11:
1411 rRet += "}} ";
1412 break;
1413 case 0x12:
1414 case 0x13:
1415 case 0x14:
1416 if (nPart == 0)
1418 newline--;
1419 rRet += "} ";
1421 nPart++;
1422 break;
1423 case 0x1b:
1424 rRet += "} ";
1425 if (nPart == 0)
1427 newline--;
1428 rRet += "overbrace";
1430 nPart++;
1431 break;
1432 case 0x1c:
1433 rRet += "} ";
1434 if (nPart == 0)
1436 newline--;
1437 rRet += "underbrace";
1439 nPart++;
1440 break;
1441 case 0x27:
1442 if (nPart==0)
1443 newline--;
1444 else if ((nPart==1) &&
1445 ((nVariation == 2) || (nVariation == 1)))
1446 newline--;
1447 rRet += "} ";
1448 nPart++;
1449 break;
1450 case 0x28:
1451 rRet += "} ";
1452 if (nVariation == 0)
1454 if (nPart == 0)
1456 sMainTerm = rRet;
1457 rRet.clear();
1459 else
1461 sPush += rRet;
1462 rRet = sPush;
1463 rRet += " over ";
1464 rRet += sMainTerm;
1467 if (nPart == 0)
1468 newline--;
1469 nPart++;
1470 break;
1471 case 0x29:
1472 rRet += "} ";
1473 if (nPart == 0)
1475 newline--;
1476 switch (nVariation)
1478 case 1:
1479 rRet += "slash";
1480 break;
1481 default:
1482 rRet += "wideslash";
1483 break;
1486 nPart++;
1487 break;
1488 case 0x1d:
1489 case 0x1e:
1490 case 0x1f:
1491 case 0x20:
1492 case 0x21:
1493 case 0x22:
1494 case 0x23:
1495 case 0x24:
1496 case 0x25:
1497 case 0x26:
1498 rRet += "} ";
1499 if (nPart == 0)
1501 if (nVariation != 2)
1503 sMainTerm = rRet;
1504 rRet.clear();
1506 newline--;
1508 else if ((nPart == 1) && (nVariation == 0))
1510 sPush += rRet;
1511 rRet = sPush;
1512 rRet += sMainTerm;
1513 newline--;
1515 else if ((nPart == 1) && (nVariation == 1))
1516 newline--;
1517 else if ((nPart == 2) && (nVariation == 1))
1519 sPush += rRet;
1520 rRet = sPush;
1521 rRet += sMainTerm;
1522 newline--;
1524 nPart++;
1525 break;
1526 case 0x15:
1527 rRet += "} ";
1528 if (nPart == 0)
1530 if ((nVariation != 0) && (nVariation != 3))
1532 sMainTerm = rRet;
1533 rRet.clear();
1535 newline--;
1537 else if ((nPart == 1) &&
1538 ((nVariation == 1) || (nVariation==4)))
1540 sPush += rRet;
1541 rRet = sPush;
1542 rRet += sMainTerm;
1543 newline--;
1545 else if ((nPart == 1) && (nVariation == 2))
1546 newline--;
1547 else if ((nPart == 2) && (nVariation == 2))
1549 sPush += rRet;
1550 rRet = sPush;
1551 rRet += sMainTerm;
1552 newline--;
1554 nPart++;
1555 break;
1556 case 0x16:
1557 case 0x17:
1558 rRet += "} ";
1559 if (nPart == 0)
1561 if ((nVariation != 0) && (nVariation != 2))
1563 sMainTerm = rRet;
1564 rRet.clear();
1566 newline--;
1568 else if ((nPart == 1) &&
1569 ((nVariation == 1) || (nVariation==3)))
1571 sPush += rRet;
1572 rRet = sPush;
1573 rRet += sMainTerm;
1574 newline--;
1576 nPart++;
1577 break;
1578 case 0x18:
1579 rRet += "} ";
1580 if (nPart == 0)
1582 sMainTerm = rRet;
1583 rRet.clear();
1584 newline--;
1586 else if ((nPart == 1) &&
1587 ((nVariation == 1) || (nVariation==2)))
1589 sPush += rRet;
1590 rRet = sPush;
1591 rRet += sMainTerm;
1592 newline--;
1594 else if ((nPart == 1) && (nVariation == 0))
1595 newline--;
1596 else if ((nPart == 2) && (nVariation == 0))
1598 sPush += rRet;
1599 rRet = sPush;
1600 rRet += sMainTerm;
1601 newline--;
1603 nPart++;
1604 break;
1605 case 0x19:
1606 case 0x1a:
1607 rRet += "} ";
1608 if (nPart == 0)
1610 sMainTerm = rRet;
1611 rRet.clear();
1612 newline--;
1614 else if (nPart == 1)
1616 sPush += rRet;
1617 rRet = sPush;
1618 rRet += sMainTerm;
1619 newline--;
1621 nPart++;
1622 break;
1623 case 0x2a:
1624 case 0x2b:
1625 rRet += "} ";
1627 if ((nPart == 0) &&
1628 ((nVariation == 0) || (nVariation == 1)))
1630 sMainTerm = rRet;
1631 rRet.clear();
1632 newline--;
1634 else if ((nPart == 0) && (nVariation == 2))
1635 newline--;
1636 else if ((nPart == 1) && (nVariation == 2))
1638 sMainTerm = rRet;
1639 rRet.clear();
1640 newline--;
1642 else if ((nPart == 2) || ((((nPart == 1) &&
1643 (nVariation == 0)) || (nVariation == 1))))
1645 sPush+=rRet;
1646 rRet = sPush;
1647 rRet += sMainTerm;
1649 nPart++;
1650 break;
1651 case 0x2d:
1652 if (nVariation==0)
1654 if (nPart == 0)
1656 newline--; //there is another term to arrive
1657 rRet += " mline ";
1659 else
1660 rRet += " rangle ";
1662 else if (nVariation==1)
1663 rRet += " \\lline ";
1664 else if (nVariation==2)
1665 rRet += " \\rangle ";
1666 nPart++;
1667 break;
1668 default:
1669 break;
1671 bSilent = true; //Skip the optional brackets and/or
1672 //symbols that follow some of these
1673 //records. Foo Data.
1675 /*In matrices and piles we cannot separate equation
1676 *lines with the newline keyword*/
1677 if (nMatrixCols==0)
1678 newline++;
1681 break;
1682 case CHAR:
1683 if (xfLMOVE(nTag))
1684 HandleNudge();
1685 bRet = HandleChar( nTextStart, nSetSize, nLevel, nTag, nSelector, nVariation, bSilent );
1686 break;
1687 case TMPL:
1688 if (xfLMOVE(nTag))
1689 HandleNudge();
1690 bRet = HandleTemplate( nLevel, nSelector, nVariation, nLastTemplateBracket );
1691 break;
1692 case PILE:
1693 if (xfLMOVE(nTag))
1694 HandleNudge();
1695 bRet = HandlePile( nSetAlign, nLevel, nSelector, nVariation );
1696 HandleMatrixSeparator( nMatrixRows, nMatrixCols, nCurCol, nCurRow );
1697 break;
1698 case MATRIX:
1699 if (xfLMOVE(nTag))
1700 HandleNudge();
1701 bRet = HandleMatrix( nLevel, nSelector, nVariation );
1702 HandleMatrixSeparator( nMatrixRows, nMatrixCols, nCurCol, nCurRow );
1703 break;
1704 case EMBEL:
1705 if (xfLMOVE(nTag))
1706 HandleNudge();
1707 HandleEmblishments();
1708 break;
1709 case RULER:
1710 pS->ReadUChar( nTabStops );
1711 for (i=0;i<nTabStops;i++)
1713 pS->ReadUChar( nTabType );
1714 pS->ReadUInt16( nTabOffset );
1716 SAL_WARN("starmath", "Not seen in the wild Equation Ruler Field");
1717 break;
1718 case FONT:
1720 MathTypeFont aFont;
1721 pS->ReadUChar( aFont.nTface );
1723 The typeface number is the negative (which makes it
1724 positive) of the typeface value (unbiased) that appears in
1725 CHAR records that might follow a given FONT record
1727 aFont.nTface = 128-aFont.nTface;
1728 pS->ReadUChar( aFont.nStyle );
1729 aUserStyles.insert(aFont);
1730 std::vector<sal_Char> aSeq;
1731 while(true)
1733 sal_Char nChar8(0);
1734 pS->ReadChar( nChar8 );
1735 if (nChar8 == 0)
1736 break;
1737 aSeq.push_back(nChar8);
1739 // Do nothing to the font name now in aSeq!?
1741 break;
1742 case SIZE:
1743 HandleSetSize();
1744 break;
1745 case 10:
1746 case 11:
1747 case 12:
1748 case 13:
1749 case 14:
1750 nLSize=nRecord-10;
1751 break;
1752 case END:
1753 default:
1754 break;
1757 while (nRecord != END && !pS->IsEof());
1758 while (nSetSize)
1760 rRet += "}";
1761 nSetSize--;
1763 return bRet;
1766 /*Simply determine if we are at the end of a record or the end of a line,
1767 *with fiddley logic to see if we are in a matrix or a pile or neither
1769 Note we cannot tell until after the event that this is the last entry
1770 of a pile, so we must strip the last separator of a pile after this
1771 is detected in the PILE handler
1773 void MathType::HandleMatrixSeparator(int nMatrixRows,int nMatrixCols,
1774 int &rCurCol,int &rCurRow)
1776 if (nMatrixRows!=0)
1778 if (rCurCol == nMatrixCols-1)
1780 if (rCurRow != nMatrixRows-1)
1781 rRet += " {} ##\n";
1782 if (nMatrixRows!=-1)
1784 rCurCol=0;
1785 rCurRow++;
1788 else
1790 rRet += " {} # ";
1791 if (nMatrixRows!=-1)
1792 rCurCol++;
1793 else
1794 rRet += "\n";
1799 /* set the alignment of the following term, but starmath currently
1800 * cannot handle vertical alignment */
1801 void MathType::HandleAlign(sal_uInt8 nHorAlign, sal_uInt8 /*nVAlign*/, int &rSetAlign)
1803 switch(nHorAlign)
1805 case 1:
1806 default:
1807 rRet += "alignl {";
1808 break;
1809 case 2:
1810 rRet += "alignc {";
1811 break;
1812 case 3:
1813 rRet += "alignr {";
1814 break;
1816 rSetAlign++;
1819 /* set size of text, complexity due to overuse of signedness as a flag
1820 * indicator by mathtype file format*/
1821 bool MathType::HandleSize(sal_Int16 nLstSize,sal_Int16 nDefSize, int &rSetSize)
1823 const sal_Int16 nDefaultSize = 12;
1824 bool bRet=false;
1825 if (nLstSize < 0)
1827 if ((-nLstSize/32 != nDefaultSize) && (-nLstSize/32 != nCurSize))
1829 if (rSetSize)
1831 rSetSize--;
1832 rRet += "}";
1833 bRet=true;
1835 if (-nLstSize/32 != nLastSize)
1837 nLastSize = nCurSize;
1838 rRet += " size ";
1839 rRet += OUString::number(-nLstSize/32);
1840 rRet += "{";
1841 bRet=true;
1842 rSetSize++;
1844 nCurSize = -nLstSize/32;
1847 else
1849 /*sizetable should theoreticaly be filled with the default sizes
1850 *of the various font groupings matching starmaths equivalents
1851 in aTypeFaces, and a test would be done to see if the new font
1852 size would be the same as what starmath would have chosen for
1853 itself anyway in which case the size setting could be ignored*/
1854 nLstSize = aSizeTable.at(nLstSize);
1855 nLstSize = nLstSize + nDefSize;
1856 if (nLstSize != nCurSize)
1858 if (rSetSize)
1860 rSetSize--;
1861 rRet += "}";
1862 bRet=true;
1864 if (nLstSize != nLastSize)
1866 nLastSize = nCurSize;
1867 rRet += " size ";
1868 rRet += OUString::number(nLstSize);
1869 rRet += "{";
1870 bRet=true;
1871 rSetSize++;
1873 nCurSize = nLstSize;
1876 return bRet;
1879 bool MathType::ConvertFromStarMath( SfxMedium& rMedium )
1881 if (!pTree)
1882 return false;
1884 SvStream *pStream = rMedium.GetOutStream();
1885 if ( pStream )
1887 tools::SvRef<SotStorage> pStor = new SotStorage( pStream, false );
1889 SvGlobalName aGName(MSO_EQUATION3_CLASSID);
1890 pStor->SetClass( aGName, SotClipboardFormatId::NONE, "Microsoft Equation 3.0");
1892 static sal_uInt8 const aCompObj[] = {
1893 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
1894 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0xCE, 0x02, 0x00,
1895 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00,
1896 0x00, 0x00, 0x00, 0x46, 0x17, 0x00, 0x00, 0x00,
1897 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66,
1898 0x74, 0x20, 0x45, 0x71, 0x75, 0x61, 0x74, 0x69,
1899 0x6F, 0x6E, 0x20, 0x33, 0x2E, 0x30, 0x00, 0x0C,
1900 0x00, 0x00, 0x00, 0x44, 0x53, 0x20, 0x45, 0x71,
1901 0x75, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x00, 0x0B,
1902 0x00, 0x00, 0x00, 0x45, 0x71, 0x75, 0x61, 0x74,
1903 0x69, 0x6F, 0x6E, 0x2E, 0x33, 0x00, 0xF4, 0x39,
1904 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1905 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1907 tools::SvRef<SotStorageStream> xStor( pStor->OpenSotStream("\1CompObj"));
1908 xStor->WriteBytes(aCompObj, sizeof(aCompObj));
1910 static sal_uInt8 const aOle[] = {
1911 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
1912 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1913 0x00, 0x00, 0x00, 0x00
1915 tools::SvRef<SotStorageStream> xStor2( pStor->OpenSotStream("\1Ole"));
1916 xStor2->WriteBytes(aOle, sizeof(aOle));
1917 xStor.Clear();
1918 xStor2.Clear();
1920 tools::SvRef<SotStorageStream> xSrc = pStor->OpenSotStream("Equation Native");
1921 if ( (!xSrc.Is()) || (SVSTREAM_OK != xSrc->GetError()))
1922 return false;
1924 pS = xSrc.get();
1925 pS->SetEndian( SvStreamEndian::LITTLE );
1927 pS->SeekRel(EQNOLEFILEHDR_SIZE); //Skip 28byte Header and fill it in later
1928 pS->WriteUChar( 0x03 );
1929 pS->WriteUChar( 0x01 );
1930 pS->WriteUChar( 0x01 );
1931 pS->WriteUChar( 0x03 );
1932 pS->WriteUChar( 0x00 );
1933 sal_uInt32 nSize = pS->Tell();
1934 nPendingAttributes=0;
1936 HandleNodes(pTree, 0);
1937 pS->WriteUChar( END );
1939 nSize = pS->Tell()-nSize;
1940 pS->Seek(0);
1941 EQNOLEFILEHDR aHdr(nSize+4+1);
1942 aHdr.Write(pS);
1944 pStor->Commit();
1947 return true;
1951 void MathType::HandleNodes(SmNode *pNode,int nLevel)
1953 switch(pNode->GetType())
1955 case NATTRIBUT:
1956 HandleAttributes(pNode,nLevel);
1957 break;
1958 case NTEXT:
1959 HandleText(pNode,nLevel);
1960 break;
1961 case NVERTICAL_BRACE:
1962 HandleVerticalBrace(pNode,nLevel);
1963 break;
1964 case NBRACE:
1965 HandleBrace(pNode,nLevel);
1966 break;
1967 case NOPER:
1968 HandleOperator(pNode,nLevel);
1969 break;
1970 case NBINVER:
1971 HandleFractions(pNode,nLevel);
1972 break;
1973 case NROOT:
1974 HandleRoot(pNode,nLevel);
1975 break;
1976 case NSPECIAL:
1978 SmTextNode *pText = static_cast<SmTextNode *>(pNode);
1979 //if the token str and the result text are the same then this
1980 //is to be seen as text, else assume it's a mathchar
1981 if (pText->GetText() == pText->GetToken().aText)
1982 HandleText(pText,nLevel);
1983 else
1984 HandleMath(pText,nLevel);
1986 break;
1987 case NMATH:
1988 case NMATHIDENT:
1989 HandleMath(pNode,nLevel);
1990 break;
1991 case NSUBSUP:
1992 HandleSubSupScript(pNode,nLevel);
1993 break;
1994 case NEXPRESSION:
1996 sal_uInt16 nSize = pNode->GetNumSubNodes();
1997 for (sal_uInt16 i = 0; i < nSize; i++)
1998 if (SmNode *pTemp = pNode->GetSubNode(i))
1999 HandleNodes(pTemp,nLevel+1);
2001 break;
2002 case NTABLE:
2003 //Root Node, PILE equivalent, i.e. vertical stack
2004 HandleTable(pNode,nLevel);
2005 break;
2006 case NMATRIX:
2007 HandleSmMatrix(static_cast<SmMatrixNode *>(pNode),nLevel);
2008 break;
2009 case NLINE:
2011 pS->WriteUChar( 0x0a );
2012 pS->WriteUChar( LINE );
2013 sal_uInt16 nSize = pNode->GetNumSubNodes();
2014 for (sal_uInt16 i = 0; i < nSize; i++)
2015 if (SmNode *pTemp = pNode->GetSubNode(i))
2016 HandleNodes(pTemp,nLevel+1);
2017 pS->WriteUChar( END );
2019 break;
2020 case NALIGN:
2021 HandleMAlign(pNode,nLevel);
2022 break;
2023 case NBLANK:
2024 pS->WriteUChar( CHAR );
2025 pS->WriteUChar( 0x98 );
2026 if (pNode->GetToken().eType == TSBLANK)
2027 pS->WriteUInt16( 0xEB04 );
2028 else
2029 pS->WriteUInt16( 0xEB05 );
2030 break;
2031 default:
2033 sal_uInt16 nSize = pNode->GetNumSubNodes();
2034 for (sal_uInt16 i = 0; i < nSize; i++)
2035 if (SmNode *pTemp = pNode->GetSubNode(i))
2036 HandleNodes(pTemp,nLevel+1);
2038 break;
2043 int MathType::StartTemplate(sal_uInt16 nSelector,sal_uInt16 nVariation)
2045 int nOldPending=nPendingAttributes;
2046 pS->WriteUChar( TMPL ); //Template
2047 pS->WriteUChar( nSelector ); //selector
2048 pS->WriteUChar( nVariation ); //variation
2049 pS->WriteUChar( 0x00 ); //options
2050 pS->WriteUChar( LINE );
2051 //theres just no way we can now handle any character
2052 //attributes (from mathtypes perspective) centered
2053 //over an expression but above template attribute
2054 //such as widevec and similar constructs
2055 //we have to drop them
2056 nPendingAttributes=0;
2057 return nOldPending;
2060 void MathType::EndTemplate(int nOldPendingAttributes)
2062 pS->WriteUChar( END ); //end line
2063 pS->WriteUChar( END ); //end template
2064 nPendingAttributes=nOldPendingAttributes;
2068 void MathType::HandleSmMatrix(SmMatrixNode *pMatrix,int nLevel)
2070 pS->WriteUChar( MATRIX );
2071 pS->WriteUChar( 0x00 ); //vAlign ?
2072 pS->WriteUChar( 0x00 ); //h_just
2073 pS->WriteUChar( 0x00 ); //v_just
2074 pS->WriteUChar( pMatrix->GetNumRows() ); //v_just
2075 pS->WriteUChar( pMatrix->GetNumCols() ); //v_just
2076 int nBytes=(pMatrix->GetNumRows()+1)*2/8;
2077 if (((pMatrix->GetNumRows()+1)*2)%8)
2078 nBytes++;
2079 for (int j = 0; j < nBytes; j++)
2080 pS->WriteUChar( 0x00 ); //row_parts
2081 nBytes=(pMatrix->GetNumCols()+1)*2/8;
2082 if (((pMatrix->GetNumCols()+1)*2)%8)
2083 nBytes++;
2084 for (int k = 0; k < nBytes; k++)
2085 pS->WriteUChar( 0x00 ); //col_parts
2086 sal_uInt16 nSize = pMatrix->GetNumSubNodes();
2087 for (sal_uInt16 i = 0; i < nSize; i++)
2088 if (SmNode *pTemp = pMatrix->GetSubNode(i))
2090 pS->WriteUChar( LINE ); //line
2091 HandleNodes(pTemp,nLevel+1);
2092 pS->WriteUChar( END ); //end line
2094 pS->WriteUChar( END );
2098 //Root Node, PILE equivalent, i.e. vertical stack
2099 void MathType::HandleTable(SmNode *pNode,int nLevel)
2101 sal_uInt16 nSize = pNode->GetNumSubNodes();
2102 //The root of the starmath is a table, if
2103 //we convert this them each iteration of
2104 //conversion from starmath to mathtype will
2105 //add an extra unnecessary level to the
2106 //mathtype output stack which would grow
2107 //without bound in a multi step conversion
2109 if (nLevel == 0)
2110 pS->WriteUChar( 0x0A ); //initial size
2112 if ( nLevel || (nSize >1))
2114 pS->WriteUChar( PILE );
2115 pS->WriteUChar( nHAlign ); //vAlign ?
2116 pS->WriteUChar( 0x01 ); //hAlign
2119 for (sal_uInt16 i = 0; i < nSize; i++)
2120 if (SmNode *pTemp = pNode->GetSubNode(i))
2122 pS->WriteUChar( LINE );
2123 HandleNodes(pTemp,nLevel+1);
2124 pS->WriteUChar( END );
2126 if (nLevel || (nSize>1))
2127 pS->WriteUChar( END );
2131 void MathType::HandleRoot(SmNode *pNode,int nLevel)
2133 SmNode *pTemp;
2134 pS->WriteUChar( TMPL ); //Template
2135 pS->WriteUChar( 0x0D ); //selector
2136 if (pNode->GetSubNode(0))
2137 pS->WriteUChar( 0x01 ); //variation
2138 else
2139 pS->WriteUChar( 0x00 ); //variation
2140 pS->WriteUChar( 0x00 ); //options
2142 if (nullptr != (pTemp = pNode->GetSubNode(2)))
2144 pS->WriteUChar( LINE ); //line
2145 HandleNodes(pTemp,nLevel+1);
2146 pS->WriteUChar( END );
2149 if (nullptr != (pTemp = pNode->GetSubNode(0)))
2151 pS->WriteUChar( LINE ); //line
2152 HandleNodes(pTemp,nLevel+1);
2153 pS->WriteUChar( END );
2155 else
2156 pS->WriteUChar( LINE|0x10 ); //dummy line
2159 pS->WriteUChar( END );
2162 sal_uInt8 MathType::HandleCScript(SmNode *pNode,SmNode *pContent,int nLevel,
2163 sal_uLong *pPos,bool bTest)
2165 sal_uInt8 nVariation2=0xff;
2167 if (bTest && pNode->GetSubNode(CSUP+1))
2169 nVariation2=0;
2170 if (pNode->GetSubNode(CSUB+1))
2171 nVariation2=2;
2173 else if (pNode->GetSubNode(CSUB+1))
2174 nVariation2=1;
2176 if (nVariation2!=0xff)
2178 if (pPos)
2179 *pPos = pS->Tell();
2180 pS->WriteUChar( TMPL ); //Template
2181 pS->WriteUChar( 0x2B ); //selector
2182 pS->WriteUChar( nVariation2 );
2183 pS->WriteUChar( 0x00 ); //options
2185 if (pContent)
2187 pS->WriteUChar( LINE ); //line
2188 HandleNodes(pContent,nLevel+1);
2189 pS->WriteUChar( END ); //line
2191 else
2192 pS->WriteUChar( LINE|0x10 );
2194 pS->WriteUChar( 0x0B );
2196 SmNode *pTemp;
2197 if (nullptr != (pTemp = pNode->GetSubNode(CSUB+1)))
2199 pS->WriteUChar( LINE ); //line
2200 HandleNodes(pTemp,nLevel+1);
2201 pS->WriteUChar( END ); //line
2203 else
2204 pS->WriteUChar( LINE|0x10 );
2205 if (bTest && nullptr != (pTemp = pNode->GetSubNode(CSUP+1)))
2207 pS->WriteUChar( LINE ); //line
2208 HandleNodes(pTemp,nLevel+1);
2209 pS->WriteUChar( END ); //line
2211 else
2212 pS->WriteUChar( LINE|0x10 );
2214 return nVariation2;
2219 Sub and Sup scripts and another problem area, StarMath
2220 can have all possible options used at the same time, whereas
2221 Mathtype cannot. The ordering of the nodes for each system
2222 is quite different as well leading to some complexity
2224 void MathType::HandleSubSupScript(SmNode *pNode,int nLevel)
2226 sal_uInt8 nVariation=0xff;
2227 if (pNode->GetSubNode(LSUP+1))
2229 nVariation=0;
2230 if (pNode->GetSubNode(LSUB+1))
2231 nVariation=2;
2233 else if ( nullptr != pNode->GetSubNode(LSUB+1) )
2234 nVariation=1;
2236 SmNode *pTemp;
2237 if (nVariation!=0xff)
2239 pS->WriteUChar( TMPL ); //Template
2240 pS->WriteUChar( 0x2c ); //selector
2241 pS->WriteUChar( nVariation );
2242 pS->WriteUChar( 0x00 ); //options
2243 pS->WriteUChar( 0x0B );
2245 if (nullptr != (pTemp = pNode->GetSubNode(LSUB+1)))
2247 pS->WriteUChar( LINE ); //line
2248 HandleNodes(pTemp,nLevel+1);
2249 pS->WriteUChar( END ); //line
2251 else
2252 pS->WriteUChar( LINE|0x10 );
2253 if (nullptr != (pTemp = pNode->GetSubNode(LSUP+1)))
2255 pS->WriteUChar( LINE ); //line
2256 HandleNodes(pTemp,nLevel+1);
2257 pS->WriteUChar( END ); //line
2259 else
2260 pS->WriteUChar( LINE|0x10 );
2261 pS->WriteUChar( END );
2262 nVariation=0xff;
2266 sal_uInt8 nVariation2=HandleCScript(pNode,nullptr,nLevel);
2268 if (nullptr != (pTemp = pNode->GetSubNode(0)))
2270 HandleNodes(pTemp,nLevel+1);
2273 if (nVariation2 != 0xff)
2274 pS->WriteUChar( END );
2276 if (nullptr != (pNode->GetSubNode(RSUP+1)))
2278 nVariation=0;
2279 if (pNode->GetSubNode(RSUB+1))
2280 nVariation=2;
2282 else if (nullptr != pNode->GetSubNode(RSUB+1))
2283 nVariation=1;
2285 if (nVariation!=0xff)
2287 pS->WriteUChar( TMPL ); //Template
2288 pS->WriteUChar( 0x0F ); //selector
2289 pS->WriteUChar( nVariation );
2290 pS->WriteUChar( 0x00 ); //options
2291 pS->WriteUChar( 0x0B );
2293 if (nullptr != (pTemp = pNode->GetSubNode(RSUB+1)))
2295 pS->WriteUChar( LINE ); //line
2296 HandleNodes(pTemp,nLevel+1);
2297 pS->WriteUChar( END ); //line
2299 else
2300 pS->WriteUChar( LINE|0x10 );
2301 if (nullptr != (pTemp = pNode->GetSubNode(RSUP+1)))
2303 pS->WriteUChar( LINE ); //line
2304 HandleNodes(pTemp,nLevel+1);
2305 pS->WriteUChar( END ); //line
2307 else
2308 pS->WriteUChar( LINE|0x10 );
2309 pS->WriteUChar( END ); //line
2312 //After subscript mathtype will keep the size of
2313 //normal text at the subscript size, sigh.
2314 pS->WriteUChar( 0x0A );
2318 void MathType::HandleFractions(SmNode *pNode,int nLevel)
2320 SmNode *pTemp;
2321 pS->WriteUChar( TMPL ); //Template
2322 pS->WriteUChar( 0x0E ); //selector
2323 pS->WriteUChar( 0x00 ); //variation
2324 pS->WriteUChar( 0x00 ); //options
2326 pS->WriteUChar( 0x0A );
2327 pS->WriteUChar( LINE ); //line
2328 if (nullptr != (pTemp = pNode->GetSubNode(0)))
2329 HandleNodes(pTemp,nLevel+1);
2330 pS->WriteUChar( END );
2332 pS->WriteUChar( 0x0A );
2333 pS->WriteUChar( LINE ); //line
2334 if (nullptr != (pTemp = pNode->GetSubNode(2)))
2335 HandleNodes(pTemp,nLevel+1);
2336 pS->WriteUChar( END );
2338 pS->WriteUChar( END );
2342 void MathType::HandleBrace(SmNode *pNode,int nLevel)
2344 SmNode *pTemp;
2345 SmNode *pLeft=pNode->GetSubNode(0);
2346 SmNode *pRight=pNode->GetSubNode(2);
2348 pS->WriteUChar( TMPL ); //Template
2349 bIsReInterpBrace=false;
2350 sal_uInt8 nBSpec=0x10;
2351 sal_uLong nLoc = pS->Tell();
2352 if (pLeft)
2354 switch (pLeft->GetToken().eType)
2356 case TLANGLE:
2357 pS->WriteUChar( tmANGLE ); //selector
2358 pS->WriteUChar( 0 ); //variation
2359 pS->WriteUChar( 0 ); //options
2360 break;
2361 case TLBRACE:
2362 pS->WriteUChar( tmBRACE ); //selector
2363 pS->WriteUChar( 0 ); //variation
2364 pS->WriteUChar( 0 ); //options
2365 nBSpec+=3;
2366 break;
2367 case TLBRACKET:
2368 pS->WriteUChar( tmBRACK ); //selector
2369 pS->WriteUChar( 0 ); //variation
2370 pS->WriteUChar( 0 ); //options
2371 nBSpec+=3;
2372 break;
2373 case TLFLOOR:
2374 pS->WriteUChar( tmFLOOR ); //selector
2375 pS->WriteUChar( 0 ); //variation
2376 pS->WriteUChar( 0 ); //options
2377 break;
2378 case TLLINE:
2379 pS->WriteUChar( tmBAR ); //selector
2380 pS->WriteUChar( 0 ); //variation
2381 pS->WriteUChar( 0 ); //options
2382 nBSpec+=3;
2383 break;
2384 case TLDLINE:
2385 pS->WriteUChar( tmDBAR ); //selector
2386 pS->WriteUChar( 0 ); //variation
2387 pS->WriteUChar( 0 ); //options
2388 break;
2389 default:
2390 pS->WriteUChar( tmPAREN ); //selector
2391 pS->WriteUChar( 0 ); //variation
2392 pS->WriteUChar( 0 ); //options
2393 nBSpec+=3;
2394 break;
2398 if (nullptr != (pTemp = pNode->GetSubNode(1)))
2400 pS->WriteUChar( LINE ); //line
2401 HandleNodes(pTemp,nLevel+1);
2402 pS->WriteUChar( END ); //options
2404 nSpec=nBSpec;
2405 if (pLeft)
2406 HandleNodes(pLeft,nLevel+1);
2407 if (bIsReInterpBrace)
2409 sal_uLong nLoc2 = pS->Tell();
2410 pS->Seek(nLoc);
2411 pS->WriteUChar( 0x2D );
2412 pS->Seek(nLoc2);
2413 pS->WriteUChar( CHAR );
2414 pS->WriteUChar( 0x96 );
2415 pS->WriteUInt16( 0xEC07 );
2416 bIsReInterpBrace=false;
2418 if (pRight)
2419 HandleNodes(pRight,nLevel+1);
2420 nSpec=0x0;
2421 pS->WriteUChar( END );
2425 void MathType::HandleVerticalBrace(SmNode *pNode,int nLevel)
2427 SmNode *pTemp;
2428 pS->WriteUChar( TMPL ); //Template
2429 if (pNode->GetToken().eType == TUNDERBRACE)
2430 pS->WriteUChar( tmLHBRACE ); //selector
2431 else
2432 pS->WriteUChar( tmUHBRACE ); //selector
2433 pS->WriteUChar( 0 ); //variation
2434 pS->WriteUChar( 0 ); //options
2436 if (nullptr != (pTemp = pNode->GetSubNode(0)))
2438 pS->WriteUChar( LINE ); //line
2439 HandleNodes(pTemp,nLevel+1);
2440 pS->WriteUChar( END ); //options
2443 if (nullptr != (pTemp = pNode->GetSubNode(2)))
2445 pS->WriteUChar( LINE ); //line
2446 HandleNodes(pTemp,nLevel+1);
2447 pS->WriteUChar( END ); //options
2449 pS->WriteUChar( END );
2452 void MathType::HandleOperator(SmNode *pNode,int nLevel)
2454 if (HandleLim(pNode,nLevel))
2455 return;
2457 sal_uLong nPos;
2458 sal_uInt8 nVariation;
2460 switch (pNode->GetToken().eType)
2462 case TIINT:
2463 case TIIINT:
2464 case TLINT:
2465 case TLLINT:
2466 case TLLLINT:
2467 nVariation=HandleCScript(pNode->GetSubNode(0),
2468 pNode->GetSubNode(1),nLevel,&nPos,false);
2469 break;
2470 default:
2471 nVariation=HandleCScript(pNode->GetSubNode(0),
2472 pNode->GetSubNode(1),nLevel,&nPos);
2473 break;
2476 sal_uInt8 nOldVariation=nVariation;
2477 sal_uInt8 nIntVariation=nVariation;
2479 sal_uLong nPos2=0;
2480 if (nVariation != 0xff)
2482 nPos2 = pS->Tell();
2483 pS->Seek(nPos);
2484 if (nVariation == 2)
2486 nIntVariation=0;
2487 nVariation = 1;
2489 else if (nVariation == 0)
2490 nVariation = 1;
2491 else if (nVariation == 1)
2492 nVariation = 0;
2494 else
2496 nVariation = 2;
2497 nIntVariation=0;
2499 pS->WriteUChar( TMPL );
2500 switch(pNode->GetToken().eType)
2502 case TINT:
2503 case TINTD:
2504 if (nOldVariation != 0xff)
2505 pS->WriteUChar( 0x18 ); //selector
2506 else
2507 pS->WriteUChar( 0x15 ); //selector
2508 pS->WriteUChar( nIntVariation ); //variation
2509 break;
2510 case TIINT:
2511 if (nOldVariation != 0xff)
2513 pS->WriteUChar( 0x19 );
2514 pS->WriteUChar( 0x01 );
2516 else
2518 pS->WriteUChar( 0x16 );
2519 pS->WriteUChar( 0x00 );
2521 break;
2522 case TIIINT:
2523 if (nOldVariation != 0xff)
2525 pS->WriteUChar( 0x1a );
2526 pS->WriteUChar( 0x01 );
2528 else
2530 pS->WriteUChar( 0x17 );
2531 pS->WriteUChar( 0x00 );
2533 break;
2534 case TLINT:
2535 if (nOldVariation != 0xff)
2537 pS->WriteUChar( 0x18 );
2538 pS->WriteUChar( 0x02 );
2540 else
2542 pS->WriteUChar( 0x15 );
2543 pS->WriteUChar( 0x03 );
2545 break;
2546 case TLLINT:
2547 if (nOldVariation != 0xff)
2549 pS->WriteUChar( 0x19 );
2550 pS->WriteUChar( 0x00 );
2552 else
2554 pS->WriteUChar( 0x16 );
2555 pS->WriteUChar( 0x02 );
2557 break;
2558 case TLLLINT:
2559 if (nOldVariation != 0xff)
2561 pS->WriteUChar( 0x1a );
2562 pS->WriteUChar( 0x00 );
2564 else
2566 pS->WriteUChar( 0x17 );
2567 pS->WriteUChar( 0x02 );
2569 break;
2570 case TSUM:
2571 default:
2572 pS->WriteUChar( 0x1d );
2573 pS->WriteUChar( nVariation );
2574 break;
2575 case TPROD:
2576 pS->WriteUChar( 0x1f );
2577 pS->WriteUChar( nVariation );
2578 break;
2579 case TCOPROD:
2580 pS->WriteUChar( 0x21 );
2581 pS->WriteUChar( nVariation );
2582 break;
2584 pS->WriteUChar( 0 ); //options
2586 if (nPos2)
2587 pS->Seek(nPos2);
2588 else
2590 pS->WriteUChar( LINE ); //line
2591 HandleNodes(pNode->GetSubNode(1),nLevel+1);
2592 pS->WriteUChar( END ); //line
2593 pS->WriteUChar( LINE|0x10 );
2594 pS->WriteUChar( LINE|0x10 );
2597 pS->WriteUChar( 0x0D );
2598 switch(pNode->GetToken().eType)
2600 case TSUM:
2601 default:
2602 pS->WriteUChar( CHAR );
2603 pS->WriteUChar( 0x86 );
2604 pS->WriteUInt16( 0x2211 );
2605 break;
2606 case TPROD:
2607 pS->WriteUChar( CHAR );
2608 pS->WriteUChar( 0x86 );
2609 pS->WriteUInt16( 0x220F );
2610 break;
2611 case TCOPROD:
2612 pS->WriteUChar( CHAR );
2613 pS->WriteUChar( 0x8B );
2614 pS->WriteUInt16( 0x2210 );
2615 break;
2616 case TIIINT:
2617 case TLLLINT:
2618 pS->WriteUChar( CHAR );
2619 pS->WriteUChar( 0x86 );
2620 pS->WriteUInt16( 0x222B );
2621 SAL_FALLTHROUGH;
2622 case TIINT:
2623 case TLLINT:
2624 pS->WriteUChar( CHAR );
2625 pS->WriteUChar( 0x86 );
2626 pS->WriteUInt16( 0x222B );
2627 SAL_FALLTHROUGH;
2628 case TINT:
2629 case TINTD:
2630 case TLINT:
2631 pS->WriteUChar( CHAR );
2632 pS->WriteUChar( 0x86 );
2633 pS->WriteUInt16( 0x222B );
2634 break;
2636 pS->WriteUChar( END );
2637 pS->WriteUChar( 0x0A );
2641 bool MathType::HandlePile(int &rSetAlign, int nLevel, sal_uInt8 nSelector, sal_uInt8 nVariation)
2643 pS->ReadUChar( nHAlign );
2644 pS->ReadUChar( nVAlign );
2646 HandleAlign(nHAlign,nVAlign,rSetAlign);
2648 rRet += " stack {\n";
2649 bool bRet = HandleRecords( nLevel+1, nSelector, nVariation, -1, -1 );
2650 rRet = rRet.replaceAt(rRet.getLength()-3,2,"");
2651 rRet += "} ";
2653 while (rSetAlign)
2655 rRet += "} ";
2656 rSetAlign--;
2658 return bRet;
2661 bool MathType::HandleMatrix(int nLevel, sal_uInt8 nSelector, sal_uInt8 nVariation)
2663 sal_uInt8 nH_just,nV_just,nRows,nCols;
2664 pS->ReadUChar( nVAlign );
2665 pS->ReadUChar( nH_just );
2666 pS->ReadUChar( nV_just );
2667 pS->ReadUChar( nRows );
2668 pS->ReadUChar( nCols );
2669 int nBytes = ((nRows+1)*2)/8;
2670 if (((nRows+1)*2)%8)
2671 nBytes++;
2672 pS->SeekRel(nBytes);
2673 nBytes = ((nCols+1)*2)/8;
2674 if (((nCols+1)*2)%8)
2675 nBytes++;
2676 pS->SeekRel(nBytes);
2677 rRet += " matrix {\n";
2678 bool bRet = HandleRecords( nLevel+1, nSelector, nVariation, nRows, nCols );
2680 sal_Int32 nI = rRet.lastIndexOf('#');
2681 if (nI > 0)
2682 if (rRet[nI-1] != '#') //missing column
2683 rRet += "{}";
2685 rRet += "\n} ";
2686 return bRet;
2689 bool MathType::HandleTemplate(int nLevel, sal_uInt8 &rSelector,
2690 sal_uInt8 &rVariation, sal_Int32 &rLastTemplateBracket)
2692 sal_uInt8 nOption; //This appears utterly unused
2693 pS->ReadUChar( rSelector );
2694 pS->ReadUChar( rVariation );
2695 pS->ReadUChar( nOption );
2696 OSL_ENSURE(rSelector < 48,"Selector out of range");
2697 if ((rSelector >= 21) && (rSelector <=26))
2699 OSL_ENSURE(nOption < 2,"Option out of range");
2701 else if (/*(rSelector >= 0) &&*/ (rSelector <=12))
2703 OSL_ENSURE(nOption < 3,"Option out of range");
2706 //For the (broken) case where one subscript template ends, and there is
2707 //another one after it, mathtype handles it as if the second one was
2708 //inside the first one and renders it as sub of sub
2709 bool bRemove=false;
2710 if ( (rSelector == 0xf) && (rLastTemplateBracket != -1) )
2712 bRemove=true;
2713 for (sal_Int32 nI = rLastTemplateBracket+1; nI < rRet.getLength(); nI++ )
2714 if (rRet[nI] != ' ')
2716 bRemove=false;
2717 break;
2721 //suborderlist
2722 bool bRet = HandleRecords( nLevel+1, rSelector, rVariation );
2724 if (bRemove)
2726 rRet = rRet.replaceAt(rLastTemplateBracket,1,"");
2727 rRet += "} ";
2728 rLastTemplateBracket = -1;
2730 if (rSelector == 0xf)
2731 rLastTemplateBracket = rRet.lastIndexOf('}');
2732 else
2733 rLastTemplateBracket = -1;
2735 rSelector = sal::static_int_cast< sal_uInt8 >(-1);
2736 return bRet;
2739 void MathType::HandleEmblishments()
2741 sal_uInt8 nEmbel;
2744 pS->ReadUChar( nEmbel );
2745 switch (nEmbel)
2747 case 0x02:
2748 rRet += " dot ";
2749 break;
2750 case 0x03:
2751 rRet += " ddot ";
2752 break;
2753 case 0x04:
2754 rRet += " dddot ";
2755 break;
2756 case 0x05:
2757 if (!nPostSup)
2759 sPost += " sup {}";
2760 nPostSup = sPost.getLength();
2762 sPost = sPost.replaceAt(nPostSup-1,0," ' ");
2763 nPostSup += 3;
2764 break;
2765 case 0x06:
2766 if (!nPostSup)
2768 sPost += " sup {}";
2769 nPostSup = sPost.getLength();
2771 sPost = sPost.replaceAt(nPostSup-1,0," '' ");
2772 nPostSup += 4;
2773 break;
2774 case 0x07:
2775 if (!nPostlSup)
2777 sPost += " lsup {}";
2778 nPostlSup = sPost.getLength();
2780 sPost = sPost.replaceAt(nPostlSup-1,0," ' ");
2781 nPostlSup += 3;
2782 break;
2783 case 0x08:
2784 rRet += " tilde ";
2785 break;
2786 case 0x09:
2787 rRet += " hat ";
2788 break;
2789 case 0x0b:
2790 rRet += " vec ";
2791 break;
2792 case 0x10:
2793 rRet += " overstrike ";
2794 break;
2795 case 0x11:
2796 rRet += " bar ";
2797 break;
2798 case 0x12:
2799 if (!nPostSup)
2801 sPost += " sup {}";
2802 nPostSup = sPost.getLength();
2804 sPost = sPost.replaceAt(nPostSup-1,0," ''' ");
2805 nPostSup += 5;
2806 break;
2807 case 0x14:
2808 rRet += " breve ";
2809 break;
2810 default:
2811 OSL_ENSURE(nEmbel < 21,"Embel out of range");
2812 break;
2814 if (nVersion < 3)
2815 break;
2816 }while (nEmbel);
2819 void MathType::HandleSetSize()
2821 sal_uInt8 nTemp;
2822 pS->ReadUChar( nTemp );
2823 switch (nTemp)
2825 case 101:
2826 pS->ReadInt16( nLSize );
2827 nLSize = -nLSize;
2828 break;
2829 case 100:
2830 pS->ReadUChar( nTemp );
2831 nLSize = nTemp;
2832 pS->ReadInt16( nDSize );
2833 break;
2834 default:
2835 nLSize = nTemp;
2836 pS->ReadUChar( nTemp );
2837 nDSize = nTemp-128;
2838 break;
2842 bool MathType::HandleChar(sal_Int32 &rTextStart, int &rSetSize, int nLevel,
2843 sal_uInt8 nTag, sal_uInt8 nSelector, sal_uInt8 nVariation, bool bSilent)
2845 sal_Unicode nChar;
2846 bool bRet = true;
2848 if (xfAUTO(nTag))
2850 //This is a candidate for function recognition, whatever
2851 //that is!
2854 sal_uInt8 nOldTypeFace = nTypeFace;
2855 pS->ReadUChar( nTypeFace );
2856 if (nVersion < 3)
2858 sal_uInt8 nChar8;
2859 pS->ReadUChar( nChar8 );
2860 nChar = nChar8;
2862 else
2863 pS->ReadUtf16( nChar );
2866 bad character, old mathtype < 3 has these
2868 if (nChar < 0x20)
2869 return bRet;
2871 if (xfEMBELL(nTag))
2873 //A bit tricky, the character emblishments for
2874 //mathtype can all be listed after eachother, in
2875 //starmath some must go before the character and some
2876 //must go after. In addition some of the emblishments
2877 //may repeated and in starmath some of these groups
2878 //must be gathered together. sPost is the portion that
2879 //follows the char and nPostSup and nPostlSup are the
2880 //indexes at which this class of emblishment is
2881 //collated together
2882 sPost.clear();
2883 nPostSup = nPostlSup = 0;
2884 int nOriglen=rRet.getLength()-rTextStart;
2885 rRet += " {"; // #i24340# make what would be "vec {A}_n" become "{vec {A}}_n"
2886 if ((!bSilent) && ((nOriglen) > 1))
2887 rRet += "\"";
2888 bRet = HandleRecords( nLevel+1, nSelector, nVariation );
2889 if (!bSilent)
2891 if (nOriglen > 1)
2893 OUString aStr;
2894 TypeFaceToString(aStr,nOldTypeFace);
2895 aStr += "\"";
2896 rRet = rRet.replaceAt(rTextStart,0,aStr);
2898 aStr.clear();
2899 TypeFaceToString(aStr,nTypeFace);
2900 rRet += aStr + "{";
2902 else
2903 rRet += " {";
2904 rTextStart = rRet.getLength();
2908 if (!bSilent)
2910 sal_Int32 nOldLen = rRet.getLength();
2911 if (
2912 HandleSize(nLSize,nDSize,rSetSize) ||
2913 (nOldTypeFace != nTypeFace)
2916 if ((nOldLen - rTextStart) > 1)
2918 rRet = rRet.replaceAt(nOldLen, 0, "\"");
2919 OUString aStr;
2920 TypeFaceToString(aStr,nOldTypeFace);
2921 aStr += "\"";
2922 rRet = rRet.replaceAt(rTextStart,0,aStr);
2924 rTextStart = rRet.getLength();
2926 nOldLen = rRet.getLength();
2927 if (!LookupChar(nChar,rRet,nVersion,nTypeFace))
2929 if (nOldLen - rTextStart > 1)
2931 rRet = rRet.replaceAt(nOldLen,0,"\"");
2932 OUString aStr;
2933 TypeFaceToString(aStr,nOldTypeFace);
2934 aStr += "\"";
2935 rRet = rRet.replaceAt(rTextStart, 0, aStr);
2937 rTextStart = rRet.getLength();
2939 lcl_PrependDummyTerm(rRet, rTextStart);
2942 if ((xfEMBELL(nTag)) && (!bSilent))
2944 rRet += "}}" + sPost; // #i24340# make what would be "vec {A}_n" become "{vec {A}}_n"
2945 rTextStart = rRet.getLength();
2947 return bRet;
2950 bool MathType::HandleLim(SmNode *pNode,int nLevel)
2952 bool bRet=false;
2953 //Special case for the "lim" option in StarMath
2954 if ((pNode->GetToken().eType == TLIM)
2955 || (pNode->GetToken().eType == TLIMSUP)
2956 || (pNode->GetToken().eType == TLIMINF)
2959 if (pNode->GetSubNode(1))
2961 sal_uInt8 nVariation2=HandleCScript(pNode->GetSubNode(0),nullptr,
2962 nLevel);
2964 pS->WriteUChar( 0x0A );
2965 pS->WriteUChar( LINE ); //line
2966 pS->WriteUChar( CHAR|0x10 );
2967 pS->WriteUChar( 0x82 );
2968 pS->WriteUInt16( 'l' );
2969 pS->WriteUChar( CHAR|0x10 );
2970 pS->WriteUChar( 0x82 );
2971 pS->WriteUInt16( 'i' );
2972 pS->WriteUChar( CHAR|0x10 );
2973 pS->WriteUChar( 0x82 );
2974 pS->WriteUInt16( 'm' );
2976 if (pNode->GetToken().eType == TLIMSUP)
2978 pS->WriteUChar( CHAR ); //some space
2979 pS->WriteUChar( 0x98 );
2980 pS->WriteUInt16( 0xEB04 );
2982 pS->WriteUChar( CHAR|0x10 );
2983 pS->WriteUChar( 0x82 );
2984 pS->WriteUInt16( 's' );
2985 pS->WriteUChar( CHAR|0x10 );
2986 pS->WriteUChar( 0x82 );
2987 pS->WriteUInt16( 'u' );
2988 pS->WriteUChar( CHAR|0x10 );
2989 pS->WriteUChar( 0x82 );
2990 pS->WriteUInt16( 'p' );
2992 else if (pNode->GetToken().eType == TLIMINF)
2994 pS->WriteUChar( CHAR ); //some space
2995 pS->WriteUChar( 0x98 );
2996 pS->WriteUInt16( 0xEB04 );
2998 pS->WriteUChar( CHAR|0x10 );
2999 pS->WriteUChar( 0x82 );
3000 pS->WriteUInt16( 'i' );
3001 pS->WriteUChar( CHAR|0x10 );
3002 pS->WriteUChar( 0x82 );
3003 pS->WriteUInt16( 'n' );
3004 pS->WriteUChar( CHAR|0x10 );
3005 pS->WriteUChar( 0x82 );
3006 pS->WriteUInt16( 'f' );
3010 pS->WriteUChar( CHAR ); //some space
3011 pS->WriteUChar( 0x98 );
3012 pS->WriteUInt16( 0xEB04 );
3014 if (nVariation2 != 0xff)
3016 pS->WriteUChar( END );
3017 pS->WriteUChar( END );
3019 HandleNodes(pNode->GetSubNode(1),nLevel+1);
3020 bRet = true;
3023 return bRet;
3026 void MathType::HandleMAlign(SmNode *pNode,int nLevel)
3028 sal_uInt8 nPushedHAlign=nHAlign;
3029 switch(pNode->GetToken().eType)
3031 case TALIGNC:
3032 nHAlign=2;
3033 break;
3034 case TALIGNR:
3035 nHAlign=3;
3036 break;
3037 default:
3038 nHAlign=1;
3039 break;
3041 sal_uInt16 nSize = pNode->GetNumSubNodes();
3042 for (sal_uInt16 i = 0; i < nSize; i++)
3043 if (SmNode *pTemp = pNode->GetSubNode(i))
3044 HandleNodes(pTemp,nLevel+1);
3045 nHAlign=nPushedHAlign;
3048 void MathType::HandleMath(SmNode *pNode, int /*nLevel*/)
3050 if (pNode->GetToken().eType == TMLINE)
3052 pS->WriteUChar( END );
3053 pS->WriteUChar( LINE );
3054 bIsReInterpBrace=true;
3055 return;
3057 SmMathSymbolNode *pTemp = static_cast<SmMathSymbolNode *>(pNode);
3058 for(sal_Int32 i=0;i<pTemp->GetText().getLength();i++)
3060 sal_Unicode nArse = SmTextNode::ConvertSymbolToUnicode(pTemp->GetText()[i]);
3061 if ((nArse == 0x2224) || (nArse == 0x2288) || (nArse == 0x2285) ||
3062 (nArse == 0x2289))
3064 pS->WriteUChar( CHAR|0x20 );
3066 else if ((nPendingAttributes) &&
3067 (i == ((pTemp->GetText().getLength()+1)/2)-1))
3069 pS->WriteUChar( 0x22 );
3071 else
3072 pS->WriteUChar( CHAR ); //char without formula recognition
3073 //The typeface seems to be MTEXTRA for unicode characters,
3074 //though how to determine when mathtype chooses one over
3075 //the other is unknown. This should do the trick
3076 //nevertheless.
3077 sal_uInt8 nBias;
3078 if ( (nArse == 0x2213) || (nArse == 0x2218) ||
3079 (nArse == 0x210F) || (
3080 (nArse >= 0x22EE) && (nArse <= 0x22FF)
3083 nBias = 0xB; //typeface
3085 else if ((nArse > 0x2000) || (nArse == 0x00D7))
3086 nBias = 0x6; //typeface
3087 else if (nArse == 0x3d1)
3088 nBias = 0x4;
3089 else if ((nArse > 0xFF) && ((nArse < 0x393) || (nArse > 0x3c9)))
3090 nBias = 0xB; //typeface
3091 else if ((nArse == 0x2F) || (nArse == 0x2225))
3092 nBias = 0x2; //typeface
3093 else
3094 nBias = 0x3; //typeface
3096 pS->WriteUChar( nSpec+nBias+128 ); //typeface
3098 if (nArse == 0x2224)
3100 pS->WriteUInt16( 0x7C );
3101 pS->WriteUChar( EMBEL );
3102 pS->WriteUChar( 0x0A );
3103 pS->WriteUChar( END ); //end embel
3104 pS->WriteUChar( END ); //end embel
3106 else if (nArse == 0x2225)
3107 pS->WriteUInt16( 0xEC09 );
3108 else if (nArse == 0xE421)
3109 pS->WriteUInt16( 0x2265 );
3110 else if (nArse == 0x230A)
3111 pS->WriteUInt16( 0xF8F0 );
3112 else if (nArse == 0x230B)
3113 pS->WriteUInt16( 0xF8FB );
3114 else if (nArse == 0xE425)
3115 pS->WriteUInt16( 0x2264 );
3116 else if (nArse == 0x226A)
3118 pS->WriteUInt16( 0x3C );
3119 pS->WriteUChar( CHAR );
3120 pS->WriteUChar( 0x98 );
3121 pS->WriteUInt16( 0xEB01 );
3122 pS->WriteUChar( CHAR );
3123 pS->WriteUChar( 0x86 );
3124 pS->WriteUInt16( 0x3c );
3126 else if (nArse == 0x2288)
3128 pS->WriteUInt16( 0x2286 );
3129 pS->WriteUChar( EMBEL );
3130 pS->WriteUChar( 0x0A );
3131 pS->WriteUChar( END ); //end embel
3132 pS->WriteUChar( END ); //end embel
3134 else if (nArse == 0x2289)
3136 pS->WriteUInt16( 0x2287 );
3137 pS->WriteUChar( EMBEL );
3138 pS->WriteUChar( 0x0A );
3139 pS->WriteUChar( END ); //end embel
3140 pS->WriteUChar( END ); //end embel
3142 else if (nArse == 0x2285)
3144 pS->WriteUInt16( 0x2283 );
3145 pS->WriteUChar( EMBEL );
3146 pS->WriteUChar( 0x0A );
3147 pS->WriteUChar( END ); //end embel
3148 pS->WriteUChar( END ); //end embel
3150 else
3151 pS->WriteUInt16( nArse );
3153 nPendingAttributes = 0;
3156 void MathType::HandleAttributes(SmNode *pNode,int nLevel)
3158 int nOldPending = 0;
3159 SmNode *pTemp = nullptr;
3160 SmTextNode *pIsText = nullptr;
3162 if (nullptr != (pTemp = pNode->GetSubNode(0)))
3164 pIsText = static_cast<SmTextNode *>(pNode->GetSubNode(1));
3166 switch (pTemp->GetToken().eType)
3168 case TWIDEVEC:
3169 //theres just no way we can now handle any character
3170 //attributes (from mathtypes perspective) centered
3171 //over an expression but above template attributes
3172 //such as widevec and similar constructs
3173 //we have to drop them
3174 nOldPending = StartTemplate(0x2f,0x01);
3175 break;
3176 case TCHECK: //Not Exportable
3177 case TACUTE: //Not Exportable
3178 case TGRAVE: //Not Exportable
3179 case TCIRCLE: //Not Exportable
3180 case TWIDETILDE: //Not Exportable
3181 case TWIDEHAT: //Not Exportable
3182 break;
3183 case TUNDERLINE:
3184 nOldPending = StartTemplate(0x10);
3185 break;
3186 case TOVERLINE: //If the next node is not text
3187 //or text with more than one char
3188 if ((pIsText->GetToken().eType != TTEXT) ||
3189 (pIsText->GetText().getLength() > 1))
3190 nOldPending = StartTemplate(0x11);
3191 break;
3192 default:
3193 nPendingAttributes++;
3194 break;
3198 if (pIsText)
3199 HandleNodes(pIsText,nLevel+1);
3201 switch (pTemp->GetToken().eType)
3203 case TWIDEVEC:
3204 case TUNDERLINE:
3205 EndTemplate(nOldPending);
3206 break;
3207 case TOVERLINE:
3208 if ((pIsText->GetToken().eType != TTEXT) ||
3209 (pIsText->GetText().getLength() > 1))
3210 EndTemplate(nOldPending);
3211 break;
3212 default:
3213 break;
3216 //if there was no suitable place to put the attribute,
3217 //then we have to just give up on it
3218 if (nPendingAttributes)
3219 nPendingAttributes--;
3220 else
3222 if ((nInsertion != 0) && nullptr != (pTemp = pNode->GetSubNode(0)))
3224 sal_uLong nPos = pS->Tell();
3225 nInsertion--;
3226 pS->Seek(nInsertion);
3227 switch(pTemp->GetToken().eType)
3229 case TACUTE: //Not Exportable
3230 case TGRAVE: //Not Exportable
3231 case TCIRCLE: //Not Exportable
3232 break;
3233 case TCDOT:
3234 pS->WriteUChar( 2 );
3235 break;
3236 case TDDOT:
3237 pS->WriteUChar( 3 );
3238 break;
3239 case TDDDOT:
3240 pS->WriteUChar( 4 );
3241 break;
3242 case TTILDE:
3243 pS->WriteUChar( 8 );
3244 break;
3245 case THAT:
3246 pS->WriteUChar( 9 );
3247 break;
3248 case TVEC:
3249 pS->WriteUChar( 11 );
3250 break;
3251 case TOVERSTRIKE:
3252 pS->WriteUChar( 16 );
3253 break;
3254 case TOVERLINE:
3255 if ((pIsText->GetToken().eType == TTEXT) &&
3256 (pIsText->GetText().getLength() == 1))
3257 pS->WriteUChar( 17 );
3258 break;
3259 case TBREVE:
3260 pS->WriteUChar( 20 );
3261 break;
3262 case TWIDEVEC:
3263 case TUNDERLINE:
3264 case TWIDETILDE:
3265 case TWIDEHAT:
3266 break;
3267 case TBAR:
3268 pS->WriteUChar( 17 );
3269 break;
3270 default:
3271 pS->WriteUChar( 2 );
3272 break;
3274 pS->Seek(nPos);
3279 void MathType::HandleText(SmNode *pNode, int /*nLevel*/)
3281 SmTextNode *pTemp = static_cast<SmTextNode *>(pNode);
3282 for(sal_Int32 i=0;i<pTemp->GetText().getLength();i++)
3284 if ((nPendingAttributes) &&
3285 (i == ((pTemp->GetText().getLength()+1)/2)-1))
3287 pS->WriteUChar( 0x22 ); //char, with attributes right
3288 //after the character
3290 else
3291 pS->WriteUChar( CHAR );
3293 sal_uInt8 nFace = 0x1;
3294 if (pNode->GetFont().GetItalic() == ITALIC_NORMAL)
3295 nFace = 0x3;
3296 else if (pNode->GetFont().GetWeight() == WEIGHT_BOLD)
3297 nFace = 0x7;
3298 pS->WriteUChar( nFace+128 ); //typeface
3299 sal_uInt16 nChar = pTemp->GetText()[i];
3300 pS->WriteUInt16( SmTextNode::ConvertSymbolToUnicode(nChar) );
3302 //Mathtype can only have these sort of character
3303 //attributes on a single character, starmath can put them
3304 //anywhere, when the entity involved is a text run this is
3305 //a large effort to place the character attribute on the
3306 //central mathtype character so that it does pretty much
3307 //what the user probably has in mind. The attributes
3308 //filled in here are dummy ones which are replaced in the
3309 //ATTRIBUT handler if a suitable location for the
3310 //attributes was found here. Unfortunately it is
3311 //possible for starmath to place character attributes on
3312 //entities which cannot occur in mathtype e.g. a Summation
3313 //symbol so these attributes may be lost
3314 if ((nPendingAttributes) &&
3315 (i == ((pTemp->GetText().getLength()+1)/2)-1))
3317 pS->WriteUChar( EMBEL );
3318 while (nPendingAttributes)
3320 pS->WriteUChar( 2 );
3321 //wedge the attributes in here and clear
3322 //the pending stack
3323 nPendingAttributes--;
3325 nInsertion=pS->Tell();
3326 pS->WriteUChar( END ); //end embel
3327 pS->WriteUChar( END ); //end embel
3332 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */