update credits
[LibreOffice.git] / starmath / source / ooxmlimport.cxx
bloba84419578bc8d1224fa507634b30c9b07e4cb96a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
11 #include "ooxmlimport.hxx"
13 #include <oox/token/tokens.hxx>
14 #include <oox/token/namespaces.hxx>
15 #include <rtl/ustring.hxx>
17 using namespace oox;
18 using namespace oox::formulaimport;
21 The primary internal data structure for the formula is the text representation
22 (the SmNode tree is built from it), so read data must be converted into this format.
25 #define M_TOKEN( token ) OOX_TOKEN( officeMath, token )
26 #define OPENING( token ) XML_STREAM_OPENING( token )
27 #define CLOSING( token ) XML_STREAM_CLOSING( token )
29 // TODO create IS_OPENING(), IS_CLOSING() instead of doing 'next == OPENING( next )' ?
31 SmOoxmlImport::SmOoxmlImport( oox::formulaimport::XmlStream& s )
32 : stream( s )
36 OUString SmOoxmlImport::ConvertToStarMath()
38 return handleStream();
41 // "toplevel" of reading, there will be oMath (if there was oMathPara, that was
42 // up to the parent component to handle)
44 // NOT complete
45 OUString SmOoxmlImport::handleStream()
47 stream.ensureOpeningTag( M_TOKEN( oMath ));
48 OUString ret;
49 while( !stream.atEnd() && stream.currentToken() != CLOSING( M_TOKEN( oMath )))
51 // strictly speaking, it is not OMathArg here, but currently supported
52 // functionality is the same like OMathArg, in the future this may need improving
53 OUString item = readOMathArg( M_TOKEN( oMath ));
54 if( item.isEmpty())
55 continue;
56 if( !ret.isEmpty())
57 ret += " ";
58 ret += item;
60 stream.ensureClosingTag( M_TOKEN( oMath ));
61 // Placeholders are written out as nothing (i.e. nothing inside e.g. the <e> element),
62 // which will result in "{}" in the formula text. Fix this up.
63 ret = ret.replaceAll( "{}", "<?>" );
64 // And as a result, empty parts of the formula that are not placeholders are written out
65 // as a single space, so fix that up too.
66 ret = ret.replaceAll( "{ }", "{}" );
67 SAL_INFO( "starmath.ooxml", "Formula: " << ret );
68 return ret;
71 OUString SmOoxmlImport::readOMathArg( int stoptoken )
73 OUString ret;
74 while( !stream.atEnd() && stream.currentToken() != CLOSING( stoptoken ))
76 if( !ret.isEmpty())
77 ret += " ";
78 switch( stream.currentToken())
80 case OPENING( M_TOKEN( acc )):
81 ret += handleAcc();
82 break;
83 case OPENING( M_TOKEN( bar )):
84 ret += handleBar();
85 break;
86 case OPENING( M_TOKEN( box )):
87 ret += handleBox();
88 break;
89 case OPENING( M_TOKEN( borderBox )):
90 ret += handleBorderBox();
91 break;
92 case OPENING( M_TOKEN( d )):
93 ret += handleD();
94 break;
95 case OPENING( M_TOKEN( eqArr )):
96 ret += handleEqArr();
97 break;
98 case OPENING( M_TOKEN( f )):
99 ret += handleF();
100 break;
101 case OPENING( M_TOKEN( func )):
102 ret += handleFunc();
103 break;
104 case OPENING( M_TOKEN( limLow )):
105 ret += handleLimLowUpp( LimLow );
106 break;
107 case OPENING( M_TOKEN( limUpp )):
108 ret += handleLimLowUpp( LimUpp );
109 break;
110 case OPENING( M_TOKEN( groupChr )):
111 ret += handleGroupChr();
112 break;
113 case OPENING( M_TOKEN( m )):
114 ret += handleM();
115 break;
116 case OPENING( M_TOKEN( nary )):
117 ret += handleNary();
118 break;
119 case OPENING( M_TOKEN( r )):
120 ret += handleR();
121 break;
122 case OPENING( M_TOKEN( rad )):
123 ret += handleRad();
124 break;
125 case OPENING( M_TOKEN( sPre )):
126 ret += handleSpre();
127 break;
128 case OPENING( M_TOKEN( sSub )):
129 ret += handleSsub();
130 break;
131 case OPENING( M_TOKEN( sSubSup )):
132 ret += handleSsubsup();
133 break;
134 case OPENING( M_TOKEN( sSup )):
135 ret += handleSsup();
136 break;
137 default:
138 stream.handleUnexpectedTag();
139 break;
142 return ret;
145 OUString SmOoxmlImport::readOMathArgInElement( int token )
147 stream.ensureOpeningTag( token );
148 OUString ret = readOMathArg( token );
149 stream.ensureClosingTag( token );
150 return ret;
153 OUString SmOoxmlImport::handleAcc()
155 stream.ensureOpeningTag( M_TOKEN( acc ));
156 sal_Unicode accChr = 0x302;
157 if( XmlStream::Tag accPr = stream.checkOpeningTag( M_TOKEN( accPr )))
159 if( XmlStream::Tag chr = stream.checkOpeningTag( M_TOKEN( chr )))
161 accChr = chr.attribute( M_TOKEN( val ), accChr );
162 stream.ensureClosingTag( M_TOKEN( chr ));
164 stream.ensureClosingTag( M_TOKEN( accPr ));
166 // see aTokenTable in parse.cxx
167 OUString acc;
168 switch( accChr )
170 case MS_BAR:
171 acc = "bar";
172 break;
173 case MS_CHECK:
174 acc = "check";
175 break;
176 case MS_ACUTE:
177 acc = "acute";
178 break;
179 case MS_GRAVE:
180 acc = "grave";
181 break;
182 case MS_BREVE:
183 acc = "breve";
184 break;
185 case MS_CIRCLE:
186 acc = "circle";
187 break;
188 case MS_VEC:
189 // prefer wide variants for these 3, .docx can't seem to differentiate
190 // between e.g. 'vec' and 'widevec', if whatever the accent is above is short, this
191 // shouldn't matter, but short above a longer expression doesn't look right
192 acc = "widevec";
193 break;
194 case MS_TILDE:
195 acc = "widetilde";
196 break;
197 case MS_HAT:
198 acc = "widehat";
199 break;
200 case MS_DOT:
201 acc = "dot";
202 break;
203 case MS_DDOT:
204 acc = "ddot";
205 break;
206 case MS_DDDOT:
207 acc = "dddot";
208 break;
209 default:
210 acc = "acute";
211 SAL_WARN( "starmath.ooxml", "Unknown m:chr in m:acc \'" << accChr << "\'" );
212 break;
214 OUString e = readOMathArgInElement( M_TOKEN( e ));
215 stream.ensureClosingTag( M_TOKEN( acc ));
216 return acc + " {" + e + "}";
219 OUString SmOoxmlImport::handleBar()
221 stream.ensureOpeningTag( M_TOKEN( bar ));
222 enum pos_t { top, bot } topbot = bot;
223 if( stream.checkOpeningTag( M_TOKEN( barPr )))
225 if( XmlStream::Tag pos = stream.checkOpeningTag( M_TOKEN( pos )))
227 if( pos.attribute( M_TOKEN( val )) == "top" )
228 topbot = top;
229 else if( pos.attribute( M_TOKEN( val )) == "bot" )
230 topbot = bot;
231 stream.ensureClosingTag( M_TOKEN( pos ));
233 stream.ensureClosingTag( M_TOKEN( barPr ));
235 OUString e = readOMathArgInElement( M_TOKEN( e ));
236 stream.ensureClosingTag( M_TOKEN( bar ));
237 if( topbot == top )
238 return "overline {" + e + "}";
239 else
240 return "underline {" + e + "}";
243 OUString SmOoxmlImport::handleBox()
245 // there does not seem to be functionality in LO to actually implement this
246 // (or is there), but at least read in the contents instead of ignoring them
247 stream.ensureOpeningTag( M_TOKEN( box ));
248 OUString e = readOMathArgInElement( M_TOKEN( e ));
249 stream.ensureClosingTag( M_TOKEN( box ));
250 return e;
254 OUString SmOoxmlImport::handleBorderBox()
256 stream.ensureOpeningTag( M_TOKEN( borderBox ));
257 bool isStrikeH = false;
258 if( stream.checkOpeningTag( M_TOKEN( borderBoxPr )))
260 if( XmlStream::Tag strikeH = stream.checkOpeningTag( M_TOKEN( strikeH )))
262 if( strikeH.attribute( M_TOKEN( val ), false ))
263 isStrikeH = true;
264 stream.ensureClosingTag( M_TOKEN( strikeH ));
266 stream.ensureClosingTag( M_TOKEN( borderBoxPr ));
268 OUString e = readOMathArgInElement( M_TOKEN( e ));
269 stream.ensureClosingTag( M_TOKEN( borderBox ));
270 if( isStrikeH )
271 return "overstrike {" + e + "}";
272 // LO does not seem to implement anything for handling the other cases
273 return e;
276 OUString SmOoxmlImport::handleD()
278 stream.ensureOpeningTag( M_TOKEN( d ));
279 OUString opening = "(";
280 OUString closing = ")";
281 OUString separator = "|";
282 if( XmlStream::Tag dPr = stream.checkOpeningTag( M_TOKEN( dPr )))
284 if( XmlStream::Tag begChr = stream.checkOpeningTag( M_TOKEN( begChr )))
286 opening = begChr.attribute( M_TOKEN( val ), opening );
287 stream.ensureClosingTag( M_TOKEN( begChr ));
289 if( XmlStream::Tag sepChr = stream.checkOpeningTag( M_TOKEN( sepChr )))
291 separator = sepChr.attribute( M_TOKEN( val ), separator );
292 stream.ensureClosingTag( M_TOKEN( sepChr ));
294 if( XmlStream::Tag endChr = stream.checkOpeningTag( M_TOKEN( endChr )))
296 closing = endChr.attribute( M_TOKEN( val ), closing );
297 stream.ensureClosingTag( M_TOKEN( endChr ));
299 stream.ensureClosingTag( M_TOKEN( dPr ));
301 if( opening == "{" )
302 opening = "left lbrace ";
303 if( closing == "}" )
304 closing = " right rbrace";
305 if( opening == OUString( sal_Unicode( 0x27e6 )))
306 opening = "left ldbracket ";
307 if( closing == OUString( sal_Unicode( 0x27e7 )))
308 closing = " right rdbracket";
309 if( opening == "|" )
310 opening = "left lline ";
311 if( closing == "|" )
312 closing = " right rline";
313 if( opening == OUString( sal_Unicode( 0x2225 )))
314 opening = "left ldline ";
315 if( closing == OUString( sal_Unicode( 0x2225 )))
316 closing = " right rdline";
317 if( opening == OUString( sal_Unicode( 0x2329 )))
318 opening = "left langle ";
319 if( closing == OUString( sal_Unicode( 0x232a )))
320 closing = " right rangle";
321 // use scalable brackets (the explicit "left" or "right")
322 if( opening == "(" || opening == "[" )
323 opening = "left " + opening;
324 if( closing == ")" || closing == "]" )
325 closing = " right " + closing;
326 if( separator == "|" ) // plain "|" would be actually "V" (logical or)
327 separator = " mline ";
328 if( opening.isEmpty())
329 opening = "left none ";
330 if( closing.isEmpty())
331 closing = " right none";
332 OUStringBuffer ret;
333 ret.append( opening );
334 bool first = true;
335 while( stream.findTag( OPENING( M_TOKEN( e ))))
337 if( !first )
338 ret.append( separator );
339 first = false;
340 ret.append( readOMathArgInElement( M_TOKEN( e )));
342 ret.append( closing );
343 stream.ensureClosingTag( M_TOKEN( d ));
344 return ret.makeStringAndClear();
347 OUString SmOoxmlImport::handleEqArr()
349 stream.ensureOpeningTag( M_TOKEN( eqArr ));
350 OUString ret;
352 { // there must be at least one m:e
353 if( !ret.isEmpty())
354 ret += "#";
355 ret += " ";
356 ret += readOMathArgInElement( M_TOKEN( e ));
357 ret += " ";
358 } while( !stream.atEnd() && stream.findTag( OPENING( M_TOKEN( e ))));
359 stream.ensureClosingTag( M_TOKEN( eqArr ));
360 return "stack {" + ret + "}";
363 OUString SmOoxmlImport::handleF()
365 stream.ensureOpeningTag( M_TOKEN( f ));
366 enum operation_t { bar, lin, noBar } operation = bar;
367 if( stream.checkOpeningTag( M_TOKEN( fPr )))
369 if( XmlStream::Tag type = stream.checkOpeningTag( M_TOKEN( type )))
371 if( type.attribute( M_TOKEN( val )) == "bar" )
372 operation = bar;
373 else if( type.attribute( M_TOKEN( val )) == "lin" )
374 operation = lin;
375 else if( type.attribute( M_TOKEN( val )) == "noBar" )
376 operation = noBar;
377 stream.ensureClosingTag( M_TOKEN( type ));
379 stream.ensureClosingTag( M_TOKEN( fPr ));
381 OUString num = readOMathArgInElement( M_TOKEN( num ));
382 OUString den = readOMathArgInElement( M_TOKEN( den ));
383 stream.ensureClosingTag( M_TOKEN( f ));
384 if( operation == bar )
385 return "{" + num + "} over {" + den + "}";
386 else if( operation == lin )
387 return "{" + num + "} / {" + den + "}";
388 else // noBar
390 return "binom {" + num + "} {" + den + "}";
394 OUString SmOoxmlImport::handleFunc()
396 //lim from{x rightarrow 1} x
397 stream.ensureOpeningTag( M_TOKEN( func ));
398 OUString fname = readOMathArgInElement( M_TOKEN( fName ));
399 // fix the various functions
400 if( fname.startsWith( "lim csub {" ))
401 fname = "lim from {" + fname.copy( 10 );
402 OUString ret = fname + " {" + readOMathArgInElement( M_TOKEN( e )) + "}";
403 stream.ensureClosingTag( M_TOKEN( func ));
404 return ret;
407 OUString SmOoxmlImport::handleLimLowUpp( LimLowUpp_t limlowupp )
409 int token = limlowupp == LimLow ? M_TOKEN( limLow ) : M_TOKEN( limUpp );
410 stream.ensureOpeningTag( token );
411 OUString e = readOMathArgInElement( M_TOKEN( e ));
412 OUString lim = readOMathArgInElement( M_TOKEN( lim ));
413 stream.ensureClosingTag( token );
414 // fix up overbrace/underbrace (use { }, as {} will be converted to a placeholder)
415 if( limlowupp == LimUpp && e.endsWith( " overbrace { }" ))
416 return e.copy( 0, e.getLength() - 2 ) + lim + "}";
417 if( limlowupp == LimLow && e.endsWith( " underbrace { }" ))
418 return e.copy( 0, e.getLength() - 2 ) + lim + "}";
419 return e
420 + ( limlowupp == LimLow ? OUString( " csub {" ) : OUString( " csup {" ))
421 + lim + "}";
424 OUString SmOoxmlImport::handleGroupChr()
426 stream.ensureOpeningTag( M_TOKEN( groupChr ));
427 sal_Unicode chr = 0x23df;
428 enum pos_t { top, bot } pos = bot;
429 if( stream.checkOpeningTag( M_TOKEN( groupChrPr )))
431 if( XmlStream::Tag chrTag = stream.checkOpeningTag( M_TOKEN( chr )))
433 chr = chrTag.attribute( M_TOKEN( val ), chr );
434 stream.ensureClosingTag( M_TOKEN( chr ));
436 if( XmlStream::Tag posTag = stream.checkOpeningTag( M_TOKEN( pos )))
438 if( posTag.attribute( M_TOKEN( val ), OUString( "bot" )) == "top" )
439 pos = top;
440 stream.ensureClosingTag( M_TOKEN( pos ));
442 stream.ensureClosingTag( M_TOKEN( groupChrPr ));
444 OUString e = readOMathArgInElement( M_TOKEN( e ));
445 stream.ensureClosingTag( M_TOKEN( groupChr ));
446 if( pos == top && chr == sal_Unicode( 0x23de ))
447 return "{" + e + "} overbrace { }";
448 if( pos == bot && chr == sal_Unicode( 0x23df ))
449 return "{" + e + "} underbrace { }";
450 if( pos == top )
451 return "{" + e + "} csup {" + OUString( chr ) + "}";
452 else
453 return "{" + e + "} csub {" + OUString( chr ) + "}";
456 OUString SmOoxmlImport::handleM()
458 stream.ensureOpeningTag( M_TOKEN( m ));
459 OUString allrows;
460 do // there must be at least one m:mr
462 stream.ensureOpeningTag( M_TOKEN( mr ));
463 OUString row;
464 do // there must be at least one m:e
466 if( !row.isEmpty())
467 row += " # ";
468 row += readOMathArgInElement( M_TOKEN( e ));
469 } while( !stream.atEnd() && stream.findTag( OPENING( M_TOKEN( e ))));
470 if( !allrows.isEmpty())
471 allrows += " ## ";
472 allrows += row;
473 stream.ensureClosingTag( M_TOKEN( mr ));
474 } while( !stream.atEnd() && stream.findTag( OPENING( M_TOKEN( mr ))));
475 stream.ensureClosingTag( M_TOKEN( m ));
476 return "matrix {" + allrows + "}";
479 OUString SmOoxmlImport::handleNary()
481 stream.ensureOpeningTag( M_TOKEN( nary ));
482 sal_Unicode chr = 0x222b;
483 bool subHide = false;
484 bool supHide = false;
485 if( stream.checkOpeningTag( M_TOKEN( naryPr )))
487 if( XmlStream::Tag chrTag = stream.checkOpeningTag( M_TOKEN( chr )))
489 chr = chrTag.attribute( M_TOKEN( val ), chr );
490 stream.ensureClosingTag( M_TOKEN( chr ));
492 if( XmlStream::Tag subHideTag = stream.checkOpeningTag( M_TOKEN( subHide )))
494 subHide = subHideTag.attribute( M_TOKEN( val ), subHide );
495 stream.ensureClosingTag( M_TOKEN( subHide ));
497 if( XmlStream::Tag supHideTag = stream.checkOpeningTag( M_TOKEN( supHide )))
499 supHide = supHideTag.attribute( M_TOKEN( val ), supHide );
500 stream.ensureClosingTag( M_TOKEN( supHide ));
502 stream.ensureClosingTag( M_TOKEN( naryPr ));
504 OUString sub = readOMathArgInElement( M_TOKEN( sub ));
505 OUString sup = readOMathArgInElement( M_TOKEN( sup ));
506 OUString e = readOMathArgInElement( M_TOKEN( e ));
507 OUString ret;
508 switch( chr )
510 case MS_INT:
511 ret = "int";
512 break;
513 case MS_IINT:
514 ret = "iint";
515 break;
516 case MS_IIINT:
517 ret = "iiint";
518 break;
519 case MS_LINT:
520 ret = "lint";
521 break;
522 case MS_LLINT:
523 ret = "llint";
524 break;
525 case MS_LLLINT:
526 ret = "lllint";
527 break;
528 case MS_PROD:
529 ret = "prod";
530 break;
531 case MS_COPROD:
532 ret = "coprod";
533 break;
534 case MS_SUM:
535 ret = "sum";
536 break;
537 default:
538 SAL_WARN( "starmath.ooxml", "Unknown m:nary chr \'" << chr << "\'" );
539 break;
541 if( !subHide )
542 ret += " from {" + sub + "}";
543 if( !supHide )
544 ret += " to {" + sup + "}";
545 ret += " {" + e + "}";
546 stream.ensureClosingTag( M_TOKEN( nary ));
547 return ret;
550 // NOT complete
551 OUString SmOoxmlImport::handleR()
553 stream.ensureOpeningTag( M_TOKEN( r ));
554 bool normal = false;
555 bool literal = false;
556 if( XmlStream::Tag rPr = stream.checkOpeningTag( M_TOKEN( rPr )))
558 if( XmlStream::Tag litTag = stream.checkOpeningTag( M_TOKEN( lit )))
560 literal = litTag.attribute( M_TOKEN( val ), true );
561 stream.ensureClosingTag( M_TOKEN( lit ));
563 if( XmlStream::Tag norTag = stream.checkOpeningTag( M_TOKEN( nor )))
565 normal = norTag.attribute( M_TOKEN( val ), true );
566 stream.ensureClosingTag( M_TOKEN( nor ));
568 stream.ensureClosingTag( M_TOKEN( rPr ));
570 OUString text;
571 while( !stream.atEnd() && stream.currentToken() != CLOSING( stream.currentToken()))
573 switch( stream.currentToken())
575 case OPENING( M_TOKEN( t )):
577 XmlStream::Tag rtag = stream.ensureOpeningTag( M_TOKEN( t ));
578 if( rtag.attribute( OOX_TOKEN( xml, space )) != "preserve" )
579 text += rtag.text.trim();
580 else
581 text += rtag.text;
582 stream.ensureClosingTag( M_TOKEN( t ));
583 break;
585 default:
586 stream.handleUnexpectedTag();
587 break;
590 stream.ensureClosingTag( M_TOKEN( r ));
591 if( normal || literal )
592 text = "\"" + text + "\"";
593 return text.replaceAll("{", "\\{").replaceAll("}", "\\}");
596 OUString SmOoxmlImport::handleRad()
598 stream.ensureOpeningTag( M_TOKEN( rad ));
599 bool degHide = false;
600 if( stream.checkOpeningTag( M_TOKEN( radPr )))
602 if( XmlStream::Tag degHideTag = stream.checkOpeningTag( M_TOKEN( degHide )))
604 degHide = degHideTag.attribute( M_TOKEN( val ), degHide );
605 stream.ensureClosingTag( M_TOKEN( degHide ));
607 stream.ensureClosingTag( M_TOKEN( radPr ));
609 OUString deg = readOMathArgInElement( M_TOKEN( deg ));
610 OUString e = readOMathArgInElement( M_TOKEN( e ));
611 stream.ensureClosingTag( M_TOKEN( rad ));
612 if( degHide )
613 return "sqrt {" + e + "}";
614 else
615 return "nroot {" + deg + "} {" + e + "}";
618 OUString SmOoxmlImport::handleSpre()
620 stream.ensureOpeningTag( M_TOKEN( sPre ));
621 OUString sub = readOMathArgInElement( M_TOKEN( sub ));
622 OUString sup = readOMathArgInElement( M_TOKEN( sup ));
623 OUString e = readOMathArgInElement( M_TOKEN( e ));
624 stream.ensureClosingTag( M_TOKEN( sPre ));
625 return "{" + e + "} lsub {" + sub + "} lsup {" + sup + "}";
628 OUString SmOoxmlImport::handleSsub()
630 stream.ensureOpeningTag( M_TOKEN( sSub ));
631 OUString e = readOMathArgInElement( M_TOKEN( e ));
632 OUString sub = readOMathArgInElement( M_TOKEN( sub ));
633 stream.ensureClosingTag( M_TOKEN( sSub ));
634 return "{" + e + "} rsub {" + sub + "}";
637 OUString SmOoxmlImport::handleSsubsup()
639 stream.ensureOpeningTag( M_TOKEN( sSubSup ));
640 OUString e = readOMathArgInElement( M_TOKEN( e ));
641 OUString sub = readOMathArgInElement( M_TOKEN( sub ));
642 OUString sup = readOMathArgInElement( M_TOKEN( sup ));
643 stream.ensureClosingTag( M_TOKEN( sSubSup ));
644 return "{" + e + "} rsub {" + sub + "} rsup {" + sup + "}";
647 OUString SmOoxmlImport::handleSsup()
649 stream.ensureOpeningTag( M_TOKEN( sSup ));
650 OUString e = readOMathArgInElement( M_TOKEN( e ));
651 OUString sup = readOMathArgInElement( M_TOKEN( sup ));
652 stream.ensureClosingTag( M_TOKEN( sSup ));
653 return "{" + e + "} ^ {" + sup + "}";
656 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */