~ubuntu-branches/ubuntu/breezy/koffice/breezy

« back to all changes in this revision

Viewing changes to filters/kword/pdf/FilterPage.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Ben Burton
  • Date: 2004-05-09 11:33:00 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20040509113300-vfrdadqsvjfuhn3b
Tags: 1:1.3.1-1
* New upstream bugfix release.
* Built against newer imagemagick (closes: #246623).
* Made koffice-libs/kformula recommend/depend on latex-xft-fonts, which
  provides mathematical fonts that the formula editor can use.  Also
  patched the kformula part to make these fonts the default.
* Changed kword menu hint from "WordProcessors" to "Word processors"
  (closes: #246209).
* Spellchecker configuration is now fixed (closes: #221256, #227568).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2002-2003 Nicolas HADACEK (hadacek@kde.org)
 
3
 *
 
4
 * This program is free software; you can redistribute it and/or modify
 
5
 * it under the terms of the GNU General Public License as published by
 
6
 * the Free Software Foundation; either version 2 of the License, or
 
7
 * (at your option) any later version.
 
8
 
 
9
 * This program 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
 
12
 * GNU General Public License for more details.
 
13
 
 
14
 * You should have received a copy of the GNU General Public License
 
15
 * along with this program; if not, write to the Free Software
 
16
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
17
 */
 
18
 
 
19
#include "FilterPage.h"
 
20
 
 
21
#include <math.h>
 
22
 
 
23
#include <kglobal.h>
 
24
#include <kdebug.h>
 
25
 
 
26
#include "data.h"
 
27
#include "transform.h"
 
28
#include "dialog.h"
 
29
 
 
30
#define TIME_START(str) { \
 
31
    kdDebug(30516) << str << endl; \
 
32
    _time.restart(); \
 
33
}
 
34
#define TIME_END { kdDebug(30516) << "elapsed=" << _time.elapsed() << endl; }
 
35
 
 
36
 
 
37
namespace PDFImport
 
38
{
 
39
 
 
40
//-----------------------------------------------------------------------------
 
41
Page::Page(Data &data)
 
42
    : TextPage(false), _data(data), _lastStr(0), _rects(Nb_ParagraphTypes)
 
43
{
 
44
    _links.setAutoDelete(true);
 
45
}
 
46
 
 
47
void Page::clear()
 
48
{
 
49
    TextPage::clear();
 
50
    _lastStr = 0;
 
51
    _links.clear();
 
52
    _pars.clear();
 
53
}
 
54
 
 
55
void Page::beginString(GfxState *state, double x0, double y0)
 
56
{
 
57
    // This check is needed because Type 3 characters can contain
 
58
    // text-drawing operations.
 
59
    if (curStr) {
 
60
        ++nest;
 
61
        return;
 
62
    }
 
63
 
 
64
//    _data.checkTextFrameset();
 
65
    curStr = new String(state, x0, y0, fontSize, _data.textIndex());
 
66
//    kdDebug(30516) << "---" << endl;
 
67
}
 
68
 
 
69
void Page::endString()
 
70
{
 
71
//    kdDebug(30516) << "endString..." << " len=" << curStr->len
 
72
//                   << " " << _lastStr
 
73
//                   << " len=" << (_lastStr ? _lastStr->len : -1) << endl;
 
74
    TextPage::endString();
 
75
//    kdDebug(30516) << "  ...endString done" << endl;
 
76
}
 
77
 
 
78
void Page::addString(TextString *str)
 
79
{
 
80
//    kdDebug(30516) << "addString..." << endl;
 
81
//    if ( str->len==0 ) kdDebug(30516) << "empty string !" << endl;
 
82
    if (_lastStr) _lastStr->checkCombination(str);
 
83
    _lastStr = (str->len==0 ? 0 : static_cast<String *>(str));
 
84
//    QString s;
 
85
//    for (int i=0; i<str->len; i++) s += QChar(str->text[i]);
 
86
//    kdDebug(30516) << "string: " << s << " ("
 
87
//                   << (str->len>0 ? s[0].unicode() : 0) << ")" << endl;
 
88
    TextPage::addString(str);
 
89
//    kdDebug(30516) << " ...addString done" << endl;
 
90
}
 
91
 
 
92
TextBlock *Page::block(TextLine *line, int index)
 
93
{
 
94
    uint k = 0;
 
95
    if ( index<0 )
 
96
        for (TextBlock *block = line->blocks; block; block = block->next) k++;
 
97
    k += index;
 
98
    uint i = 0;
 
99
    for (TextBlock *block = line->blocks; block; block = block->next) {
 
100
        if ( i==k ) return block;
 
101
        i++;
 
102
    }
 
103
    return 0;
 
104
}
 
105
 
 
106
//-----------------------------------------------------------------------------
 
107
bool Page::isLastParagraphLine(TextLine *line, const Paragraph &par)
 
108
{
 
109
    // last line of page
 
110
    if ( line->next==0 ) return true;
 
111
    double dy = line->next->yMin - line->yMax;
 
112
    double ndy = line->next->yMax - line->next->yMin;
 
113
    String *str = static_cast<String *>(line->blocks->strings);
 
114
    String *nStr = static_cast<String *>(line->next->blocks->strings);
 
115
    // next line far below
 
116
    if ( dy>0.5*ndy ) return true;
 
117
    // image displayed before next line (?)
 
118
    if ( str->frameIndex()!=nStr->frameIndex() ) return true;
 
119
    if ( line->blocks==0 ) return false; // should not happen
 
120
    // if contains one or more inside tabs
 
121
    if (line->blocks->next) return true;
 
122
    if ( line->next && line->next->blocks==0 ) return false;//should not happen
 
123
    // if next line contains one or more inside tabs
 
124
    if ( line->next && line->next->blocks->next ) return true;
 
125
    TextBlock *b = block(line, -1);
 
126
    if ( b==0 || b->len==0 ) return false; // should not happen
 
127
    QChar c = QChar(b->text[b->len-1]);
 
128
    // last line char is not '.' or ':'
 
129
    if ( c!='.' && c!=':' ) return false;
 
130
    // if at line end and block aligned : same paragraph
 
131
    return ( !equal(b->xMax, par.rect().right()) );
 
132
}
 
133
 
 
134
void Page::createParagraphs()
 
135
{
 
136
    TextLine *first = lines;
 
137
    uint nbLines = 0;
 
138
    for (TextLine *line = lines; line; line = line->next) {
 
139
        nbLines++;
 
140
        Paragraph par(first, nbLines);
 
141
        if ( isLastParagraphLine(line, par) ) {
 
142
            _pars.push_back(par);
 
143
            nbLines = 0;
 
144
            first = line->next;
 
145
        }
 
146
    }
 
147
}
 
148
 
 
149
void Page::checkHeader()
 
150
{
 
151
    uint s = _pars.size();
 
152
    if ( s==0 ) return;
 
153
    Paragraph &par = _pars[0];
 
154
    if ( par.lines().count()!=1 ) return;
 
155
    const TextLine *first = par.lines().first();
 
156
    const TextLine *second = (s>1 ? _pars[1].lines().first() : 0);
 
157
    double limit = 0.2 * _data.pageRect().height();
 
158
    double delta = 2 * kMin(first->yMax - first->yMin, 12.0);
 
159
//    kdDebug(30516) << "first: " << first->yMax << " (" << limit << ")" << endl;
 
160
//    if (second) kdDebug(30516) << "second: " << second->yMin << " "
 
161
//                               << second->yMin-first->yMax << " (" << delta
 
162
//                               << ")" << endl;
 
163
    if ( first->yMax>limit ) return;
 
164
    if ( second && (second->yMin-first->yMax)<delta ) return;
 
165
    par.type = Header;
 
166
    _rects[Header] = par.rect();
 
167
}
 
168
 
 
169
bool Page::hasHeader() const
 
170
{
 
171
    return (_pars.size()>0 ? _pars[0].type==Header : false);
 
172
}
 
173
 
 
174
void Page::checkFooter()
 
175
{
 
176
    uint s = _pars.size();
 
177
    if ( s==0 ) return;
 
178
    Paragraph &par = _pars[s-1];
 
179
    if ( par.lines().count()!=1 ) return;
 
180
    const TextLine *last = par.lines().first();
 
181
    const TextLine *blast = (s>1 ? _pars[s-2].lines().last() : 0);
 
182
    double limit = 0.8 * _data.pageRect().height();
 
183
    double delta = 2 * kMin(last->yMax-last->yMin, 12.0);
 
184
//    kdDebug(30516) << "last: " << last->yMax << " (" << limit << ")" << endl;
 
185
//    if (blast) kdDebug(30516) << "blast: " << blast->yMin << " "
 
186
//                              <<  last->yMin-blast->yMax << " (" << delta
 
187
//                              << ")" << endl;
 
188
    if ( last->yMin<limit ) return;
 
189
    if ( blast && (last->yMin-blast->yMax)<delta ) return;
 
190
    par.type = Footer;
 
191
    _rects[Footer] = par.rect();
 
192
}
 
193
 
 
194
bool Page::hasFooter() const
 
195
{
 
196
    return (_pars.size()>0 ? _pars[_pars.size()-1].type==Footer
 
197
            : false);
 
198
}
 
199
 
 
200
void Page::endPage()
 
201
{
 
202
    TIME_START("coalesce strings");
 
203
    TextPage::coalesce();
 
204
    TIME_END;
 
205
 
 
206
    createParagraphs();
 
207
 
 
208
    // check header and footer
 
209
    checkHeader();
 
210
//    if ( hasHeader() ) kdDebug(30516) << "has header" << endl;
 
211
    checkFooter();
 
212
//    if ( hasFooter() ) kdDebug(30516) << "has footer" << endl;
 
213
 
 
214
    // compute body rect
 
215
    uint begin = (hasHeader() ? 1 : 0);
 
216
    uint end = _pars.size() - (hasFooter() ? 1 : 0);
 
217
    for (uint i=begin; i<end; i++)
 
218
        _rects[Body].unite(_pars[i].rect());
 
219
}
 
220
 
 
221
 
 
222
//-----------------------------------------------------------------------------
 
223
void Page::initParagraph(Paragraph &par) const
 
224
{
 
225
    bool rightAligned = true, centered = true, leftAligned = true;
 
226
    const double pleft = _rects[par.type].left();
 
227
    const double pright = _rects[par.type].right();
 
228
    const double pmean = (pleft + pright) / 2;
 
229
 
 
230
    QValueList<TextLine *>::const_iterator it;
 
231
    for (it = par.lines().begin(); it!=par.lines().end(); ++it) {
 
232
 
 
233
        // compute tabulations
 
234
        Tabulator tab;
 
235
        for (TextBlock *blk = (*it)->blocks; blk; blk = blk->next) {
 
236
            // if tabulated text is aligned on right edge: put a tab
 
237
            // on right edge and the tab type will be right aligned...
 
238
            double tabRightAligned = equal(blk->xMax, pright);
 
239
            double dx = (tabRightAligned ? pright : blk->xMin) - pleft;
 
240
            // #### if the tab is just at the frame edge:
 
241
            // the text is sent to next line ???
 
242
            if (tabRightAligned) dx -= 0.1;
 
243
            int res = par.findTab(dx, *it);
 
244
            if ( res==-1 ) {
 
245
                tab.pos = dx;
 
246
                if (tabRightAligned) {
 
247
                    tab.alignment = Tabulator::Right;
 
248
                    kdDebug(30516) << "tabulated text right aligned.." << endl;
 
249
                } else tab.alignment = Tabulator::Left;
 
250
                par.tabs.push_back(tab);
 
251
            }
 
252
        }
 
253
        qHeapSort2(par.tabs);
 
254
 
 
255
        // compute indents
 
256
        double left = (*it)->blocks->xMin - pleft;
 
257
        if ( par.isFirst(*it) ) {
 
258
            par.firstIndent = left;
 
259
            par.leftIndent = left;
 
260
        } else if ( par.isSecond(*it) ) par.leftIndent = left;
 
261
        else par.leftIndent = kMin(par.leftIndent, left);
 
262
    }
 
263
 
 
264
    // compute alignment
 
265
    for (it = par.lines().begin(); it!=par.lines().end(); ++it) {
 
266
        double left = (*it)->blocks->xMin;
 
267
        double right = block(*it, -1)->xMax;
 
268
        double mean = (left + right) / 2;
 
269
//        QString text;
 
270
//        for (int i=0; i<kMin(4, (*it)->blocks->len); i++)
 
271
//            text += QChar((*it)->blocks->text[i]);
 
272
//        kdDebug(30516) << text << " left=" << left
 
273
//                       << " pleft=" << pleft + par.leftIndent
 
274
//                       << " indent=" << par.leftIndent
 
275
//                       << " findent=" << par.firstIndent << endl;
 
276
        if ( centered && !equal(mean, pmean) ) centered = false;
 
277
        if ( leftAligned && (!par.isFirst(*it) || par.hasOneLine())
 
278
             && !equal(left, pleft + par.leftIndent, 0.05) ) {
 
279
            kdDebug(30516) << "not left aligned" << endl;
 
280
            leftAligned = false;
 
281
        }
 
282
        if ( rightAligned && (!par.isLast(*it) || par.hasOneLine())
 
283
             && !equal(right, pright, 0.05) ) {
 
284
            kdDebug(30516) << "not right aligned" << endl;
 
285
            rightAligned = false;
 
286
        }
 
287
    }
 
288
 
 
289
    // finalize alignment
 
290
    if (rightAligned) par.align = (leftAligned ? AlignBlock : AlignRight);
 
291
    else if (centered) par.align = AlignCenter;
 
292
}
 
293
 
 
294
void Page::fillParagraph(Paragraph &par, double &offset) const
 
295
{
 
296
    const double pleft = _rects[par.type].left();
 
297
    const double pright = _rects[par.type].right();
 
298
    par.offset = par.lines().first()->yMin - offset;
 
299
//    kdDebug(30516) << "offset=" << offset
 
300
//                   << " yMin=" << par.lines().first()->yMin
 
301
//                   << " paroffset=" << par.offset << endl;
 
302
    if ( par.offset>0 ) offset += par.offset;
 
303
 
 
304
    QValueList<TextLine *>::const_iterator it;
 
305
    for (it = par.lines().begin(); it!=par.lines().end(); ++it) {
 
306
        // end of previous line (inside a paragraph)
 
307
        if ( !par.isFirst(*it) ) {
 
308
            bool hyphen = false;
 
309
            if (_data.options().smart) {
 
310
                // check hyphen
 
311
                uint bi, pbi;
 
312
                int si = par.charFromEnd(0, bi);
 
313
                Q_ASSERT( si>=0 );
 
314
                QChar c = par.blocks[bi].text[si];
 
315
                int psi = par.charFromEnd(1, pbi);
 
316
                QChar prev = (psi<0 ? QChar::null : par.blocks[pbi].text[psi]);
 
317
                if ( !prev.isNull() && type(c.unicode())==Hyphen )
 
318
                    kdDebug(30516) << "hyphen ? " << QString(prev)
 
319
                                   << " type=" << type(prev.unicode())
 
320
                                   << endl;
 
321
                TextString *next =
 
322
                    ((*it)->next ? (*it)->next->blocks->strings : 0);
 
323
                if ( !prev.isNull() && type(c.unicode())==Hyphen
 
324
                     && isLetter( type(prev.unicode()) )
 
325
                     && next && next->len>0
 
326
                     && isLetter( type(next->text[next->len-1]) ) ) {
 
327
                    kdDebug(30516) << "found hyphen" << endl;
 
328
                    hyphen = true;
 
329
                    par.blocks[bi].text.remove(si, 1);
 
330
                }
 
331
            }
 
332
            if ( !hyphen ) {
 
333
                Block b;
 
334
                bool remove = _data.options().smart;
 
335
                if ( remove && par.align!=AlignBlock )
 
336
                    remove = ( par.rect().right()>0.9*pright );
 
337
                b.text = (remove ? ' ' : '\n');
 
338
                b.font = static_cast<String *>((*it)->blocks->strings)->font();
 
339
                par.blocks.push_back(b);
 
340
            }
 
341
        }
 
342
 
 
343
        int lineHeight = 0;
 
344
        TextBlock *prevBlk = 0;
 
345
        for (TextBlock *blk = (*it)->blocks; blk; blk = blk->next) {
 
346
 
 
347
            // tabulations
 
348
            double tabRightAligned = equal(blk->xMax, pright);
 
349
            double dx = (tabRightAligned ? pright : blk->xMin) - pleft;
 
350
            int res = par.findTab(dx, *it);
 
351
            if ( res>=0 ) {
 
352
                if (prevBlk) {
 
353
                    double xMax = prevBlk->xMax - pleft;
 
354
                    res = par.findNbTabs(res, xMax);
 
355
                    if ( res==0 ) continue;
 
356
                } else res++;
 
357
                // no tabs for first block in AlignCenter and AlignRight
 
358
                // if smart mode
 
359
                if ( prevBlk || !_data.options().smart
 
360
                     || (par.align!=AlignCenter && par.align!=AlignRight) ) {
 
361
                    Block b;
 
362
                    b.font = static_cast<String *>(blk->strings)->font();
 
363
                    for (uint k=0; k<(uint)res; k++) b.text += '\t';
 
364
                    par.blocks.push_back(b);
 
365
                }
 
366
            }
 
367
 
 
368
            // text & format
 
369
            for (TextString *str = blk->strings; str; str = str->next) {
 
370
                Block b;
 
371
                for (uint k = 0; k<uint(str->len); k++)
 
372
                    b.text += QChar(str->text[k]);
 
373
                if (str->spaceAfter) b.text += ' ';
 
374
                String *fstr = static_cast<String *>(str);
 
375
                b.font = fstr->font();
 
376
                b.link = fstr->link;
 
377
                par.blocks.push_back(b);
 
378
                lineHeight = kMax(lineHeight, b.font.height());
 
379
            }
 
380
 
 
381
            prevBlk = blk;
 
382
        }
 
383
 
 
384
        offset += lineHeight;
 
385
    }
 
386
}
 
387
 
 
388
FontFamily Page::checkSpecial(QChar &c, const Font &font) const
 
389
{
 
390
    Unicode res = 0;
 
391
    switch ( PDFImport::checkSpecial(c.unicode(), res) ) {
 
392
    case Bullet:
 
393
        kdDebug(30516) << "found bullet" << endl;
 
394
        // #### FIXME : if list, use a COUNTER
 
395
        // temporarly replace by symbol
 
396
        c = res;
 
397
        return Symbol;
 
398
    case SuperScript:
 
399
        kdDebug(30516) << "found superscript" << endl;
 
400
        // #### FIXME
 
401
        break;
 
402
    case LatexSpecial:
 
403
        if ( !font.isLatex() ) break;
 
404
        kdDebug(30516) << "found latex special" << endl;
 
405
        return Times;
 
406
    case SpecialSymbol:
 
407
        kdDebug(30516) << "found symbol=" << c.unicode() << endl;
 
408
        return Times;
 
409
        //return Symbol;
 
410
    default:
 
411
        break;
 
412
    }
 
413
 
 
414
    return Nb_Family;
 
415
}
 
416
 
 
417
void Page::checkSpecialChars(Paragraph &par) const
 
418
{
 
419
    QValueList<Block> blocks;
 
420
    for (uint k=0; k<par.blocks.size(); k++) {
 
421
        const Block &b = par.blocks[k];
 
422
        QString res;
 
423
//            kdDebug(30516) << "check \"" << b.text << "\"" << endl;
 
424
        for (uint l=0; l<b.text.length(); l++) {
 
425
            QChar c = b.text[l];
 
426
            FontFamily family = checkSpecial(c, b.font);
 
427
            if ( family==Nb_Family ) res += c;
 
428
            else {
 
429
                if ( !res.isEmpty() ) {
 
430
                    blocks.push_back(b);
 
431
                    blocks.back().text = res;
 
432
                    res = QString::null;
 
433
                }
 
434
                blocks.push_back(b);
 
435
                blocks.back().font.setFamily(family);
 
436
                blocks.back().text = c;
 
437
            }
 
438
        }
 
439
        if ( !res.isEmpty() ) {
 
440
            blocks.push_back(b);
 
441
            blocks.back().text = res;
 
442
        }
 
443
    }
 
444
    par.blocks = blocks;
 
445
}
 
446
 
 
447
void Page::coalesce(Paragraph &par) const
 
448
{
 
449
    QValueList<Block> blocks;
 
450
    blocks.push_back(par.blocks[0]);
 
451
    for (uint k=1; k<par.blocks.size(); k++) {
 
452
        const Block &b = par.blocks[k];
 
453
        if ( b.link==blocks.back().link && b.font==blocks.back().font )
 
454
            blocks.back().text += b.text;
 
455
        else blocks.push_back(b);
 
456
    }
 
457
    par.blocks = blocks;
 
458
}
 
459
 
 
460
void Page::prepare()
 
461
{
 
462
    TIME_START("associate links");
 
463
    for (Link *link=_links.first(); link; link=_links.next()) {
 
464
        const DRect &r = link->rect();
 
465
//        kdDebug(30516) << "link " << r.toString() << endl;
 
466
        for (TextLine *line = lines; line; line = line->next)
 
467
            for (TextBlock *blk = line->blocks; blk; blk = blk->next)
 
468
                for (TextString *str = blk->strings; str; str = str->next) {
 
469
                    String *fstr = static_cast<String *>(str);
 
470
                    DRect sr = fstr->rect();
 
471
//                    kdDebug(30516) << "str " << sr.toString() << " "
 
472
//                                   << r.isInside(sr) << endl;
 
473
                    if ( r.isInside(sr) ) fstr->link = link;
 
474
                }
 
475
    }
 
476
    TIME_END;
 
477
 
 
478
    TIME_START("init paragraphs");
 
479
    for (uint i=0; i<_pars.size(); i++) {
 
480
        initParagraph(_pars[i]);
 
481
 
 
482
        // special case for wide and centered one liner without tab
 
483
        if ( _pars[i].align==AlignBlock && _pars[i].hasOneLine()
 
484
             && _pars[i].tabs.size()==0
 
485
             && (_pars.size()==1
 
486
                 || (i!=0 && _pars[i-1].align==AlignCenter)
 
487
                 || ((i+1)!=_pars.size() && _pars[i+1].align==AlignCenter)) )
 
488
            _pars[i].align = AlignCenter;
 
489
    }
 
490
    TIME_END;
 
491
 
 
492
    TIME_START("fill paragraphs");
 
493
    uint begin = 0;
 
494
    if ( hasHeader() ) {
 
495
        double offset = _rects[Header].top();
 
496
        fillParagraph(_pars[0], offset);
 
497
        begin++;
 
498
    }
 
499
    uint end = _pars.size();
 
500
    if ( hasFooter() ) {
 
501
        double offset = _rects[Footer].top();
 
502
        end--;
 
503
        fillParagraph(_pars[end], offset);
 
504
    }
 
505
    double offset = _rects[Body].top();
 
506
    for (uint i=begin; i<end; i++)
 
507
        fillParagraph(_pars[i], offset);
 
508
    TIME_END;
 
509
 
 
510
    TIME_START("check for special chars");
 
511
    for (uint i=0; i<_pars.size(); i++)
 
512
        checkSpecialChars(_pars[i]);
 
513
    TIME_END;
 
514
 
 
515
    // this is not really required...
 
516
    TIME_START("coalesce formats");
 
517
    for (uint i=0; i<_pars.size(); i++)
 
518
        coalesce(_pars[i]);
 
519
    TIME_END;
 
520
 
 
521
    // if no paragraph : add an empty one
 
522
    if ( _pars.size()==0 ) {
 
523
        Block b;
 
524
        Paragraph par(0, 0);
 
525
        par.blocks.push_back(b);
 
526
        _pars.push_back(par);
 
527
    }
 
528
}
 
529
 
 
530
void Page::dump(const Paragraph &par)
 
531
{
 
532
    QValueVector<QDomElement> layouts;
 
533
    QValueVector<QDomElement> formats;
 
534
 
 
535
    // tabulations
 
536
    for (uint k=0; k<par.tabs.size(); k++) {
 
537
        QDomElement element = par.tabs[k].createElement(_data);
 
538
        layouts.push_back(element);
 
539
    }
 
540
 
 
541
    // indents
 
542
    if ( !_data.options().smart || par.align!=AlignCenter ) {
 
543
        QDomElement element = _data.createElement("INDENTS");
 
544
        element.setAttribute("left", par.leftIndent);
 
545
        double delta = par.firstIndent - par.leftIndent;
 
546
        if ( !equal(delta, 0) ) element.setAttribute("first", delta);
 
547
        layouts.push_back(element);
 
548
    }
 
549
 
 
550
    // offset before
 
551
    if ( par.offset>0 ) {
 
552
        QDomElement element = _data.createElement("OFFSETS");
 
553
        element.setAttribute("before", par.offset);
 
554
        layouts.push_back(element);
 
555
    }
 
556
 
 
557
    // flow
 
558
    if (_data.options().smart) {
 
559
        QString flow;
 
560
//        kdDebug(30516) << "flow=" << par.align << endl;
 
561
        switch (par.align) {
 
562
        case AlignLeft: break;
 
563
        case AlignRight: flow = "right"; break;
 
564
        case AlignCenter: flow = "center"; break;
 
565
        case AlignBlock: flow = "justify"; break;
 
566
        }
 
567
        if ( !flow.isEmpty() ) {
 
568
            QDomElement element = _data.createElement("FLOW");
 
569
            element.setAttribute("align", flow.utf8());
 
570
            layouts.push_back(element);
 
571
        }
 
572
    }
 
573
 
 
574
    // text and formats
 
575
    QString text;
 
576
    uint pos = 0;
 
577
    for (uint k=0; k<par.blocks.size(); k++) {
 
578
        const Block &b = par.blocks[k];
 
579
        text += (b.link ? "#" : b.text);
 
580
        uint len = (b.link ? 1 : b.text.length());
 
581
        QDomElement element = _data.createElement("FORMAT");
 
582
        QDomDocument document = _data.document();
 
583
        bool r = b.font.format(document, element, pos, len);
 
584
        if (b.link) b.link->format(document, element, pos, b.text);
 
585
        if ( r || b.link ) formats.push_back(element);
 
586
        pos += len;
 
587
    }
 
588
 
 
589
    _data.createParagraph(text, par.type, layouts, formats);
 
590
}
 
591
 
 
592
void Page::dump()
 
593
{
 
594
    prepare();
 
595
 
 
596
    TIME_START("dump XML");
 
597
    for (uint i=0; i<_pars.size(); i++)
 
598
        dump(_pars[i]);
 
599
    TIME_END;
 
600
}
 
601
 
 
602
}; // namespace