Bump version to 21.06.18.1
[LibreOffice.git] / starmath / source / mathmlexport.cxx
blob0956269d1b40e2de5d1c75a5b61733b438fc55b2
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/task/XStatusIndicator.hpp>
32 #include <com/sun/star/uno/Any.h>
34 #include <rtl/math.hxx>
35 #include <sfx2/frame.hxx>
36 #include <sfx2/docfile.hxx>
37 #include <sfx2/sfxsids.hrc>
38 #include <osl/diagnose.h>
39 #include <unotools/saveopt.hxx>
40 #include <sot/storage.hxx>
41 #include <svl/itemset.hxx>
42 #include <svl/stritem.hxx>
43 #include <comphelper/fileformat.h>
44 #include <comphelper/processfactory.hxx>
45 #include <unotools/streamwrap.hxx>
46 #include <sax/tools/converter.hxx>
47 #include <xmloff/xmlnamespace.hxx>
48 #include <xmloff/xmltoken.hxx>
49 #include <xmloff/namespacemap.hxx>
50 #include <xmloff/attrlist.hxx>
51 #include <comphelper/genericpropertyset.hxx>
52 #include <comphelper/servicehelper.hxx>
53 #include <comphelper/propertysetinfo.hxx>
54 #include <tools/diagnose_ex.h>
55 #include <sal/log.hxx>
57 #include <memory>
58 #include <stack>
60 #include "mathmlexport.hxx"
61 #include <strings.hrc>
62 #include <smmod.hxx>
63 #include <unomodel.hxx>
64 #include <document.hxx>
65 #include <utility.hxx>
66 #include "cfgitem.hxx"
67 #include <starmathdatabase.hxx>
69 using namespace ::com::sun::star::beans;
70 using namespace ::com::sun::star::document;
71 using namespace ::com::sun::star::lang;
72 using namespace ::com::sun::star::uno;
73 using namespace ::com::sun::star;
74 using namespace ::xmloff::token;
76 namespace {
78 bool IsInPrivateUseArea( sal_Unicode cChar ) { return 0xE000 <= cChar && cChar <= 0xF8FF; }
80 sal_Unicode ConvertMathToMathML( sal_Unicode cChar )
82 sal_Unicode cRes = cChar;
83 if (IsInPrivateUseArea( cChar ))
85 SAL_WARN("starmath", "Error: private use area characters should no longer be in use!" );
86 cRes = u'@'; // just some character that should easily be notice as odd in the context
88 return cRes;
93 bool SmXMLExportWrapper::Export(SfxMedium &rMedium)
95 bool bRet=true;
96 uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
98 //Get model
99 uno::Reference< lang::XComponent > xModelComp = xModel;
101 bool bEmbedded = false;
102 SmModel *pModel = comphelper::getUnoTunnelImplementation<SmModel>(xModel);
104 SmDocShell *pDocShell = pModel ?
105 static_cast<SmDocShell*>(pModel->GetObjectShell()) : nullptr;
106 if ( pDocShell &&
107 SfxObjectCreateMode::EMBEDDED == pDocShell->GetCreateMode() )
108 bEmbedded = true;
110 uno::Reference<task::XStatusIndicator> xStatusIndicator;
111 if (!bEmbedded)
113 if (pDocShell /*&& pDocShell->GetMedium()*/)
115 OSL_ENSURE( pDocShell->GetMedium() == &rMedium,
116 "different SfxMedium found" );
118 SfxItemSet* pSet = rMedium.GetItemSet();
119 if (pSet)
121 const SfxUnoAnyItem* pItem = static_cast<const SfxUnoAnyItem*>(
122 pSet->GetItem(SID_PROGRESS_STATUSBAR_CONTROL) );
123 if (pItem)
124 pItem->GetValue() >>= xStatusIndicator;
128 // set progress range and start status indicator
129 if (xStatusIndicator.is())
131 sal_Int32 nProgressRange = bFlat ? 1 : 3;
132 xStatusIndicator->start(SmResId(STR_STATSTR_WRITING),
133 nProgressRange);
138 // create XPropertySet with three properties for status indicator
139 comphelper::PropertyMapEntry aInfoMap[] =
141 { OUString("UsePrettyPrinting"), 0,
142 cppu::UnoType<bool>::get(),
143 beans::PropertyAttribute::MAYBEVOID, 0},
144 { OUString("BaseURI"), 0,
145 ::cppu::UnoType<OUString>::get(),
146 beans::PropertyAttribute::MAYBEVOID, 0 },
147 { OUString("StreamRelPath"), 0,
148 ::cppu::UnoType<OUString>::get(),
149 beans::PropertyAttribute::MAYBEVOID, 0 },
150 { OUString("StreamName"), 0,
151 ::cppu::UnoType<OUString>::get(),
152 beans::PropertyAttribute::MAYBEVOID, 0 },
153 { OUString(), 0, css::uno::Type(), 0, 0 }
155 uno::Reference< beans::XPropertySet > xInfoSet(
156 comphelper::GenericPropertySet_CreateInstance(
157 new comphelper::PropertySetInfo( aInfoMap ) ) );
159 SvtSaveOptions aSaveOpt;
160 bool bUsePrettyPrinting( bFlat || aSaveOpt.IsPrettyPrinting() );
161 xInfoSet->setPropertyValue( "UsePrettyPrinting", Any(bUsePrettyPrinting) );
163 // Set base URI
164 OUString sPropName( "BaseURI" );
165 xInfoSet->setPropertyValue( sPropName, makeAny( rMedium.GetBaseURL( true ) ) );
167 sal_Int32 nSteps=0;
168 if (xStatusIndicator.is())
169 xStatusIndicator->setValue(nSteps++);
170 if (!bFlat) //Storage (Package) of Stream
172 uno::Reference < embed::XStorage > xStg = rMedium.GetOutputStorage();
173 bool bOASIS = ( SotStorage::GetVersion( xStg ) > SOFFICE_FILEFORMAT_60 );
175 // TODO/LATER: handle the case of embedded links gracefully
176 if ( bEmbedded ) //&& !pStg->IsRoot() )
178 OUString aName;
179 if ( rMedium.GetItemSet() )
181 const SfxStringItem* pDocHierarchItem = static_cast<const SfxStringItem*>(
182 rMedium.GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME) );
183 if ( pDocHierarchItem )
184 aName = pDocHierarchItem->GetValue();
187 if ( !aName.isEmpty() )
189 sPropName = "StreamRelPath";
190 xInfoSet->setPropertyValue( sPropName, makeAny( aName ) );
194 if ( !bEmbedded )
196 if (xStatusIndicator.is())
197 xStatusIndicator->setValue(nSteps++);
199 bRet = WriteThroughComponent(
200 xStg, xModelComp, "meta.xml", xContext, xInfoSet,
201 (bOASIS ? "com.sun.star.comp.Math.XMLOasisMetaExporter"
202 : "com.sun.star.comp.Math.XMLMetaExporter"));
204 if ( bRet )
206 if (xStatusIndicator.is())
207 xStatusIndicator->setValue(nSteps++);
209 bRet = WriteThroughComponent(
210 xStg, xModelComp, "content.xml", xContext, xInfoSet,
211 "com.sun.star.comp.Math.XMLContentExporter");
214 if ( bRet )
216 if (xStatusIndicator.is())
217 xStatusIndicator->setValue(nSteps++);
219 bRet = WriteThroughComponent(
220 xStg, xModelComp, "settings.xml", xContext, xInfoSet,
221 (bOASIS ? "com.sun.star.comp.Math.XMLOasisSettingsExporter"
222 : "com.sun.star.comp.Math.XMLSettingsExporter") );
225 else
227 SvStream *pStream = rMedium.GetOutStream();
228 uno::Reference<io::XOutputStream> xOut(
229 new utl::OOutputStreamWrapper(*pStream) );
231 if (xStatusIndicator.is())
232 xStatusIndicator->setValue(nSteps++);
234 bRet = WriteThroughComponent(
235 xOut, xModelComp, xContext, xInfoSet,
236 "com.sun.star.comp.Math.XMLContentExporter");
239 if (xStatusIndicator.is())
240 xStatusIndicator->end();
242 return bRet;
246 /// export through an XML exporter component (output stream version)
247 bool SmXMLExportWrapper::WriteThroughComponent(
248 const Reference<io::XOutputStream>& xOutputStream,
249 const Reference<XComponent>& xComponent,
250 Reference<uno::XComponentContext> const & rxContext,
251 Reference<beans::XPropertySet> const & rPropSet,
252 const char* pComponentName )
254 OSL_ENSURE(xOutputStream.is(), "I really need an output stream!");
255 OSL_ENSURE(xComponent.is(), "Need component!");
256 OSL_ENSURE(nullptr != pComponentName, "Need component name!");
258 // get component
259 Reference< xml::sax::XWriter > xSaxWriter = xml::sax::Writer::create(rxContext );
261 // connect XML writer to output stream
262 xSaxWriter->setOutputStream( xOutputStream );
264 // prepare arguments (prepend doc handler to given arguments)
265 Sequence<Any> aArgs( 2 );
266 aArgs[0] <<= xSaxWriter;
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 auto pFilter = comphelper::getUnoTunnelImplementation<SmXMLExport>(xFilter);
288 return pFilter == nullptr || pFilter->GetSuccess();
292 /// export through an XML exporter component (storage version)
293 bool SmXMLExportWrapper::WriteThroughComponent(
294 const Reference < embed::XStorage >& xStorage,
295 const Reference<XComponent>& xComponent,
296 const char* pStreamName,
297 Reference<uno::XComponentContext> const & rxContext,
298 Reference<beans::XPropertySet> const & rPropSet,
299 const char* pComponentName
302 OSL_ENSURE(xStorage.is(), "Need storage!");
303 OSL_ENSURE(nullptr != pStreamName, "Need stream name!");
305 // open stream
306 Reference < io::XStream > xStream;
307 OUString sStreamName = OUString::createFromAscii(pStreamName);
310 xStream = xStorage->openStreamElement( sStreamName,
311 embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
313 catch ( const uno::Exception& )
315 DBG_UNHANDLED_EXCEPTION("starmath", "Can't create output stream in package" );
316 return false;
319 uno::Reference < beans::XPropertySet > xSet( xStream, uno::UNO_QUERY );
320 xSet->setPropertyValue( "MediaType", Any(OUString( "text/xml" )) );
322 // all streams must be encrypted in encrypted document
323 xSet->setPropertyValue( "UseCommonStoragePasswordEncryption", Any(true) );
325 // set Base URL
326 if ( rPropSet.is() )
328 rPropSet->setPropertyValue( "StreamName", makeAny( sStreamName ) );
331 // write the stuff
332 bool bRet = WriteThroughComponent( xStream->getOutputStream(), xComponent, rxContext,
333 rPropSet, pComponentName );
335 return bRet;
338 SmXMLExport::SmXMLExport(
339 const css::uno::Reference< css::uno::XComponentContext >& rContext,
340 OUString const & implementationName, SvXMLExportFlags nExportFlags)
341 : SvXMLExport(rContext, implementationName, util::MeasureUnit::INCH, XML_MATH,
342 nExportFlags)
343 , pTree(nullptr)
344 , bSuccess(false)
348 sal_Int64 SAL_CALL SmXMLExport::getSomething(
349 const uno::Sequence< sal_Int8 >& rId )
351 if ( isUnoTunnelId<SmXMLExport>(rId) )
352 return sal::static_int_cast< sal_Int64 >(reinterpret_cast< sal_uIntPtr >(this));
354 return SvXMLExport::getSomething( rId );
357 namespace
359 class theSmXMLExportUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSmXMLExportUnoTunnelId> {};
362 const uno::Sequence< sal_Int8 > & SmXMLExport::getUnoTunnelId() throw()
364 return theSmXMLExportUnoTunnelId::get().getSeq();
367 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
368 Math_XMLExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
370 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::ALL));
373 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
374 Math_XMLMetaExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
376 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLMetaExporter", SvXMLExportFlags::META));
379 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
380 Math_XMLOasisMetaExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
382 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLOasisMetaExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::META));
385 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
386 Math_XMLSettingsExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
388 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLSettingsExporter", SvXMLExportFlags::SETTINGS));
391 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
392 Math_XMLOasisSettingsExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
394 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLOasisSettingsExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::SETTINGS));
397 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
398 Math_XMLContentExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
400 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLContentExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::CONTENT));
403 ErrCode SmXMLExport::exportDoc(enum XMLTokenEnum eClass)
405 if ( !(getExportFlags() & SvXMLExportFlags::CONTENT) )
407 SvXMLExport::exportDoc( eClass );
409 else
411 uno::Reference <frame::XModel> xModel = GetModel();
412 SmModel *pModel = comphelper::getUnoTunnelImplementation<SmModel>(xModel);
414 if (pModel)
416 SmDocShell *pDocShell =
417 static_cast<SmDocShell*>(pModel->GetObjectShell());
418 pTree = pDocShell->GetFormulaTree();
419 aText = pDocShell->GetText();
422 GetDocHandler()->startDocument();
424 addChaffWhenEncryptedStorage();
426 /*Add xmlns line*/
427 SvXMLAttributeList &rList = GetAttrList();
429 // make use of a default namespace
430 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)
431 GetNamespaceMap_().Add( OUString(), GetXMLToken(XML_N_MATH), XML_NAMESPACE_MATH );
433 rList.AddAttribute(GetNamespaceMap().GetAttrNameByKey(XML_NAMESPACE_MATH),
434 GetNamespaceMap().GetNameByKey( XML_NAMESPACE_MATH));
436 //I think we need something like ImplExportEntities();
437 ExportContent_();
438 GetDocHandler()->endDocument();
441 bSuccess=true;
442 return ERRCODE_NONE;
445 void SmXMLExport::ExportContent_()
447 uno::Reference <frame::XModel> xModel = GetModel();
448 SmModel *pModel = comphelper::getUnoTunnelImplementation<SmModel>(xModel);
449 SmDocShell *pDocShell = pModel ?
450 static_cast<SmDocShell*>(pModel->GetObjectShell()) : nullptr;
451 OSL_ENSURE( pDocShell, "doc shell missing" );
453 if (pDocShell && !pDocShell->GetFormat().IsTextmode())
455 // If the Math equation is not in text mode, we attach a display="block"
456 // attribute on the <math> root. We don't do anything if it is in
457 // text mode, the default display="inline" value will be used.
458 AddAttribute(XML_NAMESPACE_MATH, XML_DISPLAY, XML_BLOCK);
460 SvXMLElementExport aEquation(*this, XML_NAMESPACE_MATH, XML_MATH, true, true);
461 std::unique_ptr<SvXMLElementExport> pSemantics;
463 if (!aText.isEmpty())
465 pSemantics.reset( new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
466 XML_SEMANTICS, true, true) );
469 ExportNodes(pTree, 0);
471 if (aText.isEmpty())
472 return;
474 // Convert symbol names
475 if (pDocShell)
477 SmParser &rParser = pDocShell->GetParser();
478 bool bVal = rParser.IsExportSymbolNames();
479 rParser.SetExportSymbolNames( true );
480 auto pTmpTree = rParser.Parse( aText );
481 aText = rParser.GetText();
482 pTmpTree.reset();
483 rParser.SetExportSymbolNames( bVal );
486 AddAttribute(XML_NAMESPACE_MATH, XML_ENCODING,
487 OUString("StarMath 5.0"));
488 SvXMLElementExport aAnnotation(*this, XML_NAMESPACE_MATH,
489 XML_ANNOTATION, true, false);
490 GetDocHandler()->characters( aText );
493 void SmXMLExport::GetViewSettings( Sequence < PropertyValue >& aProps)
495 uno::Reference <frame::XModel> xModel = GetModel();
496 if ( !xModel.is() )
497 return;
499 SmModel *pModel = comphelper::getUnoTunnelImplementation<SmModel>(xModel);
501 if ( !pModel )
502 return;
504 SmDocShell *pDocShell =
505 static_cast<SmDocShell*>(pModel->GetObjectShell());
506 if ( !pDocShell )
507 return;
509 aProps.realloc( 4 );
510 PropertyValue *pValue = aProps.getArray();
511 sal_Int32 nIndex = 0;
513 tools::Rectangle aRect( pDocShell->GetVisArea() );
515 pValue[nIndex].Name = "ViewAreaTop";
516 pValue[nIndex++].Value <<= aRect.Top();
518 pValue[nIndex].Name = "ViewAreaLeft";
519 pValue[nIndex++].Value <<= aRect.Left();
521 pValue[nIndex].Name = "ViewAreaWidth";
522 pValue[nIndex++].Value <<= aRect.GetWidth();
524 pValue[nIndex].Name = "ViewAreaHeight";
525 pValue[nIndex++].Value <<= aRect.GetHeight();
528 void SmXMLExport::GetConfigurationSettings( Sequence < PropertyValue > & rProps)
530 Reference < XPropertySet > xProps ( GetModel(), UNO_QUERY );
531 if ( !xProps.is() )
532 return;
534 Reference< XPropertySetInfo > xPropertySetInfo = xProps->getPropertySetInfo();
535 if (!xPropertySetInfo.is())
536 return;
538 Sequence< Property > aProps = xPropertySetInfo->getProperties();
539 const sal_Int32 nCount = aProps.getLength();
540 if (!nCount)
541 return;
543 rProps.realloc(nCount);
544 SmMathConfig* pConfig = SM_MOD()->GetConfig();
545 const bool bUsedSymbolsOnly = pConfig && pConfig->IsSaveOnlyUsedSymbols();
547 std::transform(aProps.begin(), aProps.end(), rProps.begin(),
548 [bUsedSymbolsOnly, &xProps](Property& prop) {
549 PropertyValue aRet;
550 if (prop.Name != "Formula" && prop.Name != "BasicLibraries"
551 && prop.Name != "DialogLibraries"
552 && prop.Name != "RuntimeUID")
554 aRet.Name = prop.Name;
555 OUString aActualName(prop.Name);
556 // handle 'save used symbols only'
557 if (bUsedSymbolsOnly && prop.Name == "Symbols")
558 aActualName = "UserDefinedSymbolsInUse";
559 aRet.Value = xProps->getPropertyValue(aActualName);
561 return aRet;
565 void SmXMLExport::ExportLine(const SmNode *pNode, int nLevel)
567 ExportExpression(pNode, nLevel);
570 void SmXMLExport::ExportBinaryHorizontal(const SmNode *pNode, int nLevel)
572 TG nGroup = pNode->GetToken().nGroup;
574 SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW, true, true);
576 // Unfold the binary tree structure as long as the nodes are SmBinHorNode
577 // with the same nGroup. This will reduce the number of nested <mrow>
578 // elements e.g. we only need three <mrow> levels to export
580 // "a*b*c*d+e*f*g*h+i*j*k*l = a*b*c*d+e*f*g*h+i*j*k*l =
581 // a*b*c*d+e*f*g*h+i*j*k*l = a*b*c*d+e*f*g*h+i*j*k*l"
583 // See https://www.libreoffice.org/bugzilla/show_bug.cgi?id=66081
584 ::std::stack< const SmNode* > s;
585 s.push(pNode);
586 while (!s.empty())
588 const SmNode *node = s.top();
589 s.pop();
590 if (node->GetType() != SmNodeType::BinHor || node->GetToken().nGroup != nGroup)
592 ExportNodes(node, nLevel+1);
593 continue;
595 const SmBinHorNode* binNode = static_cast<const SmBinHorNode*>(node);
596 s.push(binNode->RightOperand());
597 s.push(binNode->Symbol());
598 s.push(binNode->LeftOperand());
602 void SmXMLExport::ExportUnaryHorizontal(const SmNode *pNode, int nLevel)
604 ExportExpression(pNode, nLevel);
607 void SmXMLExport::ExportExpression(const SmNode *pNode, int nLevel,
608 bool bNoMrowContainer /*=false*/)
610 std::unique_ptr<SvXMLElementExport> pRow;
611 size_t nSize = pNode->GetNumSubNodes();
613 // #i115443: nodes of type expression always need to be grouped with mrow statement
614 if (!bNoMrowContainer &&
615 (nSize > 1 || pNode->GetType() == SmNodeType::Expression))
616 pRow.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MROW, true, true));
618 for (size_t i = 0; i < nSize; ++i)
620 if (const SmNode *pTemp = pNode->GetSubNode(i))
621 ExportNodes(pTemp, nLevel+1);
625 void SmXMLExport::ExportBinaryVertical(const SmNode *pNode, int nLevel)
627 assert(pNode->GetNumSubNodes() == 3);
628 const SmNode *pNum = pNode->GetSubNode(0);
629 const SmNode *pDenom = pNode->GetSubNode(2);
630 if (pNum->GetType() == SmNodeType::Align && pNum->GetToken().eType != TALIGNC)
632 // A left or right alignment is specified on the numerator:
633 // attach the corresponding numalign attribute.
634 AddAttribute(XML_NAMESPACE_MATH, XML_NUMALIGN,
635 pNum->GetToken().eType == TALIGNL ? XML_LEFT : XML_RIGHT);
637 if (pDenom->GetType() == SmNodeType::Align && pDenom->GetToken().eType != TALIGNC)
639 // A left or right alignment is specified on the denominator:
640 // attach the corresponding denomalign attribute.
641 AddAttribute(XML_NAMESPACE_MATH, XML_DENOMALIGN,
642 pDenom->GetToken().eType == TALIGNL ? XML_LEFT : XML_RIGHT);
644 SvXMLElementExport aFraction(*this, XML_NAMESPACE_MATH, XML_MFRAC, true, true);
645 ExportNodes(pNum, nLevel);
646 ExportNodes(pDenom, nLevel);
649 void SmXMLExport::ExportBinaryDiagonal(const SmNode *pNode, int nLevel)
651 assert(pNode->GetNumSubNodes() == 3);
653 if (pNode->GetToken().eType == TWIDESLASH)
655 // wideslash
656 // export the node as <mfrac bevelled="true">
657 AddAttribute(XML_NAMESPACE_MATH, XML_BEVELLED, XML_TRUE);
658 SvXMLElementExport aFraction(*this, XML_NAMESPACE_MATH, XML_MFRAC,
659 true, true);
660 ExportNodes(pNode->GetSubNode(0), nLevel);
661 ExportNodes(pNode->GetSubNode(1), nLevel);
663 else
665 // widebslash
666 // We can not use <mfrac> to a backslash, so just use <mo>\</mo>
667 SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW, true, true);
669 ExportNodes(pNode->GetSubNode(0), nLevel);
671 { // Scoping for <mo> creation
672 SvXMLElementExport aMo(*this, XML_NAMESPACE_MATH, XML_MO,
673 true, true);
674 sal_Unicode const nArse[2] = {MS_BACKSLASH,0x00};
675 GetDocHandler()->characters(nArse);
678 ExportNodes(pNode->GetSubNode(1), nLevel);
682 void SmXMLExport::ExportTable(const SmNode *pNode, int nLevel)
684 std::unique_ptr<SvXMLElementExport> pTable;
686 size_t nSize = pNode->GetNumSubNodes();
688 //If the list ends in newline then the last entry has
689 //no subnodes, the newline is superfluous so we just drop
690 //the last node, inclusion would create a bad MathML
691 //table
692 if (nSize >= 1)
694 const SmNode *pLine = pNode->GetSubNode(nSize-1);
695 if (pLine->GetType() == SmNodeType::Line && pLine->GetNumSubNodes() == 1 &&
696 pLine->GetSubNode(0) != nullptr &&
697 pLine->GetSubNode(0)->GetToken().eType == TNEWLINE)
698 --nSize;
701 // try to avoid creating a mtable element when the formula consists only
702 // of a single output line
703 if (nLevel || (nSize >1))
704 pTable.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTABLE, true, true));
706 for (size_t i = 0; i < nSize; ++i)
708 if (const SmNode *pTemp = pNode->GetSubNode(i))
710 std::unique_ptr<SvXMLElementExport> pRow;
711 std::unique_ptr<SvXMLElementExport> pCell;
712 if (pTable)
714 pRow.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTR, true, true));
715 SmTokenType eAlign = TALIGNC;
716 if (pTemp->GetType() == SmNodeType::Align)
718 // For Binom() and Stack() constructions, the SmNodeType::Align nodes
719 // are direct children.
720 // binom{alignl ...}{alignr ...} and
721 // stack{alignl ... ## alignr ... ## ...}
722 eAlign = pTemp->GetToken().eType;
724 else if (pTemp->GetType() == SmNodeType::Line &&
725 pTemp->GetNumSubNodes() == 1 &&
726 pTemp->GetSubNode(0) &&
727 pTemp->GetSubNode(0)->GetType() == SmNodeType::Align)
729 // For the Table() construction, the SmNodeType::Align node is a child
730 // of an SmNodeType::Line node.
731 // alignl ... newline alignr ... newline ...
732 eAlign = pTemp->GetSubNode(0)->GetToken().eType;
734 if (eAlign != TALIGNC)
736 // If a left or right alignment is specified on this line,
737 // attach the corresponding columnalign attribute.
738 AddAttribute(XML_NAMESPACE_MATH, XML_COLUMNALIGN,
739 eAlign == TALIGNL ? XML_LEFT : XML_RIGHT);
741 pCell.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTD, true, true));
743 ExportNodes(pTemp, nLevel+1);
748 void SmXMLExport::ExportMath(const SmNode *pNode)
750 const SmTextNode *pTemp = static_cast<const SmTextNode *>(pNode);
751 std::unique_ptr<SvXMLElementExport> pMath;
753 if (pNode->GetType() == SmNodeType::Math || pNode->GetType() == SmNodeType::GlyphSpecial)
755 // Export SmNodeType::Math and SmNodeType::GlyphSpecial symbols as <mo> elements
756 pMath.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MO, true, false));
758 else if (pNode->GetType() == SmNodeType::Special)
760 bool bIsItalic = IsItalic(pNode->GetFont());
761 if (!bIsItalic)
762 AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
763 pMath.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false));
765 else
767 // Export SmNodeType::MathIdent and SmNodeType::Place symbols as <mi> elements:
768 // - These math symbols should not be drawn slanted. Hence we should
769 // attach a mathvariant="normal" attribute to single-char <mi> elements
770 // that are not mathematical alphanumeric symbol. For simplicity and to
771 // work around browser limitations, we always attach such an attribute.
772 // - The MathML specification suggests to use empty <mi> elements as
773 // placeholders but they won't be visible in most MathML rendering
774 // engines so let's use an empty square for SmNodeType::Place instead.
775 AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
776 pMath.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false));
778 sal_Unicode nArse = pTemp->GetText()[0];
779 sal_Unicode cTmp = ConvertMathToMathML( nArse );
780 if (cTmp != 0)
781 nArse = cTmp;
782 OSL_ENSURE(nArse != 0xffff,"Non existent symbol");
783 GetDocHandler()->characters(OUString(nArse));
786 void SmXMLExport::ExportText(const SmNode *pNode)
788 std::unique_ptr<SvXMLElementExport> pText;
789 const SmTextNode *pTemp = static_cast<const SmTextNode *>(pNode);
790 switch (pNode->GetToken().eType)
792 default:
793 case TIDENT:
795 //Note that we change the fontstyle to italic for strings that
796 //are italic and longer than a single character.
797 bool bIsItalic = IsItalic( pTemp->GetFont() );
798 if ((pTemp->GetText().getLength() > 1) && bIsItalic)
799 AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_ITALIC);
800 else if ((pTemp->GetText().getLength() == 1) && !bIsItalic)
801 AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
802 pText.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false));
803 break;
805 case TNUMBER:
806 pText.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MN, true, false));
807 break;
808 case TTEXT:
809 pText.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTEXT, true, false));
810 break;
812 GetDocHandler()->characters(pTemp->GetText());
815 void SmXMLExport::ExportBlank(const SmNode *pNode)
817 const SmBlankNode *pTemp = static_cast<const SmBlankNode *>(pNode);
818 //!! exports an <mspace> element. Note that for example "~_~" is allowed in
819 //!! Math (so it has no sense at all) but must not result in an empty
820 //!! <msub> tag in MathML !!
822 if (pTemp->GetBlankNum() != 0)
824 // Attach a width attribute. We choose the (somewhat arbitrary) values
825 // ".5em" for a small gap '`' and "2em" for a large gap '~'.
826 // (see SmBlankNode::IncreaseBy for how pTemp->mnNum is set).
827 OUStringBuffer sStrBuf;
828 ::sax::Converter::convertDouble(sStrBuf, pTemp->GetBlankNum() * .5);
829 sStrBuf.append("em");
830 AddAttribute(XML_NAMESPACE_MATH, XML_WIDTH, sStrBuf.makeStringAndClear());
833 SvXMLElementExport aTextExport(*this, XML_NAMESPACE_MATH, XML_MSPACE,
834 true, false);
836 GetDocHandler()->characters( OUString() );
839 void SmXMLExport::ExportSubSupScript(const SmNode *pNode, int nLevel)
841 const SmNode *pSub = nullptr;
842 const SmNode *pSup = nullptr;
843 const SmNode *pCSub = nullptr;
844 const SmNode *pCSup = nullptr;
845 const SmNode *pLSub = nullptr;
846 const SmNode *pLSup = nullptr;
847 std::unique_ptr<SvXMLElementExport> pThing2;
849 //if we have prescripts at all then we must use the tensor notation
851 //This is one of those excellent locations where scope is vital to
852 //arrange the construction and destruction of the element helper
853 //classes correctly
854 pLSub = pNode->GetSubNode(LSUB+1);
855 pLSup = pNode->GetSubNode(LSUP+1);
856 if (pLSub || pLSup)
858 SvXMLElementExport aMultiScripts(*this, XML_NAMESPACE_MATH,
859 XML_MMULTISCRIPTS, true, true);
862 if (nullptr != (pCSub = pNode->GetSubNode(CSUB+1))
863 && nullptr != (pCSup = pNode->GetSubNode(CSUP+1)))
865 pThing2.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
866 XML_MUNDEROVER, true, true));
868 else if (nullptr != (pCSub = pNode->GetSubNode(CSUB+1)))
870 pThing2.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
871 XML_MUNDER, true, true));
873 else if (nullptr != (pCSup = pNode->GetSubNode(CSUP+1)))
875 pThing2.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
876 XML_MOVER, true, true));
879 ExportNodes(pNode->GetSubNode(0), nLevel+1); //Main Term
881 if (pCSub)
882 ExportNodes(pCSub, nLevel+1);
883 if (pCSup)
884 ExportNodes(pCSup, nLevel+1);
885 pThing2.reset();
887 pSub = pNode->GetSubNode(RSUB+1);
888 pSup = pNode->GetSubNode(RSUP+1);
889 if (pSub || pSup)
891 if (pSub)
892 ExportNodes(pSub, nLevel+1);
893 else
895 SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE, true, true);
897 if (pSup)
898 ExportNodes(pSup, nLevel+1);
899 else
901 SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE, true, true);
905 //Separator element between suffix and prefix sub/sup pairs
907 SvXMLElementExport aPrescripts(*this, XML_NAMESPACE_MATH,
908 XML_MPRESCRIPTS, true, true);
911 if (pLSub)
912 ExportNodes(pLSub, nLevel+1);
913 else
915 SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE,
916 true, true);
919 if (pLSup)
920 ExportNodes(pLSup, nLevel+1);
921 else
923 SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE,
924 true, true);
928 else
930 std::unique_ptr<SvXMLElementExport> pThing;
931 if (nullptr != (pSub = pNode->GetSubNode(RSUB+1)) &&
932 nullptr != (pSup = pNode->GetSubNode(RSUP+1)))
934 pThing.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
935 XML_MSUBSUP, true, true));
937 else if (nullptr != (pSub = pNode->GetSubNode(RSUB+1)))
939 pThing.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSUB,
940 true, true));
942 else if (nullptr != (pSup = pNode->GetSubNode(RSUP+1)))
944 pThing.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSUP,
945 true, true));
948 if (nullptr != (pCSub = pNode->GetSubNode(CSUB+1))
949 && nullptr != (pCSup=pNode->GetSubNode(CSUP+1)))
951 pThing2.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
952 XML_MUNDEROVER, true, true));
954 else if (nullptr != (pCSub = pNode->GetSubNode(CSUB+1)))
956 pThing2.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
957 XML_MUNDER, true, true));
959 else if (nullptr != (pCSup = pNode->GetSubNode(CSUP+1)))
961 pThing2.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
962 XML_MOVER, true, true));
964 ExportNodes(pNode->GetSubNode(0), nLevel+1); //Main Term
966 if (pCSub)
967 ExportNodes(pCSub, nLevel+1);
968 if (pCSup)
969 ExportNodes(pCSup, nLevel+1);
970 pThing2.reset();
972 if (pSub)
973 ExportNodes(pSub, nLevel+1);
974 if (pSup)
975 ExportNodes(pSup, nLevel+1);
976 pThing.reset();
980 void SmXMLExport::ExportBrace(const SmNode *pNode, int nLevel)
982 const SmNode *pTemp;
983 const SmNode *pLeft=pNode->GetSubNode(0);
984 const SmNode *pRight=pNode->GetSubNode(2);
986 // This used to generate <mfenced> or <mrow>+<mo> elements according to
987 // the stretchiness of fences. The MathML recommendation defines an
988 // <mrow>+<mo> construction that is equivalent to the <mfenced> element:
989 // http://www.w3.org/TR/MathML3/chapter3.html#presm.mfenced
990 // To simplify our code and avoid issues with mfenced implementations in
991 // MathML rendering engines, we now always generate <mrow>+<mo> elements.
992 // See #fdo 66282.
994 // <mrow>
995 SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW,
996 true, true);
998 // <mo fence="true"> opening-fence </mo>
999 if (pLeft && (pLeft->GetToken().eType != TNONE))
1001 AddAttribute(XML_NAMESPACE_MATH, XML_FENCE, XML_TRUE);
1002 AddAttribute(XML_NAMESPACE_MATH, XML_FORM, XML_PREFIX);
1003 if (pNode->GetScaleMode() == SmScaleMode::Height)
1004 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
1005 else
1006 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
1007 ExportNodes(pLeft, nLevel+1);
1010 if (nullptr != (pTemp = pNode->GetSubNode(1)))
1012 // <mrow>
1013 SvXMLElementExport aRowExport(*this, XML_NAMESPACE_MATH, XML_MROW,
1014 true, true);
1015 ExportNodes(pTemp, nLevel+1);
1016 // </mrow>
1019 // <mo fence="true"> closing-fence </mo>
1020 if (pRight && (pRight->GetToken().eType != TNONE))
1022 AddAttribute(XML_NAMESPACE_MATH, XML_FENCE, XML_TRUE);
1023 AddAttribute(XML_NAMESPACE_MATH, XML_FORM, XML_POSTFIX);
1024 if (pNode->GetScaleMode() == SmScaleMode::Height)
1025 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
1026 else
1027 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
1028 ExportNodes(pRight, nLevel+1);
1031 // </mrow>
1034 void SmXMLExport::ExportRoot(const SmNode *pNode, int nLevel)
1036 if (pNode->GetSubNode(0))
1038 SvXMLElementExport aRoot(*this, XML_NAMESPACE_MATH, XML_MROOT, true,
1039 true);
1040 ExportNodes(pNode->GetSubNode(2), nLevel+1);
1041 ExportNodes(pNode->GetSubNode(0), nLevel+1);
1043 else
1045 SvXMLElementExport aSqrt(*this, XML_NAMESPACE_MATH, XML_MSQRT, true,
1046 true);
1047 ExportNodes(pNode->GetSubNode(2), nLevel+1);
1051 void SmXMLExport::ExportOperator(const SmNode *pNode, int nLevel)
1053 /*we need to either use content or font and size attributes
1054 *here*/
1055 SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW,
1056 true, true);
1057 ExportNodes(pNode->GetSubNode(0), nLevel+1);
1058 ExportNodes(pNode->GetSubNode(1), nLevel+1);
1061 void SmXMLExport::ExportAttributes(const SmNode *pNode, int nLevel)
1063 std::unique_ptr<SvXMLElementExport> pElement;
1065 if (pNode->GetToken().eType == TUNDERLINE)
1067 AddAttribute(XML_NAMESPACE_MATH, XML_ACCENTUNDER,
1068 XML_TRUE);
1069 pElement.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MUNDER,
1070 true, true));
1072 else if (pNode->GetToken().eType == TOVERSTRIKE)
1074 // export as <menclose notation="horizontalstrike">
1075 AddAttribute(XML_NAMESPACE_MATH, XML_NOTATION, XML_HORIZONTALSTRIKE);
1076 pElement.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
1077 XML_MENCLOSE, true, true));
1079 else
1081 AddAttribute(XML_NAMESPACE_MATH, XML_ACCENT,
1082 XML_TRUE);
1083 pElement.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MOVER,
1084 true, true));
1087 ExportNodes(pNode->GetSubNode(1), nLevel+1);
1088 switch (pNode->GetToken().eType)
1090 case TOVERLINE:
1092 //proper entity support required
1093 SvXMLElementExport aMath(*this, XML_NAMESPACE_MATH, XML_MO,
1094 true, true);
1095 sal_Unicode const nArse[2] = {0xAF,0x00};
1096 GetDocHandler()->characters(nArse);
1098 break;
1099 case TUNDERLINE:
1101 //proper entity support required
1102 SvXMLElementExport aMath(*this, XML_NAMESPACE_MATH, XML_MO,
1103 true, true);
1104 sal_Unicode const nArse[2] = {0x0332,0x00};
1105 GetDocHandler()->characters(nArse);
1107 break;
1108 case TOVERSTRIKE:
1109 break;
1110 case TWIDETILDE:
1111 case TWIDEHAT:
1112 case TWIDEVEC:
1113 case TWIDEHARPOON:
1115 // make these wide accents stretchy
1116 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
1117 ExportNodes(pNode->GetSubNode(0), nLevel+1);
1119 break;
1120 default:
1121 ExportNodes(pNode->GetSubNode(0), nLevel+1);
1122 break;
1126 static bool lcl_HasEffectOnMathvariant( const SmTokenType eType )
1128 return eType == TBOLD || eType == TNBOLD ||
1129 eType == TITALIC || eType == TNITALIC ||
1130 eType == TSANS || eType == TSERIF || eType == TFIXED;
1133 void SmXMLExport::ExportFont(const SmNode *pNode, int nLevel)
1136 // gather the mathvariant attribute relevant data from all
1137 // successively following SmFontNodes...
1139 int nBold = -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true;
1140 int nItalic = -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true;
1141 int nSansSerifFixed = -1;
1142 SmTokenType eNodeType = TUNKNOWN;
1144 for (;;)
1146 eNodeType = pNode->GetToken().eType;
1147 if (!lcl_HasEffectOnMathvariant(eNodeType))
1148 break;
1149 switch (eNodeType)
1151 case TBOLD : nBold = 1; break;
1152 case TNBOLD : nBold = 0; break;
1153 case TITALIC : nItalic = 1; break;
1154 case TNITALIC : nItalic = 0; break;
1155 case TSANS : nSansSerifFixed = 0; break;
1156 case TSERIF : nSansSerifFixed = 1; break;
1157 case TFIXED : nSansSerifFixed = 2; break;
1158 default:
1159 SAL_WARN("starmath", "unexpected case");
1161 // According to the parser every node that is to be evaluated here
1162 // has a single non-zero subnode at index 1!! Thus we only need to check
1163 // that single node for follow-up nodes that have an effect on the attribute.
1164 if (pNode->GetNumSubNodes() > 1 && pNode->GetSubNode(1) &&
1165 lcl_HasEffectOnMathvariant( pNode->GetSubNode(1)->GetToken().eType))
1167 pNode = pNode->GetSubNode(1);
1169 else
1170 break;
1173 sal_uInt32 nc;
1174 switch (pNode->GetToken().eType)
1176 case TPHANTOM:
1177 // No attribute needed. An <mphantom> element will be used below.
1178 break;
1179 case TMATHMLCOL:
1181 std::unique_ptr<SmColorTokenTableEntry> aSmColorTokenTableEntry;
1182 nc = pNode->GetToken().aText.toUInt32(16);
1183 aSmColorTokenTableEntry = starmathdatabase::Identify_Color_MATHML(nc);
1184 OUString sssStr = OUString::createFromAscii(aSmColorTokenTableEntry->cIdent);
1185 AddAttribute(XML_NAMESPACE_MATH, XML_MATHCOLOR, sssStr);
1187 break;
1188 case TRGB:
1189 case TRGBA:
1190 case THEX:
1191 case THTMLCOL:
1192 case TDVIPSNAMESCOL:
1193 case TICONICCOL:
1195 OUStringBuffer sStrBuf;
1196 sStrBuf.append('#');
1197 std::unique_ptr<SmColorTokenTableEntry> aSmColorTokenTableEntry;
1198 nc = pNode->GetToken().aText.toUInt32(16);
1199 sStrBuf.append(Color(nc).AsRGBHEXString());
1200 OUString ssStr(sStrBuf.makeStringAndClear());
1201 AddAttribute(XML_NAMESPACE_MATH, XML_MATHCOLOR, ssStr);
1203 break;
1204 case TSIZE:
1206 const SmFontNode *pFontNode = static_cast<const SmFontNode *>(pNode);
1207 const Fraction &aFrac = pFontNode->GetSizeParameter();
1209 OUStringBuffer sStrBuf;
1210 switch(pFontNode->GetSizeType())
1212 case FontSizeType::MULTIPLY:
1213 ::sax::Converter::convertDouble(sStrBuf,
1214 static_cast<double>(aFrac*Fraction(100.00)));
1215 sStrBuf.append('%');
1216 break;
1217 case FontSizeType::DIVIDE:
1218 ::sax::Converter::convertDouble(sStrBuf,
1219 static_cast<double>(Fraction(100.00)/aFrac));
1220 sStrBuf.append('%');
1221 break;
1222 case FontSizeType::ABSOLUT:
1223 ::sax::Converter::convertDouble(sStrBuf,
1224 static_cast<double>(aFrac));
1225 sStrBuf.append(
1226 GetXMLToken(XML_UNIT_PT));
1227 break;
1228 default:
1230 //The problem here is that the wheels fall off because
1231 //font size is stored in 100th's of a mm not pts, and
1232 //rounding errors take their toll on the original
1233 //value specified in points.
1235 //Must fix StarMath to retain the original pt values
1236 Fraction aTemp = Sm100th_mmToPts(pFontNode->GetFont().GetFontSize().Height());
1238 if (pFontNode->GetSizeType() == FontSizeType::MINUS)
1239 aTemp-=aFrac;
1240 else
1241 aTemp+=aFrac;
1243 double mytest = static_cast<double>(aTemp);
1245 mytest = ::rtl::math::round(mytest,1);
1246 ::sax::Converter::convertDouble(sStrBuf,mytest);
1247 sStrBuf.append(GetXMLToken(XML_UNIT_PT));
1249 break;
1252 OUString sStr(sStrBuf.makeStringAndClear());
1253 AddAttribute(XML_NAMESPACE_MATH, XML_MATHSIZE, sStr);
1255 break;
1256 case TBOLD:
1257 case TITALIC:
1258 case TNBOLD:
1259 case TNITALIC:
1260 case TFIXED:
1261 case TSANS:
1262 case TSERIF:
1264 // nBold: -1 = yet undefined; 0 = false; 1 = true;
1265 // nItalic: -1 = yet undefined; 0 = false; 1 = true;
1266 // nSansSerifFixed: -1 = undefined; 0 = sans; 1 = serif; 2 = fixed;
1267 const char *pText = "normal";
1268 if (nSansSerifFixed == -1 || nSansSerifFixed == 1)
1270 pText = "normal";
1271 if (nBold == 1 && nItalic != 1)
1272 pText = "bold";
1273 else if (nBold != 1 && nItalic == 1)
1274 pText = "italic";
1275 else if (nBold == 1 && nItalic == 1)
1276 pText = "bold-italic";
1278 else if (nSansSerifFixed == 0)
1280 pText = "sans-serif";
1281 if (nBold == 1 && nItalic != 1)
1282 pText = "bold-sans-serif";
1283 else if (nBold != 1 && nItalic == 1)
1284 pText = "sans-serif-italic";
1285 else if (nBold == 1 && nItalic == 1)
1286 pText = "sans-serif-bold-italic";
1288 else if (nSansSerifFixed == 2)
1289 pText = "monospace"; // no modifiers allowed for monospace ...
1290 else
1292 SAL_WARN("starmath", "unexpected case");
1294 AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, OUString::createFromAscii( pText ));
1296 break;
1297 default:
1298 break;
1302 // Wrap everything in an <mphantom> or <mstyle> element. These elements
1303 // are mrow-like, so ExportExpression doesn't need to add an explicit
1304 // <mrow> element. See #fdo 66283.
1305 SvXMLElementExport aElement(*this, XML_NAMESPACE_MATH,
1306 pNode->GetToken().eType == TPHANTOM ? XML_MPHANTOM : XML_MSTYLE,
1307 true, true);
1308 ExportExpression(pNode, nLevel, true);
1313 void SmXMLExport::ExportVerticalBrace(const SmVerticalBraceNode *pNode, int nLevel)
1315 // "[body] overbrace [script]"
1317 // Position body, overbrace and script vertically. First place the overbrace
1318 // OVER the body and then the script OVER this expression.
1320 // [script]
1321 // --[overbrace]--
1322 // XXXXXX[body]XXXXXXX
1324 // Similarly for the underbrace construction.
1326 XMLTokenEnum which;
1328 switch (pNode->GetToken().eType)
1330 case TOVERBRACE:
1331 default:
1332 which = XML_MOVER;
1333 break;
1334 case TUNDERBRACE:
1335 which = XML_MUNDER;
1336 break;
1339 SvXMLElementExport aOver1(*this, XML_NAMESPACE_MATH,which, true, true);
1340 {//Scoping
1341 // using accents will draw the over-/underbraces too close to the base
1342 // see http://www.w3.org/TR/MathML2/chapter3.html#id.3.4.5.2
1343 // also XML_ACCENT is illegal with XML_MUNDER. Thus no XML_ACCENT attribute here!
1344 SvXMLElementExport aOver2(*this, XML_NAMESPACE_MATH,which, true, true);
1345 ExportNodes(pNode->Body(), nLevel);
1346 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
1347 ExportNodes(pNode->Brace(), nLevel);
1349 ExportNodes(pNode->Script(), nLevel);
1352 void SmXMLExport::ExportMatrix(const SmNode *pNode, int nLevel)
1354 SvXMLElementExport aTable(*this, XML_NAMESPACE_MATH, XML_MTABLE, true, true);
1355 const SmMatrixNode *pMatrix = static_cast<const SmMatrixNode *>(pNode);
1356 size_t i = 0;
1357 for (sal_uInt16 y = 0; y < pMatrix->GetNumRows(); y++)
1359 SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MTR, true, true);
1360 for (sal_uInt16 x = 0; x < pMatrix->GetNumCols(); x++)
1362 if (const SmNode *pTemp = pNode->GetSubNode(i++))
1364 if (pTemp->GetType() == SmNodeType::Align &&
1365 pTemp->GetToken().eType != TALIGNC)
1367 // A left or right alignment is specified on this cell,
1368 // attach the corresponding columnalign attribute.
1369 AddAttribute(XML_NAMESPACE_MATH, XML_COLUMNALIGN,
1370 pTemp->GetToken().eType == TALIGNL ?
1371 XML_LEFT : XML_RIGHT);
1373 SvXMLElementExport aCell(*this, XML_NAMESPACE_MATH, XML_MTD, true, true);
1374 ExportNodes(pTemp, nLevel+1);
1380 void SmXMLExport::ExportNodes(const SmNode *pNode, int nLevel)
1382 if (!pNode)
1383 return;
1384 switch(pNode->GetType())
1386 case SmNodeType::Table:
1387 ExportTable(pNode, nLevel);
1388 break;
1389 case SmNodeType::Align:
1390 case SmNodeType::Bracebody:
1391 case SmNodeType::Expression:
1392 ExportExpression(pNode, nLevel);
1393 break;
1394 case SmNodeType::Line:
1395 ExportLine(pNode, nLevel);
1396 break;
1397 case SmNodeType::Text:
1398 ExportText(pNode);
1399 break;
1400 case SmNodeType::GlyphSpecial:
1401 case SmNodeType::Math:
1403 sal_Unicode cTmp = 0;
1404 const SmTextNode *pTemp = static_cast< const SmTextNode * >(pNode);
1405 if (!pTemp->GetText().isEmpty())
1406 cTmp = ConvertMathToMathML( pTemp->GetText()[0] );
1407 if (cTmp == 0)
1409 // no conversion to MathML implemented -> export it as text
1410 // thus at least it will not vanish into nothing
1411 ExportText(pNode);
1413 else
1415 switch (pNode->GetToken().eType)
1417 case TINTD:
1418 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
1419 break;
1420 default:
1421 break;
1423 //To fully handle generic MathML we need to implement the full
1424 //operator dictionary, we will generate MathML with explicit
1425 //stretchiness for now.
1426 sal_Int16 nLength = GetAttrList().getLength();
1427 bool bAddStretch=true;
1428 for ( sal_Int16 i = 0; i < nLength; i++ )
1430 OUString sLocalName;
1431 sal_uInt16 nPrefix = GetNamespaceMap().GetKeyByAttrName(
1432 GetAttrList().getNameByIndex(i), &sLocalName );
1434 if ( ( XML_NAMESPACE_MATH == nPrefix ) &&
1435 IsXMLToken(sLocalName, XML_STRETCHY) )
1437 bAddStretch = false;
1438 break;
1441 if (bAddStretch)
1443 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
1445 ExportMath(pNode);
1448 break;
1449 case SmNodeType::Special: //SmNodeType::Special requires some sort of Entity preservation in the XML engine.
1450 case SmNodeType::MathIdent:
1451 case SmNodeType::Place:
1452 ExportMath(pNode);
1453 break;
1454 case SmNodeType::BinHor:
1455 ExportBinaryHorizontal(pNode, nLevel);
1456 break;
1457 case SmNodeType::UnHor:
1458 ExportUnaryHorizontal(pNode, nLevel);
1459 break;
1460 case SmNodeType::Brace:
1461 ExportBrace(pNode, nLevel);
1462 break;
1463 case SmNodeType::BinVer:
1464 ExportBinaryVertical(pNode, nLevel);
1465 break;
1466 case SmNodeType::BinDiagonal:
1467 ExportBinaryDiagonal(pNode, nLevel);
1468 break;
1469 case SmNodeType::SubSup:
1470 ExportSubSupScript(pNode, nLevel);
1471 break;
1472 case SmNodeType::Root:
1473 ExportRoot(pNode, nLevel);
1474 break;
1475 case SmNodeType::Oper:
1476 ExportOperator(pNode, nLevel);
1477 break;
1478 case SmNodeType::Attribut:
1479 ExportAttributes(pNode, nLevel);
1480 break;
1481 case SmNodeType::Font:
1482 ExportFont(pNode, nLevel);
1483 break;
1484 case SmNodeType::VerticalBrace:
1485 ExportVerticalBrace(static_cast<const SmVerticalBraceNode *>(pNode), nLevel);
1486 break;
1487 case SmNodeType::Matrix:
1488 ExportMatrix(pNode, nLevel);
1489 break;
1490 case SmNodeType::Blank:
1491 ExportBlank(pNode);
1492 break;
1493 default:
1494 SAL_WARN("starmath", "Warning: failed to export a node?");
1495 break;
1500 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */