Bump version to 5.0-14
[LibreOffice.git] / starmath / source / mathmlexport.cxx
blob060451bd23a592e7bc9bea48b740e37f7e65cd78
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 Warning: The SvXMLElementExport helper class creates the beginning and
22 closing tags of xml elements in its constructor and destructor, so theres
23 hidden stuff going on, on occasion the ordering of these classes declarations
24 may be significant
27 #include <com/sun/star/xml/sax/XErrorHandler.hpp>
28 #include <com/sun/star/xml/sax/XEntityResolver.hpp>
29 #include <com/sun/star/xml/sax/InputSource.hpp>
30 #include <com/sun/star/xml/sax/XDTDHandler.hpp>
31 #include <com/sun/star/xml/sax/XParser.hpp>
32 #include <com/sun/star/xml/sax/Writer.hpp>
33 #include <com/sun/star/io/XActiveDataSource.hpp>
34 #include <com/sun/star/io/XActiveDataControl.hpp>
35 #include <com/sun/star/document/XDocumentProperties.hpp>
36 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
37 #include <com/sun/star/packages/zip/ZipIOException.hpp>
38 #include <com/sun/star/task/XStatusIndicatorFactory.hpp>
39 #include <com/sun/star/beans/PropertyAttribute.hpp>
40 #include <com/sun/star/container/XNameAccess.hpp>
41 #include <com/sun/star/embed/ElementModes.hpp>
42 #include <com/sun/star/util/MeasureUnit.hpp>
43 #include <com/sun/star/uno/Any.h>
45 #include <rtl/math.hxx>
46 #include <sfx2/frame.hxx>
47 #include <sfx2/docfile.hxx>
48 #include <osl/diagnose.h>
49 #include <svtools/sfxecode.hxx>
50 #include <unotools/saveopt.hxx>
51 #include <svl/stritem.hxx>
52 #include <svl/itemprop.hxx>
53 #include <comphelper/processfactory.hxx>
54 #include <unotools/streamwrap.hxx>
55 #include <sax/tools/converter.hxx>
56 #include <xmloff/xmlnmspe.hxx>
57 #include <xmloff/xmltoken.hxx>
58 #include <xmloff/nmspmap.hxx>
59 #include <xmloff/attrlist.hxx>
60 #include <xmloff/xmlmetai.hxx>
61 #include <osl/mutex.hxx>
62 #include <comphelper/genericpropertyset.hxx>
63 #include <comphelper/servicehelper.hxx>
65 #include <memory>
66 #include <stack>
68 #include "mathmlexport.hxx"
69 #include "register.hxx"
70 #include <starmath.hrc>
71 #include <unomodel.hxx>
72 #include <document.hxx>
73 #include <utility.hxx>
74 #include <config.hxx>
76 using namespace ::com::sun::star::beans;
77 using namespace ::com::sun::star::container;
78 using namespace ::com::sun::star::document;
79 using namespace ::com::sun::star::lang;
80 using namespace ::com::sun::star::uno;
81 using namespace ::com::sun::star;
82 using namespace ::xmloff::token;
84 sal_Unicode ConvertMathToMathML( sal_Unicode cChar )
86 sal_Unicode cRes = cChar;
87 if (IsInPrivateUseArea( cChar ))
89 SAL_WARN("starmath", "Error: private use area characters should no longer be in use!" );
90 cRes = (sal_Unicode) '@'; // just some character that should easily be notice as odd in the context
92 return cRes;
95 bool SmXMLExportWrapper::Export(SfxMedium &rMedium)
97 bool bRet=true;
98 uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
100 //Get model
101 uno::Reference< lang::XComponent > xModelComp(xModel, uno::UNO_QUERY );
103 bool bEmbedded = false;
104 uno::Reference <lang::XUnoTunnel> xTunnel;
105 xTunnel = uno::Reference <lang::XUnoTunnel> (xModel,uno::UNO_QUERY);
106 SmModel *pModel = reinterpret_cast<SmModel *>
107 (xTunnel->getSomething(SmModel::getUnoTunnelId()));
109 SmDocShell *pDocShell = pModel ?
110 static_cast<SmDocShell*>(pModel->GetObjectShell()) : 0;
111 if ( pDocShell &&
112 SfxObjectCreateMode::EMBEDDED == pDocShell->GetCreateMode() )
113 bEmbedded = true;
115 uno::Reference<task::XStatusIndicator> xStatusIndicator;
116 if (!bEmbedded)
118 if (pDocShell /*&& pDocShell->GetMedium()*/)
120 OSL_ENSURE( pDocShell->GetMedium() == &rMedium,
121 "different SfxMedium found" );
123 SfxItemSet* pSet = rMedium.GetItemSet();
124 if (pSet)
126 const SfxUnoAnyItem* pItem = static_cast<const SfxUnoAnyItem*>(
127 pSet->GetItem(SID_PROGRESS_STATUSBAR_CONTROL) );
128 if (pItem)
129 pItem->GetValue() >>= xStatusIndicator;
133 // set progress range and start status indicator
134 if (xStatusIndicator.is())
136 sal_Int32 nProgressRange = bFlat ? 1 : 3;
137 xStatusIndicator->start(SM_RESSTR(STR_STATSTR_WRITING),
138 nProgressRange);
143 // create XPropertySet with three properties for status indicator
144 comphelper::PropertyMapEntry aInfoMap[] =
146 { OUString("UsePrettyPrinting"), 0,
147 cppu::UnoType<bool>::get(),
148 beans::PropertyAttribute::MAYBEVOID, 0},
149 { OUString("BaseURI"), 0,
150 ::cppu::UnoType<OUString>::get(),
151 beans::PropertyAttribute::MAYBEVOID, 0 },
152 { OUString("StreamRelPath"), 0,
153 ::cppu::UnoType<OUString>::get(),
154 beans::PropertyAttribute::MAYBEVOID, 0 },
155 { OUString("StreamName"), 0,
156 ::cppu::UnoType<OUString>::get(),
157 beans::PropertyAttribute::MAYBEVOID, 0 },
158 { OUString(), 0, css::uno::Type(), 0, 0 }
160 uno::Reference< beans::XPropertySet > xInfoSet(
161 comphelper::GenericPropertySet_CreateInstance(
162 new comphelper::PropertySetInfo( aInfoMap ) ) );
164 SvtSaveOptions aSaveOpt;
165 OUString sUsePrettyPrinting("UsePrettyPrinting");
166 sal_Bool bUsePrettyPrinting( bFlat || aSaveOpt.IsPrettyPrinting() );
167 Any aAny;
168 aAny.setValue( &bUsePrettyPrinting, cppu::UnoType<bool>::get() );
169 xInfoSet->setPropertyValue( sUsePrettyPrinting, aAny );
171 // Set base URI
172 OUString sPropName( "BaseURI" );
173 xInfoSet->setPropertyValue( sPropName, makeAny( rMedium.GetBaseURL( true ) ) );
175 sal_Int32 nSteps=0;
176 if (xStatusIndicator.is())
177 xStatusIndicator->setValue(nSteps++);
178 if (!bFlat) //Storage (Package) of Stream
180 uno::Reference < embed::XStorage > xStg = rMedium.GetOutputStorage();
181 bool bOASIS = ( SotStorage::GetVersion( xStg ) > SOFFICE_FILEFORMAT_60 );
183 // TODO/LATER: handle the case of embedded links gracefully
184 if ( bEmbedded ) //&& !pStg->IsRoot() )
186 OUString aName;
187 if ( rMedium.GetItemSet() )
189 const SfxStringItem* pDocHierarchItem = static_cast<const SfxStringItem*>(
190 rMedium.GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME) );
191 if ( pDocHierarchItem )
192 aName = pDocHierarchItem->GetValue();
195 if ( !aName.isEmpty() )
197 sPropName = "StreamRelPath";
198 xInfoSet->setPropertyValue( sPropName, makeAny( aName ) );
202 if ( !bEmbedded )
204 if (xStatusIndicator.is())
205 xStatusIndicator->setValue(nSteps++);
207 bRet = WriteThroughComponent(
208 xStg, xModelComp, "meta.xml", xContext, xInfoSet,
209 (bOASIS ? "com.sun.star.comp.Math.XMLOasisMetaExporter"
210 : "com.sun.star.comp.Math.XMLMetaExporter"));
212 if ( bRet )
214 if (xStatusIndicator.is())
215 xStatusIndicator->setValue(nSteps++);
217 bRet = WriteThroughComponent(
218 xStg, xModelComp, "content.xml", xContext, xInfoSet,
219 "com.sun.star.comp.Math.XMLContentExporter");
222 if ( bRet )
224 if (xStatusIndicator.is())
225 xStatusIndicator->setValue(nSteps++);
227 bRet = WriteThroughComponent(
228 xStg, xModelComp, "settings.xml", xContext, xInfoSet,
229 (bOASIS ? "com.sun.star.comp.Math.XMLOasisSettingsExporter"
230 : "com.sun.star.comp.Math.XMLSettingsExporter") );
233 else
235 SvStream *pStream = rMedium.GetOutStream();
236 uno::Reference<io::XOutputStream> xOut(
237 new utl::OOutputStreamWrapper(*pStream) );
239 if (xStatusIndicator.is())
240 xStatusIndicator->setValue(nSteps++);
242 bRet = WriteThroughComponent(
243 xOut, xModelComp, xContext, xInfoSet,
244 "com.sun.star.comp.Math.XMLContentExporter");
247 if (xStatusIndicator.is())
248 xStatusIndicator->end();
250 return bRet;
254 /// export through an XML exporter component (output stream version)
255 bool SmXMLExportWrapper::WriteThroughComponent(
256 Reference<io::XOutputStream> xOutputStream,
257 Reference<XComponent> xComponent,
258 Reference<uno::XComponentContext> & rxContext,
259 Reference<beans::XPropertySet> & rPropSet,
260 const sal_Char* pComponentName )
262 OSL_ENSURE(xOutputStream.is(), "I really need an output stream!");
263 OSL_ENSURE(xComponent.is(), "Need component!");
264 OSL_ENSURE(NULL != pComponentName, "Need component name!");
266 // get component
267 Reference< xml::sax::XWriter > xSaxWriter = xml::sax::Writer::create(rxContext );
269 // connect XML writer to output stream
270 xSaxWriter->setOutputStream( xOutputStream );
272 // prepare arguments (prepend doc handler to given arguments)
273 Reference<xml::sax::XDocumentHandler> xDocHandler( xSaxWriter,UNO_QUERY);
275 Sequence<Any> aArgs( 2 );
276 aArgs[0] <<= xDocHandler;
277 aArgs[1] <<= rPropSet;
279 // get filter component
280 Reference< document::XExporter > xExporter(
281 rxContext->getServiceManager()->createInstanceWithArgumentsAndContext(OUString::createFromAscii(pComponentName), aArgs, rxContext),
282 UNO_QUERY);
283 OSL_ENSURE( xExporter.is(),
284 "can't instantiate export filter component" );
285 if ( !xExporter.is() )
286 return false;
289 // connect model and filter
290 xExporter->setSourceDocument( xComponent );
292 // filter!
293 Reference < XFilter > xFilter( xExporter, UNO_QUERY );
294 uno::Sequence< PropertyValue > aProps(0);
295 xFilter->filter( aProps );
297 uno::Reference<lang::XUnoTunnel> xFilterTunnel;
298 xFilterTunnel = uno::Reference<lang::XUnoTunnel>
299 ( xFilter, uno::UNO_QUERY );
300 SmXMLExport *pFilter = reinterpret_cast< SmXMLExport * >(
301 sal::static_int_cast< sal_uIntPtr >(
302 xFilterTunnel->getSomething( SmXMLExport::getUnoTunnelId() )));
303 return pFilter == nullptr || pFilter->GetSuccess();
307 /// export through an XML exporter component (storage version)
308 bool SmXMLExportWrapper::WriteThroughComponent(
309 const Reference < embed::XStorage >& xStorage,
310 Reference<XComponent> xComponent,
311 const sal_Char* pStreamName,
312 Reference<uno::XComponentContext> & rxContext,
313 Reference<beans::XPropertySet> & rPropSet,
314 const sal_Char* pComponentName
317 OSL_ENSURE(xStorage.is(), "Need storage!");
318 OSL_ENSURE(NULL != pStreamName, "Need stream name!");
320 // open stream
321 Reference < io::XStream > xStream;
322 OUString sStreamName = OUString::createFromAscii(pStreamName);
325 xStream = xStorage->openStreamElement( sStreamName,
326 embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
328 catch ( uno::Exception& rEx )
330 SAL_WARN("starmath", "Can't create output stream in package: " << rEx.Message );
331 return false;
334 OUString aPropName( "MediaType" );
335 OUString aMime( "text/xml" );
336 uno::Any aAny;
337 aAny <<= aMime;
339 uno::Reference < beans::XPropertySet > xSet( xStream, uno::UNO_QUERY );
340 xSet->setPropertyValue( aPropName, aAny );
342 // all streams must be encrypted in encrypted document
343 OUString aTmpPropName( "UseCommonStoragePasswordEncryption" );
344 sal_Bool bTrue = sal_True;
345 aAny.setValue( &bTrue, cppu::UnoType<bool>::get() );
346 xSet->setPropertyValue( aTmpPropName, aAny );
348 // set Base URL
349 if ( rPropSet.is() )
351 OUString sPropName( "StreamName" );
352 rPropSet->setPropertyValue( sPropName, makeAny( sStreamName ) );
355 // write the stuff
356 bool bRet = WriteThroughComponent( xStream->getOutputStream(), xComponent, rxContext,
357 rPropSet, pComponentName );
359 return bRet;
362 SmXMLExport::SmXMLExport(
363 const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XComponentContext >& rContext,
364 OUString const & implementationName, SvXMLExportFlags nExportFlags)
365 : SvXMLExport(util::MeasureUnit::INCH, rContext, implementationName, XML_MATH,
366 nExportFlags)
367 , pTree(0)
368 , bSuccess(false)
372 sal_Int64 SAL_CALL SmXMLExport::getSomething(
373 const uno::Sequence< sal_Int8 >& rId )
374 throw(uno::RuntimeException, std::exception)
376 if ( rId.getLength() == 16 &&
377 0 == memcmp( getUnoTunnelId().getConstArray(),
378 rId.getConstArray(), 16 ) )
379 return sal::static_int_cast< sal_Int64 >(reinterpret_cast< sal_uIntPtr >(this));
381 return SvXMLExport::getSomething( rId );
384 namespace
386 class theSmXMLExportUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSmXMLExportUnoTunnelId> {};
389 const uno::Sequence< sal_Int8 > & SmXMLExport::getUnoTunnelId() throw()
391 return theSmXMLExportUnoTunnelId::get().getSeq();
394 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* SAL_CALL
395 Math_XMLExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
397 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::ALL));
400 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* SAL_CALL
401 Math_XMLMetaExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
403 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLMetaExporter", SvXMLExportFlags::META));
406 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* SAL_CALL
407 Math_XMLOasisMetaExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
409 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLOasisMetaExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::META));
412 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* SAL_CALL
413 Math_XMLSettingsExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
415 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLSettingsExporter", SvXMLExportFlags::SETTINGS));
418 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* SAL_CALL
419 Math_XMLOasisSettingsExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
421 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLOasisSettingsExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::SETTINGS));
424 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* SAL_CALL
425 Math_XMLContentExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
427 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLContentExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::CONTENT));
430 sal_uInt32 SmXMLExport::exportDoc(enum XMLTokenEnum eClass)
432 if ( !(getExportFlags() & SvXMLExportFlags::CONTENT) )
434 SvXMLExport::exportDoc( eClass );
436 else
438 uno::Reference <frame::XModel> xModel = GetModel();
439 uno::Reference <lang::XUnoTunnel> xTunnel;
440 xTunnel = uno::Reference <lang::XUnoTunnel> (xModel,uno::UNO_QUERY);
441 SmModel *pModel = reinterpret_cast<SmModel *>
442 (xTunnel->getSomething(SmModel::getUnoTunnelId()));
444 if (pModel)
446 SmDocShell *pDocShell =
447 static_cast<SmDocShell*>(pModel->GetObjectShell());
448 pTree = pDocShell->GetFormulaTree();
449 aText = pDocShell->GetText();
452 GetDocHandler()->startDocument();
454 addChaffWhenEncryptedStorage();
456 /*Add xmlns line*/
457 SvXMLAttributeList &rList = GetAttrList();
459 // make use of a default namespace
460 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)
461 _GetNamespaceMap().Add( OUString(), GetXMLToken(XML_N_MATH), XML_NAMESPACE_MATH );
463 rList.AddAttribute(GetNamespaceMap().GetAttrNameByKey(XML_NAMESPACE_MATH_IDX),
464 GetNamespaceMap().GetNameByKey( XML_NAMESPACE_MATH_IDX));
466 //I think we need something like ImplExportEntities();
467 _ExportContent();
468 GetDocHandler()->endDocument();
471 bSuccess=true;
472 return 0;
475 void SmXMLExport::_ExportContent()
477 uno::Reference <frame::XModel> xModel = GetModel();
478 uno::Reference <lang::XUnoTunnel> xTunnel;
479 xTunnel = uno::Reference <lang::XUnoTunnel> (xModel,uno::UNO_QUERY);
480 SmModel *pModel = reinterpret_cast<SmModel *>
481 (xTunnel->getSomething(SmModel::getUnoTunnelId()));
482 SmDocShell *pDocShell = pModel ?
483 static_cast<SmDocShell*>(pModel->GetObjectShell()) : 0;
484 OSL_ENSURE( pDocShell, "doc shell missing" );
486 if (pDocShell && !pDocShell->GetFormat().IsTextmode())
488 // If the Math equation is not in text mode, we attach a display="block"
489 // attribute on the <math> root. We don't do anything if it is in
490 // text mode, the default display="inline" value will be used.
491 AddAttribute(XML_NAMESPACE_MATH, XML_DISPLAY, XML_BLOCK);
493 SvXMLElementExport aEquation(*this, XML_NAMESPACE_MATH, XML_MATH, true, true);
494 SvXMLElementExport *pSemantics=0;
496 if (!aText.isEmpty())
498 pSemantics = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
499 XML_SEMANTICS, true, true);
502 ExportNodes(pTree, 0);
504 if (!aText.isEmpty())
506 // Convert symbol names
507 if (pDocShell)
509 SmParser &rParser = pDocShell->GetParser();
510 bool bVal = rParser.IsExportSymbolNames();
511 rParser.SetExportSymbolNames( true );
512 SmNode *pTmpTree = rParser.Parse( aText );
513 aText = rParser.GetText();
514 delete pTmpTree;
515 rParser.SetExportSymbolNames( bVal );
518 AddAttribute(XML_NAMESPACE_MATH, XML_ENCODING,
519 OUString("StarMath 5.0"));
520 SvXMLElementExport aAnnotation(*this, XML_NAMESPACE_MATH,
521 XML_ANNOTATION, true, false);
522 GetDocHandler()->characters( aText );
524 delete pSemantics;
527 void SmXMLExport::GetViewSettings( Sequence < PropertyValue >& aProps)
529 uno::Reference <frame::XModel> xModel = GetModel();
530 if ( !xModel.is() )
531 return;
533 uno::Reference <lang::XUnoTunnel> xTunnel;
534 xTunnel = uno::Reference <lang::XUnoTunnel> (xModel,uno::UNO_QUERY);
535 SmModel *pModel = reinterpret_cast<SmModel *>
536 (xTunnel->getSomething(SmModel::getUnoTunnelId()));
538 if ( !pModel )
539 return;
541 SmDocShell *pDocShell =
542 static_cast<SmDocShell*>(pModel->GetObjectShell());
543 if ( !pDocShell )
544 return;
546 aProps.realloc( 4 );
547 PropertyValue *pValue = aProps.getArray();
548 sal_Int32 nIndex = 0;
550 Rectangle aRect( pDocShell->GetVisArea() );
552 pValue[nIndex].Name = "ViewAreaTop";
553 pValue[nIndex++].Value <<= aRect.Top();
555 pValue[nIndex].Name = "ViewAreaLeft";
556 pValue[nIndex++].Value <<= aRect.Left();
558 pValue[nIndex].Name = "ViewAreaWidth";
559 pValue[nIndex++].Value <<= aRect.GetWidth();
561 pValue[nIndex].Name = "ViewAreaHeight";
562 pValue[nIndex++].Value <<= aRect.GetHeight();
565 void SmXMLExport::GetConfigurationSettings( Sequence < PropertyValue > & rProps)
567 Reference < XPropertySet > xProps ( GetModel(), UNO_QUERY );
568 if ( xProps.is() )
570 Reference< XPropertySetInfo > xPropertySetInfo = xProps->getPropertySetInfo();
571 if (xPropertySetInfo.is())
573 Sequence< Property > aProps = xPropertySetInfo->getProperties();
574 sal_Int32 nCount(aProps.getLength());
575 if (nCount > 0)
577 rProps.realloc(nCount);
578 PropertyValue* pProps = rProps.getArray();
579 if (pProps)
581 SmConfig *pConfig = SM_MOD()->GetConfig();
582 const bool bUsedSymbolsOnly = pConfig && pConfig->IsSaveOnlyUsedSymbols();
584 const OUString sFormula ( "Formula" );
585 const OUString sBasicLibraries ( "BasicLibraries" );
586 const OUString sDialogLibraries ( "DialogLibraries" );
587 const OUString sRuntimeUID ( "RuntimeUID" );
588 for (sal_Int32 i = 0; i < nCount; i++, pProps++)
590 const OUString &rPropName = aProps[i].Name;
591 if (rPropName != sFormula &&
592 rPropName != sBasicLibraries &&
593 rPropName != sDialogLibraries &&
594 rPropName != sRuntimeUID)
596 pProps->Name = rPropName;
598 OUString aActualName( rPropName );
600 // handle 'save used symbols only'
601 if (bUsedSymbolsOnly && rPropName == "Symbols" )
602 aActualName = "UserDefinedSymbolsInUse";
604 pProps->Value = xProps->getPropertyValue( aActualName );
613 void SmXMLExport::ExportLine(const SmNode *pNode, int nLevel)
615 ExportExpression(pNode, nLevel);
618 void SmXMLExport::ExportBinaryHorizontal(const SmNode *pNode, int nLevel)
620 sal_uLong nGroup = pNode->GetToken().nGroup;
622 SvXMLElementExport* pRow = new SvXMLElementExport(*this,
623 XML_NAMESPACE_MATH, XML_MROW, true, true);
625 // Unfold the binary tree structure as long as the nodes are SmBinHorNode
626 // with the same nGroup. This will reduce the number of nested <mrow>
627 // elements e.g. we only need three <mrow> levels to export
629 // "a*b*c*d+e*f*g*h+i*j*k*l = a*b*c*d+e*f*g*h+i*j*k*l =
630 // a*b*c*d+e*f*g*h+i*j*k*l = a*b*c*d+e*f*g*h+i*j*k*l"
632 // See https://www.libreoffice.org/bugzilla/show_bug.cgi?id=66081
633 ::std::stack< const SmNode* > s;
634 s.push(pNode);
635 while (!s.empty())
637 const SmNode *node = s.top();
638 s.pop();
639 if (node->GetType() != NBINHOR || node->GetToken().nGroup != nGroup)
641 ExportNodes(node, nLevel+1);
642 continue;
644 const SmBinHorNode* binNode = static_cast<const SmBinHorNode*>(node);
645 s.push(binNode->RightOperand());
646 s.push(binNode->Symbol());
647 s.push(binNode->LeftOperand());
650 delete pRow;
653 void SmXMLExport::ExportUnaryHorizontal(const SmNode *pNode, int nLevel)
655 ExportExpression(pNode, nLevel);
658 void SmXMLExport::ExportExpression(const SmNode *pNode, int nLevel,
659 bool bNoMrowContainer /*=false*/)
661 SvXMLElementExport *pRow=0;
662 auto nSize = pNode->GetNumSubNodes();
664 // #i115443: nodes of type expression always need to be grouped with mrow statement
665 if (!bNoMrowContainer &&
666 (nSize > 1 || pNode->GetType() == NEXPRESSION))
667 pRow = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MROW, true, true);
669 for (sal_uInt16 i = 0; i < nSize; i++)
670 if (const SmNode *pTemp = pNode->GetSubNode(i))
671 ExportNodes(pTemp, nLevel+1);
673 delete pRow;
676 void SmXMLExport::ExportBinaryVertical(const SmNode *pNode, int nLevel)
678 OSL_ENSURE(pNode->GetNumSubNodes()==3,"Bad Fraction");
679 const SmNode *pNum = pNode->GetSubNode(0);
680 const SmNode *pDenom = pNode->GetSubNode(2);
681 if (pNum->GetType() == NALIGN && pNum->GetToken().eType != TALIGNC)
683 // A left or right alignment is specified on the numerator:
684 // attach the corresponding numalign attribute.
685 AddAttribute(XML_NAMESPACE_MATH, XML_NUMALIGN,
686 pNum->GetToken().eType == TALIGNL ? XML_LEFT : XML_RIGHT);
688 if (pDenom->GetType() == NALIGN && pDenom->GetToken().eType != TALIGNC)
690 // A left or right alignment is specified on the denominator:
691 // attach the corresponding denomalign attribute.
692 AddAttribute(XML_NAMESPACE_MATH, XML_DENOMALIGN,
693 pDenom->GetToken().eType == TALIGNL ? XML_LEFT : XML_RIGHT);
695 SvXMLElementExport aFraction(*this, XML_NAMESPACE_MATH, XML_MFRAC, true, true);
696 ExportNodes(pNum, nLevel);
697 ExportNodes(pDenom, nLevel);
700 void SmXMLExport::ExportBinaryDiagonal(const SmNode *pNode, int nLevel)
702 OSL_ENSURE(pNode->GetNumSubNodes()==3, "Bad Slash");
704 if (pNode->GetToken().eType == TWIDESLASH)
706 // wideslash
707 // export the node as <mfrac bevelled="true">
708 AddAttribute(XML_NAMESPACE_MATH, XML_BEVELLED, XML_TRUE);
709 SvXMLElementExport aFraction(*this, XML_NAMESPACE_MATH, XML_MFRAC,
710 true, true);
711 ExportNodes(pNode->GetSubNode(0), nLevel);
712 ExportNodes(pNode->GetSubNode(1), nLevel);
714 else
716 // widebslash
717 // We can not use <mfrac> to a backslash, so just use <mo>\</mo>
718 SvXMLElementExport *pRow = new SvXMLElementExport(*this,
719 XML_NAMESPACE_MATH, XML_MROW, true, true);
721 ExportNodes(pNode->GetSubNode(0), nLevel);
723 { // Scoping for <mo> creation
724 SvXMLElementExport aMo(*this, XML_NAMESPACE_MATH, XML_MO,
725 true, true);
726 sal_Unicode nArse[2] = {MS_BACKSLASH,0x00};
727 GetDocHandler()->characters(nArse);
730 ExportNodes(pNode->GetSubNode(1), nLevel);
732 delete pRow;
736 void SmXMLExport::ExportTable(const SmNode *pNode, int nLevel)
738 SvXMLElementExport *pTable=0;
740 sal_uInt16 nSize = pNode->GetNumSubNodes();
742 //If the list ends in newline then the last entry has
743 //no subnodes, the newline is superfulous so we just drop
744 //the last node, inclusion would create a bad MathML
745 //table
746 if (nSize >= 1)
748 const SmNode *pLine = pNode->GetSubNode(nSize-1);
749 if (pLine->GetType() == NLINE && pLine->GetNumSubNodes() == 1 &&
750 pLine->GetSubNode(0) != NULL &&
751 pLine->GetSubNode(0)->GetToken().eType == TNEWLINE)
752 --nSize;
755 // try to avoid creating a mtable element when the formula consists only
756 // of a single output line
757 if (nLevel || (nSize >1))
758 pTable = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTABLE, true, true);
760 for (sal_uInt16 i = 0; i < nSize; i++)
761 if (const SmNode *pTemp = pNode->GetSubNode(i))
763 SvXMLElementExport *pRow=0;
764 SvXMLElementExport *pCell=0;
765 if (pTable)
767 pRow = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTR, true, true);
768 SmTokenType eAlign = TALIGNC;
769 if (pTemp->GetType() == NALIGN)
771 // For Binom() and Stack() constructions, the NALIGN nodes
772 // are direct children.
773 // binom{alignl ...}{alignr ...} and
774 // stack{alignl ... ## alignr ... ## ...}
775 eAlign = pTemp->GetToken().eType;
777 else if (pTemp->GetType() == NLINE &&
778 pTemp->GetNumSubNodes() == 1 &&
779 pTemp->GetSubNode(0) &&
780 pTemp->GetSubNode(0)->GetType() == NALIGN)
782 // For the Table() construction, the NALIGN node is a child
783 // of an NLINE node.
784 // alignl ... newline alignr ... newline ...
785 eAlign = pTemp->GetSubNode(0)->GetToken().eType;
787 if (eAlign != TALIGNC)
789 // If a left or right alignment is specified on this line,
790 // attach the corresponding columnalign attribute.
791 AddAttribute(XML_NAMESPACE_MATH, XML_COLUMNALIGN,
792 eAlign == TALIGNL ? XML_LEFT : XML_RIGHT);
794 pCell = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTD, true, true);
796 ExportNodes(pTemp, nLevel+1);
797 delete pCell;
798 delete pRow;
801 delete pTable;
804 void SmXMLExport::ExportMath(const SmNode *pNode, int /*nLevel*/)
806 const SmMathSymbolNode *pTemp = static_cast<const SmMathSymbolNode *>(pNode);
807 SvXMLElementExport *pMath = 0;
809 if (pNode->GetType() == NMATH || pNode->GetType() == NGLYPH_SPECIAL)
811 // Export NMATH and NGLYPH_SPECIAL symbols as <mo> elements
812 pMath = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MO, true, false);
814 else
816 // Export NMATHIDENT and NPLACE symbols as <mi> elements:
817 // - These math symbols should not be drawn slanted. Hence we should
818 // attach a mathvariant="normal" attribute to single-char <mi> elements
819 // that are not mathematical alphanumeric symbol. For simplicity and to
820 // work around browser limitations, we always attach such an attribute.
821 // - The MathML specification suggests to use empty <mi> elements as
822 // placeholders but they won't be visible in most MathML rendering
823 // engines so let's use an empty square for NPLACE instead.
824 AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
825 pMath = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false);
827 sal_Unicode nArse[2];
828 nArse[0] = pTemp->GetText()[0];
829 sal_Unicode cTmp = ConvertMathToMathML( nArse[0] );
830 if (cTmp != 0)
831 nArse[0] = cTmp;
832 OSL_ENSURE(nArse[0] != 0xffff,"Non existent symbol");
833 nArse[1] = 0;
834 GetDocHandler()->characters(nArse);
836 delete pMath;
839 void SmXMLExport::ExportText(const SmNode *pNode, int /*nLevel*/)
841 SvXMLElementExport *pText;
842 const SmTextNode *pTemp = static_cast<const SmTextNode *>(pNode);
843 switch (pNode->GetToken().eType)
845 default:
846 case TIDENT:
848 //Note that we change the fontstyle to italic for strings that
849 //are italic and longer than a single character.
850 bool bIsItalic = IsItalic( pTemp->GetFont() );
851 if ((pTemp->GetText().getLength() > 1) && bIsItalic)
852 AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_ITALIC);
853 else if ((pTemp->GetText().getLength() == 1) && !bIsItalic)
854 AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
855 pText = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false);
856 break;
858 case TNUMBER:
859 pText = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MN, true, false);
860 break;
861 case TTEXT:
862 pText = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTEXT, true, false);
863 break;
865 GetDocHandler()->characters(pTemp->GetText());
866 delete pText;
869 void SmXMLExport::ExportBlank(const SmNode *pNode, int /*nLevel*/)
871 const SmBlankNode *pTemp = static_cast<const SmBlankNode *>(pNode);
872 //!! exports an <mspace> element. Note that for example "~_~" is allowed in
873 //!! Math (so it has no sense at all) but must not result in an empty
874 //!! <msub> tag in MathML !!
876 if (pTemp->GetBlankNum() != 0)
878 // Attach a width attribute. We choose the (somewhat arbitrary) values
879 // ".5em" for a small gap '`' and "2em" for a large gap '~'.
880 // (see SmBlankNode::IncreaseBy for how pTemp->nNum is set).
881 OUStringBuffer sStrBuf;
882 ::sax::Converter::convertDouble(sStrBuf, pTemp->GetBlankNum() * .5);
883 sStrBuf.append("em");
884 AddAttribute(XML_NAMESPACE_MATH, XML_WIDTH, sStrBuf.getStr());
887 SvXMLElementExport *pText;
889 pText = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSPACE,
890 true, false);
892 GetDocHandler()->characters( OUString() );
893 delete pText;
896 void SmXMLExport::ExportSubSupScript(const SmNode *pNode, int nLevel)
898 const SmNode *pSub = 0;
899 const SmNode *pSup = 0;
900 const SmNode *pCSub = 0;
901 const SmNode *pCSup = 0;
902 const SmNode *pLSub = 0;
903 const SmNode *pLSup = 0;
904 SvXMLElementExport *pThing = 0, *pThing2 = 0;
906 //if we have prescripts at all then we must use the tensor notation
908 //This is one of those excellent locations where scope is vital to
909 //arrange the construction and destruction of the element helper
910 //classes correctly
911 pLSub = pNode->GetSubNode(LSUB+1);
912 pLSup = pNode->GetSubNode(LSUP+1);
913 if (pLSub || pLSup)
915 SvXMLElementExport aMultiScripts(*this, XML_NAMESPACE_MATH,
916 XML_MMULTISCRIPTS, true, true);
919 if (NULL != (pCSub = pNode->GetSubNode(CSUB+1))
920 && NULL != (pCSup = pNode->GetSubNode(CSUP+1)))
922 pThing2 = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
923 XML_MUNDEROVER, true, true);
925 else if (NULL != (pCSub = pNode->GetSubNode(CSUB+1)))
927 pThing2 = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
928 XML_MUNDER, true, true);
930 else if (NULL != (pCSup = pNode->GetSubNode(CSUP+1)))
932 pThing2 = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
933 XML_MOVER, true, true);
936 ExportNodes(pNode->GetSubNode(0), nLevel+1); //Main Term
938 if (pCSub)
939 ExportNodes(pCSub, nLevel+1);
940 if (pCSup)
941 ExportNodes(pCSup, nLevel+1);
942 delete pThing2;
944 pSub = pNode->GetSubNode(RSUB+1);
945 pSup = pNode->GetSubNode(RSUP+1);
946 if (pSub || pSup)
948 if (pSub)
949 ExportNodes(pSub, nLevel+1);
950 else
952 SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE, true, true);
954 if (pSup)
955 ExportNodes(pSup, nLevel+1);
956 else
958 SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE, true, true);
962 //Separator element between suffix and prefix sub/sup pairs
964 SvXMLElementExport aPrescripts(*this, XML_NAMESPACE_MATH,
965 XML_MPRESCRIPTS, true, true);
968 if (pLSub)
969 ExportNodes(pLSub, nLevel+1);
970 else
972 SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE,
973 true, true);
976 if (pLSup)
977 ExportNodes(pLSup, nLevel+1);
978 else
980 SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE,
981 true, true);
985 else
987 if (NULL != (pSub = pNode->GetSubNode(RSUB+1)) &&
988 NULL != (pSup = pNode->GetSubNode(RSUP+1)))
990 pThing = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
991 XML_MSUBSUP, true, true);
993 else if (NULL != (pSub = pNode->GetSubNode(RSUB+1)))
995 pThing = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSUB,
996 true, true);
998 else if (NULL != (pSup = pNode->GetSubNode(RSUP+1)))
1000 pThing = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSUP,
1001 true, true);
1004 if (NULL != (pCSub = pNode->GetSubNode(CSUB+1))
1005 && NULL != (pCSup=pNode->GetSubNode(CSUP+1)))
1007 pThing2 = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
1008 XML_MUNDEROVER, true, true);
1010 else if (NULL != (pCSub = pNode->GetSubNode(CSUB+1)))
1012 pThing2 = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
1013 XML_MUNDER, true, true);
1015 else if (NULL != (pCSup = pNode->GetSubNode(CSUP+1)))
1017 pThing2 = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
1018 XML_MOVER, true, true);
1020 ExportNodes(pNode->GetSubNode(0), nLevel+1); //Main Term
1022 if (pCSub)
1023 ExportNodes(pCSub, nLevel+1);
1024 if (pCSup)
1025 ExportNodes(pCSup, nLevel+1);
1026 delete pThing2;
1028 if (pSub)
1029 ExportNodes(pSub, nLevel+1);
1030 if (pSup)
1031 ExportNodes(pSup, nLevel+1);
1032 delete pThing;
1036 void SmXMLExport::ExportBrace(const SmNode *pNode, int nLevel)
1038 const SmNode *pTemp;
1039 const SmNode *pLeft=pNode->GetSubNode(0);
1040 const SmNode *pRight=pNode->GetSubNode(2);
1041 SvXMLElementExport *pRow=0;
1043 // This used to generate <mfenced> or <mrow>+<mo> elements according to
1044 // the stretchiness of fences. The MathML recommendation defines an
1045 // <mrow>+<mo> construction that is equivalent to the <mfenced> element:
1046 // http://www.w3.org/TR/MathML3/chapter3.html#presm.mfenced
1047 // To simplify our code and avoid issues with mfenced implementations in
1048 // MathML rendering engines, we now always generate <mrow>+<mo> elements.
1049 // See #fdo 66282.
1051 // <mrow>
1052 pRow = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MROW,
1053 true, true);
1055 // <mo fence="true"> opening-fence </mo>
1056 if (pLeft && (pLeft->GetToken().eType != TNONE))
1058 AddAttribute(XML_NAMESPACE_MATH, XML_FENCE, XML_TRUE);
1059 if (pNode->GetScaleMode() == SCALE_HEIGHT)
1060 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
1061 else
1062 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
1063 ExportNodes(pLeft, nLevel+1);
1066 if (NULL != (pTemp = pNode->GetSubNode(1)))
1068 // <mrow>
1069 SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW,
1070 true, true);
1071 ExportNodes(pTemp, nLevel+1);
1072 // </mrow>
1075 // <mo fence="true"> closing-fence </mo>
1076 if (pRight && (pRight->GetToken().eType != TNONE))
1078 AddAttribute(XML_NAMESPACE_MATH, XML_FENCE, XML_TRUE);
1079 if (pNode->GetScaleMode() == SCALE_HEIGHT)
1080 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
1081 else
1082 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
1083 ExportNodes(pRight, nLevel+1);
1086 delete pRow;
1087 // </mrow>
1090 void SmXMLExport::ExportRoot(const SmNode *pNode, int nLevel)
1092 if (pNode->GetSubNode(0))
1094 SvXMLElementExport aRoot(*this, XML_NAMESPACE_MATH, XML_MROOT, true,
1095 true);
1096 ExportNodes(pNode->GetSubNode(2), nLevel+1);
1097 ExportNodes(pNode->GetSubNode(0), nLevel+1);
1099 else
1101 SvXMLElementExport aSqrt(*this, XML_NAMESPACE_MATH, XML_MSQRT, true,
1102 true);
1103 ExportNodes(pNode->GetSubNode(2), nLevel+1);
1107 void SmXMLExport::ExportOperator(const SmNode *pNode, int nLevel)
1109 /*we need to either use content or font and size attributes
1110 *here*/
1111 SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW,
1112 true, true);
1113 ExportNodes(pNode->GetSubNode(0), nLevel+1);
1114 ExportNodes(pNode->GetSubNode(1), nLevel+1);
1117 void SmXMLExport::ExportAttributes(const SmNode *pNode, int nLevel)
1119 SvXMLElementExport *pElement=0;
1121 if (pNode->GetToken().eType == TUNDERLINE)
1123 AddAttribute(XML_NAMESPACE_MATH, XML_ACCENTUNDER,
1124 XML_TRUE);
1125 pElement = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MUNDER,
1126 true, true);
1128 else if (pNode->GetToken().eType == TOVERSTRIKE)
1130 // export as <menclose notation="horizontalstrike">
1131 AddAttribute(XML_NAMESPACE_MATH, XML_NOTATION, XML_HORIZONTALSTRIKE);
1132 pElement = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
1133 XML_MENCLOSE, true, true);
1135 else
1137 AddAttribute(XML_NAMESPACE_MATH, XML_ACCENT,
1138 XML_TRUE);
1139 pElement = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MOVER,
1140 true, true);
1143 ExportNodes(pNode->GetSubNode(1), nLevel+1);
1144 switch (pNode->GetToken().eType)
1146 case TOVERLINE:
1148 //proper entity support required
1149 SvXMLElementExport aMath(*this, XML_NAMESPACE_MATH, XML_MO,
1150 true, true);
1151 sal_Unicode nArse[2] = {0xAF,0x00};
1152 GetDocHandler()->characters(nArse);
1154 break;
1155 case TUNDERLINE:
1157 //proper entity support required
1158 SvXMLElementExport aMath(*this, XML_NAMESPACE_MATH, XML_MO,
1159 true, true);
1160 sal_Unicode nArse[2] = {0x0332,0x00};
1161 GetDocHandler()->characters(nArse);
1163 break;
1164 case TOVERSTRIKE:
1165 break;
1166 case TWIDETILDE:
1167 case TWIDEHAT:
1168 case TWIDEVEC:
1170 // make these wide accents stretchy
1171 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
1172 ExportNodes(pNode->GetSubNode(0), nLevel+1);
1174 break;
1175 default:
1176 ExportNodes(pNode->GetSubNode(0), nLevel+1);
1177 break;
1179 delete pElement;
1182 static bool lcl_HasEffectOnMathvariant( const SmTokenType eType )
1184 return eType == TBOLD || eType == TNBOLD ||
1185 eType == TITALIC || eType == TNITALIC ||
1186 eType == TSANS || eType == TSERIF || eType == TFIXED;
1189 void SmXMLExport::ExportFont(const SmNode *pNode, int nLevel)
1192 // gather the mathvariant attribute relevant data from all
1193 // successively following SmFontNodes...
1195 int nBold = -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true;
1196 int nItalic = -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true;
1197 int nSansSerifFixed = -1;
1198 SmTokenType eNodeType = TUNKNOWN;
1199 while (lcl_HasEffectOnMathvariant( (eNodeType = pNode->GetToken().eType) ))
1201 switch (eNodeType)
1203 case TBOLD : nBold = 1; break;
1204 case TNBOLD : nBold = 0; break;
1205 case TITALIC : nItalic = 1; break;
1206 case TNITALIC : nItalic = 0; break;
1207 case TSANS : nSansSerifFixed = 0; break;
1208 case TSERIF : nSansSerifFixed = 1; break;
1209 case TFIXED : nSansSerifFixed = 2; break;
1210 default:
1211 SAL_WARN("starmath", "unexpected case");
1213 // According to the parser every node that is to be evaluated heres
1214 // has a single non-zero subnode at index 1!! Thus we only need to check
1215 // that single node for follow-up nodes that have an effect on the attribute.
1216 if (pNode->GetNumSubNodes() > 1 && pNode->GetSubNode(1) &&
1217 lcl_HasEffectOnMathvariant( pNode->GetSubNode(1)->GetToken().eType))
1219 pNode = pNode->GetSubNode(1);
1221 else
1222 break;
1225 switch (pNode->GetToken().eType)
1227 case TPHANTOM:
1228 // No attribute needed. An <mphantom> element will be used below.
1229 break;
1230 case TBLACK:
1231 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_BLACK);
1232 break;
1233 case TWHITE:
1234 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_WHITE);
1235 break;
1236 case TRED:
1237 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_RED);
1238 break;
1239 case TGREEN:
1240 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_GREEN);
1241 break;
1242 case TBLUE:
1243 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_BLUE);
1244 break;
1245 case TCYAN:
1246 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_AQUA);
1247 break;
1248 case TMAGENTA:
1249 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_FUCHSIA);
1250 break;
1251 case TYELLOW:
1252 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_YELLOW);
1253 break;
1254 case TSILVER:
1255 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_SILVER);
1256 break;
1257 case TGRAY:
1258 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_GRAY);
1259 break;
1260 case TMAROON:
1261 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_MAROON);
1262 break;
1263 case TOLIVE:
1264 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_OLIVE);
1265 break;
1266 case TLIME:
1267 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_LIME);
1268 break;
1269 case TAQUA:
1270 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_AQUA);
1271 break;
1272 case TTEAL:
1273 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_TEAL);
1274 break;
1275 case TNAVY:
1276 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_NAVY);
1277 break;
1278 case TFUCHSIA:
1279 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_FUCHSIA);
1280 break;
1281 case TPURPLE:
1282 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_PURPLE);
1283 break;
1284 case TSIZE:
1286 const SmFontNode *pFontNode = static_cast<const SmFontNode *>(pNode);
1287 const Fraction &aFrac = pFontNode->GetSizeParameter();
1289 OUStringBuffer sStrBuf;
1290 switch(pFontNode->GetSizeType())
1292 case FontSizeType::MULTIPLY:
1293 ::sax::Converter::convertDouble(sStrBuf,
1294 static_cast<double>(aFrac*Fraction(100.00)));
1295 sStrBuf.append('%');
1296 break;
1297 case FontSizeType::DIVIDE:
1298 ::sax::Converter::convertDouble(sStrBuf,
1299 static_cast<double>(Fraction(100.00)/aFrac));
1300 sStrBuf.append('%');
1301 break;
1302 case FontSizeType::ABSOLUT:
1303 ::sax::Converter::convertDouble(sStrBuf,
1304 static_cast<double>(aFrac));
1305 sStrBuf.append(
1306 GetXMLToken(XML_UNIT_PT));
1307 break;
1308 default:
1310 //The problem here is that the wheels fall off because
1311 //font size is stored in 100th's of a mm not pts, and
1312 //rounding errors take their toll on the original
1313 //value specified in points.
1315 //Must fix StarMath to retain the original pt values
1316 Fraction aTemp = Sm100th_mmToPts(pFontNode->GetFont().
1317 GetSize().Height());
1319 if (pFontNode->GetSizeType() == FontSizeType::MINUS)
1320 aTemp-=aFrac;
1321 else
1322 aTemp+=aFrac;
1324 double mytest = static_cast<double>(aTemp);
1326 mytest = ::rtl::math::round(mytest,1);
1327 ::sax::Converter::convertDouble(sStrBuf,mytest);
1328 sStrBuf.append(GetXMLToken(XML_UNIT_PT));
1330 break;
1333 OUString sStr(sStrBuf.makeStringAndClear());
1334 AddAttribute(XML_NAMESPACE_MATH, XML_MATHSIZE, sStr);
1336 break;
1337 case TBOLD:
1338 case TITALIC:
1339 case TNBOLD:
1340 case TNITALIC:
1341 case TFIXED:
1342 case TSANS:
1343 case TSERIF:
1345 // nBold: -1 = yet undefined; 0 = false; 1 = true;
1346 // nItalic: -1 = yet undefined; 0 = false; 1 = true;
1347 // nSansSerifFixed: -1 = undefined; 0 = sans; 1 = serif; 2 = fixed;
1348 const sal_Char *pText = "normal";
1349 if (nSansSerifFixed == -1 || nSansSerifFixed == 1)
1351 pText = "normal";
1352 if (nBold == 1 && nItalic != 1)
1353 pText = "bold";
1354 else if (nBold != 1 && nItalic == 1)
1355 pText = "italic";
1356 else if (nBold == 1 && nItalic == 1)
1357 pText = "bold-italic";
1359 else if (nSansSerifFixed == 0)
1361 pText = "sans-serif";
1362 if (nBold == 1 && nItalic != 1)
1363 pText = "bold-sans-serif";
1364 else if (nBold != 1 && nItalic == 1)
1365 pText = "sans-serif-italic";
1366 else if (nBold == 1 && nItalic == 1)
1367 pText = "sans-serif-bold-italic";
1369 else if (nSansSerifFixed == 2)
1370 pText = "monospace"; // no modifiers allowed for monospace ...
1371 else
1373 SAL_WARN("starmath", "unexpected case");
1375 AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, OUString::createFromAscii( pText ));
1377 break;
1378 default:
1379 break;
1383 // Wrap everything in an <mphantom> or <mstyle> element. These elements
1384 // are mrow-like, so ExportExpression doesn't need to add an explicit
1385 // <mrow> element. See #fdo 66283.
1386 SvXMLElementExport aElement(*this, XML_NAMESPACE_MATH,
1387 pNode->GetToken().eType == TPHANTOM ? XML_MPHANTOM : XML_MSTYLE,
1388 true, true);
1389 ExportExpression(pNode, nLevel, true);
1394 void SmXMLExport::ExportVerticalBrace(const SmNode *pNode, int nLevel)
1396 // "[body] overbrace [script]"
1398 // Position body, overbrace and script vertically. First place the overbrace
1399 // OVER the body and then the script OVER this expression.
1401 // [script]
1402 // --[overbrace]--
1403 // XXXXXX[body]XXXXXXX
1405 // Similarly for the underbrace construction.
1407 XMLTokenEnum which;
1409 switch (pNode->GetToken().eType)
1411 case TOVERBRACE:
1412 default:
1413 which = XML_MOVER;
1414 break;
1415 case TUNDERBRACE:
1416 which = XML_MUNDER;
1417 break;
1420 OSL_ENSURE(pNode->GetNumSubNodes()==3,"Bad Vertical Brace");
1421 SvXMLElementExport aOver1(*this, XML_NAMESPACE_MATH,which, true, true);
1422 {//Scoping
1423 // using accents will draw the over-/underbraces too close to the base
1424 // see http://www.w3.org/TR/MathML2/chapter3.html#id.3.4.5.2
1425 // also XML_ACCENT is illegal with XML_MUNDER. Thus no XML_ACCENT attribute here!
1426 SvXMLElementExport aOver2(*this, XML_NAMESPACE_MATH,which, true, true);
1427 ExportNodes(pNode->GetSubNode(0), nLevel);
1428 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
1429 ExportNodes(pNode->GetSubNode(1), nLevel);
1431 ExportNodes(pNode->GetSubNode(2), nLevel);
1434 void SmXMLExport::ExportMatrix(const SmNode *pNode, int nLevel)
1436 SvXMLElementExport aTable(*this, XML_NAMESPACE_MATH, XML_MTABLE, true, true);
1437 const SmMatrixNode *pMatrix = static_cast<const SmMatrixNode *>(pNode);
1438 sal_uInt16 i=0;
1439 for (sal_uInt16 y = 0; y < pMatrix->GetNumRows(); y++)
1441 SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MTR, true, true);
1442 for (sal_uInt16 x = 0; x < pMatrix->GetNumCols(); x++)
1443 if (const SmNode *pTemp = pNode->GetSubNode(i++))
1445 if (pTemp->GetType() == NALIGN &&
1446 pTemp->GetToken().eType != TALIGNC)
1448 // A left or right alignment is specified on this cell,
1449 // attach the corresponding columnalign attribute.
1450 AddAttribute(XML_NAMESPACE_MATH, XML_COLUMNALIGN,
1451 pTemp->GetToken().eType == TALIGNL ?
1452 XML_LEFT : XML_RIGHT);
1454 SvXMLElementExport aCell(*this, XML_NAMESPACE_MATH, XML_MTD, true, true);
1455 ExportNodes(pTemp, nLevel+1);
1460 void SmXMLExport::ExportNodes(const SmNode *pNode, int nLevel)
1462 if (!pNode)
1463 return;
1464 switch(pNode->GetType())
1466 case NTABLE:
1467 ExportTable(pNode, nLevel);
1468 break;
1469 case NALIGN:
1470 case NBRACEBODY:
1471 case NEXPRESSION:
1472 ExportExpression(pNode, nLevel);
1473 break;
1474 case NLINE:
1475 ExportLine(pNode, nLevel);
1476 break;
1477 case NTEXT:
1478 ExportText(pNode, nLevel);
1479 break;
1480 case NGLYPH_SPECIAL:
1481 case NMATH:
1483 sal_Unicode cTmp = 0;
1484 const SmTextNode *pTemp = static_cast< const SmTextNode * >(pNode);
1485 if (!pTemp->GetText().isEmpty())
1486 cTmp = ConvertMathToMathML( pTemp->GetText()[0] );
1487 if (cTmp == 0)
1489 // no conversion to MathML implemented -> export it as text
1490 // thus at least it will not vanish into nothing
1491 ExportText(pNode, nLevel);
1493 else
1495 //To fully handle generic MathML we need to implement the full
1496 //operator dictionary, we will generate MathML with explicit
1497 //stretchiness for now.
1498 sal_Int16 nLength = GetAttrList().getLength();
1499 bool bAddStretch=true;
1500 for ( sal_Int16 i = 0; i < nLength; i++ )
1502 OUString sLocalName;
1503 sal_uInt16 nPrefix = GetNamespaceMap().GetKeyByAttrName(
1504 GetAttrList().getNameByIndex(i), &sLocalName );
1506 if ( ( XML_NAMESPACE_MATH == nPrefix ) &&
1507 IsXMLToken(sLocalName, XML_STRETCHY) )
1509 bAddStretch = false;
1510 break;
1513 if (bAddStretch)
1515 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
1517 ExportMath(pNode, nLevel);
1520 break;
1521 case NSPECIAL: //NSPECIAL requires some sort of Entity preservation in the XML engine.
1522 case NMATHIDENT :
1523 case NPLACE:
1524 ExportMath(pNode, nLevel);
1525 break;
1526 case NBINHOR:
1527 ExportBinaryHorizontal(pNode, nLevel);
1528 break;
1529 case NUNHOR:
1530 ExportUnaryHorizontal(pNode, nLevel);
1531 break;
1532 case NBRACE:
1533 ExportBrace(pNode, nLevel);
1534 break;
1535 case NBINVER:
1536 ExportBinaryVertical(pNode, nLevel);
1537 break;
1538 case NBINDIAGONAL:
1539 ExportBinaryDiagonal(pNode, nLevel);
1540 break;
1541 case NSUBSUP:
1542 ExportSubSupScript(pNode, nLevel);
1543 break;
1544 case NROOT:
1545 ExportRoot(pNode, nLevel);
1546 break;
1547 case NOPER:
1548 ExportOperator(pNode, nLevel);
1549 break;
1550 case NATTRIBUT:
1551 ExportAttributes(pNode, nLevel);
1552 break;
1553 case NFONT:
1554 ExportFont(pNode, nLevel);
1555 break;
1556 case NVERTICAL_BRACE:
1557 ExportVerticalBrace(pNode, nLevel);
1558 break;
1559 case NMATRIX:
1560 ExportMatrix(pNode, nLevel);
1561 break;
1562 case NBLANK:
1563 ExportBlank(pNode, nLevel);
1564 break;
1565 default:
1566 SAL_WARN("starmath", "Warning: failed to export a node?");
1567 break;
1572 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */