lib Library API Documentation

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