/src/libreoffice/starmath/source/ooxmlimport.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 | | |
10 | | #include <sal/config.h> |
11 | | |
12 | | #include <string_view> |
13 | | |
14 | | #include "ooxmlimport.hxx" |
15 | | #include <types.hxx> |
16 | | |
17 | | #include <oox/mathml/importutils.hxx> |
18 | | #include <oox/token/namespaces.hxx> |
19 | | #include <rtl/ustring.hxx> |
20 | | #include <rtl/ustrbuf.hxx> |
21 | | #include <sal/log.hxx> |
22 | | #include <o3tl/string_view.hxx> |
23 | | #include <parse5.hxx> |
24 | | #include <unordered_set> |
25 | | |
26 | | using namespace oox::formulaimport; |
27 | | |
28 | | /* |
29 | | The primary internal data structure for the formula is the text representation |
30 | | (the SmNode tree is built from it), so read data must be converted into this format. |
31 | | */ |
32 | | |
33 | 0 | #define OPENING( token ) XML_STREAM_OPENING( token ) |
34 | 0 | #define CLOSING( token ) XML_STREAM_CLOSING( token ) |
35 | | |
36 | | // TODO create IS_OPENING(), IS_CLOSING() instead of doing 'next == OPENING( next )' ? |
37 | | |
38 | | SmOoxmlImport::SmOoxmlImport( oox::formulaimport::XmlStream& s ) |
39 | 0 | : m_rStream( s ) |
40 | 0 | { |
41 | 0 | } |
42 | | |
43 | | OUString SmOoxmlImport::ConvertToStarMath() |
44 | 0 | { |
45 | 0 | return handleStream(); |
46 | 0 | } |
47 | | |
48 | | // "toplevel" of reading, there will be oMath (if there was oMathPara, that was |
49 | | // up to the parent component to handle) |
50 | | |
51 | | // NOT complete |
52 | | OUString SmOoxmlImport::handleStream() |
53 | 0 | { |
54 | 0 | m_rStream.ensureOpeningTag( M_TOKEN( oMath )); |
55 | 0 | OUStringBuffer ret; |
56 | 0 | while( !m_rStream.atEnd() && m_rStream.currentToken() != CLOSING( M_TOKEN( oMath ))) |
57 | 0 | { |
58 | | // strictly speaking, it is not OMathArg here, but currently supported |
59 | | // functionality is the same like OMathArg, in the future this may need improving |
60 | 0 | OUString item = readOMathArg( M_TOKEN( oMath )); |
61 | 0 | if( item.isEmpty()) |
62 | 0 | continue; |
63 | 0 | if( !ret.isEmpty()) |
64 | 0 | ret.append(" "); |
65 | 0 | ret.append(item); |
66 | 0 | } |
67 | 0 | m_rStream.ensureClosingTag( M_TOKEN( oMath )); |
68 | | // Placeholders are written out as nothing (i.e. nothing inside e.g. the <e> element), |
69 | | // which will result in "{}" in the formula text. Fix this up. |
70 | 0 | OUString ret2 = ret.makeStringAndClear().replaceAll( "{}", "<?>" ); |
71 | | // And as a result, empty parts of the formula that are not placeholders are written out |
72 | | // as a single space, so fix that up too. |
73 | 0 | ret2 = ret2.replaceAll( "{ }", "{}" ); |
74 | 0 | SAL_INFO( "starmath.ooxml", "Formula: " << ret2 ); |
75 | 0 | return ret2; |
76 | 0 | } |
77 | | |
78 | | OUString SmOoxmlImport::readOMathArg( int stoptoken ) |
79 | 0 | { |
80 | 0 | OUStringBuffer ret; |
81 | 0 | while( !m_rStream.atEnd() && m_rStream.currentToken() != CLOSING( stoptoken )) |
82 | 0 | { |
83 | 0 | if( !ret.isEmpty()) |
84 | 0 | ret.append(" "); |
85 | 0 | switch( m_rStream.currentToken()) |
86 | 0 | { |
87 | 0 | case OPENING( M_TOKEN( acc )): |
88 | 0 | ret.append(handleAcc()); |
89 | 0 | break; |
90 | 0 | case OPENING( M_TOKEN( bar )): |
91 | 0 | ret.append(handleBar()); |
92 | 0 | break; |
93 | 0 | case OPENING( M_TOKEN( box )): |
94 | 0 | ret.append(handleBox()); |
95 | 0 | break; |
96 | 0 | case OPENING( M_TOKEN( borderBox )): |
97 | 0 | ret.append(handleBorderBox()); |
98 | 0 | break; |
99 | 0 | case OPENING( M_TOKEN( d )): |
100 | 0 | ret.append(handleD()); |
101 | 0 | break; |
102 | 0 | case OPENING( M_TOKEN( eqArr )): |
103 | 0 | ret.append(handleEqArr()); |
104 | 0 | break; |
105 | 0 | case OPENING( M_TOKEN( f )): |
106 | 0 | ret.append(handleF()); |
107 | 0 | break; |
108 | 0 | case OPENING( M_TOKEN( func )): |
109 | 0 | ret.append(handleFunc()); |
110 | 0 | break; |
111 | 0 | case OPENING( M_TOKEN( limLow )): |
112 | 0 | ret.append(handleLimLowUpp( LimLow )); |
113 | 0 | break; |
114 | 0 | case OPENING( M_TOKEN( limUpp )): |
115 | 0 | ret.append(handleLimLowUpp( LimUpp )); |
116 | 0 | break; |
117 | 0 | case OPENING( M_TOKEN( groupChr )): |
118 | 0 | ret.append(handleGroupChr()); |
119 | 0 | break; |
120 | 0 | case OPENING( M_TOKEN( m )): |
121 | 0 | ret.append(handleM()); |
122 | 0 | break; |
123 | 0 | case OPENING( M_TOKEN( nary )): |
124 | 0 | ret.append(handleNary()); |
125 | 0 | break; |
126 | 0 | case OPENING( M_TOKEN( r )): |
127 | 0 | ret.append(handleR()); |
128 | 0 | break; |
129 | 0 | case OPENING( M_TOKEN( rad )): |
130 | 0 | ret.append(handleRad()); |
131 | 0 | break; |
132 | 0 | case OPENING( M_TOKEN( sPre )): |
133 | 0 | ret.append(handleSpre()); |
134 | 0 | break; |
135 | 0 | case OPENING( M_TOKEN( sSub )): |
136 | 0 | ret.append(handleSsub()); |
137 | 0 | break; |
138 | 0 | case OPENING( M_TOKEN( sSubSup )): |
139 | 0 | ret.append(handleSsubsup()); |
140 | 0 | break; |
141 | 0 | case OPENING( M_TOKEN( sSup )): |
142 | 0 | ret.append(handleSsup()); |
143 | 0 | break; |
144 | 0 | default: |
145 | 0 | m_rStream.handleUnexpectedTag(); |
146 | 0 | break; |
147 | 0 | } |
148 | 0 | } |
149 | 0 | return ret.makeStringAndClear(); |
150 | 0 | } |
151 | | |
152 | | OUString SmOoxmlImport::readOMathArgInElement( int token ) |
153 | 0 | { |
154 | 0 | m_rStream.ensureOpeningTag( token ); |
155 | 0 | OUString ret = readOMathArg( token ); |
156 | 0 | m_rStream.ensureClosingTag( token ); |
157 | 0 | return ret; |
158 | 0 | } |
159 | | |
160 | | OUString SmOoxmlImport::handleAcc() |
161 | 0 | { |
162 | 0 | m_rStream.ensureOpeningTag( M_TOKEN( acc )); |
163 | 0 | sal_Unicode accChr = 0x302; |
164 | 0 | if( XmlStream::Tag accPr = m_rStream.checkOpeningTag( M_TOKEN( accPr ))) |
165 | 0 | { |
166 | 0 | if( XmlStream::Tag chr = m_rStream.checkOpeningTag( M_TOKEN( chr ))) |
167 | 0 | { |
168 | 0 | accChr = chr.attribute( M_TOKEN( val ), accChr ); |
169 | 0 | m_rStream.ensureClosingTag( M_TOKEN( chr )); |
170 | 0 | } |
171 | 0 | m_rStream.ensureClosingTag( M_TOKEN( accPr )); |
172 | 0 | } |
173 | | // see aTokenTable in parse.cxx |
174 | 0 | OUString acc; |
175 | 0 | switch( accChr ) |
176 | 0 | { |
177 | 0 | case MS_BAR: |
178 | 0 | case MS_COMBBAR: |
179 | 0 | acc = "bar"; |
180 | 0 | break; |
181 | 0 | case MS_CHECK: |
182 | 0 | case MS_COMBCHECK: |
183 | 0 | acc = "check"; |
184 | 0 | break; |
185 | 0 | case MS_ACUTE: |
186 | 0 | case MS_COMBACUTE: |
187 | 0 | acc = "acute"; |
188 | 0 | break; |
189 | 0 | case MS_COMBOVERLINE: |
190 | 0 | acc = "overline"; |
191 | 0 | break; |
192 | 0 | case MS_GRAVE: |
193 | 0 | case MS_COMBGRAVE: |
194 | 0 | acc = "grave"; |
195 | 0 | break; |
196 | 0 | case MS_BREVE: |
197 | 0 | case MS_COMBBREVE: |
198 | 0 | acc = "breve"; |
199 | 0 | break; |
200 | 0 | case MS_CIRCLE: |
201 | 0 | case MS_COMBCIRCLE: |
202 | 0 | acc = "circle"; |
203 | 0 | break; |
204 | 0 | case MS_RIGHTARROW: |
205 | 0 | case MS_VEC: |
206 | | // prefer wide variants for these 3, .docx can't seem to differentiate |
207 | | // between e.g. 'vec' and 'widevec', if whatever the accent is above is short, this |
208 | | // shouldn't matter, but short above a longer expression doesn't look right |
209 | 0 | acc = "widevec"; |
210 | 0 | break; |
211 | 0 | case MS_LVEC: |
212 | 0 | acc = "wideleftvec"; |
213 | 0 | break; |
214 | 0 | case MS_HARPOON: |
215 | 0 | acc = "wideharpoon"; |
216 | 0 | break; |
217 | 0 | case MS_LHARPOON: |
218 | 0 | acc = "wideleftharpoon"; |
219 | 0 | break; |
220 | 0 | case MS_TILDE: |
221 | 0 | case MS_COMBTILDE: |
222 | 0 | acc = "widetilde"; |
223 | 0 | break; |
224 | 0 | case MS_HAT: |
225 | 0 | case MS_COMBHAT: |
226 | 0 | acc = "widehat"; |
227 | 0 | break; |
228 | 0 | case MS_DOT: |
229 | 0 | case MS_COMBDOT: |
230 | 0 | acc = "dot"; |
231 | 0 | break; |
232 | 0 | case MS_DDOT: |
233 | 0 | case MS_COMBDDOT: |
234 | 0 | acc = "ddot"; |
235 | 0 | break; |
236 | 0 | case MS_DDDOT: |
237 | 0 | acc = "dddot"; |
238 | 0 | break; |
239 | 0 | default: |
240 | 0 | acc = "acute"; |
241 | 0 | SAL_WARN( "starmath.ooxml", "Unknown m:chr in m:acc \'" << OUString(accChr) << "\'" ); |
242 | 0 | break; |
243 | 0 | } |
244 | 0 | OUString e = readOMathArgInElement( M_TOKEN( e )); |
245 | 0 | m_rStream.ensureClosingTag( M_TOKEN( acc )); |
246 | 0 | return acc + " {" + e + "}"; |
247 | 0 | } |
248 | | |
249 | | OUString SmOoxmlImport::handleBar() |
250 | 0 | { |
251 | 0 | m_rStream.ensureOpeningTag( M_TOKEN( bar )); |
252 | 0 | enum pos_t { top, bot } topbot = bot; |
253 | 0 | if( m_rStream.checkOpeningTag( M_TOKEN( barPr ))) |
254 | 0 | { |
255 | 0 | if( XmlStream::Tag pos = m_rStream.checkOpeningTag( M_TOKEN( pos ))) |
256 | 0 | { |
257 | 0 | if( pos.attribute( M_TOKEN( val )) == "top" ) |
258 | 0 | topbot = top; |
259 | 0 | else if( pos.attribute( M_TOKEN( val )) == "bot" ) |
260 | 0 | topbot = bot; |
261 | 0 | m_rStream.ensureClosingTag( M_TOKEN( pos )); |
262 | 0 | } |
263 | 0 | m_rStream.ensureClosingTag( M_TOKEN( barPr )); |
264 | 0 | } |
265 | 0 | OUString e = readOMathArgInElement( M_TOKEN( e )); |
266 | 0 | m_rStream.ensureClosingTag( M_TOKEN( bar )); |
267 | 0 | if( topbot == top ) |
268 | 0 | return "overline {" + e + "}"; |
269 | 0 | else |
270 | 0 | return "underline {" + e + "}"; |
271 | 0 | } |
272 | | |
273 | | OUString SmOoxmlImport::handleBox() |
274 | 0 | { |
275 | | // there does not seem to be functionality in LO to actually implement this |
276 | | // (or is there), but at least read in the contents instead of ignoring them |
277 | 0 | m_rStream.ensureOpeningTag( M_TOKEN( box )); |
278 | 0 | OUString e = readOMathArgInElement( M_TOKEN( e )); |
279 | 0 | m_rStream.ensureClosingTag( M_TOKEN( box )); |
280 | 0 | return e; |
281 | 0 | } |
282 | | |
283 | | |
284 | | OUString SmOoxmlImport::handleBorderBox() |
285 | 0 | { |
286 | 0 | m_rStream.ensureOpeningTag( M_TOKEN( borderBox )); |
287 | 0 | bool isStrikeH = false; |
288 | 0 | if( m_rStream.checkOpeningTag( M_TOKEN( borderBoxPr ))) |
289 | 0 | { |
290 | 0 | if( XmlStream::Tag strikeH = m_rStream.checkOpeningTag( M_TOKEN( strikeH ))) |
291 | 0 | { |
292 | 0 | if( strikeH.attribute( M_TOKEN( val ), false )) |
293 | 0 | isStrikeH = true; |
294 | 0 | m_rStream.ensureClosingTag( M_TOKEN( strikeH )); |
295 | 0 | } |
296 | 0 | m_rStream.ensureClosingTag( M_TOKEN( borderBoxPr )); |
297 | 0 | } |
298 | 0 | OUString e = readOMathArgInElement( M_TOKEN( e )); |
299 | 0 | m_rStream.ensureClosingTag( M_TOKEN( borderBox )); |
300 | 0 | if( isStrikeH ) |
301 | 0 | return "overstrike {" + e + "}"; |
302 | | // LO does not seem to implement anything for handling the other cases |
303 | 0 | return e; |
304 | 0 | } |
305 | | |
306 | | OUString SmOoxmlImport::handleD() |
307 | 0 | { |
308 | 0 | m_rStream.ensureOpeningTag( M_TOKEN( d )); |
309 | 0 | OUString opening = u"("_ustr; |
310 | 0 | OUString closing = u")"_ustr; |
311 | 0 | OUString separator = u"|"_ustr; |
312 | 0 | if( XmlStream::Tag dPr = m_rStream.checkOpeningTag( M_TOKEN( dPr ))) |
313 | 0 | { |
314 | 0 | if( XmlStream::Tag begChr = m_rStream.checkOpeningTag( M_TOKEN( begChr ))) |
315 | 0 | { |
316 | 0 | opening = begChr.attribute( M_TOKEN( val ), opening ); |
317 | 0 | m_rStream.ensureClosingTag( M_TOKEN( begChr )); |
318 | 0 | } |
319 | 0 | if( XmlStream::Tag sepChr = m_rStream.checkOpeningTag( M_TOKEN( sepChr ))) |
320 | 0 | { |
321 | 0 | separator = sepChr.attribute( M_TOKEN( val ), separator ); |
322 | 0 | m_rStream.ensureClosingTag( M_TOKEN( sepChr )); |
323 | 0 | } |
324 | 0 | if( XmlStream::Tag endChr = m_rStream.checkOpeningTag( M_TOKEN( endChr ))) |
325 | 0 | { |
326 | 0 | closing = endChr.attribute( M_TOKEN( val ), closing ); |
327 | 0 | m_rStream.ensureClosingTag( M_TOKEN( endChr )); |
328 | 0 | } |
329 | 0 | m_rStream.ensureClosingTag( M_TOKEN( dPr )); |
330 | 0 | } |
331 | 0 | if( opening == "{" ) |
332 | 0 | opening = "left lbrace "; |
333 | 0 | if( closing == "}" ) |
334 | 0 | closing = " right rbrace"; |
335 | 0 | if( opening == u"\u27e6" ) |
336 | 0 | opening = "left ldbracket "; |
337 | 0 | if( closing == u"\u27e7" ) |
338 | 0 | closing = " right rdbracket"; |
339 | 0 | if( opening == "|" ) |
340 | 0 | opening = "left lline "; |
341 | 0 | if( closing == "|" ) |
342 | 0 | closing = " right rline"; |
343 | 0 | if (opening == OUStringChar(MS_DLINE) |
344 | 0 | || opening == OUStringChar(MS_DVERTLINE)) |
345 | 0 | opening = "left ldline "; |
346 | 0 | if (closing == OUStringChar(MS_DLINE) |
347 | 0 | || closing == OUStringChar(MS_DVERTLINE)) |
348 | 0 | closing = " right rdline"; |
349 | 0 | if (opening == OUStringChar(MS_LANGLE) |
350 | 0 | || opening == OUStringChar(MS_LMATHANGLE)) |
351 | 0 | opening = "left langle "; |
352 | 0 | if (closing == OUStringChar(MS_RANGLE) |
353 | 0 | || closing == OUStringChar(MS_RMATHANGLE)) |
354 | 0 | closing = " right rangle"; |
355 | | // use scalable brackets (the explicit "left" or "right") |
356 | 0 | if( opening == "(" || opening == "[" || opening == ")" || opening == "]" ) |
357 | 0 | opening = "left " + opening; |
358 | 0 | if( closing == ")" || closing == "]" || closing == "(" || closing == "[") |
359 | 0 | closing = " right " + closing; |
360 | 0 | if( separator == "|" ) // plain "|" would be actually "V" (logical or) |
361 | 0 | separator = " mline "; |
362 | 0 | if( opening.isEmpty()) |
363 | 0 | opening = "left none "; |
364 | 0 | if( closing.isEmpty()) |
365 | 0 | closing = " right none"; |
366 | 0 | OUStringBuffer ret( opening ); |
367 | 0 | bool first = true; |
368 | 0 | while( m_rStream.findTag( OPENING( M_TOKEN( e )))) |
369 | 0 | { |
370 | 0 | if( !first ) |
371 | 0 | ret.append( separator ); |
372 | 0 | first = false; |
373 | 0 | ret.append( readOMathArgInElement( M_TOKEN( e ))); |
374 | 0 | } |
375 | 0 | ret.append( closing ); |
376 | 0 | m_rStream.ensureClosingTag( M_TOKEN( d )); |
377 | 0 | return ret.makeStringAndClear(); |
378 | 0 | } |
379 | | |
380 | | OUString SmOoxmlImport::handleEqArr() |
381 | 0 | { |
382 | 0 | m_rStream.ensureOpeningTag( M_TOKEN( eqArr )); |
383 | 0 | OUStringBuffer ret; |
384 | 0 | do |
385 | 0 | { // there must be at least one m:e |
386 | 0 | if( !ret.isEmpty()) |
387 | 0 | ret.append("#"); |
388 | 0 | ret.append(" " |
389 | 0 | + readOMathArgInElement( M_TOKEN( e )) |
390 | 0 | + " "); |
391 | 0 | } while( !m_rStream.atEnd() && m_rStream.findTag( OPENING( M_TOKEN( e )))); |
392 | 0 | m_rStream.ensureClosingTag( M_TOKEN( eqArr )); |
393 | 0 | return "stack {" + ret + "}"; |
394 | 0 | } |
395 | | |
396 | | OUString SmOoxmlImport::handleF() |
397 | 0 | { |
398 | 0 | m_rStream.ensureOpeningTag( M_TOKEN( f )); |
399 | 0 | enum operation_t { bar, lin, noBar } operation = bar; |
400 | 0 | if( m_rStream.checkOpeningTag( M_TOKEN( fPr ))) |
401 | 0 | { |
402 | 0 | if( XmlStream::Tag type = m_rStream.checkOpeningTag( M_TOKEN( type ))) |
403 | 0 | { |
404 | 0 | if( type.attribute( M_TOKEN( val )) == "bar" ) |
405 | 0 | operation = bar; |
406 | 0 | else if( type.attribute( M_TOKEN( val )) == "lin" ) |
407 | 0 | operation = lin; |
408 | 0 | else if( type.attribute( M_TOKEN( val )) == "noBar" ) |
409 | 0 | operation = noBar; |
410 | 0 | m_rStream.ensureClosingTag( M_TOKEN( type )); |
411 | 0 | } |
412 | 0 | m_rStream.ensureClosingTag( M_TOKEN( fPr )); |
413 | 0 | } |
414 | 0 | OUString num = readOMathArgInElement( M_TOKEN( num )); |
415 | 0 | OUString den = readOMathArgInElement( M_TOKEN( den )); |
416 | 0 | m_rStream.ensureClosingTag( M_TOKEN( f )); |
417 | 0 | if( operation == bar ) |
418 | 0 | return "{" + num + "} over {" + den + "}"; |
419 | 0 | else if( operation == lin ) |
420 | 0 | return "{" + num + "} / {" + den + "}"; |
421 | 0 | else // noBar |
422 | 0 | { |
423 | 0 | return "binom {" + num + "} {" + den + "}"; |
424 | 0 | } |
425 | 0 | } |
426 | | |
427 | | OUString SmOoxmlImport::handleFunc() |
428 | 0 | { |
429 | | //lim from{x rightarrow 1} x |
430 | 0 | m_rStream.ensureOpeningTag( M_TOKEN( func )); |
431 | 0 | OUString fname = readOMathArgInElement( M_TOKEN( fName )); |
432 | | // fix the various functions |
433 | 0 | if( fname.startsWith( "lim csub {" )) |
434 | 0 | fname = OUString::Concat("lim from {") + fname.subView( 10 ); |
435 | 0 | OUString ret = fname + " {" + readOMathArgInElement( M_TOKEN( e )) + "}"; |
436 | 0 | m_rStream.ensureClosingTag( M_TOKEN( func )); |
437 | 0 | return ret; |
438 | 0 | } |
439 | | |
440 | | OUString SmOoxmlImport::handleLimLowUpp( LimLowUpp_t limlowupp ) |
441 | 0 | { |
442 | 0 | int token = limlowupp == LimLow ? M_TOKEN( limLow ) : M_TOKEN( limUpp ); |
443 | 0 | m_rStream.ensureOpeningTag( token ); |
444 | 0 | OUString e = readOMathArgInElement( M_TOKEN( e )); |
445 | 0 | OUString lim = readOMathArgInElement( M_TOKEN( lim )); |
446 | 0 | m_rStream.ensureClosingTag( token ); |
447 | | // fix up overbrace/underbrace (use { }, as {} will be converted to a placeholder) |
448 | 0 | if( limlowupp == LimUpp && e.endsWith( " overbrace { }" )) |
449 | 0 | return e.subView( 0, e.getLength() - 2 ) + lim + "}"; |
450 | 0 | if( limlowupp == LimLow && e.endsWith( " underbrace { }" )) |
451 | 0 | return e.subView( 0, e.getLength() - 2 ) + lim + "}"; |
452 | 0 | return e |
453 | 0 | + ( limlowupp == LimLow |
454 | 0 | ? std::u16string_view( u" csub {" ) : std::u16string_view( u" csup {" )) |
455 | 0 | + lim + "}"; |
456 | 0 | } |
457 | | |
458 | | OUString SmOoxmlImport::handleGroupChr() |
459 | 0 | { |
460 | 0 | m_rStream.ensureOpeningTag( M_TOKEN( groupChr )); |
461 | 0 | sal_Unicode chr = 0x23df; |
462 | 0 | enum pos_t { top, bot } pos = bot; |
463 | 0 | if( m_rStream.checkOpeningTag( M_TOKEN( groupChrPr ))) |
464 | 0 | { |
465 | 0 | if( XmlStream::Tag chrTag = m_rStream.checkOpeningTag( M_TOKEN( chr ))) |
466 | 0 | { |
467 | 0 | chr = chrTag.attribute( M_TOKEN( val ), chr ); |
468 | 0 | m_rStream.ensureClosingTag( M_TOKEN( chr )); |
469 | 0 | } |
470 | 0 | if( XmlStream::Tag posTag = m_rStream.checkOpeningTag( M_TOKEN( pos ))) |
471 | 0 | { |
472 | 0 | if( posTag.attribute( M_TOKEN( val ), u"bot"_ustr) == "top" ) |
473 | 0 | pos = top; |
474 | 0 | m_rStream.ensureClosingTag( M_TOKEN( pos )); |
475 | 0 | } |
476 | 0 | m_rStream.ensureClosingTag( M_TOKEN( groupChrPr )); |
477 | 0 | } |
478 | 0 | OUString e = readOMathArgInElement( M_TOKEN( e )); |
479 | 0 | m_rStream.ensureClosingTag( M_TOKEN( groupChr )); |
480 | 0 | if( pos == top && chr == u'\x23de') |
481 | 0 | return "{" + e + "} overbrace { }"; |
482 | 0 | if( pos == bot && chr == u'\x23df') |
483 | 0 | return "{" + e + "} underbrace { }"; |
484 | 0 | if( pos == top ) |
485 | 0 | return "{" + e + "} csup {" + OUStringChar( chr ) + "}"; |
486 | 0 | else |
487 | 0 | return "{" + e + "} csub {" + OUStringChar( chr ) + "}"; |
488 | 0 | } |
489 | | |
490 | | OUString SmOoxmlImport::handleM() |
491 | 0 | { |
492 | 0 | m_rStream.ensureOpeningTag( M_TOKEN( m )); |
493 | 0 | OUStringBuffer allrows; |
494 | 0 | do // there must be at least one m:mr |
495 | 0 | { |
496 | 0 | m_rStream.ensureOpeningTag( M_TOKEN( mr )); |
497 | 0 | OUStringBuffer row; |
498 | 0 | do // there must be at least one m:e |
499 | 0 | { |
500 | 0 | if( !row.isEmpty()) |
501 | 0 | row.append(" # "); |
502 | 0 | row.append(readOMathArgInElement( M_TOKEN( e ))); |
503 | 0 | } while( !m_rStream.atEnd() && m_rStream.findTag( OPENING( M_TOKEN( e )))); |
504 | 0 | if( !allrows.isEmpty()) |
505 | 0 | allrows.append(" ## "); |
506 | 0 | allrows.append(row); |
507 | 0 | m_rStream.ensureClosingTag( M_TOKEN( mr )); |
508 | 0 | } while( !m_rStream.atEnd() && m_rStream.findTag( OPENING( M_TOKEN( mr )))); |
509 | 0 | m_rStream.ensureClosingTag( M_TOKEN( m )); |
510 | 0 | return "matrix {" + allrows + "}"; |
511 | 0 | } |
512 | | |
513 | | OUString SmOoxmlImport::handleNary() |
514 | 0 | { |
515 | 0 | m_rStream.ensureOpeningTag( M_TOKEN( nary )); |
516 | 0 | sal_Unicode chr = 0x222b; |
517 | 0 | bool subHide = false; |
518 | 0 | bool supHide = false; |
519 | 0 | if( m_rStream.checkOpeningTag( M_TOKEN( naryPr ))) |
520 | 0 | { |
521 | 0 | if( XmlStream::Tag chrTag = m_rStream.checkOpeningTag( M_TOKEN( chr ))) |
522 | 0 | { |
523 | 0 | chr = chrTag.attribute( M_TOKEN( val ), chr ); |
524 | 0 | m_rStream.ensureClosingTag( M_TOKEN( chr )); |
525 | 0 | } |
526 | 0 | if( XmlStream::Tag subHideTag = m_rStream.checkOpeningTag( M_TOKEN( subHide ))) |
527 | 0 | { |
528 | 0 | subHide = subHideTag.attribute( M_TOKEN( val ), subHide ); |
529 | 0 | m_rStream.ensureClosingTag( M_TOKEN( subHide )); |
530 | 0 | } |
531 | 0 | if( XmlStream::Tag supHideTag = m_rStream.checkOpeningTag( M_TOKEN( supHide ))) |
532 | 0 | { |
533 | 0 | supHide = supHideTag.attribute( M_TOKEN( val ), supHide ); |
534 | 0 | m_rStream.ensureClosingTag( M_TOKEN( supHide )); |
535 | 0 | } |
536 | 0 | m_rStream.ensureClosingTag( M_TOKEN( naryPr )); |
537 | 0 | } |
538 | 0 | OUString sub = readOMathArgInElement( M_TOKEN( sub )); |
539 | 0 | OUString sup = readOMathArgInElement( M_TOKEN( sup )); |
540 | 0 | OUString e = readOMathArgInElement( M_TOKEN( e )); |
541 | 0 | OUString ret; |
542 | 0 | switch( chr ) |
543 | 0 | { |
544 | 0 | case MS_INT: |
545 | 0 | ret = "int"; |
546 | 0 | break; |
547 | 0 | case MS_IINT: |
548 | 0 | ret = "iint"; |
549 | 0 | break; |
550 | 0 | case MS_IIINT: |
551 | 0 | ret = "iiint"; |
552 | 0 | break; |
553 | 0 | case MS_LINT: |
554 | 0 | ret = "lint"; |
555 | 0 | break; |
556 | 0 | case MS_LLINT: |
557 | 0 | ret = "llint"; |
558 | 0 | break; |
559 | 0 | case MS_LLLINT: |
560 | 0 | ret = "lllint"; |
561 | 0 | break; |
562 | 0 | case MS_PROD: |
563 | 0 | ret = "prod"; |
564 | 0 | break; |
565 | 0 | case MS_COPROD: |
566 | 0 | ret = "coprod"; |
567 | 0 | break; |
568 | 0 | case MS_SUM: |
569 | 0 | ret = "sum"; |
570 | 0 | break; |
571 | 0 | default: |
572 | 0 | SAL_WARN( "starmath.ooxml", "Unknown m:nary chr \'" << OUString(chr) << "\'" ); |
573 | 0 | break; |
574 | 0 | } |
575 | 0 | if( !subHide ) |
576 | 0 | ret += " from {" + sub + "}"; |
577 | 0 | if( !supHide ) |
578 | 0 | ret += " to {" + sup + "}"; |
579 | 0 | ret += " {" + e + "}"; |
580 | 0 | m_rStream.ensureClosingTag( M_TOKEN( nary )); |
581 | 0 | return ret; |
582 | 0 | } |
583 | | |
584 | | // NOT complete |
585 | | OUString SmOoxmlImport::handleR() |
586 | 0 | { |
587 | 0 | m_rStream.ensureOpeningTag( M_TOKEN( r )); |
588 | 0 | bool normal = false; |
589 | 0 | bool literal = false; |
590 | 0 | OUString scrString; |
591 | 0 | if( XmlStream::Tag rPr = m_rStream.checkOpeningTag( M_TOKEN( rPr ))) |
592 | 0 | { |
593 | 0 | if( XmlStream::Tag litTag = m_rStream.checkOpeningTag( M_TOKEN( lit ))) |
594 | 0 | { |
595 | 0 | literal = litTag.attribute( M_TOKEN( val ), true ); |
596 | 0 | m_rStream.ensureClosingTag( M_TOKEN( lit )); |
597 | 0 | } |
598 | 0 | if( XmlStream::Tag norTag = m_rStream.checkOpeningTag( M_TOKEN( nor ))) |
599 | 0 | { |
600 | 0 | normal = norTag.attribute( M_TOKEN( val ), true ); |
601 | 0 | m_rStream.ensureClosingTag( M_TOKEN( nor )); |
602 | 0 | } |
603 | 0 | if (XmlStream::Tag srcTag = m_rStream.checkOpeningTag( M_TOKEN( scr ))) |
604 | 0 | { |
605 | 0 | scrString = srcTag.attribute( M_TOKEN( val ), scrString ); |
606 | 0 | m_rStream.ensureClosingTag( M_TOKEN( scr )); |
607 | 0 | } |
608 | 0 | m_rStream.ensureClosingTag( M_TOKEN( rPr )); |
609 | 0 | } |
610 | 0 | OUStringBuffer text; |
611 | 0 | bool isTagT = false; |
612 | 0 | while( !m_rStream.atEnd() && m_rStream.currentToken() != CLOSING( m_rStream.currentToken())) |
613 | 0 | { |
614 | 0 | switch( m_rStream.currentToken()) |
615 | 0 | { |
616 | 0 | case OPENING( M_TOKEN( t )): |
617 | 0 | { |
618 | 0 | isTagT = true; |
619 | 0 | XmlStream::Tag rtag = m_rStream.ensureOpeningTag( M_TOKEN( t )); |
620 | 0 | OUString sTagText = rtag.text; |
621 | 0 | if (scrString == "double-struck") |
622 | 0 | { |
623 | 0 | sTagText = SmOoxmlImport::handleSetString(sTagText); |
624 | 0 | } |
625 | 0 | if( rtag.attribute( OOX_TOKEN( xml, space )) != "preserve" ) |
626 | 0 | sTagText = o3tl::trim(sTagText); |
627 | 0 | text.append(sTagText); |
628 | 0 | m_rStream.ensureClosingTag( M_TOKEN( t )); |
629 | 0 | break; |
630 | 0 | } |
631 | 0 | default: |
632 | 0 | m_rStream.handleUnexpectedTag(); |
633 | 0 | break; |
634 | 0 | } |
635 | 0 | } |
636 | 0 | m_rStream.ensureClosingTag( M_TOKEN( r )); |
637 | 0 | if (scrString.isEmpty() && (normal || literal || isTagT)) |
638 | 0 | { |
639 | 0 | return encloseOrEscapeLiteral(text.makeStringAndClear(), normal || literal); |
640 | 0 | } |
641 | 0 | return text.makeStringAndClear(); |
642 | 0 | } |
643 | | |
644 | | OUString SmOoxmlImport::handleSetString(const OUString& setOUstring) |
645 | 0 | { |
646 | 0 | std::unordered_set<sal_Unicode> setList= {'C', 'N', 'Q', 'R', 'Z', 'c', 'n', 'q', 'r', 'z'}; |
647 | 0 | OUString result; |
648 | 0 | for (sal_Int32 i = 0; i < setOUstring.getLength(); i++) |
649 | 0 | { |
650 | 0 | if (setList.contains(setOUstring[i])) |
651 | 0 | { |
652 | 0 | result += OUString::Concat(" set" ) + OUStringChar(setOUstring[i]) + OUString::Concat(" "); |
653 | 0 | } |
654 | 0 | else |
655 | 0 | result += encloseOrEscapeLiteral(setOUstring.copy(i, 1), /*bForce=*/false); |
656 | 0 | } |
657 | 0 | return result; |
658 | 0 | } |
659 | | |
660 | | OUString SmOoxmlImport::handleRad() |
661 | 0 | { |
662 | 0 | m_rStream.ensureOpeningTag( M_TOKEN( rad )); |
663 | 0 | bool degHide = false; |
664 | 0 | if( m_rStream.checkOpeningTag( M_TOKEN( radPr ))) |
665 | 0 | { |
666 | 0 | if( XmlStream::Tag degHideTag = m_rStream.checkOpeningTag( M_TOKEN( degHide ))) |
667 | 0 | { |
668 | 0 | degHide = degHideTag.attribute( M_TOKEN( val ), degHide ); |
669 | 0 | m_rStream.ensureClosingTag( M_TOKEN( degHide )); |
670 | 0 | } |
671 | 0 | m_rStream.ensureClosingTag( M_TOKEN( radPr )); |
672 | 0 | } |
673 | 0 | OUString deg = readOMathArgInElement( M_TOKEN( deg )); |
674 | 0 | OUString e = readOMathArgInElement( M_TOKEN( e )); |
675 | 0 | m_rStream.ensureClosingTag( M_TOKEN( rad )); |
676 | 0 | if( degHide ) |
677 | 0 | return "sqrt {" + e + "}"; |
678 | 0 | else |
679 | 0 | return "nroot {" + deg + "} {" + e + "}"; |
680 | 0 | } |
681 | | |
682 | | OUString SmOoxmlImport::handleSpre() |
683 | 0 | { |
684 | 0 | m_rStream.ensureOpeningTag( M_TOKEN( sPre )); |
685 | 0 | OUString sub = readOMathArgInElement( M_TOKEN( sub )); |
686 | 0 | OUString sup = readOMathArgInElement( M_TOKEN( sup )); |
687 | 0 | OUString e = readOMathArgInElement( M_TOKEN( e )); |
688 | 0 | m_rStream.ensureClosingTag( M_TOKEN( sPre )); |
689 | 0 | return "{" + e + "} lsub {" + sub + "} lsup {" + sup + "}"; |
690 | 0 | } |
691 | | |
692 | | OUString SmOoxmlImport::handleSsub() |
693 | 0 | { |
694 | 0 | m_rStream.ensureOpeningTag( M_TOKEN( sSub )); |
695 | 0 | OUString e = readOMathArgInElement( M_TOKEN( e )); |
696 | 0 | OUString sub = readOMathArgInElement( M_TOKEN( sub )); |
697 | 0 | m_rStream.ensureClosingTag( M_TOKEN( sSub )); |
698 | 0 | return "{" + e + "} rsub {" + sub + "}"; |
699 | 0 | } |
700 | | |
701 | | OUString SmOoxmlImport::handleSsubsup() |
702 | 0 | { |
703 | 0 | m_rStream.ensureOpeningTag( M_TOKEN( sSubSup )); |
704 | 0 | OUString e = readOMathArgInElement( M_TOKEN( e )); |
705 | 0 | OUString sub = readOMathArgInElement( M_TOKEN( sub )); |
706 | 0 | OUString sup = readOMathArgInElement( M_TOKEN( sup )); |
707 | 0 | m_rStream.ensureClosingTag( M_TOKEN( sSubSup )); |
708 | 0 | return "{" + e + "} rsub {" + sub + "} rsup {" + sup + "}"; |
709 | 0 | } |
710 | | |
711 | | OUString SmOoxmlImport::handleSsup() |
712 | 0 | { |
713 | 0 | m_rStream.ensureOpeningTag( M_TOKEN( sSup )); |
714 | 0 | OUString e = readOMathArgInElement( M_TOKEN( e )); |
715 | 0 | OUString sup = readOMathArgInElement( M_TOKEN( sup )); |
716 | 0 | m_rStream.ensureClosingTag( M_TOKEN( sSup )); |
717 | 0 | if (sup == "'") |
718 | 0 | return "{" + e + "} {" + sup + "}"; |
719 | 0 | return "{" + e + "} ^ {" + sup + "}"; |
720 | 0 | } |
721 | | |
722 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |