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"
13 #include <oox/token/tokens.hxx>
14 #include <oox/token/namespaces.hxx>
15 #include <rtl/ustring.hxx>
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
)
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)
45 OUString
SmOoxmlImport::handleStream()
47 stream
.ensureOpeningTag( M_TOKEN( oMath
));
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
));
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
);
71 OUString
SmOoxmlImport::readOMathArg( int stoptoken
)
74 while( !stream
.atEnd() && stream
.currentToken() != CLOSING( stoptoken
))
78 switch( stream
.currentToken())
80 case OPENING( M_TOKEN( acc
)):
83 case OPENING( M_TOKEN( bar
)):
86 case OPENING( M_TOKEN( box
)):
89 case OPENING( M_TOKEN( borderBox
)):
90 ret
+= handleBorderBox();
92 case OPENING( M_TOKEN( d
)):
95 case OPENING( M_TOKEN( eqArr
)):
98 case OPENING( M_TOKEN( f
)):
101 case OPENING( M_TOKEN( func
)):
104 case OPENING( M_TOKEN( limLow
)):
105 ret
+= handleLimLowUpp( LimLow
);
107 case OPENING( M_TOKEN( limUpp
)):
108 ret
+= handleLimLowUpp( LimUpp
);
110 case OPENING( M_TOKEN( groupChr
)):
111 ret
+= handleGroupChr();
113 case OPENING( M_TOKEN( m
)):
116 case OPENING( M_TOKEN( nary
)):
119 case OPENING( M_TOKEN( r
)):
122 case OPENING( M_TOKEN( rad
)):
125 case OPENING( M_TOKEN( sPre
)):
128 case OPENING( M_TOKEN( sSub
)):
131 case OPENING( M_TOKEN( sSubSup
)):
132 ret
+= handleSsubsup();
134 case OPENING( M_TOKEN( sSup
)):
138 stream
.handleUnexpectedTag();
145 OUString
SmOoxmlImport::readOMathArgInElement( int token
)
147 stream
.ensureOpeningTag( token
);
148 OUString ret
= readOMathArg( token
);
149 stream
.ensureClosingTag( token
);
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
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
211 SAL_WARN( "starmath.ooxml", "Unknown m:chr in m:acc \'" << accChr
<< "\'" );
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" )
229 else if( pos
.attribute( M_TOKEN( val
)) == "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
));
238 return "overline {" + e
+ "}";
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
));
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 ))
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
));
271 return "overstrike {" + e
+ "}";
272 // LO does not seem to implement anything for handling the other cases
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
));
302 opening
= "left lbrace ";
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";
310 opening
= "left lline ";
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";
333 ret
.append( opening
);
335 while( stream
.findTag( OPENING( M_TOKEN( e
))))
338 ret
.append( separator
);
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
));
352 { // there must be at least one m:e
356 ret
+= readOMathArgInElement( M_TOKEN( e
));
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" )
373 else if( type
.attribute( M_TOKEN( val
)) == "lin" )
375 else if( type
.attribute( M_TOKEN( val
)) == "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
+ "}";
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
));
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
+ "}";
420 + ( limlowupp
== LimLow
? OUString( " csub {" ) : OUString( " csup {" ))
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" )
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 { }";
451 return "{" + e
+ "} csup {" + OUString( chr
) + "}";
453 return "{" + e
+ "} csub {" + OUString( chr
) + "}";
456 OUString
SmOoxmlImport::handleM()
458 stream
.ensureOpeningTag( M_TOKEN( m
));
460 do // there must be at least one m:mr
462 stream
.ensureOpeningTag( M_TOKEN( mr
));
464 do // there must be at least one m:e
468 row
+= readOMathArgInElement( M_TOKEN( e
));
469 } while( !stream
.atEnd() && stream
.findTag( OPENING( M_TOKEN( e
))));
470 if( !allrows
.isEmpty())
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
));
538 SAL_WARN( "starmath.ooxml", "Unknown m:nary chr \'" << chr
<< "\'" );
542 ret
+= " from {" + sub
+ "}";
544 ret
+= " to {" + sup
+ "}";
545 ret
+= " {" + e
+ "}";
546 stream
.ensureClosingTag( M_TOKEN( nary
));
551 OUString
SmOoxmlImport::handleR()
553 stream
.ensureOpeningTag( M_TOKEN( r
));
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
));
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();
582 stream
.ensureClosingTag( M_TOKEN( t
));
586 stream
.handleUnexpectedTag();
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
));
613 return "sqrt {" + e
+ "}";
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: */