kformula/flake

AttributeManager.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE project
00002    Copyright (C) 2006 Martin Pfeiffer <hubipete@gmx.net>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017    Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include "AttributeManager.h"
00021 #include "BasicElement.h"
00022 #include "ElementFactory.h"
00023 #include <KoUnit.h>
00024 #include <KoViewConverter.h>
00025 #include <KoPostscriptPaintDevice.h>
00026 #include <QFontMetricsF>
00027 #include <QColor>
00028 #include <kdebug.h>
00029 // Copied from koffice KoUnit.h
00030 
00031 // 1 inch ^= 72 pt
00032 // 1 inch ^= 25.399956 mm (-pedantic ;p)
00033 // 1 pt = 1/12 pi
00034 // 1 pt ^= 0.0077880997 cc
00035 // 1 cc = 12 dd
00036 // Note: I don't use division but multiplication with the inverse value
00037 // because it's faster ;p (Werner)
00038 #define POINT_TO_MM(px) ((px)*0.352777167)
00039 #define MM_TO_POINT(mm) ((mm)*2.83465058)
00040 #define POINT_TO_CM(px) ((px)*0.0352777167)
00041 #define CM_TO_POINT(cm) ((cm)*28.3465058)
00042 #define POINT_TO_DM(px) ((px)*0.00352777167)
00043 #define DM_TO_POINT(dm) ((dm)*283.465058)
00044 #define POINT_TO_INCH(px) ((px)*0.01388888888889)
00045 #define INCH_TO_POINT(inch) ((inch)*72.0)
00046 #define MM_TO_INCH(mm) ((mm)*0.039370147)
00047 #define INCH_TO_MM(inch) ((inch)*25.399956)
00048 #define POINT_TO_PI(px)((px)*0.083333333)
00049 #define POINT_TO_CC(px)((px)*0.077880997)
00050 #define PI_TO_POINT(pi)((pi)*12)
00051 #define CC_TO_POINT(cc)((cc)*12.840103)
00052 
00053 AttributeManager::AttributeManager()
00054 {
00055     m_viewConverter = 0;
00056 }
00057 
00058 AttributeManager::~AttributeManager()
00059 {}
00060 
00061 QString AttributeManager::findValue( const QString& attribute, const BasicElement* element ) const
00062 {
00063     // check if the current element has a value assigned
00064     QString value = element->attribute( attribute );
00065     if( !value.isEmpty() ) {
00066 //         kDebug()<<"checking for attribute "<<attribute <<" returning (s)"<<value;
00067         return value;
00068     }
00069     // if not, check if any of the parent elements inherits a value
00070     BasicElement* tmpParent = element->parentElement();
00071     while( tmpParent )
00072     {
00073         value = tmpParent->inheritsAttribute( attribute );
00074         if( !value.isEmpty() ) {
00075 //             kDebug()<<"checking for attribute "<<attribute <<" returning (p)"<<value;
00076             return value;
00077         }
00078         else {
00079             tmpParent = tmpParent->parentElement();
00080         }
00081     }
00082 
00083     // if not, return the default value of the attribute
00084 //     kDebug()<<"checking for attribute "<<attribute <<" returning (d) "<<element->attributesDefaultValue( attribute );
00085     return element->attributesDefaultValue( attribute );
00086 }
00087 
00088 bool AttributeManager::boolOf( const QString& attribute,
00089                                const BasicElement* element ) const
00090 {
00091     return findValue( attribute, element ) == "true";
00092 }
00093 
00094 double AttributeManager::doubleOf( const QString& attribute,
00095                                    const BasicElement* element ) const
00096 {
00097 
00098     return lengthToPixels(parseUnit( findValue( attribute, element ), element ), element, attribute);
00099 }
00100 
00101 QList<double> AttributeManager::doubleListOf( const QString& attribute,
00102                                               const BasicElement* element ) const
00103 {
00104     QList<double> doubleList;
00105     QStringList tmp = findValue( attribute, element ).split( ' ' );
00106     foreach( const QString &doubleValue, tmp )
00107         doubleList << lengthToPixels( parseUnit( doubleValue, element ), element, attribute);
00108 
00109     return doubleList;
00110 }
00111 
00112 QString AttributeManager::stringOf( const QString& attribute, const BasicElement* element  ) const
00113 {
00114     return findValue( attribute, element );
00115 }
00116 
00117 QColor AttributeManager::colorOf( const QString& attribute, const BasicElement* element  ) const
00118 {
00119     QString tmpColor = findValue( attribute, element );
00120     if( attribute == "mathbackground" && tmpColor.isEmpty() )
00121         return Qt::transparent;
00122 
00123     return QColor( tmpColor );
00124 }
00125 
00126 Align AttributeManager::alignOf( const QString& attribute, const BasicElement* element  ) const
00127 {
00128     return parseAlign( findValue( attribute, element ) );
00129 }
00130 
00131 QList<Align> AttributeManager::alignListOf( const QString& attribute,
00132                                             const BasicElement* element  ) const
00133 {
00134     QList<Align> alignList;
00135     QStringList tmpList = findValue( attribute, element ).split( ' ' );
00136 
00137     foreach( const QString &tmp, tmpList )
00138         alignList << parseAlign( tmp );
00139 
00140     return alignList;
00141 }
00142 
00143 Qt::PenStyle AttributeManager::penStyleOf( const QString& attribute,
00144                                            const BasicElement* element  ) const
00145 {
00146     return parsePenStyle( findValue( attribute, element ) );
00147 }
00148 
00149 QList<Qt::PenStyle> AttributeManager::penStyleListOf( const QString& attribute,
00150                                                       const BasicElement* element  ) const
00151 {
00152     QList<Qt::PenStyle> penStyleList;
00153     QStringList tmpList = findValue( attribute, element ).split( ' ' );
00154 
00155     foreach( const QString &tmp, tmpList )
00156         penStyleList << parsePenStyle( tmp );
00157 
00158     return penStyleList;
00159 }
00160 
00161 int AttributeManager::scriptLevel( const BasicElement* parent, int index ) const
00162 {
00163     ElementType parentType = parent->elementType();
00164     int current_scaleLevel = parent->scaleLevel();
00165 
00167     switch(parentType) {
00168         case Fraction:
00169             if( parent->displayStyle() == false )
00170                 return current_scaleLevel+1;
00171         else
00172         return current_scaleLevel;
00173     case Style: {
00174             QString tmp = parent->attribute( "scriptlevel" );
00175             if( tmp.startsWith( '+' ) )
00176             return current_scaleLevel + tmp.remove(0,1).toInt();
00177         if( tmp.startsWith( '-' ) )
00178             return current_scaleLevel - tmp.remove(0,1).toInt();
00179             return tmp.toInt();
00180     }
00181     case MultiScript:
00182         return current_scaleLevel + 1;
00183     case Table:
00184         return current_scaleLevel + 1;
00185     default:
00186         break;
00187     }
00188     if( index == 0) return current_scaleLevel;
00190     switch(parentType) {
00191         case SubScript:
00192         case SupScript:
00193         case SubSupScript:
00194             return current_scaleLevel + 1;
00195         case Under:
00196         if( boolOf("accentunder", parent) )
00197                 return current_scaleLevel + 1;
00198         else
00199                 return current_scaleLevel;
00200         case Over:
00201         if( boolOf("accent", parent) )
00202                 return current_scaleLevel + 1;
00203         else
00204                 return current_scaleLevel;
00205         case UnderOver:
00206         if( (index == 1 && boolOf("accentunder", parent)) || (index == 2 && boolOf("accent", parent)) )
00207                 return current_scaleLevel + 1;
00208         else
00209                 return current_scaleLevel;
00210         case Root:
00211         /* second argument to root is the base */
00212             return current_scaleLevel + 1;
00213             default:
00214             return current_scaleLevel;
00215     }
00216 }
00217 
00218 double AttributeManager::lineThickness( const BasicElement* element ) const
00219 {
00220     QFontMetricsF fm(font(element));
00221     return fm.height() * 0.06 ;
00222 }
00223 
00224 double AttributeManager::layoutSpacing( const BasicElement* element  ) const
00225 {
00226     QFontMetricsF fm(font(element));
00227 //    return fm.height() * 0.166667 ;
00228     return fm.height() * 0.05 ;
00229 }
00230 
00231 double AttributeManager::lengthToPixels( Length length, const BasicElement* element, const QString &attribute) const
00232 {
00233     if(length.value == 0)
00234         return 0;
00235 
00236     switch(length.unit) {
00237     case Length::Em: {
00238         QFontMetricsF fm(font(element));
00239         return fm.height() * length.value;
00240     }
00241     case Length::Ex: {
00242         QFontMetricsF fm(font(element));
00243         return fm.xHeight() * length.value;
00244     }
00245     case Length::Percentage:
00246         return lengthToPixels( parseUnit( element->attributesDefaultValue(attribute), element),element, attribute) * length.value / 100.0;
00247     case Length::Px: //pixels
00248         return length.value;
00249     case Length::In:  /* Note for the units below we assume point == pixel.  */
00250         return INCH_TO_POINT(length.value);
00251     case Length::Cm:
00252         return CM_TO_POINT(length.value);
00253     case Length::Mm:
00254         return MM_TO_POINT(length.value);
00255     case Length::Pt:
00256         return length.value;
00257     case Length::Pc:
00258         return PI_TO_POINT(length.value);
00259     case Length::None:
00260     default:
00261         return length.value;
00262     }
00263 }
00264 
00265 Length AttributeManager::parseUnit( const QString& value,
00266                                     const BasicElement* element ) const
00267 {
00268     Q_UNUSED(element)
00269     Length length;
00270 
00271     if (value.isEmpty())
00272         return length;
00273     QRegExp re("(-?[\\d\\.]*) *(px|em|ex|in|cm|pc|mm|pt|%)?", Qt::CaseInsensitive);
00274     if (re.indexIn(value) == -1)
00275         return length;
00276     QString real = re.cap(1);
00277     QString unit = re.cap(2).toLower();
00278 
00279     bool ok;
00280     qreal number = real.toDouble(&ok);
00281     if (!ok)
00282         return length;
00283 
00284     length.value = number;
00285     if(!unit.isEmpty()) {
00286         if (unit == "em") {
00287             length.unit = Length::Mm;
00288             length.type = Length::Relative;
00289         }
00290         else if (unit == "ex") {
00291             length.unit = Length::Ex;
00292             length.type = Length::Relative;
00293         }
00294         else if (unit == "px") {
00295             length.unit = Length::Px;
00296             length.type = Length::Pixel;
00297         }
00298         else if (unit == "in") {
00299             length.unit = Length::In;
00300             length.type = Length::Absolute;
00301         }
00302         else if (unit == "cm") {
00303             length.unit = Length::Cm;
00304             length.type = Length::Absolute;
00305         }
00306         else if (unit == "mm") {
00307             length.unit = Length::Mm;
00308             length.type = Length::Absolute;
00309         }
00310         else if (unit == "pt") {
00311             length.unit = Length::Pt;
00312             length.type = Length::Relative;
00313         }
00314         else if (unit == "pc") {
00315             length.unit = Length::Pc;
00316             length.type = Length::Relative;
00317         }
00318         else if (unit == "%") {
00319             length.unit = Length::Percentage;
00320             length.type = Length::Relative;
00321         }
00322         else {
00323             length.unit = Length::None;
00324             length.type = Length::NoType;
00325         }
00326     }
00327 
00328     return length;
00329 }
00330 
00331 Align AttributeManager::parseAlign( const QString& value ) const
00332 {
00333     if( value == "right" )
00334         return Right;
00335     else if( value == "left" )
00336         return Left;
00337     else if( value == "center" )
00338         return Center;
00339     else if( value == "top" )
00340         return Top;
00341     else if( value == "bottom" )
00342         return Bottom;
00343     else if( value == "baseline" )
00344         return BaseLine;
00345     else if( value == "axis" )
00346         return Axis;
00347     else
00348         return InvalidAlign;
00349 }
00350 
00351 Qt::PenStyle AttributeManager::parsePenStyle( const QString& value ) const
00352 {
00353     if( value == "solid" )
00354         return Qt::SolidLine;
00355     else if( value == "dashed" )
00356         return Qt::DashLine;
00357     else
00358         return Qt::NoPen;
00359 }
00360 
00361 QFont AttributeManager::font( const BasicElement* element ) const
00362 {
00363 
00364     // TODO process the mathvariant values partly
00365     // normal -> do nothing.
00366     // if contains bold -> font.setBold( true )
00367     // if contains italic -> font.setItalic( true )
00368     // if contains sans-serif setStyleHint( SansSerif ) --> Helvetica
00369 
00370     QFont font;
00371     Length unit = parseUnit( findValue( "fontsize", element ), element );
00372     if ( unit.type == Length::Absolute ) {
00373         font.setPointSizeF( lengthToPixels( unit,  element,  "fontsize" ) );
00374     } else if ( unit.type == Length::Relative ) {
00375         font.setPointSizeF( lengthToPixels( unit,  element,  "fontsize" ) * element->scaleFactor() );
00376     } else if ( unit.type == Length::Pixel ) {
00377         font.setPixelSize( lengthToPixels( unit,  element,  "fontsize" ) * element->scaleFactor() );
00378     }
00379     return font;
00380 }
00381 
00382 void AttributeManager::setViewConverter( KoViewConverter* converter )
00383 {
00384     m_viewConverter = converter;
00385 }
00386 
00387 double AttributeManager::maxHeightOfChildren( const BasicElement* element ) const
00388 {
00389     double maxHeight = 0.0;
00390     foreach( BasicElement* tmp, element->childElements() )
00391         maxHeight = qMax( maxHeight, tmp->height() );
00392 
00393     return maxHeight;
00394 }
00395 
00396 double AttributeManager::maxWidthOfChildren( const BasicElement* element  ) const
00397 {
00398     double maxWidth = 0.0;
00399     foreach( BasicElement* tmp, element->childElements() )
00400         maxWidth = qMax( maxWidth, tmp->width() );
00401 
00402     return maxWidth;
00403 }
00404 double AttributeManager::parseMathSpace( const QString& value, const BasicElement * element )  const
00405 {
00406     QFontMetricsF fm(font(element));
00407     qreal conversionEmToPixels = fm.xHeight();
00408 
00409     if( value == "negativeveryverythinmathspace" )
00410         return -1*conversionEmToPixels*0.055556;
00411     else if( value == "negativeverythinmathspace" )
00412         return -1*conversionEmToPixels*0.111111;
00413     else if( value == "negativethinmathspace" )
00414         return -1*conversionEmToPixels*0.166667;
00415     else if( value == "negativemediummathspace" )
00416         return -1*conversionEmToPixels*0.222222;
00417     else if( value == "negativethickmathspace" )
00418         return -1*conversionEmToPixels*0.277778;
00419     else if( value == "negativeverythickmathspace" )
00420         return -1*conversionEmToPixels*0.333333;
00421     else if( value == "negativeveryverythickmathspace" )
00422         return -1*conversionEmToPixels*0.388889;
00423     else if( value == "veryverythinmathspace" )
00424         return conversionEmToPixels*0.055556;
00425     else if( value == "verythinmathspace" )
00426         return conversionEmToPixels*0.111111;
00427     else if( value == "thinmathspace" )
00428         return conversionEmToPixels*0.166667;
00429     else if( value == "mediummathspace" )
00430         return conversionEmToPixels*0.222222;
00431     else if( value == "thickmathspace" )
00432         return conversionEmToPixels*0.277778;
00433     else if( value == "verythickmathspace" )
00434         return conversionEmToPixels*0.333333;
00435     else if( value == "veryverythickmathspace" )
00436         return conversionEmToPixels*0.388889;
00437     else
00438         return 0;
00439 }
00440