Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / starmath / source / mathml / mathmlexport.cxx
blob6382ade13a530c11107759292ff578c4107d4921
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 <officecfg/Office/Common.hxx>
35 #include <rtl/math.hxx>
36 #include <sfx2/frame.hxx>
37 #include <sfx2/docfile.hxx>
38 #include <sfx2/sfxsids.hrc>
39 #include <osl/diagnose.h>
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 <comphelper/genericpropertyset.hxx>
51 #include <comphelper/servicehelper.hxx>
52 #include <comphelper/propertysetinfo.hxx>
53 #include <comphelper/diagnose_ex.hxx>
54 #include <sal/log.hxx>
56 #include <stack>
58 #include <mathmlexport.hxx>
59 #include <xparsmlbase.hxx>
60 #include <strings.hrc>
61 #include <smmod.hxx>
62 #include <unomodel.hxx>
63 #include <document.hxx>
64 #include <utility.hxx>
65 #include <cfgitem.hxx>
66 #include <starmathdatabase.hxx>
68 using namespace ::com::sun::star::beans;
69 using namespace ::com::sun::star::document;
70 using namespace ::com::sun::star::lang;
71 using namespace ::com::sun::star::uno;
72 using namespace ::com::sun::star;
73 using namespace ::xmloff::token;
75 namespace
77 bool IsInPrivateUseArea(sal_Unicode cChar) { return 0xE000 <= cChar && cChar <= 0xF8FF; }
79 sal_Unicode ConvertMathToMathML(sal_Unicode cChar)
81 sal_Unicode cRes = cChar;
82 if (IsInPrivateUseArea(cChar))
84 SAL_WARN("starmath", "Error: private use area characters should no longer be in use!");
85 cRes = u'@'; // just some character that should easily be notice as odd in the context
87 return cRes;
91 bool SmXMLExportWrapper::Export(SfxMedium& rMedium)
93 bool bRet = true;
94 uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
96 //Get model
97 uno::Reference<lang::XComponent> xModelComp = xModel;
99 bool bEmbedded = false;
100 SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(xModel);
102 SmDocShell* pDocShell = pModel ? static_cast<SmDocShell*>(pModel->GetObjectShell()) : nullptr;
103 if (pDocShell && SfxObjectCreateMode::EMBEDDED == pDocShell->GetCreateMode())
104 bEmbedded = true;
106 uno::Reference<task::XStatusIndicator> xStatusIndicator;
107 if (!bEmbedded)
109 if (pDocShell /*&& pDocShell->GetMedium()*/)
111 OSL_ENSURE(pDocShell->GetMedium() == &rMedium, "different SfxMedium found");
113 SfxItemSet* pSet = rMedium.GetItemSet();
114 if (pSet)
116 const SfxUnoAnyItem* pItem = pSet->GetItem(SID_PROGRESS_STATUSBAR_CONTROL);
117 if (pItem)
118 pItem->GetValue() >>= xStatusIndicator;
122 // set progress range and start status indicator
123 if (xStatusIndicator.is())
125 sal_Int32 nProgressRange = bFlat ? 1 : 3;
126 xStatusIndicator->start(SmResId(STR_STATSTR_WRITING), nProgressRange);
130 static constexpr OUStringLiteral sUsePrettyPrinting(u"UsePrettyPrinting");
131 static constexpr OUStringLiteral sBaseURI(u"BaseURI");
132 static constexpr OUStringLiteral sStreamRelPath(u"StreamRelPath");
133 static constexpr OUStringLiteral sStreamName(u"StreamName");
135 // create XPropertySet with three properties for status indicator
136 static const comphelper::PropertyMapEntry aInfoMap[] = {
137 { sUsePrettyPrinting, 0, cppu::UnoType<bool>::get(), beans::PropertyAttribute::MAYBEVOID,
138 0 },
139 { sBaseURI, 0, ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::MAYBEVOID, 0 },
140 { sStreamRelPath, 0, ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::MAYBEVOID,
141 0 },
142 { sStreamName, 0, ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::MAYBEVOID, 0 }
144 uno::Reference<beans::XPropertySet> xInfoSet(
145 comphelper::GenericPropertySet_CreateInstance(new comphelper::PropertySetInfo(aInfoMap)));
147 bool bUsePrettyPrinting
148 = bFlat || officecfg::Office::Common::Save::Document::PrettyPrinting::get();
149 xInfoSet->setPropertyValue(sUsePrettyPrinting, Any(bUsePrettyPrinting));
151 // Set base URI
152 xInfoSet->setPropertyValue(sBaseURI, Any(rMedium.GetBaseURL(true)));
154 sal_Int32 nSteps = 0;
155 if (xStatusIndicator.is())
156 xStatusIndicator->setValue(nSteps++);
157 if (!bFlat) //Storage (Package) of Stream
159 uno::Reference<embed::XStorage> xStg = rMedium.GetOutputStorage();
160 bool bOASIS = (SotStorage::GetVersion(xStg) > SOFFICE_FILEFORMAT_60);
162 // TODO/LATER: handle the case of embedded links gracefully
163 if (bEmbedded) //&& !pStg->IsRoot() )
165 OUString aName;
166 if (rMedium.GetItemSet())
168 const SfxStringItem* pDocHierarchItem
169 = rMedium.GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME);
170 if (pDocHierarchItem)
171 aName = pDocHierarchItem->GetValue();
174 if (!aName.isEmpty())
176 xInfoSet->setPropertyValue(sStreamRelPath, Any(aName));
180 if (!bEmbedded)
182 if (xStatusIndicator.is())
183 xStatusIndicator->setValue(nSteps++);
185 bRet = WriteThroughComponent(xStg, xModelComp, "meta.xml", xContext, xInfoSet,
186 (bOASIS ? "com.sun.star.comp.Math.XMLOasisMetaExporter"
187 : "com.sun.star.comp.Math.XMLMetaExporter"));
189 if (bRet)
191 if (xStatusIndicator.is())
192 xStatusIndicator->setValue(nSteps++);
194 bRet = WriteThroughComponent(xStg, xModelComp, "content.xml", xContext, xInfoSet,
195 "com.sun.star.comp.Math.XMLContentExporter");
198 if (bRet)
200 if (xStatusIndicator.is())
201 xStatusIndicator->setValue(nSteps++);
203 bRet = WriteThroughComponent(xStg, xModelComp, "settings.xml", xContext, xInfoSet,
204 (bOASIS ? "com.sun.star.comp.Math.XMLOasisSettingsExporter"
205 : "com.sun.star.comp.Math.XMLSettingsExporter"));
208 else
210 SvStream* pStream = rMedium.GetOutStream();
211 uno::Reference<io::XOutputStream> xOut(new utl::OOutputStreamWrapper(*pStream));
213 if (xStatusIndicator.is())
214 xStatusIndicator->setValue(nSteps++);
216 bRet = WriteThroughComponent(xOut, xModelComp, xContext, xInfoSet,
217 "com.sun.star.comp.Math.XMLContentExporter");
220 if (xStatusIndicator.is())
221 xStatusIndicator->end();
223 return bRet;
226 /// export through an XML exporter component (output stream version)
227 bool SmXMLExportWrapper::WriteThroughComponent(const Reference<io::XOutputStream>& xOutputStream,
228 const Reference<XComponent>& xComponent,
229 Reference<uno::XComponentContext> const& rxContext,
230 Reference<beans::XPropertySet> const& rPropSet,
231 const char* pComponentName)
233 OSL_ENSURE(xOutputStream.is(), "I really need an output stream!");
234 OSL_ENSURE(xComponent.is(), "Need component!");
235 OSL_ENSURE(nullptr != pComponentName, "Need component name!");
237 // get component
238 Reference<xml::sax::XWriter> xSaxWriter = xml::sax::Writer::create(rxContext);
240 // connect XML writer to output stream
241 xSaxWriter->setOutputStream(xOutputStream);
242 if (m_bUseHTMLMLEntities)
243 xSaxWriter->setCustomEntityNames(starmathdatabase::icustomMathmlHtmlEntitiesExport);
245 // prepare arguments (prepend doc handler to given arguments)
246 Sequence<Any> aArgs{ Any(xSaxWriter), Any(rPropSet) };
248 // get filter component
249 Reference<document::XExporter> xExporter(
250 rxContext->getServiceManager()->createInstanceWithArgumentsAndContext(
251 OUString::createFromAscii(pComponentName), aArgs, rxContext),
252 UNO_QUERY);
253 OSL_ENSURE(xExporter.is(), "can't instantiate export filter component");
254 if (!xExporter.is())
255 return false;
257 // connect model and filter
258 xExporter->setSourceDocument(xComponent);
260 // filter!
261 Reference<XFilter> xFilter(xExporter, UNO_QUERY);
262 uno::Sequence<PropertyValue> aProps(0);
263 xFilter->filter(aProps);
265 auto pFilter = dynamic_cast<SmXMLExport*>(xFilter.get());
266 return pFilter == nullptr || pFilter->GetSuccess();
269 /// export through an XML exporter component (storage version)
270 bool SmXMLExportWrapper::WriteThroughComponent(const Reference<embed::XStorage>& xStorage,
271 const Reference<XComponent>& xComponent,
272 const char* pStreamName,
273 Reference<uno::XComponentContext> const& rxContext,
274 Reference<beans::XPropertySet> const& rPropSet,
275 const char* pComponentName)
277 OSL_ENSURE(xStorage.is(), "Need storage!");
278 OSL_ENSURE(nullptr != pStreamName, "Need stream name!");
280 // open stream
281 Reference<io::XStream> xStream;
282 OUString sStreamName = OUString::createFromAscii(pStreamName);
285 xStream = xStorage->openStreamElement(sStreamName, embed::ElementModes::READWRITE
286 | embed::ElementModes::TRUNCATE);
288 catch (const uno::Exception&)
290 DBG_UNHANDLED_EXCEPTION("starmath", "Can't create output stream in package");
291 return false;
294 uno::Reference<beans::XPropertySet> xSet(xStream, uno::UNO_QUERY);
295 static const OUStringLiteral sMediaType = u"MediaType";
296 static const OUStringLiteral sTextXml = u"text/xml";
297 xSet->setPropertyValue(sMediaType, Any(OUString(sTextXml)));
299 // all streams must be encrypted in encrypted document
300 static const OUStringLiteral sUseCommonStoragePasswordEncryption
301 = u"UseCommonStoragePasswordEncryption";
302 xSet->setPropertyValue(sUseCommonStoragePasswordEncryption, Any(true));
304 // set Base URL
305 if (rPropSet.is())
307 rPropSet->setPropertyValue("StreamName", Any(sStreamName));
310 // write the stuff
311 bool bRet = WriteThroughComponent(xStream->getOutputStream(), xComponent, rxContext, rPropSet,
312 pComponentName);
314 return bRet;
317 SmXMLExport::SmXMLExport(const css::uno::Reference<css::uno::XComponentContext>& rContext,
318 OUString const& implementationName, SvXMLExportFlags nExportFlags)
319 : SvXMLExport(rContext, implementationName, util::MeasureUnit::INCH, XML_MATH, nExportFlags)
320 , pTree(nullptr)
321 , bSuccess(false)
325 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
326 Math_XMLExporter_get_implementation(css::uno::XComponentContext* context,
327 css::uno::Sequence<css::uno::Any> const&)
329 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLExporter",
330 SvXMLExportFlags::OASIS | SvXMLExportFlags::ALL));
333 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
334 Math_XMLMetaExporter_get_implementation(css::uno::XComponentContext* context,
335 css::uno::Sequence<css::uno::Any> const&)
337 return cppu::acquire(
338 new SmXMLExport(context, "com.sun.star.comp.Math.XMLMetaExporter", SvXMLExportFlags::META));
341 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
342 Math_XMLOasisMetaExporter_get_implementation(css::uno::XComponentContext* context,
343 css::uno::Sequence<css::uno::Any> const&)
345 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLOasisMetaExporter",
346 SvXMLExportFlags::OASIS | SvXMLExportFlags::META));
349 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
350 Math_XMLSettingsExporter_get_implementation(css::uno::XComponentContext* context,
351 css::uno::Sequence<css::uno::Any> const&)
353 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLSettingsExporter",
354 SvXMLExportFlags::SETTINGS));
357 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
358 Math_XMLOasisSettingsExporter_get_implementation(css::uno::XComponentContext* context,
359 css::uno::Sequence<css::uno::Any> const&)
361 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLOasisSettingsExporter",
362 SvXMLExportFlags::OASIS | SvXMLExportFlags::SETTINGS));
365 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
366 Math_XMLContentExporter_get_implementation(css::uno::XComponentContext* context,
367 css::uno::Sequence<css::uno::Any> const&)
369 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLContentExporter",
370 SvXMLExportFlags::OASIS | SvXMLExportFlags::CONTENT));
373 ErrCode SmXMLExport::exportDoc(enum XMLTokenEnum eClass)
375 if (!(getExportFlags() & SvXMLExportFlags::CONTENT))
377 SvXMLExport::exportDoc(eClass);
379 else
381 uno::Reference<frame::XModel> xModel = GetModel();
382 SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(xModel);
384 if (pModel)
386 SmDocShell* pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell());
387 pTree = pDocShell->GetFormulaTree();
388 aText = pDocShell->GetText();
391 GetDocHandler()->startDocument();
393 addChaffWhenEncryptedStorage();
395 /*Add xmlns line*/
396 comphelper::AttributeList& rList = GetAttrList();
398 // make use of a default namespace
399 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)
400 GetNamespaceMap_().Add(OUString(), GetXMLToken(XML_N_MATH), XML_NAMESPACE_MATH);
402 rList.AddAttribute(GetNamespaceMap().GetAttrNameByKey(XML_NAMESPACE_MATH),
403 GetNamespaceMap().GetNameByKey(XML_NAMESPACE_MATH));
405 //I think we need something like ImplExportEntities();
406 ExportContent_();
407 GetDocHandler()->endDocument();
410 bSuccess = true;
411 return ERRCODE_NONE;
414 void SmXMLExport::ExportContent_()
416 uno::Reference<frame::XModel> xModel = GetModel();
417 SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(xModel);
418 SmDocShell* pDocShell = pModel ? static_cast<SmDocShell*>(pModel->GetObjectShell()) : nullptr;
419 OSL_ENSURE(pDocShell, "doc shell missing");
421 if (pDocShell && !pDocShell->GetFormat().IsTextmode())
423 // If the Math equation is not in text mode, we attach a display="block"
424 // attribute on the <math> root. We don't do anything if it is in
425 // text mode, the default display="inline" value will be used.
426 AddAttribute(XML_NAMESPACE_MATH, XML_DISPLAY, XML_BLOCK);
428 SvXMLElementExport aEquation(*this, XML_NAMESPACE_MATH, XML_MATH, true, true);
429 std::unique_ptr<SvXMLElementExport> pSemantics;
431 if (!aText.isEmpty())
433 pSemantics.reset(
434 new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_SEMANTICS, true, true));
437 ExportNodes(pTree, 0);
439 if (aText.isEmpty())
440 return;
442 SmModule* pMod = SM_MOD();
443 sal_uInt16 nSmSyntaxVersion = pMod->GetConfig()->GetDefaultSmSyntaxVersion();
445 // Convert symbol names
446 if (pDocShell)
448 nSmSyntaxVersion = pDocShell->GetSmSyntaxVersion();
449 AbstractSmParser* rParser = pDocShell->GetParser();
450 bool bVal = rParser->IsExportSymbolNames();
451 rParser->SetExportSymbolNames(true);
452 auto pTmpTree = rParser->Parse(aText);
453 aText = rParser->GetText();
454 pTmpTree.reset();
455 rParser->SetExportSymbolNames(bVal);
458 OUStringBuffer sStrBuf(12);
459 sStrBuf.append(u"StarMath ");
460 if (nSmSyntaxVersion == 5)
461 sStrBuf.append(u"5.0");
462 else
463 sStrBuf.append(static_cast<sal_Int32>(nSmSyntaxVersion));
465 AddAttribute(XML_NAMESPACE_MATH, XML_ENCODING, sStrBuf.makeStringAndClear());
466 SvXMLElementExport aAnnotation(*this, XML_NAMESPACE_MATH, XML_ANNOTATION, true, false);
467 GetDocHandler()->characters(aText);
470 void SmXMLExport::GetViewSettings(Sequence<PropertyValue>& aProps)
472 uno::Reference<frame::XModel> xModel = GetModel();
473 if (!xModel.is())
474 return;
476 SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(xModel);
478 if (!pModel)
479 return;
481 SmDocShell* pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell());
482 if (!pDocShell)
483 return;
485 aProps.realloc(4);
486 PropertyValue* pValue = aProps.getArray();
487 sal_Int32 nIndex = 0;
489 tools::Rectangle aRect(pDocShell->GetVisArea());
491 pValue[nIndex].Name = "ViewAreaTop";
492 pValue[nIndex++].Value <<= aRect.Top();
494 pValue[nIndex].Name = "ViewAreaLeft";
495 pValue[nIndex++].Value <<= aRect.Left();
497 pValue[nIndex].Name = "ViewAreaWidth";
498 pValue[nIndex++].Value <<= aRect.GetWidth();
500 pValue[nIndex].Name = "ViewAreaHeight";
501 pValue[nIndex++].Value <<= aRect.GetHeight();
504 void SmXMLExport::GetConfigurationSettings(Sequence<PropertyValue>& rProps)
506 Reference<XPropertySet> xProps(GetModel(), UNO_QUERY);
507 if (!xProps.is())
508 return;
510 Reference<XPropertySetInfo> xPropertySetInfo = xProps->getPropertySetInfo();
511 if (!xPropertySetInfo.is())
512 return;
514 const Sequence<Property> aProps = xPropertySetInfo->getProperties();
515 const sal_Int32 nCount = aProps.getLength();
516 if (!nCount)
517 return;
519 rProps.realloc(nCount);
520 SmMathConfig* pConfig = SM_MOD()->GetConfig();
521 const bool bUsedSymbolsOnly = pConfig && pConfig->IsSaveOnlyUsedSymbols();
523 std::transform(aProps.begin(), aProps.end(), rProps.getArray(),
524 [bUsedSymbolsOnly, &xProps](const Property& prop) {
525 PropertyValue aRet;
526 if (prop.Name != "Formula" && prop.Name != "BasicLibraries"
527 && prop.Name != "DialogLibraries" && prop.Name != "RuntimeUID")
529 aRet.Name = prop.Name;
530 OUString aActualName(prop.Name);
531 // handle 'save used symbols only'
532 static constexpr OUStringLiteral sUserDefinedSymbolsInUse
533 = u"UserDefinedSymbolsInUse";
534 if (bUsedSymbolsOnly && prop.Name == "Symbols")
535 aActualName = sUserDefinedSymbolsInUse;
536 aRet.Value = xProps->getPropertyValue(aActualName);
538 return aRet;
542 void SmXMLExport::ExportLine(const SmNode* pNode, int nLevel) { ExportExpression(pNode, nLevel); }
544 void SmXMLExport::ExportBinaryHorizontal(const SmNode* pNode, int nLevel)
546 TG nGroup = pNode->GetToken().nGroup;
548 SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW, true, true);
550 // Unfold the binary tree structure as long as the nodes are SmBinHorNode
551 // with the same nGroup. This will reduce the number of nested <mrow>
552 // elements e.g. we only need three <mrow> levels to export
554 // "a*b*c*d+e*f*g*h+i*j*k*l = a*b*c*d+e*f*g*h+i*j*k*l =
555 // a*b*c*d+e*f*g*h+i*j*k*l = a*b*c*d+e*f*g*h+i*j*k*l"
557 // See https://www.libreoffice.org/bugzilla/show_bug.cgi?id=66081
558 ::std::stack<const SmNode*> s;
559 s.push(pNode);
560 while (!s.empty())
562 const SmNode* node = s.top();
563 s.pop();
564 if (node->GetType() != SmNodeType::BinHor || node->GetToken().nGroup != nGroup)
566 ExportNodes(node, nLevel + 1);
567 continue;
569 const SmBinHorNode* binNode = static_cast<const SmBinHorNode*>(node);
570 s.push(binNode->RightOperand());
571 s.push(binNode->Symbol());
572 s.push(binNode->LeftOperand());
576 void SmXMLExport::ExportUnaryHorizontal(const SmNode* pNode, int nLevel)
578 ExportExpression(pNode, nLevel);
581 void SmXMLExport::ExportExpression(const SmNode* pNode, int nLevel,
582 bool bNoMrowContainer /*=false*/)
584 std::unique_ptr<SvXMLElementExport> pRow;
585 size_t nSize = pNode->GetNumSubNodes();
587 // #i115443: nodes of type expression always need to be grouped with mrow statement
588 if (!bNoMrowContainer && (nSize > 1 || pNode->GetType() == SmNodeType::Expression))
589 pRow.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MROW, true, true));
591 for (size_t i = 0; i < nSize; ++i)
593 if (const SmNode* pTemp = pNode->GetSubNode(i))
594 ExportNodes(pTemp, nLevel + 1);
598 void SmXMLExport::ExportBinaryVertical(const SmNode* pNode, int nLevel)
600 assert(pNode->GetNumSubNodes() == 3);
601 const SmNode* pNum = pNode->GetSubNode(0);
602 const SmNode* pDenom = pNode->GetSubNode(2);
603 if (pNum->GetType() == SmNodeType::Align && pNum->GetToken().eType != TALIGNC)
605 // A left or right alignment is specified on the numerator:
606 // attach the corresponding numalign attribute.
607 AddAttribute(XML_NAMESPACE_MATH, XML_NUMALIGN,
608 pNum->GetToken().eType == TALIGNL ? XML_LEFT : XML_RIGHT);
610 if (pDenom->GetType() == SmNodeType::Align && pDenom->GetToken().eType != TALIGNC)
612 // A left or right alignment is specified on the denominator:
613 // attach the corresponding denomalign attribute.
614 AddAttribute(XML_NAMESPACE_MATH, XML_DENOMALIGN,
615 pDenom->GetToken().eType == TALIGNL ? XML_LEFT : XML_RIGHT);
617 SvXMLElementExport aFraction(*this, XML_NAMESPACE_MATH, XML_MFRAC, true, true);
618 ExportNodes(pNum, nLevel);
619 ExportNodes(pDenom, nLevel);
622 void SmXMLExport::ExportBinaryDiagonal(const SmNode* pNode, int nLevel)
624 assert(pNode->GetNumSubNodes() == 3);
626 if (pNode->GetToken().eType == TWIDESLASH)
628 // wideslash
629 // export the node as <mfrac bevelled="true">
630 AddAttribute(XML_NAMESPACE_MATH, XML_BEVELLED, XML_TRUE);
631 SvXMLElementExport aFraction(*this, XML_NAMESPACE_MATH, XML_MFRAC, true, true);
632 ExportNodes(pNode->GetSubNode(0), nLevel);
633 ExportNodes(pNode->GetSubNode(1), nLevel);
635 else
637 // widebslash
638 // We can not use <mfrac> to a backslash, so just use <mo>\</mo>
639 SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW, true, true);
641 ExportNodes(pNode->GetSubNode(0), nLevel);
643 { // Scoping for <mo> creation
644 SvXMLElementExport aMo(*this, XML_NAMESPACE_MATH, XML_MO, true, true);
645 GetDocHandler()->characters(OUStringChar(MS_BACKSLASH));
648 ExportNodes(pNode->GetSubNode(1), nLevel);
652 void SmXMLExport::ExportTable(const SmNode* pNode, int nLevel)
654 std::unique_ptr<SvXMLElementExport> pTable;
656 size_t nSize = pNode->GetNumSubNodes();
658 //If the list ends in newline then the last entry has
659 //no subnodes, the newline is superfluous so we just drop
660 //the last node, inclusion would create a bad MathML
661 //table
662 if (nSize >= 1)
664 const SmNode* pLine = pNode->GetSubNode(nSize - 1);
665 if (pLine->GetType() == SmNodeType::Line && pLine->GetNumSubNodes() == 1
666 && pLine->GetSubNode(0) != nullptr
667 && pLine->GetSubNode(0)->GetToken().eType == TNEWLINE)
668 --nSize;
671 // try to avoid creating a mtable element when the formula consists only
672 // of a single output line
673 if (nLevel || (nSize > 1))
674 pTable.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTABLE, true, true));
676 for (size_t i = 0; i < nSize; ++i)
678 if (const SmNode* pTemp = pNode->GetSubNode(i))
680 std::unique_ptr<SvXMLElementExport> pRow;
681 std::unique_ptr<SvXMLElementExport> pCell;
682 if (pTable)
684 pRow.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTR, true, true));
685 SmTokenType eAlign = TALIGNC;
686 if (pTemp->GetType() == SmNodeType::Align)
688 // For Binom() and Stack() constructions, the SmNodeType::Align nodes
689 // are direct children.
690 // binom{alignl ...}{alignr ...} and
691 // stack{alignl ... ## alignr ... ## ...}
692 eAlign = pTemp->GetToken().eType;
694 else if (pTemp->GetType() == SmNodeType::Line && pTemp->GetNumSubNodes() == 1
695 && pTemp->GetSubNode(0)
696 && pTemp->GetSubNode(0)->GetType() == SmNodeType::Align)
698 // For the Table() construction, the SmNodeType::Align node is a child
699 // of an SmNodeType::Line node.
700 // alignl ... newline alignr ... newline ...
701 eAlign = pTemp->GetSubNode(0)->GetToken().eType;
703 if (eAlign != TALIGNC)
705 // If a left or right alignment is specified on this line,
706 // attach the corresponding columnalign attribute.
707 AddAttribute(XML_NAMESPACE_MATH, XML_COLUMNALIGN,
708 eAlign == TALIGNL ? XML_LEFT : XML_RIGHT);
710 pCell.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTD, true, true));
712 ExportNodes(pTemp, nLevel + 1);
717 void SmXMLExport::ExportMath(const SmNode* pNode)
719 const SmTextNode* pTemp = static_cast<const SmTextNode*>(pNode);
720 std::unique_ptr<SvXMLElementExport> pMath;
722 if (pNode->GetType() == SmNodeType::Math || pNode->GetType() == SmNodeType::GlyphSpecial)
724 // Export SmNodeType::Math and SmNodeType::GlyphSpecial symbols as <mo> elements
725 pMath.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MO, true, false));
727 else if (pNode->GetType() == SmNodeType::Special)
729 bool bIsItalic = IsItalic(pNode->GetFont());
730 if (!bIsItalic)
731 AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
732 pMath.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false));
734 else
736 // Export SmNodeType::MathIdent and SmNodeType::Place symbols as <mi> elements:
737 // - These math symbols should not be drawn slanted. Hence we should
738 // attach a mathvariant="normal" attribute to single-char <mi> elements
739 // that are not mathematical alphanumeric symbol. For simplicity and to
740 // work around browser limitations, we always attach such an attribute.
741 // - The MathML specification suggests to use empty <mi> elements as
742 // placeholders but they won't be visible in most MathML rendering
743 // engines so let's use an empty square for SmNodeType::Place instead.
744 AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
745 pMath.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false));
747 sal_Unicode nArse = pTemp->GetText()[0];
748 sal_Unicode cTmp = ConvertMathToMathML(nArse);
749 if (cTmp != 0)
750 nArse = cTmp;
751 OSL_ENSURE(nArse != 0xffff, "Non existent symbol");
752 GetDocHandler()->characters(OUString(nArse));
755 void SmXMLExport::ExportText(const SmNode* pNode)
757 std::unique_ptr<SvXMLElementExport> pText;
758 const SmTextNode* pTemp = static_cast<const SmTextNode*>(pNode);
759 switch (pNode->GetToken().eType)
761 default:
762 case TIDENT:
764 //Note that we change the fontstyle to italic for strings that
765 //are italic and longer than a single character.
766 bool bIsItalic = IsItalic(pTemp->GetFont());
767 if ((pTemp->GetText().getLength() > 1) && bIsItalic)
768 AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_ITALIC);
769 else if ((pTemp->GetText().getLength() == 1) && !bIsItalic)
770 AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
771 pText.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false));
772 break;
774 case TNUMBER:
775 pText.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MN, true, false));
776 break;
777 case TTEXT:
778 pText.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTEXT, true, false));
779 break;
781 GetDocHandler()->characters(pTemp->GetText());
784 void SmXMLExport::ExportBlank(const SmNode* pNode)
786 const SmBlankNode* pTemp = static_cast<const SmBlankNode*>(pNode);
787 //!! exports an <mspace> element. Note that for example "~_~" is allowed in
788 //!! Math (so it has no sense at all) but must not result in an empty
789 //!! <msub> tag in MathML !!
791 if (pTemp->GetBlankNum() != 0)
793 // Attach a width attribute. We choose the (somewhat arbitrary) values
794 // ".5em" for a small gap '`' and "2em" for a large gap '~'.
795 // (see SmBlankNode::IncreaseBy for how pTemp->mnNum is set).
796 OUStringBuffer sStrBuf;
797 ::sax::Converter::convertDouble(sStrBuf, pTemp->GetBlankNum() * .5);
798 sStrBuf.append("em");
799 AddAttribute(XML_NAMESPACE_MATH, XML_WIDTH, sStrBuf.makeStringAndClear());
802 SvXMLElementExport aTextExport(*this, XML_NAMESPACE_MATH, XML_MSPACE, true, false);
804 GetDocHandler()->characters(OUString());
807 void SmXMLExport::ExportSubSupScript(const SmNode* pNode, int nLevel)
809 const SmNode* pSub = nullptr;
810 const SmNode* pSup = nullptr;
811 const SmNode* pCSub = nullptr;
812 const SmNode* pCSup = nullptr;
813 const SmNode* pLSub = nullptr;
814 const SmNode* pLSup = nullptr;
815 std::unique_ptr<SvXMLElementExport> pThing2;
817 //if we have prescripts at all then we must use the tensor notation
819 //This is one of those excellent locations where scope is vital to
820 //arrange the construction and destruction of the element helper
821 //classes correctly
822 pLSub = pNode->GetSubNode(LSUB + 1);
823 pLSup = pNode->GetSubNode(LSUP + 1);
824 if (pLSub || pLSup)
826 SvXMLElementExport aMultiScripts(*this, XML_NAMESPACE_MATH, XML_MMULTISCRIPTS, true, true);
828 if (nullptr != (pCSub = pNode->GetSubNode(CSUB + 1))
829 && nullptr != (pCSup = pNode->GetSubNode(CSUP + 1)))
831 pThing2.reset(
832 new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MUNDEROVER, true, true));
834 else if (nullptr != (pCSub = pNode->GetSubNode(CSUB + 1)))
836 pThing2.reset(
837 new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MUNDER, true, true));
839 else if (nullptr != (pCSup = pNode->GetSubNode(CSUP + 1)))
841 pThing2.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MOVER, true, true));
844 ExportNodes(pNode->GetSubNode(0), nLevel + 1); //Main Term
846 if (pCSub)
847 ExportNodes(pCSub, nLevel + 1);
848 if (pCSup)
849 ExportNodes(pCSup, nLevel + 1);
850 pThing2.reset();
852 pSub = pNode->GetSubNode(RSUB + 1);
853 pSup = pNode->GetSubNode(RSUP + 1);
854 if (pSub || pSup)
856 if (pSub)
857 ExportNodes(pSub, nLevel + 1);
858 else
860 SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE, true, true);
862 if (pSup)
863 ExportNodes(pSup, nLevel + 1);
864 else
866 SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE, true, true);
870 //Separator element between suffix and prefix sub/sup pairs
872 SvXMLElementExport aPrescripts(*this, XML_NAMESPACE_MATH, XML_MPRESCRIPTS, true, true);
875 if (pLSub)
876 ExportNodes(pLSub, nLevel + 1);
877 else
879 SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE, true, true);
881 if (pLSup)
882 ExportNodes(pLSup, nLevel + 1);
883 else
885 SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE, true, true);
888 else
890 std::unique_ptr<SvXMLElementExport> pThing;
891 if (nullptr != (pSub = pNode->GetSubNode(RSUB + 1))
892 && nullptr != (pSup = pNode->GetSubNode(RSUP + 1)))
894 pThing.reset(
895 new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSUBSUP, true, true));
897 else if (nullptr != (pSub = pNode->GetSubNode(RSUB + 1)))
899 pThing.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSUB, true, true));
901 else if (nullptr != (pSup = pNode->GetSubNode(RSUP + 1)))
903 pThing.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSUP, true, true));
906 if (nullptr != (pCSub = pNode->GetSubNode(CSUB + 1))
907 && nullptr != (pCSup = pNode->GetSubNode(CSUP + 1)))
909 pThing2.reset(
910 new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MUNDEROVER, true, true));
912 else if (nullptr != (pCSub = pNode->GetSubNode(CSUB + 1)))
914 pThing2.reset(
915 new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MUNDER, true, true));
917 else if (nullptr != (pCSup = pNode->GetSubNode(CSUP + 1)))
919 pThing2.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MOVER, true, true));
921 ExportNodes(pNode->GetSubNode(0), nLevel + 1); //Main Term
923 if (pCSub)
924 ExportNodes(pCSub, nLevel + 1);
925 if (pCSup)
926 ExportNodes(pCSup, nLevel + 1);
927 pThing2.reset();
929 if (pSub)
930 ExportNodes(pSub, nLevel + 1);
931 if (pSup)
932 ExportNodes(pSup, nLevel + 1);
933 pThing.reset();
937 void SmXMLExport::ExportBrace(const SmNode* pNode, int nLevel)
939 const SmNode* pTemp;
940 const SmNode* pLeft = pNode->GetSubNode(0);
941 const SmNode* pRight = pNode->GetSubNode(2);
943 // This used to generate <mfenced> or <mrow>+<mo> elements according to
944 // the stretchiness of fences. The MathML recommendation defines an
945 // <mrow>+<mo> construction that is equivalent to the <mfenced> element:
946 // http://www.w3.org/TR/MathML3/chapter3.html#presm.mfenced
947 // To simplify our code and avoid issues with mfenced implementations in
948 // MathML rendering engines, we now always generate <mrow>+<mo> elements.
949 // See #fdo 66282.
951 // <mrow>
952 SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW, true, true);
954 // <mo fence="true"> opening-fence </mo>
955 if (pLeft && (pLeft->GetToken().eType != TNONE))
957 AddAttribute(XML_NAMESPACE_MATH, XML_FENCE, XML_TRUE);
958 AddAttribute(XML_NAMESPACE_MATH, XML_FORM, XML_PREFIX);
959 if (pNode->GetScaleMode() == SmScaleMode::Height)
960 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
961 else
962 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
963 ExportNodes(pLeft, nLevel + 1);
966 if (nullptr != (pTemp = pNode->GetSubNode(1)))
968 // <mrow>
969 SvXMLElementExport aRowExport(*this, XML_NAMESPACE_MATH, XML_MROW, true, true);
970 ExportNodes(pTemp, nLevel + 1);
971 // </mrow>
974 // <mo fence="true"> closing-fence </mo>
975 if (pRight && (pRight->GetToken().eType != TNONE))
977 AddAttribute(XML_NAMESPACE_MATH, XML_FENCE, XML_TRUE);
978 AddAttribute(XML_NAMESPACE_MATH, XML_FORM, XML_POSTFIX);
979 if (pNode->GetScaleMode() == SmScaleMode::Height)
980 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
981 else
982 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
983 ExportNodes(pRight, nLevel + 1);
986 // </mrow>
989 void SmXMLExport::ExportRoot(const SmNode* pNode, int nLevel)
991 if (pNode->GetSubNode(0))
993 SvXMLElementExport aRoot(*this, XML_NAMESPACE_MATH, XML_MROOT, true, true);
994 ExportNodes(pNode->GetSubNode(2), nLevel + 1);
995 ExportNodes(pNode->GetSubNode(0), nLevel + 1);
997 else
999 SvXMLElementExport aSqrt(*this, XML_NAMESPACE_MATH, XML_MSQRT, true, true);
1000 ExportNodes(pNode->GetSubNode(2), nLevel + 1);
1004 void SmXMLExport::ExportOperator(const SmNode* pNode, int nLevel)
1006 /*we need to either use content or font and size attributes
1007 *here*/
1008 SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW, true, true);
1009 ExportNodes(pNode->GetSubNode(0), nLevel + 1);
1010 ExportNodes(pNode->GetSubNode(1), nLevel + 1);
1013 void SmXMLExport::ExportAttributes(const SmNode* pNode, int nLevel)
1015 std::unique_ptr<SvXMLElementExport> pElement;
1017 if (pNode->GetToken().eType == TUNDERLINE)
1019 AddAttribute(XML_NAMESPACE_MATH, XML_ACCENTUNDER, XML_TRUE);
1020 pElement.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MUNDER, true, true));
1022 else if (pNode->GetToken().eType == TOVERSTRIKE)
1024 // export as <menclose notation="horizontalstrike">
1025 AddAttribute(XML_NAMESPACE_MATH, XML_NOTATION, XML_HORIZONTALSTRIKE);
1026 pElement.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MENCLOSE, true, true));
1028 else
1030 AddAttribute(XML_NAMESPACE_MATH, XML_ACCENT, XML_TRUE);
1031 pElement.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MOVER, true, true));
1034 ExportNodes(pNode->GetSubNode(1), nLevel + 1);
1035 switch (pNode->GetToken().eType)
1037 case TOVERLINE:
1039 //proper entity support required
1040 SvXMLElementExport aMath(*this, XML_NAMESPACE_MATH, XML_MO, true, true);
1041 static constexpr OUStringLiteral nArse = u"\u00AF";
1042 GetDocHandler()->characters(nArse);
1044 break;
1045 case TUNDERLINE:
1047 //proper entity support required
1048 SvXMLElementExport aMath(*this, XML_NAMESPACE_MATH, XML_MO, true, true);
1049 static constexpr OUStringLiteral nArse = u"\u0332";
1050 GetDocHandler()->characters(nArse);
1052 break;
1053 case TOVERSTRIKE:
1054 break;
1055 case TWIDETILDE:
1056 case TWIDEHAT:
1057 case TWIDEVEC:
1058 case TWIDEHARPOON:
1060 // make these wide accents stretchy
1061 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
1062 ExportNodes(pNode->GetSubNode(0), nLevel + 1);
1064 break;
1065 default:
1066 ExportNodes(pNode->GetSubNode(0), nLevel + 1);
1067 break;
1071 static bool lcl_HasEffectOnMathvariant(const SmTokenType eType)
1073 return eType == TBOLD || eType == TNBOLD || eType == TITALIC || eType == TNITALIC
1074 || eType == TSANS || eType == TSERIF || eType == TFIXED;
1077 void SmXMLExport::ExportFont(const SmNode* pNode, int nLevel)
1079 // gather the mathvariant attribute relevant data from all
1080 // successively following SmFontNodes...
1082 int nBold = -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true;
1083 int nItalic = -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true;
1084 int nSansSerifFixed = -1;
1085 SmTokenType eNodeType = TUNKNOWN;
1087 for (;;)
1089 eNodeType = pNode->GetToken().eType;
1090 if (!lcl_HasEffectOnMathvariant(eNodeType))
1091 break;
1092 switch (eNodeType)
1094 case TBOLD:
1095 nBold = 1;
1096 break;
1097 case TNBOLD:
1098 nBold = 0;
1099 break;
1100 case TITALIC:
1101 nItalic = 1;
1102 break;
1103 case TNITALIC:
1104 nItalic = 0;
1105 break;
1106 case TSANS:
1107 nSansSerifFixed = 0;
1108 break;
1109 case TSERIF:
1110 nSansSerifFixed = 1;
1111 break;
1112 case TFIXED:
1113 nSansSerifFixed = 2;
1114 break;
1115 default:
1116 SAL_WARN("starmath", "unexpected case");
1118 // According to the parser every node that is to be evaluated here
1119 // has a single non-zero subnode at index 1!! Thus we only need to check
1120 // that single node for follow-up nodes that have an effect on the attribute.
1121 if (pNode->GetNumSubNodes() > 1 && pNode->GetSubNode(1)
1122 && lcl_HasEffectOnMathvariant(pNode->GetSubNode(1)->GetToken().eType))
1124 pNode = pNode->GetSubNode(1);
1126 else
1127 break;
1130 sal_uInt32 nc;
1131 switch (pNode->GetToken().eType)
1133 case TPHANTOM:
1134 // No attribute needed. An <mphantom> element will be used below.
1135 break;
1136 case TMATHMLCOL:
1138 nc = pNode->GetToken().cMathChar.toUInt32(16);
1139 const OUString& sssStr = starmathdatabase::Identify_Color_MATHML(nc).aIdent;
1140 AddAttribute(XML_NAMESPACE_MATH, XML_MATHCOLOR, sssStr);
1142 break;
1143 case TRGB:
1144 case TRGBA:
1145 case THEX:
1146 case THTMLCOL:
1147 case TDVIPSNAMESCOL:
1148 case TICONICCOL:
1150 nc = pNode->GetToken().cMathChar.toUInt32(16);
1151 OUString ssStr("#" + Color(ColorTransparency, nc).AsRGBHEXString());
1152 AddAttribute(XML_NAMESPACE_MATH, XML_MATHCOLOR, ssStr);
1154 break;
1155 case TSIZE:
1157 const SmFontNode* pFontNode = static_cast<const SmFontNode*>(pNode);
1158 const Fraction& aFrac = pFontNode->GetSizeParameter();
1160 OUStringBuffer sStrBuf;
1161 switch (pFontNode->GetSizeType())
1163 case FontSizeType::MULTIPLY:
1164 ::sax::Converter::convertDouble(sStrBuf,
1165 static_cast<double>(aFrac * Fraction(100, 1)));
1166 sStrBuf.append('%');
1167 break;
1168 case FontSizeType::DIVIDE:
1169 ::sax::Converter::convertDouble(sStrBuf,
1170 static_cast<double>(Fraction(100, 1) / aFrac));
1171 sStrBuf.append('%');
1172 break;
1173 case FontSizeType::ABSOLUT:
1174 ::sax::Converter::convertDouble(sStrBuf, static_cast<double>(aFrac));
1175 sStrBuf.append(GetXMLToken(XML_UNIT_PT));
1176 break;
1177 default:
1179 //The problem here is that the wheels fall off because
1180 //font size is stored in 100th's of a mm not pts, and
1181 //rounding errors take their toll on the original
1182 //value specified in points.
1184 //Must fix StarMath to retain the original pt values
1185 double mytest
1186 = o3tl::convert<double>(pFontNode->GetFont().GetFontSize().Height(),
1187 SmO3tlLengthUnit(), o3tl::Length::pt);
1189 if (pFontNode->GetSizeType() == FontSizeType::MINUS)
1190 mytest -= static_cast<double>(aFrac);
1191 else
1192 mytest += static_cast<double>(aFrac);
1194 mytest = ::rtl::math::round(mytest, 1);
1195 ::sax::Converter::convertDouble(sStrBuf, mytest);
1196 sStrBuf.append(GetXMLToken(XML_UNIT_PT));
1198 break;
1201 OUString sStr(sStrBuf.makeStringAndClear());
1202 AddAttribute(XML_NAMESPACE_MATH, XML_MATHSIZE, sStr);
1204 break;
1205 case TBOLD:
1206 case TITALIC:
1207 case TNBOLD:
1208 case TNITALIC:
1209 case TFIXED:
1210 case TSANS:
1211 case TSERIF:
1213 // nBold: -1 = yet undefined; 0 = false; 1 = true;
1214 // nItalic: -1 = yet undefined; 0 = false; 1 = true;
1215 // nSansSerifFixed: -1 = undefined; 0 = sans; 1 = serif; 2 = fixed;
1216 const char* pText = "normal";
1217 if (nSansSerifFixed == -1 || nSansSerifFixed == 1)
1219 pText = "normal";
1220 if (nBold == 1 && nItalic != 1)
1221 pText = "bold";
1222 else if (nBold != 1 && nItalic == 1)
1223 pText = "italic";
1224 else if (nBold == 1 && nItalic == 1)
1225 pText = "bold-italic";
1227 else if (nSansSerifFixed == 0)
1229 pText = "sans-serif";
1230 if (nBold == 1 && nItalic != 1)
1231 pText = "bold-sans-serif";
1232 else if (nBold != 1 && nItalic == 1)
1233 pText = "sans-serif-italic";
1234 else if (nBold == 1 && nItalic == 1)
1235 pText = "sans-serif-bold-italic";
1237 else if (nSansSerifFixed == 2)
1238 pText = "monospace"; // no modifiers allowed for monospace ...
1239 else
1241 SAL_WARN("starmath", "unexpected case");
1243 AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, OUString::createFromAscii(pText));
1245 break;
1246 default:
1247 break;
1250 // Wrap everything in an <mphantom> or <mstyle> element. These elements
1251 // are mrow-like, so ExportExpression doesn't need to add an explicit
1252 // <mrow> element. See #fdo 66283.
1253 SvXMLElementExport aElement(*this, XML_NAMESPACE_MATH,
1254 pNode->GetToken().eType == TPHANTOM ? XML_MPHANTOM : XML_MSTYLE,
1255 true, true);
1256 ExportExpression(pNode, nLevel, true);
1260 void SmXMLExport::ExportVerticalBrace(const SmVerticalBraceNode* pNode, int nLevel)
1262 // "[body] overbrace [script]"
1264 // Position body, overbrace and script vertically. First place the overbrace
1265 // OVER the body and then the script OVER this expression.
1267 // [script]
1268 // --[overbrace]--
1269 // XXXXXX[body]XXXXXXX
1271 // Similarly for the underbrace construction.
1273 XMLTokenEnum which;
1275 switch (pNode->GetToken().eType)
1277 case TOVERBRACE:
1278 default:
1279 which = XML_MOVER;
1280 break;
1281 case TUNDERBRACE:
1282 which = XML_MUNDER;
1283 break;
1286 SvXMLElementExport aOver1(*this, XML_NAMESPACE_MATH, which, true, true);
1287 { //Scoping
1288 // using accents will draw the over-/underbraces too close to the base
1289 // see http://www.w3.org/TR/MathML2/chapter3.html#id.3.4.5.2
1290 // also XML_ACCENT is illegal with XML_MUNDER. Thus no XML_ACCENT attribute here!
1291 SvXMLElementExport aOver2(*this, XML_NAMESPACE_MATH, which, true, true);
1292 ExportNodes(pNode->Body(), nLevel);
1293 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
1294 ExportNodes(pNode->Brace(), nLevel);
1296 ExportNodes(pNode->Script(), nLevel);
1299 void SmXMLExport::ExportMatrix(const SmNode* pNode, int nLevel)
1301 SvXMLElementExport aTable(*this, XML_NAMESPACE_MATH, XML_MTABLE, true, true);
1302 const SmMatrixNode* pMatrix = static_cast<const SmMatrixNode*>(pNode);
1303 size_t i = 0;
1304 for (sal_uInt16 y = 0; y < pMatrix->GetNumRows(); y++)
1306 SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MTR, true, true);
1307 for (sal_uInt16 x = 0; x < pMatrix->GetNumCols(); x++)
1309 if (const SmNode* pTemp = pNode->GetSubNode(i++))
1311 if (pTemp->GetType() == SmNodeType::Align && pTemp->GetToken().eType != TALIGNC)
1313 // A left or right alignment is specified on this cell,
1314 // attach the corresponding columnalign attribute.
1315 AddAttribute(XML_NAMESPACE_MATH, XML_COLUMNALIGN,
1316 pTemp->GetToken().eType == TALIGNL ? XML_LEFT : XML_RIGHT);
1318 SvXMLElementExport aCell(*this, XML_NAMESPACE_MATH, XML_MTD, true, true);
1319 ExportNodes(pTemp, nLevel + 1);
1325 void SmXMLExport::ExportNodes(const SmNode* pNode, int nLevel)
1327 if (!pNode)
1328 return;
1329 switch (pNode->GetType())
1331 case SmNodeType::Table:
1332 ExportTable(pNode, nLevel);
1333 break;
1334 case SmNodeType::Align:
1335 case SmNodeType::Bracebody:
1336 case SmNodeType::Expression:
1337 ExportExpression(pNode, nLevel);
1338 break;
1339 case SmNodeType::Line:
1340 ExportLine(pNode, nLevel);
1341 break;
1342 case SmNodeType::Text:
1343 ExportText(pNode);
1344 break;
1345 case SmNodeType::GlyphSpecial:
1346 case SmNodeType::Math:
1348 sal_Unicode cTmp = 0;
1349 const SmTextNode* pTemp = static_cast<const SmTextNode*>(pNode);
1350 if (!pTemp->GetText().isEmpty())
1351 cTmp = ConvertMathToMathML(pTemp->GetText()[0]);
1352 if (cTmp == 0)
1354 // no conversion to MathML implemented -> export it as text
1355 // thus at least it will not vanish into nothing
1356 ExportText(pNode);
1358 else
1360 switch (pNode->GetToken().eType)
1362 case TINTD:
1363 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
1364 break;
1365 default:
1366 break;
1368 //To fully handle generic MathML we need to implement the full
1369 //operator dictionary, we will generate MathML with explicit
1370 //stretchiness for now.
1371 sal_Int16 nLength = GetAttrList().getLength();
1372 bool bAddStretch = true;
1373 for (sal_Int16 i = 0; i < nLength; i++)
1375 OUString sLocalName;
1376 sal_uInt16 nPrefix = GetNamespaceMap().GetKeyByAttrName(
1377 GetAttrList().getNameByIndex(i), &sLocalName);
1379 if ((XML_NAMESPACE_MATH == nPrefix) && IsXMLToken(sLocalName, XML_STRETCHY))
1381 bAddStretch = false;
1382 break;
1385 if (bAddStretch)
1387 AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
1389 ExportMath(pNode);
1392 break;
1393 case SmNodeType::
1394 Special: //SmNodeType::Special requires some sort of Entity preservation in the XML engine.
1395 case SmNodeType::MathIdent:
1396 case SmNodeType::Place:
1397 ExportMath(pNode);
1398 break;
1399 case SmNodeType::BinHor:
1400 ExportBinaryHorizontal(pNode, nLevel);
1401 break;
1402 case SmNodeType::UnHor:
1403 ExportUnaryHorizontal(pNode, nLevel);
1404 break;
1405 case SmNodeType::Brace:
1406 ExportBrace(pNode, nLevel);
1407 break;
1408 case SmNodeType::BinVer:
1409 ExportBinaryVertical(pNode, nLevel);
1410 break;
1411 case SmNodeType::BinDiagonal:
1412 ExportBinaryDiagonal(pNode, nLevel);
1413 break;
1414 case SmNodeType::SubSup:
1415 ExportSubSupScript(pNode, nLevel);
1416 break;
1417 case SmNodeType::Root:
1418 ExportRoot(pNode, nLevel);
1419 break;
1420 case SmNodeType::Oper:
1421 ExportOperator(pNode, nLevel);
1422 break;
1423 case SmNodeType::Attribute:
1424 ExportAttributes(pNode, nLevel);
1425 break;
1426 case SmNodeType::Font:
1427 ExportFont(pNode, nLevel);
1428 break;
1429 case SmNodeType::VerticalBrace:
1430 ExportVerticalBrace(static_cast<const SmVerticalBraceNode*>(pNode), nLevel);
1431 break;
1432 case SmNodeType::Matrix:
1433 ExportMatrix(pNode, nLevel);
1434 break;
1435 case SmNodeType::Blank:
1436 ExportBlank(pNode);
1437 break;
1438 default:
1439 SAL_WARN("starmath", "Warning: failed to export a node?");
1440 break;
1444 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */