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.
949 lines
40 KiB
949 lines
40 KiB
/* This file is part of the KDE project
|
|
Copyright (C) 2001 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 "KoParagLayout.h"
|
|
#include "KoRichText.h"
|
|
#include "KoParagCounter.h"
|
|
#include "KoStyleCollection.h"
|
|
#include "KoOasisContext.h"
|
|
#include <KoXmlWriter.h>
|
|
#include <KoXmlNS.h>
|
|
#include <KoDom.h>
|
|
#include <KoGenStyles.h>
|
|
|
|
#include <tdeglobal.h>
|
|
#include <tdelocale.h>
|
|
#include <kdebug.h>
|
|
#include <tqdom.h>
|
|
#include <tqbuffer.h>
|
|
#include <tqcolor.h>
|
|
|
|
#include <float.h>
|
|
|
|
TQString* KoParagLayout::shadowCssCompat = 0L;
|
|
|
|
// Create a default KoParagLayout.
|
|
KoParagLayout::KoParagLayout()
|
|
{
|
|
initialise();
|
|
}
|
|
|
|
void KoParagLayout::operator=( const KoParagLayout &layout )
|
|
{
|
|
alignment = layout.alignment;
|
|
for ( int i = 0 ; i < 5 ; ++i )
|
|
margins[i] = layout.margins[i];
|
|
pageBreaking = layout.pageBreaking;
|
|
leftBorder = layout.leftBorder;
|
|
rightBorder = layout.rightBorder;
|
|
topBorder = layout.topBorder;
|
|
bottomBorder = layout.bottomBorder;
|
|
joinBorder = layout.joinBorder;
|
|
backgroundColor = layout.backgroundColor;
|
|
if ( layout.counter )
|
|
counter = new KoParagCounter( *layout.counter );
|
|
else
|
|
counter = 0L;
|
|
lineSpacing = layout.lineSpacing;
|
|
lineSpacingType = layout.lineSpacingType;
|
|
style = layout.style;
|
|
direction = layout.direction;
|
|
setTabList( layout.tabList() );
|
|
}
|
|
|
|
int KoParagLayout::compare( const KoParagLayout & layout ) const
|
|
{
|
|
int flags = 0;
|
|
if ( alignment != layout.alignment )
|
|
flags |= Alignment;
|
|
for ( int i = 0 ; i < 5 ; ++i )
|
|
if ( margins[i] != layout.margins[i] )
|
|
{
|
|
flags |= Margins;
|
|
break;
|
|
}
|
|
if ( pageBreaking != layout.pageBreaking )
|
|
flags |= PageBreaking;
|
|
if ( leftBorder != layout.leftBorder
|
|
|| rightBorder != layout.rightBorder
|
|
|| topBorder != layout.topBorder
|
|
|| bottomBorder != layout.bottomBorder
|
|
|| joinBorder != layout.joinBorder )
|
|
flags |= Borders;
|
|
|
|
if ( layout.counter )
|
|
{
|
|
if ( counter )
|
|
{
|
|
if ( ! ( *layout.counter == *counter ) )
|
|
flags |= BulletNumber;
|
|
} else
|
|
if ( layout.counter->numbering() != KoParagCounter::NUM_NONE )
|
|
flags |= BulletNumber;
|
|
}
|
|
else
|
|
if ( counter && counter->numbering() != KoParagCounter::NUM_NONE )
|
|
flags |= BulletNumber;
|
|
|
|
if ( lineSpacing != layout.lineSpacing
|
|
|| lineSpacingType != layout.lineSpacingType )
|
|
flags |= LineSpacing;
|
|
//if ( style != layout.style )
|
|
// flags |= Style;
|
|
if ( m_tabList != layout.m_tabList )
|
|
flags |= Tabulator;
|
|
|
|
if ( backgroundColor != layout.backgroundColor)
|
|
flags |= BackgroundColor;
|
|
|
|
// This method is used for the GUI stuff only, so we don't have a flag
|
|
// for the Direction value.
|
|
return flags;
|
|
}
|
|
|
|
void KoParagLayout::initialise()
|
|
{
|
|
alignment = TQt::AlignAuto;
|
|
for ( int i = 0 ; i < 5 ; ++i ) // use memset ?
|
|
margins[i] = 0;
|
|
lineSpacingType = LS_SINGLE;
|
|
lineSpacing = 0;
|
|
counter = 0L;
|
|
leftBorder.setPenWidth( 0);
|
|
rightBorder.setPenWidth( 0);
|
|
topBorder.setPenWidth( 0);
|
|
bottomBorder.setPenWidth( 0);
|
|
joinBorder = true;
|
|
pageBreaking = 0;
|
|
style = 0L;
|
|
direction = TQChar::DirON;
|
|
m_tabList.clear();
|
|
}
|
|
|
|
KoParagLayout::~KoParagLayout()
|
|
{
|
|
delete counter;
|
|
}
|
|
|
|
void KoParagLayout::loadParagLayout( KoParagLayout& layout, const TQDomElement& parentElem, int docVersion )
|
|
{
|
|
// layout is an input and output parameter
|
|
// It can have been initialized already, e.g. by copying from a style
|
|
// (we don't do that anymore though).
|
|
|
|
// Load the paragraph tabs - we load into a clean list, not mixing with those already in "layout"
|
|
// We can't apply the 'default comes from the style' in this case, because
|
|
// there is no way to differentiate between "I want no tabs in the parag"
|
|
// and "use default from style".
|
|
KoTabulatorList tabList;
|
|
TQDomElement element = parentElem.firstChild().toElement();
|
|
for ( ; !element.isNull() ; element = element.nextSibling().toElement() )
|
|
{
|
|
if ( element.tagName() == "TABULATOR" )
|
|
{
|
|
KoTabulator tab;
|
|
tab.type = static_cast<KoTabulators>( getAttribute( element, "type", T_LEFT ) );
|
|
tab.ptPos = getAttribute( element, "ptpos", 0.0 );
|
|
tab.filling = static_cast<KoTabulatorFilling>( getAttribute( element, "filling", TF_BLANK ) );
|
|
tab.ptWidth = getAttribute( element, "width", 0.5 );
|
|
TQString alignCharStr = element.attribute("alignchar");
|
|
if ( alignCharStr.isEmpty() )
|
|
tab.alignChar = TDEGlobal::locale()->decimalSymbol()[0];
|
|
else
|
|
tab.alignChar = alignCharStr[0];
|
|
tabList.append( tab );
|
|
}
|
|
}
|
|
qHeapSort( tabList );
|
|
layout.setTabList( tabList );
|
|
layout.alignment = TQt::AlignAuto;
|
|
element = parentElem.namedItem( "FLOW" ).toElement(); // Flow is what is now called alignment internally
|
|
if ( !element.isNull() )
|
|
{
|
|
TQString flow = element.attribute( "align" ); // KWord-1.0 DTD
|
|
if ( !flow.isEmpty() )
|
|
{
|
|
layout.alignment = flow=="right" ? TQt::AlignRight :
|
|
flow=="center" ? TQt::AlignHCenter :
|
|
flow=="justify" ? TQt::AlignJustify :
|
|
flow=="left" ? TQt::AlignLeft : TQt::AlignAuto;
|
|
|
|
TQString dir = element.attribute( "dir" ); // KWord-1.2
|
|
if ( !dir.isEmpty() ) {
|
|
if ( dir == "L" )
|
|
layout.direction = TQChar::DirL;
|
|
else if ( dir == "R" )
|
|
layout.direction = TQChar::DirR;
|
|
else
|
|
kdWarning() << "Unexpected value for paragraph direction: " << dir << endl;
|
|
}
|
|
} else {
|
|
flow = element.attribute( "value" ); // KWord-0.8
|
|
static const int flow2align[] = { TQt::AlignAuto, TQt::AlignRight, TQt::AlignHCenter, TQt::AlignJustify };
|
|
if ( !flow.isEmpty() && flow.toInt() < 4 )
|
|
layout.alignment = flow2align[flow.toInt()];
|
|
}
|
|
}
|
|
|
|
if ( docVersion < 2 )
|
|
{
|
|
element = parentElem.namedItem( "OHEAD" ).toElement(); // used by KWord-0.8
|
|
if ( !element.isNull() )
|
|
layout.margins[TQStyleSheetItem::MarginTop] = getAttribute( element, "pt", 0.0 );
|
|
|
|
element = parentElem.namedItem( "OFOOT" ).toElement(); // used by KWord-0.8
|
|
if ( !element.isNull() )
|
|
layout.margins[TQStyleSheetItem::MarginBottom] = getAttribute( element, "pt", 0.0 );
|
|
|
|
element = parentElem.namedItem( "IFIRST" ).toElement(); // used by KWord-0.8
|
|
if ( !element.isNull() )
|
|
layout.margins[TQStyleSheetItem::MarginFirstLine] = getAttribute( element, "pt", 0.0 );
|
|
|
|
element = parentElem.namedItem( "ILEFT" ).toElement(); // used by KWord-0.8
|
|
if ( !element.isNull() )
|
|
layout.margins[TQStyleSheetItem::MarginLeft] = getAttribute( element, "pt", 0.0 );
|
|
}
|
|
|
|
// KWord-1.0 DTD
|
|
element = parentElem.namedItem( "INDENTS" ).toElement();
|
|
if ( !element.isNull() )
|
|
{
|
|
layout.margins[TQStyleSheetItem::MarginFirstLine] = getAttribute( element, "first", 0.0 );
|
|
layout.margins[TQStyleSheetItem::MarginLeft] = getAttribute( element, "left", 0.0 );
|
|
layout.margins[TQStyleSheetItem::MarginRight] = getAttribute( element, "right", 0.0 );
|
|
}
|
|
element = parentElem.namedItem( "OFFSETS" ).toElement();
|
|
if ( !element.isNull() )
|
|
{
|
|
layout.margins[TQStyleSheetItem::MarginTop] = getAttribute( element, "before", 0.0 );
|
|
layout.margins[TQStyleSheetItem::MarginBottom] = getAttribute( element, "after", 0.0 );
|
|
}
|
|
|
|
if ( docVersion < 2 )
|
|
{
|
|
element = parentElem.namedItem( "LINESPACE" ).toElement(); // used by KWord-0.8
|
|
if ( !element.isNull() )
|
|
{
|
|
layout.lineSpacingType = KoParagLayout::LS_CUSTOM;
|
|
layout.lineSpacing = getAttribute( element, "pt", 0.0 );
|
|
}
|
|
}
|
|
|
|
element = parentElem.namedItem( "LINESPACING" ).toElement(); // KWord-1.0 DTD
|
|
if ( !element.isNull() )
|
|
{
|
|
//compatibility with koffice 1.1
|
|
if ( element.hasAttribute( "value" ))
|
|
{
|
|
TQString value = element.attribute( "value" );
|
|
if ( value == "oneandhalf" )
|
|
{
|
|
layout.lineSpacingType = KoParagLayout::LS_ONEANDHALF;
|
|
layout.lineSpacing = 0;
|
|
}
|
|
else if ( value == "double" )
|
|
{
|
|
layout.lineSpacingType = KoParagLayout::LS_DOUBLE;
|
|
layout.lineSpacing = 0;
|
|
}
|
|
else
|
|
{
|
|
layout.lineSpacingType = KoParagLayout::LS_CUSTOM;
|
|
layout.lineSpacing = value.toDouble();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TQString type = element.attribute( "type" );
|
|
if ( type == "oneandhalf" )
|
|
{
|
|
layout.lineSpacingType = KoParagLayout::LS_ONEANDHALF;
|
|
layout.lineSpacing = 0;
|
|
}
|
|
else if ( type == "double" )
|
|
{
|
|
layout.lineSpacingType = KoParagLayout::LS_DOUBLE;
|
|
layout.lineSpacing = 0;
|
|
}
|
|
else if ( type == "custom" )
|
|
{
|
|
layout.lineSpacingType = KoParagLayout::LS_CUSTOM;
|
|
layout.lineSpacing = element.attribute( "spacingvalue" ).toDouble();
|
|
}
|
|
else if ( type == "atleast" )
|
|
{
|
|
layout.lineSpacingType = KoParagLayout::LS_AT_LEAST;
|
|
layout.lineSpacing = element.attribute( "spacingvalue" ).toDouble();
|
|
}
|
|
else if ( type == "multiple" )
|
|
{
|
|
layout.lineSpacingType = KoParagLayout::LS_MULTIPLE;
|
|
layout.lineSpacing = element.attribute( "spacingvalue" ).toDouble();
|
|
}
|
|
else if ( type == "fixed" )
|
|
{
|
|
layout.lineSpacingType = KoParagLayout::LS_FIXED;
|
|
layout.lineSpacing = element.attribute( "spacingvalue" ).toDouble();
|
|
}
|
|
else if ( type == "single" ) // not used; just in case future versions use it.
|
|
layout.lineSpacingType = KoParagLayout::LS_SINGLE;
|
|
}
|
|
}
|
|
|
|
int pageBreaking = 0;
|
|
element = parentElem.namedItem( "PAGEBREAKING" ).toElement();
|
|
if ( !element.isNull() )
|
|
{
|
|
if ( element.attribute( "linesTogether" ) == "true" )
|
|
pageBreaking |= KoParagLayout::KeepLinesTogether;
|
|
if ( element.attribute( "hardFrameBreak" ) == "true" )
|
|
pageBreaking |= KoParagLayout::HardFrameBreakBefore;
|
|
if ( element.attribute( "hardFrameBreakAfter" ) == "true" )
|
|
pageBreaking |= KoParagLayout::HardFrameBreakAfter;
|
|
}
|
|
if ( docVersion < 2 )
|
|
{
|
|
element = parentElem.namedItem( "HARDBRK" ).toElement(); // KWord-0.8
|
|
if ( !element.isNull() )
|
|
pageBreaking |= KoParagLayout::HardFrameBreakBefore;
|
|
}
|
|
layout.pageBreaking = pageBreaking;
|
|
|
|
element = parentElem.namedItem( "LEFTBORDER" ).toElement();
|
|
if ( !element.isNull() )
|
|
layout.leftBorder = KoBorder::loadBorder( element );
|
|
else
|
|
layout.leftBorder.setPenWidth(0);
|
|
|
|
element = parentElem.namedItem( "RIGHTBORDER" ).toElement();
|
|
if ( !element.isNull() )
|
|
layout.rightBorder = KoBorder::loadBorder( element );
|
|
else
|
|
layout.rightBorder.setPenWidth(0);
|
|
|
|
element = parentElem.namedItem( "TOPBORDER" ).toElement();
|
|
if ( !element.isNull() )
|
|
layout.topBorder = KoBorder::loadBorder( element );
|
|
else
|
|
layout.topBorder.setPenWidth(0);
|
|
|
|
element = parentElem.namedItem( "BOTTOMBORDER" ).toElement();
|
|
if ( !element.isNull() )
|
|
layout.bottomBorder = KoBorder::loadBorder( element );
|
|
else
|
|
layout.bottomBorder.setPenWidth(0);
|
|
|
|
element = parentElem.namedItem( "COUNTER" ).toElement();
|
|
if ( !element.isNull() )
|
|
{
|
|
layout.counter = new KoParagCounter;
|
|
layout.counter->load( element );
|
|
}
|
|
|
|
// Compatibility with KOffice-1.2
|
|
element = parentElem.namedItem( "SHADOW" ).toElement();
|
|
if ( !element.isNull() && element.hasAttribute("direction") )
|
|
{
|
|
int shadowDistance = element.attribute("distance").toInt();
|
|
int shadowDirection = element.attribute("direction").toInt();
|
|
TQColor shadowColor;
|
|
if ( element.hasAttribute("red") )
|
|
{
|
|
int r = element.attribute("red").toInt();
|
|
int g = element.attribute("green").toInt();
|
|
int b = element.attribute("blue").toInt();
|
|
shadowColor.setRgb( r, g, b );
|
|
}
|
|
int distanceX = 0;
|
|
int distanceY = 0;
|
|
switch ( shadowDirection )
|
|
{
|
|
case 1: // KoParagLayout::SD_LEFT_UP:
|
|
case 2: // KoParagLayout::SD_UP:
|
|
case 3: // KoParagLayout::SD_RIGHT_UP:
|
|
distanceX = - shadowDistance;
|
|
break;
|
|
case 7: // KoParagLayout::SD_LEFT_BOTTOM:
|
|
case 6: // KoParagLayout::SD_BOTTOM:
|
|
case 5: // KoParagLayout::SD_RIGHT_BOTTOM:
|
|
distanceX = shadowDistance;
|
|
break;
|
|
}
|
|
switch ( shadowDirection )
|
|
{
|
|
case 7: // KoParagLayout::SD_LEFT_BOTTOM:
|
|
case 8: // KoParagLayout::SD_LEFT:
|
|
case 1: //KoParagLayout::SD_LEFT_UP:
|
|
distanceY = - shadowDistance;
|
|
break;
|
|
case 3: // KoParagLayout::SD_RIGHT_UP:
|
|
case 4: // KoParagLayout::SD_RIGHT:
|
|
case 5: // KoParagLayout::SD_RIGHT_BOTTOM:
|
|
distanceY = shadowDistance;
|
|
break;
|
|
}
|
|
if ( !shadowCssCompat )
|
|
shadowCssCompat = new TQString;
|
|
*shadowCssCompat = KoTextFormat::shadowAsCss( distanceX, distanceY, shadowColor );
|
|
kdDebug(32500) << "setting shadow compat to " << ( *shadowCssCompat ) << endl;
|
|
}
|
|
else
|
|
{
|
|
delete shadowCssCompat;
|
|
shadowCssCompat = 0L;
|
|
}
|
|
}
|
|
|
|
//static
|
|
TQt::AlignmentFlags KoParagLayout::loadOasisAlignment( const TQCString& str )
|
|
{
|
|
return
|
|
str == "left" ? TQt::AlignLeft :
|
|
str == "right" ? TQt::AlignRight :
|
|
str == "start" ? TQt::AlignLeft :
|
|
str == "end" ? TQt::AlignRight :
|
|
str == "center" ? TQt::AlignHCenter :
|
|
str == "justify" ? TQt::AlignJustify :
|
|
str == "start" ? TQt::AlignAuto // i.e. direction-dependent
|
|
: TQt::AlignAuto; // default (can't happen unless spec is extended)
|
|
}
|
|
|
|
//static
|
|
TQCString KoParagLayout::saveOasisAlignment( TQt::AlignmentFlags alignment )
|
|
{
|
|
return alignment == TQt::AlignLeft ? "left" :
|
|
alignment == TQt::AlignRight ? "right" :
|
|
alignment == TQt::AlignHCenter ? "center" :
|
|
alignment == TQt::AlignJustify ? "justify" :
|
|
"start"; // i.e. direction-dependent
|
|
}
|
|
|
|
void KoParagLayout::loadOasisParagLayout( KoParagLayout& layout, KoOasisContext& context )
|
|
{
|
|
context.styleStack().setTypeProperties( "paragraph" );
|
|
// layout is an input and output parameter
|
|
// It can have been initialized already, e.g. by copying from a style
|
|
|
|
// code from OoWriterImport::writeLayout
|
|
if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "text-align" ) ) {
|
|
TQCString align = context.styleStack().attributeNS( KoXmlNS::fo, "text-align" ).latin1();
|
|
layout.alignment = loadOasisAlignment( align );
|
|
}
|
|
|
|
if ( context.styleStack().hasAttributeNS( KoXmlNS::style, "writing-mode" ) ) { // http://web4.w3.org/TR/xsl/slice7.html#writing-mode
|
|
// LTR is lr-tb. RTL is rl-tb
|
|
TQString writingMode = context.styleStack().attributeNS( KoXmlNS::style, "writing-mode" );
|
|
layout.direction = ( writingMode=="rl-tb" || writingMode=="rl" ) ? TQChar::DirR : TQChar::DirL;
|
|
}
|
|
|
|
// Indentation (margins)
|
|
if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "margin-left" ) || // 3.11.19
|
|
context.styleStack().hasAttributeNS( KoXmlNS::fo, "margin-right" ) ) {
|
|
layout.margins[TQStyleSheetItem::MarginLeft] = KoUnit::parseValue( context.styleStack().attributeNS( KoXmlNS::fo, "margin-left" ) );
|
|
layout.margins[TQStyleSheetItem::MarginRight] = KoUnit::parseValue( context.styleStack().attributeNS( KoXmlNS::fo, "margin-right" ) );
|
|
// *text-indent must always be bound to either margin-left or margin-right
|
|
double first = 0;
|
|
if ( context.styleStack().attributeNS( KoXmlNS::style, "auto-text-indent") == "true" ) // style:auto-text-indent takes precedence
|
|
// ### "indented by a value that is based on the current font size"
|
|
// ### and "requires margin-left and margin-right
|
|
// ### but how much is the indent?
|
|
first = 10;
|
|
else if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "text-indent") )
|
|
first = KoUnit::parseValue( context.styleStack().attributeNS( KoXmlNS::fo, "text-indent") );
|
|
|
|
layout.margins[TQStyleSheetItem::MarginFirstLine] = first;
|
|
}
|
|
|
|
// Offset before and after paragraph
|
|
if( context.styleStack().hasAttributeNS( KoXmlNS::fo, "margin-top") || // 3.11.22
|
|
context.styleStack().hasAttributeNS( KoXmlNS::fo, "margin-bottom")) {
|
|
layout.margins[TQStyleSheetItem::MarginTop] = KoUnit::parseValue( context.styleStack().attributeNS( KoXmlNS::fo, "margin-top" ) );
|
|
layout.margins[TQStyleSheetItem::MarginBottom] = KoUnit::parseValue( context.styleStack().attributeNS( KoXmlNS::fo, "margin-bottom" ) );
|
|
}
|
|
|
|
// Line spacing
|
|
if( context.styleStack().hasAttributeNS( KoXmlNS::fo, "line-height") ) { // 3.11.1
|
|
// Fixed line height
|
|
TQString value = context.styleStack().attributeNS( KoXmlNS::fo, "line-height" );
|
|
if ( value != "normal" ) {
|
|
if ( value == "100%" )
|
|
layout.lineSpacingType = KoParagLayout::LS_SINGLE;
|
|
else if( value=="150%")
|
|
layout.lineSpacingType = KoParagLayout::LS_ONEANDHALF;
|
|
else if( value=="200%")
|
|
layout.lineSpacingType = KoParagLayout::LS_DOUBLE;
|
|
else if ( value.find('%') > -1 )
|
|
{
|
|
value = value.remove( '%' );
|
|
double percent = value.toDouble();
|
|
layout.lineSpacingType = KoParagLayout::LS_MULTIPLE;
|
|
layout.lineSpacing = percent / 100.0;
|
|
kdDebug(33001) << "line-height =" << percent << ", " << layout.lineSpacing << ", " << percent/100 << endl;
|
|
}
|
|
else // fixed value
|
|
{
|
|
layout.lineSpacingType = KoParagLayout::LS_FIXED;
|
|
layout.lineSpacing = KoUnit::parseValue( value );
|
|
}
|
|
}
|
|
}
|
|
// Line-height-at-least is mutually exclusive with line-height
|
|
else if ( context.styleStack().hasAttributeNS( KoXmlNS::style, "line-height-at-least") ) // 3.11.2
|
|
{
|
|
TQString value = context.styleStack().attributeNS( KoXmlNS::style, "line-height-at-least" );
|
|
// kotext has "at least" but that's for the linespacing, not for the entire line height!
|
|
// Strange. kotext also has "at least" for the whole line height....
|
|
// Did we make the wrong choice in kotext?
|
|
//kdWarning() << "Unimplemented support for style:line-height-at-least: " << value << endl;
|
|
// Well let's see if this makes a big difference.
|
|
layout.lineSpacingType = KoParagLayout::LS_AT_LEAST;
|
|
layout.lineSpacing = KoUnit::parseValue( value );
|
|
}
|
|
// Line-spacing is mutually exclusive with line-height and line-height-at-least
|
|
else if ( context.styleStack().hasAttributeNS( KoXmlNS::style, "line-spacing") ) // 3.11.3
|
|
{
|
|
double value = KoUnit::parseValue( context.styleStack().attributeNS( KoXmlNS::style, "line-spacing" ) );
|
|
if ( value != 0.0 )
|
|
{
|
|
layout.lineSpacingType = KoParagLayout::LS_CUSTOM;
|
|
layout.lineSpacing = value;
|
|
}
|
|
}
|
|
|
|
// Tabulators
|
|
KoTabulatorList tabList;
|
|
if ( context.styleStack().hasChildNodeNS( KoXmlNS::style, "tab-stops" ) ) { // 3.11.10
|
|
TQDomElement tabStops = context.styleStack().childNodeNS( KoXmlNS::style, "tab-stops" );
|
|
//kdDebug(30519) << k_funcinfo << tabStops.childNodes().count() << " tab stops in layout." << endl;
|
|
TQDomElement tabStop;
|
|
forEachElement( tabStop, tabStops )
|
|
{
|
|
Q_ASSERT( tabStop.localName() == "tab-stop" );
|
|
const TQString type = tabStop.attributeNS( KoXmlNS::style, "type", TQString() ); // left, right, center or char
|
|
|
|
KoTabulator tab;
|
|
tab.ptPos = KoUnit::parseValue( tabStop.attributeNS( KoXmlNS::style, "position", TQString() ) );
|
|
// Tab stop positions in the XML are relative to the left-margin
|
|
tab.ptPos += layout.margins[TQStyleSheetItem::MarginLeft];
|
|
if ( type == "center" )
|
|
tab.type = T_CENTER;
|
|
else if ( type == "right" )
|
|
tab.type = T_RIGHT;
|
|
else if ( type == "char" ) {
|
|
TQString delimiterChar = tabStop.attributeNS( KoXmlNS::style, "char", TQString() ); // single character
|
|
if ( !delimiterChar.isEmpty() )
|
|
tab.alignChar = delimiterChar[0];
|
|
tab.type = T_DEC_PNT; // "alignment on decimal point"
|
|
}
|
|
else //if ( type == "left" )
|
|
tab.type = T_LEFT;
|
|
|
|
tab.ptWidth = KoUnit::parseValue( tabStop.attributeNS( KoXmlNS::style, "leader-width", TQString() ), 0.5 );
|
|
|
|
tab.filling = TF_BLANK;
|
|
if ( tabStop.attributeNS( KoXmlNS::style, "leader-type", TQString() ) == "single" )
|
|
{
|
|
TQString leaderStyle = tabStop.attributeNS( KoXmlNS::style, "leader-style", TQString() );
|
|
if ( leaderStyle == "solid" )
|
|
tab.filling = TF_LINE;
|
|
else if ( leaderStyle == "dotted" )
|
|
tab.filling = TF_DOTS;
|
|
else if ( leaderStyle == "dash" )
|
|
tab.filling = TF_DASH;
|
|
else if ( leaderStyle == "dot-dash" )
|
|
tab.filling = TF_DASH_DOT;
|
|
else if ( leaderStyle == "dot-dot-dash" )
|
|
tab.filling = TF_DASH_DOT_DOT;
|
|
}
|
|
else
|
|
{
|
|
// Fallback: convert leaderChar's unicode value
|
|
TQString leaderChar = tabStop.attributeNS( KoXmlNS::style, "leader-text", TQString() );
|
|
if ( !leaderChar.isEmpty() )
|
|
{
|
|
TQChar ch = leaderChar[0];
|
|
switch (ch.latin1()) {
|
|
case '.':
|
|
tab.filling = TF_DOTS; break;
|
|
case '-':
|
|
case '_': // TODO in KWord: differentiate --- and ___
|
|
tab.filling = TF_LINE; break;
|
|
default:
|
|
// KWord doesn't have support for "any char" as filling.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
tabList.append( tab );
|
|
} //for
|
|
}
|
|
qHeapSort( tabList );
|
|
layout.setTabList( tabList );
|
|
|
|
layout.joinBorder = !( context.styleStack().attributeNS( KoXmlNS::style, "join-border") == "false" );
|
|
|
|
// Borders
|
|
if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "border","left") )
|
|
layout.leftBorder.loadFoBorder( context.styleStack().attributeNS( KoXmlNS::fo, "border","left") );
|
|
else
|
|
layout.leftBorder.setPenWidth(0);
|
|
if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "border","right") )
|
|
layout.rightBorder.loadFoBorder( context.styleStack().attributeNS( KoXmlNS::fo, "border","right") );
|
|
else
|
|
layout.rightBorder.setPenWidth(0);
|
|
if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "border","top") )
|
|
layout.topBorder.loadFoBorder( context.styleStack().attributeNS( KoXmlNS::fo, "border","top") );
|
|
else
|
|
layout.topBorder.setPenWidth(0);
|
|
if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "border","bottom") )
|
|
layout.bottomBorder.loadFoBorder( context.styleStack().attributeNS( KoXmlNS::fo, "border","bottom") );
|
|
else
|
|
layout.bottomBorder.setPenWidth(0);
|
|
|
|
|
|
// Page breaking
|
|
int pageBreaking = 0;
|
|
if( context.styleStack().hasAttributeNS( KoXmlNS::fo, "break-before") ||
|
|
context.styleStack().hasAttributeNS( KoXmlNS::fo, "break-after") ||
|
|
context.styleStack().hasAttributeNS( KoXmlNS::fo, "keep-together") ||
|
|
context.styleStack().hasAttributeNS( KoXmlNS::style, "keep-with-next") ||
|
|
context.styleStack().hasAttributeNS( KoXmlNS::fo, "keep-with-next") )
|
|
{
|
|
if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "break-before") ) { // 3.11.24
|
|
// TODO in KWord: implement difference between "column" and "page"
|
|
if ( context.styleStack().attributeNS( KoXmlNS::fo, "break-before" ) != "auto" )
|
|
pageBreaking |= KoParagLayout::HardFrameBreakBefore;
|
|
}
|
|
else if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "break-after") ) { // 3.11.24
|
|
// TODO in KWord: implement difference between "column" and "page"
|
|
if ( context.styleStack().attributeNS( KoXmlNS::fo, "break-after" ) != "auto" )
|
|
pageBreaking |= KoParagLayout::HardFrameBreakAfter;
|
|
}
|
|
|
|
if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "keep-together" ) ) { // was style:break-inside in OOo-1.1, renamed in OASIS
|
|
if ( context.styleStack().attributeNS( KoXmlNS::fo, "keep-together" ) != "auto" )
|
|
pageBreaking |= KoParagLayout::KeepLinesTogether;
|
|
}
|
|
if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "keep-with-next" ) ) {
|
|
// OASIS spec says it's "auto"/"always", not a boolean.
|
|
TQString val = context.styleStack().attributeNS( KoXmlNS::fo, "keep-with-next" );
|
|
if ( val == "true" || val == "always" )
|
|
pageBreaking |= KoParagLayout::KeepWithNext;
|
|
}
|
|
}
|
|
layout.pageBreaking = pageBreaking;
|
|
|
|
// Paragraph background color - fo:background-color
|
|
// The background color for parts of a paragraph that have no text underneath
|
|
if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "background-color" ) ) {
|
|
TQString bgColor = context.styleStack().attributeNS( KoXmlNS::fo, "background-color");
|
|
if (bgColor != "transparent")
|
|
layout.backgroundColor.setNamedColor( bgColor );
|
|
}
|
|
}
|
|
|
|
void KoParagLayout::saveParagLayout( TQDomElement & parentElem, int alignment ) const
|
|
{
|
|
const KoParagLayout& layout = *this; // code moved from somewhere else;)
|
|
TQDomDocument doc = parentElem.ownerDocument();
|
|
TQDomElement element = doc.createElement( "NAME" );
|
|
parentElem.appendChild( element );
|
|
if ( layout.style )
|
|
element.setAttribute( "value", layout.style->displayName() );
|
|
//else
|
|
// kdWarning() << "KoParagLayout::saveParagLayout: style==0!" << endl;
|
|
|
|
element = doc.createElement( "FLOW" );
|
|
parentElem.appendChild( element );
|
|
|
|
element.setAttribute( "align", alignment==TQt::AlignRight ? "right" :
|
|
alignment==TQt::AlignHCenter ? "center" :
|
|
alignment==TQt::AlignJustify ? "justify" :
|
|
alignment==TQt::AlignAuto ? "auto" : "left" ); // Note: styles can have AlignAuto. Not paragraphs.
|
|
|
|
if ( static_cast<TQChar::Direction>(layout.direction) == TQChar::DirR )
|
|
element.setAttribute( "dir", "R" );
|
|
else
|
|
if ( static_cast<TQChar::Direction>(layout.direction) == TQChar::DirL )
|
|
element.setAttribute( "dir", "L" );
|
|
|
|
if ( layout.margins[TQStyleSheetItem::MarginFirstLine] != 0 ||
|
|
layout.margins[TQStyleSheetItem::MarginLeft] != 0 ||
|
|
layout.margins[TQStyleSheetItem::MarginRight] != 0 )
|
|
{
|
|
element = doc.createElement( "INDENTS" );
|
|
parentElem.appendChild( element );
|
|
if ( layout.margins[TQStyleSheetItem::MarginFirstLine] != 0 )
|
|
element.setAttribute( "first", layout.margins[TQStyleSheetItem::MarginFirstLine] );
|
|
if ( layout.margins[TQStyleSheetItem::MarginLeft] != 0 )
|
|
element.setAttribute( "left", layout.margins[TQStyleSheetItem::MarginLeft] );
|
|
if ( layout.margins[TQStyleSheetItem::MarginRight] != 0 )
|
|
element.setAttribute( "right", layout.margins[TQStyleSheetItem::MarginRight] );
|
|
}
|
|
|
|
if ( layout.margins[TQStyleSheetItem::MarginTop] != 0 ||
|
|
layout.margins[TQStyleSheetItem::MarginBottom] != 0 )
|
|
{
|
|
element = doc.createElement( "OFFSETS" );
|
|
parentElem.appendChild( element );
|
|
if ( layout.margins[TQStyleSheetItem::MarginTop] != 0 )
|
|
element.setAttribute( "before", layout.margins[TQStyleSheetItem::MarginTop] );
|
|
if ( layout.margins[TQStyleSheetItem::MarginBottom] != 0 )
|
|
element.setAttribute( "after", layout.margins[TQStyleSheetItem::MarginBottom] );
|
|
}
|
|
if ( layout.lineSpacingType != LS_SINGLE )
|
|
{
|
|
element = doc.createElement( "LINESPACING" );
|
|
parentElem.appendChild( element );
|
|
if ( layout.lineSpacingType == KoParagLayout::LS_ONEANDHALF ) {
|
|
element.setAttribute( "type", "oneandhalf" );
|
|
element.setAttribute( "value", "oneandhalf" ); //compatibility with koffice 1.2
|
|
}
|
|
else if ( layout.lineSpacingType == KoParagLayout::LS_DOUBLE ) {
|
|
element.setAttribute( "type", "double" );
|
|
element.setAttribute( "value", "double" ); //compatibility with koffice 1.2
|
|
}
|
|
else if ( layout.lineSpacingType == KoParagLayout::LS_CUSTOM )
|
|
{
|
|
element.setAttribute( "type", "custom" );
|
|
element.setAttribute( "spacingvalue", layout.lineSpacing);
|
|
element.setAttribute( "value", layout.lineSpacing ); //compatibility with koffice 1.2
|
|
}
|
|
else if ( layout.lineSpacingType == KoParagLayout::LS_AT_LEAST )
|
|
{
|
|
element.setAttribute( "type", "atleast" );
|
|
element.setAttribute( "spacingvalue", layout.lineSpacing);
|
|
}
|
|
else if ( layout.lineSpacingType == KoParagLayout::LS_MULTIPLE )
|
|
{
|
|
element.setAttribute( "type", "multiple" );
|
|
element.setAttribute( "spacingvalue", layout.lineSpacing);
|
|
}
|
|
else if ( layout.lineSpacingType == KoParagLayout::LS_FIXED )
|
|
{
|
|
element.setAttribute( "type", "fixed" );
|
|
element.setAttribute( "spacingvalue", layout.lineSpacing);
|
|
}
|
|
else
|
|
kdDebug()<<" error in lineSpacing Type\n";
|
|
}
|
|
|
|
if ( layout.pageBreaking != 0 )
|
|
{
|
|
element = doc.createElement( "PAGEBREAKING" );
|
|
parentElem.appendChild( element );
|
|
if ( layout.pageBreaking & KoParagLayout::KeepLinesTogether )
|
|
element.setAttribute( "linesTogether", "true" );
|
|
if ( layout.pageBreaking & KoParagLayout::HardFrameBreakBefore )
|
|
element.setAttribute( "hardFrameBreak", "true" );
|
|
if ( layout.pageBreaking & KoParagLayout::HardFrameBreakAfter )
|
|
element.setAttribute( "hardFrameBreakAfter", "true" );
|
|
}
|
|
|
|
if ( layout.leftBorder.penWidth() > 0 )
|
|
{
|
|
element = doc.createElement( "LEFTBORDER" );
|
|
parentElem.appendChild( element );
|
|
layout.leftBorder.save( element );
|
|
}
|
|
if ( layout.rightBorder.penWidth() > 0 )
|
|
{
|
|
element = doc.createElement( "RIGHTBORDER" );
|
|
parentElem.appendChild( element );
|
|
layout.rightBorder.save( element );
|
|
}
|
|
if ( layout.topBorder.penWidth() > 0 )
|
|
{
|
|
element = doc.createElement( "TOPBORDER" );
|
|
parentElem.appendChild( element );
|
|
layout.topBorder.save( element );
|
|
}
|
|
if ( layout.bottomBorder.penWidth() > 0 )
|
|
{
|
|
element = doc.createElement( "BOTTOMBORDER" );
|
|
parentElem.appendChild( element );
|
|
layout.bottomBorder.save( element );
|
|
}
|
|
if ( layout.counter && layout.counter->numbering() != KoParagCounter::NUM_NONE )
|
|
{
|
|
element = doc.createElement( "COUNTER" );
|
|
parentElem.appendChild( element );
|
|
if ( layout.counter )
|
|
layout.counter->save( element );
|
|
}
|
|
|
|
KoTabulatorList tabList = layout.tabList();
|
|
KoTabulatorList::ConstIterator it = tabList.begin();
|
|
for ( ; it != tabList.end() ; it++ )
|
|
{
|
|
element = doc.createElement( "TABULATOR" );
|
|
parentElem.appendChild( element );
|
|
element.setAttribute( "type", (*it).type );
|
|
element.setAttribute( "ptpos", (*it).ptPos );
|
|
element.setAttribute( "filling", (*it).filling );
|
|
if ( (*it).filling != TF_BLANK )
|
|
element.setAttribute( "width", TQString::number( (*it).ptWidth, 'g', DBL_DIG ) );
|
|
if ( (*it).type == T_DEC_PNT && !(*it).alignChar.isNull() )
|
|
element.setAttribute( "alignchar", TQString((*it).alignChar) );
|
|
}
|
|
}
|
|
|
|
void KoParagLayout::saveOasis( KoGenStyle& gs, KoSavingContext& context, bool savingStyle ) const
|
|
{
|
|
gs.addProperty( "fo:text-align", saveOasisAlignment( (TQt::AlignmentFlags)alignment ).data() );
|
|
// Don't save the direction for a style, if "auto", so that the
|
|
// auto-determination of direction based on first char, works.
|
|
if ( !savingStyle || (TQChar::Direction) direction != TQChar::DirON )
|
|
gs.addProperty( "style:writing-mode", (TQChar::Direction)direction == TQChar::DirR ? "rl-tb" : "lr-tb" );
|
|
gs.addPropertyPt( "fo:margin-left", margins[TQStyleSheetItem::MarginLeft] );
|
|
gs.addPropertyPt( "fo:margin-right", margins[TQStyleSheetItem::MarginRight] );
|
|
gs.addPropertyPt( "fo:text-indent", margins[TQStyleSheetItem::MarginFirstLine] );
|
|
gs.addPropertyPt( "fo:margin-top", margins[TQStyleSheetItem::MarginTop] );
|
|
gs.addPropertyPt( "fo:margin-bottom", margins[TQStyleSheetItem::MarginBottom] );
|
|
|
|
switch ( lineSpacingType ) {
|
|
case KoParagLayout::LS_SINGLE:
|
|
gs.addProperty( "fo:line-height", "100%" );
|
|
break;
|
|
case KoParagLayout::LS_ONEANDHALF:
|
|
gs.addProperty( "fo:line-height", "150%" );
|
|
break;
|
|
case KoParagLayout::LS_DOUBLE:
|
|
gs.addProperty( "fo:line-height", "200%" );
|
|
break;
|
|
case KoParagLayout::LS_MULTIPLE:
|
|
gs.addProperty( "fo:line-height", TQString::number( lineSpacing * 100.0 ) + '%' );
|
|
break;
|
|
case KoParagLayout::LS_FIXED:
|
|
gs.addPropertyPt( "fo:line-height", lineSpacing );
|
|
break;
|
|
case KoParagLayout::LS_CUSTOM:
|
|
gs.addPropertyPt( "style:line-spacing", lineSpacing );
|
|
break;
|
|
case KoParagLayout::LS_AT_LEAST:
|
|
gs.addPropertyPt( "style:line-height-at-least", lineSpacing );
|
|
break;
|
|
}
|
|
|
|
TQBuffer buffer;
|
|
buffer.open( IO_WriteOnly );
|
|
KoXmlWriter tabsWriter( TQT_TQIODEVICE(&buffer), 4 ); // indent==4: root,autostyle,style,parag-props
|
|
tabsWriter.startElement( "style:tab-stops" );
|
|
KoTabulatorList::ConstIterator it = m_tabList.begin();
|
|
for ( ; it != m_tabList.end() ; it++ )
|
|
{
|
|
tabsWriter.startElement( "style:tab-stop" );
|
|
// Tab stop positions in the XML are relative to the left-margin
|
|
double pos = (*it).ptPos - margins[TQStyleSheetItem::MarginLeft];
|
|
tabsWriter.addAttributePt( "style:position", pos );
|
|
|
|
switch ( (*it).type ) {
|
|
case T_LEFT:
|
|
tabsWriter.addAttribute( "style:type", "left" );
|
|
break;
|
|
case T_CENTER:
|
|
tabsWriter.addAttribute( "style:type", "center" );
|
|
break;
|
|
case T_RIGHT:
|
|
tabsWriter.addAttribute( "style:type", "right" );
|
|
break;
|
|
case T_DEC_PNT: // "alignment on decimal point"
|
|
tabsWriter.addAttribute( "style:type", "char" );
|
|
if ( !(*it).alignChar.isNull() )
|
|
tabsWriter.addAttribute( "style:char", TQString( (*it).alignChar ) );
|
|
break;
|
|
case T_INVALID: // keep compiler happy, this can't happen
|
|
break;
|
|
}
|
|
switch( (*it).filling ) {
|
|
case TF_BLANK:
|
|
tabsWriter.addAttribute( "style:leader-type", "none" );
|
|
break;
|
|
case TF_LINE:
|
|
tabsWriter.addAttribute( "style:leader-type", "single" );
|
|
tabsWriter.addAttribute( "style:leader-style", "solid" );
|
|
// Give OOo a chance to show something, since it doesn't support lines here.
|
|
tabsWriter.addAttribute( "style:leader-text", "_" );
|
|
break;
|
|
case TF_DOTS:
|
|
tabsWriter.addAttribute( "style:leader-type", "single" );
|
|
tabsWriter.addAttribute( "style:leader-style", "dotted" );
|
|
// Give OOo a chance to show something, since it doesn't support lines here.
|
|
tabsWriter.addAttribute( "style:leader-text", "." );
|
|
break;
|
|
case TF_DASH:
|
|
tabsWriter.addAttribute( "style:leader-type", "single" );
|
|
tabsWriter.addAttribute( "style:leader-style", "dash" );
|
|
// Give OOo a chance to show something, since it doesn't support lines here.
|
|
tabsWriter.addAttribute( "style:leader-text", "_" );
|
|
break;
|
|
case TF_DASH_DOT:
|
|
tabsWriter.addAttribute( "style:leader-type", "single" );
|
|
tabsWriter.addAttribute( "style:leader-style", "dot-dash" );
|
|
// Give OOo a chance to show something, since it doesn't support lines here.
|
|
tabsWriter.addAttribute( "style:leader-text", "." );
|
|
break;
|
|
case TF_DASH_DOT_DOT:
|
|
tabsWriter.addAttribute( "style:leader-type", "single" );
|
|
tabsWriter.addAttribute( "style:leader-style", "dot-dot-dash" );
|
|
// Give OOo a chance to show something, since it doesn't support lines here.
|
|
tabsWriter.addAttribute( "style:leader-text", "." );
|
|
break;
|
|
}
|
|
if ( (*it).filling != TF_BLANK )
|
|
tabsWriter.addAttributePt( "style:leader-width", (*it).ptWidth );
|
|
// If we want to support it, oasis also defines style:leader-color
|
|
tabsWriter.endElement();
|
|
}
|
|
tabsWriter.endElement();
|
|
buffer.close();
|
|
TQString elementContents = TQString::fromUtf8( buffer.buffer(), buffer.buffer().size() );
|
|
gs.addChildElement( "style:tab-stops", elementContents );
|
|
|
|
if ( !joinBorder )
|
|
gs.addProperty( "style:join-border", "false" );
|
|
bool fourBordersEqual = leftBorder.penWidth() > 0 &&
|
|
leftBorder == rightBorder && rightBorder == topBorder && topBorder == bottomBorder;
|
|
if ( fourBordersEqual ) {
|
|
gs.addProperty( "fo:border", leftBorder.saveFoBorder() );
|
|
} else {
|
|
if ( leftBorder.penWidth() > 0 )
|
|
gs.addProperty( "fo:border-left", leftBorder.saveFoBorder() );
|
|
if ( rightBorder.penWidth() > 0 )
|
|
gs.addProperty( "fo:border-right", rightBorder.saveFoBorder() );
|
|
if ( topBorder.penWidth() > 0 )
|
|
gs.addProperty( "fo:border-top", topBorder.saveFoBorder() );
|
|
if ( bottomBorder.penWidth() > 0 )
|
|
gs.addProperty( "fo:border-bottom", bottomBorder.saveFoBorder() );
|
|
}
|
|
|
|
if ( pageBreaking & KoParagLayout::HardFrameBreakBefore )
|
|
gs.addProperty( "fo:break-before", context.hasColumns() ? "column" : "page" );
|
|
else if ( pageBreaking & KoParagLayout::HardFrameBreakAfter )
|
|
gs.addProperty( "fo:break-after", context.hasColumns() ? "column" : "page" );
|
|
if ( pageBreaking & KoParagLayout::KeepLinesTogether )
|
|
gs.addProperty( "fo:keep-together", "always" );
|
|
if ( pageBreaking & KoParagLayout::KeepWithNext )
|
|
gs.addProperty( "fo:keep-with-next", "always" );
|
|
|
|
gs.addProperty( "fo:background-color",
|
|
backgroundColor.isValid() ?
|
|
backgroundColor.name() : "transparent");
|
|
}
|