You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koffice/lib/kotext/KoTextFormat.cpp

1890 lines
63 KiB

/* This file is part of the KDE project
Copyright (C) 2001-2006 David Faure <faure@kde.org>
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 <KoGenStyles.h>
#include <KoXmlNS.h>
#include <kglobal.h>
#include <kdebug.h>
#include <klocale.h>
#include <tqapplication.h>
#include <tqregexp.h>
#include <assert.h>
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 = TDEGlobal::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 *parent )
: fn( f ), col( c ) /*fm( TQFontMetrics( f ) ),*/ //linkColor( TRUE )
{
#ifdef DEBUG_COLLECTION
kdDebug(32500) << "KoTextFormat with font & color & parent (" << parent << "), 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 = parent;
//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 ( parent() && parent()->defaultFormat() ) // not when destroying the collection
assert( ! ( parent()->dict().find( 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: <vertical position (% or sub or super)> [<size as %>]
//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.find( '_' );
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(); // invalidate 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<KoTextFormat*>( 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 unicode = c->c.unicode();
if ( !c->charStop || unicode == 0xad || unicode == 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( unicode < 256 );
pixelww = d->m_screenWidths[ unicode ];
// Not in cache yet -> calculate
if ( pixelww == 0 ) {
pixelww = this->screenFontMetrics( zh ).width( displayedChar( c->c ) );
Q_ASSERT( pixelww < 65535 );
d->m_screenWidths[ unicode ] = 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<double>( 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<double>( 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<double>( 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.unicode() == 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 | [<color> || <length (h)> <length (v)> <length (blur radius, not used here)>] ...
// => 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 <<i18n("Underline Style", "None");
lst <<i18n("Single");
lst <<i18n("Double");
lst <<i18n("Simple Bold");
lst <<i18n("Wave");
return lst;
}
TQStringList KoTextFormat::strikeOutTypeList()
{
TQStringList lst;
lst <<i18n("Strikeout Style", "None");
lst <<i18n("Single");
lst <<i18n("Double");
lst <<i18n("Simple Bold");
return lst;
}
TQStringList KoTextFormat::fontAttributeList()
{
TQStringList lst;
lst <<i18n("Normal");
lst <<i18n("Uppercase");
lst <<i18n("Lowercase");
lst <<i18n("Small Caps");
return lst;
}
TQStringList KoTextFormat::underlineStyleList()
{
TQStringList lst;
lst <<"_________"; // SOLID
lst <<"___ ___ __"; // DASH
lst <<"_ _ _ _ _ _"; // DOT
lst <<"___ _ ___ _"; // DASH_DOT
lst <<"___ _ _ ___"; // DASH_DOT_DOT
return lst;
}
TQStringList KoTextFormat::strikeOutStyleList()
{
TQStringList lst;
lst <<"_________"; // SOLID
lst <<"___ ___ __"; // DASH
lst <<"_ _ _ _ _ _"; // DOT
lst <<"___ _ ___ _"; // DASH_DOT
lst <<"___ _ _ ___"; // DASH_DOT_DOT
return lst;
}
#ifndef NDEBUG
void KoTextFormat::printDebug()
{
TQString col = color().isValid() ? color().name() : TQString("(default)");
kdDebug(32500) << "format '" << key() << "' (" << (void*)this << "):"
<< " refcount: " << ref
<< " realfont: " << TQFontInfo( font() ).family()
<< " color: " << col << " shadow=" << shadowAsCss() << endl;
}
#endif
////////////////
KoTextFormatCollection::KoTextFormatCollection()
: cKey( 307 )//, sheet( 0 )
{
#ifdef DEBUG_COLLECTION
kdDebug(32500) << "KoTextFormatCollection::KoTextFormatCollection " << this << endl;
#endif
defFormat = new KoTextFormat( TQApplication::font(), TQColor(), TDEGlobal::locale()->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->parent() == this || f == defFormat ) {
#ifdef DEBUG_COLLECTION
kdDebug(32500) << " format(f) need '" << f->key() << "', best case!" << endl;
#endif
lastFormat = const_cast<KoTextFormat*>(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.find( 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.find( 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.find( 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<KoTextFormat> it( cKey );
for ( ; it.current(); ++it ) {
it.current()->zoomChanged();
}
}
#if 0
void KoTextFormatCollection::setPainter( TQPainter *p )
{
TQDictIterator<KoTextFormat> 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<KoTextFormat> 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