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