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