Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : /*
21 : Warning: The SvXMLElementExport helper class creates the beginning and
22 : closing tags of xml elements in its constructor and destructor, so theres
23 : hidden stuff going on, on occasion the ordering of these classes declarations
24 : may be significant
25 : */
26 :
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>
44 :
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>
64 :
65 : #include <memory>
66 : #include <stack>
67 :
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"
75 :
76 : using namespace ::com::sun::star::beans;
77 : using namespace ::com::sun::star::container;
78 : using namespace ::com::sun::star::document;
79 : using namespace ::com::sun::star::lang;
80 : using namespace ::com::sun::star::uno;
81 : using namespace ::com::sun::star;
82 : using namespace ::xmloff::token;
83 :
84 2772 : sal_Unicode ConvertMathToMathML( sal_Unicode cChar )
85 : {
86 2772 : sal_Unicode cRes = cChar;
87 2772 : if (IsInPrivateUseArea( cChar ))
88 : {
89 : SAL_WARN("starmath", "Error: private use area characters should no longer be in use!" );
90 0 : cRes = (sal_Unicode) '@'; // just some character that should easily be notice as odd in the context
91 : }
92 2772 : return cRes;
93 : }
94 :
95 561 : bool SmXMLExportWrapper::Export(SfxMedium &rMedium)
96 : {
97 561 : bool bRet=true;
98 561 : uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
99 :
100 : //Get model
101 1122 : uno::Reference< lang::XComponent > xModelComp(xModel, uno::UNO_QUERY );
102 :
103 561 : bool bEmbedded = false;
104 1122 : uno::Reference <lang::XUnoTunnel> xTunnel;
105 561 : xTunnel = uno::Reference <lang::XUnoTunnel> (xModel,uno::UNO_QUERY);
106 : SmModel *pModel = reinterpret_cast<SmModel *>
107 561 : (xTunnel->getSomething(SmModel::getUnoTunnelId()));
108 :
109 : SmDocShell *pDocShell = pModel ?
110 561 : static_cast<SmDocShell*>(pModel->GetObjectShell()) : 0;
111 1122 : if ( pDocShell &&
112 561 : SfxObjectCreateMode::EMBEDDED == pDocShell->GetCreateMode() )
113 561 : bEmbedded = true;
114 :
115 1122 : uno::Reference<task::XStatusIndicator> xStatusIndicator;
116 561 : if (!bEmbedded)
117 : {
118 0 : if (pDocShell /*&& pDocShell->GetMedium()*/)
119 : {
120 : OSL_ENSURE( pDocShell->GetMedium() == &rMedium,
121 : "different SfxMedium found" );
122 :
123 0 : SfxItemSet* pSet = rMedium.GetItemSet();
124 0 : if (pSet)
125 : {
126 : const SfxUnoAnyItem* pItem = static_cast<const SfxUnoAnyItem*>(
127 0 : pSet->GetItem(SID_PROGRESS_STATUSBAR_CONTROL) );
128 0 : if (pItem)
129 0 : pItem->GetValue() >>= xStatusIndicator;
130 : }
131 : }
132 :
133 : // set progress range and start status indicator
134 0 : if (xStatusIndicator.is())
135 : {
136 0 : sal_Int32 nProgressRange = bFlat ? 1 : 3;
137 0 : xStatusIndicator->start(SM_RESSTR(STR_STATSTR_WRITING),
138 0 : nProgressRange);
139 : }
140 : }
141 :
142 :
143 : // create XPropertySet with three properties for status indicator
144 : comphelper::PropertyMapEntry aInfoMap[] =
145 : {
146 : { OUString("UsePrettyPrinting"), 0,
147 561 : cppu::UnoType<bool>::get(),
148 : beans::PropertyAttribute::MAYBEVOID, 0},
149 : { OUString("BaseURI"), 0,
150 561 : ::cppu::UnoType<OUString>::get(),
151 : beans::PropertyAttribute::MAYBEVOID, 0 },
152 : { OUString("StreamRelPath"), 0,
153 561 : ::cppu::UnoType<OUString>::get(),
154 : beans::PropertyAttribute::MAYBEVOID, 0 },
155 : { OUString("StreamName"), 0,
156 561 : ::cppu::UnoType<OUString>::get(),
157 : beans::PropertyAttribute::MAYBEVOID, 0 },
158 : { OUString(), 0, css::uno::Type(), 0, 0 }
159 3366 : };
160 : uno::Reference< beans::XPropertySet > xInfoSet(
161 : comphelper::GenericPropertySet_CreateInstance(
162 1122 : new comphelper::PropertySetInfo( aInfoMap ) ) );
163 :
164 1122 : SvtSaveOptions aSaveOpt;
165 1122 : OUString sUsePrettyPrinting("UsePrettyPrinting");
166 561 : sal_Bool bUsePrettyPrinting( bFlat || aSaveOpt.IsPrettyPrinting() );
167 1122 : Any aAny;
168 561 : aAny.setValue( &bUsePrettyPrinting, cppu::UnoType<bool>::get() );
169 561 : xInfoSet->setPropertyValue( sUsePrettyPrinting, aAny );
170 :
171 : // Set base URI
172 1122 : OUString sPropName( "BaseURI" );
173 561 : xInfoSet->setPropertyValue( sPropName, makeAny( rMedium.GetBaseURL( true ) ) );
174 :
175 561 : sal_Int32 nSteps=0;
176 561 : if (xStatusIndicator.is())
177 0 : xStatusIndicator->setValue(nSteps++);
178 561 : if (!bFlat) //Storage (Package) of Stream
179 : {
180 561 : uno::Reference < embed::XStorage > xStg = rMedium.GetOutputStorage();
181 561 : bool bOASIS = ( SotStorage::GetVersion( xStg ) > SOFFICE_FILEFORMAT_60 );
182 :
183 : // TODO/LATER: handle the case of embedded links gracefully
184 561 : if ( bEmbedded ) //&& !pStg->IsRoot() )
185 : {
186 561 : OUString aName;
187 561 : if ( rMedium.GetItemSet() )
188 : {
189 : const SfxStringItem* pDocHierarchItem = static_cast<const SfxStringItem*>(
190 561 : rMedium.GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME) );
191 561 : if ( pDocHierarchItem )
192 561 : aName = pDocHierarchItem->GetValue();
193 : }
194 :
195 561 : if ( !aName.isEmpty() )
196 : {
197 561 : sPropName = "StreamRelPath";
198 561 : xInfoSet->setPropertyValue( sPropName, makeAny( aName ) );
199 561 : }
200 : }
201 :
202 561 : if ( !bEmbedded )
203 : {
204 0 : if (xStatusIndicator.is())
205 0 : xStatusIndicator->setValue(nSteps++);
206 :
207 : bRet = WriteThroughComponent(
208 : xStg, xModelComp, "meta.xml", xContext, xInfoSet,
209 : (bOASIS ? "com.sun.star.comp.Math.XMLOasisMetaExporter"
210 0 : : "com.sun.star.comp.Math.XMLMetaExporter"));
211 : }
212 561 : if ( bRet )
213 : {
214 561 : if (xStatusIndicator.is())
215 0 : xStatusIndicator->setValue(nSteps++);
216 :
217 : bRet = WriteThroughComponent(
218 : xStg, xModelComp, "content.xml", xContext, xInfoSet,
219 561 : "com.sun.star.comp.Math.XMLContentExporter");
220 : }
221 :
222 561 : if ( bRet )
223 : {
224 561 : if (xStatusIndicator.is())
225 0 : xStatusIndicator->setValue(nSteps++);
226 :
227 : bRet = WriteThroughComponent(
228 : xStg, xModelComp, "settings.xml", xContext, xInfoSet,
229 : (bOASIS ? "com.sun.star.comp.Math.XMLOasisSettingsExporter"
230 561 : : "com.sun.star.comp.Math.XMLSettingsExporter") );
231 561 : }
232 : }
233 : else
234 : {
235 0 : SvStream *pStream = rMedium.GetOutStream();
236 : uno::Reference<io::XOutputStream> xOut(
237 0 : new utl::OOutputStreamWrapper(*pStream) );
238 :
239 0 : if (xStatusIndicator.is())
240 0 : xStatusIndicator->setValue(nSteps++);
241 :
242 : bRet = WriteThroughComponent(
243 : xOut, xModelComp, xContext, xInfoSet,
244 0 : "com.sun.star.comp.Math.XMLContentExporter");
245 : }
246 :
247 561 : if (xStatusIndicator.is())
248 0 : xStatusIndicator->end();
249 :
250 1122 : return bRet;
251 : }
252 :
253 :
254 : /// export through an XML exporter component (output stream version)
255 1122 : bool SmXMLExportWrapper::WriteThroughComponent(
256 : Reference<io::XOutputStream> xOutputStream,
257 : Reference<XComponent> xComponent,
258 : Reference<uno::XComponentContext> & rxContext,
259 : Reference<beans::XPropertySet> & rPropSet,
260 : const sal_Char* pComponentName )
261 : {
262 : OSL_ENSURE(xOutputStream.is(), "I really need an output stream!");
263 : OSL_ENSURE(xComponent.is(), "Need component!");
264 : OSL_ENSURE(NULL != pComponentName, "Need component name!");
265 :
266 : // get component
267 1122 : Reference< xml::sax::XWriter > xSaxWriter = xml::sax::Writer::create(rxContext );
268 :
269 : // connect XML writer to output stream
270 1122 : xSaxWriter->setOutputStream( xOutputStream );
271 :
272 : // prepare arguments (prepend doc handler to given arguments)
273 2244 : Reference<xml::sax::XDocumentHandler> xDocHandler( xSaxWriter,UNO_QUERY);
274 :
275 2244 : Sequence<Any> aArgs( 2 );
276 1122 : aArgs[0] <<= xDocHandler;
277 1122 : aArgs[1] <<= rPropSet;
278 :
279 : // get filter component
280 : Reference< document::XExporter > xExporter(
281 2244 : rxContext->getServiceManager()->createInstanceWithArgumentsAndContext(OUString::createFromAscii(pComponentName), aArgs, rxContext),
282 2244 : UNO_QUERY);
283 : OSL_ENSURE( xExporter.is(),
284 : "can't instantiate export filter component" );
285 1122 : if ( !xExporter.is() )
286 0 : return false;
287 :
288 :
289 : // connect model and filter
290 1122 : xExporter->setSourceDocument( xComponent );
291 :
292 : // filter!
293 2244 : Reference < XFilter > xFilter( xExporter, UNO_QUERY );
294 2244 : uno::Sequence< PropertyValue > aProps(0);
295 1122 : xFilter->filter( aProps );
296 :
297 2244 : uno::Reference<lang::XUnoTunnel> xFilterTunnel;
298 2244 : xFilterTunnel = uno::Reference<lang::XUnoTunnel>
299 1122 : ( xFilter, uno::UNO_QUERY );
300 : SmXMLExport *pFilter = reinterpret_cast< SmXMLExport * >(
301 : sal::static_int_cast< sal_uIntPtr >(
302 1122 : xFilterTunnel->getSomething( SmXMLExport::getUnoTunnelId() )));
303 2244 : return pFilter == nullptr || pFilter->GetSuccess();
304 : }
305 :
306 :
307 : /// export through an XML exporter component (storage version)
308 1122 : bool SmXMLExportWrapper::WriteThroughComponent(
309 : const Reference < embed::XStorage >& xStorage,
310 : Reference<XComponent> xComponent,
311 : const sal_Char* pStreamName,
312 : Reference<uno::XComponentContext> & rxContext,
313 : Reference<beans::XPropertySet> & rPropSet,
314 : const sal_Char* pComponentName
315 : )
316 : {
317 : OSL_ENSURE(xStorage.is(), "Need storage!");
318 : OSL_ENSURE(NULL != pStreamName, "Need stream name!");
319 :
320 : // open stream
321 1122 : Reference < io::XStream > xStream;
322 2244 : OUString sStreamName = OUString::createFromAscii(pStreamName);
323 : try
324 : {
325 3366 : xStream = xStorage->openStreamElement( sStreamName,
326 2244 : embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
327 : }
328 0 : catch ( uno::Exception& rEx )
329 : {
330 : SAL_WARN("starmath", "Can't create output stream in package: " << rEx.Message );
331 0 : return false;
332 : }
333 :
334 2244 : OUString aPropName( "MediaType" );
335 2244 : OUString aMime( "text/xml" );
336 2244 : uno::Any aAny;
337 1122 : aAny <<= aMime;
338 :
339 2244 : uno::Reference < beans::XPropertySet > xSet( xStream, uno::UNO_QUERY );
340 1122 : xSet->setPropertyValue( aPropName, aAny );
341 :
342 : // all streams must be encrypted in encrypted document
343 2244 : OUString aTmpPropName( "UseCommonStoragePasswordEncryption" );
344 1122 : sal_Bool bTrue = sal_True;
345 1122 : aAny.setValue( &bTrue, cppu::UnoType<bool>::get() );
346 1122 : xSet->setPropertyValue( aTmpPropName, aAny );
347 :
348 : // set Base URL
349 1122 : if ( rPropSet.is() )
350 : {
351 1122 : OUString sPropName( "StreamName" );
352 1122 : rPropSet->setPropertyValue( sPropName, makeAny( sStreamName ) );
353 : }
354 :
355 : // write the stuff
356 1122 : bool bRet = WriteThroughComponent( xStream->getOutputStream(), xComponent, rxContext,
357 2244 : rPropSet, pComponentName );
358 :
359 2244 : return bRet;
360 : }
361 :
362 1134 : SmXMLExport::SmXMLExport(
363 : const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XComponentContext >& rContext,
364 : OUString const & implementationName, SvXMLExportFlags nExportFlags)
365 : : SvXMLExport(util::MeasureUnit::INCH, rContext, implementationName, XML_MATH,
366 : nExportFlags)
367 : , pTree(0)
368 1134 : , bSuccess(false)
369 : {
370 1134 : }
371 :
372 1122 : sal_Int64 SAL_CALL SmXMLExport::getSomething(
373 : const uno::Sequence< sal_Int8 >& rId )
374 : throw(uno::RuntimeException, std::exception)
375 : {
376 2244 : if ( rId.getLength() == 16 &&
377 1122 : 0 == memcmp( getUnoTunnelId().getConstArray(),
378 2244 : rId.getConstArray(), 16 ) )
379 1122 : return sal::static_int_cast< sal_Int64 >(reinterpret_cast< sal_uIntPtr >(this));
380 :
381 0 : return SvXMLExport::getSomething( rId );
382 : }
383 :
384 : namespace
385 : {
386 : class theSmXMLExportUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSmXMLExportUnoTunnelId> {};
387 : }
388 :
389 2244 : const uno::Sequence< sal_Int8 > & SmXMLExport::getUnoTunnelId() throw()
390 : {
391 2244 : return theSmXMLExportUnoTunnelId::get().getSeq();
392 : }
393 :
394 : extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* SAL_CALL
395 3 : Math_XMLExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
396 : {
397 3 : return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::ALL));
398 : }
399 :
400 : extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* SAL_CALL
401 3 : Math_XMLMetaExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
402 : {
403 3 : return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLMetaExporter", SvXMLExportFlags::META));
404 : }
405 :
406 : extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* SAL_CALL
407 1 : Math_XMLOasisMetaExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
408 : {
409 1 : return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLOasisMetaExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::META));
410 : }
411 :
412 : extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* SAL_CALL
413 3 : Math_XMLSettingsExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
414 : {
415 3 : return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLSettingsExporter", SvXMLExportFlags::SETTINGS));
416 : }
417 :
418 : extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* SAL_CALL
419 562 : Math_XMLOasisSettingsExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
420 : {
421 562 : return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLOasisSettingsExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::SETTINGS));
422 : }
423 :
424 : extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* SAL_CALL
425 562 : Math_XMLContentExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
426 : {
427 562 : return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLContentExporter", SvXMLExportFlags::OASIS|SvXMLExportFlags::CONTENT));
428 : }
429 :
430 1122 : sal_uInt32 SmXMLExport::exportDoc(enum XMLTokenEnum eClass)
431 : {
432 1122 : if ( !(getExportFlags() & SvXMLExportFlags::CONTENT) )
433 : {
434 561 : SvXMLExport::exportDoc( eClass );
435 : }
436 : else
437 : {
438 561 : uno::Reference <frame::XModel> xModel = GetModel();
439 1122 : uno::Reference <lang::XUnoTunnel> xTunnel;
440 561 : xTunnel = uno::Reference <lang::XUnoTunnel> (xModel,uno::UNO_QUERY);
441 : SmModel *pModel = reinterpret_cast<SmModel *>
442 561 : (xTunnel->getSomething(SmModel::getUnoTunnelId()));
443 :
444 561 : if (pModel)
445 : {
446 : SmDocShell *pDocShell =
447 561 : static_cast<SmDocShell*>(pModel->GetObjectShell());
448 561 : pTree = pDocShell->GetFormulaTree();
449 561 : aText = pDocShell->GetText();
450 : }
451 :
452 561 : GetDocHandler()->startDocument();
453 :
454 561 : addChaffWhenEncryptedStorage();
455 :
456 : /*Add xmlns line*/
457 561 : SvXMLAttributeList &rList = GetAttrList();
458 :
459 : // make use of a default namespace
460 561 : 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)
461 561 : _GetNamespaceMap().Add( OUString(), GetXMLToken(XML_N_MATH), XML_NAMESPACE_MATH );
462 :
463 561 : rList.AddAttribute(GetNamespaceMap().GetAttrNameByKey(XML_NAMESPACE_MATH_IDX),
464 1122 : GetNamespaceMap().GetNameByKey( XML_NAMESPACE_MATH_IDX));
465 :
466 : //I think we need something like ImplExportEntities();
467 561 : _ExportContent();
468 1122 : GetDocHandler()->endDocument();
469 : }
470 :
471 1122 : bSuccess=true;
472 1122 : return 0;
473 : }
474 :
475 561 : void SmXMLExport::_ExportContent()
476 : {
477 561 : uno::Reference <frame::XModel> xModel = GetModel();
478 1122 : uno::Reference <lang::XUnoTunnel> xTunnel;
479 561 : xTunnel = uno::Reference <lang::XUnoTunnel> (xModel,uno::UNO_QUERY);
480 : SmModel *pModel = reinterpret_cast<SmModel *>
481 561 : (xTunnel->getSomething(SmModel::getUnoTunnelId()));
482 : SmDocShell *pDocShell = pModel ?
483 561 : static_cast<SmDocShell*>(pModel->GetObjectShell()) : 0;
484 : OSL_ENSURE( pDocShell, "doc shell missing" );
485 :
486 561 : if (pDocShell && !pDocShell->GetFormat().IsTextmode())
487 : {
488 : // If the Math equation is not in text mode, we attach a display="block"
489 : // attribute on the <math> root. We don't do anything if it is in
490 : // text mode, the default display="inline" value will be used.
491 561 : AddAttribute(XML_NAMESPACE_MATH, XML_DISPLAY, XML_BLOCK);
492 : }
493 1122 : SvXMLElementExport aEquation(*this, XML_NAMESPACE_MATH, XML_MATH, true, true);
494 561 : SvXMLElementExport *pSemantics=0;
495 :
496 561 : if (!aText.isEmpty())
497 : {
498 : pSemantics = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
499 559 : XML_SEMANTICS, true, true);
500 : }
501 :
502 561 : ExportNodes(pTree, 0);
503 :
504 561 : if (!aText.isEmpty())
505 : {
506 : // Convert symbol names
507 559 : if (pDocShell)
508 : {
509 559 : SmParser &rParser = pDocShell->GetParser();
510 559 : bool bVal = rParser.IsExportSymbolNames();
511 559 : rParser.SetExportSymbolNames( true );
512 559 : SmNode *pTmpTree = rParser.Parse( aText );
513 559 : aText = rParser.GetText();
514 559 : delete pTmpTree;
515 559 : rParser.SetExportSymbolNames( bVal );
516 : }
517 :
518 : AddAttribute(XML_NAMESPACE_MATH, XML_ENCODING,
519 559 : OUString("StarMath 5.0"));
520 : SvXMLElementExport aAnnotation(*this, XML_NAMESPACE_MATH,
521 559 : XML_ANNOTATION, true, false);
522 559 : GetDocHandler()->characters( aText );
523 : }
524 1122 : delete pSemantics;
525 561 : }
526 :
527 561 : void SmXMLExport::GetViewSettings( Sequence < PropertyValue >& aProps)
528 : {
529 561 : uno::Reference <frame::XModel> xModel = GetModel();
530 561 : if ( !xModel.is() )
531 0 : return;
532 :
533 1122 : uno::Reference <lang::XUnoTunnel> xTunnel;
534 561 : xTunnel = uno::Reference <lang::XUnoTunnel> (xModel,uno::UNO_QUERY);
535 : SmModel *pModel = reinterpret_cast<SmModel *>
536 561 : (xTunnel->getSomething(SmModel::getUnoTunnelId()));
537 :
538 561 : if ( !pModel )
539 0 : return;
540 :
541 : SmDocShell *pDocShell =
542 561 : static_cast<SmDocShell*>(pModel->GetObjectShell());
543 561 : if ( !pDocShell )
544 0 : return;
545 :
546 561 : aProps.realloc( 4 );
547 561 : PropertyValue *pValue = aProps.getArray();
548 561 : sal_Int32 nIndex = 0;
549 :
550 561 : Rectangle aRect( pDocShell->GetVisArea() );
551 :
552 561 : pValue[nIndex].Name = "ViewAreaTop";
553 561 : pValue[nIndex++].Value <<= aRect.Top();
554 :
555 561 : pValue[nIndex].Name = "ViewAreaLeft";
556 561 : pValue[nIndex++].Value <<= aRect.Left();
557 :
558 561 : pValue[nIndex].Name = "ViewAreaWidth";
559 561 : pValue[nIndex++].Value <<= aRect.GetWidth();
560 :
561 561 : pValue[nIndex].Name = "ViewAreaHeight";
562 1122 : pValue[nIndex++].Value <<= aRect.GetHeight();
563 : }
564 :
565 561 : void SmXMLExport::GetConfigurationSettings( Sequence < PropertyValue > & rProps)
566 : {
567 561 : Reference < XPropertySet > xProps ( GetModel(), UNO_QUERY );
568 561 : if ( xProps.is() )
569 : {
570 561 : Reference< XPropertySetInfo > xPropertySetInfo = xProps->getPropertySetInfo();
571 561 : if (xPropertySetInfo.is())
572 : {
573 561 : Sequence< Property > aProps = xPropertySetInfo->getProperties();
574 561 : sal_Int32 nCount(aProps.getLength());
575 561 : if (nCount > 0)
576 : {
577 561 : rProps.realloc(nCount);
578 561 : PropertyValue* pProps = rProps.getArray();
579 561 : if (pProps)
580 : {
581 561 : SmMathConfig *pConfig = SM_MOD()->GetConfig();
582 561 : const bool bUsedSymbolsOnly = pConfig && pConfig->IsSaveOnlyUsedSymbols();
583 :
584 561 : const OUString sFormula ( "Formula" );
585 1122 : const OUString sBasicLibraries ( "BasicLibraries" );
586 1122 : const OUString sDialogLibraries ( "DialogLibraries" );
587 1122 : const OUString sRuntimeUID ( "RuntimeUID" );
588 37587 : for (sal_Int32 i = 0; i < nCount; i++, pProps++)
589 : {
590 37026 : const OUString &rPropName = aProps[i].Name;
591 110517 : if (rPropName != sFormula &&
592 72369 : rPropName != sBasicLibraries &&
593 108273 : rPropName != sDialogLibraries &&
594 35343 : rPropName != sRuntimeUID)
595 : {
596 34782 : pProps->Name = rPropName;
597 :
598 34782 : OUString aActualName( rPropName );
599 :
600 : // handle 'save used symbols only'
601 34782 : if (bUsedSymbolsOnly && rPropName == "Symbols" )
602 561 : aActualName = "UserDefinedSymbolsInUse";
603 :
604 34782 : pProps->Value = xProps->getPropertyValue( aActualName );
605 : }
606 561 : }
607 : }
608 561 : }
609 561 : }
610 561 : }
611 561 : }
612 :
613 559 : void SmXMLExport::ExportLine(const SmNode *pNode, int nLevel)
614 : {
615 559 : ExportExpression(pNode, nLevel);
616 559 : }
617 :
618 364 : void SmXMLExport::ExportBinaryHorizontal(const SmNode *pNode, int nLevel)
619 : {
620 364 : sal_uLong nGroup = pNode->GetToken().nGroup;
621 :
622 : SvXMLElementExport* pRow = new SvXMLElementExport(*this,
623 364 : XML_NAMESPACE_MATH, XML_MROW, true, true);
624 :
625 : // Unfold the binary tree structure as long as the nodes are SmBinHorNode
626 : // with the same nGroup. This will reduce the number of nested <mrow>
627 : // elements e.g. we only need three <mrow> levels to export
628 :
629 : // "a*b*c*d+e*f*g*h+i*j*k*l = a*b*c*d+e*f*g*h+i*j*k*l =
630 : // a*b*c*d+e*f*g*h+i*j*k*l = a*b*c*d+e*f*g*h+i*j*k*l"
631 :
632 : // See https://www.libreoffice.org/bugzilla/show_bug.cgi?id=66081
633 364 : ::std::stack< const SmNode* > s;
634 364 : s.push(pNode);
635 2388 : while (!s.empty())
636 : {
637 1660 : const SmNode *node = s.top();
638 1660 : s.pop();
639 1660 : if (node->GetType() != NBINHOR || node->GetToken().nGroup != nGroup)
640 : {
641 1228 : ExportNodes(node, nLevel+1);
642 1228 : continue;
643 : }
644 432 : const SmBinHorNode* binNode = static_cast<const SmBinHorNode*>(node);
645 432 : s.push(binNode->RightOperand());
646 432 : s.push(binNode->Symbol());
647 432 : s.push(binNode->LeftOperand());
648 : }
649 :
650 364 : delete pRow;
651 364 : }
652 :
653 36 : void SmXMLExport::ExportUnaryHorizontal(const SmNode *pNode, int nLevel)
654 : {
655 36 : ExportExpression(pNode, nLevel);
656 36 : }
657 :
658 1367 : void SmXMLExport::ExportExpression(const SmNode *pNode, int nLevel,
659 : bool bNoMrowContainer /*=false*/)
660 : {
661 1367 : SvXMLElementExport *pRow=0;
662 1367 : auto nSize = pNode->GetNumSubNodes();
663 :
664 : // #i115443: nodes of type expression always need to be grouped with mrow statement
665 1867 : if (!bNoMrowContainer &&
666 863 : (nSize > 1 || pNode->GetType() == NEXPRESSION))
667 500 : pRow = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MROW, true, true);
668 :
669 3780 : for (sal_uInt16 i = 0; i < nSize; i++)
670 2413 : if (const SmNode *pTemp = pNode->GetSubNode(i))
671 2373 : ExportNodes(pTemp, nLevel+1);
672 :
673 1367 : delete pRow;
674 1367 : }
675 :
676 142 : void SmXMLExport::ExportBinaryVertical(const SmNode *pNode, int nLevel)
677 : {
678 : OSL_ENSURE(pNode->GetNumSubNodes()==3,"Bad Fraction");
679 142 : const SmNode *pNum = pNode->GetSubNode(0);
680 142 : const SmNode *pDenom = pNode->GetSubNode(2);
681 142 : if (pNum->GetType() == NALIGN && pNum->GetToken().eType != TALIGNC)
682 : {
683 : // A left or right alignment is specified on the numerator:
684 : // attach the corresponding numalign attribute.
685 : AddAttribute(XML_NAMESPACE_MATH, XML_NUMALIGN,
686 0 : pNum->GetToken().eType == TALIGNL ? XML_LEFT : XML_RIGHT);
687 : }
688 142 : if (pDenom->GetType() == NALIGN && pDenom->GetToken().eType != TALIGNC)
689 : {
690 : // A left or right alignment is specified on the denominator:
691 : // attach the corresponding denomalign attribute.
692 : AddAttribute(XML_NAMESPACE_MATH, XML_DENOMALIGN,
693 0 : pDenom->GetToken().eType == TALIGNL ? XML_LEFT : XML_RIGHT);
694 : }
695 142 : SvXMLElementExport aFraction(*this, XML_NAMESPACE_MATH, XML_MFRAC, true, true);
696 142 : ExportNodes(pNum, nLevel);
697 142 : ExportNodes(pDenom, nLevel);
698 142 : }
699 :
700 0 : void SmXMLExport::ExportBinaryDiagonal(const SmNode *pNode, int nLevel)
701 : {
702 : OSL_ENSURE(pNode->GetNumSubNodes()==3, "Bad Slash");
703 :
704 0 : if (pNode->GetToken().eType == TWIDESLASH)
705 : {
706 : // wideslash
707 : // export the node as <mfrac bevelled="true">
708 0 : AddAttribute(XML_NAMESPACE_MATH, XML_BEVELLED, XML_TRUE);
709 : SvXMLElementExport aFraction(*this, XML_NAMESPACE_MATH, XML_MFRAC,
710 0 : true, true);
711 0 : ExportNodes(pNode->GetSubNode(0), nLevel);
712 0 : ExportNodes(pNode->GetSubNode(1), nLevel);
713 : }
714 : else
715 : {
716 : // widebslash
717 : // We can not use <mfrac> to a backslash, so just use <mo>\</mo>
718 : SvXMLElementExport *pRow = new SvXMLElementExport(*this,
719 0 : XML_NAMESPACE_MATH, XML_MROW, true, true);
720 :
721 0 : ExportNodes(pNode->GetSubNode(0), nLevel);
722 :
723 : { // Scoping for <mo> creation
724 : SvXMLElementExport aMo(*this, XML_NAMESPACE_MATH, XML_MO,
725 0 : true, true);
726 0 : sal_Unicode nArse[2] = {MS_BACKSLASH,0x00};
727 0 : GetDocHandler()->characters(nArse);
728 : }
729 :
730 0 : ExportNodes(pNode->GetSubNode(1), nLevel);
731 :
732 0 : delete pRow;
733 : }
734 0 : }
735 :
736 621 : void SmXMLExport::ExportTable(const SmNode *pNode, int nLevel)
737 : {
738 621 : SvXMLElementExport *pTable=0;
739 :
740 621 : sal_uInt16 nSize = pNode->GetNumSubNodes();
741 :
742 : //If the list ends in newline then the last entry has
743 : //no subnodes, the newline is superfulous so we just drop
744 : //the last node, inclusion would create a bad MathML
745 : //table
746 621 : if (nSize >= 1)
747 : {
748 621 : const SmNode *pLine = pNode->GetSubNode(nSize-1);
749 2364 : if (pLine->GetType() == NLINE && pLine->GetNumSubNodes() == 1 &&
750 1743 : pLine->GetSubNode(0) != NULL &&
751 561 : pLine->GetSubNode(0)->GetToken().eType == TNEWLINE)
752 2 : --nSize;
753 : }
754 :
755 : // try to avoid creating a mtable element when the formula consists only
756 : // of a single output line
757 621 : if (nLevel || (nSize >1))
758 60 : pTable = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTABLE, true, true);
759 :
760 1304 : for (sal_uInt16 i = 0; i < nSize; i++)
761 683 : if (const SmNode *pTemp = pNode->GetSubNode(i))
762 : {
763 683 : SvXMLElementExport *pRow=0;
764 683 : SvXMLElementExport *pCell=0;
765 683 : if (pTable)
766 : {
767 124 : pRow = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTR, true, true);
768 124 : SmTokenType eAlign = TALIGNC;
769 124 : if (pTemp->GetType() == NALIGN)
770 : {
771 : // For Binom() and Stack() constructions, the NALIGN nodes
772 : // are direct children.
773 : // binom{alignl ...}{alignr ...} and
774 : // stack{alignl ... ## alignr ... ## ...}
775 0 : eAlign = pTemp->GetToken().eType;
776 : }
777 248 : else if (pTemp->GetType() == NLINE &&
778 0 : pTemp->GetNumSubNodes() == 1 &&
779 124 : pTemp->GetSubNode(0) &&
780 0 : pTemp->GetSubNode(0)->GetType() == NALIGN)
781 : {
782 : // For the Table() construction, the NALIGN node is a child
783 : // of an NLINE node.
784 : // alignl ... newline alignr ... newline ...
785 0 : eAlign = pTemp->GetSubNode(0)->GetToken().eType;
786 : }
787 124 : if (eAlign != TALIGNC)
788 : {
789 : // If a left or right alignment is specified on this line,
790 : // attach the corresponding columnalign attribute.
791 : AddAttribute(XML_NAMESPACE_MATH, XML_COLUMNALIGN,
792 0 : eAlign == TALIGNL ? XML_LEFT : XML_RIGHT);
793 : }
794 124 : pCell = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTD, true, true);
795 : }
796 683 : ExportNodes(pTemp, nLevel+1);
797 683 : delete pCell;
798 683 : delete pRow;
799 : }
800 :
801 621 : delete pTable;
802 621 : }
803 :
804 1416 : void SmXMLExport::ExportMath(const SmNode *pNode, int /*nLevel*/)
805 : {
806 1416 : const SmMathSymbolNode *pTemp = static_cast<const SmMathSymbolNode *>(pNode);
807 1416 : SvXMLElementExport *pMath = 0;
808 :
809 1416 : if (pNode->GetType() == NMATH || pNode->GetType() == NGLYPH_SPECIAL)
810 : {
811 : // Export NMATH and NGLYPH_SPECIAL symbols as <mo> elements
812 1356 : pMath = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MO, true, false);
813 : }
814 : else
815 : {
816 : // Export NMATHIDENT and NPLACE symbols as <mi> elements:
817 : // - These math symbols should not be drawn slanted. Hence we should
818 : // attach a mathvariant="normal" attribute to single-char <mi> elements
819 : // that are not mathematical alphanumeric symbol. For simplicity and to
820 : // work around browser limitations, we always attach such an attribute.
821 : // - The MathML specification suggests to use empty <mi> elements as
822 : // placeholders but they won't be visible in most MathML rendering
823 : // engines so let's use an empty square for NPLACE instead.
824 60 : AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
825 60 : pMath = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false);
826 : }
827 : sal_Unicode nArse[2];
828 1416 : nArse[0] = pTemp->GetText()[0];
829 1416 : sal_Unicode cTmp = ConvertMathToMathML( nArse[0] );
830 1416 : if (cTmp != 0)
831 1416 : nArse[0] = cTmp;
832 : OSL_ENSURE(nArse[0] != 0xffff,"Non existent symbol");
833 1416 : nArse[1] = 0;
834 1416 : GetDocHandler()->characters(nArse);
835 :
836 1416 : delete pMath;
837 1416 : }
838 :
839 2779 : void SmXMLExport::ExportText(const SmNode *pNode, int /*nLevel*/)
840 : {
841 : SvXMLElementExport *pText;
842 2779 : const SmTextNode *pTemp = static_cast<const SmTextNode *>(pNode);
843 2779 : switch (pNode->GetToken().eType)
844 : {
845 : default:
846 : case TIDENT:
847 : {
848 : //Note that we change the fontstyle to italic for strings that
849 : //are italic and longer than a single character.
850 1989 : bool bIsItalic = IsItalic( pTemp->GetFont() );
851 1989 : if ((pTemp->GetText().getLength() > 1) && bIsItalic)
852 158 : AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_ITALIC);
853 1831 : else if ((pTemp->GetText().getLength() == 1) && !bIsItalic)
854 0 : AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
855 1989 : pText = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false);
856 1989 : break;
857 : }
858 : case TNUMBER:
859 742 : pText = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MN, true, false);
860 742 : break;
861 : case TTEXT:
862 48 : pText = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTEXT, true, false);
863 48 : break;
864 : }
865 2779 : GetDocHandler()->characters(pTemp->GetText());
866 2779 : delete pText;
867 2779 : }
868 :
869 0 : void SmXMLExport::ExportBlank(const SmNode *pNode, int /*nLevel*/)
870 : {
871 0 : const SmBlankNode *pTemp = static_cast<const SmBlankNode *>(pNode);
872 : //!! exports an <mspace> element. Note that for example "~_~" is allowed in
873 : //!! Math (so it has no sense at all) but must not result in an empty
874 : //!! <msub> tag in MathML !!
875 :
876 0 : if (pTemp->GetBlankNum() != 0)
877 : {
878 : // Attach a width attribute. We choose the (somewhat arbitrary) values
879 : // ".5em" for a small gap '`' and "2em" for a large gap '~'.
880 : // (see SmBlankNode::IncreaseBy for how pTemp->nNum is set).
881 0 : OUStringBuffer sStrBuf;
882 0 : ::sax::Converter::convertDouble(sStrBuf, pTemp->GetBlankNum() * .5);
883 0 : sStrBuf.append("em");
884 0 : AddAttribute(XML_NAMESPACE_MATH, XML_WIDTH, sStrBuf.getStr());
885 : }
886 :
887 : SvXMLElementExport *pText;
888 :
889 : pText = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSPACE,
890 0 : true, false);
891 :
892 0 : GetDocHandler()->characters( OUString() );
893 0 : delete pText;
894 0 : }
895 :
896 496 : void SmXMLExport::ExportSubSupScript(const SmNode *pNode, int nLevel)
897 : {
898 496 : const SmNode *pSub = 0;
899 496 : const SmNode *pSup = 0;
900 496 : const SmNode *pCSub = 0;
901 496 : const SmNode *pCSup = 0;
902 496 : const SmNode *pLSub = 0;
903 496 : const SmNode *pLSup = 0;
904 496 : SvXMLElementExport *pThing2 = nullptr;
905 :
906 : //if we have prescripts at all then we must use the tensor notation
907 :
908 : //This is one of those excellent locations where scope is vital to
909 : //arrange the construction and destruction of the element helper
910 : //classes correctly
911 496 : pLSub = pNode->GetSubNode(LSUB+1);
912 496 : pLSup = pNode->GetSubNode(LSUP+1);
913 496 : if (pLSub || pLSup)
914 : {
915 : SvXMLElementExport aMultiScripts(*this, XML_NAMESPACE_MATH,
916 24 : XML_MMULTISCRIPTS, true, true);
917 :
918 :
919 24 : if (NULL != (pCSub = pNode->GetSubNode(CSUB+1))
920 24 : && NULL != (pCSup = pNode->GetSubNode(CSUP+1)))
921 : {
922 : pThing2 = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
923 0 : XML_MUNDEROVER, true, true);
924 : }
925 24 : else if (NULL != (pCSub = pNode->GetSubNode(CSUB+1)))
926 : {
927 : pThing2 = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
928 0 : XML_MUNDER, true, true);
929 : }
930 24 : else if (NULL != (pCSup = pNode->GetSubNode(CSUP+1)))
931 : {
932 : pThing2 = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
933 0 : XML_MOVER, true, true);
934 : }
935 :
936 24 : ExportNodes(pNode->GetSubNode(0), nLevel+1); //Main Term
937 :
938 24 : if (pCSub)
939 0 : ExportNodes(pCSub, nLevel+1);
940 24 : if (pCSup)
941 0 : ExportNodes(pCSup, nLevel+1);
942 24 : delete pThing2;
943 :
944 24 : pSub = pNode->GetSubNode(RSUB+1);
945 24 : pSup = pNode->GetSubNode(RSUP+1);
946 24 : if (pSub || pSup)
947 : {
948 0 : if (pSub)
949 0 : ExportNodes(pSub, nLevel+1);
950 : else
951 : {
952 0 : SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE, true, true);
953 : }
954 0 : if (pSup)
955 0 : ExportNodes(pSup, nLevel+1);
956 : else
957 : {
958 0 : SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE, true, true);
959 : }
960 : }
961 :
962 : //Separator element between suffix and prefix sub/sup pairs
963 : {
964 : SvXMLElementExport aPrescripts(*this, XML_NAMESPACE_MATH,
965 24 : XML_MPRESCRIPTS, true, true);
966 : }
967 :
968 24 : if (pLSub)
969 24 : ExportNodes(pLSub, nLevel+1);
970 : else
971 : {
972 : SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE,
973 0 : true, true);
974 :
975 : }
976 24 : if (pLSup)
977 24 : ExportNodes(pLSup, nLevel+1);
978 : else
979 : {
980 : SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE,
981 0 : true, true);
982 :
983 24 : }
984 : }
985 : else
986 : {
987 472 : SvXMLElementExport *pThing = nullptr;
988 472 : if (NULL != (pSub = pNode->GetSubNode(RSUB+1)) &&
989 : NULL != (pSup = pNode->GetSubNode(RSUP+1)))
990 : {
991 : pThing = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
992 56 : XML_MSUBSUP, true, true);
993 : }
994 416 : else if (NULL != (pSub = pNode->GetSubNode(RSUB+1)))
995 : {
996 : pThing = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSUB,
997 80 : true, true);
998 : }
999 336 : else if (NULL != (pSup = pNode->GetSubNode(RSUP+1)))
1000 : {
1001 : pThing = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSUP,
1002 218 : true, true);
1003 : }
1004 :
1005 472 : if (NULL != (pCSub = pNode->GetSubNode(CSUB+1))
1006 472 : && NULL != (pCSup=pNode->GetSubNode(CSUP+1)))
1007 : {
1008 : pThing2 = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
1009 88 : XML_MUNDEROVER, true, true);
1010 : }
1011 384 : else if (NULL != (pCSub = pNode->GetSubNode(CSUB+1)))
1012 : {
1013 : pThing2 = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
1014 20 : XML_MUNDER, true, true);
1015 : }
1016 364 : else if (NULL != (pCSup = pNode->GetSubNode(CSUP+1)))
1017 : {
1018 : pThing2 = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
1019 10 : XML_MOVER, true, true);
1020 : }
1021 472 : ExportNodes(pNode->GetSubNode(0), nLevel+1); //Main Term
1022 :
1023 472 : if (pCSub)
1024 108 : ExportNodes(pCSub, nLevel+1);
1025 472 : if (pCSup)
1026 98 : ExportNodes(pCSup, nLevel+1);
1027 472 : delete pThing2;
1028 :
1029 472 : if (pSub)
1030 136 : ExportNodes(pSub, nLevel+1);
1031 472 : if (pSup)
1032 274 : ExportNodes(pSup, nLevel+1);
1033 472 : delete pThing;
1034 : }
1035 496 : }
1036 :
1037 292 : void SmXMLExport::ExportBrace(const SmNode *pNode, int nLevel)
1038 : {
1039 : const SmNode *pTemp;
1040 292 : const SmNode *pLeft=pNode->GetSubNode(0);
1041 292 : const SmNode *pRight=pNode->GetSubNode(2);
1042 292 : SvXMLElementExport *pRow=0;
1043 :
1044 : // This used to generate <mfenced> or <mrow>+<mo> elements according to
1045 : // the stretchiness of fences. The MathML recommendation defines an
1046 : // <mrow>+<mo> construction that is equivalent to the <mfenced> element:
1047 : // http://www.w3.org/TR/MathML3/chapter3.html#presm.mfenced
1048 : // To simplify our code and avoid issues with mfenced implementations in
1049 : // MathML rendering engines, we now always generate <mrow>+<mo> elements.
1050 : // See #fdo 66282.
1051 :
1052 : // <mrow>
1053 : pRow = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MROW,
1054 292 : true, true);
1055 :
1056 : // <mo fence="true"> opening-fence </mo>
1057 292 : if (pLeft && (pLeft->GetToken().eType != TNONE))
1058 : {
1059 286 : AddAttribute(XML_NAMESPACE_MATH, XML_FENCE, XML_TRUE);
1060 286 : if (pNode->GetScaleMode() == SCALE_HEIGHT)
1061 278 : AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
1062 : else
1063 8 : AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
1064 286 : ExportNodes(pLeft, nLevel+1);
1065 : }
1066 :
1067 292 : if (NULL != (pTemp = pNode->GetSubNode(1)))
1068 : {
1069 : // <mrow>
1070 : SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW,
1071 292 : true, true);
1072 292 : ExportNodes(pTemp, nLevel+1);
1073 : // </mrow>
1074 : }
1075 :
1076 : // <mo fence="true"> closing-fence </mo>
1077 292 : if (pRight && (pRight->GetToken().eType != TNONE))
1078 : {
1079 282 : AddAttribute(XML_NAMESPACE_MATH, XML_FENCE, XML_TRUE);
1080 282 : if (pNode->GetScaleMode() == SCALE_HEIGHT)
1081 274 : AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
1082 : else
1083 8 : AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
1084 282 : ExportNodes(pRight, nLevel+1);
1085 : }
1086 :
1087 292 : delete pRow;
1088 : // </mrow>
1089 292 : }
1090 :
1091 52 : void SmXMLExport::ExportRoot(const SmNode *pNode, int nLevel)
1092 : {
1093 52 : if (pNode->GetSubNode(0))
1094 : {
1095 : SvXMLElementExport aRoot(*this, XML_NAMESPACE_MATH, XML_MROOT, true,
1096 10 : true);
1097 10 : ExportNodes(pNode->GetSubNode(2), nLevel+1);
1098 10 : ExportNodes(pNode->GetSubNode(0), nLevel+1);
1099 : }
1100 : else
1101 : {
1102 : SvXMLElementExport aSqrt(*this, XML_NAMESPACE_MATH, XML_MSQRT, true,
1103 42 : true);
1104 42 : ExportNodes(pNode->GetSubNode(2), nLevel+1);
1105 : }
1106 52 : }
1107 :
1108 106 : void SmXMLExport::ExportOperator(const SmNode *pNode, int nLevel)
1109 : {
1110 : /*we need to either use content or font and size attributes
1111 : *here*/
1112 : SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW,
1113 106 : true, true);
1114 106 : ExportNodes(pNode->GetSubNode(0), nLevel+1);
1115 106 : ExportNodes(pNode->GetSubNode(1), nLevel+1);
1116 106 : }
1117 :
1118 168 : void SmXMLExport::ExportAttributes(const SmNode *pNode, int nLevel)
1119 : {
1120 168 : SvXMLElementExport *pElement=0;
1121 :
1122 168 : if (pNode->GetToken().eType == TUNDERLINE)
1123 : {
1124 : AddAttribute(XML_NAMESPACE_MATH, XML_ACCENTUNDER,
1125 12 : XML_TRUE);
1126 : pElement = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MUNDER,
1127 12 : true, true);
1128 : }
1129 156 : else if (pNode->GetToken().eType == TOVERSTRIKE)
1130 : {
1131 : // export as <menclose notation="horizontalstrike">
1132 12 : AddAttribute(XML_NAMESPACE_MATH, XML_NOTATION, XML_HORIZONTALSTRIKE);
1133 : pElement = new SvXMLElementExport(*this, XML_NAMESPACE_MATH,
1134 12 : XML_MENCLOSE, true, true);
1135 : }
1136 : else
1137 : {
1138 : AddAttribute(XML_NAMESPACE_MATH, XML_ACCENT,
1139 144 : XML_TRUE);
1140 : pElement = new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MOVER,
1141 144 : true, true);
1142 : }
1143 :
1144 168 : ExportNodes(pNode->GetSubNode(1), nLevel+1);
1145 168 : switch (pNode->GetToken().eType)
1146 : {
1147 : case TOVERLINE:
1148 : {
1149 : //proper entity support required
1150 : SvXMLElementExport aMath(*this, XML_NAMESPACE_MATH, XML_MO,
1151 0 : true, true);
1152 0 : sal_Unicode nArse[2] = {0xAF,0x00};
1153 0 : GetDocHandler()->characters(nArse);
1154 : }
1155 0 : break;
1156 : case TUNDERLINE:
1157 : {
1158 : //proper entity support required
1159 : SvXMLElementExport aMath(*this, XML_NAMESPACE_MATH, XML_MO,
1160 12 : true, true);
1161 12 : sal_Unicode nArse[2] = {0x0332,0x00};
1162 12 : GetDocHandler()->characters(nArse);
1163 : }
1164 12 : break;
1165 : case TOVERSTRIKE:
1166 12 : break;
1167 : case TWIDETILDE:
1168 : case TWIDEHAT:
1169 : case TWIDEVEC:
1170 : {
1171 : // make these wide accents stretchy
1172 72 : AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
1173 72 : ExportNodes(pNode->GetSubNode(0), nLevel+1);
1174 : }
1175 72 : break;
1176 : default:
1177 72 : ExportNodes(pNode->GetSubNode(0), nLevel+1);
1178 72 : break;
1179 : }
1180 168 : delete pElement;
1181 168 : }
1182 :
1183 40 : static bool lcl_HasEffectOnMathvariant( const SmTokenType eType )
1184 : {
1185 40 : return eType == TBOLD || eType == TNBOLD ||
1186 40 : eType == TITALIC || eType == TNITALIC ||
1187 80 : eType == TSANS || eType == TSERIF || eType == TFIXED;
1188 : }
1189 :
1190 40 : void SmXMLExport::ExportFont(const SmNode *pNode, int nLevel)
1191 : {
1192 :
1193 : // gather the mathvariant attribute relevant data from all
1194 : // successively following SmFontNodes...
1195 :
1196 40 : int nBold = -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true;
1197 40 : int nItalic = -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true;
1198 40 : int nSansSerifFixed = -1;
1199 40 : SmTokenType eNodeType = TUNKNOWN;
1200 80 : while (lcl_HasEffectOnMathvariant( (eNodeType = pNode->GetToken().eType) ))
1201 : {
1202 0 : switch (eNodeType)
1203 : {
1204 0 : case TBOLD : nBold = 1; break;
1205 0 : case TNBOLD : nBold = 0; break;
1206 0 : case TITALIC : nItalic = 1; break;
1207 0 : case TNITALIC : nItalic = 0; break;
1208 0 : case TSANS : nSansSerifFixed = 0; break;
1209 0 : case TSERIF : nSansSerifFixed = 1; break;
1210 0 : case TFIXED : nSansSerifFixed = 2; break;
1211 : default:
1212 : SAL_WARN("starmath", "unexpected case");
1213 : }
1214 : // According to the parser every node that is to be evaluated heres
1215 : // has a single non-zero subnode at index 1!! Thus we only need to check
1216 : // that single node for follow-up nodes that have an effect on the attribute.
1217 0 : if (pNode->GetNumSubNodes() > 1 && pNode->GetSubNode(1) &&
1218 0 : lcl_HasEffectOnMathvariant( pNode->GetSubNode(1)->GetToken().eType))
1219 : {
1220 0 : pNode = pNode->GetSubNode(1);
1221 : }
1222 : else
1223 0 : break;
1224 : }
1225 :
1226 40 : switch (pNode->GetToken().eType)
1227 : {
1228 : case TPHANTOM:
1229 : // No attribute needed. An <mphantom> element will be used below.
1230 0 : break;
1231 : case TBLACK:
1232 0 : AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_BLACK);
1233 0 : break;
1234 : case TWHITE:
1235 0 : AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_WHITE);
1236 0 : break;
1237 : case TRED:
1238 0 : AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_RED);
1239 0 : break;
1240 : case TGREEN:
1241 0 : AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_GREEN);
1242 0 : break;
1243 : case TBLUE:
1244 0 : AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_BLUE);
1245 0 : break;
1246 : case TCYAN:
1247 0 : AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_AQUA);
1248 0 : break;
1249 : case TMAGENTA:
1250 0 : AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_FUCHSIA);
1251 0 : break;
1252 : case TYELLOW:
1253 0 : AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_YELLOW);
1254 0 : break;
1255 : case TSILVER:
1256 0 : AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_SILVER);
1257 0 : break;
1258 : case TGRAY:
1259 0 : AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_GRAY);
1260 0 : break;
1261 : case TMAROON:
1262 0 : AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_MAROON);
1263 0 : break;
1264 : case TOLIVE:
1265 0 : AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_OLIVE);
1266 0 : break;
1267 : case TLIME:
1268 0 : AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_LIME);
1269 0 : break;
1270 : case TAQUA:
1271 0 : AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_AQUA);
1272 0 : break;
1273 : case TTEAL:
1274 0 : AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_TEAL);
1275 0 : break;
1276 : case TNAVY:
1277 0 : AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_NAVY);
1278 0 : break;
1279 : case TFUCHSIA:
1280 0 : AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_FUCHSIA);
1281 0 : break;
1282 : case TPURPLE:
1283 0 : AddAttribute(XML_NAMESPACE_MATH, XML_COLOR, XML_PURPLE);
1284 0 : break;
1285 : case TSIZE:
1286 : {
1287 40 : const SmFontNode *pFontNode = static_cast<const SmFontNode *>(pNode);
1288 40 : const Fraction &aFrac = pFontNode->GetSizeParameter();
1289 :
1290 40 : OUStringBuffer sStrBuf;
1291 40 : switch(pFontNode->GetSizeType())
1292 : {
1293 : case FontSizeType::MULTIPLY:
1294 : ::sax::Converter::convertDouble(sStrBuf,
1295 0 : static_cast<double>(aFrac*Fraction(100.00)));
1296 0 : sStrBuf.append('%');
1297 0 : break;
1298 : case FontSizeType::DIVIDE:
1299 : ::sax::Converter::convertDouble(sStrBuf,
1300 0 : static_cast<double>(Fraction(100.00)/aFrac));
1301 0 : sStrBuf.append('%');
1302 0 : break;
1303 : case FontSizeType::ABSOLUT:
1304 : ::sax::Converter::convertDouble(sStrBuf,
1305 40 : static_cast<double>(aFrac));
1306 : sStrBuf.append(
1307 40 : GetXMLToken(XML_UNIT_PT));
1308 40 : break;
1309 : default:
1310 : {
1311 : //The problem here is that the wheels fall off because
1312 : //font size is stored in 100th's of a mm not pts, and
1313 : //rounding errors take their toll on the original
1314 : //value specified in points.
1315 :
1316 : //Must fix StarMath to retain the original pt values
1317 0 : Fraction aTemp = Sm100th_mmToPts(pFontNode->GetFont().
1318 0 : GetSize().Height());
1319 :
1320 0 : if (pFontNode->GetSizeType() == FontSizeType::MINUS)
1321 0 : aTemp-=aFrac;
1322 : else
1323 0 : aTemp+=aFrac;
1324 :
1325 0 : double mytest = static_cast<double>(aTemp);
1326 :
1327 0 : mytest = ::rtl::math::round(mytest,1);
1328 0 : ::sax::Converter::convertDouble(sStrBuf,mytest);
1329 0 : sStrBuf.append(GetXMLToken(XML_UNIT_PT));
1330 : }
1331 0 : break;
1332 : }
1333 :
1334 40 : OUString sStr(sStrBuf.makeStringAndClear());
1335 40 : AddAttribute(XML_NAMESPACE_MATH, XML_MATHSIZE, sStr);
1336 : }
1337 40 : break;
1338 : case TBOLD:
1339 : case TITALIC:
1340 : case TNBOLD:
1341 : case TNITALIC:
1342 : case TFIXED:
1343 : case TSANS:
1344 : case TSERIF:
1345 : {
1346 : // nBold: -1 = yet undefined; 0 = false; 1 = true;
1347 : // nItalic: -1 = yet undefined; 0 = false; 1 = true;
1348 : // nSansSerifFixed: -1 = undefined; 0 = sans; 1 = serif; 2 = fixed;
1349 0 : const sal_Char *pText = "normal";
1350 0 : if (nSansSerifFixed == -1 || nSansSerifFixed == 1)
1351 : {
1352 0 : pText = "normal";
1353 0 : if (nBold == 1 && nItalic != 1)
1354 0 : pText = "bold";
1355 0 : else if (nBold != 1 && nItalic == 1)
1356 0 : pText = "italic";
1357 0 : else if (nBold == 1 && nItalic == 1)
1358 0 : pText = "bold-italic";
1359 : }
1360 0 : else if (nSansSerifFixed == 0)
1361 : {
1362 0 : pText = "sans-serif";
1363 0 : if (nBold == 1 && nItalic != 1)
1364 0 : pText = "bold-sans-serif";
1365 0 : else if (nBold != 1 && nItalic == 1)
1366 0 : pText = "sans-serif-italic";
1367 0 : else if (nBold == 1 && nItalic == 1)
1368 0 : pText = "sans-serif-bold-italic";
1369 : }
1370 0 : else if (nSansSerifFixed == 2)
1371 0 : pText = "monospace"; // no modifiers allowed for monospace ...
1372 : else
1373 : {
1374 : SAL_WARN("starmath", "unexpected case");
1375 : }
1376 0 : AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, OUString::createFromAscii( pText ));
1377 : }
1378 0 : break;
1379 : default:
1380 0 : break;
1381 :
1382 : }
1383 : {
1384 : // Wrap everything in an <mphantom> or <mstyle> element. These elements
1385 : // are mrow-like, so ExportExpression doesn't need to add an explicit
1386 : // <mrow> element. See #fdo 66283.
1387 : SvXMLElementExport aElement(*this, XML_NAMESPACE_MATH,
1388 40 : pNode->GetToken().eType == TPHANTOM ? XML_MPHANTOM : XML_MSTYLE,
1389 40 : true, true);
1390 40 : ExportExpression(pNode, nLevel, true);
1391 : }
1392 40 : }
1393 :
1394 :
1395 24 : void SmXMLExport::ExportVerticalBrace(const SmNode *pNode, int nLevel)
1396 : {
1397 : // "[body] overbrace [script]"
1398 :
1399 : // Position body, overbrace and script vertically. First place the overbrace
1400 : // OVER the body and then the script OVER this expression.
1401 :
1402 : // [script]
1403 : // --[overbrace]--
1404 : // XXXXXX[body]XXXXXXX
1405 :
1406 : // Similarly for the underbrace construction.
1407 :
1408 : XMLTokenEnum which;
1409 :
1410 24 : switch (pNode->GetToken().eType)
1411 : {
1412 : case TOVERBRACE:
1413 : default:
1414 12 : which = XML_MOVER;
1415 12 : break;
1416 : case TUNDERBRACE:
1417 12 : which = XML_MUNDER;
1418 12 : break;
1419 : }
1420 :
1421 : OSL_ENSURE(pNode->GetNumSubNodes()==3,"Bad Vertical Brace");
1422 24 : SvXMLElementExport aOver1(*this, XML_NAMESPACE_MATH,which, true, true);
1423 : {//Scoping
1424 : // using accents will draw the over-/underbraces too close to the base
1425 : // see http://www.w3.org/TR/MathML2/chapter3.html#id.3.4.5.2
1426 : // also XML_ACCENT is illegal with XML_MUNDER. Thus no XML_ACCENT attribute here!
1427 24 : SvXMLElementExport aOver2(*this, XML_NAMESPACE_MATH,which, true, true);
1428 24 : ExportNodes(pNode->GetSubNode(0), nLevel);
1429 24 : AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
1430 24 : ExportNodes(pNode->GetSubNode(1), nLevel);
1431 : }
1432 24 : ExportNodes(pNode->GetSubNode(2), nLevel);
1433 24 : }
1434 :
1435 12 : void SmXMLExport::ExportMatrix(const SmNode *pNode, int nLevel)
1436 : {
1437 12 : SvXMLElementExport aTable(*this, XML_NAMESPACE_MATH, XML_MTABLE, true, true);
1438 12 : const SmMatrixNode *pMatrix = static_cast<const SmMatrixNode *>(pNode);
1439 12 : sal_uInt16 i=0;
1440 36 : for (sal_uInt16 y = 0; y < pMatrix->GetNumRows(); y++)
1441 : {
1442 24 : SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MTR, true, true);
1443 72 : for (sal_uInt16 x = 0; x < pMatrix->GetNumCols(); x++)
1444 48 : if (const SmNode *pTemp = pNode->GetSubNode(i++))
1445 : {
1446 48 : if (pTemp->GetType() == NALIGN &&
1447 0 : pTemp->GetToken().eType != TALIGNC)
1448 : {
1449 : // A left or right alignment is specified on this cell,
1450 : // attach the corresponding columnalign attribute.
1451 : AddAttribute(XML_NAMESPACE_MATH, XML_COLUMNALIGN,
1452 0 : pTemp->GetToken().eType == TALIGNL ?
1453 0 : XML_LEFT : XML_RIGHT);
1454 : }
1455 48 : SvXMLElementExport aCell(*this, XML_NAMESPACE_MATH, XML_MTD, true, true);
1456 48 : ExportNodes(pTemp, nLevel+1);
1457 : }
1458 36 : }
1459 12 : }
1460 :
1461 7855 : void SmXMLExport::ExportNodes(const SmNode *pNode, int nLevel)
1462 : {
1463 7855 : if (!pNode)
1464 7855 : return;
1465 7855 : switch(pNode->GetType())
1466 : {
1467 : case NTABLE:
1468 621 : ExportTable(pNode, nLevel);
1469 621 : break;
1470 : case NALIGN:
1471 : case NBRACEBODY:
1472 : case NEXPRESSION:
1473 732 : ExportExpression(pNode, nLevel);
1474 732 : break;
1475 : case NLINE:
1476 559 : ExportLine(pNode, nLevel);
1477 559 : break;
1478 : case NTEXT:
1479 2779 : ExportText(pNode, nLevel);
1480 2779 : break;
1481 : case NGLYPH_SPECIAL:
1482 : case NMATH:
1483 : {
1484 1356 : sal_Unicode cTmp = 0;
1485 1356 : const SmTextNode *pTemp = static_cast< const SmTextNode * >(pNode);
1486 1356 : if (!pTemp->GetText().isEmpty())
1487 1356 : cTmp = ConvertMathToMathML( pTemp->GetText()[0] );
1488 1356 : if (cTmp == 0)
1489 : {
1490 : // no conversion to MathML implemented -> export it as text
1491 : // thus at least it will not vanish into nothing
1492 0 : ExportText(pNode, nLevel);
1493 : }
1494 : else
1495 : {
1496 : //To fully handle generic MathML we need to implement the full
1497 : //operator dictionary, we will generate MathML with explicit
1498 : //stretchiness for now.
1499 1356 : sal_Int16 nLength = GetAttrList().getLength();
1500 1356 : bool bAddStretch=true;
1501 1924 : for ( sal_Int16 i = 0; i < nLength; i++ )
1502 : {
1503 1232 : OUString sLocalName;
1504 1232 : sal_uInt16 nPrefix = GetNamespaceMap().GetKeyByAttrName(
1505 2464 : GetAttrList().getNameByIndex(i), &sLocalName );
1506 :
1507 2464 : if ( ( XML_NAMESPACE_MATH == nPrefix ) &&
1508 1232 : IsXMLToken(sLocalName, XML_STRETCHY) )
1509 : {
1510 664 : bAddStretch = false;
1511 664 : break;
1512 : }
1513 568 : }
1514 1356 : if (bAddStretch)
1515 : {
1516 692 : AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
1517 : }
1518 1356 : ExportMath(pNode, nLevel);
1519 : }
1520 : }
1521 1356 : break;
1522 : case NSPECIAL: //NSPECIAL requires some sort of Entity preservation in the XML engine.
1523 : case NMATHIDENT :
1524 : case NPLACE:
1525 60 : ExportMath(pNode, nLevel);
1526 60 : break;
1527 : case NBINHOR:
1528 364 : ExportBinaryHorizontal(pNode, nLevel);
1529 364 : break;
1530 : case NUNHOR:
1531 36 : ExportUnaryHorizontal(pNode, nLevel);
1532 36 : break;
1533 : case NBRACE:
1534 292 : ExportBrace(pNode, nLevel);
1535 292 : break;
1536 : case NBINVER:
1537 142 : ExportBinaryVertical(pNode, nLevel);
1538 142 : break;
1539 : case NBINDIAGONAL:
1540 0 : ExportBinaryDiagonal(pNode, nLevel);
1541 0 : break;
1542 : case NSUBSUP:
1543 496 : ExportSubSupScript(pNode, nLevel);
1544 496 : break;
1545 : case NROOT:
1546 52 : ExportRoot(pNode, nLevel);
1547 52 : break;
1548 : case NOPER:
1549 106 : ExportOperator(pNode, nLevel);
1550 106 : break;
1551 : case NATTRIBUT:
1552 168 : ExportAttributes(pNode, nLevel);
1553 168 : break;
1554 : case NFONT:
1555 40 : ExportFont(pNode, nLevel);
1556 40 : break;
1557 : case NVERTICAL_BRACE:
1558 24 : ExportVerticalBrace(pNode, nLevel);
1559 24 : break;
1560 : case NMATRIX:
1561 12 : ExportMatrix(pNode, nLevel);
1562 12 : break;
1563 : case NBLANK:
1564 0 : ExportBlank(pNode, nLevel);
1565 0 : break;
1566 : default:
1567 : SAL_WARN("starmath", "Warning: failed to export a node?");
1568 16 : break;
1569 :
1570 : }
1571 42 : }
1572 :
1573 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|