File indexing completed on 2024-05-12 16:33:59

0001 /* This file is part of the KDE project
0002    Copyright (C) 2006 Martin Pfeiffer <hubipete@gmx.net>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LIB.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017    Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "AttributeManager.h"
0021 
0022 #include "BasicElement.h"
0023 #include "ElementFactory.h"
0024 #include "FormulaDebug.h"
0025 
0026 #include <KoUnit.h>
0027 #include <KoViewConverter.h>
0028 #include <KoPostscriptPaintDevice.h>
0029 
0030 #include <QFontMetricsF>
0031 #include <QColor>
0032 
0033 //// Copied from calligra KoUnit.h
0034 //
0035 //// 1 inch ^= 72 pt
0036 //// 1 inch ^= 25.399956 mm (-pedantic ;p)
0037 //// 1 pt = 1/12 pi
0038 //// 1 pt ^= 0.0077880997 cc
0039 //// 1 cc = 12 dd
0040 //// Note: I don't use division but multiplication with the inverse value
0041 //// because it's faster ;p (Werner)
0042 //#define POINT_TO_MM(px) ((px)*0.352777167)
0043 //#define MM_TO_POINT(mm) ((mm)*2.83465058)
0044 //#define POINT_TO_CM(px) ((px)*0.0352777167)
0045 //#define CM_TO_POINT(cm) ((cm)*28.3465058)
0046 //#define POINT_TO_DM(px) ((px)*0.00352777167)
0047 //#define DM_TO_POINT(dm) ((dm)*283.465058)
0048 //#define POINT_TO_INCH(px) ((px)*0.01388888888889)
0049 //#define INCH_TO_POINT(inch) ((inch)*72.0)
0050 //#define MM_TO_INCH(mm) ((mm)*0.039370147)
0051 //#define INCH_TO_MM(inch) ((inch)*25.399956)
0052 //#define POINT_TO_PI(px)((px)*0.083333333)
0053 //#define POINT_TO_CC(px)((px)*0.077880997)
0054 //#define PI_TO_POINT(pi)((pi)*12)
0055 //#define CC_TO_POINT(cc)((cc)*12.840103)
0056 
0057 AttributeManager::AttributeManager()
0058 {
0059     m_viewConverter = 0;
0060 }
0061 
0062 AttributeManager::~AttributeManager()
0063 {}
0064 
0065 QString AttributeManager::findValue( const QString& attribute, const BasicElement* element ) const
0066 {
0067     // check if the current element has a value assigned
0068     QString value = element->attribute( attribute );
0069     if( !value.isEmpty() ) {
0070 //         debugFormula << "checking for attribute "<<attribute <<" returning (s)"<<value;
0071         return value;
0072     }
0073     // if not, check if any of the parent elements inherits a value
0074     BasicElement* tmpParent = element->parentElement();
0075     while( tmpParent )
0076     {
0077         value = tmpParent->inheritsAttribute( attribute );
0078         if( !value.isEmpty() ) {
0079 //             debugFormula << "checking for attribute "<<attribute <<" returning (p)"<<value;
0080             return value;
0081         }
0082         else {
0083             tmpParent = tmpParent->parentElement();
0084         }
0085     }
0086 
0087     // if not, return the default value of the attribute
0088 //     debugFormula << "checking for attribute "<<attribute <<" returning (d) "<<element->attributesDefaultValue( attribute );
0089     return element->attributesDefaultValue( attribute );
0090 }
0091 
0092 bool AttributeManager::boolOf( const QString& attribute,
0093                                const BasicElement* element ) const
0094 {
0095     return findValue( attribute, element ) == "true";
0096 }
0097 
0098 qreal AttributeManager::doubleOf( const QString& attribute,
0099                                   const BasicElement* element ) const
0100 {
0101 
0102     return lengthToPixels(parseUnit( findValue( attribute, element ), element ), element, attribute);
0103 }
0104 
0105 QList<qreal> AttributeManager::doubleListOf( const QString& attribute,
0106                                              const BasicElement* element ) const
0107 {
0108     QList<qreal> doubleList;
0109     QStringList tmp = findValue( attribute, element ).split( ' ' );
0110     foreach( const QString &doubleValue, tmp )
0111         doubleList << lengthToPixels( parseUnit( doubleValue, element ), element, attribute);
0112 
0113     return doubleList;
0114 }
0115 
0116 QString AttributeManager::stringOf( const QString& attribute, const BasicElement* element  ) const
0117 {
0118     return findValue( attribute, element );
0119 }
0120 
0121 QColor AttributeManager::colorOf( const QString& attribute, const BasicElement* element  ) const
0122 {
0123     QString tmpColor = findValue( attribute, element );
0124     if( attribute == "mathbackground" && tmpColor.isEmpty() )
0125         return Qt::transparent;
0126 
0127     return QColor( tmpColor );
0128 }
0129 
0130 Align AttributeManager::alignOf( const QString& attribute, const BasicElement* element  ) const
0131 {
0132     return parseAlign( findValue( attribute, element ) );
0133 }
0134 
0135 QList<Align> AttributeManager::alignListOf( const QString& attribute,
0136                                             const BasicElement* element  ) const
0137 {
0138     QList<Align> alignList;
0139     QStringList tmpList = findValue( attribute, element ).split( ' ' );
0140 
0141     foreach( const QString &tmp, tmpList )
0142         alignList << parseAlign( tmp );
0143 
0144     return alignList;
0145 }
0146 
0147 Qt::PenStyle AttributeManager::penStyleOf( const QString& attribute,
0148                                            const BasicElement* element  ) const
0149 {
0150     return parsePenStyle( findValue( attribute, element ) );
0151 }
0152 
0153 QList<Qt::PenStyle> AttributeManager::penStyleListOf( const QString& attribute,
0154                                                       const BasicElement* element  ) const
0155 {
0156     QList<Qt::PenStyle> penStyleList;
0157     QStringList tmpList = findValue( attribute, element ).split( ' ' );
0158 
0159     foreach( const QString &tmp, tmpList )
0160         penStyleList << parsePenStyle( tmp );
0161 
0162     return penStyleList;
0163 }
0164 
0165 int AttributeManager::scriptLevel( const BasicElement* parent, int index ) const
0166 {
0167     ElementType parentType = parent->elementType();
0168     int current_scaleLevel = parent->scaleLevel();
0169 
0170     /** First check for types where all children are scaled */
0171     switch(parentType) {
0172         case Fraction:
0173             if( parent->displayStyle() == false )
0174                 return current_scaleLevel+1;
0175             else
0176                 return current_scaleLevel;
0177         case Style: {
0178             QString tmp = parent->attribute( "scriptlevel" );
0179             if( tmp.startsWith( '+' ) )
0180                     return current_scaleLevel + tmp.remove(0,1).toInt();
0181             if( tmp.startsWith( '-' ) )
0182                     return current_scaleLevel - tmp.remove(0,1).toInt();
0183             return tmp.toInt();
0184         }
0185         case MultiScript:
0186             return current_scaleLevel + 1;
0187         case Table:
0188             return current_scaleLevel + 1;
0189         default:
0190             break;
0191     }
0192     if( index == 0) return current_scaleLevel;
0193     /** Now check for types where the first child isn't scaled, but the rest are */
0194     switch(parentType) {
0195             case SubScript:
0196             case SupScript:
0197             case SubSupScript:
0198                 return current_scaleLevel + 1;
0199             case Under:
0200                 if( boolOf("accentunder", parent) )
0201                     return current_scaleLevel + 1;
0202                 else
0203                     return current_scaleLevel;
0204             case Over:
0205                 if( boolOf("accent", parent) )
0206                     return current_scaleLevel + 1;
0207                 else
0208                     return current_scaleLevel;
0209             case UnderOver:
0210                 if( (index == 1 && boolOf("accentunder", parent)) || (index == 2 && boolOf("accent", parent)) )
0211                     return current_scaleLevel + 1;
0212                 else
0213                     return current_scaleLevel;
0214             case Root:
0215                 /* second argument to root is the base */
0216                 return current_scaleLevel + 1;
0217             default:
0218                 return current_scaleLevel;
0219     }
0220 }
0221 
0222 qreal AttributeManager::lineThickness( const BasicElement* element ) const
0223 {
0224     QFontMetricsF fm(font(element));
0225     return fm.height() * 0.06 ;
0226 }
0227 
0228 qreal AttributeManager::layoutSpacing( const BasicElement* element  ) const
0229 {
0230     QFontMetricsF fm(font(element));
0231 //    return fm.height() * 0.166667 ;
0232     return fm.height() * 0.05 ;
0233 }
0234 
0235 qreal AttributeManager::lengthToPixels( Length length, const BasicElement* element, const QString &attribute) const
0236 {
0237     if(length.value == 0)
0238         return 0;
0239 
0240     switch(length.unit) {
0241     case Length::Em: {
0242         QFontMetricsF fm(font(element));
0243         return fm.height() * length.value;
0244     }
0245     case Length::Ex: {
0246         QFontMetricsF fm(font(element));
0247         return fm.xHeight() * length.value;
0248     }
0249     case Length::Percentage:
0250         return lengthToPixels( parseUnit( element->attributesDefaultValue(attribute), element),element, attribute) * length.value / 100.0;
0251     case Length::Px: //pixels
0252         return length.value;
0253     case Length::In:  /* Note for the units below we assume point == pixel.  */
0254         return INCH_TO_POINT(length.value);
0255     case Length::Cm:
0256         return CM_TO_POINT(length.value);
0257     case Length::Mm:
0258         return MM_TO_POINT(length.value);
0259     case Length::Pt:
0260         return length.value;
0261     case Length::Pc:
0262         return PI_TO_POINT(length.value);
0263     case Length::None:
0264     default:
0265         return length.value;
0266     }
0267 }
0268 
0269 Length AttributeManager::parseUnit( const QString& value,
0270                                     const BasicElement* element ) const
0271 {
0272     Q_UNUSED(element)
0273     Length length;
0274 
0275     if (value.isEmpty())
0276         return length;
0277     QRegExp re("(-?[\\d\\.]*) *(px|em|ex|in|cm|pc|mm|pt|%)?", Qt::CaseInsensitive);
0278     if (re.indexIn(value) == -1)
0279         return length;
0280     QString real = re.cap(1);
0281     QString unit = re.cap(2).toLower();
0282 
0283     bool ok;
0284     qreal number = real.toDouble(&ok);
0285     if (!ok)
0286         return length;
0287 
0288     length.value = number;
0289     if(!unit.isEmpty()) {
0290         if (unit == "em") {
0291             length.unit = Length::Mm;
0292             length.type = Length::Relative;
0293         }
0294         else if (unit == "ex") {
0295             length.unit = Length::Ex;
0296             length.type = Length::Relative;
0297         }
0298         else if (unit == "px") {
0299             length.unit = Length::Px;
0300             length.type = Length::Pixel;
0301         }
0302         else if (unit == "in") {
0303             length.unit = Length::In;
0304             length.type = Length::Absolute;
0305         }
0306         else if (unit == "cm") {
0307             length.unit = Length::Cm;
0308             length.type = Length::Absolute;
0309         }
0310         else if (unit == "mm") {
0311             length.unit = Length::Mm;
0312             length.type = Length::Absolute;
0313         }
0314         else if (unit == "pt") {
0315             length.unit = Length::Pt;
0316             length.type = Length::Relative;
0317         }
0318         else if (unit == "pc") {
0319             length.unit = Length::Pc;
0320             length.type = Length::Relative;
0321         }
0322         else if (unit == "%") {
0323             length.unit = Length::Percentage;
0324             length.type = Length::Relative;
0325         }
0326         else {
0327             length.unit = Length::None;
0328             length.type = Length::NoType;
0329         }
0330     }
0331 
0332     return length;
0333 }
0334 
0335 Align AttributeManager::parseAlign( const QString& value ) const
0336 {
0337     if( value == "right" )
0338         return Right;
0339     else if( value == "left" )
0340         return Left;
0341     else if( value == "center" )
0342         return Center;
0343     else if( value == "top" )
0344         return Top;
0345     else if( value == "bottom" )
0346         return Bottom;
0347     else if( value == "baseline" )
0348         return BaseLine;
0349     else if( value == "axis" )
0350         return Axis;
0351     else
0352         return InvalidAlign;
0353 }
0354 
0355 Qt::PenStyle AttributeManager::parsePenStyle( const QString& value ) const
0356 {
0357     if( value == "solid" )
0358         return Qt::SolidLine;
0359     else if( value == "dashed" )
0360         return Qt::DashLine;
0361     else
0362         return Qt::NoPen;
0363 }
0364 
0365 QFont AttributeManager::font( const BasicElement* element ) const
0366 {
0367 
0368     // TODO process the mathvariant values partly
0369     // normal -> do nothing.
0370     // if contains bold -> font.setBold( true )
0371     // if contains italic -> font.setItalic( true )
0372     // if contains sans-serif setStyleHint( SansSerif ) --> Helvetica
0373 
0374     QFont font;
0375     Length unit = parseUnit( findValue( "fontsize", element ), element );
0376     if ( unit.type == Length::Absolute ) {
0377         font.setPointSizeF( lengthToPixels( unit,  element,  "fontsize" ) );
0378     } else if ( unit.type == Length::Relative ) {
0379         font.setPointSizeF( lengthToPixels( unit,  element,  "fontsize" ) * element->scaleFactor() );
0380     } else if ( unit.type == Length::Pixel ) {
0381         font.setPixelSize( lengthToPixels( unit,  element,  "fontsize" ) * element->scaleFactor() );
0382     }
0383     return font;
0384 }
0385 
0386 void AttributeManager::setViewConverter( KoViewConverter* converter )
0387 {
0388     m_viewConverter = converter;
0389 }
0390 
0391 qreal AttributeManager::maxHeightOfChildren( const BasicElement* element ) const
0392 {
0393     qreal maxHeight = 0.0;
0394     foreach( BasicElement* tmp, element->childElements() )
0395         maxHeight = qMax( maxHeight, tmp->height() );
0396 
0397     return maxHeight;
0398 }
0399 
0400 qreal AttributeManager::maxWidthOfChildren( const BasicElement* element  ) const
0401 {
0402     qreal maxWidth = 0.0;
0403     foreach( BasicElement* tmp, element->childElements() )
0404         maxWidth = qMax( maxWidth, tmp->width() );
0405 
0406     return maxWidth;
0407 }
0408 qreal AttributeManager::parseMathSpace( const QString& value, const BasicElement * element )  const
0409 {
0410     QFontMetricsF fm(font(element));
0411     qreal conversionEmToPixels = fm.xHeight();
0412 
0413     if( value == "negativeveryverythinmathspace" )
0414         return -1*conversionEmToPixels*0.055556;
0415     else if( value == "negativeverythinmathspace" )
0416         return -1*conversionEmToPixels*0.111111;
0417     else if( value == "negativethinmathspace" )
0418         return -1*conversionEmToPixels*0.166667;
0419     else if( value == "negativemediummathspace" )
0420         return -1*conversionEmToPixels*0.222222;
0421     else if( value == "negativethickmathspace" )
0422         return -1*conversionEmToPixels*0.277778;
0423     else if( value == "negativeverythickmathspace" )
0424         return -1*conversionEmToPixels*0.333333;
0425     else if( value == "negativeveryverythickmathspace" )
0426         return -1*conversionEmToPixels*0.388889;
0427     else if( value == "veryverythinmathspace" )
0428         return conversionEmToPixels*0.055556;
0429     else if( value == "verythinmathspace" )
0430         return conversionEmToPixels*0.111111;
0431     else if( value == "thinmathspace" )
0432         return conversionEmToPixels*0.166667;
0433     else if( value == "mediummathspace" )
0434         return conversionEmToPixels*0.222222;
0435     else if( value == "thickmathspace" )
0436         return conversionEmToPixels*0.277778;
0437     else if( value == "verythickmathspace" )
0438         return conversionEmToPixels*0.333333;
0439     else if( value == "veryverythickmathspace" )
0440         return conversionEmToPixels*0.388889;
0441     else
0442         return 0;
0443 }
0444