summaryrefslogtreecommitdiffstats
path: root/starmath/source/mathmlexport.cxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--starmath/source/mathmlexport.cxx1526
1 files changed, 1526 insertions, 0 deletions
diff --git a/starmath/source/mathmlexport.cxx b/starmath/source/mathmlexport.cxx
new file mode 100644
index 000000000..c619f22f4
--- /dev/null
+++ b/starmath/source/mathmlexport.cxx
@@ -0,0 +1,1526 @@
+/* -*- 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 .
+ */
+
+/*
+ Warning: The SvXMLElementExport helper class creates the beginning and
+ closing tags of xml elements in its constructor and destructor, so there's
+ hidden stuff going on, on occasion the ordering of these classes declarations
+ may be significant
+*/
+
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/util/MeasureUnit.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <com/sun/star/uno/Any.h>
+
+#include <rtl/math.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <osl/diagnose.h>
+#include <unotools/saveopt.hxx>
+#include <sot/storage.hxx>
+#include <svl/itemset.hxx>
+#include <svl/stritem.hxx>
+#include <comphelper/fileformat.h>
+#include <comphelper/processfactory.hxx>
+#include <unotools/streamwrap.hxx>
+#include <sax/tools/converter.hxx>
+#include <xmloff/xmlnmspe.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/nmspmap.hxx>
+#include <xmloff/attrlist.hxx>
+#include <comphelper/genericpropertyset.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/propertysetinfo.hxx>
+#include <tools/diagnose_ex.h>
+#include <sal/log.hxx>
+
+#include <memory>
+#include <stack>
+
+#include "mathmlexport.hxx"
+#include <strings.hrc>
+#include <smmod.hxx>
+#include <unomodel.hxx>
+#include <document.hxx>
+#include <utility.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 {
+
+bool IsInPrivateUseArea( sal_Unicode cChar ) { return 0xE000 <= cChar && cChar <= 0xF8FF; }
+
+sal_Unicode ConvertMathToMathML( sal_Unicode cChar )
+{
+ sal_Unicode cRes = cChar;
+ if (IsInPrivateUseArea( cChar ))
+ {
+ SAL_WARN("starmath", "Error: private use area characters should no longer be in use!" );
+ cRes = u'@'; // just some character that should easily be notice as odd in the context
+ }
+ return cRes;
+}
+
+}
+
+bool SmXMLExportWrapper::Export(SfxMedium &rMedium)
+{
+ bool bRet=true;
+ uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
+
+ //Get model
+ uno::Reference< lang::XComponent > xModelComp = xModel;
+
+ bool bEmbedded = false;
+ SmModel *pModel = comphelper::getUnoTunnelImplementation<SmModel>(xModel);
+
+ SmDocShell *pDocShell = pModel ?
+ static_cast<SmDocShell*>(pModel->GetObjectShell()) : nullptr;
+ if ( pDocShell &&
+ SfxObjectCreateMode::EMBEDDED == pDocShell->GetCreateMode() )
+ bEmbedded = true;
+
+ uno::Reference<task::XStatusIndicator> xStatusIndicator;
+ if (!bEmbedded)
+ {
+ if (pDocShell /*&& pDocShell->GetMedium()*/)
+ {
+ OSL_ENSURE( pDocShell->GetMedium() == &rMedium,
+ "different SfxMedium found" );
+
+ SfxItemSet* pSet = rMedium.GetItemSet();
+ if (pSet)
+ {
+ const SfxUnoAnyItem* pItem = static_cast<const SfxUnoAnyItem*>(
+ pSet->GetItem(SID_PROGRESS_STATUSBAR_CONTROL) );
+ if (pItem)
+ pItem->GetValue() >>= xStatusIndicator;
+ }
+ }
+
+ // set progress range and start status indicator
+ if (xStatusIndicator.is())
+ {
+ sal_Int32 nProgressRange = bFlat ? 1 : 3;
+ xStatusIndicator->start(SmResId(STR_STATSTR_WRITING),
+ nProgressRange);
+ }
+ }
+
+
+ // create XPropertySet with three properties for status indicator
+ comphelper::PropertyMapEntry aInfoMap[] =
+ {
+ { OUString("UsePrettyPrinting"), 0,
+ cppu::UnoType<bool>::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 },
+ { OUString(), 0, css::uno::Type(), 0, 0 }
+ };
+ uno::Reference< beans::XPropertySet > xInfoSet(
+ comphelper::GenericPropertySet_CreateInstance(
+ new comphelper::PropertySetInfo( aInfoMap ) ) );
+
+ SvtSaveOptions aSaveOpt;
+ bool bUsePrettyPrinting( bFlat || aSaveOpt.IsPrettyPrinting() );
+ xInfoSet->setPropertyValue( "UsePrettyPrinting", Any(bUsePrettyPrinting) );
+
+ // Set base URI
+ OUString sPropName( "BaseURI" );
+ xInfoSet->setPropertyValue( sPropName, makeAny( rMedium.GetBaseURL( true ) ) );
+
+ sal_Int32 nSteps=0;
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+ if (!bFlat) //Storage (Package) of Stream
+ {
+ uno::Reference < embed::XStorage > xStg = rMedium.GetOutputStorage();
+ bool bOASIS = ( SotStorage::GetVersion( xStg ) > SOFFICE_FILEFORMAT_60 );
+
+ // TODO/LATER: handle the case of embedded links gracefully
+ if ( bEmbedded ) //&& !pStg->IsRoot() )
+ {
+ OUString aName;
+ if ( rMedium.GetItemSet() )
+ {
+ const SfxStringItem* pDocHierarchItem = static_cast<const SfxStringItem*>(
+ rMedium.GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME) );
+ if ( pDocHierarchItem )
+ aName = pDocHierarchItem->GetValue();
+ }
+
+ if ( !aName.isEmpty() )
+ {
+ sPropName = "StreamRelPath";
+ xInfoSet->setPropertyValue( sPropName, makeAny( aName ) );
+ }
+ }
+
+ if ( !bEmbedded )
+ {
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ bRet = WriteThroughComponent(
+ xStg, xModelComp, "meta.xml", xContext, xInfoSet,
+ (bOASIS ? "com.sun.star.comp.Math.XMLOasisMetaExporter"
+ : "com.sun.star.comp.Math.XMLMetaExporter"));
+ }
+ if ( bRet )
+ {
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ bRet = WriteThroughComponent(
+ xStg, xModelComp, "content.xml", xContext, xInfoSet,
+ "com.sun.star.comp.Math.XMLContentExporter");
+ }
+
+ if ( bRet )
+ {
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ bRet = WriteThroughComponent(
+ xStg, xModelComp, "settings.xml", xContext, xInfoSet,
+ (bOASIS ? "com.sun.star.comp.Math.XMLOasisSettingsExporter"
+ : "com.sun.star.comp.Math.XMLSettingsExporter") );
+ }
+ }
+ else
+ {
+ SvStream *pStream = rMedium.GetOutStream();
+ uno::Reference<io::XOutputStream> xOut(
+ new utl::OOutputStreamWrapper(*pStream) );
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ bRet = WriteThroughComponent(
+ xOut, xModelComp, xContext, xInfoSet,
+ "com.sun.star.comp.Math.XMLContentExporter");
+ }
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->end();
+
+ return bRet;
+}
+
+
+/// export through an XML exporter component (output stream version)
+bool SmXMLExportWrapper::WriteThroughComponent(
+ const Reference<io::XOutputStream>& xOutputStream,
+ const Reference<XComponent>& xComponent,
+ Reference<uno::XComponentContext> const & rxContext,
+ Reference<beans::XPropertySet> const & rPropSet,
+ const char* pComponentName )
+{
+ OSL_ENSURE(xOutputStream.is(), "I really need an output stream!");
+ OSL_ENSURE(xComponent.is(), "Need component!");
+ OSL_ENSURE(nullptr != pComponentName, "Need component name!");
+
+ // get component
+ Reference< xml::sax::XWriter > xSaxWriter = xml::sax::Writer::create(rxContext );
+
+ // connect XML writer to output stream
+ xSaxWriter->setOutputStream( xOutputStream );
+
+ // prepare arguments (prepend doc handler to given arguments)
+ Sequence<Any> aArgs( 2 );
+ aArgs[0] <<= xSaxWriter;
+ aArgs[1] <<= rPropSet;
+
+ // get filter component
+ Reference< document::XExporter > xExporter(
+ rxContext->getServiceManager()->createInstanceWithArgumentsAndContext(OUString::createFromAscii(pComponentName), aArgs, rxContext),
+ UNO_QUERY);
+ OSL_ENSURE( xExporter.is(),
+ "can't instantiate export filter component" );
+ if ( !xExporter.is() )
+ return false;
+
+
+ // connect model and filter
+ xExporter->setSourceDocument( xComponent );
+
+ // filter!
+ Reference < XFilter > xFilter( xExporter, UNO_QUERY );
+ uno::Sequence< PropertyValue > aProps(0);
+ xFilter->filter( aProps );
+
+ auto pFilter = comphelper::getUnoTunnelImplementation<SmXMLExport>(xFilter);
+ return pFilter == nullptr || pFilter->GetSuccess();
+}
+
+
+/// export through an XML exporter component (storage version)
+bool SmXMLExportWrapper::WriteThroughComponent(
+ const Reference < embed::XStorage >& xStorage,
+ const Reference<XComponent>& xComponent,
+ const char* pStreamName,
+ Reference<uno::XComponentContext> const & rxContext,
+ Reference<beans::XPropertySet> const & rPropSet,
+ const char* pComponentName
+ )
+{
+ OSL_ENSURE(xStorage.is(), "Need storage!");
+ OSL_ENSURE(nullptr != pStreamName, "Need stream name!");
+
+ // open stream
+ Reference < io::XStream > xStream;
+ OUString sStreamName = OUString::createFromAscii(pStreamName);
+ try
+ {
+ xStream = xStorage->openStreamElement( sStreamName,
+ embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
+ }
+ catch ( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("starmath", "Can't create output stream in package" );
+ return false;
+ }
+
+ uno::Reference < beans::XPropertySet > xSet( xStream, uno::UNO_QUERY );
+ xSet->setPropertyValue( "MediaType", Any(OUString( "text/xml" )) );
+
+ // all streams must be encrypted in encrypted document
+ xSet->setPropertyValue( "UseCommonStoragePasswordEncryption", Any(true) );
+
+ // set Base URL
+ if ( rPropSet.is() )
+ {
+ rPropSet->setPropertyValue( "StreamName", makeAny( sStreamName ) );
+ }
+
+ // write the stuff
+ bool bRet = WriteThroughComponent( xStream->getOutputStream(), xComponent, rxContext,
+ rPropSet, pComponentName );
+
+ return bRet;
+}
+
+SmXMLExport::SmXMLExport(
+ const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ OUString const & implementationName, SvXMLExportFlags nExportFlags)
+ : SvXMLExport(util::MeasureUnit::INCH, rContext, implementationName, XML_MATH,
+ nExportFlags)
+ , pTree(nullptr)
+ , bSuccess(false)
+{
+}
+
+sal_Int64 SAL_CALL SmXMLExport::getSomething(
+ const uno::Sequence< sal_Int8 >& rId )
+{
+ if ( isUnoTunnelId<SmXMLExport>(rId) )
+ return sal::static_int_cast< sal_Int64 >(reinterpret_cast< sal_uIntPtr >(this));
+
+ return SvXMLExport::getSomething( rId );
+}
+
+namespace
+{
+ class theSmXMLExportUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSmXMLExportUnoTunnelId> {};
+}
+
+const uno::Sequence< sal_Int8 > & SmXMLExport::getUnoTunnelId() throw()
+{
+ return theSmXMLExportUnoTunnelId::get().getSeq();
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Math_XMLExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::ALL));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Math_XMLMetaExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLMetaExporter", SvXMLExportFlags::META));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Math_XMLOasisMetaExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLOasisMetaExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::META));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Math_XMLSettingsExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLSettingsExporter", SvXMLExportFlags::SETTINGS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Math_XMLOasisSettingsExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLOasisSettingsExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::SETTINGS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Math_XMLContentExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLContentExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::CONTENT));
+}
+
+ErrCode SmXMLExport::exportDoc(enum XMLTokenEnum eClass)
+{
+ if ( !(getExportFlags() & SvXMLExportFlags::CONTENT) )
+ {
+ SvXMLExport::exportDoc( eClass );
+ }
+ else
+ {
+ uno::Reference <frame::XModel> xModel = GetModel();
+ SmModel *pModel = comphelper::getUnoTunnelImplementation<SmModel>(xModel);
+
+ if (pModel)
+ {
+ SmDocShell *pDocShell =
+ static_cast<SmDocShell*>(pModel->GetObjectShell());
+ pTree = pDocShell->GetFormulaTree();
+ aText = pDocShell->GetText();
+ }
+
+ GetDocHandler()->startDocument();
+
+ addChaffWhenEncryptedStorage();
+
+ /*Add xmlns line*/
+ SvXMLAttributeList &rList = GetAttrList();
+
+ // make use of a default namespace
+ ResetNamespaceMap(); // Math doesn't need namespaces from xmloff, since it now uses default namespaces (because that is common with current MathML usage in the web)
+ GetNamespaceMap_().Add( OUString(), GetXMLToken(XML_N_MATH), XML_NAMESPACE_MATH );
+
+ rList.AddAttribute(GetNamespaceMap().GetAttrNameByKey(XML_NAMESPACE_MATH),
+ GetNamespaceMap().GetNameByKey( XML_NAMESPACE_MATH));
+
+ //I think we need something like ImplExportEntities();
+ ExportContent_();
+ GetDocHandler()->endDocument();
+ }
+
+ bSuccess=true;
+ return ERRCODE_NONE;
+}
+
+void SmXMLExport::ExportContent_()
+{
+ uno::Reference <frame::XModel> xModel = GetModel();
+ SmModel *pModel = comphelper::getUnoTunnelImplementation<SmModel>(xModel);
+ SmDocShell *pDocShell = pModel ?
+ static_cast<SmDocShell*>(pModel->GetObjectShell()) : nullptr;
+ OSL_ENSURE( pDocShell, "doc shell missing" );
+
+ if (pDocShell && !pDocShell->GetFormat().IsTextmode())
+ {
+ // If the Math equation is not in text mode, we attach a display="block"
+ // attribute on the <math> root. We don't do anything if it is in
+ // text mode, the default display="inline" value will be used.
+ AddAttribute(XML_NAMESPACE_MATH, XML_DISPLAY, XML_BLOCK);
+ }
+ SvXMLElementExport aEquation(*this, XML_NAMESPACE_MATH, XML_MATH, true, true);
+ std::unique_ptr<SvXMLElementExport> pSemantics;
+
+ if (!aText.isEmpty())
+ {
+ pSemantics.reset( new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
+ XML_SEMANTICS, true, true) );
+ }
+
+ ExportNodes(pTree, 0);
+
+ if (aText.isEmpty())
+ return;
+
+ // Convert symbol names
+ if (pDocShell)
+ {
+ SmParser &rParser = pDocShell->GetParser();
+ bool bVal = rParser.IsExportSymbolNames();
+ rParser.SetExportSymbolNames( true );
+ auto pTmpTree = rParser.Parse( aText );
+ aText = rParser.GetText();
+ pTmpTree.reset();
+ rParser.SetExportSymbolNames( bVal );
+ }
+
+ AddAttribute(XML_NAMESPACE_MATH, XML_ENCODING,
+ OUString("StarMath 5.0"));
+ SvXMLElementExport aAnnotation(*this, XML_NAMESPACE_MATH,
+ XML_ANNOTATION, true, false);
+ GetDocHandler()->characters( aText );
+}
+
+void SmXMLExport::GetViewSettings( Sequence < PropertyValue >& aProps)
+{
+ uno::Reference <frame::XModel> xModel = GetModel();
+ if ( !xModel.is() )
+ return;
+
+ SmModel *pModel = comphelper::getUnoTunnelImplementation<SmModel>(xModel);
+
+ if ( !pModel )
+ return;
+
+ SmDocShell *pDocShell =
+ static_cast<SmDocShell*>(pModel->GetObjectShell());
+ if ( !pDocShell )
+ return;
+
+ aProps.realloc( 4 );
+ PropertyValue *pValue = aProps.getArray();
+ sal_Int32 nIndex = 0;
+
+ tools::Rectangle aRect( pDocShell->GetVisArea() );
+
+ pValue[nIndex].Name = "ViewAreaTop";
+ pValue[nIndex++].Value <<= aRect.Top();
+
+ pValue[nIndex].Name = "ViewAreaLeft";
+ pValue[nIndex++].Value <<= aRect.Left();
+
+ pValue[nIndex].Name = "ViewAreaWidth";
+ pValue[nIndex++].Value <<= aRect.GetWidth();
+
+ pValue[nIndex].Name = "ViewAreaHeight";
+ pValue[nIndex++].Value <<= aRect.GetHeight();
+}
+
+void SmXMLExport::GetConfigurationSettings( Sequence < PropertyValue > & rProps)
+{
+ Reference < XPropertySet > xProps ( GetModel(), UNO_QUERY );
+ if ( !xProps.is() )
+ return;
+
+ Reference< XPropertySetInfo > xPropertySetInfo = xProps->getPropertySetInfo();
+ if (!xPropertySetInfo.is())
+ return;
+
+ Sequence< Property > aProps = xPropertySetInfo->getProperties();
+ const sal_Int32 nCount = aProps.getLength();
+ if (!nCount)
+ return;
+
+ rProps.realloc(nCount);
+ SmMathConfig* pConfig = SM_MOD()->GetConfig();
+ const bool bUsedSymbolsOnly = pConfig && pConfig->IsSaveOnlyUsedSymbols();
+
+ std::transform(aProps.begin(), aProps.end(), rProps.begin(),
+ [bUsedSymbolsOnly, &xProps](Property& prop) {
+ PropertyValue aRet;
+ if (prop.Name != "Formula" && prop.Name != "BasicLibraries"
+ && prop.Name != "DialogLibraries"
+ && prop.Name != "RuntimeUID")
+ {
+ aRet.Name = prop.Name;
+ OUString aActualName(prop.Name);
+ // handle 'save used symbols only'
+ if (bUsedSymbolsOnly && prop.Name == "Symbols")
+ aActualName = "UserDefinedSymbolsInUse";
+ aRet.Value = xProps->getPropertyValue(aActualName);
+ }
+ return aRet;
+ });
+}
+
+void SmXMLExport::ExportLine(const SmNode *pNode, int nLevel)
+{
+ ExportExpression(pNode, nLevel);
+}
+
+void SmXMLExport::ExportBinaryHorizontal(const SmNode *pNode, int nLevel)
+{
+ TG nGroup = pNode->GetToken().nGroup;
+
+ SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW, true, true);
+
+ // Unfold the binary tree structure as long as the nodes are SmBinHorNode
+ // with the same nGroup. This will reduce the number of nested <mrow>
+ // elements e.g. we only need three <mrow> levels to export
+
+ // "a*b*c*d+e*f*g*h+i*j*k*l = a*b*c*d+e*f*g*h+i*j*k*l =
+ // a*b*c*d+e*f*g*h+i*j*k*l = a*b*c*d+e*f*g*h+i*j*k*l"
+
+ // See https://www.libreoffice.org/bugzilla/show_bug.cgi?id=66081
+ ::std::stack< const SmNode* > s;
+ s.push(pNode);
+ while (!s.empty())
+ {
+ const SmNode *node = s.top();
+ s.pop();
+ if (node->GetType() != SmNodeType::BinHor || node->GetToken().nGroup != nGroup)
+ {
+ ExportNodes(node, nLevel+1);
+ continue;
+ }
+ const SmBinHorNode* binNode = static_cast<const SmBinHorNode*>(node);
+ s.push(binNode->RightOperand());
+ s.push(binNode->Symbol());
+ s.push(binNode->LeftOperand());
+ }
+}
+
+void SmXMLExport::ExportUnaryHorizontal(const SmNode *pNode, int nLevel)
+{
+ ExportExpression(pNode, nLevel);
+}
+
+void SmXMLExport::ExportExpression(const SmNode *pNode, int nLevel,
+ bool bNoMrowContainer /*=false*/)
+{
+ std::unique_ptr<SvXMLElementExport> pRow;
+ size_t nSize = pNode->GetNumSubNodes();
+
+ // #i115443: nodes of type expression always need to be grouped with mrow statement
+ if (!bNoMrowContainer &&
+ (nSize > 1 || pNode->GetType() == SmNodeType::Expression))
+ pRow.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MROW, true, true));
+
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (const SmNode *pTemp = pNode->GetSubNode(i))
+ ExportNodes(pTemp, nLevel+1);
+ }
+}
+
+void SmXMLExport::ExportBinaryVertical(const SmNode *pNode, int nLevel)
+{
+ assert(pNode->GetNumSubNodes() == 3);
+ const SmNode *pNum = pNode->GetSubNode(0);
+ const SmNode *pDenom = pNode->GetSubNode(2);
+ if (pNum->GetType() == SmNodeType::Align && pNum->GetToken().eType != TALIGNC)
+ {
+ // A left or right alignment is specified on the numerator:
+ // attach the corresponding numalign attribute.
+ AddAttribute(XML_NAMESPACE_MATH, XML_NUMALIGN,
+ pNum->GetToken().eType == TALIGNL ? XML_LEFT : XML_RIGHT);
+ }
+ if (pDenom->GetType() == SmNodeType::Align && pDenom->GetToken().eType != TALIGNC)
+ {
+ // A left or right alignment is specified on the denominator:
+ // attach the corresponding denomalign attribute.
+ AddAttribute(XML_NAMESPACE_MATH, XML_DENOMALIGN,
+ pDenom->GetToken().eType == TALIGNL ? XML_LEFT : XML_RIGHT);
+ }
+ SvXMLElementExport aFraction(*this, XML_NAMESPACE_MATH, XML_MFRAC, true, true);
+ ExportNodes(pNum, nLevel);
+ ExportNodes(pDenom, nLevel);
+}
+
+void SmXMLExport::ExportBinaryDiagonal(const SmNode *pNode, int nLevel)
+{
+ assert(pNode->GetNumSubNodes() == 3);
+
+ if (pNode->GetToken().eType == TWIDESLASH)
+ {
+ // wideslash
+ // export the node as <mfrac bevelled="true">
+ AddAttribute(XML_NAMESPACE_MATH, XML_BEVELLED, XML_TRUE);
+ SvXMLElementExport aFraction(*this, XML_NAMESPACE_MATH, XML_MFRAC,
+ true, true);
+ ExportNodes(pNode->GetSubNode(0), nLevel);
+ ExportNodes(pNode->GetSubNode(1), nLevel);
+ }
+ else
+ {
+ // widebslash
+ // We can not use <mfrac> to a backslash, so just use <mo>\</mo>
+ SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW, true, true);
+
+ ExportNodes(pNode->GetSubNode(0), nLevel);
+
+ { // Scoping for <mo> creation
+ SvXMLElementExport aMo(*this, XML_NAMESPACE_MATH, XML_MO,
+ true, true);
+ sal_Unicode const nArse[2] = {MS_BACKSLASH,0x00};
+ GetDocHandler()->characters(nArse);
+ }
+
+ ExportNodes(pNode->GetSubNode(1), nLevel);
+ }
+}
+
+void SmXMLExport::ExportTable(const SmNode *pNode, int nLevel)
+{
+ std::unique_ptr<SvXMLElementExport> pTable;
+
+ size_t nSize = pNode->GetNumSubNodes();
+
+ //If the list ends in newline then the last entry has
+ //no subnodes, the newline is superfluous so we just drop
+ //the last node, inclusion would create a bad MathML
+ //table
+ if (nSize >= 1)
+ {
+ const SmNode *pLine = pNode->GetSubNode(nSize-1);
+ if (pLine->GetType() == SmNodeType::Line && pLine->GetNumSubNodes() == 1 &&
+ pLine->GetSubNode(0) != nullptr &&
+ pLine->GetSubNode(0)->GetToken().eType == TNEWLINE)
+ --nSize;
+ }
+
+ // try to avoid creating a mtable element when the formula consists only
+ // of a single output line
+ if (nLevel || (nSize >1))
+ pTable.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTABLE, true, true));
+
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (const SmNode *pTemp = pNode->GetSubNode(i))
+ {
+ std::unique_ptr<SvXMLElementExport> pRow;
+ std::unique_ptr<SvXMLElementExport> pCell;
+ if (pTable)
+ {
+ pRow.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTR, true, true));
+ SmTokenType eAlign = TALIGNC;
+ if (pTemp->GetType() == SmNodeType::Align)
+ {
+ // For Binom() and Stack() constructions, the SmNodeType::Align nodes
+ // are direct children.
+ // binom{alignl ...}{alignr ...} and
+ // stack{alignl ... ## alignr ... ## ...}
+ eAlign = pTemp->GetToken().eType;
+ }
+ else if (pTemp->GetType() == SmNodeType::Line &&
+ pTemp->GetNumSubNodes() == 1 &&
+ pTemp->GetSubNode(0) &&
+ pTemp->GetSubNode(0)->GetType() == SmNodeType::Align)
+ {
+ // For the Table() construction, the SmNodeType::Align node is a child
+ // of an SmNodeType::Line node.
+ // alignl ... newline alignr ... newline ...
+ eAlign = pTemp->GetSubNode(0)->GetToken().eType;
+ }
+ if (eAlign != TALIGNC)
+ {
+ // If a left or right alignment is specified on this line,
+ // attach the corresponding columnalign attribute.
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLUMNALIGN,
+ eAlign == TALIGNL ? XML_LEFT : XML_RIGHT);
+ }
+ pCell.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTD, true, true));
+ }
+ ExportNodes(pTemp, nLevel+1);
+ }
+ }
+}
+
+void SmXMLExport::ExportMath(const SmNode *pNode)
+{
+ const SmTextNode *pTemp = static_cast<const SmTextNode *>(pNode);
+ std::unique_ptr<SvXMLElementExport> pMath;
+
+ if (pNode->GetType() == SmNodeType::Math || pNode->GetType() == SmNodeType::GlyphSpecial)
+ {
+ // Export SmNodeType::Math and SmNodeType::GlyphSpecial symbols as <mo> elements
+ pMath.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MO, true, false));
+ }
+ else if (pNode->GetType() == SmNodeType::Special)
+ {
+ bool bIsItalic = IsItalic(pNode->GetFont());
+ if (!bIsItalic)
+ AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
+ pMath.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false));
+ }
+ else
+ {
+ // Export SmNodeType::MathIdent and SmNodeType::Place symbols as <mi> elements:
+ // - These math symbols should not be drawn slanted. Hence we should
+ // attach a mathvariant="normal" attribute to single-char <mi> elements
+ // that are not mathematical alphanumeric symbol. For simplicity and to
+ // work around browser limitations, we always attach such an attribute.
+ // - The MathML specification suggests to use empty <mi> elements as
+ // placeholders but they won't be visible in most MathML rendering
+ // engines so let's use an empty square for SmNodeType::Place instead.
+ AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
+ pMath.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false));
+ }
+ sal_Unicode nArse[2];
+ nArse[0] = pTemp->GetText()[0];
+ sal_Unicode cTmp = ConvertMathToMathML( nArse[0] );
+ if (cTmp != 0)
+ nArse[0] = cTmp;
+ OSL_ENSURE(nArse[0] != 0xffff,"Non existent symbol");
+ nArse[1] = 0;
+ GetDocHandler()->characters(nArse);
+}
+
+void SmXMLExport::ExportText(const SmNode *pNode)
+{
+ std::unique_ptr<SvXMLElementExport> pText;
+ const SmTextNode *pTemp = static_cast<const SmTextNode *>(pNode);
+ switch (pNode->GetToken().eType)
+ {
+ default:
+ case TIDENT:
+ {
+ //Note that we change the fontstyle to italic for strings that
+ //are italic and longer than a single character.
+ bool bIsItalic = IsItalic( pTemp->GetFont() );
+ if ((pTemp->GetText().getLength() > 1) && bIsItalic)
+ AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_ITALIC);
+ else if ((pTemp->GetText().getLength() == 1) && !bIsItalic)
+ AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
+ pText.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false));
+ break;
+ }
+ case TNUMBER:
+ pText.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MN, true, false));
+ break;
+ case TTEXT:
+ pText.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTEXT, true, false));
+ break;
+ }
+ GetDocHandler()->characters(pTemp->GetText());
+}
+
+void SmXMLExport::ExportBlank(const SmNode *pNode)
+{
+ const SmBlankNode *pTemp = static_cast<const SmBlankNode *>(pNode);
+ //!! exports an <mspace> element. Note that for example "~_~" is allowed in
+ //!! Math (so it has no sense at all) but must not result in an empty
+ //!! <msub> tag in MathML !!
+
+ if (pTemp->GetBlankNum() != 0)
+ {
+ // Attach a width attribute. We choose the (somewhat arbitrary) values
+ // ".5em" for a small gap '`' and "2em" for a large gap '~'.
+ // (see SmBlankNode::IncreaseBy for how pTemp->mnNum is set).
+ OUStringBuffer sStrBuf;
+ ::sax::Converter::convertDouble(sStrBuf, pTemp->GetBlankNum() * .5);
+ sStrBuf.append("em");
+ AddAttribute(XML_NAMESPACE_MATH, XML_WIDTH, sStrBuf.getStr());
+ }
+
+ SvXMLElementExport aTextExport(*this, XML_NAMESPACE_MATH, XML_MSPACE,
+ true, false);
+
+ GetDocHandler()->characters( OUString() );
+}
+
+void SmXMLExport::ExportSubSupScript(const SmNode *pNode, int nLevel)
+{
+ const SmNode *pSub = nullptr;
+ const SmNode *pSup = nullptr;
+ const SmNode *pCSub = nullptr;
+ const SmNode *pCSup = nullptr;
+ const SmNode *pLSub = nullptr;
+ const SmNode *pLSup = nullptr;
+ std::unique_ptr<SvXMLElementExport> pThing2;
+
+ //if we have prescripts at all then we must use the tensor notation
+
+ //This is one of those excellent locations where scope is vital to
+ //arrange the construction and destruction of the element helper
+ //classes correctly
+ pLSub = pNode->GetSubNode(LSUB+1);
+ pLSup = pNode->GetSubNode(LSUP+1);
+ if (pLSub || pLSup)
+ {
+ SvXMLElementExport aMultiScripts(*this, XML_NAMESPACE_MATH,
+ XML_MMULTISCRIPTS, true, true);
+
+
+ if (nullptr != (pCSub = pNode->GetSubNode(CSUB+1))
+ && nullptr != (pCSup = pNode->GetSubNode(CSUP+1)))
+ {
+ pThing2.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
+ XML_MUNDEROVER, true, true));
+ }
+ else if (nullptr != (pCSub = pNode->GetSubNode(CSUB+1)))
+ {
+ pThing2.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
+ XML_MUNDER, true, true));
+ }
+ else if (nullptr != (pCSup = pNode->GetSubNode(CSUP+1)))
+ {
+ pThing2.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
+ XML_MOVER, true, true));
+ }
+
+ ExportNodes(pNode->GetSubNode(0), nLevel+1); //Main Term
+
+ if (pCSub)
+ ExportNodes(pCSub, nLevel+1);
+ if (pCSup)
+ ExportNodes(pCSup, nLevel+1);
+ pThing2.reset();
+
+ pSub = pNode->GetSubNode(RSUB+1);
+ pSup = pNode->GetSubNode(RSUP+1);
+ if (pSub || pSup)
+ {
+ if (pSub)
+ ExportNodes(pSub, nLevel+1);
+ else
+ {
+ SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE, true, true);
+ }
+ if (pSup)
+ ExportNodes(pSup, nLevel+1);
+ else
+ {
+ SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE, true, true);
+ }
+ }
+
+ //Separator element between suffix and prefix sub/sup pairs
+ {
+ SvXMLElementExport aPrescripts(*this, XML_NAMESPACE_MATH,
+ XML_MPRESCRIPTS, true, true);
+ }
+
+ if (pLSub)
+ ExportNodes(pLSub, nLevel+1);
+ else
+ {
+ SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE,
+ true, true);
+
+ }
+ if (pLSup)
+ ExportNodes(pLSup, nLevel+1);
+ else
+ {
+ SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE,
+ true, true);
+
+ }
+ }
+ else
+ {
+ std::unique_ptr<SvXMLElementExport> pThing;
+ if (nullptr != (pSub = pNode->GetSubNode(RSUB+1)) &&
+ nullptr != (pSup = pNode->GetSubNode(RSUP+1)))
+ {
+ pThing.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
+ XML_MSUBSUP, true, true));
+ }
+ else if (nullptr != (pSub = pNode->GetSubNode(RSUB+1)))
+ {
+ pThing.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSUB,
+ true, true));
+ }
+ else if (nullptr != (pSup = pNode->GetSubNode(RSUP+1)))
+ {
+ pThing.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSUP,
+ true, true));
+ }
+
+ if (nullptr != (pCSub = pNode->GetSubNode(CSUB+1))
+ && nullptr != (pCSup=pNode->GetSubNode(CSUP+1)))
+ {
+ pThing2.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
+ XML_MUNDEROVER, true, true));
+ }
+ else if (nullptr != (pCSub = pNode->GetSubNode(CSUB+1)))
+ {
+ pThing2.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
+ XML_MUNDER, true, true));
+ }
+ else if (nullptr != (pCSup = pNode->GetSubNode(CSUP+1)))
+ {
+ pThing2.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
+ XML_MOVER, true, true));
+ }
+ ExportNodes(pNode->GetSubNode(0), nLevel+1); //Main Term
+
+ if (pCSub)
+ ExportNodes(pCSub, nLevel+1);
+ if (pCSup)
+ ExportNodes(pCSup, nLevel+1);
+ pThing2.reset();
+
+ if (pSub)
+ ExportNodes(pSub, nLevel+1);
+ if (pSup)
+ ExportNodes(pSup, nLevel+1);
+ pThing.reset();
+ }
+}
+
+void SmXMLExport::ExportBrace(const SmNode *pNode, int nLevel)
+{
+ const SmNode *pTemp;
+ const SmNode *pLeft=pNode->GetSubNode(0);
+ const SmNode *pRight=pNode->GetSubNode(2);
+
+ // This used to generate <mfenced> or <mrow>+<mo> elements according to
+ // the stretchiness of fences. The MathML recommendation defines an
+ // <mrow>+<mo> construction that is equivalent to the <mfenced> element:
+ // http://www.w3.org/TR/MathML3/chapter3.html#presm.mfenced
+ // To simplify our code and avoid issues with mfenced implementations in
+ // MathML rendering engines, we now always generate <mrow>+<mo> elements.
+ // See #fdo 66282.
+
+ // <mrow>
+ SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW,
+ true, true);
+
+ // <mo fence="true"> opening-fence </mo>
+ if (pLeft && (pLeft->GetToken().eType != TNONE))
+ {
+ AddAttribute(XML_NAMESPACE_MATH, XML_FENCE, XML_TRUE);
+ if (pNode->GetScaleMode() == SmScaleMode::Height)
+ AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
+ else
+ AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
+ ExportNodes(pLeft, nLevel+1);
+ }
+
+ if (nullptr != (pTemp = pNode->GetSubNode(1)))
+ {
+ // <mrow>
+ SvXMLElementExport aRowExport(*this, XML_NAMESPACE_MATH, XML_MROW,
+ true, true);
+ ExportNodes(pTemp, nLevel+1);
+ // </mrow>
+ }
+
+ // <mo fence="true"> closing-fence </mo>
+ if (pRight && (pRight->GetToken().eType != TNONE))
+ {
+ AddAttribute(XML_NAMESPACE_MATH, XML_FENCE, XML_TRUE);
+ if (pNode->GetScaleMode() == SmScaleMode::Height)
+ AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
+ else
+ AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
+ ExportNodes(pRight, nLevel+1);
+ }
+
+ // </mrow>
+}
+
+void SmXMLExport::ExportRoot(const SmNode *pNode, int nLevel)
+{
+ if (pNode->GetSubNode(0))
+ {
+ SvXMLElementExport aRoot(*this, XML_NAMESPACE_MATH, XML_MROOT, true,
+ true);
+ ExportNodes(pNode->GetSubNode(2), nLevel+1);
+ ExportNodes(pNode->GetSubNode(0), nLevel+1);
+ }
+ else
+ {
+ SvXMLElementExport aSqrt(*this, XML_NAMESPACE_MATH, XML_MSQRT, true,
+ true);
+ ExportNodes(pNode->GetSubNode(2), nLevel+1);
+ }
+}
+
+void SmXMLExport::ExportOperator(const SmNode *pNode, int nLevel)
+{
+ /*we need to either use content or font and size attributes
+ *here*/
+ SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW,
+ true, true);
+ ExportNodes(pNode->GetSubNode(0), nLevel+1);
+ ExportNodes(pNode->GetSubNode(1), nLevel+1);
+}
+
+void SmXMLExport::ExportAttributes(const SmNode *pNode, int nLevel)
+{
+ std::unique_ptr<SvXMLElementExport> pElement;
+
+ if (pNode->GetToken().eType == TUNDERLINE)
+ {
+ AddAttribute(XML_NAMESPACE_MATH, XML_ACCENTUNDER,
+ XML_TRUE);
+ pElement.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MUNDER,
+ true, true));
+ }
+ else if (pNode->GetToken().eType == TOVERSTRIKE)
+ {
+ // export as <menclose notation="horizontalstrike">
+ AddAttribute(XML_NAMESPACE_MATH, XML_NOTATION, XML_HORIZONTALSTRIKE);
+ pElement.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
+ XML_MENCLOSE, true, true));
+ }
+ else
+ {
+ AddAttribute(XML_NAMESPACE_MATH, XML_ACCENT,
+ XML_TRUE);
+ pElement.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MOVER,
+ true, true));
+ }
+
+ ExportNodes(pNode->GetSubNode(1), nLevel+1);
+ switch (pNode->GetToken().eType)
+ {
+ case TOVERLINE:
+ {
+ //proper entity support required
+ SvXMLElementExport aMath(*this, XML_NAMESPACE_MATH, XML_MO,
+ true, true);
+ sal_Unicode const nArse[2] = {0xAF,0x00};
+ GetDocHandler()->characters(nArse);
+ }
+ break;
+ case TUNDERLINE:
+ {
+ //proper entity support required
+ SvXMLElementExport aMath(*this, XML_NAMESPACE_MATH, XML_MO,
+ true, true);
+ sal_Unicode const nArse[2] = {0x0332,0x00};
+ GetDocHandler()->characters(nArse);
+ }
+ break;
+ case TOVERSTRIKE:
+ break;
+ case TWIDETILDE:
+ case TWIDEHAT:
+ case TWIDEVEC:
+ case TWIDEHARPOON:
+ {
+ // make these wide accents stretchy
+ AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
+ ExportNodes(pNode->GetSubNode(0), nLevel+1);
+ }
+ break;
+ default:
+ ExportNodes(pNode->GetSubNode(0), nLevel+1);
+ break;
+ }
+}
+
+static bool lcl_HasEffectOnMathvariant( const SmTokenType eType )
+{
+ return eType == TBOLD || eType == TNBOLD ||
+ eType == TITALIC || eType == TNITALIC ||
+ eType == TSANS || eType == TSERIF || eType == TFIXED;
+}
+
+void SmXMLExport::ExportFont(const SmNode *pNode, int nLevel)
+{
+
+ // gather the mathvariant attribute relevant data from all
+ // successively following SmFontNodes...
+
+ int nBold = -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true;
+ int nItalic = -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true;
+ int nSansSerifFixed = -1;
+ SmTokenType eNodeType = TUNKNOWN;
+ for (;;)
+ {
+ eNodeType = pNode->GetToken().eType;
+ if (!lcl_HasEffectOnMathvariant(eNodeType))
+ break;
+ switch (eNodeType)
+ {
+ case TBOLD : nBold = 1; break;
+ case TNBOLD : nBold = 0; break;
+ case TITALIC : nItalic = 1; break;
+ case TNITALIC : nItalic = 0; break;
+ case TSANS : nSansSerifFixed = 0; break;
+ case TSERIF : nSansSerifFixed = 1; break;
+ case TFIXED : nSansSerifFixed = 2; break;
+ default:
+ SAL_WARN("starmath", "unexpected case");
+ }
+ // According to the parser every node that is to be evaluated here
+ // has a single non-zero subnode at index 1!! Thus we only need to check
+ // that single node for follow-up nodes that have an effect on the attribute.
+ if (pNode->GetNumSubNodes() > 1 && pNode->GetSubNode(1) &&
+ lcl_HasEffectOnMathvariant( pNode->GetSubNode(1)->GetToken().eType))
+ {
+ pNode = pNode->GetSubNode(1);
+ }
+ else
+ break;
+ }
+
+ switch (pNode->GetToken().eType)
+ {
+ case TPHANTOM:
+ // No attribute needed. An <mphantom> element will be used below.
+ break;
+ case TBLACK:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_BLACK);
+ break;
+ case TWHITE:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_WHITE);
+ break;
+ case TRED:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_RED);
+ break;
+ case TGREEN:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_GREEN);
+ break;
+ case TBLUE:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_BLUE);
+ break;
+ case TCYAN:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_AQUA);
+ break;
+ case TMAGENTA:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_FUCHSIA);
+ break;
+ case TYELLOW:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_YELLOW);
+ break;
+ case TSILVER:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_SILVER);
+ break;
+ case TGRAY:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_GRAY);
+ break;
+ case TMAROON:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_MAROON);
+ break;
+ case TOLIVE:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_OLIVE);
+ break;
+ case TLIME:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_LIME);
+ break;
+ case TAQUA:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_AQUA);
+ break;
+ case TTEAL:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_TEAL);
+ break;
+ case TNAVY:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_NAVY);
+ break;
+ case TFUCHSIA:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_FUCHSIA);
+ break;
+ case TPURPLE:
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_PURPLE);
+ break;
+ case TSIZE:
+ {
+ const SmFontNode *pFontNode = static_cast<const SmFontNode *>(pNode);
+ const Fraction &aFrac = pFontNode->GetSizeParameter();
+
+ OUStringBuffer sStrBuf;
+ switch(pFontNode->GetSizeType())
+ {
+ case FontSizeType::MULTIPLY:
+ ::sax::Converter::convertDouble(sStrBuf,
+ static_cast<double>(aFrac*Fraction(100.00)));
+ sStrBuf.append('%');
+ break;
+ case FontSizeType::DIVIDE:
+ ::sax::Converter::convertDouble(sStrBuf,
+ static_cast<double>(Fraction(100.00)/aFrac));
+ sStrBuf.append('%');
+ break;
+ case FontSizeType::ABSOLUT:
+ ::sax::Converter::convertDouble(sStrBuf,
+ static_cast<double>(aFrac));
+ sStrBuf.append(
+ GetXMLToken(XML_UNIT_PT));
+ break;
+ default:
+ {
+ //The problem here is that the wheels fall off because
+ //font size is stored in 100th's of a mm not pts, and
+ //rounding errors take their toll on the original
+ //value specified in points.
+
+ //Must fix StarMath to retain the original pt values
+ Fraction aTemp = Sm100th_mmToPts(pFontNode->GetFont().GetFontSize().Height());
+
+ if (pFontNode->GetSizeType() == FontSizeType::MINUS)
+ aTemp-=aFrac;
+ else
+ aTemp+=aFrac;
+
+ double mytest = static_cast<double>(aTemp);
+
+ mytest = ::rtl::math::round(mytest,1);
+ ::sax::Converter::convertDouble(sStrBuf,mytest);
+ sStrBuf.append(GetXMLToken(XML_UNIT_PT));
+ }
+ break;
+ }
+
+ OUString sStr(sStrBuf.makeStringAndClear());
+ AddAttribute(XML_NAMESPACE_MATH, XML_MATHSIZE, sStr);
+ }
+ break;
+ case TBOLD:
+ case TITALIC:
+ case TNBOLD:
+ case TNITALIC:
+ case TFIXED:
+ case TSANS:
+ case TSERIF:
+ {
+ // nBold: -1 = yet undefined; 0 = false; 1 = true;
+ // nItalic: -1 = yet undefined; 0 = false; 1 = true;
+ // nSansSerifFixed: -1 = undefined; 0 = sans; 1 = serif; 2 = fixed;
+ const char *pText = "normal";
+ if (nSansSerifFixed == -1 || nSansSerifFixed == 1)
+ {
+ pText = "normal";
+ if (nBold == 1 && nItalic != 1)
+ pText = "bold";
+ else if (nBold != 1 && nItalic == 1)
+ pText = "italic";
+ else if (nBold == 1 && nItalic == 1)
+ pText = "bold-italic";
+ }
+ else if (nSansSerifFixed == 0)
+ {
+ pText = "sans-serif";
+ if (nBold == 1 && nItalic != 1)
+ pText = "bold-sans-serif";
+ else if (nBold != 1 && nItalic == 1)
+ pText = "sans-serif-italic";
+ else if (nBold == 1 && nItalic == 1)
+ pText = "sans-serif-bold-italic";
+ }
+ else if (nSansSerifFixed == 2)
+ pText = "monospace"; // no modifiers allowed for monospace ...
+ else
+ {
+ SAL_WARN("starmath", "unexpected case");
+ }
+ AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, OUString::createFromAscii( pText ));
+ }
+ break;
+ default:
+ break;
+
+ }
+ {
+ // Wrap everything in an <mphantom> or <mstyle> element. These elements
+ // are mrow-like, so ExportExpression doesn't need to add an explicit
+ // <mrow> element. See #fdo 66283.
+ SvXMLElementExport aElement(*this, XML_NAMESPACE_MATH,
+ pNode->GetToken().eType == TPHANTOM ? XML_MPHANTOM : XML_MSTYLE,
+ true, true);
+ ExportExpression(pNode, nLevel, true);
+ }
+}
+
+
+void SmXMLExport::ExportVerticalBrace(const SmVerticalBraceNode *pNode, int nLevel)
+{
+ // "[body] overbrace [script]"
+
+ // Position body, overbrace and script vertically. First place the overbrace
+ // OVER the body and then the script OVER this expression.
+
+ // [script]
+ // --[overbrace]--
+ // XXXXXX[body]XXXXXXX
+
+ // Similarly for the underbrace construction.
+
+ XMLTokenEnum which;
+
+ switch (pNode->GetToken().eType)
+ {
+ case TOVERBRACE:
+ default:
+ which = XML_MOVER;
+ break;
+ case TUNDERBRACE:
+ which = XML_MUNDER;
+ break;
+ }
+
+ SvXMLElementExport aOver1(*this, XML_NAMESPACE_MATH,which, true, true);
+ {//Scoping
+ // using accents will draw the over-/underbraces too close to the base
+ // see http://www.w3.org/TR/MathML2/chapter3.html#id.3.4.5.2
+ // also XML_ACCENT is illegal with XML_MUNDER. Thus no XML_ACCENT attribute here!
+ SvXMLElementExport aOver2(*this, XML_NAMESPACE_MATH,which, true, true);
+ ExportNodes(pNode->Body(), nLevel);
+ AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
+ ExportNodes(pNode->Brace(), nLevel);
+ }
+ ExportNodes(pNode->Script(), nLevel);
+}
+
+void SmXMLExport::ExportMatrix(const SmNode *pNode, int nLevel)
+{
+ SvXMLElementExport aTable(*this, XML_NAMESPACE_MATH, XML_MTABLE, true, true);
+ const SmMatrixNode *pMatrix = static_cast<const SmMatrixNode *>(pNode);
+ size_t i = 0;
+ for (sal_uInt16 y = 0; y < pMatrix->GetNumRows(); y++)
+ {
+ SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MTR, true, true);
+ for (sal_uInt16 x = 0; x < pMatrix->GetNumCols(); x++)
+ {
+ if (const SmNode *pTemp = pNode->GetSubNode(i++))
+ {
+ if (pTemp->GetType() == SmNodeType::Align &&
+ pTemp->GetToken().eType != TALIGNC)
+ {
+ // A left or right alignment is specified on this cell,
+ // attach the corresponding columnalign attribute.
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLUMNALIGN,
+ pTemp->GetToken().eType == TALIGNL ?
+ XML_LEFT : XML_RIGHT);
+ }
+ SvXMLElementExport aCell(*this, XML_NAMESPACE_MATH, XML_MTD, true, true);
+ ExportNodes(pTemp, nLevel+1);
+ }
+ }
+ }
+}
+
+void SmXMLExport::ExportNodes(const SmNode *pNode, int nLevel)
+{
+ if (!pNode)
+ return;
+ switch(pNode->GetType())
+ {
+ case SmNodeType::Table:
+ ExportTable(pNode, nLevel);
+ break;
+ case SmNodeType::Align:
+ case SmNodeType::Bracebody:
+ case SmNodeType::Expression:
+ ExportExpression(pNode, nLevel);
+ break;
+ case SmNodeType::Line:
+ ExportLine(pNode, nLevel);
+ break;
+ case SmNodeType::Text:
+ ExportText(pNode);
+ break;
+ case SmNodeType::GlyphSpecial:
+ case SmNodeType::Math:
+ {
+ sal_Unicode cTmp = 0;
+ const SmTextNode *pTemp = static_cast< const SmTextNode * >(pNode);
+ if (!pTemp->GetText().isEmpty())
+ cTmp = ConvertMathToMathML( pTemp->GetText()[0] );
+ if (cTmp == 0)
+ {
+ // no conversion to MathML implemented -> export it as text
+ // thus at least it will not vanish into nothing
+ ExportText(pNode);
+ }
+ else
+ {
+ switch (pNode->GetToken().eType)
+ {
+ case TINTD:
+ AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
+ break;
+ default:
+ break;
+ }
+ //To fully handle generic MathML we need to implement the full
+ //operator dictionary, we will generate MathML with explicit
+ //stretchiness for now.
+ sal_Int16 nLength = GetAttrList().getLength();
+ bool bAddStretch=true;
+ for ( sal_Int16 i = 0; i < nLength; i++ )
+ {
+ OUString sLocalName;
+ sal_uInt16 nPrefix = GetNamespaceMap().GetKeyByAttrName(
+ GetAttrList().getNameByIndex(i), &sLocalName );
+
+ if ( ( XML_NAMESPACE_MATH == nPrefix ) &&
+ IsXMLToken(sLocalName, XML_STRETCHY) )
+ {
+ bAddStretch = false;
+ break;
+ }
+ }
+ if (bAddStretch)
+ {
+ AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
+ }
+ ExportMath(pNode);
+ }
+ }
+ break;
+ case SmNodeType::Special: //SmNodeType::Special requires some sort of Entity preservation in the XML engine.
+ case SmNodeType::MathIdent:
+ case SmNodeType::Place:
+ ExportMath(pNode);
+ break;
+ case SmNodeType::BinHor:
+ ExportBinaryHorizontal(pNode, nLevel);
+ break;
+ case SmNodeType::UnHor:
+ ExportUnaryHorizontal(pNode, nLevel);
+ break;
+ case SmNodeType::Brace:
+ ExportBrace(pNode, nLevel);
+ break;
+ case SmNodeType::BinVer:
+ ExportBinaryVertical(pNode, nLevel);
+ break;
+ case SmNodeType::BinDiagonal:
+ ExportBinaryDiagonal(pNode, nLevel);
+ break;
+ case SmNodeType::SubSup:
+ ExportSubSupScript(pNode, nLevel);
+ break;
+ case SmNodeType::Root:
+ ExportRoot(pNode, nLevel);
+ break;
+ case SmNodeType::Oper:
+ ExportOperator(pNode, nLevel);
+ break;
+ case SmNodeType::Attribut:
+ ExportAttributes(pNode, nLevel);
+ break;
+ case SmNodeType::Font:
+ ExportFont(pNode, nLevel);
+ break;
+ case SmNodeType::VerticalBrace:
+ ExportVerticalBrace(static_cast<const SmVerticalBraceNode *>(pNode), nLevel);
+ break;
+ case SmNodeType::Matrix:
+ ExportMatrix(pNode, nLevel);
+ break;
+ case SmNodeType::Blank:
+ ExportBlank(pNode);
+ break;
+ default:
+ SAL_WARN("starmath", "Warning: failed to export a node?");
+ break;
+
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */