1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  */
9 
10 #include "rtfexport.hxx"
11 #include <node.hxx>
12 
13 #include <svtools/rtfkeywd.hxx>
14 #include <filter/msfilter/rtfutil.hxx>
15 #include <sal/log.hxx>
16 #include <osl/diagnose.h>
17 
SmRtfExport(const SmNode * pIn)18 SmRtfExport::SmRtfExport(const SmNode* pIn)
19     : SmWordExportBase(pIn)
20     , m_pBuffer(nullptr)
21     , m_nEncoding(RTL_TEXTENCODING_DONTKNOW)
22 {
23 }
24 
ConvertFromStarMath(OStringBuffer & rBuffer,rtl_TextEncoding nEncoding)25 void SmRtfExport::ConvertFromStarMath(OStringBuffer& rBuffer, rtl_TextEncoding nEncoding)
26 {
27     if (!m_pTree)
28         return;
29     m_pBuffer = &rBuffer;
30     m_nEncoding = nEncoding;
31     m_pBuffer->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE LO_STRING_SVTOOLS_RTF_MOMATH " ");
32     HandleNode(m_pTree, 0);
33     m_pBuffer->append("}"); // moMath
34 }
35 
36 // NOTE: This is still work in progress and unfinished, but it already covers a good
37 // part of the rtf math stuff.
38 
HandleVerticalStack(const SmNode * pNode,int nLevel)39 void SmRtfExport::HandleVerticalStack(const SmNode* pNode, int nLevel)
40 {
41     m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MEQARR " ");
42     int size = pNode->GetNumSubNodes();
43     for (int i = 0; i < size; ++i)
44     {
45         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
46         HandleNode(pNode->GetSubNode(i), nLevel + 1);
47         m_pBuffer->append("}"); // me
48     }
49     m_pBuffer->append("}"); // meqArr
50 }
51 
HandleText(const SmNode * pNode,int)52 void SmRtfExport::HandleText(const SmNode* pNode, int /*nLevel*/)
53 {
54     m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MR " ");
55 
56     if (pNode->GetToken().eType == TTEXT) // literal text
57         m_pBuffer->append(LO_STRING_SVTOOLS_RTF_MNOR " ");
58 
59     auto pTemp = static_cast<const SmTextNode*>(pNode);
60     SAL_INFO("starmath.rtf", "Text: " << pTemp->GetText());
61     for (sal_Int32 i = 0; i < pTemp->GetText().getLength(); i++)
62     {
63         sal_uInt16 nChar = pTemp->GetText()[i];
64         OUString aValue(SmTextNode::ConvertSymbolToUnicode(nChar));
65         m_pBuffer->append(msfilter::rtfutil::OutString(aValue, m_nEncoding));
66     }
67 
68     m_pBuffer->append("}"); // mr
69 }
70 
HandleFractions(const SmNode * pNode,int nLevel,const char * type)71 void SmRtfExport::HandleFractions(const SmNode* pNode, int nLevel, const char* type)
72 {
73     m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MF " ");
74     if (type)
75     {
76         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MFPR " ");
77         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MTYPE " ");
78         m_pBuffer->append(type);
79         m_pBuffer->append("}"); // mtype
80         m_pBuffer->append("}"); // mfPr
81     }
82     assert(pNode->GetNumSubNodes() == 3);
83     m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MNUM " ");
84     HandleNode(pNode->GetSubNode(0), nLevel + 1);
85     m_pBuffer->append("}"); // mnum
86     m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MDEN " ");
87     HandleNode(pNode->GetSubNode(2), nLevel + 1);
88     m_pBuffer->append("}"); // mden
89     m_pBuffer->append("}"); // mf
90 }
91 
HandleAttribute(const SmAttributNode * pNode,int nLevel)92 void SmRtfExport::HandleAttribute(const SmAttributNode* pNode, int nLevel)
93 {
94     switch (pNode->Attribute()->GetToken().eType)
95     {
96         case TCHECK:
97         case TACUTE:
98         case TGRAVE:
99         case TBREVE:
100         case TCIRCLE:
101         case TVEC:
102         case TTILDE:
103         case THAT:
104         case TDOT:
105         case TDDOT:
106         case TDDDOT:
107         case TWIDETILDE:
108         case TWIDEHAT:
109         case TWIDEHARPOON:
110         case TWIDEVEC:
111         case TBAR:
112         {
113             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MACC " ");
114             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MACCPR " ");
115             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MCHR " ");
116             OUString aValue(pNode->Attribute()->GetToken().cMathChar);
117             m_pBuffer->append(msfilter::rtfutil::OutString(aValue, m_nEncoding));
118             m_pBuffer->append("}"); // mchr
119             m_pBuffer->append("}"); // maccPr
120             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
121             HandleNode(pNode->Body(), nLevel + 1);
122             m_pBuffer->append("}"); // me
123             m_pBuffer->append("}"); // macc
124             break;
125         }
126         case TOVERLINE:
127         case TUNDERLINE:
128             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MBAR " ");
129             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MBARPR " ");
130             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MPOS " ");
131             m_pBuffer->append((pNode->Attribute()->GetToken().eType == TUNDERLINE) ? "bot" : "top");
132             m_pBuffer->append("}"); // mpos
133             m_pBuffer->append("}"); // mbarPr
134             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
135             HandleNode(pNode->Body(), nLevel + 1);
136             m_pBuffer->append("}"); // me
137             m_pBuffer->append("}"); // mbar
138             break;
139         case TOVERSTRIKE:
140             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MBORDERBOX " ");
141             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MBORDERBOXPR " ");
142             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MHIDETOP " 1}");
143             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MHIDEBOT " 1}");
144             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MHIDELEFT " 1}");
145             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MHIDERIGHT " 1}");
146             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSTRIKEH " 1}");
147             m_pBuffer->append("}"); // mborderBoxPr
148             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
149             HandleNode(pNode->Body(), nLevel + 1);
150             m_pBuffer->append("}"); // me
151             m_pBuffer->append("}"); // mborderBox
152             break;
153         default:
154             HandleAllSubNodes(pNode, nLevel);
155             break;
156     }
157 }
158 
HandleRoot(const SmRootNode * pNode,int nLevel)159 void SmRtfExport::HandleRoot(const SmRootNode* pNode, int nLevel)
160 {
161     m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MRAD " ");
162     if (const SmNode* argument = pNode->Argument())
163     {
164         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MDEG " ");
165         HandleNode(argument, nLevel + 1);
166         m_pBuffer->append("}"); // mdeg
167     }
168     else
169     {
170         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MRADPR " ");
171         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MDEGHIDE " 1}");
172         m_pBuffer->append("}"); // mradPr
173         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MDEG " }"); // empty but present
174     }
175     m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
176     HandleNode(pNode->Body(), nLevel + 1);
177     m_pBuffer->append("}"); // me
178     m_pBuffer->append("}"); // mrad
179 }
180 
181 namespace
182 {
mathSymbolToString(const SmNode * node,rtl_TextEncoding nEncoding)183 OString mathSymbolToString(const SmNode* node, rtl_TextEncoding nEncoding)
184 {
185     assert(node->GetType() == SmNodeType::Math || node->GetType() == SmNodeType::MathIdent);
186     auto txtnode = static_cast<const SmTextNode*>(node);
187     if (txtnode->GetText().isEmpty())
188         return OString();
189     assert(txtnode->GetText().getLength() == 1);
190     sal_Unicode chr = SmTextNode::ConvertSymbolToUnicode(txtnode->GetText()[0]);
191     OUString aValue(chr);
192     return msfilter::rtfutil::OutString(aValue, nEncoding);
193 }
194 }
195 
HandleOperator(const SmOperNode * pNode,int nLevel)196 void SmRtfExport::HandleOperator(const SmOperNode* pNode, int nLevel)
197 {
198     SAL_INFO("starmath.rtf", "Operator: " << int(pNode->GetToken().eType));
199     switch (pNode->GetToken().eType)
200     {
201         case TINT:
202         case TINTD:
203         case TIINT:
204         case TIIINT:
205         case TLINT:
206         case TLLINT:
207         case TLLLINT:
208         case TPROD:
209         case TCOPROD:
210         case TSUM:
211         {
212             const SmSubSupNode* subsup
213                 = pNode->GetSubNode(0)->GetType() == SmNodeType::SubSup
214                       ? static_cast<const SmSubSupNode*>(pNode->GetSubNode(0))
215                       : nullptr;
216             const SmNode* operation = subsup ? subsup->GetBody() : pNode->GetSubNode(0);
217             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MNARY " ");
218             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MNARYPR " ");
219             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MCHR " ");
220             m_pBuffer->append(mathSymbolToString(operation, m_nEncoding));
221             m_pBuffer->append("}"); // mchr
222             if (!subsup || !subsup->GetSubSup(CSUB))
223                 m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUBHIDE " 1}");
224             if (!subsup || !subsup->GetSubSup(CSUP))
225                 m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUPHIDE " 1}");
226             m_pBuffer->append("}"); // mnaryPr
227             if (!subsup || !subsup->GetSubSup(CSUB))
228                 m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUB " }");
229             else
230             {
231                 m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUB " ");
232                 HandleNode(subsup->GetSubSup(CSUB), nLevel + 1);
233                 m_pBuffer->append("}"); // msub
234             }
235             if (!subsup || !subsup->GetSubSup(CSUP))
236                 m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUP " }");
237             else
238             {
239                 m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUP " ");
240                 HandleNode(subsup->GetSubSup(CSUP), nLevel + 1);
241                 m_pBuffer->append("}"); // msup
242             }
243             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
244             HandleNode(pNode->GetSubNode(1), nLevel + 1); // body
245             m_pBuffer->append("}"); // me
246             m_pBuffer->append("}"); // mnary
247             break;
248         }
249         case TLIM:
250             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MFUNC " ");
251             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MFNAME " ");
252             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIMLOW " ");
253             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
254             HandleNode(pNode->GetSymbol(), nLevel + 1);
255             m_pBuffer->append("}"); // me
256             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIM " ");
257             if (const SmSubSupNode* subsup
258                 = pNode->GetSubNode(0)->GetType() == SmNodeType::SubSup
259                       ? static_cast<const SmSubSupNode*>(pNode->GetSubNode(0))
260                       : nullptr)
261                 if (subsup->GetSubSup(CSUB))
262                     HandleNode(subsup->GetSubSup(CSUB), nLevel + 1);
263             m_pBuffer->append("}"); // mlim
264             m_pBuffer->append("}"); // mlimLow
265             m_pBuffer->append("}"); // mfName
266             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
267             HandleNode(pNode->GetSubNode(1), nLevel + 1); // body
268             m_pBuffer->append("}"); // me
269             m_pBuffer->append("}"); // mfunc
270             break;
271         default:
272             SAL_INFO("starmath.rtf", "TODO: " << OSL_THIS_FUNC << " unhandled oper type");
273             break;
274     }
275 }
276 
HandleSubSupScriptInternal(const SmSubSupNode * pNode,int nLevel,int flags)277 void SmRtfExport::HandleSubSupScriptInternal(const SmSubSupNode* pNode, int nLevel, int flags)
278 {
279     // rtf supports only a certain combination of sub/super scripts, but LO can have any,
280     // so try to merge it using several tags if necessary
281     if (flags == 0) // none
282         return;
283     if ((flags & (1 << RSUP | 1 << RSUB)) == (1 << RSUP | 1 << RSUB))
284     {
285         // m:sSubSup
286         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSSUBSUP " ");
287         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
288         flags &= ~(1 << RSUP | 1 << RSUB);
289         if (flags == 0)
290             HandleNode(pNode->GetBody(), nLevel + 1);
291         else
292             HandleSubSupScriptInternal(pNode, nLevel, flags);
293         m_pBuffer->append("}"); // me
294         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUB " ");
295         HandleNode(pNode->GetSubSup(RSUB), nLevel + 1);
296         m_pBuffer->append("}"); // msub
297         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUP " ");
298         HandleNode(pNode->GetSubSup(RSUP), nLevel + 1);
299         m_pBuffer->append("}"); // msup
300         m_pBuffer->append("}"); // msubSup
301     }
302     else if ((flags & (1 << RSUB)) == 1 << RSUB)
303     {
304         // m:sSub
305         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSSUB " ");
306         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
307         flags &= ~(1 << RSUB);
308         if (flags == 0)
309             HandleNode(pNode->GetBody(), nLevel + 1);
310         else
311             HandleSubSupScriptInternal(pNode, nLevel, flags);
312         m_pBuffer->append("}"); // me
313         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUB " ");
314         HandleNode(pNode->GetSubSup(RSUB), nLevel + 1);
315         m_pBuffer->append("}"); // msub
316         m_pBuffer->append("}"); // msSub
317     }
318     else if ((flags & (1 << RSUP)) == 1 << RSUP)
319     {
320         // m:sSup
321         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSSUP " ");
322         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
323         flags &= ~(1 << RSUP);
324         if (flags == 0)
325             HandleNode(pNode->GetBody(), nLevel + 1);
326         else
327             HandleSubSupScriptInternal(pNode, nLevel, flags);
328         m_pBuffer->append("}"); // me
329         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUP " ");
330         HandleNode(pNode->GetSubSup(RSUP), nLevel + 1);
331         m_pBuffer->append("}"); // msup
332         m_pBuffer->append("}"); // msSup
333     }
334     else if ((flags & (1 << LSUP | 1 << LSUB)) == (1 << LSUP | 1 << LSUB))
335     {
336         // m:sPre
337         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSPRE " ");
338         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUB " ");
339         HandleNode(pNode->GetSubSup(LSUB), nLevel + 1);
340         m_pBuffer->append("}"); // msub
341         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUP " ");
342         HandleNode(pNode->GetSubSup(LSUP), nLevel + 1);
343         m_pBuffer->append("}"); // msup
344         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
345         flags &= ~(1 << LSUP | 1 << LSUB);
346         if (flags == 0)
347             HandleNode(pNode->GetBody(), nLevel + 1);
348         else
349             HandleSubSupScriptInternal(pNode, nLevel, flags);
350         m_pBuffer->append("}"); // me
351         m_pBuffer->append("}"); // msPre
352     }
353     else if ((flags & (1 << CSUB)) == (1 << CSUB))
354     {
355         // m:limLow looks like a good element for central superscript
356         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIMLOW " ");
357         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
358         flags &= ~(1 << CSUB);
359         if (flags == 0)
360             HandleNode(pNode->GetBody(), nLevel + 1);
361         else
362             HandleSubSupScriptInternal(pNode, nLevel, flags);
363         m_pBuffer->append("}"); // me
364         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIM " ");
365         HandleNode(pNode->GetSubSup(CSUB), nLevel + 1);
366         m_pBuffer->append("}"); // mlim
367         m_pBuffer->append("}"); // mlimLow
368     }
369     else if ((flags & (1 << CSUP)) == (1 << CSUP))
370     {
371         // m:limUpp looks like a good element for central superscript
372         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIMUPP " ");
373         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
374         flags &= ~(1 << CSUP);
375         if (flags == 0)
376             HandleNode(pNode->GetBody(), nLevel + 1);
377         else
378             HandleSubSupScriptInternal(pNode, nLevel, flags);
379         m_pBuffer->append("}"); // me
380         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIM " ");
381         HandleNode(pNode->GetSubSup(CSUP), nLevel + 1);
382         m_pBuffer->append("}"); // mlim
383         m_pBuffer->append("}"); // mlimUpp
384     }
385     else
386         SAL_INFO("starmath.rtf", "TODO: " << OSL_THIS_FUNC << " unhandled subsup type");
387 }
388 
HandleMatrix(const SmMatrixNode * pNode,int nLevel)389 void SmRtfExport::HandleMatrix(const SmMatrixNode* pNode, int nLevel)
390 {
391     m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MM " ");
392     for (size_t row = 0; row < pNode->GetNumRows(); ++row)
393     {
394         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MMR " ");
395         for (size_t col = 0; col < pNode->GetNumCols(); ++col)
396         {
397             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
398             if (const SmNode* node = pNode->GetSubNode(row * pNode->GetNumCols() + col))
399                 HandleNode(node, nLevel + 1);
400             m_pBuffer->append("}"); // me
401         }
402         m_pBuffer->append("}"); // mmr
403     }
404     m_pBuffer->append("}"); // mm
405 }
406 
HandleBrace(const SmBraceNode * pNode,int nLevel)407 void SmRtfExport::HandleBrace(const SmBraceNode* pNode, int nLevel)
408 {
409     m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MD " ");
410     m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MDPR " ");
411     m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MBEGCHR " ");
412     m_pBuffer->append(mathSymbolToString(pNode->OpeningBrace(), m_nEncoding));
413     m_pBuffer->append("}"); // mbegChr
414     std::vector<const SmNode*> subnodes;
415     if (pNode->Body()->GetType() == SmNodeType::Bracebody)
416     {
417         auto body = static_cast<const SmBracebodyNode*>(pNode->Body());
418         bool separatorWritten = false; // assume all separators are the same
419         for (size_t i = 0; i < body->GetNumSubNodes(); ++i)
420         {
421             const SmNode* subnode = body->GetSubNode(i);
422             if (subnode->GetType() == SmNodeType::Math
423                 || subnode->GetType() == SmNodeType::MathIdent)
424             {
425                 // do not write, but write what separator it is
426                 auto math = static_cast<const SmMathSymbolNode*>(subnode);
427                 if (!separatorWritten)
428                 {
429                     m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSEPCHR " ");
430                     m_pBuffer->append(mathSymbolToString(math, m_nEncoding));
431                     m_pBuffer->append("}"); // msepChr
432                     separatorWritten = true;
433                 }
434             }
435             else
436                 subnodes.push_back(subnode);
437         }
438     }
439     else
440         subnodes.push_back(pNode->Body());
441     m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MENDCHR " ");
442     m_pBuffer->append(mathSymbolToString(pNode->ClosingBrace(), m_nEncoding));
443     m_pBuffer->append("}"); // mendChr
444     m_pBuffer->append("}"); // mdPr
445     for (const SmNode* subnode : subnodes)
446     {
447         m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
448         HandleNode(subnode, nLevel + 1);
449         m_pBuffer->append("}"); // me
450     }
451     m_pBuffer->append("}"); // md
452 }
453 
HandleVerticalBrace(const SmVerticalBraceNode * pNode,int nLevel)454 void SmRtfExport::HandleVerticalBrace(const SmVerticalBraceNode* pNode, int nLevel)
455 {
456     SAL_INFO("starmath.rtf", "Vertical: " << int(pNode->GetToken().eType));
457     switch (pNode->GetToken().eType)
458     {
459         case TOVERBRACE:
460         case TUNDERBRACE:
461         {
462             bool top = (pNode->GetToken().eType == TOVERBRACE);
463             if (top)
464                 m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIMUPP " ");
465             else
466                 m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIMLOW " ");
467             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
468             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MGROUPCHR " ");
469             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MGROUPCHRPR " ");
470             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MCHR " ");
471             m_pBuffer->append(mathSymbolToString(pNode->Brace(), m_nEncoding));
472             m_pBuffer->append("}"); // mchr
473             // TODO not sure if pos and vertJc are correct
474             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MPOS " ")
475                 .append(top ? "top" : "bot")
476                 .append("}");
477             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MVERTJC " ")
478                 .append(top ? "bot" : "top")
479                 .append("}");
480             m_pBuffer->append("}"); // mgroupChrPr
481             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
482             HandleNode(pNode->Body(), nLevel + 1);
483             m_pBuffer->append("}"); // me
484             m_pBuffer->append("}"); // mgroupChr
485             m_pBuffer->append("}"); // me
486             m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIM " ");
487             HandleNode(pNode->Script(), nLevel + 1);
488             m_pBuffer->append("}"); // mlim
489             m_pBuffer->append("}"); // mlimUpp or mlimLow
490             break;
491         }
492         default:
493             SAL_INFO("starmath.rtf", "TODO: " << OSL_THIS_FUNC << " unhandled vertical brace type");
494             break;
495     }
496 }
497 
HandleBlank()498 void SmRtfExport::HandleBlank()
499 {
500     m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MR " ");
501     m_pBuffer->append(" ");
502     m_pBuffer->append("}"); // mr
503 }
504 
505 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
506