1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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
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>
58 #include <mathmlexport.hxx>
59 #include <xparsmlbase.hxx>
60 #include <strings.hrc>
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
;
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
91 bool SmXMLExportWrapper::Export(SfxMedium
& rMedium
)
94 uno::Reference
<uno::XComponentContext
> xContext(comphelper::getProcessComponentContext());
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())
106 uno::Reference
<task::XStatusIndicator
> xStatusIndicator
;
109 if (pDocShell
/*&& pDocShell->GetMedium()*/)
111 OSL_ENSURE(pDocShell
->GetMedium() == &rMedium
, "different SfxMedium found");
113 SfxItemSet
* pSet
= rMedium
.GetItemSet();
116 const SfxUnoAnyItem
* pItem
= pSet
->GetItem(SID_PROGRESS_STATUSBAR_CONTROL
);
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
,
139 { sBaseURI
, 0, ::cppu::UnoType
<OUString
>::get(), beans::PropertyAttribute::MAYBEVOID
, 0 },
140 { sStreamRelPath
, 0, ::cppu::UnoType
<OUString
>::get(), beans::PropertyAttribute::MAYBEVOID
,
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
));
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() )
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
));
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"));
191 if (xStatusIndicator
.is())
192 xStatusIndicator
->setValue(nSteps
++);
194 bRet
= WriteThroughComponent(xStg
, xModelComp
, "content.xml", xContext
, xInfoSet
,
195 "com.sun.star.comp.Math.XMLContentExporter");
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"));
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();
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!");
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
),
253 OSL_ENSURE(xExporter
.is(), "can't instantiate export filter component");
257 // connect model and filter
258 xExporter
->setSourceDocument(xComponent
);
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!");
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");
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));
307 rPropSet
->setPropertyValue("StreamName", Any(sStreamName
));
311 bool bRet
= WriteThroughComponent(xStream
->getOutputStream(), xComponent
, rxContext
, rPropSet
,
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
)
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
);
381 uno::Reference
<frame::XModel
> xModel
= GetModel();
382 SmModel
* pModel
= comphelper::getFromUnoTunnel
<SmModel
>(xModel
);
386 SmDocShell
* pDocShell
= static_cast<SmDocShell
*>(pModel
->GetObjectShell());
387 pTree
= pDocShell
->GetFormulaTree();
388 aText
= pDocShell
->GetText();
391 GetDocHandler()->startDocument();
393 addChaffWhenEncryptedStorage();
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();
407 GetDocHandler()->endDocument();
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())
434 new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_SEMANTICS
, true, true));
437 ExportNodes(pTree
, 0);
442 SmModule
* pMod
= SM_MOD();
443 sal_uInt16 nSmSyntaxVersion
= pMod
->GetConfig()->GetDefaultSmSyntaxVersion();
445 // Convert symbol names
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();
455 rParser
->SetExportSymbolNames(bVal
);
458 OUStringBuffer
sStrBuf(12);
459 sStrBuf
.append(u
"StarMath ");
460 if (nSmSyntaxVersion
== 5)
461 sStrBuf
.append(u
"5.0");
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();
476 SmModel
* pModel
= comphelper::getFromUnoTunnel
<SmModel
>(xModel
);
481 SmDocShell
* pDocShell
= static_cast<SmDocShell
*>(pModel
->GetObjectShell());
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
);
510 Reference
<XPropertySetInfo
> xPropertySetInfo
= xProps
->getPropertySetInfo();
511 if (!xPropertySetInfo
.is())
514 const Sequence
<Property
> aProps
= xPropertySetInfo
->getProperties();
515 const sal_Int32 nCount
= aProps
.getLength();
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
) {
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
);
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
;
562 const SmNode
* node
= s
.top();
564 if (node
->GetType() != SmNodeType::BinHor
|| node
->GetToken().nGroup
!= nGroup
)
566 ExportNodes(node
, nLevel
+ 1);
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
)
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
);
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
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
)
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
;
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());
731 AddAttribute(XML_NAMESPACE_MATH
, XML_MATHVARIANT
, XML_NORMAL
);
732 pMath
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MI
, true, false));
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
);
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
)
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));
775 pText
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MN
, true, false));
778 pText
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MTEXT
, true, false));
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
822 pLSub
= pNode
->GetSubNode(LSUB
+ 1);
823 pLSup
= pNode
->GetSubNode(LSUP
+ 1);
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)))
832 new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MUNDEROVER
, true, true));
834 else if (nullptr != (pCSub
= pNode
->GetSubNode(CSUB
+ 1)))
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
847 ExportNodes(pCSub
, nLevel
+ 1);
849 ExportNodes(pCSup
, nLevel
+ 1);
852 pSub
= pNode
->GetSubNode(RSUB
+ 1);
853 pSup
= pNode
->GetSubNode(RSUP
+ 1);
857 ExportNodes(pSub
, nLevel
+ 1);
860 SvXMLElementExport
aNone(*this, XML_NAMESPACE_MATH
, XML_NONE
, true, true);
863 ExportNodes(pSup
, nLevel
+ 1);
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);
876 ExportNodes(pLSub
, nLevel
+ 1);
879 SvXMLElementExport
aNone(*this, XML_NAMESPACE_MATH
, XML_NONE
, true, true);
882 ExportNodes(pLSup
, nLevel
+ 1);
885 SvXMLElementExport
aNone(*this, XML_NAMESPACE_MATH
, XML_NONE
, true, true);
890 std::unique_ptr
<SvXMLElementExport
> pThing
;
891 if (nullptr != (pSub
= pNode
->GetSubNode(RSUB
+ 1))
892 && nullptr != (pSup
= pNode
->GetSubNode(RSUP
+ 1)))
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)))
910 new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MUNDEROVER
, true, true));
912 else if (nullptr != (pCSub
= pNode
->GetSubNode(CSUB
+ 1)))
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
924 ExportNodes(pCSub
, nLevel
+ 1);
926 ExportNodes(pCSup
, nLevel
+ 1);
930 ExportNodes(pSub
, nLevel
+ 1);
932 ExportNodes(pSup
, nLevel
+ 1);
937 void SmXMLExport::ExportBrace(const SmNode
* pNode
, int nLevel
)
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.
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
);
962 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_FALSE
);
963 ExportNodes(pLeft
, nLevel
+ 1);
966 if (nullptr != (pTemp
= pNode
->GetSubNode(1)))
969 SvXMLElementExport
aRowExport(*this, XML_NAMESPACE_MATH
, XML_MROW
, true, true);
970 ExportNodes(pTemp
, nLevel
+ 1);
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
);
982 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_FALSE
);
983 ExportNodes(pRight
, nLevel
+ 1);
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);
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
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));
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
)
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
);
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
);
1060 // make these wide accents stretchy
1061 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_TRUE
);
1062 ExportNodes(pNode
->GetSubNode(0), nLevel
+ 1);
1066 ExportNodes(pNode
->GetSubNode(0), nLevel
+ 1);
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
;
1089 eNodeType
= pNode
->GetToken().eType
;
1090 if (!lcl_HasEffectOnMathvariant(eNodeType
))
1107 nSansSerifFixed
= 0;
1110 nSansSerifFixed
= 1;
1113 nSansSerifFixed
= 2;
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);
1131 switch (pNode
->GetToken().eType
)
1134 // No attribute needed. An <mphantom> element will be used below.
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
);
1147 case TDVIPSNAMESCOL
:
1150 nc
= pNode
->GetToken().cMathChar
.toUInt32(16);
1151 OUString
ssStr("#" + Color(ColorTransparency
, nc
).AsRGBHEXString());
1152 AddAttribute(XML_NAMESPACE_MATH
, XML_MATHCOLOR
, ssStr
);
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('%');
1168 case FontSizeType::DIVIDE
:
1169 ::sax::Converter::convertDouble(sStrBuf
,
1170 static_cast<double>(Fraction(100, 1) / aFrac
));
1171 sStrBuf
.append('%');
1173 case FontSizeType::ABSOLUT
:
1174 ::sax::Converter::convertDouble(sStrBuf
, static_cast<double>(aFrac
));
1175 sStrBuf
.append(GetXMLToken(XML_UNIT_PT
));
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
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
);
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
));
1201 OUString
sStr(sStrBuf
.makeStringAndClear());
1202 AddAttribute(XML_NAMESPACE_MATH
, XML_MATHSIZE
, sStr
);
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)
1220 if (nBold
== 1 && nItalic
!= 1)
1222 else if (nBold
!= 1 && nItalic
== 1)
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 ...
1241 SAL_WARN("starmath", "unexpected case");
1243 AddAttribute(XML_NAMESPACE_MATH
, XML_MATHVARIANT
, OUString::createFromAscii(pText
));
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
,
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.
1269 // XXXXXX[body]XXXXXXX
1271 // Similarly for the underbrace construction.
1275 switch (pNode
->GetToken().eType
)
1286 SvXMLElementExport
aOver1(*this, XML_NAMESPACE_MATH
, which
, true, true);
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
);
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
)
1329 switch (pNode
->GetType())
1331 case SmNodeType::Table
:
1332 ExportTable(pNode
, nLevel
);
1334 case SmNodeType::Align
:
1335 case SmNodeType::Bracebody
:
1336 case SmNodeType::Expression
:
1337 ExportExpression(pNode
, nLevel
);
1339 case SmNodeType::Line
:
1340 ExportLine(pNode
, nLevel
);
1342 case SmNodeType::Text
:
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]);
1354 // no conversion to MathML implemented -> export it as text
1355 // thus at least it will not vanish into nothing
1360 switch (pNode
->GetToken().eType
)
1363 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_TRUE
);
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;
1387 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_FALSE
);
1394 Special
: //SmNodeType::Special requires some sort of Entity preservation in the XML engine.
1395 case SmNodeType::MathIdent
:
1396 case SmNodeType::Place
:
1399 case SmNodeType::BinHor
:
1400 ExportBinaryHorizontal(pNode
, nLevel
);
1402 case SmNodeType::UnHor
:
1403 ExportUnaryHorizontal(pNode
, nLevel
);
1405 case SmNodeType::Brace
:
1406 ExportBrace(pNode
, nLevel
);
1408 case SmNodeType::BinVer
:
1409 ExportBinaryVertical(pNode
, nLevel
);
1411 case SmNodeType::BinDiagonal
:
1412 ExportBinaryDiagonal(pNode
, nLevel
);
1414 case SmNodeType::SubSup
:
1415 ExportSubSupScript(pNode
, nLevel
);
1417 case SmNodeType::Root
:
1418 ExportRoot(pNode
, nLevel
);
1420 case SmNodeType::Oper
:
1421 ExportOperator(pNode
, nLevel
);
1423 case SmNodeType::Attribute
:
1424 ExportAttributes(pNode
, nLevel
);
1426 case SmNodeType::Font
:
1427 ExportFont(pNode
, nLevel
);
1429 case SmNodeType::VerticalBrace
:
1430 ExportVerticalBrace(static_cast<const SmVerticalBraceNode
*>(pNode
), nLevel
);
1432 case SmNodeType::Matrix
:
1433 ExportMatrix(pNode
, nLevel
);
1435 case SmNodeType::Blank
:
1439 SAL_WARN("starmath", "Warning: failed to export a node?");
1444 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */