lok: vcl: fix multiple floatwin removal case more robustly.
[LibreOffice.git] / starmath / source / mathmlexport.cxx
blobd8924c95cb305d18fb47c54ea63f25a8b1a21ce1
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/Writer.hpp>
28 #include <com/sun/star/beans/PropertyAttribute.hpp>
29 #include <com/sun/star/embed/ElementModes.hpp>
30 #include <com/sun/star/util/MeasureUnit.hpp>
31 #include <com/sun/star/uno/Any.h>
33 #include <rtl/math.hxx>
34 #include <sfx2/frame.hxx>
35 #include <sfx2/docfile.hxx>
36 #include <sfx2/sfxsids.hrc>
37 #include <osl/diagnose.h>
38 #include <unotools/saveopt.hxx>
39 #include <svl/itemset.hxx>
40 #include <svl/stritem.hxx>
41 #include <comphelper/fileformat.h>
42 #include <comphelper/processfactory.hxx>
43 #include <unotools/streamwrap.hxx>
44 #include <sax/tools/converter.hxx>
45 #include <xmloff/xmlnmspe.hxx>
46 #include <xmloff/xmltoken.hxx>
47 #include <xmloff/nmspmap.hxx>
48 #include <xmloff/attrlist.hxx>
49 #include <comphelper/genericpropertyset.hxx>
50 #include <comphelper/servicehelper.hxx>
51 #include <comphelper/propertysetinfo.hxx>
52 #include <tools/diagnose_ex.h>
53 #include <sal/log.hxx>
55 #include <memory>
56 #include <stack>
58 #include "mathmlexport.hxx"
59 #include <strings.hrc>
60 #include <unomodel.hxx>
61 #include <document.hxx>
62 #include <utility.hxx>
63 #include "cfgitem.hxx"
65 using namespace ::com::sun::star::beans;
66 using namespace ::com::sun::star::document;
67 using namespace ::com::sun::star::lang;
68 using namespace ::com::sun::star::uno;
69 using namespace ::com::sun::star;
70 using namespace ::xmloff::token;
72 namespace {
74 bool IsInPrivateUseArea( sal_Unicode cChar ) { return 0xE000 <= cChar && cChar <= 0xF8FF; }
76 sal_Unicode ConvertMathToMathML( sal_Unicode cChar )
78 sal_Unicode cRes = cChar;
79 if (IsInPrivateUseArea( cChar ))
81 SAL_WARN("starmath", "Error: private use area characters should no longer be in use!" );
82 cRes = u'@'; // just some character that should easily be notice as odd in the context
84 return cRes;
89 bool SmXMLExportWrapper::Export(SfxMedium &rMedium)
91 bool bRet=true;
92 uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
94 //Get model
95 uno::Reference< lang::XComponent > xModelComp(xModel, uno::UNO_QUERY );
97 bool bEmbedded = false;
98 uno::Reference <lang::XUnoTunnel> xTunnel(xModel,uno::UNO_QUERY);
99 SmModel *pModel = reinterpret_cast<SmModel *>
100 (xTunnel->getSomething(SmModel::getUnoTunnelId()));
102 SmDocShell *pDocShell = pModel ?
103 static_cast<SmDocShell*>(pModel->GetObjectShell()) : nullptr;
104 if ( pDocShell &&
105 SfxObjectCreateMode::EMBEDDED == pDocShell->GetCreateMode() )
106 bEmbedded = true;
108 uno::Reference<task::XStatusIndicator> xStatusIndicator;
109 if (!bEmbedded)
111 if (pDocShell /*&& pDocShell->GetMedium()*/)
113 OSL_ENSURE( pDocShell->GetMedium() == &rMedium,
114 "different SfxMedium found" );
116 SfxItemSet* pSet = rMedium.GetItemSet();
117 if (pSet)
119 const SfxUnoAnyItem* pItem = static_cast<const SfxUnoAnyItem*>(
120 pSet->GetItem(SID_PROGRESS_STATUSBAR_CONTROL) );
121 if (pItem)
122 pItem->GetValue() >>= xStatusIndicator;
126 // set progress range and start status indicator
127 if (xStatusIndicator.is())
129 sal_Int32 nProgressRange = bFlat ? 1 : 3;
130 xStatusIndicator->start(SmResId(STR_STATSTR_WRITING),
131 nProgressRange);
136 // create XPropertySet with three properties for status indicator
137 comphelper::PropertyMapEntry aInfoMap[] =
139 { OUString("UsePrettyPrinting"), 0,
140 cppu::UnoType<bool>::get(),
141 beans::PropertyAttribute::MAYBEVOID, 0},
142 { OUString("BaseURI"), 0,
143 ::cppu::UnoType<OUString>::get(),
144 beans::PropertyAttribute::MAYBEVOID, 0 },
145 { OUString("StreamRelPath"), 0,
146 ::cppu::UnoType<OUString>::get(),
147 beans::PropertyAttribute::MAYBEVOID, 0 },
148 { OUString("StreamName"), 0,
149 ::cppu::UnoType<OUString>::get(),
150 beans::PropertyAttribute::MAYBEVOID, 0 },
151 { OUString(), 0, css::uno::Type(), 0, 0 }
153 uno::Reference< beans::XPropertySet > xInfoSet(
154 comphelper::GenericPropertySet_CreateInstance(
155 new comphelper::PropertySetInfo( aInfoMap ) ) );
157 SvtSaveOptions aSaveOpt;
158 bool bUsePrettyPrinting( bFlat || aSaveOpt.IsPrettyPrinting() );
159 xInfoSet->setPropertyValue( "UsePrettyPrinting", Any(bUsePrettyPrinting) );
161 // Set base URI
162 OUString sPropName( "BaseURI" );
163 xInfoSet->setPropertyValue( sPropName, makeAny( rMedium.GetBaseURL( true ) ) );
165 sal_Int32 nSteps=0;
166 if (xStatusIndicator.is())
167 xStatusIndicator->setValue(nSteps++);
168 if (!bFlat) //Storage (Package) of Stream
170 uno::Reference < embed::XStorage > xStg = rMedium.GetOutputStorage();
171 bool bOASIS = ( SotStorage::GetVersion( xStg ) > SOFFICE_FILEFORMAT_60 );
173 // TODO/LATER: handle the case of embedded links gracefully
174 if ( bEmbedded ) //&& !pStg->IsRoot() )
176 OUString aName;
177 if ( rMedium.GetItemSet() )
179 const SfxStringItem* pDocHierarchItem = static_cast<const SfxStringItem*>(
180 rMedium.GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME) );
181 if ( pDocHierarchItem )
182 aName = pDocHierarchItem->GetValue();
185 if ( !aName.isEmpty() )
187 sPropName = "StreamRelPath";
188 xInfoSet->setPropertyValue( sPropName, makeAny( aName ) );
192 if ( !bEmbedded )
194 if (xStatusIndicator.is())
195 xStatusIndicator->setValue(nSteps++);
197 bRet = WriteThroughComponent(
198 xStg, xModelComp, "meta.xml", xContext, xInfoSet,
199 (bOASIS ? "com.sun.star.comp.Math.XMLOasisMetaExporter"
200 : "com.sun.star.comp.Math.XMLMetaExporter"));
202 if ( bRet )
204 if (xStatusIndicator.is())
205 xStatusIndicator->setValue(nSteps++);
207 bRet = WriteThroughComponent(
208 xStg, xModelComp, "content.xml", xContext, xInfoSet,
209 "com.sun.star.comp.Math.XMLContentExporter");
212 if ( bRet )
214 if (xStatusIndicator.is())
215 xStatusIndicator->setValue(nSteps++);
217 bRet = WriteThroughComponent(
218 xStg, xModelComp, "settings.xml", xContext, xInfoSet,
219 (bOASIS ? "com.sun.star.comp.Math.XMLOasisSettingsExporter"
220 : "com.sun.star.comp.Math.XMLSettingsExporter") );
223 else
225 SvStream *pStream = rMedium.GetOutStream();
226 uno::Reference<io::XOutputStream> xOut(
227 new utl::OOutputStreamWrapper(*pStream) );
229 if (xStatusIndicator.is())
230 xStatusIndicator->setValue(nSteps++);
232 bRet = WriteThroughComponent(
233 xOut, xModelComp, xContext, xInfoSet,
234 "com.sun.star.comp.Math.XMLContentExporter");
237 if (xStatusIndicator.is())
238 xStatusIndicator->end();
240 return bRet;
244 /// export through an XML exporter component (output stream version)
245 bool SmXMLExportWrapper::WriteThroughComponent(
246 const Reference<io::XOutputStream>& xOutputStream,
247 const Reference<XComponent>& xComponent,
248 Reference<uno::XComponentContext> const & rxContext,
249 Reference<beans::XPropertySet> const & rPropSet,
250 const sal_Char* pComponentName )
252 OSL_ENSURE(xOutputStream.is(), "I really need an output stream!");
253 OSL_ENSURE(xComponent.is(), "Need component!");
254 OSL_ENSURE(nullptr != pComponentName, "Need component name!");
256 // get component
257 Reference< xml::sax::XWriter > xSaxWriter = xml::sax::Writer::create(rxContext );
259 // connect XML writer to output stream
260 xSaxWriter->setOutputStream( xOutputStream );
262 // prepare arguments (prepend doc handler to given arguments)
263 Reference<xml::sax::XDocumentHandler> xDocHandler( xSaxWriter,UNO_QUERY);
265 Sequence<Any> aArgs( 2 );
266 aArgs[0] <<= xDocHandler;
267 aArgs[1] <<= rPropSet;
269 // get filter component
270 Reference< document::XExporter > xExporter(
271 rxContext->getServiceManager()->createInstanceWithArgumentsAndContext(OUString::createFromAscii(pComponentName), aArgs, rxContext),
272 UNO_QUERY);
273 OSL_ENSURE( xExporter.is(),
274 "can't instantiate export filter component" );
275 if ( !xExporter.is() )
276 return false;
279 // connect model and filter
280 xExporter->setSourceDocument( xComponent );
282 // filter!
283 Reference < XFilter > xFilter( xExporter, UNO_QUERY );
284 uno::Sequence< PropertyValue > aProps(0);
285 xFilter->filter( aProps );
287 uno::Reference<lang::XUnoTunnel> xFilterTunnel( xFilter, uno::UNO_QUERY );
288 SmXMLExport *pFilter = reinterpret_cast< SmXMLExport * >(
289 sal::static_int_cast< sal_uIntPtr >(
290 xFilterTunnel->getSomething( SmXMLExport::getUnoTunnelId() )));
291 return pFilter == nullptr || pFilter->GetSuccess();
295 /// export through an XML exporter component (storage version)
296 bool SmXMLExportWrapper::WriteThroughComponent(
297 const Reference < embed::XStorage >& xStorage,
298 const Reference<XComponent>& xComponent,
299 const sal_Char* pStreamName,
300 Reference<uno::XComponentContext> const & rxContext,
301 Reference<beans::XPropertySet> const & rPropSet,
302 const sal_Char* pComponentName
305 OSL_ENSURE(xStorage.is(), "Need storage!");
306 OSL_ENSURE(nullptr != pStreamName, "Need stream name!");
308 // open stream
309 Reference < io::XStream > xStream;
310 OUString sStreamName = OUString::createFromAscii(pStreamName);
313 xStream = xStorage->openStreamElement( sStreamName,
314 embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
316 catch ( const uno::Exception& )
318 DBG_UNHANDLED_EXCEPTION("starmath", "Can't create output stream in package" );
319 return false;
322 uno::Reference < beans::XPropertySet > xSet( xStream, uno::UNO_QUERY );
323 xSet->setPropertyValue( "MediaType", Any(OUString( "text/xml" )) );
325 // all streams must be encrypted in encrypted document
326 xSet->setPropertyValue( "UseCommonStoragePasswordEncryption", Any(true) );
328 // set Base URL
329 if ( rPropSet.is() )
331 rPropSet->setPropertyValue( "StreamName", makeAny( sStreamName ) );
334 // write the stuff
335 bool bRet = WriteThroughComponent( xStream->getOutputStream(), xComponent, rxContext,
336 rPropSet, pComponentName );
338 return bRet;
341 SmXMLExport::SmXMLExport(
342 const css::uno::Reference< css::uno::XComponentContext >& rContext,
343 OUString const & implementationName, SvXMLExportFlags nExportFlags)
344 : SvXMLExport(util::MeasureUnit::INCH, rContext, implementationName, XML_MATH,
345 nExportFlags)
346 , pTree(nullptr)
347 , bSuccess(false)
351 sal_Int64 SAL_CALL SmXMLExport::getSomething(
352 const uno::Sequence< sal_Int8 >& rId )
354 if ( rId.getLength() == 16 &&
355 0 == memcmp( getUnoTunnelId().getConstArray(),
356 rId.getConstArray(), 16 ) )
357 return sal::static_int_cast< sal_Int64 >(reinterpret_cast< sal_uIntPtr >(this));
359 return SvXMLExport::getSomething( rId );
362 namespace
364 class theSmXMLExportUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSmXMLExportUnoTunnelId> {};
367 const uno::Sequence< sal_Int8 > & SmXMLExport::getUnoTunnelId() throw()
369 return theSmXMLExportUnoTunnelId::get().getSeq();
372 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
373 Math_XMLExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
375 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::ALL));
378 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
379 Math_XMLMetaExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
381 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLMetaExporter", SvXMLExportFlags::META));
384 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
385 Math_XMLOasisMetaExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
387 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLOasisMetaExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::META));
390 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
391 Math_XMLSettingsExporter_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.XMLSettingsExporter", SvXMLExportFlags::SETTINGS));
396 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
397 Math_XMLOasisSettingsExporter_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.XMLOasisSettingsExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::SETTINGS));
402 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
403 Math_XMLContentExporter_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.XMLContentExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::CONTENT));
408 ErrCode SmXMLExport::exportDoc(enum XMLTokenEnum eClass)
410 if ( !(getExportFlags() & SvXMLExportFlags::CONTENT) )
412 SvXMLExport::exportDoc( eClass );
414 else
416 uno::Reference <frame::XModel> xModel = GetModel();
417 uno::Reference <lang::XUnoTunnel> xTunnel(xModel,uno::UNO_QUERY);
418 SmModel *pModel = reinterpret_cast<SmModel *>
419 (xTunnel->getSomething(SmModel::getUnoTunnelId()));
421 if (pModel)
423 SmDocShell *pDocShell =
424 static_cast<SmDocShell*>(pModel->GetObjectShell());
425 pTree = pDocShell->GetFormulaTree();
426 aText = pDocShell->GetText();
429 GetDocHandler()->startDocument();
431 addChaffWhenEncryptedStorage();
433 /*Add xmlns line*/
434 SvXMLAttributeList &rList = GetAttrList();
436 // make use of a default namespace
437 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)
438 GetNamespaceMap_().Add( OUString(), GetXMLToken(XML_N_MATH), XML_NAMESPACE_MATH );
440 rList.AddAttribute(GetNamespaceMap().GetAttrNameByKey(XML_NAMESPACE_MATH),
441 GetNamespaceMap().GetNameByKey( XML_NAMESPACE_MATH));
443 //I think we need something like ImplExportEntities();
444 ExportContent_();
445 GetDocHandler()->endDocument();
448 bSuccess=true;
449 return ERRCODE_NONE;
452 void SmXMLExport::ExportContent_()
454 uno::Reference <frame::XModel> xModel = GetModel();
455 uno::Reference <lang::XUnoTunnel> xTunnel(xModel,uno::UNO_QUERY);
456 SmModel *pModel = reinterpret_cast<SmModel *>
457 (xTunnel->getSomething(SmModel::getUnoTunnelId()));
458 SmDocShell *pDocShell = pModel ?
459 static_cast<SmDocShell*>(pModel->GetObjectShell()) : nullptr;
460 OSL_ENSURE( pDocShell, "doc shell missing" );
462 if (pDocShell && !pDocShell->GetFormat().IsTextmode())
464 // If the Math equation is not in text mode, we attach a display="block"
465 // attribute on the <math> root. We don't do anything if it is in
466 // text mode, the default display="inline" value will be used.
467 AddAttribute(XML_NAMESPACE_MATH, XML_DISPLAY, XML_BLOCK);
469 SvXMLElementExport aEquation(*this, XML_NAMESPACE_MATH, XML_MATH, true, true);
470 std::unique_ptr<SvXMLElementExport> pSemantics;
472 if (!aText.isEmpty())
474 pSemantics.reset( new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
475 XML_SEMANTICS, true, true) );
478 ExportNodes(pTree, 0);
480 if (!aText.isEmpty())
482 // Convert symbol names
483 if (pDocShell)
485 SmParser &rParser = pDocShell->GetParser();
486 bool bVal = rParser.IsExportSymbolNames();
487 rParser.SetExportSymbolNames( true );
488 auto pTmpTree = rParser.Parse( aText );
489 aText = rParser.GetText();
490 pTmpTree.reset();
491 rParser.SetExportSymbolNames( bVal );
494 AddAttribute(XML_NAMESPACE_MATH, XML_ENCODING,
495 OUString("StarMath 5.0"));
496 SvXMLElementExport aAnnotation(*this, XML_NAMESPACE_MATH,
497 XML_ANNOTATION, true, false);
498 GetDocHandler()->characters( aText );
502 void SmXMLExport::GetViewSettings( Sequence < PropertyValue >& aProps)
504 uno::Reference <frame::XModel> xModel = GetModel();
505 if ( !xModel.is() )
506 return;
508 uno::Reference <lang::XUnoTunnel> xTunnel(xModel,uno::UNO_QUERY);
509 SmModel *pModel = reinterpret_cast<SmModel *>
510 (xTunnel->getSomething(SmModel::getUnoTunnelId()));
512 if ( !pModel )
513 return;
515 SmDocShell *pDocShell =
516 static_cast<SmDocShell*>(pModel->GetObjectShell());
517 if ( !pDocShell )
518 return;
520 aProps.realloc( 4 );
521 PropertyValue *pValue = aProps.getArray();
522 sal_Int32 nIndex = 0;
524 tools::Rectangle aRect( pDocShell->GetVisArea() );
526 pValue[nIndex].Name = "ViewAreaTop";
527 pValue[nIndex++].Value <<= aRect.Top();
529 pValue[nIndex].Name = "ViewAreaLeft";
530 pValue[nIndex++].Value <<= aRect.Left();
532 pValue[nIndex].Name = "ViewAreaWidth";
533 pValue[nIndex++].Value <<= aRect.GetWidth();
535 pValue[nIndex].Name = "ViewAreaHeight";
536 pValue[nIndex++].Value <<= aRect.GetHeight();
539 void SmXMLExport::GetConfigurationSettings( Sequence < PropertyValue > & rProps)
541 Reference < XPropertySet > xProps ( GetModel(), UNO_QUERY );
542 if ( xProps.is() )
544 Reference< XPropertySetInfo > xPropertySetInfo = xProps->getPropertySetInfo();
545 if (xPropertySetInfo.is())
547 Sequence< Property > aProps = xPropertySetInfo->getProperties();
548 if (const sal_Int32 nCount = aProps.getLength())
550 rProps.realloc(nCount);
551 SmMathConfig* pConfig = SM_MOD()->GetConfig();
552 const bool bUsedSymbolsOnly = pConfig && pConfig->IsSaveOnlyUsedSymbols();
554 std::transform(aProps.begin(), aProps.end(), rProps.begin(),
555 [bUsedSymbolsOnly, &xProps](Property& prop) {
556 PropertyValue aRet;
557 if (prop.Name != "Formula" && prop.Name != "BasicLibraries"
558 && prop.Name != "DialogLibraries"
559 && prop.Name != "RuntimeUID")
561 aRet.Name = prop.Name;
562 OUString aActualName(prop.Name);
563 // handle 'save used symbols only'
564 if (bUsedSymbolsOnly && prop.Name == "Symbols")
565 aActualName = "UserDefinedSymbolsInUse";
566 aRet.Value = xProps->getPropertyValue(aActualName);
568 return aRet;
575 void SmXMLExport::ExportLine(const SmNode *pNode, int nLevel)
577 ExportExpression(pNode, nLevel);
580 void SmXMLExport::ExportBinaryHorizontal(const SmNode *pNode, int nLevel)
582 TG nGroup = pNode->GetToken().nGroup;
584 std::unique_ptr<SvXMLElementExport> pRow( new SvXMLElementExport(*this,
585 XML_NAMESPACE_MATH, XML_MROW, true, true) );
587 // Unfold the binary tree structure as long as the nodes are SmBinHorNode
588 // with the same nGroup. This will reduce the number of nested <mrow>
589 // elements e.g. we only need three <mrow> levels to export
591 // "a*b*c*d+e*f*g*h+i*j*k*l = a*b*c*d+e*f*g*h+i*j*k*l =
592 // a*b*c*d+e*f*g*h+i*j*k*l = a*b*c*d+e*f*g*h+i*j*k*l"
594 // See https://www.libreoffice.org/bugzilla/show_bug.cgi?id=66081
595 ::std::stack< const SmNode* > s;
596 s.push(pNode);
597 while (!s.empty())
599 const SmNode *node = s.top();
600 s.pop();
601 if (node->GetType() != SmNodeType::BinHor || node->GetToken().nGroup != nGroup)
603 ExportNodes(node, nLevel+1);
604 continue;
606 const SmBinHorNode* binNode = static_cast<const SmBinHorNode*>(node);
607 s.push(binNode->RightOperand());
608 s.push(binNode->Symbol());
609 s.push(binNode->LeftOperand());
613 void SmXMLExport::ExportUnaryHorizontal(const SmNode *pNode, int nLevel)
615 ExportExpression(pNode, nLevel);
618 void SmXMLExport::ExportExpression(const SmNode *pNode, int nLevel,
619 bool bNoMrowContainer /*=false*/)
621 std::unique_ptr<SvXMLElementExport> pRow;
622 size_t nSize = pNode->GetNumSubNodes();
624 // #i115443: nodes of type expression always need to be grouped with mrow statement
625 if (!bNoMrowContainer &&
626 (nSize > 1 || pNode->GetType() == SmNodeType::Expression))
627 pRow.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MROW, true, true));
629 for (size_t i = 0; i < nSize; ++i)
631 if (const SmNode *pTemp = pNode->GetSubNode(i))
632 ExportNodes(pTemp, nLevel+1);
636 void SmXMLExport::ExportBinaryVertical(const SmNode *pNode, int nLevel)
638 assert(pNode->GetNumSubNodes() == 3);
639 const SmNode *pNum = pNode->GetSubNode(0);
640 const SmNode *pDenom = pNode->GetSubNode(2);
641 if (pNum->GetType() == SmNodeType::Align && pNum->GetToken().eType != TALIGNC)
643 // A left or right alignment is specified on the numerator:
644 // attach the corresponding numalign attribute.
645 AddAttribute(XML_NAMESPACE_MATH, XML_NUMALIGN,
646 pNum->GetToken().eType == TALIGNL ? XML_LEFT : XML_RIGHT);
648 if (pDenom->GetType() == SmNodeType::Align && pDenom->GetToken().eType != TALIGNC)
650 // A left or right alignment is specified on the denominator:
651 // attach the corresponding denomalign attribute.
652 AddAttribute(XML_NAMESPACE_MATH, XML_DENOMALIGN,
653 pDenom->GetToken().eType == TALIGNL ? XML_LEFT : XML_RIGHT);
655 SvXMLElementExport aFraction(*this, XML_NAMESPACE_MATH, XML_MFRAC, true, true);
656 ExportNodes(pNum, nLevel);
657 ExportNodes(pDenom, nLevel);
660 void SmXMLExport::ExportBinaryDiagonal(const SmNode *pNode, int nLevel)
662 assert(pNode->GetNumSubNodes() == 3);
664 if (pNode->GetToken().eType == TWIDESLASH)
666 // wideslash
667 // export the node as <mfrac bevelled="true">
668 AddAttribute(XML_NAMESPACE_MATH, XML_BEVELLED, XML_TRUE);
669 SvXMLElementExport aFraction(*this, XML_NAMESPACE_MATH, XML_MFRAC,
670 true, true);
671 ExportNodes(pNode->GetSubNode(0), nLevel);
672 ExportNodes(pNode->GetSubNode(1), nLevel);
674 else
676 // widebslash
677 // We can not use <mfrac> to a backslash, so just use <mo>\</mo>
678 std::unique_ptr<SvXMLElementExport> pRow( new SvXMLElementExport(*this,
679 XML_NAMESPACE_MATH, XML_MROW, true, true) );
681 ExportNodes(pNode->GetSubNode(0), nLevel);
683 { // Scoping for <mo> creation
684 SvXMLElementExport aMo(*this, XML_NAMESPACE_MATH, XML_MO,
685 true, true);
686 sal_Unicode const nArse[2] = {MS_BACKSLASH,0x00};
687 GetDocHandler()->characters(nArse);
690 ExportNodes(pNode->GetSubNode(1), nLevel);
694 void SmXMLExport::ExportTable(const SmNode *pNode, int nLevel)
696 std::unique_ptr<SvXMLElementExport> pTable;
698 size_t nSize = pNode->GetNumSubNodes();
700 //If the list ends in newline then the last entry has
701 //no subnodes, the newline is superfluous so we just drop
702 //the last node, inclusion would create a bad MathML
703 //table
704 if (nSize >= 1)
706 const SmNode *pLine = pNode->GetSubNode(nSize-1);
707 if (pLine->GetType() == SmNodeType::Line && pLine->GetNumSubNodes() == 1 &&
708 pLine->GetSubNode(0) != nullptr &&
709 pLine->GetSubNode(0)->GetToken().eType == TNEWLINE)
710 --nSize;
713 // try to avoid creating a mtable element when the formula consists only
714 // of a single output line
715 if (nLevel || (nSize >1))
716 pTable.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTABLE, true, true));
718 for (size_t i = 0; i < nSize; ++i)
720 if (const SmNode *pTemp = pNode->GetSubNode(i))
722 SvXMLElementExport *pRow=nullptr;
723 SvXMLElementExport *pCell=nullptr;
724 if (pTable)
726 pRow = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTR, true, true);
727 SmTokenType eAlign = TALIGNC;
728 if (pTemp->GetType() == SmNodeType::Align)
730 // For Binom() and Stack() constructions, the SmNodeType::Align nodes
731 // are direct children.
732 // binom{alignl ...}{alignr ...} and
733 // stack{alignl ... ## alignr ... ## ...}
734 eAlign = pTemp->GetToken().eType;
736 else if (pTemp->GetType() == SmNodeType::Line &&
737 pTemp->GetNumSubNodes() == 1 &&
738 pTemp->GetSubNode(0) &&
739 pTemp->GetSubNode(0)->GetType() == SmNodeType::Align)
741 // For the Table() construction, the SmNodeType::Align node is a child
742 // of an SmNodeType::Line node.
743 // alignl ... newline alignr ... newline ...
744 eAlign = pTemp->GetSubNode(0)->GetToken().eType;
746 if (eAlign != TALIGNC)
748 // If a left or right alignment is specified on this line,
749 // attach the corresponding columnalign attribute.
750 AddAttribute(XML_NAMESPACE_MATH, XML_COLUMNALIGN,
751 eAlign == TALIGNL ? XML_LEFT : XML_RIGHT);
753 pCell = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTD, true, true);
755 ExportNodes(pTemp, nLevel+1);
756 delete pCell;
757 delete pRow;
762 void SmXMLExport::ExportMath(const SmNode *pNode)
764 const SmTextNode *pTemp = static_cast<const SmTextNode *>(pNode);
765 std::unique_ptr<SvXMLElementExport> pMath;
767 if (pNode->GetType() == SmNodeType::Math || pNode->GetType() == SmNodeType::GlyphSpecial)
769 // Export SmNodeType::Math and SmNodeType::GlyphSpecial symbols as <mo> elements
770 pMath.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MO, true, false));
772 else if (pNode->GetType() == SmNodeType::Special)
774 bool bIsItalic = IsItalic(pNode->GetFont());
775 if (!bIsItalic)
776 AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
777 pMath.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false));
779 else
781 // Export SmNodeType::MathIdent and SmNodeType::Place symbols as <mi> elements:
782 // - These math symbols should not be drawn slanted. Hence we should
783 // attach a mathvariant="normal" attribute to single-char <mi> elements
784 // that are not mathematical alphanumeric symbol. For simplicity and to
785 // work around browser limitations, we always attach such an attribute.
786 // - The MathML specification suggests to use empty <mi> elements as
787 // placeholders but they won't be visible in most MathML rendering
788 // engines so let's use an empty square for SmNodeType::Place instead.
789 AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
790 pMath.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false));
792 sal_Unicode nArse[2];
793 nArse[0] = pTemp->GetText()[0];
794 sal_Unicode cTmp = ConvertMathToMathML( nArse[0] );
795 if (cTmp != 0)
796 nArse[0] = cTmp;
797 OSL_ENSURE(nArse[0] != 0xffff,"Non existent symbol");
798 nArse[1] = 0;
799 GetDocHandler()->characters(nArse);
802 void SmXMLExport::ExportText(const SmNode *pNode)
804 std::unique_ptr<SvXMLElementExport> pText;
805 const SmTextNode *pTemp = static_cast<const SmTextNode *>(pNode);
806 switch (pNode->GetToken().eType)
808 default:
809 case TIDENT:
811 //Note that we change the fontstyle to italic for strings that
812 //are italic and longer than a single character.
813 bool bIsItalic = IsItalic( pTemp->GetFont() );
814 if ((pTemp->GetText().getLength() > 1) && bIsItalic)
815 AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_ITALIC);
816 else if ((pTemp->GetText().getLength() == 1) && !bIsItalic)
817 AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
818 pText.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false));
819 break;
821 case TNUMBER:
822 pText.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MN, true, false));
823 break;
824 case TTEXT:
825 pText.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTEXT, true, false));
826 break;
828 GetDocHandler()->characters(pTemp->GetText());
831 void SmXMLExport::ExportBlank(const SmNode *pNode)
833 const SmBlankNode *pTemp = static_cast<const SmBlankNode *>(pNode);
834 //!! exports an <mspace> element. Note that for example "~_~" is allowed in
835 //!! Math (so it has no sense at all) but must not result in an empty
836 //!! <msub> tag in MathML !!
838 if (pTemp->GetBlankNum() != 0)
840 // Attach a width attribute. We choose the (somewhat arbitrary) values
841 // ".5em" for a small gap '`' and "2em" for a large gap '~'.
842 // (see SmBlankNode::IncreaseBy for how pTemp->mnNum is set).
843 OUStringBuffer sStrBuf;
844 ::sax::Converter::convertDouble(sStrBuf, pTemp->GetBlankNum() * .5);
845 sStrBuf.append("em");
846 AddAttribute(XML_NAMESPACE_MATH, XML_WIDTH, sStrBuf.getStr());
849 std::unique_ptr<SvXMLElementExport> pText(
850 new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSPACE,
851 true, false));
853 GetDocHandler()->characters( OUString() );
856 void SmXMLExport::ExportSubSupScript(const SmNode *pNode, int nLevel)
858 const SmNode *pSub = nullptr;
859 const SmNode *pSup = nullptr;
860 const SmNode *pCSub = nullptr;
861 const SmNode *pCSup = nullptr;
862 const SmNode *pLSub = nullptr;
863 const SmNode *pLSup = nullptr;
864 SvXMLElementExport *pThing2 = nullptr;
866 //if we have prescripts at all then we must use the tensor notation
868 //This is one of those excellent locations where scope is vital to
869 //arrange the construction and destruction of the element helper
870 //classes correctly
871 pLSub = pNode->GetSubNode(LSUB+1);
872 pLSup = pNode->GetSubNode(LSUP+1);
873 if (pLSub || pLSup)
875 SvXMLElementExport aMultiScripts(*this, XML_NAMESPACE_MATH,
876 XML_MMULTISCRIPTS, true, true);
879 if (nullptr != (pCSub = pNode->GetSubNode(CSUB+1))
880 && nullptr != (pCSup = pNode->GetSubNode(CSUP+1)))
882 pThing2 = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
883 XML_MUNDEROVER, true, true);
885 else if (nullptr != (pCSub = pNode->GetSubNode(CSUB+1)))
887 pThing2 = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
888 XML_MUNDER, true, true);
890 else if (nullptr != (pCSup = pNode->GetSubNode(CSUP+1)))
892 pThing2 = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
893 XML_MOVER, true, true);
896 ExportNodes(pNode->GetSubNode(0), nLevel+1); //Main Term
898 if (pCSub)
899 ExportNodes(pCSub, nLevel+1);
900 if (pCSup)
901 ExportNodes(pCSup, nLevel+1);
902 delete pThing2;
904 pSub = pNode->GetSubNode(RSUB+1);
905 pSup = pNode->GetSubNode(RSUP+1);
906 if (pSub || pSup)
908 if (pSub)
909 ExportNodes(pSub, nLevel+1);
910 else
912 SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE, true, true);
914 if (pSup)
915 ExportNodes(pSup, nLevel+1);
916 else
918 SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE, true, true);
922 //Separator element between suffix and prefix sub/sup pairs
924 SvXMLElementExport aPrescripts(*this, XML_NAMESPACE_MATH,
925 XML_MPRESCRIPTS, true, true);
928 if (pLSub)
929 ExportNodes(pLSub, nLevel+1);
930 else
932 SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE,
933 true, true);
936 if (pLSup)
937 ExportNodes(pLSup, nLevel+1);
938 else
940 SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE,
941 true, true);
945 else
947 SvXMLElementExport *pThing = nullptr;
948 if (nullptr != (pSub = pNode->GetSubNode(RSUB+1)) &&
949 nullptr != (pSup = pNode->GetSubNode(RSUP+1)))
951 pThing = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
952 XML_MSUBSUP, true, true);
954 else if (nullptr != (pSub = pNode->GetSubNode(RSUB+1)))
956 pThing = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSUB,
957 true, true);
959 else if (nullptr != (pSup = pNode->GetSubNode(RSUP+1)))
961 pThing = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSUP,
962 true, true);
965 if (nullptr != (pCSub = pNode->GetSubNode(CSUB+1))
966 && nullptr != (pCSup=pNode->GetSubNode(CSUP+1)))
968 pThing2 = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
969 XML_MUNDEROVER, true, true);
971 else if (nullptr != (pCSub = pNode->GetSubNode(CSUB+1)))
973 pThing2 = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
974 XML_MUNDER, true, true);
976 else if (nullptr != (pCSup = pNode->GetSubNode(CSUP+1)))
978 pThing2 = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
979 XML_MOVER, true, true);
981 ExportNodes(pNode->GetSubNode(0), nLevel+1); //Main Term
983 if (pCSub)
984 ExportNodes(pCSub, nLevel+1);
985 if (pCSup)
986 ExportNodes(pCSup, nLevel+1);
987 delete pThing2;
989 if (pSub)
990 ExportNodes(pSub, nLevel+1);
991 if (pSup)
992 ExportNodes(pSup, nLevel+1);
993 delete pThing;
997 void SmXMLExport::ExportBrace(const SmNode *pNode, int nLevel)
999 const SmNode *pTemp;
1000 const SmNode *pLeft=pNode->GetSubNode(0);
1001 const SmNode *pRight=pNode->GetSubNode(2);
1003 // This used to generate <mfenced> or <mrow>+<mo> elements according to
1004 // the stretchiness of fences. The MathML recommendation defines an
1005 // <mrow>+<mo> construction that is equivalent to the <mfenced> element:
1006 // http://www.w3.org/TR/MathML3/chapter3.html#presm.mfenced
1007 // To simplify our code and avoid issues with mfenced implementations in
1008 // MathML rendering engines, we now always generate <mrow>+<mo> elements.
1009 // See #fdo 66282.
1011 // <mrow>
1012 std::unique_ptr<SvXMLElementExport> pRow(
1013 new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MROW,
1014 true, true));
1016 // <mo fence="true"> opening-fence </mo>
1017 if (pLeft && (pLeft->GetToken().eType != TNONE))
1019 AddAttribute(XML_NAMESPACE_MATH, XML_FENCE, XML_TRUE);
1020 if (pNode->GetScaleMode() == SmScaleMode::Height)
1021 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
1022 else
1023 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
1024 ExportNodes(pLeft, nLevel+1);
1027 if (nullptr != (pTemp = pNode->GetSubNode(1)))
1029 // <mrow>
1030 SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW,
1031 true, true);
1032 ExportNodes(pTemp, nLevel+1);
1033 // </mrow>
1036 // <mo fence="true"> closing-fence </mo>
1037 if (pRight && (pRight->GetToken().eType != TNONE))
1039 AddAttribute(XML_NAMESPACE_MATH, XML_FENCE, XML_TRUE);
1040 if (pNode->GetScaleMode() == SmScaleMode::Height)
1041 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
1042 else
1043 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
1044 ExportNodes(pRight, nLevel+1);
1047 // </mrow>
1050 void SmXMLExport::ExportRoot(const SmNode *pNode, int nLevel)
1052 if (pNode->GetSubNode(0))
1054 SvXMLElementExport aRoot(*this, XML_NAMESPACE_MATH, XML_MROOT, true,
1055 true);
1056 ExportNodes(pNode->GetSubNode(2), nLevel+1);
1057 ExportNodes(pNode->GetSubNode(0), nLevel+1);
1059 else
1061 SvXMLElementExport aSqrt(*this, XML_NAMESPACE_MATH, XML_MSQRT, true,
1062 true);
1063 ExportNodes(pNode->GetSubNode(2), nLevel+1);
1067 void SmXMLExport::ExportOperator(const SmNode *pNode, int nLevel)
1069 /*we need to either use content or font and size attributes
1070 *here*/
1071 SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW,
1072 true, true);
1073 ExportNodes(pNode->GetSubNode(0), nLevel+1);
1074 ExportNodes(pNode->GetSubNode(1), nLevel+1);
1077 void SmXMLExport::ExportAttributes(const SmNode *pNode, int nLevel)
1079 std::unique_ptr<SvXMLElementExport> pElement;
1081 if (pNode->GetToken().eType == TUNDERLINE)
1083 AddAttribute(XML_NAMESPACE_MATH, XML_ACCENTUNDER,
1084 XML_TRUE);
1085 pElement.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MUNDER,
1086 true, true));
1088 else if (pNode->GetToken().eType == TOVERSTRIKE)
1090 // export as <menclose notation="horizontalstrike">
1091 AddAttribute(XML_NAMESPACE_MATH, XML_NOTATION, XML_HORIZONTALSTRIKE);
1092 pElement.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
1093 XML_MENCLOSE, true, true));
1095 else
1097 AddAttribute(XML_NAMESPACE_MATH, XML_ACCENT,
1098 XML_TRUE);
1099 pElement.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MOVER,
1100 true, true));
1103 ExportNodes(pNode->GetSubNode(1), nLevel+1);
1104 switch (pNode->GetToken().eType)
1106 case TOVERLINE:
1108 //proper entity support required
1109 SvXMLElementExport aMath(*this, XML_NAMESPACE_MATH, XML_MO,
1110 true, true);
1111 sal_Unicode const nArse[2] = {0xAF,0x00};
1112 GetDocHandler()->characters(nArse);
1114 break;
1115 case TUNDERLINE:
1117 //proper entity support required
1118 SvXMLElementExport aMath(*this, XML_NAMESPACE_MATH, XML_MO,
1119 true, true);
1120 sal_Unicode const nArse[2] = {0x0332,0x00};
1121 GetDocHandler()->characters(nArse);
1123 break;
1124 case TOVERSTRIKE:
1125 break;
1126 case TWIDETILDE:
1127 case TWIDEHAT:
1128 case TWIDEVEC:
1130 // make these wide accents stretchy
1131 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
1132 ExportNodes(pNode->GetSubNode(0), nLevel+1);
1134 break;
1135 default:
1136 ExportNodes(pNode->GetSubNode(0), nLevel+1);
1137 break;
1141 static bool lcl_HasEffectOnMathvariant( const SmTokenType eType )
1143 return eType == TBOLD || eType == TNBOLD ||
1144 eType == TITALIC || eType == TNITALIC ||
1145 eType == TSANS || eType == TSERIF || eType == TFIXED;
1148 void SmXMLExport::ExportFont(const SmNode *pNode, int nLevel)
1151 // gather the mathvariant attribute relevant data from all
1152 // successively following SmFontNodes...
1154 int nBold = -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true;
1155 int nItalic = -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true;
1156 int nSansSerifFixed = -1;
1157 SmTokenType eNodeType = TUNKNOWN;
1158 while (lcl_HasEffectOnMathvariant( (eNodeType = pNode->GetToken().eType) ))
1160 switch (eNodeType)
1162 case TBOLD : nBold = 1; break;
1163 case TNBOLD : nBold = 0; break;
1164 case TITALIC : nItalic = 1; break;
1165 case TNITALIC : nItalic = 0; break;
1166 case TSANS : nSansSerifFixed = 0; break;
1167 case TSERIF : nSansSerifFixed = 1; break;
1168 case TFIXED : nSansSerifFixed = 2; break;
1169 default:
1170 SAL_WARN("starmath", "unexpected case");
1172 // According to the parser every node that is to be evaluated here
1173 // has a single non-zero subnode at index 1!! Thus we only need to check
1174 // that single node for follow-up nodes that have an effect on the attribute.
1175 if (pNode->GetNumSubNodes() > 1 && pNode->GetSubNode(1) &&
1176 lcl_HasEffectOnMathvariant( pNode->GetSubNode(1)->GetToken().eType))
1178 pNode = pNode->GetSubNode(1);
1180 else
1181 break;
1184 switch (pNode->GetToken().eType)
1186 case TPHANTOM:
1187 // No attribute needed. An <mphantom> element will be used below.
1188 break;
1189 case TBLACK:
1190 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_BLACK);
1191 break;
1192 case TWHITE:
1193 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_WHITE);
1194 break;
1195 case TRED:
1196 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_RED);
1197 break;
1198 case TGREEN:
1199 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_GREEN);
1200 break;
1201 case TBLUE:
1202 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_BLUE);
1203 break;
1204 case TCYAN:
1205 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_AQUA);
1206 break;
1207 case TMAGENTA:
1208 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_FUCHSIA);
1209 break;
1210 case TYELLOW:
1211 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_YELLOW);
1212 break;
1213 case TSILVER:
1214 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_SILVER);
1215 break;
1216 case TGRAY:
1217 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_GRAY);
1218 break;
1219 case TMAROON:
1220 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_MAROON);
1221 break;
1222 case TOLIVE:
1223 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_OLIVE);
1224 break;
1225 case TLIME:
1226 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_LIME);
1227 break;
1228 case TAQUA:
1229 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_AQUA);
1230 break;
1231 case TTEAL:
1232 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_TEAL);
1233 break;
1234 case TNAVY:
1235 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_NAVY);
1236 break;
1237 case TFUCHSIA:
1238 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_FUCHSIA);
1239 break;
1240 case TPURPLE:
1241 AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_PURPLE);
1242 break;
1243 case TSIZE:
1245 const SmFontNode *pFontNode = static_cast<const SmFontNode *>(pNode);
1246 const Fraction &aFrac = pFontNode->GetSizeParameter();
1248 OUStringBuffer sStrBuf;
1249 switch(pFontNode->GetSizeType())
1251 case FontSizeType::MULTIPLY:
1252 ::sax::Converter::convertDouble(sStrBuf,
1253 static_cast<double>(aFrac*Fraction(100.00)));
1254 sStrBuf.append('%');
1255 break;
1256 case FontSizeType::DIVIDE:
1257 ::sax::Converter::convertDouble(sStrBuf,
1258 static_cast<double>(Fraction(100.00)/aFrac));
1259 sStrBuf.append('%');
1260 break;
1261 case FontSizeType::ABSOLUT:
1262 ::sax::Converter::convertDouble(sStrBuf,
1263 static_cast<double>(aFrac));
1264 sStrBuf.append(
1265 GetXMLToken(XML_UNIT_PT));
1266 break;
1267 default:
1269 //The problem here is that the wheels fall off because
1270 //font size is stored in 100th's of a mm not pts, and
1271 //rounding errors take their toll on the original
1272 //value specified in points.
1274 //Must fix StarMath to retain the original pt values
1275 Fraction aTemp = Sm100th_mmToPts(pFontNode->GetFont().GetFontSize().Height());
1277 if (pFontNode->GetSizeType() == FontSizeType::MINUS)
1278 aTemp-=aFrac;
1279 else
1280 aTemp+=aFrac;
1282 double mytest = static_cast<double>(aTemp);
1284 mytest = ::rtl::math::round(mytest,1);
1285 ::sax::Converter::convertDouble(sStrBuf,mytest);
1286 sStrBuf.append(GetXMLToken(XML_UNIT_PT));
1288 break;
1291 OUString sStr(sStrBuf.makeStringAndClear());
1292 AddAttribute(XML_NAMESPACE_MATH, XML_MATHSIZE, sStr);
1294 break;
1295 case TBOLD:
1296 case TITALIC:
1297 case TNBOLD:
1298 case TNITALIC:
1299 case TFIXED:
1300 case TSANS:
1301 case TSERIF:
1303 // nBold: -1 = yet undefined; 0 = false; 1 = true;
1304 // nItalic: -1 = yet undefined; 0 = false; 1 = true;
1305 // nSansSerifFixed: -1 = undefined; 0 = sans; 1 = serif; 2 = fixed;
1306 const sal_Char *pText = "normal";
1307 if (nSansSerifFixed == -1 || nSansSerifFixed == 1)
1309 pText = "normal";
1310 if (nBold == 1 && nItalic != 1)
1311 pText = "bold";
1312 else if (nBold != 1 && nItalic == 1)
1313 pText = "italic";
1314 else if (nBold == 1 && nItalic == 1)
1315 pText = "bold-italic";
1317 else if (nSansSerifFixed == 0)
1319 pText = "sans-serif";
1320 if (nBold == 1 && nItalic != 1)
1321 pText = "bold-sans-serif";
1322 else if (nBold != 1 && nItalic == 1)
1323 pText = "sans-serif-italic";
1324 else if (nBold == 1 && nItalic == 1)
1325 pText = "sans-serif-bold-italic";
1327 else if (nSansSerifFixed == 2)
1328 pText = "monospace"; // no modifiers allowed for monospace ...
1329 else
1331 SAL_WARN("starmath", "unexpected case");
1333 AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, OUString::createFromAscii( pText ));
1335 break;
1336 default:
1337 break;
1341 // Wrap everything in an <mphantom> or <mstyle> element. These elements
1342 // are mrow-like, so ExportExpression doesn't need to add an explicit
1343 // <mrow> element. See #fdo 66283.
1344 SvXMLElementExport aElement(*this, XML_NAMESPACE_MATH,
1345 pNode->GetToken().eType == TPHANTOM ? XML_MPHANTOM : XML_MSTYLE,
1346 true, true);
1347 ExportExpression(pNode, nLevel, true);
1352 void SmXMLExport::ExportVerticalBrace(const SmVerticalBraceNode *pNode, int nLevel)
1354 // "[body] overbrace [script]"
1356 // Position body, overbrace and script vertically. First place the overbrace
1357 // OVER the body and then the script OVER this expression.
1359 // [script]
1360 // --[overbrace]--
1361 // XXXXXX[body]XXXXXXX
1363 // Similarly for the underbrace construction.
1365 XMLTokenEnum which;
1367 switch (pNode->GetToken().eType)
1369 case TOVERBRACE:
1370 default:
1371 which = XML_MOVER;
1372 break;
1373 case TUNDERBRACE:
1374 which = XML_MUNDER;
1375 break;
1378 SvXMLElementExport aOver1(*this, XML_NAMESPACE_MATH,which, true, true);
1379 {//Scoping
1380 // using accents will draw the over-/underbraces too close to the base
1381 // see http://www.w3.org/TR/MathML2/chapter3.html#id.3.4.5.2
1382 // also XML_ACCENT is illegal with XML_MUNDER. Thus no XML_ACCENT attribute here!
1383 SvXMLElementExport aOver2(*this, XML_NAMESPACE_MATH,which, true, true);
1384 ExportNodes(pNode->Body(), nLevel);
1385 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
1386 ExportNodes(pNode->Brace(), nLevel);
1388 ExportNodes(pNode->Script(), nLevel);
1391 void SmXMLExport::ExportMatrix(const SmNode *pNode, int nLevel)
1393 SvXMLElementExport aTable(*this, XML_NAMESPACE_MATH, XML_MTABLE, true, true);
1394 const SmMatrixNode *pMatrix = static_cast<const SmMatrixNode *>(pNode);
1395 size_t i = 0;
1396 for (sal_uInt16 y = 0; y < pMatrix->GetNumRows(); y++)
1398 SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MTR, true, true);
1399 for (sal_uInt16 x = 0; x < pMatrix->GetNumCols(); x++)
1401 if (const SmNode *pTemp = pNode->GetSubNode(i++))
1403 if (pTemp->GetType() == SmNodeType::Align &&
1404 pTemp->GetToken().eType != TALIGNC)
1406 // A left or right alignment is specified on this cell,
1407 // attach the corresponding columnalign attribute.
1408 AddAttribute(XML_NAMESPACE_MATH, XML_COLUMNALIGN,
1409 pTemp->GetToken().eType == TALIGNL ?
1410 XML_LEFT : XML_RIGHT);
1412 SvXMLElementExport aCell(*this, XML_NAMESPACE_MATH, XML_MTD, true, true);
1413 ExportNodes(pTemp, nLevel+1);
1419 void SmXMLExport::ExportNodes(const SmNode *pNode, int nLevel)
1421 if (!pNode)
1422 return;
1423 switch(pNode->GetType())
1425 case SmNodeType::Table:
1426 ExportTable(pNode, nLevel);
1427 break;
1428 case SmNodeType::Align:
1429 case SmNodeType::Bracebody:
1430 case SmNodeType::Expression:
1431 ExportExpression(pNode, nLevel);
1432 break;
1433 case SmNodeType::Line:
1434 ExportLine(pNode, nLevel);
1435 break;
1436 case SmNodeType::Text:
1437 ExportText(pNode);
1438 break;
1439 case SmNodeType::GlyphSpecial:
1440 case SmNodeType::Math:
1442 sal_Unicode cTmp = 0;
1443 const SmTextNode *pTemp = static_cast< const SmTextNode * >(pNode);
1444 if (!pTemp->GetText().isEmpty())
1445 cTmp = ConvertMathToMathML( pTemp->GetText()[0] );
1446 if (cTmp == 0)
1448 // no conversion to MathML implemented -> export it as text
1449 // thus at least it will not vanish into nothing
1450 ExportText(pNode);
1452 else
1454 switch (pNode->GetToken().eType)
1456 case TINTD:
1457 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
1458 break;
1459 default:
1460 break;
1462 //To fully handle generic MathML we need to implement the full
1463 //operator dictionary, we will generate MathML with explicit
1464 //stretchiness for now.
1465 sal_Int16 nLength = GetAttrList().getLength();
1466 bool bAddStretch=true;
1467 for ( sal_Int16 i = 0; i < nLength; i++ )
1469 OUString sLocalName;
1470 sal_uInt16 nPrefix = GetNamespaceMap().GetKeyByAttrName(
1471 GetAttrList().getNameByIndex(i), &sLocalName );
1473 if ( ( XML_NAMESPACE_MATH == nPrefix ) &&
1474 IsXMLToken(sLocalName, XML_STRETCHY) )
1476 bAddStretch = false;
1477 break;
1480 if (bAddStretch)
1482 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
1484 ExportMath(pNode);
1487 break;
1488 case SmNodeType::Special: //SmNodeType::Special requires some sort of Entity preservation in the XML engine.
1489 case SmNodeType::MathIdent:
1490 case SmNodeType::Place:
1491 ExportMath(pNode);
1492 break;
1493 case SmNodeType::BinHor:
1494 ExportBinaryHorizontal(pNode, nLevel);
1495 break;
1496 case SmNodeType::UnHor:
1497 ExportUnaryHorizontal(pNode, nLevel);
1498 break;
1499 case SmNodeType::Brace:
1500 ExportBrace(pNode, nLevel);
1501 break;
1502 case SmNodeType::BinVer:
1503 ExportBinaryVertical(pNode, nLevel);
1504 break;
1505 case SmNodeType::BinDiagonal:
1506 ExportBinaryDiagonal(pNode, nLevel);
1507 break;
1508 case SmNodeType::SubSup:
1509 ExportSubSupScript(pNode, nLevel);
1510 break;
1511 case SmNodeType::Root:
1512 ExportRoot(pNode, nLevel);
1513 break;
1514 case SmNodeType::Oper:
1515 ExportOperator(pNode, nLevel);
1516 break;
1517 case SmNodeType::Attribut:
1518 ExportAttributes(pNode, nLevel);
1519 break;
1520 case SmNodeType::Font:
1521 ExportFont(pNode, nLevel);
1522 break;
1523 case SmNodeType::VerticalBrace:
1524 ExportVerticalBrace(static_cast<const SmVerticalBraceNode *>(pNode), nLevel);
1525 break;
1526 case SmNodeType::Matrix:
1527 ExportMatrix(pNode, nLevel);
1528 break;
1529 case SmNodeType::Blank:
1530 ExportBlank(pNode);
1531 break;
1532 default:
1533 SAL_WARN("starmath", "Warning: failed to export a node?");
1534 break;
1539 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */