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