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 <xmloff/attrlist.hxx>
51 #include <comphelper/genericpropertyset.hxx>
52 #include <comphelper/servicehelper.hxx>
53 #include <comphelper/propertysetinfo.hxx>
54 #include <comphelper/diagnose_ex.hxx>
55 #include <sal/log.hxx>
59 #include <mathmlexport.hxx>
60 #include <xparsmlbase.hxx>
61 #include <strings.hrc>
63 #include <unomodel.hxx>
64 #include <document.hxx>
65 #include <utility.hxx>
66 #include <cfgitem.hxx>
67 #include <starmathdatabase.hxx>
69 using namespace ::com::sun::star::beans
;
70 using namespace ::com::sun::star::document
;
71 using namespace ::com::sun::star::lang
;
72 using namespace ::com::sun::star::uno
;
73 using namespace ::com::sun::star
;
74 using namespace ::xmloff::token
;
78 bool IsInPrivateUseArea(sal_Unicode cChar
) { return 0xE000 <= cChar
&& cChar
<= 0xF8FF; }
80 sal_Unicode
ConvertMathToMathML(sal_Unicode cChar
)
82 sal_Unicode cRes
= cChar
;
83 if (IsInPrivateUseArea(cChar
))
85 SAL_WARN("starmath", "Error: private use area characters should no longer be in use!");
86 cRes
= u
'@'; // just some character that should easily be notice as odd in the context
92 bool SmXMLExportWrapper::Export(SfxMedium
& rMedium
)
95 uno::Reference
<uno::XComponentContext
> xContext(comphelper::getProcessComponentContext());
98 uno::Reference
<lang::XComponent
> xModelComp
= xModel
;
100 bool bEmbedded
= false;
101 SmModel
* pModel
= comphelper::getFromUnoTunnel
<SmModel
>(xModel
);
103 SmDocShell
* pDocShell
= pModel
? static_cast<SmDocShell
*>(pModel
->GetObjectShell()) : nullptr;
104 if (pDocShell
&& SfxObjectCreateMode::EMBEDDED
== pDocShell
->GetCreateMode())
107 uno::Reference
<task::XStatusIndicator
> xStatusIndicator
;
110 if (pDocShell
/*&& pDocShell->GetMedium()*/)
112 OSL_ENSURE(pDocShell
->GetMedium() == &rMedium
, "different SfxMedium found");
114 SfxItemSet
* pSet
= rMedium
.GetItemSet();
117 const SfxUnoAnyItem
* pItem
= pSet
->GetItem(SID_PROGRESS_STATUSBAR_CONTROL
);
119 pItem
->GetValue() >>= xStatusIndicator
;
123 // set progress range and start status indicator
124 if (xStatusIndicator
.is())
126 sal_Int32 nProgressRange
= bFlat
? 1 : 3;
127 xStatusIndicator
->start(SmResId(STR_STATSTR_WRITING
), nProgressRange
);
131 static constexpr OUStringLiteral
sUsePrettyPrinting(u
"UsePrettyPrinting");
132 static constexpr OUStringLiteral
sBaseURI(u
"BaseURI");
133 static constexpr OUStringLiteral
sStreamRelPath(u
"StreamRelPath");
134 static constexpr OUStringLiteral
sStreamName(u
"StreamName");
136 // create XPropertySet with three properties for status indicator
137 static const comphelper::PropertyMapEntry aInfoMap
[] = {
138 { sUsePrettyPrinting
, 0, cppu::UnoType
<bool>::get(), beans::PropertyAttribute::MAYBEVOID
,
140 { sBaseURI
, 0, ::cppu::UnoType
<OUString
>::get(), beans::PropertyAttribute::MAYBEVOID
, 0 },
141 { sStreamRelPath
, 0, ::cppu::UnoType
<OUString
>::get(), beans::PropertyAttribute::MAYBEVOID
,
143 { sStreamName
, 0, ::cppu::UnoType
<OUString
>::get(), beans::PropertyAttribute::MAYBEVOID
, 0 }
145 uno::Reference
<beans::XPropertySet
> xInfoSet(
146 comphelper::GenericPropertySet_CreateInstance(new comphelper::PropertySetInfo(aInfoMap
)));
148 bool bUsePrettyPrinting
149 = bFlat
|| officecfg::Office::Common::Save::Document::PrettyPrinting::get();
150 xInfoSet
->setPropertyValue(sUsePrettyPrinting
, Any(bUsePrettyPrinting
));
153 xInfoSet
->setPropertyValue(sBaseURI
, Any(rMedium
.GetBaseURL(true)));
155 sal_Int32 nSteps
= 0;
156 if (xStatusIndicator
.is())
157 xStatusIndicator
->setValue(nSteps
++);
158 if (!bFlat
) //Storage (Package) of Stream
160 uno::Reference
<embed::XStorage
> xStg
= rMedium
.GetOutputStorage();
161 bool bOASIS
= (SotStorage::GetVersion(xStg
) > SOFFICE_FILEFORMAT_60
);
163 // TODO/LATER: handle the case of embedded links gracefully
164 if (bEmbedded
) //&& !pStg->IsRoot() )
167 if (rMedium
.GetItemSet())
169 const SfxStringItem
* pDocHierarchItem
170 = rMedium
.GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME
);
171 if (pDocHierarchItem
)
172 aName
= pDocHierarchItem
->GetValue();
175 if (!aName
.isEmpty())
177 xInfoSet
->setPropertyValue(sStreamRelPath
, Any(aName
));
183 if (xStatusIndicator
.is())
184 xStatusIndicator
->setValue(nSteps
++);
186 bRet
= WriteThroughComponent(xStg
, xModelComp
, "meta.xml", xContext
, xInfoSet
,
187 (bOASIS
? "com.sun.star.comp.Math.XMLOasisMetaExporter"
188 : "com.sun.star.comp.Math.XMLMetaExporter"));
192 if (xStatusIndicator
.is())
193 xStatusIndicator
->setValue(nSteps
++);
195 bRet
= WriteThroughComponent(xStg
, xModelComp
, "content.xml", xContext
, xInfoSet
,
196 "com.sun.star.comp.Math.XMLContentExporter");
201 if (xStatusIndicator
.is())
202 xStatusIndicator
->setValue(nSteps
++);
204 bRet
= WriteThroughComponent(xStg
, xModelComp
, "settings.xml", xContext
, xInfoSet
,
205 (bOASIS
? "com.sun.star.comp.Math.XMLOasisSettingsExporter"
206 : "com.sun.star.comp.Math.XMLSettingsExporter"));
211 SvStream
* pStream
= rMedium
.GetOutStream();
212 uno::Reference
<io::XOutputStream
> xOut(new utl::OOutputStreamWrapper(*pStream
));
214 if (xStatusIndicator
.is())
215 xStatusIndicator
->setValue(nSteps
++);
217 bRet
= WriteThroughComponent(xOut
, xModelComp
, xContext
, xInfoSet
,
218 "com.sun.star.comp.Math.XMLContentExporter");
221 if (xStatusIndicator
.is())
222 xStatusIndicator
->end();
227 /// export through an XML exporter component (output stream version)
228 bool SmXMLExportWrapper::WriteThroughComponent(const Reference
<io::XOutputStream
>& xOutputStream
,
229 const Reference
<XComponent
>& xComponent
,
230 Reference
<uno::XComponentContext
> const& rxContext
,
231 Reference
<beans::XPropertySet
> const& rPropSet
,
232 const char* pComponentName
)
234 OSL_ENSURE(xOutputStream
.is(), "I really need an output stream!");
235 OSL_ENSURE(xComponent
.is(), "Need component!");
236 OSL_ENSURE(nullptr != pComponentName
, "Need component name!");
239 Reference
<xml::sax::XWriter
> xSaxWriter
= xml::sax::Writer::create(rxContext
);
241 // connect XML writer to output stream
242 xSaxWriter
->setOutputStream(xOutputStream
);
243 if (m_bUseHTMLMLEntities
)
244 xSaxWriter
->setCustomEntityNames(starmathdatabase::icustomMathmlHtmlEntitiesExport
);
246 // prepare arguments (prepend doc handler to given arguments)
247 Sequence
<Any
> aArgs
{ Any(xSaxWriter
), Any(rPropSet
) };
249 // get filter component
250 Reference
<document::XExporter
> xExporter(
251 rxContext
->getServiceManager()->createInstanceWithArgumentsAndContext(
252 OUString::createFromAscii(pComponentName
), aArgs
, rxContext
),
254 OSL_ENSURE(xExporter
.is(), "can't instantiate export filter component");
258 // connect model and filter
259 xExporter
->setSourceDocument(xComponent
);
262 Reference
<XFilter
> xFilter(xExporter
, UNO_QUERY
);
263 uno::Sequence
<PropertyValue
> aProps(0);
264 xFilter
->filter(aProps
);
266 auto pFilter
= comphelper::getFromUnoTunnel
<SmXMLExport
>(xFilter
);
267 return pFilter
== nullptr || pFilter
->GetSuccess();
270 /// export through an XML exporter component (storage version)
271 bool SmXMLExportWrapper::WriteThroughComponent(const Reference
<embed::XStorage
>& xStorage
,
272 const Reference
<XComponent
>& xComponent
,
273 const char* pStreamName
,
274 Reference
<uno::XComponentContext
> const& rxContext
,
275 Reference
<beans::XPropertySet
> const& rPropSet
,
276 const char* pComponentName
)
278 OSL_ENSURE(xStorage
.is(), "Need storage!");
279 OSL_ENSURE(nullptr != pStreamName
, "Need stream name!");
282 Reference
<io::XStream
> xStream
;
283 OUString sStreamName
= OUString::createFromAscii(pStreamName
);
286 xStream
= xStorage
->openStreamElement(sStreamName
, embed::ElementModes::READWRITE
287 | embed::ElementModes::TRUNCATE
);
289 catch (const uno::Exception
&)
291 DBG_UNHANDLED_EXCEPTION("starmath", "Can't create output stream in package");
295 uno::Reference
<beans::XPropertySet
> xSet(xStream
, uno::UNO_QUERY
);
296 static const OUStringLiteral sMediaType
= u
"MediaType";
297 static const OUStringLiteral sTextXml
= u
"text/xml";
298 xSet
->setPropertyValue(sMediaType
, Any(OUString(sTextXml
)));
300 // all streams must be encrypted in encrypted document
301 static const OUStringLiteral sUseCommonStoragePasswordEncryption
302 = u
"UseCommonStoragePasswordEncryption";
303 xSet
->setPropertyValue(sUseCommonStoragePasswordEncryption
, Any(true));
308 rPropSet
->setPropertyValue("StreamName", Any(sStreamName
));
312 bool bRet
= WriteThroughComponent(xStream
->getOutputStream(), xComponent
, rxContext
, rPropSet
,
318 SmXMLExport::SmXMLExport(const css::uno::Reference
<css::uno::XComponentContext
>& rContext
,
319 OUString
const& implementationName
, SvXMLExportFlags nExportFlags
)
320 : SvXMLExport(rContext
, implementationName
, util::MeasureUnit::INCH
, XML_MATH
, nExportFlags
)
326 sal_Int64 SAL_CALL
SmXMLExport::getSomething(const uno::Sequence
<sal_Int8
>& rId
)
328 return comphelper::getSomethingImpl(rId
, this,
329 comphelper::FallbackToGetSomethingOf
<SvXMLExport
>{});
332 const uno::Sequence
<sal_Int8
>& SmXMLExport::getUnoTunnelId() noexcept
334 static const comphelper::UnoIdInit theSmXMLExportUnoTunnelId
;
335 return theSmXMLExportUnoTunnelId
.getSeq();
338 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
339 Math_XMLExporter_get_implementation(css::uno::XComponentContext
* context
,
340 css::uno::Sequence
<css::uno::Any
> const&)
342 return cppu::acquire(new SmXMLExport(context
, "com.sun.star.comp.Math.XMLExporter",
343 SvXMLExportFlags::OASIS
| SvXMLExportFlags::ALL
));
346 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
347 Math_XMLMetaExporter_get_implementation(css::uno::XComponentContext
* context
,
348 css::uno::Sequence
<css::uno::Any
> const&)
350 return cppu::acquire(
351 new SmXMLExport(context
, "com.sun.star.comp.Math.XMLMetaExporter", SvXMLExportFlags::META
));
354 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
355 Math_XMLOasisMetaExporter_get_implementation(css::uno::XComponentContext
* context
,
356 css::uno::Sequence
<css::uno::Any
> const&)
358 return cppu::acquire(new SmXMLExport(context
, "com.sun.star.comp.Math.XMLOasisMetaExporter",
359 SvXMLExportFlags::OASIS
| SvXMLExportFlags::META
));
362 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
363 Math_XMLSettingsExporter_get_implementation(css::uno::XComponentContext
* context
,
364 css::uno::Sequence
<css::uno::Any
> const&)
366 return cppu::acquire(new SmXMLExport(context
, "com.sun.star.comp.Math.XMLSettingsExporter",
367 SvXMLExportFlags::SETTINGS
));
370 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
371 Math_XMLOasisSettingsExporter_get_implementation(css::uno::XComponentContext
* context
,
372 css::uno::Sequence
<css::uno::Any
> const&)
374 return cppu::acquire(new SmXMLExport(context
, "com.sun.star.comp.Math.XMLOasisSettingsExporter",
375 SvXMLExportFlags::OASIS
| SvXMLExportFlags::SETTINGS
));
378 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
379 Math_XMLContentExporter_get_implementation(css::uno::XComponentContext
* context
,
380 css::uno::Sequence
<css::uno::Any
> const&)
382 return cppu::acquire(new SmXMLExport(context
, "com.sun.star.comp.Math.XMLContentExporter",
383 SvXMLExportFlags::OASIS
| SvXMLExportFlags::CONTENT
));
386 ErrCode
SmXMLExport::exportDoc(enum XMLTokenEnum eClass
)
388 if (!(getExportFlags() & SvXMLExportFlags::CONTENT
))
390 SvXMLExport::exportDoc(eClass
);
394 uno::Reference
<frame::XModel
> xModel
= GetModel();
395 SmModel
* pModel
= comphelper::getFromUnoTunnel
<SmModel
>(xModel
);
399 SmDocShell
* pDocShell
= static_cast<SmDocShell
*>(pModel
->GetObjectShell());
400 pTree
= pDocShell
->GetFormulaTree();
401 aText
= pDocShell
->GetText();
404 GetDocHandler()->startDocument();
406 addChaffWhenEncryptedStorage();
409 SvXMLAttributeList
& rList
= GetAttrList();
411 // make use of a default namespace
412 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)
413 GetNamespaceMap_().Add(OUString(), GetXMLToken(XML_N_MATH
), XML_NAMESPACE_MATH
);
415 rList
.AddAttribute(GetNamespaceMap().GetAttrNameByKey(XML_NAMESPACE_MATH
),
416 GetNamespaceMap().GetNameByKey(XML_NAMESPACE_MATH
));
418 //I think we need something like ImplExportEntities();
420 GetDocHandler()->endDocument();
427 void SmXMLExport::ExportContent_()
429 uno::Reference
<frame::XModel
> xModel
= GetModel();
430 SmModel
* pModel
= comphelper::getFromUnoTunnel
<SmModel
>(xModel
);
431 SmDocShell
* pDocShell
= pModel
? static_cast<SmDocShell
*>(pModel
->GetObjectShell()) : nullptr;
432 OSL_ENSURE(pDocShell
, "doc shell missing");
434 if (pDocShell
&& !pDocShell
->GetFormat().IsTextmode())
436 // If the Math equation is not in text mode, we attach a display="block"
437 // attribute on the <math> root. We don't do anything if it is in
438 // text mode, the default display="inline" value will be used.
439 AddAttribute(XML_NAMESPACE_MATH
, XML_DISPLAY
, XML_BLOCK
);
441 SvXMLElementExport
aEquation(*this, XML_NAMESPACE_MATH
, XML_MATH
, true, true);
442 std::unique_ptr
<SvXMLElementExport
> pSemantics
;
444 if (!aText
.isEmpty())
447 new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_SEMANTICS
, true, true));
450 ExportNodes(pTree
, 0);
455 SmModule
* pMod
= SM_MOD();
456 sal_uInt16 nSmSyntaxVersion
= pMod
->GetConfig()->GetDefaultSmSyntaxVersion();
458 // Convert symbol names
461 nSmSyntaxVersion
= pDocShell
->GetSmSyntaxVersion();
462 AbstractSmParser
* rParser
= pDocShell
->GetParser();
463 bool bVal
= rParser
->IsExportSymbolNames();
464 rParser
->SetExportSymbolNames(true);
465 auto pTmpTree
= rParser
->Parse(aText
);
466 aText
= rParser
->GetText();
468 rParser
->SetExportSymbolNames(bVal
);
471 OUStringBuffer
sStrBuf(12);
472 sStrBuf
.append(u
"StarMath ");
473 if (nSmSyntaxVersion
== 5)
474 sStrBuf
.append(u
"5.0");
476 sStrBuf
.append(static_cast<sal_Int32
>(nSmSyntaxVersion
));
478 AddAttribute(XML_NAMESPACE_MATH
, XML_ENCODING
, sStrBuf
.makeStringAndClear());
479 SvXMLElementExport
aAnnotation(*this, XML_NAMESPACE_MATH
, XML_ANNOTATION
, true, false);
480 GetDocHandler()->characters(aText
);
483 void SmXMLExport::GetViewSettings(Sequence
<PropertyValue
>& aProps
)
485 uno::Reference
<frame::XModel
> xModel
= GetModel();
489 SmModel
* pModel
= comphelper::getFromUnoTunnel
<SmModel
>(xModel
);
494 SmDocShell
* pDocShell
= static_cast<SmDocShell
*>(pModel
->GetObjectShell());
499 PropertyValue
* pValue
= aProps
.getArray();
500 sal_Int32 nIndex
= 0;
502 tools::Rectangle
aRect(pDocShell
->GetVisArea());
504 pValue
[nIndex
].Name
= "ViewAreaTop";
505 pValue
[nIndex
++].Value
<<= aRect
.Top();
507 pValue
[nIndex
].Name
= "ViewAreaLeft";
508 pValue
[nIndex
++].Value
<<= aRect
.Left();
510 pValue
[nIndex
].Name
= "ViewAreaWidth";
511 pValue
[nIndex
++].Value
<<= aRect
.GetWidth();
513 pValue
[nIndex
].Name
= "ViewAreaHeight";
514 pValue
[nIndex
++].Value
<<= aRect
.GetHeight();
517 void SmXMLExport::GetConfigurationSettings(Sequence
<PropertyValue
>& rProps
)
519 Reference
<XPropertySet
> xProps(GetModel(), UNO_QUERY
);
523 Reference
<XPropertySetInfo
> xPropertySetInfo
= xProps
->getPropertySetInfo();
524 if (!xPropertySetInfo
.is())
527 const Sequence
<Property
> aProps
= xPropertySetInfo
->getProperties();
528 const sal_Int32 nCount
= aProps
.getLength();
532 rProps
.realloc(nCount
);
533 SmMathConfig
* pConfig
= SM_MOD()->GetConfig();
534 const bool bUsedSymbolsOnly
= pConfig
&& pConfig
->IsSaveOnlyUsedSymbols();
536 std::transform(aProps
.begin(), aProps
.end(), rProps
.getArray(),
537 [bUsedSymbolsOnly
, &xProps
](const Property
& prop
) {
539 if (prop
.Name
!= "Formula" && prop
.Name
!= "BasicLibraries"
540 && prop
.Name
!= "DialogLibraries" && prop
.Name
!= "RuntimeUID")
542 aRet
.Name
= prop
.Name
;
543 OUString
aActualName(prop
.Name
);
544 // handle 'save used symbols only'
545 static constexpr OUStringLiteral sUserDefinedSymbolsInUse
546 = u
"UserDefinedSymbolsInUse";
547 if (bUsedSymbolsOnly
&& prop
.Name
== "Symbols")
548 aActualName
= sUserDefinedSymbolsInUse
;
549 aRet
.Value
= xProps
->getPropertyValue(aActualName
);
555 void SmXMLExport::ExportLine(const SmNode
* pNode
, int nLevel
) { ExportExpression(pNode
, nLevel
); }
557 void SmXMLExport::ExportBinaryHorizontal(const SmNode
* pNode
, int nLevel
)
559 TG nGroup
= pNode
->GetToken().nGroup
;
561 SvXMLElementExport
aRow(*this, XML_NAMESPACE_MATH
, XML_MROW
, true, true);
563 // Unfold the binary tree structure as long as the nodes are SmBinHorNode
564 // with the same nGroup. This will reduce the number of nested <mrow>
565 // elements e.g. we only need three <mrow> levels to export
567 // "a*b*c*d+e*f*g*h+i*j*k*l = a*b*c*d+e*f*g*h+i*j*k*l =
568 // a*b*c*d+e*f*g*h+i*j*k*l = a*b*c*d+e*f*g*h+i*j*k*l"
570 // See https://www.libreoffice.org/bugzilla/show_bug.cgi?id=66081
571 ::std::stack
<const SmNode
*> s
;
575 const SmNode
* node
= s
.top();
577 if (node
->GetType() != SmNodeType::BinHor
|| node
->GetToken().nGroup
!= nGroup
)
579 ExportNodes(node
, nLevel
+ 1);
582 const SmBinHorNode
* binNode
= static_cast<const SmBinHorNode
*>(node
);
583 s
.push(binNode
->RightOperand());
584 s
.push(binNode
->Symbol());
585 s
.push(binNode
->LeftOperand());
589 void SmXMLExport::ExportUnaryHorizontal(const SmNode
* pNode
, int nLevel
)
591 ExportExpression(pNode
, nLevel
);
594 void SmXMLExport::ExportExpression(const SmNode
* pNode
, int nLevel
,
595 bool bNoMrowContainer
/*=false*/)
597 std::unique_ptr
<SvXMLElementExport
> pRow
;
598 size_t nSize
= pNode
->GetNumSubNodes();
600 // #i115443: nodes of type expression always need to be grouped with mrow statement
601 if (!bNoMrowContainer
&& (nSize
> 1 || pNode
->GetType() == SmNodeType::Expression
))
602 pRow
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MROW
, true, true));
604 for (size_t i
= 0; i
< nSize
; ++i
)
606 if (const SmNode
* pTemp
= pNode
->GetSubNode(i
))
607 ExportNodes(pTemp
, nLevel
+ 1);
611 void SmXMLExport::ExportBinaryVertical(const SmNode
* pNode
, int nLevel
)
613 assert(pNode
->GetNumSubNodes() == 3);
614 const SmNode
* pNum
= pNode
->GetSubNode(0);
615 const SmNode
* pDenom
= pNode
->GetSubNode(2);
616 if (pNum
->GetType() == SmNodeType::Align
&& pNum
->GetToken().eType
!= TALIGNC
)
618 // A left or right alignment is specified on the numerator:
619 // attach the corresponding numalign attribute.
620 AddAttribute(XML_NAMESPACE_MATH
, XML_NUMALIGN
,
621 pNum
->GetToken().eType
== TALIGNL
? XML_LEFT
: XML_RIGHT
);
623 if (pDenom
->GetType() == SmNodeType::Align
&& pDenom
->GetToken().eType
!= TALIGNC
)
625 // A left or right alignment is specified on the denominator:
626 // attach the corresponding denomalign attribute.
627 AddAttribute(XML_NAMESPACE_MATH
, XML_DENOMALIGN
,
628 pDenom
->GetToken().eType
== TALIGNL
? XML_LEFT
: XML_RIGHT
);
630 SvXMLElementExport
aFraction(*this, XML_NAMESPACE_MATH
, XML_MFRAC
, true, true);
631 ExportNodes(pNum
, nLevel
);
632 ExportNodes(pDenom
, nLevel
);
635 void SmXMLExport::ExportBinaryDiagonal(const SmNode
* pNode
, int nLevel
)
637 assert(pNode
->GetNumSubNodes() == 3);
639 if (pNode
->GetToken().eType
== TWIDESLASH
)
642 // export the node as <mfrac bevelled="true">
643 AddAttribute(XML_NAMESPACE_MATH
, XML_BEVELLED
, XML_TRUE
);
644 SvXMLElementExport
aFraction(*this, XML_NAMESPACE_MATH
, XML_MFRAC
, true, true);
645 ExportNodes(pNode
->GetSubNode(0), nLevel
);
646 ExportNodes(pNode
->GetSubNode(1), nLevel
);
651 // We can not use <mfrac> to a backslash, so just use <mo>\</mo>
652 SvXMLElementExport
aRow(*this, XML_NAMESPACE_MATH
, XML_MROW
, true, true);
654 ExportNodes(pNode
->GetSubNode(0), nLevel
);
656 { // Scoping for <mo> creation
657 SvXMLElementExport
aMo(*this, XML_NAMESPACE_MATH
, XML_MO
, true, true);
658 GetDocHandler()->characters(OUStringChar(MS_BACKSLASH
));
661 ExportNodes(pNode
->GetSubNode(1), nLevel
);
665 void SmXMLExport::ExportTable(const SmNode
* pNode
, int nLevel
)
667 std::unique_ptr
<SvXMLElementExport
> pTable
;
669 size_t nSize
= pNode
->GetNumSubNodes();
671 //If the list ends in newline then the last entry has
672 //no subnodes, the newline is superfluous so we just drop
673 //the last node, inclusion would create a bad MathML
677 const SmNode
* pLine
= pNode
->GetSubNode(nSize
- 1);
678 if (pLine
->GetType() == SmNodeType::Line
&& pLine
->GetNumSubNodes() == 1
679 && pLine
->GetSubNode(0) != nullptr
680 && pLine
->GetSubNode(0)->GetToken().eType
== TNEWLINE
)
684 // try to avoid creating a mtable element when the formula consists only
685 // of a single output line
686 if (nLevel
|| (nSize
> 1))
687 pTable
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MTABLE
, true, true));
689 for (size_t i
= 0; i
< nSize
; ++i
)
691 if (const SmNode
* pTemp
= pNode
->GetSubNode(i
))
693 std::unique_ptr
<SvXMLElementExport
> pRow
;
694 std::unique_ptr
<SvXMLElementExport
> pCell
;
697 pRow
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MTR
, true, true));
698 SmTokenType eAlign
= TALIGNC
;
699 if (pTemp
->GetType() == SmNodeType::Align
)
701 // For Binom() and Stack() constructions, the SmNodeType::Align nodes
702 // are direct children.
703 // binom{alignl ...}{alignr ...} and
704 // stack{alignl ... ## alignr ... ## ...}
705 eAlign
= pTemp
->GetToken().eType
;
707 else if (pTemp
->GetType() == SmNodeType::Line
&& pTemp
->GetNumSubNodes() == 1
708 && pTemp
->GetSubNode(0)
709 && pTemp
->GetSubNode(0)->GetType() == SmNodeType::Align
)
711 // For the Table() construction, the SmNodeType::Align node is a child
712 // of an SmNodeType::Line node.
713 // alignl ... newline alignr ... newline ...
714 eAlign
= pTemp
->GetSubNode(0)->GetToken().eType
;
716 if (eAlign
!= TALIGNC
)
718 // If a left or right alignment is specified on this line,
719 // attach the corresponding columnalign attribute.
720 AddAttribute(XML_NAMESPACE_MATH
, XML_COLUMNALIGN
,
721 eAlign
== TALIGNL
? XML_LEFT
: XML_RIGHT
);
723 pCell
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MTD
, true, true));
725 ExportNodes(pTemp
, nLevel
+ 1);
730 void SmXMLExport::ExportMath(const SmNode
* pNode
)
732 const SmTextNode
* pTemp
= static_cast<const SmTextNode
*>(pNode
);
733 std::unique_ptr
<SvXMLElementExport
> pMath
;
735 if (pNode
->GetType() == SmNodeType::Math
|| pNode
->GetType() == SmNodeType::GlyphSpecial
)
737 // Export SmNodeType::Math and SmNodeType::GlyphSpecial symbols as <mo> elements
738 pMath
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MO
, true, false));
740 else if (pNode
->GetType() == SmNodeType::Special
)
742 bool bIsItalic
= IsItalic(pNode
->GetFont());
744 AddAttribute(XML_NAMESPACE_MATH
, XML_MATHVARIANT
, XML_NORMAL
);
745 pMath
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MI
, true, false));
749 // Export SmNodeType::MathIdent and SmNodeType::Place symbols as <mi> elements:
750 // - These math symbols should not be drawn slanted. Hence we should
751 // attach a mathvariant="normal" attribute to single-char <mi> elements
752 // that are not mathematical alphanumeric symbol. For simplicity and to
753 // work around browser limitations, we always attach such an attribute.
754 // - The MathML specification suggests to use empty <mi> elements as
755 // placeholders but they won't be visible in most MathML rendering
756 // engines so let's use an empty square for SmNodeType::Place instead.
757 AddAttribute(XML_NAMESPACE_MATH
, XML_MATHVARIANT
, XML_NORMAL
);
758 pMath
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MI
, true, false));
760 sal_Unicode nArse
= pTemp
->GetText()[0];
761 sal_Unicode cTmp
= ConvertMathToMathML(nArse
);
764 OSL_ENSURE(nArse
!= 0xffff, "Non existent symbol");
765 GetDocHandler()->characters(OUString(nArse
));
768 void SmXMLExport::ExportText(const SmNode
* pNode
)
770 std::unique_ptr
<SvXMLElementExport
> pText
;
771 const SmTextNode
* pTemp
= static_cast<const SmTextNode
*>(pNode
);
772 switch (pNode
->GetToken().eType
)
777 //Note that we change the fontstyle to italic for strings that
778 //are italic and longer than a single character.
779 bool bIsItalic
= IsItalic(pTemp
->GetFont());
780 if ((pTemp
->GetText().getLength() > 1) && bIsItalic
)
781 AddAttribute(XML_NAMESPACE_MATH
, XML_MATHVARIANT
, XML_ITALIC
);
782 else if ((pTemp
->GetText().getLength() == 1) && !bIsItalic
)
783 AddAttribute(XML_NAMESPACE_MATH
, XML_MATHVARIANT
, XML_NORMAL
);
784 pText
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MI
, true, false));
788 pText
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MN
, true, false));
791 pText
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MTEXT
, true, false));
794 GetDocHandler()->characters(pTemp
->GetText());
797 void SmXMLExport::ExportBlank(const SmNode
* pNode
)
799 const SmBlankNode
* pTemp
= static_cast<const SmBlankNode
*>(pNode
);
800 //!! exports an <mspace> element. Note that for example "~_~" is allowed in
801 //!! Math (so it has no sense at all) but must not result in an empty
802 //!! <msub> tag in MathML !!
804 if (pTemp
->GetBlankNum() != 0)
806 // Attach a width attribute. We choose the (somewhat arbitrary) values
807 // ".5em" for a small gap '`' and "2em" for a large gap '~'.
808 // (see SmBlankNode::IncreaseBy for how pTemp->mnNum is set).
809 OUStringBuffer sStrBuf
;
810 ::sax::Converter::convertDouble(sStrBuf
, pTemp
->GetBlankNum() * .5);
811 sStrBuf
.append("em");
812 AddAttribute(XML_NAMESPACE_MATH
, XML_WIDTH
, sStrBuf
.makeStringAndClear());
815 SvXMLElementExport
aTextExport(*this, XML_NAMESPACE_MATH
, XML_MSPACE
, true, false);
817 GetDocHandler()->characters(OUString());
820 void SmXMLExport::ExportSubSupScript(const SmNode
* pNode
, int nLevel
)
822 const SmNode
* pSub
= nullptr;
823 const SmNode
* pSup
= nullptr;
824 const SmNode
* pCSub
= nullptr;
825 const SmNode
* pCSup
= nullptr;
826 const SmNode
* pLSub
= nullptr;
827 const SmNode
* pLSup
= nullptr;
828 std::unique_ptr
<SvXMLElementExport
> pThing2
;
830 //if we have prescripts at all then we must use the tensor notation
832 //This is one of those excellent locations where scope is vital to
833 //arrange the construction and destruction of the element helper
835 pLSub
= pNode
->GetSubNode(LSUB
+ 1);
836 pLSup
= pNode
->GetSubNode(LSUP
+ 1);
839 SvXMLElementExport
aMultiScripts(*this, XML_NAMESPACE_MATH
, XML_MMULTISCRIPTS
, true, true);
841 if (nullptr != (pCSub
= pNode
->GetSubNode(CSUB
+ 1))
842 && nullptr != (pCSup
= pNode
->GetSubNode(CSUP
+ 1)))
845 new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MUNDEROVER
, true, true));
847 else if (nullptr != (pCSub
= pNode
->GetSubNode(CSUB
+ 1)))
850 new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MUNDER
, true, true));
852 else if (nullptr != (pCSup
= pNode
->GetSubNode(CSUP
+ 1)))
854 pThing2
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MOVER
, true, true));
857 ExportNodes(pNode
->GetSubNode(0), nLevel
+ 1); //Main Term
860 ExportNodes(pCSub
, nLevel
+ 1);
862 ExportNodes(pCSup
, nLevel
+ 1);
865 pSub
= pNode
->GetSubNode(RSUB
+ 1);
866 pSup
= pNode
->GetSubNode(RSUP
+ 1);
870 ExportNodes(pSub
, nLevel
+ 1);
873 SvXMLElementExport
aNone(*this, XML_NAMESPACE_MATH
, XML_NONE
, true, true);
876 ExportNodes(pSup
, nLevel
+ 1);
879 SvXMLElementExport
aNone(*this, XML_NAMESPACE_MATH
, XML_NONE
, true, true);
883 //Separator element between suffix and prefix sub/sup pairs
885 SvXMLElementExport
aPrescripts(*this, XML_NAMESPACE_MATH
, XML_MPRESCRIPTS
, true, true);
889 ExportNodes(pLSub
, nLevel
+ 1);
892 SvXMLElementExport
aNone(*this, XML_NAMESPACE_MATH
, XML_NONE
, true, true);
895 ExportNodes(pLSup
, nLevel
+ 1);
898 SvXMLElementExport
aNone(*this, XML_NAMESPACE_MATH
, XML_NONE
, true, true);
903 std::unique_ptr
<SvXMLElementExport
> pThing
;
904 if (nullptr != (pSub
= pNode
->GetSubNode(RSUB
+ 1))
905 && nullptr != (pSup
= pNode
->GetSubNode(RSUP
+ 1)))
908 new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MSUBSUP
, true, true));
910 else if (nullptr != (pSub
= pNode
->GetSubNode(RSUB
+ 1)))
912 pThing
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MSUB
, true, true));
914 else if (nullptr != (pSup
= pNode
->GetSubNode(RSUP
+ 1)))
916 pThing
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MSUP
, true, true));
919 if (nullptr != (pCSub
= pNode
->GetSubNode(CSUB
+ 1))
920 && nullptr != (pCSup
= pNode
->GetSubNode(CSUP
+ 1)))
923 new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MUNDEROVER
, true, true));
925 else if (nullptr != (pCSub
= pNode
->GetSubNode(CSUB
+ 1)))
928 new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MUNDER
, true, true));
930 else if (nullptr != (pCSup
= pNode
->GetSubNode(CSUP
+ 1)))
932 pThing2
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MOVER
, true, true));
934 ExportNodes(pNode
->GetSubNode(0), nLevel
+ 1); //Main Term
937 ExportNodes(pCSub
, nLevel
+ 1);
939 ExportNodes(pCSup
, nLevel
+ 1);
943 ExportNodes(pSub
, nLevel
+ 1);
945 ExportNodes(pSup
, nLevel
+ 1);
950 void SmXMLExport::ExportBrace(const SmNode
* pNode
, int nLevel
)
953 const SmNode
* pLeft
= pNode
->GetSubNode(0);
954 const SmNode
* pRight
= pNode
->GetSubNode(2);
956 // This used to generate <mfenced> or <mrow>+<mo> elements according to
957 // the stretchiness of fences. The MathML recommendation defines an
958 // <mrow>+<mo> construction that is equivalent to the <mfenced> element:
959 // http://www.w3.org/TR/MathML3/chapter3.html#presm.mfenced
960 // To simplify our code and avoid issues with mfenced implementations in
961 // MathML rendering engines, we now always generate <mrow>+<mo> elements.
965 SvXMLElementExport
aRow(*this, XML_NAMESPACE_MATH
, XML_MROW
, true, true);
967 // <mo fence="true"> opening-fence </mo>
968 if (pLeft
&& (pLeft
->GetToken().eType
!= TNONE
))
970 AddAttribute(XML_NAMESPACE_MATH
, XML_FENCE
, XML_TRUE
);
971 AddAttribute(XML_NAMESPACE_MATH
, XML_FORM
, XML_PREFIX
);
972 if (pNode
->GetScaleMode() == SmScaleMode::Height
)
973 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_TRUE
);
975 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_FALSE
);
976 ExportNodes(pLeft
, nLevel
+ 1);
979 if (nullptr != (pTemp
= pNode
->GetSubNode(1)))
982 SvXMLElementExport
aRowExport(*this, XML_NAMESPACE_MATH
, XML_MROW
, true, true);
983 ExportNodes(pTemp
, nLevel
+ 1);
987 // <mo fence="true"> closing-fence </mo>
988 if (pRight
&& (pRight
->GetToken().eType
!= TNONE
))
990 AddAttribute(XML_NAMESPACE_MATH
, XML_FENCE
, XML_TRUE
);
991 AddAttribute(XML_NAMESPACE_MATH
, XML_FORM
, XML_POSTFIX
);
992 if (pNode
->GetScaleMode() == SmScaleMode::Height
)
993 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_TRUE
);
995 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_FALSE
);
996 ExportNodes(pRight
, nLevel
+ 1);
1002 void SmXMLExport::ExportRoot(const SmNode
* pNode
, int nLevel
)
1004 if (pNode
->GetSubNode(0))
1006 SvXMLElementExport
aRoot(*this, XML_NAMESPACE_MATH
, XML_MROOT
, true, true);
1007 ExportNodes(pNode
->GetSubNode(2), nLevel
+ 1);
1008 ExportNodes(pNode
->GetSubNode(0), nLevel
+ 1);
1012 SvXMLElementExport
aSqrt(*this, XML_NAMESPACE_MATH
, XML_MSQRT
, true, true);
1013 ExportNodes(pNode
->GetSubNode(2), nLevel
+ 1);
1017 void SmXMLExport::ExportOperator(const SmNode
* pNode
, int nLevel
)
1019 /*we need to either use content or font and size attributes
1021 SvXMLElementExport
aRow(*this, XML_NAMESPACE_MATH
, XML_MROW
, true, true);
1022 ExportNodes(pNode
->GetSubNode(0), nLevel
+ 1);
1023 ExportNodes(pNode
->GetSubNode(1), nLevel
+ 1);
1026 void SmXMLExport::ExportAttributes(const SmNode
* pNode
, int nLevel
)
1028 std::unique_ptr
<SvXMLElementExport
> pElement
;
1030 if (pNode
->GetToken().eType
== TUNDERLINE
)
1032 AddAttribute(XML_NAMESPACE_MATH
, XML_ACCENTUNDER
, XML_TRUE
);
1033 pElement
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MUNDER
, true, true));
1035 else if (pNode
->GetToken().eType
== TOVERSTRIKE
)
1037 // export as <menclose notation="horizontalstrike">
1038 AddAttribute(XML_NAMESPACE_MATH
, XML_NOTATION
, XML_HORIZONTALSTRIKE
);
1039 pElement
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MENCLOSE
, true, true));
1043 AddAttribute(XML_NAMESPACE_MATH
, XML_ACCENT
, XML_TRUE
);
1044 pElement
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MOVER
, true, true));
1047 ExportNodes(pNode
->GetSubNode(1), nLevel
+ 1);
1048 switch (pNode
->GetToken().eType
)
1052 //proper entity support required
1053 SvXMLElementExport
aMath(*this, XML_NAMESPACE_MATH
, XML_MO
, true, true);
1054 static constexpr OUStringLiteral nArse
= u
"\u00AF";
1055 GetDocHandler()->characters(nArse
);
1060 //proper entity support required
1061 SvXMLElementExport
aMath(*this, XML_NAMESPACE_MATH
, XML_MO
, true, true);
1062 static constexpr OUStringLiteral nArse
= u
"\u0332";
1063 GetDocHandler()->characters(nArse
);
1073 // make these wide accents stretchy
1074 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_TRUE
);
1075 ExportNodes(pNode
->GetSubNode(0), nLevel
+ 1);
1079 ExportNodes(pNode
->GetSubNode(0), nLevel
+ 1);
1084 static bool lcl_HasEffectOnMathvariant(const SmTokenType eType
)
1086 return eType
== TBOLD
|| eType
== TNBOLD
|| eType
== TITALIC
|| eType
== TNITALIC
1087 || eType
== TSANS
|| eType
== TSERIF
|| eType
== TFIXED
;
1090 void SmXMLExport::ExportFont(const SmNode
* pNode
, int nLevel
)
1092 // gather the mathvariant attribute relevant data from all
1093 // successively following SmFontNodes...
1095 int nBold
= -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true;
1096 int nItalic
= -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true;
1097 int nSansSerifFixed
= -1;
1098 SmTokenType eNodeType
= TUNKNOWN
;
1102 eNodeType
= pNode
->GetToken().eType
;
1103 if (!lcl_HasEffectOnMathvariant(eNodeType
))
1120 nSansSerifFixed
= 0;
1123 nSansSerifFixed
= 1;
1126 nSansSerifFixed
= 2;
1129 SAL_WARN("starmath", "unexpected case");
1131 // According to the parser every node that is to be evaluated here
1132 // has a single non-zero subnode at index 1!! Thus we only need to check
1133 // that single node for follow-up nodes that have an effect on the attribute.
1134 if (pNode
->GetNumSubNodes() > 1 && pNode
->GetSubNode(1)
1135 && lcl_HasEffectOnMathvariant(pNode
->GetSubNode(1)->GetToken().eType
))
1137 pNode
= pNode
->GetSubNode(1);
1144 switch (pNode
->GetToken().eType
)
1147 // No attribute needed. An <mphantom> element will be used below.
1151 nc
= pNode
->GetToken().cMathChar
.toUInt32(16);
1152 const OUString
& sssStr
= starmathdatabase::Identify_Color_MATHML(nc
).aIdent
;
1153 AddAttribute(XML_NAMESPACE_MATH
, XML_MATHCOLOR
, sssStr
);
1160 case TDVIPSNAMESCOL
:
1163 nc
= pNode
->GetToken().cMathChar
.toUInt32(16);
1164 OUString
ssStr("#" + Color(ColorTransparency
, nc
).AsRGBHEXString());
1165 AddAttribute(XML_NAMESPACE_MATH
, XML_MATHCOLOR
, ssStr
);
1170 const SmFontNode
* pFontNode
= static_cast<const SmFontNode
*>(pNode
);
1171 const Fraction
& aFrac
= pFontNode
->GetSizeParameter();
1173 OUStringBuffer sStrBuf
;
1174 switch (pFontNode
->GetSizeType())
1176 case FontSizeType::MULTIPLY
:
1177 ::sax::Converter::convertDouble(sStrBuf
,
1178 static_cast<double>(aFrac
* Fraction(100.00)));
1179 sStrBuf
.append('%');
1181 case FontSizeType::DIVIDE
:
1182 ::sax::Converter::convertDouble(sStrBuf
,
1183 static_cast<double>(Fraction(100.00) / aFrac
));
1184 sStrBuf
.append('%');
1186 case FontSizeType::ABSOLUT
:
1187 ::sax::Converter::convertDouble(sStrBuf
, static_cast<double>(aFrac
));
1188 sStrBuf
.append(GetXMLToken(XML_UNIT_PT
));
1192 //The problem here is that the wheels fall off because
1193 //font size is stored in 100th's of a mm not pts, and
1194 //rounding errors take their toll on the original
1195 //value specified in points.
1197 //Must fix StarMath to retain the original pt values
1198 Fraction aTemp
= Sm100th_mmToPts(pFontNode
->GetFont().GetFontSize().Height());
1200 if (pFontNode
->GetSizeType() == FontSizeType::MINUS
)
1205 double mytest
= static_cast<double>(aTemp
);
1207 mytest
= ::rtl::math::round(mytest
, 1);
1208 ::sax::Converter::convertDouble(sStrBuf
, mytest
);
1209 sStrBuf
.append(GetXMLToken(XML_UNIT_PT
));
1214 OUString
sStr(sStrBuf
.makeStringAndClear());
1215 AddAttribute(XML_NAMESPACE_MATH
, XML_MATHSIZE
, sStr
);
1226 // nBold: -1 = yet undefined; 0 = false; 1 = true;
1227 // nItalic: -1 = yet undefined; 0 = false; 1 = true;
1228 // nSansSerifFixed: -1 = undefined; 0 = sans; 1 = serif; 2 = fixed;
1229 const char* pText
= "normal";
1230 if (nSansSerifFixed
== -1 || nSansSerifFixed
== 1)
1233 if (nBold
== 1 && nItalic
!= 1)
1235 else if (nBold
!= 1 && nItalic
== 1)
1237 else if (nBold
== 1 && nItalic
== 1)
1238 pText
= "bold-italic";
1240 else if (nSansSerifFixed
== 0)
1242 pText
= "sans-serif";
1243 if (nBold
== 1 && nItalic
!= 1)
1244 pText
= "bold-sans-serif";
1245 else if (nBold
!= 1 && nItalic
== 1)
1246 pText
= "sans-serif-italic";
1247 else if (nBold
== 1 && nItalic
== 1)
1248 pText
= "sans-serif-bold-italic";
1250 else if (nSansSerifFixed
== 2)
1251 pText
= "monospace"; // no modifiers allowed for monospace ...
1254 SAL_WARN("starmath", "unexpected case");
1256 AddAttribute(XML_NAMESPACE_MATH
, XML_MATHVARIANT
, OUString::createFromAscii(pText
));
1263 // Wrap everything in an <mphantom> or <mstyle> element. These elements
1264 // are mrow-like, so ExportExpression doesn't need to add an explicit
1265 // <mrow> element. See #fdo 66283.
1266 SvXMLElementExport
aElement(*this, XML_NAMESPACE_MATH
,
1267 pNode
->GetToken().eType
== TPHANTOM
? XML_MPHANTOM
: XML_MSTYLE
,
1269 ExportExpression(pNode
, nLevel
, true);
1273 void SmXMLExport::ExportVerticalBrace(const SmVerticalBraceNode
* pNode
, int nLevel
)
1275 // "[body] overbrace [script]"
1277 // Position body, overbrace and script vertically. First place the overbrace
1278 // OVER the body and then the script OVER this expression.
1282 // XXXXXX[body]XXXXXXX
1284 // Similarly for the underbrace construction.
1288 switch (pNode
->GetToken().eType
)
1299 SvXMLElementExport
aOver1(*this, XML_NAMESPACE_MATH
, which
, true, true);
1301 // using accents will draw the over-/underbraces too close to the base
1302 // see http://www.w3.org/TR/MathML2/chapter3.html#id.3.4.5.2
1303 // also XML_ACCENT is illegal with XML_MUNDER. Thus no XML_ACCENT attribute here!
1304 SvXMLElementExport
aOver2(*this, XML_NAMESPACE_MATH
, which
, true, true);
1305 ExportNodes(pNode
->Body(), nLevel
);
1306 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_TRUE
);
1307 ExportNodes(pNode
->Brace(), nLevel
);
1309 ExportNodes(pNode
->Script(), nLevel
);
1312 void SmXMLExport::ExportMatrix(const SmNode
* pNode
, int nLevel
)
1314 SvXMLElementExport
aTable(*this, XML_NAMESPACE_MATH
, XML_MTABLE
, true, true);
1315 const SmMatrixNode
* pMatrix
= static_cast<const SmMatrixNode
*>(pNode
);
1317 for (sal_uInt16 y
= 0; y
< pMatrix
->GetNumRows(); y
++)
1319 SvXMLElementExport
aRow(*this, XML_NAMESPACE_MATH
, XML_MTR
, true, true);
1320 for (sal_uInt16 x
= 0; x
< pMatrix
->GetNumCols(); x
++)
1322 if (const SmNode
* pTemp
= pNode
->GetSubNode(i
++))
1324 if (pTemp
->GetType() == SmNodeType::Align
&& pTemp
->GetToken().eType
!= TALIGNC
)
1326 // A left or right alignment is specified on this cell,
1327 // attach the corresponding columnalign attribute.
1328 AddAttribute(XML_NAMESPACE_MATH
, XML_COLUMNALIGN
,
1329 pTemp
->GetToken().eType
== TALIGNL
? XML_LEFT
: XML_RIGHT
);
1331 SvXMLElementExport
aCell(*this, XML_NAMESPACE_MATH
, XML_MTD
, true, true);
1332 ExportNodes(pTemp
, nLevel
+ 1);
1338 void SmXMLExport::ExportNodes(const SmNode
* pNode
, int nLevel
)
1342 switch (pNode
->GetType())
1344 case SmNodeType::Table
:
1345 ExportTable(pNode
, nLevel
);
1347 case SmNodeType::Align
:
1348 case SmNodeType::Bracebody
:
1349 case SmNodeType::Expression
:
1350 ExportExpression(pNode
, nLevel
);
1352 case SmNodeType::Line
:
1353 ExportLine(pNode
, nLevel
);
1355 case SmNodeType::Text
:
1358 case SmNodeType::GlyphSpecial
:
1359 case SmNodeType::Math
:
1361 sal_Unicode cTmp
= 0;
1362 const SmTextNode
* pTemp
= static_cast<const SmTextNode
*>(pNode
);
1363 if (!pTemp
->GetText().isEmpty())
1364 cTmp
= ConvertMathToMathML(pTemp
->GetText()[0]);
1367 // no conversion to MathML implemented -> export it as text
1368 // thus at least it will not vanish into nothing
1373 switch (pNode
->GetToken().eType
)
1376 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_TRUE
);
1381 //To fully handle generic MathML we need to implement the full
1382 //operator dictionary, we will generate MathML with explicit
1383 //stretchiness for now.
1384 sal_Int16 nLength
= GetAttrList().getLength();
1385 bool bAddStretch
= true;
1386 for (sal_Int16 i
= 0; i
< nLength
; i
++)
1388 OUString sLocalName
;
1389 sal_uInt16 nPrefix
= GetNamespaceMap().GetKeyByAttrName(
1390 GetAttrList().getNameByIndex(i
), &sLocalName
);
1392 if ((XML_NAMESPACE_MATH
== nPrefix
) && IsXMLToken(sLocalName
, XML_STRETCHY
))
1394 bAddStretch
= false;
1400 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_FALSE
);
1407 Special
: //SmNodeType::Special requires some sort of Entity preservation in the XML engine.
1408 case SmNodeType::MathIdent
:
1409 case SmNodeType::Place
:
1412 case SmNodeType::BinHor
:
1413 ExportBinaryHorizontal(pNode
, nLevel
);
1415 case SmNodeType::UnHor
:
1416 ExportUnaryHorizontal(pNode
, nLevel
);
1418 case SmNodeType::Brace
:
1419 ExportBrace(pNode
, nLevel
);
1421 case SmNodeType::BinVer
:
1422 ExportBinaryVertical(pNode
, nLevel
);
1424 case SmNodeType::BinDiagonal
:
1425 ExportBinaryDiagonal(pNode
, nLevel
);
1427 case SmNodeType::SubSup
:
1428 ExportSubSupScript(pNode
, nLevel
);
1430 case SmNodeType::Root
:
1431 ExportRoot(pNode
, nLevel
);
1433 case SmNodeType::Oper
:
1434 ExportOperator(pNode
, nLevel
);
1436 case SmNodeType::Attribute
:
1437 ExportAttributes(pNode
, nLevel
);
1439 case SmNodeType::Font
:
1440 ExportFont(pNode
, nLevel
);
1442 case SmNodeType::VerticalBrace
:
1443 ExportVerticalBrace(static_cast<const SmVerticalBraceNode
*>(pNode
), nLevel
);
1445 case SmNodeType::Matrix
:
1446 ExportMatrix(pNode
, nLevel
);
1448 case SmNodeType::Blank
:
1452 SAL_WARN("starmath", "Warning: failed to export a node?");
1457 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */