1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
11 #include "ooxmlimport.hxx"
14 #include <oox/mathml/importutils.hxx>
15 #include <oox/token/namespaces.hxx>
16 #include <rtl/ustring.hxx>
17 #include <rtl/ustrbuf.hxx>
18 #include <sal/log.hxx>
20 using namespace oox::formulaimport
;
23 The primary internal data structure for the formula is the text representation
24 (the SmNode tree is built from it), so read data must be converted into this format.
27 #define OPENING( token ) XML_STREAM_OPENING( token )
28 #define CLOSING( token ) XML_STREAM_CLOSING( token )
30 // TODO create IS_OPENING(), IS_CLOSING() instead of doing 'next == OPENING( next )' ?
32 SmOoxmlImport::SmOoxmlImport( oox::formulaimport::XmlStream
& s
)
37 OUString
SmOoxmlImport::ConvertToStarMath()
39 return handleStream();
42 // "toplevel" of reading, there will be oMath (if there was oMathPara, that was
43 // up to the parent component to handle)
46 OUString
SmOoxmlImport::handleStream()
48 m_rStream
.ensureOpeningTag( M_TOKEN( oMath
));
50 while( !m_rStream
.atEnd() && m_rStream
.currentToken() != CLOSING( M_TOKEN( oMath
)))
52 // strictly speaking, it is not OMathArg here, but currently supported
53 // functionality is the same like OMathArg, in the future this may need improving
54 OUString item
= readOMathArg( M_TOKEN( oMath
));
61 m_rStream
.ensureClosingTag( M_TOKEN( oMath
));
62 // Placeholders are written out as nothing (i.e. nothing inside e.g. the <e> element),
63 // which will result in "{}" in the formula text. Fix this up.
64 OUString ret2
= ret
.makeStringAndClear().replaceAll( "{}", "<?>" );
65 // And as a result, empty parts of the formula that are not placeholders are written out
66 // as a single space, so fix that up too.
67 ret2
= ret2
.replaceAll( "{ }", "{}" );
68 SAL_INFO( "starmath.ooxml", "Formula: " << ret2
);
72 OUString
SmOoxmlImport::readOMathArg( int stoptoken
)
75 while( !m_rStream
.atEnd() && m_rStream
.currentToken() != CLOSING( stoptoken
))
79 switch( m_rStream
.currentToken())
81 case OPENING( M_TOKEN( acc
)):
82 ret
.append(handleAcc());
84 case OPENING( M_TOKEN( bar
)):
85 ret
.append(handleBar());
87 case OPENING( M_TOKEN( box
)):
88 ret
.append(handleBox());
90 case OPENING( M_TOKEN( borderBox
)):
91 ret
.append(handleBorderBox());
93 case OPENING( M_TOKEN( d
)):
94 ret
.append(handleD());
96 case OPENING( M_TOKEN( eqArr
)):
97 ret
.append(handleEqArr());
99 case OPENING( M_TOKEN( f
)):
100 ret
.append(handleF());
102 case OPENING( M_TOKEN( func
)):
103 ret
.append(handleFunc());
105 case OPENING( M_TOKEN( limLow
)):
106 ret
.append(handleLimLowUpp( LimLow
));
108 case OPENING( M_TOKEN( limUpp
)):
109 ret
.append(handleLimLowUpp( LimUpp
));
111 case OPENING( M_TOKEN( groupChr
)):
112 ret
.append(handleGroupChr());
114 case OPENING( M_TOKEN( m
)):
115 ret
.append(handleM());
117 case OPENING( M_TOKEN( nary
)):
118 ret
.append(handleNary());
120 case OPENING( M_TOKEN( r
)):
121 ret
.append(handleR());
123 case OPENING( M_TOKEN( rad
)):
124 ret
.append(handleRad());
126 case OPENING( M_TOKEN( sPre
)):
127 ret
.append(handleSpre());
129 case OPENING( M_TOKEN( sSub
)):
130 ret
.append(handleSsub());
132 case OPENING( M_TOKEN( sSubSup
)):
133 ret
.append(handleSsubsup());
135 case OPENING( M_TOKEN( sSup
)):
136 ret
.append(handleSsup());
139 m_rStream
.handleUnexpectedTag();
143 return ret
.makeStringAndClear();
146 OUString
SmOoxmlImport::readOMathArgInElement( int token
)
148 m_rStream
.ensureOpeningTag( token
);
149 OUString ret
= readOMathArg( token
);
150 m_rStream
.ensureClosingTag( token
);
154 OUString
SmOoxmlImport::handleAcc()
156 m_rStream
.ensureOpeningTag( M_TOKEN( acc
));
157 sal_Unicode accChr
= 0x302;
158 if( XmlStream::Tag accPr
= m_rStream
.checkOpeningTag( M_TOKEN( accPr
)))
160 if( XmlStream::Tag chr
= m_rStream
.checkOpeningTag( M_TOKEN( chr
)))
162 accChr
= chr
.attribute( M_TOKEN( val
), accChr
);
163 m_rStream
.ensureClosingTag( M_TOKEN( chr
));
165 m_rStream
.ensureClosingTag( M_TOKEN( accPr
));
167 // see aTokenTable in parse.cxx
183 case MS_COMBOVERLINE
:
200 // prefer wide variants for these 3, .docx can't seem to differentiate
201 // between e.g. 'vec' and 'widevec', if whatever the accent is above is short, this
202 // shouldn't matter, but short above a longer expression doesn't look right
229 SAL_WARN( "starmath.ooxml", "Unknown m:chr in m:acc \'" << OUString(accChr
) << "\'" );
232 OUString e
= readOMathArgInElement( M_TOKEN( e
));
233 m_rStream
.ensureClosingTag( M_TOKEN( acc
));
234 return acc
+ " {" + e
+ "}";
237 OUString
SmOoxmlImport::handleBar()
239 m_rStream
.ensureOpeningTag( M_TOKEN( bar
));
240 enum pos_t
{ top
, bot
} topbot
= bot
;
241 if( m_rStream
.checkOpeningTag( M_TOKEN( barPr
)))
243 if( XmlStream::Tag pos
= m_rStream
.checkOpeningTag( M_TOKEN( pos
)))
245 if( pos
.attribute( M_TOKEN( val
)) == "top" )
247 else if( pos
.attribute( M_TOKEN( val
)) == "bot" )
249 m_rStream
.ensureClosingTag( M_TOKEN( pos
));
251 m_rStream
.ensureClosingTag( M_TOKEN( barPr
));
253 OUString e
= readOMathArgInElement( M_TOKEN( e
));
254 m_rStream
.ensureClosingTag( M_TOKEN( bar
));
256 return "overline {" + e
+ "}";
258 return "underline {" + e
+ "}";
261 OUString
SmOoxmlImport::handleBox()
263 // there does not seem to be functionality in LO to actually implement this
264 // (or is there), but at least read in the contents instead of ignoring them
265 m_rStream
.ensureOpeningTag( M_TOKEN( box
));
266 OUString e
= readOMathArgInElement( M_TOKEN( e
));
267 m_rStream
.ensureClosingTag( M_TOKEN( box
));
272 OUString
SmOoxmlImport::handleBorderBox()
274 m_rStream
.ensureOpeningTag( M_TOKEN( borderBox
));
275 bool isStrikeH
= false;
276 if( m_rStream
.checkOpeningTag( M_TOKEN( borderBoxPr
)))
278 if( XmlStream::Tag strikeH
= m_rStream
.checkOpeningTag( M_TOKEN( strikeH
)))
280 if( strikeH
.attribute( M_TOKEN( val
), false ))
282 m_rStream
.ensureClosingTag( M_TOKEN( strikeH
));
284 m_rStream
.ensureClosingTag( M_TOKEN( borderBoxPr
));
286 OUString e
= readOMathArgInElement( M_TOKEN( e
));
287 m_rStream
.ensureClosingTag( M_TOKEN( borderBox
));
289 return "overstrike {" + e
+ "}";
290 // LO does not seem to implement anything for handling the other cases
294 OUString
SmOoxmlImport::handleD()
296 m_rStream
.ensureOpeningTag( M_TOKEN( d
));
297 OUString opening
= "(";
298 OUString closing
= ")";
299 OUString separator
= "|";
300 if( XmlStream::Tag dPr
= m_rStream
.checkOpeningTag( M_TOKEN( dPr
)))
302 if( XmlStream::Tag begChr
= m_rStream
.checkOpeningTag( M_TOKEN( begChr
)))
304 opening
= begChr
.attribute( M_TOKEN( val
), opening
);
305 m_rStream
.ensureClosingTag( M_TOKEN( begChr
));
307 if( XmlStream::Tag sepChr
= m_rStream
.checkOpeningTag( M_TOKEN( sepChr
)))
309 separator
= sepChr
.attribute( M_TOKEN( val
), separator
);
310 m_rStream
.ensureClosingTag( M_TOKEN( sepChr
));
312 if( XmlStream::Tag endChr
= m_rStream
.checkOpeningTag( M_TOKEN( endChr
)))
314 closing
= endChr
.attribute( M_TOKEN( val
), closing
);
315 m_rStream
.ensureClosingTag( M_TOKEN( endChr
));
317 m_rStream
.ensureClosingTag( M_TOKEN( dPr
));
320 opening
= "left lbrace ";
322 closing
= " right rbrace";
323 if( opening
== u
"\u27e6" )
324 opening
= "left ldbracket ";
325 if( closing
== u
"\u27e7" )
326 closing
= " right rdbracket";
328 opening
= "left lline ";
330 closing
= " right rline";
331 if (opening
== OUStringChar(MS_DLINE
)
332 || opening
== OUStringChar(MS_DVERTLINE
))
333 opening
= "left ldline ";
334 if (closing
== OUStringChar(MS_DLINE
)
335 || closing
== OUStringChar(MS_DVERTLINE
))
336 closing
= " right rdline";
337 if (opening
== OUStringChar(MS_LANGLE
)
338 || opening
== OUStringChar(MS_LMATHANGLE
))
339 opening
= "left langle ";
340 if (closing
== OUStringChar(MS_RANGLE
)
341 || closing
== OUStringChar(MS_RMATHANGLE
))
342 closing
= " right rangle";
343 // use scalable brackets (the explicit "left" or "right")
344 if( opening
== "(" || opening
== "[" )
345 opening
= "left " + opening
;
346 if( closing
== ")" || closing
== "]" )
347 closing
= " right " + closing
;
348 if( separator
== "|" ) // plain "|" would be actually "V" (logical or)
349 separator
= " mline ";
350 if( opening
.isEmpty())
351 opening
= "left none ";
352 if( closing
.isEmpty())
353 closing
= " right none";
355 ret
.append( opening
);
357 while( m_rStream
.findTag( OPENING( M_TOKEN( e
))))
360 ret
.append( separator
);
362 ret
.append( readOMathArgInElement( M_TOKEN( e
)));
364 ret
.append( closing
);
365 m_rStream
.ensureClosingTag( M_TOKEN( d
));
366 return ret
.makeStringAndClear();
369 OUString
SmOoxmlImport::handleEqArr()
371 m_rStream
.ensureOpeningTag( M_TOKEN( eqArr
));
374 { // there must be at least one m:e
378 ret
.append(readOMathArgInElement( M_TOKEN( e
)));
380 } while( !m_rStream
.atEnd() && m_rStream
.findTag( OPENING( M_TOKEN( e
))));
381 m_rStream
.ensureClosingTag( M_TOKEN( eqArr
));
382 return "stack {" + ret
.makeStringAndClear() + "}";
385 OUString
SmOoxmlImport::handleF()
387 m_rStream
.ensureOpeningTag( M_TOKEN( f
));
388 enum operation_t
{ bar
, lin
, noBar
} operation
= bar
;
389 if( m_rStream
.checkOpeningTag( M_TOKEN( fPr
)))
391 if( XmlStream::Tag type
= m_rStream
.checkOpeningTag( M_TOKEN( type
)))
393 if( type
.attribute( M_TOKEN( val
)) == "bar" )
395 else if( type
.attribute( M_TOKEN( val
)) == "lin" )
397 else if( type
.attribute( M_TOKEN( val
)) == "noBar" )
399 m_rStream
.ensureClosingTag( M_TOKEN( type
));
401 m_rStream
.ensureClosingTag( M_TOKEN( fPr
));
403 OUString num
= readOMathArgInElement( M_TOKEN( num
));
404 OUString den
= readOMathArgInElement( M_TOKEN( den
));
405 m_rStream
.ensureClosingTag( M_TOKEN( f
));
406 if( operation
== bar
)
407 return "{" + num
+ "} over {" + den
+ "}";
408 else if( operation
== lin
)
409 return "{" + num
+ "} / {" + den
+ "}";
412 return "binom {" + num
+ "} {" + den
+ "}";
416 OUString
SmOoxmlImport::handleFunc()
418 //lim from{x rightarrow 1} x
419 m_rStream
.ensureOpeningTag( M_TOKEN( func
));
420 OUString fname
= readOMathArgInElement( M_TOKEN( fName
));
421 // fix the various functions
422 if( fname
.startsWith( "lim csub {" ))
423 fname
= OUString::Concat("lim from {") + fname
.subView( 10 );
424 OUString ret
= fname
+ " {" + readOMathArgInElement( M_TOKEN( e
)) + "}";
425 m_rStream
.ensureClosingTag( M_TOKEN( func
));
429 OUString
SmOoxmlImport::handleLimLowUpp( LimLowUpp_t limlowupp
)
431 int token
= limlowupp
== LimLow
? M_TOKEN( limLow
) : M_TOKEN( limUpp
);
432 m_rStream
.ensureOpeningTag( token
);
433 OUString e
= readOMathArgInElement( M_TOKEN( e
));
434 OUString lim
= readOMathArgInElement( M_TOKEN( lim
));
435 m_rStream
.ensureClosingTag( token
);
436 // fix up overbrace/underbrace (use { }, as {} will be converted to a placeholder)
437 if( limlowupp
== LimUpp
&& e
.endsWith( " overbrace { }" ))
438 return e
.subView( 0, e
.getLength() - 2 ) + lim
+ "}";
439 if( limlowupp
== LimLow
&& e
.endsWith( " underbrace { }" ))
440 return e
.subView( 0, e
.getLength() - 2 ) + lim
+ "}";
442 + ( limlowupp
== LimLow
? OUStringLiteral( u
" csub {" ) : OUStringLiteral( u
" csup {" ))
446 OUString
SmOoxmlImport::handleGroupChr()
448 m_rStream
.ensureOpeningTag( M_TOKEN( groupChr
));
449 sal_Unicode chr
= 0x23df;
450 enum pos_t
{ top
, bot
} pos
= bot
;
451 if( m_rStream
.checkOpeningTag( M_TOKEN( groupChrPr
)))
453 if( XmlStream::Tag chrTag
= m_rStream
.checkOpeningTag( M_TOKEN( chr
)))
455 chr
= chrTag
.attribute( M_TOKEN( val
), chr
);
456 m_rStream
.ensureClosingTag( M_TOKEN( chr
));
458 if( XmlStream::Tag posTag
= m_rStream
.checkOpeningTag( M_TOKEN( pos
)))
460 if( posTag
.attribute( M_TOKEN( val
), OUString( "bot" )) == "top" )
462 m_rStream
.ensureClosingTag( M_TOKEN( pos
));
464 m_rStream
.ensureClosingTag( M_TOKEN( groupChrPr
));
466 OUString e
= readOMathArgInElement( M_TOKEN( e
));
467 m_rStream
.ensureClosingTag( M_TOKEN( groupChr
));
468 if( pos
== top
&& chr
== u
'\x23de')
469 return "{" + e
+ "} overbrace { }";
470 if( pos
== bot
&& chr
== u
'\x23df')
471 return "{" + e
+ "} underbrace { }";
473 return "{" + e
+ "} csup {" + OUStringChar( chr
) + "}";
475 return "{" + e
+ "} csub {" + OUStringChar( chr
) + "}";
478 OUString
SmOoxmlImport::handleM()
480 m_rStream
.ensureOpeningTag( M_TOKEN( m
));
481 OUStringBuffer allrows
;
482 do // there must be at least one m:mr
484 m_rStream
.ensureOpeningTag( M_TOKEN( mr
));
486 do // there must be at least one m:e
490 row
.append(readOMathArgInElement( M_TOKEN( e
)));
491 } while( !m_rStream
.atEnd() && m_rStream
.findTag( OPENING( M_TOKEN( e
))));
492 if( !allrows
.isEmpty())
493 allrows
.append(" ## ");
495 m_rStream
.ensureClosingTag( M_TOKEN( mr
));
496 } while( !m_rStream
.atEnd() && m_rStream
.findTag( OPENING( M_TOKEN( mr
))));
497 m_rStream
.ensureClosingTag( M_TOKEN( m
));
498 return "matrix {" + allrows
.makeStringAndClear() + "}";
501 OUString
SmOoxmlImport::handleNary()
503 m_rStream
.ensureOpeningTag( M_TOKEN( nary
));
504 sal_Unicode chr
= 0x222b;
505 bool subHide
= false;
506 bool supHide
= false;
507 if( m_rStream
.checkOpeningTag( M_TOKEN( naryPr
)))
509 if( XmlStream::Tag chrTag
= m_rStream
.checkOpeningTag( M_TOKEN( chr
)))
511 chr
= chrTag
.attribute( M_TOKEN( val
), chr
);
512 m_rStream
.ensureClosingTag( M_TOKEN( chr
));
514 if( XmlStream::Tag subHideTag
= m_rStream
.checkOpeningTag( M_TOKEN( subHide
)))
516 subHide
= subHideTag
.attribute( M_TOKEN( val
), subHide
);
517 m_rStream
.ensureClosingTag( M_TOKEN( subHide
));
519 if( XmlStream::Tag supHideTag
= m_rStream
.checkOpeningTag( M_TOKEN( supHide
)))
521 supHide
= supHideTag
.attribute( M_TOKEN( val
), supHide
);
522 m_rStream
.ensureClosingTag( M_TOKEN( supHide
));
524 m_rStream
.ensureClosingTag( M_TOKEN( naryPr
));
526 OUString sub
= readOMathArgInElement( M_TOKEN( sub
));
527 OUString sup
= readOMathArgInElement( M_TOKEN( sup
));
528 OUString e
= readOMathArgInElement( M_TOKEN( e
));
560 SAL_WARN( "starmath.ooxml", "Unknown m:nary chr \'" << OUString(chr
) << "\'" );
564 ret
+= " from {" + sub
+ "}";
566 ret
+= " to {" + sup
+ "}";
567 ret
+= " {" + e
+ "}";
568 m_rStream
.ensureClosingTag( M_TOKEN( nary
));
573 OUString
SmOoxmlImport::handleR()
575 m_rStream
.ensureOpeningTag( M_TOKEN( r
));
577 bool literal
= false;
578 if( XmlStream::Tag rPr
= m_rStream
.checkOpeningTag( M_TOKEN( rPr
)))
580 if( XmlStream::Tag litTag
= m_rStream
.checkOpeningTag( M_TOKEN( lit
)))
582 literal
= litTag
.attribute( M_TOKEN( val
), true );
583 m_rStream
.ensureClosingTag( M_TOKEN( lit
));
585 if( XmlStream::Tag norTag
= m_rStream
.checkOpeningTag( M_TOKEN( nor
)))
587 normal
= norTag
.attribute( M_TOKEN( val
), true );
588 m_rStream
.ensureClosingTag( M_TOKEN( nor
));
590 m_rStream
.ensureClosingTag( M_TOKEN( rPr
));
593 while( !m_rStream
.atEnd() && m_rStream
.currentToken() != CLOSING( m_rStream
.currentToken()))
595 switch( m_rStream
.currentToken())
597 case OPENING( M_TOKEN( t
)):
599 XmlStream::Tag rtag
= m_rStream
.ensureOpeningTag( M_TOKEN( t
));
600 if( rtag
.attribute( OOX_TOKEN( xml
, space
)) != "preserve" )
601 text
.append(rtag
.text
.trim());
603 text
.append(rtag
.text
);
604 m_rStream
.ensureClosingTag( M_TOKEN( t
));
608 m_rStream
.handleUnexpectedTag();
612 m_rStream
.ensureClosingTag( M_TOKEN( r
));
613 if( normal
|| literal
)
615 text
.insert(0, "\"");
618 return text
.makeStringAndClear().replaceAll("{", "\\{").replaceAll("}", "\\}");
621 OUString
SmOoxmlImport::handleRad()
623 m_rStream
.ensureOpeningTag( M_TOKEN( rad
));
624 bool degHide
= false;
625 if( m_rStream
.checkOpeningTag( M_TOKEN( radPr
)))
627 if( XmlStream::Tag degHideTag
= m_rStream
.checkOpeningTag( M_TOKEN( degHide
)))
629 degHide
= degHideTag
.attribute( M_TOKEN( val
), degHide
);
630 m_rStream
.ensureClosingTag( M_TOKEN( degHide
));
632 m_rStream
.ensureClosingTag( M_TOKEN( radPr
));
634 OUString deg
= readOMathArgInElement( M_TOKEN( deg
));
635 OUString e
= readOMathArgInElement( M_TOKEN( e
));
636 m_rStream
.ensureClosingTag( M_TOKEN( rad
));
638 return "sqrt {" + e
+ "}";
640 return "nroot {" + deg
+ "} {" + e
+ "}";
643 OUString
SmOoxmlImport::handleSpre()
645 m_rStream
.ensureOpeningTag( M_TOKEN( sPre
));
646 OUString sub
= readOMathArgInElement( M_TOKEN( sub
));
647 OUString sup
= readOMathArgInElement( M_TOKEN( sup
));
648 OUString e
= readOMathArgInElement( M_TOKEN( e
));
649 m_rStream
.ensureClosingTag( M_TOKEN( sPre
));
650 return "{" + e
+ "} lsub {" + sub
+ "} lsup {" + sup
+ "}";
653 OUString
SmOoxmlImport::handleSsub()
655 m_rStream
.ensureOpeningTag( M_TOKEN( sSub
));
656 OUString e
= readOMathArgInElement( M_TOKEN( e
));
657 OUString sub
= readOMathArgInElement( M_TOKEN( sub
));
658 m_rStream
.ensureClosingTag( M_TOKEN( sSub
));
659 return "{" + e
+ "} rsub {" + sub
+ "}";
662 OUString
SmOoxmlImport::handleSsubsup()
664 m_rStream
.ensureOpeningTag( M_TOKEN( sSubSup
));
665 OUString e
= readOMathArgInElement( M_TOKEN( e
));
666 OUString sub
= readOMathArgInElement( M_TOKEN( sub
));
667 OUString sup
= readOMathArgInElement( M_TOKEN( sup
));
668 m_rStream
.ensureClosingTag( M_TOKEN( sSubSup
));
669 return "{" + e
+ "} rsub {" + sub
+ "} rsup {" + sup
+ "}";
672 OUString
SmOoxmlImport::handleSsup()
674 m_rStream
.ensureOpeningTag( M_TOKEN( sSup
));
675 OUString e
= readOMathArgInElement( M_TOKEN( e
));
676 OUString sup
= readOMathArgInElement( M_TOKEN( sup
));
677 m_rStream
.ensureClosingTag( M_TOKEN( sSup
));
678 return "{" + e
+ "} ^ {" + sup
+ "}";
681 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */