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
197 // prefer wide variants for these 3, .docx can't seem to differentiate
198 // between e.g. 'vec' and 'widevec', if whatever the accent is above is short, this
199 // shouldn't matter, but short above a longer expression doesn't look right
226 SAL_WARN( "starmath.ooxml", "Unknown m:chr in m:acc \'" << accChr
<< "\'" );
229 OUString e
= readOMathArgInElement( M_TOKEN( e
));
230 m_rStream
.ensureClosingTag( M_TOKEN( acc
));
231 return acc
+ " {" + e
+ "}";
234 OUString
SmOoxmlImport::handleBar()
236 m_rStream
.ensureOpeningTag( M_TOKEN( bar
));
237 enum pos_t
{ top
, bot
} topbot
= bot
;
238 if( m_rStream
.checkOpeningTag( M_TOKEN( barPr
)))
240 if( XmlStream::Tag pos
= m_rStream
.checkOpeningTag( M_TOKEN( pos
)))
242 if( pos
.attribute( M_TOKEN( val
)) == "top" )
244 else if( pos
.attribute( M_TOKEN( val
)) == "bot" )
246 m_rStream
.ensureClosingTag( M_TOKEN( pos
));
248 m_rStream
.ensureClosingTag( M_TOKEN( barPr
));
250 OUString e
= readOMathArgInElement( M_TOKEN( e
));
251 m_rStream
.ensureClosingTag( M_TOKEN( bar
));
253 return "overline {" + e
+ "}";
255 return "underline {" + e
+ "}";
258 OUString
SmOoxmlImport::handleBox()
260 // there does not seem to be functionality in LO to actually implement this
261 // (or is there), but at least read in the contents instead of ignoring them
262 m_rStream
.ensureOpeningTag( M_TOKEN( box
));
263 OUString e
= readOMathArgInElement( M_TOKEN( e
));
264 m_rStream
.ensureClosingTag( M_TOKEN( box
));
269 OUString
SmOoxmlImport::handleBorderBox()
271 m_rStream
.ensureOpeningTag( M_TOKEN( borderBox
));
272 bool isStrikeH
= false;
273 if( m_rStream
.checkOpeningTag( M_TOKEN( borderBoxPr
)))
275 if( XmlStream::Tag strikeH
= m_rStream
.checkOpeningTag( M_TOKEN( strikeH
)))
277 if( strikeH
.attribute( M_TOKEN( val
), false ))
279 m_rStream
.ensureClosingTag( M_TOKEN( strikeH
));
281 m_rStream
.ensureClosingTag( M_TOKEN( borderBoxPr
));
283 OUString e
= readOMathArgInElement( M_TOKEN( e
));
284 m_rStream
.ensureClosingTag( M_TOKEN( borderBox
));
286 return "overstrike {" + e
+ "}";
287 // LO does not seem to implement anything for handling the other cases
291 OUString
SmOoxmlImport::handleD()
293 m_rStream
.ensureOpeningTag( M_TOKEN( d
));
294 OUString opening
= "(";
295 OUString closing
= ")";
296 OUString separator
= "|";
297 if( XmlStream::Tag dPr
= m_rStream
.checkOpeningTag( M_TOKEN( dPr
)))
299 if( XmlStream::Tag begChr
= m_rStream
.checkOpeningTag( M_TOKEN( begChr
)))
301 opening
= begChr
.attribute( M_TOKEN( val
), opening
);
302 m_rStream
.ensureClosingTag( M_TOKEN( begChr
));
304 if( XmlStream::Tag sepChr
= m_rStream
.checkOpeningTag( M_TOKEN( sepChr
)))
306 separator
= sepChr
.attribute( M_TOKEN( val
), separator
);
307 m_rStream
.ensureClosingTag( M_TOKEN( sepChr
));
309 if( XmlStream::Tag endChr
= m_rStream
.checkOpeningTag( M_TOKEN( endChr
)))
311 closing
= endChr
.attribute( M_TOKEN( val
), closing
);
312 m_rStream
.ensureClosingTag( M_TOKEN( endChr
));
314 m_rStream
.ensureClosingTag( M_TOKEN( dPr
));
317 opening
= "left lbrace ";
319 closing
= " right rbrace";
320 if( opening
== u
"\u27e6" )
321 opening
= "left ldbracket ";
322 if( closing
== u
"\u27e7" )
323 closing
= " right rdbracket";
325 opening
= "left lline ";
327 closing
= " right rline";
328 if (opening
== OUStringChar(MS_DLINE
)
329 || opening
== OUStringChar(MS_DVERTLINE
))
330 opening
= "left ldline ";
331 if (closing
== OUStringChar(MS_DLINE
)
332 || closing
== OUStringChar(MS_DVERTLINE
))
333 closing
= " right rdline";
334 if (opening
== OUStringChar(MS_LANGLE
)
335 || opening
== OUStringChar(MS_LMATHANGLE
))
336 opening
= "left langle ";
337 if (closing
== OUStringChar(MS_RANGLE
)
338 || closing
== OUStringChar(MS_RMATHANGLE
))
339 closing
= " right rangle";
340 // use scalable brackets (the explicit "left" or "right")
341 if( opening
== "(" || opening
== "[" )
342 opening
= "left " + opening
;
343 if( closing
== ")" || closing
== "]" )
344 closing
= " right " + closing
;
345 if( separator
== "|" ) // plain "|" would be actually "V" (logical or)
346 separator
= " mline ";
347 if( opening
.isEmpty())
348 opening
= "left none ";
349 if( closing
.isEmpty())
350 closing
= " right none";
352 ret
.append( opening
);
354 while( m_rStream
.findTag( OPENING( M_TOKEN( e
))))
357 ret
.append( separator
);
359 ret
.append( readOMathArgInElement( M_TOKEN( e
)));
361 ret
.append( closing
);
362 m_rStream
.ensureClosingTag( M_TOKEN( d
));
363 return ret
.makeStringAndClear();
366 OUString
SmOoxmlImport::handleEqArr()
368 m_rStream
.ensureOpeningTag( M_TOKEN( eqArr
));
371 { // there must be at least one m:e
375 ret
.append(readOMathArgInElement( M_TOKEN( e
)));
377 } while( !m_rStream
.atEnd() && m_rStream
.findTag( OPENING( M_TOKEN( e
))));
378 m_rStream
.ensureClosingTag( M_TOKEN( eqArr
));
379 return "stack {" + ret
.makeStringAndClear() + "}";
382 OUString
SmOoxmlImport::handleF()
384 m_rStream
.ensureOpeningTag( M_TOKEN( f
));
385 enum operation_t
{ bar
, lin
, noBar
} operation
= bar
;
386 if( m_rStream
.checkOpeningTag( M_TOKEN( fPr
)))
388 if( XmlStream::Tag type
= m_rStream
.checkOpeningTag( M_TOKEN( type
)))
390 if( type
.attribute( M_TOKEN( val
)) == "bar" )
392 else if( type
.attribute( M_TOKEN( val
)) == "lin" )
394 else if( type
.attribute( M_TOKEN( val
)) == "noBar" )
396 m_rStream
.ensureClosingTag( M_TOKEN( type
));
398 m_rStream
.ensureClosingTag( M_TOKEN( fPr
));
400 OUString num
= readOMathArgInElement( M_TOKEN( num
));
401 OUString den
= readOMathArgInElement( M_TOKEN( den
));
402 m_rStream
.ensureClosingTag( M_TOKEN( f
));
403 if( operation
== bar
)
404 return "{" + num
+ "} over {" + den
+ "}";
405 else if( operation
== lin
)
406 return "{" + num
+ "} / {" + den
+ "}";
409 return "binom {" + num
+ "} {" + den
+ "}";
413 OUString
SmOoxmlImport::handleFunc()
415 //lim from{x rightarrow 1} x
416 m_rStream
.ensureOpeningTag( M_TOKEN( func
));
417 OUString fname
= readOMathArgInElement( M_TOKEN( fName
));
418 // fix the various functions
419 if( fname
.startsWith( "lim csub {" ))
420 fname
= "lim from {" + fname
.copy( 10 );
421 OUString ret
= fname
+ " {" + readOMathArgInElement( M_TOKEN( e
)) + "}";
422 m_rStream
.ensureClosingTag( M_TOKEN( func
));
426 OUString
SmOoxmlImport::handleLimLowUpp( LimLowUpp_t limlowupp
)
428 int token
= limlowupp
== LimLow
? M_TOKEN( limLow
) : M_TOKEN( limUpp
);
429 m_rStream
.ensureOpeningTag( token
);
430 OUString e
= readOMathArgInElement( M_TOKEN( e
));
431 OUString lim
= readOMathArgInElement( M_TOKEN( lim
));
432 m_rStream
.ensureClosingTag( token
);
433 // fix up overbrace/underbrace (use { }, as {} will be converted to a placeholder)
434 if( limlowupp
== LimUpp
&& e
.endsWith( " overbrace { }" ))
435 return e
.copy( 0, e
.getLength() - 2 ) + lim
+ "}";
436 if( limlowupp
== LimLow
&& e
.endsWith( " underbrace { }" ))
437 return e
.copy( 0, e
.getLength() - 2 ) + lim
+ "}";
439 + ( limlowupp
== LimLow
? OUStringLiteral( " csub {" ) : OUStringLiteral( " csup {" ))
443 OUString
SmOoxmlImport::handleGroupChr()
445 m_rStream
.ensureOpeningTag( M_TOKEN( groupChr
));
446 sal_Unicode chr
= 0x23df;
447 enum pos_t
{ top
, bot
} pos
= bot
;
448 if( m_rStream
.checkOpeningTag( M_TOKEN( groupChrPr
)))
450 if( XmlStream::Tag chrTag
= m_rStream
.checkOpeningTag( M_TOKEN( chr
)))
452 chr
= chrTag
.attribute( M_TOKEN( val
), chr
);
453 m_rStream
.ensureClosingTag( M_TOKEN( chr
));
455 if( XmlStream::Tag posTag
= m_rStream
.checkOpeningTag( M_TOKEN( pos
)))
457 if( posTag
.attribute( M_TOKEN( val
), OUString( "bot" )) == "top" )
459 m_rStream
.ensureClosingTag( M_TOKEN( pos
));
461 m_rStream
.ensureClosingTag( M_TOKEN( groupChrPr
));
463 OUString e
= readOMathArgInElement( M_TOKEN( e
));
464 m_rStream
.ensureClosingTag( M_TOKEN( groupChr
));
465 if( pos
== top
&& chr
== u
'\x23de')
466 return "{" + e
+ "} overbrace { }";
467 if( pos
== bot
&& chr
== u
'\x23df')
468 return "{" + e
+ "} underbrace { }";
470 return "{" + e
+ "} csup {" + OUStringChar( chr
) + "}";
472 return "{" + e
+ "} csub {" + OUStringChar( chr
) + "}";
475 OUString
SmOoxmlImport::handleM()
477 m_rStream
.ensureOpeningTag( M_TOKEN( m
));
478 OUStringBuffer allrows
;
479 do // there must be at least one m:mr
481 m_rStream
.ensureOpeningTag( M_TOKEN( mr
));
483 do // there must be at least one m:e
487 row
.append(readOMathArgInElement( M_TOKEN( e
)));
488 } while( !m_rStream
.atEnd() && m_rStream
.findTag( OPENING( M_TOKEN( e
))));
489 if( !allrows
.isEmpty())
490 allrows
.append(" ## ");
492 m_rStream
.ensureClosingTag( M_TOKEN( mr
));
493 } while( !m_rStream
.atEnd() && m_rStream
.findTag( OPENING( M_TOKEN( mr
))));
494 m_rStream
.ensureClosingTag( M_TOKEN( m
));
495 return "matrix {" + allrows
.makeStringAndClear() + "}";
498 OUString
SmOoxmlImport::handleNary()
500 m_rStream
.ensureOpeningTag( M_TOKEN( nary
));
501 sal_Unicode chr
= 0x222b;
502 bool subHide
= false;
503 bool supHide
= false;
504 if( m_rStream
.checkOpeningTag( M_TOKEN( naryPr
)))
506 if( XmlStream::Tag chrTag
= m_rStream
.checkOpeningTag( M_TOKEN( chr
)))
508 chr
= chrTag
.attribute( M_TOKEN( val
), chr
);
509 m_rStream
.ensureClosingTag( M_TOKEN( chr
));
511 if( XmlStream::Tag subHideTag
= m_rStream
.checkOpeningTag( M_TOKEN( subHide
)))
513 subHide
= subHideTag
.attribute( M_TOKEN( val
), subHide
);
514 m_rStream
.ensureClosingTag( M_TOKEN( subHide
));
516 if( XmlStream::Tag supHideTag
= m_rStream
.checkOpeningTag( M_TOKEN( supHide
)))
518 supHide
= supHideTag
.attribute( M_TOKEN( val
), supHide
);
519 m_rStream
.ensureClosingTag( M_TOKEN( supHide
));
521 m_rStream
.ensureClosingTag( M_TOKEN( naryPr
));
523 OUString sub
= readOMathArgInElement( M_TOKEN( sub
));
524 OUString sup
= readOMathArgInElement( M_TOKEN( sup
));
525 OUString e
= readOMathArgInElement( M_TOKEN( e
));
557 SAL_WARN( "starmath.ooxml", "Unknown m:nary chr \'" << chr
<< "\'" );
561 ret
+= " from {" + sub
+ "}";
563 ret
+= " to {" + sup
+ "}";
564 ret
+= " {" + e
+ "}";
565 m_rStream
.ensureClosingTag( M_TOKEN( nary
));
570 OUString
SmOoxmlImport::handleR()
572 m_rStream
.ensureOpeningTag( M_TOKEN( r
));
574 bool literal
= false;
575 if( XmlStream::Tag rPr
= m_rStream
.checkOpeningTag( M_TOKEN( rPr
)))
577 if( XmlStream::Tag litTag
= m_rStream
.checkOpeningTag( M_TOKEN( lit
)))
579 literal
= litTag
.attribute( M_TOKEN( val
), true );
580 m_rStream
.ensureClosingTag( M_TOKEN( lit
));
582 if( XmlStream::Tag norTag
= m_rStream
.checkOpeningTag( M_TOKEN( nor
)))
584 normal
= norTag
.attribute( M_TOKEN( val
), true );
585 m_rStream
.ensureClosingTag( M_TOKEN( nor
));
587 m_rStream
.ensureClosingTag( M_TOKEN( rPr
));
590 while( !m_rStream
.atEnd() && m_rStream
.currentToken() != CLOSING( m_rStream
.currentToken()))
592 switch( m_rStream
.currentToken())
594 case OPENING( M_TOKEN( t
)):
596 XmlStream::Tag rtag
= m_rStream
.ensureOpeningTag( M_TOKEN( t
));
597 if( rtag
.attribute( OOX_TOKEN( xml
, space
)) != "preserve" )
598 text
.append(rtag
.text
.trim());
600 text
.append(rtag
.text
);
601 m_rStream
.ensureClosingTag( M_TOKEN( t
));
605 m_rStream
.handleUnexpectedTag();
609 m_rStream
.ensureClosingTag( M_TOKEN( r
));
610 if( normal
|| literal
)
612 text
.insert(0, "\"");
615 return text
.makeStringAndClear().replaceAll("{", "\\{").replaceAll("}", "\\}");
618 OUString
SmOoxmlImport::handleRad()
620 m_rStream
.ensureOpeningTag( M_TOKEN( rad
));
621 bool degHide
= false;
622 if( m_rStream
.checkOpeningTag( M_TOKEN( radPr
)))
624 if( XmlStream::Tag degHideTag
= m_rStream
.checkOpeningTag( M_TOKEN( degHide
)))
626 degHide
= degHideTag
.attribute( M_TOKEN( val
), degHide
);
627 m_rStream
.ensureClosingTag( M_TOKEN( degHide
));
629 m_rStream
.ensureClosingTag( M_TOKEN( radPr
));
631 OUString deg
= readOMathArgInElement( M_TOKEN( deg
));
632 OUString e
= readOMathArgInElement( M_TOKEN( e
));
633 m_rStream
.ensureClosingTag( M_TOKEN( rad
));
635 return "sqrt {" + e
+ "}";
637 return "nroot {" + deg
+ "} {" + e
+ "}";
640 OUString
SmOoxmlImport::handleSpre()
642 m_rStream
.ensureOpeningTag( M_TOKEN( sPre
));
643 OUString sub
= readOMathArgInElement( M_TOKEN( sub
));
644 OUString sup
= readOMathArgInElement( M_TOKEN( sup
));
645 OUString e
= readOMathArgInElement( M_TOKEN( e
));
646 m_rStream
.ensureClosingTag( M_TOKEN( sPre
));
647 return "{" + e
+ "} lsub {" + sub
+ "} lsup {" + sup
+ "}";
650 OUString
SmOoxmlImport::handleSsub()
652 m_rStream
.ensureOpeningTag( M_TOKEN( sSub
));
653 OUString e
= readOMathArgInElement( M_TOKEN( e
));
654 OUString sub
= readOMathArgInElement( M_TOKEN( sub
));
655 m_rStream
.ensureClosingTag( M_TOKEN( sSub
));
656 return "{" + e
+ "} rsub {" + sub
+ "}";
659 OUString
SmOoxmlImport::handleSsubsup()
661 m_rStream
.ensureOpeningTag( M_TOKEN( sSubSup
));
662 OUString e
= readOMathArgInElement( M_TOKEN( e
));
663 OUString sub
= readOMathArgInElement( M_TOKEN( sub
));
664 OUString sup
= readOMathArgInElement( M_TOKEN( sup
));
665 m_rStream
.ensureClosingTag( M_TOKEN( sSubSup
));
666 return "{" + e
+ "} rsub {" + sub
+ "} rsup {" + sup
+ "}";
669 OUString
SmOoxmlImport::handleSsup()
671 m_rStream
.ensureOpeningTag( M_TOKEN( sSup
));
672 OUString e
= readOMathArgInElement( M_TOKEN( e
));
673 OUString sup
= readOMathArgInElement( M_TOKEN( sup
));
674 m_rStream
.ensureClosingTag( M_TOKEN( sSup
));
675 return "{" + e
+ "} ^ {" + sup
+ "}";
678 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */