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