LibreOffice Module starmath (master) 1
mathmlexport.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20/*
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
25*/
26
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>
33
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>
48#include <xmloff/xmltoken.hxx>
54#include <sal/log.hxx>
55
56#include <stack>
57
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>
67
68using namespace ::com::sun::star::beans;
69using namespace ::com::sun::star::document;
70using namespace ::com::sun::star::lang;
71using namespace ::com::sun::star::uno;
72using namespace ::com::sun::star;
73using namespace ::xmloff::token;
74
75namespace
76{
77bool IsInPrivateUseArea(sal_Unicode cChar) { return 0xE000 <= cChar && cChar <= 0xF8FF; }
78
79sal_Unicode ConvertMathToMathML(sal_Unicode cChar)
80{
81 sal_Unicode cRes = cChar;
82 if (IsInPrivateUseArea(cChar))
83 {
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
86 }
87 return cRes;
88}
89}
90
92{
93 bool bRet = true;
94 uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
95
96 //Get model
97 uno::Reference<lang::XComponent> xModelComp = xModel;
98
99 bool bEmbedded = false;
100 SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(xModel);
101
102 SmDocShell* pDocShell = pModel ? static_cast<SmDocShell*>(pModel->GetObjectShell()) : nullptr;
103 if (pDocShell && SfxObjectCreateMode::EMBEDDED == pDocShell->GetCreateMode())
104 bEmbedded = true;
105
106 uno::Reference<task::XStatusIndicator> xStatusIndicator;
107 if (!bEmbedded)
108 {
109 if (pDocShell /*&& pDocShell->GetMedium()*/)
110 {
111 OSL_ENSURE(pDocShell->GetMedium() == &rMedium, "different SfxMedium found");
112
113 const SfxUnoAnyItem* pItem
114 = rMedium.GetItemSet().GetItem(SID_PROGRESS_STATUSBAR_CONTROL);
115 if (pItem)
116 pItem->GetValue() >>= xStatusIndicator;
117 }
118
119 // set progress range and start status indicator
120 if (xStatusIndicator.is())
121 {
122 sal_Int32 nProgressRange = bFlat ? 1 : 3;
123 xStatusIndicator->start(SmResId(STR_STATSTR_WRITING), nProgressRange);
124 }
125 }
126
127 static constexpr OUStringLiteral sUsePrettyPrinting(u"UsePrettyPrinting");
128 static constexpr OUStringLiteral sBaseURI(u"BaseURI");
129 static constexpr OUStringLiteral sStreamRelPath(u"StreamRelPath");
130 static constexpr OUStringLiteral sStreamName(u"StreamName");
131
132 // create XPropertySet with three properties for status indicator
133 static const comphelper::PropertyMapEntry aInfoMap[] = {
134 { sUsePrettyPrinting, 0, cppu::UnoType<bool>::get(), beans::PropertyAttribute::MAYBEVOID,
135 0 },
136 { sBaseURI, 0, ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::MAYBEVOID, 0 },
137 { sStreamRelPath, 0, ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::MAYBEVOID,
138 0 },
139 { sStreamName, 0, ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::MAYBEVOID, 0 }
140 };
141 uno::Reference<beans::XPropertySet> xInfoSet(
143
144 bool bUsePrettyPrinting
145 = bFlat || officecfg::Office::Common::Save::Document::PrettyPrinting::get();
146 xInfoSet->setPropertyValue(sUsePrettyPrinting, Any(bUsePrettyPrinting));
147
148 // Set base URI
149 xInfoSet->setPropertyValue(sBaseURI, Any(rMedium.GetBaseURL(true)));
150
151 sal_Int32 nSteps = 0;
152 if (xStatusIndicator.is())
153 xStatusIndicator->setValue(nSteps++);
154 if (!bFlat) //Storage (Package) of Stream
155 {
156 uno::Reference<embed::XStorage> xStg = rMedium.GetOutputStorage();
157 bool bOASIS = (SotStorage::GetVersion(xStg) > SOFFICE_FILEFORMAT_60);
158
159 // TODO/LATER: handle the case of embedded links gracefully
160 if (bEmbedded) //&& !pStg->IsRoot() )
161 {
162 OUString aName;
163 const SfxStringItem* pDocHierarchItem
164 = rMedium.GetItemSet().GetItem(SID_DOC_HIERARCHICALNAME);
165 if (pDocHierarchItem)
166 aName = pDocHierarchItem->GetValue();
167
168 if (!aName.isEmpty())
169 {
170 xInfoSet->setPropertyValue(sStreamRelPath, Any(aName));
171 }
172 }
173
174 if (!bEmbedded)
175 {
176 if (xStatusIndicator.is())
177 xStatusIndicator->setValue(nSteps++);
178
179 bRet = WriteThroughComponent(xStg, xModelComp, "meta.xml", xContext, xInfoSet,
180 (bOASIS ? "com.sun.star.comp.Math.XMLOasisMetaExporter"
181 : "com.sun.star.comp.Math.XMLMetaExporter"));
182 }
183 if (bRet)
184 {
185 if (xStatusIndicator.is())
186 xStatusIndicator->setValue(nSteps++);
187
188 bRet = WriteThroughComponent(xStg, xModelComp, "content.xml", xContext, xInfoSet,
189 "com.sun.star.comp.Math.XMLContentExporter");
190 }
191
192 if (bRet)
193 {
194 if (xStatusIndicator.is())
195 xStatusIndicator->setValue(nSteps++);
196
197 bRet = WriteThroughComponent(xStg, xModelComp, "settings.xml", xContext, xInfoSet,
198 (bOASIS ? "com.sun.star.comp.Math.XMLOasisSettingsExporter"
199 : "com.sun.star.comp.Math.XMLSettingsExporter"));
200 }
201 }
202 else
203 {
204 SvStream* pStream = rMedium.GetOutStream();
205 uno::Reference<io::XOutputStream> xOut(new utl::OOutputStreamWrapper(*pStream));
206
207 if (xStatusIndicator.is())
208 xStatusIndicator->setValue(nSteps++);
209
210 bRet = WriteThroughComponent(xOut, xModelComp, xContext, xInfoSet,
211 "com.sun.star.comp.Math.XMLContentExporter");
212 }
213
214 if (xStatusIndicator.is())
215 xStatusIndicator->end();
216
217 return bRet;
218}
219
221bool SmXMLExportWrapper::WriteThroughComponent(const Reference<io::XOutputStream>& xOutputStream,
222 const Reference<XComponent>& xComponent,
223 Reference<uno::XComponentContext> const& rxContext,
224 Reference<beans::XPropertySet> const& rPropSet,
225 const char* pComponentName)
226{
227 OSL_ENSURE(xOutputStream.is(), "I really need an output stream!");
228 OSL_ENSURE(xComponent.is(), "Need component!");
229 OSL_ENSURE(nullptr != pComponentName, "Need component name!");
230
231 // get component
232 Reference<xml::sax::XWriter> xSaxWriter = xml::sax::Writer::create(rxContext);
233
234 // connect XML writer to output stream
235 xSaxWriter->setOutputStream(xOutputStream);
237 xSaxWriter->setCustomEntityNames(starmathdatabase::icustomMathmlHtmlEntitiesExport);
238
239 // prepare arguments (prepend doc handler to given arguments)
240 Sequence<Any> aArgs{ Any(xSaxWriter), Any(rPropSet) };
241
242 // get filter component
243 Reference<document::XExporter> xExporter(
244 rxContext->getServiceManager()->createInstanceWithArgumentsAndContext(
245 OUString::createFromAscii(pComponentName), aArgs, rxContext),
246 UNO_QUERY);
247 OSL_ENSURE(xExporter.is(), "can't instantiate export filter component");
248 if (!xExporter.is())
249 return false;
250
251 // connect model and filter
252 xExporter->setSourceDocument(xComponent);
253
254 // filter!
255 Reference<XFilter> xFilter(xExporter, UNO_QUERY);
256 uno::Sequence<PropertyValue> aProps(0);
257 xFilter->filter(aProps);
258
259 auto pFilter = dynamic_cast<SmXMLExport*>(xFilter.get());
260 return pFilter == nullptr || pFilter->GetSuccess();
261}
262
264bool SmXMLExportWrapper::WriteThroughComponent(const Reference<embed::XStorage>& xStorage,
265 const Reference<XComponent>& xComponent,
266 const char* pStreamName,
267 Reference<uno::XComponentContext> const& rxContext,
268 Reference<beans::XPropertySet> const& rPropSet,
269 const char* pComponentName)
270{
271 OSL_ENSURE(xStorage.is(), "Need storage!");
272 OSL_ENSURE(nullptr != pStreamName, "Need stream name!");
273
274 // open stream
275 Reference<io::XStream> xStream;
276 OUString sStreamName = OUString::createFromAscii(pStreamName);
277 try
278 {
279 xStream = xStorage->openStreamElement(sStreamName, embed::ElementModes::READWRITE
280 | embed::ElementModes::TRUNCATE);
281 }
282 catch (const uno::Exception&)
283 {
284 DBG_UNHANDLED_EXCEPTION("starmath", "Can't create output stream in package");
285 return false;
286 }
287
288 uno::Reference<beans::XPropertySet> xSet(xStream, uno::UNO_QUERY);
289 static constexpr OUStringLiteral sMediaType = u"MediaType";
290 static constexpr OUStringLiteral sTextXml = u"text/xml";
291 xSet->setPropertyValue(sMediaType, Any(OUString(sTextXml)));
292
293 // all streams must be encrypted in encrypted document
294 static constexpr OUStringLiteral sUseCommonStoragePasswordEncryption
295 = u"UseCommonStoragePasswordEncryption";
296 xSet->setPropertyValue(sUseCommonStoragePasswordEncryption, Any(true));
297
298 // set Base URL
299 if (rPropSet.is())
300 {
301 rPropSet->setPropertyValue("StreamName", Any(sStreamName));
302 }
303
304 // write the stuff
305 bool bRet = WriteThroughComponent(xStream->getOutputStream(), xComponent, rxContext, rPropSet,
306 pComponentName);
307
308 return bRet;
309}
310
311SmXMLExport::SmXMLExport(const css::uno::Reference<css::uno::XComponentContext>& rContext,
312 OUString const& implementationName, SvXMLExportFlags nExportFlags)
313 : SvXMLExport(rContext, implementationName, util::MeasureUnit::INCH, XML_MATH, nExportFlags)
314 , pTree(nullptr)
315 , bSuccess(false)
316{
317}
318
319extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
320Math_XMLExporter_get_implementation(css::uno::XComponentContext* context,
321 css::uno::Sequence<css::uno::Any> const&)
322{
323 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLExporter",
324 SvXMLExportFlags::OASIS | SvXMLExportFlags::ALL));
325}
326
327extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
328Math_XMLMetaExporter_get_implementation(css::uno::XComponentContext* context,
329 css::uno::Sequence<css::uno::Any> const&)
330{
331 return cppu::acquire(
332 new SmXMLExport(context, "com.sun.star.comp.Math.XMLMetaExporter", SvXMLExportFlags::META));
333}
334
335extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
336Math_XMLOasisMetaExporter_get_implementation(css::uno::XComponentContext* context,
337 css::uno::Sequence<css::uno::Any> const&)
338{
339 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLOasisMetaExporter",
340 SvXMLExportFlags::OASIS | SvXMLExportFlags::META));
341}
342
343extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
344Math_XMLSettingsExporter_get_implementation(css::uno::XComponentContext* context,
345 css::uno::Sequence<css::uno::Any> const&)
346{
347 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLSettingsExporter",
348 SvXMLExportFlags::SETTINGS));
349}
350
351extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
352Math_XMLOasisSettingsExporter_get_implementation(css::uno::XComponentContext* context,
353 css::uno::Sequence<css::uno::Any> const&)
354{
355 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLOasisSettingsExporter",
356 SvXMLExportFlags::OASIS | SvXMLExportFlags::SETTINGS));
357}
358
359extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
360Math_XMLContentExporter_get_implementation(css::uno::XComponentContext* context,
361 css::uno::Sequence<css::uno::Any> const&)
362{
363 return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLContentExporter",
364 SvXMLExportFlags::OASIS | SvXMLExportFlags::CONTENT));
365}
366
368{
369 if (!(getExportFlags() & SvXMLExportFlags::CONTENT))
370 {
372 }
373 else
374 {
375 uno::Reference<frame::XModel> xModel = GetModel();
376 SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(xModel);
377
378 if (pModel)
379 {
380 SmDocShell* pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell());
381 pTree = pDocShell->GetFormulaTree();
382 aText = pDocShell->GetText();
383 }
384
385 GetDocHandler()->startDocument();
386
388
389 /*Add xmlns line*/
391
392 // make use of a default namespace
393 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)
395
396 rList.AddAttribute(GetNamespaceMap().GetAttrNameByKey(XML_NAMESPACE_MATH),
397 GetNamespaceMap().GetNameByKey(XML_NAMESPACE_MATH));
398
399 //I think we need something like ImplExportEntities();
401 GetDocHandler()->endDocument();
402 }
403
404 bSuccess = true;
405 return ERRCODE_NONE;
406}
407
409{
410 uno::Reference<frame::XModel> xModel = GetModel();
411 SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(xModel);
412 SmDocShell* pDocShell = pModel ? static_cast<SmDocShell*>(pModel->GetObjectShell()) : nullptr;
413 OSL_ENSURE(pDocShell, "doc shell missing");
414
415 if (pDocShell && !pDocShell->GetFormat().IsTextmode())
416 {
417 // If the Math equation is not in text mode, we attach a display="block"
418 // attribute on the <math> root. We don't do anything if it is in
419 // text mode, the default display="inline" value will be used.
421 }
422 SvXMLElementExport aEquation(*this, XML_NAMESPACE_MATH, XML_MATH, true, true);
423 std::unique_ptr<SvXMLElementExport> pSemantics;
424
425 if (!aText.isEmpty())
426 {
427 pSemantics.reset(
428 new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_SEMANTICS, true, true));
429 }
430
431 ExportNodes(pTree, 0);
432
433 if (aText.isEmpty())
434 return;
435
436 SmModule* pMod = SM_MOD();
437 sal_uInt16 nSmSyntaxVersion = pMod->GetConfig()->GetDefaultSmSyntaxVersion();
438
439 // Convert symbol names
440 if (pDocShell)
441 {
442 nSmSyntaxVersion = pDocShell->GetSmSyntaxVersion();
443 AbstractSmParser* rParser = pDocShell->GetParser();
444 bool bVal = rParser->IsExportSymbolNames();
445 rParser->SetExportSymbolNames(true);
446 auto pTmpTree = rParser->Parse(aText);
447 aText = rParser->GetText();
448 pTmpTree.reset();
449 rParser->SetExportSymbolNames(bVal);
450 }
451
452 OUStringBuffer sStrBuf(12);
453 sStrBuf.append(u"StarMath ");
454 if (nSmSyntaxVersion == 5)
455 sStrBuf.append(u"5.0");
456 else
457 sStrBuf.append(static_cast<sal_Int32>(nSmSyntaxVersion));
458
459 AddAttribute(XML_NAMESPACE_MATH, XML_ENCODING, sStrBuf.makeStringAndClear());
460 SvXMLElementExport aAnnotation(*this, XML_NAMESPACE_MATH, XML_ANNOTATION, true, false);
461 GetDocHandler()->characters(aText);
462}
463
464void SmXMLExport::GetViewSettings(Sequence<PropertyValue>& aProps)
465{
466 uno::Reference<frame::XModel> xModel = GetModel();
467 if (!xModel.is())
468 return;
469
470 SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(xModel);
471
472 if (!pModel)
473 return;
474
475 SmDocShell* pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell());
476 if (!pDocShell)
477 return;
478
479 aProps.realloc(4);
480 PropertyValue* pValue = aProps.getArray();
481 sal_Int32 nIndex = 0;
482
483 tools::Rectangle aRect(pDocShell->GetVisArea());
484
485 pValue[nIndex].Name = "ViewAreaTop";
486 pValue[nIndex++].Value <<= aRect.Top();
487
488 pValue[nIndex].Name = "ViewAreaLeft";
489 pValue[nIndex++].Value <<= aRect.Left();
490
491 pValue[nIndex].Name = "ViewAreaWidth";
492 pValue[nIndex++].Value <<= aRect.GetWidth();
493
494 pValue[nIndex].Name = "ViewAreaHeight";
495 pValue[nIndex++].Value <<= aRect.GetHeight();
496}
497
498void SmXMLExport::GetConfigurationSettings(Sequence<PropertyValue>& rProps)
499{
500 Reference<XPropertySet> xProps(GetModel(), UNO_QUERY);
501 if (!xProps.is())
502 return;
503
504 Reference<XPropertySetInfo> xPropertySetInfo = xProps->getPropertySetInfo();
505 if (!xPropertySetInfo.is())
506 return;
507
508 const Sequence<Property> aProps = xPropertySetInfo->getProperties();
509 const sal_Int32 nCount = aProps.getLength();
510 if (!nCount)
511 return;
512
513 rProps.realloc(nCount);
514 SmMathConfig* pConfig = SM_MOD()->GetConfig();
515 const bool bUsedSymbolsOnly = pConfig && pConfig->IsSaveOnlyUsedSymbols();
516
517 std::transform(aProps.begin(), aProps.end(), rProps.getArray(),
518 [bUsedSymbolsOnly, &xProps](const Property& prop) {
519 PropertyValue aRet;
520 if (prop.Name != "Formula" && prop.Name != "BasicLibraries"
521 && prop.Name != "DialogLibraries" && prop.Name != "RuntimeUID")
522 {
523 aRet.Name = prop.Name;
524 OUString aActualName(prop.Name);
525 // handle 'save used symbols only'
526 static constexpr OUStringLiteral sUserDefinedSymbolsInUse
527 = u"UserDefinedSymbolsInUse";
528 if (bUsedSymbolsOnly && prop.Name == "Symbols")
529 aActualName = sUserDefinedSymbolsInUse;
530 aRet.Value = xProps->getPropertyValue(aActualName);
531 }
532 return aRet;
533 });
534}
535
536void SmXMLExport::ExportLine(const SmNode* pNode, int nLevel) { ExportExpression(pNode, nLevel); }
537
538void SmXMLExport::ExportBinaryHorizontal(const SmNode* pNode, int nLevel)
539{
540 TG nGroup = pNode->GetToken().nGroup;
541
542 SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW, true, true);
543
544 // Unfold the binary tree structure as long as the nodes are SmBinHorNode
545 // with the same nGroup. This will reduce the number of nested <mrow>
546 // elements e.g. we only need three <mrow> levels to export
547
548 // "a*b*c*d+e*f*g*h+i*j*k*l = a*b*c*d+e*f*g*h+i*j*k*l =
549 // a*b*c*d+e*f*g*h+i*j*k*l = a*b*c*d+e*f*g*h+i*j*k*l"
550
551 // See https://www.libreoffice.org/bugzilla/show_bug.cgi?id=66081
552 ::std::stack<const SmNode*> s;
553 s.push(pNode);
554 while (!s.empty())
555 {
556 const SmNode* node = s.top();
557 s.pop();
558 if (node->GetType() != SmNodeType::BinHor || node->GetToken().nGroup != nGroup)
559 {
560 ExportNodes(node, nLevel + 1);
561 continue;
562 }
563 const SmBinHorNode* binNode = static_cast<const SmBinHorNode*>(node);
564 s.push(binNode->RightOperand());
565 s.push(binNode->Symbol());
566 s.push(binNode->LeftOperand());
567 }
568}
569
570void SmXMLExport::ExportUnaryHorizontal(const SmNode* pNode, int nLevel)
571{
572 ExportExpression(pNode, nLevel);
573}
574
575void SmXMLExport::ExportExpression(const SmNode* pNode, int nLevel,
576 bool bNoMrowContainer /*=false*/)
577{
578 std::unique_ptr<SvXMLElementExport> pRow;
579 size_t nSize = pNode->GetNumSubNodes();
580
581 // #i115443: nodes of type expression always need to be grouped with mrow statement
582 if (!bNoMrowContainer && (nSize > 1 || pNode->GetType() == SmNodeType::Expression))
583 pRow.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MROW, true, true));
584
585 for (size_t i = 0; i < nSize; ++i)
586 {
587 if (const SmNode* pTemp = pNode->GetSubNode(i))
588 ExportNodes(pTemp, nLevel + 1);
589 }
590}
591
592void SmXMLExport::ExportBinaryVertical(const SmNode* pNode, int nLevel)
593{
594 assert(pNode->GetNumSubNodes() == 3);
595 const SmNode* pNum = pNode->GetSubNode(0);
596 const SmNode* pDenom = pNode->GetSubNode(2);
597 if (pNum->GetType() == SmNodeType::Align && pNum->GetToken().eType != TALIGNC)
598 {
599 // A left or right alignment is specified on the numerator:
600 // attach the corresponding numalign attribute.
602 pNum->GetToken().eType == TALIGNL ? XML_LEFT : XML_RIGHT);
603 }
604 if (pDenom->GetType() == SmNodeType::Align && pDenom->GetToken().eType != TALIGNC)
605 {
606 // A left or right alignment is specified on the denominator:
607 // attach the corresponding denomalign attribute.
609 pDenom->GetToken().eType == TALIGNL ? XML_LEFT : XML_RIGHT);
610 }
611 SvXMLElementExport aFraction(*this, XML_NAMESPACE_MATH, XML_MFRAC, true, true);
612 ExportNodes(pNum, nLevel);
613 ExportNodes(pDenom, nLevel);
614}
615
616void SmXMLExport::ExportBinaryDiagonal(const SmNode* pNode, int nLevel)
617{
618 assert(pNode->GetNumSubNodes() == 3);
619
620 if (pNode->GetToken().eType == TWIDESLASH)
621 {
622 // wideslash
623 // export the node as <mfrac bevelled="true">
625 SvXMLElementExport aFraction(*this, XML_NAMESPACE_MATH, XML_MFRAC, true, true);
626 ExportNodes(pNode->GetSubNode(0), nLevel);
627 ExportNodes(pNode->GetSubNode(1), nLevel);
628 }
629 else
630 {
631 // widebslash
632 // We can not use <mfrac> to a backslash, so just use <mo></mo>
633 SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW, true, true);
634
635 ExportNodes(pNode->GetSubNode(0), nLevel);
636
637 { // Scoping for <mo> creation
638 SvXMLElementExport aMo(*this, XML_NAMESPACE_MATH, XML_MO, true, true);
639 GetDocHandler()->characters(OUStringChar(MS_BACKSLASH));
640 }
641
642 ExportNodes(pNode->GetSubNode(1), nLevel);
643 }
644}
645
646void SmXMLExport::ExportTable(const SmNode* pNode, int nLevel)
647{
648 std::unique_ptr<SvXMLElementExport> pTable;
649
650 size_t nSize = pNode->GetNumSubNodes();
651
652 //If the list ends in newline then the last entry has
653 //no subnodes, the newline is superfluous so we just drop
654 //the last node, inclusion would create a bad MathML
655 //table
656 if (nSize >= 1)
657 {
658 const SmNode* pLine = pNode->GetSubNode(nSize - 1);
659 if (pLine->GetType() == SmNodeType::Line && pLine->GetNumSubNodes() == 1
660 && pLine->GetSubNode(0) != nullptr
661 && pLine->GetSubNode(0)->GetToken().eType == TNEWLINE)
662 --nSize;
663 }
664
665 // try to avoid creating a mtable element when the formula consists only
666 // of a single output line
667 if (nLevel || (nSize > 1))
668 pTable.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTABLE, true, true));
669
670 for (size_t i = 0; i < nSize; ++i)
671 {
672 if (const SmNode* pTemp = pNode->GetSubNode(i))
673 {
674 std::unique_ptr<SvXMLElementExport> pRow;
675 std::unique_ptr<SvXMLElementExport> pCell;
676 if (pTable)
677 {
678 pRow.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTR, true, true));
679 SmTokenType eAlign = TALIGNC;
680 if (pTemp->GetType() == SmNodeType::Align)
681 {
682 // For Binom() and Stack() constructions, the SmNodeType::Align nodes
683 // are direct children.
684 // binom{alignl ...}{alignr ...} and
685 // stack{alignl ... ## alignr ... ## ...}
686 eAlign = pTemp->GetToken().eType;
687 }
688 else if (pTemp->GetType() == SmNodeType::Line && pTemp->GetNumSubNodes() == 1
689 && pTemp->GetSubNode(0)
690 && pTemp->GetSubNode(0)->GetType() == SmNodeType::Align)
691 {
692 // For the Table() construction, the SmNodeType::Align node is a child
693 // of an SmNodeType::Line node.
694 // alignl ... newline alignr ... newline ...
695 eAlign = pTemp->GetSubNode(0)->GetToken().eType;
696 }
697 if (eAlign != TALIGNC)
698 {
699 // If a left or right alignment is specified on this line,
700 // attach the corresponding columnalign attribute.
702 eAlign == TALIGNL ? XML_LEFT : XML_RIGHT);
703 }
704 pCell.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTD, true, true));
705 }
706 ExportNodes(pTemp, nLevel + 1);
707 }
708 }
709}
710
712{
713 const SmTextNode* pTemp = static_cast<const SmTextNode*>(pNode);
714 std::unique_ptr<SvXMLElementExport> pMath;
715
716 if (pNode->GetType() == SmNodeType::Math || pNode->GetType() == SmNodeType::GlyphSpecial)
717 {
718 // Export SmNodeType::Math and SmNodeType::GlyphSpecial symbols as <mo> elements
719 pMath.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MO, true, false));
720 }
721 else if (pNode->GetType() == SmNodeType::Special)
722 {
723 bool bIsItalic = IsItalic(pNode->GetFont());
724 if (!bIsItalic)
726 pMath.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false));
727 }
728 else
729 {
730 // Export SmNodeType::MathIdent and SmNodeType::Place symbols as <mi> elements:
731 // - These math symbols should not be drawn slanted. Hence we should
732 // attach a mathvariant="normal" attribute to single-char <mi> elements
733 // that are not mathematical alphanumeric symbol. For simplicity and to
734 // work around browser limitations, we always attach such an attribute.
735 // - The MathML specification suggests to use empty <mi> elements as
736 // placeholders but they won't be visible in most MathML rendering
737 // engines so let's use an empty square for SmNodeType::Place instead.
739 pMath.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false));
740 }
741 sal_Unicode nArse = pTemp->GetText()[0];
742 sal_Unicode cTmp = ConvertMathToMathML(nArse);
743 if (cTmp != 0)
744 nArse = cTmp;
745 OSL_ENSURE(nArse != 0xffff, "Non existent symbol");
746 GetDocHandler()->characters(OUString(nArse));
747}
748
750{
751 std::unique_ptr<SvXMLElementExport> pText;
752 const SmTextNode* pTemp = static_cast<const SmTextNode*>(pNode);
753 switch (pNode->GetToken().eType)
754 {
755 default:
756 case TIDENT:
757 {
758 //Note that we change the fontstyle to italic for strings that
759 //are italic and longer than a single character.
760 bool bIsItalic = IsItalic(pTemp->GetFont());
761 if ((pTemp->GetText().getLength() > 1) && bIsItalic)
763 else if ((pTemp->GetText().getLength() == 1) && !bIsItalic)
765 pText.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false));
766 break;
767 }
768 case TNUMBER:
769 pText.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MN, true, false));
770 break;
771 case TTEXT:
772 pText.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTEXT, true, false));
773 break;
774 }
775 GetDocHandler()->characters(pTemp->GetText());
776}
777
779{
780 const SmBlankNode* pTemp = static_cast<const SmBlankNode*>(pNode);
784
785 if (pTemp->GetBlankNum() != 0)
786 {
787 // Attach a width attribute. We choose the (somewhat arbitrary) values
788 // ".5em" for a small gap '`' and "2em" for a large gap '~'.
789 // (see SmBlankNode::IncreaseBy for how pTemp->mnNum is set).
790 OUStringBuffer sStrBuf;
791 ::sax::Converter::convertDouble(sStrBuf, pTemp->GetBlankNum() * .5);
792 sStrBuf.append("em");
793 AddAttribute(XML_NAMESPACE_MATH, XML_WIDTH, sStrBuf.makeStringAndClear());
794 }
795
796 SvXMLElementExport aTextExport(*this, XML_NAMESPACE_MATH, XML_MSPACE, true, false);
797
798 GetDocHandler()->characters(OUString());
799}
800
801void SmXMLExport::ExportSubSupScript(const SmNode* pNode, int nLevel)
802{
803 const SmNode* pSub = nullptr;
804 const SmNode* pSup = nullptr;
805 const SmNode* pCSub = nullptr;
806 const SmNode* pCSup = nullptr;
807 const SmNode* pLSub = nullptr;
808 const SmNode* pLSup = nullptr;
809 std::unique_ptr<SvXMLElementExport> pThing2;
810
811 //if we have prescripts at all then we must use the tensor notation
812
813 //This is one of those excellent locations where scope is vital to
814 //arrange the construction and destruction of the element helper
815 //classes correctly
816 pLSub = pNode->GetSubNode(LSUB + 1);
817 pLSup = pNode->GetSubNode(LSUP + 1);
818 if (pLSub || pLSup)
819 {
820 SvXMLElementExport aMultiScripts(*this, XML_NAMESPACE_MATH, XML_MMULTISCRIPTS, true, true);
821
822 if (nullptr != (pCSub = pNode->GetSubNode(CSUB + 1))
823 && nullptr != (pCSup = pNode->GetSubNode(CSUP + 1)))
824 {
825 pThing2.reset(
826 new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MUNDEROVER, true, true));
827 }
828 else if (nullptr != (pCSub = pNode->GetSubNode(CSUB + 1)))
829 {
830 pThing2.reset(
831 new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MUNDER, true, true));
832 }
833 else if (nullptr != (pCSup = pNode->GetSubNode(CSUP + 1)))
834 {
835 pThing2.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MOVER, true, true));
836 }
837
838 ExportNodes(pNode->GetSubNode(0), nLevel + 1); //Main Term
839
840 if (pCSub)
841 ExportNodes(pCSub, nLevel + 1);
842 if (pCSup)
843 ExportNodes(pCSup, nLevel + 1);
844 pThing2.reset();
845
846 pSub = pNode->GetSubNode(RSUB + 1);
847 pSup = pNode->GetSubNode(RSUP + 1);
848 if (pSub || pSup)
849 {
850 if (pSub)
851 ExportNodes(pSub, nLevel + 1);
852 else
853 {
854 SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE, true, true);
855 }
856 if (pSup)
857 ExportNodes(pSup, nLevel + 1);
858 else
859 {
860 SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE, true, true);
861 }
862 }
863
864 //Separator element between suffix and prefix sub/sup pairs
865 {
866 SvXMLElementExport aPrescripts(*this, XML_NAMESPACE_MATH, XML_MPRESCRIPTS, true, true);
867 }
868
869 if (pLSub)
870 ExportNodes(pLSub, nLevel + 1);
871 else
872 {
873 SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE, true, true);
874 }
875 if (pLSup)
876 ExportNodes(pLSup, nLevel + 1);
877 else
878 {
879 SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE, true, true);
880 }
881 }
882 else
883 {
884 std::unique_ptr<SvXMLElementExport> pThing;
885 if (nullptr != (pSub = pNode->GetSubNode(RSUB + 1))
886 && nullptr != (pSup = pNode->GetSubNode(RSUP + 1)))
887 {
888 pThing.reset(
889 new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSUBSUP, true, true));
890 }
891 else if (nullptr != (pSub = pNode->GetSubNode(RSUB + 1)))
892 {
893 pThing.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSUB, true, true));
894 }
895 else if (nullptr != (pSup = pNode->GetSubNode(RSUP + 1)))
896 {
897 pThing.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSUP, true, true));
898 }
899
900 if (nullptr != (pCSub = pNode->GetSubNode(CSUB + 1))
901 && nullptr != (pCSup = pNode->GetSubNode(CSUP + 1)))
902 {
903 pThing2.reset(
904 new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MUNDEROVER, true, true));
905 }
906 else if (nullptr != (pCSub = pNode->GetSubNode(CSUB + 1)))
907 {
908 pThing2.reset(
909 new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MUNDER, true, true));
910 }
911 else if (nullptr != (pCSup = pNode->GetSubNode(CSUP + 1)))
912 {
913 pThing2.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MOVER, true, true));
914 }
915 ExportNodes(pNode->GetSubNode(0), nLevel + 1); //Main Term
916
917 if (pCSub)
918 ExportNodes(pCSub, nLevel + 1);
919 if (pCSup)
920 ExportNodes(pCSup, nLevel + 1);
921 pThing2.reset();
922
923 if (pSub)
924 ExportNodes(pSub, nLevel + 1);
925 if (pSup)
926 ExportNodes(pSup, nLevel + 1);
927 pThing.reset();
928 }
929}
930
931void SmXMLExport::ExportBrace(const SmNode* pNode, int nLevel)
932{
933 const SmNode* pTemp;
934 const SmNode* pLeft = pNode->GetSubNode(0);
935 const SmNode* pRight = pNode->GetSubNode(2);
936
937 // This used to generate <mfenced> or <mrow>+<mo> elements according to
938 // the stretchiness of fences. The MathML recommendation defines an
939 // <mrow>+<mo> construction that is equivalent to the <mfenced> element:
940 // http://www.w3.org/TR/MathML3/chapter3.html#presm.mfenced
941 // To simplify our code and avoid issues with mfenced implementations in
942 // MathML rendering engines, we now always generate <mrow>+<mo> elements.
943 // See #fdo 66282.
944
945 // <mrow>
946 SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW, true, true);
947
948 // <mo fence="true"> opening-fence </mo>
949 if (pLeft && (pLeft->GetToken().eType != TNONE))
950 {
953 if (pNode->GetScaleMode() == SmScaleMode::Height)
955 else
957 ExportNodes(pLeft, nLevel + 1);
958 }
959
960 if (nullptr != (pTemp = pNode->GetSubNode(1)))
961 {
962 // <mrow>
963 SvXMLElementExport aRowExport(*this, XML_NAMESPACE_MATH, XML_MROW, true, true);
964 ExportNodes(pTemp, nLevel + 1);
965 // </mrow>
966 }
967
968 // <mo fence="true"> closing-fence </mo>
969 if (pRight && (pRight->GetToken().eType != TNONE))
970 {
973 if (pNode->GetScaleMode() == SmScaleMode::Height)
975 else
977 ExportNodes(pRight, nLevel + 1);
978 }
979
980 // </mrow>
981}
982
983void SmXMLExport::ExportRoot(const SmNode* pNode, int nLevel)
984{
985 if (pNode->GetSubNode(0))
986 {
987 SvXMLElementExport aRoot(*this, XML_NAMESPACE_MATH, XML_MROOT, true, true);
988 ExportNodes(pNode->GetSubNode(2), nLevel + 1);
989 ExportNodes(pNode->GetSubNode(0), nLevel + 1);
990 }
991 else
992 {
993 SvXMLElementExport aSqrt(*this, XML_NAMESPACE_MATH, XML_MSQRT, true, true);
994 ExportNodes(pNode->GetSubNode(2), nLevel + 1);
995 }
996}
997
998void SmXMLExport::ExportOperator(const SmNode* pNode, int nLevel)
999{
1000 /*we need to either use content or font and size attributes
1001 *here*/
1002 SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW, true, true);
1003 ExportNodes(pNode->GetSubNode(0), nLevel + 1);
1004 ExportNodes(pNode->GetSubNode(1), nLevel + 1);
1005}
1006
1007void SmXMLExport::ExportAttributes(const SmNode* pNode, int nLevel)
1008{
1009 std::unique_ptr<SvXMLElementExport> pElement;
1010
1011 if (pNode->GetToken().eType == TUNDERLINE)
1012 {
1014 pElement.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MUNDER, true, true));
1015 }
1016 else if (pNode->GetToken().eType == TOVERSTRIKE)
1017 {
1018 // export as <menclose notation="horizontalstrike">
1020 pElement.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MENCLOSE, true, true));
1021 }
1022 else
1023 {
1025 pElement.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MOVER, true, true));
1026 }
1027
1028 ExportNodes(pNode->GetSubNode(1), nLevel + 1);
1029 switch (pNode->GetToken().eType)
1030 {
1031 case TOVERLINE:
1032 {
1033 //proper entity support required
1034 SvXMLElementExport aMath(*this, XML_NAMESPACE_MATH, XML_MO, true, true);
1035 static constexpr OUStringLiteral nArse = u"\u00AF";
1036 GetDocHandler()->characters(nArse);
1037 }
1038 break;
1039 case TUNDERLINE:
1040 {
1041 //proper entity support required
1042 SvXMLElementExport aMath(*this, XML_NAMESPACE_MATH, XML_MO, true, true);
1043 static constexpr OUStringLiteral nArse = u"\u0332";
1044 GetDocHandler()->characters(nArse);
1045 }
1046 break;
1047 case TOVERSTRIKE:
1048 break;
1049 case TWIDETILDE:
1050 case TWIDEHAT:
1051 case TWIDEVEC:
1052 case TWIDEHARPOON:
1053 {
1054 // make these wide accents stretchy
1056 ExportNodes(pNode->GetSubNode(0), nLevel + 1);
1057 }
1058 break;
1059 default:
1060 ExportNodes(pNode->GetSubNode(0), nLevel + 1);
1061 break;
1062 }
1063}
1064
1066{
1067 return eType == TBOLD || eType == TNBOLD || eType == TITALIC || eType == TNITALIC
1068 || eType == TSANS || eType == TSERIF || eType == TFIXED;
1069}
1070
1071void SmXMLExport::ExportFont(const SmNode* pNode, int nLevel)
1072{
1073 // gather the mathvariant attribute relevant data from all
1074 // successively following SmFontNodes...
1075
1076 int nBold = -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true;
1077 int nItalic = -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true;
1078 int nSansSerifFixed = -1;
1079 SmTokenType eNodeType = TUNKNOWN;
1080
1081 for (;;)
1082 {
1083 eNodeType = pNode->GetToken().eType;
1084 if (!lcl_HasEffectOnMathvariant(eNodeType))
1085 break;
1086 switch (eNodeType)
1087 {
1088 case TBOLD:
1089 nBold = 1;
1090 break;
1091 case TNBOLD:
1092 nBold = 0;
1093 break;
1094 case TITALIC:
1095 nItalic = 1;
1096 break;
1097 case TNITALIC:
1098 nItalic = 0;
1099 break;
1100 case TSANS:
1101 nSansSerifFixed = 0;
1102 break;
1103 case TSERIF:
1104 nSansSerifFixed = 1;
1105 break;
1106 case TFIXED:
1107 nSansSerifFixed = 2;
1108 break;
1109 default:
1110 SAL_WARN("starmath", "unexpected case");
1111 }
1112 // According to the parser every node that is to be evaluated here
1113 // has a single non-zero subnode at index 1!! Thus we only need to check
1114 // that single node for follow-up nodes that have an effect on the attribute.
1115 if (pNode->GetNumSubNodes() > 1 && pNode->GetSubNode(1)
1117 {
1118 pNode = pNode->GetSubNode(1);
1119 }
1120 else
1121 break;
1122 }
1123
1124 sal_uInt32 nc;
1125 switch (pNode->GetToken().eType)
1126 {
1127 case TPHANTOM:
1128 // No attribute needed. An <mphantom> element will be used below.
1129 break;
1130 case TMATHMLCOL:
1131 {
1132 nc = pNode->GetToken().cMathChar.toUInt32(16);
1133 const OUString& sssStr = starmathdatabase::Identify_Color_MATHML(nc).aIdent;
1135 }
1136 break;
1137 case TRGB:
1138 case TRGBA:
1139 case THEX:
1140 case THTMLCOL:
1141 case TDVIPSNAMESCOL:
1142 case TICONICCOL:
1143 {
1144 nc = pNode->GetToken().cMathChar.toUInt32(16);
1145 OUString ssStr("#" + Color(ColorTransparency, nc).AsRGBHEXString());
1147 }
1148 break;
1149 case TSIZE:
1150 {
1151 const SmFontNode* pFontNode = static_cast<const SmFontNode*>(pNode);
1152 const Fraction& aFrac = pFontNode->GetSizeParameter();
1153
1154 OUStringBuffer sStrBuf;
1155 switch (pFontNode->GetSizeType())
1156 {
1159 static_cast<double>(aFrac * Fraction(100, 1)));
1160 sStrBuf.append('%');
1161 break;
1164 static_cast<double>(Fraction(100, 1) / aFrac));
1165 sStrBuf.append('%');
1166 break;
1168 ::sax::Converter::convertDouble(sStrBuf, static_cast<double>(aFrac));
1169 sStrBuf.append(GetXMLToken(XML_UNIT_PT));
1170 break;
1171 default:
1172 {
1173 //The problem here is that the wheels fall off because
1174 //font size is stored in 100th's of a mm not pts, and
1175 //rounding errors take their toll on the original
1176 //value specified in points.
1177
1178 //Must fix StarMath to retain the original pt values
1179 double mytest
1180 = o3tl::convert<double>(pFontNode->GetFont().GetFontSize().Height(),
1182
1183 if (pFontNode->GetSizeType() == FontSizeType::MINUS)
1184 mytest -= static_cast<double>(aFrac);
1185 else
1186 mytest += static_cast<double>(aFrac);
1187
1188 mytest = ::rtl::math::round(mytest, 1);
1189 ::sax::Converter::convertDouble(sStrBuf, mytest);
1190 sStrBuf.append(GetXMLToken(XML_UNIT_PT));
1191 }
1192 break;
1193 }
1194
1195 OUString sStr(sStrBuf.makeStringAndClear());
1197 }
1198 break;
1199 case TBOLD:
1200 case TITALIC:
1201 case TNBOLD:
1202 case TNITALIC:
1203 case TFIXED:
1204 case TSANS:
1205 case TSERIF:
1206 {
1207 // nBold: -1 = yet undefined; 0 = false; 1 = true;
1208 // nItalic: -1 = yet undefined; 0 = false; 1 = true;
1209 // nSansSerifFixed: -1 = undefined; 0 = sans; 1 = serif; 2 = fixed;
1210 const char* pText = "normal";
1211 if (nSansSerifFixed == -1 || nSansSerifFixed == 1)
1212 {
1213 pText = "normal";
1214 if (nBold == 1 && nItalic != 1)
1215 pText = "bold";
1216 else if (nBold != 1 && nItalic == 1)
1217 pText = "italic";
1218 else if (nBold == 1 && nItalic == 1)
1219 pText = "bold-italic";
1220 }
1221 else if (nSansSerifFixed == 0)
1222 {
1223 pText = "sans-serif";
1224 if (nBold == 1 && nItalic != 1)
1225 pText = "bold-sans-serif";
1226 else if (nBold != 1 && nItalic == 1)
1227 pText = "sans-serif-italic";
1228 else if (nBold == 1 && nItalic == 1)
1229 pText = "sans-serif-bold-italic";
1230 }
1231 else if (nSansSerifFixed == 2)
1232 pText = "monospace"; // no modifiers allowed for monospace ...
1233 else
1234 {
1235 SAL_WARN("starmath", "unexpected case");
1236 }
1237 AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, OUString::createFromAscii(pText));
1238 }
1239 break;
1240 default:
1241 break;
1242 }
1243 {
1244 // Wrap everything in an <mphantom> or <mstyle> element. These elements
1245 // are mrow-like, so ExportExpression doesn't need to add an explicit
1246 // <mrow> element. See #fdo 66283.
1247 SvXMLElementExport aElement(*this, XML_NAMESPACE_MATH,
1249 true, true);
1250 ExportExpression(pNode, nLevel, true);
1251 }
1252}
1253
1255{
1256 // "[body] overbrace [script]"
1257
1258 // Position body, overbrace and script vertically. First place the overbrace
1259 // OVER the body and then the script OVER this expression.
1260
1261 // [script]
1262 // --[overbrace]--
1263 // XXXXXX[body]XXXXXXX
1264
1265 // Similarly for the underbrace construction.
1266
1267 XMLTokenEnum which;
1268
1269 switch (pNode->GetToken().eType)
1270 {
1271 case TOVERBRACE:
1272 default:
1273 which = XML_MOVER;
1274 break;
1275 case TUNDERBRACE:
1276 which = XML_MUNDER;
1277 break;
1278 }
1279
1280 SvXMLElementExport aOver1(*this, XML_NAMESPACE_MATH, which, true, true);
1281 { //Scoping
1282 // using accents will draw the over-/underbraces too close to the base
1283 // see http://www.w3.org/TR/MathML2/chapter3.html#id.3.4.5.2
1284 // also XML_ACCENT is illegal with XML_MUNDER. Thus no XML_ACCENT attribute here!
1285 SvXMLElementExport aOver2(*this, XML_NAMESPACE_MATH, which, true, true);
1286 ExportNodes(pNode->Body(), nLevel);
1288 ExportNodes(pNode->Brace(), nLevel);
1289 }
1290 ExportNodes(pNode->Script(), nLevel);
1291}
1292
1293void SmXMLExport::ExportMatrix(const SmNode* pNode, int nLevel)
1294{
1295 SvXMLElementExport aTable(*this, XML_NAMESPACE_MATH, XML_MTABLE, true, true);
1296 const SmMatrixNode* pMatrix = static_cast<const SmMatrixNode*>(pNode);
1297 size_t i = 0;
1298 for (sal_uInt16 y = 0; y < pMatrix->GetNumRows(); y++)
1299 {
1300 SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MTR, true, true);
1301 for (sal_uInt16 x = 0; x < pMatrix->GetNumCols(); x++)
1302 {
1303 if (const SmNode* pTemp = pNode->GetSubNode(i++))
1304 {
1305 if (pTemp->GetType() == SmNodeType::Align && pTemp->GetToken().eType != TALIGNC)
1306 {
1307 // A left or right alignment is specified on this cell,
1308 // attach the corresponding columnalign attribute.
1310 pTemp->GetToken().eType == TALIGNL ? XML_LEFT : XML_RIGHT);
1311 }
1312 SvXMLElementExport aCell(*this, XML_NAMESPACE_MATH, XML_MTD, true, true);
1313 ExportNodes(pTemp, nLevel + 1);
1314 }
1315 }
1316 }
1317}
1318
1319void SmXMLExport::ExportNodes(const SmNode* pNode, int nLevel)
1320{
1321 if (!pNode)
1322 return;
1323 switch (pNode->GetType())
1324 {
1325 case SmNodeType::Table:
1326 ExportTable(pNode, nLevel);
1327 break;
1328 case SmNodeType::Align:
1331 ExportExpression(pNode, nLevel);
1332 break;
1333 case SmNodeType::Line:
1334 ExportLine(pNode, nLevel);
1335 break;
1336 case SmNodeType::Text:
1337 ExportText(pNode);
1338 break;
1340 case SmNodeType::Math:
1341 {
1342 sal_Unicode cTmp = 0;
1343 const SmTextNode* pTemp = static_cast<const SmTextNode*>(pNode);
1344 if (!pTemp->GetText().isEmpty())
1345 cTmp = ConvertMathToMathML(pTemp->GetText()[0]);
1346 if (cTmp == 0)
1347 {
1348 // no conversion to MathML implemented -> export it as text
1349 // thus at least it will not vanish into nothing
1350 ExportText(pNode);
1351 }
1352 else
1353 {
1354 switch (pNode->GetToken().eType)
1355 {
1356 case TINTD:
1358 break;
1359 default:
1360 break;
1361 }
1362 //To fully handle generic MathML we need to implement the full
1363 //operator dictionary, we will generate MathML with explicit
1364 //stretchiness for now.
1365 sal_Int16 nLength = GetAttrList().getLength();
1366 bool bAddStretch = true;
1367 for (sal_Int16 i = 0; i < nLength; i++)
1368 {
1369 OUString sLocalName;
1370 sal_uInt16 nPrefix = GetNamespaceMap().GetKeyByAttrName(
1371 GetAttrList().getNameByIndex(i), &sLocalName);
1372
1373 if ((XML_NAMESPACE_MATH == nPrefix) && IsXMLToken(sLocalName, XML_STRETCHY))
1374 {
1375 bAddStretch = false;
1376 break;
1377 }
1378 }
1379 if (bAddStretch)
1380 {
1382 }
1383 ExportMath(pNode);
1384 }
1385 }
1386 break;
1387 case SmNodeType::
1388 Special: //SmNodeType::Special requires some sort of Entity preservation in the XML engine.
1390 case SmNodeType::Place:
1391 ExportMath(pNode);
1392 break;
1393 case SmNodeType::BinHor:
1394 ExportBinaryHorizontal(pNode, nLevel);
1395 break;
1396 case SmNodeType::UnHor:
1397 ExportUnaryHorizontal(pNode, nLevel);
1398 break;
1399 case SmNodeType::Brace:
1400 ExportBrace(pNode, nLevel);
1401 break;
1402 case SmNodeType::BinVer:
1403 ExportBinaryVertical(pNode, nLevel);
1404 break;
1406 ExportBinaryDiagonal(pNode, nLevel);
1407 break;
1408 case SmNodeType::SubSup:
1409 ExportSubSupScript(pNode, nLevel);
1410 break;
1411 case SmNodeType::Root:
1412 ExportRoot(pNode, nLevel);
1413 break;
1414 case SmNodeType::Oper:
1415 ExportOperator(pNode, nLevel);
1416 break;
1418 ExportAttributes(pNode, nLevel);
1419 break;
1420 case SmNodeType::Font:
1421 ExportFont(pNode, nLevel);
1422 break;
1424 ExportVerticalBrace(static_cast<const SmVerticalBraceNode*>(pNode), nLevel);
1425 break;
1426 case SmNodeType::Matrix:
1427 ExportMatrix(pNode, nLevel);
1428 break;
1429 case SmNodeType::Blank:
1430 ExportBlank(pNode);
1431 break;
1432 default:
1433 SAL_WARN("starmath", "Warning: failed to export a node?");
1434 break;
1435 }
1436}
1437
1438/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Reference< XInputStream > xStream
constexpr OUStringLiteral sMediaType
virtual void SetExportSymbolNames(bool bVal)=0
virtual std::unique_ptr< SmTableNode > Parse(const OUString &rBuffer)=0
Parse rBuffer to formula tree.
virtual bool IsExportSymbolNames() const =0
virtual const OUString & GetText() const =0
const OUString & GetValue() const
SfxObjectShell * GetObjectShell() const
const SfxPoolItem * GetItem(sal_uInt16 nWhich, bool bSearchInParent=true) const
OUString GetBaseURL(bool bForSaving=false)
SfxItemSet & GetItemSet() const
SvStream * GetOutStream()
css::uno::Reference< css::embed::XStorage > GetOutputStorage()
SfxMedium * GetMedium() const
virtual tools::Rectangle GetVisArea(sal_uInt16 nAspect) const
SfxObjectCreateMode GetCreateMode() const
const css::uno::Any & GetValue() const
constexpr tools::Long Height() const
Binary horizontal node.
Definition: node.hxx:1368
const SmNode * Symbol() const
Returns the node containing the data of the binary operator.
Definition: node.hxx:1393
const SmNode * LeftOperand() const
Returns the node containing the data of the left operand.
Definition: node.hxx:1400
const SmNode * RightOperand() const
Returns the node containing the data of the right operand.
Definition: node.hxx:1407
Node for whitespace.
Definition: node.hxx:2056
sal_uInt16 GetBlankNum() const
Definition: node.hxx:2066
const SmTableNode * GetFormulaTree() const
Definition: document.hxx:176
const OUString & GetText() const
Definition: document.hxx:170
const SmFormat & GetFormat() const
Definition: document.hxx:172
sal_uInt16 GetSmSyntaxVersion() const
Definition: document.hxx:178
AbstractSmParser * GetParser()
Definition: document.hxx:175
Font node.
Definition: node.hxx:1932
const Fraction & GetSizeParameter() const
Returns the font size.
Definition: node.hxx:1956
FontSizeType GetSizeType() const
Returns the font size type.
Definition: node.hxx:1963
bool IsTextmode() const
Definition: format.hxx:126
bool IsSaveOnlyUsedSymbols() const
Definition: cfgitem.cxx:1230
sal_uInt16 GetDefaultSmSyntaxVersion() const
Definition: cfgitem.cxx:1244
Matrix node.
Definition: node.hxx:2000
sal_uInt16 GetNumRows() const
Gets the number of rows of the matrix.
Definition: node.hxx:2014
sal_uInt16 GetNumCols() const
Gets the number of columns of the matrix.
Definition: node.hxx:2020
SmMathConfig * GetConfig()
Definition: smmod.cxx:162
Definition: node.hxx:125
SmScaleMode GetScaleMode() const
Gets the scale mode.
Definition: node.hxx:362
virtual size_t GetNumSubNodes() const =0
Gets the number of subnodes.
const SmToken & GetToken() const
Gets the token.
Definition: node.hxx:387
virtual SmNode * GetSubNode(size_t nIndex)=0
Gets the subnode of index nIndex.
SmNodeType GetType() const
Gets the node type.
Definition: node.hxx:379
const SmFace & GetFont() const
Gets the font.
Definition: node.hxx:227
Text node.
Definition: node.hxx:747
const OUString & GetText() const
Gets the node text.
Definition: node.hxx:782
Node for vertical brace construction.
Definition: node.hxx:1750
const SmMathSymbolNode * Brace() const
Returns the node containing the data of the brace.
Definition: node.hxx:1767
const SmNode * Script() const
Returns the node containing the data of what is in the brace.
Definition: node.hxx:1780
const SmNode * Body() const
Returns the node containing the data of what the brace is pointing for.
Definition: node.hxx:1760
bool WriteThroughComponent(const css::uno::Reference< css::io::XOutputStream > &xOutputStream, const css::uno::Reference< css::lang::XComponent > &xComponent, css::uno::Reference< css::uno::XComponentContext > const &rxContext, css::uno::Reference< css::beans::XPropertySet > const &rPropSet, const char *pComponentName)
css::uno::Reference< css::frame::XModel > xModel
bool Export(SfxMedium &rMedium)
void ExportBinaryHorizontal(const SmNode *pNode, int nLevel)
void ExportTable(const SmNode *pNode, int nLevel)
ErrCode exportDoc(enum ::xmloff::token::XMLTokenEnum eClass=::xmloff::token::XML_TOKEN_INVALID) override
bool GetSuccess() const
void ExportText(const SmNode *pNode)
const SmNode * pTree
void ExportLine(const SmNode *pNode, int nLevel)
void ExportBinaryVertical(const SmNode *pNode, int nLevel)
void ExportVerticalBrace(const SmVerticalBraceNode *pNode, int nLevel)
SmXMLExport(const css::uno::Reference< css::uno::XComponentContext > &rContext, OUString const &implementationName, SvXMLExportFlags nExportFlags)
void ExportNodes(const SmNode *pNode, int nLevel)
void ExportMatrix(const SmNode *pNode, int nLevel)
virtual void GetConfigurationSettings(css::uno::Sequence< css::beans::PropertyValue > &aProps) override
void ExportExpression(const SmNode *pNode, int nLevel, bool bNoMrowContainer=false)
void ExportBlank(const SmNode *pNode)
void ExportContent_() override
void ExportBrace(const SmNode *pNode, int nLevel)
void ExportSubSupScript(const SmNode *pNode, int nLevel)
OUString aText
void ExportRoot(const SmNode *pNode, int nLevel)
void ExportFont(const SmNode *pNode, int nLevel)
virtual void GetViewSettings(css::uno::Sequence< css::beans::PropertyValue > &aProps) override
void ExportUnaryHorizontal(const SmNode *pNode, int nLevel)
void ExportBinaryDiagonal(const SmNode *pNode, int nLevel)
void ExportMath(const SmNode *pNode)
void ExportAttributes(const SmNode *pNode, int nLevel)
void ExportOperator(const SmNode *pNode, int nLevel)
sal_Int32 GetVersion() const
const SvXMLNamespaceMap & GetNamespaceMap() const
virtual ErrCode exportDoc(enum ::xmloff::token::XMLTokenEnum eClass=::xmloff::token::XML_TOKEN_INVALID)
void AddAttribute(sal_uInt16 nPrefix, const OUString &rName, const OUString &rValue)
SvXMLExportFlags getExportFlags() const
SvXMLNamespaceMap & GetNamespaceMap_()
const css::uno::Reference< css::frame::XModel > & GetModel() const
void addChaffWhenEncryptedStorage()
const css::uno::Reference< css::xml::sax::XDocumentHandler > & GetDocHandler() const
comphelper::AttributeList & GetAttrList()
void ResetNamespaceMap()
sal_uInt16 GetKeyByAttrName(const OUString &rAttrName, OUString *pPrefix, OUString *pLocalName, OUString *pNamespace) const
sal_uInt16 Add(const OUString &rPrefix, const OUString &rName, sal_uInt16 nKey=XML_NAMESPACE_UNKNOWN)
void AddAttribute(const OUString &sName, const OUString &sValue)
virtual sal_Int16 SAL_CALL getLength() override
css::uno::Type const & get()
static void convertDouble(OUStringBuffer &rBuffer, double fNumber, bool bWriteUnits, sal_Int16 nSourceUnit, sal_Int16 nTargetUnit)
constexpr tools::Long GetWidth() const
constexpr tools::Long Top() const
constexpr tools::Long GetHeight() const
constexpr tools::Long Left() const
const Size & GetFontSize() const
ColorTransparency
int nCount
#define DBG_UNHANDLED_EXCEPTION(...)
float u
float y
float x
#define ERRCODE_NONE
#define SOFFICE_FILEFORMAT_60
DocumentType eType
sal_Int32 nIndex
OUString aName
#define SAL_WARN(area, stream)
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * Math_XMLContentExporter_get_implementation(css::uno::XComponentContext *context, css::uno::Sequence< css::uno::Any > const &)
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * Math_XMLOasisMetaExporter_get_implementation(css::uno::XComponentContext *context, css::uno::Sequence< css::uno::Any > const &)
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * Math_XMLExporter_get_implementation(css::uno::XComponentContext *context, css::uno::Sequence< css::uno::Any > const &)
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * Math_XMLOasisSettingsExporter_get_implementation(css::uno::XComponentContext *context, css::uno::Sequence< css::uno::Any > const &)
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * Math_XMLMetaExporter_get_implementation(css::uno::XComponentContext *context, css::uno::Sequence< css::uno::Any > const &)
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * Math_XMLSettingsExporter_get_implementation(css::uno::XComponentContext *context, css::uno::Sequence< css::uno::Any > const &)
static bool lcl_HasEffectOnMathvariant(const SmTokenType eType)
COMPHELPER_DLLPUBLIC css::uno::Reference< css::beans::XPropertySet > GenericPropertySet_CreateInstance(PropertySetInfo *pInfo)
Reference< XComponentContext > getProcessComponentContext()
int i
constexpr OUStringLiteral implementationName
SmColorTokenTableEntry Identify_Color_MATHML(sal_uInt32 cColor)
Identifies color from color code cColor.
const ::css::uno::Sequence<::css::beans::Pair<::rtl::OUString, ::rtl::OUString > > icustomMathmlHtmlEntitiesExport
Entity names for mathml.
XMLTokenEnum
XML_MATHSIZE
XML_MENCLOSE
XML_ITALIC
XML_BLOCK
XML_SEMANTICS
XML_UNIT_PT
XML_MI
XML_MFRAC
XML_STRETCHY
XML_MTD
XML_MTABLE
XML_MN
XML_MSTYLE
XML_PREFIX
XML_MSUBSUP
XML_N_MATH
XML_POSTFIX
XML_FORM
XML_MTEXT
XML_WIDTH
XML_MROW
XML_DISPLAY
XML_TRUE
XML_NONE
XML_MUNDEROVER
XML_MPRESCRIPTS
XML_FENCE
XML_MMULTISCRIPTS
XML_ENCODING
XML_MROOT
XML_NOTATION
XML_MUNDER
XML_HORIZONTALSTRIKE
XML_MSPACE
XML_MSUP
XML_DENOMALIGN
XML_MATHCOLOR
XML_NORMAL
XML_BEVELLED
XML_MATHVARIANT
XML_FALSE
XML_COLUMNALIGN
XML_ACCENT
XML_MATH
XML_LEFT
XML_ANNOTATION
XML_ACCENTUNDER
XML_NUMALIGN
XML_MO
XML_RIGHT
XML_MTR
XML_MSUB
XML_MSQRT
XML_MOVER
XML_MPHANTOM
bool IsXMLToken(std::u16string_view rString, enum XMLTokenEnum eToken)
const OUString & GetXMLToken(enum XMLTokenEnum eToken)
@ LSUB
Definition: node.hxx:1536
@ CSUB
Definition: node.hxx:1536
@ RSUP
Definition: node.hxx:1536
@ RSUB
Definition: node.hxx:1536
@ LSUP
Definition: node.hxx:1536
@ CSUP
Definition: node.hxx:1536
OUString SmResId(TranslateId aId)
Definition: smmod.cxx:42
#define SM_MOD()
Definition: smmod.hxx:98
SmTokenType eType
Definition: token.hxx:213
OUString cMathChar
Definition: token.hxx:214
TG nGroup
Definition: token.hxx:217
Reference< XModel > xModel
TG
The tokens contain the information gathered by the parser.
Definition: token.hxx:41
SmTokenType
Definition: token.hxx:71
@ TWIDEHAT
Definition: token.hxx:121
@ TWIDEHARPOON
Definition: token.hxx:121
@ TSIZE
Definition: token.hxx:115
@ TNITALIC
Definition: token.hxx:116
@ TUNDERLINE
Definition: token.hxx:118
@ TMATHMLCOL
Definition: token.hxx:147
@ TALIGNL
Definition: token.hxx:117
@ TRGBA
Definition: token.hxx:146
@ TUNKNOWN
Definition: token.hxx:74
@ TWIDEVEC
Definition: token.hxx:121
@ TSERIF
Definition: token.hxx:119
@ TNONE
Definition: token.hxx:74
@ TNBOLD
Definition: token.hxx:116
@ TNUMBER
Definition: token.hxx:124
@ TICONICCOL
Definition: token.hxx:147
@ TSANS
Definition: token.hxx:119
@ TRGB
Definition: token.hxx:146
@ TUNDERBRACE
Definition: token.hxx:126
@ TBOLD
Definition: token.hxx:116
@ TWIDESLASH
Definition: token.hxx:88
@ TOVERLINE
Definition: token.hxx:118
@ THTMLCOL
Definition: token.hxx:146
@ TALIGNC
Definition: token.hxx:117
@ TNEWLINE
Definition: token.hxx:76
@ TDVIPSNAMESCOL
Definition: token.hxx:146
@ TFIXED
Definition: token.hxx:119
@ TWIDETILDE
Definition: token.hxx:121
@ TITALIC
Definition: token.hxx:116
@ TTEXT
Definition: token.hxx:124
@ TINTD
Definition: token.hxx:135
@ TIDENT
Definition: token.hxx:124
@ TOVERBRACE
Definition: token.hxx:126
@ THEX
Definition: token.hxx:146
@ TPHANTOM
Definition: token.hxx:115
@ TOVERSTRIKE
Definition: token.hxx:118
sal_uInt16 sal_Unicode
sal_Unicode const MS_BACKSLASH
Definition: types.hxx:120
bool IsItalic(const vcl::Font &rFont)
Definition: utility.cxx:176
o3tl::Length SmO3tlLengthUnit()
Definition: utility.hxx:125
SvXMLExportFlags
constexpr sal_uInt16 XML_NAMESPACE_MATH
sal_Int32 nLength