/* This file is part of the KDE project Copyright (C) 2001 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoParagLayout.h" #include "KoRichText.h" #include "KoParagCounter.h" #include "KoStyleCollection.h" #include "KoOasisContext.h" #include #include #include #include #include #include #include #include #include #include #include TQString* KoParagLayout::shadowCssCompat = 0L; // Create a default KoParagLayout. KoParagLayout::KoParagLayout() { initialise(); } void KoParagLayout::operator=( const KoParagLayout &tqlayout ) { tqalignment = tqlayout.tqalignment; for ( int i = 0 ; i < 5 ; ++i ) margins[i] = tqlayout.margins[i]; pageBreaking = tqlayout.pageBreaking; leftBorder = tqlayout.leftBorder; rightBorder = tqlayout.rightBorder; topBorder = tqlayout.topBorder; bottomBorder = tqlayout.bottomBorder; joinBorder = tqlayout.joinBorder; backgroundColor = tqlayout.backgroundColor; if ( tqlayout.counter ) counter = new KoParagCounter( *tqlayout.counter ); else counter = 0L; lineSpacing = tqlayout.lineSpacing; lineSpacingType = tqlayout.lineSpacingType; style = tqlayout.style; direction = tqlayout.direction; setTabList( tqlayout.tabList() ); } int KoParagLayout::compare( const KoParagLayout & tqlayout ) const { int flags = 0; if ( tqalignment != tqlayout.tqalignment ) flags |= Alignment; for ( int i = 0 ; i < 5 ; ++i ) if ( margins[i] != tqlayout.margins[i] ) { flags |= Margins; break; } if ( pageBreaking != tqlayout.pageBreaking ) flags |= PageBreaking; if ( leftBorder != tqlayout.leftBorder || rightBorder != tqlayout.rightBorder || topBorder != tqlayout.topBorder || bottomBorder != tqlayout.bottomBorder || joinBorder != tqlayout.joinBorder ) flags |= Borders; if ( tqlayout.counter ) { if ( counter ) { if ( ! ( *tqlayout.counter == *counter ) ) flags |= BulletNumber; } else if ( tqlayout.counter->numbering() != KoParagCounter::NUM_NONE ) flags |= BulletNumber; } else if ( counter && counter->numbering() != KoParagCounter::NUM_NONE ) flags |= BulletNumber; if ( lineSpacing != tqlayout.lineSpacing || lineSpacingType != tqlayout.lineSpacingType ) flags |= LineSpacing; //if ( style != tqlayout.style ) // flags |= Style; if ( m_tabList != tqlayout.m_tabList ) flags |= Tabulator; if ( backgroundColor != tqlayout.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() { tqalignment = 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& tqlayout, const TQDomElement& tqparentElem, int docVersion ) { // tqlayout 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 "tqlayout" // 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 = tqparentElem.firstChild().toElement(); for ( ; !element.isNull() ; element = element.nextSibling().toElement() ) { if ( element.tagName() == "TABULATOR" ) { KoTabulator tab; tab.type = static_cast( getAttribute( element, "type", T_LEFT ) ); tab.ptPos = getAttribute( element, "ptpos", 0.0 ); tab.filling = static_cast( getAttribute( element, "filling", TF_BLANK ) ); tab.ptWidth = getAttribute( element, "width", 0.5 ); TQString alignCharStr = element.attribute("alignchar"); if ( alignCharStr.isEmpty() ) tab.alignChar = KGlobal::locale()->decimalSymbol()[0]; else tab.alignChar = alignCharStr[0]; tabList.append( tab ); } } qHeapSort( tabList ); tqlayout.setTabList( tabList ); tqlayout.tqalignment = TQt::AlignAuto; element = tqparentElem.namedItem( "FLOW" ).toElement(); // Flow is what is now called tqalignment internally if ( !element.isNull() ) { TQString flow = element.attribute( "align" ); // KWord-1.0 DTD if ( !flow.isEmpty() ) { tqlayout.tqalignment = 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" ) tqlayout.direction = TQChar::DirL; else if ( dir == "R" ) tqlayout.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 ) tqlayout.tqalignment = flow2align[flow.toInt()]; } } if ( docVersion < 2 ) { element = tqparentElem.namedItem( "OHEAD" ).toElement(); // used by KWord-0.8 if ( !element.isNull() ) tqlayout.margins[TQStyleSheetItem::MarginTop] = getAttribute( element, "pt", 0.0 ); element = tqparentElem.namedItem( "OFOOT" ).toElement(); // used by KWord-0.8 if ( !element.isNull() ) tqlayout.margins[TQStyleSheetItem::MarginBottom] = getAttribute( element, "pt", 0.0 ); element = tqparentElem.namedItem( "IFIRST" ).toElement(); // used by KWord-0.8 if ( !element.isNull() ) tqlayout.margins[TQStyleSheetItem::MarginFirstLine] = getAttribute( element, "pt", 0.0 ); element = tqparentElem.namedItem( "ILEFT" ).toElement(); // used by KWord-0.8 if ( !element.isNull() ) tqlayout.margins[TQStyleSheetItem::MarginLeft] = getAttribute( element, "pt", 0.0 ); } // KWord-1.0 DTD element = tqparentElem.namedItem( "INDENTS" ).toElement(); if ( !element.isNull() ) { tqlayout.margins[TQStyleSheetItem::MarginFirstLine] = getAttribute( element, "first", 0.0 ); tqlayout.margins[TQStyleSheetItem::MarginLeft] = getAttribute( element, "left", 0.0 ); tqlayout.margins[TQStyleSheetItem::MarginRight] = getAttribute( element, "right", 0.0 ); } element = tqparentElem.namedItem( "OFFSETS" ).toElement(); if ( !element.isNull() ) { tqlayout.margins[TQStyleSheetItem::MarginTop] = getAttribute( element, "before", 0.0 ); tqlayout.margins[TQStyleSheetItem::MarginBottom] = getAttribute( element, "after", 0.0 ); } if ( docVersion < 2 ) { element = tqparentElem.namedItem( "LINESPACE" ).toElement(); // used by KWord-0.8 if ( !element.isNull() ) { tqlayout.lineSpacingType = KoParagLayout::LS_CUSTOM; tqlayout.lineSpacing = getAttribute( element, "pt", 0.0 ); } } element = tqparentElem.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" ) { tqlayout.lineSpacingType = KoParagLayout::LS_ONEANDHALF; tqlayout.lineSpacing = 0; } else if ( value == "double" ) { tqlayout.lineSpacingType = KoParagLayout::LS_DOUBLE; tqlayout.lineSpacing = 0; } else { tqlayout.lineSpacingType = KoParagLayout::LS_CUSTOM; tqlayout.lineSpacing = value.toDouble(); } } else { TQString type = element.attribute( "type" ); if ( type == "oneandhalf" ) { tqlayout.lineSpacingType = KoParagLayout::LS_ONEANDHALF; tqlayout.lineSpacing = 0; } else if ( type == "double" ) { tqlayout.lineSpacingType = KoParagLayout::LS_DOUBLE; tqlayout.lineSpacing = 0; } else if ( type == "custom" ) { tqlayout.lineSpacingType = KoParagLayout::LS_CUSTOM; tqlayout.lineSpacing = element.attribute( "spacingvalue" ).toDouble(); } else if ( type == "atleast" ) { tqlayout.lineSpacingType = KoParagLayout::LS_AT_LEAST; tqlayout.lineSpacing = element.attribute( "spacingvalue" ).toDouble(); } else if ( type == "multiple" ) { tqlayout.lineSpacingType = KoParagLayout::LS_MULTIPLE; tqlayout.lineSpacing = element.attribute( "spacingvalue" ).toDouble(); } else if ( type == "fixed" ) { tqlayout.lineSpacingType = KoParagLayout::LS_FIXED; tqlayout.lineSpacing = element.attribute( "spacingvalue" ).toDouble(); } else if ( type == "single" ) // not used; just in case future versions use it. tqlayout.lineSpacingType = KoParagLayout::LS_SINGLE; } } int pageBreaking = 0; element = tqparentElem.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 = tqparentElem.namedItem( "HARDBRK" ).toElement(); // KWord-0.8 if ( !element.isNull() ) pageBreaking |= KoParagLayout::HardFrameBreakBefore; } tqlayout.pageBreaking = pageBreaking; element = tqparentElem.namedItem( "LEFTBORDER" ).toElement(); if ( !element.isNull() ) tqlayout.leftBorder = KoBorder::loadBorder( element ); else tqlayout.leftBorder.setPenWidth(0); element = tqparentElem.namedItem( "RIGHTBORDER" ).toElement(); if ( !element.isNull() ) tqlayout.rightBorder = KoBorder::loadBorder( element ); else tqlayout.rightBorder.setPenWidth(0); element = tqparentElem.namedItem( "TOPBORDER" ).toElement(); if ( !element.isNull() ) tqlayout.topBorder = KoBorder::loadBorder( element ); else tqlayout.topBorder.setPenWidth(0); element = tqparentElem.namedItem( "BOTTOMBORDER" ).toElement(); if ( !element.isNull() ) tqlayout.bottomBorder = KoBorder::loadBorder( element ); else tqlayout.bottomBorder.setPenWidth(0); element = tqparentElem.namedItem( "COUNTER" ).toElement(); if ( !element.isNull() ) { tqlayout.counter = new KoParagCounter; tqlayout.counter->load( element ); } // Compatibility with KOffice-1.2 element = tqparentElem.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 tqalignment ) { return tqalignment == TQt::AlignLeft ? "left" : tqalignment == TQt::AlignRight ? "right" : tqalignment == TQt::AlignHCenter ? "center" : tqalignment == TQt::AlignJustify ? "justify" : "start"; // i.e. direction-dependent } void KoParagLayout::loadOasisParagLayout( KoParagLayout& tqlayout, KoOasisContext& context ) { context.styleStack().setTypeProperties( "paragraph" ); // tqlayout 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(); tqlayout.tqalignment = 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" ); tqlayout.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" ) ) { tqlayout.margins[TQStyleSheetItem::MarginLeft] = KoUnit::parseValue( context.styleStack().attributeNS( KoXmlNS::fo, "margin-left" ) ); tqlayout.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") ); tqlayout.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")) { tqlayout.margins[TQStyleSheetItem::MarginTop] = KoUnit::parseValue( context.styleStack().attributeNS( KoXmlNS::fo, "margin-top" ) ); tqlayout.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%" ) tqlayout.lineSpacingType = KoParagLayout::LS_SINGLE; else if( value=="150%") tqlayout.lineSpacingType = KoParagLayout::LS_ONEANDHALF; else if( value=="200%") tqlayout.lineSpacingType = KoParagLayout::LS_DOUBLE; else if ( value.tqfind('%') > -1 ) { value = value.remove( '%' ); double percent = value.toDouble(); tqlayout.lineSpacingType = KoParagLayout::LS_MULTIPLE; tqlayout.lineSpacing = percent / 100.0; kdDebug(33001) << "line-height =" << percent << ", " << tqlayout.lineSpacing << ", " << percent/100 << endl; } else // fixed value { tqlayout.lineSpacingType = KoParagLayout::LS_FIXED; tqlayout.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. tqlayout.lineSpacingType = KoParagLayout::LS_AT_LEAST; tqlayout.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 ) { tqlayout.lineSpacingType = KoParagLayout::LS_CUSTOM; tqlayout.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 tqlayout." << 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 += tqlayout.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; // "tqalignment 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 tqunicode 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 ); tqlayout.setTabList( tabList ); tqlayout.joinBorder = !( context.styleStack().attributeNS( KoXmlNS::style, "join-border") == "false" ); // Borders if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "border","left") ) tqlayout.leftBorder.loadFoBorder( context.styleStack().attributeNS( KoXmlNS::fo, "border","left") ); else tqlayout.leftBorder.setPenWidth(0); if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "border","right") ) tqlayout.rightBorder.loadFoBorder( context.styleStack().attributeNS( KoXmlNS::fo, "border","right") ); else tqlayout.rightBorder.setPenWidth(0); if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "border","top") ) tqlayout.topBorder.loadFoBorder( context.styleStack().attributeNS( KoXmlNS::fo, "border","top") ); else tqlayout.topBorder.setPenWidth(0); if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "border","bottom") ) tqlayout.bottomBorder.loadFoBorder( context.styleStack().attributeNS( KoXmlNS::fo, "border","bottom") ); else tqlayout.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; } } tqlayout.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") tqlayout.backgroundColor.setNamedColor( bgColor ); } } void KoParagLayout::saveParagLayout( TQDomElement & tqparentElem, int tqalignment ) const { const KoParagLayout& tqlayout = *this; // code moved from somewhere else;) TQDomDocument doc = tqparentElem.ownerDocument(); TQDomElement element = doc.createElement( "NAME" ); tqparentElem.appendChild( element ); if ( tqlayout.style ) element.setAttribute( "value", tqlayout.style->displayName() ); //else // kdWarning() << "KoParagLayout::saveParagLayout: style==0!" << endl; element = doc.createElement( "FLOW" ); tqparentElem.appendChild( element ); element.setAttribute( "align", tqalignment==TQt::AlignRight ? "right" : tqalignment==TQt::AlignHCenter ? "center" : tqalignment==TQt::AlignJustify ? "justify" : tqalignment==TQt::AlignAuto ? "auto" : "left" ); // Note: styles can have AlignAuto. Not paragraphs. if ( static_cast(tqlayout.direction) == TQChar::DirR ) element.setAttribute( "dir", "R" ); else if ( static_cast(tqlayout.direction) == TQChar::DirL ) element.setAttribute( "dir", "L" ); if ( tqlayout.margins[TQStyleSheetItem::MarginFirstLine] != 0 || tqlayout.margins[TQStyleSheetItem::MarginLeft] != 0 || tqlayout.margins[TQStyleSheetItem::MarginRight] != 0 ) { element = doc.createElement( "INDENTS" ); tqparentElem.appendChild( element ); if ( tqlayout.margins[TQStyleSheetItem::MarginFirstLine] != 0 ) element.setAttribute( "first", tqlayout.margins[TQStyleSheetItem::MarginFirstLine] ); if ( tqlayout.margins[TQStyleSheetItem::MarginLeft] != 0 ) element.setAttribute( "left", tqlayout.margins[TQStyleSheetItem::MarginLeft] ); if ( tqlayout.margins[TQStyleSheetItem::MarginRight] != 0 ) element.setAttribute( "right", tqlayout.margins[TQStyleSheetItem::MarginRight] ); } if ( tqlayout.margins[TQStyleSheetItem::MarginTop] != 0 || tqlayout.margins[TQStyleSheetItem::MarginBottom] != 0 ) { element = doc.createElement( "OFFSETS" ); tqparentElem.appendChild( element ); if ( tqlayout.margins[TQStyleSheetItem::MarginTop] != 0 ) element.setAttribute( "before", tqlayout.margins[TQStyleSheetItem::MarginTop] ); if ( tqlayout.margins[TQStyleSheetItem::MarginBottom] != 0 ) element.setAttribute( "after", tqlayout.margins[TQStyleSheetItem::MarginBottom] ); } if ( tqlayout.lineSpacingType != LS_SINGLE ) { element = doc.createElement( "LINESPACING" ); tqparentElem.appendChild( element ); if ( tqlayout.lineSpacingType == KoParagLayout::LS_ONEANDHALF ) { element.setAttribute( "type", "oneandhalf" ); element.setAttribute( "value", "oneandhalf" ); //compatibility with koffice 1.2 } else if ( tqlayout.lineSpacingType == KoParagLayout::LS_DOUBLE ) { element.setAttribute( "type", "double" ); element.setAttribute( "value", "double" ); //compatibility with koffice 1.2 } else if ( tqlayout.lineSpacingType == KoParagLayout::LS_CUSTOM ) { element.setAttribute( "type", "custom" ); element.setAttribute( "spacingvalue", tqlayout.lineSpacing); element.setAttribute( "value", tqlayout.lineSpacing ); //compatibility with koffice 1.2 } else if ( tqlayout.lineSpacingType == KoParagLayout::LS_AT_LEAST ) { element.setAttribute( "type", "atleast" ); element.setAttribute( "spacingvalue", tqlayout.lineSpacing); } else if ( tqlayout.lineSpacingType == KoParagLayout::LS_MULTIPLE ) { element.setAttribute( "type", "multiple" ); element.setAttribute( "spacingvalue", tqlayout.lineSpacing); } else if ( tqlayout.lineSpacingType == KoParagLayout::LS_FIXED ) { element.setAttribute( "type", "fixed" ); element.setAttribute( "spacingvalue", tqlayout.lineSpacing); } else kdDebug()<<" error in lineSpacing Type\n"; } if ( tqlayout.pageBreaking != 0 ) { element = doc.createElement( "PAGEBREAKING" ); tqparentElem.appendChild( element ); if ( tqlayout.pageBreaking & KoParagLayout::KeepLinesTogether ) element.setAttribute( "linesTogether", "true" ); if ( tqlayout.pageBreaking & KoParagLayout::HardFrameBreakBefore ) element.setAttribute( "hardFrameBreak", "true" ); if ( tqlayout.pageBreaking & KoParagLayout::HardFrameBreakAfter ) element.setAttribute( "hardFrameBreakAfter", "true" ); } if ( tqlayout.leftBorder.penWidth() > 0 ) { element = doc.createElement( "LEFTBORDER" ); tqparentElem.appendChild( element ); tqlayout.leftBorder.save( element ); } if ( tqlayout.rightBorder.penWidth() > 0 ) { element = doc.createElement( "RIGHTBORDER" ); tqparentElem.appendChild( element ); tqlayout.rightBorder.save( element ); } if ( tqlayout.topBorder.penWidth() > 0 ) { element = doc.createElement( "TOPBORDER" ); tqparentElem.appendChild( element ); tqlayout.topBorder.save( element ); } if ( tqlayout.bottomBorder.penWidth() > 0 ) { element = doc.createElement( "BOTTOMBORDER" ); tqparentElem.appendChild( element ); tqlayout.bottomBorder.save( element ); } if ( tqlayout.counter && tqlayout.counter->numbering() != KoParagCounter::NUM_NONE ) { element = doc.createElement( "COUNTER" ); tqparentElem.appendChild( element ); if ( tqlayout.counter ) tqlayout.counter->save( element ); } KoTabulatorList tabList = tqlayout.tabList(); KoTabulatorList::ConstIterator it = tabList.begin(); for ( ; it != tabList.end() ; it++ ) { element = doc.createElement( "TABULATOR" ); tqparentElem.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)tqalignment ).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: // "tqalignment 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"); }