lib

KoTextFormat.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2001-2006 David Faure <faure@kde.org>
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 "KoTextFormat.h"
00021 #include "KoTextParag.h"
00022 #include "KoTextZoomHandler.h"
00023 #include "KoStyleCollection.h"
00024 #include "KoOasisContext.h"
00025 #include <KoGenStyles.h>
00026 #include <KoXmlNS.h>
00027 
00028 #include <kglobal.h>
00029 #include <kdebug.h>
00030 #include <klocale.h>
00031 
00032 #include <qapplication.h>
00033 #include <qregexp.h>
00034 #include <assert.h>
00035 
00036 void KoTextFormat::KoTextFormatPrivate::clearCache()
00037 {
00038     delete m_screenFontMetrics; m_screenFontMetrics = 0L;
00039     delete m_screenFont; m_screenFont = 0L;
00040     delete m_refFontMetrics; m_refFontMetrics = 0L;
00041     delete m_refFont; m_refFont = 0L;
00042     m_refAscent = -1;
00043     m_refDescent = -1;
00044     m_refHeight = -1;
00045     memset( m_screenWidths, 0, 256 * sizeof( ushort ) );
00046 }
00047 
00048 void KoTextFormat::zoomChanged()
00049 {
00050     delete d->m_screenFontMetrics; d->m_screenFontMetrics = 0;
00051     delete d->m_screenFont; d->m_screenFont = 0;
00052     memset( d->m_screenWidths, 0, 256 * sizeof( ushort ) );
00053 }
00054 
00055 KoTextFormat::KoTextFormat()
00056 {
00057     //linkColor = TRUE;
00058     ref = 0;
00059     missp = FALSE;
00060     va = AlignNormal;
00061     collection = 0;
00063     fn.setStyleStrategy( QFont::ForceOutline );
00064     d = new KoTextFormatPrivate;
00065     m_textUnderlineColor=QColor();
00066     m_underlineType = U_NONE;
00067     m_strikeOutType = S_NONE;
00068     m_underlineStyle = U_SOLID;
00069     m_strikeOutStyle = S_SOLID;
00070     m_language = KGlobal::locale()->language();
00071     d->m_bHyphenation = false;
00072     d->m_underLineWidth = 1.0;
00073     d->m_shadowDistanceX = 0;
00074     d->m_shadowDistanceY = 0;
00075     d->m_relativeTextSize = 0.66;
00076     d->m_offsetFromBaseLine= 0;
00077     d->m_bWordByWord = false;
00078     m_attributeFont = ATT_NONE;
00080 //#ifdef DEBUG_COLLECTION
00081 //    kdDebug(32500) << "KoTextFormat simple ctor, no addRef, no generateKey ! " << this << endl;
00082 //#endif
00083 }
00084 
00085 KoTextFormat::KoTextFormat( const QFont &f, const QColor &c, const QString &_language, bool hyphenation, KoTextFormatCollection *parent )
00086     : fn( f ), col( c ) /*fm( QFontMetrics( f ) ),*/ //linkColor( TRUE )
00087 {
00088 #ifdef DEBUG_COLLECTION
00089     kdDebug(32500) << "KoTextFormat with font & color & parent (" << parent << "), addRef. " << this << endl;
00090 #endif
00091     int pointSize;
00092     if ( f.pointSize() == -1 ) // font was set with a pixelsize, we need a pointsize!
00093         pointSize = (int)( ( (double)fn.pixelSize() * 72.0 ) / (double)KoGlobal::dpiY() );
00094     else
00095         pointSize = f.pointSize();
00096     fn.setPointSize( pointSize );
00097     // WYSIWYG works much much better with scalable fonts -> force it to be scalable
00098     fn.setStyleStrategy( QFont::ForceOutline );
00099     ref = 0;
00100     collection = parent;
00101     //leftBearing = fm.minLeftBearing();
00102     //rightBearing = fm.minRightBearing();
00103     //hei = fm.height();
00104     //asc = fm.ascent();
00105     //dsc = fm.descent();
00106     missp = FALSE;
00107     va = AlignNormal;
00109     d = new KoTextFormatPrivate;
00110     m_textUnderlineColor = QColor();
00111     m_underlineType = U_NONE;
00112     m_strikeOutType = S_NONE;
00113     m_underlineStyle = U_SOLID;
00114     m_strikeOutStyle = S_SOLID;
00115     m_language = _language;
00116     d->m_shadowDistanceX = 0;
00117     d->m_shadowDistanceY = 0;
00118     d->m_relativeTextSize= 0.66;
00119     d->m_offsetFromBaseLine = 0;
00120     d->m_bWordByWord = false;
00121     d->m_charStyle = 0L;
00122     d->m_bHyphenation = hyphenation;
00123     d->m_underLineWidth = 1.0;
00124     m_attributeFont = ATT_NONE;
00126     generateKey();
00127     addRef();
00128 }
00129 
00130 KoTextFormat::KoTextFormat( const QFont &_font,
00131                             VerticalAlignment _valign,
00132                             const QColor & _color,
00133                             const QColor & _backGroundColor,
00134                             const QColor & _underlineColor,
00135                             KoTextFormat::UnderlineType _underlineType,
00136                             KoTextFormat::UnderlineStyle _underlineStyle,
00137                             KoTextFormat::StrikeOutType _strikeOutType,
00138                             KoTextFormat::StrikeOutStyle _strikeOutStyle,
00139                             KoTextFormat::AttributeStyle _fontAttribute,
00140                             const QString &_language,
00141                             double _relativeTextSize,
00142                             int _offsetFromBaseLine,
00143                             bool _wordByWord,
00144                             bool _hyphenation,
00145                             double _shadowDistanceX,
00146                             double _shadowDistanceY,
00147                             const QColor& _shadowColor )
00148 {
00149     ref = 0;
00150     collection = 0;
00151     fn = _font;
00152     fn.setStyleStrategy( QFont::ForceOutline );
00153     col = _color;
00154     missp = false;
00155     va = _valign;
00156     d = new KoTextFormatPrivate;
00157     m_textBackColor = _backGroundColor;
00158     m_textUnderlineColor = _underlineColor;
00159     m_underlineType = _underlineType;
00160     m_strikeOutType = _strikeOutType;
00161     m_underlineStyle = _underlineStyle;
00162     m_strikeOutStyle = _strikeOutStyle;
00163     m_language = _language;
00164     d->m_bHyphenation = _hyphenation;
00165     d->m_underLineWidth = 1.0;
00166     d->m_shadowDistanceX = _shadowDistanceX;
00167     d->m_shadowDistanceY = _shadowDistanceY;
00168     d->m_shadowColor = _shadowColor;
00169     d->m_relativeTextSize = _relativeTextSize;
00170     d->m_offsetFromBaseLine = _offsetFromBaseLine;
00171     d->m_bWordByWord = _wordByWord;
00172     m_attributeFont = _fontAttribute;
00173     d->m_charStyle = 0L;
00175     generateKey();
00176     addRef();
00177 }
00178 
00179 KoTextFormat::KoTextFormat( const KoTextFormat &f )
00180 {
00181     d = 0L;
00182     operator=( f );
00183 }
00184 
00185 KoTextFormat::~KoTextFormat()
00186 {
00188     // Removing a format that is in the collection is forbidden, in fact.
00189     // It should have been removed from the collection before being deleted.
00190 #ifndef NDEBUG
00191     if ( parent() && parent()->defaultFormat() ) // not when destroying the collection
00192         assert( ! ( parent()->dict().find( key() ) == this ) );
00193         // (has to be the same pointer, not only the same key)
00194 #endif
00195     delete d;
00197 }
00198 
00199 KoTextFormat& KoTextFormat::operator=( const KoTextFormat &f )
00200 {
00201 #ifdef DEBUG_COLLECTION
00202     kdDebug(32500) << "KoTextFormat::operator= " << this << " (copying " << &f << "). Will addRef" << endl;
00203 #endif
00204     ref = 0;
00205     collection = 0; // f might be in the collection, but we are not
00206     fn = f.fn;
00207     col = f.col;
00208     //fm = f.fm;
00209     //leftBearing = f.leftBearing;
00210     //rightBearing = f.rightBearing;
00211     //hei = f.hei;
00212     //asc = f.asc;
00213     //dsc = f.dsc;
00214     missp = f.missp;
00215     va = f.va;
00216     m_key = f.m_key;
00217     //linkColor = f.linkColor;
00219     delete d;
00220     d = new KoTextFormatPrivate;
00221     m_textBackColor=f.m_textBackColor;
00222     m_textUnderlineColor=f.m_textUnderlineColor;
00223     m_underlineType = f.m_underlineType;
00224     m_strikeOutType = f.m_strikeOutType;
00225     m_underlineStyle = f.m_underlineStyle;
00226     m_strikeOutStyle = f.m_strikeOutStyle;
00227     m_language = f.m_language;
00228     d->m_bHyphenation=f.d->m_bHyphenation;
00229     d->m_underLineWidth=f.d->m_underLineWidth;
00230     d->m_shadowDistanceX = f.d->m_shadowDistanceX;
00231     d->m_shadowDistanceY = f.d->m_shadowDistanceY;
00232     d->m_shadowColor = f.d->m_shadowColor;
00233     d->m_relativeTextSize = f.d->m_relativeTextSize;
00234     d->m_offsetFromBaseLine = f.d->m_offsetFromBaseLine;
00235     d->m_bWordByWord = f.d->m_bWordByWord;
00236     m_attributeFont = f.m_attributeFont;
00237     d->m_charStyle = 0L;
00239     addRef();
00240     return *this;
00241 }
00242 
00243 // Helper for load
00244 static void importTextPosition( const QString& text_position, double fontSize, KoTextFormat::VerticalAlignment& value, double& relativetextsize, int& offset, KoOasisContext& context )
00245 {
00246     //OO: <vertical position (% or sub or super)> [<size as %>]
00247     //Examples: "super" or "super 58%" or "82% 58%" (where 82% is the vertical position)
00248     QStringList lst = QStringList::split( ' ', text_position );
00249     if ( !lst.isEmpty() )
00250     {
00251         QString textPos = lst.front().stripWhiteSpace();
00252         QString textSize;
00253         lst.pop_front();
00254         if ( !lst.isEmpty() )
00255             textSize = lst.front().stripWhiteSpace();
00256         // Workaround bug in KOffice-1.4: it saved '0% 66%' for normal text
00257         if ( context.generator().startsWith( "KOffice/1.4" )
00258              && text_position.startsWith( "0%" ) ) {
00259             //kdDebug(32500) << "Detected koffice-1.4 bug in text-position, assuming Normal text" << endl;
00260             value = KoTextFormat::AlignNormal;
00261             return;
00262         }
00263 
00264         if ( textPos.endsWith("%") && textPos != "0% 100%" && textPos != "0%" )
00265         {
00266             textPos.truncate( textPos.length() - 1 );
00267             double val = textPos.toDouble();
00268             offset = qRound( fontSize * val / 100.0 );
00269             value = KoTextFormat::AlignCustom;
00270         }
00271         else if ( textPos == "super" )
00272             value = KoTextFormat::AlignSuperScript;
00273         else if ( textPos == "sub" )
00274             value = KoTextFormat::AlignSubScript;
00275         else
00276             value = KoTextFormat::AlignNormal;
00277         if ( !textSize.isEmpty() && textSize.endsWith("%") )
00278         {
00279             textSize.truncate( textSize.length() - 1 );
00280             relativetextsize = textSize.toDouble() / 100; // e.g. 0.58
00281         }
00282     }
00283     else
00284         value = KoTextFormat::AlignNormal;
00285 }
00286 
00287 // OASIS 14.2.29
00288 static void importOasisUnderline( const QString& type, const QString& style,
00289                                   KoTextFormat::UnderlineType& underline,
00290                                   KoTextFormat::UnderlineStyle& styleline )
00291 {
00292   if ( type == "single" )
00293       underline = KoTextFormat::U_SIMPLE;
00294   else if ( type == "double" )
00295       underline = KoTextFormat::U_DOUBLE;
00296   else if ( type == "none" )
00297       underline = KoTextFormat::U_NONE;
00298   else if ( style.isEmpty() || style == "none" )
00299       underline = KoTextFormat::U_NONE;
00300   else
00301       underline = KoTextFormat::U_SIMPLE; // OO exports empty type, and style=solid, for normal underline
00302 
00303   styleline = KoTextFormat::U_SOLID; // assume "solid" if unknown
00304   if ( style == "dotted" )
00305       styleline = KoTextFormat::U_DOT;
00306   else if ( style == "dash"
00307             || style == "long-dash" ) // not in kotext
00308       styleline = KoTextFormat::U_DASH;
00309   else if ( style == "dot-dash" )
00310       styleline = KoTextFormat::U_DASH_DOT;
00311   else if ( style == "dot-dot-dash" )
00312       styleline = KoTextFormat::U_DASH_DOT_DOT;
00313   else if ( style == "wave" )
00314       underline = KoTextFormat::U_WAVE;
00315 
00316   // TODO bold. But this is another attribute in OASIS (text-underline-width), which makes sense.
00317   // We should separate them in kotext...
00318 }
00319 
00320 QString exportOasisUnderline( KoTextFormat::UnderlineStyle styleline )
00321 {
00322     switch( styleline ) {
00323     case KoTextFormat::U_DOT:
00324         return "dotted";
00325     case KoTextFormat::U_DASH:
00326         return "dash";
00327     case KoTextFormat::U_DASH_DOT:
00328         return "dot-dash";
00329     case KoTextFormat::U_DASH_DOT_DOT:
00330         return "dot-dot-dash";
00331     default:
00332         return "solid";
00333     }
00334 }
00335 
00336 // Helper for load. Legacy OO format.
00337 static void importUnderline( const QString& in,
00338                              KoTextFormat::UnderlineType& underline,
00339                              KoTextFormat::UnderlineStyle& styleline )
00340 {
00341     underline = KoTextFormat::U_SIMPLE;
00342     styleline = KoTextFormat::U_SOLID;
00343     if ( in == "none" )
00344         underline = KoTextFormat::U_NONE;
00345     else if ( in == "single" )
00346         styleline = KoTextFormat::U_SOLID;
00347     else if ( in == "double" ) {
00348         underline = KoTextFormat::U_DOUBLE;
00349     }
00350     else if ( in == "dotted" || in == "bold-dotted" ) // bold-dotted not in libkotext
00351         styleline = KoTextFormat::U_DOT;
00352     else if ( in == "dash"
00353               // those are not in libkotext:
00354               || in == "long-dash"
00355               || in == "bold-dash"
00356               || in == "bold-long-dash" )
00357         styleline = KoTextFormat::U_DASH;
00358     else if ( in == "dot-dash"
00359               || in == "bold-dot-dash") // not in libkotext
00360         styleline = KoTextFormat::U_DASH_DOT; // tricky ;)
00361     else if ( in == "dot-dot-dash"
00362               || in == "bold-dot-dot-dash") // not in libkotext
00363         styleline = KoTextFormat::U_DASH_DOT_DOT; // this is getting fun...
00364     else if ( in == "wave"
00365               || in == "bold-wave" // not in libkotext
00366               || in == "double-wave" // not in libkotext
00367               || in == "small-wave" ) { // not in libkotext
00368         underline = KoTextFormat::U_WAVE;
00369     } else if( in == "bold" ) {
00370         underline = KoTextFormat::U_SIMPLE_BOLD;
00371     } else
00372         kdWarning() << k_funcinfo << " unsupported text-underline value: " << in << endl;
00373 }
00374 
00375 void KoTextFormat::load( KoOasisContext& context )
00376 {
00377     KoStyleStack& styleStack = context.styleStack();
00378     styleStack.setTypeProperties( "text" ); // load all style attributes from "style:text-properties"
00379 
00380     if ( styleStack.hasAttributeNS( KoXmlNS::fo, "color" ) ) { // 3.10.3
00381         col.setNamedColor( styleStack.attributeNS( KoXmlNS::fo, "color" ) ); // #rrggbb format
00382     }
00383     if ( styleStack.hasAttributeNS( KoXmlNS::fo, "font-family" )  // 3.10.9
00384          || styleStack.hasAttributeNS( KoXmlNS::style, "font-name") ) { // 3.10.8
00385         // Hmm, the remove "'" could break it's in the middle of the fontname...
00386         QString fontName = styleStack.attributeNS( KoXmlNS::fo, "font-family" ).remove( "'" );
00387         if (fontName.isEmpty()) {
00388             // ##### TODO. This is wrong. style:font-name refers to a font-decl entry.
00389             // We have to look it up there, and retrieve _all_ font attributes from it, not just the name.
00390             fontName = styleStack.attributeNS( KoXmlNS::style, "font-name" ).remove( "'" );
00391         }
00392         // 'Thorndale' is not known outside OpenOffice so we substitute it
00393         // with 'Times New Roman' that looks nearly the same.
00394         if ( fontName == "Thorndale" )
00395             fontName = "Times New Roman";
00396 
00397         fontName.remove(QRegExp("\\sCE$")); // Arial CE -> Arial
00398         fn.setFamily( fontName );
00399     }
00400     if ( styleStack.hasAttributeNS( KoXmlNS::fo, "font-size" ) ) { // 3.10.14
00401         double pointSize = styleStack.fontSize();
00402         fn.setPointSizeFloat( pointSize );
00403     }
00404     if ( styleStack.hasAttributeNS( KoXmlNS::fo, "font-weight" ) ) { // 3.10.24
00405         QString fontWeight = styleStack.attributeNS( KoXmlNS::fo, "font-weight" );
00406         int boldness;
00407         if ( fontWeight == "normal" )
00408             boldness = 50;
00409         else if ( fontWeight == "bold" )
00410             boldness = 75;
00411         else
00412             // XSL/CSS has 100,200,300...900. Not the same scale as Qt!
00413             // See http://www.w3.org/TR/2001/REC-xsl-20011015/slice7.html#font-weight
00414             boldness = fontWeight.toInt() / 10;
00415         fn.setWeight( boldness );
00416     }
00417     if ( styleStack.hasAttributeNS( KoXmlNS::fo, "font-style" ) ) // 3.10.19
00418         if ( styleStack.attributeNS( KoXmlNS::fo, "font-style" ) == "italic" ||
00419              styleStack.attributeNS( KoXmlNS::fo, "font-style" ) == "oblique" ) { // no difference in kotext
00420             fn.setItalic( true );
00421         }
00422 
00423     d->m_bWordByWord = styleStack.attributeNS( KoXmlNS::style, "text-underline-mode" ) == "skip-white-space";
00424     // TODO style:text-line-through-mode
00425 
00426 #if 0 // OO compat code, to move to OO import filter
00427     d->m_bWordByWord = (styleStack.hasAttributeNS( KoXmlNS::fo, "score-spaces")) // 3.10.25
00428                       && (styleStack.attributeNS( KoXmlNS::fo, "score-spaces") == "false");
00429     if( styleStack.hasAttributeNS( KoXmlNS::style, "text-crossing-out" )) { // 3.10.6
00430         QString strikeOutType = styleStack.attributeNS( KoXmlNS::style, "text-crossing-out" );
00431         if( strikeOutType =="double-line")
00432             m_strikeOutType = S_DOUBLE;
00433         else if( strikeOutType =="single-line")
00434             m_strikeOutType = S_SIMPLE;
00435         else if( strikeOutType =="thick-line")
00436             m_strikeOutType = S_SIMPLE_BOLD;
00437         // not supported by KWord: "slash" and "X"
00438         // not supported by OO: stylelines (solid, dash, dot, dashdot, dashdotdot)
00439     }
00440 #endif
00441     if ( styleStack.hasAttributeNS( KoXmlNS::style, "text-underline-type" )
00442         || styleStack.hasAttributeNS( KoXmlNS::style, "text-underline-style" ) ) { // OASIS 14.4.28
00443         importOasisUnderline( styleStack.attributeNS( KoXmlNS::style, "text-underline-type" ),
00444                               styleStack.attributeNS( KoXmlNS::style, "text-underline-style" ),
00445                               m_underlineType, m_underlineStyle );
00446     }
00447     else if ( styleStack.hasAttributeNS( KoXmlNS::style, "text-underline" ) ) { // OO compat (3.10.22), to be moved out
00448         importUnderline( styleStack.attributeNS( KoXmlNS::style, "text-underline" ),
00449                          m_underlineType, m_underlineStyle );
00450     }
00451     QString underLineColor = styleStack.attributeNS( KoXmlNS::style, "text-underline-color" ); // OO 3.10.23, OASIS 14.4.31
00452     if ( !underLineColor.isEmpty() && underLineColor != "font-color" )
00453         m_textUnderlineColor.setNamedColor( underLineColor );
00454 
00455     if ( styleStack.hasAttributeNS( KoXmlNS::style, "text-line-through-type" ) ) { // OASIS 14.4.7
00456         // Reuse code for loading underlines, and convert to strikeout enum (if not wave)
00457         UnderlineType uType; UnderlineStyle uStyle;
00458         importOasisUnderline( styleStack.attributeNS( KoXmlNS::style, "text-line-through-type" ),
00459                               styleStack.attributeNS( KoXmlNS::style, "text-line-through-style" ),
00460                               uType, uStyle );
00461         m_strikeOutType = S_NONE;
00462         if ( uType != U_WAVE )
00463             m_strikeOutType = (StrikeOutType)uType;
00464         m_strikeOutStyle = (StrikeOutStyle)uStyle;
00465     }
00466 
00467     // Text position
00468     va = AlignNormal;
00469     d->m_relativeTextSize = 0.58;
00470     d->m_offsetFromBaseLine = 0;
00471     if( styleStack.hasAttributeNS( KoXmlNS::style, "text-position")) { // OO 3.10.7
00472         importTextPosition( styleStack.attributeNS( KoXmlNS::style, "text-position"), fn.pointSizeFloat(),
00473                             va, d->m_relativeTextSize, d->m_offsetFromBaseLine, context );
00474     }
00475     // Small caps, lowercase, uppercase
00476     m_attributeFont = ATT_NONE;
00477     if ( styleStack.hasAttributeNS( KoXmlNS::fo, "font-variant" ) // 3.10.1
00478          || styleStack.hasAttributeNS( KoXmlNS::fo, "text-transform" ) ) { // 3.10.2
00479         bool smallCaps = styleStack.attributeNS( KoXmlNS::fo, "font-variant" ) == "small-caps";
00480         if ( smallCaps ) {
00481             m_attributeFont = ATT_SMALL_CAPS;
00482         } else {
00483             QString textTransform = styleStack.attributeNS( KoXmlNS::fo, "text-transform" );
00484             if ( textTransform == "uppercase" )
00485                 m_attributeFont = ATT_UPPER;
00486             else if ( textTransform == "lowercase" )
00487                 m_attributeFont = ATT_LOWER;
00488             // TODO in KWord: "capitalize".
00489         }
00490     }
00491     if ( styleStack.hasAttributeNS( KoXmlNS::fo, "language") ) { // 3.10.17
00492         m_language = styleStack.attributeNS( KoXmlNS::fo, "language");
00493         const QString country = styleStack.attributeNS( KoXmlNS::fo, "country" );
00494         if ( !country.isEmpty() ) {
00495             m_language += '_';
00496             m_language += country;
00497         }
00498     }
00499     if ( styleStack.hasAttributeNS( KoXmlNS::fo, "background-color") ) {
00500         QString tmp = styleStack.attributeNS( KoXmlNS::fo, "background-color");
00501         if (tmp != "transparent")
00502             m_textBackColor.setNamedColor( tmp );
00503     }
00504     if ( styleStack.hasAttributeNS( KoXmlNS::fo, "text-shadow") ) { // 3.10.21
00505         parseShadowFromCss( styleStack.attributeNS( KoXmlNS::fo, "text-shadow") );
00506     }
00507 
00508     d->m_bHyphenation = true;
00509     if ( styleStack.hasAttributeNS( KoXmlNS::fo, "hyphenate" ) ) // it's a character property in OASIS (but not in OO-1.1)
00510         d->m_bHyphenation = styleStack.attributeNS( KoXmlNS::fo, "hyphenate" ) == "true";
00511 
00512     /*
00513       Missing properties:
00514       style:use-window-font-color, 3.10.4 - this is what KWord uses by default (fg color from the color style)
00515          OO also switches to another color when necessary to avoid dark-on-dark and light-on-light cases.
00516          (that is TODO in KWord)
00517       style:text-outline, 3.10.5 - not implemented in kotext
00518       style:font-family-generic, 3.10.10 - roman, swiss, modern -> map to a font?
00519       style:font-style-name, 3.10.11 - can be ignored, says DV, the other ways to specify a font are more precise
00520       style:font-pitch, 3.10.12 - fixed or variable -> map to a font?
00521       style:font-charset, 3.10.14 - not necessary with Qt
00522       style:font-size-rel, 3.10.15 - TODO in StyleStack::fontSize()
00523       fo:letter-spacing, 3.10.16 - not implemented in kotext
00524       style:text-relief, 3.10.20 - not implemented in kotext
00525       style:letter-kerning, 3.10.20 - not implemented in kotext
00526       style:text-blinking, 3.10.27 - not implemented in kotext IIRC
00527       style:text-combine, 3.10.29/30 - not implemented, see http://www.w3.org/TR/WD-i18n-format/
00528       style:text-emphasis, 3.10.31 - not implemented in kotext
00529       style:text-scale, 3.10.33 - not implemented in kotext
00530       style:text-rotation-angle, 3.10.34 - not implemented in kotext (kpr rotates whole objects)
00531       style:text-rotation-scale, 3.10.35 - not implemented in kotext (kpr rotates whole objects)
00532       style:punctuation-wrap, 3.10.36 - not implemented in kotext
00533     */
00534 
00535     d->m_underLineWidth = 1.0;
00536 
00537     generateKey();
00538     addRef();
00539 }
00540 
00541 void KoTextFormat::save( KoGenStyle& gs, KoSavingContext& context, KoTextFormat * refFormat ) const
00542 {
00543     KoGenStyle::PropertyType tt = KoGenStyle::TextType;
00544     if ( !refFormat || this->color() != refFormat->color() )
00545     {
00546         gs.addProperty( "fo:color", col.isValid() ? col.name() : "#000000", tt );
00547     }
00548     if ( !refFormat || this->font().family() != refFormat->font().family() )
00549     {
00550         gs.addProperty( "style:font-name", fn.family(), tt );
00551         context.addFontFace( fn.family() );
00552     }
00553     if ( !refFormat || this->pointSize() != refFormat->pointSize() )
00554     {
00555         gs.addPropertyPt( "fo:font-size", fn.pointSize(), tt );
00556     }
00557     int w = fn.weight();
00558     if ( !refFormat || w != refFormat->font().weight() )
00559     {
00560         gs.addProperty( "fo:font-weight", w == 50 ? "normal" : w == 75 ? "bold" : QString::number( qRound( w / 10 ) * 100 ), tt );
00561     }
00562     if ( !refFormat || this->font().italic() != refFormat->font().italic() )
00563     {
00564         gs.addProperty( "fo:font-style", fn.italic() ? "italic" : "normal", tt );
00565     }
00566     if ( !refFormat || this->wordByWord() != refFormat->wordByWord() )
00567     {
00568         gs.addProperty( "style:text-underline-mode", d->m_bWordByWord ? "skip-white-space" : "continuous", tt );
00569     }
00570     if ( !refFormat || this->underlineType() != refFormat->underlineType()
00571                     || this->underlineStyle() !=refFormat->underlineStyle() )
00572     {
00573         gs.addProperty( "style:text-underline-type", m_underlineType == U_NONE ? "none" :
00574                         m_underlineType == U_DOUBLE ? "double" : "single", tt );
00575         QString styleline;
00576         if ( m_underlineType == U_WAVE )
00577             styleline = "wave";
00578         else
00579             styleline = exportOasisUnderline( m_underlineStyle );
00580         gs.addProperty( "style:text-underline-style", m_underlineType == U_NONE ? "none" : styleline, tt );
00581     }
00582     if ( !refFormat || this->textUnderlineColor() !=refFormat->textUnderlineColor() )
00583     {
00584         gs.addProperty( "style:text-underline-color", m_textUnderlineColor.isValid() ? m_textUnderlineColor.name() : "font-color", tt );
00585     }
00586 
00587     if ( !refFormat
00588         || this->strikeOutType() != refFormat->strikeOutType()
00589         || this->strikeOutStyle()!= refFormat->strikeOutStyle() )
00590     {
00591         if ( m_strikeOutType != S_NONE )
00592         {
00593             // TODO U_SIMPLE_BOLD
00594             // TODO style:text-line-through-mode
00595             gs.addProperty( "style:text-line-through-type", m_strikeOutType == S_DOUBLE ? "double" : "single", tt );
00596             const QString styleline = exportOasisUnderline( (UnderlineStyle) m_strikeOutStyle );
00597             gs.addProperty( "style:text-line-through-style", styleline, tt );
00598             //gs.addProperty( "style:text-line-through-color", ...) TODO in kotext
00599         }
00600         else
00601         {
00602             gs.addProperty( "style:text-line-through-type", "none", tt );
00603             gs.addProperty( "style:text-line-through-style", "none", tt );
00604         }
00605     }
00606     if ( !refFormat || (this->vAlign() != refFormat->vAlign())
00607         || (this->relativeTextSize() != refFormat->relativeTextSize()) )
00608     {
00609         QString textPos;
00610         if ( d->m_offsetFromBaseLine != 0 )
00611             textPos = QString::number( 100 * d->m_offsetFromBaseLine / fn.pointSizeFloat() ) + '%';
00612         else if ( va == AlignSuperScript ) textPos = "super";
00613         else if ( va == AlignSubScript ) textPos = "sub";
00614         else textPos = "0%"; // AlignNormal
00615         if ( va != AlignNormal )
00616         {
00617             textPos += ' ';
00618             textPos += QString::number( d->m_relativeTextSize * 100 );
00619             textPos += '%';
00620         }
00621         gs.addProperty( "style:text-position", textPos, tt );
00622     }
00623 
00624     if( !refFormat || this->attributeFont() != refFormat->attributeFont())
00625     {
00626         if ( m_attributeFont == ATT_SMALL_CAPS ) {
00627             gs.addProperty( "fo:font-variant", "small-caps", tt );
00628             gs.addProperty( "fo:text-transform", "none", tt );
00629         }
00630         else if ( m_attributeFont == ATT_UPPER ) {
00631             gs.addProperty( "fo:font-variant", "normal", tt );
00632             gs.addProperty( "fo:text-transform", "uppercase", tt );
00633         }
00634         else if ( m_attributeFont == ATT_LOWER ) {
00635             gs.addProperty( "fo:font-variant", "normal", tt );
00636             gs.addProperty( "fo:text-transform", "lowercase", tt );
00637         }
00638         else {
00639             gs.addProperty( "fo:font-variant", "normal", tt );
00640             gs.addProperty( "fo:text-transform", "none", tt );
00641         }
00642     }
00643 
00644     if( !refFormat || this->language() != refFormat->language())
00645     {
00646         QString lang = m_language;
00647         QString country;
00648         const int pos = lang.find( '_' );
00649         if ( pos != -1 ) {
00650             country = lang.mid( pos + 1 );
00651             lang = lang.left( pos );
00652         }
00653 
00654         gs.addProperty( "fo:language", lang, tt );
00655         gs.addProperty( "fo:country", country, tt );
00656     }
00657     if( !refFormat || this->textBackgroundColor() != refFormat->textBackgroundColor() )
00658     {
00659         gs.addProperty( "fo:background-color",
00660                         m_textBackColor.isValid() ? m_textBackColor.name() : "transparent", tt );
00661     }
00662     if( !refFormat ||
00663         ( this->shadowDistanceX() != refFormat->shadowDistanceX()
00664           || ( this->shadowDistanceY() != refFormat->shadowDistanceY() )
00665           || ( this->shadowColor() != refFormat->shadowColor() ) ) )
00666     {
00667         gs.addProperty( "fo:text-shadow", shadowAsCss(), tt );
00668     }
00669     if ( !refFormat || this->hyphenation() != refFormat->hyphenation() )
00670     {
00671         gs.addProperty( "fo:hyphenate", d->m_bHyphenation, tt );
00672     }
00673 }
00674 
00675 void KoTextFormat::update()
00676 {
00677     //kdDebug(32500) << this << " KoTextFormat::update " << fn.family() << " " << pointSize() << endl;
00678     m_key = QString::null; // invalidate key, recalc at the next key() call
00679     assert( d );
00680     d->clearCache(); // i.e. recalc at the next screenFont[Metrics]() call
00681 }
00682 
00683 void KoTextFormat::copyFormat( const KoTextFormat & nf, int flags )
00684 {
00685     if ( flags & KoTextFormat::Bold )
00686     fn.setBold( nf.fn.bold() );
00687     if ( flags & KoTextFormat::Italic )
00688     fn.setItalic( nf.fn.italic() );
00689     if ( flags & KoTextFormat::Underline )
00690     fn.setUnderline( nf.fn.underline() );
00691     if ( flags & KoTextFormat::Family )
00692     fn.setFamily( nf.fn.family() );
00693     if ( flags & KoTextFormat::Size )
00694     fn.setPointSize( nf.fn.pointSize() );
00695     if ( flags & KoTextFormat::Color )
00696     col = nf.col;
00697     if ( flags & KoTextFormat::Misspelled )
00698     missp = nf.missp;
00699     if ( flags & KoTextFormat::VAlign )
00700     {
00701     va = nf.va;
00702         setRelativeTextSize( nf.relativeTextSize());
00703     }
00705     if ( flags & KoTextFormat::StrikeOut )
00706     {
00707         setStrikeOutStyle( nf.strikeOutStyle() );
00708         setStrikeOutType (nf.strikeOutType());
00709     }
00710     if( flags & KoTextFormat::TextBackgroundColor)
00711         setTextBackgroundColor(nf.textBackgroundColor());
00712     if( flags & KoTextFormat::ExtendUnderLine)
00713     {
00714         setTextUnderlineColor(nf.textUnderlineColor());
00715         setUnderlineType (nf.underlineType());
00716         setUnderlineStyle (nf.underlineStyle());
00717     }
00718     if( flags & KoTextFormat::Language)
00719         setLanguage(nf.language());
00720     if( flags & KoTextFormat::ShadowText)
00721         setShadow(nf.shadowDistanceX(), nf.shadowDistanceY(), nf.shadowColor());
00722     if( flags & KoTextFormat::OffsetFromBaseLine)
00723         setOffsetFromBaseLine(nf.offsetFromBaseLine());
00724     if( flags & KoTextFormat::WordByWord)
00725         setWordByWord(nf.wordByWord());
00726     if( flags & KoTextFormat::Attribute)
00727         setAttributeFont(nf.attributeFont());
00728     if( flags & KoTextFormat::Hyphenation )
00729         setHyphenation( nf.hyphenation());
00730     if( flags & KoTextFormat::UnderLineWidth )
00731         setUnderLineWidth( nf.underLineWidth());
00733     update();
00734     //kdDebug(32500) << "KoTextFormat " << (void*)this << " copyFormat nf=" << (void*)&nf << " " << nf.key() << " flags=" << flags
00735     //        << " ==> result " << this << " " << key() << endl;
00736 }
00737 
00738 void KoTextFormat::setBold( bool b )
00739 {
00740     if ( b == fn.bold() )
00741     return;
00742     fn.setBold( b );
00743     update();
00744 }
00745 
00746 void KoTextFormat::setMisspelled( bool b )
00747 {
00748     if ( b == (bool)missp )
00749     return;
00750     missp = b;
00751     update();
00752 }
00753 
00754 void KoTextFormat::setVAlign( VerticalAlignment a )
00755 {
00756     if ( a == va )
00757     return;
00758     va = a;
00759     update();
00760 }
00761 
00762 void KoTextFormat::setItalic( bool b )
00763 {
00764     if ( b == fn.italic() )
00765     return;
00766     fn.setItalic( b );
00767     update();
00768 }
00769 
00770 void KoTextFormat::setUnderline( bool b )
00771 {
00772     if ( b == fn.underline() )
00773     return;
00774     fn.setUnderline( b );
00775     update();
00776 }
00777 
00778 void KoTextFormat::setFamily( const QString &f )
00779 {
00780     if ( f == fn.family() )
00781     return;
00782     fn.setFamily( f );
00783     update();
00784 }
00785 
00786 void KoTextFormat::setPointSize( int s )
00787 {
00788     if ( s == fn.pointSize() )
00789     return;
00790     fn.setPointSize( s );
00791     update();
00792 }
00793 
00794 void KoTextFormat::setFont( const QFont &f )
00795 {
00796     if ( f == fn )
00797     return;
00798     fn = f;
00799     fn.setStyleStrategy( QFont::ForceOutline );
00800     update();
00801 }
00802 
00803 void KoTextFormat::setColor( const QColor &c )
00804 {
00805     if ( c == col )
00806     return;
00807     col = c;
00808     update();
00809 }
00810 
00811 #if 0
00812 int KoTextFormat::minLeftBearing() const
00813 {
00814     if ( !painter || !painter->isActive() )
00815     return leftBearing;
00816     painter->setFont( fn );
00817     return painter->fontMetrics().minLeftBearing();
00818 }
00819 
00820 int KoTextFormat::minRightBearing() const
00821 {
00822     if ( !painter || !painter->isActive() )
00823     return rightBearing;
00824     painter->setFont( fn );
00825     return painter->fontMetrics().minRightBearing();
00826 }
00827 #endif
00828 
00829 // ## Maybe we need a binary form for speed when NDEBUG, and to keep the
00830 // ## readable form when !NDEBUG, like QFont does?
00831 void KoTextFormat::generateKey()
00832 {
00833     QString k = fn.key();
00834     k += '/';
00835     if ( col.isValid() ) // just to shorten the key in the common case
00836         k += QString::number( (uint)col.rgb() );
00837     k += '/';
00838     k += QString::number( (int)isMisspelled() ); // 1 digit, no need for '/'
00839     k += QString::number( (int)vAlign() );
00841     k += '/';
00842     if (m_textBackColor.isValid())
00843         k += QString::number( (uint)m_textBackColor.rgb() );
00844     k += '/';
00845     if ( m_textUnderlineColor.isValid())
00846         k += QString::number( (uint)m_textUnderlineColor.rgb() );
00847     k += '/';
00848     k += QString::number( (int)m_underlineType ); // a digit each, no need for '/'
00849     k += QString::number( (int)m_strikeOutType );
00850     k += QString::number( (int)m_underlineStyle );
00851     k += QString::number( (int)m_strikeOutStyle );
00852     k += '/';
00853     k += m_language;
00854     k += '/';
00855     if ( d->m_shadowDistanceX != 0 || d->m_shadowDistanceY != 0 )
00856     {
00857         k += QString::number( d->m_shadowDistanceX );
00858         k += '/';
00859         k += QString::number( d->m_shadowDistanceY );
00860         k += '/';
00861         k += QString::number( (uint)d->m_shadowColor.rgb() );
00862     }
00863     k += '/';
00864     k += QString::number( d->m_relativeTextSize);
00865     k += '/';
00866     k += QString::number( d->m_offsetFromBaseLine);
00867     k += '/';
00868     k += QString::number( (int)d->m_bWordByWord); // boolean -> 1 digit -> no '/'
00869     k += QString::number( (int)m_attributeFont);
00870     k += '/';
00871     k += QString::number( (int)d->m_bHyphenation); // boolean -> 1 digit -> no '/'
00872     k += QString::number( (double)d->m_underLineWidth);
00874     // Keep in sync with method below
00875     m_key = k;
00876 }
00877 
00878 // This is used to create "simple formats", with font and color etc., but without
00879 // advanced features. Doesn't matter, don't extend the args.
00880 QString KoTextFormat::getKey( const QFont &fn, const QColor &col, bool misspelled, VerticalAlignment a )
00881 {
00882     QString k = fn.key();
00883     k += '/';
00884     if ( col.isValid() ) // just to shorten the key in the common case
00885         k += QString::number( (uint)col.rgb() );
00886     k += '/';
00887     k += QString::number( (int)misspelled );
00888     k += QString::number( (int)a );
00890     k += '/';
00891         // no background color
00892     k += '/';
00893         // no underline color
00894     k += '/';
00895     k += QString::number( (int)U_NONE );
00896     k += QString::number( (int)S_NONE ); // no double-underline in a "simple format"
00897     k += QString::number( (int)U_SOLID );
00898     k += QString::number( (int)S_SOLID ); // no double-underline in a "simple format"
00899     k += '/';
00900     //k += QString::null; // spellcheck language
00901     k += '/';
00902       //no shadow
00903     k += '/';
00904     k += "0.66"; //relative text size
00905     k += '/';
00906     k += "0"; // no offset from base line
00907     k += '/';
00908     k += "0"; //no wordbyword attribute
00909     k += "0"; //no font attribute
00910     k += '/';
00911     k += "0"; //no hyphen
00912     k += "0"; //no ulw
00913 
00915     return k;
00916 }
00917 
00918 
00919 QString KoTextFormat::key() const
00920 {
00921     if ( m_key.isEmpty() )
00922         const_cast<KoTextFormat*>( this )->generateKey();
00923     return m_key;
00924 }
00925 
00926 void KoTextFormat::addRef()
00927 {
00928     ref++;
00929 #ifdef DEBUG_COLLECTION
00930     if ( collection )
00931         kdDebug(32500) << "  add ref of '" << k << "' to " << ref << " (" << this << ") (coll " << collection << ")" << endl;
00932 #endif
00933 }
00934 
00935 void KoTextFormat::removeRef()
00936 {
00937     ref--;
00938     if ( !collection )
00939         return;
00940 #ifdef DEBUG_COLLECTION
00941     kdDebug(32500) << "  remove ref of '" << k << "' to " << ref << " (" << this << ") (coll " << collection << ")" << endl;
00942 #endif
00943     if ( ref == 0 )
00944         collection->remove( this );
00945 }
00946 
00947 void KoTextFormat::setStrikeOutType (StrikeOutType _type)
00948 {
00949     if ( m_strikeOutType == _type )
00950         return;
00951     m_strikeOutType = _type;
00952     update();
00953 }
00954 
00955 void KoTextFormat::setUnderlineType (UnderlineType _type)
00956 {
00957     if ( m_underlineType == _type )
00958         return;
00959     m_underlineType = _type;
00960     update();
00961 }
00962 
00963 void KoTextFormat::setUnderlineStyle (UnderlineStyle _type)
00964 {
00965     if ( m_underlineStyle == _type )
00966         return;
00967     m_underlineStyle = _type;
00968     update();
00969 }
00970 
00971 void KoTextFormat::setStrikeOutStyle( StrikeOutStyle _type )
00972 {
00973     if ( m_strikeOutStyle == _type )
00974         return;
00975     m_strikeOutStyle = _type;
00976     update();
00977 }
00978 
00979 void KoTextFormat::setTextBackgroundColor(const QColor &_col)
00980 {
00981     if(m_textBackColor==_col)
00982         return;
00983     m_textBackColor=_col;
00984     update();
00985 }
00986 void KoTextFormat::setTextUnderlineColor(const QColor &_col)
00987 {
00988     if ( m_textUnderlineColor == _col )
00989         return;
00990     m_textUnderlineColor=_col;
00991     update();
00992 }
00993 
00994 void KoTextFormat::setShadow( double shadowDistanceX, double shadowDistanceY, const QColor& shadowColor )
00995 {
00996     if ( d->m_shadowDistanceX == shadowDistanceX &&
00997          d->m_shadowDistanceY == shadowDistanceY &&
00998          d->m_shadowColor == shadowColor )
00999         return;
01000     d->m_shadowDistanceX = shadowDistanceX;
01001     d->m_shadowDistanceY = shadowDistanceY;
01002     d->m_shadowColor = shadowColor;
01003     update();
01004 }
01005 
01006 void KoTextFormat::setRelativeTextSize( double _size )
01007 {
01008     if ( d->m_relativeTextSize == _size)
01009         return;
01010     d->m_relativeTextSize = _size;
01011     update();
01012 }
01013 
01014 void KoTextFormat::setOffsetFromBaseLine( int _offset )
01015 {
01016     if ( d->m_offsetFromBaseLine == _offset)
01017         return;
01018     d->m_offsetFromBaseLine = _offset;
01019     update();
01020 }
01021 
01022 void KoTextFormat::setWordByWord( bool _b )
01023 {
01024     if ( d->m_bWordByWord == _b)
01025         return;
01026     d->m_bWordByWord = _b;
01027     update();
01028 }
01029 
01030 
01031 void KoTextFormat::setAttributeFont(KoTextFormat::AttributeStyle _att )
01032 {
01033     if ( m_attributeFont == _att)
01034         return;
01035     m_attributeFont = _att;
01036     update();
01037 
01038 }
01039 
01040 int KoTextFormat::compare( const KoTextFormat & format ) const
01041 {
01042     int flags = 0;
01043     if ( fn.weight() != format.fn.weight() )
01044         flags |= KoTextFormat::Bold;
01045     if ( fn.italic() != format.fn.italic() )
01046         flags |= KoTextFormat::Italic;
01047     if ( textUnderlineColor()!=format.textUnderlineColor() ||
01048          underlineType()!= format.underlineType() ||
01049          underlineStyle() != format.underlineStyle())
01050         flags |= KoTextFormat::ExtendUnderLine;
01051     if ( fn.family() != format.fn.family() )
01052         flags |= KoTextFormat::Family;
01053     if ( pointSize() != format.pointSize() )
01054         flags |= KoTextFormat::Size;
01055     if ( color() != format.color() )
01056         flags |= KoTextFormat::Color;
01057     if ( vAlign() != format.vAlign() ||
01058         relativeTextSize() != format.relativeTextSize())
01059         flags |= KoTextFormat::VAlign;
01060     if ( strikeOutType() != format.strikeOutType()
01061         || underlineStyle() != format.underlineStyle())
01062         flags |= KoTextFormat::StrikeOut;
01063     if ( textBackgroundColor() != format.textBackgroundColor() )
01064         flags |= KoTextFormat::TextBackgroundColor;
01065     if ( language() != format.language() )
01066         flags |= KoTextFormat::Language;
01067     if ( d->m_shadowDistanceX != format.shadowDistanceX()
01068          || d->m_shadowDistanceY != format.shadowDistanceY()
01069          || d->m_shadowColor != format.shadowColor() )
01070         flags |= KoTextFormat::ShadowText;
01071     if ( offsetFromBaseLine() != format.offsetFromBaseLine() )
01072         flags |= KoTextFormat::OffsetFromBaseLine;
01073     if ( wordByWord() != format.wordByWord() )
01074         flags |= KoTextFormat::WordByWord;
01075     if ( attributeFont() != format.attributeFont() )
01076         flags |= KoTextFormat::Attribute;
01077     if( hyphenation() != format.hyphenation() )
01078         flags |= KoTextFormat::Hyphenation;
01079     if( underLineWidth() != format.underLineWidth() )
01080         flags |= KoTextFormat::UnderLineWidth;
01081     return flags;
01082 }
01083 
01084 QColor KoTextFormat::defaultTextColor( QPainter * painter )
01085 {
01086     if ( painter->device()->devType() == QInternal::Printer )
01087         return Qt::black;
01088     return QApplication::palette().color( QPalette::Active, QColorGroup::Text );
01089 }
01090 
01091 float KoTextFormat::screenPointSize( const KoTextZoomHandler* zh ) const
01092 {
01093     // ## simplify (needs a change in KoTextZoomHandler)
01094     int pointSizeLU = KoTextZoomHandler::ptToLayoutUnitPt( pointSize() );
01095     if ( vAlign() != KoTextFormat::AlignNormal )
01096         pointSizeLU = (int)( pointSizeLU *relativeTextSize() );
01097     return zh->layoutUnitToFontSize( pointSizeLU, false /* forPrint */ );
01098 }
01099 
01100 float KoTextFormat::refPointSize() const
01101 {
01102     if ( vAlign() != KoTextFormat::AlignNormal )
01103         return (float)pointSize() * relativeTextSize();
01104     else
01105         return pointSize();
01106 }
01107 
01108 QFont KoTextFormat::refFont() const
01109 {
01110     float pointSize = refPointSize();
01111     if ( !d->m_refFont || pointSize != d->m_refFont->pointSizeFloat() )
01112     {
01113         delete d->m_refFont;
01114         d->m_refFont = new QFont( font() );
01115         d->m_refFont->setPointSizeFloat( pointSize );
01116         delete d->m_refFontMetrics;
01117         d->m_refFontMetrics = 0;
01118         //kdDebug(32500) << "KoTextFormat::refFont created new font with size " << pointSize << endl;
01119     }
01120     return *d->m_refFont;
01121 }
01122 
01123 QFont KoTextFormat::screenFont( const KoTextZoomHandler* zh ) const
01124 {
01125     float pointSize = screenPointSize( zh );
01126     //kdDebug(32500) << "KoTextFormat::screenFont pointSize=" << pointSize << endl;
01127     // Compare if this is the size for which we cached the font metrics.
01128     // We have to do this very dynamically, because 2 views could be painting the same
01129     // stuff, with different zoom levels. So no absolute caching possible.
01130     /*if ( d->m_screenFont )
01131       kdDebug(32500) << " d->m_screenFont->pointSizeFloat()=" << d->m_screenFont->pointSizeFloat() << endl;*/
01132     if ( !d->m_screenFont || kAbs( pointSize - d->m_screenFont->pointSizeFloat() ) > 1E-4 )
01133     {
01134         delete d->m_screenFont;
01135         d->m_screenFont = new QFont( font() );
01136         d->m_screenFont->setPointSizeFloat( pointSize );
01137         delete d->m_screenFontMetrics;
01138         d->m_screenFontMetrics = 0;
01139         //kdDebug(32500) << "KoTextFormat::screenFont created new font with size " << pointSize << endl;
01140     }
01141     return *d->m_screenFont;
01142 }
01143 
01144 const QFontMetrics& KoTextFormat::screenFontMetrics( const KoTextZoomHandler* zh ) const
01145 {
01146     QFont f = screenFont(zh); // don't move inside the if!
01147 
01148     if ( !d->m_screenFontMetrics ) // not calculated, or invalidated by screenFont above
01149     {
01150         //kdDebug(32500) << this << " KoTextFormat::screenFontMetrics pointSize=" << pointSize << " d->m_screenFont->pointSizeFloat()=" << d->m_screenFont->pointSizeFloat() << endl;
01151         d->m_screenFontMetrics = new QFontMetrics( f );
01152         //kdDebug(32500) << "KoTextFormat::screenFontMetrics created new metrics with size " << pointSize << "   height:" << d->m_screenFontMetrics->height() << endl;
01153     }
01154     return *d->m_screenFontMetrics;
01155 }
01156 
01157 const QFontMetrics& KoTextFormat::refFontMetrics() const
01158 {
01159     QFont f = refFont();
01160 
01161     if ( !d->m_refFontMetrics )
01162     {
01163         //kdDebug(32500) << this << " KoTextFormat::refFontMetrics pointSize=" << pointSize << " d->m_refFont->pointSizeFloat()=" << d->m_refFont->pointSizeFloat() << endl;
01164         d->m_refFontMetrics = new QFontMetrics( f );
01165         //kdDebug(32500) << "KoTextFormat::refFontMetrics created new metrics with size " << pointSize << "   height:" << d->m_refFontMetrics->height() << endl;
01166     }
01167     return *d->m_refFontMetrics;
01168 }
01169 
01170 QFont KoTextFormat::smallCapsFont( const KoTextZoomHandler* zh, bool applyZoom ) const
01171 {
01172     QFont font = applyZoom ? screenFont( zh ) : refFont();
01173     QFontMetrics fm = refFontMetrics(); // only used for proportions, so applyZoom doesn't matter
01174     double pointSize = font.pointSize() * ((double)fm.boundingRect("x").height()/(double)fm.boundingRect("X").height());
01175     font.setPointSizeFloat( pointSize );
01176     return font;
01177 }
01178 
01179 int KoTextFormat::charWidth( const KoTextZoomHandler* zh, bool applyZoom, const KoTextStringChar* c,
01180                              const KoTextParag* parag, int i ) const
01181 {
01182     ushort unicode = c->c.unicode();
01183     if ( !c->charStop || unicode == 0xad || unicode == 0x2028 )
01184      return 0;
01185     Q_ASSERT( !c->isCustom() ); // actually it's a bit stupid to call this for custom items
01186     if( c->isCustom() ) {
01187      if( c->customItem()->placement() == KoTextCustomItem::PlaceInline ) {
01188              // customitem width is in LU pixels. Convert to 100%-zoom-pixels (pt2pt==pix2pix)
01189              double w = KoTextZoomHandler::layoutUnitPtToPt( c->customItem()->width );
01190              return qRound( applyZoom ? ( w * zh->zoomFactorX() ) : w );
01191          }
01192          else
01193              return 0;
01194     }
01195     int pixelww;
01196     int r = c->c.row();
01197     if( /*r < 0x06 || r > 0x1f*/ r < 0x06 || (r > 0x1f && !(r > 0xd7 && r < 0xe0)) )
01198     {
01199         // Small caps -> we can't use the cached font metrics from KoTextFormat
01200         if ( attributeFont() == KoTextFormat::ATT_SMALL_CAPS && c->c.upper() != c->c )
01201         {
01202             pixelww = QFontMetrics( smallCapsFont( zh, applyZoom ) ).width( displayedChar( c->c ) );
01203         }
01204         else
01205         // Use the cached font metrics from KoTextFormat
01206         if ( applyZoom )
01207         {
01208         if ( r ) {
01209                 pixelww = this->screenFontMetrics( zh ).width( displayedChar( c->c ) );
01210         } else {
01211                 // Use the m_screenWidths[] array when possible, even faster
01212                 Q_ASSERT( unicode < 256 );
01213         pixelww = d->m_screenWidths[ unicode ];
01214                 // Not in cache yet -> calculate
01215                 if ( pixelww == 0 ) {
01216                     pixelww = this->screenFontMetrics( zh ).width( displayedChar( c->c ) );
01217                     Q_ASSERT( pixelww < 65535 );
01218                     d->m_screenWidths[ unicode ] = pixelww;
01219                 }
01220         }
01221         }
01222         else {
01223             pixelww = this->refFontMetrics().width( displayedChar( c->c ) );
01224     }
01225     }
01226     else {
01227         // Complex text. We need some hacks to get the right metric here
01228         bool smallCaps = ( attributeFont() == KoTextFormat::ATT_SMALL_CAPS && c->c.upper() != c->c );
01229         const QFontMetrics& fontMetrics = smallCaps ? smallCapsFont( zh, applyZoom ) : applyZoom ? screenFontMetrics( zh ) : refFontMetrics();
01230         QString str;
01231         int pos = 0;
01232         if( i > 8 )
01233             pos = i - 8;
01234         int off = i - pos;
01235         int end = QMIN( parag->length(), i + 8 );
01236         while ( pos < end ) {
01237             str += displayedChar( parag->at(pos)->c );
01238             pos++;
01239         }
01240         pixelww = fontMetrics.charWidth( str, off );
01241     }
01242 
01243 #if 0
01244         kdDebug(32500) << "KoTextFormat::charWidth: char=" << QString(c->c) << " format=" << key()
01245                        << ", applyZoom=" << applyZoom << " pixel-width=" << pixelww << endl;
01246 #endif
01247     return pixelww;
01248 }
01249 
01250 int KoTextFormat::height() const
01251 {
01252     if ( d->m_refHeight < 0 )
01253     {
01254         // Calculate height using 100%-zoom font
01255         int h = refFontMetrics().height()+QABS(offsetFromBaseLine());
01256         if ( vAlign() == KoTextFormat::AlignSuperScript )
01257             h += refFontMetrics().height()/2;
01258         else if ( vAlign() == KoTextFormat::AlignSubScript )
01259             h += refFontMetrics().height()/6;
01260 
01261         // Add room for the shadow
01262         if ( d->m_shadowDistanceY != 0 ) {
01263             // pt -> pixel (at 100% zoom)
01264             h += (int)(POINT_TO_INCH( static_cast<double>( KoGlobal::dpiY() ) ) * QABS( d->m_shadowDistanceY ) );
01265         }
01266 
01267         //kdDebug(32500) << "KoTextFormat::height 100%-zoom font says h=" << h << " in LU:" << KoTextZoomHandler::ptToLayoutUnitPt(h) << endl;
01268         // Then scale to LU
01269         d->m_refHeight = qRound( KoTextZoomHandler::ptToLayoutUnitPt( h ) );
01270     }
01271     return d->m_refHeight;
01272 }
01273 
01274 int KoTextFormat::offsetX() const // in LU pixels
01275 {
01276     int off = 0;
01277 #if 0
01278     // Shadow on left -> character is moved to the right
01279     // Wrong if next char has no shadow (they'll run into each other)
01280     // Somehow we should only do this if x == 0 (in the formatter)
01281     if ( d->m_shadowDistanceX < 0 )
01282     {
01283         double lupt = KoTextZoomHandler::ptToLayoutUnitPt( QABS( d->m_shadowDistanceX ) );
01284         off += (int)(POINT_TO_INCH( static_cast<double>( KoGlobal::dpiX() ) ) * lupt );
01285     }
01286 #endif
01287     return off;
01288 }
01289 
01290 int KoTextFormat::offsetY() const // in LU pixels
01291 {
01292     int off = 0;
01293 #if 0
01294     // Shadow on top -> character is moved down
01295     if ( d->m_shadowDistanceY < 0 )
01296     {
01297         double lupt = KoTextZoomHandler::ptToLayoutUnitPt( QABS( d->m_shadowDistanceY ) );
01298         off += (int)(POINT_TO_INCH( static_cast<double>( KoGlobal::dpiY() ) ) * lupt );
01299     }
01300 #endif
01301     return off;
01302 }
01303 
01304 QString KoTextFormat::displayedString( const QString& str )const
01305 {
01306     switch ( m_attributeFont ) {
01307     case ATT_NONE:
01308         return str;
01309     case ATT_UPPER:
01310     case ATT_SMALL_CAPS:
01311         return str.upper();
01312     case ATT_LOWER:
01313         return str.lower();
01314     default:
01315         kdDebug(32500)<<" Error in AttributeStyle \n";
01316         return str;
01317     }
01318 }
01319 
01320 QChar KoTextFormat::displayedChar( QChar c )const
01321 {
01322     if ( c.unicode() == 0xa0 ) // nbsp
01323         return ' ';
01324     switch ( m_attributeFont ) {
01325     case ATT_NONE:
01326         return c;
01327     case ATT_SMALL_CAPS:
01328     case ATT_UPPER:
01329         return c.upper();
01330     case ATT_LOWER:
01331         return c.lower();
01332     default:
01333         kdDebug(32500)<<" Error in AttributeStyle \n";
01334         return c;
01335     }
01336 }
01337 
01338 int KoTextFormat::ascent() const
01339 {
01340     if ( d->m_refAscent < 0 )
01341     {
01342         // Calculate ascent using 100%-zoom font
01343         int h = refFontMetrics().ascent();
01344         if ( offsetFromBaseLine()>0 )
01345             h += offsetFromBaseLine();
01346         if ( vAlign() == KoTextFormat::AlignSuperScript )
01347             h += refFontMetrics().height()/2;
01348         // Then scale to LU
01349         d->m_refAscent = qRound( KoTextZoomHandler::ptToLayoutUnitPt( h ) );
01350         //d->m_refAscent += offsetY();
01351     }
01352     return d->m_refAscent;
01353 }
01354 
01355 int KoTextFormat::descent() const
01356 {
01357     if ( d->m_refDescent < 0 )
01358     {
01359         // Calculate descent using 100%-zoom font
01360         int h = refFontMetrics().descent();
01361         if ( offsetFromBaseLine()<0 )
01362             h -= offsetFromBaseLine();
01363         // Then scale to LU
01364         d->m_refDescent = qRound( KoTextZoomHandler::ptToLayoutUnitPt( h ) );
01365         //d->m_refDescent += offsetY();
01366     }
01367     return d->m_refDescent;
01368 }
01369 
01370 int KoTextFormat::charWidthLU( const KoTextStringChar* c, const KoTextParag* parag, int i ) const
01371 {
01372     // Hmm, we add precision to the least precise one!
01373     // TODO: We should instead implement it here in LU, and let charWidth call it...
01374    return KoTextZoomHandler::ptToLayoutUnitPt( charWidth( 0L, false, c, parag, i ) );
01375 }
01376 
01377 int KoTextFormat::width( const QChar& ch ) const
01378 {
01379     // Warning this doesn't take into account the shadow
01380     return KoTextZoomHandler::ptToLayoutUnitPt( refFontMetrics().width( ch ) );
01381 }
01382 
01383 void KoTextFormat::applyCharStyle( KoCharStyle *_style )
01384 {
01385     d->m_charStyle = _style;
01386 }
01387 
01388 KoCharStyle *KoTextFormat::style() const
01389 {
01390     return d->m_charStyle;
01391 }
01392 
01393 QString KoTextFormat::shadowAsCss(  double shadowDistanceX, double shadowDistanceY, const QColor& shadowColor )
01394 {
01395     // http://www.w3.org/TR/REC-CSS2/text.html#text-shadow-props
01396     // none | [<color> || <length (h)> <length (v)> <length (blur radius, not used here)>] ...
01397     // => none or color length length
01398     if ( shadowDistanceX != 0 || shadowDistanceY != 0 )
01399     {
01400         QString css = shadowColor.name() + " ";
01401         css += QString::number(shadowDistanceX) + "pt ";
01402         css += QString::number(shadowDistanceY) + "pt";
01403         return css;
01404     }
01405     return "none";
01406 }
01407 
01408 QString KoTextFormat::shadowAsCss() const
01409 {
01410     return shadowAsCss( d->m_shadowDistanceX, d->m_shadowDistanceY, d->m_shadowColor );
01411 }
01412 
01413 void KoTextFormat::parseShadowFromCss( const QString& _css )
01414 {
01415     QString css = _css.simplifyWhiteSpace();
01416     if ( css.isEmpty() || css == "none" )
01417     {
01418         d->m_shadowDistanceX = 0;
01419         d->m_shadowDistanceY = 0;
01420         d->m_shadowColor = QColor();
01421     } else
01422     {
01423         QStringList tokens = QStringList::split(' ', css);
01424         if ( tokens.isEmpty() ) {
01425             kdWarning(32500) << "Parse error in text-shadow: " << css << endl;
01426             return;
01427         }
01428         // Check which token looks like a color
01429         QColor col( tokens.first() );
01430         if ( col.isValid() )
01431             tokens.pop_front();
01432         else if ( tokens.count() > 1 )
01433         {
01434             col.setNamedColor( tokens.last() );
01435             if ( col.isValid() )
01436                 tokens.pop_back();
01437         }
01438         d->m_shadowColor = col; // whether valid or not
01439         // Parse x distance
01440         if ( !tokens.isEmpty() ) {
01441             d->m_shadowDistanceX = KoUnit::parseValue( tokens.first() );
01442             tokens.pop_front();
01443         }
01444         // Parse y distance
01445         if ( !tokens.isEmpty() ) {
01446             d->m_shadowDistanceY = KoUnit::parseValue( tokens.first() );
01447             tokens.pop_front();
01448         }
01449         // We ignore whatever else is in the string (e.g. blur radius, other shadows)
01450 
01451     }
01452     update();
01453 }
01454 
01455 QColor KoTextFormat::shadowColor() const
01456 {
01457     if ( d->m_shadowColor.isValid() )
01458         return d->m_shadowColor;
01459     else // CSS says "[If] no color has been specified, the shadow will have the same color as the [text] itself"
01460         return col;
01461 }
01462 
01463 int KoTextFormat::shadowX( KoTextZoomHandler *zh ) const
01464 {
01465     return zh->zoomItX( d->m_shadowDistanceX );
01466 }
01467 
01468 int KoTextFormat::shadowY( KoTextZoomHandler *zh ) const
01469 {
01470     return zh->zoomItY( d->m_shadowDistanceY );
01471 }
01472 
01473 //static
01474 QString KoTextFormat::underlineStyleToString( KoTextFormat::UnderlineStyle _lineType )
01475 {
01476     QString strLineType;
01477     switch ( _lineType )
01478     {
01479     case KoTextFormat::U_SOLID:
01480         strLineType ="solid";
01481         break;
01482     case KoTextFormat::U_DASH:
01483         strLineType ="dash";
01484         break;
01485     case KoTextFormat::U_DOT:
01486         strLineType ="dot";
01487         break;
01488     case KoTextFormat::U_DASH_DOT:
01489         strLineType="dashdot";
01490         break;
01491     case KoTextFormat::U_DASH_DOT_DOT:
01492         strLineType="dashdotdot";
01493         break;
01494     }
01495     return strLineType;
01496 }
01497 
01498 QString KoTextFormat::strikeOutStyleToString( KoTextFormat::StrikeOutStyle _lineType )
01499 {
01500     QString strLineType;
01501     switch ( _lineType )
01502     {
01503     case KoTextFormat::S_SOLID:
01504         strLineType ="solid";
01505         break;
01506     case KoTextFormat::S_DASH:
01507         strLineType ="dash";
01508         break;
01509     case KoTextFormat::S_DOT:
01510         strLineType ="dot";
01511         break;
01512     case KoTextFormat::S_DASH_DOT:
01513         strLineType="dashdot";
01514         break;
01515     case KoTextFormat::S_DASH_DOT_DOT:
01516         strLineType="dashdotdot";
01517         break;
01518     }
01519     return strLineType;
01520 }
01521 
01522 KoTextFormat::UnderlineStyle KoTextFormat::stringToUnderlineStyle( const QString & _str )
01523 {
01524     if ( _str =="solid")
01525         return KoTextFormat::U_SOLID;
01526     else if ( _str =="dash" )
01527         return KoTextFormat::U_DASH;
01528     else if ( _str =="dot" )
01529         return KoTextFormat::U_DOT;
01530     else if ( _str =="dashdot")
01531         return KoTextFormat::U_DASH_DOT;
01532     else if ( _str=="dashdotdot")
01533         return KoTextFormat::U_DASH_DOT_DOT;
01534     else
01535         return KoTextFormat::U_SOLID;
01536 }
01537 
01538 KoTextFormat::StrikeOutStyle KoTextFormat::stringToStrikeOutStyle( const QString & _str )
01539 {
01540     if ( _str =="solid")
01541         return KoTextFormat::S_SOLID;
01542     else if ( _str =="dash" )
01543         return KoTextFormat::S_DASH;
01544     else if ( _str =="dot" )
01545         return KoTextFormat::S_DOT;
01546     else if ( _str =="dashdot")
01547         return KoTextFormat::S_DASH_DOT;
01548     else if ( _str=="dashdotdot")
01549         return KoTextFormat::S_DASH_DOT_DOT;
01550     else
01551         return KoTextFormat::S_SOLID;
01552 }
01553 
01554 QString KoTextFormat::attributeFontToString( KoTextFormat::AttributeStyle _attr )
01555 {
01556     if (_attr == KoTextFormat::ATT_NONE )
01557         return QString("none");
01558     else if ( _attr == KoTextFormat::ATT_UPPER )
01559         return QString("uppercase");
01560     else if ( _attr == KoTextFormat::ATT_LOWER )
01561         return QString("lowercase");
01562     else if ( _attr == KoTextFormat::ATT_SMALL_CAPS )
01563         return QString("smallcaps");
01564     else
01565         return QString("none");
01566 }
01567 
01568 KoTextFormat::AttributeStyle KoTextFormat::stringToAttributeFont( const QString & _str )
01569 {
01570     if ( _str == "none" )
01571         return KoTextFormat::ATT_NONE;
01572     else if ( _str == "uppercase")
01573         return KoTextFormat::ATT_UPPER;
01574     else if ( _str == "lowercase")
01575         return KoTextFormat::ATT_LOWER;
01576     else if ( _str == "smallcaps" )
01577         return KoTextFormat::ATT_SMALL_CAPS;
01578     else
01579         return KoTextFormat::ATT_NONE;
01580 }
01581 
01582 
01583 void KoTextFormat::setHyphenation( bool b )
01584 {
01585     if ( d->m_bHyphenation == b )
01586         return;
01587     d->m_bHyphenation = b;
01588     update();
01589 
01590 }
01591 
01592 void KoTextFormat::setUnderLineWidth( double ulw )
01593 {
01594     if ( d->m_underLineWidth == ulw )
01595         return;
01596     d->m_underLineWidth = ulw;
01597     update();
01598 
01599 }
01600 
01601 void KoTextFormat::setLanguage( const QString & _lang)
01602 {
01603     if ( m_language == _lang )
01604         return;
01605     m_language = _lang;
01606     update();
01607 }
01608 
01609 QStringList KoTextFormat::underlineTypeList()
01610 {
01611     QStringList lst;
01612     lst <<i18n("Underline Style", "None");
01613     lst <<i18n("Single");
01614     lst <<i18n("Double");
01615     lst <<i18n("Simple Bold");
01616     lst <<i18n("Wave");
01617     return lst;
01618 }
01619 
01620 QStringList KoTextFormat::strikeOutTypeList()
01621 {
01622     QStringList lst;
01623     lst <<i18n("Strikeout Style", "None");
01624     lst <<i18n("Single");
01625     lst <<i18n("Double");
01626     lst <<i18n("Simple Bold");
01627     return lst;
01628 }
01629 
01630 QStringList KoTextFormat::fontAttributeList()
01631 {
01632     QStringList lst;
01633     lst <<i18n("Normal");
01634     lst <<i18n("Uppercase");
01635     lst <<i18n("Lowercase");
01636     lst <<i18n("Small Caps");
01637     return lst;
01638 }
01639 
01640 QStringList KoTextFormat::underlineStyleList()
01641 {
01642     QStringList lst;
01643     lst <<"_________";   // SOLID
01644     lst <<"___ ___ __";  // DASH
01645     lst <<"_ _ _ _ _ _"; // DOT
01646     lst <<"___ _ ___ _"; // DASH_DOT
01647     lst <<"___ _ _ ___"; // DASH_DOT_DOT
01648     return lst;
01649 }
01650 
01651 QStringList KoTextFormat::strikeOutStyleList()
01652 {
01653     QStringList lst;
01654     lst <<"_________";   // SOLID
01655     lst <<"___ ___ __";  // DASH
01656     lst <<"_ _ _ _ _ _"; // DOT
01657     lst <<"___ _ ___ _"; // DASH_DOT
01658     lst <<"___ _ _ ___"; // DASH_DOT_DOT
01659     return lst;
01660 }
01661 
01662 #ifndef NDEBUG
01663 void KoTextFormat::printDebug()
01664 {
01665     QString col = color().isValid() ? color().name() : QString("(default)");
01666     kdDebug(32500) << "format '" << key() << "' (" << (void*)this << "):"
01667                    << " refcount: " << ref
01668                    << " realfont: " << QFontInfo( font() ).family()
01669                    << " color: " << col << " shadow=" << shadowAsCss() << endl;
01670 }
01671 #endif
01672 
01674 
01675 KoTextFormatCollection::KoTextFormatCollection()
01676     : cKey( 307 )//, sheet( 0 )
01677 {
01678 #ifdef DEBUG_COLLECTION
01679     kdDebug(32500) << "KoTextFormatCollection::KoTextFormatCollection " << this << endl;
01680 #endif
01681     defFormat = new KoTextFormat( QApplication::font(), QColor(), KGlobal::locale()->language(), false );
01682     lastFormat = cres = 0;
01683     cflags = -1;
01684     cKey.setAutoDelete( TRUE );
01685     cachedFormat = 0;
01686 }
01687 
01688 KoTextFormatCollection::KoTextFormatCollection( const QFont& defaultFont, const QColor& defaultColor, const QString & defaultLanguage, bool defaultHyphenation )
01689     : cKey( 307 )
01690 {
01691 #ifdef DEBUG_COLLECTION
01692     kdDebug(32500) << "KoTextFormatCollection::KoTextFormatCollection " << this << endl;
01693 #endif
01694     defFormat = new KoTextFormat( defaultFont, defaultColor, defaultLanguage, defaultHyphenation );
01695     lastFormat = cres = 0;
01696     cflags = -1;
01697     cKey.setAutoDelete( TRUE );
01698     cachedFormat = 0;
01699 }
01700 
01701 KoTextFormatCollection::~KoTextFormatCollection()
01702 {
01703 #ifdef DEBUG_COLLECTION
01704     kdDebug(32500) << "KoTextFormatCollection::~KoTextFormatCollection " << this << endl;
01705 #endif
01706     delete defFormat;
01707     defFormat = 0;
01708 }
01709 
01710 KoTextFormat *KoTextFormatCollection::format( const KoTextFormat *f )
01711 {
01712     if ( f->parent() == this || f == defFormat ) {
01713 #ifdef DEBUG_COLLECTION
01714         kdDebug(32500) << " format(f) need '" << f->key() << "', best case!" << endl;
01715 #endif
01716     lastFormat = const_cast<KoTextFormat*>(f);
01717     lastFormat->addRef();
01718     return lastFormat;
01719     }
01720 
01721     if ( f == lastFormat || ( lastFormat && f->key() == lastFormat->key() ) ) {
01722 #ifdef DEBUG_COLLECTION
01723         kdDebug(32500) << " format(f) need '" << f->key() << "', good case!" << endl;
01724 #endif
01725     lastFormat->addRef();
01726     return lastFormat;
01727     }
01728 
01729 #if 0 // #### disabled, because if this format is not in the
01730  // formatcollection, it doesn't get the painter through
01731  // KoTextFormatCollection::setPainter() which breaks printing on
01732  // windows
01733     if ( f->isAnchor() ) {
01734     lastFormat = createFormat( *f );
01735     lastFormat->collection = 0;
01736     return lastFormat;
01737     }
01738 #endif
01739 
01740     KoTextFormat *fm = cKey.find( f->key() );
01741     if ( fm ) {
01742 #ifdef DEBUG_COLLECTION
01743         kdDebug(32500) << " format(f) need '" << f->key() << "', normal case!" << endl;
01744 #endif
01745     lastFormat = fm;
01746     lastFormat->addRef();
01747     return lastFormat;
01748     }
01749 
01750     if ( f->key() == defFormat->key() )
01751     return defFormat;
01752 
01753 #ifdef DEBUG_COLLECTION
01754     kdDebug(32500) << " format(f) need '" << f->key() << "', worst case!" << endl;
01755 #endif
01756     lastFormat = createFormat( *f );
01757     lastFormat->collection = this;
01758     cKey.insert( lastFormat->key(), lastFormat );
01759     Q_ASSERT( f->key() == lastFormat->key() );
01760     return lastFormat;
01761 }
01762 
01763 KoTextFormat *KoTextFormatCollection::format( const KoTextFormat *of, const KoTextFormat *nf, int flags )
01764 {
01765     if ( cres && kof == of->key() && knf == nf->key() && cflags == flags ) {
01766 #ifdef DEBUG_COLLECTION
01767     kdDebug(32500) << " format(of,nf,flags) mix of '" << of->key() << "' and '" << nf->key() << "', best case!" << endl;
01768 #endif
01769     cres->addRef();
01770     return cres;
01771     }
01772 
01773 #ifdef DEBUG_COLLECTION
01774     kdDebug(32500) << " format(of,nf," << flags << ") calling createFormat(of=" << of << " " << of->key() << ")" << endl;
01775 #endif
01776     cres = createFormat( *of );
01777     kof = of->key();
01778     knf = nf->key();
01779     cflags = flags;
01780 
01781 #ifdef DEBUG_COLLECTION
01782     kdDebug(32500) << " format(of,nf," << flags << ") calling copyFormat(nf=" << nf << " " << nf->key() << ")" << endl;
01783 #endif
01784     cres->copyFormat( *nf, flags );
01785 
01786     KoTextFormat *fm = cKey.find( cres->key() );
01787     if ( !fm ) {
01788 #ifdef DEBUG_COLLECTION
01789     kdDebug(32500) << " format(of,nf,flags) mix of '" << of->key() << "' and '" << nf->key() << ", worst case!" << endl;
01790 #endif
01791     cres->collection = this;
01792     cKey.insert( cres->key(), cres );
01793     } else {
01794 #ifdef DEBUG_COLLECTION
01795     kdDebug(32500) << " format(of,nf,flags) mix of '" << of->key() << "' and '" << nf->key() << ", good case!" << endl;
01796 #endif
01797     delete cres;
01798     cres = fm;
01799     cres->addRef();
01800     }
01801 
01802     return cres;
01803 }
01804 
01805 #if 0
01806 KoTextFormat *KoTextFormatCollection::format( const QFont &f, const QColor &c, const QString & language, bool hyphen )
01807 {
01808     if ( cachedFormat && cfont == f && ccol == c ) {
01809 #ifdef DEBUG_COLLECTION
01810     kdDebug(32500) << " format of font and col '" << cachedFormat->key() << "' - best case" << endl;
01811 #endif
01812     cachedFormat->addRef();
01813     return cachedFormat;
01814     }
01815 
01816     QString key = KoTextFormat::getKey( f, c, FALSE, KoTextFormat::AlignNormal );
01817     cachedFormat = cKey.find( key );
01818     cfont = f;
01819     ccol = c;
01820 
01821     if ( cachedFormat ) {
01822 #ifdef DEBUG_COLLECTION
01823     kdDebug(32500) << " format of font and col '" << cachedFormat->key() << "' - good case" << endl;
01824 #endif
01825     cachedFormat->addRef();
01826     return cachedFormat;
01827     }
01828 
01829     if ( key == defFormat->key() )
01830     return defFormat;
01831 
01832     cachedFormat = createFormat( f, c, language, hyphen );
01833     cachedFormat->collection = this;
01834     cKey.insert( cachedFormat->key(), cachedFormat );
01835     if ( cachedFormat->key() != key )
01836     kdWarning() << "ASSERT: keys for format not identical: '" << cachedFormat->key() << " '" << key << "'" << endl;
01837 #ifdef DEBUG_COLLECTION
01838     kdDebug(32500) << " format of font and col '" << cachedFormat->key() << "' - worst case" << endl;
01839 #endif
01840     return cachedFormat;
01841 }
01842 #endif
01843 
01844 void KoTextFormatCollection::remove( KoTextFormat *f )
01845 {
01846     if ( lastFormat == f )
01847     lastFormat = 0;
01848     if ( cres == f )
01849     cres = 0;
01850     if ( cachedFormat == f )
01851     cachedFormat = 0;
01852     cKey.remove( f->key() );
01853 }
01854 
01855 void KoTextFormatCollection::zoomChanged()
01856 {
01857     QDictIterator<KoTextFormat> it( cKey );
01858     for ( ; it.current(); ++it ) {
01859         it.current()->zoomChanged();
01860     }
01861 }
01862 
01863 #if 0
01864 void KoTextFormatCollection::setPainter( QPainter *p )
01865 {
01866     QDictIterator<KoTextFormat> it( cKey );
01867     KoTextFormat *f;
01868     while ( ( f = it.current() ) ) {
01869     ++it;
01870     f->setPainter( p );
01871     }
01872 }
01873 #endif
01874 
01875 #ifndef NDEBUG
01876 void KoTextFormatCollection::debug()
01877 {
01878     kdDebug(32500) << "------------ KoTextFormatCollection: debug --------------- BEGIN" << endl;
01879     kdDebug(32500) << "Default Format: '" << defFormat->key() << "' (" << (void*)defFormat << "): realfont: " << QFontInfo( defFormat->font() ).family() << endl;
01880     QDictIterator<KoTextFormat> it( cKey );
01881     for ( ; it.current(); ++it ) {
01882          Q_ASSERT(it.currentKey() == it.current()->key());
01883          if(it.currentKey() != it.current()->key())
01884              kdDebug(32500) << "**** MISMATCH key=" << it.currentKey() << " (see line below for format)" << endl;
01885      it.current()->printDebug();
01886     }
01887     kdDebug(32500) << "------------ KoTextFormatCollection: debug --------------- END" << endl;
01888 }
01889 #endif
KDE Home | KDE Accessibility Home | Description of Access Keys