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/token/tokens.hxx>
15 #include <oox/token/namespaces.hxx>
16 #include <rtl/ustring.hxx>
17 #include <rtl/ustrbuf.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 M_TOKEN( token ) OOX_TOKEN( officeMath, token )
28 #define OPENING( token ) XML_STREAM_OPENING( token )
29 #define CLOSING( token ) XML_STREAM_CLOSING( token )
31 // TODO create IS_OPENING(), IS_CLOSING() instead of doing 'next == OPENING( next )' ?
33 SmOoxmlImport::SmOoxmlImport( oox::formulaimport::XmlStream
& s
)
38 OUString
SmOoxmlImport::ConvertToStarMath()
40 return handleStream();
43 // "toplevel" of reading, there will be oMath (if there was oMathPara, that was
44 // up to the parent component to handle)
47 OUString
SmOoxmlImport::handleStream()
49 stream
.ensureOpeningTag( M_TOKEN( oMath
));
51 while( !stream
.atEnd() && stream
.currentToken() != CLOSING( M_TOKEN( oMath
)))
53 // strictly speaking, it is not OMathArg here, but currently supported
54 // functionality is the same like OMathArg, in the future this may need improving
55 OUString item
= readOMathArg( M_TOKEN( oMath
));
62 stream
.ensureClosingTag( M_TOKEN( oMath
));
63 // Placeholders are written out as nothing (i.e. nothing inside e.g. the <e> element),
64 // which will result in "{}" in the formula text. Fix this up.
65 ret
= ret
.replaceAll( "{}", "<?>" );
66 // And as a result, empty parts of the formula that are not placeholders are written out
67 // as a single space, so fix that up too.
68 ret
= ret
.replaceAll( "{ }", "{}" );
69 SAL_INFO( "starmath.ooxml", "Formula: " << ret
);
73 OUString
SmOoxmlImport::readOMathArg( int stoptoken
)
76 while( !stream
.atEnd() && stream
.currentToken() != CLOSING( stoptoken
))
80 switch( stream
.currentToken())
82 case OPENING( M_TOKEN( acc
)):
85 case OPENING( M_TOKEN( bar
)):
88 case OPENING( M_TOKEN( box
)):
91 case OPENING( M_TOKEN( borderBox
)):
92 ret
+= handleBorderBox();
94 case OPENING( M_TOKEN( d
)):
97 case OPENING( M_TOKEN( eqArr
)):
100 case OPENING( M_TOKEN( f
)):
103 case OPENING( M_TOKEN( func
)):
106 case OPENING( M_TOKEN( limLow
)):
107 ret
+= handleLimLowUpp( LimLow
);
109 case OPENING( M_TOKEN( limUpp
)):
110 ret
+= handleLimLowUpp( LimUpp
);
112 case OPENING( M_TOKEN( groupChr
)):
113 ret
+= handleGroupChr();
115 case OPENING( M_TOKEN( m
)):
118 case OPENING( M_TOKEN( nary
)):
121 case OPENING( M_TOKEN( r
)):
124 case OPENING( M_TOKEN( rad
)):
127 case OPENING( M_TOKEN( sPre
)):
130 case OPENING( M_TOKEN( sSub
)):
133 case OPENING( M_TOKEN( sSubSup
)):
134 ret
+= handleSsubsup();
136 case OPENING( M_TOKEN( sSup
)):
140 stream
.handleUnexpectedTag();
147 OUString
SmOoxmlImport::readOMathArgInElement( int token
)
149 stream
.ensureOpeningTag( token
);
150 OUString ret
= readOMathArg( token
);
151 stream
.ensureClosingTag( token
);
155 OUString
SmOoxmlImport::handleAcc()
157 stream
.ensureOpeningTag( M_TOKEN( acc
));
158 sal_Unicode accChr
= 0x302;
159 if( XmlStream::Tag accPr
= stream
.checkOpeningTag( M_TOKEN( accPr
)))
161 if( XmlStream::Tag chr
= stream
.checkOpeningTag( M_TOKEN( chr
)))
163 accChr
= chr
.attribute( M_TOKEN( val
), accChr
);
164 stream
.ensureClosingTag( M_TOKEN( chr
));
166 stream
.ensureClosingTag( M_TOKEN( accPr
));
168 // see aTokenTable in parse.cxx
198 // prefer wide variants for these 3, .docx can't seem to differentiate
199 // between e.g. 'vec' and 'widevec', if whatever the accent is above is short, this
200 // shouldn't matter, but short above a longer expression doesn't look right
222 SAL_WARN( "starmath.ooxml", "Unknown m:chr in m:acc \'" << accChr
<< "\'" );
225 OUString e
= readOMathArgInElement( M_TOKEN( e
));
226 stream
.ensureClosingTag( M_TOKEN( acc
));
227 return acc
+ " {" + e
+ "}";
230 OUString
SmOoxmlImport::handleBar()
232 stream
.ensureOpeningTag( M_TOKEN( bar
));
233 enum pos_t
{ top
, bot
} topbot
= bot
;
234 if( stream
.checkOpeningTag( M_TOKEN( barPr
)))
236 if( XmlStream::Tag pos
= stream
.checkOpeningTag( M_TOKEN( pos
)))
238 if( pos
.attribute( M_TOKEN( val
)) == "top" )
240 else if( pos
.attribute( M_TOKEN( val
)) == "bot" )
242 stream
.ensureClosingTag( M_TOKEN( pos
));
244 stream
.ensureClosingTag( M_TOKEN( barPr
));
246 OUString e
= readOMathArgInElement( M_TOKEN( e
));
247 stream
.ensureClosingTag( M_TOKEN( bar
));
249 return "overline {" + e
+ "}";
251 return "underline {" + e
+ "}";
254 OUString
SmOoxmlImport::handleBox()
256 // there does not seem to be functionality in LO to actually implement this
257 // (or is there), but at least read in the contents instead of ignoring them
258 stream
.ensureOpeningTag( M_TOKEN( box
));
259 OUString e
= readOMathArgInElement( M_TOKEN( e
));
260 stream
.ensureClosingTag( M_TOKEN( box
));
265 OUString
SmOoxmlImport::handleBorderBox()
267 stream
.ensureOpeningTag( M_TOKEN( borderBox
));
268 bool isStrikeH
= false;
269 if( stream
.checkOpeningTag( M_TOKEN( borderBoxPr
)))
271 if( XmlStream::Tag strikeH
= stream
.checkOpeningTag( M_TOKEN( strikeH
)))
273 if( strikeH
.attribute( M_TOKEN( val
), false ))
275 stream
.ensureClosingTag( M_TOKEN( strikeH
));
277 stream
.ensureClosingTag( M_TOKEN( borderBoxPr
));
279 OUString e
= readOMathArgInElement( M_TOKEN( e
));
280 stream
.ensureClosingTag( M_TOKEN( borderBox
));
282 return "overstrike {" + e
+ "}";
283 // LO does not seem to implement anything for handling the other cases
287 OUString
SmOoxmlImport::handleD()
289 stream
.ensureOpeningTag( M_TOKEN( d
));
290 OUString opening
= "(";
291 OUString closing
= ")";
292 OUString separator
= "|";
293 if( XmlStream::Tag dPr
= stream
.checkOpeningTag( M_TOKEN( dPr
)))
295 if( XmlStream::Tag begChr
= stream
.checkOpeningTag( M_TOKEN( begChr
)))
297 opening
= begChr
.attribute( M_TOKEN( val
), opening
);
298 stream
.ensureClosingTag( M_TOKEN( begChr
));
300 if( XmlStream::Tag sepChr
= stream
.checkOpeningTag( M_TOKEN( sepChr
)))
302 separator
= sepChr
.attribute( M_TOKEN( val
), separator
);
303 stream
.ensureClosingTag( M_TOKEN( sepChr
));
305 if( XmlStream::Tag endChr
= stream
.checkOpeningTag( M_TOKEN( endChr
)))
307 closing
= endChr
.attribute( M_TOKEN( val
), closing
);
308 stream
.ensureClosingTag( M_TOKEN( endChr
));
310 stream
.ensureClosingTag( M_TOKEN( dPr
));
313 opening
= "left lbrace ";
315 closing
= " right rbrace";
316 if( opening
== OUString( sal_Unicode( 0x27e6 )))
317 opening
= "left ldbracket ";
318 if( closing
== OUString( sal_Unicode( 0x27e7 )))
319 closing
= " right rdbracket";
321 opening
= "left lline ";
323 closing
= " right rline";
324 if (opening
== OUString(MS_DLINE
) || opening
== OUString(MS_DVERTLINE
))
325 opening
= "left ldline ";
326 if (closing
== OUString(MS_DLINE
) || closing
== OUString(MS_DVERTLINE
))
327 closing
= " right rdline";
328 if (opening
== OUString(MS_LANGLE
) || opening
== OUString(MS_LMATHANGLE
))
329 opening
= "left langle ";
330 if (closing
== OUString(MS_RANGLE
) || closing
== OUString(MS_RMATHANGLE
))
331 closing
= " right rangle";
332 // use scalable brackets (the explicit "left" or "right")
333 if( opening
== "(" || opening
== "[" )
334 opening
= "left " + opening
;
335 if( closing
== ")" || closing
== "]" )
336 closing
= " right " + closing
;
337 if( separator
== "|" ) // plain "|" would be actually "V" (logical or)
338 separator
= " mline ";
339 if( opening
.isEmpty())
340 opening
= "left none ";
341 if( closing
.isEmpty())
342 closing
= " right none";
344 ret
.append( opening
);
346 while( stream
.findTag( OPENING( M_TOKEN( e
))))
349 ret
.append( separator
);
351 ret
.append( readOMathArgInElement( M_TOKEN( e
)));
353 ret
.append( closing
);
354 stream
.ensureClosingTag( M_TOKEN( d
));
355 return ret
.makeStringAndClear();
358 OUString
SmOoxmlImport::handleEqArr()
360 stream
.ensureOpeningTag( M_TOKEN( eqArr
));
363 { // there must be at least one m:e
367 ret
+= readOMathArgInElement( M_TOKEN( e
));
369 } while( !stream
.atEnd() && stream
.findTag( OPENING( M_TOKEN( e
))));
370 stream
.ensureClosingTag( M_TOKEN( eqArr
));
371 return "stack {" + ret
+ "}";
374 OUString
SmOoxmlImport::handleF()
376 stream
.ensureOpeningTag( M_TOKEN( f
));
377 enum operation_t
{ bar
, lin
, noBar
} operation
= bar
;
378 if( stream
.checkOpeningTag( M_TOKEN( fPr
)))
380 if( XmlStream::Tag type
= stream
.checkOpeningTag( M_TOKEN( type
)))
382 if( type
.attribute( M_TOKEN( val
)) == "bar" )
384 else if( type
.attribute( M_TOKEN( val
)) == "lin" )
386 else if( type
.attribute( M_TOKEN( val
)) == "noBar" )
388 stream
.ensureClosingTag( M_TOKEN( type
));
390 stream
.ensureClosingTag( M_TOKEN( fPr
));
392 OUString num
= readOMathArgInElement( M_TOKEN( num
));
393 OUString den
= readOMathArgInElement( M_TOKEN( den
));
394 stream
.ensureClosingTag( M_TOKEN( f
));
395 if( operation
== bar
)
396 return "{" + num
+ "} over {" + den
+ "}";
397 else if( operation
== lin
)
398 return "{" + num
+ "} / {" + den
+ "}";
401 return "binom {" + num
+ "} {" + den
+ "}";
405 OUString
SmOoxmlImport::handleFunc()
407 //lim from{x rightarrow 1} x
408 stream
.ensureOpeningTag( M_TOKEN( func
));
409 OUString fname
= readOMathArgInElement( M_TOKEN( fName
));
410 // fix the various functions
411 if( fname
.startsWith( "lim csub {" ))
412 fname
= "lim from {" + fname
.copy( 10 );
413 OUString ret
= fname
+ " {" + readOMathArgInElement( M_TOKEN( e
)) + "}";
414 stream
.ensureClosingTag( M_TOKEN( func
));
418 OUString
SmOoxmlImport::handleLimLowUpp( LimLowUpp_t limlowupp
)
420 int token
= limlowupp
== LimLow
? M_TOKEN( limLow
) : M_TOKEN( limUpp
);
421 stream
.ensureOpeningTag( token
);
422 OUString e
= readOMathArgInElement( M_TOKEN( e
));
423 OUString lim
= readOMathArgInElement( M_TOKEN( lim
));
424 stream
.ensureClosingTag( token
);
425 // fix up overbrace/underbrace (use { }, as {} will be converted to a placeholder)
426 if( limlowupp
== LimUpp
&& e
.endsWith( " overbrace { }" ))
427 return e
.copy( 0, e
.getLength() - 2 ) + lim
+ "}";
428 if( limlowupp
== LimLow
&& e
.endsWith( " underbrace { }" ))
429 return e
.copy( 0, e
.getLength() - 2 ) + lim
+ "}";
431 + ( limlowupp
== LimLow
? OUString( " csub {" ) : OUString( " csup {" ))
435 OUString
SmOoxmlImport::handleGroupChr()
437 stream
.ensureOpeningTag( M_TOKEN( groupChr
));
438 sal_Unicode chr
= 0x23df;
439 enum pos_t
{ top
, bot
} pos
= bot
;
440 if( stream
.checkOpeningTag( M_TOKEN( groupChrPr
)))
442 if( XmlStream::Tag chrTag
= stream
.checkOpeningTag( M_TOKEN( chr
)))
444 chr
= chrTag
.attribute( M_TOKEN( val
), chr
);
445 stream
.ensureClosingTag( M_TOKEN( chr
));
447 if( XmlStream::Tag posTag
= stream
.checkOpeningTag( M_TOKEN( pos
)))
449 if( posTag
.attribute( M_TOKEN( val
), OUString( "bot" )) == "top" )
451 stream
.ensureClosingTag( M_TOKEN( pos
));
453 stream
.ensureClosingTag( M_TOKEN( groupChrPr
));
455 OUString e
= readOMathArgInElement( M_TOKEN( e
));
456 stream
.ensureClosingTag( M_TOKEN( groupChr
));
457 if( pos
== top
&& chr
== sal_Unicode( 0x23de ))
458 return "{" + e
+ "} overbrace { }";
459 if( pos
== bot
&& chr
== sal_Unicode( 0x23df ))
460 return "{" + e
+ "} underbrace { }";
462 return "{" + e
+ "} csup {" + OUString( chr
) + "}";
464 return "{" + e
+ "} csub {" + OUString( chr
) + "}";
467 OUString
SmOoxmlImport::handleM()
469 stream
.ensureOpeningTag( M_TOKEN( m
));
471 do // there must be at least one m:mr
473 stream
.ensureOpeningTag( M_TOKEN( mr
));
475 do // there must be at least one m:e
479 row
+= readOMathArgInElement( M_TOKEN( e
));
480 } while( !stream
.atEnd() && stream
.findTag( OPENING( M_TOKEN( e
))));
481 if( !allrows
.isEmpty())
484 stream
.ensureClosingTag( M_TOKEN( mr
));
485 } while( !stream
.atEnd() && stream
.findTag( OPENING( M_TOKEN( mr
))));
486 stream
.ensureClosingTag( M_TOKEN( m
));
487 return "matrix {" + allrows
+ "}";
490 OUString
SmOoxmlImport::handleNary()
492 stream
.ensureOpeningTag( M_TOKEN( nary
));
493 sal_Unicode chr
= 0x222b;
494 bool subHide
= false;
495 bool supHide
= false;
496 if( stream
.checkOpeningTag( M_TOKEN( naryPr
)))
498 if( XmlStream::Tag chrTag
= stream
.checkOpeningTag( M_TOKEN( chr
)))
500 chr
= chrTag
.attribute( M_TOKEN( val
), chr
);
501 stream
.ensureClosingTag( M_TOKEN( chr
));
503 if( XmlStream::Tag subHideTag
= stream
.checkOpeningTag( M_TOKEN( subHide
)))
505 subHide
= subHideTag
.attribute( M_TOKEN( val
), subHide
);
506 stream
.ensureClosingTag( M_TOKEN( subHide
));
508 if( XmlStream::Tag supHideTag
= stream
.checkOpeningTag( M_TOKEN( supHide
)))
510 supHide
= supHideTag
.attribute( M_TOKEN( val
), supHide
);
511 stream
.ensureClosingTag( M_TOKEN( supHide
));
513 stream
.ensureClosingTag( M_TOKEN( naryPr
));
515 OUString sub
= readOMathArgInElement( M_TOKEN( sub
));
516 OUString sup
= readOMathArgInElement( M_TOKEN( sup
));
517 OUString e
= readOMathArgInElement( M_TOKEN( e
));
549 SAL_WARN( "starmath.ooxml", "Unknown m:nary chr \'" << chr
<< "\'" );
553 ret
+= " from {" + sub
+ "}";
555 ret
+= " to {" + sup
+ "}";
556 ret
+= " {" + e
+ "}";
557 stream
.ensureClosingTag( M_TOKEN( nary
));
562 OUString
SmOoxmlImport::handleR()
564 stream
.ensureOpeningTag( M_TOKEN( r
));
566 bool literal
= false;
567 if( XmlStream::Tag rPr
= stream
.checkOpeningTag( M_TOKEN( rPr
)))
569 if( XmlStream::Tag litTag
= stream
.checkOpeningTag( M_TOKEN( lit
)))
571 literal
= litTag
.attribute( M_TOKEN( val
), true );
572 stream
.ensureClosingTag( M_TOKEN( lit
));
574 if( XmlStream::Tag norTag
= stream
.checkOpeningTag( M_TOKEN( nor
)))
576 normal
= norTag
.attribute( M_TOKEN( val
), true );
577 stream
.ensureClosingTag( M_TOKEN( nor
));
579 stream
.ensureClosingTag( M_TOKEN( rPr
));
582 while( !stream
.atEnd() && stream
.currentToken() != CLOSING( stream
.currentToken()))
584 switch( stream
.currentToken())
586 case OPENING( M_TOKEN( t
)):
588 XmlStream::Tag rtag
= stream
.ensureOpeningTag( M_TOKEN( t
));
589 if( rtag
.attribute( OOX_TOKEN( xml
, space
)) != "preserve" )
590 text
+= rtag
.text
.trim();
593 stream
.ensureClosingTag( M_TOKEN( t
));
597 stream
.handleUnexpectedTag();
601 stream
.ensureClosingTag( M_TOKEN( r
));
602 if( normal
|| literal
)
603 text
= "\"" + text
+ "\"";
604 return text
.replaceAll("{", "\\{").replaceAll("}", "\\}");
607 OUString
SmOoxmlImport::handleRad()
609 stream
.ensureOpeningTag( M_TOKEN( rad
));
610 bool degHide
= false;
611 if( stream
.checkOpeningTag( M_TOKEN( radPr
)))
613 if( XmlStream::Tag degHideTag
= stream
.checkOpeningTag( M_TOKEN( degHide
)))
615 degHide
= degHideTag
.attribute( M_TOKEN( val
), degHide
);
616 stream
.ensureClosingTag( M_TOKEN( degHide
));
618 stream
.ensureClosingTag( M_TOKEN( radPr
));
620 OUString deg
= readOMathArgInElement( M_TOKEN( deg
));
621 OUString e
= readOMathArgInElement( M_TOKEN( e
));
622 stream
.ensureClosingTag( M_TOKEN( rad
));
624 return "sqrt {" + e
+ "}";
626 return "nroot {" + deg
+ "} {" + e
+ "}";
629 OUString
SmOoxmlImport::handleSpre()
631 stream
.ensureOpeningTag( M_TOKEN( sPre
));
632 OUString sub
= readOMathArgInElement( M_TOKEN( sub
));
633 OUString sup
= readOMathArgInElement( M_TOKEN( sup
));
634 OUString e
= readOMathArgInElement( M_TOKEN( e
));
635 stream
.ensureClosingTag( M_TOKEN( sPre
));
636 return "{" + e
+ "} lsub {" + sub
+ "} lsup {" + sup
+ "}";
639 OUString
SmOoxmlImport::handleSsub()
641 stream
.ensureOpeningTag( M_TOKEN( sSub
));
642 OUString e
= readOMathArgInElement( M_TOKEN( e
));
643 OUString sub
= readOMathArgInElement( M_TOKEN( sub
));
644 stream
.ensureClosingTag( M_TOKEN( sSub
));
645 return "{" + e
+ "} rsub {" + sub
+ "}";
648 OUString
SmOoxmlImport::handleSsubsup()
650 stream
.ensureOpeningTag( M_TOKEN( sSubSup
));
651 OUString e
= readOMathArgInElement( M_TOKEN( e
));
652 OUString sub
= readOMathArgInElement( M_TOKEN( sub
));
653 OUString sup
= readOMathArgInElement( M_TOKEN( sup
));
654 stream
.ensureClosingTag( M_TOKEN( sSubSup
));
655 return "{" + e
+ "} rsub {" + sub
+ "} rsup {" + sup
+ "}";
658 OUString
SmOoxmlImport::handleSsup()
660 stream
.ensureOpeningTag( M_TOKEN( sSup
));
661 OUString e
= readOMathArgInElement( M_TOKEN( e
));
662 OUString sup
= readOMathArgInElement( M_TOKEN( sup
));
663 stream
.ensureClosingTag( M_TOKEN( sSup
));
664 return "{" + e
+ "} ^ {" + sup
+ "}";
667 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */