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/XErrorHandler.hpp>
28 #include <com/sun/star/xml/sax/XEntityResolver.hpp>
29 #include <com/sun/star/xml/sax/InputSource.hpp>
30 #include <com/sun/star/xml/sax/XDTDHandler.hpp>
31 #include <com/sun/star/xml/sax/XParser.hpp>
32 #include <com/sun/star/xml/sax/Writer.hpp>
33 #include <com/sun/star/io/XActiveDataSource.hpp>
34 #include <com/sun/star/io/XActiveDataControl.hpp>
35 #include <com/sun/star/document/XDocumentProperties.hpp>
36 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
37 #include <com/sun/star/packages/zip/ZipIOException.hpp>
38 #include <com/sun/star/task/XStatusIndicatorFactory.hpp>
39 #include <com/sun/star/beans/PropertyAttribute.hpp>
40 #include <com/sun/star/container/XNameAccess.hpp>
41 #include <com/sun/star/embed/ElementModes.hpp>
42 #include <com/sun/star/util/MeasureUnit.hpp>
43 #include <com/sun/star/uno/Any.h>
45 #include <rtl/math.hxx>
46 #include <sfx2/frame.hxx>
47 #include <sfx2/docfile.hxx>
48 #include <osl/diagnose.h>
49 #include <svtools/sfxecode.hxx>
50 #include <unotools/saveopt.hxx>
51 #include <svl/stritem.hxx>
52 #include <svl/itemprop.hxx>
53 #include <comphelper/processfactory.hxx>
54 #include <unotools/streamwrap.hxx>
55 #include <sax/tools/converter.hxx>
56 #include <xmloff/xmlnmspe.hxx>
57 #include <xmloff/xmltoken.hxx>
58 #include <xmloff/nmspmap.hxx>
59 #include <xmloff/attrlist.hxx>
60 #include <xmloff/xmlmetai.hxx>
61 #include <osl/mutex.hxx>
62 #include <comphelper/genericpropertyset.hxx>
63 #include <comphelper/servicehelper.hxx>
68 #include "mathmlexport.hxx"
69 #include "register.hxx"
70 #include <starmath.hrc>
71 #include <unomodel.hxx>
72 #include <document.hxx>
73 #include <utility.hxx>
74 #include "cfgitem.hxx"
76 using namespace ::com::sun::star::beans
;
77 using namespace ::com::sun::star::document
;
78 using namespace ::com::sun::star::lang
;
79 using namespace ::com::sun::star::uno
;
80 using namespace ::com::sun::star
;
81 using namespace ::xmloff::token
;
85 inline bool IsInPrivateUseArea( sal_Unicode cChar
) { return 0xE000 <= cChar
&& cChar
<= 0xF8FF; }
87 sal_Unicode
ConvertMathToMathML( sal_Unicode cChar
)
89 sal_Unicode cRes
= cChar
;
90 if (IsInPrivateUseArea( cChar
))
92 SAL_WARN("starmath", "Error: private use area characters should no longer be in use!" );
93 cRes
= sal_Unicode('@'); // just some character that should easily be notice as odd in the context
100 bool SmXMLExportWrapper::Export(SfxMedium
&rMedium
)
103 uno::Reference
<uno::XComponentContext
> xContext(comphelper::getProcessComponentContext());
106 uno::Reference
< lang::XComponent
> xModelComp(xModel
, uno::UNO_QUERY
);
108 bool bEmbedded
= false;
109 uno::Reference
<lang::XUnoTunnel
> xTunnel(xModel
,uno::UNO_QUERY
);
110 SmModel
*pModel
= reinterpret_cast<SmModel
*>
111 (xTunnel
->getSomething(SmModel::getUnoTunnelId()));
113 SmDocShell
*pDocShell
= pModel
?
114 static_cast<SmDocShell
*>(pModel
->GetObjectShell()) : nullptr;
116 SfxObjectCreateMode::EMBEDDED
== pDocShell
->GetCreateMode() )
119 uno::Reference
<task::XStatusIndicator
> xStatusIndicator
;
122 if (pDocShell
/*&& pDocShell->GetMedium()*/)
124 OSL_ENSURE( pDocShell
->GetMedium() == &rMedium
,
125 "different SfxMedium found" );
127 SfxItemSet
* pSet
= rMedium
.GetItemSet();
130 const SfxUnoAnyItem
* pItem
= static_cast<const SfxUnoAnyItem
*>(
131 pSet
->GetItem(SID_PROGRESS_STATUSBAR_CONTROL
) );
133 pItem
->GetValue() >>= xStatusIndicator
;
137 // set progress range and start status indicator
138 if (xStatusIndicator
.is())
140 sal_Int32 nProgressRange
= bFlat
? 1 : 3;
141 xStatusIndicator
->start(SM_RESSTR(STR_STATSTR_WRITING
),
147 // create XPropertySet with three properties for status indicator
148 comphelper::PropertyMapEntry aInfoMap
[] =
150 { OUString("UsePrettyPrinting"), 0,
151 cppu::UnoType
<bool>::get(),
152 beans::PropertyAttribute::MAYBEVOID
, 0},
153 { OUString("BaseURI"), 0,
154 ::cppu::UnoType
<OUString
>::get(),
155 beans::PropertyAttribute::MAYBEVOID
, 0 },
156 { OUString("StreamRelPath"), 0,
157 ::cppu::UnoType
<OUString
>::get(),
158 beans::PropertyAttribute::MAYBEVOID
, 0 },
159 { OUString("StreamName"), 0,
160 ::cppu::UnoType
<OUString
>::get(),
161 beans::PropertyAttribute::MAYBEVOID
, 0 },
162 { OUString(), 0, css::uno::Type(), 0, 0 }
164 uno::Reference
< beans::XPropertySet
> xInfoSet(
165 comphelper::GenericPropertySet_CreateInstance(
166 new comphelper::PropertySetInfo( aInfoMap
) ) );
168 SvtSaveOptions aSaveOpt
;
169 OUString
sUsePrettyPrinting("UsePrettyPrinting");
170 bool bUsePrettyPrinting( bFlat
|| aSaveOpt
.IsPrettyPrinting() );
171 xInfoSet
->setPropertyValue( sUsePrettyPrinting
, Any(bUsePrettyPrinting
) );
174 OUString
sPropName( "BaseURI" );
175 xInfoSet
->setPropertyValue( sPropName
, makeAny( rMedium
.GetBaseURL( true ) ) );
178 if (xStatusIndicator
.is())
179 xStatusIndicator
->setValue(nSteps
++);
180 if (!bFlat
) //Storage (Package) of Stream
182 uno::Reference
< embed::XStorage
> xStg
= rMedium
.GetOutputStorage();
183 bool bOASIS
= ( SotStorage::GetVersion( xStg
) > SOFFICE_FILEFORMAT_60
);
185 // TODO/LATER: handle the case of embedded links gracefully
186 if ( bEmbedded
) //&& !pStg->IsRoot() )
189 if ( rMedium
.GetItemSet() )
191 const SfxStringItem
* pDocHierarchItem
= static_cast<const SfxStringItem
*>(
192 rMedium
.GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME
) );
193 if ( pDocHierarchItem
)
194 aName
= pDocHierarchItem
->GetValue();
197 if ( !aName
.isEmpty() )
199 sPropName
= "StreamRelPath";
200 xInfoSet
->setPropertyValue( sPropName
, makeAny( aName
) );
206 if (xStatusIndicator
.is())
207 xStatusIndicator
->setValue(nSteps
++);
209 bRet
= WriteThroughComponent(
210 xStg
, xModelComp
, "meta.xml", xContext
, xInfoSet
,
211 (bOASIS
? "com.sun.star.comp.Math.XMLOasisMetaExporter"
212 : "com.sun.star.comp.Math.XMLMetaExporter"));
216 if (xStatusIndicator
.is())
217 xStatusIndicator
->setValue(nSteps
++);
219 bRet
= WriteThroughComponent(
220 xStg
, xModelComp
, "content.xml", xContext
, xInfoSet
,
221 "com.sun.star.comp.Math.XMLContentExporter");
226 if (xStatusIndicator
.is())
227 xStatusIndicator
->setValue(nSteps
++);
229 bRet
= WriteThroughComponent(
230 xStg
, xModelComp
, "settings.xml", xContext
, xInfoSet
,
231 (bOASIS
? "com.sun.star.comp.Math.XMLOasisSettingsExporter"
232 : "com.sun.star.comp.Math.XMLSettingsExporter") );
237 SvStream
*pStream
= rMedium
.GetOutStream();
238 uno::Reference
<io::XOutputStream
> xOut(
239 new utl::OOutputStreamWrapper(*pStream
) );
241 if (xStatusIndicator
.is())
242 xStatusIndicator
->setValue(nSteps
++);
244 bRet
= WriteThroughComponent(
245 xOut
, xModelComp
, xContext
, xInfoSet
,
246 "com.sun.star.comp.Math.XMLContentExporter");
249 if (xStatusIndicator
.is())
250 xStatusIndicator
->end();
256 /// export through an XML exporter component (output stream version)
257 bool SmXMLExportWrapper::WriteThroughComponent(
258 const Reference
<io::XOutputStream
>& xOutputStream
,
259 const Reference
<XComponent
>& xComponent
,
260 Reference
<uno::XComponentContext
> & rxContext
,
261 Reference
<beans::XPropertySet
> & rPropSet
,
262 const sal_Char
* pComponentName
)
264 OSL_ENSURE(xOutputStream
.is(), "I really need an output stream!");
265 OSL_ENSURE(xComponent
.is(), "Need component!");
266 OSL_ENSURE(nullptr != pComponentName
, "Need component name!");
269 Reference
< xml::sax::XWriter
> xSaxWriter
= xml::sax::Writer::create(rxContext
);
271 // connect XML writer to output stream
272 xSaxWriter
->setOutputStream( xOutputStream
);
274 // prepare arguments (prepend doc handler to given arguments)
275 Reference
<xml::sax::XDocumentHandler
> xDocHandler( xSaxWriter
,UNO_QUERY
);
277 Sequence
<Any
> aArgs( 2 );
278 aArgs
[0] <<= xDocHandler
;
279 aArgs
[1] <<= rPropSet
;
281 // get filter component
282 Reference
< document::XExporter
> xExporter(
283 rxContext
->getServiceManager()->createInstanceWithArgumentsAndContext(OUString::createFromAscii(pComponentName
), aArgs
, rxContext
),
285 OSL_ENSURE( xExporter
.is(),
286 "can't instantiate export filter component" );
287 if ( !xExporter
.is() )
291 // connect model and filter
292 xExporter
->setSourceDocument( xComponent
);
295 Reference
< XFilter
> xFilter( xExporter
, UNO_QUERY
);
296 uno::Sequence
< PropertyValue
> aProps(0);
297 xFilter
->filter( aProps
);
299 uno::Reference
<lang::XUnoTunnel
> xFilterTunnel( xFilter
, uno::UNO_QUERY
);
300 SmXMLExport
*pFilter
= reinterpret_cast< SmXMLExport
* >(
301 sal::static_int_cast
< sal_uIntPtr
>(
302 xFilterTunnel
->getSomething( SmXMLExport::getUnoTunnelId() )));
303 return pFilter
== nullptr || pFilter
->GetSuccess();
307 /// export through an XML exporter component (storage version)
308 bool SmXMLExportWrapper::WriteThroughComponent(
309 const Reference
< embed::XStorage
>& xStorage
,
310 const Reference
<XComponent
>& xComponent
,
311 const sal_Char
* pStreamName
,
312 Reference
<uno::XComponentContext
> & rxContext
,
313 Reference
<beans::XPropertySet
> & rPropSet
,
314 const sal_Char
* pComponentName
317 OSL_ENSURE(xStorage
.is(), "Need storage!");
318 OSL_ENSURE(nullptr != pStreamName
, "Need stream name!");
321 Reference
< io::XStream
> xStream
;
322 OUString sStreamName
= OUString::createFromAscii(pStreamName
);
325 xStream
= xStorage
->openStreamElement( sStreamName
,
326 embed::ElementModes::READWRITE
| embed::ElementModes::TRUNCATE
);
328 catch ( const uno::Exception
& rEx
)
330 SAL_WARN("starmath", "Can't create output stream in package: " << rEx
.Message
);
334 OUString
aPropName( "MediaType" );
335 OUString
aMime( "text/xml" );
337 uno::Reference
< beans::XPropertySet
> xSet( xStream
, uno::UNO_QUERY
);
338 xSet
->setPropertyValue( aPropName
, Any(aMime
) );
340 // all streams must be encrypted in encrypted document
341 OUString
aTmpPropName( "UseCommonStoragePasswordEncryption" );
342 xSet
->setPropertyValue( aTmpPropName
, Any(true) );
347 OUString
sPropName( "StreamName" );
348 rPropSet
->setPropertyValue( sPropName
, makeAny( sStreamName
) );
352 bool bRet
= WriteThroughComponent( xStream
->getOutputStream(), xComponent
, rxContext
,
353 rPropSet
, pComponentName
);
358 SmXMLExport::SmXMLExport(
359 const css::uno::Reference
< css::uno::XComponentContext
>& rContext
,
360 OUString
const & implementationName
, SvXMLExportFlags nExportFlags
)
361 : SvXMLExport(util::MeasureUnit::INCH
, rContext
, implementationName
, XML_MATH
,
368 sal_Int64 SAL_CALL
SmXMLExport::getSomething(
369 const uno::Sequence
< sal_Int8
>& rId
)
370 throw(uno::RuntimeException
, std::exception
)
372 if ( rId
.getLength() == 16 &&
373 0 == memcmp( getUnoTunnelId().getConstArray(),
374 rId
.getConstArray(), 16 ) )
375 return sal::static_int_cast
< sal_Int64
>(reinterpret_cast< sal_uIntPtr
>(this));
377 return SvXMLExport::getSomething( rId
);
382 class theSmXMLExportUnoTunnelId
: public rtl::Static
< UnoTunnelIdInit
, theSmXMLExportUnoTunnelId
> {};
385 const uno::Sequence
< sal_Int8
> & SmXMLExport::getUnoTunnelId() throw()
387 return theSmXMLExportUnoTunnelId::get().getSeq();
390 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
* SAL_CALL
391 Math_XMLExporter_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.XMLExporter", SvXMLExportFlags::OASIS
|SvXMLExportFlags::ALL
));
396 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
* SAL_CALL
397 Math_XMLMetaExporter_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.XMLMetaExporter", SvXMLExportFlags::META
));
402 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
* SAL_CALL
403 Math_XMLOasisMetaExporter_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.XMLOasisMetaExporter", SvXMLExportFlags::OASIS
|SvXMLExportFlags::META
));
408 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
* SAL_CALL
409 Math_XMLSettingsExporter_get_implementation(css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const &)
411 return cppu::acquire(new SmXMLExport(context
, "com.sun.star.comp.Math.XMLSettingsExporter", SvXMLExportFlags::SETTINGS
));
414 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
* SAL_CALL
415 Math_XMLOasisSettingsExporter_get_implementation(css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const &)
417 return cppu::acquire(new SmXMLExport(context
, "com.sun.star.comp.Math.XMLOasisSettingsExporter", SvXMLExportFlags::OASIS
|SvXMLExportFlags::SETTINGS
));
420 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
* SAL_CALL
421 Math_XMLContentExporter_get_implementation(css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const &)
423 return cppu::acquire(new SmXMLExport(context
, "com.sun.star.comp.Math.XMLContentExporter", SvXMLExportFlags::OASIS
|SvXMLExportFlags::CONTENT
));
426 sal_uInt32
SmXMLExport::exportDoc(enum XMLTokenEnum eClass
)
428 if ( !(getExportFlags() & SvXMLExportFlags::CONTENT
) )
430 SvXMLExport::exportDoc( eClass
);
434 uno::Reference
<frame::XModel
> xModel
= GetModel();
435 uno::Reference
<lang::XUnoTunnel
> xTunnel(xModel
,uno::UNO_QUERY
);
436 SmModel
*pModel
= reinterpret_cast<SmModel
*>
437 (xTunnel
->getSomething(SmModel::getUnoTunnelId()));
441 SmDocShell
*pDocShell
=
442 static_cast<SmDocShell
*>(pModel
->GetObjectShell());
443 pTree
= pDocShell
->GetFormulaTree();
444 aText
= pDocShell
->GetText();
447 GetDocHandler()->startDocument();
449 addChaffWhenEncryptedStorage();
452 SvXMLAttributeList
&rList
= GetAttrList();
454 // make use of a default namespace
455 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)
456 GetNamespaceMap_().Add( OUString(), GetXMLToken(XML_N_MATH
), XML_NAMESPACE_MATH
);
458 rList
.AddAttribute(GetNamespaceMap().GetAttrNameByKey(XML_NAMESPACE_MATH_IDX
),
459 GetNamespaceMap().GetNameByKey( XML_NAMESPACE_MATH_IDX
));
461 //I think we need something like ImplExportEntities();
463 GetDocHandler()->endDocument();
470 void SmXMLExport::ExportContent_()
472 uno::Reference
<frame::XModel
> xModel
= GetModel();
473 uno::Reference
<lang::XUnoTunnel
> xTunnel(xModel
,uno::UNO_QUERY
);
474 SmModel
*pModel
= reinterpret_cast<SmModel
*>
475 (xTunnel
->getSomething(SmModel::getUnoTunnelId()));
476 SmDocShell
*pDocShell
= pModel
?
477 static_cast<SmDocShell
*>(pModel
->GetObjectShell()) : nullptr;
478 OSL_ENSURE( pDocShell
, "doc shell missing" );
480 if (pDocShell
&& !pDocShell
->GetFormat().IsTextmode())
482 // If the Math equation is not in text mode, we attach a display="block"
483 // attribute on the <math> root. We don't do anything if it is in
484 // text mode, the default display="inline" value will be used.
485 AddAttribute(XML_NAMESPACE_MATH
, XML_DISPLAY
, XML_BLOCK
);
487 SvXMLElementExport
aEquation(*this, XML_NAMESPACE_MATH
, XML_MATH
, true, true);
488 SvXMLElementExport
*pSemantics
=nullptr;
490 if (!aText
.isEmpty())
492 pSemantics
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
,
493 XML_SEMANTICS
, true, true);
496 ExportNodes(pTree
, 0);
498 if (!aText
.isEmpty())
500 // Convert symbol names
503 SmParser
&rParser
= pDocShell
->GetParser();
504 bool bVal
= rParser
.IsExportSymbolNames();
505 rParser
.SetExportSymbolNames( true );
506 SmNode
*pTmpTree
= rParser
.Parse( aText
);
507 aText
= rParser
.GetText();
509 rParser
.SetExportSymbolNames( bVal
);
512 AddAttribute(XML_NAMESPACE_MATH
, XML_ENCODING
,
513 OUString("StarMath 5.0"));
514 SvXMLElementExport
aAnnotation(*this, XML_NAMESPACE_MATH
,
515 XML_ANNOTATION
, true, false);
516 GetDocHandler()->characters( aText
);
521 void SmXMLExport::GetViewSettings( Sequence
< PropertyValue
>& aProps
)
523 uno::Reference
<frame::XModel
> xModel
= GetModel();
527 uno::Reference
<lang::XUnoTunnel
> xTunnel(xModel
,uno::UNO_QUERY
);
528 SmModel
*pModel
= reinterpret_cast<SmModel
*>
529 (xTunnel
->getSomething(SmModel::getUnoTunnelId()));
534 SmDocShell
*pDocShell
=
535 static_cast<SmDocShell
*>(pModel
->GetObjectShell());
540 PropertyValue
*pValue
= aProps
.getArray();
541 sal_Int32 nIndex
= 0;
543 Rectangle
aRect( pDocShell
->GetVisArea() );
545 pValue
[nIndex
].Name
= "ViewAreaTop";
546 pValue
[nIndex
++].Value
<<= aRect
.Top();
548 pValue
[nIndex
].Name
= "ViewAreaLeft";
549 pValue
[nIndex
++].Value
<<= aRect
.Left();
551 pValue
[nIndex
].Name
= "ViewAreaWidth";
552 pValue
[nIndex
++].Value
<<= aRect
.GetWidth();
554 pValue
[nIndex
].Name
= "ViewAreaHeight";
555 pValue
[nIndex
++].Value
<<= aRect
.GetHeight();
558 void SmXMLExport::GetConfigurationSettings( Sequence
< PropertyValue
> & rProps
)
560 Reference
< XPropertySet
> xProps ( GetModel(), UNO_QUERY
);
563 Reference
< XPropertySetInfo
> xPropertySetInfo
= xProps
->getPropertySetInfo();
564 if (xPropertySetInfo
.is())
566 Sequence
< Property
> aProps
= xPropertySetInfo
->getProperties();
567 sal_Int32
nCount(aProps
.getLength());
570 rProps
.realloc(nCount
);
571 PropertyValue
* pProps
= rProps
.getArray();
574 SmMathConfig
*pConfig
= SM_MOD()->GetConfig();
575 const bool bUsedSymbolsOnly
= pConfig
&& pConfig
->IsSaveOnlyUsedSymbols();
577 const OUString
sFormula ( "Formula" );
578 const OUString
sBasicLibraries ( "BasicLibraries" );
579 const OUString
sDialogLibraries ( "DialogLibraries" );
580 const OUString
sRuntimeUID ( "RuntimeUID" );
581 for (sal_Int32 i
= 0; i
< nCount
; i
++, pProps
++)
583 const OUString
&rPropName
= aProps
[i
].Name
;
584 if (rPropName
!= sFormula
&&
585 rPropName
!= sBasicLibraries
&&
586 rPropName
!= sDialogLibraries
&&
587 rPropName
!= sRuntimeUID
)
589 pProps
->Name
= rPropName
;
591 OUString
aActualName( rPropName
);
593 // handle 'save used symbols only'
594 if (bUsedSymbolsOnly
&& rPropName
== "Symbols" )
595 aActualName
= "UserDefinedSymbolsInUse";
597 pProps
->Value
= xProps
->getPropertyValue( aActualName
);
606 void SmXMLExport::ExportLine(const SmNode
*pNode
, int nLevel
)
608 ExportExpression(pNode
, nLevel
);
611 void SmXMLExport::ExportBinaryHorizontal(const SmNode
*pNode
, int nLevel
)
613 TG nGroup
= pNode
->GetToken().nGroup
;
615 std::unique_ptr
<SvXMLElementExport
> pRow( new SvXMLElementExport(*this,
616 XML_NAMESPACE_MATH
, XML_MROW
, true, true) );
618 // Unfold the binary tree structure as long as the nodes are SmBinHorNode
619 // with the same nGroup. This will reduce the number of nested <mrow>
620 // elements e.g. we only need three <mrow> levels to export
622 // "a*b*c*d+e*f*g*h+i*j*k*l = a*b*c*d+e*f*g*h+i*j*k*l =
623 // a*b*c*d+e*f*g*h+i*j*k*l = a*b*c*d+e*f*g*h+i*j*k*l"
625 // See https://www.libreoffice.org/bugzilla/show_bug.cgi?id=66081
626 ::std::stack
< const SmNode
* > s
;
630 const SmNode
*node
= s
.top();
632 if (node
->GetType() != NBINHOR
|| node
->GetToken().nGroup
!= nGroup
)
634 ExportNodes(node
, nLevel
+1);
637 const SmBinHorNode
* binNode
= static_cast<const SmBinHorNode
*>(node
);
638 s
.push(binNode
->RightOperand());
639 s
.push(binNode
->Symbol());
640 s
.push(binNode
->LeftOperand());
644 void SmXMLExport::ExportUnaryHorizontal(const SmNode
*pNode
, int nLevel
)
646 ExportExpression(pNode
, nLevel
);
649 void SmXMLExport::ExportExpression(const SmNode
*pNode
, int nLevel
,
650 bool bNoMrowContainer
/*=false*/)
652 SvXMLElementExport
*pRow
=nullptr;
653 auto nSize
= pNode
->GetNumSubNodes();
655 // #i115443: nodes of type expression always need to be grouped with mrow statement
656 if (!bNoMrowContainer
&&
657 (nSize
> 1 || pNode
->GetType() == NEXPRESSION
))
658 pRow
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MROW
, true, true);
660 for (sal_uInt16 i
= 0; i
< nSize
; i
++)
661 if (const SmNode
*pTemp
= pNode
->GetSubNode(i
))
662 ExportNodes(pTemp
, nLevel
+1);
667 void SmXMLExport::ExportBinaryVertical(const SmNode
*pNode
, int nLevel
)
669 assert(pNode
->GetNumSubNodes() == 3);
670 const SmNode
*pNum
= pNode
->GetSubNode(0);
671 const SmNode
*pDenom
= pNode
->GetSubNode(2);
672 if (pNum
->GetType() == NALIGN
&& pNum
->GetToken().eType
!= TALIGNC
)
674 // A left or right alignment is specified on the numerator:
675 // attach the corresponding numalign attribute.
676 AddAttribute(XML_NAMESPACE_MATH
, XML_NUMALIGN
,
677 pNum
->GetToken().eType
== TALIGNL
? XML_LEFT
: XML_RIGHT
);
679 if (pDenom
->GetType() == NALIGN
&& pDenom
->GetToken().eType
!= TALIGNC
)
681 // A left or right alignment is specified on the denominator:
682 // attach the corresponding denomalign attribute.
683 AddAttribute(XML_NAMESPACE_MATH
, XML_DENOMALIGN
,
684 pDenom
->GetToken().eType
== TALIGNL
? XML_LEFT
: XML_RIGHT
);
686 SvXMLElementExport
aFraction(*this, XML_NAMESPACE_MATH
, XML_MFRAC
, true, true);
687 ExportNodes(pNum
, nLevel
);
688 ExportNodes(pDenom
, nLevel
);
691 void SmXMLExport::ExportBinaryDiagonal(const SmNode
*pNode
, int nLevel
)
693 assert(pNode
->GetNumSubNodes() == 3);
695 if (pNode
->GetToken().eType
== TWIDESLASH
)
698 // export the node as <mfrac bevelled="true">
699 AddAttribute(XML_NAMESPACE_MATH
, XML_BEVELLED
, XML_TRUE
);
700 SvXMLElementExport
aFraction(*this, XML_NAMESPACE_MATH
, XML_MFRAC
,
702 ExportNodes(pNode
->GetSubNode(0), nLevel
);
703 ExportNodes(pNode
->GetSubNode(1), nLevel
);
708 // We can not use <mfrac> to a backslash, so just use <mo>\</mo>
709 std::unique_ptr
<SvXMLElementExport
> pRow( new SvXMLElementExport(*this,
710 XML_NAMESPACE_MATH
, XML_MROW
, true, true) );
712 ExportNodes(pNode
->GetSubNode(0), nLevel
);
714 { // Scoping for <mo> creation
715 SvXMLElementExport
aMo(*this, XML_NAMESPACE_MATH
, XML_MO
,
717 sal_Unicode nArse
[2] = {MS_BACKSLASH
,0x00};
718 GetDocHandler()->characters(nArse
);
721 ExportNodes(pNode
->GetSubNode(1), nLevel
);
725 void SmXMLExport::ExportTable(const SmNode
*pNode
, int nLevel
)
727 SvXMLElementExport
*pTable
=nullptr;
729 sal_uInt16 nSize
= pNode
->GetNumSubNodes();
731 //If the list ends in newline then the last entry has
732 //no subnodes, the newline is superfluous so we just drop
733 //the last node, inclusion would create a bad MathML
737 const SmNode
*pLine
= pNode
->GetSubNode(nSize
-1);
738 if (pLine
->GetType() == NLINE
&& pLine
->GetNumSubNodes() == 1 &&
739 pLine
->GetSubNode(0) != nullptr &&
740 pLine
->GetSubNode(0)->GetToken().eType
== TNEWLINE
)
744 // try to avoid creating a mtable element when the formula consists only
745 // of a single output line
746 if (nLevel
|| (nSize
>1))
747 pTable
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MTABLE
, true, true);
749 for (sal_uInt16 i
= 0; i
< nSize
; i
++)
750 if (const SmNode
*pTemp
= pNode
->GetSubNode(i
))
752 SvXMLElementExport
*pRow
=nullptr;
753 SvXMLElementExport
*pCell
=nullptr;
756 pRow
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MTR
, true, true);
757 SmTokenType eAlign
= TALIGNC
;
758 if (pTemp
->GetType() == NALIGN
)
760 // For Binom() and Stack() constructions, the NALIGN nodes
761 // are direct children.
762 // binom{alignl ...}{alignr ...} and
763 // stack{alignl ... ## alignr ... ## ...}
764 eAlign
= pTemp
->GetToken().eType
;
766 else if (pTemp
->GetType() == NLINE
&&
767 pTemp
->GetNumSubNodes() == 1 &&
768 pTemp
->GetSubNode(0) &&
769 pTemp
->GetSubNode(0)->GetType() == NALIGN
)
771 // For the Table() construction, the NALIGN node is a child
773 // alignl ... newline alignr ... newline ...
774 eAlign
= pTemp
->GetSubNode(0)->GetToken().eType
;
776 if (eAlign
!= TALIGNC
)
778 // If a left or right alignment is specified on this line,
779 // attach the corresponding columnalign attribute.
780 AddAttribute(XML_NAMESPACE_MATH
, XML_COLUMNALIGN
,
781 eAlign
== TALIGNL
? XML_LEFT
: XML_RIGHT
);
783 pCell
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MTD
, true, true);
785 ExportNodes(pTemp
, nLevel
+1);
793 void SmXMLExport::ExportMath(const SmNode
*pNode
, int /*nLevel*/)
795 const SmTextNode
*pTemp
= static_cast<const SmTextNode
*>(pNode
);
796 SvXMLElementExport
*pMath
= nullptr;
798 if (pNode
->GetType() == NMATH
|| pNode
->GetType() == NGLYPH_SPECIAL
)
800 // Export NMATH and NGLYPH_SPECIAL symbols as <mo> elements
801 pMath
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MO
, true, false);
803 else if (pNode
->GetType() == NSPECIAL
)
805 bool bIsItalic
= IsItalic(pNode
->GetFont());
807 AddAttribute(XML_NAMESPACE_MATH
, XML_MATHVARIANT
, XML_NORMAL
);
808 pMath
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MI
, true, false);
812 // Export NMATHIDENT and NPLACE symbols as <mi> elements:
813 // - These math symbols should not be drawn slanted. Hence we should
814 // attach a mathvariant="normal" attribute to single-char <mi> elements
815 // that are not mathematical alphanumeric symbol. For simplicity and to
816 // work around browser limitations, we always attach such an attribute.
817 // - The MathML specification suggests to use empty <mi> elements as
818 // placeholders but they won't be visible in most MathML rendering
819 // engines so let's use an empty square for NPLACE instead.
820 AddAttribute(XML_NAMESPACE_MATH
, XML_MATHVARIANT
, XML_NORMAL
);
821 pMath
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MI
, true, false);
823 sal_Unicode nArse
[2];
824 nArse
[0] = pTemp
->GetText()[0];
825 sal_Unicode cTmp
= ConvertMathToMathML( nArse
[0] );
828 OSL_ENSURE(nArse
[0] != 0xffff,"Non existent symbol");
830 GetDocHandler()->characters(nArse
);
835 void SmXMLExport::ExportText(const SmNode
*pNode
, int /*nLevel*/)
837 SvXMLElementExport
*pText
;
838 const SmTextNode
*pTemp
= static_cast<const SmTextNode
*>(pNode
);
839 switch (pNode
->GetToken().eType
)
844 //Note that we change the fontstyle to italic for strings that
845 //are italic and longer than a single character.
846 bool bIsItalic
= IsItalic( pTemp
->GetFont() );
847 if ((pTemp
->GetText().getLength() > 1) && bIsItalic
)
848 AddAttribute(XML_NAMESPACE_MATH
, XML_MATHVARIANT
, XML_ITALIC
);
849 else if ((pTemp
->GetText().getLength() == 1) && !bIsItalic
)
850 AddAttribute(XML_NAMESPACE_MATH
, XML_MATHVARIANT
, XML_NORMAL
);
851 pText
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MI
, true, false);
855 pText
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MN
, true, false);
858 pText
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MTEXT
, true, false);
861 GetDocHandler()->characters(pTemp
->GetText());
865 void SmXMLExport::ExportBlank(const SmNode
*pNode
, int /*nLevel*/)
867 const SmBlankNode
*pTemp
= static_cast<const SmBlankNode
*>(pNode
);
868 //!! exports an <mspace> element. Note that for example "~_~" is allowed in
869 //!! Math (so it has no sense at all) but must not result in an empty
870 //!! <msub> tag in MathML !!
872 if (pTemp
->GetBlankNum() != 0)
874 // Attach a width attribute. We choose the (somewhat arbitrary) values
875 // ".5em" for a small gap '`' and "2em" for a large gap '~'.
876 // (see SmBlankNode::IncreaseBy for how pTemp->mnNum is set).
877 OUStringBuffer sStrBuf
;
878 ::sax::Converter::convertDouble(sStrBuf
, pTemp
->GetBlankNum() * .5);
879 sStrBuf
.append("em");
880 AddAttribute(XML_NAMESPACE_MATH
, XML_WIDTH
, sStrBuf
.getStr());
883 SvXMLElementExport
*pText
;
885 pText
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MSPACE
,
888 GetDocHandler()->characters( OUString() );
892 void SmXMLExport::ExportSubSupScript(const SmNode
*pNode
, int nLevel
)
894 const SmNode
*pSub
= nullptr;
895 const SmNode
*pSup
= nullptr;
896 const SmNode
*pCSub
= nullptr;
897 const SmNode
*pCSup
= nullptr;
898 const SmNode
*pLSub
= nullptr;
899 const SmNode
*pLSup
= nullptr;
900 SvXMLElementExport
*pThing2
= nullptr;
902 //if we have prescripts at all then we must use the tensor notation
904 //This is one of those excellent locations where scope is vital to
905 //arrange the construction and destruction of the element helper
907 pLSub
= pNode
->GetSubNode(LSUB
+1);
908 pLSup
= pNode
->GetSubNode(LSUP
+1);
911 SvXMLElementExport
aMultiScripts(*this, XML_NAMESPACE_MATH
,
912 XML_MMULTISCRIPTS
, true, true);
915 if (nullptr != (pCSub
= pNode
->GetSubNode(CSUB
+1))
916 && nullptr != (pCSup
= pNode
->GetSubNode(CSUP
+1)))
918 pThing2
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
,
919 XML_MUNDEROVER
, true, true);
921 else if (nullptr != (pCSub
= pNode
->GetSubNode(CSUB
+1)))
923 pThing2
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
,
924 XML_MUNDER
, true, true);
926 else if (nullptr != (pCSup
= pNode
->GetSubNode(CSUP
+1)))
928 pThing2
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
,
929 XML_MOVER
, true, true);
932 ExportNodes(pNode
->GetSubNode(0), nLevel
+1); //Main Term
935 ExportNodes(pCSub
, nLevel
+1);
937 ExportNodes(pCSup
, nLevel
+1);
940 pSub
= pNode
->GetSubNode(RSUB
+1);
941 pSup
= pNode
->GetSubNode(RSUP
+1);
945 ExportNodes(pSub
, nLevel
+1);
948 SvXMLElementExport
aNone(*this, XML_NAMESPACE_MATH
, XML_NONE
, true, true);
951 ExportNodes(pSup
, nLevel
+1);
954 SvXMLElementExport
aNone(*this, XML_NAMESPACE_MATH
, XML_NONE
, true, true);
958 //Separator element between suffix and prefix sub/sup pairs
960 SvXMLElementExport
aPrescripts(*this, XML_NAMESPACE_MATH
,
961 XML_MPRESCRIPTS
, true, true);
965 ExportNodes(pLSub
, nLevel
+1);
968 SvXMLElementExport
aNone(*this, XML_NAMESPACE_MATH
, XML_NONE
,
973 ExportNodes(pLSup
, nLevel
+1);
976 SvXMLElementExport
aNone(*this, XML_NAMESPACE_MATH
, XML_NONE
,
983 SvXMLElementExport
*pThing
= nullptr;
984 if (nullptr != (pSub
= pNode
->GetSubNode(RSUB
+1)) &&
985 nullptr != (pSup
= pNode
->GetSubNode(RSUP
+1)))
987 pThing
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
,
988 XML_MSUBSUP
, true, true);
990 else if (nullptr != (pSub
= pNode
->GetSubNode(RSUB
+1)))
992 pThing
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MSUB
,
995 else if (nullptr != (pSup
= pNode
->GetSubNode(RSUP
+1)))
997 pThing
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MSUP
,
1001 if (nullptr != (pCSub
= pNode
->GetSubNode(CSUB
+1))
1002 && nullptr != (pCSup
=pNode
->GetSubNode(CSUP
+1)))
1004 pThing2
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
,
1005 XML_MUNDEROVER
, true, true);
1007 else if (nullptr != (pCSub
= pNode
->GetSubNode(CSUB
+1)))
1009 pThing2
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
,
1010 XML_MUNDER
, true, true);
1012 else if (nullptr != (pCSup
= pNode
->GetSubNode(CSUP
+1)))
1014 pThing2
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
,
1015 XML_MOVER
, true, true);
1017 ExportNodes(pNode
->GetSubNode(0), nLevel
+1); //Main Term
1020 ExportNodes(pCSub
, nLevel
+1);
1022 ExportNodes(pCSup
, nLevel
+1);
1026 ExportNodes(pSub
, nLevel
+1);
1028 ExportNodes(pSup
, nLevel
+1);
1033 void SmXMLExport::ExportBrace(const SmNode
*pNode
, int nLevel
)
1035 const SmNode
*pTemp
;
1036 const SmNode
*pLeft
=pNode
->GetSubNode(0);
1037 const SmNode
*pRight
=pNode
->GetSubNode(2);
1038 SvXMLElementExport
*pRow
=nullptr;
1040 // This used to generate <mfenced> or <mrow>+<mo> elements according to
1041 // the stretchiness of fences. The MathML recommendation defines an
1042 // <mrow>+<mo> construction that is equivalent to the <mfenced> element:
1043 // http://www.w3.org/TR/MathML3/chapter3.html#presm.mfenced
1044 // To simplify our code and avoid issues with mfenced implementations in
1045 // MathML rendering engines, we now always generate <mrow>+<mo> elements.
1049 pRow
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MROW
,
1052 // <mo fence="true"> opening-fence </mo>
1053 if (pLeft
&& (pLeft
->GetToken().eType
!= TNONE
))
1055 AddAttribute(XML_NAMESPACE_MATH
, XML_FENCE
, XML_TRUE
);
1056 if (pNode
->GetScaleMode() == SCALE_HEIGHT
)
1057 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_TRUE
);
1059 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_FALSE
);
1060 ExportNodes(pLeft
, nLevel
+1);
1063 if (nullptr != (pTemp
= pNode
->GetSubNode(1)))
1066 SvXMLElementExport
aRow(*this, XML_NAMESPACE_MATH
, XML_MROW
,
1068 ExportNodes(pTemp
, nLevel
+1);
1072 // <mo fence="true"> closing-fence </mo>
1073 if (pRight
&& (pRight
->GetToken().eType
!= TNONE
))
1075 AddAttribute(XML_NAMESPACE_MATH
, XML_FENCE
, XML_TRUE
);
1076 if (pNode
->GetScaleMode() == SCALE_HEIGHT
)
1077 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_TRUE
);
1079 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_FALSE
);
1080 ExportNodes(pRight
, nLevel
+1);
1087 void SmXMLExport::ExportRoot(const SmNode
*pNode
, int nLevel
)
1089 if (pNode
->GetSubNode(0))
1091 SvXMLElementExport
aRoot(*this, XML_NAMESPACE_MATH
, XML_MROOT
, true,
1093 ExportNodes(pNode
->GetSubNode(2), nLevel
+1);
1094 ExportNodes(pNode
->GetSubNode(0), nLevel
+1);
1098 SvXMLElementExport
aSqrt(*this, XML_NAMESPACE_MATH
, XML_MSQRT
, true,
1100 ExportNodes(pNode
->GetSubNode(2), nLevel
+1);
1104 void SmXMLExport::ExportOperator(const SmNode
*pNode
, int nLevel
)
1106 /*we need to either use content or font and size attributes
1108 SvXMLElementExport
aRow(*this, XML_NAMESPACE_MATH
, XML_MROW
,
1110 ExportNodes(pNode
->GetSubNode(0), nLevel
+1);
1111 ExportNodes(pNode
->GetSubNode(1), nLevel
+1);
1114 void SmXMLExport::ExportAttributes(const SmNode
*pNode
, int nLevel
)
1116 SvXMLElementExport
*pElement
=nullptr;
1118 if (pNode
->GetToken().eType
== TUNDERLINE
)
1120 AddAttribute(XML_NAMESPACE_MATH
, XML_ACCENTUNDER
,
1122 pElement
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MUNDER
,
1125 else if (pNode
->GetToken().eType
== TOVERSTRIKE
)
1127 // export as <menclose notation="horizontalstrike">
1128 AddAttribute(XML_NAMESPACE_MATH
, XML_NOTATION
, XML_HORIZONTALSTRIKE
);
1129 pElement
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
,
1130 XML_MENCLOSE
, true, true);
1134 AddAttribute(XML_NAMESPACE_MATH
, XML_ACCENT
,
1136 pElement
= new SvXMLElementExport(*this, XML_NAMESPACE_MATH
, XML_MOVER
,
1140 ExportNodes(pNode
->GetSubNode(1), nLevel
+1);
1141 switch (pNode
->GetToken().eType
)
1145 //proper entity support required
1146 SvXMLElementExport
aMath(*this, XML_NAMESPACE_MATH
, XML_MO
,
1148 sal_Unicode nArse
[2] = {0xAF,0x00};
1149 GetDocHandler()->characters(nArse
);
1154 //proper entity support required
1155 SvXMLElementExport
aMath(*this, XML_NAMESPACE_MATH
, XML_MO
,
1157 sal_Unicode nArse
[2] = {0x0332,0x00};
1158 GetDocHandler()->characters(nArse
);
1167 // make these wide accents stretchy
1168 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_TRUE
);
1169 ExportNodes(pNode
->GetSubNode(0), nLevel
+1);
1173 ExportNodes(pNode
->GetSubNode(0), nLevel
+1);
1179 static bool lcl_HasEffectOnMathvariant( const SmTokenType eType
)
1181 return eType
== TBOLD
|| eType
== TNBOLD
||
1182 eType
== TITALIC
|| eType
== TNITALIC
||
1183 eType
== TSANS
|| eType
== TSERIF
|| eType
== TFIXED
;
1186 void SmXMLExport::ExportFont(const SmNode
*pNode
, int nLevel
)
1189 // gather the mathvariant attribute relevant data from all
1190 // successively following SmFontNodes...
1192 int nBold
= -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true;
1193 int nItalic
= -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true;
1194 int nSansSerifFixed
= -1;
1195 SmTokenType eNodeType
= TUNKNOWN
;
1196 while (lcl_HasEffectOnMathvariant( (eNodeType
= pNode
->GetToken().eType
) ))
1200 case TBOLD
: nBold
= 1; break;
1201 case TNBOLD
: nBold
= 0; break;
1202 case TITALIC
: nItalic
= 1; break;
1203 case TNITALIC
: nItalic
= 0; break;
1204 case TSANS
: nSansSerifFixed
= 0; break;
1205 case TSERIF
: nSansSerifFixed
= 1; break;
1206 case TFIXED
: nSansSerifFixed
= 2; break;
1208 SAL_WARN("starmath", "unexpected case");
1210 // According to the parser every node that is to be evaluated here
1211 // has a single non-zero subnode at index 1!! Thus we only need to check
1212 // that single node for follow-up nodes that have an effect on the attribute.
1213 if (pNode
->GetNumSubNodes() > 1 && pNode
->GetSubNode(1) &&
1214 lcl_HasEffectOnMathvariant( pNode
->GetSubNode(1)->GetToken().eType
))
1216 pNode
= pNode
->GetSubNode(1);
1222 switch (pNode
->GetToken().eType
)
1225 // No attribute needed. An <mphantom> element will be used below.
1228 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_BLACK
);
1231 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_WHITE
);
1234 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_RED
);
1237 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_GREEN
);
1240 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_BLUE
);
1243 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_AQUA
);
1246 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_FUCHSIA
);
1249 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_YELLOW
);
1252 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_SILVER
);
1255 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_GRAY
);
1258 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_MAROON
);
1261 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_OLIVE
);
1264 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_LIME
);
1267 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_AQUA
);
1270 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_TEAL
);
1273 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_NAVY
);
1276 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_FUCHSIA
);
1279 AddAttribute(XML_NAMESPACE_MATH
, XML_COLOR
, XML_PURPLE
);
1283 const SmFontNode
*pFontNode
= static_cast<const SmFontNode
*>(pNode
);
1284 const Fraction
&aFrac
= pFontNode
->GetSizeParameter();
1286 OUStringBuffer sStrBuf
;
1287 switch(pFontNode
->GetSizeType())
1289 case FontSizeType::MULTIPLY
:
1290 ::sax::Converter::convertDouble(sStrBuf
,
1291 static_cast<double>(aFrac
*Fraction(100.00)));
1292 sStrBuf
.append('%');
1294 case FontSizeType::DIVIDE
:
1295 ::sax::Converter::convertDouble(sStrBuf
,
1296 static_cast<double>(Fraction(100.00)/aFrac
));
1297 sStrBuf
.append('%');
1299 case FontSizeType::ABSOLUT
:
1300 ::sax::Converter::convertDouble(sStrBuf
,
1301 static_cast<double>(aFrac
));
1303 GetXMLToken(XML_UNIT_PT
));
1307 //The problem here is that the wheels fall off because
1308 //font size is stored in 100th's of a mm not pts, and
1309 //rounding errors take their toll on the original
1310 //value specified in points.
1312 //Must fix StarMath to retain the original pt values
1313 Fraction aTemp
= Sm100th_mmToPts(pFontNode
->GetFont().GetFontSize().Height());
1315 if (pFontNode
->GetSizeType() == FontSizeType::MINUS
)
1320 double mytest
= static_cast<double>(aTemp
);
1322 mytest
= ::rtl::math::round(mytest
,1);
1323 ::sax::Converter::convertDouble(sStrBuf
,mytest
);
1324 sStrBuf
.append(GetXMLToken(XML_UNIT_PT
));
1329 OUString
sStr(sStrBuf
.makeStringAndClear());
1330 AddAttribute(XML_NAMESPACE_MATH
, XML_MATHSIZE
, sStr
);
1341 // nBold: -1 = yet undefined; 0 = false; 1 = true;
1342 // nItalic: -1 = yet undefined; 0 = false; 1 = true;
1343 // nSansSerifFixed: -1 = undefined; 0 = sans; 1 = serif; 2 = fixed;
1344 const sal_Char
*pText
= "normal";
1345 if (nSansSerifFixed
== -1 || nSansSerifFixed
== 1)
1348 if (nBold
== 1 && nItalic
!= 1)
1350 else if (nBold
!= 1 && nItalic
== 1)
1352 else if (nBold
== 1 && nItalic
== 1)
1353 pText
= "bold-italic";
1355 else if (nSansSerifFixed
== 0)
1357 pText
= "sans-serif";
1358 if (nBold
== 1 && nItalic
!= 1)
1359 pText
= "bold-sans-serif";
1360 else if (nBold
!= 1 && nItalic
== 1)
1361 pText
= "sans-serif-italic";
1362 else if (nBold
== 1 && nItalic
== 1)
1363 pText
= "sans-serif-bold-italic";
1365 else if (nSansSerifFixed
== 2)
1366 pText
= "monospace"; // no modifiers allowed for monospace ...
1369 SAL_WARN("starmath", "unexpected case");
1371 AddAttribute(XML_NAMESPACE_MATH
, XML_MATHVARIANT
, OUString::createFromAscii( pText
));
1379 // Wrap everything in an <mphantom> or <mstyle> element. These elements
1380 // are mrow-like, so ExportExpression doesn't need to add an explicit
1381 // <mrow> element. See #fdo 66283.
1382 SvXMLElementExport
aElement(*this, XML_NAMESPACE_MATH
,
1383 pNode
->GetToken().eType
== TPHANTOM
? XML_MPHANTOM
: XML_MSTYLE
,
1385 ExportExpression(pNode
, nLevel
, true);
1390 void SmXMLExport::ExportVerticalBrace(const SmVerticalBraceNode
*pNode
, int nLevel
)
1392 // "[body] overbrace [script]"
1394 // Position body, overbrace and script vertically. First place the overbrace
1395 // OVER the body and then the script OVER this expression.
1399 // XXXXXX[body]XXXXXXX
1401 // Similarly for the underbrace construction.
1405 switch (pNode
->GetToken().eType
)
1416 SvXMLElementExport
aOver1(*this, XML_NAMESPACE_MATH
,which
, true, true);
1418 // using accents will draw the over-/underbraces too close to the base
1419 // see http://www.w3.org/TR/MathML2/chapter3.html#id.3.4.5.2
1420 // also XML_ACCENT is illegal with XML_MUNDER. Thus no XML_ACCENT attribute here!
1421 SvXMLElementExport
aOver2(*this, XML_NAMESPACE_MATH
,which
, true, true);
1422 ExportNodes(pNode
->Body(), nLevel
);
1423 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_TRUE
);
1424 ExportNodes(pNode
->Brace(), nLevel
);
1426 ExportNodes(pNode
->Script(), nLevel
);
1429 void SmXMLExport::ExportMatrix(const SmNode
*pNode
, int nLevel
)
1431 SvXMLElementExport
aTable(*this, XML_NAMESPACE_MATH
, XML_MTABLE
, true, true);
1432 const SmMatrixNode
*pMatrix
= static_cast<const SmMatrixNode
*>(pNode
);
1434 for (sal_uInt16 y
= 0; y
< pMatrix
->GetNumRows(); y
++)
1436 SvXMLElementExport
aRow(*this, XML_NAMESPACE_MATH
, XML_MTR
, true, true);
1437 for (sal_uInt16 x
= 0; x
< pMatrix
->GetNumCols(); x
++)
1438 if (const SmNode
*pTemp
= pNode
->GetSubNode(i
++))
1440 if (pTemp
->GetType() == NALIGN
&&
1441 pTemp
->GetToken().eType
!= TALIGNC
)
1443 // A left or right alignment is specified on this cell,
1444 // attach the corresponding columnalign attribute.
1445 AddAttribute(XML_NAMESPACE_MATH
, XML_COLUMNALIGN
,
1446 pTemp
->GetToken().eType
== TALIGNL
?
1447 XML_LEFT
: XML_RIGHT
);
1449 SvXMLElementExport
aCell(*this, XML_NAMESPACE_MATH
, XML_MTD
, true, true);
1450 ExportNodes(pTemp
, nLevel
+1);
1455 void SmXMLExport::ExportNodes(const SmNode
*pNode
, int nLevel
)
1459 switch(pNode
->GetType())
1462 ExportTable(pNode
, nLevel
);
1467 ExportExpression(pNode
, nLevel
);
1470 ExportLine(pNode
, nLevel
);
1473 ExportText(pNode
, nLevel
);
1475 case NGLYPH_SPECIAL
:
1478 sal_Unicode cTmp
= 0;
1479 const SmTextNode
*pTemp
= static_cast< const SmTextNode
* >(pNode
);
1480 if (!pTemp
->GetText().isEmpty())
1481 cTmp
= ConvertMathToMathML( pTemp
->GetText()[0] );
1484 // no conversion to MathML implemented -> export it as text
1485 // thus at least it will not vanish into nothing
1486 ExportText(pNode
, nLevel
);
1490 switch (pNode
->GetToken().eType
)
1493 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_TRUE
);
1498 //To fully handle generic MathML we need to implement the full
1499 //operator dictionary, we will generate MathML with explicit
1500 //stretchiness for now.
1501 sal_Int16 nLength
= GetAttrList().getLength();
1502 bool bAddStretch
=true;
1503 for ( sal_Int16 i
= 0; i
< nLength
; i
++ )
1505 OUString sLocalName
;
1506 sal_uInt16 nPrefix
= GetNamespaceMap().GetKeyByAttrName(
1507 GetAttrList().getNameByIndex(i
), &sLocalName
);
1509 if ( ( XML_NAMESPACE_MATH
== nPrefix
) &&
1510 IsXMLToken(sLocalName
, XML_STRETCHY
) )
1512 bAddStretch
= false;
1518 AddAttribute(XML_NAMESPACE_MATH
, XML_STRETCHY
, XML_FALSE
);
1520 ExportMath(pNode
, nLevel
);
1524 case NSPECIAL
: //NSPECIAL requires some sort of Entity preservation in the XML engine.
1527 ExportMath(pNode
, nLevel
);
1530 ExportBinaryHorizontal(pNode
, nLevel
);
1533 ExportUnaryHorizontal(pNode
, nLevel
);
1536 ExportBrace(pNode
, nLevel
);
1539 ExportBinaryVertical(pNode
, nLevel
);
1542 ExportBinaryDiagonal(pNode
, nLevel
);
1545 ExportSubSupScript(pNode
, nLevel
);
1548 ExportRoot(pNode
, nLevel
);
1551 ExportOperator(pNode
, nLevel
);
1554 ExportAttributes(pNode
, nLevel
);
1557 ExportFont(pNode
, nLevel
);
1559 case NVERTICAL_BRACE
:
1560 ExportVerticalBrace(static_cast<const SmVerticalBraceNode
*>(pNode
), nLevel
);
1563 ExportMatrix(pNode
, nLevel
);
1566 ExportBlank(pNode
, nLevel
);
1569 SAL_WARN("starmath", "Warning: failed to export a node?");
1575 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */