lib

kformulamathmlread.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 2001 Andrea Rizzi <rizzi@kde.org>
00003                   Ulrich Kuettler <ulrich.kuettler@mailbox.tu-dresden.de>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include <iostream>
00022 #include <qstring.h>
00023 #include <qfontmetrics.h>
00024 
00025 #include <klocale.h>
00026 #include <kmessagebox.h>
00027 
00028 //#include <KoUnit.h>
00029 
00030 #include "kformulamathmlread.h"
00031 #include "symboltable.h"
00032 
00033 KFORMULA_NAMESPACE_BEGIN
00034 using namespace std;
00035 
00036 class MathML2KFormulaPrivate
00037 {
00038     friend class MathML2KFormula;
00039 
00040 public:
00041     MathML2KFormulaPrivate( MathML2KFormula* mml_filter,
00042                             const ContextStyle& contextStyle,
00043                             const QDomDocument& formuladoc );
00044     ~MathML2KFormulaPrivate();
00045 
00046     void math( QDomElement element );
00047 
00048     // Token Elements
00049     void mi( QDomElement element, QDomNode docnode );
00050     void mn( QDomElement element, QDomNode docnode );
00051     void mo( QDomElement element, QDomNode docnode );
00052     void mtext( QDomElement element, QDomNode docnode );
00053     void mspace( QDomElement element, QDomNode docnode );
00054     void ms( QDomElement element, QDomNode docnode );
00055     // mglyph not supported
00056 
00057     // General Layout Schemata
00058     void mrow( QDomElement element, QDomNode docnode );
00059     void mfrac( QDomElement element, QDomNode docnode );
00060     void msqrt( QDomElement element, QDomNode docnode );
00061     void mroot( QDomElement element, QDomNode docnode );
00062     void mstyle( QDomElement element, QDomNode docnode );
00063     // merror not supported
00064     // mpadded not supported
00065     // mphantom not supported
00066     void mfenced( QDomElement element, QDomNode docnode );
00067     // menclose not supported
00068 
00069     // Script and Limit Schemata
00070     void msub_msup( QDomElement element, QDomNode docnode );
00071     void msubsup( QDomElement element, QDomNode docnode );
00072     void munder( QDomElement element, QDomNode docnode, bool oasisFormat );
00073     void mover( QDomElement element, QDomNode docnode, bool oasisFormat );
00074     void munderover( QDomElement element, QDomNode docnode, bool oasisFormat );
00075     // mmultiscripts not supported
00076 
00077     // Tables and Matrices
00078     void mtable( QDomElement element, QDomNode docnode );
00079     // not much supported
00080 
00081     // Enlivening Expressions
00082     // maction not supported
00083 
00084 protected:
00085     void createTextElements( QString text, QDomNode docnode );
00086     void createNameSequence( QString text, QDomNode docnode );
00087     double convertToPoint( QString value, bool* ok );
00088     bool isEmbellishedOperator( QDomNode node, QDomElement* mo, bool oasisFormat );
00089     bool isSpaceLike( QDomNode node, bool oasisFormat );
00090 
00091     enum MathVariant {
00092         normal,
00093         bold,
00094         italic,
00095         bold_italic,
00096         double_struck,
00097         bold_fraktur,
00098         script,
00099         bold_script,
00100         fraktur,
00101         sans_serif,
00102         bold_sans_serif,
00103         sans_serif_italic,
00104         sans_serif_bold_italic,
00105         monospace
00106     };
00107 
00108     struct MathStyle {
00109         MathStyle()
00110             : scriptsizemultiplier( 0.71 ),
00111               scriptminsize( 8 ),
00112               veryverythinmathspace( 1.0/18.0 ),
00113               verythinmathspace( 2.0/18.0 ),
00114               thinmathspace( 3.0/18.0 ),
00115               mediummathspace( 4.0/18.0 ),
00116               thickmathspace( 5.0/18.0 ),
00117               verythickmathspace( 6.0/18.0 ),
00118               veryverythickmathspace( 7.0/18.0 ),
00119 
00120               useVariant( false )
00121             {
00122             }
00123 
00124         void styleChange()
00125             {
00126                 kdDebug( DEBUGID ) << "Style Change:"
00127                                    << "\n scriptlevel = " << scriptlevel
00128                                    << "\n displaystyle = " << displaystyle
00129                                    << "\n scriptsizemultiplier = "
00130                                    << scriptsizemultiplier
00131                                    << "\n scriptminsize = " << scriptminsize
00132                                    << endl;
00133             }
00134 
00135         void setStyles( QDomElement element )
00136             {
00137                 if ( !useVariant )
00138                     return;
00139 
00140                 switch ( mathvariant )
00141                 {
00142                 case normal:
00143                     element.setAttribute( "STYLE", "normal" );
00144                     break;
00145                 case bold:
00146                     element.setAttribute( "STYLE", "bold" );
00147                     break;
00148 
00149                 case bold_italic:
00150                     element.setAttribute( "STYLE", "bolditalic" );
00151                     break;
00152                 case italic:
00153                     element.setAttribute( "STYLE", "italic" );
00154                     break;
00155 
00156                 case double_struck:
00157                     element.setAttribute( "FAMILY", "doublestruck" );
00158                     break;
00159 
00160                 case bold_fraktur:
00161                     element.setAttribute( "STYLE", "bold" );
00162                 case fraktur:
00163                     element.setAttribute( "FAMILY", "fraktur" );
00164                     break;
00165 
00166                 case bold_script:
00167                     element.setAttribute( "STYLE", "bold" );
00168                 case script:
00169                     element.setAttribute( "FAMILY", "script" );
00170                     break;
00171 
00172                 case bold_sans_serif:
00173                     element.setAttribute( "STYLE", "bold" );
00174                 case sans_serif:
00175                     element.setAttribute( "FAMILY", "normal" );
00176                     break;
00177                 case sans_serif_bold_italic:
00178                     element.setAttribute( "STYLE", "bolditalic" );
00179                     element.setAttribute( "FAMILY", "normal" );
00180                     break;
00181                 case sans_serif_italic:
00182                     element.setAttribute( "STYLE", "italic" );
00183                     element.setAttribute( "FAMILY", "normal" );
00184                     break;
00185 
00186                 //case monospace:
00187                 default:
00188                     break;
00189                 }
00190             }
00191 
00192         void readStyles( QDomElement mmlElement )
00193             {
00194                 if ( mmlElement.hasAttribute( "mathvariant" ) )
00195                 {
00196                     useVariant = true;
00197 
00198                     if ( mmlElement.attribute( "mathvariant" ) == "normal" )
00199                         mathvariant = normal;
00200                     else if ( mmlElement.attribute( "mathvariant" ) == "bold" )
00201                         mathvariant = bold;
00202                     else if ( mmlElement.attribute( "mathvariant" ) == "italic" )
00203                         mathvariant = italic;
00204                     else if ( mmlElement.attribute( "mathvariant" ) == "bold-italic" )
00205                         mathvariant = bold_italic;
00206                     else if ( mmlElement.attribute( "mathvariant" ) == "double-struck" )
00207                         mathvariant = double_struck;
00208                     else if ( mmlElement.attribute( "mathvariant" ) == "bold-fraktur" )
00209                         mathvariant = bold_fraktur;
00210                     else if ( mmlElement.attribute( "mathvariant" ) == "script" )
00211                         mathvariant = script;
00212                     else if ( mmlElement.attribute( "mathvariant" ) == "bold-script" )
00213                         mathvariant = bold_script;
00214                     else if ( mmlElement.attribute( "mathvariant" ) == "fraktur" )
00215                         mathvariant = fraktur;
00216                     else if ( mmlElement.attribute( "mathvariant" ) == "sans-serif" )
00217                         mathvariant = sans_serif;
00218                     else if ( mmlElement.attribute( "mathvariant" ) == "bold-sans-serif" )
00219                         mathvariant = bold_sans_serif;
00220                     else if ( mmlElement.attribute( "mathvariant" ) == "sans-serif-italic" )
00221                         mathvariant = sans_serif_italic;
00222                     else if ( mmlElement.attribute( "mathvariant" ) == "sans-serif-bold-italic" )
00223                         mathvariant = sans_serif_bold_italic;
00224                     else if ( mmlElement.attribute( "mathvariant" ) == "monospace" )
00225                         mathvariant = monospace;
00226                 }
00227             }
00228 
00229         // Styles, set by <mstyle>     // default
00230 
00231         int scriptlevel;               // inherited
00232         bool displaystyle;             // inherited
00233         double scriptsizemultiplier;   // 0.71
00234         double scriptminsize;          // 8pt
00235         // color
00236         // background
00237         double veryverythinmathspace;  // 1/18em = 0.0555556em
00238         double verythinmathspace;      // 2/18em = 0.111111em
00239         double thinmathspace;          // 3/18em = 0.166667em
00240         double mediummathspace;        // 4/18em = 0.222222em
00241         double thickmathspace;         // 5/18em = 0.277778em
00242         double verythickmathspace;     // 6/18em = 0.333333em
00243         double veryverythickmathspace; // 7/18em = 0.388889em
00244 
00245         // 'Local' styles
00246 
00247         MathVariant mathvariant;
00248         bool useVariant;
00249         //int mathsize;
00250     };
00251 
00252     MathStyle style;
00253     QDomDocument doc;
00254 
00255 private:
00256     const ContextStyle& context;
00257     MathML2KFormula* filter;
00258 };
00259 
00260 MathML2KFormulaPrivate::MathML2KFormulaPrivate( MathML2KFormula* mml_filter, const ContextStyle& cs, const QDomDocument& formuladoc )
00261     : doc( formuladoc ), context( cs ), filter( mml_filter )
00262 {
00263 }
00264 
00265 MathML2KFormulaPrivate::~MathML2KFormulaPrivate()
00266 {
00267 }
00268 
00269 void MathML2KFormulaPrivate::math( QDomElement element )
00270 {
00271     QDomElement formula = doc.createElement( "FORMULA" );
00272     QDomNode n = element.firstChild();
00273 
00274     QString display = element.attribute( "display" );
00275 
00276     if ( display == "block" ) {
00277         style.displaystyle = true;
00278     }
00279     else {
00280         // if display == "inline" (default) or illegal (then use default)
00281         style.displaystyle = false;
00282     }
00283 
00284     style.scriptlevel = 0;
00285 
00286     /*kdDebug( DEBUGID ) << "<math> element:\n displaystyle = "
00287                        << style.displaystyle << "\n scriptlevel = "
00288                        << style.scriptlevel << endl;*/
00289 
00290     while ( !n.isNull() ) {
00291         filter->processElement( n, doc, formula );
00292         n = n.nextSibling();
00293     }
00294 
00295     doc.appendChild( formula );
00296 }
00297 
00298 void MathML2KFormulaPrivate::mi( QDomElement element, QDomNode docnode )
00299 {
00300     MathStyle previousStyle( style );
00301     QString text = element.text().stripWhiteSpace();
00302     if ( text.length() == 1 ) { // Default italic, only when content is one char
00303         style.mathvariant = italic;
00304         style.useVariant = true;
00305         style.readStyles( element );
00306         createTextElements( text, docnode );
00307     } else { // If length is 0 or >1, it should be a text sequence
00308         style.readStyles( element );
00309         createNameSequence( text, docnode );
00310     }
00311     style = previousStyle;
00312 }
00313 
00314 void MathML2KFormulaPrivate::mo( QDomElement element, QDomNode docnode )
00315 {
00316     MathStyle previousStyle( style );
00317     style.readStyles( element );
00318 
00319     QString text = element.text().stripWhiteSpace();
00320     createTextElements( text, docnode );
00321 
00322     style = previousStyle;
00323 }
00324 
00325 void MathML2KFormulaPrivate::mn( QDomElement element, QDomNode docnode )
00326 {
00327     MathStyle previousStyle( style );
00328     style.readStyles( element );
00329 
00330     QString text = element.text().stripWhiteSpace();
00331     createTextElements( text, docnode );
00332 
00333     style = previousStyle;
00334 }
00335 
00336 void MathML2KFormulaPrivate::mtext( QDomElement element, QDomNode docnode )
00337 {
00338     MathStyle previousStyle( style );
00339     style.readStyles( element );
00340 
00341     QDomNode n = element.firstChild();
00342 
00343     while ( !n.isNull() ) {
00344         if ( n.isText() ) {
00345             QString text = n.toText().data().stripWhiteSpace();
00346             createTextElements( text, docnode );
00347         }
00348         else if ( n.isElement() ) {
00349             filter->processElement( n, doc, docnode );
00350         }
00351         else {
00352             kdDebug( DEBUGID ) << "<mtext> child: " << n.nodeName() << endl;
00353         }
00354 
00355         n = n.nextSibling();
00356     }
00357 
00358     style = previousStyle;
00359 }
00360 
00361 void MathML2KFormulaPrivate::ms( QDomElement element, QDomNode docnode )
00362 {
00363     QString lquote = element.attribute( "lquote", "\"" );
00364     QString rquote = element.attribute( "rquote", "\"" );
00365     QString text;
00366 
00367     text = lquote;
00368     text += element.text().stripWhiteSpace();
00369     text += rquote;
00370 
00371     createTextElements( text, docnode );
00372 }
00373 
00374 void MathML2KFormulaPrivate::mspace( QDomElement element, QDomNode docnode )
00375 {
00376     // we support only horizontal space
00377     QString width = element.attribute( "width" );
00378 
00379     QDomElement spaceelement = doc.createElement( "SPACE" );
00380 
00381     // check for namedspace. We don't support much...
00382     if ( width == "veryverythinmathspace" ) {
00383         spaceelement.setAttribute( "WIDTH", "thin" );
00384     }
00385     else if ( width == "verythinmathspace" ) {
00386         spaceelement.setAttribute( "WIDTH", "thin" );
00387     }
00388     else if ( width == "thinmathspace" ) {
00389         spaceelement.setAttribute( "WIDTH", "thin" );
00390     }
00391     else if ( width == "mediummathspace" ) {
00392         spaceelement.setAttribute( "WIDTH", "medium" );
00393     }
00394     else if ( width == "thickmathspace" ) {
00395         spaceelement.setAttribute( "WIDTH", "thick" );
00396     }
00397     else if ( width == "verythickmathspace" ) {
00398         spaceelement.setAttribute( "WIDTH", "thick" );
00399     }
00400     else if ( width == "veryverythickmathspace" ) {
00401         spaceelement.setAttribute( "WIDTH", "quad" );
00402     }
00403 
00404     else {
00405         // units
00406 
00407         double w = 0;
00408         bool ok;
00409 
00410         if ( width.endsWith( "em" ) ) {
00411             // See MathML specification, Appendix H
00412             w = context.getDefaultFont().pointSize();
00413             if ( w == -1 ) {
00414                 QFontMetrics fm( context.getDefaultFont() );
00415                 w = fm.width( 'm' );
00416             }
00417             w = w * width.remove( width.length() - 2, 2 ).toDouble( &ok );
00418             // w in points?
00419         }
00420         else if ( width.endsWith( "px" ) ) {
00421             w = width.remove( width.length() - 2, 2 ).toDouble( &ok );
00422             // w in pixels
00423         }
00424         else if ( width.endsWith( "in" ) ) {
00425             w = width.remove( width.length() - 2, 2 ).toDouble( &ok );
00426             w *= 72; // w in points
00427         }
00428         else if ( width.endsWith( "cm" ) ) {
00429             w = width.remove( width.length() - 2, 2 ).toDouble( &ok );
00430             w *= 1/2.54 * 72; // w in points
00431         }
00432         else if ( width.endsWith( "mm" ) ) {
00433             w = width.remove( width.length() - 2, 2 ).toDouble( &ok );
00434             w *= 1/25.4 * 72; // w in points
00435         }
00436         else if ( width.endsWith( "pt" ) ) {
00437             w = width.remove( width.length() - 2, 2 ).toDouble( &ok );
00438             // w in points
00439         }
00440         else if ( width.endsWith( "pc" ) ) {
00441             w = width.remove( width.length() - 2, 2 ).toDouble( &ok );
00442             w /= 12; // w in points
00443         }
00444         else {
00445             w = width.toDouble( &ok );
00446         }
00447 
00448         if ( !ok )
00449             return;
00450 
00451         if ( w < 20 )
00452             spaceelement.setAttribute( "WIDTH", "thin" );
00453         else if ( w < 40 )
00454             spaceelement.setAttribute( "WIDTH", "medium" );
00455         else if ( w < 80 )
00456             spaceelement.setAttribute( "WIDTH", "thick" );
00457         else
00458             spaceelement.setAttribute( "WIDTH", "quad" );
00459     }
00460 
00461     docnode.appendChild( spaceelement );
00462 }
00463 
00464 void MathML2KFormulaPrivate::mrow( QDomElement element, QDomNode docnode )
00465 {
00466     QDomNode n = element.firstChild();
00467     while ( !n.isNull() ) {
00468         if ( n.isElement () ) {
00469             QDomElement e = n.toElement();
00470             // We do not allow sequence inside sequence
00471             filter->processElement( e, doc, docnode );
00472         }
00473         else {
00474             kdDebug( DEBUGID ) << "<mrow> child: " << n.nodeName() << endl;
00475         }
00476         n = n.nextSibling();
00477     }
00478 }
00479 
00480 void MathML2KFormulaPrivate::mfrac( QDomElement element, QDomNode docnode )
00481 {
00482     QDomNode n = element.firstChild();
00483     QDomElement fraction = doc.createElement( "FRACTION" );
00484 
00485     MathStyle previousStyle( style );
00486     style.displaystyle ? style.displaystyle = false : style.scriptlevel += 1;
00487     style.styleChange();
00488 
00489     int i = 0;
00490     while ( !n.isNull() && i < 2 ) {
00491         if ( n.isElement() ) {
00492             ++i;
00493             if ( i == 1 ) { //first is numerator
00494                 QDomElement numerator =
00495                     doc.createElement( "NUMERATOR" );
00496                 QDomElement sequence = doc.createElement( "SEQUENCE" );
00497                 numerator.appendChild( sequence );
00498                 QDomElement e = n.toElement();
00499                 filter->processElement( e, doc, sequence );
00500                 fraction.appendChild( numerator );
00501 
00502             }
00503             else {
00504                 QDomElement denominator =
00505                     doc.createElement( "DENOMINATOR" );
00506                 QDomElement sequence = doc.createElement( "SEQUENCE" );
00507                 denominator.appendChild( sequence );
00508                 QDomElement e = n.toElement();
00509                 filter->processElement( e, doc, sequence );
00510                 fraction.appendChild( denominator );
00511 
00512             }
00513         }
00514         else {
00515             kdDebug( DEBUGID ) << "<mfrac> child: " << n.nodeName() << endl;
00516         }
00517         n = n.nextSibling();
00518     }
00519 
00520     style = previousStyle;
00521     docnode.appendChild( fraction );
00522 }
00523 
00524 void MathML2KFormulaPrivate::mroot( QDomElement element, QDomNode docnode )
00525 {
00526     QDomNode n = element.firstChild();
00527     int i = 0;
00528     QDomElement root = doc.createElement( "ROOT" );
00529 
00530     while ( !n.isNull() && i < 2 ) {
00531         if ( n.isElement() ) {
00532             ++i;
00533             if ( i == 1 ) { //first is content (base)
00534                 QDomElement content = doc.createElement( "CONTENT" );
00535                 QDomElement sequence = doc.createElement( "SEQUENCE" );
00536                 content.appendChild( sequence );
00537                 QDomElement e = n.toElement();
00538                 filter->processElement( e, doc, sequence );
00539 
00540                 root.appendChild(content);
00541             }
00542             else { // index
00543                 MathStyle previousStyle( style );
00544                 style.scriptlevel += 2;
00545                 style.displaystyle = false;
00546                 style.styleChange();
00547 
00548                 QDomElement index = doc.createElement( "INDEX" );
00549                 QDomElement sequence = doc.createElement( "SEQUENCE" );
00550                 index.appendChild( sequence );
00551                 QDomElement e = n.toElement();
00552                 filter->processElement( e, doc, sequence );
00553                 root.appendChild( index );
00554 
00555                 style = previousStyle;
00556             }
00557         }
00558         else {
00559             kdDebug( DEBUGID ) << "<mroot> child: " << n.nodeName() << endl;
00560         }
00561         n = n.nextSibling();
00562     }
00563     docnode.appendChild( root );
00564 }
00565 
00566 void MathML2KFormulaPrivate::msqrt( QDomElement element, QDomNode docnode )
00567 {
00568     QDomNode n = element.firstChild();
00569     QDomElement root = doc.createElement( "ROOT" );
00570 
00571     QDomElement content = doc.createElement( "CONTENT" );
00572     QDomElement sequence = doc.createElement( "SEQUENCE" );
00573     content.appendChild( sequence );
00574     root.appendChild( content );
00575 
00576     while ( !n.isNull() ) {
00577         if ( n.isElement() ) {
00578             filter->processElement( n.toElement(), doc, sequence );
00579         }
00580         else {
00581             kdDebug( DEBUGID ) << "<msqrt> child: " << n.nodeName() << endl;
00582         }
00583         n = n.nextSibling();
00584     }
00585 
00586     docnode.appendChild( root );
00587 }
00588 
00589 void MathML2KFormulaPrivate::mstyle( QDomElement element, QDomNode docnode )
00590 {
00591     bool ok;
00592 
00593     MathStyle previousStyle( style );
00594     style.readStyles( element );
00595 
00596     if ( element.hasAttribute( "scriptlevel" ) ) {
00597         QString scriptlevel = element.attribute( "scriptlevel" );
00598         if ( scriptlevel.startsWith( "+" ) || scriptlevel.startsWith( "-" ) )
00599             style.scriptlevel += scriptlevel.toInt( &ok );
00600         else
00601             style.scriptlevel = scriptlevel.toInt( &ok );
00602         if ( !ok )
00603             style.scriptlevel = previousStyle.scriptlevel;
00604     }
00605     if ( element.hasAttribute( "displaystyle" ) ) {
00606         QString displaystyle = element.attribute( "displaystyle" );
00607         if ( displaystyle == "true" )
00608             style.displaystyle = true;
00609         else if ( displaystyle == "false" )
00610             style.displaystyle = false;
00611     }
00612     if ( element.hasAttribute( "scriptsizemultiplier" ) ) {
00613         style.scriptsizemultiplier =
00614             element.attribute( "scriptsizemultiplier" ).toDouble( &ok );
00615         if ( !ok )
00616             style.scriptsizemultiplier = previousStyle.scriptsizemultiplier;
00617     }
00618     if ( element.hasAttribute( "scriptminsize" ) ) {
00619         QString scriptminsize = element.attribute( "scriptminsize" );
00620         style.scriptminsize = convertToPoint( scriptminsize, &ok );
00621         if ( !ok )
00622             style.scriptminsize = previousStyle.scriptminsize;
00623     }
00624 
00625     if ( element.hasAttribute( "veryverythinmathspace" ) ) {
00626         QString vvthinmspace = element.attribute( "veryverythinmathspace" );
00627         style.veryverythinmathspace = convertToPoint( vvthinmspace, &ok );
00628         if ( !ok )
00629             style.veryverythinmathspace = previousStyle.veryverythinmathspace;
00630     }
00631     if ( element.hasAttribute( "verythinmathspace" ) ) {
00632         QString vthinmspace = element.attribute( "verythinmathspace" );
00633         style.verythinmathspace = convertToPoint( vthinmspace, &ok );
00634         if ( !ok )
00635             style.verythinmathspace = previousStyle.verythinmathspace;
00636     }
00637     if ( element.hasAttribute( "thinmathspace" ) ) {
00638         QString thinmathspace = element.attribute( "thinmathspace" );
00639         style.thinmathspace = convertToPoint( thinmathspace, &ok );
00640         if ( !ok )
00641             style.thinmathspace = previousStyle.thinmathspace;
00642     }
00643     if ( element.hasAttribute( "mediummathspace" ) ) {
00644         QString mediummathspace = element.attribute( "mediummathspace" );
00645         style.mediummathspace = convertToPoint( mediummathspace, &ok );
00646         if ( !ok )
00647             style.mediummathspace = previousStyle.mediummathspace;
00648     }
00649     if ( element.hasAttribute( "thickmathspace" ) ) {
00650         QString thickmathspace = element.attribute( "thickmathspace" );
00651         style.thickmathspace = convertToPoint( thickmathspace, &ok );
00652         if ( !ok )
00653             style.thickmathspace = previousStyle.thickmathspace;
00654     }
00655     if ( element.hasAttribute( "verythickmathspace" ) ) {
00656         QString vthickmspace = element.attribute( "verythickmathspace" );
00657         style.verythickmathspace = convertToPoint( vthickmspace, &ok );
00658         if ( !ok )
00659             style.verythickmathspace = previousStyle.verythickmathspace;
00660     }
00661     if ( element.hasAttribute( "veryverythickmathspace" ) ) {
00662         QString vvthickmspace = element.attribute( "veryverythickmathspace" );
00663         style.veryverythickmathspace = convertToPoint( vvthickmspace, &ok );
00664         if ( !ok )
00665             style.veryverythickmathspace =
00666                 previousStyle.veryverythickmathspace;
00667     }
00668 
00669     style.styleChange();
00670 
00671     QDomNode n = element.firstChild();
00672     while ( !n.isNull() ) {
00673         filter->processElement( n, doc, docnode );
00674         n = n.nextSibling();
00675     }
00676 
00677     style = previousStyle;
00678 }
00679 
00680 void MathML2KFormulaPrivate::mfenced( QDomElement element, QDomNode docnode )
00681 {
00682     QDomElement bracket = doc.createElement( "BRACKET" );
00683     QString value = element.attribute( "open", "(" );
00684     bracket.setAttribute( "LEFT", QString::number( value.at( 0 ).latin1() ) );
00685     value = element.attribute( "close", ")" );
00686     bracket.setAttribute( "RIGHT", QString::number( value.at( 0 ).latin1() ) );
00687 
00688     QDomElement content = doc.createElement( "CONTENT" );
00689     QDomElement sequence = doc.createElement( "SEQUENCE" );
00690     content.appendChild( sequence );
00691 
00692     QString separators = element.attribute( "separators", "," );
00693 
00694     QDomNode n = element.firstChild();
00695     uint i = 0;
00696     while ( !n.isNull() ) {
00697         if ( n.isElement() ) {
00698             if ( i != 0 && !separators.isEmpty() ) {
00699                 QDomElement textelement = doc.createElement( "TEXT" );
00700                 if ( i > separators.length() )
00701                     i = separators.length();
00702                 textelement.setAttribute( "CHAR", QString( separators.at( i - 1 ) ) );
00703                 //style.setStyles( textelement );
00704                 sequence.appendChild( textelement );
00705             }
00706             ++i;
00707             QDomElement e = n.toElement();
00708             filter->processElement( e, doc, sequence );
00709         }
00710         else {
00711             kdDebug( DEBUGID ) << "<mfenced> child: " << n.nodeName() << endl;
00712         }
00713         n = n.nextSibling();
00714     }
00715     bracket.appendChild( content );
00716     docnode.appendChild( bracket );
00717 }
00718 
00719 void MathML2KFormulaPrivate::mtable( QDomElement element, QDomNode docnode )
00720 {
00721     MathStyle previousStyle( style );
00722     QString displaystyle = element.attribute( "displaystyle", "false" );
00723     if ( displaystyle == "true" ) {
00724         style.displaystyle = true;
00725     }
00726     else {
00727         // false is default and also used for illegal values
00728         style.displaystyle = false;
00729     }
00730     style.styleChange();
00731 
00732     QString subtag;
00733     int rows = 0; int cols = 0;
00734     QDomNode n = element.firstChild();
00735 
00736     while ( !n.isNull() ) {
00737         if ( n.isElement() ) {
00738             QDomElement e = n.toElement();
00739             subtag = e.tagName();
00740             if (subtag == "mtr")
00741             {
00742                 ++rows;
00743 
00744                 /* Determins the number of columns */
00745 
00746                 QDomNode cellnode = e.firstChild();
00747                 int cc = 0;
00748 
00749                 while ( !cellnode.isNull() ) {
00750                     if ( cellnode.isElement() )
00751                         cc++;
00752                     cellnode = cellnode.nextSibling();
00753                 }
00754 
00755                 if ( cc > cols )
00756                     cols = cc;
00757 
00758             }
00759         }
00760         else {
00761             kdDebug( DEBUGID ) << "<mtable> child: " << n.nodeName() << endl;
00762         }
00763         n = n.nextSibling();
00764     }
00765 
00766     /* Again createing elements, I need to know the number
00767        of rows and cols to leave empty spaces */
00768 
00769     n = element.firstChild();
00770     QDomElement matrix = doc.createElement( "MATRIX" );
00771     matrix.setAttribute( "COLUMNS", cols );
00772     matrix.setAttribute( "ROWS", rows );
00773 
00774     while ( !n.isNull() ) {
00775         if ( n.isElement() ) {
00776             QDomElement e = n.toElement();
00777             subtag = e.tagName();
00778             if ( subtag == "mtr" ) {
00779                 QDomNode cellnode = e.firstChild();
00780                 int cc = 0;
00781                 while ( !cellnode.isNull() ) {
00782                     if ( cellnode.isElement() ) {
00783                         ++cc;
00784                         QDomElement cell = doc.createElement( "SEQUENCE" );
00785                         QDomElement cellelement = cellnode.toElement();
00786                         filter->processElement( cellelement, doc, cell );
00787                         matrix.appendChild( cell );
00788                     }
00789                     cellnode = cellnode.nextSibling();
00790                 }
00791 
00792                 /* Add empty elements */
00793                 for(; cc < cols; cc++ ) {
00794                     QDomElement cell = doc.createElement( "SEQUENCE" );
00795                     matrix.appendChild( cell );
00796                 }
00797             }
00798         }
00799         n = n.nextSibling();
00800     }
00801 
00802     style = previousStyle;
00803     docnode.appendChild(matrix);
00804 }
00805 
00806 void MathML2KFormulaPrivate::msub_msup( QDomElement element, QDomNode docnode )
00807 {
00808     QDomNode n = element.firstChild();
00809     int i = 0;
00810     QDomElement root = doc.createElement( "INDEX" );
00811 
00812     while ( !n.isNull() && i < 2 ) {
00813         if ( n.isElement() ) {
00814             ++i;
00815             if ( i == 1 ) { // first is content (base)
00816                 QDomElement content = doc.createElement( "CONTENT" );
00817                 QDomElement sequence = doc.createElement( "SEQUENCE" );
00818                 content.appendChild( sequence );
00819                 QDomElement e = n.toElement();
00820                 filter->processElement( e, doc, sequence );
00821 
00822                 root.appendChild( content );
00823             }
00824             else {
00825                 QDomElement index;
00826                 if ( element.tagName() == "msup" )
00827                     index = doc.createElement( "UPPERRIGHT" );
00828                 else
00829                     index = doc.createElement( "LOWERRIGHT" );
00830 
00831                 MathStyle previousStyle( style );
00832                 style.scriptlevel += 1;
00833                 style.displaystyle = false;
00834                 style.styleChange();
00835 
00836                 QDomElement sequence = doc.createElement( "SEQUENCE" );
00837                 index.appendChild( sequence );
00838                 QDomElement e = n.toElement();
00839                 filter->processElement( e, doc, sequence );
00840                 root.appendChild( index );
00841 
00842                 style = previousStyle;
00843             }
00844         }
00845         else {
00846             kdDebug( DEBUGID ) << "<" << element.tagName() << "> child: "
00847                                << n.nodeName() << endl;
00848         }
00849         n = n.nextSibling();
00850     }
00851     docnode.appendChild( root );
00852 }
00853 
00854 void MathML2KFormulaPrivate::munder( QDomElement element, QDomNode docnode, bool oasisFormat )
00855 {
00856     bool accentunder;
00857 
00858     QString au = element.attribute( "accentunder" );
00859     if ( au == "true" )
00860         accentunder = true;
00861     else if ( au == "false" )
00862         accentunder = false;
00863     else {
00864         // use default
00865 
00866         QDomElement mo;
00867         // is underscript an embellished operator?
00868         if ( isEmbellishedOperator( element.childNodes().item( 1 ), &mo, oasisFormat ) ) {
00869             if ( mo.attribute( "accent" ) == "true" )
00870                 accentunder = true;
00871             else
00872                 accentunder = false;
00873         }
00874         else
00875             accentunder = false;
00876     }
00877 
00878     QDomNode n = element.firstChild();
00879     int i = 0;
00880     QDomElement root = doc.createElement( "INDEX" );
00881 
00882     while ( !n.isNull() && i < 2 ) {
00883         if ( n.isElement() ) {
00884             ++i;
00885             if ( i == 1 ) { // first is content (base)
00886                 QDomElement content = doc.createElement( "CONTENT" );
00887                 QDomElement sequence = doc.createElement( "SEQUENCE" );
00888                 content.appendChild( sequence );
00889                 QDomElement e = n.toElement();
00890                 filter->processElement( e, doc, sequence );
00891 
00892                 root.appendChild( content );
00893             }
00894             else { // underscript
00895                 MathStyle previousStyle( style );
00896                 style.displaystyle = false;
00897                 if ( !accentunder ) {
00898                     style.scriptlevel += 1;
00899                     style.styleChange();
00900                 }
00901 
00902                 QDomElement mo; QDomElement index;
00903                 if ( isEmbellishedOperator( n.previousSibling(), &mo, oasisFormat ) &&
00904                      !previousStyle.displaystyle &&
00905                      mo.attribute( "movablelimits" ) == "true" )
00906                 {
00907                     index = doc.createElement( "LOWERRIGHT" );
00908                 }
00909                 else {
00910                     index = doc.createElement( "LOWERMIDDLE" );
00911                 }
00912 
00913                 QDomElement sequence = doc.createElement( "SEQUENCE" );
00914                 index.appendChild( sequence );
00915                 QDomElement e = n.toElement();
00916                 filter->processElement( e, doc, sequence );
00917                 root.appendChild( index );
00918 
00919                 style = previousStyle;
00920             }
00921         }
00922         else {
00923             kdDebug( DEBUGID ) << "<" << element.tagName() << "> child: "
00924                                << n.nodeName() << endl;
00925         }
00926         n = n.nextSibling();
00927     }
00928 
00929     docnode.appendChild( root );
00930 }
00931 
00932 void MathML2KFormulaPrivate::mover( QDomElement element, QDomNode docnode, bool oasisFormat )
00933 {
00934     bool accent;
00935 
00936     QString ac = element.attribute( "accent" );
00937     if ( ac == "true" )
00938         accent = true;
00939     else if ( ac == "false" )
00940         accent = false;
00941     else {
00942         // use default
00943 
00944         QDomElement mo;
00945         // is overscript an embellished operator?
00946         if ( isEmbellishedOperator( element.childNodes().item( 1 ), &mo, oasisFormat ) ) {
00947             if ( mo.attribute( "accent" ) == "true" )
00948                 accent = true;
00949             else
00950                 accent = false;
00951         }
00952         else
00953             accent = false;
00954     }
00955 
00956     QDomNode n = element.firstChild();
00957     int i = 0;
00958     QDomElement root = doc.createElement( "INDEX" );
00959 
00960     while ( !n.isNull() && i < 2 ) {
00961         if ( n.isElement() ) {
00962             ++i;
00963             if ( i == 1 ) { // first is content (base)
00964                 QDomElement content = doc.createElement( "CONTENT" );
00965                 QDomElement sequence = doc.createElement( "SEQUENCE" );
00966                 content.appendChild( sequence );
00967                 QDomElement e = n.toElement();
00968                 filter->processElement( e, doc, sequence );
00969 
00970                 root.appendChild( content );
00971             }
00972             else { // overscript
00973                 MathStyle previousStyle( style );
00974                 style.displaystyle = false;
00975                 if ( !accent ) {
00976                     style.scriptlevel += 1;
00977                     style.styleChange();
00978                 }
00979 
00980                 QDomElement mo; QDomElement index;
00981                 if ( isEmbellishedOperator( n.previousSibling(), &mo, oasisFormat ) &&
00982                      !previousStyle.displaystyle &&
00983                      mo.attribute( "movablelimits" ) == "true" )
00984                 {
00985                     index = doc.createElement( "UPPERRIGHT" );
00986                 }
00987                 else {
00988                     index = doc.createElement( "UPPERMIDDLE" );
00989                 }
00990 
00991                 QDomElement sequence = doc.createElement( "SEQUENCE" );
00992                 index.appendChild( sequence );
00993                 QDomElement e = n.toElement();
00994                 filter->processElement( e, doc, sequence );
00995                 root.appendChild( index );
00996 
00997                 style = previousStyle;
00998             }
00999         }
01000         else {
01001             kdDebug( DEBUGID ) << "<" << element.tagName() << "> child: "
01002                                << n.nodeName() << endl;
01003         }
01004         n = n.nextSibling();
01005     }
01006 
01007     docnode.appendChild( root );
01008 }
01009 
01010 void MathML2KFormulaPrivate::munderover( QDomElement element, QDomNode docnode, bool oasisFormat )
01011 {
01012     bool accent;
01013     bool accentunder;
01014 
01015     QString value = element.attribute( "accentunder" );
01016     if ( value == "true" )
01017         accentunder = true;
01018     else if ( value == "false" )
01019         accentunder = false;
01020     else {
01021         // use default
01022 
01023         QDomElement mo;
01024         // is underscript an embellished operator?
01025         if ( isEmbellishedOperator( element.childNodes().item( 1 ), &mo, oasisFormat ) ) {
01026             if ( mo.attribute( "accent" ) == "true" )
01027                 accentunder = true;
01028             else
01029                 accentunder = false;
01030         }
01031         else
01032             accentunder = false;
01033     }
01034     value = element.attribute( "accent" );
01035     if ( value == "true" )
01036         accent = true;
01037     else if ( value == "false" )
01038         accent = false;
01039     else {
01040         // use default
01041 
01042         QDomElement mo;
01043         // is overscript an embellished operator?
01044         if ( isEmbellishedOperator( element.childNodes().item( 2 ), &mo,oasisFormat ) ) {
01045             kdDebug( DEBUGID ) << "embellished operator" << endl;
01046             if ( mo.attribute( "accent" ) == "true" )
01047                 accent = true;
01048             else
01049                 accent = false;
01050         }
01051         else
01052             accent = false;
01053     }
01054     kdDebug( DEBUGID ) << "munderover:\n accentunder = " << accentunder
01055                        << "\n accent = " << accent << endl;
01056 
01057     QDomNode n = element.firstChild();
01058     int i = 0;
01059     QDomElement root = doc.createElement( "INDEX" );
01060 
01061     while ( !n.isNull() && i < 3 ) {
01062         if ( n.isElement() ) {
01063             ++i;
01064             if ( i == 1 ) { // base
01065                 QDomElement content = doc.createElement( "CONTENT" );
01066                 QDomElement sequence = doc.createElement( "SEQUENCE" );
01067                 content.appendChild( sequence );
01068                 QDomElement e = n.toElement();
01069                 filter->processElement( e, doc, sequence );
01070 
01071                 root.appendChild( content );
01072             }
01073             else if ( i == 2 ) { // underscript
01074                 MathStyle previousStyle( style );
01075                 style.displaystyle = false;
01076                 if ( !accentunder ) {
01077                     style.scriptlevel += 1;
01078                     style.styleChange();
01079                 }
01080 
01081                 QDomElement mo; QDomElement index;
01082                 // is the base an embellished operator?
01083                 if ( isEmbellishedOperator( element.firstChild(), &mo, oasisFormat ) &&
01084                      !previousStyle.displaystyle &&
01085                      mo.attribute( "movablelimits" ) == "true" )
01086                 {
01087                     index = doc.createElement( "LOWERRIGHT" );
01088                 }
01089                 else {
01090                     index = doc.createElement( "LOWERMIDDLE" );
01091                 }
01092 
01093                 QDomElement sequence = doc.createElement( "SEQUENCE" );
01094                 index.appendChild( sequence );
01095                 QDomElement e = n.toElement();
01096                 filter->processElement( e, doc, sequence );
01097                 root.appendChild( index );
01098 
01099                 style = previousStyle;
01100             }
01101             else { // overscript
01102                 MathStyle previousStyle( style );
01103                 style.displaystyle = false;
01104                 if ( !accent ) {
01105                     style.scriptlevel += 1;
01106                     style.styleChange();
01107                 }
01108 
01109                 QDomElement mo; QDomElement index;
01110                 if ( isEmbellishedOperator( element.firstChild(), &mo, oasisFormat ) &&
01111                      !previousStyle.displaystyle &&
01112                      mo.attribute( "movablelimits" ) == "true" )
01113                 {
01114                     index = doc.createElement( "UPPERRIGHT" );
01115                 }
01116                 else {
01117                     index = doc.createElement( "UPPERMIDDLE" );
01118                 }
01119 
01120                 QDomElement sequence = doc.createElement( "SEQUENCE" );
01121                 index.appendChild( sequence );
01122                 QDomElement e = n.toElement();
01123                 filter->processElement( e, doc, sequence );
01124                 root.appendChild( index );
01125 
01126                 style = previousStyle;
01127             }
01128         }
01129         else {
01130             kdDebug( DEBUGID ) << "<" << element.tagName() << "> child: "
01131                                << n.nodeName() << endl;
01132         }
01133         n = n.nextSibling();
01134     }
01135 
01136     docnode.appendChild( root );
01137 }
01138 
01139 void MathML2KFormulaPrivate::msubsup( QDomElement element, QDomNode docnode )
01140 {
01141     QDomNode n = element.firstChild();
01142     int i = 0;
01143     QDomElement root = doc.createElement("INDEX");
01144     MathStyle previousStyle( style );
01145 
01146     while ( !n.isNull() && i < 2 ) {
01147         if ( n.isElement() ) {
01148             ++i;
01149             if ( i == 1 ) { // base
01150                 QDomElement content = doc.createElement( "CONTENT" );
01151                 QDomElement sequence = doc.createElement( "SEQUENCE" );
01152                 content.appendChild( sequence );
01153                 QDomElement e = n.toElement();
01154                 filter->processElement( e, doc, sequence );
01155 
01156                 root.appendChild( content );
01157             }
01158             else if ( i == 2 ) { // subscript
01159                 style.scriptlevel += 1;
01160                 style.displaystyle = false;
01161                 style.styleChange();
01162 
01163                 QDomElement index;
01164                 index = doc.createElement( "LOWERRIGHT" );
01165 
01166                 QDomElement sequence = doc.createElement( "SEQUENCE" );
01167                 index.appendChild( sequence );
01168                 QDomElement e = n.toElement();
01169                 filter->processElement( e, doc, sequence );
01170                 root.appendChild( index );
01171             }
01172             else { // superscript
01173                 QDomElement index;
01174                 index = doc.createElement( "UPPERRIGHT" );
01175 
01176                 QDomElement sequence = doc.createElement( "SEQUENCE" );
01177                 index.appendChild( sequence );
01178                 QDomElement e = n.toElement();
01179                 filter->processElement( e, doc, sequence );
01180                 root.appendChild( index );
01181 
01182                 style = previousStyle;
01183 
01184             }
01185         }
01186         else {
01187             kdDebug( DEBUGID ) << "<msubsup> child: " << n.nodeName() << endl;
01188         }
01189         n = n.nextSibling();
01190     }
01191     docnode.appendChild( root );
01192 }
01193 
01194 void MathML2KFormulaPrivate::createTextElements( QString text, QDomNode docnode )
01195 {
01196     for ( uint i = 0; i < text.length(); ++i ) {
01197         QDomElement textelement = doc.createElement( "TEXT" );
01198         textelement.setAttribute( "CHAR", QString( text.at( i ) ) );
01199         style.setStyles( textelement );
01200         if ( context.symbolTable().inTable( text.at( i ) ) ) {
01201             // The element is a symbol.
01202             textelement.setAttribute( "SYMBOL", "3" );
01203         }
01204         docnode.appendChild( textelement );
01205     }
01206 }
01207 
01208 void MathML2KFormulaPrivate::createNameSequence( QString text, QDomNode docnode )
01209 {
01210     QDomElement namesequence = doc.createElement( "NAMESEQUENCE" );
01211     for ( uint i = 0; i < text.length(); ++i ) {
01212         QDomElement textelement = doc.createElement( "TEXT" );
01213         textelement.setAttribute( "CHAR", QString( text.at( i ) ) );
01214         style.setStyles( textelement );
01215         if ( context.symbolTable().inTable( text.at( i ) ) ) {
01216             // The element is a symbol.
01217             textelement.setAttribute( "SYMBOL", "3" );
01218         }
01219         namesequence.appendChild( textelement );
01220     }
01221     docnode.appendChild( namesequence );
01222 }
01223 
01224 double MathML2KFormulaPrivate::convertToPoint( QString value, bool* ok )
01225 {
01226     double pt = 0;
01227 
01228     if ( value.endsWith( "em" ) ) {
01229         // See MathML specification, Appendix H
01230         pt = context.getDefaultFont().pointSize();
01231         if ( pt == -1 ) {
01232             QFontMetrics fm( context.getDefaultFont() );
01233             pt = fm.width( 'M' );
01234             // PIXELS!
01235         }
01236         pt = pt * value.remove( value.length() - 2, 2 ).toDouble( ok );
01237     }
01238     else if ( value.endsWith( "ex" ) ) {
01239         QFontMetrics fm( context.getDefaultFont() );
01240         pt = fm.height();
01241         // PIXELS, and totally wrong!
01242     }
01243     else if ( value.endsWith( "px" ) ) {
01244         pt = value.remove( value.length() - 2, 2 ).toDouble( ok );
01245         // PIXELS!
01246     }
01247     else if ( value.endsWith( "in" ) ) {
01248         pt = value.remove( value.length() - 2, 2 ).toDouble( ok );
01249         pt *= 72;
01250     }
01251     else if ( value.endsWith( "cm" ) ) {
01252         pt = value.remove( value.length() - 2, 2 ).toDouble( ok );
01253         pt *= 1/2.54 * 72;
01254     }
01255     else if ( value.endsWith( "mm" ) ) {
01256         pt = value.remove( value.length() - 2, 2 ).toDouble( ok );
01257         pt *= 1/25.4 * 72;
01258     }
01259     else if ( value.endsWith( "pt" ) ) {
01260         pt = value.remove( value.length() - 2, 2 ).toDouble( ok );
01261     }
01262     else if ( value.endsWith( "pc" ) ) {
01263         pt = value.remove( value.length() - 2, 2 ).toDouble( ok );
01264         pt /= 12;
01265     }
01266     else {
01267         pt = value.toDouble( ok );
01268     }
01269 
01270     return pt;
01271 }
01272 
01273 bool MathML2KFormulaPrivate::isEmbellishedOperator( QDomNode node,
01274                                                     QDomElement* mo, bool oasisFormat )
01275 {
01276     // See MathML 2.0 specification: 3.2.5.7
01277 
01278     if ( !node.isElement() )
01279         return false;
01280 
01281     QDomElement element = node.toElement();
01282     QString tag = element.tagName();
01283 
01284     if ( tag == "mo" )
01285     {
01286         *mo = element;
01287         return true;
01288     }
01289     if ( tag == "msub" || tag == "msup" || tag == "msubsup" ||
01290          tag == "munder" || tag == "mover" || tag == "munderover" ||
01291          tag == "mmultiscripts" || tag == "mfrac" || tag == "semantics" )
01292     {
01293         return isEmbellishedOperator( element.firstChild(), mo,oasisFormat );
01294     }
01295     if ( tag == "maction" )
01296     {
01297         return false; // not supported
01298     }
01299     if ( tag == "mrow"  || tag == "mstyle" || tag == "mphantom" || tag == "mpadded" ) {
01300         QDomNode n = element.firstChild();
01301         int i = 0;
01302 
01303         while ( !n.isNull() ) {
01304             if ( isEmbellishedOperator( n, mo,oasisFormat ) ) {
01305                 if ( ++i > 1 ) // one (only one) embellished operator
01306                     return false;
01307             }
01308             else if ( !isSpaceLike( n, oasisFormat ) ) { // zero or more space-like elements
01309                 return false;
01310             }
01311             n = n.nextSibling();
01312         }
01313         return ( i == 1 );
01314     }
01315     return false;
01316 }
01317 
01318 bool MathML2KFormulaPrivate::isSpaceLike( QDomNode node, bool oasisFormat )
01319 {
01320     // See MathML 2.0 specification: 3.2.7.3
01321 
01322     if ( !node.isElement() )
01323         return false;
01324 
01325     QDomElement element = node.toElement();
01326     QString tag = element.tagName();
01327 
01328     if ( tag == "mtext" || tag == "mspace" ||
01329          tag == "maligngroup" || tag == "malignmark" ) {
01330         return true;
01331     }
01332     if ( tag == "mstyle" || tag == "mphantom" || tag == "mpadded" || tag == "mrow" ) {
01333         QDomNode n = element.firstChild();
01334         while ( !n.isNull() ) {
01335             if ( isSpaceLike( n,oasisFormat ) )
01336                 n = n.nextSibling();
01337             else
01338                 return false;
01339         }
01340         return true;
01341     }
01342     if ( tag == "maction" ) {
01343         return false; // not supported
01344     }
01345 
01346     return false;
01347 }
01348 
01349 
01350 MathML2KFormula::MathML2KFormula( const QDomDocument& mmldoc, const ContextStyle &contextStyle, bool _oasisFormat )
01351     : m_error( false ), oasisFormat( _oasisFormat ), context( contextStyle )
01352 {
01353     orig_element = mmldoc.documentElement();
01354     done = false;
01355 }
01356 
01357 MathML2KFormula::MathML2KFormula( const QDomElement& mmlelm, const ContextStyle &contextStyle, bool _oasisFormat )
01358     : m_error( false ), orig_element( mmlelm ), oasisFormat( _oasisFormat ), context( contextStyle )
01359 {
01360     done = false;
01361 }
01362 
01363 QDomDocument MathML2KFormula::getKFormulaDom()
01364 {
01365     return formuladoc;
01366 }
01367 
01368 
01369 
01370 void MathML2KFormula::startConversion()
01371 {
01372     //TODO:let it be async
01373     //kdDebug() << origdoc.toString() << endl;
01374     done = false;
01375     formuladoc = QDomDocument( "KFORMULA" );
01376     impl = new MathML2KFormulaPrivate( this, context, formuladoc );
01377     if ( orig_element.tagName() == "math" ) {
01378         impl->math( orig_element );
01379         m_error = false;
01380     }
01381     else {
01382         kdError() << "Not a MathML document!" << endl;
01383         KMessageBox::error( 0, i18n( "The document does not seem to be MathML." ), i18n( "MathML Import Error" ) );
01384         m_error = true;
01385     }
01386     done = true;
01387 }
01388 
01389 bool MathML2KFormula::processElement( QDomNode node, QDomDocument& doc, QDomNode docnode )
01390 {
01391 
01392     //QDomElement *element;
01393     Type type = UNKNOWN;
01394 
01395     if ( node.isElement() ) {
01396     QDomElement element = node.toElement();
01397     QString tag = element.tagName();
01398 
01399     if ( tag == "mi" ) {
01400         type = TOKEN;
01401             impl->mi( element, docnode );
01402     }
01403     else if ( tag == "mo" ) {
01404         type = TOKEN;
01405             impl->mo( element, docnode );
01406     }
01407     else if ( tag == "mn" ) {
01408         type = TOKEN;
01409             impl->mn( element, docnode );
01410     }
01411     else if ( tag == "mtext" ) {
01412         type = TOKEN;
01413             impl->mtext( element, docnode );
01414     }
01415     else if ( tag == "ms" ) {
01416         type = TOKEN;
01417             impl->ms( element, docnode );
01418     }
01419     else if ( tag == "mspace" ) {
01420         type = TOKEN;
01421         impl->mspace( element, docnode );
01422     }
01423     else if ( tag == "mrow" ) {
01424         type = LAYOUT;
01425             impl->mrow( element, docnode );
01426     }
01427     else if ( tag == "mfrac" ) {
01428         type = LAYOUT;
01429             impl->mfrac( element, docnode );
01430     }
01431     else if ( tag == "mroot" ) {
01432         type = LAYOUT;
01433         impl->mroot( element, docnode );
01434     }
01435     else if ( tag == "msqrt" ) {
01436         type = LAYOUT;
01437         impl->msqrt( element, docnode );
01438     }
01439     else if ( tag == "mstyle" ) {
01440         type = LAYOUT;
01441         impl->mstyle( element, docnode );
01442     }
01443     else if ( tag == "mfenced" ) {
01444         type = LAYOUT;
01445         impl->mfenced( element, docnode );
01446     }
01447     else if ( tag == "mtable" ) {
01448         type = TABLE;
01449         impl->mtable( element, docnode );
01450     }
01451     else if ( tag == "msub"  || tag == "msup" ) {
01452         type = SCRIPT;
01453         impl->msub_msup( element, docnode );
01454     }
01455     else if ( tag == "munder" ) {
01456         type = SCRIPT;
01457         impl->munder( element, docnode,oasisFormat );
01458     }
01459     else if ( tag == "mover" ) {
01460         type = SCRIPT;
01461         impl->mover( element, docnode,oasisFormat );
01462     }
01463     else if ( tag == "munderover" ) {
01464         type = SCRIPT;
01465         impl->munderover( element, docnode, oasisFormat );
01466     }
01467     else if ( tag == "msubsup" ) {
01468         type = SCRIPT;
01469         impl->msubsup( element, docnode );
01470     }
01471 
01472     // content markup (not yet complete)
01473     else if ( tag == "apply" ) {
01474         type = CONTENT;
01475         QDomNode n = element.firstChild();
01476         QDomElement op = n.toElement();
01477         uint count = element.childNodes().count();
01478         //adding explicit brackets to replace "apply"s implicit ones
01479         QDomElement brackets = doc.createElement("BRACKET");
01480         brackets.setAttribute("RIGHT", "41");
01481         brackets.setAttribute("LEFT", "40");
01482         QDomElement content = doc.createElement("CONTENT");
01483         brackets.appendChild(content);
01484         QDomElement base = doc.createElement("SEQUENCE");
01485         content.appendChild(base);
01486         docnode.appendChild(brackets);
01487         //Arithmetic, Algebra and Logic operators status
01488         // quotient X
01489         // factorial    O
01490         // divide   O
01491         // max, min X
01492         // minus    O
01493         // plus     O
01494         // power    O
01495         // rem      X
01496         // times    O
01497         // root     X
01498         // gcd      X
01499         // and      O
01500         // or       O
01501         // xor      O
01502         // not      O
01503         // implies  O
01504         // forall   X
01505         // exists   X
01506         // abs      O
01507         // conjugate    X
01508         // arg      X
01509         // real     X
01510         // imaginary    X
01511         // lcm      X
01512         // floor    X
01513         // ceiling  X
01514 
01515             // n-ary
01516             if ( op.tagName() == "plus" || op.tagName() == "times" ||
01517                  op.tagName() == "and" || op.tagName() == "or" ||
01518                  op.tagName() == "xor" ) {
01519 
01520                 n = n.nextSibling();
01521                 bool first = true;
01522 
01523                 while ( !n.isNull() ) {
01524                     if ( n.isElement() ) {
01525                         if ( !first ) {
01526                             QDomElement text = doc.createElement( "TEXT" );
01527                             QString value;
01528 
01529                             if ( op.tagName() == "plus" )
01530                                 value = "+";
01531                             else if ( op.tagName() == "times" )
01532                                 value = "*";
01533                             else if ( op.tagName() == "and" )
01534                                 value = "&";
01535                             else if ( op.tagName() == "or" )
01536                                 value = "|";
01537                             else if ( op.tagName() == "xor" )
01538                                 value = "^"; // ???
01539 
01540                             text.setAttribute( "CHAR", value ); //switch to createTextElements?
01541                             base.appendChild( text );
01542                         }
01543                         first = false;
01544                         QDomElement e = n.toElement();
01545                         processElement( e, doc, base );
01546                     }
01547                     n = n.nextSibling();
01548                 }
01549             }
01550 
01551             else if ( op.tagName() == "factorial" ) {
01552                 QDomElement e = n.nextSibling().toElement();
01553                 processElement( e, doc, docnode );
01554                 impl->createTextElements( "!", base );
01555             }
01556             else if ( op.tagName() == "minus" ) {
01557                 n = n.nextSibling();
01558                 if ( count == 2 ) { // unary
01559                     impl->createTextElements( "-", base );
01560                     QDomElement e = n.toElement();
01561                     processElement( e, doc, base );
01562                 }
01563                 else if ( count == 3 ) { // binary
01564                     QDomElement e = n.toElement();
01565                     processElement( e, doc, base );
01566                     impl->createTextElements( "-", base );
01567                     n = n.nextSibling();
01568                     e = n.toElement();
01569                     processElement( e, doc, base );
01570                 }
01571             }
01572 
01573         else if ( op.tagName() == "divide" && count == 3 ) {
01574             n = n.nextSibling();
01575                     QDomElement e = n.toElement();
01576             processElement( e, doc, base );
01577             impl->createTextElements("/", base);
01578             n = n.nextSibling();
01579             e = n.toElement();
01580             processElement( e, doc, base );
01581         }
01582         else if ( op.tagName() == "power" && count == 3 ) {
01583             //code duplication of msub_sup(), but I can't find a way to cleanly call it
01584             n = n.nextSibling();
01585                     QDomElement e = n.toElement();
01586             QDomElement index = doc.createElement("INDEX");
01587             base.appendChild(index);
01588             QDomElement content = doc.createElement("CONTENT");
01589             index.appendChild(content);
01590             QDomElement sequence = doc.createElement("SEQUENCE");
01591             content.appendChild(sequence);
01592             processElement(e, doc, sequence);
01593             QDomElement upper = doc.createElement("UPPERRIGHT");
01594             index.appendChild(upper);
01595             sequence = doc.createElement("SEQUENCE");
01596             upper.appendChild(sequence);
01597             n = n.nextSibling();
01598                     e = n.toElement();
01599             processElement(e, doc, sequence);
01600         }
01601         else if ( op.tagName() == "abs" && count == 2) {
01602             n = n.nextSibling();
01603                     QDomElement e = n.toElement();
01604             QDomElement bracket = doc.createElement("BRACKET");
01605             bracket.setAttribute("RIGHT", "257");
01606             bracket.setAttribute("LEFT", "256");
01607             base.appendChild(bracket);
01608             QDomElement content = doc.createElement("CONTENT");
01609             bracket.appendChild(content);
01610             QDomElement sequence = doc.createElement("SEQUENCE");
01611             content.appendChild(sequence);
01612             processElement(e, doc, sequence);
01613         }
01614         else if ( op.tagName() == "not" && count == 2) {
01615             n = n.nextSibling();
01616                     QDomElement e = n.toElement();
01617             impl->createTextElements(QString(QChar(0xAC)), base);
01618             processElement(e, doc, base);
01619         }
01620         else if ( op.tagName() == "implies" && count == 3 ) {
01621             n = n.nextSibling();
01622                     QDomElement e = n.toElement();
01623             processElement( e, doc, base );
01624             impl->createTextElements(QString(QChar(0x21D2)), base);
01625             n = n.nextSibling();
01626             e = n.toElement();
01627             processElement( e, doc, base );
01628         }
01629             // many, many more...
01630 
01631         }
01632 
01633         else if ( tag == "cn" ) {
01634             type = CONTENT;
01635             QString type = element.attribute( "type", "real" );
01636 
01637             if ( type == "real" || type == "constant" ) {
01638                 impl->createTextElements( element.text().stripWhiteSpace(),
01639                                           docnode );
01640             }
01641             else if ( type == "integer" ) {
01642                 QString base = element.attribute( "base" );
01643                 if ( !base ) {
01644                     impl->createTextElements( element.text().stripWhiteSpace(),
01645                                               docnode );
01646                 }
01647                 else {
01648                     QDomElement index = doc.createElement( "INDEX" );
01649                     QDomElement content = doc.createElement( "CONTENT" );
01650                     QDomElement sequence = doc.createElement( "SEQUENCE" );
01651                     impl->createTextElements( element.text().stripWhiteSpace(),
01652                                               sequence );
01653                     content.appendChild( sequence );
01654                     index.appendChild( content );
01655 
01656                     QDomElement lowerright = doc.createElement( "LOWERRIGHT" );
01657                     sequence = doc.createElement( "SEQUENCE" );
01658 
01659                     impl->createTextElements( base, sequence );
01660 
01661                     lowerright.appendChild( sequence );
01662                     index.appendChild( lowerright );
01663 
01664                     docnode.appendChild( index );
01665                 }
01666             }
01667             else if ( type == "rational" ) {
01668                 QDomNode n = element.firstChild();
01669                 impl->createTextElements( n.toText().data().stripWhiteSpace(),
01670                                           docnode );
01671 
01672                 n = n.nextSibling(); // <sep/>
01673                 impl->createTextElements( "/", docnode );
01674 
01675                 n = n.nextSibling();
01676                 impl->createTextElements( n.toText().data().stripWhiteSpace(),
01677                                           docnode );
01678             }
01679             else if ( type == "complex-cartesian" ) {
01680                 QDomNode n = element.firstChild();
01681                 impl->createTextElements( n.toText().data().stripWhiteSpace(),
01682                                           docnode );
01683 
01684                 n = n.nextSibling(); // <sep/>
01685                 impl->createTextElements( "+", docnode );
01686 
01687                 n = n.nextSibling();
01688                 impl->createTextElements( n.toText().data().stripWhiteSpace(),
01689                                           docnode );
01690 
01691                 impl->createTextElements( "i", docnode );
01692             }
01693 
01694             else if ( type == "complex-polar" ) {
01695                 QDomNode n = element.firstChild();
01696                 impl->createTextElements( n.toText().data().stripWhiteSpace(),
01697                                           docnode );
01698 
01699                 n = n.nextSibling(); // <sep/>
01700                 QDomElement index = doc.createElement( "INDEX" );
01701                 QDomElement content = doc.createElement( "CONTENT" );
01702                 QDomElement sequence = doc.createElement( "SEQUENCE" );
01703                 QDomElement textelement = doc.createElement( "TEXT" );
01704                 textelement.setAttribute( "CHAR", "e" );
01705                 sequence.appendChild( textelement );
01706                 content.appendChild( sequence );
01707                 index.appendChild( content );
01708 
01709                 QDomElement upperright = doc.createElement( "UPPERRIGHT" );
01710                 sequence = doc.createElement( "SEQUENCE" );
01711                 textelement = doc.createElement( "TEXT" );
01712                 textelement.setAttribute( "CHAR", "i" );
01713                 sequence.appendChild( textelement );
01714 
01715                 n = n.nextSibling();
01716                 impl->createTextElements( n.toText().data().stripWhiteSpace(),
01717                                           sequence );
01718 
01719                 upperright.appendChild( sequence );
01720                 index.appendChild( upperright );
01721 
01722                 docnode.appendChild( index );
01723             }
01724         }
01725 
01726         else if ( tag == "ci" ) {
01727             type = CONTENT;
01728             QDomNode n = element.firstChild();
01729 
01730             if ( n.isText() ) {
01731                 impl->createTextElements( n.toText().data().stripWhiteSpace(),
01732                                           docnode );
01733             }
01734             else if ( n.isElement() ) {
01735                 QDomElement e = n.toElement();
01736                 processElement( e, doc, docnode );
01737             }
01738             else if ( n.isEntityReference() ) {
01739                 kdDebug( DEBUGID ) << "isEntityReference: "
01740                                    << n.toEntityReference().nodeName().latin1()
01741                                    << endl;
01742             }
01743             else
01744                 kdDebug( DEBUGID ) << "ci: " << n.nodeName().latin1() << endl;
01745         }
01746 
01747         else if ( tag == "list" ) {
01748             type = CONTENT;
01749             QDomNode n = element.firstChild();
01750 
01751             QDomElement bracket = doc.createElement( "BRACKET" );
01752             bracket.setAttribute( "LEFT", 91 );  // [
01753             bracket.setAttribute( "RIGHT", 93 ); // ]
01754             QDomElement content = doc.createElement( "CONTENT" );
01755             QDomElement sequence = doc.createElement( "SEQUENCE" );
01756 
01757             bool first = true;
01758 
01759             while ( !n.isNull() ) {
01760                 if ( n.isElement() ) {
01761                     if ( !first ) {
01762                         QDomElement textelement = doc.createElement( "TEXT" );
01763                         textelement.setAttribute( "CHAR", "," );
01764                         sequence.appendChild( textelement );
01765                     }
01766                     first = false;
01767                     QDomElement e = n.toElement();
01768                     processElement( e, doc, sequence );
01769                 }
01770                 n = n.nextSibling();
01771             }
01772 
01773             content.appendChild( sequence );
01774             bracket.appendChild( content );
01775             docnode.appendChild( bracket );
01776         }
01777     }
01778 
01779     if ( type == UNKNOWN && node.nodeType() != QDomNode::AttributeNode ) {
01780         kdDebug() << "Not an element: " << node.nodeName() << endl;
01781     QDomNode n = node.firstChild();
01782     while ( !n.isNull() ) {
01783         processElement( n, doc, docnode );
01784         n = n.nextSibling();
01785     }
01786     }
01787 
01788     return true;
01789 }
01790 
01791 KFORMULA_NAMESPACE_END
01792 
01793 using namespace KFormula;
01794 #include "kformulamathmlread.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys