cURL: follow redirects
[LibreOffice.git] / starmath / source / mathmlexport.cxx
blob41cc8a120a742b1d6b51c8c60367dce060f1b27f
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 there's
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 "cfgitem.hxx"
76 using namespace ::com::sun::star::beans;
77 using namespace ::com::sun::star::document;
78 using namespace ::com::sun::star::lang;
79 using namespace ::com::sun::star::uno;
80 using namespace ::com::sun::star;
81 using namespace ::xmloff::token;
83 namespace {
85 inline bool IsInPrivateUseArea( sal_Unicode cChar ) { return 0xE000 <= cChar && cChar <= 0xF8FF; }
87 sal_Unicode ConvertMathToMathML( sal_Unicode cChar )
89 sal_Unicode cRes = cChar;
90 if (IsInPrivateUseArea( cChar ))
92 SAL_WARN("starmath", "Error: private use area characters should no longer be in use!" );
93 cRes = sal_Unicode('@'); // just some character that should easily be notice as odd in the context
95 return cRes;
100 bool SmXMLExportWrapper::Export(SfxMedium &rMedium)
102 bool bRet=true;
103 uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
105 //Get model
106 uno::Reference< lang::XComponent > xModelComp(xModel, uno::UNO_QUERY );
108 bool bEmbedded = false;
109 uno::Reference <lang::XUnoTunnel> xTunnel(xModel,uno::UNO_QUERY);
110 SmModel *pModel = reinterpret_cast<SmModel *>
111 (xTunnel->getSomething(SmModel::getUnoTunnelId()));
113 SmDocShell *pDocShell = pModel ?
114 static_cast<SmDocShell*>(pModel->GetObjectShell()) : nullptr;
115 if ( pDocShell &&
116 SfxObjectCreateMode::EMBEDDED == pDocShell->GetCreateMode() )
117 bEmbedded = true;
119 uno::Reference<task::XStatusIndicator> xStatusIndicator;
120 if (!bEmbedded)
122 if (pDocShell /*&& pDocShell->GetMedium()*/)
124 OSL_ENSURE( pDocShell->GetMedium() == &rMedium,
125 "different SfxMedium found" );
127 SfxItemSet* pSet = rMedium.GetItemSet();
128 if (pSet)
130 const SfxUnoAnyItem* pItem = static_cast<const SfxUnoAnyItem*>(
131 pSet->GetItem(SID_PROGRESS_STATUSBAR_CONTROL) );
132 if (pItem)
133 pItem->GetValue() >>= xStatusIndicator;
137 // set progress range and start status indicator
138 if (xStatusIndicator.is())
140 sal_Int32 nProgressRange = bFlat ? 1 : 3;
141 xStatusIndicator->start(SM_RESSTR(STR_STATSTR_WRITING),
142 nProgressRange);
147 // create XPropertySet with three properties for status indicator
148 comphelper::PropertyMapEntry aInfoMap[] =
150 { OUString("UsePrettyPrinting"), 0,
151 cppu::UnoType<bool>::get(),
152 beans::PropertyAttribute::MAYBEVOID, 0},
153 { OUString("BaseURI"), 0,
154 ::cppu::UnoType<OUString>::get(),
155 beans::PropertyAttribute::MAYBEVOID, 0 },
156 { OUString("StreamRelPath"), 0,
157 ::cppu::UnoType<OUString>::get(),
158 beans::PropertyAttribute::MAYBEVOID, 0 },
159 { OUString("StreamName"), 0,
160 ::cppu::UnoType<OUString>::get(),
161 beans::PropertyAttribute::MAYBEVOID, 0 },
162 { OUString(), 0, css::uno::Type(), 0, 0 }
164 uno::Reference< beans::XPropertySet > xInfoSet(
165 comphelper::GenericPropertySet_CreateInstance(
166 new comphelper::PropertySetInfo( aInfoMap ) ) );
168 SvtSaveOptions aSaveOpt;
169 OUString sUsePrettyPrinting("UsePrettyPrinting");
170 bool bUsePrettyPrinting( bFlat || aSaveOpt.IsPrettyPrinting() );
171 xInfoSet->setPropertyValue( sUsePrettyPrinting, Any(bUsePrettyPrinting) );
173 // Set base URI
174 OUString sPropName( "BaseURI" );
175 xInfoSet->setPropertyValue( sPropName, makeAny( rMedium.GetBaseURL( true ) ) );
177 sal_Int32 nSteps=0;
178 if (xStatusIndicator.is())
179 xStatusIndicator->setValue(nSteps++);
180 if (!bFlat) //Storage (Package) of Stream
182 uno::Reference < embed::XStorage > xStg = rMedium.GetOutputStorage();
183 bool bOASIS = ( SotStorage::GetVersion( xStg ) > SOFFICE_FILEFORMAT_60 );
185 // TODO/LATER: handle the case of embedded links gracefully
186 if ( bEmbedded ) //&& !pStg->IsRoot() )
188 OUString aName;
189 if ( rMedium.GetItemSet() )
191 const SfxStringItem* pDocHierarchItem = static_cast<const SfxStringItem*>(
192 rMedium.GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME) );
193 if ( pDocHierarchItem )
194 aName = pDocHierarchItem->GetValue();
197 if ( !aName.isEmpty() )
199 sPropName = "StreamRelPath";
200 xInfoSet->setPropertyValue( sPropName, makeAny( aName ) );
204 if ( !bEmbedded )
206 if (xStatusIndicator.is())
207 xStatusIndicator->setValue(nSteps++);
209 bRet = WriteThroughComponent(
210 xStg, xModelComp, "meta.xml", xContext, xInfoSet,
211 (bOASIS ? "com.sun.star.comp.Math.XMLOasisMetaExporter"
212 : "com.sun.star.comp.Math.XMLMetaExporter"));
214 if ( bRet )
216 if (xStatusIndicator.is())
217 xStatusIndicator->setValue(nSteps++);
219 bRet = WriteThroughComponent(
220 xStg, xModelComp, "content.xml", xContext, xInfoSet,
221 "com.sun.star.comp.Math.XMLContentExporter");
224 if ( bRet )
226 if (xStatusIndicator.is())
227 xStatusIndicator->setValue(nSteps++);
229 bRet = WriteThroughComponent(
230 xStg, xModelComp, "settings.xml", xContext, xInfoSet,
231 (bOASIS ? "com.sun.star.comp.Math.XMLOasisSettingsExporter"
232 : "com.sun.star.comp.Math.XMLSettingsExporter") );
235 else
237 SvStream *pStream = rMedium.GetOutStream();
238 uno::Reference<io::XOutputStream> xOut(
239 new utl::OOutputStreamWrapper(*pStream) );
241 if (xStatusIndicator.is())
242 xStatusIndicator->setValue(nSteps++);
244 bRet = WriteThroughComponent(
245 xOut, xModelComp, xContext, xInfoSet,
246 "com.sun.star.comp.Math.XMLContentExporter");
249 if (xStatusIndicator.is())
250 xStatusIndicator->end();
252 return bRet;
256 /// export through an XML exporter component (output stream version)
257 bool SmXMLExportWrapper::WriteThroughComponent(
258 const Reference<io::XOutputStream>& xOutputStream,
259 const Reference<XComponent>& xComponent,
260 Reference<uno::XComponentContext> & rxContext,
261 Reference<beans::XPropertySet> & rPropSet,
262 const sal_Char* pComponentName )
264 OSL_ENSURE(xOutputStream.is(), "I really need an output stream!");
265 OSL_ENSURE(xComponent.is(), "Need component!");
266 OSL_ENSURE(nullptr != pComponentName, "Need component name!");
268 // get component
269 Reference< xml::sax::XWriter > xSaxWriter = xml::sax::Writer::create(rxContext );
271 // connect XML writer to output stream
272 xSaxWriter->setOutputStream( xOutputStream );
274 // prepare arguments (prepend doc handler to given arguments)
275 Reference<xml::sax::XDocumentHandler> xDocHandler( xSaxWriter,UNO_QUERY);
277 Sequence<Any> aArgs( 2 );
278 aArgs[0] <<= xDocHandler;
279 aArgs[1] <<= rPropSet;
281 // get filter component
282 Reference< document::XExporter > xExporter(
283 rxContext->getServiceManager()->createInstanceWithArgumentsAndContext(OUString::createFromAscii(pComponentName), aArgs, rxContext),
284 UNO_QUERY);
285 OSL_ENSURE( xExporter.is(),
286 "can't instantiate export filter component" );
287 if ( !xExporter.is() )
288 return false;
291 // connect model and filter
292 xExporter->setSourceDocument( xComponent );
294 // filter!
295 Reference < XFilter > xFilter( xExporter, UNO_QUERY );
296 uno::Sequence< PropertyValue > aProps(0);
297 xFilter->filter( aProps );
299 uno::Reference<lang::XUnoTunnel> xFilterTunnel( 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 const 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(nullptr != 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 ( const 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" );
337 uno::Reference < beans::XPropertySet > xSet( xStream, uno::UNO_QUERY );
338 xSet->setPropertyValue( aPropName, Any(aMime) );
340 // all streams must be encrypted in encrypted document
341 OUString aTmpPropName( "UseCommonStoragePasswordEncryption" );
342 xSet->setPropertyValue( aTmpPropName, Any(true) );
344 // set Base URL
345 if ( rPropSet.is() )
347 OUString sPropName( "StreamName" );
348 rPropSet->setPropertyValue( sPropName, makeAny( sStreamName ) );
351 // write the stuff
352 bool bRet = WriteThroughComponent( xStream->getOutputStream(), xComponent, rxContext,
353 rPropSet, pComponentName );
355 return bRet;
358 SmXMLExport::SmXMLExport(
359 const css::uno::Reference< css::uno::XComponentContext >& rContext,
360 OUString const & implementationName, SvXMLExportFlags nExportFlags)
361 : SvXMLExport(util::MeasureUnit::INCH, rContext, implementationName, XML_MATH,
362 nExportFlags)
363 , pTree(nullptr)
364 , bSuccess(false)
368 sal_Int64 SAL_CALL SmXMLExport::getSomething(
369 const uno::Sequence< sal_Int8 >& rId )
370 throw(uno::RuntimeException, std::exception)
372 if ( rId.getLength() == 16 &&
373 0 == memcmp( getUnoTunnelId().getConstArray(),
374 rId.getConstArray(), 16 ) )
375 return sal::static_int_cast< sal_Int64 >(reinterpret_cast< sal_uIntPtr >(this));
377 return SvXMLExport::getSomething( rId );
380 namespace
382 class theSmXMLExportUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSmXMLExportUnoTunnelId> {};
385 const uno::Sequence< sal_Int8 > & SmXMLExport::getUnoTunnelId() throw()
387 return theSmXMLExportUnoTunnelId::get().getSeq();
390 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* SAL_CALL
391 Math_XMLExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
393 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::ALL));
396 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* SAL_CALL
397 Math_XMLMetaExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
399 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLMetaExporter", SvXMLExportFlags::META));
402 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* SAL_CALL
403 Math_XMLOasisMetaExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
405 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLOasisMetaExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::META));
408 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* SAL_CALL
409 Math_XMLSettingsExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
411 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLSettingsExporter", SvXMLExportFlags::SETTINGS));
414 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* SAL_CALL
415 Math_XMLOasisSettingsExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
417 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLOasisSettingsExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::SETTINGS));
420 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* SAL_CALL
421 Math_XMLContentExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
423 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLContentExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::CONTENT));
426 sal_uInt32 SmXMLExport::exportDoc(enum XMLTokenEnum eClass)
428 if ( !(getExportFlags() & SvXMLExportFlags::CONTENT) )
430 SvXMLExport::exportDoc( eClass );
432 else
434 uno::Reference <frame::XModel> xModel = GetModel();
435 uno::Reference <lang::XUnoTunnel> xTunnel(xModel,uno::UNO_QUERY);
436 SmModel *pModel = reinterpret_cast<SmModel *>
437 (xTunnel->getSomething(SmModel::getUnoTunnelId()));
439 if (pModel)
441 SmDocShell *pDocShell =
442 static_cast<SmDocShell*>(pModel->GetObjectShell());
443 pTree = pDocShell->GetFormulaTree();
444 aText = pDocShell->GetText();
447 GetDocHandler()->startDocument();
449 addChaffWhenEncryptedStorage();
451 /*Add xmlns line*/
452 SvXMLAttributeList &rList = GetAttrList();
454 // make use of a default namespace
455 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)
456 GetNamespaceMap_().Add( OUString(), GetXMLToken(XML_N_MATH), XML_NAMESPACE_MATH );
458 rList.AddAttribute(GetNamespaceMap().GetAttrNameByKey(XML_NAMESPACE_MATH_IDX),
459 GetNamespaceMap().GetNameByKey( XML_NAMESPACE_MATH_IDX));
461 //I think we need something like ImplExportEntities();
462 ExportContent_();
463 GetDocHandler()->endDocument();
466 bSuccess=true;
467 return 0;
470 void SmXMLExport::ExportContent_()
472 uno::Reference <frame::XModel> xModel = GetModel();
473 uno::Reference <lang::XUnoTunnel> xTunnel(xModel,uno::UNO_QUERY);
474 SmModel *pModel = reinterpret_cast<SmModel *>
475 (xTunnel->getSomething(SmModel::getUnoTunnelId()));
476 SmDocShell *pDocShell = pModel ?
477 static_cast<SmDocShell*>(pModel->GetObjectShell()) : nullptr;
478 OSL_ENSURE( pDocShell, "doc shell missing" );
480 if (pDocShell && !pDocShell->GetFormat().IsTextmode())
482 // If the Math equation is not in text mode, we attach a display="block"
483 // attribute on the <math> root. We don't do anything if it is in
484 // text mode, the default display="inline" value will be used.
485 AddAttribute(XML_NAMESPACE_MATH, XML_DISPLAY, XML_BLOCK);
487 SvXMLElementExport aEquation(*this, XML_NAMESPACE_MATH, XML_MATH, true, true);
488 SvXMLElementExport *pSemantics=nullptr;
490 if (!aText.isEmpty())
492 pSemantics = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
493 XML_SEMANTICS, true, true);
496 ExportNodes(pTree, 0);
498 if (!aText.isEmpty())
500 // Convert symbol names
501 if (pDocShell)
503 SmParser &rParser = pDocShell->GetParser();
504 bool bVal = rParser.IsExportSymbolNames();
505 rParser.SetExportSymbolNames( true );
506 SmNode *pTmpTree = rParser.Parse( aText );
507 aText = rParser.GetText();
508 delete pTmpTree;
509 rParser.SetExportSymbolNames( bVal );
512 AddAttribute(XML_NAMESPACE_MATH, XML_ENCODING,
513 OUString("StarMath 5.0"));
514 SvXMLElementExport aAnnotation(*this, XML_NAMESPACE_MATH,
515 XML_ANNOTATION, true, false);
516 GetDocHandler()->characters( aText );
518 delete pSemantics;
521 void SmXMLExport::GetViewSettings( Sequence < PropertyValue >& aProps)
523 uno::Reference <frame::XModel> xModel = GetModel();
524 if ( !xModel.is() )
525 return;
527 uno::Reference <lang::XUnoTunnel> xTunnel(xModel,uno::UNO_QUERY);
528 SmModel *pModel = reinterpret_cast<SmModel *>
529 (xTunnel->getSomething(SmModel::getUnoTunnelId()));
531 if ( !pModel )
532 return;
534 SmDocShell *pDocShell =
535 static_cast<SmDocShell*>(pModel->GetObjectShell());
536 if ( !pDocShell )
537 return;
539 aProps.realloc( 4 );
540 PropertyValue *pValue = aProps.getArray();
541 sal_Int32 nIndex = 0;
543 Rectangle aRect( pDocShell->GetVisArea() );
545 pValue[nIndex].Name = "ViewAreaTop";
546 pValue[nIndex++].Value <<= aRect.Top();
548 pValue[nIndex].Name = "ViewAreaLeft";
549 pValue[nIndex++].Value <<= aRect.Left();
551 pValue[nIndex].Name = "ViewAreaWidth";
552 pValue[nIndex++].Value <<= aRect.GetWidth();
554 pValue[nIndex].Name = "ViewAreaHeight";
555 pValue[nIndex++].Value <<= aRect.GetHeight();
558 void SmXMLExport::GetConfigurationSettings( Sequence < PropertyValue > & rProps)
560 Reference < XPropertySet > xProps ( GetModel(), UNO_QUERY );
561 if ( xProps.is() )
563 Reference< XPropertySetInfo > xPropertySetInfo = xProps->getPropertySetInfo();
564 if (xPropertySetInfo.is())
566 Sequence< Property > aProps = xPropertySetInfo->getProperties();
567 sal_Int32 nCount(aProps.getLength());
568 if (nCount > 0)
570 rProps.realloc(nCount);
571 PropertyValue* pProps = rProps.getArray();
572 if (pProps)
574 SmMathConfig *pConfig = SM_MOD()->GetConfig();
575 const bool bUsedSymbolsOnly = pConfig && pConfig->IsSaveOnlyUsedSymbols();
577 const OUString sFormula ( "Formula" );
578 const OUString sBasicLibraries ( "BasicLibraries" );
579 const OUString sDialogLibraries ( "DialogLibraries" );
580 const OUString sRuntimeUID ( "RuntimeUID" );
581 for (sal_Int32 i = 0; i < nCount; i++, pProps++)
583 const OUString &rPropName = aProps[i].Name;
584 if (rPropName != sFormula &&
585 rPropName != sBasicLibraries &&
586 rPropName != sDialogLibraries &&
587 rPropName != sRuntimeUID)
589 pProps->Name = rPropName;
591 OUString aActualName( rPropName );
593 // handle 'save used symbols only'
594 if (bUsedSymbolsOnly && rPropName == "Symbols" )
595 aActualName = "UserDefinedSymbolsInUse";
597 pProps->Value = xProps->getPropertyValue( aActualName );
606 void SmXMLExport::ExportLine(const SmNode *pNode, int nLevel)
608 ExportExpression(pNode, nLevel);
611 void SmXMLExport::ExportBinaryHorizontal(const SmNode *pNode, int nLevel)
613 TG nGroup = pNode->GetToken().nGroup;
615 std::unique_ptr<SvXMLElementExport> pRow( new SvXMLElementExport(*this,
616 XML_NAMESPACE_MATH, XML_MROW, true, true) );
618 // Unfold the binary tree structure as long as the nodes are SmBinHorNode
619 // with the same nGroup. This will reduce the number of nested <mrow>
620 // elements e.g. we only need three <mrow> levels to export
622 // "a*b*c*d+e*f*g*h+i*j*k*l = a*b*c*d+e*f*g*h+i*j*k*l =
623 // a*b*c*d+e*f*g*h+i*j*k*l = a*b*c*d+e*f*g*h+i*j*k*l"
625 // See https://www.libreoffice.org/bugzilla/show_bug.cgi?id=66081
626 ::std::stack< const SmNode* > s;
627 s.push(pNode);
628 while (!s.empty())
630 const SmNode *node = s.top();
631 s.pop();
632 if (node->GetType() != NBINHOR || node->GetToken().nGroup != nGroup)
634 ExportNodes(node, nLevel+1);
635 continue;
637 const SmBinHorNode* binNode = static_cast<const SmBinHorNode*>(node);
638 s.push(binNode->RightOperand());
639 s.push(binNode->Symbol());
640 s.push(binNode->LeftOperand());
644 void SmXMLExport::ExportUnaryHorizontal(const SmNode *pNode, int nLevel)
646 ExportExpression(pNode, nLevel);
649 void SmXMLExport::ExportExpression(const SmNode *pNode, int nLevel,
650 bool bNoMrowContainer /*=false*/)
652 SvXMLElementExport *pRow=nullptr;
653 auto nSize = pNode->GetNumSubNodes();
655 // #i115443: nodes of type expression always need to be grouped with mrow statement
656 if (!bNoMrowContainer &&
657 (nSize > 1 || pNode->GetType() == NEXPRESSION))
658 pRow = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MROW, true, true);
660 for (sal_uInt16 i = 0; i < nSize; i++)
661 if (const SmNode *pTemp = pNode->GetSubNode(i))
662 ExportNodes(pTemp, nLevel+1);
664 delete pRow;
667 void SmXMLExport::ExportBinaryVertical(const SmNode *pNode, int nLevel)
669 assert(pNode->GetNumSubNodes() == 3);
670 const SmNode *pNum = pNode->GetSubNode(0);
671 const SmNode *pDenom = pNode->GetSubNode(2);
672 if (pNum->GetType() == NALIGN && pNum->GetToken().eType != TALIGNC)
674 // A left or right alignment is specified on the numerator:
675 // attach the corresponding numalign attribute.
676 AddAttribute(XML_NAMESPACE_MATH, XML_NUMALIGN,
677 pNum->GetToken().eType == TALIGNL ? XML_LEFT : XML_RIGHT);
679 if (pDenom->GetType() == NALIGN && pDenom->GetToken().eType != TALIGNC)
681 // A left or right alignment is specified on the denominator:
682 // attach the corresponding denomalign attribute.
683 AddAttribute(XML_NAMESPACE_MATH, XML_DENOMALIGN,
684 pDenom->GetToken().eType == TALIGNL ? XML_LEFT : XML_RIGHT);
686 SvXMLElementExport aFraction(*this, XML_NAMESPACE_MATH, XML_MFRAC, true, true);
687 ExportNodes(pNum, nLevel);
688 ExportNodes(pDenom, nLevel);
691 void SmXMLExport::ExportBinaryDiagonal(const SmNode *pNode, int nLevel)
693 assert(pNode->GetNumSubNodes() == 3);
695 if (pNode->GetToken().eType == TWIDESLASH)
697 // wideslash
698 // export the node as <mfrac bevelled="true">
699 AddAttribute(XML_NAMESPACE_MATH, XML_BEVELLED, XML_TRUE);
700 SvXMLElementExport aFraction(*this, XML_NAMESPACE_MATH, XML_MFRAC,
701 true, true);
702 ExportNodes(pNode->GetSubNode(0), nLevel);
703 ExportNodes(pNode->GetSubNode(1), nLevel);
705 else
707 // widebslash
708 // We can not use <mfrac> to a backslash, so just use <mo>\</mo>
709 std::unique_ptr<SvXMLElementExport> pRow( new SvXMLElementExport(*this,
710 XML_NAMESPACE_MATH, XML_MROW, true, true) );
712 ExportNodes(pNode->GetSubNode(0), nLevel);
714 { // Scoping for <mo> creation
715 SvXMLElementExport aMo(*this, XML_NAMESPACE_MATH, XML_MO,
716 true, true);
717 sal_Unicode nArse[2] = {MS_BACKSLASH,0x00};
718 GetDocHandler()->characters(nArse);
721 ExportNodes(pNode->GetSubNode(1), nLevel);
725 void SmXMLExport::ExportTable(const SmNode *pNode, int nLevel)
727 SvXMLElementExport *pTable=nullptr;
729 sal_uInt16 nSize = pNode->GetNumSubNodes();
731 //If the list ends in newline then the last entry has
732 //no subnodes, the newline is superfluous so we just drop
733 //the last node, inclusion would create a bad MathML
734 //table
735 if (nSize >= 1)
737 const SmNode *pLine = pNode->GetSubNode(nSize-1);
738 if (pLine->GetType() == NLINE && pLine->GetNumSubNodes() == 1 &&
739 pLine->GetSubNode(0) != nullptr &&
740 pLine->GetSubNode(0)->GetToken().eType == TNEWLINE)
741 --nSize;
744 // try to avoid creating a mtable element when the formula consists only
745 // of a single output line
746 if (nLevel || (nSize >1))
747 pTable = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTABLE, true, true);
749 for (sal_uInt16 i = 0; i < nSize; i++)
750 if (const SmNode *pTemp = pNode->GetSubNode(i))
752 SvXMLElementExport *pRow=nullptr;
753 SvXMLElementExport *pCell=nullptr;
754 if (pTable)
756 pRow = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTR, true, true);
757 SmTokenType eAlign = TALIGNC;
758 if (pTemp->GetType() == NALIGN)
760 // For Binom() and Stack() constructions, the NALIGN nodes
761 // are direct children.
762 // binom{alignl ...}{alignr ...} and
763 // stack{alignl ... ## alignr ... ## ...}
764 eAlign = pTemp->GetToken().eType;
766 else if (pTemp->GetType() == NLINE &&
767 pTemp->GetNumSubNodes() == 1 &&
768 pTemp->GetSubNode(0) &&
769 pTemp->GetSubNode(0)->GetType() == NALIGN)
771 // For the Table() construction, the NALIGN node is a child
772 // of an NLINE node.
773 // alignl ... newline alignr ... newline ...
774 eAlign = pTemp->GetSubNode(0)->GetToken().eType;
776 if (eAlign != TALIGNC)
778 // If a left or right alignment is specified on this line,
779 // attach the corresponding columnalign attribute.
780 AddAttribute(XML_NAMESPACE_MATH, XML_COLUMNALIGN,
781 eAlign == TALIGNL ? XML_LEFT : XML_RIGHT);
783 pCell = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTD, true, true);
785 ExportNodes(pTemp, nLevel+1);
786 delete pCell;
787 delete pRow;
790 delete pTable;
793 void SmXMLExport::ExportMath(const SmNode *pNode, int /*nLevel*/)
795 const SmTextNode *pTemp = static_cast<const SmTextNode *>(pNode);
796 SvXMLElementExport *pMath = nullptr;
798 if (pNode->GetType() == NMATH || pNode->GetType() == NGLYPH_SPECIAL)
800 // Export NMATH and NGLYPH_SPECIAL symbols as <mo> elements
801 pMath = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MO, true, false);
803 else if (pNode->GetType() == NSPECIAL)
805 bool bIsItalic = IsItalic(pNode->GetFont());
806 if (!bIsItalic)
807 AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
808 pMath = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false);
810 else
812 // Export NMATHIDENT and NPLACE symbols as <mi> elements:
813 // - These math symbols should not be drawn slanted. Hence we should
814 // attach a mathvariant="normal" attribute to single-char <mi> elements
815 // that are not mathematical alphanumeric symbol. For simplicity and to
816 // work around browser limitations, we always attach such an attribute.
817 // - The MathML specification suggests to use empty <mi> elements as
818 // placeholders but they won't be visible in most MathML rendering
819 // engines so let's use an empty square for NPLACE instead.
820 AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
821 pMath = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false);
823 sal_Unicode nArse[2];
824 nArse[0] = pTemp->GetText()[0];
825 sal_Unicode cTmp = ConvertMathToMathML( nArse[0] );
826 if (cTmp != 0)
827 nArse[0] = cTmp;
828 OSL_ENSURE(nArse[0] != 0xffff,"Non existent symbol");
829 nArse[1] = 0;
830 GetDocHandler()->characters(nArse);
832 delete pMath;
835 void SmXMLExport::ExportText(const SmNode *pNode, int /*nLevel*/)
837 SvXMLElementExport *pText;
838 const SmTextNode *pTemp = static_cast<const SmTextNode *>(pNode);
839 switch (pNode->GetToken().eType)
841 default:
842 case TIDENT:
844 //Note that we change the fontstyle to italic for strings that
845 //are italic and longer than a single character.
846 bool bIsItalic = IsItalic( pTemp->GetFont() );
847 if ((pTemp->GetText().getLength() > 1) && bIsItalic)
848 AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_ITALIC);
849 else if ((pTemp->GetText().getLength() == 1) && !bIsItalic)
850 AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
851 pText = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false);
852 break;
854 case TNUMBER:
855 pText = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MN, true, false);
856 break;
857 case TTEXT:
858 pText = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTEXT, true, false);
859 break;
861 GetDocHandler()->characters(pTemp->GetText());
862 delete pText;
865 void SmXMLExport::ExportBlank(const SmNode *pNode, int /*nLevel*/)
867 const SmBlankNode *pTemp = static_cast<const SmBlankNode *>(pNode);
868 //!! exports an <mspace> element. Note that for example "~_~" is allowed in
869 //!! Math (so it has no sense at all) but must not result in an empty
870 //!! <msub> tag in MathML !!
872 if (pTemp->GetBlankNum() != 0)
874 // Attach a width attribute. We choose the (somewhat arbitrary) values
875 // ".5em" for a small gap '`' and "2em" for a large gap '~'.
876 // (see SmBlankNode::IncreaseBy for how pTemp->mnNum is set).
877 OUStringBuffer sStrBuf;
878 ::sax::Converter::convertDouble(sStrBuf, pTemp->GetBlankNum() * .5);
879 sStrBuf.append("em");
880 AddAttribute(XML_NAMESPACE_MATH, XML_WIDTH, sStrBuf.getStr());
883 SvXMLElementExport *pText;
885 pText = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSPACE,
886 true, false);
888 GetDocHandler()->characters( OUString() );
889 delete pText;
892 void SmXMLExport::ExportSubSupScript(const SmNode *pNode, int nLevel)
894 const SmNode *pSub = nullptr;
895 const SmNode *pSup = nullptr;
896 const SmNode *pCSub = nullptr;
897 const SmNode *pCSup = nullptr;
898 const SmNode *pLSub = nullptr;
899 const SmNode *pLSup = nullptr;
900 SvXMLElementExport *pThing2 = nullptr;
902 //if we have prescripts at all then we must use the tensor notation
904 //This is one of those excellent locations where scope is vital to
905 //arrange the construction and destruction of the element helper
906 //classes correctly
907 pLSub = pNode->GetSubNode(LSUB+1);
908 pLSup = pNode->GetSubNode(LSUP+1);
909 if (pLSub || pLSup)
911 SvXMLElementExport aMultiScripts(*this, XML_NAMESPACE_MATH,
912 XML_MMULTISCRIPTS, true, true);
915 if (nullptr != (pCSub = pNode->GetSubNode(CSUB+1))
916 && nullptr != (pCSup = pNode->GetSubNode(CSUP+1)))
918 pThing2 = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
919 XML_MUNDEROVER, true, true);
921 else if (nullptr != (pCSub = pNode->GetSubNode(CSUB+1)))
923 pThing2 = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
924 XML_MUNDER, true, true);
926 else if (nullptr != (pCSup = pNode->GetSubNode(CSUP+1)))
928 pThing2 = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
929 XML_MOVER, true, true);
932 ExportNodes(pNode->GetSubNode(0), nLevel+1); //Main Term
934 if (pCSub)
935 ExportNodes(pCSub, nLevel+1);
936 if (pCSup)
937 ExportNodes(pCSup, nLevel+1);
938 delete pThing2;
940 pSub = pNode->GetSubNode(RSUB+1);
941 pSup = pNode->GetSubNode(RSUP+1);
942 if (pSub || pSup)
944 if (pSub)
945 ExportNodes(pSub, nLevel+1);
946 else
948 SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE, true, true);
950 if (pSup)
951 ExportNodes(pSup, nLevel+1);
952 else
954 SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE, true, true);
958 //Separator element between suffix and prefix sub/sup pairs
960 SvXMLElementExport aPrescripts(*this, XML_NAMESPACE_MATH,
961 XML_MPRESCRIPTS, true, true);
964 if (pLSub)
965 ExportNodes(pLSub, nLevel+1);
966 else
968 SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE,
969 true, true);
972 if (pLSup)
973 ExportNodes(pLSup, nLevel+1);
974 else
976 SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE,
977 true, true);
981 else
983 SvXMLElementExport *pThing = nullptr;
984 if (nullptr != (pSub = pNode->GetSubNode(RSUB+1)) &&
985 nullptr != (pSup = pNode->GetSubNode(RSUP+1)))
987 pThing = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
988 XML_MSUBSUP, true, true);
990 else if (nullptr != (pSub = pNode->GetSubNode(RSUB+1)))
992 pThing = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSUB,
993 true, true);
995 else if (nullptr != (pSup = pNode->GetSubNode(RSUP+1)))
997 pThing = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSUP,
998 true, true);
1001 if (nullptr != (pCSub = pNode->GetSubNode(CSUB+1))
1002 && nullptr != (pCSup=pNode->GetSubNode(CSUP+1)))
1004 pThing2 = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
1005 XML_MUNDEROVER, true, true);
1007 else if (nullptr != (pCSub = pNode->GetSubNode(CSUB+1)))
1009 pThing2 = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
1010 XML_MUNDER, true, true);
1012 else if (nullptr != (pCSup = pNode->GetSubNode(CSUP+1)))
1014 pThing2 = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
1015 XML_MOVER, true, true);
1017 ExportNodes(pNode->GetSubNode(0), nLevel+1); //Main Term
1019 if (pCSub)
1020 ExportNodes(pCSub, nLevel+1);
1021 if (pCSup)
1022 ExportNodes(pCSup, nLevel+1);
1023 delete pThing2;
1025 if (pSub)
1026 ExportNodes(pSub, nLevel+1);
1027 if (pSup)
1028 ExportNodes(pSup, nLevel+1);
1029 delete pThing;
1033 void SmXMLExport::ExportBrace(const SmNode *pNode, int nLevel)
1035 const SmNode *pTemp;
1036 const SmNode *pLeft=pNode->GetSubNode(0);
1037 const SmNode *pRight=pNode->GetSubNode(2);
1038 SvXMLElementExport *pRow=nullptr;
1040 // This used to generate <mfenced> or <mrow>+<mo> elements according to
1041 // the stretchiness of fences. The MathML recommendation defines an
1042 // <mrow>+<mo> construction that is equivalent to the <mfenced> element:
1043 // http://www.w3.org/TR/MathML3/chapter3.html#presm.mfenced
1044 // To simplify our code and avoid issues with mfenced implementations in
1045 // MathML rendering engines, we now always generate <mrow>+<mo> elements.
1046 // See #fdo 66282.
1048 // <mrow>
1049 pRow = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MROW,
1050 true, true);
1052 // <mo fence="true"> opening-fence </mo>
1053 if (pLeft && (pLeft->GetToken().eType != TNONE))
1055 AddAttribute(XML_NAMESPACE_MATH, XML_FENCE, XML_TRUE);
1056 if (pNode->GetScaleMode() == SCALE_HEIGHT)
1057 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
1058 else
1059 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
1060 ExportNodes(pLeft, nLevel+1);
1063 if (nullptr != (pTemp = pNode->GetSubNode(1)))
1065 // <mrow>
1066 SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW,
1067 true, true);
1068 ExportNodes(pTemp, nLevel+1);
1069 // </mrow>
1072 // <mo fence="true"> closing-fence </mo>
1073 if (pRight && (pRight->GetToken().eType != TNONE))
1075 AddAttribute(XML_NAMESPACE_MATH, XML_FENCE, XML_TRUE);
1076 if (pNode->GetScaleMode() == SCALE_HEIGHT)
1077 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
1078 else
1079 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
1080 ExportNodes(pRight, nLevel+1);
1083 delete pRow;
1084 // </mrow>
1087 void SmXMLExport::ExportRoot(const SmNode *pNode, int nLevel)
1089 if (pNode->GetSubNode(0))
1091 SvXMLElementExport aRoot(*this, XML_NAMESPACE_MATH, XML_MROOT, true,
1092 true);
1093 ExportNodes(pNode->GetSubNode(2), nLevel+1);
1094 ExportNodes(pNode->GetSubNode(0), nLevel+1);
1096 else
1098 SvXMLElementExport aSqrt(*this, XML_NAMESPACE_MATH, XML_MSQRT, true,
1099 true);
1100 ExportNodes(pNode->GetSubNode(2), nLevel+1);
1104 void SmXMLExport::ExportOperator(const SmNode *pNode, int nLevel)
1106 /*we need to either use content or font and size attributes
1107 *here*/
1108 SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW,
1109 true, true);
1110 ExportNodes(pNode->GetSubNode(0), nLevel+1);
1111 ExportNodes(pNode->GetSubNode(1), nLevel+1);
1114 void SmXMLExport::ExportAttributes(const SmNode *pNode, int nLevel)
1116 SvXMLElementExport *pElement=nullptr;
1118 if (pNode->GetToken().eType == TUNDERLINE)
1120 AddAttribute(XML_NAMESPACE_MATH, XML_ACCENTUNDER,
1121 XML_TRUE);
1122 pElement = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MUNDER,
1123 true, true);
1125 else if (pNode->GetToken().eType == TOVERSTRIKE)
1127 // export as <menclose notation="horizontalstrike">
1128 AddAttribute(XML_NAMESPACE_MATH, XML_NOTATION, XML_HORIZONTALSTRIKE);
1129 pElement = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
1130 XML_MENCLOSE, true, true);
1132 else
1134 AddAttribute(XML_NAMESPACE_MATH, XML_ACCENT,
1135 XML_TRUE);
1136 pElement = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MOVER,
1137 true, true);
1140 ExportNodes(pNode->GetSubNode(1), nLevel+1);
1141 switch (pNode->GetToken().eType)
1143 case TOVERLINE:
1145 //proper entity support required
1146 SvXMLElementExport aMath(*this, XML_NAMESPACE_MATH, XML_MO,
1147 true, true);
1148 sal_Unicode nArse[2] = {0xAF,0x00};
1149 GetDocHandler()->characters(nArse);
1151 break;
1152 case TUNDERLINE:
1154 //proper entity support required
1155 SvXMLElementExport aMath(*this, XML_NAMESPACE_MATH, XML_MO,
1156 true, true);
1157 sal_Unicode nArse[2] = {0x0332,0x00};
1158 GetDocHandler()->characters(nArse);
1160 break;
1161 case TOVERSTRIKE:
1162 break;
1163 case TWIDETILDE:
1164 case TWIDEHAT:
1165 case TWIDEVEC:
1167 // make these wide accents stretchy
1168 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
1169 ExportNodes(pNode->GetSubNode(0), nLevel+1);
1171 break;
1172 default:
1173 ExportNodes(pNode->GetSubNode(0), nLevel+1);
1174 break;
1176 delete pElement;
1179 static bool lcl_HasEffectOnMathvariant( const SmTokenType eType )
1181 return eType == TBOLD || eType == TNBOLD ||
1182 eType == TITALIC || eType == TNITALIC ||
1183 eType == TSANS || eType == TSERIF || eType == TFIXED;
1186 void SmXMLExport::ExportFont(const SmNode *pNode, int nLevel)
1189 // gather the mathvariant attribute relevant data from all
1190 // successively following SmFontNodes...
1192 int nBold = -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true;
1193 int nItalic = -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true;
1194 int nSansSerifFixed = -1;
1195 SmTokenType eNodeType = TUNKNOWN;
1196 while (lcl_HasEffectOnMathvariant( (eNodeType = pNode->GetToken().eType) ))
1198 switch (eNodeType)
1200 case TBOLD : nBold = 1; break;
1201 case TNBOLD : nBold = 0; break;
1202 case TITALIC : nItalic = 1; break;
1203 case TNITALIC : nItalic = 0; break;
1204 case TSANS : nSansSerifFixed = 0; break;
1205 case TSERIF : nSansSerifFixed = 1; break;
1206 case TFIXED : nSansSerifFixed = 2; break;
1207 default:
1208 SAL_WARN("starmath", "unexpected case");
1210 // According to the parser every node that is to be evaluated here
1211 // has a single non-zero subnode at index 1!! Thus we only need to check
1212 // that single node for follow-up nodes that have an effect on the attribute.
1213 if (pNode->GetNumSubNodes() > 1 && pNode->GetSubNode(1) &&
1214 lcl_HasEffectOnMathvariant( pNode->GetSubNode(1)->GetToken().eType))
1216 pNode = pNode->GetSubNode(1);
1218 else
1219 break;
1222 switch (pNode->GetToken().eType)
1224 case TPHANTOM:
1225 // No attribute needed. An <mphantom> element will be used below.
1226 break;
1227 case TBLACK:
1228 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_BLACK);
1229 break;
1230 case TWHITE:
1231 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_WHITE);
1232 break;
1233 case TRED:
1234 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_RED);
1235 break;
1236 case TGREEN:
1237 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_GREEN);
1238 break;
1239 case TBLUE:
1240 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_BLUE);
1241 break;
1242 case TCYAN:
1243 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_AQUA);
1244 break;
1245 case TMAGENTA:
1246 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_FUCHSIA);
1247 break;
1248 case TYELLOW:
1249 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_YELLOW);
1250 break;
1251 case TSILVER:
1252 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_SILVER);
1253 break;
1254 case TGRAY:
1255 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_GRAY);
1256 break;
1257 case TMAROON:
1258 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_MAROON);
1259 break;
1260 case TOLIVE:
1261 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_OLIVE);
1262 break;
1263 case TLIME:
1264 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_LIME);
1265 break;
1266 case TAQUA:
1267 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_AQUA);
1268 break;
1269 case TTEAL:
1270 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_TEAL);
1271 break;
1272 case TNAVY:
1273 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_NAVY);
1274 break;
1275 case TFUCHSIA:
1276 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_FUCHSIA);
1277 break;
1278 case TPURPLE:
1279 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_PURPLE);
1280 break;
1281 case TSIZE:
1283 const SmFontNode *pFontNode = static_cast<const SmFontNode *>(pNode);
1284 const Fraction &aFrac = pFontNode->GetSizeParameter();
1286 OUStringBuffer sStrBuf;
1287 switch(pFontNode->GetSizeType())
1289 case FontSizeType::MULTIPLY:
1290 ::sax::Converter::convertDouble(sStrBuf,
1291 static_cast<double>(aFrac*Fraction(100.00)));
1292 sStrBuf.append('%');
1293 break;
1294 case FontSizeType::DIVIDE:
1295 ::sax::Converter::convertDouble(sStrBuf,
1296 static_cast<double>(Fraction(100.00)/aFrac));
1297 sStrBuf.append('%');
1298 break;
1299 case FontSizeType::ABSOLUT:
1300 ::sax::Converter::convertDouble(sStrBuf,
1301 static_cast<double>(aFrac));
1302 sStrBuf.append(
1303 GetXMLToken(XML_UNIT_PT));
1304 break;
1305 default:
1307 //The problem here is that the wheels fall off because
1308 //font size is stored in 100th's of a mm not pts, and
1309 //rounding errors take their toll on the original
1310 //value specified in points.
1312 //Must fix StarMath to retain the original pt values
1313 Fraction aTemp = Sm100th_mmToPts(pFontNode->GetFont().GetFontSize().Height());
1315 if (pFontNode->GetSizeType() == FontSizeType::MINUS)
1316 aTemp-=aFrac;
1317 else
1318 aTemp+=aFrac;
1320 double mytest = static_cast<double>(aTemp);
1322 mytest = ::rtl::math::round(mytest,1);
1323 ::sax::Converter::convertDouble(sStrBuf,mytest);
1324 sStrBuf.append(GetXMLToken(XML_UNIT_PT));
1326 break;
1329 OUString sStr(sStrBuf.makeStringAndClear());
1330 AddAttribute(XML_NAMESPACE_MATH, XML_MATHSIZE, sStr);
1332 break;
1333 case TBOLD:
1334 case TITALIC:
1335 case TNBOLD:
1336 case TNITALIC:
1337 case TFIXED:
1338 case TSANS:
1339 case TSERIF:
1341 // nBold: -1 = yet undefined; 0 = false; 1 = true;
1342 // nItalic: -1 = yet undefined; 0 = false; 1 = true;
1343 // nSansSerifFixed: -1 = undefined; 0 = sans; 1 = serif; 2 = fixed;
1344 const sal_Char *pText = "normal";
1345 if (nSansSerifFixed == -1 || nSansSerifFixed == 1)
1347 pText = "normal";
1348 if (nBold == 1 && nItalic != 1)
1349 pText = "bold";
1350 else if (nBold != 1 && nItalic == 1)
1351 pText = "italic";
1352 else if (nBold == 1 && nItalic == 1)
1353 pText = "bold-italic";
1355 else if (nSansSerifFixed == 0)
1357 pText = "sans-serif";
1358 if (nBold == 1 && nItalic != 1)
1359 pText = "bold-sans-serif";
1360 else if (nBold != 1 && nItalic == 1)
1361 pText = "sans-serif-italic";
1362 else if (nBold == 1 && nItalic == 1)
1363 pText = "sans-serif-bold-italic";
1365 else if (nSansSerifFixed == 2)
1366 pText = "monospace"; // no modifiers allowed for monospace ...
1367 else
1369 SAL_WARN("starmath", "unexpected case");
1371 AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, OUString::createFromAscii( pText ));
1373 break;
1374 default:
1375 break;
1379 // Wrap everything in an <mphantom> or <mstyle> element. These elements
1380 // are mrow-like, so ExportExpression doesn't need to add an explicit
1381 // <mrow> element. See #fdo 66283.
1382 SvXMLElementExport aElement(*this, XML_NAMESPACE_MATH,
1383 pNode->GetToken().eType == TPHANTOM ? XML_MPHANTOM : XML_MSTYLE,
1384 true, true);
1385 ExportExpression(pNode, nLevel, true);
1390 void SmXMLExport::ExportVerticalBrace(const SmVerticalBraceNode *pNode, int nLevel)
1392 // "[body] overbrace [script]"
1394 // Position body, overbrace and script vertically. First place the overbrace
1395 // OVER the body and then the script OVER this expression.
1397 // [script]
1398 // --[overbrace]--
1399 // XXXXXX[body]XXXXXXX
1401 // Similarly for the underbrace construction.
1403 XMLTokenEnum which;
1405 switch (pNode->GetToken().eType)
1407 case TOVERBRACE:
1408 default:
1409 which = XML_MOVER;
1410 break;
1411 case TUNDERBRACE:
1412 which = XML_MUNDER;
1413 break;
1416 SvXMLElementExport aOver1(*this, XML_NAMESPACE_MATH,which, true, true);
1417 {//Scoping
1418 // using accents will draw the over-/underbraces too close to the base
1419 // see http://www.w3.org/TR/MathML2/chapter3.html#id.3.4.5.2
1420 // also XML_ACCENT is illegal with XML_MUNDER. Thus no XML_ACCENT attribute here!
1421 SvXMLElementExport aOver2(*this, XML_NAMESPACE_MATH,which, true, true);
1422 ExportNodes(pNode->Body(), nLevel);
1423 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
1424 ExportNodes(pNode->Brace(), nLevel);
1426 ExportNodes(pNode->Script(), nLevel);
1429 void SmXMLExport::ExportMatrix(const SmNode *pNode, int nLevel)
1431 SvXMLElementExport aTable(*this, XML_NAMESPACE_MATH, XML_MTABLE, true, true);
1432 const SmMatrixNode *pMatrix = static_cast<const SmMatrixNode *>(pNode);
1433 sal_uInt16 i=0;
1434 for (sal_uInt16 y = 0; y < pMatrix->GetNumRows(); y++)
1436 SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MTR, true, true);
1437 for (sal_uInt16 x = 0; x < pMatrix->GetNumCols(); x++)
1438 if (const SmNode *pTemp = pNode->GetSubNode(i++))
1440 if (pTemp->GetType() == NALIGN &&
1441 pTemp->GetToken().eType != TALIGNC)
1443 // A left or right alignment is specified on this cell,
1444 // attach the corresponding columnalign attribute.
1445 AddAttribute(XML_NAMESPACE_MATH, XML_COLUMNALIGN,
1446 pTemp->GetToken().eType == TALIGNL ?
1447 XML_LEFT : XML_RIGHT);
1449 SvXMLElementExport aCell(*this, XML_NAMESPACE_MATH, XML_MTD, true, true);
1450 ExportNodes(pTemp, nLevel+1);
1455 void SmXMLExport::ExportNodes(const SmNode *pNode, int nLevel)
1457 if (!pNode)
1458 return;
1459 switch(pNode->GetType())
1461 case NTABLE:
1462 ExportTable(pNode, nLevel);
1463 break;
1464 case NALIGN:
1465 case NBRACEBODY:
1466 case NEXPRESSION:
1467 ExportExpression(pNode, nLevel);
1468 break;
1469 case NLINE:
1470 ExportLine(pNode, nLevel);
1471 break;
1472 case NTEXT:
1473 ExportText(pNode, nLevel);
1474 break;
1475 case NGLYPH_SPECIAL:
1476 case NMATH:
1478 sal_Unicode cTmp = 0;
1479 const SmTextNode *pTemp = static_cast< const SmTextNode * >(pNode);
1480 if (!pTemp->GetText().isEmpty())
1481 cTmp = ConvertMathToMathML( pTemp->GetText()[0] );
1482 if (cTmp == 0)
1484 // no conversion to MathML implemented -> export it as text
1485 // thus at least it will not vanish into nothing
1486 ExportText(pNode, nLevel);
1488 else
1490 switch (pNode->GetToken().eType)
1492 case TINTD:
1493 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
1494 break;
1495 default:
1496 break;
1498 //To fully handle generic MathML we need to implement the full
1499 //operator dictionary, we will generate MathML with explicit
1500 //stretchiness for now.
1501 sal_Int16 nLength = GetAttrList().getLength();
1502 bool bAddStretch=true;
1503 for ( sal_Int16 i = 0; i < nLength; i++ )
1505 OUString sLocalName;
1506 sal_uInt16 nPrefix = GetNamespaceMap().GetKeyByAttrName(
1507 GetAttrList().getNameByIndex(i), &sLocalName );
1509 if ( ( XML_NAMESPACE_MATH == nPrefix ) &&
1510 IsXMLToken(sLocalName, XML_STRETCHY) )
1512 bAddStretch = false;
1513 break;
1516 if (bAddStretch)
1518 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
1520 ExportMath(pNode, nLevel);
1523 break;
1524 case NSPECIAL: //NSPECIAL requires some sort of Entity preservation in the XML engine.
1525 case NMATHIDENT :
1526 case NPLACE:
1527 ExportMath(pNode, nLevel);
1528 break;
1529 case NBINHOR:
1530 ExportBinaryHorizontal(pNode, nLevel);
1531 break;
1532 case NUNHOR:
1533 ExportUnaryHorizontal(pNode, nLevel);
1534 break;
1535 case NBRACE:
1536 ExportBrace(pNode, nLevel);
1537 break;
1538 case NBINVER:
1539 ExportBinaryVertical(pNode, nLevel);
1540 break;
1541 case NBINDIAGONAL:
1542 ExportBinaryDiagonal(pNode, nLevel);
1543 break;
1544 case NSUBSUP:
1545 ExportSubSupScript(pNode, nLevel);
1546 break;
1547 case NROOT:
1548 ExportRoot(pNode, nLevel);
1549 break;
1550 case NOPER:
1551 ExportOperator(pNode, nLevel);
1552 break;
1553 case NATTRIBUT:
1554 ExportAttributes(pNode, nLevel);
1555 break;
1556 case NFONT:
1557 ExportFont(pNode, nLevel);
1558 break;
1559 case NVERTICAL_BRACE:
1560 ExportVerticalBrace(static_cast<const SmVerticalBraceNode *>(pNode), nLevel);
1561 break;
1562 case NMATRIX:
1563 ExportMatrix(pNode, nLevel);
1564 break;
1565 case NBLANK:
1566 ExportBlank(pNode, nLevel);
1567 break;
1568 default:
1569 SAL_WARN("starmath", "Warning: failed to export a node?");
1570 break;
1575 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */