Coverage Report

Created: 2026-10-14 09:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/starmath/source/mathml/mathmlimport.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
 * This file incorporates work covered by the following license notice:
10
 *
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 .
18
 */
19
20
/*todo: Change characters and tcharacters to accumulate the characters together
21
into one string, xml parser hands them to us line by line rather than all in
22
one go*/
23
24
#include <com/sun/star/xml/sax/InputSource.hpp>
25
#include <com/sun/star/xml/sax/FastParser.hpp>
26
#include <com/sun/star/xml/sax/Parser.hpp>
27
#include <com/sun/star/xml/sax/SAXParseException.hpp>
28
#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
29
#include <com/sun/star/packages/WrongPasswordException.hpp>
30
#include <com/sun/star/packages/zip/ZipIOException.hpp>
31
#include <com/sun/star/beans/PropertyAttribute.hpp>
32
#include <com/sun/star/embed/ElementModes.hpp>
33
#include <com/sun/star/uno/Any.h>
34
#include <com/sun/star/task/XStatusIndicator.hpp>
35
36
#include <comphelper/fileformat.h>
37
#include <comphelper/genericpropertyset.hxx>
38
#include <comphelper/processfactory.hxx>
39
#include <comphelper/servicehelper.hxx>
40
#include <comphelper/propertysetinfo.hxx>
41
#include <rtl/character.hxx>
42
#include <sal/log.hxx>
43
#include <sfx2/frame.hxx>
44
#include <sfx2/docfile.hxx>
45
#include <sfx2/sfxsids.hrc>
46
#include <sfx2/sfxmodelfactory.hxx>
47
#include <osl/diagnose.h>
48
#include <sot/storage.hxx>
49
#include <svtools/sfxecode.hxx>
50
#include <svl/itemset.hxx>
51
#include <svl/stritem.hxx>
52
#include <unotools/streamwrap.hxx>
53
#include <sax/tools/converter.hxx>
54
#include <xmloff/DocumentSettingsContext.hxx>
55
#include <xmloff/xmlnamespace.hxx>
56
#include <xmloff/xmltoken.hxx>
57
#include <xmloff/xmluconv.hxx>
58
#include <xmloff/xmlmetai.hxx>
59
#include <svx/dialmgr.hxx>
60
#include <svx/strings.hrc>
61
#include <comphelper/diagnose_ex.hxx>
62
#include <o3tl/string_view.hxx>
63
64
#include <mathmlattr.hxx>
65
#include <xparsmlbase.hxx>
66
#include <mathmlimport.hxx>
67
#include <document.hxx>
68
#include <smdll.hxx>
69
#include <unomodel.hxx>
70
#include <utility.hxx>
71
#include <visitors.hxx>
72
#include <starmathdatabase.hxx>
73
#include <smmod.hxx>
74
#include <cfgitem.hxx>
75
76
using namespace ::com::sun::star::beans;
77
using namespace ::com::sun::star::document;
78
using namespace ::com::sun::star::lang;
79
using namespace ::com::sun::star::uno;
80
using namespace ::com::sun::star;
81
using namespace ::xmloff::token;
82
83
namespace
84
{
85
std::unique_ptr<SmNode> popOrZero(SmNodeStack& rStack)
86
0
{
87
0
    if (rStack.empty())
88
0
        return nullptr;
89
0
    auto pTmp = std::move(rStack.front());
90
0
    rStack.pop_front();
91
0
    return pTmp;
92
0
}
93
}
94
95
ErrCode SmXMLImportWrapper::Import(SfxMedium& rMedium)
96
0
{
97
0
    ErrCode nError = ERRCODE_SFX_DOLOADFAILED;
98
99
0
    const uno::Reference<uno::XComponentContext>& xContext(
100
0
        comphelper::getProcessComponentContext());
101
102
0
    OSL_ENSURE(m_xModel.is(), "XMLReader::Read: got no model");
103
104
    // try to get an XStatusIndicator from the Medium
105
0
    uno::Reference<task::XStatusIndicator> xStatusIndicator;
106
107
0
    bool bEmbedded = false;
108
0
    SmModel* pModel = m_xModel.get();
109
110
0
    SmDocShell* pDocShell = pModel ? static_cast<SmDocShell*>(pModel->GetObjectShell()) : nullptr;
111
0
    if (pDocShell)
112
0
    {
113
0
        OSL_ENSURE(pDocShell->GetMedium() == &rMedium, "different SfxMedium found");
114
115
0
        const SfxUnoAnyItem* pItem = rMedium.GetItemSet().GetItem(SID_PROGRESS_STATUSBAR_CONTROL);
116
0
        if (pItem)
117
0
            pItem->GetValue() >>= xStatusIndicator;
118
119
0
        if (SfxObjectCreateMode::EMBEDDED == pDocShell->GetCreateMode())
120
0
            bEmbedded = true;
121
0
    }
122
123
0
    static const comphelper::PropertyMapEntry aInfoMap[]
124
0
        = { { u"PrivateData"_ustr, 0, cppu::UnoType<XInterface>::get(),
125
0
              beans::PropertyAttribute::MAYBEVOID, 0 },
126
0
            { u"BaseURI"_ustr, 0, ::cppu::UnoType<OUString>::get(),
127
0
              beans::PropertyAttribute::MAYBEVOID, 0 },
128
0
            { u"StreamRelPath"_ustr, 0, ::cppu::UnoType<OUString>::get(),
129
0
              beans::PropertyAttribute::MAYBEVOID, 0 },
130
0
            { u"StreamName"_ustr, 0, ::cppu::UnoType<OUString>::get(),
131
0
              beans::PropertyAttribute::MAYBEVOID, 0 } };
132
0
    uno::Reference<beans::XPropertySet> xInfoSet(
133
0
        comphelper::GenericPropertySet_CreateInstance(new comphelper::PropertySetInfo(aInfoMap)));
134
135
    // Set base URI
136
0
    OUString const baseURI(rMedium.GetBaseURL());
137
    // needed for relative URLs; but it's OK to import e.g. MathML from the
138
    // clipboard without one
139
0
    SAL_INFO_IF(baseURI.isEmpty(), "starmath", "SmXMLImportWrapper: no base URL");
140
0
    xInfoSet->setPropertyValue(u"BaseURI"_ustr, Any(baseURI));
141
142
0
    sal_Int32 nSteps = 3;
143
0
    if (!(rMedium.IsStorage()))
144
0
        nSteps = 1;
145
146
0
    sal_Int32 nProgressRange(nSteps);
147
0
    if (xStatusIndicator.is())
148
0
    {
149
0
        xStatusIndicator->start(SvxResId(RID_SVXSTR_DOC_LOAD), nProgressRange);
150
0
    }
151
152
0
    nSteps = 0;
153
0
    if (xStatusIndicator.is())
154
0
        xStatusIndicator->setValue(nSteps++);
155
156
0
    if (rMedium.IsStorage())
157
0
    {
158
        // TODO/LATER: handle the case of embedded links gracefully
159
0
        if (bEmbedded) // && !rMedium.GetStorage()->IsRoot() )
160
0
        {
161
0
            OUString aName(u"dummyObjName"_ustr);
162
0
            const SfxStringItem* pDocHierarchItem
163
0
                = rMedium.GetItemSet().GetItem(SID_DOC_HIERARCHICALNAME);
164
0
            if (pDocHierarchItem)
165
0
                aName = pDocHierarchItem->GetValue();
166
167
0
            if (!aName.isEmpty())
168
0
            {
169
0
                xInfoSet->setPropertyValue(u"StreamRelPath"_ustr, Any(aName));
170
0
            }
171
0
        }
172
173
0
        bool bOASIS = (SotStorage::GetVersion(rMedium.GetStorage()) > SOFFICE_FILEFORMAT_60);
174
0
        if (xStatusIndicator.is())
175
0
            xStatusIndicator->setValue(nSteps++);
176
177
0
        auto nWarn
178
0
            = ReadThroughComponent(rMedium.GetStorage(), m_xModel, "meta.xml", xContext, xInfoSet,
179
0
                                   (bOASIS ? "com.sun.star.comp.Math.XMLOasisMetaImporter"
180
0
                                           : "com.sun.star.comp.Math.XMLMetaImporter"),
181
0
                                   m_bUseHTMLMLEntities);
182
183
0
        if (nWarn != ERRCODE_IO_BROKENPACKAGE)
184
0
        {
185
0
            if (xStatusIndicator.is())
186
0
                xStatusIndicator->setValue(nSteps++);
187
188
0
            nWarn = ReadThroughComponent(rMedium.GetStorage(), m_xModel, "settings.xml", xContext,
189
0
                                         xInfoSet,
190
0
                                         (bOASIS ? "com.sun.star.comp.Math.XMLOasisSettingsImporter"
191
0
                                                 : "com.sun.star.comp.Math.XMLSettingsImporter"),
192
0
                                         m_bUseHTMLMLEntities);
193
194
0
            if (nWarn != ERRCODE_IO_BROKENPACKAGE)
195
0
            {
196
0
                if (xStatusIndicator.is())
197
0
                    xStatusIndicator->setValue(nSteps++);
198
199
0
                nError = ReadThroughComponent(
200
0
                    rMedium.GetStorage(), m_xModel, "content.xml", xContext, xInfoSet,
201
0
                    "com.sun.star.comp.Math.XMLImporter", m_bUseHTMLMLEntities);
202
0
            }
203
0
            else
204
0
                nError = ERRCODE_IO_BROKENPACKAGE;
205
0
        }
206
0
        else
207
0
            nError = ERRCODE_IO_BROKENPACKAGE;
208
0
    }
209
0
    else
210
0
    {
211
0
        Reference<io::XInputStream> xInputStream
212
0
            = new utl::OInputStreamWrapper(rMedium.GetInStream());
213
214
0
        if (xStatusIndicator.is())
215
0
            xStatusIndicator->setValue(nSteps++);
216
217
0
        nError = ReadThroughComponent(xInputStream, m_xModel, xContext, xInfoSet,
218
0
                                      "com.sun.star.comp.Math.XMLImporter", false,
219
0
                                      m_bUseHTMLMLEntities);
220
0
    }
221
222
0
    if (xStatusIndicator.is())
223
0
        xStatusIndicator->end();
224
0
    return nError;
225
0
}
226
227
/// read a component (file + filter version)
228
ErrCode SmXMLImportWrapper::ReadThroughComponent(const Reference<io::XInputStream>& xInputStream,
229
                                                 const Reference<XComponent>& xModelComponent,
230
                                                 Reference<uno::XComponentContext> const& rxContext,
231
                                                 Reference<beans::XPropertySet> const& rPropSet,
232
                                                 const char* pFilterName, bool bEncrypted,
233
                                                 bool bUseHTMLMLEntities)
234
0
{
235
0
    ErrCode nError = ERRCODE_SFX_DOLOADFAILED;
236
0
    OSL_ENSURE(xInputStream.is(), "input stream missing");
237
0
    OSL_ENSURE(xModelComponent.is(), "document missing");
238
0
    OSL_ENSURE(rxContext.is(), "factory missing");
239
0
    OSL_ENSURE(nullptr != pFilterName, "I need a service name for the component!");
240
241
    // prepare ParserInputSource
242
0
    xml::sax::InputSource aParserInput;
243
0
    aParserInput.aInputStream = xInputStream;
244
245
0
    Sequence<Any> aArgs{ Any(rPropSet) };
246
247
    // get filter
248
0
    Reference<XInterface> xFilter
249
0
        = rxContext->getServiceManager()->createInstanceWithArgumentsAndContext(
250
0
            OUString::createFromAscii(pFilterName), aArgs, rxContext);
251
0
    SAL_WARN_IF(!xFilter, "starmath", "Can't instantiate filter component " << pFilterName);
252
0
    if (!xFilter.is())
253
0
        return nError;
254
255
    // connect model and filter
256
0
    Reference<XImporter> xImporter(xFilter, UNO_QUERY);
257
0
    xImporter->setTargetDocument(xModelComponent);
258
259
    // finally, parser the stream
260
0
    try
261
0
    {
262
0
        Reference<css::xml::sax::XFastParser> xFastParser(xFilter, UNO_QUERY);
263
0
        Reference<css::xml::sax::XFastDocumentHandler> xFastDocHandler(xFilter, UNO_QUERY);
264
0
        if (xFastParser)
265
0
        {
266
0
            if (bUseHTMLMLEntities)
267
0
                xFastParser->setCustomEntityNames(starmathdatabase::icustomMathmlHtmlEntities);
268
0
            xFastParser->parseStream(aParserInput);
269
0
        }
270
0
        else if (xFastDocHandler)
271
0
        {
272
0
            Reference<css::xml::sax::XFastParser> xParser
273
0
                = css::xml::sax::FastParser::create(rxContext);
274
0
            if (bUseHTMLMLEntities)
275
0
                xParser->setCustomEntityNames(starmathdatabase::icustomMathmlHtmlEntities);
276
0
            xParser->setFastDocumentHandler(xFastDocHandler);
277
0
            xParser->parseStream(aParserInput);
278
0
        }
279
0
        else
280
0
        {
281
0
            Reference<css::xml::sax::XDocumentHandler> xDocHandler(xFilter, UNO_QUERY);
282
0
            assert(xDocHandler);
283
0
            Reference<css::xml::sax::XParser> xParser = css::xml::sax::Parser::create(rxContext);
284
0
            xParser->setDocumentHandler(xDocHandler);
285
0
            xParser->parseStream(aParserInput);
286
0
        }
287
288
0
        auto pFilter = dynamic_cast<SmXMLImport*>(xFilter.get());
289
0
        if (pFilter && pFilter->GetSuccess())
290
0
            nError = ERRCODE_NONE;
291
0
    }
292
0
    catch (const xml::sax::SAXParseException& r)
293
0
    {
294
        // sax parser sends wrapped exceptions,
295
        // try to find the original one
296
0
        xml::sax::SAXException aSaxEx = *static_cast<const xml::sax::SAXException*>(&r);
297
0
        bool bTryChild = true;
298
299
0
        while (bTryChild)
300
0
        {
301
0
            xml::sax::SAXException aTmp;
302
0
            if (aSaxEx.WrappedException >>= aTmp)
303
0
                aSaxEx = std::move(aTmp);
304
0
            else
305
0
                bTryChild = false;
306
0
        }
307
308
0
        packages::zip::ZipIOException aBrokenPackage;
309
0
        if (aSaxEx.WrappedException >>= aBrokenPackage)
310
0
            return ERRCODE_IO_BROKENPACKAGE;
311
312
0
        if (bEncrypted)
313
0
            nError = ERRCODE_SFX_WRONGPASSWORD;
314
0
    }
315
0
    catch (const xml::sax::SAXException& r)
316
0
    {
317
0
        packages::zip::ZipIOException aBrokenPackage;
318
0
        if (r.WrappedException >>= aBrokenPackage)
319
0
            return ERRCODE_IO_BROKENPACKAGE;
320
321
0
        if (bEncrypted)
322
0
            nError = ERRCODE_SFX_WRONGPASSWORD;
323
0
    }
324
0
    catch (const packages::zip::ZipIOException&)
325
0
    {
326
0
        nError = ERRCODE_IO_BROKENPACKAGE;
327
0
    }
328
0
    catch (const io::IOException&)
329
0
    {
330
0
    }
331
0
    catch (const std::range_error&)
332
0
    {
333
0
    }
334
335
0
    return nError;
336
0
}
337
338
ErrCode SmXMLImportWrapper::ReadThroughComponent(const uno::Reference<embed::XStorage>& xStorage,
339
                                                 const Reference<XComponent>& xModelComponent,
340
                                                 const char* pStreamName,
341
                                                 Reference<uno::XComponentContext> const& rxContext,
342
                                                 Reference<beans::XPropertySet> const& rPropSet,
343
                                                 const char* pFilterName, bool bUseHTMLMLEntities)
344
0
{
345
0
    OSL_ENSURE(xStorage.is(), "Need storage!");
346
0
    OSL_ENSURE(nullptr != pStreamName, "Please, please, give me a name!");
347
348
    // open stream (and set parser input)
349
0
    OUString sStreamName = OUString::createFromAscii(pStreamName);
350
351
    // get input stream
352
0
    try
353
0
    {
354
0
        uno::Reference<io::XStream> xEventsStream
355
0
            = xStorage->openStreamElement(sStreamName, embed::ElementModes::READ);
356
357
        // determine if stream is encrypted or not
358
0
        uno::Reference<beans::XPropertySet> xProps(xEventsStream, uno::UNO_QUERY);
359
0
        Any aAny = xProps->getPropertyValue(u"Encrypted"_ustr);
360
0
        bool bEncrypted = false;
361
0
        if (aAny.getValueType() == cppu::UnoType<bool>::get())
362
0
            aAny >>= bEncrypted;
363
364
        // set Base URL
365
0
        if (rPropSet.is())
366
0
        {
367
0
            rPropSet->setPropertyValue(u"StreamName"_ustr, Any(sStreamName));
368
0
        }
369
370
0
        Reference<io::XInputStream> xStream = xEventsStream->getInputStream();
371
0
        return ReadThroughComponent(xStream, xModelComponent, rxContext, rPropSet, pFilterName,
372
0
                                    bEncrypted, bUseHTMLMLEntities);
373
0
    }
374
0
    catch (packages::WrongPasswordException&)
375
0
    {
376
0
        return ERRCODE_SFX_WRONGPASSWORD;
377
0
    }
378
0
    catch (packages::zip::ZipIOException&)
379
0
    {
380
0
        return ERRCODE_IO_BROKENPACKAGE;
381
0
    }
382
0
    catch (uno::Exception&)
383
0
    {
384
0
    }
385
386
0
    return ERRCODE_SFX_DOLOADFAILED;
387
0
}
388
389
SmXMLImport::SmXMLImport(const css::uno::Reference<css::uno::XComponentContext>& rContext,
390
                         OUString const& implementationName, SvXMLImportFlags nImportFlags)
391
0
    : SvXMLImport(rContext, implementationName, nImportFlags)
392
0
    , bSuccess(false)
393
0
    , nParseDepth(0)
394
0
    , mnSmSyntaxVersion(SmModule::get()->GetConfig()->GetDefaultSmSyntaxVersion())
395
0
{
396
0
}
397
398
extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
399
Math_XMLImporter_get_implementation(uno::XComponentContext* pCtx,
400
                                    uno::Sequence<uno::Any> const& /*rSeq*/)
401
0
{
402
0
    return cppu::acquire(
403
0
        new SmXMLImport(pCtx, u"com.sun.star.comp.Math.XMLImporter"_ustr, SvXMLImportFlags::ALL));
404
0
}
405
406
extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
407
Math_XMLOasisMetaImporter_get_implementation(uno::XComponentContext* pCtx,
408
                                             uno::Sequence<uno::Any> const& /*rSeq*/)
409
0
{
410
0
    return cppu::acquire(new SmXMLImport(pCtx, u"com.sun.star.comp.Math.XMLOasisMetaImporter"_ustr,
411
0
                                         SvXMLImportFlags::META));
412
0
}
413
414
extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
415
Math_XMLOasisSettingsImporter_get_implementation(uno::XComponentContext* pCtx,
416
                                                 uno::Sequence<uno::Any> const& /*rSeq*/)
417
0
{
418
0
    return cppu::acquire(new SmXMLImport(
419
0
        pCtx, u"com.sun.star.comp.Math.XMLOasisSettingsImporter"_ustr, SvXMLImportFlags::SETTINGS));
420
0
}
421
422
void SmXMLImport::endDocument()
423
0
{
424
    //Set the resulted tree into the SmDocShell where it belongs
425
0
    std::unique_ptr<SmNode> pTree = popOrZero(aNodeStack);
426
0
    if (pTree && pTree->GetType() == SmNodeType::Table)
427
0
    {
428
0
        uno::Reference<frame::XModel> xModel = GetModel();
429
0
        SmModel* pModel = dynamic_cast<SmModel*>(xModel.get());
430
431
0
        if (pModel)
432
0
        {
433
0
            SmDocShell* pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell());
434
0
            auto pTreeTmp = pTree.get();
435
0
            pDocShell->SetFormulaTree(static_cast<SmTableNode*>(pTree.release()));
436
0
            if (aText.isEmpty()) //If we picked up no annotation text
437
0
            {
438
                // Get text from imported formula
439
0
                SmNodeToTextVisitor tmpvisitor(pTreeTmp, aText);
440
0
            }
441
442
            // Convert symbol names
443
0
            AbstractSmParser* rParser = pDocShell->GetParser();
444
0
            bool bVal = rParser->IsImportSymbolNames();
445
0
            rParser->SetImportSymbolNames(true);
446
0
            auto pTmpTree = rParser->Parse(aText);
447
0
            aText = rParser->GetText();
448
0
            pTmpTree.reset();
449
0
            rParser->SetImportSymbolNames(bVal);
450
451
0
            pDocShell->SetText(aText);
452
0
            pDocShell->SetSmSyntaxVersion(mnSmSyntaxVersion);
453
0
        }
454
0
        OSL_ENSURE(pModel, "So there *was* a UNO problem after all");
455
456
0
        bSuccess = true;
457
0
    }
458
459
0
    SvXMLImport::endDocument();
460
0
}
461
462
namespace
463
{
464
class SmXMLImportContext : public SvXMLImportContext
465
{
466
public:
467
    SmXMLImportContext(SmXMLImport& rImport)
468
0
        : SvXMLImportContext(rImport)
469
0
    {
470
0
        GetSmImport().IncParseDepth();
471
0
    }
472
473
0
    virtual ~SmXMLImportContext() override { GetSmImport().DecParseDepth(); }
474
475
0
    SmXMLImport& GetSmImport() { return static_cast<SmXMLImport&>(GetImport()); }
476
477
    virtual void TCharacters(const OUString& /*rChars*/);
478
    virtual void SAL_CALL characters(const OUString& rChars) override;
479
    virtual void SAL_CALL startFastElement(
480
        sal_Int32 /*nElement*/,
481
        const css::uno::Reference<css::xml::sax::XFastAttributeList>& /*rAttrList*/) override
482
0
    {
483
0
        if (GetSmImport().TooDeep())
484
0
            throw std::range_error("too deep");
485
0
    }
486
};
487
}
488
489
0
void SmXMLImportContext::TCharacters(const OUString& /*rChars*/) {}
490
491
void SmXMLImportContext::characters(const OUString& rChars)
492
0
{
493
    /*
494
    Whitespace occurring within the content of token elements is "trimmed"
495
    from the ends (i.e. all whitespace at the beginning and end of the
496
    content is removed), and "collapsed" internally (i.e. each sequence of
497
    1 or more whitespace characters is replaced with one blank character).
498
    */
499
    //collapsing not done yet!
500
0
    const OUString aChars2 = rChars.trim();
501
0
    if (!aChars2.isEmpty())
502
0
        TCharacters(aChars2 /*.collapse()*/);
503
0
}
504
505
namespace
506
{
507
struct SmXMLContext_Helper
508
{
509
    sal_Int8 nIsBold;
510
    sal_Int8 nIsItalic;
511
    double nFontSize;
512
    OUString sFontFamily;
513
    OUString sColor;
514
515
    SmXMLImportContext& rContext;
516
517
    explicit SmXMLContext_Helper(SmXMLImportContext& rImport)
518
0
        : nIsBold(-1)
519
0
        , nIsItalic(-1)
520
0
        , nFontSize(0.0)
521
0
        , rContext(rImport)
522
0
    {
523
0
    }
524
525
    bool IsFontNodeNeeded() const;
526
    void RetrieveAttrs(const uno::Reference<xml::sax::XFastAttributeList>& xAttrList);
527
    void ApplyAttrs();
528
};
529
}
530
531
bool SmXMLContext_Helper::IsFontNodeNeeded() const
532
0
{
533
0
    return nIsBold != -1 || nIsItalic != -1 || nFontSize != 0.0 || !sFontFamily.isEmpty()
534
0
           || !sColor.isEmpty();
535
0
}
536
537
void SmXMLContext_Helper::RetrieveAttrs(
538
    const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
539
0
{
540
0
    bool bMvFound = false;
541
0
    for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
542
0
    {
543
        // sometimes they have namespace, sometimes not?
544
0
        switch (aIter.getToken() & TOKEN_MASK)
545
0
        {
546
0
            case XML_FONTWEIGHT:
547
0
                nIsBold = sal_Int8(IsXMLToken(aIter, XML_BOLD));
548
0
                break;
549
0
            case XML_FONTSTYLE:
550
0
                nIsItalic = sal_Int8(IsXMLToken(aIter, XML_ITALIC));
551
0
                break;
552
0
            case XML_FONTSIZE:
553
0
            case XML_MATHSIZE:
554
0
            {
555
0
                OUString sValue = aIter.toString();
556
0
                ::sax::Converter::convertDouble(nFontSize, sValue);
557
0
                rContext.GetSmImport().GetMM100UnitConverter().SetXMLMeasureUnit(
558
0
                    util::MeasureUnit::POINT);
559
0
                if (-1 == sValue.indexOf(GetXMLToken(XML_UNIT_PT)))
560
0
                {
561
0
                    if (-1 == sValue.indexOf('%'))
562
0
                        nFontSize = 0.0;
563
0
                    else
564
0
                    {
565
0
                        rContext.GetSmImport().GetMM100UnitConverter().SetXMLMeasureUnit(
566
0
                            util::MeasureUnit::PERCENT);
567
0
                    }
568
0
                }
569
0
                break;
570
0
            }
571
0
            case XML_FONTFAMILY:
572
0
                sFontFamily = aIter.toString();
573
0
                break;
574
0
            case XML_COLOR:
575
0
            case XML_MATHCOLOR:
576
0
                sColor = aIter.toString();
577
0
                break;
578
0
            case XML_MATHVARIANT:
579
0
                bMvFound = true;
580
0
                break;
581
0
            default:
582
0
                XMLOFF_WARN_UNKNOWN("starmath", aIter);
583
0
                break;
584
0
        }
585
0
    }
586
587
0
    if (bMvFound)
588
0
    {
589
        // Ignore deprecated attributes fontfamily, fontweight, and fontstyle
590
        // in favor of mathvariant, as specified in
591
        // <https://www.w3.org/TR/MathML3/chapter3.html#presm.deprecatt>.
592
0
        sFontFamily.clear();
593
0
        nIsBold = -1;
594
0
        nIsItalic = -1;
595
0
    }
596
0
}
597
598
void SmXMLContext_Helper::ApplyAttrs()
599
0
{
600
0
    SmNodeStack& rNodeStack = rContext.GetSmImport().GetNodeStack();
601
602
0
    if (!IsFontNodeNeeded())
603
0
        return;
604
605
0
    SmToken aToken;
606
0
    aToken.cMathChar = u""_ustr;
607
0
    aToken.nLevel = 5;
608
609
0
    if (nIsBold != -1)
610
0
    {
611
0
        if (nIsBold)
612
0
            aToken.eType = TBOLD;
613
0
        else
614
0
            aToken.eType = TNBOLD;
615
0
        std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(aToken));
616
0
        pFontNode->SetSubNodes(nullptr, popOrZero(rNodeStack));
617
0
        rNodeStack.push_front(std::move(pFontNode));
618
0
    }
619
0
    if (nIsItalic != -1)
620
0
    {
621
0
        if (nIsItalic)
622
0
            aToken.eType = TITALIC;
623
0
        else
624
0
            aToken.eType = TNITALIC;
625
0
        std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(aToken));
626
0
        pFontNode->SetSubNodes(nullptr, popOrZero(rNodeStack));
627
0
        rNodeStack.push_front(std::move(pFontNode));
628
0
    }
629
0
    if (nFontSize != 0.0)
630
0
    {
631
0
        aToken.eType = TSIZE;
632
0
        std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(aToken));
633
634
0
        if (util::MeasureUnit::PERCENT
635
0
            == rContext.GetSmImport().GetMM100UnitConverter().GetXMLMeasureUnit())
636
0
        {
637
0
            if (nFontSize < 100.00)
638
0
                pFontNode->SetSizeParameter(100.00 / nFontSize, FontSizeType::DIVIDE);
639
0
            else
640
0
                pFontNode->SetSizeParameter(nFontSize / 100.00, FontSizeType::MULTIPLY);
641
0
        }
642
0
        else
643
0
            pFontNode->SetSizeParameter(nFontSize, FontSizeType::ABSOLUT);
644
645
0
        pFontNode->SetSubNodes(nullptr, popOrZero(rNodeStack));
646
0
        rNodeStack.push_front(std::move(pFontNode));
647
0
    }
648
0
    if (!sColor.isEmpty())
649
0
    {
650
0
        SmColorTokenTableEntry aSmColorTokenTableEntry;
651
0
        aSmColorTokenTableEntry = starmathdatabase::Identify_ColorName_HTML(sColor);
652
0
        if (aSmColorTokenTableEntry.eType == TRGB)
653
0
            aSmColorTokenTableEntry = starmathdatabase::Identify_Color_Parser(
654
0
                sal_uInt32(aSmColorTokenTableEntry.cColor));
655
0
        if (aSmColorTokenTableEntry.eType != TERROR)
656
0
        {
657
0
            aToken = aSmColorTokenTableEntry;
658
0
            std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(aToken));
659
0
            pFontNode->SetSubNodes(nullptr, popOrZero(rNodeStack));
660
0
            rNodeStack.push_front(std::move(pFontNode));
661
0
        }
662
        // If not known, not implemented yet. Giving up.
663
0
    }
664
0
    if (sFontFamily.isEmpty())
665
0
        return;
666
667
0
    if (sFontFamily.equalsIgnoreAsciiCase(GetXMLToken(XML_FIXED)))
668
0
        aToken.eType = TFIXED;
669
0
    else if (sFontFamily.equalsIgnoreAsciiCase("sans"))
670
0
        aToken.eType = TSANS;
671
0
    else if (sFontFamily.equalsIgnoreAsciiCase("serif"))
672
0
        aToken.eType = TSERIF;
673
0
    else //Just give up, we need to extend our font mechanism to be
674
        //more general
675
0
        return;
676
677
0
    aToken.aText = sFontFamily;
678
0
    std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(aToken));
679
0
    pFontNode->SetSubNodes(nullptr, popOrZero(rNodeStack));
680
0
    rNodeStack.push_front(std::move(pFontNode));
681
0
}
682
683
namespace
684
{
685
class SmXMLTokenAttrHelper
686
{
687
    SmXMLImportContext& mrContext;
688
    MathMLMathvariantValue meMv;
689
    bool mbMvFound;
690
691
public:
692
    SmXMLTokenAttrHelper(SmXMLImportContext& rContext)
693
0
        : mrContext(rContext)
694
0
        , meMv(MathMLMathvariantValue::Normal)
695
0
        , mbMvFound(false)
696
0
    {
697
0
    }
698
699
    void RetrieveAttrs(const uno::Reference<xml::sax::XFastAttributeList>& xAttrList);
700
    void ApplyAttrs(MathMLMathvariantValue eDefaultMv);
701
};
702
}
703
704
void SmXMLTokenAttrHelper::RetrieveAttrs(
705
    const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
706
0
{
707
0
    for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
708
0
    {
709
0
        OUString sValue = aIter.toString();
710
0
        switch (aIter.getToken())
711
0
        {
712
0
            case XML_MATHVARIANT:
713
0
                if (!GetMathMLMathvariantValue(sValue, meMv))
714
0
                    SAL_WARN("starmath", "failed to recognize mathvariant: " << sValue);
715
0
                mbMvFound = true;
716
0
                break;
717
0
            default:
718
0
                XMLOFF_WARN_UNKNOWN("starmath", aIter);
719
0
                break;
720
0
        }
721
0
    }
722
0
}
723
724
void SmXMLTokenAttrHelper::ApplyAttrs(MathMLMathvariantValue eDefaultMv)
725
0
{
726
0
    assert(eDefaultMv == MathMLMathvariantValue::Normal
727
0
           || eDefaultMv == MathMLMathvariantValue::Italic);
728
729
0
    std::vector<SmTokenType> vVariant;
730
0
    MathMLMathvariantValue eMv = mbMvFound ? meMv : eDefaultMv;
731
0
    switch (eMv)
732
0
    {
733
0
        case MathMLMathvariantValue::Normal:
734
0
            vVariant.push_back(TNITALIC);
735
0
            break;
736
0
        case MathMLMathvariantValue::Bold:
737
0
        case MathMLMathvariantValue::BoldFraktur:
738
            // TODO: Fraktur
739
0
        case MathMLMathvariantValue::BoldScript:
740
            // TODO: Script
741
0
            vVariant.push_back(TBOLD);
742
0
            break;
743
0
        case MathMLMathvariantValue::Italic:
744
            // nothing to do
745
0
            break;
746
0
        case MathMLMathvariantValue::BoldItalic:
747
0
            vVariant.push_back(TITALIC);
748
0
            vVariant.push_back(TBOLD);
749
0
            break;
750
0
        case MathMLMathvariantValue::DoubleStruck:
751
            // TODO
752
0
            break;
753
0
        case MathMLMathvariantValue::Script:
754
            // TODO
755
0
            break;
756
0
        case MathMLMathvariantValue::Fraktur:
757
            // TODO
758
0
            break;
759
0
        case MathMLMathvariantValue::SansSerif:
760
0
            vVariant.push_back(TSANS);
761
0
            break;
762
0
        case MathMLMathvariantValue::BoldSansSerif:
763
0
            vVariant.push_back(TSANS);
764
0
            vVariant.push_back(TBOLD);
765
0
            break;
766
0
        case MathMLMathvariantValue::SansSerifItalic:
767
0
            vVariant.push_back(TITALIC);
768
0
            vVariant.push_back(TSANS);
769
0
            break;
770
0
        case MathMLMathvariantValue::SansSerifBoldItalic:
771
0
            vVariant.push_back(TITALIC);
772
0
            vVariant.push_back(TBOLD);
773
0
            vVariant.push_back(TSANS);
774
0
            break;
775
0
        case MathMLMathvariantValue::Monospace:
776
0
            vVariant.push_back(TFIXED);
777
0
            break;
778
0
        case MathMLMathvariantValue::Initial:
779
0
        case MathMLMathvariantValue::Tailed:
780
0
        case MathMLMathvariantValue::Looped:
781
0
        case MathMLMathvariantValue::Stretched:
782
            // TODO
783
0
            break;
784
0
    }
785
0
    if (vVariant.empty())
786
0
        return;
787
0
    SmNodeStack& rNodeStack = mrContext.GetSmImport().GetNodeStack();
788
0
    for (auto eType : vVariant)
789
0
    {
790
0
        SmToken aToken;
791
0
        aToken.eType = eType;
792
0
        aToken.cMathChar = u""_ustr;
793
0
        aToken.nLevel = 5;
794
0
        std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(aToken));
795
0
        pFontNode->SetSubNodes(nullptr, popOrZero(rNodeStack));
796
0
        rNodeStack.push_front(std::move(pFontNode));
797
0
    }
798
0
}
799
800
namespace
801
{
802
class SmXMLDocContext_Impl : public SmXMLImportContext
803
{
804
public:
805
    SmXMLDocContext_Impl(SmXMLImport& rImport)
806
0
        : SmXMLImportContext(rImport)
807
0
    {
808
0
    }
809
810
    virtual uno::Reference<xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
811
        sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
812
813
    void SAL_CALL endFastElement(sal_Int32 nElement) override;
814
};
815
816
/*avert the gaze from the originator*/
817
class SmXMLRowContext_Impl : public SmXMLDocContext_Impl
818
{
819
protected:
820
    size_t nElementCount;
821
822
public:
823
    SmXMLRowContext_Impl(SmXMLImport& rImport)
824
0
        : SmXMLDocContext_Impl(rImport)
825
0
        , nElementCount(GetSmImport().GetNodeStack().size())
826
0
    {
827
0
    }
828
829
    virtual uno::Reference<xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
830
        sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
831
832
    uno::Reference<xml::sax::XFastContextHandler> StrictCreateChildContext(sal_Int32 nElement);
833
834
    virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
835
};
836
837
class SmXMLEncloseContext_Impl : public SmXMLRowContext_Impl
838
{
839
public:
840
    // TODO/LATER: convert <menclose notation="horizontalstrike"> into
841
    // "overstrike{}" and extend the Math syntax to support more notations
842
    SmXMLEncloseContext_Impl(SmXMLImport& rImport)
843
0
        : SmXMLRowContext_Impl(rImport)
844
0
    {
845
0
    }
846
847
    void SAL_CALL endFastElement(sal_Int32 nElement) override;
848
};
849
}
850
851
void SmXMLEncloseContext_Impl::endFastElement(sal_Int32 nElement)
852
0
{
853
    /*
854
    <menclose> accepts any number of arguments; if this number is not 1, its
855
    contents are treated as a single "inferred <mrow>" containing its
856
    arguments
857
    */
858
0
    if (GetSmImport().GetNodeStack().size() - nElementCount != 1)
859
0
        SmXMLRowContext_Impl::endFastElement(nElement);
860
0
}
861
862
namespace
863
{
864
class SmXMLFracContext_Impl : public SmXMLRowContext_Impl
865
{
866
public:
867
    // TODO/LATER: convert <mfrac bevelled="true"> into "wideslash{}{}"
868
    SmXMLFracContext_Impl(SmXMLImport& rImport)
869
0
        : SmXMLRowContext_Impl(rImport)
870
0
    {
871
0
    }
872
873
    void SAL_CALL endFastElement(sal_Int32 nElement) override;
874
};
875
876
class SmXMLSqrtContext_Impl : public SmXMLRowContext_Impl
877
{
878
public:
879
    SmXMLSqrtContext_Impl(SmXMLImport& rImport)
880
0
        : SmXMLRowContext_Impl(rImport)
881
0
    {
882
0
    }
883
884
    void SAL_CALL endFastElement(sal_Int32 nElement) override;
885
};
886
887
class SmXMLRootContext_Impl : public SmXMLRowContext_Impl
888
{
889
public:
890
    SmXMLRootContext_Impl(SmXMLImport& rImport)
891
0
        : SmXMLRowContext_Impl(rImport)
892
0
    {
893
0
    }
894
895
    void SAL_CALL endFastElement(sal_Int32 nElement) override;
896
};
897
898
class SmXMLStyleContext_Impl : public SmXMLRowContext_Impl
899
{
900
protected:
901
    SmXMLContext_Helper aStyleHelper;
902
903
public:
904
    /*Right now the style tag is completely ignored*/
905
    SmXMLStyleContext_Impl(SmXMLImport& rImport)
906
0
        : SmXMLRowContext_Impl(rImport)
907
0
        , aStyleHelper(*this)
908
0
    {
909
0
    }
910
911
    void SAL_CALL endFastElement(sal_Int32 nElement) override;
912
    void SAL_CALL startFastElement(
913
        sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
914
};
915
}
916
917
void SmXMLStyleContext_Impl::startFastElement(
918
    sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
919
0
{
920
0
    aStyleHelper.RetrieveAttrs(xAttrList);
921
0
}
922
923
void SmXMLStyleContext_Impl::endFastElement(sal_Int32 nElement)
924
0
{
925
    /*
926
    <mstyle> accepts any number of arguments; if this number is not 1, its
927
    contents are treated as a single "inferred <mrow>" containing its
928
    arguments
929
    */
930
0
    SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
931
0
    if (rNodeStack.size() - nElementCount != 1)
932
0
        SmXMLRowContext_Impl::endFastElement(nElement);
933
0
    aStyleHelper.ApplyAttrs();
934
0
}
935
936
namespace
937
{
938
class SmXMLPaddedContext_Impl : public SmXMLRowContext_Impl
939
{
940
public:
941
    /*Right now the style tag is completely ignored*/
942
    SmXMLPaddedContext_Impl(SmXMLImport& rImport)
943
0
        : SmXMLRowContext_Impl(rImport)
944
0
    {
945
0
    }
946
947
    void SAL_CALL endFastElement(sal_Int32 nElement) override;
948
};
949
}
950
951
void SmXMLPaddedContext_Impl::endFastElement(sal_Int32 nElement)
952
0
{
953
    /*
954
    <mpadded> accepts any number of arguments; if this number is not 1, its
955
    contents are treated as a single "inferred <mrow>" containing its
956
    arguments
957
    */
958
0
    if (GetSmImport().GetNodeStack().size() - nElementCount != 1)
959
0
        SmXMLRowContext_Impl::endFastElement(nElement);
960
0
}
961
962
namespace
963
{
964
class SmXMLPhantomContext_Impl : public SmXMLRowContext_Impl
965
{
966
public:
967
    /*Right now the style tag is completely ignored*/
968
    SmXMLPhantomContext_Impl(SmXMLImport& rImport)
969
0
        : SmXMLRowContext_Impl(rImport)
970
0
    {
971
0
    }
972
973
    void SAL_CALL endFastElement(sal_Int32 nElement) override;
974
};
975
}
976
977
void SmXMLPhantomContext_Impl::endFastElement(sal_Int32 nElement)
978
0
{
979
    /*
980
    <mphantom> accepts any number of arguments; if this number is not 1, its
981
    contents are treated as a single "inferred <mrow>" containing its
982
    arguments
983
    */
984
0
    if (GetSmImport().GetNodeStack().size() - nElementCount != 1)
985
0
        SmXMLRowContext_Impl::endFastElement(nElement);
986
987
0
    SmToken aToken;
988
0
    aToken.cMathChar = u""_ustr;
989
0
    aToken.nLevel = 5;
990
0
    aToken.eType = TPHANTOM;
991
992
0
    std::unique_ptr<SmFontNode> pPhantom(new SmFontNode(aToken));
993
0
    SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
994
0
    pPhantom->SetSubNodes(nullptr, popOrZero(rNodeStack));
995
0
    rNodeStack.push_front(std::move(pPhantom));
996
0
}
997
998
namespace
999
{
1000
class SmXMLFencedContext_Impl : public SmXMLRowContext_Impl
1001
{
1002
protected:
1003
    OUString cBegin;
1004
    OUString cEnd;
1005
    bool bIsStretchy;
1006
1007
public:
1008
    SmXMLFencedContext_Impl(SmXMLImport& rImport)
1009
0
        : SmXMLRowContext_Impl(rImport)
1010
0
        , cBegin('(')
1011
0
        , cEnd(')')
1012
0
        , bIsStretchy(false)
1013
0
    {
1014
0
    }
1015
1016
    void SAL_CALL startFastElement(
1017
        sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
1018
    void SAL_CALL endFastElement(sal_Int32 nElement) override;
1019
};
1020
}
1021
1022
void SmXMLFencedContext_Impl::startFastElement(
1023
    sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
1024
0
{
1025
0
    for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
1026
0
    {
1027
0
        switch (aIter.getToken())
1028
0
        {
1029
            //temp, starmath cannot handle multichar brackets (I think)
1030
0
            case XML_OPEN:
1031
0
                cBegin = aIter.toString();
1032
0
                break;
1033
0
            case XML_CLOSE:
1034
0
                cEnd = aIter.toString();
1035
0
                break;
1036
0
            case XML_STRETCHY:
1037
0
                bIsStretchy = IsXMLToken(aIter, XML_TRUE);
1038
0
                break;
1039
0
            default:
1040
0
                XMLOFF_WARN_UNKNOWN("starmath", aIter);
1041
                /*Go to superclass*/
1042
0
                break;
1043
0
        }
1044
0
    }
1045
0
}
1046
1047
void SmXMLFencedContext_Impl::endFastElement(sal_Int32 /*nElement*/)
1048
0
{
1049
0
    SmToken aToken;
1050
0
    aToken.cMathChar = u""_ustr;
1051
0
    aToken.aText = ",";
1052
0
    aToken.nLevel = 5;
1053
1054
0
    std::unique_ptr<SmStructureNode> pSNode(new SmBraceNode(aToken));
1055
0
    if (bIsStretchy)
1056
0
        aToken = starmathdatabase::Identify_PrefixPostfix_SmXMLOperatorContext_Impl(cBegin);
1057
0
    else
1058
0
        aToken = starmathdatabase::Identify_Prefix_SmXMLOperatorContext_Impl(cBegin);
1059
0
    if (aToken.eType == TERROR)
1060
0
        aToken = SmToken(TLPARENT, MS_LPARENT, u"("_ustr, TG::LBrace, 5);
1061
0
    std::unique_ptr<SmNode> pLeft(new SmMathSymbolNode(aToken));
1062
0
    if (bIsStretchy)
1063
0
        aToken = starmathdatabase::Identify_PrefixPostfix_SmXMLOperatorContext_Impl(cEnd);
1064
0
    else
1065
0
        aToken = starmathdatabase::Identify_Postfix_SmXMLOperatorContext_Impl(cEnd);
1066
0
    if (aToken.eType == TERROR)
1067
0
        aToken = SmToken(TRPARENT, MS_RPARENT, u")"_ustr, TG::LBrace, 5);
1068
0
    std::unique_ptr<SmNode> pRight(new SmMathSymbolNode(aToken));
1069
1070
0
    SmNodeArray aRelationArray;
1071
0
    SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
1072
0
    aToken.cMathChar = u""_ustr;
1073
0
    aToken.eType = TIDENT;
1074
1075
0
    auto i = rNodeStack.size() - nElementCount;
1076
0
    if (rNodeStack.size() - nElementCount > 1)
1077
0
        i += rNodeStack.size() - 1 - nElementCount;
1078
0
    aRelationArray.resize(i);
1079
0
    while (rNodeStack.size() > nElementCount)
1080
0
    {
1081
0
        auto pNode = std::move(rNodeStack.front());
1082
0
        rNodeStack.pop_front();
1083
0
        aRelationArray[--i] = pNode.release();
1084
0
        if (i > 1 && rNodeStack.size() > 1)
1085
0
            aRelationArray[--i] = new SmGlyphSpecialNode(aToken);
1086
0
    }
1087
1088
0
    SmToken aDummy;
1089
0
    std::unique_ptr<SmStructureNode> pBody(new SmExpressionNode(aDummy));
1090
0
    pBody->SetSubNodes(std::move(aRelationArray));
1091
1092
0
    pSNode->SetSubNodes(std::move(pLeft), std::move(pBody), std::move(pRight));
1093
    // mfenced is always scalable. Stretchy keyword is not official, but in case of been in there
1094
    // can be used as a hint.
1095
0
    pSNode->SetScaleMode(SmScaleMode::Height);
1096
0
    GetSmImport().GetNodeStack().push_front(std::move(pSNode));
1097
0
}
1098
1099
namespace
1100
{
1101
class SmXMLErrorContext_Impl : public SmXMLRowContext_Impl
1102
{
1103
public:
1104
    SmXMLErrorContext_Impl(SmXMLImport& rImport)
1105
0
        : SmXMLRowContext_Impl(rImport)
1106
0
    {
1107
0
    }
1108
1109
    void SAL_CALL endFastElement(sal_Int32 nElement) override;
1110
};
1111
}
1112
1113
void SmXMLErrorContext_Impl::endFastElement(sal_Int32 /*nElement*/)
1114
0
{
1115
    /*Right now the error tag is completely ignored, what
1116
     can I do with it in starmath, ?, maybe we need a
1117
     report window ourselves, do a test for validity of
1118
     the xml input, use mirrors, and then generate
1119
     the markup inside the merror with a big red colour
1120
     of something. For now just throw them all away.
1121
     */
1122
0
    SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
1123
0
    while (rNodeStack.size() > nElementCount)
1124
0
    {
1125
0
        rNodeStack.pop_front();
1126
0
    }
1127
0
}
1128
1129
namespace
1130
{
1131
class SmXMLNumberContext_Impl : public SmXMLImportContext
1132
{
1133
protected:
1134
    SmToken aToken;
1135
1136
public:
1137
    SmXMLNumberContext_Impl(SmXMLImport& rImport)
1138
0
        : SmXMLImportContext(rImport)
1139
0
    {
1140
0
        aToken.cMathChar = u""_ustr;
1141
0
        aToken.nLevel = 5;
1142
0
        aToken.eType = TNUMBER;
1143
0
    }
1144
1145
    virtual void TCharacters(const OUString& rChars) override;
1146
1147
    void SAL_CALL endFastElement(sal_Int32 nElement) override;
1148
};
1149
}
1150
1151
0
void SmXMLNumberContext_Impl::TCharacters(const OUString& rChars) { aToken.aText = rChars; }
1152
1153
void SmXMLNumberContext_Impl::endFastElement(sal_Int32)
1154
0
{
1155
0
    GetSmImport().GetNodeStack().push_front(std::make_unique<SmTextNode>(aToken, FNT_NUMBER));
1156
0
}
1157
1158
namespace
1159
{
1160
class SmXMLAnnotationContext_Impl : public SmXMLImportContext
1161
{
1162
    sal_uInt8 mnStarMathVersion;
1163
1164
public:
1165
    SmXMLAnnotationContext_Impl(SmXMLImport& rImport)
1166
0
        : SmXMLImportContext(rImport)
1167
0
        , mnStarMathVersion(0)
1168
0
    {
1169
0
    }
1170
1171
    void SAL_CALL characters(const OUString& rChars) override;
1172
1173
    void SAL_CALL startFastElement(
1174
        sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
1175
};
1176
}
1177
1178
void SmXMLAnnotationContext_Impl::startFastElement(
1179
    sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
1180
0
{
1181
0
    for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
1182
0
    {
1183
        // sometimes they have namespace, sometimes not?
1184
0
        switch (aIter.getToken() & TOKEN_MASK)
1185
0
        {
1186
0
            case XML_ENCODING:
1187
0
                mnStarMathVersion
1188
0
                    = aIter.toView() == "StarMath 5.0" ? 5 : aIter.toView() == "StarMath 6" ? 6 : 0;
1189
0
                break;
1190
0
            default:
1191
0
                XMLOFF_WARN_UNKNOWN("starmath", aIter);
1192
0
                break;
1193
0
        }
1194
0
    }
1195
0
}
1196
1197
void SmXMLAnnotationContext_Impl::characters(const OUString& rChars)
1198
0
{
1199
0
    if (mnStarMathVersion)
1200
0
    {
1201
0
        GetSmImport().SetText(GetSmImport().GetText() + rChars);
1202
0
        GetSmImport().SetSmSyntaxVersion(mnStarMathVersion);
1203
0
    }
1204
0
}
1205
1206
namespace
1207
{
1208
class SmXMLTextContext_Impl : public SmXMLImportContext
1209
{
1210
protected:
1211
    SmToken aToken;
1212
1213
public:
1214
    SmXMLTextContext_Impl(SmXMLImport& rImport)
1215
0
        : SmXMLImportContext(rImport)
1216
0
    {
1217
0
        aToken.cMathChar = u""_ustr;
1218
0
        aToken.nLevel = 5;
1219
0
        aToken.eType = TTEXT;
1220
0
    }
1221
1222
    virtual void TCharacters(const OUString& rChars) override;
1223
1224
    void SAL_CALL endFastElement(sal_Int32 nElement) override;
1225
};
1226
}
1227
1228
0
void SmXMLTextContext_Impl::TCharacters(const OUString& rChars) { aToken.aText = rChars; }
1229
1230
void SmXMLTextContext_Impl::endFastElement(sal_Int32)
1231
0
{
1232
0
    GetSmImport().GetNodeStack().push_front(std::make_unique<SmTextNode>(aToken, FNT_TEXT));
1233
0
}
1234
1235
namespace
1236
{
1237
class SmXMLStringContext_Impl : public SmXMLImportContext
1238
{
1239
protected:
1240
    SmToken aToken;
1241
1242
public:
1243
    SmXMLStringContext_Impl(SmXMLImport& rImport)
1244
0
        : SmXMLImportContext(rImport)
1245
0
    {
1246
0
        aToken.cMathChar = u""_ustr;
1247
0
        aToken.nLevel = 5;
1248
0
        aToken.eType = TTEXT;
1249
0
    }
1250
1251
    virtual void TCharacters(const OUString& rChars) override;
1252
1253
    void SAL_CALL endFastElement(sal_Int32 nElement) override;
1254
};
1255
}
1256
1257
void SmXMLStringContext_Impl::TCharacters(const OUString& rChars)
1258
0
{
1259
    /*
1260
    The content of <ms> elements should be rendered with visible "escaping" of
1261
    certain characters in the content, including at least "double quote"
1262
    itself, and preferably whitespace other than individual blanks. The intent
1263
    is for the viewer to see that the expression is a string literal, and to
1264
    see exactly which characters form its content. For example, <ms>double
1265
    quote is "</ms> might be rendered as "double quote is \"".
1266
1267
    Obviously this isn't fully done here.
1268
    */
1269
0
    aToken.aText = "\"" + rChars + "\"";
1270
0
}
1271
1272
void SmXMLStringContext_Impl::endFastElement(sal_Int32)
1273
0
{
1274
0
    GetSmImport().GetNodeStack().push_front(std::make_unique<SmTextNode>(aToken, FNT_FIXED));
1275
0
}
1276
1277
namespace
1278
{
1279
class SmXMLIdentifierContext_Impl : public SmXMLImportContext
1280
{
1281
    SmXMLTokenAttrHelper maTokenAttrHelper;
1282
    SmXMLContext_Helper aStyleHelper;
1283
    SmToken aToken;
1284
1285
public:
1286
    SmXMLIdentifierContext_Impl(SmXMLImport& rImport)
1287
0
        : SmXMLImportContext(rImport)
1288
0
        , maTokenAttrHelper(*this)
1289
0
        , aStyleHelper(*this)
1290
0
    {
1291
0
        aToken.cMathChar = u""_ustr;
1292
0
        aToken.nLevel = 5;
1293
0
        aToken.eType = TIDENT;
1294
0
    }
1295
1296
    void TCharacters(const OUString& rChars) override;
1297
    void SAL_CALL
1298
    startFastElement(sal_Int32 /*nElement*/,
1299
                     const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override
1300
0
    {
1301
0
        maTokenAttrHelper.RetrieveAttrs(xAttrList);
1302
0
        aStyleHelper.RetrieveAttrs(xAttrList);
1303
0
    };
1304
    void SAL_CALL endFastElement(sal_Int32 nElement) override;
1305
};
1306
}
1307
1308
void SmXMLIdentifierContext_Impl::endFastElement(sal_Int32)
1309
0
{
1310
0
    std::unique_ptr<SmTextNode> pNode;
1311
    //we will handle identifier italic/normal here instead of with a standalone
1312
    //font node
1313
0
    if (((aStyleHelper.nIsItalic == -1) && (aToken.aText.getLength() > 1))
1314
0
        || ((aStyleHelper.nIsItalic == 0) && (aToken.aText.getLength() == 1)))
1315
0
    {
1316
0
        pNode.reset(new SmTextNode(aToken, FNT_FUNCTION));
1317
0
        pNode->GetFont().SetItalic(ITALIC_NONE);
1318
0
        aStyleHelper.nIsItalic = -1;
1319
0
    }
1320
0
    else
1321
0
        pNode.reset(new SmTextNode(aToken, FNT_VARIABLE));
1322
0
    if (aStyleHelper.nIsItalic != -1)
1323
0
    {
1324
0
        if (aStyleHelper.nIsItalic)
1325
0
            pNode->GetFont().SetItalic(ITALIC_NORMAL);
1326
0
        else
1327
0
            pNode->GetFont().SetItalic(ITALIC_NONE);
1328
0
        aStyleHelper.nIsItalic = -1;
1329
0
    }
1330
0
    GetSmImport().GetNodeStack().push_front(std::move(pNode));
1331
0
    aStyleHelper.ApplyAttrs();
1332
1333
0
    maTokenAttrHelper.ApplyAttrs((aToken.aText.getLength() == 1) ? MathMLMathvariantValue::Italic
1334
0
                                                                 : MathMLMathvariantValue::Normal);
1335
0
}
1336
1337
0
void SmXMLIdentifierContext_Impl::TCharacters(const OUString& rChars) { aToken.aText = rChars; }
1338
1339
namespace
1340
{
1341
class SmXMLOperatorContext_Impl : public SmXMLImportContext
1342
{
1343
    SmXMLTokenAttrHelper maTokenAttrHelper;
1344
    bool bIsStretchy;
1345
    bool bIsFenced;
1346
    bool isPrefix;
1347
    bool isInfix;
1348
    bool isPostfix;
1349
    SmToken aToken;
1350
1351
public:
1352
    SmXMLOperatorContext_Impl(SmXMLImport& rImport)
1353
0
        : SmXMLImportContext(rImport)
1354
0
        , maTokenAttrHelper(*this)
1355
0
        , bIsStretchy(false)
1356
0
        , bIsFenced(false)
1357
0
        , isPrefix(false)
1358
0
        , isInfix(false)
1359
0
        , isPostfix(false)
1360
0
    {
1361
0
        aToken.eType = TSPECIAL;
1362
0
        aToken.nLevel = 5;
1363
0
    }
1364
1365
    void TCharacters(const OUString& rChars) override;
1366
    void SAL_CALL startFastElement(
1367
        sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
1368
    void SAL_CALL endFastElement(sal_Int32 nElement) override;
1369
};
1370
}
1371
1372
void SmXMLOperatorContext_Impl::TCharacters(const OUString& rChars)
1373
0
{
1374
0
    aToken.setChar(rChars);
1375
0
    SmToken bToken;
1376
0
    if (bIsFenced)
1377
0
    {
1378
0
        if (isPrefix)
1379
0
            bToken = starmathdatabase::Identify_Prefix_SmXMLOperatorContext_Impl(aToken.cMathChar);
1380
0
        else if (isInfix)
1381
0
            bToken = SmToken(TMLINE, MS_VERTLINE, u"mline"_ustr, TG::NONE, 0);
1382
0
        else if (isPostfix)
1383
0
            bToken = starmathdatabase::Identify_Postfix_SmXMLOperatorContext_Impl(aToken.cMathChar);
1384
0
        else
1385
0
            bToken = starmathdatabase::Identify_PrefixPostfix_SmXMLOperatorContext_Impl(
1386
0
                aToken.cMathChar);
1387
0
    }
1388
0
    else
1389
0
        bToken
1390
0
            = starmathdatabase::Identify_SmXMLOperatorContext_Impl(aToken.cMathChar, bIsStretchy);
1391
0
    if (bToken.eType != TERROR)
1392
0
        aToken = std::move(bToken);
1393
0
}
1394
1395
void SmXMLOperatorContext_Impl::endFastElement(sal_Int32)
1396
0
{
1397
0
    std::unique_ptr<SmMathSymbolNode> pNode(new SmMathSymbolNode(aToken));
1398
    //For stretchy scaling the scaling must be retrieved from this node
1399
    //and applied to the expression itself so as to get the expression
1400
    //to scale the operator to the height of the expression itself
1401
0
    if (bIsStretchy)
1402
0
        pNode->SetScaleMode(SmScaleMode::Height);
1403
0
    GetSmImport().GetNodeStack().push_front(std::move(pNode));
1404
1405
    // TODO: apply to non-alphabetic characters too
1406
0
    if (rtl::isAsciiAlpha(aToken.cMathChar[0]))
1407
0
        maTokenAttrHelper.ApplyAttrs(MathMLMathvariantValue::Normal);
1408
0
}
1409
1410
void SmXMLOperatorContext_Impl::startFastElement(
1411
    sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
1412
0
{
1413
0
    maTokenAttrHelper.RetrieveAttrs(xAttrList);
1414
1415
0
    for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
1416
0
    {
1417
0
        switch (aIter.getToken())
1418
0
        {
1419
0
            case XML_STRETCHY:
1420
0
                bIsStretchy = IsXMLToken(aIter, XML_TRUE);
1421
0
                break;
1422
0
            case XML_FENCE:
1423
0
                bIsFenced = IsXMLToken(aIter, XML_TRUE);
1424
0
                break;
1425
0
            case XML_FORM:
1426
0
                isPrefix = IsXMLToken(aIter, XML_PREFIX); // <
1427
0
                isInfix = IsXMLToken(aIter, XML_INFIX); // |
1428
0
                isPostfix = IsXMLToken(aIter, XML_POSTFIX); // >
1429
0
                break;
1430
0
            default:
1431
0
                XMLOFF_WARN_UNKNOWN("starmath", aIter);
1432
0
                break;
1433
0
        }
1434
0
    }
1435
0
}
1436
1437
namespace
1438
{
1439
class SmXMLSpaceContext_Impl : public SmXMLImportContext
1440
{
1441
public:
1442
    SmXMLSpaceContext_Impl(SmXMLImport& rImport)
1443
0
        : SmXMLImportContext(rImport)
1444
0
    {
1445
0
    }
1446
1447
    void SAL_CALL startFastElement(
1448
        sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
1449
};
1450
1451
bool lcl_CountBlanks(const MathMLAttributeLengthValue& rLV, sal_Int32* pWide, sal_Int32* pNarrow)
1452
0
{
1453
0
    assert(pWide);
1454
0
    assert(pNarrow);
1455
0
    if (rLV.fNumber == 0)
1456
0
    {
1457
0
        *pWide = *pNarrow = 0;
1458
0
        return true;
1459
0
    }
1460
    // TODO: honor other units than em
1461
0
    if (rLV.eUnit != MathMLLengthUnit::Em)
1462
0
        return false;
1463
0
    if (rLV.fNumber < 0)
1464
0
        return false;
1465
0
    auto aWide = rLV.fNumber / 2;
1466
0
    if (aWide > SAL_MAX_INT32)
1467
0
    {
1468
0
        SAL_WARN("starmath", "mspace width too large: " << rLV.fNumber);
1469
0
        return false;
1470
0
    }
1471
0
    sal_Int32 nWide = aWide;
1472
0
    sal_Int32 nNarrow = (rLV.fNumber - nWide * 2.0) * 2.0; // 0, 1, 2, or 3
1473
0
    *pWide = nWide;
1474
0
    *pNarrow = nNarrow;
1475
0
    return true;
1476
0
}
1477
}
1478
1479
void SmXMLSpaceContext_Impl::startFastElement(
1480
    sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
1481
0
{
1482
    // There is no syntax in Math to specify blank nodes of arbitrary size yet.
1483
0
    MathMLAttributeLengthValue aLV;
1484
0
    sal_Int32 nWide = 0, nNarrow = 0;
1485
1486
0
    for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
1487
0
    {
1488
0
        OUString sValue = aIter.toString();
1489
0
        switch (aIter.getToken())
1490
0
        {
1491
0
            case XML_WIDTH:
1492
0
                if (!ParseMathMLAttributeLengthValue(o3tl::trim(sValue), aLV)
1493
0
                    || !lcl_CountBlanks(aLV, &nWide, &nNarrow))
1494
0
                    SAL_WARN("starmath", "ignore mspace's width: " << sValue);
1495
0
                break;
1496
0
            default:
1497
0
                XMLOFF_WARN_UNKNOWN("starmath", aIter);
1498
0
                break;
1499
0
        }
1500
0
    }
1501
0
    SmToken aToken;
1502
0
    aToken.eType = TBLANK;
1503
0
    aToken.cMathChar = u""_ustr;
1504
0
    aToken.nGroup = TG::Blank;
1505
0
    aToken.nLevel = 5;
1506
0
    std::unique_ptr<SmBlankNode> pBlank(new SmBlankNode(aToken));
1507
0
    if (nWide > 0)
1508
0
        pBlank->IncreaseBy(aToken, nWide);
1509
0
    if (nNarrow > 0)
1510
0
    {
1511
0
        aToken.eType = TSBLANK;
1512
0
        pBlank->IncreaseBy(aToken, nNarrow);
1513
0
    }
1514
0
    GetSmImport().GetNodeStack().push_front(std::move(pBlank));
1515
0
}
1516
1517
namespace
1518
{
1519
class SmXMLSubContext_Impl : public SmXMLRowContext_Impl
1520
{
1521
protected:
1522
    void GenericEndElement(SmTokenType eType, SmSubSup aSubSup);
1523
1524
public:
1525
    SmXMLSubContext_Impl(SmXMLImport& rImport)
1526
0
        : SmXMLRowContext_Impl(rImport)
1527
0
    {
1528
0
    }
1529
1530
0
    void SAL_CALL endFastElement(sal_Int32) override { GenericEndElement(TRSUB, RSUB); }
1531
};
1532
}
1533
1534
void SmXMLSubContext_Impl::GenericEndElement(SmTokenType eType, SmSubSup eSubSup)
1535
0
{
1536
    /*The <msub> element requires exactly 2 arguments.*/
1537
0
    const bool bNodeCheck = GetSmImport().GetNodeStack().size() - nElementCount == 2;
1538
0
    OSL_ENSURE(bNodeCheck, "Sub has not two arguments");
1539
0
    if (!bNodeCheck)
1540
0
        return;
1541
1542
0
    SmToken aToken;
1543
0
    aToken.cMathChar = u""_ustr;
1544
0
    aToken.eType = eType;
1545
0
    std::unique_ptr<SmSubSupNode> pNode(new SmSubSupNode(aToken));
1546
0
    SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
1547
1548
    // initialize subnodes array
1549
0
    SmNodeArray aSubNodes;
1550
0
    aSubNodes.resize(1 + SUBSUP_NUM_ENTRIES);
1551
0
    for (size_t i = 1; i < aSubNodes.size(); i++)
1552
0
        aSubNodes[i] = nullptr;
1553
1554
0
    aSubNodes[eSubSup + 1] = popOrZero(rNodeStack).release();
1555
0
    aSubNodes[0] = popOrZero(rNodeStack).release();
1556
0
    pNode->SetSubNodes(std::move(aSubNodes));
1557
0
    rNodeStack.push_front(std::move(pNode));
1558
0
}
1559
1560
namespace
1561
{
1562
class SmXMLSupContext_Impl : public SmXMLSubContext_Impl
1563
{
1564
public:
1565
    SmXMLSupContext_Impl(SmXMLImport& rImport)
1566
0
        : SmXMLSubContext_Impl(rImport)
1567
0
    {
1568
0
    }
1569
1570
0
    void SAL_CALL endFastElement(sal_Int32) override { GenericEndElement(TRSUP, RSUP); }
1571
};
1572
1573
class SmXMLSubSupContext_Impl : public SmXMLRowContext_Impl
1574
{
1575
protected:
1576
    void GenericEndElement(SmTokenType eType, SmSubSup aSub, SmSubSup aSup);
1577
1578
public:
1579
    SmXMLSubSupContext_Impl(SmXMLImport& rImport)
1580
0
        : SmXMLRowContext_Impl(rImport)
1581
0
    {
1582
0
    }
1583
1584
0
    void SAL_CALL endFastElement(sal_Int32) override { GenericEndElement(TRSUB, RSUB, RSUP); }
1585
};
1586
}
1587
1588
void SmXMLSubSupContext_Impl::GenericEndElement(SmTokenType eType, SmSubSup aSub, SmSubSup aSup)
1589
0
{
1590
    /*The <msub> element requires exactly 3 arguments.*/
1591
0
    const bool bNodeCheck = GetSmImport().GetNodeStack().size() - nElementCount == 3;
1592
0
    OSL_ENSURE(bNodeCheck, "SubSup has not three arguments");
1593
0
    if (!bNodeCheck)
1594
0
        return;
1595
1596
0
    SmToken aToken;
1597
0
    aToken.cMathChar = u""_ustr;
1598
0
    aToken.eType = eType;
1599
0
    std::unique_ptr<SmSubSupNode> pNode(new SmSubSupNode(aToken));
1600
0
    SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
1601
1602
    // initialize subnodes array
1603
0
    SmNodeArray aSubNodes;
1604
0
    aSubNodes.resize(1 + SUBSUP_NUM_ENTRIES);
1605
0
    for (size_t i = 1; i < aSubNodes.size(); i++)
1606
0
        aSubNodes[i] = nullptr;
1607
1608
0
    aSubNodes[aSup + 1] = popOrZero(rNodeStack).release();
1609
0
    aSubNodes[aSub + 1] = popOrZero(rNodeStack).release();
1610
0
    aSubNodes[0] = popOrZero(rNodeStack).release();
1611
0
    pNode->SetSubNodes(std::move(aSubNodes));
1612
0
    rNodeStack.push_front(std::move(pNode));
1613
0
}
1614
1615
namespace
1616
{
1617
class SmXMLUnderContext_Impl : public SmXMLSubContext_Impl
1618
{
1619
protected:
1620
    sal_Int16 nAttrCount;
1621
1622
public:
1623
    SmXMLUnderContext_Impl(SmXMLImport& rImport)
1624
0
        : SmXMLSubContext_Impl(rImport)
1625
0
        , nAttrCount(0)
1626
0
    {
1627
0
    }
1628
1629
    void SAL_CALL startFastElement(
1630
        sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
1631
    void SAL_CALL endFastElement(sal_Int32 nElement) override;
1632
    void HandleAccent();
1633
};
1634
}
1635
1636
void SmXMLUnderContext_Impl::startFastElement(
1637
    sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
1638
0
{
1639
0
    sax_fastparser::FastAttributeList& rAttribList
1640
0
        = sax_fastparser::castToFastAttributeList(xAttrList);
1641
0
    nAttrCount = rAttribList.getFastAttributeTokens().size();
1642
0
}
1643
1644
void SmXMLUnderContext_Impl::HandleAccent()
1645
0
{
1646
0
    const bool bNodeCheck = GetSmImport().GetNodeStack().size() - nElementCount == 2;
1647
0
    OSL_ENSURE(bNodeCheck, "Sub has not two arguments");
1648
0
    if (!bNodeCheck)
1649
0
        return;
1650
1651
    /*Just one special case for the underline thing*/
1652
0
    SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
1653
0
    std::unique_ptr<SmNode> pTest = popOrZero(rNodeStack);
1654
0
    SmToken aToken;
1655
0
    aToken.cMathChar = u""_ustr;
1656
0
    aToken.eType = TUNDERLINE;
1657
1658
0
    std::unique_ptr<SmNode> pFirst;
1659
0
    std::unique_ptr<SmStructureNode> pNode(new SmAttributeNode(aToken));
1660
0
    if ((pTest->GetToken().cMathChar[0] & 0x0FFF) == 0x0332)
1661
0
    {
1662
0
        pFirst.reset(new SmRectangleNode(aToken));
1663
0
    }
1664
0
    else
1665
0
        pFirst = std::move(pTest);
1666
1667
0
    std::unique_ptr<SmNode> pSecond = popOrZero(rNodeStack);
1668
0
    pNode->SetSubNodes(std::move(pFirst), std::move(pSecond));
1669
0
    pNode->SetScaleMode(SmScaleMode::Width);
1670
0
    rNodeStack.push_front(std::move(pNode));
1671
0
}
1672
1673
void SmXMLUnderContext_Impl::endFastElement(sal_Int32)
1674
0
{
1675
0
    if (!nAttrCount)
1676
0
        GenericEndElement(TCSUB, CSUB);
1677
0
    else
1678
0
        HandleAccent();
1679
0
}
1680
1681
namespace
1682
{
1683
class SmXMLOverContext_Impl : public SmXMLSubContext_Impl
1684
{
1685
protected:
1686
    sal_Int16 nAttrCount;
1687
1688
public:
1689
    SmXMLOverContext_Impl(SmXMLImport& rImport)
1690
0
        : SmXMLSubContext_Impl(rImport)
1691
0
        , nAttrCount(0)
1692
0
    {
1693
0
    }
1694
1695
    void SAL_CALL endFastElement(sal_Int32 nElement) override;
1696
    void SAL_CALL startFastElement(
1697
        sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
1698
    void HandleAccent();
1699
};
1700
}
1701
1702
void SmXMLOverContext_Impl::startFastElement(
1703
    sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
1704
0
{
1705
0
    sax_fastparser::FastAttributeList& rAttribList
1706
0
        = sax_fastparser::castToFastAttributeList(xAttrList);
1707
0
    nAttrCount = rAttribList.getFastAttributeTokens().size();
1708
0
}
1709
1710
void SmXMLOverContext_Impl::endFastElement(sal_Int32)
1711
0
{
1712
0
    if (!nAttrCount)
1713
0
        GenericEndElement(TCSUP, CSUP);
1714
0
    else
1715
0
        HandleAccent();
1716
0
}
1717
1718
void SmXMLOverContext_Impl::HandleAccent()
1719
0
{
1720
0
    const bool bNodeCheck = GetSmImport().GetNodeStack().size() - nElementCount == 2;
1721
0
    OSL_ENSURE(bNodeCheck, "Sub has not two arguments");
1722
0
    if (!bNodeCheck)
1723
0
        return;
1724
1725
0
    SmToken aToken;
1726
0
    aToken.cMathChar = u""_ustr;
1727
0
    aToken.eType = TACUTE;
1728
1729
0
    std::unique_ptr<SmAttributeNode> pNode(new SmAttributeNode(aToken));
1730
0
    SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
1731
1732
0
    std::unique_ptr<SmNode> pFirst = popOrZero(rNodeStack);
1733
0
    std::unique_ptr<SmNode> pSecond = popOrZero(rNodeStack);
1734
0
    pNode->SetSubNodes(std::move(pFirst), std::move(pSecond));
1735
0
    pNode->SetScaleMode(SmScaleMode::Width);
1736
0
    rNodeStack.push_front(std::move(pNode));
1737
0
}
1738
1739
namespace
1740
{
1741
class SmXMLUnderOverContext_Impl : public SmXMLSubSupContext_Impl
1742
{
1743
public:
1744
    SmXMLUnderOverContext_Impl(SmXMLImport& rImport)
1745
0
        : SmXMLSubSupContext_Impl(rImport)
1746
0
    {
1747
0
    }
1748
1749
0
    void SAL_CALL endFastElement(sal_Int32) override { GenericEndElement(TCSUB, CSUB, CSUP); }
1750
};
1751
1752
class SmXMLMultiScriptsContext_Impl : public SmXMLSubSupContext_Impl
1753
{
1754
    bool bHasPrescripts;
1755
1756
    void ProcessSubSupPairs(bool bIsPrescript);
1757
1758
public:
1759
    SmXMLMultiScriptsContext_Impl(SmXMLImport& rImport)
1760
0
        : SmXMLSubSupContext_Impl(rImport)
1761
0
        , bHasPrescripts(false)
1762
0
    {
1763
0
    }
1764
1765
    void SAL_CALL endFastElement(sal_Int32 nElement) override;
1766
    virtual uno::Reference<xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
1767
        sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
1768
};
1769
1770
class SmXMLNoneContext_Impl : public SmXMLImportContext
1771
{
1772
public:
1773
    SmXMLNoneContext_Impl(SmXMLImport& rImport)
1774
0
        : SmXMLImportContext(rImport)
1775
0
    {
1776
0
    }
1777
1778
    void SAL_CALL endFastElement(sal_Int32 nElement) override;
1779
};
1780
}
1781
1782
void SmXMLNoneContext_Impl::endFastElement(sal_Int32)
1783
0
{
1784
0
    SmToken aToken;
1785
0
    aToken.cMathChar = u""_ustr;
1786
0
    aToken.aText.clear();
1787
0
    aToken.nLevel = 5;
1788
0
    aToken.eType = TIDENT;
1789
0
    GetSmImport().GetNodeStack().push_front(std::make_unique<SmTextNode>(aToken, FNT_VARIABLE));
1790
0
}
1791
1792
namespace
1793
{
1794
class SmXMLPrescriptsContext_Impl : public SmXMLImportContext
1795
{
1796
public:
1797
    SmXMLPrescriptsContext_Impl(SmXMLImport& rImport)
1798
0
        : SmXMLImportContext(rImport)
1799
0
    {
1800
0
    }
1801
};
1802
1803
class SmXMLTableRowContext_Impl : public SmXMLRowContext_Impl
1804
{
1805
public:
1806
    SmXMLTableRowContext_Impl(SmXMLImport& rImport)
1807
0
        : SmXMLRowContext_Impl(rImport)
1808
0
    {
1809
0
    }
1810
1811
    virtual uno::Reference<xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
1812
        sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
1813
};
1814
1815
class SmXMLTableContext_Impl : public SmXMLTableRowContext_Impl
1816
{
1817
public:
1818
    SmXMLTableContext_Impl(SmXMLImport& rImport)
1819
0
        : SmXMLTableRowContext_Impl(rImport)
1820
0
    {
1821
0
    }
1822
1823
    void SAL_CALL endFastElement(sal_Int32 nElement) override;
1824
    virtual uno::Reference<xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
1825
        sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
1826
};
1827
1828
class SmXMLTableCellContext_Impl : public SmXMLRowContext_Impl
1829
{
1830
public:
1831
    SmXMLTableCellContext_Impl(SmXMLImport& rImport)
1832
0
        : SmXMLRowContext_Impl(rImport)
1833
0
    {
1834
0
    }
1835
};
1836
1837
class SmXMLAlignGroupContext_Impl : public SmXMLRowContext_Impl
1838
{
1839
public:
1840
    SmXMLAlignGroupContext_Impl(SmXMLImport& rImport)
1841
0
        : SmXMLRowContext_Impl(rImport)
1842
0
    {
1843
0
    }
1844
1845
    /*Don't do anything with alignment for now*/
1846
};
1847
1848
class SmXMLActionContext_Impl : public SmXMLRowContext_Impl
1849
{
1850
    size_t mnSelection; // 1-based
1851
1852
public:
1853
    SmXMLActionContext_Impl(SmXMLImport& rImport)
1854
0
        : SmXMLRowContext_Impl(rImport)
1855
0
        , mnSelection(1)
1856
0
    {
1857
0
    }
1858
1859
    void SAL_CALL startFastElement(
1860
        sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
1861
    void SAL_CALL endFastElement(sal_Int32 nElement) override;
1862
};
1863
1864
// NB: virtually inherit so we can multiply inherit properly
1865
//     in SmXMLFlatDocContext_Impl
1866
class SmXMLOfficeContext_Impl : public virtual SvXMLImportContext
1867
{
1868
public:
1869
    SmXMLOfficeContext_Impl(SmXMLImport& rImport)
1870
0
        : SvXMLImportContext(rImport)
1871
0
    {
1872
0
    }
Unexecuted instantiation: mathmlimport.cxx:(anonymous namespace)::SmXMLOfficeContext_Impl::SmXMLOfficeContext_Impl(SmXMLImport&)
Unexecuted instantiation: mathmlimport.cxx:(anonymous namespace)::SmXMLOfficeContext_Impl::SmXMLOfficeContext_Impl(SmXMLImport&)
1873
1874
    virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
1875
        sal_Int32 nElement,
1876
        const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
1877
};
1878
}
1879
1880
uno::Reference<xml::sax::XFastContextHandler> SmXMLOfficeContext_Impl::createFastChildContext(
1881
    sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& /*xAttrList*/)
1882
0
{
1883
0
    if (nElement == XML_ELEMENT(OFFICE, XML_META))
1884
0
    {
1885
0
        SAL_WARN("starmath",
1886
0
                 "XML_TOK_DOC_META: should not have come here, maybe document is invalid?");
1887
0
    }
1888
0
    else if (nElement == XML_ELEMENT(OFFICE, XML_SETTINGS))
1889
0
    {
1890
0
        return new XMLDocumentSettingsContext(GetImport());
1891
0
    }
1892
0
    return nullptr;
1893
0
}
1894
1895
namespace
1896
{
1897
// context for flat file xml format
1898
class SmXMLFlatDocContext_Impl : public SmXMLOfficeContext_Impl, public SvXMLMetaDocumentContext
1899
{
1900
public:
1901
    SmXMLFlatDocContext_Impl(SmXMLImport& i_rImport,
1902
                             const uno::Reference<document::XDocumentProperties>& i_xDocProps);
1903
1904
    virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
1905
        sal_Int32 nElement,
1906
        const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
1907
};
1908
}
1909
1910
SmXMLFlatDocContext_Impl::SmXMLFlatDocContext_Impl(
1911
    SmXMLImport& i_rImport, const uno::Reference<document::XDocumentProperties>& i_xDocProps)
1912
0
    : SvXMLImportContext(i_rImport)
1913
0
    , SmXMLOfficeContext_Impl(i_rImport)
1914
0
    , SvXMLMetaDocumentContext(i_rImport, i_xDocProps)
1915
0
{
1916
0
}
1917
1918
uno::Reference<xml::sax::XFastContextHandler>
1919
    SAL_CALL SmXMLFlatDocContext_Impl::createFastChildContext(
1920
        sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
1921
0
{
1922
    // behave like meta base class iff we encounter office:meta
1923
0
    if (nElement == XML_ELEMENT(OFFICE, XML_META))
1924
0
    {
1925
0
        return SvXMLMetaDocumentContext::createFastChildContext(nElement, xAttrList);
1926
0
    }
1927
0
    else
1928
0
    {
1929
0
        return SmXMLOfficeContext_Impl::createFastChildContext(nElement, xAttrList);
1930
0
    }
1931
0
}
1932
1933
uno::Reference<xml::sax::XFastContextHandler> SmXMLDocContext_Impl::createFastChildContext(
1934
    sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& /*xAttrList*/)
1935
0
{
1936
0
    uno::Reference<xml::sax::XFastContextHandler> xContext;
1937
1938
0
    switch (nElement)
1939
0
    {
1940
        //Consider semantics a dummy except for any starmath annotations
1941
0
        case XML_ELEMENT(MATH, XML_SEMANTICS):
1942
        /*General Layout Schemata*/
1943
0
        case XML_ELEMENT(MATH, XML_MROW):
1944
0
            xContext = new SmXMLRowContext_Impl(GetSmImport());
1945
0
            break;
1946
0
        case XML_ELEMENT(MATH, XML_MENCLOSE):
1947
0
            xContext = new SmXMLEncloseContext_Impl(GetSmImport());
1948
0
            break;
1949
0
        case XML_ELEMENT(MATH, XML_MFRAC):
1950
0
            xContext = new SmXMLFracContext_Impl(GetSmImport());
1951
0
            break;
1952
0
        case XML_ELEMENT(MATH, XML_MSQRT):
1953
0
            xContext = new SmXMLSqrtContext_Impl(GetSmImport());
1954
0
            break;
1955
0
        case XML_ELEMENT(MATH, XML_MROOT):
1956
0
            xContext = new SmXMLRootContext_Impl(GetSmImport());
1957
0
            break;
1958
0
        case XML_ELEMENT(MATH, XML_MSTYLE):
1959
0
            xContext = new SmXMLStyleContext_Impl(GetSmImport());
1960
0
            break;
1961
0
        case XML_ELEMENT(MATH, XML_MERROR):
1962
0
            xContext = new SmXMLErrorContext_Impl(GetSmImport());
1963
0
            break;
1964
0
        case XML_ELEMENT(MATH, XML_MPADDED):
1965
0
            xContext = new SmXMLPaddedContext_Impl(GetSmImport());
1966
0
            break;
1967
0
        case XML_ELEMENT(MATH, XML_MPHANTOM):
1968
0
            xContext = new SmXMLPhantomContext_Impl(GetSmImport());
1969
0
            break;
1970
0
        case XML_ELEMENT(MATH, XML_MFENCED):
1971
0
            xContext = new SmXMLFencedContext_Impl(GetSmImport());
1972
0
            break;
1973
        /*Script and Limit Schemata*/
1974
0
        case XML_ELEMENT(MATH, XML_MSUB):
1975
0
            xContext = new SmXMLSubContext_Impl(GetSmImport());
1976
0
            break;
1977
0
        case XML_ELEMENT(MATH, XML_MSUP):
1978
0
            xContext = new SmXMLSupContext_Impl(GetSmImport());
1979
0
            break;
1980
0
        case XML_ELEMENT(MATH, XML_MSUBSUP):
1981
0
            xContext = new SmXMLSubSupContext_Impl(GetSmImport());
1982
0
            break;
1983
0
        case XML_ELEMENT(MATH, XML_MUNDER):
1984
0
            xContext = new SmXMLUnderContext_Impl(GetSmImport());
1985
0
            break;
1986
0
        case XML_ELEMENT(MATH, XML_MOVER):
1987
0
            xContext = new SmXMLOverContext_Impl(GetSmImport());
1988
0
            break;
1989
0
        case XML_ELEMENT(MATH, XML_MUNDEROVER):
1990
0
            xContext = new SmXMLUnderOverContext_Impl(GetSmImport());
1991
0
            break;
1992
0
        case XML_ELEMENT(MATH, XML_MMULTISCRIPTS):
1993
0
            xContext = new SmXMLMultiScriptsContext_Impl(GetSmImport());
1994
0
            break;
1995
0
        case XML_ELEMENT(MATH, XML_MTABLE):
1996
0
            xContext = new SmXMLTableContext_Impl(GetSmImport());
1997
0
            break;
1998
0
        case XML_ELEMENT(MATH, XML_MACTION):
1999
0
            xContext = new SmXMLActionContext_Impl(GetSmImport());
2000
0
            break;
2001
0
        default:
2002
            /*Basically there's an implicit mrow around certain bare
2003
             *elements, use a RowContext to see if this is one of
2004
             *those ones*/
2005
0
            rtl::Reference<SmXMLRowContext_Impl> aTempContext(
2006
0
                new SmXMLRowContext_Impl(GetSmImport()));
2007
2008
0
            xContext = aTempContext->StrictCreateChildContext(nElement);
2009
0
            break;
2010
0
    }
2011
0
    return xContext;
2012
0
}
2013
2014
void SmXMLDocContext_Impl::endFastElement(sal_Int32)
2015
0
{
2016
0
    SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
2017
2018
0
    std::unique_ptr<SmNode> pContextNode = popOrZero(rNodeStack);
2019
2020
0
    SmToken aDummy;
2021
0
    std::unique_ptr<SmStructureNode> pSNode(new SmLineNode(aDummy));
2022
0
    pSNode->SetSubNodes(std::move(pContextNode), nullptr);
2023
0
    rNodeStack.push_front(std::move(pSNode));
2024
2025
0
    SmNodeArray LineArray;
2026
0
    auto n = rNodeStack.size();
2027
0
    LineArray.resize(n);
2028
0
    for (size_t j = 0; j < n; j++)
2029
0
    {
2030
0
        auto pNode = std::move(rNodeStack.front());
2031
0
        rNodeStack.pop_front();
2032
0
        LineArray[n - (j + 1)] = pNode.release();
2033
0
    }
2034
0
    std::unique_ptr<SmStructureNode> pSNode2(new SmTableNode(aDummy));
2035
0
    pSNode2->SetSubNodes(std::move(LineArray));
2036
0
    rNodeStack.push_front(std::move(pSNode2));
2037
0
}
2038
2039
void SmXMLFracContext_Impl::endFastElement(sal_Int32)
2040
0
{
2041
0
    SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
2042
0
    const bool bNodeCheck = rNodeStack.size() - nElementCount == 2;
2043
0
    OSL_ENSURE(bNodeCheck, "Fraction (mfrac) tag is missing component");
2044
0
    if (!bNodeCheck)
2045
0
        return;
2046
2047
0
    SmToken aToken;
2048
0
    aToken.cMathChar = u""_ustr;
2049
0
    aToken.eType = TFRAC;
2050
0
    std::unique_ptr<SmStructureNode> pSNode(new SmBinVerNode(aToken));
2051
0
    std::unique_ptr<SmNode> pOper(new SmRectangleNode(aToken));
2052
0
    std::unique_ptr<SmNode> pSecond = popOrZero(rNodeStack);
2053
0
    std::unique_ptr<SmNode> pFirst = popOrZero(rNodeStack);
2054
0
    pSNode->SetSubNodes(std::move(pFirst), std::move(pOper), std::move(pSecond));
2055
0
    rNodeStack.push_front(std::move(pSNode));
2056
0
}
2057
2058
void SmXMLRootContext_Impl::endFastElement(sal_Int32)
2059
0
{
2060
    /*The <mroot> element requires exactly 2 arguments.*/
2061
0
    const bool bNodeCheck = GetSmImport().GetNodeStack().size() - nElementCount == 2;
2062
0
    OSL_ENSURE(bNodeCheck, "Root tag is missing component");
2063
0
    if (!bNodeCheck)
2064
0
        return;
2065
2066
0
    SmToken aToken;
2067
0
    aToken.setChar(MS_SQRT); //Temporary: alert, based on StarSymbol font
2068
0
    aToken.eType = TNROOT;
2069
0
    std::unique_ptr<SmStructureNode> pSNode(new SmRootNode(aToken));
2070
0
    std::unique_ptr<SmNode> pOper(new SmRootSymbolNode(aToken));
2071
0
    SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
2072
0
    std::unique_ptr<SmNode> pIndex = popOrZero(rNodeStack);
2073
0
    std::unique_ptr<SmNode> pBase = popOrZero(rNodeStack);
2074
0
    pSNode->SetSubNodes(std::move(pIndex), std::move(pOper), std::move(pBase));
2075
0
    rNodeStack.push_front(std::move(pSNode));
2076
0
}
2077
2078
void SmXMLSqrtContext_Impl::endFastElement(sal_Int32 nElement)
2079
0
{
2080
    /*
2081
    <msqrt> accepts any number of arguments; if this number is not 1, its
2082
    contents are treated as a single "inferred <mrow>" containing its
2083
    arguments
2084
    */
2085
0
    if (GetSmImport().GetNodeStack().size() - nElementCount != 1)
2086
0
        SmXMLRowContext_Impl::endFastElement(nElement);
2087
2088
0
    SmToken aToken;
2089
0
    aToken.setChar(MS_SQRT); //Temporary: alert, based on StarSymbol font
2090
0
    aToken.eType = TSQRT;
2091
0
    std::unique_ptr<SmStructureNode> pSNode(new SmRootNode(aToken));
2092
0
    std::unique_ptr<SmNode> pOper(new SmRootSymbolNode(aToken));
2093
0
    SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
2094
0
    pSNode->SetSubNodes(nullptr, std::move(pOper), popOrZero(rNodeStack));
2095
0
    rNodeStack.push_front(std::move(pSNode));
2096
0
}
2097
2098
void SmXMLRowContext_Impl::endFastElement(sal_Int32)
2099
0
{
2100
0
    SmNodeArray aRelationArray;
2101
0
    SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
2102
2103
0
    if (rNodeStack.size() > nElementCount)
2104
0
    {
2105
0
        auto nSize = rNodeStack.size() - nElementCount;
2106
2107
0
        aRelationArray.resize(nSize);
2108
0
        for (auto j = nSize; j > 0; j--)
2109
0
        {
2110
0
            auto pNode = std::move(rNodeStack.front());
2111
0
            rNodeStack.pop_front();
2112
0
            aRelationArray[j - 1] = pNode.release();
2113
0
        }
2114
2115
        //If the first or last element is an operator with stretchyness
2116
        //set then we must create a brace node here from those elements,
2117
        //removing the stretchness from the operators and applying it to
2118
        //ourselves, and creating the appropriate dummy StarMath none bracket
2119
        //to balance the arrangement
2120
0
        if (((aRelationArray[0]->GetScaleMode() == SmScaleMode::Height)
2121
0
             && (aRelationArray[0]->GetType() == SmNodeType::Math))
2122
0
            || ((aRelationArray[nSize - 1]->GetScaleMode() == SmScaleMode::Height)
2123
0
                && (aRelationArray[nSize - 1]->GetType() == SmNodeType::Math)))
2124
0
        {
2125
0
            SmToken aToken;
2126
0
            aToken.cMathChar = u""_ustr;
2127
0
            aToken.nLevel = 5;
2128
2129
0
            int nLeft = 0, nRight = 0;
2130
0
            if ((aRelationArray[0]->GetScaleMode() == SmScaleMode::Height)
2131
0
                && (aRelationArray[0]->GetType() == SmNodeType::Math))
2132
0
            {
2133
0
                aToken = aRelationArray[0]->GetToken();
2134
0
                nLeft = 1;
2135
0
            }
2136
0
            else
2137
0
                aToken.cMathChar = u""_ustr;
2138
2139
0
            aToken.eType = TLPARENT;
2140
0
            std::unique_ptr<SmNode> pLeft(new SmMathSymbolNode(aToken));
2141
2142
0
            if ((aRelationArray[nSize - 1]->GetScaleMode() == SmScaleMode::Height)
2143
0
                && (aRelationArray[nSize - 1]->GetType() == SmNodeType::Math))
2144
0
            {
2145
0
                aToken = aRelationArray[nSize - 1]->GetToken();
2146
0
                nRight = 1;
2147
0
            }
2148
0
            else
2149
0
                aToken.cMathChar = u""_ustr;
2150
2151
0
            aToken.eType = TRPARENT;
2152
0
            std::unique_ptr<SmNode> pRight(new SmMathSymbolNode(aToken));
2153
2154
0
            SmNodeArray aRelationArray2;
2155
2156
            //!! nSize-nLeft-nRight may be < 0 !!
2157
0
            int nRelArrSize = nSize - nLeft - nRight;
2158
0
            if (nRelArrSize > 0)
2159
0
            {
2160
0
                aRelationArray2.resize(nRelArrSize);
2161
0
                for (int i = 0; i < nRelArrSize; i++)
2162
0
                {
2163
0
                    aRelationArray2[i] = aRelationArray[i + nLeft];
2164
0
                    aRelationArray[i + nLeft] = nullptr;
2165
0
                }
2166
0
            }
2167
2168
0
            SmToken aDummy;
2169
0
            std::unique_ptr<SmStructureNode> pSNode(new SmBraceNode(aToken));
2170
0
            std::unique_ptr<SmStructureNode> pBody(new SmExpressionNode(aDummy));
2171
0
            pBody->SetSubNodes(std::move(aRelationArray2));
2172
2173
0
            pSNode->SetSubNodes(std::move(pLeft), std::move(pBody), std::move(pRight));
2174
0
            pSNode->SetScaleMode(SmScaleMode::Height);
2175
0
            rNodeStack.push_front(std::move(pSNode));
2176
2177
0
            for (auto a : aRelationArray)
2178
0
                delete a;
2179
2180
0
            return;
2181
0
        }
2182
0
    }
2183
0
    else
2184
0
    {
2185
        // The elements msqrt, mstyle, merror, menclose, mpadded, mphantom, mtd, and math
2186
        // treat their content as a single inferred mrow in case their content is empty.
2187
        // Here an empty group {} is used to catch those cases and transform them without error
2188
        // to StarMath.
2189
0
        aRelationArray.resize(2);
2190
0
        SmToken aToken;
2191
0
        aToken.setChar(MS_LBRACE);
2192
0
        aToken.nLevel = 5;
2193
0
        aToken.eType = TLGROUP;
2194
0
        aToken.nGroup = TG::NONE;
2195
0
        aToken.aText = "{";
2196
0
        aRelationArray[0] = new SmLineNode(aToken);
2197
2198
0
        aToken.setChar(MS_RBRACE);
2199
0
        aToken.nLevel = 0;
2200
0
        aToken.eType = TRGROUP;
2201
0
        aToken.nGroup = TG::NONE;
2202
0
        aToken.aText = "}";
2203
0
        aRelationArray[1] = new SmLineNode(aToken);
2204
0
    }
2205
2206
0
    SmToken aDummy;
2207
0
    std::unique_ptr<SmStructureNode> pSNode(new SmExpressionNode(aDummy));
2208
0
    pSNode->SetSubNodes(std::move(aRelationArray));
2209
0
    rNodeStack.push_front(std::move(pSNode));
2210
0
}
2211
2212
uno::Reference<xml::sax::XFastContextHandler>
2213
SmXMLRowContext_Impl::StrictCreateChildContext(sal_Int32 nElement)
2214
0
{
2215
0
    uno::Reference<xml::sax::XFastContextHandler> pContext;
2216
2217
0
    switch (nElement)
2218
0
    {
2219
        /*Note that these should accept malignmark subelements, but do not*/
2220
0
        case XML_ELEMENT(MATH, XML_MN):
2221
0
            pContext = new SmXMLNumberContext_Impl(GetSmImport());
2222
0
            break;
2223
0
        case XML_ELEMENT(MATH, XML_MI):
2224
0
            pContext = new SmXMLIdentifierContext_Impl(GetSmImport());
2225
0
            break;
2226
0
        case XML_ELEMENT(MATH, XML_MO):
2227
0
            pContext = new SmXMLOperatorContext_Impl(GetSmImport());
2228
0
            break;
2229
0
        case XML_ELEMENT(MATH, XML_MTEXT):
2230
0
            pContext = new SmXMLTextContext_Impl(GetSmImport());
2231
0
            break;
2232
0
        case XML_ELEMENT(MATH, XML_MSPACE):
2233
0
            pContext = new SmXMLSpaceContext_Impl(GetSmImport());
2234
0
            break;
2235
0
        case XML_ELEMENT(MATH, XML_MS):
2236
0
            pContext = new SmXMLStringContext_Impl(GetSmImport());
2237
0
            break;
2238
2239
        /*Note: The maligngroup should only be seen when the row
2240
         * (or descendants) are in a table*/
2241
0
        case XML_ELEMENT(MATH, XML_MALIGNGROUP):
2242
0
            pContext = new SmXMLAlignGroupContext_Impl(GetSmImport());
2243
0
            break;
2244
2245
0
        case XML_ELEMENT(MATH, XML_ANNOTATION):
2246
0
            pContext = new SmXMLAnnotationContext_Impl(GetSmImport());
2247
0
            break;
2248
2249
0
        default:
2250
0
            break;
2251
0
    }
2252
0
    return pContext;
2253
0
}
2254
2255
uno::Reference<xml::sax::XFastContextHandler> SmXMLRowContext_Impl::createFastChildContext(
2256
    sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
2257
0
{
2258
0
    uno::Reference<xml::sax::XFastContextHandler> xContext = StrictCreateChildContext(nElement);
2259
2260
0
    if (!xContext)
2261
0
    {
2262
        //Hmm, unrecognized for this level, check to see if it's
2263
        //an element that can have an implicit schema around it
2264
0
        xContext = SmXMLDocContext_Impl::createFastChildContext(nElement, xAttrList);
2265
0
    }
2266
0
    return xContext;
2267
0
}
2268
2269
uno::Reference<xml::sax::XFastContextHandler> SmXMLMultiScriptsContext_Impl::createFastChildContext(
2270
    sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
2271
0
{
2272
0
    uno::Reference<xml::sax::XFastContextHandler> xContext;
2273
2274
0
    switch (nElement)
2275
0
    {
2276
0
        case XML_ELEMENT(MATH, XML_MPRESCRIPTS):
2277
0
            bHasPrescripts = true;
2278
0
            ProcessSubSupPairs(false);
2279
0
            xContext = new SmXMLPrescriptsContext_Impl(GetSmImport());
2280
0
            break;
2281
0
        case XML_ELEMENT(MATH, XML_NONE):
2282
0
            xContext = new SmXMLNoneContext_Impl(GetSmImport());
2283
0
            break;
2284
0
        default:
2285
0
            xContext = SmXMLRowContext_Impl::createFastChildContext(nElement, xAttrList);
2286
0
            break;
2287
0
    }
2288
0
    return xContext;
2289
0
}
2290
2291
void SmXMLMultiScriptsContext_Impl::ProcessSubSupPairs(bool bIsPrescript)
2292
0
{
2293
0
    SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
2294
2295
0
    if (rNodeStack.size() <= nElementCount)
2296
0
        return;
2297
2298
0
    auto nCount = rNodeStack.size() - nElementCount - 1;
2299
0
    if (nCount == 0)
2300
0
        return;
2301
2302
0
    if (nCount % 2 == 0)
2303
0
    {
2304
0
        SmToken aToken;
2305
0
        aToken.cMathChar = u""_ustr;
2306
0
        aToken.eType = bIsPrescript ? TLSUB : TRSUB;
2307
2308
0
        SmNodeStack aReverseStack;
2309
0
        for (size_t i = 0; i < nCount + 1; i++)
2310
0
        {
2311
0
            auto pNode = std::move(rNodeStack.front());
2312
0
            rNodeStack.pop_front();
2313
0
            aReverseStack.push_front(std::move(pNode));
2314
0
        }
2315
2316
0
        SmSubSup eSub = bIsPrescript ? LSUB : RSUB;
2317
0
        SmSubSup eSup = bIsPrescript ? LSUP : RSUP;
2318
2319
0
        for (size_t i = 0; i < nCount; i += 2)
2320
0
        {
2321
0
            std::unique_ptr<SmSubSupNode> pNode(new SmSubSupNode(aToken));
2322
2323
            // initialize subnodes array
2324
0
            SmNodeArray aSubNodes(1 + SUBSUP_NUM_ENTRIES);
2325
2326
            /*On each loop the base and its sub sup pair becomes the
2327
             base for the next loop to which the next sub sup pair is
2328
             attached, i.e. wheels within wheels*/
2329
0
            aSubNodes[0] = popOrZero(aReverseStack).release();
2330
2331
0
            std::unique_ptr<SmNode> pScriptNode = popOrZero(aReverseStack);
2332
2333
0
            if (pScriptNode
2334
0
                && ((pScriptNode->GetToken().eType != TIDENT)
2335
0
                    || (!pScriptNode->GetToken().aText.isEmpty())))
2336
0
                aSubNodes[eSub + 1] = pScriptNode.release();
2337
0
            pScriptNode = popOrZero(aReverseStack);
2338
0
            if (pScriptNode
2339
0
                && ((pScriptNode->GetToken().eType != TIDENT)
2340
0
                    || (!pScriptNode->GetToken().aText.isEmpty())))
2341
0
                aSubNodes[eSup + 1] = pScriptNode.release();
2342
2343
0
            pNode->SetSubNodes(std::move(aSubNodes));
2344
0
            aReverseStack.push_front(std::move(pNode));
2345
0
        }
2346
0
        assert(!aReverseStack.empty());
2347
0
        auto pNode = std::move(aReverseStack.front());
2348
0
        aReverseStack.pop_front();
2349
0
        rNodeStack.push_front(std::move(pNode));
2350
0
    }
2351
0
    else
2352
0
    {
2353
        // Ignore odd number of elements.
2354
0
        for (size_t i = 0; i < nCount; i++)
2355
0
        {
2356
0
            rNodeStack.pop_front();
2357
0
        }
2358
0
    }
2359
0
}
2360
2361
void SmXMLTableContext_Impl::endFastElement(sal_Int32)
2362
0
{
2363
0
    SmNodeArray aExpressionArray;
2364
0
    SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
2365
0
    SmNodeStack aReverseStack;
2366
0
    aExpressionArray.resize(rNodeStack.size() - nElementCount);
2367
2368
0
    size_t nRows = rNodeStack.size() - nElementCount;
2369
0
    size_t nCols = 0;
2370
2371
0
    for (size_t i = nRows; i > 0; --i)
2372
0
    {
2373
0
        SmNode* pArray = rNodeStack.front().release();
2374
0
        rNodeStack.pop_front();
2375
0
        if (pArray->GetNumSubNodes() == 0)
2376
0
        {
2377
            //This is a little tricky, it is possible that there was
2378
            //be elements that were not inside a <mtd> pair, in which
2379
            //case they will not be in a row, i.e. they will not have
2380
            //SubNodes, so we have to wait until here before we can
2381
            //resolve the situation. Implicit surrounding tags are
2382
            //surprisingly difficult to get right within this
2383
            //architecture
2384
2385
0
            SmNodeArray aRelationArray;
2386
0
            aRelationArray.resize(1);
2387
0
            aRelationArray[0] = pArray;
2388
0
            SmToken aDummy;
2389
0
            SmExpressionNode* pExprNode = new SmExpressionNode(aDummy);
2390
0
            pExprNode->SetSubNodes(std::move(aRelationArray));
2391
0
            pArray = pExprNode;
2392
0
        }
2393
2394
0
        nCols = std::max(nCols, pArray->GetNumSubNodes());
2395
0
        aReverseStack.push_front(std::unique_ptr<SmNode>(pArray));
2396
0
    }
2397
0
    if (nCols > SAL_MAX_UINT16)
2398
0
        throw std::range_error("column limit");
2399
0
    if (nRows > SAL_MAX_UINT16)
2400
0
        throw std::range_error("row limit");
2401
0
    aExpressionArray.resize(nCols * nRows);
2402
0
    size_t j = 0;
2403
0
    for (auto& elem : aReverseStack)
2404
0
    {
2405
0
        std::unique_ptr<SmStructureNode> xArray(static_cast<SmStructureNode*>(elem.release()));
2406
0
        for (size_t i = 0; i < xArray->GetNumSubNodes(); ++i)
2407
0
            aExpressionArray[j++] = xArray->GetSubNode(i);
2408
0
        xArray->ClearSubNodes();
2409
0
    }
2410
0
    aReverseStack.clear();
2411
2412
0
    SmToken aToken;
2413
0
    aToken.cMathChar = u""_ustr;
2414
0
    aToken.eType = TMATRIX;
2415
0
    std::unique_ptr<SmMatrixNode> pSNode(new SmMatrixNode(aToken));
2416
0
    pSNode->SetSubNodes(std::move(aExpressionArray));
2417
0
    pSNode->SetRowCol(nRows, nCols);
2418
0
    rNodeStack.push_front(std::move(pSNode));
2419
0
}
2420
2421
uno::Reference<xml::sax::XFastContextHandler> SmXMLTableRowContext_Impl::createFastChildContext(
2422
    sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
2423
0
{
2424
0
    uno::Reference<xml::sax::XFastContextHandler> xContext;
2425
2426
0
    switch (nElement)
2427
0
    {
2428
0
        case XML_ELEMENT(MATH, XML_MTD):
2429
0
            xContext = new SmXMLTableCellContext_Impl(GetSmImport());
2430
0
            break;
2431
0
        default:
2432
0
            xContext = SmXMLRowContext_Impl::createFastChildContext(nElement, xAttrList);
2433
0
            break;
2434
0
    }
2435
0
    return xContext;
2436
0
}
2437
2438
uno::Reference<xml::sax::XFastContextHandler> SmXMLTableContext_Impl::createFastChildContext(
2439
    sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
2440
0
{
2441
0
    uno::Reference<xml::sax::XFastContextHandler> xContext;
2442
2443
0
    switch (nElement)
2444
0
    {
2445
0
        case XML_ELEMENT(MATH, XML_MTR):
2446
0
            xContext = new SmXMLTableRowContext_Impl(GetSmImport());
2447
0
            break;
2448
0
        default:
2449
0
            xContext = SmXMLTableRowContext_Impl::createFastChildContext(nElement, xAttrList);
2450
0
            break;
2451
0
    }
2452
0
    return xContext;
2453
0
}
2454
2455
void SmXMLMultiScriptsContext_Impl::endFastElement(sal_Int32)
2456
0
{
2457
0
    ProcessSubSupPairs(bHasPrescripts);
2458
0
}
2459
2460
void SmXMLActionContext_Impl::startFastElement(
2461
    sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
2462
0
{
2463
0
    for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
2464
0
    {
2465
0
        switch (aIter.getToken())
2466
0
        {
2467
0
            case XML_SELECTION:
2468
0
            {
2469
0
                sal_Int32 n = aIter.toInt32();
2470
0
                if (n > 0)
2471
0
                    mnSelection = static_cast<size_t>(n);
2472
0
            }
2473
0
            break;
2474
0
            default:
2475
0
                XMLOFF_WARN_UNKNOWN("starmath", aIter);
2476
0
                break;
2477
0
        }
2478
0
    }
2479
0
}
2480
2481
void SmXMLActionContext_Impl::endFastElement(sal_Int32)
2482
0
{
2483
0
    SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
2484
0
    auto nSize = rNodeStack.size();
2485
0
    if (nSize <= nElementCount)
2486
0
    {
2487
        // not compliant to maction's specification, e.g., no subexpressions
2488
0
        return;
2489
0
    }
2490
0
    assert(mnSelection > 0);
2491
0
    if (nSize < nElementCount + mnSelection)
2492
0
    {
2493
        // No selected subexpression exists, which is a MathML error;
2494
        // fallback to selecting the first
2495
0
        mnSelection = 1;
2496
0
    }
2497
0
    assert(nSize >= nElementCount + mnSelection);
2498
0
    for (auto i = nSize - (nElementCount + mnSelection); i > 0; i--)
2499
0
    {
2500
0
        rNodeStack.pop_front();
2501
0
    }
2502
0
    auto pSelected = std::move(rNodeStack.front());
2503
0
    rNodeStack.pop_front();
2504
0
    for (auto i = rNodeStack.size() - nElementCount; i > 0; i--)
2505
0
    {
2506
0
        rNodeStack.pop_front();
2507
0
    }
2508
0
    rNodeStack.push_front(std::move(pSelected));
2509
0
}
2510
2511
SvXMLImportContext*
2512
SmXMLImport::CreateFastContext(sal_Int32 nElement,
2513
                               const uno::Reference<xml::sax::XFastAttributeList>& /*xAttrList*/)
2514
0
{
2515
0
    SvXMLImportContext* pContext = nullptr;
2516
2517
0
    switch (nElement)
2518
0
    {
2519
0
        case XML_ELEMENT(OFFICE, XML_DOCUMENT):
2520
0
        case XML_ELEMENT(OFFICE, XML_DOCUMENT_META):
2521
0
        {
2522
0
            uno::Reference<document::XDocumentPropertiesSupplier> xDPS(GetModel(),
2523
0
                                                                       uno::UNO_QUERY_THROW);
2524
0
            pContext = ((nElement & TOKEN_MASK) == XML_DOCUMENT_META)
2525
0
                           ? new SvXMLMetaDocumentContext(*this, xDPS->getDocumentProperties())
2526
                           // flat OpenDocument file format -- this has not been tested...
2527
0
                           : new SmXMLFlatDocContext_Impl(*this, xDPS->getDocumentProperties());
2528
0
        }
2529
0
        break;
2530
0
        default:
2531
0
            if (IsTokenInNamespace(nElement, XML_NAMESPACE_OFFICE))
2532
0
                pContext = new SmXMLOfficeContext_Impl(*this);
2533
0
            else
2534
0
                pContext = new SmXMLDocContext_Impl(*this);
2535
0
    }
2536
0
    return pContext;
2537
0
}
2538
2539
0
SmXMLImport::~SmXMLImport() noexcept { cleanup(); }
2540
2541
void SmXMLImport::SetViewSettings(const Sequence<PropertyValue>& aViewProps)
2542
0
{
2543
0
    uno::Reference<frame::XModel> xModel = GetModel();
2544
0
    if (!xModel.is())
2545
0
        return;
2546
2547
0
    SmModel* pModel = dynamic_cast<SmModel*>(xModel.get());
2548
2549
0
    if (!pModel)
2550
0
        return;
2551
2552
0
    SmDocShell* pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell());
2553
0
    if (!pDocShell)
2554
0
        return;
2555
2556
0
    tools::Rectangle aRect(pDocShell->GetVisArea());
2557
2558
0
    tools::Long nTmp = 0;
2559
2560
0
    for (const PropertyValue& rValue : aViewProps)
2561
0
    {
2562
0
        if (rValue.Name == "ViewAreaTop")
2563
0
        {
2564
0
            rValue.Value >>= nTmp;
2565
0
            aRect.SaturatingSetPosY(nTmp);
2566
0
        }
2567
0
        else if (rValue.Name == "ViewAreaLeft")
2568
0
        {
2569
0
            rValue.Value >>= nTmp;
2570
0
            aRect.SaturatingSetPosX(nTmp);
2571
0
        }
2572
0
        else if (rValue.Name == "ViewAreaWidth")
2573
0
        {
2574
0
            rValue.Value >>= nTmp;
2575
0
            Size aSize(aRect.GetSize());
2576
0
            aSize.setWidth(nTmp);
2577
0
            aRect.SaturatingSetSize(aSize);
2578
0
        }
2579
0
        else if (rValue.Name == "ViewAreaHeight")
2580
0
        {
2581
0
            rValue.Value >>= nTmp;
2582
0
            Size aSize(aRect.GetSize());
2583
0
            aSize.setHeight(nTmp);
2584
0
            aRect.SaturatingSetSize(aSize);
2585
0
        }
2586
0
    }
2587
2588
0
    pDocShell->SetVisArea(aRect);
2589
0
}
2590
2591
void SmXMLImport::SetConfigurationSettings(const Sequence<PropertyValue>& aConfProps)
2592
0
{
2593
0
    uno::Reference<XPropertySet> xProps(GetModel(), UNO_QUERY);
2594
0
    if (!xProps.is())
2595
0
        return;
2596
2597
0
    Reference<XPropertySetInfo> xInfo(xProps->getPropertySetInfo());
2598
0
    if (!xInfo.is())
2599
0
        return;
2600
2601
0
    static constexpr OUStringLiteral sFormula(u"Formula");
2602
0
    static constexpr OUStringLiteral sBasicLibraries(u"BasicLibraries");
2603
0
    static constexpr OUStringLiteral sDialogLibraries(u"DialogLibraries");
2604
0
    for (const PropertyValue& rValue : aConfProps)
2605
0
    {
2606
0
        if (rValue.Name != sFormula && rValue.Name != sBasicLibraries
2607
0
            && rValue.Name != sDialogLibraries)
2608
0
        {
2609
0
            try
2610
0
            {
2611
0
                if (xInfo->hasPropertyByName(rValue.Name))
2612
0
                    xProps->setPropertyValue(rValue.Name, rValue.Value);
2613
0
            }
2614
0
            catch (const beans::PropertyVetoException&)
2615
0
            {
2616
                // dealing with read-only properties here. Nothing to do...
2617
0
            }
2618
0
            catch (const Exception&)
2619
0
            {
2620
0
                DBG_UNHANDLED_EXCEPTION("starmath");
2621
0
            }
2622
0
        }
2623
0
    }
2624
0
}
2625
2626
extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportMML(SvStream& rStream)
2627
0
{
2628
0
    SmGlobals::ensure();
2629
2630
0
    SfxObjectShellLock xDocSh(new SmDocShell(SfxModelFlags::EMBEDDED_OBJECT));
2631
0
    xDocSh->DoInitNew();
2632
0
    uno::Reference<frame::XModel> xModel(xDocSh->GetModel());
2633
2634
0
    uno::Reference<beans::XPropertySet> xInfoSet;
2635
0
    const uno::Reference<uno::XComponentContext>& xContext(
2636
0
        comphelper::getProcessComponentContext());
2637
0
    uno::Reference<io::XInputStream> xStream(new utl::OSeekableInputStreamWrapper(rStream));
2638
2639
    //SetLoading hack because the document properties will be re-initted
2640
    //by the xml filter and during the init, while it's considered uninitialized,
2641
    //setting a property will inform the document it's modified, which attempts
2642
    //to update the properties, which throws cause the properties are uninitialized
2643
0
    xDocSh->SetLoading(SfxLoadedFlags::NONE);
2644
2645
0
    ErrCode nRet = ERRCODE_SFX_DOLOADFAILED;
2646
2647
0
    try
2648
0
    {
2649
0
        nRet = SmXMLImportWrapper::ReadThroughComponent(xStream, xModel, xContext, xInfoSet,
2650
0
                                                        "com.sun.star.comp.Math.XMLImporter", false,
2651
0
                                                        false);
2652
0
    }
2653
0
    catch (...)
2654
0
    {
2655
0
    }
2656
2657
0
    xDocSh->SetLoading(SfxLoadedFlags::ALL);
2658
2659
0
    xDocSh->DoClose();
2660
2661
0
    return nRet != ERRCODE_NONE;
2662
0
}
2663
2664
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */