F:/KPlato/koffice/libs/kotext/KoParagLayout.cpp

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

Généré le Wed Nov 22 23:41:08 2006 pour KPlato par  doxygen 1.5.1-p1