lib Library API Documentation

koparaglayout.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 2001 David Faure <faure@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00017    Boston, MA 02111-1307, USA.
00018 */
00019 
00020 #include "koparaglayout.h"
00021 #include "korichtext.h"
00022 #include "koparagcounter.h"
00023 #include "kostyle.h"
00024 #include "kooasiscontext.h"
00025 #include <koxmlwriter.h>
00026 #include <koxmlns.h>
00027 #include <kodom.h>
00028 #include <koGenStyles.h>
00029 
00030 #include <kglobal.h>
00031 #include <klocale.h>
00032 #include <kdebug.h>
00033 #include <qdom.h>
00034 #include <qbuffer.h>
00035 
00036 #include <float.h>
00037 
00038 QString* KoParagLayout::shadowCssCompat = 0L;
00039 
00040 // Create a default KoParagLayout.
00041 KoParagLayout::KoParagLayout()
00042 {
00043     initialise();
00044 }
00045 
00046 void KoParagLayout::operator=( const KoParagLayout &layout )
00047 {
00048     alignment = layout.alignment;
00049     for ( int i = 0 ; i < 5 ; ++i )
00050         margins[i] = layout.margins[i];
00051     pageBreaking = layout.pageBreaking;
00052     leftBorder = layout.leftBorder;
00053     rightBorder = layout.rightBorder;
00054     topBorder = layout.topBorder;
00055     bottomBorder = layout.bottomBorder;
00056     if ( layout.counter )
00057         counter = new KoParagCounter( *layout.counter );
00058     else
00059         counter = 0L;
00060     lineSpacing = layout.lineSpacing;
00061     lineSpacingType = layout.lineSpacingType;
00062     style = layout.style;
00063     direction = layout.direction;
00064     setTabList( layout.tabList() );
00065 }
00066 
00067 int KoParagLayout::compare( const KoParagLayout & layout ) const
00068 {
00069     int flags = 0;
00070     if ( alignment != layout.alignment )
00071         flags |= Alignment;
00072     for ( int i = 0 ; i < 5 ; ++i )
00073         if ( margins[i] != layout.margins[i] )
00074         {
00075             flags |= Margins;
00076             break;
00077         }
00078     if ( pageBreaking != layout.pageBreaking )
00079         flags |= PageBreaking;
00080     if ( leftBorder != layout.leftBorder
00081          || rightBorder != layout.rightBorder
00082          || topBorder != layout.topBorder
00083          || bottomBorder != layout.bottomBorder )
00084         flags |= Borders;
00085 
00086     if ( layout.counter )
00087     {
00088         if ( counter )
00089         {
00090             if ( ! ( *layout.counter == *counter ) )
00091                 flags |= BulletNumber;
00092         } else
00093             if ( layout.counter->numbering() != KoParagCounter::NUM_NONE )
00094                 flags |= BulletNumber;
00095     }
00096     else
00097         if ( counter && counter->numbering() != KoParagCounter::NUM_NONE )
00098             flags |= BulletNumber;
00099 
00100     if ( lineSpacing != layout.lineSpacing
00101         || lineSpacingType != layout.lineSpacingType )
00102         flags |= LineSpacing;
00103     //if ( style != layout.style )
00104     //    flags |= Style;
00105     if ( m_tabList != layout.m_tabList )
00106         flags |= Tabulator;
00107 
00108     // This method is used for the GUI stuff only, so we don't have a flag
00109     // for the Direction value.
00110     return flags;
00111 }
00112 
00113 void KoParagLayout::initialise()
00114 {
00115     alignment = Qt::AlignAuto;
00116     for ( int i = 0 ; i < 5 ; ++i ) // use memset ?
00117         margins[i] = 0;
00118     lineSpacingType = LS_SINGLE;
00119     lineSpacing = 0;
00120     counter = 0L;
00121     leftBorder.setPenWidth( 0);
00122     rightBorder.setPenWidth( 0);
00123     topBorder.setPenWidth( 0);
00124     bottomBorder.setPenWidth( 0);
00125     pageBreaking = 0;
00126     style = 0L;
00127     direction = QChar::DirON;
00128     m_tabList.clear();
00129 }
00130 
00131 KoParagLayout::~KoParagLayout()
00132 {
00133     delete counter;
00134 }
00135 
00136 void KoParagLayout::loadParagLayout( KoParagLayout& layout, const QDomElement& parentElem, int docVersion )
00137 {
00138     // layout is an input and output parameter
00139     // It can have been initialized already, e.g. by copying from a style
00140     // (we don't do that anymore though).
00141 
00142     // Load the paragraph tabs - we load into a clean list, not mixing with those already in "layout"
00143     // We can't apply the 'default comes from the style' in this case, because
00144     // there is no way to differentiate between "I want no tabs in the parag"
00145     // and "use default from style".
00146     KoTabulatorList tabList;
00147     QDomElement element = parentElem.firstChild().toElement();
00148     for ( ; !element.isNull() ; element = element.nextSibling().toElement() )
00149     {
00150         if ( element.tagName() == "TABULATOR" )
00151         {
00152             KoTabulator tab;
00153             tab.type = static_cast<KoTabulators>( getAttribute( element, "type", T_LEFT ) );
00154             tab.ptPos = getAttribute( element, "ptpos", 0.0 );
00155             tab.filling = static_cast<KoTabulatorFilling>( getAttribute( element, "filling", TF_BLANK ) );
00156             tab.ptWidth = getAttribute( element, "width", 0.5 );
00157             QString alignCharStr = element.attribute("alignchar");
00158             if ( alignCharStr.isEmpty() )
00159                 tab.alignChar = KGlobal::locale()->decimalSymbol()[0];
00160             else
00161                 tab.alignChar = alignCharStr[0];
00162             tabList.append( tab );
00163         }
00164     }
00165     qHeapSort( tabList );
00166     layout.setTabList( tabList );
00167     layout.alignment = Qt::AlignAuto;
00168     element = parentElem.namedItem( "FLOW" ).toElement(); // Flow is what is now called alignment internally
00169     if ( !element.isNull() )
00170     {
00171         QString flow = element.attribute( "align" ); // KWord-1.0 DTD
00172         if ( !flow.isEmpty() )
00173         {
00174             layout.alignment = flow=="right" ? Qt::AlignRight :
00175                          flow=="center" ? Qt::AlignHCenter :
00176                          flow=="justify" ? Qt::AlignJustify :
00177                          flow=="left" ? Qt::AlignLeft : Qt::AlignAuto;
00178 
00179             QString dir = element.attribute( "dir" ); // KWord-1.2
00180             if ( !dir.isEmpty() ) {
00181                 if ( dir == "L" )
00182                     layout.direction = QChar::DirL;
00183                 else if ( dir == "R" )
00184                     layout.direction = QChar::DirR;
00185                 else
00186                     kdWarning() << "Unexpected value for paragraph direction: " << dir << endl;
00187             }
00188         } else {
00189             flow = element.attribute( "value" ); // KWord-0.8
00190             static const int flow2align[] = { Qt::AlignAuto, Qt::AlignRight, Qt::AlignHCenter, Qt::AlignJustify };
00191             if ( !flow.isEmpty() && flow.toInt() < 4 )
00192                 layout.alignment = flow2align[flow.toInt()];
00193         }
00194     }
00195 
00196     if ( docVersion < 2 )
00197     {
00198         element = parentElem.namedItem( "OHEAD" ).toElement(); // used by KWord-0.8
00199         if ( !element.isNull() )
00200             layout.margins[QStyleSheetItem::MarginTop] = getAttribute( element, "pt", 0.0 );
00201 
00202         element = parentElem.namedItem( "OFOOT" ).toElement(); // used by KWord-0.8
00203         if ( !element.isNull() )
00204             layout.margins[QStyleSheetItem::MarginBottom] = getAttribute( element, "pt", 0.0 );
00205 
00206         element = parentElem.namedItem( "IFIRST" ).toElement(); // used by KWord-0.8
00207         if ( !element.isNull() )
00208             layout.margins[QStyleSheetItem::MarginFirstLine] = getAttribute( element, "pt", 0.0 );
00209 
00210         element = parentElem.namedItem( "ILEFT" ).toElement(); // used by KWord-0.8
00211         if ( !element.isNull() )
00212             layout.margins[QStyleSheetItem::MarginLeft] = getAttribute( element, "pt", 0.0 );
00213     }
00214 
00215     // KWord-1.0 DTD
00216     element = parentElem.namedItem( "INDENTS" ).toElement();
00217     if ( !element.isNull() )
00218     {
00219         layout.margins[QStyleSheetItem::MarginFirstLine] = getAttribute( element, "first", 0.0 );
00220         layout.margins[QStyleSheetItem::MarginLeft] = getAttribute( element, "left", 0.0 );
00221         layout.margins[QStyleSheetItem::MarginRight] = getAttribute( element, "right", 0.0 );
00222     }
00223     element = parentElem.namedItem( "OFFSETS" ).toElement();
00224     if ( !element.isNull() )
00225     {
00226         layout.margins[QStyleSheetItem::MarginTop] = getAttribute( element, "before", 0.0 );
00227         layout.margins[QStyleSheetItem::MarginBottom] = getAttribute( element, "after", 0.0 );
00228     }
00229 
00230     if ( docVersion < 2 )
00231     {
00232         element = parentElem.namedItem( "LINESPACE" ).toElement(); // used by KWord-0.8
00233         if ( !element.isNull() )
00234         {
00235             layout.lineSpacingType = KoParagLayout::LS_CUSTOM;
00236             layout.lineSpacing = getAttribute( element, "pt", 0.0 );
00237         }
00238     }
00239 
00240     element = parentElem.namedItem( "LINESPACING" ).toElement(); // KWord-1.0 DTD
00241     if ( !element.isNull() )
00242     {
00243         //compatibility with koffice 1.1
00244         if ( element.hasAttribute( "value" ))
00245         {
00246             QString value = element.attribute( "value" );
00247             if ( value == "oneandhalf" )
00248             {
00249                 layout.lineSpacingType = KoParagLayout::LS_ONEANDHALF;
00250                 layout.lineSpacing = 0;
00251             }
00252             else if ( value == "double" )
00253             {
00254                 layout.lineSpacingType = KoParagLayout::LS_DOUBLE;
00255                 layout.lineSpacing = 0;
00256             }
00257             else
00258             {
00259                 layout.lineSpacingType = KoParagLayout::LS_CUSTOM;
00260                 layout.lineSpacing = value.toDouble();
00261             }
00262         }
00263         else
00264         {
00265             QString type = element.attribute( "type" );
00266             if ( type == "oneandhalf" )
00267             {
00268                 layout.lineSpacingType = KoParagLayout::LS_ONEANDHALF;
00269                 layout.lineSpacing = 0;
00270             }
00271             else if ( type == "double" )
00272             {
00273                 layout.lineSpacingType = KoParagLayout::LS_DOUBLE;
00274                 layout.lineSpacing = 0;
00275             }
00276             else if ( type == "custom" )
00277             {
00278                 layout.lineSpacingType = KoParagLayout::LS_CUSTOM;
00279                 layout.lineSpacing = element.attribute( "spacingvalue" ).toDouble();
00280             }
00281             else if ( type == "atleast" )
00282             {
00283                 layout.lineSpacingType = KoParagLayout::LS_AT_LEAST;
00284                 layout.lineSpacing = element.attribute( "spacingvalue" ).toDouble();
00285             }
00286             else if ( type == "multiple" )
00287             {
00288                 layout.lineSpacingType = KoParagLayout::LS_MULTIPLE;
00289                 layout.lineSpacing = element.attribute( "spacingvalue" ).toDouble();
00290             }
00291             else if ( type == "fixed" )
00292             {
00293                 layout.lineSpacingType = KoParagLayout::LS_FIXED;
00294                 layout.lineSpacing = element.attribute( "spacingvalue" ).toDouble();
00295             }
00296             else if ( type == "single" ) // not used; just in case future versions use it.
00297                 layout.lineSpacingType = KoParagLayout::LS_SINGLE;
00298         }
00299     }
00300 
00301     int pageBreaking = 0;
00302     element = parentElem.namedItem( "PAGEBREAKING" ).toElement();
00303     if ( !element.isNull() )
00304     {
00305         if ( element.attribute( "linesTogether" ) == "true" )
00306             pageBreaking |= KoParagLayout::KeepLinesTogether;
00307         if ( element.attribute( "hardFrameBreak" ) == "true" )
00308             pageBreaking |= KoParagLayout::HardFrameBreakBefore;
00309         if ( element.attribute( "hardFrameBreakAfter" ) == "true" )
00310             pageBreaking |= KoParagLayout::HardFrameBreakAfter;
00311     }
00312     if ( docVersion < 2 )
00313     {
00314         element = parentElem.namedItem( "HARDBRK" ).toElement(); // KWord-0.8
00315         if ( !element.isNull() )
00316             pageBreaking |= KoParagLayout::HardFrameBreakBefore;
00317     }
00318     layout.pageBreaking = pageBreaking;
00319 
00320     element = parentElem.namedItem( "LEFTBORDER" ).toElement();
00321     if ( !element.isNull() )
00322         layout.leftBorder = KoBorder::loadBorder( element );
00323     else
00324         layout.leftBorder.setPenWidth(0);
00325 
00326     element = parentElem.namedItem( "RIGHTBORDER" ).toElement();
00327     if ( !element.isNull() )
00328         layout.rightBorder = KoBorder::loadBorder( element );
00329     else
00330         layout.rightBorder.setPenWidth(0);
00331 
00332     element = parentElem.namedItem( "TOPBORDER" ).toElement();
00333     if ( !element.isNull() )
00334         layout.topBorder = KoBorder::loadBorder( element );
00335     else
00336         layout.topBorder.setPenWidth(0);
00337 
00338     element = parentElem.namedItem( "BOTTOMBORDER" ).toElement();
00339     if ( !element.isNull() )
00340         layout.bottomBorder = KoBorder::loadBorder( element );
00341     else
00342         layout.bottomBorder.setPenWidth(0);
00343 
00344     element = parentElem.namedItem( "COUNTER" ).toElement();
00345     if ( !element.isNull() )
00346     {
00347         layout.counter = new KoParagCounter;
00348         layout.counter->load( element );
00349     }
00350 
00351     // Compatibility with KOffice-1.2
00352     element = parentElem.namedItem( "SHADOW" ).toElement();
00353     if ( !element.isNull() && element.hasAttribute("direction") )
00354     {
00355         int shadowDistance = element.attribute("distance").toInt();
00356         int shadowDirection = element.attribute("direction").toInt();
00357         QColor shadowColor;
00358         if ( element.hasAttribute("red") )
00359         {
00360             int r = element.attribute("red").toInt();
00361             int g = element.attribute("green").toInt();
00362             int b = element.attribute("blue").toInt();
00363             shadowColor.setRgb( r, g, b );
00364         }
00365         int distanceX = 0;
00366         int distanceY = 0;
00367         switch ( shadowDirection )
00368         {
00369         case 1: // KoParagLayout::SD_LEFT_UP:
00370         case 2: // KoParagLayout::SD_UP:
00371         case 3: // KoParagLayout::SD_RIGHT_UP:
00372             distanceX = - shadowDistance;
00373         case 7: // KoParagLayout::SD_LEFT_BOTTOM:
00374         case 6: // KoParagLayout::SD_BOTTOM:
00375         case 5: // KoParagLayout::SD_RIGHT_BOTTOM:
00376             distanceX = shadowDistance;
00377         }
00378         switch ( shadowDirection )
00379         {
00380         case 7: // KoParagLayout::SD_LEFT_BOTTOM:
00381         case 8: // KoParagLayout::SD_LEFT:
00382         case 1: //KoParagLayout::SD_LEFT_UP:
00383             distanceY = - shadowDistance;
00384         case 3: // KoParagLayout::SD_RIGHT_UP:
00385         case 4: // KoParagLayout::SD_RIGHT:
00386         case 5: // KoParagLayout::SD_RIGHT_BOTTOM:
00387             distanceY = shadowDistance;
00388         }
00389         if ( !shadowCssCompat )
00390             shadowCssCompat = new QString;
00391         *shadowCssCompat = KoTextFormat::shadowAsCss( distanceX, distanceY, shadowColor );
00392         kdDebug(32500) << "setting shadow compat to " << ( *shadowCssCompat ) << endl;
00393     }
00394     else
00395     {
00396         delete shadowCssCompat;
00397         shadowCssCompat = 0L;
00398     }
00399 }
00400 
00401 //static
00402 Qt::AlignmentFlags KoParagLayout::loadOasisAlignment( const QCString& str )
00403 {
00404     return
00405         str == "left" ? Qt::AlignLeft :
00406         str == "right" ? Qt::AlignRight :
00407         str == "start" ? Qt::AlignLeft :
00408         str == "end" ? Qt::AlignRight :
00409         str == "center" ? Qt::AlignHCenter :
00410         str == "justify" ? Qt::AlignJustify :
00411         str == "start" ? Qt::AlignAuto // i.e. direction-dependent
00412         : Qt::AlignAuto; // default (can't happen unless spec is extended)
00413 }
00414 
00415 //static
00416 QCString KoParagLayout::saveOasisAlignment( Qt::AlignmentFlags alignment )
00417 {
00418    return alignment == Qt::AlignLeft ? "left" :
00419        alignment == Qt::AlignRight ? "right" :
00420        alignment == Qt::AlignHCenter ? "center" :
00421        alignment == Qt::AlignJustify ? "justify" :
00422        "start"; // i.e. direction-dependent
00423 }
00424 
00425 void KoParagLayout::loadOasisParagLayout( KoParagLayout& layout, KoOasisContext& context )
00426 {
00427     context.styleStack().setTypeProperties( "paragraph" );
00428     // layout is an input and output parameter
00429     // It can have been initialized already, e.g. by copying from a style
00430 
00431     // code from OoWriterImport::writeLayout
00432     if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "text-align" ) ) {
00433         QCString align = context.styleStack().attributeNS( KoXmlNS::fo, "text-align" ).latin1();
00434         layout.alignment = loadOasisAlignment( align );
00435     }
00436 
00437     if ( context.styleStack().hasAttributeNS( KoXmlNS::style, "writing-mode" ) ) { // http://web4.w3.org/TR/xsl/slice7.html#writing-mode
00438         // LTR is lr-tb. RTL is rl-tb
00439         QString writingMode = context.styleStack().attributeNS( KoXmlNS::style, "writing-mode" );
00440         layout.direction = ( writingMode=="rl-tb" || writingMode=="rl" ) ? QChar::DirR : QChar::DirL;
00441     }
00442 
00443     // Indentation (margins)
00444     if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "margin-left" ) || // 3.11.19
00445          context.styleStack().hasAttributeNS( KoXmlNS::fo, "margin-right" ) ) {
00446         layout.margins[QStyleSheetItem::MarginLeft] = KoUnit::parseValue( context.styleStack().attributeNS( KoXmlNS::fo, "margin-left" ) );
00447         layout.margins[QStyleSheetItem::MarginRight] = KoUnit::parseValue( context.styleStack().attributeNS( KoXmlNS::fo, "margin-right" ) );
00448         // *text-indent must always be bound to either margin-left or margin-right
00449         double first = 0;
00450         if ( context.styleStack().attributeNS( KoXmlNS::style, "auto-text-indent") == "true" ) // style:auto-text-indent takes precedence
00451             // ### "indented by a value that is based on the current font size"
00452             // ### and "requires margin-left and margin-right
00453             // ### but how much is the indent?
00454             first = 10;
00455         else if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "text-indent") )
00456             first = KoUnit::parseValue( context.styleStack().attributeNS( KoXmlNS::fo, "text-indent") );
00457 
00458         layout.margins[QStyleSheetItem::MarginFirstLine] = first;
00459     }
00460 
00461     // Offset before and after paragraph
00462     if( context.styleStack().hasAttributeNS( KoXmlNS::fo, "margin-top") || // 3.11.22
00463         context.styleStack().hasAttributeNS( KoXmlNS::fo, "margin-bottom")) {
00464         layout.margins[QStyleSheetItem::MarginTop] = KoUnit::parseValue( context.styleStack().attributeNS( KoXmlNS::fo, "margin-top" ) );
00465         layout.margins[QStyleSheetItem::MarginBottom] = KoUnit::parseValue( context.styleStack().attributeNS( KoXmlNS::fo, "margin-bottom" ) );
00466     }
00467 
00468     // Line spacing
00469     if( context.styleStack().hasAttributeNS( KoXmlNS::fo, "line-height") ) {  // 3.11.1
00470         // Fixed line height
00471         QString value = context.styleStack().attributeNS( KoXmlNS::fo, "line-height" );
00472         if ( value != "normal" ) {
00473             if ( value == "100%" )
00474                 layout.lineSpacingType = KoParagLayout::LS_SINGLE;
00475             else if( value=="150%")
00476                 layout.lineSpacingType = KoParagLayout::LS_ONEANDHALF;
00477             else if( value=="200%")
00478                 layout.lineSpacingType = KoParagLayout::LS_DOUBLE;
00479             else if ( value.find('%') > -1 )
00480             {
00481                 double percent = value.toDouble();
00482                 layout.lineSpacingType = KoParagLayout::LS_MULTIPLE;
00483                 layout.lineSpacing = percent/100;
00484             }
00485             else // fixed value
00486             {
00487                 layout.lineSpacingType = KoParagLayout::LS_FIXED;
00488                 layout.lineSpacing = KoUnit::parseValue( value );
00489             }
00490         }
00491     }
00492     // Line-height-at-least is mutually exclusive with line-height
00493     else if ( context.styleStack().hasAttributeNS( KoXmlNS::style, "line-height-at-least") ) // 3.11.2
00494     {
00495         QString value = context.styleStack().attributeNS( KoXmlNS::style, "line-height-at-least" );
00496         // kotext has "at least" but that's for the linespacing, not for the entire line height!
00497         // Strange. kotext also has "at least" for the whole line height....
00498         // Did we make the wrong choice in kotext?
00499         //kdWarning() << "Unimplemented support for style:line-height-at-least: " << value << endl;
00500         // Well let's see if this makes a big difference.
00501         layout.lineSpacingType = KoParagLayout::LS_AT_LEAST;
00502         layout.lineSpacing = KoUnit::parseValue( value );
00503     }
00504     // Line-spacing is mutually exclusive with line-height and line-height-at-least
00505     else if ( context.styleStack().hasAttributeNS( KoXmlNS::style, "line-spacing") ) // 3.11.3
00506     {
00507         double value = KoUnit::parseValue( context.styleStack().attributeNS( KoXmlNS::style, "line-spacing" ) );
00508         if ( value != 0.0 )
00509         {
00510             layout.lineSpacingType = KoParagLayout::LS_CUSTOM;
00511             layout.lineSpacing = value;
00512         }
00513     }
00514 
00515     // Tabulators
00516     KoTabulatorList tabList;
00517     if ( context.styleStack().hasChildNodeNS( KoXmlNS::style, "tab-stops" ) ) { // 3.11.10
00518         QDomElement tabStops = context.styleStack().childNodeNS( KoXmlNS::style, "tab-stops" );
00519         //kdDebug(30519) << k_funcinfo << tabStops.childNodes().count() << " tab stops in layout." << endl;
00520         QDomElement tabStop;
00521         forEachElement( tabStop, tabStops )
00522         {
00523             Q_ASSERT( tabStop.localName() == "tab-stop" );
00524             const QString type = tabStop.attributeNS( KoXmlNS::style, "type", QString::null ); // left, right, center or char
00525 
00526             KoTabulator tab;
00527             tab.ptPos = KoUnit::parseValue( tabStop.attributeNS( KoXmlNS::style, "position", QString::null ) );
00528             if ( type == "center" )
00529                 tab.type = T_CENTER;
00530             else if ( type == "right" )
00531                 tab.type = T_RIGHT;
00532             else if ( type == "char" ) {
00533                 QString delimiterChar = tabStop.attributeNS( KoXmlNS::style, "char", QString::null ); // single character
00534                 if ( !delimiterChar.isEmpty() )
00535                     tab.alignChar = delimiterChar[0];
00536                 tab.type = T_DEC_PNT; // "alignment on decimal point"
00537             }
00538             else //if ( type == "left" )
00539                 tab.type = T_LEFT;
00540 
00541             tab.ptWidth = KoUnit::parseValue( tabStop.attributeNS( KoXmlNS::style, "leader-width", QString::null ), 0.5 );
00542 
00543             tab.filling = TF_BLANK;
00544             if ( tabStop.attributeNS( KoXmlNS::style, "leader-type", QString::null ) == "single" )
00545             {
00546                 QString leaderStyle = tabStop.attributeNS( KoXmlNS::style, "leader-style", QString::null );
00547                 if ( leaderStyle == "solid" )
00548                     tab.filling = TF_LINE;
00549                 else if ( leaderStyle == "dotted" )
00550                     tab.filling = TF_DOTS;
00551                 else if ( leaderStyle == "dash" )
00552                     tab.filling = TF_DASH;
00553                 else if ( leaderStyle == "dot-dash" )
00554                     tab.filling = TF_DASH_DOT;
00555                 else if ( leaderStyle == "dot-dot-dash" )
00556                     tab.filling = TF_DASH_DOT_DOT;
00557             }
00558             else
00559             {
00560                 // Fallback: convert leaderChar's unicode value
00561                 QString leaderChar = tabStop.attributeNS( KoXmlNS::style, "leader-text", QString::null );
00562                 if ( !leaderChar.isEmpty() )
00563                 {
00564                     QChar ch = leaderChar[0];
00565                     switch (ch.latin1()) {
00566                     case '.':
00567                         tab.filling = TF_DOTS; break;
00568                     case '-':
00569                     case '_':  // TODO in KWord: differentiate --- and ___
00570                         tab.filling = TF_LINE; break;
00571                     default:
00572                         // KWord doesn't have support for "any char" as filling.
00573                         break;
00574                     }
00575                 }
00576             }
00577             tabList.append( tab );
00578         } //for
00579     }
00580     qHeapSort( tabList );
00581     layout.setTabList( tabList );
00582 
00583     // Borders
00584     if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "border","left") )
00585         layout.leftBorder.loadFoBorder( context.styleStack().attributeNS( KoXmlNS::fo, "border","left") );
00586     else
00587         layout.leftBorder.setPenWidth(0);
00588     if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "border","right") )
00589         layout.rightBorder.loadFoBorder( context.styleStack().attributeNS( KoXmlNS::fo, "border","right") );
00590     else
00591         layout.rightBorder.setPenWidth(0);
00592     if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "border","top") )
00593         layout.topBorder.loadFoBorder( context.styleStack().attributeNS( KoXmlNS::fo, "border","top") );
00594     else
00595         layout.topBorder.setPenWidth(0);
00596     if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "border","bottom") )
00597         layout.bottomBorder.loadFoBorder( context.styleStack().attributeNS( KoXmlNS::fo, "border","bottom") );
00598     else
00599         layout.bottomBorder.setPenWidth(0);
00600 
00601 
00602     // Page breaking
00603     int pageBreaking = 0;
00604     if( context.styleStack().hasAttributeNS( KoXmlNS::fo, "break-before") ||
00605         context.styleStack().hasAttributeNS( KoXmlNS::fo, "break-after") ||
00606         context.styleStack().hasAttributeNS( KoXmlNS::fo, "keep-together") ||
00607         context.styleStack().hasAttributeNS( KoXmlNS::style, "keep-with-next") ||
00608         context.styleStack().hasAttributeNS( KoXmlNS::fo, "keep-with-next") )
00609     {
00610         if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "break-before") ) { // 3.11.24
00611             // TODO in KWord: implement difference between "column" and "page"
00612             if ( context.styleStack().attributeNS( KoXmlNS::fo, "break-before" ) != "auto" )
00613                 pageBreaking |= KoParagLayout::HardFrameBreakBefore;
00614         }
00615         else if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "break-after") ) { // 3.11.24
00616             // TODO in KWord: implement difference between "column" and "page"
00617             if ( context.styleStack().attributeNS( KoXmlNS::fo, "break-after" ) != "auto" )
00618                 pageBreaking |= KoParagLayout::HardFrameBreakAfter;
00619         }
00620 
00621         if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "keep-together" ) ) { // was style:break-inside in OOo-1.1, renamed in OASIS
00622             if ( context.styleStack().attributeNS( KoXmlNS::fo, "keep-together" ) != "auto" )
00623                  pageBreaking |= KoParagLayout::KeepLinesTogether;
00624         }
00625         if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "keep-with-next" ) ) {
00626             // OASIS spec says it's "auto"/"always", not a boolean.
00627             QString val = context.styleStack().attributeNS( KoXmlNS::fo, "keep-with-next" );
00628             if ( val == "true" || val == "always" )
00629                 pageBreaking |= KoParagLayout::KeepWithNext;
00630         }
00631     }
00632     layout.pageBreaking = pageBreaking;
00633 
00634     // TODO (new feature) fo:background-color (3.11.25)
00635 }
00636 
00637 void KoParagLayout::saveParagLayout( QDomElement & parentElem, int alignment ) const
00638 {
00639     const KoParagLayout& layout = *this; // code moved from somewhere else;)
00640     QDomDocument doc = parentElem.ownerDocument();
00641     QDomElement element = doc.createElement( "NAME" );
00642     parentElem.appendChild( element );
00643     if ( layout.style )
00644         element.setAttribute( "value", layout.style->displayName() );
00645     //else
00646     //    kdWarning() << "KoParagLayout::saveParagLayout: style==0!" << endl;
00647 
00648     element = doc.createElement( "FLOW" );
00649     parentElem.appendChild( element );
00650 
00651     element.setAttribute( "align", alignment==Qt::AlignRight ? "right" :
00652                           alignment==Qt::AlignHCenter ? "center" :
00653                           alignment==Qt::AlignJustify ? "justify" :
00654                           alignment==Qt::AlignAuto ? "auto" : "left" ); // Note: styles can have AlignAuto. Not paragraphs.
00655 
00656     if ( static_cast<QChar::Direction>(layout.direction) == QChar::DirR )
00657         element.setAttribute( "dir", "R" );
00658     else
00659     if ( static_cast<QChar::Direction>(layout.direction) == QChar::DirL )
00660             element.setAttribute( "dir", "L" );
00661 
00662     if ( layout.margins[QStyleSheetItem::MarginFirstLine] != 0 ||
00663          layout.margins[QStyleSheetItem::MarginLeft] != 0 ||
00664          layout.margins[QStyleSheetItem::MarginRight] != 0 )
00665     {
00666         element = doc.createElement( "INDENTS" );
00667         parentElem.appendChild( element );
00668         if ( layout.margins[QStyleSheetItem::MarginFirstLine] != 0 )
00669             element.setAttribute( "first", layout.margins[QStyleSheetItem::MarginFirstLine] );
00670         if ( layout.margins[QStyleSheetItem::MarginLeft] != 0 )
00671             element.setAttribute( "left", layout.margins[QStyleSheetItem::MarginLeft] );
00672         if ( layout.margins[QStyleSheetItem::MarginRight] != 0 )
00673             element.setAttribute( "right", layout.margins[QStyleSheetItem::MarginRight] );
00674     }
00675 
00676     if ( layout.margins[QStyleSheetItem::MarginTop] != 0 ||
00677          layout.margins[QStyleSheetItem::MarginBottom] != 0 )
00678     {
00679         element = doc.createElement( "OFFSETS" );
00680         parentElem.appendChild( element );
00681         if ( layout.margins[QStyleSheetItem::MarginTop] != 0 )
00682             element.setAttribute( "before", layout.margins[QStyleSheetItem::MarginTop] );
00683         if ( layout.margins[QStyleSheetItem::MarginBottom] != 0 )
00684             element.setAttribute( "after", layout.margins[QStyleSheetItem::MarginBottom] );
00685     }
00686     if ( layout.lineSpacingType != LS_SINGLE )
00687     {
00688         element = doc.createElement( "LINESPACING" );
00689         parentElem.appendChild( element );
00690         if ( layout.lineSpacingType == KoParagLayout::LS_ONEANDHALF )  {
00691             element.setAttribute( "type", "oneandhalf" );
00692             element.setAttribute( "value", "oneandhalf" ); //compatibility with koffice 1.2
00693         }
00694         else if ( layout.lineSpacingType == KoParagLayout::LS_DOUBLE ) {
00695             element.setAttribute( "type", "double" );
00696             element.setAttribute( "value", "double" ); //compatibility with koffice 1.2
00697         }
00698         else if ( layout.lineSpacingType == KoParagLayout::LS_CUSTOM )
00699         {
00700             element.setAttribute( "type", "custom" );
00701             element.setAttribute( "spacingvalue", layout.lineSpacing);
00702             element.setAttribute( "value", layout.lineSpacing ); //compatibility with koffice 1.2
00703         }
00704         else if ( layout.lineSpacingType == KoParagLayout::LS_AT_LEAST )
00705         {
00706             element.setAttribute( "type", "atleast" );
00707             element.setAttribute( "spacingvalue", layout.lineSpacing);
00708         }
00709         else if ( layout.lineSpacingType == KoParagLayout::LS_MULTIPLE )
00710         {
00711             element.setAttribute( "type", "multiple" );
00712             element.setAttribute( "spacingvalue", layout.lineSpacing);
00713         }
00714         else if ( layout.lineSpacingType == KoParagLayout::LS_FIXED )
00715         {
00716             element.setAttribute( "type", "fixed" );
00717             element.setAttribute( "spacingvalue", layout.lineSpacing);
00718         }
00719         else
00720             kdDebug()<<" error in lineSpacing Type\n";
00721     }
00722 
00723     if ( layout.pageBreaking != 0 )
00724     {
00725         element = doc.createElement( "PAGEBREAKING" );
00726         parentElem.appendChild( element );
00727         if ( layout.pageBreaking & KoParagLayout::KeepLinesTogether )
00728             element.setAttribute( "linesTogether",  "true" );
00729         if ( layout.pageBreaking & KoParagLayout::HardFrameBreakBefore )
00730             element.setAttribute( "hardFrameBreak", "true" );
00731         if ( layout.pageBreaking & KoParagLayout::HardFrameBreakAfter )
00732             element.setAttribute( "hardFrameBreakAfter", "true" );
00733     }
00734 
00735     if ( layout.leftBorder.penWidth() > 0 )
00736     {
00737         element = doc.createElement( "LEFTBORDER" );
00738         parentElem.appendChild( element );
00739         layout.leftBorder.save( element );
00740     }
00741     if ( layout.rightBorder.penWidth() > 0 )
00742     {
00743         element = doc.createElement( "RIGHTBORDER" );
00744         parentElem.appendChild( element );
00745         layout.rightBorder.save( element );
00746     }
00747     if ( layout.topBorder.penWidth() > 0 )
00748     {
00749         element = doc.createElement( "TOPBORDER" );
00750         parentElem.appendChild( element );
00751         layout.topBorder.save( element );
00752     }
00753     if ( layout.bottomBorder.penWidth() > 0 )
00754     {
00755         element = doc.createElement( "BOTTOMBORDER" );
00756         parentElem.appendChild( element );
00757         layout.bottomBorder.save( element );
00758     }
00759     if ( layout.counter && layout.counter->numbering() != KoParagCounter::NUM_NONE )
00760     {
00761         element = doc.createElement( "COUNTER" );
00762         parentElem.appendChild( element );
00763         if ( layout.counter )
00764             layout.counter->save( element );
00765     }
00766 
00767     KoTabulatorList tabList = layout.tabList();
00768     KoTabulatorList::ConstIterator it = tabList.begin();
00769     for ( ; it != tabList.end() ; it++ )
00770     {
00771         element = doc.createElement( "TABULATOR" );
00772         parentElem.appendChild( element );
00773         element.setAttribute( "type", (*it).type );
00774         element.setAttribute( "ptpos", (*it).ptPos );
00775         element.setAttribute( "filling", (*it).filling );
00776         if ( (*it).filling != TF_BLANK )
00777             element.setAttribute( "width", QString::number( (*it).ptWidth, 'g', DBL_DIG ) );
00778         if ( (*it).type == T_DEC_PNT && !(*it).alignChar.isNull() )
00779             element.setAttribute( "alignchar", QString((*it).alignChar) );
00780     }
00781 }
00782 
00783 void KoParagLayout::saveOasis( KoGenStyle& gs, KoSavingContext& context, bool savingStyle ) const
00784 {
00785     gs.addProperty( "fo:text-align", saveOasisAlignment( (Qt::AlignmentFlags)alignment ).data() );
00786     // Don't save the direction for a style, if "auto", so that the
00787     // auto-determination of direction based on first char, works.
00788     if ( !savingStyle || (QChar::Direction) direction != QChar::DirON )
00789         gs.addProperty( "style:writing-mode", (QChar::Direction)direction == QChar::DirR ? "rl-tb" : "lr-tb" );
00790     gs.addPropertyPt( "fo:margin-left", margins[QStyleSheetItem::MarginLeft] );
00791     gs.addPropertyPt( "fo:margin-right", margins[QStyleSheetItem::MarginRight] );
00792     gs.addPropertyPt( "fo:text-indent", margins[QStyleSheetItem::MarginFirstLine] );
00793     gs.addPropertyPt( "fo:margin-top", margins[QStyleSheetItem::MarginTop] );
00794     gs.addPropertyPt( "fo:margin-bottom", margins[QStyleSheetItem::MarginBottom] );
00795 
00796     switch ( lineSpacingType ) {
00797     case KoParagLayout::LS_SINGLE:
00798         gs.addProperty( "fo:line-height", "100%" );
00799         break;
00800     case KoParagLayout::LS_ONEANDHALF:
00801         gs.addProperty( "fo:line-height", "150%" );
00802         break;
00803     case KoParagLayout::LS_DOUBLE:
00804         gs.addProperty( "fo:line-height", "200%" );
00805         break;
00806     case KoParagLayout::LS_MULTIPLE:
00807         gs.addProperty( "fo:line-height", QString::number( lineSpacing * 100.0 ) + '%' );
00808         break;
00809     case KoParagLayout::LS_FIXED:
00810         gs.addPropertyPt( "fo:line-height", lineSpacing );
00811         break;
00812     case KoParagLayout::LS_CUSTOM:
00813         gs.addPropertyPt( "style:line-spacing", lineSpacing );
00814         break;
00815     case KoParagLayout::LS_AT_LEAST:
00816         gs.addPropertyPt( "style:line-height-at-least", lineSpacing );
00817         break;
00818     }
00819 
00820     QBuffer buffer;
00821     buffer.open( IO_WriteOnly );
00822     KoXmlWriter tabsWriter( &buffer, 4 ); // indent==4: root,autostyle,style,parag-props
00823     tabsWriter.startElement( "style:tab-stops" );
00824     KoTabulatorList::ConstIterator it = m_tabList.begin();
00825     for ( ; it != m_tabList.end() ; it++ )
00826     {
00827         tabsWriter.startElement( "style:tab-stop" );
00828         tabsWriter.addAttributePt( "style:position", (*it).ptPos );
00829 
00830         switch ( (*it).type ) {
00831         case T_LEFT:
00832             tabsWriter.addAttribute( "style:type", "left" );
00833             break;
00834         case T_CENTER:
00835             tabsWriter.addAttribute( "style:type", "center" );
00836             break;
00837         case T_RIGHT:
00838             tabsWriter.addAttribute( "style:type", "right" );
00839             break;
00840         case T_DEC_PNT:  // "alignment on decimal point"
00841             tabsWriter.addAttribute( "style:type", "char" );
00842             if ( !(*it).alignChar.isNull() )
00843               tabsWriter.addAttribute( "style:char", QString( (*it).alignChar ) );
00844             break;
00845         case T_INVALID: // keep compiler happy, this can't happen
00846             break;
00847         }
00848         switch( (*it).filling ) {
00849         case TF_BLANK:
00850             tabsWriter.addAttribute( "style:leader-type", "none" );
00851             break;
00852         case TF_LINE:
00853             tabsWriter.addAttribute( "style:leader-type", "single" );
00854             tabsWriter.addAttribute( "style:leader-style", "solid" );
00855             // Give OOo a chance to show something, since it doesn't support lines here.
00856             tabsWriter.addAttribute( "style:leader-text", "_" );
00857             break;
00858         case TF_DOTS:
00859             tabsWriter.addAttribute( "style:leader-type", "single" );
00860             tabsWriter.addAttribute( "style:leader-style", "dotted" );
00861             // Give OOo a chance to show something, since it doesn't support lines here.
00862             tabsWriter.addAttribute( "style:leader-text", "." );
00863             break;
00864         case TF_DASH:
00865             tabsWriter.addAttribute( "style:leader-type", "single" );
00866             tabsWriter.addAttribute( "style:leader-style", "dash" );
00867             // Give OOo a chance to show something, since it doesn't support lines here.
00868             tabsWriter.addAttribute( "style:leader-text", "_" );
00869             break;
00870         case TF_DASH_DOT:
00871             tabsWriter.addAttribute( "style:leader-type", "single" );
00872             tabsWriter.addAttribute( "style:leader-style", "dot-dash" );
00873             // Give OOo a chance to show something, since it doesn't support lines here.
00874             tabsWriter.addAttribute( "style:leader-text", "." );
00875             break;
00876         case TF_DASH_DOT_DOT:
00877             tabsWriter.addAttribute( "style:leader-type", "single" );
00878             tabsWriter.addAttribute( "style:leader-style", "dot-dot-dash" );
00879             // Give OOo a chance to show something, since it doesn't support lines here.
00880             tabsWriter.addAttribute( "style:leader-text", "." );
00881             break;
00882         }
00883         if ( (*it).filling != TF_BLANK )
00884             tabsWriter.addAttributePt( "style:leader-width", (*it).ptWidth );
00885         // If we want to support it, oasis also defines style:leader-color
00886         tabsWriter.endElement();
00887     }
00888     tabsWriter.endElement();
00889     buffer.close();
00890     QString elementContents = QString::fromUtf8( buffer.buffer(), buffer.buffer().size() );
00891     gs.addChildElement( "style:tab-stops", elementContents );
00892 
00893     bool fourBordersEqual = leftBorder.penWidth() > 0 &&
00894                leftBorder == rightBorder && rightBorder == topBorder && topBorder == bottomBorder;
00895     if ( fourBordersEqual ) {
00896         gs.addProperty( "fo:border", leftBorder.saveFoBorder() );
00897     } else {
00898         if ( leftBorder.penWidth() > 0 )
00899             gs.addProperty( "fo:border-left", leftBorder.saveFoBorder() );
00900         if ( rightBorder.penWidth() > 0 )
00901             gs.addProperty( "fo:border-right", rightBorder.saveFoBorder() );
00902         if ( topBorder.penWidth() > 0 )
00903             gs.addProperty( "fo:border-top", topBorder.saveFoBorder() );
00904         if ( bottomBorder.penWidth() > 0 )
00905             gs.addProperty( "fo:border-bottom", bottomBorder.saveFoBorder() );
00906     }
00907 
00908     if ( pageBreaking & KoParagLayout::HardFrameBreakBefore )
00909         gs.addProperty( "fo:break-before", context.hasColumns() ? "column" : "page" );
00910     else if ( pageBreaking & KoParagLayout::HardFrameBreakAfter )
00911         gs.addProperty( "fo:break-after", context.hasColumns() ? "column" : "page" );
00912     if ( pageBreaking & KoParagLayout::KeepLinesTogether )
00913         gs.addProperty( "fo:keep-together", "always" );
00914     if ( pageBreaking & KoParagLayout::KeepWithNext )
00915         gs.addProperty( "fo:keep-with-next", "always" );
00916 }
KDE Logo
This file is part of the documentation for lib Library Version 1.4.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Mon Feb 13 09:40:04 2006 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003