summaryrefslogtreecommitdiffstats
path: root/starmath/source/mathml/mathmlimport.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /starmath/source/mathml/mathmlimport.cxx
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--starmath/source/mathml/mathmlimport.cxx2696
1 files changed, 2696 insertions, 0 deletions
diff --git a/starmath/source/mathml/mathmlimport.cxx b/starmath/source/mathml/mathmlimport.cxx
new file mode 100644
index 000000000..44d8cc1e3
--- /dev/null
+++ b/starmath/source/mathml/mathmlimport.cxx
@@ -0,0 +1,2696 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+/*todo: Change characters and tcharacters to accumulate the characters together
+into one string, xml parser hands them to us line by line rather than all in
+one go*/
+
+#include <com/sun/star/xml/sax/InputSource.hpp>
+#include <com/sun/star/xml/sax/FastParser.hpp>
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <com/sun/star/xml/sax/SAXParseException.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/packages/WrongPasswordException.hpp>
+#include <com/sun/star/packages/zip/ZipIOException.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/uno/Any.h>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+
+#include <comphelper/fileformat.h>
+#include <comphelper/genericpropertyset.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/propertysetinfo.hxx>
+#include <rtl/character.hxx>
+#include <sal/log.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/sfxmodelfactory.hxx>
+#include <osl/diagnose.h>
+#include <sot/storage.hxx>
+#include <svtools/sfxecode.hxx>
+#include <svl/itemset.hxx>
+#include <svl/stritem.hxx>
+#include <unotools/streamwrap.hxx>
+#include <sax/tools/converter.hxx>
+#include <xmloff/DocumentSettingsContext.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmluconv.hxx>
+#include <xmloff/xmlmetai.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <tools/diagnose_ex.h>
+#include <o3tl/string_view.hxx>
+
+#include <mathmlattr.hxx>
+#include <xparsmlbase.hxx>
+#include <mathmlimport.hxx>
+#include <document.hxx>
+#include <smdll.hxx>
+#include <unomodel.hxx>
+#include <utility.hxx>
+#include <visitors.hxx>
+#include <starmathdatabase.hxx>
+#include <smmod.hxx>
+#include <cfgitem.hxx>
+
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star;
+using namespace ::xmloff::token;
+
+namespace
+{
+std::unique_ptr<SmNode> popOrZero(SmNodeStack& rStack)
+{
+ if (rStack.empty())
+ return nullptr;
+ auto pTmp = std::move(rStack.front());
+ rStack.pop_front();
+ return pTmp;
+}
+}
+
+ErrCode SmXMLImportWrapper::Import(SfxMedium& rMedium)
+{
+ ErrCode nError = ERRCODE_SFX_DOLOADFAILED;
+
+ uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
+
+ //Make a model component from our SmModel
+ uno::Reference<lang::XComponent> xModelComp = xModel;
+ OSL_ENSURE(xModelComp.is(), "XMLReader::Read: got no model");
+
+ // try to get an XStatusIndicator from the Medium
+ uno::Reference<task::XStatusIndicator> xStatusIndicator;
+
+ bool bEmbedded = false;
+ SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(xModel);
+
+ SmDocShell* pDocShell = pModel ? static_cast<SmDocShell*>(pModel->GetObjectShell()) : nullptr;
+ if (pDocShell)
+ {
+ OSL_ENSURE(pDocShell->GetMedium() == &rMedium, "different SfxMedium found");
+
+ SfxItemSet* pSet = rMedium.GetItemSet();
+ if (pSet)
+ {
+ const SfxUnoAnyItem* pItem = pSet->GetItem(SID_PROGRESS_STATUSBAR_CONTROL);
+ if (pItem)
+ pItem->GetValue() >>= xStatusIndicator;
+ }
+
+ if (SfxObjectCreateMode::EMBEDDED == pDocShell->GetCreateMode())
+ bEmbedded = true;
+ }
+
+ static const comphelper::PropertyMapEntry aInfoMap[]
+ = { { OUString("PrivateData"), 0, cppu::UnoType<XInterface>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("BaseURI"), 0, ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StreamRelPath"), 0, ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StreamName"), 0, ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 } };
+ uno::Reference<beans::XPropertySet> xInfoSet(
+ comphelper::GenericPropertySet_CreateInstance(new comphelper::PropertySetInfo(aInfoMap)));
+
+ // Set base URI
+ OUString const baseURI(rMedium.GetBaseURL());
+ // needed for relative URLs; but it's OK to import e.g. MathML from the
+ // clipboard without one
+ SAL_INFO_IF(baseURI.isEmpty(), "starmath", "SmXMLImportWrapper: no base URL");
+ xInfoSet->setPropertyValue("BaseURI", Any(baseURI));
+
+ sal_Int32 nSteps = 3;
+ if (!(rMedium.IsStorage()))
+ nSteps = 1;
+
+ sal_Int32 nProgressRange(nSteps);
+ if (xStatusIndicator.is())
+ {
+ xStatusIndicator->start(SvxResId(RID_SVXSTR_DOC_LOAD), nProgressRange);
+ }
+
+ nSteps = 0;
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ if (rMedium.IsStorage())
+ {
+ // TODO/LATER: handle the case of embedded links gracefully
+ if (bEmbedded) // && !rMedium.GetStorage()->IsRoot() )
+ {
+ OUString aName("dummyObjName");
+ if (rMedium.GetItemSet())
+ {
+ const SfxStringItem* pDocHierarchItem
+ = rMedium.GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME);
+ if (pDocHierarchItem)
+ aName = pDocHierarchItem->GetValue();
+ }
+
+ if (!aName.isEmpty())
+ {
+ xInfoSet->setPropertyValue("StreamRelPath", Any(aName));
+ }
+ }
+
+ bool bOASIS = (SotStorage::GetVersion(rMedium.GetStorage()) > SOFFICE_FILEFORMAT_60);
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ auto nWarn
+ = ReadThroughComponent(rMedium.GetStorage(), xModelComp, "meta.xml", xContext, xInfoSet,
+ (bOASIS ? "com.sun.star.comp.Math.XMLOasisMetaImporter"
+ : "com.sun.star.comp.Math.XMLMetaImporter"),
+ m_bUseHTMLMLEntities);
+
+ if (nWarn != ERRCODE_IO_BROKENPACKAGE)
+ {
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ nWarn = ReadThroughComponent(rMedium.GetStorage(), xModelComp, "settings.xml", xContext,
+ xInfoSet,
+ (bOASIS ? "com.sun.star.comp.Math.XMLOasisSettingsImporter"
+ : "com.sun.star.comp.Math.XMLSettingsImporter"),
+ m_bUseHTMLMLEntities);
+
+ if (nWarn != ERRCODE_IO_BROKENPACKAGE)
+ {
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ nError = ReadThroughComponent(
+ rMedium.GetStorage(), xModelComp, "content.xml", xContext, xInfoSet,
+ "com.sun.star.comp.Math.XMLImporter", m_bUseHTMLMLEntities);
+ }
+ else
+ nError = ERRCODE_IO_BROKENPACKAGE;
+ }
+ else
+ nError = ERRCODE_IO_BROKENPACKAGE;
+ }
+ else
+ {
+ Reference<io::XInputStream> xInputStream
+ = new utl::OInputStreamWrapper(rMedium.GetInStream());
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ nError = ReadThroughComponent(xInputStream, xModelComp, xContext, xInfoSet,
+ "com.sun.star.comp.Math.XMLImporter", false,
+ m_bUseHTMLMLEntities);
+ }
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->end();
+ return nError;
+}
+
+/// read a component (file + filter version)
+ErrCode SmXMLImportWrapper::ReadThroughComponent(const Reference<io::XInputStream>& xInputStream,
+ const Reference<XComponent>& xModelComponent,
+ Reference<uno::XComponentContext> const& rxContext,
+ Reference<beans::XPropertySet> const& rPropSet,
+ const char* pFilterName, bool bEncrypted,
+ bool bUseHTMLMLEntities)
+{
+ ErrCode nError = ERRCODE_SFX_DOLOADFAILED;
+ OSL_ENSURE(xInputStream.is(), "input stream missing");
+ OSL_ENSURE(xModelComponent.is(), "document missing");
+ OSL_ENSURE(rxContext.is(), "factory missing");
+ OSL_ENSURE(nullptr != pFilterName, "I need a service name for the component!");
+
+ // prepare ParserInputSource
+ xml::sax::InputSource aParserInput;
+ aParserInput.aInputStream = xInputStream;
+
+ Sequence<Any> aArgs{ Any(rPropSet) };
+
+ // get filter
+ Reference<XInterface> xFilter
+ = rxContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ OUString::createFromAscii(pFilterName), aArgs, rxContext);
+ SAL_WARN_IF(!xFilter, "starmath", "Can't instantiate filter component " << pFilterName);
+ if (!xFilter.is())
+ return nError;
+
+ // connect model and filter
+ Reference<XImporter> xImporter(xFilter, UNO_QUERY);
+ xImporter->setTargetDocument(xModelComponent);
+
+ // finally, parser the stream
+ try
+ {
+ Reference<css::xml::sax::XFastParser> xFastParser(xFilter, UNO_QUERY);
+ Reference<css::xml::sax::XFastDocumentHandler> xFastDocHandler(xFilter, UNO_QUERY);
+ if (xFastParser)
+ {
+ if (bUseHTMLMLEntities)
+ xFastParser->setCustomEntityNames(starmathdatabase::icustomMathmlHtmlEntities);
+ xFastParser->parseStream(aParserInput);
+ }
+ else if (xFastDocHandler)
+ {
+ Reference<css::xml::sax::XFastParser> xParser
+ = css::xml::sax::FastParser::create(rxContext);
+ if (bUseHTMLMLEntities)
+ xParser->setCustomEntityNames(starmathdatabase::icustomMathmlHtmlEntities);
+ xParser->setFastDocumentHandler(xFastDocHandler);
+ xParser->parseStream(aParserInput);
+ }
+ else
+ {
+ Reference<css::xml::sax::XDocumentHandler> xDocHandler(xFilter, UNO_QUERY);
+ assert(xDocHandler);
+ Reference<css::xml::sax::XParser> xParser = css::xml::sax::Parser::create(rxContext);
+ xParser->setDocumentHandler(xDocHandler);
+ xParser->parseStream(aParserInput);
+ }
+
+ auto pFilter = comphelper::getFromUnoTunnel<SmXMLImport>(xFilter);
+ if (pFilter && pFilter->GetSuccess())
+ nError = ERRCODE_NONE;
+ }
+ catch (const xml::sax::SAXParseException& r)
+ {
+ // sax parser sends wrapped exceptions,
+ // try to find the original one
+ xml::sax::SAXException aSaxEx = *static_cast<const xml::sax::SAXException*>(&r);
+ bool bTryChild = true;
+
+ while (bTryChild)
+ {
+ xml::sax::SAXException aTmp;
+ if (aSaxEx.WrappedException >>= aTmp)
+ aSaxEx = aTmp;
+ else
+ bTryChild = false;
+ }
+
+ packages::zip::ZipIOException aBrokenPackage;
+ if (aSaxEx.WrappedException >>= aBrokenPackage)
+ return ERRCODE_IO_BROKENPACKAGE;
+
+ if (bEncrypted)
+ nError = ERRCODE_SFX_WRONGPASSWORD;
+ }
+ catch (const xml::sax::SAXException& r)
+ {
+ packages::zip::ZipIOException aBrokenPackage;
+ if (r.WrappedException >>= aBrokenPackage)
+ return ERRCODE_IO_BROKENPACKAGE;
+
+ if (bEncrypted)
+ nError = ERRCODE_SFX_WRONGPASSWORD;
+ }
+ catch (const packages::zip::ZipIOException&)
+ {
+ nError = ERRCODE_IO_BROKENPACKAGE;
+ }
+ catch (const io::IOException&)
+ {
+ }
+ catch (const std::range_error&)
+ {
+ }
+
+ return nError;
+}
+
+ErrCode SmXMLImportWrapper::ReadThroughComponent(const uno::Reference<embed::XStorage>& xStorage,
+ const Reference<XComponent>& xModelComponent,
+ const char* pStreamName,
+ Reference<uno::XComponentContext> const& rxContext,
+ Reference<beans::XPropertySet> const& rPropSet,
+ const char* pFilterName, bool bUseHTMLMLEntities)
+{
+ OSL_ENSURE(xStorage.is(), "Need storage!");
+ OSL_ENSURE(nullptr != pStreamName, "Please, please, give me a name!");
+
+ // open stream (and set parser input)
+ OUString sStreamName = OUString::createFromAscii(pStreamName);
+
+ // get input stream
+ try
+ {
+ uno::Reference<io::XStream> xEventsStream
+ = xStorage->openStreamElement(sStreamName, embed::ElementModes::READ);
+
+ // determine if stream is encrypted or not
+ uno::Reference<beans::XPropertySet> xProps(xEventsStream, uno::UNO_QUERY);
+ Any aAny = xProps->getPropertyValue("Encrypted");
+ bool bEncrypted = false;
+ if (aAny.getValueType() == cppu::UnoType<bool>::get())
+ aAny >>= bEncrypted;
+
+ // set Base URL
+ if (rPropSet.is())
+ {
+ rPropSet->setPropertyValue("StreamName", Any(sStreamName));
+ }
+
+ Reference<io::XInputStream> xStream = xEventsStream->getInputStream();
+ return ReadThroughComponent(xStream, xModelComponent, rxContext, rPropSet, pFilterName,
+ bEncrypted, bUseHTMLMLEntities);
+ }
+ catch (packages::WrongPasswordException&)
+ {
+ return ERRCODE_SFX_WRONGPASSWORD;
+ }
+ catch (packages::zip::ZipIOException&)
+ {
+ return ERRCODE_IO_BROKENPACKAGE;
+ }
+ catch (uno::Exception&)
+ {
+ }
+
+ return ERRCODE_SFX_DOLOADFAILED;
+}
+
+SmXMLImport::SmXMLImport(const css::uno::Reference<css::uno::XComponentContext>& rContext,
+ OUString const& implementationName, SvXMLImportFlags nImportFlags)
+ : SvXMLImport(rContext, implementationName, nImportFlags)
+ , bSuccess(false)
+ , nParseDepth(0)
+ , mnSmSyntaxVersion(SM_MOD()->GetConfig()->GetDefaultSmSyntaxVersion())
+{
+}
+
+const uno::Sequence<sal_Int8>& SmXMLImport::getUnoTunnelId() noexcept
+{
+ static const comphelper::UnoIdInit theSmXMLImportUnoTunnelId;
+ return theSmXMLImportUnoTunnelId.getSeq();
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+Math_XMLImporter_get_implementation(uno::XComponentContext* pCtx,
+ uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(
+ new SmXMLImport(pCtx, "com.sun.star.comp.Math.XMLImporter", SvXMLImportFlags::ALL));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+Math_XMLOasisMetaImporter_get_implementation(uno::XComponentContext* pCtx,
+ uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new SmXMLImport(pCtx, "com.sun.star.comp.Math.XMLOasisMetaImporter",
+ SvXMLImportFlags::META));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+Math_XMLOasisSettingsImporter_get_implementation(uno::XComponentContext* pCtx,
+ uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new SmXMLImport(pCtx, "com.sun.star.comp.Math.XMLOasisSettingsImporter",
+ SvXMLImportFlags::SETTINGS));
+}
+
+sal_Int64 SAL_CALL SmXMLImport::getSomething(const uno::Sequence<sal_Int8>& rId)
+{
+ return comphelper::getSomethingImpl(rId, this,
+ comphelper::FallbackToGetSomethingOf<SvXMLImport>{});
+}
+
+void SmXMLImport::endDocument()
+{
+ //Set the resulted tree into the SmDocShell where it belongs
+ std::unique_ptr<SmNode> pTree = popOrZero(aNodeStack);
+ if (pTree && pTree->GetType() == SmNodeType::Table)
+ {
+ uno::Reference<frame::XModel> xModel = GetModel();
+ SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(xModel);
+
+ if (pModel)
+ {
+ SmDocShell* pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell());
+ auto pTreeTmp = pTree.get();
+ pDocShell->SetFormulaTree(static_cast<SmTableNode*>(pTree.release()));
+ if (aText.isEmpty()) //If we picked up no annotation text
+ {
+ // Get text from imported formula
+ SmNodeToTextVisitor tmpvisitor(pTreeTmp, aText);
+ }
+
+ // Convert symbol names
+ AbstractSmParser* rParser = pDocShell->GetParser();
+ bool bVal = rParser->IsImportSymbolNames();
+ rParser->SetImportSymbolNames(true);
+ auto pTmpTree = rParser->Parse(aText);
+ aText = rParser->GetText();
+ pTmpTree.reset();
+ rParser->SetImportSymbolNames(bVal);
+
+ pDocShell->SetText(aText);
+ pDocShell->SetSmSyntaxVersion(mnSmSyntaxVersion);
+ }
+ OSL_ENSURE(pModel, "So there *was* a UNO problem after all");
+
+ bSuccess = true;
+ }
+
+ SvXMLImport::endDocument();
+}
+
+namespace
+{
+class SmXMLImportContext : public SvXMLImportContext
+{
+public:
+ SmXMLImportContext(SmXMLImport& rImport)
+ : SvXMLImportContext(rImport)
+ {
+ GetSmImport().IncParseDepth();
+ }
+
+ virtual ~SmXMLImportContext() override { GetSmImport().DecParseDepth(); }
+
+ SmXMLImport& GetSmImport() { return static_cast<SmXMLImport&>(GetImport()); }
+
+ virtual void TCharacters(const OUString& /*rChars*/);
+ virtual void SAL_CALL characters(const OUString& rChars) override;
+ virtual void SAL_CALL startFastElement(
+ sal_Int32 /*nElement*/,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& /*rAttrList*/) override
+ {
+ if (GetSmImport().TooDeep())
+ throw std::range_error("too deep");
+ }
+};
+}
+
+void SmXMLImportContext::TCharacters(const OUString& /*rChars*/) {}
+
+void SmXMLImportContext::characters(const OUString& rChars)
+{
+ /*
+ Whitespace occurring within the content of token elements is "trimmed"
+ from the ends (i.e. all whitespace at the beginning and end of the
+ content is removed), and "collapsed" internally (i.e. each sequence of
+ 1 or more whitespace characters is replaced with one blank character).
+ */
+ //collapsing not done yet!
+ const OUString& rChars2 = rChars.trim();
+ if (!rChars2.isEmpty())
+ TCharacters(rChars2 /*.collapse()*/);
+}
+
+namespace
+{
+struct SmXMLContext_Helper
+{
+ sal_Int8 nIsBold;
+ sal_Int8 nIsItalic;
+ double nFontSize;
+ OUString sFontFamily;
+ OUString sColor;
+
+ SmXMLImportContext& rContext;
+
+ explicit SmXMLContext_Helper(SmXMLImportContext& rImport)
+ : nIsBold(-1)
+ , nIsItalic(-1)
+ , nFontSize(0.0)
+ , rContext(rImport)
+ {
+ }
+
+ bool IsFontNodeNeeded() const;
+ void RetrieveAttrs(const uno::Reference<xml::sax::XFastAttributeList>& xAttrList);
+ void ApplyAttrs();
+};
+}
+
+bool SmXMLContext_Helper::IsFontNodeNeeded() const
+{
+ return nIsBold != -1 || nIsItalic != -1 || nFontSize != 0.0 || !sFontFamily.isEmpty()
+ || !sColor.isEmpty();
+}
+
+void SmXMLContext_Helper::RetrieveAttrs(
+ const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ bool bMvFound = false;
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ // sometimes they have namespace, sometimes not?
+ switch (aIter.getToken() & TOKEN_MASK)
+ {
+ case XML_FONTWEIGHT:
+ nIsBold = sal_Int8(IsXMLToken(aIter, XML_BOLD));
+ break;
+ case XML_FONTSTYLE:
+ nIsItalic = sal_Int8(IsXMLToken(aIter, XML_ITALIC));
+ break;
+ case XML_FONTSIZE:
+ case XML_MATHSIZE:
+ {
+ OUString sValue = aIter.toString();
+ ::sax::Converter::convertDouble(nFontSize, sValue);
+ rContext.GetSmImport().GetMM100UnitConverter().SetXMLMeasureUnit(
+ util::MeasureUnit::POINT);
+ if (-1 == sValue.indexOf(GetXMLToken(XML_UNIT_PT)))
+ {
+ if (-1 == sValue.indexOf('%'))
+ nFontSize = 0.0;
+ else
+ {
+ rContext.GetSmImport().GetMM100UnitConverter().SetXMLMeasureUnit(
+ util::MeasureUnit::PERCENT);
+ }
+ }
+ break;
+ }
+ case XML_FONTFAMILY:
+ sFontFamily = aIter.toString();
+ break;
+ case XML_COLOR:
+ sColor = aIter.toString();
+ break;
+ case XML_MATHCOLOR:
+ sColor = aIter.toString();
+ break;
+ case XML_MATHVARIANT:
+ bMvFound = true;
+ break;
+ default:
+ XMLOFF_WARN_UNKNOWN("starmath", aIter);
+ break;
+ }
+ }
+
+ if (bMvFound)
+ {
+ // Ignore deprecated attributes fontfamily, fontweight, and fontstyle
+ // in favor of mathvariant, as specified in
+ // <https://www.w3.org/TR/MathML3/chapter3.html#presm.deprecatt>.
+ sFontFamily.clear();
+ nIsBold = -1;
+ nIsItalic = -1;
+ }
+}
+
+void SmXMLContext_Helper::ApplyAttrs()
+{
+ SmNodeStack& rNodeStack = rContext.GetSmImport().GetNodeStack();
+
+ if (!IsFontNodeNeeded())
+ return;
+
+ SmToken aToken;
+ aToken.cMathChar = u"";
+ aToken.nLevel = 5;
+
+ if (nIsBold != -1)
+ {
+ if (nIsBold)
+ aToken.eType = TBOLD;
+ else
+ aToken.eType = TNBOLD;
+ std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(aToken));
+ pFontNode->SetSubNodes(nullptr, popOrZero(rNodeStack));
+ rNodeStack.push_front(std::move(pFontNode));
+ }
+ if (nIsItalic != -1)
+ {
+ if (nIsItalic)
+ aToken.eType = TITALIC;
+ else
+ aToken.eType = TNITALIC;
+ std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(aToken));
+ pFontNode->SetSubNodes(nullptr, popOrZero(rNodeStack));
+ rNodeStack.push_front(std::move(pFontNode));
+ }
+ if (nFontSize != 0.0)
+ {
+ aToken.eType = TSIZE;
+ std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(aToken));
+
+ if (util::MeasureUnit::PERCENT
+ == rContext.GetSmImport().GetMM100UnitConverter().GetXMLMeasureUnit())
+ {
+ if (nFontSize < 100.00)
+ pFontNode->SetSizeParameter(Fraction(100.00 / nFontSize), FontSizeType::DIVIDE);
+ else
+ pFontNode->SetSizeParameter(Fraction(nFontSize / 100.00), FontSizeType::MULTIPLY);
+ }
+ else
+ pFontNode->SetSizeParameter(Fraction(nFontSize), FontSizeType::ABSOLUT);
+
+ pFontNode->SetSubNodes(nullptr, popOrZero(rNodeStack));
+ rNodeStack.push_front(std::move(pFontNode));
+ }
+ if (!sColor.isEmpty())
+ {
+ SmColorTokenTableEntry aSmColorTokenTableEntry;
+ aSmColorTokenTableEntry = starmathdatabase::Identify_ColorName_HTML(sColor);
+ if (aSmColorTokenTableEntry.eType == TRGB)
+ aSmColorTokenTableEntry = starmathdatabase::Identify_Color_Parser(
+ sal_uInt32(aSmColorTokenTableEntry.cColor));
+ if (aSmColorTokenTableEntry.eType != TERROR)
+ {
+ aToken = aSmColorTokenTableEntry;
+ std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(aToken));
+ pFontNode->SetSubNodes(nullptr, popOrZero(rNodeStack));
+ rNodeStack.push_front(std::move(pFontNode));
+ }
+ // If not known, not implemented yet. Giving up.
+ }
+ if (sFontFamily.isEmpty())
+ return;
+
+ if (sFontFamily.equalsIgnoreAsciiCase(GetXMLToken(XML_FIXED)))
+ aToken.eType = TFIXED;
+ else if (sFontFamily.equalsIgnoreAsciiCase("sans"))
+ aToken.eType = TSANS;
+ else if (sFontFamily.equalsIgnoreAsciiCase("serif"))
+ aToken.eType = TSERIF;
+ else //Just give up, we need to extend our font mechanism to be
+ //more general
+ return;
+
+ aToken.aText = sFontFamily;
+ std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(aToken));
+ pFontNode->SetSubNodes(nullptr, popOrZero(rNodeStack));
+ rNodeStack.push_front(std::move(pFontNode));
+}
+
+namespace
+{
+class SmXMLTokenAttrHelper
+{
+ SmXMLImportContext& mrContext;
+ MathMLMathvariantValue meMv;
+ bool mbMvFound;
+
+public:
+ SmXMLTokenAttrHelper(SmXMLImportContext& rContext)
+ : mrContext(rContext)
+ , meMv(MathMLMathvariantValue::Normal)
+ , mbMvFound(false)
+ {
+ }
+
+ void RetrieveAttrs(const uno::Reference<xml::sax::XFastAttributeList>& xAttrList);
+ void ApplyAttrs(MathMLMathvariantValue eDefaultMv);
+};
+}
+
+void SmXMLTokenAttrHelper::RetrieveAttrs(
+ const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ OUString sValue = aIter.toString();
+ switch (aIter.getToken())
+ {
+ case XML_MATHVARIANT:
+ if (!GetMathMLMathvariantValue(sValue, meMv))
+ SAL_WARN("starmath", "failed to recognize mathvariant: " << sValue);
+ mbMvFound = true;
+ break;
+ default:
+ XMLOFF_WARN_UNKNOWN("starmath", aIter);
+ break;
+ }
+ }
+}
+
+void SmXMLTokenAttrHelper::ApplyAttrs(MathMLMathvariantValue eDefaultMv)
+{
+ assert(eDefaultMv == MathMLMathvariantValue::Normal
+ || eDefaultMv == MathMLMathvariantValue::Italic);
+
+ std::vector<SmTokenType> vVariant;
+ MathMLMathvariantValue eMv = mbMvFound ? meMv : eDefaultMv;
+ switch (eMv)
+ {
+ case MathMLMathvariantValue::Normal:
+ vVariant.push_back(TNITALIC);
+ break;
+ case MathMLMathvariantValue::Bold:
+ vVariant.push_back(TBOLD);
+ break;
+ case MathMLMathvariantValue::Italic:
+ // nothing to do
+ break;
+ case MathMLMathvariantValue::BoldItalic:
+ vVariant.push_back(TITALIC);
+ vVariant.push_back(TBOLD);
+ break;
+ case MathMLMathvariantValue::DoubleStruck:
+ // TODO
+ break;
+ case MathMLMathvariantValue::BoldFraktur:
+ // TODO: Fraktur
+ vVariant.push_back(TBOLD);
+ break;
+ case MathMLMathvariantValue::Script:
+ // TODO
+ break;
+ case MathMLMathvariantValue::BoldScript:
+ // TODO: Script
+ vVariant.push_back(TBOLD);
+ break;
+ case MathMLMathvariantValue::Fraktur:
+ // TODO
+ break;
+ case MathMLMathvariantValue::SansSerif:
+ vVariant.push_back(TSANS);
+ break;
+ case MathMLMathvariantValue::BoldSansSerif:
+ vVariant.push_back(TSANS);
+ vVariant.push_back(TBOLD);
+ break;
+ case MathMLMathvariantValue::SansSerifItalic:
+ vVariant.push_back(TITALIC);
+ vVariant.push_back(TSANS);
+ break;
+ case MathMLMathvariantValue::SansSerifBoldItalic:
+ vVariant.push_back(TITALIC);
+ vVariant.push_back(TBOLD);
+ vVariant.push_back(TSANS);
+ break;
+ case MathMLMathvariantValue::Monospace:
+ vVariant.push_back(TFIXED);
+ break;
+ case MathMLMathvariantValue::Initial:
+ case MathMLMathvariantValue::Tailed:
+ case MathMLMathvariantValue::Looped:
+ case MathMLMathvariantValue::Stretched:
+ // TODO
+ break;
+ }
+ if (vVariant.empty())
+ return;
+ SmNodeStack& rNodeStack = mrContext.GetSmImport().GetNodeStack();
+ for (auto eType : vVariant)
+ {
+ SmToken aToken;
+ aToken.eType = eType;
+ aToken.cMathChar = u"";
+ aToken.nLevel = 5;
+ std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(aToken));
+ pFontNode->SetSubNodes(nullptr, popOrZero(rNodeStack));
+ rNodeStack.push_front(std::move(pFontNode));
+ }
+}
+
+namespace
+{
+class SmXMLDocContext_Impl : public SmXMLImportContext
+{
+public:
+ SmXMLDocContext_Impl(SmXMLImport& rImport)
+ : SmXMLImportContext(rImport)
+ {
+ }
+
+ virtual uno::Reference<xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+/*avert the gaze from the originator*/
+class SmXMLRowContext_Impl : public SmXMLDocContext_Impl
+{
+protected:
+ size_t nElementCount;
+
+public:
+ SmXMLRowContext_Impl(SmXMLImport& rImport)
+ : SmXMLDocContext_Impl(rImport)
+ , nElementCount(GetSmImport().GetNodeStack().size())
+ {
+ }
+
+ virtual uno::Reference<xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
+
+ uno::Reference<xml::sax::XFastContextHandler> StrictCreateChildContext(sal_Int32 nElement);
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+class SmXMLEncloseContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ // TODO/LATER: convert <menclose notation="horizontalstrike"> into
+ // "overstrike{}" and extend the Math syntax to support more notations
+ SmXMLEncloseContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+}
+
+void SmXMLEncloseContext_Impl::endFastElement(sal_Int32 nElement)
+{
+ /*
+ <menclose> accepts any number of arguments; if this number is not 1, its
+ contents are treated as a single "inferred <mrow>" containing its
+ arguments
+ */
+ if (GetSmImport().GetNodeStack().size() - nElementCount != 1)
+ SmXMLRowContext_Impl::endFastElement(nElement);
+}
+
+namespace
+{
+class SmXMLFracContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ // TODO/LATER: convert <mfrac bevelled="true"> into "wideslash{}{}"
+ SmXMLFracContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+class SmXMLSqrtContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ SmXMLSqrtContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+class SmXMLRootContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ SmXMLRootContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+class SmXMLStyleContext_Impl : public SmXMLRowContext_Impl
+{
+protected:
+ SmXMLContext_Helper aStyleHelper;
+
+public:
+ /*Right now the style tag is completely ignored*/
+ SmXMLStyleContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ , aStyleHelper(*this)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+ void SAL_CALL startFastElement(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
+};
+}
+
+void SmXMLStyleContext_Impl::startFastElement(
+ sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ aStyleHelper.RetrieveAttrs(xAttrList);
+}
+
+void SmXMLStyleContext_Impl::endFastElement(sal_Int32 nElement)
+{
+ /*
+ <mstyle> accepts any number of arguments; if this number is not 1, its
+ contents are treated as a single "inferred <mrow>" containing its
+ arguments
+ */
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+ if (rNodeStack.size() - nElementCount != 1)
+ SmXMLRowContext_Impl::endFastElement(nElement);
+ aStyleHelper.ApplyAttrs();
+}
+
+namespace
+{
+class SmXMLPaddedContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ /*Right now the style tag is completely ignored*/
+ SmXMLPaddedContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+}
+
+void SmXMLPaddedContext_Impl::endFastElement(sal_Int32 nElement)
+{
+ /*
+ <mpadded> accepts any number of arguments; if this number is not 1, its
+ contents are treated as a single "inferred <mrow>" containing its
+ arguments
+ */
+ if (GetSmImport().GetNodeStack().size() - nElementCount != 1)
+ SmXMLRowContext_Impl::endFastElement(nElement);
+}
+
+namespace
+{
+class SmXMLPhantomContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ /*Right now the style tag is completely ignored*/
+ SmXMLPhantomContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+}
+
+void SmXMLPhantomContext_Impl::endFastElement(sal_Int32 nElement)
+{
+ /*
+ <mphantom> accepts any number of arguments; if this number is not 1, its
+ contents are treated as a single "inferred <mrow>" containing its
+ arguments
+ */
+ if (GetSmImport().GetNodeStack().size() - nElementCount != 1)
+ SmXMLRowContext_Impl::endFastElement(nElement);
+
+ SmToken aToken;
+ aToken.cMathChar = u"";
+ aToken.nLevel = 5;
+ aToken.eType = TPHANTOM;
+
+ std::unique_ptr<SmFontNode> pPhantom(new SmFontNode(aToken));
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+ pPhantom->SetSubNodes(nullptr, popOrZero(rNodeStack));
+ rNodeStack.push_front(std::move(pPhantom));
+}
+
+namespace
+{
+class SmXMLFencedContext_Impl : public SmXMLRowContext_Impl
+{
+protected:
+ sal_Unicode cBegin;
+ sal_Unicode cEnd;
+ bool bIsStretchy;
+
+public:
+ SmXMLFencedContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ , cBegin('(')
+ , cEnd(')')
+ , bIsStretchy(false)
+ {
+ }
+
+ void SAL_CALL startFastElement(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+}
+
+void SmXMLFencedContext_Impl::startFastElement(
+ sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ switch (aIter.getToken())
+ {
+ //temp, starmath cannot handle multichar brackets (I think)
+ case XML_OPEN:
+ cBegin = aIter.toString()[0];
+ break;
+ case XML_CLOSE:
+ cEnd = aIter.toString()[0];
+ break;
+ case XML_STRETCHY:
+ bIsStretchy = IsXMLToken(aIter, XML_TRUE);
+ break;
+ default:
+ XMLOFF_WARN_UNKNOWN("starmath", aIter);
+ /*Go to superclass*/
+ break;
+ }
+ }
+}
+
+void SmXMLFencedContext_Impl::endFastElement(sal_Int32 /*nElement*/)
+{
+ SmToken aToken;
+ aToken.cMathChar = u"";
+ aToken.aText = ",";
+ aToken.nLevel = 5;
+
+ std::unique_ptr<SmStructureNode> pSNode(new SmBraceNode(aToken));
+ if (bIsStretchy)
+ aToken = starmathdatabase::Identify_PrefixPostfix_SmXMLOperatorContext_Impl(cBegin);
+ else
+ aToken = starmathdatabase::Identify_Prefix_SmXMLOperatorContext_Impl(cBegin);
+ if (aToken.eType == TERROR)
+ aToken = SmToken(TLPARENT, MS_LPARENT, "(", TG::LBrace, 5);
+ std::unique_ptr<SmNode> pLeft(new SmMathSymbolNode(aToken));
+ if (bIsStretchy)
+ aToken = starmathdatabase::Identify_PrefixPostfix_SmXMLOperatorContext_Impl(cEnd);
+ else
+ aToken = starmathdatabase::Identify_Postfix_SmXMLOperatorContext_Impl(cEnd);
+ if (aToken.eType == TERROR)
+ aToken = SmToken(TRPARENT, MS_RPARENT, ")", TG::LBrace, 5);
+ std::unique_ptr<SmNode> pRight(new SmMathSymbolNode(aToken));
+
+ SmNodeArray aRelationArray;
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+ aToken.cMathChar = u"";
+ aToken.eType = TIDENT;
+
+ auto i = rNodeStack.size() - nElementCount;
+ if (rNodeStack.size() - nElementCount > 1)
+ i += rNodeStack.size() - 1 - nElementCount;
+ aRelationArray.resize(i);
+ while (rNodeStack.size() > nElementCount)
+ {
+ auto pNode = std::move(rNodeStack.front());
+ rNodeStack.pop_front();
+ aRelationArray[--i] = pNode.release();
+ if (i > 1 && rNodeStack.size() > 1)
+ aRelationArray[--i] = new SmGlyphSpecialNode(aToken);
+ }
+
+ SmToken aDummy;
+ std::unique_ptr<SmStructureNode> pBody(new SmExpressionNode(aDummy));
+ pBody->SetSubNodes(std::move(aRelationArray));
+
+ pSNode->SetSubNodes(std::move(pLeft), std::move(pBody), std::move(pRight));
+ // mfenced is always scalable. Stretchy keyword is not official, but in case of been in there
+ // can be used as a hint.
+ pSNode->SetScaleMode(SmScaleMode::Height);
+ GetSmImport().GetNodeStack().push_front(std::move(pSNode));
+}
+
+namespace
+{
+class SmXMLErrorContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ SmXMLErrorContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+}
+
+void SmXMLErrorContext_Impl::endFastElement(sal_Int32 /*nElement*/)
+{
+ /*Right now the error tag is completely ignored, what
+ can I do with it in starmath, ?, maybe we need a
+ report window ourselves, do a test for validity of
+ the xml input, use mirrors, and then generate
+ the markup inside the merror with a big red colour
+ of something. For now just throw them all away.
+ */
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+ while (rNodeStack.size() > nElementCount)
+ {
+ rNodeStack.pop_front();
+ }
+}
+
+namespace
+{
+class SmXMLNumberContext_Impl : public SmXMLImportContext
+{
+protected:
+ SmToken aToken;
+
+public:
+ SmXMLNumberContext_Impl(SmXMLImport& rImport)
+ : SmXMLImportContext(rImport)
+ {
+ aToken.cMathChar = u"";
+ aToken.nLevel = 5;
+ aToken.eType = TNUMBER;
+ }
+
+ virtual void TCharacters(const OUString& rChars) override;
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+}
+
+void SmXMLNumberContext_Impl::TCharacters(const OUString& rChars) { aToken.aText = rChars; }
+
+void SmXMLNumberContext_Impl::endFastElement(sal_Int32)
+{
+ GetSmImport().GetNodeStack().push_front(std::make_unique<SmTextNode>(aToken, FNT_NUMBER));
+}
+
+namespace
+{
+class SmXMLAnnotationContext_Impl : public SmXMLImportContext
+{
+ sal_uInt8 mnStarMathVersion;
+
+public:
+ SmXMLAnnotationContext_Impl(SmXMLImport& rImport)
+ : SmXMLImportContext(rImport)
+ , mnStarMathVersion(0)
+ {
+ }
+
+ void SAL_CALL characters(const OUString& rChars) override;
+
+ void SAL_CALL startFastElement(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
+};
+}
+
+void SmXMLAnnotationContext_Impl::startFastElement(
+ sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ // sometimes they have namespace, sometimes not?
+ switch (aIter.getToken() & TOKEN_MASK)
+ {
+ case XML_ENCODING:
+ mnStarMathVersion
+ = aIter.toView() == "StarMath 5.0" ? 5 : aIter.toView() == "StarMath 6" ? 6 : 0;
+ break;
+ default:
+ XMLOFF_WARN_UNKNOWN("starmath", aIter);
+ break;
+ }
+ }
+}
+
+void SmXMLAnnotationContext_Impl::characters(const OUString& rChars)
+{
+ if (mnStarMathVersion)
+ {
+ GetSmImport().SetText(GetSmImport().GetText() + rChars);
+ GetSmImport().SetSmSyntaxVersion(mnStarMathVersion);
+ }
+}
+
+namespace
+{
+class SmXMLTextContext_Impl : public SmXMLImportContext
+{
+protected:
+ SmToken aToken;
+
+public:
+ SmXMLTextContext_Impl(SmXMLImport& rImport)
+ : SmXMLImportContext(rImport)
+ {
+ aToken.cMathChar = u"";
+ aToken.nLevel = 5;
+ aToken.eType = TTEXT;
+ }
+
+ virtual void TCharacters(const OUString& rChars) override;
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+}
+
+void SmXMLTextContext_Impl::TCharacters(const OUString& rChars) { aToken.aText = rChars; }
+
+void SmXMLTextContext_Impl::endFastElement(sal_Int32)
+{
+ GetSmImport().GetNodeStack().push_front(std::make_unique<SmTextNode>(aToken, FNT_TEXT));
+}
+
+namespace
+{
+class SmXMLStringContext_Impl : public SmXMLImportContext
+{
+protected:
+ SmToken aToken;
+
+public:
+ SmXMLStringContext_Impl(SmXMLImport& rImport)
+ : SmXMLImportContext(rImport)
+ {
+ aToken.cMathChar = u"";
+ aToken.nLevel = 5;
+ aToken.eType = TTEXT;
+ }
+
+ virtual void TCharacters(const OUString& rChars) override;
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+}
+
+void SmXMLStringContext_Impl::TCharacters(const OUString& rChars)
+{
+ /*
+ The content of <ms> elements should be rendered with visible "escaping" of
+ certain characters in the content, including at least "double quote"
+ itself, and preferably whitespace other than individual blanks. The intent
+ is for the viewer to see that the expression is a string literal, and to
+ see exactly which characters form its content. For example, <ms>double
+ quote is "</ms> might be rendered as "double quote is \"".
+
+ Obviously this isn't fully done here.
+ */
+ aToken.aText = "\"" + rChars + "\"";
+}
+
+void SmXMLStringContext_Impl::endFastElement(sal_Int32)
+{
+ GetSmImport().GetNodeStack().push_front(std::make_unique<SmTextNode>(aToken, FNT_FIXED));
+}
+
+namespace
+{
+class SmXMLIdentifierContext_Impl : public SmXMLImportContext
+{
+ SmXMLTokenAttrHelper maTokenAttrHelper;
+ SmXMLContext_Helper aStyleHelper;
+ SmToken aToken;
+
+public:
+ SmXMLIdentifierContext_Impl(SmXMLImport& rImport)
+ : SmXMLImportContext(rImport)
+ , maTokenAttrHelper(*this)
+ , aStyleHelper(*this)
+ {
+ aToken.cMathChar = u"";
+ aToken.nLevel = 5;
+ aToken.eType = TIDENT;
+ }
+
+ void TCharacters(const OUString& rChars) override;
+ void SAL_CALL
+ startFastElement(sal_Int32 /*nElement*/,
+ const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override
+ {
+ maTokenAttrHelper.RetrieveAttrs(xAttrList);
+ aStyleHelper.RetrieveAttrs(xAttrList);
+ };
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+}
+
+void SmXMLIdentifierContext_Impl::endFastElement(sal_Int32)
+{
+ std::unique_ptr<SmTextNode> pNode;
+ //we will handle identifier italic/normal here instead of with a standalone
+ //font node
+ if (((aStyleHelper.nIsItalic == -1) && (aToken.aText.getLength() > 1))
+ || ((aStyleHelper.nIsItalic == 0) && (aToken.aText.getLength() == 1)))
+ {
+ pNode.reset(new SmTextNode(aToken, FNT_FUNCTION));
+ pNode->GetFont().SetItalic(ITALIC_NONE);
+ aStyleHelper.nIsItalic = -1;
+ }
+ else
+ pNode.reset(new SmTextNode(aToken, FNT_VARIABLE));
+ if (aStyleHelper.nIsItalic != -1)
+ {
+ if (aStyleHelper.nIsItalic)
+ pNode->GetFont().SetItalic(ITALIC_NORMAL);
+ else
+ pNode->GetFont().SetItalic(ITALIC_NONE);
+ aStyleHelper.nIsItalic = -1;
+ }
+ GetSmImport().GetNodeStack().push_front(std::move(pNode));
+ aStyleHelper.ApplyAttrs();
+
+ maTokenAttrHelper.ApplyAttrs((aToken.aText.getLength() == 1) ? MathMLMathvariantValue::Italic
+ : MathMLMathvariantValue::Normal);
+}
+
+void SmXMLIdentifierContext_Impl::TCharacters(const OUString& rChars) { aToken.aText = rChars; }
+
+namespace
+{
+class SmXMLOperatorContext_Impl : public SmXMLImportContext
+{
+ SmXMLTokenAttrHelper maTokenAttrHelper;
+ bool bIsStretchy;
+ bool bIsFenced;
+ bool isPrefix;
+ bool isInfix;
+ bool isPostfix;
+ SmToken aToken;
+
+public:
+ SmXMLOperatorContext_Impl(SmXMLImport& rImport)
+ : SmXMLImportContext(rImport)
+ , maTokenAttrHelper(*this)
+ , bIsStretchy(false)
+ , bIsFenced(false)
+ , isPrefix(false)
+ , isInfix(false)
+ , isPostfix(false)
+ {
+ aToken.eType = TSPECIAL;
+ aToken.nLevel = 5;
+ }
+
+ void TCharacters(const OUString& rChars) override;
+ void SAL_CALL startFastElement(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+}
+
+void SmXMLOperatorContext_Impl::TCharacters(const OUString& rChars)
+{
+ aToken.setChar(rChars[0]);
+ SmToken bToken;
+ if (bIsFenced)
+ {
+ if (isPrefix)
+ bToken
+ = starmathdatabase::Identify_Prefix_SmXMLOperatorContext_Impl(aToken.cMathChar[0]);
+ else if (isInfix)
+ bToken = SmToken(TMLINE, MS_VERTLINE, "mline", TG::NONE, 0);
+ else if (isPostfix)
+ bToken
+ = starmathdatabase::Identify_Postfix_SmXMLOperatorContext_Impl(aToken.cMathChar[0]);
+ else
+ bToken = starmathdatabase::Identify_PrefixPostfix_SmXMLOperatorContext_Impl(
+ aToken.cMathChar[0]);
+ }
+ else
+ bToken = starmathdatabase::Identify_SmXMLOperatorContext_Impl(aToken.cMathChar[0],
+ bIsStretchy);
+ if (bToken.eType != TERROR)
+ aToken = bToken;
+}
+
+void SmXMLOperatorContext_Impl::endFastElement(sal_Int32)
+{
+ std::unique_ptr<SmMathSymbolNode> pNode(new SmMathSymbolNode(aToken));
+ //For stretchy scaling the scaling must be retrieved from this node
+ //and applied to the expression itself so as to get the expression
+ //to scale the operator to the height of the expression itself
+ if (bIsStretchy)
+ pNode->SetScaleMode(SmScaleMode::Height);
+ GetSmImport().GetNodeStack().push_front(std::move(pNode));
+
+ // TODO: apply to non-alphabetic characters too
+ if (rtl::isAsciiAlpha(aToken.cMathChar[0]))
+ maTokenAttrHelper.ApplyAttrs(MathMLMathvariantValue::Normal);
+}
+
+void SmXMLOperatorContext_Impl::startFastElement(
+ sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ maTokenAttrHelper.RetrieveAttrs(xAttrList);
+
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_STRETCHY:
+ bIsStretchy = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_FENCE:
+ bIsFenced = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_FORM:
+ isPrefix = IsXMLToken(aIter, XML_PREFIX); // <
+ isInfix = IsXMLToken(aIter, XML_INFIX); // |
+ isPostfix = IsXMLToken(aIter, XML_POSTFIX); // >
+ break;
+ default:
+ XMLOFF_WARN_UNKNOWN("starmath", aIter);
+ break;
+ }
+ }
+}
+
+namespace
+{
+class SmXMLSpaceContext_Impl : public SmXMLImportContext
+{
+public:
+ SmXMLSpaceContext_Impl(SmXMLImport& rImport)
+ : SmXMLImportContext(rImport)
+ {
+ }
+
+ void SAL_CALL startFastElement(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+bool lcl_CountBlanks(const MathMLAttributeLengthValue& rLV, sal_Int32* pWide, sal_Int32* pNarrow)
+{
+ assert(pWide);
+ assert(pNarrow);
+ if (rLV.aNumber.GetNumerator() == 0)
+ {
+ *pWide = *pNarrow = 0;
+ return true;
+ }
+ // TODO: honor other units than em
+ if (rLV.eUnit != MathMLLengthUnit::Em)
+ return false;
+ if (rLV.aNumber.GetNumerator() < 0)
+ return false;
+ const Fraction aTwo(2, 1);
+ auto aWide = rLV.aNumber / aTwo;
+ auto nWide = static_cast<sal_Int32>(static_cast<tools::Long>(aWide));
+ if (nWide < 0)
+ return false;
+ const Fraction aPointFive(1, 2);
+ auto aNarrow = (rLV.aNumber - Fraction(nWide, 1) * aTwo) / aPointFive;
+ auto nNarrow = static_cast<sal_Int32>(static_cast<tools::Long>(aNarrow));
+ if (nNarrow < 0)
+ return false;
+ *pWide = nWide;
+ *pNarrow = nNarrow;
+ return true;
+}
+}
+
+void SmXMLSpaceContext_Impl::startFastElement(
+ sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ // There is no syntax in Math to specify blank nodes of arbitrary size yet.
+ MathMLAttributeLengthValue aLV;
+ sal_Int32 nWide = 0, nNarrow = 0;
+
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ OUString sValue = aIter.toString();
+ switch (aIter.getToken())
+ {
+ case XML_WIDTH:
+ if (!ParseMathMLAttributeLengthValue(o3tl::trim(sValue), aLV)
+ || !lcl_CountBlanks(aLV, &nWide, &nNarrow))
+ SAL_WARN("starmath", "ignore mspace's width: " << sValue);
+ break;
+ default:
+ XMLOFF_WARN_UNKNOWN("starmath", aIter);
+ break;
+ }
+ }
+ SmToken aToken;
+ aToken.eType = TBLANK;
+ aToken.cMathChar = u"";
+ aToken.nGroup = TG::Blank;
+ aToken.nLevel = 5;
+ std::unique_ptr<SmBlankNode> pBlank(new SmBlankNode(aToken));
+ if (nWide > 0)
+ pBlank->IncreaseBy(aToken, nWide);
+ if (nNarrow > 0)
+ {
+ aToken.eType = TSBLANK;
+ pBlank->IncreaseBy(aToken, nNarrow);
+ }
+ GetSmImport().GetNodeStack().push_front(std::move(pBlank));
+}
+
+namespace
+{
+class SmXMLSubContext_Impl : public SmXMLRowContext_Impl
+{
+protected:
+ void GenericEndElement(SmTokenType eType, SmSubSup aSubSup);
+
+public:
+ SmXMLSubContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32) override { GenericEndElement(TRSUB, RSUB); }
+};
+}
+
+void SmXMLSubContext_Impl::GenericEndElement(SmTokenType eType, SmSubSup eSubSup)
+{
+ /*The <msub> element requires exactly 2 arguments.*/
+ const bool bNodeCheck = GetSmImport().GetNodeStack().size() - nElementCount == 2;
+ OSL_ENSURE(bNodeCheck, "Sub has not two arguments");
+ if (!bNodeCheck)
+ return;
+
+ SmToken aToken;
+ aToken.cMathChar = u"";
+ aToken.eType = eType;
+ std::unique_ptr<SmSubSupNode> pNode(new SmSubSupNode(aToken));
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+
+ // initialize subnodes array
+ SmNodeArray aSubNodes;
+ aSubNodes.resize(1 + SUBSUP_NUM_ENTRIES);
+ for (size_t i = 1; i < aSubNodes.size(); i++)
+ aSubNodes[i] = nullptr;
+
+ aSubNodes[eSubSup + 1] = popOrZero(rNodeStack).release();
+ aSubNodes[0] = popOrZero(rNodeStack).release();
+ pNode->SetSubNodes(std::move(aSubNodes));
+ rNodeStack.push_front(std::move(pNode));
+}
+
+namespace
+{
+class SmXMLSupContext_Impl : public SmXMLSubContext_Impl
+{
+public:
+ SmXMLSupContext_Impl(SmXMLImport& rImport)
+ : SmXMLSubContext_Impl(rImport)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32) override { GenericEndElement(TRSUP, RSUP); }
+};
+
+class SmXMLSubSupContext_Impl : public SmXMLRowContext_Impl
+{
+protected:
+ void GenericEndElement(SmTokenType eType, SmSubSup aSub, SmSubSup aSup);
+
+public:
+ SmXMLSubSupContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32) override { GenericEndElement(TRSUB, RSUB, RSUP); }
+};
+}
+
+void SmXMLSubSupContext_Impl::GenericEndElement(SmTokenType eType, SmSubSup aSub, SmSubSup aSup)
+{
+ /*The <msub> element requires exactly 3 arguments.*/
+ const bool bNodeCheck = GetSmImport().GetNodeStack().size() - nElementCount == 3;
+ OSL_ENSURE(bNodeCheck, "SubSup has not three arguments");
+ if (!bNodeCheck)
+ return;
+
+ SmToken aToken;
+ aToken.cMathChar = u"";
+ aToken.eType = eType;
+ std::unique_ptr<SmSubSupNode> pNode(new SmSubSupNode(aToken));
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+
+ // initialize subnodes array
+ SmNodeArray aSubNodes;
+ aSubNodes.resize(1 + SUBSUP_NUM_ENTRIES);
+ for (size_t i = 1; i < aSubNodes.size(); i++)
+ aSubNodes[i] = nullptr;
+
+ aSubNodes[aSup + 1] = popOrZero(rNodeStack).release();
+ aSubNodes[aSub + 1] = popOrZero(rNodeStack).release();
+ aSubNodes[0] = popOrZero(rNodeStack).release();
+ pNode->SetSubNodes(std::move(aSubNodes));
+ rNodeStack.push_front(std::move(pNode));
+}
+
+namespace
+{
+class SmXMLUnderContext_Impl : public SmXMLSubContext_Impl
+{
+protected:
+ sal_Int16 nAttrCount;
+
+public:
+ SmXMLUnderContext_Impl(SmXMLImport& rImport)
+ : SmXMLSubContext_Impl(rImport)
+ , nAttrCount(0)
+ {
+ }
+
+ void SAL_CALL startFastElement(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+ void HandleAccent();
+};
+}
+
+void SmXMLUnderContext_Impl::startFastElement(
+ sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ sax_fastparser::FastAttributeList& rAttribList
+ = sax_fastparser::castToFastAttributeList(xAttrList);
+ nAttrCount = rAttribList.getFastAttributeTokens().size();
+}
+
+void SmXMLUnderContext_Impl::HandleAccent()
+{
+ const bool bNodeCheck = GetSmImport().GetNodeStack().size() - nElementCount == 2;
+ OSL_ENSURE(bNodeCheck, "Sub has not two arguments");
+ if (!bNodeCheck)
+ return;
+
+ /*Just one special case for the underline thing*/
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+ std::unique_ptr<SmNode> pTest = popOrZero(rNodeStack);
+ SmToken aToken;
+ aToken.cMathChar = u"";
+ aToken.eType = TUNDERLINE;
+
+ std::unique_ptr<SmNode> pFirst;
+ std::unique_ptr<SmStructureNode> pNode(new SmAttributeNode(aToken));
+ if ((pTest->GetToken().cMathChar[0] & 0x0FFF) == 0x0332)
+ {
+ pFirst.reset(new SmRectangleNode(aToken));
+ }
+ else
+ pFirst = std::move(pTest);
+
+ std::unique_ptr<SmNode> pSecond = popOrZero(rNodeStack);
+ pNode->SetSubNodes(std::move(pFirst), std::move(pSecond));
+ pNode->SetScaleMode(SmScaleMode::Width);
+ rNodeStack.push_front(std::move(pNode));
+}
+
+void SmXMLUnderContext_Impl::endFastElement(sal_Int32)
+{
+ if (!nAttrCount)
+ GenericEndElement(TCSUB, CSUB);
+ else
+ HandleAccent();
+}
+
+namespace
+{
+class SmXMLOverContext_Impl : public SmXMLSubContext_Impl
+{
+protected:
+ sal_Int16 nAttrCount;
+
+public:
+ SmXMLOverContext_Impl(SmXMLImport& rImport)
+ : SmXMLSubContext_Impl(rImport)
+ , nAttrCount(0)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+ void SAL_CALL startFastElement(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
+ void HandleAccent();
+};
+}
+
+void SmXMLOverContext_Impl::startFastElement(
+ sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ sax_fastparser::FastAttributeList& rAttribList
+ = sax_fastparser::castToFastAttributeList(xAttrList);
+ nAttrCount = rAttribList.getFastAttributeTokens().size();
+}
+
+void SmXMLOverContext_Impl::endFastElement(sal_Int32)
+{
+ if (!nAttrCount)
+ GenericEndElement(TCSUP, CSUP);
+ else
+ HandleAccent();
+}
+
+void SmXMLOverContext_Impl::HandleAccent()
+{
+ const bool bNodeCheck = GetSmImport().GetNodeStack().size() - nElementCount == 2;
+ OSL_ENSURE(bNodeCheck, "Sub has not two arguments");
+ if (!bNodeCheck)
+ return;
+
+ SmToken aToken;
+ aToken.cMathChar = u"";
+ aToken.eType = TACUTE;
+
+ std::unique_ptr<SmAttributeNode> pNode(new SmAttributeNode(aToken));
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+
+ std::unique_ptr<SmNode> pFirst = popOrZero(rNodeStack);
+ std::unique_ptr<SmNode> pSecond = popOrZero(rNodeStack);
+ pNode->SetSubNodes(std::move(pFirst), std::move(pSecond));
+ pNode->SetScaleMode(SmScaleMode::Width);
+ rNodeStack.push_front(std::move(pNode));
+}
+
+namespace
+{
+class SmXMLUnderOverContext_Impl : public SmXMLSubSupContext_Impl
+{
+public:
+ SmXMLUnderOverContext_Impl(SmXMLImport& rImport)
+ : SmXMLSubSupContext_Impl(rImport)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32) override { GenericEndElement(TCSUB, CSUB, CSUP); }
+};
+
+class SmXMLMultiScriptsContext_Impl : public SmXMLSubSupContext_Impl
+{
+ bool bHasPrescripts;
+
+ void ProcessSubSupPairs(bool bIsPrescript);
+
+public:
+ SmXMLMultiScriptsContext_Impl(SmXMLImport& rImport)
+ : SmXMLSubSupContext_Impl(rImport)
+ , bHasPrescripts(false)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+ virtual uno::Reference<xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+class SmXMLNoneContext_Impl : public SmXMLImportContext
+{
+public:
+ SmXMLNoneContext_Impl(SmXMLImport& rImport)
+ : SmXMLImportContext(rImport)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+}
+
+void SmXMLNoneContext_Impl::endFastElement(sal_Int32)
+{
+ SmToken aToken;
+ aToken.cMathChar = u"";
+ aToken.aText.clear();
+ aToken.nLevel = 5;
+ aToken.eType = TIDENT;
+ GetSmImport().GetNodeStack().push_front(std::make_unique<SmTextNode>(aToken, FNT_VARIABLE));
+}
+
+namespace
+{
+class SmXMLPrescriptsContext_Impl : public SmXMLImportContext
+{
+public:
+ SmXMLPrescriptsContext_Impl(SmXMLImport& rImport)
+ : SmXMLImportContext(rImport)
+ {
+ }
+};
+
+class SmXMLTableRowContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ SmXMLTableRowContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ {
+ }
+
+ virtual uno::Reference<xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+class SmXMLTableContext_Impl : public SmXMLTableRowContext_Impl
+{
+public:
+ SmXMLTableContext_Impl(SmXMLImport& rImport)
+ : SmXMLTableRowContext_Impl(rImport)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+ virtual uno::Reference<xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+class SmXMLTableCellContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ SmXMLTableCellContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ {
+ }
+};
+
+class SmXMLAlignGroupContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ SmXMLAlignGroupContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ {
+ }
+
+ /*Don't do anything with alignment for now*/
+};
+
+class SmXMLActionContext_Impl : public SmXMLRowContext_Impl
+{
+ size_t mnSelection; // 1-based
+
+public:
+ SmXMLActionContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ , mnSelection(1)
+ {
+ }
+
+ void SAL_CALL startFastElement(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+// NB: virtually inherit so we can multiply inherit properly
+// in SmXMLFlatDocContext_Impl
+class SmXMLOfficeContext_Impl : public virtual SvXMLImportContext
+{
+public:
+ SmXMLOfficeContext_Impl(SmXMLImport& rImport)
+ : SvXMLImportContext(rImport)
+ {
+ }
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+}
+
+uno::Reference<xml::sax::XFastContextHandler> SmXMLOfficeContext_Impl::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& /*xAttrList*/)
+{
+ if (nElement == XML_ELEMENT(OFFICE, XML_META))
+ {
+ SAL_WARN("starmath",
+ "XML_TOK_DOC_META: should not have come here, maybe document is invalid?");
+ }
+ else if (nElement == XML_ELEMENT(OFFICE, XML_SETTINGS))
+ {
+ return new XMLDocumentSettingsContext(GetImport());
+ }
+ return nullptr;
+}
+
+namespace
+{
+// context for flat file xml format
+class SmXMLFlatDocContext_Impl : public SmXMLOfficeContext_Impl, public SvXMLMetaDocumentContext
+{
+public:
+ SmXMLFlatDocContext_Impl(SmXMLImport& i_rImport,
+ const uno::Reference<document::XDocumentProperties>& i_xDocProps);
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+}
+
+SmXMLFlatDocContext_Impl::SmXMLFlatDocContext_Impl(
+ SmXMLImport& i_rImport, const uno::Reference<document::XDocumentProperties>& i_xDocProps)
+ : SvXMLImportContext(i_rImport)
+ , SmXMLOfficeContext_Impl(i_rImport)
+ , SvXMLMetaDocumentContext(i_rImport, i_xDocProps)
+{
+}
+
+uno::Reference<xml::sax::XFastContextHandler>
+ SAL_CALL SmXMLFlatDocContext_Impl::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ // behave like meta base class iff we encounter office:meta
+ if (nElement == XML_ELEMENT(OFFICE, XML_META))
+ {
+ return SvXMLMetaDocumentContext::createFastChildContext(nElement, xAttrList);
+ }
+ else
+ {
+ return SmXMLOfficeContext_Impl::createFastChildContext(nElement, xAttrList);
+ }
+}
+
+uno::Reference<xml::sax::XFastContextHandler> SmXMLDocContext_Impl::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& /*xAttrList*/)
+{
+ uno::Reference<xml::sax::XFastContextHandler> xContext;
+
+ switch (nElement)
+ {
+ //Consider semantics a dummy except for any starmath annotations
+ case XML_ELEMENT(MATH, XML_SEMANTICS):
+ xContext = new SmXMLRowContext_Impl(GetSmImport());
+ break;
+ /*General Layout Schemata*/
+ case XML_ELEMENT(MATH, XML_MROW):
+ xContext = new SmXMLRowContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MENCLOSE):
+ xContext = new SmXMLEncloseContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MFRAC):
+ xContext = new SmXMLFracContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MSQRT):
+ xContext = new SmXMLSqrtContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MROOT):
+ xContext = new SmXMLRootContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MSTYLE):
+ xContext = new SmXMLStyleContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MERROR):
+ xContext = new SmXMLErrorContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MPADDED):
+ xContext = new SmXMLPaddedContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MPHANTOM):
+ xContext = new SmXMLPhantomContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MFENCED):
+ xContext = new SmXMLFencedContext_Impl(GetSmImport());
+ break;
+ /*Script and Limit Schemata*/
+ case XML_ELEMENT(MATH, XML_MSUB):
+ xContext = new SmXMLSubContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MSUP):
+ xContext = new SmXMLSupContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MSUBSUP):
+ xContext = new SmXMLSubSupContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MUNDER):
+ xContext = new SmXMLUnderContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MOVER):
+ xContext = new SmXMLOverContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MUNDEROVER):
+ xContext = new SmXMLUnderOverContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MMULTISCRIPTS):
+ xContext = new SmXMLMultiScriptsContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MTABLE):
+ xContext = new SmXMLTableContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MACTION):
+ xContext = new SmXMLActionContext_Impl(GetSmImport());
+ break;
+ default:
+ /*Basically there's an implicit mrow around certain bare
+ *elements, use a RowContext to see if this is one of
+ *those ones*/
+ rtl::Reference<SmXMLRowContext_Impl> aTempContext(
+ new SmXMLRowContext_Impl(GetSmImport()));
+
+ xContext = aTempContext->StrictCreateChildContext(nElement);
+ break;
+ }
+ return xContext;
+}
+
+void SmXMLDocContext_Impl::endFastElement(sal_Int32)
+{
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+
+ std::unique_ptr<SmNode> pContextNode = popOrZero(rNodeStack);
+
+ SmToken aDummy;
+ std::unique_ptr<SmStructureNode> pSNode(new SmLineNode(aDummy));
+ pSNode->SetSubNodes(std::move(pContextNode), nullptr);
+ rNodeStack.push_front(std::move(pSNode));
+
+ SmNodeArray LineArray;
+ auto n = rNodeStack.size();
+ LineArray.resize(n);
+ for (size_t j = 0; j < n; j++)
+ {
+ auto pNode = std::move(rNodeStack.front());
+ rNodeStack.pop_front();
+ LineArray[n - (j + 1)] = pNode.release();
+ }
+ std::unique_ptr<SmStructureNode> pSNode2(new SmTableNode(aDummy));
+ pSNode2->SetSubNodes(std::move(LineArray));
+ rNodeStack.push_front(std::move(pSNode2));
+}
+
+void SmXMLFracContext_Impl::endFastElement(sal_Int32)
+{
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+ const bool bNodeCheck = rNodeStack.size() - nElementCount == 2;
+ OSL_ENSURE(bNodeCheck, "Fraction (mfrac) tag is missing component");
+ if (!bNodeCheck)
+ return;
+
+ SmToken aToken;
+ aToken.cMathChar = u"";
+ aToken.eType = TFRAC;
+ std::unique_ptr<SmStructureNode> pSNode(new SmBinVerNode(aToken));
+ std::unique_ptr<SmNode> pOper(new SmRectangleNode(aToken));
+ std::unique_ptr<SmNode> pSecond = popOrZero(rNodeStack);
+ std::unique_ptr<SmNode> pFirst = popOrZero(rNodeStack);
+ pSNode->SetSubNodes(std::move(pFirst), std::move(pOper), std::move(pSecond));
+ rNodeStack.push_front(std::move(pSNode));
+}
+
+void SmXMLRootContext_Impl::endFastElement(sal_Int32)
+{
+ /*The <mroot> element requires exactly 2 arguments.*/
+ const bool bNodeCheck = GetSmImport().GetNodeStack().size() - nElementCount == 2;
+ OSL_ENSURE(bNodeCheck, "Root tag is missing component");
+ if (!bNodeCheck)
+ return;
+
+ SmToken aToken;
+ aToken.setChar(MS_SQRT); //Temporary: alert, based on StarSymbol font
+ aToken.eType = TNROOT;
+ std::unique_ptr<SmStructureNode> pSNode(new SmRootNode(aToken));
+ std::unique_ptr<SmNode> pOper(new SmRootSymbolNode(aToken));
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+ std::unique_ptr<SmNode> pIndex = popOrZero(rNodeStack);
+ std::unique_ptr<SmNode> pBase = popOrZero(rNodeStack);
+ pSNode->SetSubNodes(std::move(pIndex), std::move(pOper), std::move(pBase));
+ rNodeStack.push_front(std::move(pSNode));
+}
+
+void SmXMLSqrtContext_Impl::endFastElement(sal_Int32 nElement)
+{
+ /*
+ <msqrt> accepts any number of arguments; if this number is not 1, its
+ contents are treated as a single "inferred <mrow>" containing its
+ arguments
+ */
+ if (GetSmImport().GetNodeStack().size() - nElementCount != 1)
+ SmXMLRowContext_Impl::endFastElement(nElement);
+
+ SmToken aToken;
+ aToken.setChar(MS_SQRT); //Temporary: alert, based on StarSymbol font
+ aToken.eType = TSQRT;
+ std::unique_ptr<SmStructureNode> pSNode(new SmRootNode(aToken));
+ std::unique_ptr<SmNode> pOper(new SmRootSymbolNode(aToken));
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+ pSNode->SetSubNodes(nullptr, std::move(pOper), popOrZero(rNodeStack));
+ rNodeStack.push_front(std::move(pSNode));
+}
+
+void SmXMLRowContext_Impl::endFastElement(sal_Int32)
+{
+ SmNodeArray aRelationArray;
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+
+ if (rNodeStack.size() > nElementCount)
+ {
+ auto nSize = rNodeStack.size() - nElementCount;
+
+ aRelationArray.resize(nSize);
+ for (auto j = nSize; j > 0; j--)
+ {
+ auto pNode = std::move(rNodeStack.front());
+ rNodeStack.pop_front();
+ aRelationArray[j - 1] = pNode.release();
+ }
+
+ //If the first or last element is an operator with stretchyness
+ //set then we must create a brace node here from those elements,
+ //removing the stretchness from the operators and applying it to
+ //ourselves, and creating the appropriate dummy StarMath none bracket
+ //to balance the arrangement
+ if (((aRelationArray[0]->GetScaleMode() == SmScaleMode::Height)
+ && (aRelationArray[0]->GetType() == SmNodeType::Math))
+ || ((aRelationArray[nSize - 1]->GetScaleMode() == SmScaleMode::Height)
+ && (aRelationArray[nSize - 1]->GetType() == SmNodeType::Math)))
+ {
+ SmToken aToken;
+ aToken.cMathChar = u"";
+ aToken.nLevel = 5;
+
+ int nLeft = 0, nRight = 0;
+ if ((aRelationArray[0]->GetScaleMode() == SmScaleMode::Height)
+ && (aRelationArray[0]->GetType() == SmNodeType::Math))
+ {
+ aToken = aRelationArray[0]->GetToken();
+ nLeft = 1;
+ }
+ else
+ aToken.cMathChar = u"";
+
+ aToken.eType = TLPARENT;
+ std::unique_ptr<SmNode> pLeft(new SmMathSymbolNode(aToken));
+
+ if ((aRelationArray[nSize - 1]->GetScaleMode() == SmScaleMode::Height)
+ && (aRelationArray[nSize - 1]->GetType() == SmNodeType::Math))
+ {
+ aToken = aRelationArray[nSize - 1]->GetToken();
+ nRight = 1;
+ }
+ else
+ aToken.cMathChar = u"";
+
+ aToken.eType = TRPARENT;
+ std::unique_ptr<SmNode> pRight(new SmMathSymbolNode(aToken));
+
+ SmNodeArray aRelationArray2;
+
+ //!! nSize-nLeft-nRight may be < 0 !!
+ int nRelArrSize = nSize - nLeft - nRight;
+ if (nRelArrSize > 0)
+ {
+ aRelationArray2.resize(nRelArrSize);
+ for (int i = 0; i < nRelArrSize; i++)
+ {
+ aRelationArray2[i] = aRelationArray[i + nLeft];
+ aRelationArray[i + nLeft] = nullptr;
+ }
+ }
+
+ SmToken aDummy;
+ std::unique_ptr<SmStructureNode> pSNode(new SmBraceNode(aToken));
+ std::unique_ptr<SmStructureNode> pBody(new SmExpressionNode(aDummy));
+ pBody->SetSubNodes(std::move(aRelationArray2));
+
+ pSNode->SetSubNodes(std::move(pLeft), std::move(pBody), std::move(pRight));
+ pSNode->SetScaleMode(SmScaleMode::Height);
+ rNodeStack.push_front(std::move(pSNode));
+
+ for (auto a : aRelationArray)
+ delete a;
+
+ return;
+ }
+ }
+ else
+ {
+ // The elements msqrt, mstyle, merror, menclose, mpadded, mphantom, mtd, and math
+ // treat their content as a single inferred mrow in case their content is empty.
+ // Here an empty group {} is used to catch those cases and transform them without error
+ // to StarMath.
+ aRelationArray.resize(2);
+ SmToken aToken;
+ aToken.setChar(MS_LBRACE);
+ aToken.nLevel = 5;
+ aToken.eType = TLGROUP;
+ aToken.nGroup = TG::NONE;
+ aToken.aText = "{";
+ aRelationArray[0] = new SmLineNode(aToken);
+
+ aToken.setChar(MS_RBRACE);
+ aToken.nLevel = 0;
+ aToken.eType = TRGROUP;
+ aToken.nGroup = TG::NONE;
+ aToken.aText = "}";
+ aRelationArray[1] = new SmLineNode(aToken);
+ }
+
+ SmToken aDummy;
+ std::unique_ptr<SmStructureNode> pSNode(new SmExpressionNode(aDummy));
+ pSNode->SetSubNodes(std::move(aRelationArray));
+ rNodeStack.push_front(std::move(pSNode));
+}
+
+uno::Reference<xml::sax::XFastContextHandler>
+SmXMLRowContext_Impl::StrictCreateChildContext(sal_Int32 nElement)
+{
+ uno::Reference<xml::sax::XFastContextHandler> pContext;
+
+ switch (nElement)
+ {
+ /*Note that these should accept malignmark subelements, but do not*/
+ case XML_ELEMENT(MATH, XML_MN):
+ pContext = new SmXMLNumberContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MI):
+ pContext = new SmXMLIdentifierContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MO):
+ pContext = new SmXMLOperatorContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MTEXT):
+ pContext = new SmXMLTextContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MSPACE):
+ pContext = new SmXMLSpaceContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MS):
+ pContext = new SmXMLStringContext_Impl(GetSmImport());
+ break;
+
+ /*Note: The maligngroup should only be seen when the row
+ * (or descendants) are in a table*/
+ case XML_ELEMENT(MATH, XML_MALIGNGROUP):
+ pContext = new SmXMLAlignGroupContext_Impl(GetSmImport());
+ break;
+
+ case XML_ELEMENT(MATH, XML_ANNOTATION):
+ pContext = new SmXMLAnnotationContext_Impl(GetSmImport());
+ break;
+
+ default:
+ break;
+ }
+ return pContext;
+}
+
+uno::Reference<xml::sax::XFastContextHandler> SmXMLRowContext_Impl::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ uno::Reference<xml::sax::XFastContextHandler> xContext = StrictCreateChildContext(nElement);
+
+ if (!xContext)
+ {
+ //Hmm, unrecognized for this level, check to see if it's
+ //an element that can have an implicit schema around it
+ xContext = SmXMLDocContext_Impl::createFastChildContext(nElement, xAttrList);
+ }
+ return xContext;
+}
+
+uno::Reference<xml::sax::XFastContextHandler> SmXMLMultiScriptsContext_Impl::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ uno::Reference<xml::sax::XFastContextHandler> xContext;
+
+ switch (nElement)
+ {
+ case XML_ELEMENT(MATH, XML_MPRESCRIPTS):
+ bHasPrescripts = true;
+ ProcessSubSupPairs(false);
+ xContext = new SmXMLPrescriptsContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_NONE):
+ xContext = new SmXMLNoneContext_Impl(GetSmImport());
+ break;
+ default:
+ xContext = SmXMLRowContext_Impl::createFastChildContext(nElement, xAttrList);
+ break;
+ }
+ return xContext;
+}
+
+void SmXMLMultiScriptsContext_Impl::ProcessSubSupPairs(bool bIsPrescript)
+{
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+
+ if (rNodeStack.size() <= nElementCount)
+ return;
+
+ auto nCount = rNodeStack.size() - nElementCount - 1;
+ if (nCount == 0)
+ return;
+
+ if (nCount % 2 == 0)
+ {
+ SmToken aToken;
+ aToken.cMathChar = u"";
+ aToken.eType = bIsPrescript ? TLSUB : TRSUB;
+
+ SmNodeStack aReverseStack;
+ for (size_t i = 0; i < nCount + 1; i++)
+ {
+ auto pNode = std::move(rNodeStack.front());
+ rNodeStack.pop_front();
+ aReverseStack.push_front(std::move(pNode));
+ }
+
+ SmSubSup eSub = bIsPrescript ? LSUB : RSUB;
+ SmSubSup eSup = bIsPrescript ? LSUP : RSUP;
+
+ for (size_t i = 0; i < nCount; i += 2)
+ {
+ std::unique_ptr<SmSubSupNode> pNode(new SmSubSupNode(aToken));
+
+ // initialize subnodes array
+ SmNodeArray aSubNodes(1 + SUBSUP_NUM_ENTRIES);
+
+ /*On each loop the base and its sub sup pair becomes the
+ base for the next loop to which the next sub sup pair is
+ attached, i.e. wheels within wheels*/
+ aSubNodes[0] = popOrZero(aReverseStack).release();
+
+ std::unique_ptr<SmNode> pScriptNode = popOrZero(aReverseStack);
+
+ if (pScriptNode
+ && ((pScriptNode->GetToken().eType != TIDENT)
+ || (!pScriptNode->GetToken().aText.isEmpty())))
+ aSubNodes[eSub + 1] = pScriptNode.release();
+ pScriptNode = popOrZero(aReverseStack);
+ if (pScriptNode
+ && ((pScriptNode->GetToken().eType != TIDENT)
+ || (!pScriptNode->GetToken().aText.isEmpty())))
+ aSubNodes[eSup + 1] = pScriptNode.release();
+
+ pNode->SetSubNodes(std::move(aSubNodes));
+ aReverseStack.push_front(std::move(pNode));
+ }
+ assert(!aReverseStack.empty());
+ auto pNode = std::move(aReverseStack.front());
+ aReverseStack.pop_front();
+ rNodeStack.push_front(std::move(pNode));
+ }
+ else
+ {
+ // Ignore odd number of elements.
+ for (size_t i = 0; i < nCount; i++)
+ {
+ rNodeStack.pop_front();
+ }
+ }
+}
+
+void SmXMLTableContext_Impl::endFastElement(sal_Int32)
+{
+ SmNodeArray aExpressionArray;
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+ SmNodeStack aReverseStack;
+ aExpressionArray.resize(rNodeStack.size() - nElementCount);
+
+ size_t nRows = rNodeStack.size() - nElementCount;
+ size_t nCols = 0;
+
+ for (size_t i = nRows; i > 0; --i)
+ {
+ SmNode* pArray = rNodeStack.front().release();
+ rNodeStack.pop_front();
+ if (pArray->GetNumSubNodes() == 0)
+ {
+ //This is a little tricky, it is possible that there was
+ //be elements that were not inside a <mtd> pair, in which
+ //case they will not be in a row, i.e. they will not have
+ //SubNodes, so we have to wait until here before we can
+ //resolve the situation. Implicit surrounding tags are
+ //surprisingly difficult to get right within this
+ //architecture
+
+ SmNodeArray aRelationArray;
+ aRelationArray.resize(1);
+ aRelationArray[0] = pArray;
+ SmToken aDummy;
+ SmExpressionNode* pExprNode = new SmExpressionNode(aDummy);
+ pExprNode->SetSubNodes(std::move(aRelationArray));
+ pArray = pExprNode;
+ }
+
+ nCols = std::max(nCols, pArray->GetNumSubNodes());
+ aReverseStack.push_front(std::unique_ptr<SmNode>(pArray));
+ }
+ if (nCols > SAL_MAX_UINT16)
+ throw std::range_error("column limit");
+ if (nRows > SAL_MAX_UINT16)
+ throw std::range_error("row limit");
+ aExpressionArray.resize(nCols * nRows);
+ size_t j = 0;
+ for (auto& elem : aReverseStack)
+ {
+ std::unique_ptr<SmStructureNode> xArray(static_cast<SmStructureNode*>(elem.release()));
+ for (size_t i = 0; i < xArray->GetNumSubNodes(); ++i)
+ aExpressionArray[j++] = xArray->GetSubNode(i);
+ xArray->ClearSubNodes();
+ }
+ aReverseStack.clear();
+
+ SmToken aToken;
+ aToken.cMathChar = u"";
+ aToken.eType = TMATRIX;
+ std::unique_ptr<SmMatrixNode> pSNode(new SmMatrixNode(aToken));
+ pSNode->SetSubNodes(std::move(aExpressionArray));
+ pSNode->SetRowCol(nRows, nCols);
+ rNodeStack.push_front(std::move(pSNode));
+}
+
+uno::Reference<xml::sax::XFastContextHandler> SmXMLTableRowContext_Impl::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ uno::Reference<xml::sax::XFastContextHandler> xContext;
+
+ switch (nElement)
+ {
+ case XML_ELEMENT(MATH, XML_MTD):
+ xContext = new SmXMLTableCellContext_Impl(GetSmImport());
+ break;
+ default:
+ xContext = SmXMLRowContext_Impl::createFastChildContext(nElement, xAttrList);
+ break;
+ }
+ return xContext;
+}
+
+uno::Reference<xml::sax::XFastContextHandler> SmXMLTableContext_Impl::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ uno::Reference<xml::sax::XFastContextHandler> xContext;
+
+ switch (nElement)
+ {
+ case XML_ELEMENT(MATH, XML_MTR):
+ xContext = new SmXMLTableRowContext_Impl(GetSmImport());
+ break;
+ default:
+ xContext = SmXMLTableRowContext_Impl::createFastChildContext(nElement, xAttrList);
+ break;
+ }
+ return xContext;
+}
+
+void SmXMLMultiScriptsContext_Impl::endFastElement(sal_Int32)
+{
+ ProcessSubSupPairs(bHasPrescripts);
+}
+
+void SmXMLActionContext_Impl::startFastElement(
+ sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_SELECTION:
+ {
+ sal_Int32 n = aIter.toInt32();
+ if (n > 0)
+ mnSelection = static_cast<size_t>(n);
+ }
+ break;
+ default:
+ XMLOFF_WARN_UNKNOWN("starmath", aIter);
+ break;
+ }
+ }
+}
+
+void SmXMLActionContext_Impl::endFastElement(sal_Int32)
+{
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+ auto nSize = rNodeStack.size();
+ if (nSize <= nElementCount)
+ {
+ // not compliant to maction's specification, e.g., no subexpressions
+ return;
+ }
+ assert(mnSelection > 0);
+ if (nSize < nElementCount + mnSelection)
+ {
+ // No selected subexpression exists, which is a MathML error;
+ // fallback to selecting the first
+ mnSelection = 1;
+ }
+ assert(nSize >= nElementCount + mnSelection);
+ for (auto i = nSize - (nElementCount + mnSelection); i > 0; i--)
+ {
+ rNodeStack.pop_front();
+ }
+ auto pSelected = std::move(rNodeStack.front());
+ rNodeStack.pop_front();
+ for (auto i = rNodeStack.size() - nElementCount; i > 0; i--)
+ {
+ rNodeStack.pop_front();
+ }
+ rNodeStack.push_front(std::move(pSelected));
+}
+
+SvXMLImportContext*
+SmXMLImport::CreateFastContext(sal_Int32 nElement,
+ const uno::Reference<xml::sax::XFastAttributeList>& /*xAttrList*/)
+{
+ SvXMLImportContext* pContext = nullptr;
+
+ switch (nElement)
+ {
+ case XML_ELEMENT(OFFICE, XML_DOCUMENT):
+ case XML_ELEMENT(OFFICE, XML_DOCUMENT_META):
+ {
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(GetModel(),
+ uno::UNO_QUERY_THROW);
+ pContext = ((nElement & TOKEN_MASK) == XML_DOCUMENT_META)
+ ? new SvXMLMetaDocumentContext(*this, xDPS->getDocumentProperties())
+ // flat OpenDocument file format -- this has not been tested...
+ : new SmXMLFlatDocContext_Impl(*this, xDPS->getDocumentProperties());
+ }
+ break;
+ default:
+ if (IsTokenInNamespace(nElement, XML_NAMESPACE_OFFICE))
+ pContext = new SmXMLOfficeContext_Impl(*this);
+ else
+ pContext = new SmXMLDocContext_Impl(*this);
+ }
+ return pContext;
+}
+
+SmXMLImport::~SmXMLImport() noexcept { cleanup(); }
+
+void SmXMLImport::SetViewSettings(const Sequence<PropertyValue>& aViewProps)
+{
+ uno::Reference<frame::XModel> xModel = GetModel();
+ if (!xModel.is())
+ return;
+
+ SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(xModel);
+
+ if (!pModel)
+ return;
+
+ SmDocShell* pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell());
+ if (!pDocShell)
+ return;
+
+ tools::Rectangle aRect(pDocShell->GetVisArea());
+
+ tools::Long nTmp = 0;
+
+ for (const PropertyValue& rValue : aViewProps)
+ {
+ if (rValue.Name == "ViewAreaTop")
+ {
+ rValue.Value >>= nTmp;
+ aRect.SaturatingSetPosY(nTmp);
+ }
+ else if (rValue.Name == "ViewAreaLeft")
+ {
+ rValue.Value >>= nTmp;
+ aRect.SaturatingSetPosX(nTmp);
+ }
+ else if (rValue.Name == "ViewAreaWidth")
+ {
+ rValue.Value >>= nTmp;
+ Size aSize(aRect.GetSize());
+ aSize.setWidth(nTmp);
+ aRect.SaturatingSetSize(aSize);
+ }
+ else if (rValue.Name == "ViewAreaHeight")
+ {
+ rValue.Value >>= nTmp;
+ Size aSize(aRect.GetSize());
+ aSize.setHeight(nTmp);
+ aRect.SaturatingSetSize(aSize);
+ }
+ }
+
+ pDocShell->SetVisArea(aRect);
+}
+
+void SmXMLImport::SetConfigurationSettings(const Sequence<PropertyValue>& aConfProps)
+{
+ uno::Reference<XPropertySet> xProps(GetModel(), UNO_QUERY);
+ if (!xProps.is())
+ return;
+
+ Reference<XPropertySetInfo> xInfo(xProps->getPropertySetInfo());
+ if (!xInfo.is())
+ return;
+
+ static const OUStringLiteral sFormula(u"Formula");
+ static const OUStringLiteral sBasicLibraries(u"BasicLibraries");
+ static const OUStringLiteral sDialogLibraries(u"DialogLibraries");
+ for (const PropertyValue& rValue : aConfProps)
+ {
+ if (rValue.Name != sFormula && rValue.Name != sBasicLibraries
+ && rValue.Name != sDialogLibraries)
+ {
+ try
+ {
+ if (xInfo->hasPropertyByName(rValue.Name))
+ xProps->setPropertyValue(rValue.Name, rValue.Value);
+ }
+ catch (const beans::PropertyVetoException&)
+ {
+ // dealing with read-only properties here. Nothing to do...
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("starmath");
+ }
+ }
+ }
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportMML(SvStream& rStream)
+{
+ SmGlobals::ensure();
+
+ SfxObjectShellLock xDocSh(new SmDocShell(SfxModelFlags::EMBEDDED_OBJECT));
+ xDocSh->DoInitNew();
+ uno::Reference<frame::XModel> xModel(xDocSh->GetModel());
+
+ uno::Reference<beans::XPropertySet> xInfoSet;
+ uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
+ uno::Reference<io::XInputStream> xStream(new utl::OSeekableInputStreamWrapper(rStream));
+
+ //SetLoading hack because the document properties will be re-initted
+ //by the xml filter and during the init, while it's considered uninitialized,
+ //setting a property will inform the document it's modified, which attempts
+ //to update the properties, which throws cause the properties are uninitialized
+ xDocSh->SetLoading(SfxLoadedFlags::NONE);
+
+ ErrCode nRet = ERRCODE_SFX_DOLOADFAILED;
+
+ try
+ {
+ nRet = SmXMLImportWrapper::ReadThroughComponent(xStream, xModel, xContext, xInfoSet,
+ "com.sun.star.comp.Math.XMLImporter", false,
+ false);
+ }
+ catch (...)
+ {
+ }
+
+ xDocSh->SetLoading(SfxLoadedFlags::ALL);
+
+ xDocSh->DoClose();
+
+ return nRet != ERRCODE_NONE;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */