Coverage Report

Created: 2026-04-09 11:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/starmath/source/ooxmlimport.cxx
Line
Count
Source
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 <sal/config.h>
11
12
#include <string_view>
13
14
#include "ooxmlimport.hxx"
15
#include <types.hxx>
16
17
#include <oox/mathml/importutils.hxx>
18
#include <oox/token/namespaces.hxx>
19
#include <rtl/ustring.hxx>
20
#include <rtl/ustrbuf.hxx>
21
#include <sal/log.hxx>
22
#include <o3tl/string_view.hxx>
23
#include <parse5.hxx>
24
#include <unordered_set>
25
26
using namespace oox::formulaimport;
27
28
/*
29
The primary internal data structure for the formula is the text representation
30
(the SmNode tree is built from it), so read data must be converted into this format.
31
*/
32
33
0
#define OPENING( token ) XML_STREAM_OPENING( token )
34
0
#define CLOSING( token ) XML_STREAM_CLOSING( token )
35
36
// TODO create IS_OPENING(), IS_CLOSING() instead of doing 'next == OPENING( next )' ?
37
38
SmOoxmlImport::SmOoxmlImport( oox::formulaimport::XmlStream& s )
39
0
    : m_rStream( s )
40
0
{
41
0
}
42
43
OUString SmOoxmlImport::ConvertToStarMath()
44
0
{
45
0
    return handleStream();
46
0
}
47
48
// "toplevel" of reading, there will be oMath (if there was oMathPara, that was
49
// up to the parent component to handle)
50
51
// NOT complete
52
OUString SmOoxmlImport::handleStream()
53
0
{
54
0
    m_rStream.ensureOpeningTag( M_TOKEN( oMath ));
55
0
    OUStringBuffer ret;
56
0
    while( !m_rStream.atEnd() && m_rStream.currentToken() != CLOSING( M_TOKEN( oMath )))
57
0
    {
58
        // strictly speaking, it is not OMathArg here, but currently supported
59
        // functionality is the same like OMathArg, in the future this may need improving
60
0
        OUString item = readOMathArg( M_TOKEN( oMath ));
61
0
        if( item.isEmpty())
62
0
            continue;
63
0
        if( !ret.isEmpty())
64
0
            ret.append(" ");
65
0
        ret.append(item);
66
0
    }
67
0
    m_rStream.ensureClosingTag( M_TOKEN( oMath ));
68
    // Placeholders are written out as nothing (i.e. nothing inside e.g. the <e> element),
69
    // which will result in "{}" in the formula text. Fix this up.
70
0
    OUString ret2 = ret.makeStringAndClear().replaceAll( "{}", "<?>" );
71
    // And as a result, empty parts of the formula that are not placeholders are written out
72
    // as a single space, so fix that up too.
73
0
    ret2 = ret2.replaceAll( "{ }", "{}" );
74
0
    SAL_INFO( "starmath.ooxml", "Formula: " << ret2 );
75
0
    return ret2;
76
0
}
77
78
OUString SmOoxmlImport::readOMathArg( int stoptoken )
79
0
{
80
0
    OUStringBuffer ret;
81
0
    while( !m_rStream.atEnd() && m_rStream.currentToken() != CLOSING( stoptoken ))
82
0
    {
83
0
        if( !ret.isEmpty())
84
0
            ret.append(" ");
85
0
        switch( m_rStream.currentToken())
86
0
        {
87
0
            case OPENING( M_TOKEN( acc )):
88
0
                ret.append(handleAcc());
89
0
                break;
90
0
            case OPENING( M_TOKEN( bar )):
91
0
                ret.append(handleBar());
92
0
                break;
93
0
            case OPENING( M_TOKEN( box )):
94
0
                ret.append(handleBox());
95
0
                break;
96
0
            case OPENING( M_TOKEN( borderBox )):
97
0
                ret.append(handleBorderBox());
98
0
                break;
99
0
            case OPENING( M_TOKEN( d )):
100
0
                ret.append(handleD());
101
0
                break;
102
0
            case OPENING( M_TOKEN( eqArr )):
103
0
                ret.append(handleEqArr());
104
0
                break;
105
0
            case OPENING( M_TOKEN( f )):
106
0
                ret.append(handleF());
107
0
                break;
108
0
            case OPENING( M_TOKEN( func )):
109
0
                ret.append(handleFunc());
110
0
                break;
111
0
            case OPENING( M_TOKEN( limLow )):
112
0
                ret.append(handleLimLowUpp( LimLow ));
113
0
                break;
114
0
            case OPENING( M_TOKEN( limUpp )):
115
0
                ret.append(handleLimLowUpp( LimUpp ));
116
0
                break;
117
0
            case OPENING( M_TOKEN( groupChr )):
118
0
                ret.append(handleGroupChr());
119
0
                break;
120
0
            case OPENING( M_TOKEN( m )):
121
0
                ret.append(handleM());
122
0
                break;
123
0
            case OPENING( M_TOKEN( nary )):
124
0
                ret.append(handleNary());
125
0
                break;
126
0
            case OPENING( M_TOKEN( r )):
127
0
                ret.append(handleR());
128
0
                break;
129
0
            case OPENING( M_TOKEN( rad )):
130
0
                ret.append(handleRad());
131
0
                break;
132
0
            case OPENING( M_TOKEN( sPre )):
133
0
                ret.append(handleSpre());
134
0
                break;
135
0
            case OPENING( M_TOKEN( sSub )):
136
0
                ret.append(handleSsub());
137
0
                break;
138
0
            case OPENING( M_TOKEN( sSubSup )):
139
0
                ret.append(handleSsubsup());
140
0
                break;
141
0
            case OPENING( M_TOKEN( sSup )):
142
0
                ret.append(handleSsup());
143
0
                break;
144
0
            default:
145
0
                m_rStream.handleUnexpectedTag();
146
0
                break;
147
0
        }
148
0
    }
149
0
    return ret.makeStringAndClear();
150
0
}
151
152
OUString SmOoxmlImport::readOMathArgInElement( int token )
153
0
{
154
0
    m_rStream.ensureOpeningTag( token );
155
0
    OUString ret = readOMathArg( token );
156
0
    m_rStream.ensureClosingTag( token );
157
0
    return ret;
158
0
}
159
160
OUString SmOoxmlImport::handleAcc()
161
0
{
162
0
    m_rStream.ensureOpeningTag( M_TOKEN( acc ));
163
0
    sal_Unicode accChr = 0x302;
164
0
    if( XmlStream::Tag accPr = m_rStream.checkOpeningTag( M_TOKEN( accPr )))
165
0
    {
166
0
        if( XmlStream::Tag chr = m_rStream.checkOpeningTag( M_TOKEN( chr )))
167
0
        {
168
0
            accChr = chr.attribute( M_TOKEN( val ), accChr );
169
0
            m_rStream.ensureClosingTag( M_TOKEN( chr ));
170
0
        }
171
0
        m_rStream.ensureClosingTag( M_TOKEN( accPr ));
172
0
    }
173
    // see aTokenTable in parse.cxx
174
0
    OUString acc;
175
0
    switch( accChr )
176
0
    {
177
0
        case MS_BAR:
178
0
        case MS_COMBBAR:
179
0
            acc = "bar";
180
0
            break;
181
0
        case MS_CHECK:
182
0
        case MS_COMBCHECK:
183
0
            acc = "check";
184
0
            break;
185
0
        case MS_ACUTE:
186
0
        case MS_COMBACUTE:
187
0
            acc = "acute";
188
0
            break;
189
0
        case MS_COMBOVERLINE:
190
0
            acc = "overline";
191
0
            break;
192
0
        case MS_GRAVE:
193
0
        case MS_COMBGRAVE:
194
0
            acc = "grave";
195
0
            break;
196
0
        case MS_BREVE:
197
0
        case MS_COMBBREVE:
198
0
            acc = "breve";
199
0
            break;
200
0
        case MS_CIRCLE:
201
0
        case MS_COMBCIRCLE:
202
0
            acc = "circle";
203
0
            break;
204
0
        case MS_RIGHTARROW:
205
0
        case MS_VEC:
206
            // prefer wide variants for these 3, .docx can't seem to differentiate
207
            // between e.g. 'vec' and 'widevec', if whatever the accent is above is short, this
208
            // shouldn't matter, but short above a longer expression doesn't look right
209
0
            acc = "widevec";
210
0
            break;
211
0
        case MS_LVEC:
212
0
            acc = "wideleftvec";
213
0
            break;
214
0
        case MS_HARPOON:
215
0
            acc = "wideharpoon";
216
0
            break;
217
0
        case MS_LHARPOON:
218
0
            acc = "wideleftharpoon";
219
0
            break;
220
0
        case MS_TILDE:
221
0
        case MS_COMBTILDE:
222
0
            acc = "widetilde";
223
0
            break;
224
0
        case MS_HAT:
225
0
        case MS_COMBHAT:
226
0
            acc = "widehat";
227
0
            break;
228
0
        case MS_DOT:
229
0
        case MS_COMBDOT:
230
0
            acc = "dot";
231
0
            break;
232
0
        case MS_DDOT:
233
0
        case MS_COMBDDOT:
234
0
            acc = "ddot";
235
0
            break;
236
0
        case MS_DDDOT:
237
0
            acc = "dddot";
238
0
            break;
239
0
        default:
240
0
            acc = "acute";
241
0
            SAL_WARN( "starmath.ooxml", "Unknown m:chr in m:acc \'" << OUString(accChr) << "\'" );
242
0
            break;
243
0
    }
244
0
    OUString e = readOMathArgInElement( M_TOKEN( e ));
245
0
    m_rStream.ensureClosingTag( M_TOKEN( acc ));
246
0
    return acc + " {" + e + "}";
247
0
}
248
249
OUString SmOoxmlImport::handleBar()
250
0
{
251
0
    m_rStream.ensureOpeningTag( M_TOKEN( bar ));
252
0
    enum pos_t { top, bot } topbot = bot;
253
0
    if( m_rStream.checkOpeningTag( M_TOKEN( barPr )))
254
0
    {
255
0
        if( XmlStream::Tag pos = m_rStream.checkOpeningTag( M_TOKEN( pos )))
256
0
        {
257
0
            if( pos.attribute( M_TOKEN( val )) == "top" )
258
0
                topbot = top;
259
0
            else if( pos.attribute( M_TOKEN( val )) == "bot" )
260
0
                topbot = bot;
261
0
            m_rStream.ensureClosingTag( M_TOKEN( pos ));
262
0
        }
263
0
        m_rStream.ensureClosingTag( M_TOKEN( barPr ));
264
0
    }
265
0
    OUString e = readOMathArgInElement( M_TOKEN( e ));
266
0
    m_rStream.ensureClosingTag( M_TOKEN( bar ));
267
0
    if( topbot == top )
268
0
        return "overline {" + e + "}";
269
0
    else
270
0
        return "underline {" + e + "}";
271
0
}
272
273
OUString SmOoxmlImport::handleBox()
274
0
{
275
    // there does not seem to be functionality in LO to actually implement this
276
    // (or is there), but at least read in the contents instead of ignoring them
277
0
    m_rStream.ensureOpeningTag( M_TOKEN( box ));
278
0
    OUString e = readOMathArgInElement( M_TOKEN( e ));
279
0
    m_rStream.ensureClosingTag( M_TOKEN( box ));
280
0
    return e;
281
0
}
282
283
284
OUString SmOoxmlImport::handleBorderBox()
285
0
{
286
0
    m_rStream.ensureOpeningTag( M_TOKEN( borderBox ));
287
0
    bool isStrikeH = false;
288
0
    if( m_rStream.checkOpeningTag( M_TOKEN( borderBoxPr )))
289
0
    {
290
0
        if( XmlStream::Tag strikeH = m_rStream.checkOpeningTag( M_TOKEN( strikeH )))
291
0
        {
292
0
            if( strikeH.attribute( M_TOKEN( val ), false ))
293
0
                isStrikeH = true;
294
0
            m_rStream.ensureClosingTag( M_TOKEN( strikeH ));
295
0
        }
296
0
        m_rStream.ensureClosingTag( M_TOKEN( borderBoxPr ));
297
0
    }
298
0
    OUString e = readOMathArgInElement( M_TOKEN( e ));
299
0
    m_rStream.ensureClosingTag( M_TOKEN( borderBox ));
300
0
    if( isStrikeH )
301
0
        return "overstrike {" + e + "}";
302
    // LO does not seem to implement anything for handling the other cases
303
0
    return e;
304
0
}
305
306
OUString SmOoxmlImport::handleD()
307
0
{
308
0
    m_rStream.ensureOpeningTag( M_TOKEN( d ));
309
0
    OUString opening = u"("_ustr;
310
0
    OUString closing = u")"_ustr;
311
0
    OUString separator = u"|"_ustr;
312
0
    if( XmlStream::Tag dPr = m_rStream.checkOpeningTag( M_TOKEN( dPr )))
313
0
    {
314
0
        if( XmlStream::Tag begChr = m_rStream.checkOpeningTag( M_TOKEN( begChr )))
315
0
        {
316
0
            opening = begChr.attribute( M_TOKEN( val ), opening );
317
0
            m_rStream.ensureClosingTag( M_TOKEN( begChr ));
318
0
        }
319
0
        if( XmlStream::Tag sepChr = m_rStream.checkOpeningTag( M_TOKEN( sepChr )))
320
0
        {
321
0
            separator = sepChr.attribute( M_TOKEN( val ), separator );
322
0
            m_rStream.ensureClosingTag( M_TOKEN( sepChr ));
323
0
        }
324
0
        if( XmlStream::Tag endChr = m_rStream.checkOpeningTag( M_TOKEN( endChr )))
325
0
        {
326
0
            closing = endChr.attribute( M_TOKEN( val ), closing );
327
0
            m_rStream.ensureClosingTag( M_TOKEN( endChr ));
328
0
        }
329
0
        m_rStream.ensureClosingTag( M_TOKEN( dPr ));
330
0
    }
331
0
    if( opening == "{" )
332
0
        opening = "left lbrace ";
333
0
    if( closing == "}" )
334
0
        closing = " right rbrace";
335
0
    if( opening == u"\u27e6" )
336
0
        opening = "left ldbracket ";
337
0
    if( closing == u"\u27e7" )
338
0
        closing = " right rdbracket";
339
0
    if( opening == "|" )
340
0
        opening = "left lline ";
341
0
    if( closing == "|" )
342
0
        closing = " right rline";
343
0
    if (opening == OUStringChar(MS_DLINE)
344
0
        || opening == OUStringChar(MS_DVERTLINE))
345
0
        opening = "left ldline ";
346
0
    if (closing == OUStringChar(MS_DLINE)
347
0
        || closing == OUStringChar(MS_DVERTLINE))
348
0
        closing = " right rdline";
349
0
    if (opening == OUStringChar(MS_LANGLE)
350
0
        || opening == OUStringChar(MS_LMATHANGLE))
351
0
        opening = "left langle ";
352
0
    if (closing == OUStringChar(MS_RANGLE)
353
0
        || closing == OUStringChar(MS_RMATHANGLE))
354
0
        closing = " right rangle";
355
    // use scalable brackets (the explicit "left" or "right")
356
0
    if( opening == "(" || opening == "[" || opening == ")" || opening == "]" )
357
0
        opening = "left " + opening;
358
0
    if( closing == ")" || closing == "]" || closing == "(" || closing == "[")
359
0
        closing = " right " + closing;
360
0
    if( separator == "|" ) // plain "|" would be actually "V" (logical or)
361
0
        separator = " mline ";
362
0
    if( opening.isEmpty())
363
0
        opening = "left none ";
364
0
    if( closing.isEmpty())
365
0
        closing = " right none";
366
0
    OUStringBuffer ret( opening );
367
0
    bool first = true;
368
0
    while( m_rStream.findTag( OPENING( M_TOKEN( e ))))
369
0
    {
370
0
        if( !first )
371
0
            ret.append( separator );
372
0
        first = false;
373
0
        ret.append( readOMathArgInElement( M_TOKEN( e )));
374
0
    }
375
0
    ret.append( closing );
376
0
    m_rStream.ensureClosingTag( M_TOKEN( d ));
377
0
    return ret.makeStringAndClear();
378
0
}
379
380
OUString SmOoxmlImport::handleEqArr()
381
0
{
382
0
    m_rStream.ensureOpeningTag( M_TOKEN( eqArr ));
383
0
    OUStringBuffer ret;
384
0
    do
385
0
    { // there must be at least one m:e
386
0
        if( !ret.isEmpty())
387
0
            ret.append("#");
388
0
        ret.append(" "
389
0
            + readOMathArgInElement( M_TOKEN( e ))
390
0
            + " ");
391
0
    } while( !m_rStream.atEnd() && m_rStream.findTag( OPENING( M_TOKEN( e ))));
392
0
    m_rStream.ensureClosingTag( M_TOKEN( eqArr ));
393
0
    return "stack {" + ret + "}";
394
0
}
395
396
OUString SmOoxmlImport::handleF()
397
0
{
398
0
    m_rStream.ensureOpeningTag( M_TOKEN( f ));
399
0
    enum operation_t { bar, lin, noBar } operation = bar;
400
0
    if( m_rStream.checkOpeningTag( M_TOKEN( fPr )))
401
0
    {
402
0
        if( XmlStream::Tag type = m_rStream.checkOpeningTag( M_TOKEN( type )))
403
0
        {
404
0
            if( type.attribute( M_TOKEN( val )) == "bar" )
405
0
                operation = bar;
406
0
            else if( type.attribute( M_TOKEN( val )) == "lin" )
407
0
                operation = lin;
408
0
            else if( type.attribute( M_TOKEN( val )) == "noBar" )
409
0
                operation = noBar;
410
0
            m_rStream.ensureClosingTag( M_TOKEN( type ));
411
0
        }
412
0
        m_rStream.ensureClosingTag( M_TOKEN( fPr ));
413
0
    }
414
0
    OUString num = readOMathArgInElement( M_TOKEN( num ));
415
0
    OUString den = readOMathArgInElement( M_TOKEN( den ));
416
0
    m_rStream.ensureClosingTag( M_TOKEN( f ));
417
0
    if( operation == bar )
418
0
        return "{" + num + "} over {" + den + "}";
419
0
    else if( operation == lin )
420
0
        return "{" + num + "} / {" + den + "}";
421
0
    else // noBar
422
0
    {
423
0
        return "binom {" + num + "} {" + den + "}";
424
0
    }
425
0
}
426
427
OUString SmOoxmlImport::handleFunc()
428
0
{
429
//lim from{x rightarrow 1} x
430
0
    m_rStream.ensureOpeningTag( M_TOKEN( func ));
431
0
    OUString fname = readOMathArgInElement( M_TOKEN( fName ));
432
    // fix the various functions
433
0
    if( fname.startsWith( "lim csub {" ))
434
0
        fname = OUString::Concat("lim from {") + fname.subView( 10 );
435
0
    OUString ret = fname + " {" + readOMathArgInElement( M_TOKEN( e )) + "}";
436
0
    m_rStream.ensureClosingTag( M_TOKEN( func ));
437
0
    return ret;
438
0
}
439
440
OUString SmOoxmlImport::handleLimLowUpp( LimLowUpp_t limlowupp )
441
0
{
442
0
    int token = limlowupp == LimLow ? M_TOKEN( limLow ) : M_TOKEN( limUpp );
443
0
    m_rStream.ensureOpeningTag( token );
444
0
    OUString e = readOMathArgInElement( M_TOKEN( e ));
445
0
    OUString lim = readOMathArgInElement( M_TOKEN( lim ));
446
0
    m_rStream.ensureClosingTag( token );
447
    // fix up overbrace/underbrace  (use { }, as {} will be converted to a placeholder)
448
0
    if( limlowupp == LimUpp && e.endsWith( " overbrace { }" ))
449
0
        return e.subView( 0, e.getLength() - 2 ) + lim + "}";
450
0
    if( limlowupp == LimLow && e.endsWith( " underbrace { }" ))
451
0
        return e.subView( 0, e.getLength() - 2 ) + lim + "}";
452
0
    return e
453
0
        + ( limlowupp == LimLow
454
0
            ? std::u16string_view( u" csub {" ) : std::u16string_view( u" csup {" ))
455
0
        + lim + "}";
456
0
}
457
458
OUString SmOoxmlImport::handleGroupChr()
459
0
{
460
0
    m_rStream.ensureOpeningTag( M_TOKEN( groupChr ));
461
0
    sal_Unicode chr = 0x23df;
462
0
    enum pos_t { top, bot } pos = bot;
463
0
    if( m_rStream.checkOpeningTag( M_TOKEN( groupChrPr )))
464
0
    {
465
0
        if( XmlStream::Tag chrTag = m_rStream.checkOpeningTag( M_TOKEN( chr )))
466
0
        {
467
0
            chr = chrTag.attribute( M_TOKEN( val ), chr );
468
0
            m_rStream.ensureClosingTag( M_TOKEN( chr ));
469
0
        }
470
0
        if( XmlStream::Tag posTag = m_rStream.checkOpeningTag( M_TOKEN( pos )))
471
0
        {
472
0
            if( posTag.attribute( M_TOKEN( val ), u"bot"_ustr) == "top" )
473
0
                pos = top;
474
0
            m_rStream.ensureClosingTag( M_TOKEN( pos ));
475
0
        }
476
0
        m_rStream.ensureClosingTag( M_TOKEN( groupChrPr ));
477
0
    }
478
0
    OUString e = readOMathArgInElement( M_TOKEN( e ));
479
0
    m_rStream.ensureClosingTag( M_TOKEN( groupChr ));
480
0
    if( pos == top && chr == u'\x23de')
481
0
        return "{" + e + "} overbrace { }";
482
0
    if( pos == bot && chr == u'\x23df')
483
0
        return "{" + e + "} underbrace { }";
484
0
    if( pos == top )
485
0
        return "{" + e + "} csup {" + OUStringChar( chr ) + "}";
486
0
    else
487
0
        return "{" + e + "} csub {" + OUStringChar( chr ) + "}";
488
0
}
489
490
OUString SmOoxmlImport::handleM()
491
0
{
492
0
    m_rStream.ensureOpeningTag( M_TOKEN( m ));
493
0
    OUStringBuffer allrows;
494
0
    do // there must be at least one m:mr
495
0
    {
496
0
        m_rStream.ensureOpeningTag( M_TOKEN( mr ));
497
0
        OUStringBuffer row;
498
0
        do // there must be at least one m:e
499
0
        {
500
0
            if( !row.isEmpty())
501
0
                row.append(" # ");
502
0
            row.append(readOMathArgInElement( M_TOKEN( e )));
503
0
        } while( !m_rStream.atEnd() && m_rStream.findTag( OPENING( M_TOKEN( e ))));
504
0
        if( !allrows.isEmpty())
505
0
            allrows.append(" ## ");
506
0
        allrows.append(row);
507
0
        m_rStream.ensureClosingTag( M_TOKEN( mr ));
508
0
    } while( !m_rStream.atEnd() && m_rStream.findTag( OPENING( M_TOKEN( mr ))));
509
0
    m_rStream.ensureClosingTag( M_TOKEN( m ));
510
0
    return "matrix {" + allrows + "}";
511
0
}
512
513
OUString SmOoxmlImport::handleNary()
514
0
{
515
0
    m_rStream.ensureOpeningTag( M_TOKEN( nary ));
516
0
    sal_Unicode chr = 0x222b;
517
0
    bool subHide = false;
518
0
    bool supHide = false;
519
0
    if( m_rStream.checkOpeningTag( M_TOKEN( naryPr )))
520
0
    {
521
0
        if( XmlStream::Tag chrTag = m_rStream.checkOpeningTag( M_TOKEN( chr )))
522
0
        {
523
0
            chr = chrTag.attribute( M_TOKEN( val ), chr );
524
0
            m_rStream.ensureClosingTag( M_TOKEN( chr ));
525
0
        }
526
0
        if( XmlStream::Tag subHideTag = m_rStream.checkOpeningTag( M_TOKEN( subHide )))
527
0
        {
528
0
            subHide = subHideTag.attribute( M_TOKEN( val ), subHide );
529
0
            m_rStream.ensureClosingTag( M_TOKEN( subHide ));
530
0
        }
531
0
        if( XmlStream::Tag supHideTag = m_rStream.checkOpeningTag( M_TOKEN( supHide )))
532
0
        {
533
0
            supHide = supHideTag.attribute( M_TOKEN( val ), supHide );
534
0
            m_rStream.ensureClosingTag( M_TOKEN( supHide ));
535
0
        }
536
0
        m_rStream.ensureClosingTag( M_TOKEN( naryPr ));
537
0
    }
538
0
    OUString sub = readOMathArgInElement( M_TOKEN( sub ));
539
0
    OUString sup = readOMathArgInElement( M_TOKEN( sup ));
540
0
    OUString e = readOMathArgInElement( M_TOKEN( e ));
541
0
    OUString ret;
542
0
    switch( chr )
543
0
    {
544
0
        case MS_INT:
545
0
            ret = "int";
546
0
            break;
547
0
        case MS_IINT:
548
0
            ret = "iint";
549
0
            break;
550
0
        case MS_IIINT:
551
0
            ret = "iiint";
552
0
            break;
553
0
        case MS_LINT:
554
0
            ret = "lint";
555
0
            break;
556
0
        case MS_LLINT:
557
0
            ret = "llint";
558
0
            break;
559
0
        case MS_LLLINT:
560
0
            ret = "lllint";
561
0
            break;
562
0
        case MS_PROD:
563
0
            ret = "prod";
564
0
            break;
565
0
        case MS_COPROD:
566
0
            ret = "coprod";
567
0
            break;
568
0
        case MS_SUM:
569
0
            ret = "sum";
570
0
            break;
571
0
        default:
572
0
            SAL_WARN( "starmath.ooxml", "Unknown m:nary chr \'" << OUString(chr) << "\'" );
573
0
            break;
574
0
    }
575
0
    if( !subHide )
576
0
        ret += " from {" + sub + "}";
577
0
    if( !supHide )
578
0
        ret += " to {" + sup + "}";
579
0
    ret += " {" + e + "}";
580
0
    m_rStream.ensureClosingTag( M_TOKEN( nary ));
581
0
    return ret;
582
0
}
583
584
// NOT complete
585
OUString SmOoxmlImport::handleR()
586
0
{
587
0
    m_rStream.ensureOpeningTag( M_TOKEN( r ));
588
0
    bool normal = false;
589
0
    bool literal = false;
590
0
    OUString scrString;
591
0
    if( XmlStream::Tag rPr = m_rStream.checkOpeningTag( M_TOKEN( rPr )))
592
0
    {
593
0
        if( XmlStream::Tag litTag = m_rStream.checkOpeningTag( M_TOKEN( lit )))
594
0
        {
595
0
            literal = litTag.attribute( M_TOKEN( val ), true );
596
0
            m_rStream.ensureClosingTag( M_TOKEN( lit ));
597
0
        }
598
0
        if( XmlStream::Tag norTag = m_rStream.checkOpeningTag( M_TOKEN( nor )))
599
0
        {
600
0
            normal = norTag.attribute( M_TOKEN( val ), true );
601
0
            m_rStream.ensureClosingTag( M_TOKEN( nor ));
602
0
        }
603
0
        if (XmlStream::Tag srcTag = m_rStream.checkOpeningTag( M_TOKEN( scr )))
604
0
        {
605
0
            scrString = srcTag.attribute( M_TOKEN( val ), scrString );
606
0
            m_rStream.ensureClosingTag( M_TOKEN( scr ));
607
0
        }
608
0
        m_rStream.ensureClosingTag( M_TOKEN( rPr ));
609
0
    }
610
0
    OUStringBuffer text;
611
0
    bool isTagT = false;
612
0
    while( !m_rStream.atEnd() && m_rStream.currentToken() != CLOSING( m_rStream.currentToken()))
613
0
    {
614
0
        switch( m_rStream.currentToken())
615
0
        {
616
0
            case OPENING( M_TOKEN( t )):
617
0
            {
618
0
                isTagT = true;
619
0
                XmlStream::Tag rtag = m_rStream.ensureOpeningTag( M_TOKEN( t ));
620
0
                OUString sTagText = rtag.text;
621
0
                if (scrString == "double-struck")
622
0
                {
623
0
                    sTagText = SmOoxmlImport::handleSetString(sTagText);
624
0
                }
625
0
                if( rtag.attribute( OOX_TOKEN( xml, space )) != "preserve" )
626
0
                    sTagText = o3tl::trim(sTagText);
627
0
                text.append(sTagText);
628
0
                m_rStream.ensureClosingTag( M_TOKEN( t ));
629
0
                break;
630
0
            }
631
0
            default:
632
0
                m_rStream.handleUnexpectedTag();
633
0
                break;
634
0
        }
635
0
    }
636
0
    m_rStream.ensureClosingTag( M_TOKEN( r ));
637
0
    if (scrString.isEmpty() && (normal || literal || isTagT))
638
0
    {
639
0
        return encloseOrEscapeLiteral(text.makeStringAndClear(), normal || literal);
640
0
    }
641
0
    return text.makeStringAndClear();
642
0
}
643
644
OUString SmOoxmlImport::handleSetString(const OUString& setOUstring)
645
0
{
646
0
    std::unordered_set<sal_Unicode> setList= {'C', 'N', 'Q', 'R', 'Z', 'c', 'n', 'q', 'r', 'z'};
647
0
    OUString result;
648
0
    for (sal_Int32 i = 0; i < setOUstring.getLength(); i++)
649
0
    {
650
0
        if (setList.contains(setOUstring[i]))
651
0
        {
652
0
            result += OUString::Concat(" set" ) + OUStringChar(setOUstring[i]) + OUString::Concat(" ");
653
0
        }
654
0
        else
655
0
            result += encloseOrEscapeLiteral(setOUstring.copy(i, 1), /*bForce=*/false);
656
0
    }
657
0
    return result;
658
0
}
659
660
OUString SmOoxmlImport::handleRad()
661
0
{
662
0
    m_rStream.ensureOpeningTag( M_TOKEN( rad ));
663
0
    bool degHide = false;
664
0
    if( m_rStream.checkOpeningTag( M_TOKEN( radPr )))
665
0
    {
666
0
        if( XmlStream::Tag degHideTag = m_rStream.checkOpeningTag( M_TOKEN( degHide )))
667
0
        {
668
0
            degHide = degHideTag.attribute( M_TOKEN( val ), degHide );
669
0
            m_rStream.ensureClosingTag( M_TOKEN( degHide ));
670
0
        }
671
0
        m_rStream.ensureClosingTag( M_TOKEN( radPr ));
672
0
    }
673
0
    OUString deg = readOMathArgInElement( M_TOKEN( deg ));
674
0
    OUString e = readOMathArgInElement( M_TOKEN( e ));
675
0
    m_rStream.ensureClosingTag( M_TOKEN( rad ));
676
0
    if( degHide )
677
0
        return "sqrt {" + e + "}";
678
0
    else
679
0
        return "nroot {" + deg + "} {" + e + "}";
680
0
}
681
682
OUString SmOoxmlImport::handleSpre()
683
0
{
684
0
    m_rStream.ensureOpeningTag( M_TOKEN( sPre ));
685
0
    OUString sub = readOMathArgInElement( M_TOKEN( sub ));
686
0
    OUString sup = readOMathArgInElement( M_TOKEN( sup ));
687
0
    OUString e = readOMathArgInElement( M_TOKEN( e ));
688
0
    m_rStream.ensureClosingTag( M_TOKEN( sPre ));
689
0
    return "{" + e + "} lsub {" + sub + "} lsup {" + sup + "}";
690
0
}
691
692
OUString SmOoxmlImport::handleSsub()
693
0
{
694
0
    m_rStream.ensureOpeningTag( M_TOKEN( sSub ));
695
0
    OUString e = readOMathArgInElement( M_TOKEN( e ));
696
0
    OUString sub = readOMathArgInElement( M_TOKEN( sub ));
697
0
    m_rStream.ensureClosingTag( M_TOKEN( sSub ));
698
0
    return "{" + e + "} rsub {" + sub + "}";
699
0
}
700
701
OUString SmOoxmlImport::handleSsubsup()
702
0
{
703
0
    m_rStream.ensureOpeningTag( M_TOKEN( sSubSup ));
704
0
    OUString e = readOMathArgInElement( M_TOKEN( e ));
705
0
    OUString sub = readOMathArgInElement( M_TOKEN( sub ));
706
0
    OUString sup = readOMathArgInElement( M_TOKEN( sup ));
707
0
    m_rStream.ensureClosingTag( M_TOKEN( sSubSup ));
708
0
    return "{" + e + "} rsub {" + sub + "} rsup {" + sup + "}";
709
0
}
710
711
OUString SmOoxmlImport::handleSsup()
712
0
{
713
0
    m_rStream.ensureOpeningTag( M_TOKEN( sSup ));
714
0
    OUString e = readOMathArgInElement( M_TOKEN( e ));
715
0
    OUString sup = readOMathArgInElement( M_TOKEN( sup ));
716
0
    m_rStream.ensureClosingTag( M_TOKEN( sSup ));
717
0
    if (sup == "'")
718
0
        return "{" + e + "} {" + sup + "}";
719
0
    return "{" + e + "} ^ {" + sup + "}";
720
0
}
721
722
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */