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/uno/Any.h>
33 #include <rtl/math.hxx>
34 #include <sfx2/frame.hxx>
35 #include <sfx2/docfile.hxx>
36 #include <sfx2/sfxsids.hrc>
37 #include <osl/diagnose.h>
38 #include <unotools/saveopt.hxx>
39 #include <svl/itemset.hxx>
40 #include <svl/stritem.hxx>
41 #include <comphelper/fileformat.h>
42 #include <comphelper/processfactory.hxx>
43 #include <unotools/streamwrap.hxx>
44 #include <sax/tools/converter.hxx>
45 #include <xmloff/xmlnmspe.hxx>
46 #include <xmloff/xmltoken.hxx>
47 #include <xmloff/nmspmap.hxx>
48 #include <xmloff/attrlist.hxx>
49 #include <comphelper/genericpropertyset.hxx>
50 #include <comphelper/servicehelper.hxx>
51 #include <comphelper/propertysetinfo.hxx>
52 #include <tools/diagnose_ex.h>
53 #include <sal/log.hxx>
58 #include "mathmlexport.hxx"
59 #include <strings.hrc>
60 #include <unomodel.hxx>
61 #include <document.hxx>
62 #include <utility.hxx>
63 #include "cfgitem.hxx"
65 using namespace ::com::sun::star::beans
;
66 using namespace ::com::sun::star::document
;
67 using namespace ::com::sun::star::lang
;
68 using namespace ::com::sun::star::uno
;
69 using namespace ::com::sun::star
;
70 using namespace ::xmloff::token
;
74 bool IsInPrivateUseArea( sal_Unicode cChar
) { return 0xE000 <= cChar
&& cChar
<= 0xF8FF; }
76 sal_Unicode
ConvertMathToMathML( sal_Unicode cChar
)
78 sal_Unicode cRes
= cChar
;
79 if (IsInPrivateUseArea( cChar
))
81 SAL_WARN("starmath", "Error: private use area characters should no longer be in use!" );
82 cRes
= u
'@'; // just some character that should easily be notice as odd in the context
89 bool SmXMLExportWrapper::Export(SfxMedium
&rMedium
)
92 uno::Reference
<uno::XComponentContext
> xContext(comphelper::getProcessComponentContext());
95 uno::Reference
< lang::XComponent
> xModelComp(xModel
, uno::UNO_QUERY
);
97 bool bEmbedded
= false;
98 uno::Reference
<lang::XUnoTunnel
> xTunnel(xModel
,uno::UNO_QUERY
);
99 SmModel
*pModel
= reinterpret_cast<SmModel
*>
100 (xTunnel
->getSomething(SmModel::getUnoTunnelId()));
102 SmDocShell
*pDocShell
= pModel
?
103 static_cast<SmDocShell
*>(pModel
->GetObjectShell()) : nullptr;
105 SfxObjectCreateMode::EMBEDDED
== pDocShell
->GetCreateMode() )
108 uno::Reference
<task::XStatusIndicator
> xStatusIndicator
;
111 if (pDocShell
/*&& pDocShell->GetMedium()*/)
113 OSL_ENSURE( pDocShell
->GetMedium() == &rMedium
,
114 "different SfxMedium found" );
116 SfxItemSet
* pSet
= rMedium
.GetItemSet();
119 const SfxUnoAnyItem
* pItem
= static_cast<const SfxUnoAnyItem
*>(
120 pSet
->GetItem(SID_PROGRESS_STATUSBAR_CONTROL
) );
122 pItem
->GetValue() >>= xStatusIndicator
;
126 // set progress range and start status indicator
127 if (xStatusIndicator
.is())
129 sal_Int32 nProgressRange
= bFlat
? 1 : 3;
130 xStatusIndicator
->start(SmResId(STR_STATSTR_WRITING
),
136 // create XPropertySet with three properties for status indicator
137 comphelper::PropertyMapEntry aInfoMap
[] =
139 { OUString("UsePrettyPrinting"), 0,
140 cppu::UnoType
<bool>::get(),
141 beans::PropertyAttribute::MAYBEVOID
, 0},
142 { OUString("BaseURI"), 0,
143 ::cppu::UnoType
<OUString
>::get(),
144 beans::PropertyAttribute::MAYBEVOID
, 0 },
145 { OUString("StreamRelPath"), 0,
146 ::cppu::UnoType
<OUString
>::get(),
147 beans::PropertyAttribute::MAYBEVOID
, 0 },
148 { OUString("StreamName"), 0,
149 ::cppu::UnoType
<OUString
>::get(),
150 beans::PropertyAttribute::MAYBEVOID
, 0 },
151 { OUString(), 0, css::uno::Type(), 0, 0 }
153 uno::Reference
< beans::XPropertySet
> xInfoSet(
154 comphelper::GenericPropertySet_CreateInstance(
155 new comphelper::PropertySetInfo( aInfoMap
) ) );
157 SvtSaveOptions aSaveOpt
;
158 bool bUsePrettyPrinting( bFlat
|| aSaveOpt
.IsPrettyPrinting() );
159 xInfoSet
->setPropertyValue( "UsePrettyPrinting", Any(bUsePrettyPrinting
) );
162 OUString
sPropName( "BaseURI" );
163 xInfoSet
->setPropertyValue( sPropName
, makeAny( rMedium
.GetBaseURL( true ) ) );
166 if (xStatusIndicator
.is())
167 xStatusIndicator
->setValue(nSteps
++);
168 if (!bFlat
) //Storage (Package) of Stream
170 uno::Reference
< embed::XStorage
> xStg
= rMedium
.GetOutputStorage();
171 bool bOASIS
= ( SotStorage::GetVersion( xStg
) > SOFFICE_FILEFORMAT_60
);
173 // TODO/LATER: handle the case of embedded links gracefully
174 if ( bEmbedded
) //&& !pStg->IsRoot() )
177 if ( rMedium
.GetItemSet() )
179 const SfxStringItem
* pDocHierarchItem
= static_cast<const SfxStringItem
*>(
180 rMedium
.GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME
) );
181 if ( pDocHierarchItem
)
182 aName
= pDocHierarchItem
->GetValue();
185 if ( !aName
.isEmpty() )
187 sPropName
= "StreamRelPath";
188 xInfoSet
->setPropertyValue( sPropName
, makeAny( aName
) );
194 if (xStatusIndicator
.is())
195 xStatusIndicator
->setValue(nSteps
++);
197 bRet
= WriteThroughComponent(
198 xStg
, xModelComp
, "meta.xml", xContext
, xInfoSet
,
199 (bOASIS
? "com.sun.star.comp.Math.XMLOasisMetaExporter"
200 : "com.sun.star.comp.Math.XMLMetaExporter"));
204 if (xStatusIndicator
.is())
205 xStatusIndicator
->setValue(nSteps
++);
207 bRet
= WriteThroughComponent(
208 xStg
, xModelComp
, "content.xml", xContext
, xInfoSet
,
209 "com.sun.star.comp.Math.XMLContentExporter");
214 if (xStatusIndicator
.is())
215 xStatusIndicator
->setValue(nSteps
++);
217 bRet
= WriteThroughComponent(
218 xStg
, xModelComp
, "settings.xml", xContext
, xInfoSet
,
219 (bOASIS
? "com.sun.star.comp.Math.XMLOasisSettingsExporter"
220 : "com.sun.star.comp.Math.XMLSettingsExporter") );
225 SvStream
*pStream
= rMedium
.GetOutStream();
226 uno::Reference
<io::XOutputStream
> xOut(
227 new utl::OOutputStreamWrapper(*pStream
) );
229 if (xStatusIndicator
.is())
230 xStatusIndicator
->setValue(nSteps
++);
232 bRet
= WriteThroughComponent(
233 xOut
, xModelComp
, xContext
, xInfoSet
,
234 "com.sun.star.comp.Math.XMLContentExporter");
237 if (xStatusIndicator
.is())
238 xStatusIndicator
->end();
244 /// export through an XML exporter component (output stream version)
245 bool SmXMLExportWrapper::WriteThroughComponent(
246 const Reference
<io::XOutputStream
>& xOutputStream
,
247 const Reference
<XComponent
>& xComponent
,
248 Reference
<uno::XComponentContext
> const & rxContext
,
249 Reference
<beans::XPropertySet
> const & rPropSet
,
250 const sal_Char
* pComponentName
)
252 OSL_ENSURE(xOutputStream
.is(), "I really need an output stream!");
253 OSL_ENSURE(xComponent
.is(), "Need component!");
254 OSL_ENSURE(nullptr != pComponentName
, "Need component name!");
257 Reference
< xml::sax::XWriter
> xSaxWriter
= xml::sax::Writer::create(rxContext
);
259 // connect XML writer to output stream
260 xSaxWriter
->setOutputStream( xOutputStream
);
262 // prepare arguments (prepend doc handler to given arguments)
263 Reference
<xml::sax::XDocumentHandler
> xDocHandler( xSaxWriter
,UNO_QUERY
);
265 Sequence
<Any
> aArgs( 2 );
266 aArgs
[0] <<= xDocHandler
;
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 uno::Reference
<lang::XUnoTunnel
> xFilterTunnel( xFilter
, uno::UNO_QUERY
);
288 SmXMLExport
*pFilter
= reinterpret_cast< SmXMLExport
* >(
289 sal::static_int_cast
< sal_uIntPtr
>(
290 xFilterTunnel
->getSomething( SmXMLExport::getUnoTunnelId() )));
291 return pFilter
== nullptr || pFilter
->GetSuccess();
295 /// export through an XML exporter component (storage version)
296 bool SmXMLExportWrapper::WriteThroughComponent(
297 const Reference
< embed::XStorage
>& xStorage
,
298 const Reference
<XComponent
>& xComponent
,
299 const sal_Char
* pStreamName
,
300 Reference
<uno::XComponentContext
> const & rxContext
,
301 Reference
<beans::XPropertySet
> const & rPropSet
,
302 const sal_Char
* pComponentName
305 OSL_ENSURE(xStorage
.is(), "Need storage!");
306 OSL_ENSURE(nullptr != pStreamName
, "Need stream name!");
309 Reference
< io::XStream
> xStream
;
310 OUString sStreamName
= OUString::createFromAscii(pStreamName
);
313 xStream
= xStorage
->openStreamElement( sStreamName
,
314 embed::ElementModes::READWRITE
| embed::ElementModes::TRUNCATE
);
316 catch ( const uno::Exception
& )
318 DBG_UNHANDLED_EXCEPTION("starmath", "Can't create output stream in package" );
322 uno::Reference
< beans::XPropertySet
> xSet( xStream
, uno::UNO_QUERY
);
323 xSet
->setPropertyValue( "MediaType", Any(OUString( "text/xml" )) );
325 // all streams must be encrypted in encrypted document
326 xSet
->setPropertyValue( "UseCommonStoragePasswordEncryption", Any(true) );
331 rPropSet
->setPropertyValue( "StreamName", makeAny( sStreamName
) );
335 bool bRet
= WriteThroughComponent( xStream
->getOutputStream(), xComponent
, rxContext
,
336 rPropSet
, pComponentName
);
341 SmXMLExport::SmXMLExport(
342 const css::uno::Reference
< css::uno::XComponentContext
>& rContext
,
343 OUString
const & implementationName
, SvXMLExportFlags nExportFlags
)
344 : SvXMLExport(util::MeasureUnit::INCH
, rContext
, implementationName
, XML_MATH
,
351 sal_Int64 SAL_CALL
SmXMLExport::getSomething(
352 const uno::Sequence
< sal_Int8
>& rId
)
354 if ( rId
.getLength() == 16 &&
355 0 == memcmp( getUnoTunnelId().getConstArray(),
356 rId
.getConstArray(), 16 ) )
357 return sal::static_int_cast
< sal_Int64
>(reinterpret_cast< sal_uIntPtr
>(this));
359 return SvXMLExport::getSomething( rId
);
364 class theSmXMLExportUnoTunnelId
: public rtl::Static
< UnoTunnelIdInit
, theSmXMLExportUnoTunnelId
> {};
367 const uno::Sequence
< sal_Int8
> & SmXMLExport::getUnoTunnelId() throw()
369 return theSmXMLExportUnoTunnelId::get().getSeq();
372 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
373 Math_XMLExporter_get_implementation(css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const &)
375 return cppu::acquire(new SmXMLExport(context
, "com.sun.star.comp.Math.XMLExporter", SvXMLExportFlags::OASIS
|SvXMLExportFlags::ALL
));
378 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
379 Math_XMLMetaExporter_get_implementation(css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const &)
381 return cppu::acquire(new SmXMLExport(context
, "com.sun.star.comp.Math.XMLMetaExporter", SvXMLExportFlags::META
));
384 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
385 Math_XMLOasisMetaExporter_get_implementation(css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const &)
387 return cppu::acquire(new SmXMLExport(context
, "com.sun.star.comp.Math.XMLOasisMetaExporter", SvXMLExportFlags::OASIS
|SvXMLExportFlags::META
));
390 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
391 Math_XMLSettingsExporter_get_implementation(css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const &)
393 return cppu::acquire(new SmXMLExport(context
, "com.sun.star.comp.Math.XMLSettingsExporter", SvXMLExportFlags::SETTINGS
));
396 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
397 Math_XMLOasisSettingsExporter_get_implementation(css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const &)
399 return cppu::acquire(new SmXMLExport(context
, "com.sun.star.comp.Math.XMLOasisSettingsExporter", SvXMLExportFlags::OASIS
|SvXMLExportFlags::SETTINGS
));
402 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
403 Math_XMLContentExporter_get_implementation(css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const &)
405 return cppu::acquire(new SmXMLExport(context
, "com.sun.star.comp.Math.XMLContentExporter", SvXMLExportFlags::OASIS
|SvXMLExportFlags::CONTENT
));
408 ErrCode
SmXMLExport::exportDoc(enum XMLTokenEnum eClass
)
410 if ( !(getExportFlags() & SvXMLExportFlags::CONTENT
) )
412 SvXMLExport::exportDoc( eClass
);
416 uno::Reference
<frame::XModel
> xModel
= GetModel();
417 uno::Reference
<lang::XUnoTunnel
> xTunnel(xModel
,uno::UNO_QUERY
);
418 SmModel
*pModel
= reinterpret_cast<SmModel
*>
419 (xTunnel
->getSomething(SmModel::getUnoTunnelId()));
423 SmDocShell
*pDocShell
=
424 static_cast<SmDocShell
*>(pModel
->GetObjectShell());
425 pTree
= pDocShell
->GetFormulaTree();
426 aText
= pDocShell
->GetText();
429 GetDocHandler()->startDocument();
431 addChaffWhenEncryptedStorage();
434 SvXMLAttributeList
&rList
= GetAttrList();
436 // make use of a default namespace
437 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)
438 GetNamespaceMap_().Add( OUString(), GetXMLToken(XML_N_MATH
), XML_NAMESPACE_MATH
);
440 rList
.AddAttribute(GetNamespaceMap().GetAttrNameByKey(XML_NAMESPACE_MATH
),
441 GetNamespaceMap().GetNameByKey( XML_NAMESPACE_MATH
));
443 //I think we need something like ImplExportEntities();
445 GetDocHandler()->endDocument();
452 void SmXMLExport::ExportContent_()
454 uno::Reference
<frame::XModel
> xModel
= GetModel();
455 uno::Reference
<lang::XUnoTunnel
> xTunnel(xModel
,uno::UNO_QUERY
);
456 SmModel
*pModel
= reinterpret_cast<SmModel
*>
457 (xTunnel
->getSomething(SmModel::getUnoTunnelId()));
458 SmDocShell
*pDocShell
= pModel
?
459 static_cast<SmDocShell
*>(pModel
->GetObjectShell()) : nullptr;
460 OSL_ENSURE( pDocShell
, "doc shell missing" );
462 if (pDocShell
&& !pDocShell
->GetFormat().IsTextmode())
464 // If the Math equation is not in text mode, we attach a display="block"
465 // attribute on the <math> root. We don't do anything if it is in
466 // text mode, the default display="inline" value will be used.
467 AddAttribute(XML_NAMESPACE_MATH
, XML_DISPLAY
, XML_BLOCK
);
469 SvXMLElementExport
aEquation(*this, XML_NAMESPACE_MATH
, XML_MATH
, true, true);
470 std::unique_ptr
<SvXMLElementExport
> pSemantics
;
472 if (!aText
.isEmpty())
474 pSemantics
.reset( new SvXMLElementExport(*this, XML_NAMESPACE_MATH
,
475 XML_SEMANTICS
, true, true) );
478 ExportNodes(pTree
, 0);
480 if (!aText
.isEmpty())
482 // Convert symbol names
485 SmParser
&rParser
= pDocShell
->GetParser();
486 bool bVal
= rParser
.IsExportSymbolNames();
487 rParser
.SetExportSymbolNames( true );
488 auto pTmpTree
= rParser
.Parse( aText
);
489 aText
= rParser
.GetText();
491 rParser
.SetExportSymbolNames( bVal
);
494 AddAttribute(XML_NAMESPACE_MATH
, XML_ENCODING
,
495 OUString("StarMath 5.0"));
496 SvXMLElementExport
aAnnotation(*this, XML_NAMESPACE_MATH
,
497 XML_ANNOTATION
, true, false);
498 GetDocHandler()->characters( aText
);
502 void SmXMLExport::GetViewSettings( Sequence
< PropertyValue
>& aProps
)
504 uno::Reference
<frame::XModel
> xModel
= GetModel();
508 uno::Reference
<lang::XUnoTunnel
> xTunnel(xModel
,uno::UNO_QUERY
);
509 SmModel
*pModel
= reinterpret_cast<SmModel
*>
510 (xTunnel
->getSomething(SmModel::getUnoTunnelId()));
515 SmDocShell
*pDocShell
=
516 static_cast<SmDocShell
*>(pModel
->GetObjectShell());
521 PropertyValue
*pValue
= aProps
.getArray();
522 sal_Int32 nIndex
= 0;
524 tools::Rectangle
aRect( pDocShell
->GetVisArea() );
526 pValue
[nIndex
].Name
= "ViewAreaTop";
527 pValue
[nIndex
++].Value
<<= aRect
.Top();
529 pValue
[nIndex
].Name
= "ViewAreaLeft";
530 pValue
[nIndex
++].Value
<<= aRect
.Left();
532 pValue
[nIndex
].Name
= "ViewAreaWidth";
533 pValue
[nIndex
++].Value
<<= aRect
.GetWidth();
535 pValue
[nIndex
].Name
= "ViewAreaHeight";
536 pValue
[nIndex
++].Value
<<= aRect
.GetHeight();
539 void SmXMLExport::GetConfigurationSettings( Sequence
< PropertyValue
> & rProps
)
541 Reference
< XPropertySet
> xProps ( GetModel(), UNO_QUERY
);
544 Reference
< XPropertySetInfo
> xPropertySetInfo
= xProps
->getPropertySetInfo();
545 if (xPropertySetInfo
.is())
547 Sequence
< Property
> aProps
= xPropertySetInfo
->getProperties();
548 if (const sal_Int32 nCount
= aProps
.getLength())
550 rProps
.realloc(nCount
);
551 SmMathConfig
* pConfig
= SM_MOD()->GetConfig();
552 const bool bUsedSymbolsOnly
= pConfig
&& pConfig
->IsSaveOnlyUsedSymbols();
554 std::transform(aProps
.begin(), aProps
.end(), rProps
.begin(),
555 [bUsedSymbolsOnly
, &xProps
](Property
& prop
) {
557 if (prop
.Name
!= "Formula" && prop
.Name
!= "BasicLibraries"
558 && prop
.Name
!= "DialogLibraries"
559 && prop
.Name
!= "RuntimeUID")
561 aRet
.Name
= prop
.Name
;
562 OUString
aActualName(prop
.Name
);
563 // handle 'save used symbols only'
564 if (bUsedSymbolsOnly
&& prop
.Name
== "Symbols")
565 aActualName
= "UserDefinedSymbolsInUse";
566 aRet
.Value
= xProps
->getPropertyValue(aActualName
);
575 void SmXMLExport::ExportLine(const SmNode
*pNode
, int nLevel
)
577 ExportExpression(pNode
, nLevel
);
580 void SmXMLExport::ExportBinaryHorizontal(const SmNode
*pNode
, int nLevel
)
582 TG nGroup
= pNode
->GetToken().nGroup
;
584 std::unique_ptr
<SvXMLElementExport
> pRow( new SvXMLElementExport(*this,
585 XML_NAMESPACE_MATH
, XML_MROW
, true, true) );
587 // Unfold the binary tree structure as long as the nodes are SmBinHorNode
588 // with the same nGroup. This will reduce the number of nested <mrow>
589 // elements e.g. we only need three <mrow> levels to export
591 // "a*b*c*d+e*f*g*h+i*j*k*l = a*b*c*d+e*f*g*h+i*j*k*l =
592 // a*b*c*d+e*f*g*h+i*j*k*l = a*b*c*d+e*f*g*h+i*j*k*l"
594 // See https://www.libreoffice.org/bugzilla/show_bug.cgi?id=66081
595 ::std::stack
< const SmNode
* > s
;
599 const SmNode
*node
= s
.top();
601 if (node
->GetType() != SmNodeType::BinHor
|| node
->GetToken().nGroup
!= nGroup
)
603 ExportNodes(node
, nLevel
+1);
606 const SmBinHorNode
* binNode
= static_cast<const SmBinHorNode
*>(node
);
607 s
.push(binNode
->RightOperand());
608 s
.push(binNode
->Symbol());
609 s
.push(binNode
->LeftOperand());
613 void SmXMLExport::ExportUnaryHorizontal(const SmNode
*pNode
, int nLevel
)
615 ExportExpression(pNode
, nLevel
);
618 void SmXMLExport::ExportExpression(const SmNode
*pNode
, int nLevel
,
619 bool bNoMrowContainer
/*=false*/)
621 std::unique_ptr
<SvXMLElementExport
> pRow
;
622 size_t nSize
= pNode
->GetNumSubNodes();
624 // #i115443: nodes of type expression always need to be grouped with mrow statement
625 if (!bNoMrowContainer
&&
626 (nSize
> 1 || pNode
->GetType() == SmNodeType::Expression
))
627 pRow
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MROW
, true, true));
629 for (size_t i
= 0; i
< nSize
; ++i
)
631 if (const SmNode
*pTemp
= pNode
->GetSubNode(i
))
632 ExportNodes(pTemp
, nLevel
+1);
636 void SmXMLExport::ExportBinaryVertical(const SmNode
*pNode
, int nLevel
)
638 assert(pNode
->GetNumSubNodes() == 3);
639 const SmNode
*pNum
= pNode
->GetSubNode(0);
640 const SmNode
*pDenom
= pNode
->GetSubNode(2);
641 if (pNum
->GetType() == SmNodeType::Align
&& pNum
->GetToken().eType
!= TALIGNC
)
643 // A left or right alignment is specified on the numerator:
644 // attach the corresponding numalign attribute.
645 AddAttribute(XML_NAMESPACE_MATH
, XML_NUMALIGN
,
646 pNum
->GetToken().eType
== TALIGNL
? XML_LEFT
: XML_RIGHT
);
648 if (pDenom
->GetType() == SmNodeType::Align
&& pDenom
->GetToken().eType
!= TALIGNC
)
650 // A left or right alignment is specified on the denominator:
651 // attach the corresponding denomalign attribute.
652 AddAttribute(XML_NAMESPACE_MATH
, XML_DENOMALIGN
,
653 pDenom
->GetToken().eType
== TALIGNL
? XML_LEFT
: XML_RIGHT
);
655 SvXMLElementExport
aFraction(*this, XML_NAMESPACE_MATH
, XML_MFRAC
, true, true);
656 ExportNodes(pNum
, nLevel
);
657 ExportNodes(pDenom
, nLevel
);
660 void SmXMLExport::ExportBinaryDiagonal(const SmNode
*pNode
, int nLevel
)
662 assert(pNode
->GetNumSubNodes() == 3);
664 if (pNode
->GetToken().eType
== TWIDESLASH
)
667 // export the node as <mfrac bevelled="true">
668 AddAttribute(XML_NAMESPACE_MATH
, XML_BEVELLED
, XML_TRUE
);
669 SvXMLElementExport
aFraction(*this, XML_NAMESPACE_MATH
, XML_MFRAC
,
671 ExportNodes(pNode
->GetSubNode(0), nLevel
);
672 ExportNodes(pNode
->GetSubNode(1), nLevel
);
677 // We can not use <mfrac> to a backslash, so just use <mo>\</mo>
678 std::unique_ptr
<SvXMLElementExport
> pRow( new SvXMLElementExport(*this,
679 XML_NAMESPACE_MATH
, XML_MROW
, true, true) );
681 ExportNodes(pNode
->GetSubNode(0), nLevel
);
683 { // Scoping for <mo> creation
684 SvXMLElementExport
aMo(*this, XML_NAMESPACE_MATH
, XML_MO
,
686 sal_Unicode
const nArse
[2] = {MS_BACKSLASH
,0x00};
687 GetDocHandler()->characters(nArse
);
690 ExportNodes(pNode
->GetSubNode(1), nLevel
);
694 void SmXMLExport::ExportTable(const SmNode
*pNode
, int nLevel
)
696 std::unique_ptr
<SvXMLElementExport
> pTable
;
698 size_t nSize
= pNode
->GetNumSubNodes();
700 //If the list ends in newline then the last entry has
701 //no subnodes, the newline is superfluous so we just drop
702 //the last node, inclusion would create a bad MathML
706 const SmNode
*pLine
= pNode
->GetSubNode(nSize
-1);
707 if (pLine
->GetType() == SmNodeType::Line
&& pLine
->GetNumSubNodes() == 1 &&
708 pLine
->GetSubNode(0) != nullptr &&
709 pLine
->GetSubNode(0)->GetToken().eType
== TNEWLINE
)
713 // try to avoid creating a mtable element when the formula consists only
714 // of a single output line
715 if (nLevel
|| (nSize
>1))
716 pTable
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MTABLE
, true, true));
718 for (size_t i
= 0; i
< nSize
; ++i
)
720 if (const SmNode
*pTemp
= pNode
->GetSubNode(i
))
722 SvXMLElementExport
*pRow
=nullptr;
723 SvXMLElementExport
*pCell
=nullptr;
726 pRow
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MTR
, true, true);
727 SmTokenType eAlign
= TALIGNC
;
728 if (pTemp
->GetType() == SmNodeType::Align
)
730 // For Binom() and Stack() constructions, the SmNodeType::Align nodes
731 // are direct children.
732 // binom{alignl ...}{alignr ...} and
733 // stack{alignl ... ## alignr ... ## ...}
734 eAlign
= pTemp
->GetToken().eType
;
736 else if (pTemp
->GetType() == SmNodeType::Line
&&
737 pTemp
->GetNumSubNodes() == 1 &&
738 pTemp
->GetSubNode(0) &&
739 pTemp
->GetSubNode(0)->GetType() == SmNodeType::Align
)
741 // For the Table() construction, the SmNodeType::Align node is a child
742 // of an SmNodeType::Line node.
743 // alignl ... newline alignr ... newline ...
744 eAlign
= pTemp
->GetSubNode(0)->GetToken().eType
;
746 if (eAlign
!= TALIGNC
)
748 // If a left or right alignment is specified on this line,
749 // attach the corresponding columnalign attribute.
750 AddAttribute(XML_NAMESPACE_MATH
, XML_COLUMNALIGN
,
751 eAlign
== TALIGNL
? XML_LEFT
: XML_RIGHT
);
753 pCell
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MTD
, true, true);
755 ExportNodes(pTemp
, nLevel
+1);
762 void SmXMLExport::ExportMath(const SmNode
*pNode
)
764 const SmTextNode
*pTemp
= static_cast<const SmTextNode
*>(pNode
);
765 std::unique_ptr
<SvXMLElementExport
> pMath
;
767 if (pNode
->GetType() == SmNodeType::Math
|| pNode
->GetType() == SmNodeType::GlyphSpecial
)
769 // Export SmNodeType::Math and SmNodeType::GlyphSpecial symbols as <mo> elements
770 pMath
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MO
, true, false));
772 else if (pNode
->GetType() == SmNodeType::Special
)
774 bool bIsItalic
= IsItalic(pNode
->GetFont());
776 AddAttribute(XML_NAMESPACE_MATH
, XML_MATHVARIANT
, XML_NORMAL
);
777 pMath
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MI
, true, false));
781 // Export SmNodeType::MathIdent and SmNodeType::Place symbols as <mi> elements:
782 // - These math symbols should not be drawn slanted. Hence we should
783 // attach a mathvariant="normal" attribute to single-char <mi> elements
784 // that are not mathematical alphanumeric symbol. For simplicity and to
785 // work around browser limitations, we always attach such an attribute.
786 // - The MathML specification suggests to use empty <mi> elements as
787 // placeholders but they won't be visible in most MathML rendering
788 // engines so let's use an empty square for SmNodeType::Place instead.
789 AddAttribute(XML_NAMESPACE_MATH
, XML_MATHVARIANT
, XML_NORMAL
);
790 pMath
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MI
, true, false));
792 sal_Unicode nArse
[2];
793 nArse
[0] = pTemp
->GetText()[0];
794 sal_Unicode cTmp
= ConvertMathToMathML( nArse
[0] );
797 OSL_ENSURE(nArse
[0] != 0xffff,"Non existent symbol");
799 GetDocHandler()->characters(nArse
);
802 void SmXMLExport::ExportText(const SmNode
*pNode
)
804 std::unique_ptr
<SvXMLElementExport
> pText
;
805 const SmTextNode
*pTemp
= static_cast<const SmTextNode
*>(pNode
);
806 switch (pNode
->GetToken().eType
)
811 //Note that we change the fontstyle to italic for strings that
812 //are italic and longer than a single character.
813 bool bIsItalic
= IsItalic( pTemp
->GetFont() );
814 if ((pTemp
->GetText().getLength() > 1) && bIsItalic
)
815 AddAttribute(XML_NAMESPACE_MATH
, XML_MATHVARIANT
, XML_ITALIC
);
816 else if ((pTemp
->GetText().getLength() == 1) && !bIsItalic
)
817 AddAttribute(XML_NAMESPACE_MATH
, XML_MATHVARIANT
, XML_NORMAL
);
818 pText
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MI
, true, false));
822 pText
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MN
, true, false));
825 pText
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MTEXT
, true, false));
828 GetDocHandler()->characters(pTemp
->GetText());
831 void SmXMLExport::ExportBlank(const SmNode
*pNode
)
833 const SmBlankNode
*pTemp
= static_cast<const SmBlankNode
*>(pNode
);
834 //!! exports an <mspace> element. Note that for example "~_~" is allowed in
835 //!! Math (so it has no sense at all) but must not result in an empty
836 //!! <msub> tag in MathML !!
838 if (pTemp
->GetBlankNum() != 0)
840 // Attach a width attribute. We choose the (somewhat arbitrary) values
841 // ".5em" for a small gap '`' and "2em" for a large gap '~'.
842 // (see SmBlankNode::IncreaseBy for how pTemp->mnNum is set).
843 OUStringBuffer sStrBuf
;
844 ::sax::Converter::convertDouble(sStrBuf
, pTemp
->GetBlankNum() * .5);
845 sStrBuf
.append("em");
846 AddAttribute(XML_NAMESPACE_MATH
, XML_WIDTH
, sStrBuf
.getStr());
849 std::unique_ptr
<SvXMLElementExport
> pText(
850 new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MSPACE
,
853 GetDocHandler()->characters( OUString() );
856 void SmXMLExport::ExportSubSupScript(const SmNode
*pNode
, int nLevel
)
858 const SmNode
*pSub
= nullptr;
859 const SmNode
*pSup
= nullptr;
860 const SmNode
*pCSub
= nullptr;
861 const SmNode
*pCSup
= nullptr;
862 const SmNode
*pLSub
= nullptr;
863 const SmNode
*pLSup
= nullptr;
864 SvXMLElementExport
*pThing2
= nullptr;
866 //if we have prescripts at all then we must use the tensor notation
868 //This is one of those excellent locations where scope is vital to
869 //arrange the construction and destruction of the element helper
871 pLSub
= pNode
->GetSubNode(LSUB
+1);
872 pLSup
= pNode
->GetSubNode(LSUP
+1);
875 SvXMLElementExport
aMultiScripts(*this, XML_NAMESPACE_MATH
,
876 XML_MMULTISCRIPTS
, true, true);
879 if (nullptr != (pCSub
= pNode
->GetSubNode(CSUB
+1))
880 && nullptr != (pCSup
= pNode
->GetSubNode(CSUP
+1)))
882 pThing2
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
,
883 XML_MUNDEROVER
, true, true);
885 else if (nullptr != (pCSub
= pNode
->GetSubNode(CSUB
+1)))
887 pThing2
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
,
888 XML_MUNDER
, true, true);
890 else if (nullptr != (pCSup
= pNode
->GetSubNode(CSUP
+1)))
892 pThing2
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
,
893 XML_MOVER
, true, true);
896 ExportNodes(pNode
->GetSubNode(0), nLevel
+1); //Main Term
899 ExportNodes(pCSub
, nLevel
+1);
901 ExportNodes(pCSup
, nLevel
+1);
904 pSub
= pNode
->GetSubNode(RSUB
+1);
905 pSup
= pNode
->GetSubNode(RSUP
+1);
909 ExportNodes(pSub
, nLevel
+1);
912 SvXMLElementExport
aNone(*this, XML_NAMESPACE_MATH
, XML_NONE
, true, true);
915 ExportNodes(pSup
, nLevel
+1);
918 SvXMLElementExport
aNone(*this, XML_NAMESPACE_MATH
, XML_NONE
, true, true);
922 //Separator element between suffix and prefix sub/sup pairs
924 SvXMLElementExport
aPrescripts(*this, XML_NAMESPACE_MATH
,
925 XML_MPRESCRIPTS
, true, true);
929 ExportNodes(pLSub
, nLevel
+1);
932 SvXMLElementExport
aNone(*this, XML_NAMESPACE_MATH
, XML_NONE
,
937 ExportNodes(pLSup
, nLevel
+1);
940 SvXMLElementExport
aNone(*this, XML_NAMESPACE_MATH
, XML_NONE
,
947 SvXMLElementExport
*pThing
= nullptr;
948 if (nullptr != (pSub
= pNode
->GetSubNode(RSUB
+1)) &&
949 nullptr != (pSup
= pNode
->GetSubNode(RSUP
+1)))
951 pThing
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
,
952 XML_MSUBSUP
, true, true);
954 else if (nullptr != (pSub
= pNode
->GetSubNode(RSUB
+1)))
956 pThing
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MSUB
,
959 else if (nullptr != (pSup
= pNode
->GetSubNode(RSUP
+1)))
961 pThing
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MSUP
,
965 if (nullptr != (pCSub
= pNode
->GetSubNode(CSUB
+1))
966 && nullptr != (pCSup
=pNode
->GetSubNode(CSUP
+1)))
968 pThing2
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
,
969 XML_MUNDEROVER
, true, true);
971 else if (nullptr != (pCSub
= pNode
->GetSubNode(CSUB
+1)))
973 pThing2
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
,
974 XML_MUNDER
, true, true);
976 else if (nullptr != (pCSup
= pNode
->GetSubNode(CSUP
+1)))
978 pThing2
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
,
979 XML_MOVER
, true, true);
981 ExportNodes(pNode
->GetSubNode(0), nLevel
+1); //Main Term
984 ExportNodes(pCSub
, nLevel
+1);
986 ExportNodes(pCSup
, nLevel
+1);
990 ExportNodes(pSub
, nLevel
+1);
992 ExportNodes(pSup
, nLevel
+1);
997 void SmXMLExport::ExportBrace(const SmNode
*pNode
, int nLevel
)
1000 const SmNode
*pLeft
=pNode
->GetSubNode(0);
1001 const SmNode
*pRight
=pNode
->GetSubNode(2);
1003 // This used to generate <mfenced> or <mrow>+<mo> elements according to
1004 // the stretchiness of fences. The MathML recommendation defines an
1005 // <mrow>+<mo> construction that is equivalent to the <mfenced> element:
1006 // http://www.w3.org/TR/MathML3/chapter3.html#presm.mfenced
1007 // To simplify our code and avoid issues with mfenced implementations in
1008 // MathML rendering engines, we now always generate <mrow>+<mo> elements.
1012 std::unique_ptr
<SvXMLElementExport
> pRow(
1013 new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MROW
,
1016 // <mo fence="true"> opening-fence </mo>
1017 if (pLeft
&& (pLeft
->GetToken().eType
!= TNONE
))
1019 AddAttribute(XML_NAMESPACE_MATH
, XML_FENCE
, XML_TRUE
);
1020 if (pNode
->GetScaleMode() == SmScaleMode::Height
)
1021 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_TRUE
);
1023 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_FALSE
);
1024 ExportNodes(pLeft
, nLevel
+1);
1027 if (nullptr != (pTemp
= pNode
->GetSubNode(1)))
1030 SvXMLElementExport
aRow(*this, XML_NAMESPACE_MATH
, XML_MROW
,
1032 ExportNodes(pTemp
, nLevel
+1);
1036 // <mo fence="true"> closing-fence </mo>
1037 if (pRight
&& (pRight
->GetToken().eType
!= TNONE
))
1039 AddAttribute(XML_NAMESPACE_MATH
, XML_FENCE
, XML_TRUE
);
1040 if (pNode
->GetScaleMode() == SmScaleMode::Height
)
1041 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_TRUE
);
1043 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_FALSE
);
1044 ExportNodes(pRight
, nLevel
+1);
1050 void SmXMLExport::ExportRoot(const SmNode
*pNode
, int nLevel
)
1052 if (pNode
->GetSubNode(0))
1054 SvXMLElementExport
aRoot(*this, XML_NAMESPACE_MATH
, XML_MROOT
, true,
1056 ExportNodes(pNode
->GetSubNode(2), nLevel
+1);
1057 ExportNodes(pNode
->GetSubNode(0), nLevel
+1);
1061 SvXMLElementExport
aSqrt(*this, XML_NAMESPACE_MATH
, XML_MSQRT
, true,
1063 ExportNodes(pNode
->GetSubNode(2), nLevel
+1);
1067 void SmXMLExport::ExportOperator(const SmNode
*pNode
, int nLevel
)
1069 /*we need to either use content or font and size attributes
1071 SvXMLElementExport
aRow(*this, XML_NAMESPACE_MATH
, XML_MROW
,
1073 ExportNodes(pNode
->GetSubNode(0), nLevel
+1);
1074 ExportNodes(pNode
->GetSubNode(1), nLevel
+1);
1077 void SmXMLExport::ExportAttributes(const SmNode
*pNode
, int nLevel
)
1079 std::unique_ptr
<SvXMLElementExport
> pElement
;
1081 if (pNode
->GetToken().eType
== TUNDERLINE
)
1083 AddAttribute(XML_NAMESPACE_MATH
, XML_ACCENTUNDER
,
1085 pElement
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MUNDER
,
1088 else if (pNode
->GetToken().eType
== TOVERSTRIKE
)
1090 // export as <menclose notation="horizontalstrike">
1091 AddAttribute(XML_NAMESPACE_MATH
, XML_NOTATION
, XML_HORIZONTALSTRIKE
);
1092 pElement
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
,
1093 XML_MENCLOSE
, true, true));
1097 AddAttribute(XML_NAMESPACE_MATH
, XML_ACCENT
,
1099 pElement
.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MOVER
,
1103 ExportNodes(pNode
->GetSubNode(1), nLevel
+1);
1104 switch (pNode
->GetToken().eType
)
1108 //proper entity support required
1109 SvXMLElementExport
aMath(*this, XML_NAMESPACE_MATH
, XML_MO
,
1111 sal_Unicode
const nArse
[2] = {0xAF,0x00};
1112 GetDocHandler()->characters(nArse
);
1117 //proper entity support required
1118 SvXMLElementExport
aMath(*this, XML_NAMESPACE_MATH
, XML_MO
,
1120 sal_Unicode
const nArse
[2] = {0x0332,0x00};
1121 GetDocHandler()->characters(nArse
);
1130 // make these wide accents stretchy
1131 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_TRUE
);
1132 ExportNodes(pNode
->GetSubNode(0), nLevel
+1);
1136 ExportNodes(pNode
->GetSubNode(0), nLevel
+1);
1141 static bool lcl_HasEffectOnMathvariant( const SmTokenType eType
)
1143 return eType
== TBOLD
|| eType
== TNBOLD
||
1144 eType
== TITALIC
|| eType
== TNITALIC
||
1145 eType
== TSANS
|| eType
== TSERIF
|| eType
== TFIXED
;
1148 void SmXMLExport::ExportFont(const SmNode
*pNode
, int nLevel
)
1151 // gather the mathvariant attribute relevant data from all
1152 // successively following SmFontNodes...
1154 int nBold
= -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true;
1155 int nItalic
= -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true;
1156 int nSansSerifFixed
= -1;
1157 SmTokenType eNodeType
= TUNKNOWN
;
1158 while (lcl_HasEffectOnMathvariant( (eNodeType
= pNode
->GetToken().eType
) ))
1162 case TBOLD
: nBold
= 1; break;
1163 case TNBOLD
: nBold
= 0; break;
1164 case TITALIC
: nItalic
= 1; break;
1165 case TNITALIC
: nItalic
= 0; break;
1166 case TSANS
: nSansSerifFixed
= 0; break;
1167 case TSERIF
: nSansSerifFixed
= 1; break;
1168 case TFIXED
: nSansSerifFixed
= 2; break;
1170 SAL_WARN("starmath", "unexpected case");
1172 // According to the parser every node that is to be evaluated here
1173 // has a single non-zero subnode at index 1!! Thus we only need to check
1174 // that single node for follow-up nodes that have an effect on the attribute.
1175 if (pNode
->GetNumSubNodes() > 1 && pNode
->GetSubNode(1) &&
1176 lcl_HasEffectOnMathvariant( pNode
->GetSubNode(1)->GetToken().eType
))
1178 pNode
= pNode
->GetSubNode(1);
1184 switch (pNode
->GetToken().eType
)
1187 // No attribute needed. An <mphantom> element will be used below.
1190 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_BLACK
);
1193 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_WHITE
);
1196 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_RED
);
1199 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_GREEN
);
1202 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_BLUE
);
1205 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_AQUA
);
1208 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_FUCHSIA
);
1211 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_YELLOW
);
1214 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_SILVER
);
1217 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_GRAY
);
1220 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_MAROON
);
1223 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_OLIVE
);
1226 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_LIME
);
1229 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_AQUA
);
1232 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_TEAL
);
1235 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_NAVY
);
1238 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_FUCHSIA
);
1241 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_PURPLE
);
1245 const SmFontNode
*pFontNode
= static_cast<const SmFontNode
*>(pNode
);
1246 const Fraction
&aFrac
= pFontNode
->GetSizeParameter();
1248 OUStringBuffer sStrBuf
;
1249 switch(pFontNode
->GetSizeType())
1251 case FontSizeType::MULTIPLY
:
1252 ::sax::Converter::convertDouble(sStrBuf
,
1253 static_cast<double>(aFrac
*Fraction(100.00)));
1254 sStrBuf
.append('%');
1256 case FontSizeType::DIVIDE
:
1257 ::sax::Converter::convertDouble(sStrBuf
,
1258 static_cast<double>(Fraction(100.00)/aFrac
));
1259 sStrBuf
.append('%');
1261 case FontSizeType::ABSOLUT
:
1262 ::sax::Converter::convertDouble(sStrBuf
,
1263 static_cast<double>(aFrac
));
1265 GetXMLToken(XML_UNIT_PT
));
1269 //The problem here is that the wheels fall off because
1270 //font size is stored in 100th's of a mm not pts, and
1271 //rounding errors take their toll on the original
1272 //value specified in points.
1274 //Must fix StarMath to retain the original pt values
1275 Fraction aTemp
= Sm100th_mmToPts(pFontNode
->GetFont().GetFontSize().Height());
1277 if (pFontNode
->GetSizeType() == FontSizeType::MINUS
)
1282 double mytest
= static_cast<double>(aTemp
);
1284 mytest
= ::rtl::math::round(mytest
,1);
1285 ::sax::Converter::convertDouble(sStrBuf
,mytest
);
1286 sStrBuf
.append(GetXMLToken(XML_UNIT_PT
));
1291 OUString
sStr(sStrBuf
.makeStringAndClear());
1292 AddAttribute(XML_NAMESPACE_MATH
, XML_MATHSIZE
, sStr
);
1303 // nBold: -1 = yet undefined; 0 = false; 1 = true;
1304 // nItalic: -1 = yet undefined; 0 = false; 1 = true;
1305 // nSansSerifFixed: -1 = undefined; 0 = sans; 1 = serif; 2 = fixed;
1306 const sal_Char
*pText
= "normal";
1307 if (nSansSerifFixed
== -1 || nSansSerifFixed
== 1)
1310 if (nBold
== 1 && nItalic
!= 1)
1312 else if (nBold
!= 1 && nItalic
== 1)
1314 else if (nBold
== 1 && nItalic
== 1)
1315 pText
= "bold-italic";
1317 else if (nSansSerifFixed
== 0)
1319 pText
= "sans-serif";
1320 if (nBold
== 1 && nItalic
!= 1)
1321 pText
= "bold-sans-serif";
1322 else if (nBold
!= 1 && nItalic
== 1)
1323 pText
= "sans-serif-italic";
1324 else if (nBold
== 1 && nItalic
== 1)
1325 pText
= "sans-serif-bold-italic";
1327 else if (nSansSerifFixed
== 2)
1328 pText
= "monospace"; // no modifiers allowed for monospace ...
1331 SAL_WARN("starmath", "unexpected case");
1333 AddAttribute(XML_NAMESPACE_MATH
, XML_MATHVARIANT
, OUString::createFromAscii( pText
));
1341 // Wrap everything in an <mphantom> or <mstyle> element. These elements
1342 // are mrow-like, so ExportExpression doesn't need to add an explicit
1343 // <mrow> element. See #fdo 66283.
1344 SvXMLElementExport
aElement(*this, XML_NAMESPACE_MATH
,
1345 pNode
->GetToken().eType
== TPHANTOM
? XML_MPHANTOM
: XML_MSTYLE
,
1347 ExportExpression(pNode
, nLevel
, true);
1352 void SmXMLExport::ExportVerticalBrace(const SmVerticalBraceNode
*pNode
, int nLevel
)
1354 // "[body] overbrace [script]"
1356 // Position body, overbrace and script vertically. First place the overbrace
1357 // OVER the body and then the script OVER this expression.
1361 // XXXXXX[body]XXXXXXX
1363 // Similarly for the underbrace construction.
1367 switch (pNode
->GetToken().eType
)
1378 SvXMLElementExport
aOver1(*this, XML_NAMESPACE_MATH
,which
, true, true);
1380 // using accents will draw the over-/underbraces too close to the base
1381 // see http://www.w3.org/TR/MathML2/chapter3.html#id.3.4.5.2
1382 // also XML_ACCENT is illegal with XML_MUNDER. Thus no XML_ACCENT attribute here!
1383 SvXMLElementExport
aOver2(*this, XML_NAMESPACE_MATH
,which
, true, true);
1384 ExportNodes(pNode
->Body(), nLevel
);
1385 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_TRUE
);
1386 ExportNodes(pNode
->Brace(), nLevel
);
1388 ExportNodes(pNode
->Script(), nLevel
);
1391 void SmXMLExport::ExportMatrix(const SmNode
*pNode
, int nLevel
)
1393 SvXMLElementExport
aTable(*this, XML_NAMESPACE_MATH
, XML_MTABLE
, true, true);
1394 const SmMatrixNode
*pMatrix
= static_cast<const SmMatrixNode
*>(pNode
);
1396 for (sal_uInt16 y
= 0; y
< pMatrix
->GetNumRows(); y
++)
1398 SvXMLElementExport
aRow(*this, XML_NAMESPACE_MATH
, XML_MTR
, true, true);
1399 for (sal_uInt16 x
= 0; x
< pMatrix
->GetNumCols(); x
++)
1401 if (const SmNode
*pTemp
= pNode
->GetSubNode(i
++))
1403 if (pTemp
->GetType() == SmNodeType::Align
&&
1404 pTemp
->GetToken().eType
!= TALIGNC
)
1406 // A left or right alignment is specified on this cell,
1407 // attach the corresponding columnalign attribute.
1408 AddAttribute(XML_NAMESPACE_MATH
, XML_COLUMNALIGN
,
1409 pTemp
->GetToken().eType
== TALIGNL
?
1410 XML_LEFT
: XML_RIGHT
);
1412 SvXMLElementExport
aCell(*this, XML_NAMESPACE_MATH
, XML_MTD
, true, true);
1413 ExportNodes(pTemp
, nLevel
+1);
1419 void SmXMLExport::ExportNodes(const SmNode
*pNode
, int nLevel
)
1423 switch(pNode
->GetType())
1425 case SmNodeType::Table
:
1426 ExportTable(pNode
, nLevel
);
1428 case SmNodeType::Align
:
1429 case SmNodeType::Bracebody
:
1430 case SmNodeType::Expression
:
1431 ExportExpression(pNode
, nLevel
);
1433 case SmNodeType::Line
:
1434 ExportLine(pNode
, nLevel
);
1436 case SmNodeType::Text
:
1439 case SmNodeType::GlyphSpecial
:
1440 case SmNodeType::Math
:
1442 sal_Unicode cTmp
= 0;
1443 const SmTextNode
*pTemp
= static_cast< const SmTextNode
* >(pNode
);
1444 if (!pTemp
->GetText().isEmpty())
1445 cTmp
= ConvertMathToMathML( pTemp
->GetText()[0] );
1448 // no conversion to MathML implemented -> export it as text
1449 // thus at least it will not vanish into nothing
1454 switch (pNode
->GetToken().eType
)
1457 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_TRUE
);
1462 //To fully handle generic MathML we need to implement the full
1463 //operator dictionary, we will generate MathML with explicit
1464 //stretchiness for now.
1465 sal_Int16 nLength
= GetAttrList().getLength();
1466 bool bAddStretch
=true;
1467 for ( sal_Int16 i
= 0; i
< nLength
; i
++ )
1469 OUString sLocalName
;
1470 sal_uInt16 nPrefix
= GetNamespaceMap().GetKeyByAttrName(
1471 GetAttrList().getNameByIndex(i
), &sLocalName
);
1473 if ( ( XML_NAMESPACE_MATH
== nPrefix
) &&
1474 IsXMLToken(sLocalName
, XML_STRETCHY
) )
1476 bAddStretch
= false;
1482 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_FALSE
);
1488 case SmNodeType::Special
: //SmNodeType::Special requires some sort of Entity preservation in the XML engine.
1489 case SmNodeType::MathIdent
:
1490 case SmNodeType::Place
:
1493 case SmNodeType::BinHor
:
1494 ExportBinaryHorizontal(pNode
, nLevel
);
1496 case SmNodeType::UnHor
:
1497 ExportUnaryHorizontal(pNode
, nLevel
);
1499 case SmNodeType::Brace
:
1500 ExportBrace(pNode
, nLevel
);
1502 case SmNodeType::BinVer
:
1503 ExportBinaryVertical(pNode
, nLevel
);
1505 case SmNodeType::BinDiagonal
:
1506 ExportBinaryDiagonal(pNode
, nLevel
);
1508 case SmNodeType::SubSup
:
1509 ExportSubSupScript(pNode
, nLevel
);
1511 case SmNodeType::Root
:
1512 ExportRoot(pNode
, nLevel
);
1514 case SmNodeType::Oper
:
1515 ExportOperator(pNode
, nLevel
);
1517 case SmNodeType::Attribut
:
1518 ExportAttributes(pNode
, nLevel
);
1520 case SmNodeType::Font
:
1521 ExportFont(pNode
, nLevel
);
1523 case SmNodeType::VerticalBrace
:
1524 ExportVerticalBrace(static_cast<const SmVerticalBraceNode
*>(pNode
), nLevel
);
1526 case SmNodeType::Matrix
:
1527 ExportMatrix(pNode
, nLevel
);
1529 case SmNodeType::Blank
:
1533 SAL_WARN("starmath", "Warning: failed to export a node?");
1539 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */