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