lib

KoParagLayout.cpp

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