/* This file is part of the KDE project Copyright (C) 2001-2006 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoTextFormat.h" #include "KoTextParag.h" #include "KoTextZoomHandler.h" #include "KoStyleCollection.h" #include "KoOasisContext.h" #include #include #include #include #include #include #include #include void KoTextFormat::KoTextFormatPrivate::clearCache() { delete m_screenFontMetrics; m_screenFontMetrics = 0L; delete m_screenFont; m_screenFont = 0L; delete m_refFontMetrics; m_refFontMetrics = 0L; delete m_refFont; m_refFont = 0L; m_refAscent = -1; m_refDescent = -1; m_refHeight = -1; memset( m_screenWidths, 0, 256 * sizeof( ushort ) ); } void KoTextFormat::zoomChanged() { delete d->m_screenFontMetrics; d->m_screenFontMetrics = 0; delete d->m_screenFont; d->m_screenFont = 0; memset( d->m_screenWidths, 0, 256 * sizeof( ushort ) ); } KoTextFormat::KoTextFormat() { //linkColor = TRUE; ref = 0; missp = FALSE; va = AlignNormal; collection = 0; //// kotext: WYSIWYG works much much better with scalable fonts -> force it to be scalable fn.setStyleStrategy( TQFont::ForceOutline ); d = new KoTextFormatPrivate; m_textUnderlineColor=TQColor(); m_underlineType = U_NONE; m_strikeOutType = S_NONE; m_underlineStyle = U_SOLID; m_strikeOutStyle = S_SOLID; m_language = KGlobal::locale()->language(); d->m_bHyphenation = false; d->m_underLineWidth = 1.0; d->m_shadowDistanceX = 0; d->m_shadowDistanceY = 0; d->m_relativeTextSize = 0.66; d->m_offsetFromBaseLine= 0; d->m_bWordByWord = false; m_attributeFont = ATT_NONE; //// //#ifdef DEBUG_COLLECTION // kdDebug(32500) << "KoTextFormat simple ctor, no addRef, no generateKey ! " << this << endl; //#endif } KoTextFormat::KoTextFormat( const TQFont &f, const TQColor &c, const TQString &_language, bool hyphenation, KoTextFormatCollection *tqparent ) : fn( f ), col( c ) /*fm( TQFontMetrics( f ) ),*/ //linkColor( TRUE ) { #ifdef DEBUG_COLLECTION kdDebug(32500) << "KoTextFormat with font & color & tqparent (" << tqparent << "), addRef. " << this << endl; #endif int pointSize; if ( f.pointSize() == -1 ) // font was set with a pixelsize, we need a pointsize! pointSize = (int)( ( (double)fn.pixelSize() * 72.0 ) / (double)KoGlobal::dpiY() ); else pointSize = f.pointSize(); fn.setPointSize( pointSize ); // WYSIWYG works much much better with scalable fonts -> force it to be scalable fn.setStyleStrategy( TQFont::ForceOutline ); ref = 0; collection = tqparent; //leftBearing = fm.minLeftBearing(); //rightBearing = fm.minRightBearing(); //hei = fm.height(); //asc = fm.ascent(); //dsc = fm.descent(); missp = FALSE; va = AlignNormal; //// kotext d = new KoTextFormatPrivate; m_textUnderlineColor = TQColor(); m_underlineType = U_NONE; m_strikeOutType = S_NONE; m_underlineStyle = U_SOLID; m_strikeOutStyle = S_SOLID; m_language = _language; d->m_shadowDistanceX = 0; d->m_shadowDistanceY = 0; d->m_relativeTextSize= 0.66; d->m_offsetFromBaseLine = 0; d->m_bWordByWord = false; d->m_charStyle = 0L; d->m_bHyphenation = hyphenation; d->m_underLineWidth = 1.0; m_attributeFont = ATT_NONE; //// generateKey(); addRef(); } KoTextFormat::KoTextFormat( const TQFont &_font, VerticalAlignment _valign, const TQColor & _color, const TQColor & _backGroundColor, const TQColor & _underlineColor, KoTextFormat::UnderlineType _underlineType, KoTextFormat::UnderlineStyle _underlineStyle, KoTextFormat::StrikeOutType _strikeOutType, KoTextFormat::StrikeOutStyle _strikeOutStyle, KoTextFormat::AttributeStyle _fontAttribute, const TQString &_language, double _relativeTextSize, int _offsetFromBaseLine, bool _wordByWord, bool _hyphenation, double _shadowDistanceX, double _shadowDistanceY, const TQColor& _shadowColor ) { ref = 0; collection = 0; fn = _font; fn.setStyleStrategy( TQFont::ForceOutline ); col = _color; missp = false; va = _valign; d = new KoTextFormatPrivate; m_textBackColor = _backGroundColor; m_textUnderlineColor = _underlineColor; m_underlineType = _underlineType; m_strikeOutType = _strikeOutType; m_underlineStyle = _underlineStyle; m_strikeOutStyle = _strikeOutStyle; m_language = _language; d->m_bHyphenation = _hyphenation; d->m_underLineWidth = 1.0; d->m_shadowDistanceX = _shadowDistanceX; d->m_shadowDistanceY = _shadowDistanceY; d->m_shadowColor = _shadowColor; d->m_relativeTextSize = _relativeTextSize; d->m_offsetFromBaseLine = _offsetFromBaseLine; d->m_bWordByWord = _wordByWord; m_attributeFont = _fontAttribute; d->m_charStyle = 0L; //// generateKey(); addRef(); } KoTextFormat::KoTextFormat( const KoTextFormat &f ) { d = 0L; operator=( f ); } KoTextFormat::~KoTextFormat() { //// kotext addition // Removing a format that is in the collection is forbidden, in fact. // It should have been removed from the collection before being deleted. #ifndef NDEBUG if ( tqparent() && tqparent()->defaultFormat() ) // not when destroying the collection assert( ! ( tqparent()->dict().tqfind( key() ) == this ) ); // (has to be the same pointer, not only the same key) #endif delete d; //// } KoTextFormat& KoTextFormat::operator=( const KoTextFormat &f ) { #ifdef DEBUG_COLLECTION kdDebug(32500) << "KoTextFormat::operator= " << this << " (copying " << &f << "). Will addRef" << endl; #endif ref = 0; collection = 0; // f might be in the collection, but we are not fn = f.fn; col = f.col; //fm = f.fm; //leftBearing = f.leftBearing; //rightBearing = f.rightBearing; //hei = f.hei; //asc = f.asc; //dsc = f.dsc; missp = f.missp; va = f.va; m_key = f.m_key; //linkColor = f.linkColor; //// kotext addition delete d; d = new KoTextFormatPrivate; m_textBackColor=f.m_textBackColor; m_textUnderlineColor=f.m_textUnderlineColor; m_underlineType = f.m_underlineType; m_strikeOutType = f.m_strikeOutType; m_underlineStyle = f.m_underlineStyle; m_strikeOutStyle = f.m_strikeOutStyle; m_language = f.m_language; d->m_bHyphenation=f.d->m_bHyphenation; d->m_underLineWidth=f.d->m_underLineWidth; d->m_shadowDistanceX = f.d->m_shadowDistanceX; d->m_shadowDistanceY = f.d->m_shadowDistanceY; d->m_shadowColor = f.d->m_shadowColor; d->m_relativeTextSize = f.d->m_relativeTextSize; d->m_offsetFromBaseLine = f.d->m_offsetFromBaseLine; d->m_bWordByWord = f.d->m_bWordByWord; m_attributeFont = f.m_attributeFont; d->m_charStyle = 0L; //// addRef(); return *this; } // Helper for load static void importTextPosition( const TQString& text_position, double fontSize, KoTextFormat::VerticalAlignment& value, double& relativetextsize, int& offset, KoOasisContext& context ) { //OO: [] //Examples: "super" or "super 58%" or "82% 58%" (where 82% is the vertical position) TQStringList lst = TQStringList::split( ' ', text_position ); if ( !lst.isEmpty() ) { TQString textPos = lst.front().stripWhiteSpace(); TQString textSize; lst.pop_front(); if ( !lst.isEmpty() ) textSize = lst.front().stripWhiteSpace(); // Workaround bug in KOffice-1.4: it saved '0% 66%' for normal text if ( context.generator().startsWith( "KOffice/1.4" ) && text_position.startsWith( "0%" ) ) { //kdDebug(32500) << "Detected koffice-1.4 bug in text-position, assuming Normal text" << endl; value = KoTextFormat::AlignNormal; return; } if ( textPos.endsWith("%") && textPos != "0% 100%" && textPos != "0%" ) { textPos.truncate( textPos.length() - 1 ); double val = textPos.toDouble(); offset = tqRound( fontSize * val / 100.0 ); value = KoTextFormat::AlignCustom; } else if ( textPos == "super" ) value = KoTextFormat::AlignSuperScript; else if ( textPos == "sub" ) value = KoTextFormat::AlignSubScript; else value = KoTextFormat::AlignNormal; if ( !textSize.isEmpty() && textSize.endsWith("%") ) { textSize.truncate( textSize.length() - 1 ); relativetextsize = textSize.toDouble() / 100; // e.g. 0.58 } } else value = KoTextFormat::AlignNormal; } // OASIS 14.2.29 static void importOasisUnderline( const TQString& type, const TQString& style, KoTextFormat::UnderlineType& underline, KoTextFormat::UnderlineStyle& styleline ) { if ( type == "single" ) underline = KoTextFormat::U_SIMPLE; else if ( type == "double" ) underline = KoTextFormat::U_DOUBLE; else if ( type == "none" ) underline = KoTextFormat::U_NONE; else if ( style.isEmpty() || style == "none" ) underline = KoTextFormat::U_NONE; else underline = KoTextFormat::U_SIMPLE; // OO exports empty type, and style=solid, for normal underline styleline = KoTextFormat::U_SOLID; // assume "solid" if unknown if ( style == "dotted" ) styleline = KoTextFormat::U_DOT; else if ( style == "dash" || style == "long-dash" ) // not in kotext styleline = KoTextFormat::U_DASH; else if ( style == "dot-dash" ) styleline = KoTextFormat::U_DASH_DOT; else if ( style == "dot-dot-dash" ) styleline = KoTextFormat::U_DASH_DOT_DOT; else if ( style == "wave" ) underline = KoTextFormat::U_WAVE; // TODO bold. But this is another attribute in OASIS (text-underline-width), which makes sense. // We should separate them in kotext... } TQString exportOasisUnderline( KoTextFormat::UnderlineStyle styleline ) { switch( styleline ) { case KoTextFormat::U_DOT: return "dotted"; case KoTextFormat::U_DASH: return "dash"; case KoTextFormat::U_DASH_DOT: return "dot-dash"; case KoTextFormat::U_DASH_DOT_DOT: return "dot-dot-dash"; default: return "solid"; } } // Helper for load. Legacy OO format. static void importUnderline( const TQString& in, KoTextFormat::UnderlineType& underline, KoTextFormat::UnderlineStyle& styleline ) { underline = KoTextFormat::U_SIMPLE; styleline = KoTextFormat::U_SOLID; if ( in == "none" ) underline = KoTextFormat::U_NONE; else if ( in == "single" ) styleline = KoTextFormat::U_SOLID; else if ( in == "double" ) { underline = KoTextFormat::U_DOUBLE; } else if ( in == "dotted" || in == "bold-dotted" ) // bold-dotted not in libkotext styleline = KoTextFormat::U_DOT; else if ( in == "dash" // those are not in libkotext: || in == "long-dash" || in == "bold-dash" || in == "bold-long-dash" ) styleline = KoTextFormat::U_DASH; else if ( in == "dot-dash" || in == "bold-dot-dash") // not in libkotext styleline = KoTextFormat::U_DASH_DOT; // tricky ;) else if ( in == "dot-dot-dash" || in == "bold-dot-dot-dash") // not in libkotext styleline = KoTextFormat::U_DASH_DOT_DOT; // this is getting fun... else if ( in == "wave" || in == "bold-wave" // not in libkotext || in == "double-wave" // not in libkotext || in == "small-wave" ) { // not in libkotext underline = KoTextFormat::U_WAVE; } else if( in == "bold" ) { underline = KoTextFormat::U_SIMPLE_BOLD; } else kdWarning() << k_funcinfo << " unsupported text-underline value: " << in << endl; } void KoTextFormat::load( KoOasisContext& context ) { KoStyleStack& styleStack = context.styleStack(); styleStack.setTypeProperties( "text" ); // load all style attributes from "style:text-properties" if ( styleStack.hasAttributeNS( KoXmlNS::fo, "color" ) ) { // 3.10.3 col.setNamedColor( styleStack.attributeNS( KoXmlNS::fo, "color" ) ); // #rrggbb format } if ( styleStack.hasAttributeNS( KoXmlNS::fo, "font-family" ) // 3.10.9 || styleStack.hasAttributeNS( KoXmlNS::style, "font-name") ) { // 3.10.8 // Hmm, the remove "'" could break it's in the middle of the fontname... TQString fontName = styleStack.attributeNS( KoXmlNS::fo, "font-family" ).remove( "'" ); if (fontName.isEmpty()) { // ##### TODO. This is wrong. style:font-name refers to a font-decl entry. // We have to look it up there, and retrieve _all_ font attributes from it, not just the name. fontName = styleStack.attributeNS( KoXmlNS::style, "font-name" ).remove( "'" ); } // 'Thorndale' is not known outside OpenOffice so we substitute it // with 'Times New Roman' that looks nearly the same. if ( fontName == "Thorndale" ) fontName = "Times New Roman"; fontName.remove(TQRegExp("\\sCE$")); // Arial CE -> Arial fn.setFamily( fontName ); } if ( styleStack.hasAttributeNS( KoXmlNS::fo, "font-size" ) ) { // 3.10.14 double pointSize = styleStack.fontSize(); fn.setPointSizeFloat( pointSize ); } if ( styleStack.hasAttributeNS( KoXmlNS::fo, "font-weight" ) ) { // 3.10.24 TQString fontWeight = styleStack.attributeNS( KoXmlNS::fo, "font-weight" ); int boldness; if ( fontWeight == "normal" ) boldness = 50; else if ( fontWeight == "bold" ) boldness = 75; else // XSL/CSS has 100,200,300...900. Not the same scale as TQt! // See http://www.w3.org/TR/2001/REC-xsl-20011015/slice7.html#font-weight boldness = fontWeight.toInt() / 10; fn.setWeight( boldness ); } if ( styleStack.hasAttributeNS( KoXmlNS::fo, "font-style" ) ) // 3.10.19 if ( styleStack.attributeNS( KoXmlNS::fo, "font-style" ) == "italic" || styleStack.attributeNS( KoXmlNS::fo, "font-style" ) == "oblique" ) { // no difference in kotext fn.setItalic( true ); } d->m_bWordByWord = styleStack.attributeNS( KoXmlNS::style, "text-underline-mode" ) == "skip-white-space"; // TODO style:text-line-through-mode #if 0 // OO compat code, to move to OO import filter d->m_bWordByWord = (styleStack.hasAttributeNS( KoXmlNS::fo, "score-spaces")) // 3.10.25 && (styleStack.attributeNS( KoXmlNS::fo, "score-spaces") == "false"); if( styleStack.hasAttributeNS( KoXmlNS::style, "text-crossing-out" )) { // 3.10.6 TQString strikeOutType = styleStack.attributeNS( KoXmlNS::style, "text-crossing-out" ); if( strikeOutType =="double-line") m_strikeOutType = S_DOUBLE; else if( strikeOutType =="single-line") m_strikeOutType = S_SIMPLE; else if( strikeOutType =="thick-line") m_strikeOutType = S_SIMPLE_BOLD; // not supported by KWord: "slash" and "X" // not supported by OO: stylelines (solid, dash, dot, dashdot, dashdotdot) } #endif if ( styleStack.hasAttributeNS( KoXmlNS::style, "text-underline-type" ) || styleStack.hasAttributeNS( KoXmlNS::style, "text-underline-style" ) ) { // OASIS 14.4.28 importOasisUnderline( styleStack.attributeNS( KoXmlNS::style, "text-underline-type" ), styleStack.attributeNS( KoXmlNS::style, "text-underline-style" ), m_underlineType, m_underlineStyle ); } else if ( styleStack.hasAttributeNS( KoXmlNS::style, "text-underline" ) ) { // OO compat (3.10.22), to be moved out importUnderline( styleStack.attributeNS( KoXmlNS::style, "text-underline" ), m_underlineType, m_underlineStyle ); } TQString underLineColor = styleStack.attributeNS( KoXmlNS::style, "text-underline-color" ); // OO 3.10.23, OASIS 14.4.31 if ( !underLineColor.isEmpty() && underLineColor != "font-color" ) m_textUnderlineColor.setNamedColor( underLineColor ); if ( styleStack.hasAttributeNS( KoXmlNS::style, "text-line-through-type" ) ) { // OASIS 14.4.7 // Reuse code for loading underlines, and convert to strikeout enum (if not wave) UnderlineType uType; UnderlineStyle uStyle; importOasisUnderline( styleStack.attributeNS( KoXmlNS::style, "text-line-through-type" ), styleStack.attributeNS( KoXmlNS::style, "text-line-through-style" ), uType, uStyle ); m_strikeOutType = S_NONE; if ( uType != U_WAVE ) m_strikeOutType = (StrikeOutType)uType; m_strikeOutStyle = (StrikeOutStyle)uStyle; } // Text position va = AlignNormal; d->m_relativeTextSize = 0.58; d->m_offsetFromBaseLine = 0; if( styleStack.hasAttributeNS( KoXmlNS::style, "text-position")) { // OO 3.10.7 importTextPosition( styleStack.attributeNS( KoXmlNS::style, "text-position"), fn.pointSizeFloat(), va, d->m_relativeTextSize, d->m_offsetFromBaseLine, context ); } // Small caps, lowercase, uppercase m_attributeFont = ATT_NONE; if ( styleStack.hasAttributeNS( KoXmlNS::fo, "font-variant" ) // 3.10.1 || styleStack.hasAttributeNS( KoXmlNS::fo, "text-transform" ) ) { // 3.10.2 bool smallCaps = styleStack.attributeNS( KoXmlNS::fo, "font-variant" ) == "small-caps"; if ( smallCaps ) { m_attributeFont = ATT_SMALL_CAPS; } else { TQString textTransform = styleStack.attributeNS( KoXmlNS::fo, "text-transform" ); if ( textTransform == "uppercase" ) m_attributeFont = ATT_UPPER; else if ( textTransform == "lowercase" ) m_attributeFont = ATT_LOWER; // TODO in KWord: "capitalize". } } if ( styleStack.hasAttributeNS( KoXmlNS::fo, "language") ) { // 3.10.17 m_language = styleStack.attributeNS( KoXmlNS::fo, "language"); const TQString country = styleStack.attributeNS( KoXmlNS::fo, "country" ); if ( !country.isEmpty() ) { m_language += '_'; m_language += country; } } if ( styleStack.hasAttributeNS( KoXmlNS::fo, "background-color") ) { TQString tmp = styleStack.attributeNS( KoXmlNS::fo, "background-color"); if (tmp != "transparent") m_textBackColor.setNamedColor( tmp ); } if ( styleStack.hasAttributeNS( KoXmlNS::fo, "text-shadow") ) { // 3.10.21 parseShadowFromCss( styleStack.attributeNS( KoXmlNS::fo, "text-shadow") ); } d->m_bHyphenation = true; if ( styleStack.hasAttributeNS( KoXmlNS::fo, "hyphenate" ) ) // it's a character property in OASIS (but not in OO-1.1) d->m_bHyphenation = styleStack.attributeNS( KoXmlNS::fo, "hyphenate" ) == "true"; /* Missing properties: style:use-window-font-color, 3.10.4 - this is what KWord uses by default (fg color from the color style) OO also switches to another color when necessary to avoid dark-on-dark and light-on-light cases. (that is TODO in KWord) style:text-outline, 3.10.5 - not implemented in kotext style:font-family-generic, 3.10.10 - roman, swiss, modern -> map to a font? style:font-style-name, 3.10.11 - can be ignored, says DV, the other ways to specify a font are more precise style:font-pitch, 3.10.12 - fixed or variable -> map to a font? style:font-charset, 3.10.14 - not necessary with TQt style:font-size-rel, 3.10.15 - TODO in StyleStack::fontSize() fo:letter-spacing, 3.10.16 - not implemented in kotext style:text-relief, 3.10.20 - not implemented in kotext style:letter-kerning, 3.10.20 - not implemented in kotext style:text-blinking, 3.10.27 - not implemented in kotext IIRC style:text-combine, 3.10.29/30 - not implemented, see http://www.w3.org/TR/WD-i18n-format/ style:text-emphasis, 3.10.31 - not implemented in kotext style:text-scale, 3.10.33 - not implemented in kotext style:text-rotation-angle, 3.10.34 - not implemented in kotext (kpr rotates whole objects) style:text-rotation-scale, 3.10.35 - not implemented in kotext (kpr rotates whole objects) style:punctuation-wrap, 3.10.36 - not implemented in kotext */ d->m_underLineWidth = 1.0; generateKey(); addRef(); } void KoTextFormat::save( KoGenStyle& gs, KoSavingContext& context, KoTextFormat * refFormat ) const { KoGenStyle::PropertyType tt = KoGenStyle::TextType; if ( !refFormat || this->color() != refFormat->color() ) { gs.addProperty( "fo:color", col.isValid() ? col.name() : "#000000", tt ); } if ( !refFormat || this->font().family() != refFormat->font().family() ) { gs.addProperty( "style:font-name", fn.family(), tt ); context.addFontFace( fn.family() ); } if ( !refFormat || this->pointSize() != refFormat->pointSize() ) { gs.addPropertyPt( "fo:font-size", fn.pointSize(), tt ); } int w = fn.weight(); if ( !refFormat || w != refFormat->font().weight() ) { gs.addProperty( "fo:font-weight", w == 50 ? "normal" : w == 75 ? "bold" : TQString::number( tqRound( w / 10 ) * 100 ), tt ); } if ( !refFormat || this->font().italic() != refFormat->font().italic() ) { gs.addProperty( "fo:font-style", fn.italic() ? "italic" : "normal", tt ); } if ( !refFormat || this->wordByWord() != refFormat->wordByWord() ) { gs.addProperty( "style:text-underline-mode", d->m_bWordByWord ? "skip-white-space" : "continuous", tt ); } if ( !refFormat || this->underlineType() != refFormat->underlineType() || this->underlineStyle() !=refFormat->underlineStyle() ) { gs.addProperty( "style:text-underline-type", m_underlineType == U_NONE ? "none" : m_underlineType == U_DOUBLE ? "double" : "single", tt ); TQString styleline; if ( m_underlineType == U_WAVE ) styleline = "wave"; else styleline = exportOasisUnderline( m_underlineStyle ); gs.addProperty( "style:text-underline-style", m_underlineType == U_NONE ? "none" : styleline, tt ); } if ( !refFormat || this->textUnderlineColor() !=refFormat->textUnderlineColor() ) { gs.addProperty( "style:text-underline-color", m_textUnderlineColor.isValid() ? m_textUnderlineColor.name() : "font-color", tt ); } if ( !refFormat || this->strikeOutType() != refFormat->strikeOutType() || this->strikeOutStyle()!= refFormat->strikeOutStyle() ) { if ( m_strikeOutType != S_NONE ) { // TODO U_SIMPLE_BOLD // TODO style:text-line-through-mode gs.addProperty( "style:text-line-through-type", m_strikeOutType == S_DOUBLE ? "double" : "single", tt ); const TQString styleline = exportOasisUnderline( (UnderlineStyle) m_strikeOutStyle ); gs.addProperty( "style:text-line-through-style", styleline, tt ); //gs.addProperty( "style:text-line-through-color", ...) TODO in kotext } else { gs.addProperty( "style:text-line-through-type", "none", tt ); gs.addProperty( "style:text-line-through-style", "none", tt ); } } if ( !refFormat || (this->vAlign() != refFormat->vAlign()) || (this->relativeTextSize() != refFormat->relativeTextSize()) ) { TQString textPos; if ( d->m_offsetFromBaseLine != 0 ) textPos = TQString::number( 100 * d->m_offsetFromBaseLine / fn.pointSizeFloat() ) + '%'; else if ( va == AlignSuperScript ) textPos = "super"; else if ( va == AlignSubScript ) textPos = "sub"; else textPos = "0%"; // AlignNormal if ( va != AlignNormal ) { textPos += ' '; textPos += TQString::number( d->m_relativeTextSize * 100 ); textPos += '%'; } gs.addProperty( "style:text-position", textPos, tt ); } if( !refFormat || this->attributeFont() != refFormat->attributeFont()) { if ( m_attributeFont == ATT_SMALL_CAPS ) { gs.addProperty( "fo:font-variant", "small-caps", tt ); gs.addProperty( "fo:text-transform", "none", tt ); } else if ( m_attributeFont == ATT_UPPER ) { gs.addProperty( "fo:font-variant", "normal", tt ); gs.addProperty( "fo:text-transform", "uppercase", tt ); } else if ( m_attributeFont == ATT_LOWER ) { gs.addProperty( "fo:font-variant", "normal", tt ); gs.addProperty( "fo:text-transform", "lowercase", tt ); } else { gs.addProperty( "fo:font-variant", "normal", tt ); gs.addProperty( "fo:text-transform", "none", tt ); } } if( !refFormat || this->language() != refFormat->language()) { TQString lang = m_language; TQString country; const int pos = lang.tqfind( '_' ); if ( pos != -1 ) { country = lang.mid( pos + 1 ); lang = lang.left( pos ); } gs.addProperty( "fo:language", lang, tt ); gs.addProperty( "fo:country", country, tt ); } if( !refFormat || this->textBackgroundColor() != refFormat->textBackgroundColor() ) { gs.addProperty( "fo:background-color", m_textBackColor.isValid() ? m_textBackColor.name() : "transparent", tt ); } if( !refFormat || ( this->shadowDistanceX() != refFormat->shadowDistanceX() || ( this->shadowDistanceY() != refFormat->shadowDistanceY() ) || ( this->shadowColor() != refFormat->shadowColor() ) ) ) { gs.addProperty( "fo:text-shadow", shadowAsCss(), tt ); } if ( !refFormat || this->hyphenation() != refFormat->hyphenation() ) { gs.addProperty( "fo:hyphenate", d->m_bHyphenation, tt ); } } void KoTextFormat::update() { //kdDebug(32500) << this << " KoTextFormat::update " << fn.family() << " " << pointSize() << endl; m_key = TQString(); // tqinvalidate key, recalc at the next key() call assert( d ); d->clearCache(); // i.e. recalc at the next screenFont[Metrics]() call } void KoTextFormat::copyFormat( const KoTextFormat & nf, int flags ) { if ( flags & KoTextFormat::Bold ) fn.setBold( nf.fn.bold() ); if ( flags & KoTextFormat::Italic ) fn.setItalic( nf.fn.italic() ); if ( flags & KoTextFormat::Underline ) fn.setUnderline( nf.fn.underline() ); if ( flags & KoTextFormat::Family ) fn.setFamily( nf.fn.family() ); if ( flags & KoTextFormat::Size ) fn.setPointSize( nf.fn.pointSize() ); if ( flags & KoTextFormat::Color ) col = nf.col; if ( flags & KoTextFormat::Misspelled ) missp = nf.missp; if ( flags & KoTextFormat::VAlign ) { va = nf.va; setRelativeTextSize( nf.relativeTextSize()); } ////// kotext addition if ( flags & KoTextFormat::StrikeOut ) { setStrikeOutStyle( nf.strikeOutStyle() ); setStrikeOutType (nf.strikeOutType()); } if( flags & KoTextFormat::TextBackgroundColor) setTextBackgroundColor(nf.textBackgroundColor()); if( flags & KoTextFormat::ExtendUnderLine) { setTextUnderlineColor(nf.textUnderlineColor()); setUnderlineType (nf.underlineType()); setUnderlineStyle (nf.underlineStyle()); } if( flags & KoTextFormat::Language) setLanguage(nf.language()); if( flags & KoTextFormat::ShadowText) setShadow(nf.shadowDistanceX(), nf.shadowDistanceY(), nf.shadowColor()); if( flags & KoTextFormat::OffsetFromBaseLine) setOffsetFromBaseLine(nf.offsetFromBaseLine()); if( flags & KoTextFormat::WordByWord) setWordByWord(nf.wordByWord()); if( flags & KoTextFormat::Attribute) setAttributeFont(nf.attributeFont()); if( flags & KoTextFormat::Hyphenation ) setHyphenation( nf.hyphenation()); if( flags & KoTextFormat::UnderLineWidth ) setUnderLineWidth( nf.underLineWidth()); ////// update(); //kdDebug(32500) << "KoTextFormat " << (void*)this << " copyFormat nf=" << (void*)&nf << " " << nf.key() << " flags=" << flags // << " ==> result " << this << " " << key() << endl; } void KoTextFormat::setBold( bool b ) { if ( b == fn.bold() ) return; fn.setBold( b ); update(); } void KoTextFormat::setMisspelled( bool b ) { if ( b == (bool)missp ) return; missp = b; update(); } void KoTextFormat::setVAlign( VerticalAlignment a ) { if ( a == va ) return; va = a; update(); } void KoTextFormat::setItalic( bool b ) { if ( b == fn.italic() ) return; fn.setItalic( b ); update(); } void KoTextFormat::setUnderline( bool b ) { if ( b == fn.underline() ) return; fn.setUnderline( b ); update(); } void KoTextFormat::setFamily( const TQString &f ) { if ( f == fn.family() ) return; fn.setFamily( f ); update(); } void KoTextFormat::setPointSize( int s ) { if ( s == fn.pointSize() ) return; fn.setPointSize( s ); update(); } void KoTextFormat::setFont( const TQFont &f ) { if ( f == fn ) return; fn = f; fn.setStyleStrategy( TQFont::ForceOutline ); update(); } void KoTextFormat::setColor( const TQColor &c ) { if ( c == col ) return; col = c; update(); } #if 0 int KoTextFormat::minLeftBearing() const { if ( !painter || !painter->isActive() ) return leftBearing; painter->setFont( fn ); return painter->fontMetrics().minLeftBearing(); } int KoTextFormat::minRightBearing() const { if ( !painter || !painter->isActive() ) return rightBearing; painter->setFont( fn ); return painter->fontMetrics().minRightBearing(); } #endif // ## Maybe we need a binary form for speed when NDEBUG, and to keep the // ## readable form when !NDEBUG, like TQFont does? void KoTextFormat::generateKey() { TQString k = fn.key(); k += '/'; if ( col.isValid() ) // just to shorten the key in the common case k += TQString::number( (uint)col.rgb() ); k += '/'; k += TQString::number( (int)isMisspelled() ); // 1 digit, no need for '/' k += TQString::number( (int)vAlign() ); //// kotext addition k += '/'; if (m_textBackColor.isValid()) k += TQString::number( (uint)m_textBackColor.rgb() ); k += '/'; if ( m_textUnderlineColor.isValid()) k += TQString::number( (uint)m_textUnderlineColor.rgb() ); k += '/'; k += TQString::number( (int)m_underlineType ); // a digit each, no need for '/' k += TQString::number( (int)m_strikeOutType ); k += TQString::number( (int)m_underlineStyle ); k += TQString::number( (int)m_strikeOutStyle ); k += '/'; k += m_language; k += '/'; if ( d->m_shadowDistanceX != 0 || d->m_shadowDistanceY != 0 ) { k += TQString::number( d->m_shadowDistanceX ); k += '/'; k += TQString::number( d->m_shadowDistanceY ); k += '/'; k += TQString::number( (uint)d->m_shadowColor.rgb() ); } k += '/'; k += TQString::number( d->m_relativeTextSize); k += '/'; k += TQString::number( d->m_offsetFromBaseLine); k += '/'; k += TQString::number( (int)d->m_bWordByWord); // boolean -> 1 digit -> no '/' k += TQString::number( (int)m_attributeFont); k += '/'; k += TQString::number( (int)d->m_bHyphenation); // boolean -> 1 digit -> no '/' k += TQString::number( (double)d->m_underLineWidth); //// // Keep in sync with method below m_key = k; } // This is used to create "simple formats", with font and color etc., but without // advanced features. Doesn't matter, don't extend the args. TQString KoTextFormat::getKey( const TQFont &fn, const TQColor &col, bool misspelled, VerticalAlignment a ) { TQString k = fn.key(); k += '/'; if ( col.isValid() ) // just to shorten the key in the common case k += TQString::number( (uint)col.rgb() ); k += '/'; k += TQString::number( (int)misspelled ); k += TQString::number( (int)a ); //// kotext addition k += '/'; // no background color k += '/'; // no underline color k += '/'; k += TQString::number( (int)U_NONE ); k += TQString::number( (int)S_NONE ); // no double-underline in a "simple format" k += TQString::number( (int)U_SOLID ); k += TQString::number( (int)S_SOLID ); // no double-underline in a "simple format" k += '/'; //k += TQString(); // spellcheck language k += '/'; //no shadow k += '/'; k += "0.66"; //relative text size k += '/'; k += "0"; // no offset from base line k += '/'; k += "0"; //no wordbyword attribute k += "0"; //no font attribute k += '/'; k += "0"; //no hyphen k += "0"; //no ulw //// return k; } TQString KoTextFormat::key() const { if ( m_key.isEmpty() ) const_cast( this )->generateKey(); return m_key; } void KoTextFormat::addRef() { ref++; #ifdef DEBUG_COLLECTION if ( collection ) kdDebug(32500) << " add ref of '" << k << "' to " << ref << " (" << this << ") (coll " << collection << ")" << endl; #endif } void KoTextFormat::removeRef() { ref--; if ( !collection ) return; #ifdef DEBUG_COLLECTION kdDebug(32500) << " remove ref of '" << k << "' to " << ref << " (" << this << ") (coll " << collection << ")" << endl; #endif if ( ref == 0 ) collection->remove( this ); } void KoTextFormat::setStrikeOutType (StrikeOutType _type) { if ( m_strikeOutType == _type ) return; m_strikeOutType = _type; update(); } void KoTextFormat::setUnderlineType (UnderlineType _type) { if ( m_underlineType == _type ) return; m_underlineType = _type; update(); } void KoTextFormat::setUnderlineStyle (UnderlineStyle _type) { if ( m_underlineStyle == _type ) return; m_underlineStyle = _type; update(); } void KoTextFormat::setStrikeOutStyle( StrikeOutStyle _type ) { if ( m_strikeOutStyle == _type ) return; m_strikeOutStyle = _type; update(); } void KoTextFormat::setTextBackgroundColor(const TQColor &_col) { if(m_textBackColor==_col) return; m_textBackColor=_col; update(); } void KoTextFormat::setTextUnderlineColor(const TQColor &_col) { if ( m_textUnderlineColor == _col ) return; m_textUnderlineColor=_col; update(); } void KoTextFormat::setShadow( double shadowDistanceX, double shadowDistanceY, const TQColor& shadowColor ) { if ( d->m_shadowDistanceX == shadowDistanceX && d->m_shadowDistanceY == shadowDistanceY && d->m_shadowColor == shadowColor ) return; d->m_shadowDistanceX = shadowDistanceX; d->m_shadowDistanceY = shadowDistanceY; d->m_shadowColor = shadowColor; update(); } void KoTextFormat::setRelativeTextSize( double _size ) { if ( d->m_relativeTextSize == _size) return; d->m_relativeTextSize = _size; update(); } void KoTextFormat::setOffsetFromBaseLine( int _offset ) { if ( d->m_offsetFromBaseLine == _offset) return; d->m_offsetFromBaseLine = _offset; update(); } void KoTextFormat::setWordByWord( bool _b ) { if ( d->m_bWordByWord == _b) return; d->m_bWordByWord = _b; update(); } void KoTextFormat::setAttributeFont(KoTextFormat::AttributeStyle _att ) { if ( m_attributeFont == _att) return; m_attributeFont = _att; update(); } int KoTextFormat::compare( const KoTextFormat & format ) const { int flags = 0; if ( fn.weight() != format.fn.weight() ) flags |= KoTextFormat::Bold; if ( fn.italic() != format.fn.italic() ) flags |= KoTextFormat::Italic; if ( textUnderlineColor()!=format.textUnderlineColor() || underlineType()!= format.underlineType() || underlineStyle() != format.underlineStyle()) flags |= KoTextFormat::ExtendUnderLine; if ( fn.family() != format.fn.family() ) flags |= KoTextFormat::Family; if ( pointSize() != format.pointSize() ) flags |= KoTextFormat::Size; if ( color() != format.color() ) flags |= KoTextFormat::Color; if ( vAlign() != format.vAlign() || relativeTextSize() != format.relativeTextSize()) flags |= KoTextFormat::VAlign; if ( strikeOutType() != format.strikeOutType() || underlineStyle() != format.underlineStyle()) flags |= KoTextFormat::StrikeOut; if ( textBackgroundColor() != format.textBackgroundColor() ) flags |= KoTextFormat::TextBackgroundColor; if ( language() != format.language() ) flags |= KoTextFormat::Language; if ( d->m_shadowDistanceX != format.shadowDistanceX() || d->m_shadowDistanceY != format.shadowDistanceY() || d->m_shadowColor != format.shadowColor() ) flags |= KoTextFormat::ShadowText; if ( offsetFromBaseLine() != format.offsetFromBaseLine() ) flags |= KoTextFormat::OffsetFromBaseLine; if ( wordByWord() != format.wordByWord() ) flags |= KoTextFormat::WordByWord; if ( attributeFont() != format.attributeFont() ) flags |= KoTextFormat::Attribute; if( hyphenation() != format.hyphenation() ) flags |= KoTextFormat::Hyphenation; if( underLineWidth() != format.underLineWidth() ) flags |= KoTextFormat::UnderLineWidth; return flags; } TQColor KoTextFormat::defaultTextColor( TQPainter * painter ) { if ( painter->device()->devType() == TQInternal::Printer ) return TQt::black; return TQApplication::palette().color( TQPalette::Active, TQColorGroup::Text ); } float KoTextFormat::screenPointSize( const KoTextZoomHandler* zh ) const { // ## simplify (needs a change in KoTextZoomHandler) int pointSizeLU = KoTextZoomHandler::ptToLayoutUnitPt( pointSize() ); if ( vAlign() != KoTextFormat::AlignNormal ) pointSizeLU = (int)( pointSizeLU *relativeTextSize() ); return zh->layoutUnitToFontSize( pointSizeLU, false /* forPrint */ ); } float KoTextFormat::refPointSize() const { if ( vAlign() != KoTextFormat::AlignNormal ) return (float)pointSize() * relativeTextSize(); else return pointSize(); } TQFont KoTextFormat::refFont() const { float pointSize = refPointSize(); if ( !d->m_refFont || pointSize != d->m_refFont->pointSizeFloat() ) { delete d->m_refFont; d->m_refFont = new TQFont( font() ); d->m_refFont->setPointSizeFloat( pointSize ); delete d->m_refFontMetrics; d->m_refFontMetrics = 0; //kdDebug(32500) << "KoTextFormat::refFont created new font with size " << pointSize << endl; } return *d->m_refFont; } TQFont KoTextFormat::screenFont( const KoTextZoomHandler* zh ) const { float pointSize = screenPointSize( zh ); //kdDebug(32500) << "KoTextFormat::screenFont pointSize=" << pointSize << endl; // Compare if this is the size for which we cached the font metrics. // We have to do this very dynamically, because 2 views could be painting the same // stuff, with different zoom levels. So no absolute caching possible. /*if ( d->m_screenFont ) kdDebug(32500) << " d->m_screenFont->pointSizeFloat()=" << d->m_screenFont->pointSizeFloat() << endl;*/ if ( !d->m_screenFont || kAbs( pointSize - d->m_screenFont->pointSizeFloat() ) > 1E-4 ) { delete d->m_screenFont; d->m_screenFont = new TQFont( font() ); d->m_screenFont->setPointSizeFloat( pointSize ); delete d->m_screenFontMetrics; d->m_screenFontMetrics = 0; //kdDebug(32500) << "KoTextFormat::screenFont created new font with size " << pointSize << endl; } return *d->m_screenFont; } const TQFontMetrics& KoTextFormat::screenFontMetrics( const KoTextZoomHandler* zh ) const { TQFont f = screenFont(zh); // don't move inside the if! if ( !d->m_screenFontMetrics ) // not calculated, or invalidated by screenFont above { //kdDebug(32500) << this << " KoTextFormat::screenFontMetrics pointSize=" << pointSize << " d->m_screenFont->pointSizeFloat()=" << d->m_screenFont->pointSizeFloat() << endl; d->m_screenFontMetrics = new TQFontMetrics( f ); //kdDebug(32500) << "KoTextFormat::screenFontMetrics created new metrics with size " << pointSize << " height:" << d->m_screenFontMetrics->height() << endl; } return *d->m_screenFontMetrics; } const TQFontMetrics& KoTextFormat::refFontMetrics() const { TQFont f = refFont(); if ( !d->m_refFontMetrics ) { //kdDebug(32500) << this << " KoTextFormat::refFontMetrics pointSize=" << pointSize << " d->m_refFont->pointSizeFloat()=" << d->m_refFont->pointSizeFloat() << endl; d->m_refFontMetrics = new TQFontMetrics( f ); //kdDebug(32500) << "KoTextFormat::refFontMetrics created new metrics with size " << pointSize << " height:" << d->m_refFontMetrics->height() << endl; } return *d->m_refFontMetrics; } TQFont KoTextFormat::smallCapsFont( const KoTextZoomHandler* zh, bool applyZoom ) const { TQFont font = applyZoom ? screenFont( zh ) : refFont(); TQFontMetrics fm = refFontMetrics(); // only used for proportions, so applyZoom doesn't matter double pointSize = font.pointSize() * ((double)fm.boundingRect("x").height()/(double)fm.boundingRect("X").height()); font.setPointSizeFloat( pointSize ); return font; } int KoTextFormat::charWidth( const KoTextZoomHandler* zh, bool applyZoom, const KoTextStringChar* c, const KoTextParag* parag, int i ) const { ushort tqunicode = c->c.tqunicode(); if ( !c->charStop || tqunicode == 0xad || tqunicode == 0x2028 ) return 0; Q_ASSERT( !c->isCustom() ); // actually it's a bit stupid to call this for custom items if( c->isCustom() ) { if( c->customItem()->placement() == KoTextCustomItem::PlaceInline ) { // customitem width is in LU pixels. Convert to 100%-zoom-pixels (pt2pt==pix2pix) double w = KoTextZoomHandler::layoutUnitPtToPt( c->customItem()->width ); return tqRound( applyZoom ? ( w * zh->zoomFactorX() ) : w ); } else return 0; } int pixelww; int r = c->c.row(); if( /*r < 0x06 || r > 0x1f*/ r < 0x06 || (r > 0x1f && !(r > 0xd7 && r < 0xe0)) ) { // Small caps -> we can't use the cached font metrics from KoTextFormat if ( attributeFont() == KoTextFormat::ATT_SMALL_CAPS && c->c.upper() != c->c ) { pixelww = TQFontMetrics( smallCapsFont( zh, applyZoom ) ).width( displayedChar( c->c ) ); } else // Use the cached font metrics from KoTextFormat if ( applyZoom ) { if ( r ) { pixelww = this->screenFontMetrics( zh ).width( displayedChar( c->c ) ); } else { // Use the m_screenWidths[] array when possible, even faster Q_ASSERT( tqunicode < 256 ); pixelww = d->m_screenWidths[ tqunicode ]; // Not in cache yet -> calculate if ( pixelww == 0 ) { pixelww = this->screenFontMetrics( zh ).width( displayedChar( c->c ) ); Q_ASSERT( pixelww < 65535 ); d->m_screenWidths[ tqunicode ] = pixelww; } } } else { pixelww = this->refFontMetrics().width( displayedChar( c->c ) ); } } else { // Complex text. We need some hacks to get the right metric here bool smallCaps = ( attributeFont() == KoTextFormat::ATT_SMALL_CAPS && c->c.upper() != c->c ); const TQFontMetrics& fontMetrics = smallCaps ? smallCapsFont( zh, applyZoom ) : applyZoom ? screenFontMetrics( zh ) : refFontMetrics(); TQString str; int pos = 0; if( i > 8 ) pos = i - 8; int off = i - pos; int end = TQMIN( parag->length(), i + 8 ); while ( pos < end ) { str += displayedChar( parag->at(pos)->c ); pos++; } pixelww = fontMetrics.charWidth( str, off ); } #if 0 kdDebug(32500) << "KoTextFormat::charWidth: char=" << TQString(c->c) << " format=" << key() << ", applyZoom=" << applyZoom << " pixel-width=" << pixelww << endl; #endif return pixelww; } int KoTextFormat::height() const { if ( d->m_refHeight < 0 ) { // Calculate height using 100%-zoom font int h = refFontMetrics().height()+TQABS(offsetFromBaseLine()); if ( vAlign() == KoTextFormat::AlignSuperScript ) h += refFontMetrics().height()/2; else if ( vAlign() == KoTextFormat::AlignSubScript ) h += refFontMetrics().height()/6; // Add room for the shadow if ( d->m_shadowDistanceY != 0 ) { // pt -> pixel (at 100% zoom) h += (int)(POINT_TO_INCH( static_cast( KoGlobal::dpiY() ) ) * TQABS( d->m_shadowDistanceY ) ); } //kdDebug(32500) << "KoTextFormat::height 100%-zoom font says h=" << h << " in LU:" << KoTextZoomHandler::ptToLayoutUnitPt(h) << endl; // Then scale to LU d->m_refHeight = tqRound( KoTextZoomHandler::ptToLayoutUnitPt( h ) ); } return d->m_refHeight; } int KoTextFormat::offsetX() const // in LU pixels { int off = 0; #if 0 // Shadow on left -> character is moved to the right // Wrong if next char has no shadow (they'll run into each other) // Somehow we should only do this if x == 0 (in the formatter) if ( d->m_shadowDistanceX < 0 ) { double lupt = KoTextZoomHandler::ptToLayoutUnitPt( TQABS( d->m_shadowDistanceX ) ); off += (int)(POINT_TO_INCH( static_cast( KoGlobal::dpiX() ) ) * lupt ); } #endif return off; } int KoTextFormat::offsetY() const // in LU pixels { int off = 0; #if 0 // Shadow on top -> character is moved down if ( d->m_shadowDistanceY < 0 ) { double lupt = KoTextZoomHandler::ptToLayoutUnitPt( TQABS( d->m_shadowDistanceY ) ); off += (int)(POINT_TO_INCH( static_cast( KoGlobal::dpiY() ) ) * lupt ); } #endif return off; } TQString KoTextFormat::displayedString( const TQString& str )const { switch ( m_attributeFont ) { case ATT_NONE: return str; case ATT_UPPER: case ATT_SMALL_CAPS: return str.upper(); case ATT_LOWER: return str.lower(); default: kdDebug(32500)<<" Error in AttributeStyle \n"; return str; } } TQChar KoTextFormat::displayedChar( TQChar c )const { if ( c.tqunicode() == 0xa0 ) // nbsp return ' '; switch ( m_attributeFont ) { case ATT_NONE: return c; case ATT_SMALL_CAPS: case ATT_UPPER: return c.upper(); case ATT_LOWER: return c.lower(); default: kdDebug(32500)<<" Error in AttributeStyle \n"; return c; } } int KoTextFormat::ascent() const { if ( d->m_refAscent < 0 ) { // Calculate ascent using 100%-zoom font int h = refFontMetrics().ascent(); if ( offsetFromBaseLine()>0 ) h += offsetFromBaseLine(); if ( vAlign() == KoTextFormat::AlignSuperScript ) h += refFontMetrics().height()/2; // Then scale to LU d->m_refAscent = tqRound( KoTextZoomHandler::ptToLayoutUnitPt( h ) ); //d->m_refAscent += offsetY(); } return d->m_refAscent; } int KoTextFormat::descent() const { if ( d->m_refDescent < 0 ) { // Calculate descent using 100%-zoom font int h = refFontMetrics().descent(); if ( offsetFromBaseLine()<0 ) h -= offsetFromBaseLine(); // Then scale to LU d->m_refDescent = tqRound( KoTextZoomHandler::ptToLayoutUnitPt( h ) ); //d->m_refDescent += offsetY(); } return d->m_refDescent; } int KoTextFormat::charWidthLU( const KoTextStringChar* c, const KoTextParag* parag, int i ) const { // Hmm, we add precision to the least precise one! // TODO: We should instead implement it here in LU, and let charWidth call it... return KoTextZoomHandler::ptToLayoutUnitPt( charWidth( 0L, false, c, parag, i ) ); } int KoTextFormat::width( const TQChar& ch ) const { // Warning this doesn't take into account the shadow return KoTextZoomHandler::ptToLayoutUnitPt( refFontMetrics().width( ch ) ); } void KoTextFormat::applyCharStyle( KoCharStyle *_style ) { d->m_charStyle = _style; } KoCharStyle *KoTextFormat::style() const { return d->m_charStyle; } TQString KoTextFormat::shadowAsCss( double shadowDistanceX, double shadowDistanceY, const TQColor& shadowColor ) { // http://www.w3.org/TR/REC-CSS2/text.html#text-shadow-props // none | [ || ] ... // => none or color length length if ( shadowDistanceX != 0 || shadowDistanceY != 0 ) { TQString css = shadowColor.name() + " "; css += TQString::number(shadowDistanceX) + "pt "; css += TQString::number(shadowDistanceY) + "pt"; return css; } return "none"; } TQString KoTextFormat::shadowAsCss() const { return shadowAsCss( d->m_shadowDistanceX, d->m_shadowDistanceY, d->m_shadowColor ); } void KoTextFormat::parseShadowFromCss( const TQString& _css ) { TQString css = _css.simplifyWhiteSpace(); if ( css.isEmpty() || css == "none" ) { d->m_shadowDistanceX = 0; d->m_shadowDistanceY = 0; d->m_shadowColor = TQColor(); } else { TQStringList tokens = TQStringList::split(' ', css); if ( tokens.isEmpty() ) { kdWarning(32500) << "Parse error in text-shadow: " << css << endl; return; } // Check which token looks like a color TQColor col( tokens.first() ); if ( col.isValid() ) tokens.pop_front(); else if ( tokens.count() > 1 ) { col.setNamedColor( tokens.last() ); if ( col.isValid() ) tokens.pop_back(); } d->m_shadowColor = col; // whether valid or not // Parse x distance if ( !tokens.isEmpty() ) { d->m_shadowDistanceX = KoUnit::parseValue( tokens.first() ); tokens.pop_front(); } // Parse y distance if ( !tokens.isEmpty() ) { d->m_shadowDistanceY = KoUnit::parseValue( tokens.first() ); tokens.pop_front(); } // We ignore whatever else is in the string (e.g. blur radius, other shadows) } update(); } TQColor KoTextFormat::shadowColor() const { if ( d->m_shadowColor.isValid() ) return d->m_shadowColor; else // CSS says "[If] no color has been specified, the shadow will have the same color as the [text] itself" return col; } int KoTextFormat::shadowX( KoTextZoomHandler *zh ) const { return zh->zoomItX( d->m_shadowDistanceX ); } int KoTextFormat::shadowY( KoTextZoomHandler *zh ) const { return zh->zoomItY( d->m_shadowDistanceY ); } //static TQString KoTextFormat::underlineStyleToString( KoTextFormat::UnderlineStyle _lineType ) { TQString strLineType; switch ( _lineType ) { case KoTextFormat::U_SOLID: strLineType ="solid"; break; case KoTextFormat::U_DASH: strLineType ="dash"; break; case KoTextFormat::U_DOT: strLineType ="dot"; break; case KoTextFormat::U_DASH_DOT: strLineType="dashdot"; break; case KoTextFormat::U_DASH_DOT_DOT: strLineType="dashdotdot"; break; } return strLineType; } TQString KoTextFormat::strikeOutStyleToString( KoTextFormat::StrikeOutStyle _lineType ) { TQString strLineType; switch ( _lineType ) { case KoTextFormat::S_SOLID: strLineType ="solid"; break; case KoTextFormat::S_DASH: strLineType ="dash"; break; case KoTextFormat::S_DOT: strLineType ="dot"; break; case KoTextFormat::S_DASH_DOT: strLineType="dashdot"; break; case KoTextFormat::S_DASH_DOT_DOT: strLineType="dashdotdot"; break; } return strLineType; } KoTextFormat::UnderlineStyle KoTextFormat::stringToUnderlineStyle( const TQString & _str ) { if ( _str =="solid") return KoTextFormat::U_SOLID; else if ( _str =="dash" ) return KoTextFormat::U_DASH; else if ( _str =="dot" ) return KoTextFormat::U_DOT; else if ( _str =="dashdot") return KoTextFormat::U_DASH_DOT; else if ( _str=="dashdotdot") return KoTextFormat::U_DASH_DOT_DOT; else return KoTextFormat::U_SOLID; } KoTextFormat::StrikeOutStyle KoTextFormat::stringToStrikeOutStyle( const TQString & _str ) { if ( _str =="solid") return KoTextFormat::S_SOLID; else if ( _str =="dash" ) return KoTextFormat::S_DASH; else if ( _str =="dot" ) return KoTextFormat::S_DOT; else if ( _str =="dashdot") return KoTextFormat::S_DASH_DOT; else if ( _str=="dashdotdot") return KoTextFormat::S_DASH_DOT_DOT; else return KoTextFormat::S_SOLID; } TQString KoTextFormat::attributeFontToString( KoTextFormat::AttributeStyle _attr ) { if (_attr == KoTextFormat::ATT_NONE ) return TQString("none"); else if ( _attr == KoTextFormat::ATT_UPPER ) return TQString("uppercase"); else if ( _attr == KoTextFormat::ATT_LOWER ) return TQString("lowercase"); else if ( _attr == KoTextFormat::ATT_SMALL_CAPS ) return TQString("smallcaps"); else return TQString("none"); } KoTextFormat::AttributeStyle KoTextFormat::stringToAttributeFont( const TQString & _str ) { if ( _str == "none" ) return KoTextFormat::ATT_NONE; else if ( _str == "uppercase") return KoTextFormat::ATT_UPPER; else if ( _str == "lowercase") return KoTextFormat::ATT_LOWER; else if ( _str == "smallcaps" ) return KoTextFormat::ATT_SMALL_CAPS; else return KoTextFormat::ATT_NONE; } void KoTextFormat::setHyphenation( bool b ) { if ( d->m_bHyphenation == b ) return; d->m_bHyphenation = b; update(); } void KoTextFormat::setUnderLineWidth( double ulw ) { if ( d->m_underLineWidth == ulw ) return; d->m_underLineWidth = ulw; update(); } void KoTextFormat::setLanguage( const TQString & _lang) { if ( m_language == _lang ) return; m_language = _lang; update(); } TQStringList KoTextFormat::underlineTypeList() { TQStringList lst; lst <language(), false ); lastFormat = cres = 0; cflags = -1; cKey.setAutoDelete( TRUE ); cachedFormat = 0; } KoTextFormatCollection::KoTextFormatCollection( const TQFont& defaultFont, const TQColor& defaultColor, const TQString & defaultLanguage, bool defaultHyphenation ) : cKey( 307 ) { #ifdef DEBUG_COLLECTION kdDebug(32500) << "KoTextFormatCollection::KoTextFormatCollection " << this << endl; #endif defFormat = new KoTextFormat( defaultFont, defaultColor, defaultLanguage, defaultHyphenation ); lastFormat = cres = 0; cflags = -1; cKey.setAutoDelete( TRUE ); cachedFormat = 0; } KoTextFormatCollection::~KoTextFormatCollection() { #ifdef DEBUG_COLLECTION kdDebug(32500) << "KoTextFormatCollection::~KoTextFormatCollection " << this << endl; #endif delete defFormat; defFormat = 0; } KoTextFormat *KoTextFormatCollection::format( const KoTextFormat *f ) { if ( f->tqparent() == this || f == defFormat ) { #ifdef DEBUG_COLLECTION kdDebug(32500) << " format(f) need '" << f->key() << "', best case!" << endl; #endif lastFormat = const_cast(f); lastFormat->addRef(); return lastFormat; } if ( f == lastFormat || ( lastFormat && f->key() == lastFormat->key() ) ) { #ifdef DEBUG_COLLECTION kdDebug(32500) << " format(f) need '" << f->key() << "', good case!" << endl; #endif lastFormat->addRef(); return lastFormat; } #if 0 // #### disabled, because if this format is not in the // formatcollection, it doesn't get the painter through // KoTextFormatCollection::setPainter() which breaks printing on // windows if ( f->isAnchor() ) { lastFormat = createFormat( *f ); lastFormat->collection = 0; return lastFormat; } #endif KoTextFormat *fm = cKey.tqfind( f->key() ); if ( fm ) { #ifdef DEBUG_COLLECTION kdDebug(32500) << " format(f) need '" << f->key() << "', normal case!" << endl; #endif lastFormat = fm; lastFormat->addRef(); return lastFormat; } if ( f->key() == defFormat->key() ) return defFormat; #ifdef DEBUG_COLLECTION kdDebug(32500) << " format(f) need '" << f->key() << "', worst case!" << endl; #endif lastFormat = createFormat( *f ); lastFormat->collection = this; cKey.insert( lastFormat->key(), lastFormat ); Q_ASSERT( f->key() == lastFormat->key() ); return lastFormat; } KoTextFormat *KoTextFormatCollection::format( const KoTextFormat *of, const KoTextFormat *nf, int flags ) { if ( cres && kof == of->key() && knf == nf->key() && cflags == flags ) { #ifdef DEBUG_COLLECTION kdDebug(32500) << " format(of,nf,flags) mix of '" << of->key() << "' and '" << nf->key() << "', best case!" << endl; #endif cres->addRef(); return cres; } #ifdef DEBUG_COLLECTION kdDebug(32500) << " format(of,nf," << flags << ") calling createFormat(of=" << of << " " << of->key() << ")" << endl; #endif cres = createFormat( *of ); kof = of->key(); knf = nf->key(); cflags = flags; #ifdef DEBUG_COLLECTION kdDebug(32500) << " format(of,nf," << flags << ") calling copyFormat(nf=" << nf << " " << nf->key() << ")" << endl; #endif cres->copyFormat( *nf, flags ); KoTextFormat *fm = cKey.tqfind( cres->key() ); if ( !fm ) { #ifdef DEBUG_COLLECTION kdDebug(32500) << " format(of,nf,flags) mix of '" << of->key() << "' and '" << nf->key() << ", worst case!" << endl; #endif cres->collection = this; cKey.insert( cres->key(), cres ); } else { #ifdef DEBUG_COLLECTION kdDebug(32500) << " format(of,nf,flags) mix of '" << of->key() << "' and '" << nf->key() << ", good case!" << endl; #endif delete cres; cres = fm; cres->addRef(); } return cres; } #if 0 KoTextFormat *KoTextFormatCollection::format( const TQFont &f, const TQColor &c, const TQString & language, bool hyphen ) { if ( cachedFormat && cfont == f && ccol == c ) { #ifdef DEBUG_COLLECTION kdDebug(32500) << " format of font and col '" << cachedFormat->key() << "' - best case" << endl; #endif cachedFormat->addRef(); return cachedFormat; } TQString key = KoTextFormat::getKey( f, c, FALSE, KoTextFormat::AlignNormal ); cachedFormat = cKey.tqfind( key ); cfont = f; ccol = c; if ( cachedFormat ) { #ifdef DEBUG_COLLECTION kdDebug(32500) << " format of font and col '" << cachedFormat->key() << "' - good case" << endl; #endif cachedFormat->addRef(); return cachedFormat; } if ( key == defFormat->key() ) return defFormat; cachedFormat = createFormat( f, c, language, hyphen ); cachedFormat->collection = this; cKey.insert( cachedFormat->key(), cachedFormat ); if ( cachedFormat->key() != key ) kdWarning() << "ASSERT: keys for format not identical: '" << cachedFormat->key() << " '" << key << "'" << endl; #ifdef DEBUG_COLLECTION kdDebug(32500) << " format of font and col '" << cachedFormat->key() << "' - worst case" << endl; #endif return cachedFormat; } #endif void KoTextFormatCollection::remove( KoTextFormat *f ) { if ( lastFormat == f ) lastFormat = 0; if ( cres == f ) cres = 0; if ( cachedFormat == f ) cachedFormat = 0; cKey.remove( f->key() ); } void KoTextFormatCollection::zoomChanged() { TQDictIterator it( cKey ); for ( ; it.current(); ++it ) { it.current()->zoomChanged(); } } #if 0 void KoTextFormatCollection::setPainter( TQPainter *p ) { TQDictIterator it( cKey ); KoTextFormat *f; while ( ( f = it.current() ) ) { ++it; f->setPainter( p ); } } #endif #ifndef NDEBUG void KoTextFormatCollection::debug() { kdDebug(32500) << "------------ KoTextFormatCollection: debug --------------- BEGIN" << endl; kdDebug(32500) << "Default Format: '" << defFormat->key() << "' (" << (void*)defFormat << "): realfont: " << TQFontInfo( defFormat->font() ).family() << endl; TQDictIterator it( cKey ); for ( ; it.current(); ++it ) { Q_ASSERT(it.currentKey() == it.current()->key()); if(it.currentKey() != it.current()->key()) kdDebug(32500) << "**** MISMATCH key=" << it.currentKey() << " (see line below for format)" << endl; it.current()->printDebug(); } kdDebug(32500) << "------------ KoTextFormatCollection: debug --------------- END" << endl; } #endif