lib

KoParagLayout.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2001 David Faure <faure@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20#include "KoParagLayout.h"
21#include "KoRichText.h"
22#include "KoParagCounter.h"
23#include "KoStyleCollection.h"
24#include "KoOasisContext.h"
25#include <KoXmlWriter.h>
26#include <KoXmlNS.h>
27#include <KoDom.h>
28#include <KoGenStyles.h>
29
30#include <tdeglobal.h>
31#include <tdelocale.h>
32#include <kdebug.h>
33#include <tqdom.h>
34#include <tqbuffer.h>
35#include <tqcolor.h>
36
37#include <float.h>
38
39TQString* KoParagLayout::shadowCssCompat = 0L;
40
41// Create a default KoParagLayout.
42KoParagLayout::KoParagLayout()
43{
44 initialise();
45}
46
48{
49 alignment = layout.alignment;
50 for ( int i = 0 ; i < 5 ; ++i )
51 margins[i] = layout.margins[i];
52 pageBreaking = layout.pageBreaking;
53 leftBorder = layout.leftBorder;
54 rightBorder = layout.rightBorder;
55 topBorder = layout.topBorder;
56 bottomBorder = layout.bottomBorder;
57 joinBorder = layout.joinBorder;
59 if ( layout.counter )
60 counter = new KoParagCounter( *layout.counter );
61 else
62 counter = 0L;
63 lineSpacing = layout.lineSpacing;
64 lineSpacingType = layout.lineSpacingType;
65 style = layout.style;
66 direction = layout.direction;
67 setTabList( layout.tabList() );
68}
69
70int KoParagLayout::compare( const KoParagLayout & layout ) const
71{
72 int flags = 0;
73 if ( alignment != layout.alignment )
74 flags |= Alignment;
75 for ( int i = 0 ; i < 5 ; ++i )
76 if ( margins[i] != layout.margins[i] )
77 {
78 flags |= Margins;
79 break;
80 }
81 if ( pageBreaking != layout.pageBreaking )
82 flags |= PageBreaking;
83 if ( leftBorder != layout.leftBorder
84 || rightBorder != layout.rightBorder
85 || topBorder != layout.topBorder
86 || bottomBorder != layout.bottomBorder
87 || joinBorder != layout.joinBorder )
88 flags |= Borders;
89
90 if ( layout.counter )
91 {
92 if ( counter )
93 {
94 if ( ! ( *layout.counter == *counter ) )
95 flags |= BulletNumber;
96 } else
97 if ( layout.counter->numbering() != KoParagCounter::NUM_NONE )
98 flags |= BulletNumber;
99 }
100 else
101 if ( counter && counter->numbering() != KoParagCounter::NUM_NONE )
102 flags |= BulletNumber;
103
104 if ( lineSpacing != layout.lineSpacing
105 || lineSpacingType != layout.lineSpacingType )
106 flags |= LineSpacing;
107 //if ( style != layout.style )
108 // flags |= Style;
109 if ( m_tabList != layout.m_tabList )
110 flags |= Tabulator;
111
112 if ( backgroundColor != layout.backgroundColor)
113 flags |= BackgroundColor;
114
115 // This method is used for the GUI stuff only, so we don't have a flag
116 // for the Direction value.
117 return flags;
118}
119
120void KoParagLayout::initialise()
121{
122 alignment = TQt::AlignAuto;
123 for ( int i = 0 ; i < 5 ; ++i ) // use memset ?
124 margins[i] = 0;
125 lineSpacingType = LS_SINGLE;
126 lineSpacing = 0;
127 counter = 0L;
128 leftBorder.setPenWidth( 0);
129 rightBorder.setPenWidth( 0);
130 topBorder.setPenWidth( 0);
131 bottomBorder.setPenWidth( 0);
132 joinBorder = true;
133 pageBreaking = 0;
134 style = 0L;
135 direction = TQChar::DirON;
136 m_tabList.clear();
137}
138
139KoParagLayout::~KoParagLayout()
140{
141 delete counter;
142}
143
144void KoParagLayout::loadParagLayout( KoParagLayout& layout, const TQDomElement& parentElem, int docVersion )
145{
146 // layout is an input and output parameter
147 // It can have been initialized already, e.g. by copying from a style
148 // (we don't do that anymore though).
149
150 // Load the paragraph tabs - we load into a clean list, not mixing with those already in "layout"
151 // We can't apply the 'default comes from the style' in this case, because
152 // there is no way to differentiate between "I want no tabs in the parag"
153 // and "use default from style".
154 KoTabulatorList tabList;
155 TQDomElement element = parentElem.firstChild().toElement();
156 for ( ; !element.isNull() ; element = element.nextSibling().toElement() )
157 {
158 if ( element.tagName() == "TABULATOR" )
159 {
160 KoTabulator tab;
161 tab.type = static_cast<KoTabulators>( getAttribute( element, "type", T_LEFT ) );
162 tab.ptPos = getAttribute( element, "ptpos", 0.0 );
163 tab.filling = static_cast<KoTabulatorFilling>( getAttribute( element, "filling", TF_BLANK ) );
164 tab.ptWidth = getAttribute( element, "width", 0.5 );
165 TQString alignCharStr = element.attribute("alignchar");
166 if ( alignCharStr.isEmpty() )
167 tab.alignChar = TDEGlobal::locale()->decimalSymbol()[0];
168 else
169 tab.alignChar = alignCharStr[0];
170 tabList.append( tab );
171 }
172 }
173 qHeapSort( tabList );
174 layout.setTabList( tabList );
175 layout.alignment = TQt::AlignAuto;
176 element = parentElem.namedItem( "FLOW" ).toElement(); // Flow is what is now called alignment internally
177 if ( !element.isNull() )
178 {
179 TQString flow = element.attribute( "align" ); // KWord-1.0 DTD
180 if ( !flow.isEmpty() )
181 {
182 layout.alignment = flow=="right" ? TQt::AlignRight :
183 flow=="center" ? TQt::AlignHCenter :
184 flow=="justify" ? TQt::AlignJustify :
185 flow=="left" ? TQt::AlignLeft : TQt::AlignAuto;
186
187 TQString dir = element.attribute( "dir" ); // KWord-1.2
188 if ( !dir.isEmpty() ) {
189 if ( dir == "L" )
190 layout.direction = TQChar::DirL;
191 else if ( dir == "R" )
192 layout.direction = TQChar::DirR;
193 else
194 kdWarning() << "Unexpected value for paragraph direction: " << dir << endl;
195 }
196 } else {
197 flow = element.attribute( "value" ); // KWord-0.8
198 static const int flow2align[] = { TQt::AlignAuto, TQt::AlignRight, TQt::AlignHCenter, TQt::AlignJustify };
199 if ( !flow.isEmpty() && flow.toInt() < 4 )
200 layout.alignment = flow2align[flow.toInt()];
201 }
202 }
203
204 if ( docVersion < 2 )
205 {
206 element = parentElem.namedItem( "OHEAD" ).toElement(); // used by KWord-0.8
207 if ( !element.isNull() )
208 layout.margins[TQStyleSheetItem::MarginTop] = getAttribute( element, "pt", 0.0 );
209
210 element = parentElem.namedItem( "OFOOT" ).toElement(); // used by KWord-0.8
211 if ( !element.isNull() )
212 layout.margins[TQStyleSheetItem::MarginBottom] = getAttribute( element, "pt", 0.0 );
213
214 element = parentElem.namedItem( "IFIRST" ).toElement(); // used by KWord-0.8
215 if ( !element.isNull() )
216 layout.margins[TQStyleSheetItem::MarginFirstLine] = getAttribute( element, "pt", 0.0 );
217
218 element = parentElem.namedItem( "ILEFT" ).toElement(); // used by KWord-0.8
219 if ( !element.isNull() )
220 layout.margins[TQStyleSheetItem::MarginLeft] = getAttribute( element, "pt", 0.0 );
221 }
222
223 // KWord-1.0 DTD
224 element = parentElem.namedItem( "INDENTS" ).toElement();
225 if ( !element.isNull() )
226 {
227 layout.margins[TQStyleSheetItem::MarginFirstLine] = getAttribute( element, "first", 0.0 );
228 layout.margins[TQStyleSheetItem::MarginLeft] = getAttribute( element, "left", 0.0 );
229 layout.margins[TQStyleSheetItem::MarginRight] = getAttribute( element, "right", 0.0 );
230 }
231 element = parentElem.namedItem( "OFFSETS" ).toElement();
232 if ( !element.isNull() )
233 {
234 layout.margins[TQStyleSheetItem::MarginTop] = getAttribute( element, "before", 0.0 );
235 layout.margins[TQStyleSheetItem::MarginBottom] = getAttribute( element, "after", 0.0 );
236 }
237
238 if ( docVersion < 2 )
239 {
240 element = parentElem.namedItem( "LINESPACE" ).toElement(); // used by KWord-0.8
241 if ( !element.isNull() )
242 {
243 layout.lineSpacingType = KoParagLayout::LS_CUSTOM;
244 layout.lineSpacing = getAttribute( element, "pt", 0.0 );
245 }
246 }
247
248 element = parentElem.namedItem( "LINESPACING" ).toElement(); // KWord-1.0 DTD
249 if ( !element.isNull() )
250 {
251 //compatibility with koffice 1.1
252 if ( element.hasAttribute( "value" ))
253 {
254 TQString value = element.attribute( "value" );
255 if ( value == "oneandhalf" )
256 {
257 layout.lineSpacingType = KoParagLayout::LS_ONEANDHALF;
258 layout.lineSpacing = 0;
259 }
260 else if ( value == "double" )
261 {
262 layout.lineSpacingType = KoParagLayout::LS_DOUBLE;
263 layout.lineSpacing = 0;
264 }
265 else
266 {
267 layout.lineSpacingType = KoParagLayout::LS_CUSTOM;
268 layout.lineSpacing = value.toDouble();
269 }
270 }
271 else
272 {
273 TQString type = element.attribute( "type" );
274 if ( type == "oneandhalf" )
275 {
276 layout.lineSpacingType = KoParagLayout::LS_ONEANDHALF;
277 layout.lineSpacing = 0;
278 }
279 else if ( type == "double" )
280 {
281 layout.lineSpacingType = KoParagLayout::LS_DOUBLE;
282 layout.lineSpacing = 0;
283 }
284 else if ( type == "custom" )
285 {
286 layout.lineSpacingType = KoParagLayout::LS_CUSTOM;
287 layout.lineSpacing = element.attribute( "spacingvalue" ).toDouble();
288 }
289 else if ( type == "atleast" )
290 {
291 layout.lineSpacingType = KoParagLayout::LS_AT_LEAST;
292 layout.lineSpacing = element.attribute( "spacingvalue" ).toDouble();
293 }
294 else if ( type == "multiple" )
295 {
296 layout.lineSpacingType = KoParagLayout::LS_MULTIPLE;
297 layout.lineSpacing = element.attribute( "spacingvalue" ).toDouble();
298 }
299 else if ( type == "fixed" )
300 {
301 layout.lineSpacingType = KoParagLayout::LS_FIXED;
302 layout.lineSpacing = element.attribute( "spacingvalue" ).toDouble();
303 }
304 else if ( type == "single" ) // not used; just in case future versions use it.
305 layout.lineSpacingType = KoParagLayout::LS_SINGLE;
306 }
307 }
308
309 int pageBreaking = 0;
310 element = parentElem.namedItem( "PAGEBREAKING" ).toElement();
311 if ( !element.isNull() )
312 {
313 if ( element.attribute( "linesTogether" ) == "true" )
314 pageBreaking |= KoParagLayout::KeepLinesTogether;
315 if ( element.attribute( "hardFrameBreak" ) == "true" )
316 pageBreaking |= KoParagLayout::HardFrameBreakBefore;
317 if ( element.attribute( "hardFrameBreakAfter" ) == "true" )
318 pageBreaking |= KoParagLayout::HardFrameBreakAfter;
319 }
320 if ( docVersion < 2 )
321 {
322 element = parentElem.namedItem( "HARDBRK" ).toElement(); // KWord-0.8
323 if ( !element.isNull() )
324 pageBreaking |= KoParagLayout::HardFrameBreakBefore;
325 }
326 layout.pageBreaking = pageBreaking;
327
328 element = parentElem.namedItem( "LEFTBORDER" ).toElement();
329 if ( !element.isNull() )
330 layout.leftBorder = KoBorder::loadBorder( element );
331 else
332 layout.leftBorder.setPenWidth(0);
333
334 element = parentElem.namedItem( "RIGHTBORDER" ).toElement();
335 if ( !element.isNull() )
336 layout.rightBorder = KoBorder::loadBorder( element );
337 else
338 layout.rightBorder.setPenWidth(0);
339
340 element = parentElem.namedItem( "TOPBORDER" ).toElement();
341 if ( !element.isNull() )
342 layout.topBorder = KoBorder::loadBorder( element );
343 else
344 layout.topBorder.setPenWidth(0);
345
346 element = parentElem.namedItem( "BOTTOMBORDER" ).toElement();
347 if ( !element.isNull() )
348 layout.bottomBorder = KoBorder::loadBorder( element );
349 else
350 layout.bottomBorder.setPenWidth(0);
351
352 element = parentElem.namedItem( "COUNTER" ).toElement();
353 if ( !element.isNull() )
354 {
355 layout.counter = new KoParagCounter;
356 layout.counter->load( element );
357 }
358
359 // Compatibility with KOffice-1.2
360 element = parentElem.namedItem( "SHADOW" ).toElement();
361 if ( !element.isNull() && element.hasAttribute("direction") )
362 {
363 int shadowDistance = element.attribute("distance").toInt();
364 int shadowDirection = element.attribute("direction").toInt();
365 TQColor shadowColor;
366 if ( element.hasAttribute("red") )
367 {
368 int r = element.attribute("red").toInt();
369 int g = element.attribute("green").toInt();
370 int b = element.attribute("blue").toInt();
371 shadowColor.setRgb( r, g, b );
372 }
373 int distanceX = 0;
374 int distanceY = 0;
375 switch ( shadowDirection )
376 {
377 case 1: // KoParagLayout::SD_LEFT_UP:
378 case 2: // KoParagLayout::SD_UP:
379 case 3: // KoParagLayout::SD_RIGHT_UP:
380 distanceX = - shadowDistance;
381 break;
382 case 7: // KoParagLayout::SD_LEFT_BOTTOM:
383 case 6: // KoParagLayout::SD_BOTTOM:
384 case 5: // KoParagLayout::SD_RIGHT_BOTTOM:
385 distanceX = shadowDistance;
386 break;
387 }
388 switch ( shadowDirection )
389 {
390 case 7: // KoParagLayout::SD_LEFT_BOTTOM:
391 case 8: // KoParagLayout::SD_LEFT:
392 case 1: //KoParagLayout::SD_LEFT_UP:
393 distanceY = - shadowDistance;
394 break;
395 case 3: // KoParagLayout::SD_RIGHT_UP:
396 case 4: // KoParagLayout::SD_RIGHT:
397 case 5: // KoParagLayout::SD_RIGHT_BOTTOM:
398 distanceY = shadowDistance;
399 break;
400 }
401 if ( !shadowCssCompat )
402 shadowCssCompat = new TQString;
403 *shadowCssCompat = KoTextFormat::shadowAsCss( distanceX, distanceY, shadowColor );
404 kdDebug(32500) << "setting shadow compat to " << ( *shadowCssCompat ) << endl;
405 }
406 else
407 {
408 delete shadowCssCompat;
409 shadowCssCompat = 0L;
410 }
411}
412
413//static
414TQt::AlignmentFlags KoParagLayout::loadOasisAlignment( const TQCString& str )
415{
416 return
417 str == "left" ? TQt::AlignLeft :
418 str == "right" ? TQt::AlignRight :
419 str == "start" ? TQt::AlignLeft :
420 str == "end" ? TQt::AlignRight :
421 str == "center" ? TQt::AlignHCenter :
422 str == "justify" ? TQt::AlignJustify :
423 str == "start" ? TQt::AlignAuto // i.e. direction-dependent
424 : TQt::AlignAuto; // default (can't happen unless spec is extended)
425}
426
427//static
428TQCString KoParagLayout::saveOasisAlignment( TQt::AlignmentFlags alignment )
429{
430 return alignment == TQt::AlignLeft ? "left" :
431 alignment == TQt::AlignRight ? "right" :
432 alignment == TQt::AlignHCenter ? "center" :
433 alignment == TQt::AlignJustify ? "justify" :
434 "start"; // i.e. direction-dependent
435}
436
438{
439 context.styleStack().setTypeProperties( "paragraph" );
440 // layout is an input and output parameter
441 // It can have been initialized already, e.g. by copying from a style
442
443 // code from OoWriterImport::writeLayout
444 if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "text-align" ) ) {
445 TQCString align = context.styleStack().attributeNS( KoXmlNS::fo, "text-align" ).latin1();
446 layout.alignment = loadOasisAlignment( align );
447 }
448
449 if ( context.styleStack().hasAttributeNS( KoXmlNS::style, "writing-mode" ) ) { // http://web4.w3.org/TR/xsl/slice7.html#writing-mode
450 // LTR is lr-tb. RTL is rl-tb
451 TQString writingMode = context.styleStack().attributeNS( KoXmlNS::style, "writing-mode" );
452 layout.direction = ( writingMode=="rl-tb" || writingMode=="rl" ) ? TQChar::DirR : TQChar::DirL;
453 }
454
455 // Indentation (margins)
456 if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "margin-left" ) || // 3.11.19
457 context.styleStack().hasAttributeNS( KoXmlNS::fo, "margin-right" ) ) {
458 layout.margins[TQStyleSheetItem::MarginLeft] = KoUnit::parseValue( context.styleStack().attributeNS( KoXmlNS::fo, "margin-left" ) );
459 layout.margins[TQStyleSheetItem::MarginRight] = KoUnit::parseValue( context.styleStack().attributeNS( KoXmlNS::fo, "margin-right" ) );
460 // *text-indent must always be bound to either margin-left or margin-right
461 double first = 0;
462 if ( context.styleStack().attributeNS( KoXmlNS::style, "auto-text-indent") == "true" ) // style:auto-text-indent takes precedence
463 // ### "indented by a value that is based on the current font size"
464 // ### and "requires margin-left and margin-right
465 // ### but how much is the indent?
466 first = 10;
467 else if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "text-indent") )
468 first = KoUnit::parseValue( context.styleStack().attributeNS( KoXmlNS::fo, "text-indent") );
469
470 layout.margins[TQStyleSheetItem::MarginFirstLine] = first;
471 }
472
473 // Offset before and after paragraph
474 if( context.styleStack().hasAttributeNS( KoXmlNS::fo, "margin-top") || // 3.11.22
475 context.styleStack().hasAttributeNS( KoXmlNS::fo, "margin-bottom")) {
476 layout.margins[TQStyleSheetItem::MarginTop] = KoUnit::parseValue( context.styleStack().attributeNS( KoXmlNS::fo, "margin-top" ) );
477 layout.margins[TQStyleSheetItem::MarginBottom] = KoUnit::parseValue( context.styleStack().attributeNS( KoXmlNS::fo, "margin-bottom" ) );
478 }
479
480 // Line spacing
481 if( context.styleStack().hasAttributeNS( KoXmlNS::fo, "line-height") ) { // 3.11.1
482 // Fixed line height
483 TQString value = context.styleStack().attributeNS( KoXmlNS::fo, "line-height" );
484 if ( value != "normal" ) {
485 if ( value == "100%" )
486 layout.lineSpacingType = KoParagLayout::LS_SINGLE;
487 else if( value=="150%")
488 layout.lineSpacingType = KoParagLayout::LS_ONEANDHALF;
489 else if( value=="200%")
490 layout.lineSpacingType = KoParagLayout::LS_DOUBLE;
491 else if ( value.find('%') > -1 )
492 {
493 value = value.remove( '%' );
494 double percent = value.toDouble();
495 layout.lineSpacingType = KoParagLayout::LS_MULTIPLE;
496 layout.lineSpacing = percent / 100.0;
497 kdDebug(33001) << "line-height =" << percent << ", " << layout.lineSpacing << ", " << percent/100 << endl;
498 }
499 else // fixed value
500 {
501 layout.lineSpacingType = KoParagLayout::LS_FIXED;
502 layout.lineSpacing = KoUnit::parseValue( value );
503 }
504 }
505 }
506 // Line-height-at-least is mutually exclusive with line-height
507 else if ( context.styleStack().hasAttributeNS( KoXmlNS::style, "line-height-at-least") ) // 3.11.2
508 {
509 TQString value = context.styleStack().attributeNS( KoXmlNS::style, "line-height-at-least" );
510 // kotext has "at least" but that's for the linespacing, not for the entire line height!
511 // Strange. kotext also has "at least" for the whole line height....
512 // Did we make the wrong choice in kotext?
513 //kdWarning() << "Unimplemented support for style:line-height-at-least: " << value << endl;
514 // Well let's see if this makes a big difference.
515 layout.lineSpacingType = KoParagLayout::LS_AT_LEAST;
516 layout.lineSpacing = KoUnit::parseValue( value );
517 }
518 // Line-spacing is mutually exclusive with line-height and line-height-at-least
519 else if ( context.styleStack().hasAttributeNS( KoXmlNS::style, "line-spacing") ) // 3.11.3
520 {
521 double value = KoUnit::parseValue( context.styleStack().attributeNS( KoXmlNS::style, "line-spacing" ) );
522 if ( value != 0.0 )
523 {
524 layout.lineSpacingType = KoParagLayout::LS_CUSTOM;
525 layout.lineSpacing = value;
526 }
527 }
528
529 // Tabulators
530 KoTabulatorList tabList;
531 if ( context.styleStack().hasChildNodeNS( KoXmlNS::style, "tab-stops" ) ) { // 3.11.10
532 TQDomElement tabStops = context.styleStack().childNodeNS( KoXmlNS::style, "tab-stops" );
533 //kdDebug(30519) << k_funcinfo << tabStops.childNodes().count() << " tab stops in layout." << endl;
534 TQDomElement tabStop;
535 forEachElement( tabStop, tabStops )
536 {
537 Q_ASSERT( tabStop.localName() == "tab-stop" );
538 const TQString type = tabStop.attributeNS( KoXmlNS::style, "type", TQString() ); // left, right, center or char
539
540 KoTabulator tab;
541 tab.ptPos = KoUnit::parseValue( tabStop.attributeNS( KoXmlNS::style, "position", TQString() ) );
542 // Tab stop positions in the XML are relative to the left-margin
543 tab.ptPos += layout.margins[TQStyleSheetItem::MarginLeft];
544 if ( type == "center" )
545 tab.type = T_CENTER;
546 else if ( type == "right" )
547 tab.type = T_RIGHT;
548 else if ( type == "char" ) {
549 TQString delimiterChar = tabStop.attributeNS( KoXmlNS::style, "char", TQString() ); // single character
550 if ( !delimiterChar.isEmpty() )
551 tab.alignChar = delimiterChar[0];
552 tab.type = T_DEC_PNT; // "alignment on decimal point"
553 }
554 else //if ( type == "left" )
555 tab.type = T_LEFT;
556
557 tab.ptWidth = KoUnit::parseValue( tabStop.attributeNS( KoXmlNS::style, "leader-width", TQString() ), 0.5 );
558
559 tab.filling = TF_BLANK;
560 if ( tabStop.attributeNS( KoXmlNS::style, "leader-type", TQString() ) == "single" )
561 {
562 TQString leaderStyle = tabStop.attributeNS( KoXmlNS::style, "leader-style", TQString() );
563 if ( leaderStyle == "solid" )
564 tab.filling = TF_LINE;
565 else if ( leaderStyle == "dotted" )
566 tab.filling = TF_DOTS;
567 else if ( leaderStyle == "dash" )
568 tab.filling = TF_DASH;
569 else if ( leaderStyle == "dot-dash" )
570 tab.filling = TF_DASH_DOT;
571 else if ( leaderStyle == "dot-dot-dash" )
572 tab.filling = TF_DASH_DOT_DOT;
573 }
574 else
575 {
576 // Fallback: convert leaderChar's unicode value
577 TQString leaderChar = tabStop.attributeNS( KoXmlNS::style, "leader-text", TQString() );
578 if ( !leaderChar.isEmpty() )
579 {
580 TQChar ch = leaderChar[0];
581 switch (ch.latin1()) {
582 case '.':
583 tab.filling = TF_DOTS; break;
584 case '-':
585 case '_': // TODO in KWord: differentiate --- and ___
586 tab.filling = TF_LINE; break;
587 default:
588 // KWord doesn't have support for "any char" as filling.
589 break;
590 }
591 }
592 }
593 tabList.append( tab );
594 } //for
595 }
596 qHeapSort( tabList );
597 layout.setTabList( tabList );
598
599 layout.joinBorder = !( context.styleStack().attributeNS( KoXmlNS::style, "join-border") == "false" );
600
601 // Borders
602 if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "border","left") )
603 layout.leftBorder.loadFoBorder( context.styleStack().attributeNS( KoXmlNS::fo, "border","left") );
604 else
605 layout.leftBorder.setPenWidth(0);
606 if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "border","right") )
607 layout.rightBorder.loadFoBorder( context.styleStack().attributeNS( KoXmlNS::fo, "border","right") );
608 else
609 layout.rightBorder.setPenWidth(0);
610 if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "border","top") )
611 layout.topBorder.loadFoBorder( context.styleStack().attributeNS( KoXmlNS::fo, "border","top") );
612 else
613 layout.topBorder.setPenWidth(0);
614 if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "border","bottom") )
615 layout.bottomBorder.loadFoBorder( context.styleStack().attributeNS( KoXmlNS::fo, "border","bottom") );
616 else
617 layout.bottomBorder.setPenWidth(0);
618
619
620 // Page breaking
621 int pageBreaking = 0;
622 if( context.styleStack().hasAttributeNS( KoXmlNS::fo, "break-before") ||
623 context.styleStack().hasAttributeNS( KoXmlNS::fo, "break-after") ||
624 context.styleStack().hasAttributeNS( KoXmlNS::fo, "keep-together") ||
625 context.styleStack().hasAttributeNS( KoXmlNS::style, "keep-with-next") ||
626 context.styleStack().hasAttributeNS( KoXmlNS::fo, "keep-with-next") )
627 {
628 if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "break-before") ) { // 3.11.24
629 // TODO in KWord: implement difference between "column" and "page"
630 if ( context.styleStack().attributeNS( KoXmlNS::fo, "break-before" ) != "auto" )
631 pageBreaking |= KoParagLayout::HardFrameBreakBefore;
632 }
633 else if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "break-after") ) { // 3.11.24
634 // TODO in KWord: implement difference between "column" and "page"
635 if ( context.styleStack().attributeNS( KoXmlNS::fo, "break-after" ) != "auto" )
636 pageBreaking |= KoParagLayout::HardFrameBreakAfter;
637 }
638
639 if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "keep-together" ) ) { // was style:break-inside in OOo-1.1, renamed in OASIS
640 if ( context.styleStack().attributeNS( KoXmlNS::fo, "keep-together" ) != "auto" )
641 pageBreaking |= KoParagLayout::KeepLinesTogether;
642 }
643 if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "keep-with-next" ) ) {
644 // OASIS spec says it's "auto"/"always", not a boolean.
645 TQString val = context.styleStack().attributeNS( KoXmlNS::fo, "keep-with-next" );
646 if ( val == "true" || val == "always" )
647 pageBreaking |= KoParagLayout::KeepWithNext;
648 }
649 }
650 layout.pageBreaking = pageBreaking;
651
652 // Paragraph background color - fo:background-color
653 // The background color for parts of a paragraph that have no text underneath
654 if ( context.styleStack().hasAttributeNS( KoXmlNS::fo, "background-color" ) ) {
655 TQString bgColor = context.styleStack().attributeNS( KoXmlNS::fo, "background-color");
656 if (bgColor != "transparent")
657 layout.backgroundColor.setNamedColor( bgColor );
658 }
659}
660
661void KoParagLayout::saveParagLayout( TQDomElement & parentElem, int alignment ) const
662{
663 const KoParagLayout& layout = *this; // code moved from somewhere else;)
664 TQDomDocument doc = parentElem.ownerDocument();
665 TQDomElement element = doc.createElement( "NAME" );
666 parentElem.appendChild( element );
667 if ( layout.style )
668 element.setAttribute( "value", layout.style->displayName() );
669 //else
670 // kdWarning() << "KoParagLayout::saveParagLayout: style==0!" << endl;
671
672 element = doc.createElement( "FLOW" );
673 parentElem.appendChild( element );
674
675 element.setAttribute( "align", alignment==TQt::AlignRight ? "right" :
676 alignment==TQt::AlignHCenter ? "center" :
677 alignment==TQt::AlignJustify ? "justify" :
678 alignment==TQt::AlignAuto ? "auto" : "left" ); // Note: styles can have AlignAuto. Not paragraphs.
679
680 if ( static_cast<TQChar::Direction>(layout.direction) == TQChar::DirR )
681 element.setAttribute( "dir", "R" );
682 else
683 if ( static_cast<TQChar::Direction>(layout.direction) == TQChar::DirL )
684 element.setAttribute( "dir", "L" );
685
686 if ( layout.margins[TQStyleSheetItem::MarginFirstLine] != 0 ||
687 layout.margins[TQStyleSheetItem::MarginLeft] != 0 ||
688 layout.margins[TQStyleSheetItem::MarginRight] != 0 )
689 {
690 element = doc.createElement( "INDENTS" );
691 parentElem.appendChild( element );
692 if ( layout.margins[TQStyleSheetItem::MarginFirstLine] != 0 )
693 element.setAttribute( "first", layout.margins[TQStyleSheetItem::MarginFirstLine] );
694 if ( layout.margins[TQStyleSheetItem::MarginLeft] != 0 )
695 element.setAttribute( "left", layout.margins[TQStyleSheetItem::MarginLeft] );
696 if ( layout.margins[TQStyleSheetItem::MarginRight] != 0 )
697 element.setAttribute( "right", layout.margins[TQStyleSheetItem::MarginRight] );
698 }
699
700 if ( layout.margins[TQStyleSheetItem::MarginTop] != 0 ||
701 layout.margins[TQStyleSheetItem::MarginBottom] != 0 )
702 {
703 element = doc.createElement( "OFFSETS" );
704 parentElem.appendChild( element );
705 if ( layout.margins[TQStyleSheetItem::MarginTop] != 0 )
706 element.setAttribute( "before", layout.margins[TQStyleSheetItem::MarginTop] );
707 if ( layout.margins[TQStyleSheetItem::MarginBottom] != 0 )
708 element.setAttribute( "after", layout.margins[TQStyleSheetItem::MarginBottom] );
709 }
710 if ( layout.lineSpacingType != LS_SINGLE )
711 {
712 element = doc.createElement( "LINESPACING" );
713 parentElem.appendChild( element );
714 if ( layout.lineSpacingType == KoParagLayout::LS_ONEANDHALF ) {
715 element.setAttribute( "type", "oneandhalf" );
716 element.setAttribute( "value", "oneandhalf" ); //compatibility with koffice 1.2
717 }
718 else if ( layout.lineSpacingType == KoParagLayout::LS_DOUBLE ) {
719 element.setAttribute( "type", "double" );
720 element.setAttribute( "value", "double" ); //compatibility with koffice 1.2
721 }
722 else if ( layout.lineSpacingType == KoParagLayout::LS_CUSTOM )
723 {
724 element.setAttribute( "type", "custom" );
725 element.setAttribute( "spacingvalue", layout.lineSpacing);
726 element.setAttribute( "value", layout.lineSpacing ); //compatibility with koffice 1.2
727 }
728 else if ( layout.lineSpacingType == KoParagLayout::LS_AT_LEAST )
729 {
730 element.setAttribute( "type", "atleast" );
731 element.setAttribute( "spacingvalue", layout.lineSpacing);
732 }
733 else if ( layout.lineSpacingType == KoParagLayout::LS_MULTIPLE )
734 {
735 element.setAttribute( "type", "multiple" );
736 element.setAttribute( "spacingvalue", layout.lineSpacing);
737 }
738 else if ( layout.lineSpacingType == KoParagLayout::LS_FIXED )
739 {
740 element.setAttribute( "type", "fixed" );
741 element.setAttribute( "spacingvalue", layout.lineSpacing);
742 }
743 else
744 kdDebug()<<" error in lineSpacing Type\n";
745 }
746
747 if ( layout.pageBreaking != 0 )
748 {
749 element = doc.createElement( "PAGEBREAKING" );
750 parentElem.appendChild( element );
751 if ( layout.pageBreaking & KoParagLayout::KeepLinesTogether )
752 element.setAttribute( "linesTogether", "true" );
753 if ( layout.pageBreaking & KoParagLayout::HardFrameBreakBefore )
754 element.setAttribute( "hardFrameBreak", "true" );
755 if ( layout.pageBreaking & KoParagLayout::HardFrameBreakAfter )
756 element.setAttribute( "hardFrameBreakAfter", "true" );
757 }
758
759 if ( layout.leftBorder.penWidth() > 0 )
760 {
761 element = doc.createElement( "LEFTBORDER" );
762 parentElem.appendChild( element );
763 layout.leftBorder.save( element );
764 }
765 if ( layout.rightBorder.penWidth() > 0 )
766 {
767 element = doc.createElement( "RIGHTBORDER" );
768 parentElem.appendChild( element );
769 layout.rightBorder.save( element );
770 }
771 if ( layout.topBorder.penWidth() > 0 )
772 {
773 element = doc.createElement( "TOPBORDER" );
774 parentElem.appendChild( element );
775 layout.topBorder.save( element );
776 }
777 if ( layout.bottomBorder.penWidth() > 0 )
778 {
779 element = doc.createElement( "BOTTOMBORDER" );
780 parentElem.appendChild( element );
781 layout.bottomBorder.save( element );
782 }
783 if ( layout.counter && layout.counter->numbering() != KoParagCounter::NUM_NONE )
784 {
785 element = doc.createElement( "COUNTER" );
786 parentElem.appendChild( element );
787 if ( layout.counter )
788 layout.counter->save( element );
789 }
790
791 KoTabulatorList tabList = layout.tabList();
792 KoTabulatorList::ConstIterator it = tabList.begin();
793 for ( ; it != tabList.end() ; it++ )
794 {
795 element = doc.createElement( "TABULATOR" );
796 parentElem.appendChild( element );
797 element.setAttribute( "type", (*it).type );
798 element.setAttribute( "ptpos", (*it).ptPos );
799 element.setAttribute( "filling", (*it).filling );
800 if ( (*it).filling != TF_BLANK )
801 element.setAttribute( "width", TQString::number( (*it).ptWidth, 'g', DBL_DIG ) );
802 if ( (*it).type == T_DEC_PNT && !(*it).alignChar.isNull() )
803 element.setAttribute( "alignchar", TQString((*it).alignChar) );
804 }
805}
806
807void KoParagLayout::saveOasis( KoGenStyle& gs, KoSavingContext& context, bool savingStyle ) const
808{
809 gs.addProperty( "fo:text-align", saveOasisAlignment( (TQt::AlignmentFlags)alignment ).data() );
810 // Don't save the direction for a style, if "auto", so that the
811 // auto-determination of direction based on first char, works.
812 if ( !savingStyle || (TQChar::Direction) direction != TQChar::DirON )
813 gs.addProperty( "style:writing-mode", (TQChar::Direction)direction == TQChar::DirR ? "rl-tb" : "lr-tb" );
814 gs.addPropertyPt( "fo:margin-left", margins[TQStyleSheetItem::MarginLeft] );
815 gs.addPropertyPt( "fo:margin-right", margins[TQStyleSheetItem::MarginRight] );
816 gs.addPropertyPt( "fo:text-indent", margins[TQStyleSheetItem::MarginFirstLine] );
817 gs.addPropertyPt( "fo:margin-top", margins[TQStyleSheetItem::MarginTop] );
818 gs.addPropertyPt( "fo:margin-bottom", margins[TQStyleSheetItem::MarginBottom] );
819
820 switch ( lineSpacingType ) {
821 case KoParagLayout::LS_SINGLE:
822 gs.addProperty( "fo:line-height", "100%" );
823 break;
824 case KoParagLayout::LS_ONEANDHALF:
825 gs.addProperty( "fo:line-height", "150%" );
826 break;
827 case KoParagLayout::LS_DOUBLE:
828 gs.addProperty( "fo:line-height", "200%" );
829 break;
830 case KoParagLayout::LS_MULTIPLE:
831 gs.addProperty( "fo:line-height", TQString::number( lineSpacing * 100.0 ) + '%' );
832 break;
833 case KoParagLayout::LS_FIXED:
834 gs.addPropertyPt( "fo:line-height", lineSpacing );
835 break;
836 case KoParagLayout::LS_CUSTOM:
837 gs.addPropertyPt( "style:line-spacing", lineSpacing );
838 break;
839 case KoParagLayout::LS_AT_LEAST:
840 gs.addPropertyPt( "style:line-height-at-least", lineSpacing );
841 break;
842 }
843
844 TQBuffer buffer;
845 buffer.open( IO_WriteOnly );
846 KoXmlWriter tabsWriter( &buffer, 4 ); // indent==4: root,autostyle,style,parag-props
847 tabsWriter.startElement( "style:tab-stops" );
848 KoTabulatorList::ConstIterator it = m_tabList.begin();
849 for ( ; it != m_tabList.end() ; it++ )
850 {
851 tabsWriter.startElement( "style:tab-stop" );
852 // Tab stop positions in the XML are relative to the left-margin
853 double pos = (*it).ptPos - margins[TQStyleSheetItem::MarginLeft];
854 tabsWriter.addAttributePt( "style:position", pos );
855
856 switch ( (*it).type ) {
857 case T_LEFT:
858 tabsWriter.addAttribute( "style:type", "left" );
859 break;
860 case T_CENTER:
861 tabsWriter.addAttribute( "style:type", "center" );
862 break;
863 case T_RIGHT:
864 tabsWriter.addAttribute( "style:type", "right" );
865 break;
866 case T_DEC_PNT: // "alignment on decimal point"
867 tabsWriter.addAttribute( "style:type", "char" );
868 if ( !(*it).alignChar.isNull() )
869 tabsWriter.addAttribute( "style:char", TQString( (*it).alignChar ) );
870 break;
871 case T_INVALID: // keep compiler happy, this can't happen
872 break;
873 }
874 switch( (*it).filling ) {
875 case TF_BLANK:
876 tabsWriter.addAttribute( "style:leader-type", "none" );
877 break;
878 case TF_LINE:
879 tabsWriter.addAttribute( "style:leader-type", "single" );
880 tabsWriter.addAttribute( "style:leader-style", "solid" );
881 // Give OOo a chance to show something, since it doesn't support lines here.
882 tabsWriter.addAttribute( "style:leader-text", "_" );
883 break;
884 case TF_DOTS:
885 tabsWriter.addAttribute( "style:leader-type", "single" );
886 tabsWriter.addAttribute( "style:leader-style", "dotted" );
887 // Give OOo a chance to show something, since it doesn't support lines here.
888 tabsWriter.addAttribute( "style:leader-text", "." );
889 break;
890 case TF_DASH:
891 tabsWriter.addAttribute( "style:leader-type", "single" );
892 tabsWriter.addAttribute( "style:leader-style", "dash" );
893 // Give OOo a chance to show something, since it doesn't support lines here.
894 tabsWriter.addAttribute( "style:leader-text", "_" );
895 break;
896 case TF_DASH_DOT:
897 tabsWriter.addAttribute( "style:leader-type", "single" );
898 tabsWriter.addAttribute( "style:leader-style", "dot-dash" );
899 // Give OOo a chance to show something, since it doesn't support lines here.
900 tabsWriter.addAttribute( "style:leader-text", "." );
901 break;
902 case TF_DASH_DOT_DOT:
903 tabsWriter.addAttribute( "style:leader-type", "single" );
904 tabsWriter.addAttribute( "style:leader-style", "dot-dot-dash" );
905 // Give OOo a chance to show something, since it doesn't support lines here.
906 tabsWriter.addAttribute( "style:leader-text", "." );
907 break;
908 }
909 if ( (*it).filling != TF_BLANK )
910 tabsWriter.addAttributePt( "style:leader-width", (*it).ptWidth );
911 // If we want to support it, oasis also defines style:leader-color
912 tabsWriter.endElement();
913 }
914 tabsWriter.endElement();
915 buffer.close();
916 TQString elementContents = TQString::fromUtf8( buffer.buffer(), buffer.buffer().size() );
917 gs.addChildElement( "style:tab-stops", elementContents );
918
919 if ( !joinBorder )
920 gs.addProperty( "style:join-border", "false" );
921 bool fourBordersEqual = leftBorder.penWidth() > 0 &&
922 leftBorder == rightBorder && rightBorder == topBorder && topBorder == bottomBorder;
923 if ( fourBordersEqual ) {
924 gs.addProperty( "fo:border", leftBorder.saveFoBorder() );
925 } else {
926 if ( leftBorder.penWidth() > 0 )
927 gs.addProperty( "fo:border-left", leftBorder.saveFoBorder() );
928 if ( rightBorder.penWidth() > 0 )
929 gs.addProperty( "fo:border-right", rightBorder.saveFoBorder() );
930 if ( topBorder.penWidth() > 0 )
931 gs.addProperty( "fo:border-top", topBorder.saveFoBorder() );
932 if ( bottomBorder.penWidth() > 0 )
933 gs.addProperty( "fo:border-bottom", bottomBorder.saveFoBorder() );
934 }
935
936 if ( pageBreaking & KoParagLayout::HardFrameBreakBefore )
937 gs.addProperty( "fo:break-before", context.hasColumns() ? "column" : "page" );
938 else if ( pageBreaking & KoParagLayout::HardFrameBreakAfter )
939 gs.addProperty( "fo:break-after", context.hasColumns() ? "column" : "page" );
940 if ( pageBreaking & KoParagLayout::KeepLinesTogether )
941 gs.addProperty( "fo:keep-together", "always" );
942 if ( pageBreaking & KoParagLayout::KeepWithNext )
943 gs.addProperty( "fo:keep-with-next", "always" );
944
945 gs.addProperty( "fo:background-color",
946 backgroundColor.isValid() ?
947 backgroundColor.name() : "transparent");
948}
A generic style, i.e.
void addChildElement(const TQString &elementName, const TQString &elementContents)
Add a child element to the style properties.
void addProperty(const TQString &propName, const TQString &propValue, PropertyType type=DefaultType)
Add a property to the style.
void addPropertyPt(const TQString &propName, double propValue, PropertyType type=DefaultType)
Add a property which represents a distance, measured in pt The number is written out with the highest...
Used during loading of Oasis format (and discarded at the end of the loading).
This is the structure associated with a paragraph (KoTextParag), to define the bullet or numbering of...
void load(TQDomElement &element)
KOffice-1.3 loading code.
Numbering numbering() const
Numbering type and style.
void save(TQDomElement &element)
KOffice-1.3 saving code.
This class holds the paragraph-specific formatting information It's separated from KoTextParag so tha...
static void loadOasisParagLayout(KoParagLayout &layout, KoOasisContext &context)
Load this parag layout from Oasis XML.
void saveOasis(KoGenStyle &gs, KoSavingContext &context, bool savingStyle) const
Save this parag layout to Oasis XML.
static void loadParagLayout(KoParagLayout &layout, const TQDomElement &parentElem, int docVersion=2)
Load this parag layout from XML.
static TQt::AlignmentFlags loadOasisAlignment(const TQCString &str)
Convert an alignment string into an alignment flag (load)
TQColor backgroundColor
The background color of the paragraph.
int compare(const KoParagLayout &layout) const
Return a set of flags showing the differences between this and 'layout'.
void operator=(const KoParagLayout &)
Assignment operator for KoParagLayout.
double margins[5]
left, right, top, bottom, firstLineSpacing - in pt
static TQCString saveOasisAlignment(TQt::AlignmentFlags alignment)
Convert an alignment flag into an alignment string (save)
KoParagCounter * counter
can be 0 if no counter set
char alignment
Alignment flag (AlignAuto/AlignLeft/AlignRight/AlignJustify)
void saveParagLayout(TQDomElement &parentElem, int alignment) const
Save this parag layout to XML.
Used during saving to Oasis format (and discarded at the end of the saving).
bool hasAttributeNS(const char *nsURI, const char *localName, const char *detail=0) const
Check if any of the styles on the stack has an attribute called 'name'-'detail' where detail is e....
TQDomElement childNodeNS(const char *nsURI, const char *localName) const
Search for a child element which has a child element called 'localName' in the namespace 'nsURI' star...
void setTypeProperties(const char *typeProperties)
Set the type of properties that will be looked for.
TQString attributeNS(const char *nsURI, const char *localName, const char *detail=0) const
Search for the attribute called 'name', starting on top of the stack, and return it.
bool hasChildNodeNS(const char *nsURI, const char *localName) const
Check if any of the styles on the stack has a child element called 'localName' in the namespace 'nsUR...
TQString shadowAsCss() const
Return css string for the shadow, used when saving.
static double parseValue(TQString value, double defaultVal=0.0)
parse common KOffice and OO values, like "10cm", "5mm" to pt
Definition KoUnit.cpp:148
TQString displayName() const
The user-visible name (e.g. translated, or set by the user)
A class for writing out XML (to any TQIODevice), with a special attention on performance.
Definition KoXmlWriter.h:37
void startElement(const char *tagName, bool indentInside=true)
Start a new element, as a child of the current element.
void addAttributePt(const char *attrName, double value)
Add an attribute which represents a distance, measured in pt The number is written out with the highe...
void endElement()
Terminate the current element.
void addAttribute(const char *attrName, const TQString &value)
Overloaded version of addAttribute( const char*, const char* ), which is a bit slower because it need...
Definition KoXmlWriter.h:76
Struct: KoTabulator Defines the position of a tabulation (in pt), and its type.
Definition KoRuler.h:46
KoTabulators type
Type of tab (left/center/right/decimalpoint)
Definition KoRuler.h:54
KoTabulatorFilling filling
Type of tab filling.
Definition KoRuler.h:58
double ptPos
Position of the tab in pt.
Definition KoRuler.h:50
TQChar alignChar
Alignment character.
Definition KoRuler.h:66
double ptWidth
Width of the tab filling line.
Definition KoRuler.h:62