2666 lines
86 KiB
C++
2666 lines
86 KiB
C++
/* -*- 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 <comphelper/diagnose_ex.hxx>
|
|
#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::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;
|
|
|
|
const uno::Reference<uno::XComponentContext>& xContext(
|
|
comphelper::getProcessComponentContext());
|
|
|
|
OSL_ENSURE(m_xModel.is(), "XMLReader::Read: got no model");
|
|
|
|
// try to get an XStatusIndicator from the Medium
|
|
uno::Reference<task::XStatusIndicator> xStatusIndicator;
|
|
|
|
bool bEmbedded = false;
|
|
SmModel* pModel = m_xModel.get();
|
|
|
|
SmDocShell* pDocShell = pModel ? static_cast<SmDocShell*>(pModel->GetObjectShell()) : nullptr;
|
|
if (pDocShell)
|
|
{
|
|
OSL_ENSURE(pDocShell->GetMedium() == &rMedium, "different SfxMedium found");
|
|
|
|
const SfxUnoAnyItem* pItem = rMedium.GetItemSet().GetItem(SID_PROGRESS_STATUSBAR_CONTROL);
|
|
if (pItem)
|
|
pItem->GetValue() >>= xStatusIndicator;
|
|
|
|
if (SfxObjectCreateMode::EMBEDDED == pDocShell->GetCreateMode())
|
|
bEmbedded = true;
|
|
}
|
|
|
|
static const comphelper::PropertyMapEntry aInfoMap[]
|
|
= { { u"PrivateData"_ustr, 0, cppu::UnoType<XInterface>::get(),
|
|
beans::PropertyAttribute::MAYBEVOID, 0 },
|
|
{ u"BaseURI"_ustr, 0, ::cppu::UnoType<OUString>::get(),
|
|
beans::PropertyAttribute::MAYBEVOID, 0 },
|
|
{ u"StreamRelPath"_ustr, 0, ::cppu::UnoType<OUString>::get(),
|
|
beans::PropertyAttribute::MAYBEVOID, 0 },
|
|
{ u"StreamName"_ustr, 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(u"BaseURI"_ustr, 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(u"dummyObjName"_ustr);
|
|
const SfxStringItem* pDocHierarchItem
|
|
= rMedium.GetItemSet().GetItem(SID_DOC_HIERARCHICALNAME);
|
|
if (pDocHierarchItem)
|
|
aName = pDocHierarchItem->GetValue();
|
|
|
|
if (!aName.isEmpty())
|
|
{
|
|
xInfoSet->setPropertyValue(u"StreamRelPath"_ustr, Any(aName));
|
|
}
|
|
}
|
|
|
|
bool bOASIS = (SotStorage::GetVersion(rMedium.GetStorage()) > SOFFICE_FILEFORMAT_60);
|
|
if (xStatusIndicator.is())
|
|
xStatusIndicator->setValue(nSteps++);
|
|
|
|
auto nWarn
|
|
= ReadThroughComponent(rMedium.GetStorage(), m_xModel, "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(), m_xModel, "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(), m_xModel, "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, m_xModel, 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 = dynamic_cast<SmXMLImport*>(xFilter.get());
|
|
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 = std::move(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(u"Encrypted"_ustr);
|
|
bool bEncrypted = false;
|
|
if (aAny.getValueType() == cppu::UnoType<bool>::get())
|
|
aAny >>= bEncrypted;
|
|
|
|
// set Base URL
|
|
if (rPropSet.is())
|
|
{
|
|
rPropSet->setPropertyValue(u"StreamName"_ustr, 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(SmModule::get()->GetConfig()->GetDefaultSmSyntaxVersion())
|
|
{
|
|
}
|
|
|
|
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, u"com.sun.star.comp.Math.XMLImporter"_ustr, 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, u"com.sun.star.comp.Math.XMLOasisMetaImporter"_ustr,
|
|
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, u"com.sun.star.comp.Math.XMLOasisSettingsImporter"_ustr, SvXMLImportFlags::SETTINGS));
|
|
}
|
|
|
|
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 = dynamic_cast<SmModel*>(xModel.get());
|
|
|
|
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 aChars2 = rChars.trim();
|
|
if (!aChars2.isEmpty())
|
|
TCharacters(aChars2 /*.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:
|
|
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""_ustr;
|
|
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:
|
|
case MathMLMathvariantValue::BoldFraktur:
|
|
// TODO: Fraktur
|
|
case MathMLMathvariantValue::BoldScript:
|
|
// TODO: Script
|
|
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::Script:
|
|
// TODO
|
|
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""_ustr;
|
|
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""_ustr;
|
|
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:
|
|
OUString cBegin;
|
|
OUString 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();
|
|
break;
|
|
case XML_CLOSE:
|
|
cEnd = aIter.toString();
|
|
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""_ustr;
|
|
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, u"("_ustr, 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, u")"_ustr, TG::LBrace, 5);
|
|
std::unique_ptr<SmNode> pRight(new SmMathSymbolNode(aToken));
|
|
|
|
SmNodeArray aRelationArray;
|
|
SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
|
|
aToken.cMathChar = u""_ustr;
|
|
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""_ustr;
|
|
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""_ustr;
|
|
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""_ustr;
|
|
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""_ustr;
|
|
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);
|
|
SmToken bToken;
|
|
if (bIsFenced)
|
|
{
|
|
if (isPrefix)
|
|
bToken = starmathdatabase::Identify_Prefix_SmXMLOperatorContext_Impl(aToken.cMathChar);
|
|
else if (isInfix)
|
|
bToken = SmToken(TMLINE, MS_VERTLINE, u"mline"_ustr, TG::NONE, 0);
|
|
else if (isPostfix)
|
|
bToken = starmathdatabase::Identify_Postfix_SmXMLOperatorContext_Impl(aToken.cMathChar);
|
|
else
|
|
bToken = starmathdatabase::Identify_PrefixPostfix_SmXMLOperatorContext_Impl(
|
|
aToken.cMathChar);
|
|
}
|
|
else
|
|
bToken
|
|
= starmathdatabase::Identify_SmXMLOperatorContext_Impl(aToken.cMathChar, bIsStretchy);
|
|
if (bToken.eType != TERROR)
|
|
aToken = std::move(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""_ustr;
|
|
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""_ustr;
|
|
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""_ustr;
|
|
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""_ustr;
|
|
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""_ustr;
|
|
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""_ustr;
|
|
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):
|
|
/*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""_ustr;
|
|
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""_ustr;
|
|
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""_ustr;
|
|
|
|
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""_ustr;
|
|
|
|
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""_ustr;
|
|
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""_ustr;
|
|
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 = dynamic_cast<SmModel*>(xModel.get());
|
|
|
|
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 constexpr OUStringLiteral sFormula(u"Formula");
|
|
static constexpr OUStringLiteral sBasicLibraries(u"BasicLibraries");
|
|
static constexpr 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;
|
|
const 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: */
|