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