Subversion Repositories Scribus

Rev

Rev 17140 | Rev 17199 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
4430 cbradney 1
/*
2
For general Scribus (>=1.3.2) copyright and licensing information please refer
3
to the COPYING file provided with the program. Following this notice may exist
4
a copyright and/or license notice that predates the release of Scribus 1.3.2
5
for which a new license (GPL+exception) is in place.
6
*/
3614 cbradney 7
/***************************************************************************
8
                          pageitem.cpp  -  description
9
                             -------------------
10
    begin                : Sat Apr 7 2001
11
    copyright            : (C) 2001 by Franz Schmid
12
    email                : Franz.Schmid@altmuehlnet.de
13
 ***************************************************************************/
14
 
15
/***************************************************************************
16
 *                                                                         *
17
 *   This program is free software; you can redistribute it and/or modify  *
18
 *   it under the terms of the GNU General Public License as published by  *
19
 *   the Free Software Foundation; either version 2 of the License, or     *
20
 *   (at your option) any later version.                                   *
21
 *                                                                         *
22
 ***************************************************************************/
23
 
12269 cbradney 24
#include <QDebug>
9803 fschmid 25
#include <QList>
13951 fschmid 26
#include <QTransform>
12269 cbradney 27
#include <QPalette>
10220 cbradney 28
#include <QPoint>
12269 cbradney 29
#include <QPolygon>
10220 cbradney 30
#include <QRegion>
12269 cbradney 31
#include <cassert>
3614 cbradney 32
 
16736 jghali 33
#include "scconfig.h"
12269 cbradney 34
 
10532 avox 35
#include "canvas.h"
10220 cbradney 36
#include "commonstrings.h"
3829 cbradney 37
#include "hyphenator.h"
3614 cbradney 38
#include "pageitem.h"
16943 fschmid 39
#include "pageitem_group.h"
10220 cbradney 40
#include "pageitem_textframe.h"
3614 cbradney 41
#include "prefsmanager.h"
16736 jghali 42
#include "scpage.h"
10601 mrdocs 43
#include "scpainter.h"
3614 cbradney 44
#include "scpaths.h"
10601 mrdocs 45
#include "scraction.h"
3614 cbradney 46
#include "scribus.h"
10220 cbradney 47
#include "scribusdoc.h"
3614 cbradney 48
#include "scribusstructs.h"
4145 cbradney 49
#include "selection.h"
10220 cbradney 50
#include "text/nlsconfig.h"
3614 cbradney 51
#include "undomanager.h"
52
#include "undostate.h"
10601 mrdocs 53
#include "util.h"
10203 cbradney 54
#include "util_math.h"
16736 jghali 55
 
56
#include "ui/guidemanager.h"
57
 
16281 jghali 58
#include <cairo.h>
3614 cbradney 59
 
60
using namespace std;
61
 
62
PageItem_TextFrame::PageItem_TextFrame(ScribusDoc *pa, double x, double y, double w, double h, double w2, QString fill, QString outline)
63
        : PageItem(pa, PageItem::TextFrame, x, y, w, h, w2, fill, outline)
64
{
9816 avox 65
        invalid = true;
66
        firstChar = 0;
8931 cbradney 67
        cursorBiasBackward = false;
3829 cbradney 68
        unicodeTextEditMode = false;
69
        unicodeInputCount = 0;
70
        unicodeInputString = "";
13148 pierre 71
 
72
        connect(&itemText,SIGNAL(changed()), this, SLOT(slotInvalidateLayout()));
3614 cbradney 73
}
74
 
12839 fschmid 75
PageItem_TextFrame::PageItem_TextFrame(const PageItem & p) : PageItem(p)
76
{
77
        invalid = true;
78
        cursorBiasBackward = false;
79
        unicodeTextEditMode = false;
80
        unicodeInputCount = 0;
81
        unicodeInputString = "";
13148 pierre 82
 
83
        connect(&itemText,SIGNAL(changed()), this, SLOT(slotInvalidateLayout()));
12839 fschmid 84
}
5184 avox 85
 
10701 avox 86
static QRegion itemShape(PageItem* docItem, double xOffset, double yOffset)
5184 avox 87
{
88
        QRegion res;
13951 fschmid 89
        QTransform pp;
5184 avox 90
        pp.translate(docItem->xPos() - xOffset, docItem->yPos() - yOffset);
91
        pp.rotate(docItem->rotation());
92
        if (docItem->textFlowUsesBoundingBox())
93
        {
17038 fschmid 94
                QRectF bb = docItem->getVisualBoundingRect();
17132 fschmid 95
                res = QRegion(bb.toRect());
5184 avox 96
        }
8445 fschmid 97
        else if ((docItem->textFlowUsesImageClipping()) && (docItem->imageClip.size() != 0))
5184 avox 98
        {
9803 fschmid 99
                QList<uint> Segs;
10284 fschmid 100
                QPolygon Clip2 = FlattenPath(docItem->imageClip, Segs);
9668 fschmid 101
                res = QRegion(pp.map(Clip2)).intersect(QRegion(pp.map(docItem->Clip)));
5184 avox 102
        }
8445 fschmid 103
        else if ((docItem->textFlowUsesContourLine()) && (docItem->ContourLine.size() != 0))
104
        {
9803 fschmid 105
                QList<uint> Segs;
10284 fschmid 106
                QPolygon Clip2 = FlattenPath(docItem->ContourLine, Segs);
9372 fschmid 107
                res = QRegion(pp.map(Clip2));
8445 fschmid 108
        }
109
        else
17038 fschmid 110
        {
17061 fschmid 111
                if (docItem->isSymbol() || docItem->isGroup())
112
                {
113
                        if (docItem->imageFlippedH())
114
                        {
115
                                pp.translate(docItem->width(), 0);
116
                                pp.scale(-1, 1);
117
                        }
118
                        if (docItem->imageFlippedV())
119
                        {
120
                                pp.translate(0, docItem->height());
121
                                pp.scale(1, -1);
122
                        }
123
                }
17039 fschmid 124
                if ((((docItem->lineColor() != CommonStrings::None) || (!docItem->patternStrokeVal.isEmpty()) || (docItem->GrTypeStroke > 0)) && (docItem->lineWidth() > 1)) || (!docItem->NamedLStyle.isEmpty()))
17038 fschmid 125
                {
17039 fschmid 126
                        QVector<double> m_array;
127
                        QPainterPath ppa;
128
                        QPainterPath result;
129
                        if (docItem->itemType() == PageItem::PolyLine)
130
                                ppa = docItem->PoLine.toQPainterPath(false);
131
                        else
132
                                ppa = docItem->PoLine.toQPainterPath(true);
133
                        if (docItem->NamedLStyle.isEmpty())
17038 fschmid 134
                        {
135
                                QPainterPathStroker stroke;
17039 fschmid 136
                                stroke.setCapStyle(docItem->lineEnd());
137
                                stroke.setJoinStyle(docItem->lineJoin());
17038 fschmid 138
                                stroke.setDashPattern(Qt::SolidLine);
17039 fschmid 139
                                stroke.setWidth(docItem->lineWidth());
17038 fschmid 140
                                result = stroke.createStroke(ppa);
141
                        }
17039 fschmid 142
                        else
143
                        {
144
                                multiLine ml = docItem->doc()->MLineStyles[docItem->NamedLStyle];
145
                                int ind = ml.size()-1;
146
                                if ((ml[ind].Color != CommonStrings::None) && (ml[ind].Width != 0))
147
                                {
148
                                        QPainterPathStroker stroke;
149
                                        stroke.setCapStyle(static_cast<Qt::PenCapStyle>(ml[ind].LineEnd));
150
                                        stroke.setJoinStyle(static_cast<Qt::PenJoinStyle>(ml[ind].LineJoin));
151
                                        stroke.setDashPattern(Qt::SolidLine);
152
                                        stroke.setWidth(ml[ind].Width);
153
                                        result = stroke.createStroke(ppa);
154
                                }
155
                        }
156
                        res = QRegion(pp.map(docItem->Clip));
157
                        QList<QPolygonF> pl = result.toSubpathPolygons();
158
                        for (int b = 0; b < pl.count(); b++)
159
                        {
160
                                res = res.united(QRegion(pp.map(pl[b].toPolygon())));
161
                        }
17038 fschmid 162
                }
17039 fschmid 163
                else
164
                        res = QRegion(pp.map(docItem->Clip));
17038 fschmid 165
        }
5184 avox 166
        return  res;
167
}
168
 
16989 jghali 169
QRegion PageItem_TextFrame::availableRegion()
5184 avox 170
{
16989 jghali 171
        QRegion result(this->Clip);
5184 avox 172
        if (!isEmbedded)
173
        {
16989 jghali 174
                bool invertible(false);
175
                QTransform canvasToLocalMat;
176
                canvasToLocalMat.translate(Xpos, Ypos);
177
                canvasToLocalMat.rotate(Rot);
178
                canvasToLocalMat = canvasToLocalMat.inverted(&invertible);
179
 
180
                if (!invertible) return QRegion();
181
 
13875 jghali 182
                int LayerLev = m_Doc->layerLevelFromID(LayerID);
7181 cbradney 183
                uint docItemsCount=m_Doc->Items->count();
16729 fschmid 184
                ScPage* Mp=0;
185
                ScPage* Dp=0;
7181 cbradney 186
                PageItem* docItem=0;
187
                int LayerLevItem;
16943 fschmid 188
                QList<PageItem*> thisList;
5184 avox 189
                if (!OnMasterPage.isEmpty())
190
                {
5559 avox 191
                        if ((savedOwnPage == -1) || (savedOwnPage >= signed(m_Doc->Pages->count())))
5375 fschmid 192
                                return result;
7175 fschmid 193
                        Mp = m_Doc->MasterPages.at(m_Doc->MasterNames[OnMasterPage]);
194
                        Dp = m_Doc->Pages->at(savedOwnPage);
16943 fschmid 195
                        if (Parent != NULL)
196
                                thisList = this->asGroupFrame()->groupItemList;
197
                        else
198
                                thisList = m_Doc->MasterItems;
199
                        int thisid = thisList.indexOf(this);
9856 fschmid 200
                        for (int a = 0; a < m_Doc->MasterItems.count(); ++a)
5184 avox 201
                        {
7175 fschmid 202
                                docItem = m_Doc->MasterItems.at(a);
16943 fschmid 203
                                int did = m_Doc->MasterItems.indexOf(docItem);
13875 jghali 204
                                LayerLevItem = m_Doc->layerLevelFromID(docItem->LayerID);
16943 fschmid 205
                                if (((did > thisid) && (docItem->LayerID == LayerID)) || (LayerLevItem > LayerLev && m_Doc->layerFlow(docItem->LayerID)))
5184 avox 206
                                {
5620 jghali 207
                                        if (docItem->textFlowAroundObject())
16989 jghali 208
                                        {
209
                                                QRegion itemRgn = itemShape(docItem, Mp->xOffset() - Dp->xOffset(), Mp->yOffset() - Dp->yOffset());
210
                                                result = result.subtracted( canvasToLocalMat.map(itemRgn) );
211
                                        }
5387 avox 212
                                }
213
                        } // for all masterItems
10240 jghali 214
                        // (JG) #6009 : disable possible interaction between master text frames and normal frames
215
                        // which have the text flow option set
216
                        /*if (!m_Doc->masterPageMode())
5184 avox 217
                        {
218
                                for (uint a = 0; a < docItemsCount; ++a)
219
                                {
7175 fschmid 220
                                        docItem = m_Doc->Items->at(a);
221
                                        Mp = m_Doc->MasterPages.at(m_Doc->MasterNames[OnMasterPage]);
222
                                        Dp = m_Doc->Pages->at(OwnPage);
5620 jghali 223
                                        if ((docItem->textFlowAroundObject()) && (docItem->OwnPage == OwnPage))
5184 avox 224
                                        {
225
                                                result = result.subtract(itemShape(docItem, m_Doc->view(), Mp->xOffset() - Dp->xOffset(), Mp->yOffset() - Dp->yOffset()));
226
                                        }
227
                                } // for all docItems
10240 jghali 228
                        } // if (! masterPageMode) */
5387 avox 229
                } // if (!OnMasterPage.isEmpty())
5184 avox 230
                else
231
                {
16943 fschmid 232
                        int thisid = 0;
233
                        if (Parent != NULL)
17107 fschmid 234
                                thisid = Parent->asGroupFrame()->groupItemList.indexOf(this);
16943 fschmid 235
                        else
236
                                thisid = m_Doc->Items->indexOf(this);
5184 avox 237
                        for (uint a = 0; a < docItemsCount; ++a)
238
                        {
7175 fschmid 239
                                docItem = m_Doc->Items->at(a);
16943 fschmid 240
                                int did = m_Doc->Items->indexOf(docItem);
13875 jghali 241
                                LayerLevItem = m_Doc->layerLevelFromID(docItem->LayerID);
16943 fschmid 242
                                if (((did > thisid) && (docItem->LayerID == LayerID)) || (LayerLevItem > LayerLev && m_Doc->layerFlow(docItem->LayerID)))
5184 avox 243
                                {
11310 fschmid 244
                                        if (docItem->textFlowAroundObject())
16989 jghali 245
                                        {
246
                                                QRegion itemRgn = itemShape(docItem, 0, 0);
247
                                                result = result.subtracted( canvasToLocalMat.map(itemRgn) );
248
                                        }
5387 avox 249
                                }
250
                        } // for all docItems
11785 jghali 251
                } // if(OnMasterPage.isEmpty()          
5184 avox 252
        } // if(!Embedded)
253
        return result;
254
}
255
 
6144 avox 256
void PageItem_TextFrame::setShadow()
257
{
258
        if (OnMasterPage.isEmpty())
259
                return;
6366 avox 260
 
6144 avox 261
        QString newShadow = m_Doc->masterPageMode() ? OnMasterPage : QString::number(OwnPage);
262
        if (newShadow != currentShadow) {
6366 avox 263
                if (currentShadow == OnMasterPage) {
264
                        // masterpage was edited, clear all shadows
265
                        shadows.clear();
266
                }
6144 avox 267
                if (!shadows.contains(newShadow)) {
268
                        if (!shadows.contains(OnMasterPage)) {
269
                                shadows[OnMasterPage] = itemText;
7905 avox 270
//                              const ParagraphStyle& pstyle(shadows[OnMasterPage].paragraphStyle(0));
13085 jghali 271
//                              qDebug() << QString("Pageitem_Textframe: style of master: %1 align=%2").arg(pstyle.parent()).arg(pstyle.alignment());
272
//                              qDebug() << QString("Pageitem_Textframe: shadow itemText->%1").arg(OnMasterPage);
6144 avox 273
                        }
7887 avox 274
                        if (newShadow != OnMasterPage) {
275
                                shadows[newShadow] = shadows[OnMasterPage].copy();
276
//                              const ParagraphStyle& pstyle(shadows[newShadow].paragraphStyle(0));
13085 jghali 277
//                              qDebug() << QString("Pageitem_Textframe: style of shadow copy: %1 align=%2").arg(pstyle.parent()).arg(pstyle.alignment());
7887 avox 278
                        }
13085 jghali 279
//                      qDebug() << QString("Pageitem_Textframe: shadow %1<-%2").arg(newShadow).arg(OnMasterPage);
6144 avox 280
                }
281
                itemText = shadows[newShadow];
7887 avox 282
//              const ParagraphStyle& pstyle(itemText.paragraphStyle(0));
13085 jghali 283
//              qDebug() << QString("Pageitem_Textframe: style of shadow: %1 align=%2").arg(pstyle.parent()).arg(pstyle.alignment());
6144 avox 284
                invalid = true;
285
                currentShadow = newShadow;
286
        }
287
}
5291 avox 288
 
5778 avox 289
static void dumpIt(const ParagraphStyle& pstyle, QString indent = QString("->"))
290
{
10553 fschmid 291
        QString db = QString("%6%1/%2 @ %3: %4--%5 linespa%6: %7 align%8")
5778 avox 292
                   .arg(pstyle.name())
6733 avox 293
                   .arg(pstyle.parent())
5850 tsoots 294
                   .arg( (unsigned long int) &pstyle)
5778 avox 295
                   .arg(pstyle.leftMargin())
296
                   .arg(pstyle.rightMargin())
6805 avox 297
                   .arg(indent)
298
                   .arg(pstyle.lineSpacingMode())
299
                   .arg(pstyle.lineSpacing())
10553 fschmid 300
                   .arg(pstyle.alignment());
13085 jghali 301
        qDebug() << db;
5778 avox 302
        static QString more("  ");
6733 avox 303
        if (pstyle.hasParent())
304
                dumpIt(*dynamic_cast<const ParagraphStyle*>(pstyle.parentStyle()), more + indent);
5778 avox 305
}
306
 
8103 avox 307
static const bool legacy = true;
308
 
10553 fschmid 309
/*
7313 avox 310
static void layoutDropCap(GlyphLayout layout, double curX, double curY, double offsetX, double offsetY, double dropCapDrop)
311
{      
6370 avox 312
}
10553 fschmid 313
*/
6370 avox 314
 
8761 avox 315
/**
316
 Clones the tab fill char as often as necssary after all distances are known
317
 */
7905 avox 318
static void fillInTabLeaders(StoryText & itemText, LineSpec & curLine)
319
{
320
        // fill in tab leaders
321
        double xPos = curLine.x;
13178 jghali 322
        for (int ti= curLine.firstItem; ti <= curLine.lastItem; ++ti)
7905 avox 323
        {
324
                ScText * hl = itemText.item(ti);
9737 jghali 325
                if (hl->ch == SpecialChars::TAB)
7905 avox 326
                {
327
                        GlyphLayout * tglyph = hl->glyph.more;
328
 
329
                        if (!tglyph)
330
                                continue;
331
 
332
                        const CharStyle & charStyle(itemText.charStyle(ti));
333
                        double wt   = charStyle.font().glyphWidth(tglyph->glyph, charStyle.fontSize() * tglyph->scaleV / 10.0);
334
                        double len  = hl->glyph.xadvance;
335
                        int count   = static_cast<int>(len / wt);
336
                        double sPos = -len;
337
                        tglyph->xoffset = sPos;
13085 jghali 338
//                      qDebug() << QString("tab leaders: %1 %2 width=%3 count=%4").arg(sPos).arg(curLine.y).arg(wt).arg(count);
7905 avox 339
                        for (int cx = 1; cx < count; ++cx)
340
                        {
341
                                // clone fillchar
342
                                tglyph->grow();
343
                                *(tglyph->more) = *tglyph;
344
                                tglyph->more->more = NULL;
345
                                tglyph = tglyph->more;
346
                                tglyph->xoffset =  sPos + wt * cx;
347
                        }
348
                }
349
                xPos += hl->glyph.wide();
350
        }
351
}
352
 
353
 
7887 avox 354
 
7905 avox 355
enum TabStatus {
356
        TabNONE    = 0,
357
        TabLEFT    = TabNONE,
358
        TabRIGHT   = 1,
359
        TabPOINT   = 2,
360
        TabCOMMA   = 3,
361
        TabCENTER  = 4
7313 avox 362
};
6370 avox 363
 
8761 avox 364
 
365
/**
366
fields which describe what type of tab is currently active
367
 */
7313 avox 368
struct TabControl {
369
        bool   active;
7905 avox 370
        int    status;
7313 avox 371
        int    charIndex;
372
        double xPos;
7905 avox 373
        QChar  fillChar;
7313 avox 374
};
375
 
8761 avox 376
/**
377
fields which describe how the current line is placed into the frame
378
 */
7313 avox 379
struct LineControl {
380
        LineSpec line;
381
        int      itemsInLine;
382
        int      hyphenCount;
383
        double   colWidth;
8095 avox 384
        double   colGap;
7313 avox 385
        double   colLeft;
386
        double   colRight;
387
        int      column;
388
        bool     startOfCol;
15029 jghali 389
        bool     hasDropCap;
16868 jghali 390
        bool     afterOverflow;
391
        bool     addLine;
392
        bool     recalculateY;
393
        bool     lastInRowLine;
394
        bool     addLeftIndent;
395
        bool     wasFirstInRow;
396
        double   leftIndent;
397
        double   rightMargin;
398
        double   mustLineEnd;
399
        int      restartIndex;  //index of char where line computing should be restarted
400
        int      restartRowIndex;  //index of char where row of text is started
401
        double   restartX; //starting X position of line if must be restarted
402
        double   rowDesc;
403
 
7313 avox 404
        double   ascend;
405
        double   descend;
406
        double   width;
407
        double   xPos;
408
        double   yPos;
409
        int      breakIndex;
410
        double   breakXPos;
9047 avox 411
 
412
        double   maxShrink;
413
        double   maxStretch;
8095 avox 414
 
415
 
8761 avox 416
        /// remember frame dimensions and offsets
8095 avox 417
        void init(double w, double h, const MarginStruct& extra, double lCorr)
418
        {
419
                insets = extra;
420
                lineCorr = lCorr;
421
                frameWidth = w;
422
                frameHeight = h;
15056 jghali 423
                hasDropCap = false;
8095 avox 424
        }
425
 
8761 avox 426
        /// start at column 0
8095 avox 427
        void initColumns(double width, double gap)
428
        {
429
                column = 0;
430
                colWidth = width;
431
                colGap = gap;
432
        }
433
 
8761 avox 434
        /// move position to next column
16868 jghali 435
        void nextColumn(double asce = 0.0)
8095 avox 436
        {
437
                startOfCol = true;
438
                colLeft = (colWidth + colGap) * column + insets.Left + lineCorr;
16868 jghali 439
                //now colRight is REAL column right edge
8095 avox 440
                colRight = colLeft + colWidth;
8103 avox 441
                if (legacy)
16868 jghali 442
                        colRight += lineCorr;
8095 avox 443
                xPos = colLeft;
16868 jghali 444
                yPos = asce + insets.Top + lineCorr;
8095 avox 445
        }
446
 
447
        bool isEndOfCol(double morespace = 0)
448
        {
449
                return yPos + morespace + insets.Bottom + lineCorr > frameHeight;
450
        }
16868 jghali 451
 
8761 avox 452
        /**
453
                init fields for a new line at current position
454
         */
8095 avox 455
        void startLine(int first)
15029 jghali 456
        {
8095 avox 457
                itemsInLine = 0;
458
                line.x = xPos;
459
                line.y = yPos;
460
                line.firstItem = first;
16868 jghali 461
                line.lastItem = 0;
462
                line.ascent = 0.0;
463
                line.descent = 0.0;
464
                line.width = 0.0;
465
                line.naturalWidth = 0.0;
13211 jghali 466
                line.colLeft = colLeft;
8095 avox 467
                breakIndex = -1;
16868 jghali 468
                breakXPos = 0.0;
469
                maxShrink = 0.0;
470
                maxStretch = 0.0;
471
                width = 0.0;
472
                leftIndent = 0.0;
473
                rightMargin = 0.0;
474
                rowDesc = 0.0;
8095 avox 475
        }
476
 
9047 avox 477
 
478
        /// called when glyphs are placed on the line
479
        void rememberShrinkStretch(QChar ch, double wide, const ParagraphStyle& style)
480
        {
481
                if (SpecialChars::isExpandingSpace(ch))
482
                        maxShrink += (1 - style.minWordTracking()) * wide;
483
                else
484
                {
485
                        maxShrink += (1 - style.minGlyphExtension()) * wide;
486
                }
487
                maxStretch += (style.maxGlyphExtension() - 1) * wide;
12883 jghali 488
        }
9047 avox 489
 
8761 avox 490
        /// called when a possible break is passed
16868 jghali 491
        void rememberBreak(int index, double pos, double morespace = 0)
8095 avox 492
        {
16868 jghali 493
                if (pos > colRight - morespace)
9047 avox 494
                {
495
                        // only look for the first break behind the right edge
16868 jghali 496
                        //maxShrink = 0;
9047 avox 497
 
498
                        // check if we already have a better break
499
                        if (breakIndex >= 0)
500
                        {
501
                                double oldLooseness = qAbs(colRight - breakXPos);
502
 
503
                                double newLooseness = pos - colRight;
504
                                if (newLooseness >= oldLooseness)
505
                                        return;
506
                        }
507
                }
16868 jghali 508
                breakXPos = pos;
8095 avox 509
                breakIndex = index;
510
        }
511
 
8761 avox 512
        /// called when a mandatory break is found
15029 jghali 513
        void breakLine(const StoryText& itemText, const ParagraphStyle& style, FirstLineOffsetPolicy offsetPolicy, int last)
8095 avox 514
        {
515
                breakIndex = last;
516
                breakXPos  = line.x;
517
                for (int j = line.firstItem; j <= last; ++j)
518
                        if ( (itemText.item(j)->effects() & ScStyle_SuppressSpace) == 0)
519
                                breakXPos += itemText.item(j)->glyph.wide();
14554 jghali 520
                // #8194, #8717 : update line ascent and descent with sensible values
521
                // so that endOfLine() returns correct result
522
                updateHeightMetrics(itemText);
15029 jghali 523
                // #9060 : update line offset too
16868 jghali 524
                //updateLineOffset(itemText, style, offsetPolicy);
8095 avox 525
        }
16868 jghali 526
 
8761 avox 527
        /// use the last remembered break to set line width and itemrange
8095 avox 528
        void finishLine(double endX)
529
        {
530
                line.lastItem = breakIndex;
531
                line.naturalWidth = breakXPos - line.x;
532
                line.width = endX - line.x;
9047 avox 533
                maxShrink = maxStretch = 0;
8095 avox 534
        }
16868 jghali 535
 
536
        int restartRow(bool recalcY)
537
        {
538
                if (recalcY)
539
                        yPos++;
540
                recalculateY = recalcY;
541
                xPos = restartX = colLeft;
542
                startLine(restartRowIndex);
543
                addLeftIndent = true;
544
                afterOverflow = false;
545
                return restartRowIndex -1;
546
        }
547
 
548
        int restartLine(bool recalcY, bool add)
549
        {
550
                recalculateY = recalcY;
551
                addLine = add;
552
                xPos = restartX;
553
                startLine(restartIndex);
554
                afterOverflow = false;
555
                return restartIndex -1;
556
        }
8095 avox 557
 
558
        bool isEndOfLine(double moreSpace = 0)
559
        {
16868 jghali 560
                bool res;
8103 avox 561
                if (legacy)
16868 jghali 562
                        res = ceil(xPos + lineCorr - maxShrink) + ceil(moreSpace) >= floor(colRight);
8103 avox 563
                else
16868 jghali 564
                        res = ceil(xPos - maxShrink)  + ceil(moreSpace) >= floor(colRight);
565
                return res;
8095 avox 566
        }
567
 
16923 jghali 568
        /// Keep old endOfLine code for reference
569
        /*double endOfLine_old(const QRegion& shape, const QTransform& pf2, double morespace, int Yasc, int Ydesc)
8095 avox 570
        {
13402 jghali 571
                // if we aren't restricted further, we'll end at this maxX:
572
                double maxX = colRight - morespace;
16868 jghali 573
                if (legacy) maxX -= lineCorr;
13402 jghali 574
 
16868 jghali 575
                double StartX = floor(qMax(line.x, qMin(maxX,breakXPos-maxShrink-1))-1);
576
                int xPos  = static_cast<int>(ceil(maxX));
13402 jghali 577
 
16921 jghali 578
                QPoint  pt12 (xPos, Yasc);
579
                QPoint  pt22 (xPos, Ydesc);
580
                QRect   pt(pt12,pt22);
16923 jghali 581
                QRegion region;
13402 jghali 582
 
583
                double EndX2 = StartX;
16868 jghali 584
                double Interval = 0.25;
13402 jghali 585
                do {
16868 jghali 586
                        int xP = static_cast<int>(ceil(EndX2 + morespace));
587
                        pt.moveTopLeft(QPoint(xP, Yasc));
16921 jghali 588
                        region = QRegion(pf2.mapToPolygon(pt)).subtracted(shape);
589
                        if (!region.isEmpty())
16868 jghali 590
                                break;
13402 jghali 591
                        EndX2 += Interval;
16921 jghali 592
                } while ((EndX2 < maxX) && region.isEmpty());
13402 jghali 593
 
16868 jghali 594
                return qMin(EndX2, maxX);
16923 jghali 595
        }*/
596
 
597
        /// find x position where this line must end
16989 jghali 598
        double endOfLine(const QRegion& shape, double morespace, int yAsc, int yDesc)
16923 jghali 599
        {
600
                // if we aren't restricted further, we'll end at this maxX:
601
                double maxX = colRight - morespace;
602
                if (legacy) maxX -= lineCorr;
603
 
604
                double StartX = floor(qMax(line.x, qMin(maxX,breakXPos-maxShrink-1))-1);
605
                int xPos  = static_cast<int>(ceil(maxX));
606
 
607
                QPoint  pt12 (xPos, yAsc);
608
                QPoint  pt22 (xPos, yDesc);
609
 
610
                QPolygon p;
16989 jghali 611
                p.append (QPoint (StartX, yAsc));
612
                p.append (QPoint (StartX, yDesc));
613
                p.append (pt12);
614
                p.append (pt22);
16923 jghali 615
                // check if something gets in the way
616
                QRegion lineI = shape.intersected (p.boundingRect());
617
                // if the intersection only has 1 rectangle, then nothing gets in the way
618
                if (lineI.numRects() == 1)
619
                {
620
                        int   cPos = static_cast<int>(ceil(StartX + morespace));
621
                        QRect cRect (QPoint(cPos, yAsc), QPoint(cPos, yDesc));
16989 jghali 622
                        if (QRegion(cRect).subtracted(shape).isEmpty())
16923 jghali 623
                        {
624
                                QRect rect = lineI.rects().at(0);
16989 jghali 625
                                double  mx = qMax(rect.left(), rect.right()) /*- pf2.dx()*/;
626
                                int steps  = static_cast<int>((mx - StartX - morespace - 2) / 0.25);
16923 jghali 627
                                if (steps > 0)
628
                                {
629
                                        StartX += steps * 0.25;
630
                                }
631
                        }
632
                }
633
 
634
                QRect   pt(pt12, pt22);
635
 
636
                double EndX2 = StartX;
637
                double Interval = 0.25;
638
                do {
639
                        int xP = static_cast<int>(ceil(EndX2 + morespace));
640
                        pt.moveTopLeft(QPoint(xP, yAsc));
17044 jghali 641
                        if (!regionContainsRect(shape, pt))
16923 jghali 642
                                break;
643
                        EndX2 += Interval;
17044 jghali 644
                } while ((EndX2 < maxX) && regionContainsRect(shape, pt));
16923 jghali 645
 
646
                /*double oldEndX2 = endOfLine_old(shape, pf2, morespace, yAsc, yDesc);
647
                if (oldEndX2 != qMin(EndX2, maxX))
648
                {
649
                        qDebug() << "Different EndX : " << oldEndX2 << " (old) " << EndX2 << " (new) ";
650
                }*/
651
 
652
                return qMin(EndX2, maxX);
8095 avox 653
        }
654
 
8956 avox 655
        double getLineAscent(const StoryText& itemText)
656
        {
657
                double result = 0;
16840 jghali 658
                QChar firstChar = itemText.text (line.firstItem);
659
                if ((firstChar == SpecialChars::PAGENUMBER) || (firstChar == SpecialChars::PAGECOUNT))
660
                        firstChar = '8';
661
                PageItem *obj = itemText.object (line.firstItem);
16656 jghali 662
                const CharStyle& fcStyle(itemText.charStyle(line.firstItem));
16840 jghali 663
                if ((firstChar == SpecialChars::PARSEP) || (firstChar == SpecialChars::LINEBREAK))
16656 jghali 664
                        result = fcStyle.font().ascent(fcStyle.fontSize() / 10.0);
16840 jghali 665
                else if (obj)
666
                        result = qMax(result, (obj->gHeight + obj->lineWidth()) * (fcStyle.scaleV() / 1000.0));
667
                else
668
                        result = fcStyle.font().realCharAscent(firstChar, fcStyle.fontSize() / 10.0);
8956 avox 669
                for (int zc = 0; zc < itemsInLine; ++zc)
670
                {
671
                        QChar ch = itemText.text(line.firstItem + zc);
11713 fschmid 672
                        if ((ch == SpecialChars::PAGENUMBER) || (ch == SpecialChars::PAGECOUNT))
8956 avox 673
                                ch = '8'; // should have highest ascender even in oldstyle
674
                        const CharStyle& cStyle(itemText.charStyle(line.firstItem + zc));
675
                        if ((ch == SpecialChars::TAB) || (ch == QChar(10))
16840 jghali 676
                                || SpecialChars::isBreak (ch, true) || (ch == SpecialChars::NBHYPHEN) || (ch.isSpace()))
8956 avox 677
                                continue;
678
                        double asce;
16840 jghali 679
                        PageItem *obj = itemText.object (line.firstItem + zc);
680
                        if (obj)
681
                                asce = obj->gHeight + obj->lineWidth() * (cStyle.scaleV() / 1000.0);
682
                        else
8956 avox 683
                                asce = cStyle.font().realCharAscent(ch, cStyle.fontSize() / 10.0);
13085 jghali 684
                        //      qDebug() << QString("checking char 'x%2' with ascender %1 > %3").arg(asce).arg(ch.unicode()).arg(result);
10394 cbradney 685
                        result = qMax(result, asce);
8956 avox 686
                }
687
                return result;
688
        }
14554 jghali 689
 
690
        double getLineDescent(const StoryText& itemText)
691
        {
692
                double result = 0;
16656 jghali 693
                QChar  firstChar = itemText.text(line.firstItem);
16840 jghali 694
                if ((firstChar == SpecialChars::PAGENUMBER) || (firstChar == SpecialChars::PAGECOUNT))
695
                        firstChar = '8';
16656 jghali 696
                const CharStyle& fcStyle(itemText.charStyle(line.firstItem));
697
                if ((firstChar == SpecialChars::PARSEP) || (firstChar == SpecialChars::LINEBREAK))
698
                        result = fcStyle.font().descent(fcStyle.fontSize() / 10.0);
16840 jghali 699
                else if (itemText.object(line.firstItem))
14554 jghali 700
                        result = 0.0;
16840 jghali 701
                else
16656 jghali 702
                        result = fcStyle.font().realCharDescent(firstChar, fcStyle.fontSize() / 10.0);
14554 jghali 703
                for (int zc = 0; zc < itemsInLine; ++zc)
704
                {
705
                        QChar ch = itemText.text(line.firstItem + zc);
706
                        if ((ch == SpecialChars::PAGENUMBER) || (ch == SpecialChars::PAGECOUNT))
707
                                ch = '8'; // should have highest ascender even in oldstyle
708
                        const CharStyle& cStyle(itemText.charStyle(line.firstItem + zc));
709
                        if ((ch == SpecialChars::TAB) || (ch == QChar(10))
16840 jghali 710
                                || SpecialChars::isBreak (ch, true) || (ch == SpecialChars::NBHYPHEN) || (ch.isSpace()))
14554 jghali 711
                                continue;
712
                        double desc;
16840 jghali 713
                        if (itemText.object(line.firstItem + zc))
14554 jghali 714
                                desc = 0.0;
16840 jghali 715
                        else
14554 jghali 716
                                desc = cStyle.font().realCharDescent(ch, cStyle.fontSize() / 10.0);
717
                        //      qDebug() << QString("checking char 'x%2' with ascender %1 > %3").arg(asce).arg(ch.unicode()).arg(result);
718
                        result = qMax(result, desc);
719
                }
720
                return result;
721
        }
8956 avox 722
 
723
        double getLineHeight(const StoryText& itemText)
724
        {
725
                double result = 0;
16840 jghali 726
                const CharStyle& firstStyle(itemText.charStyle(line.firstItem));
727
                PageItem *obj = itemText.object (line.firstItem);
728
                if (obj)
729
                        result = qMax(result, (obj->gHeight + obj->lineWidth()) * (firstStyle.scaleV() / 1000.0));
730
                else
731
                        result = firstStyle.font().height(firstStyle.fontSize() / 10.0);
8956 avox 732
                for (int zc = 0; zc < itemsInLine; ++zc)
733
                {
734
                        QChar ch = itemText.text(line.firstItem+zc);
735
                        if ((ch == SpecialChars::TAB) || (ch == QChar(10))
16840 jghali 736
                                || SpecialChars::isBreak (ch, true) || (ch == SpecialChars::NBHYPHEN) || (ch.isSpace()))
8956 avox 737
                                continue;
16840 jghali 738
                        const CharStyle& cStyle(itemText.charStyle(line.firstItem + zc));
739
                        PageItem *obj = itemText.object (line.firstItem + zc);
8956 avox 740
                        double asce;
16840 jghali 741
                        if (obj)
742
                                asce = (obj->gHeight + obj->lineWidth()) * (cStyle.scaleV() / 1000.0);
743
                        else
744
                                asce = cStyle.font().height (cStyle.fontSize() / 10.0);
13085 jghali 745
                        //      qDebug() << QString("checking char 'x%2' with ascender %1 > %3").arg(asce).arg(ch.unicode()).arg(result);
10394 cbradney 746
                        result = qMax(result, asce);
8956 avox 747
                }
748
                return result;
749
        }
14554 jghali 750
 
751
        void updateHeightMetrics(const StoryText& itemText)
752
        {
753
                double asce, desc;
754
                line.ascent  = 0;
755
                line.descent = 0;
756
                for (int zc = 0; zc < itemsInLine; ++zc)
757
                {
758
                        QChar ch = itemText.text(line.firstItem+zc);
759
                        if ((ch == SpecialChars::TAB) || (ch == QChar(10))
16840 jghali 760
                                || SpecialChars::isBreak (ch, true) || (ch == SpecialChars::NBHYPHEN) || (ch.isSpace()))
14554 jghali 761
                                continue;
762
                        const CharStyle& cStyle(itemText.charStyle(line.firstItem + zc));
16868 jghali 763
                        double scaleV = cStyle.scaleV() / 1000.0;
764
                        double offset = (cStyle.fontSize() / 10) * (cStyle.baselineOffset() / 1000.0);
765
 
14554 jghali 766
                        if (itemText.object(line.firstItem+zc) != 0)
767
                        {
16868 jghali 768
                                asce = (itemText.object(line.firstItem+zc)->gHeight + itemText.object(line.firstItem+zc)->lineWidth()) * scaleV + offset;
14554 jghali 769
                                desc = 0.0;
770
                        }
771
                        else //if (itemText.charStyle(current.line.firstItem+zc).effects() & ScStyle_DropCap == 0)
772
                        {
16868 jghali 773
                                asce = cStyle.font().realCharAscent(ch, cStyle.fontSize() / 10.0) * scaleV + offset;
774
                                desc = cStyle.font().realCharDescent(ch, cStyle.fontSize() / 10.0) * scaleV - offset;
14554 jghali 775
                        }
776
                        //      qDebug() << QString("checking char 'x%2' with ascender %1 > %3").arg(asce).arg(ch.unicode()).arg(result);
777
                        line.ascent  = qMax(line.ascent, asce);
778
                        line.descent = qMax(line.descent, desc);
779
                }
780
        }
16868 jghali 781
 
782
// yPos should not be changed when all line is already calculated - at new y position there can be overflow!!!
783
//
784
//      void updateLineOffset(const StoryText& itemText, const ParagraphStyle& style, FirstLineOffsetPolicy offsetPolicy)
785
//      {
786
//              if (itemsInLine <= 0)
787
//                      return;
788
//              if ((!hasDropCap) && (startOfCol) && (style.lineSpacingMode() != ParagraphStyle::BaselineGridLineSpacing))
789
//              {
790
//                      //FIXME: use glyphs, not chars
791
//                      double firstasce = itemText.charStyle(line.firstItem).font().ascent(itemText.charStyle(line.firstItem).fontSize() / 10.0);
792
//                      double adj (0.0);
793
//                      double currasce (this->getLineAscent(itemText));
794
//                      if( offsetPolicy == FLOPRealGlyphHeight )
795
//                      {
796
//                              adj = firstasce - currasce;
797
//                      }
798
//                      else if( offsetPolicy == FLOPFontAscent )
799
//                      {
800
//                              adj = 0.0;
801
//                      }
802
//                      else if( offsetPolicy == FLOPLineSpacing )
803
//                      {
804
//                              adj = firstasce - style.lineSpacing();
805
//                      }
806
//                      line.ascent = currasce;
807
//                      line.y -= adj;
808
//                      yPos -= adj;
809
//              }
810
//              else if ((!startOfCol) && (style.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing))
811
//              {
812
//                      QChar ch = itemText.text(line.firstItem);
813
//                      double firstasce = style.lineSpacing();
814
//                      double currasce  = getLineHeight(itemText);                                             
815
//                      double adj = firstasce - currasce;
15029 jghali 816
//                      qDebug() << QString("move2 line %1.. down by %2").arg(current.line.firstItem).arg(-adj);
16868 jghali 817
//                      line.ascent = currasce;
818
//                      line.y -= adj;
819
//                      yPos -= adj;
820
//              }
821
//      }
15029 jghali 822
 
8095 avox 823
private:
824
        double frameWidth;
825
        double frameHeight;
826
        MarginStruct insets;
827
        double lineCorr;
7313 avox 828
};
829
 
16876 jghali 830
static int checkCJK(QChar ch) {
831
        unsigned int code = ch.unicode();
832
        if (    (0x2E80 < code && code < 0x2EFF) ||   // CJK Radicals Supplement
833
                (0x3000 < code && code < 0x303F) ||   // CJK Symbols and Punctuation
834
                (0x31C0 < code && code < 0x31EF) ||   // CJK Strokes
835
                (0x3200 < code && code < 0x32FF) ||   // Enclosed CJK Letters and Months
836
                (0x3300 < code && code < 0x33FF) ||   // CJK Compatibility
837
                (0x3400 < code && code < 0x4DBF) ||   // CJK Unified Ideographs Extension A
838
                (0x4E00 < code && code < 0x9FFF) ||   // CJK Unified Ideographs
839
                (0xF900 < code && code < 0xFAFF) ||   // CJK Compatibility Ideographs
840
                (0xFE30 < code && code < 0xFE4F) ||   // CJK Compatibility Forms
841
                (0x20000 < code && code < 0x2A6DF) || // CJK Unified Ideographs Extension B
842
                (0x2A700 < code && code < 0x2B73F) || // CJK Unified Ideographs Extension C
843
                (0x2B740 < code && code < 0x2B81F) || // CJK Unified Ideographs Extension D
844
                (0x2F800 < code && code < 0x2FA1F) || // CJK Compatibility Ideographs Supplement
845
                (0xFF01 < code && code < 0xFF0F) ||
846
                (0xFF1A < code && code < 0xFF20) ||
847
                (0xFF58 < code && code < 0xFFDC) ||
848
                (code == 0x3000) ||
849
                (code == 0x3002) ||
850
                (code == 0x201C) ||
851
                (code == 0x201D))
852
                return 1;
853
        else
854
                return 0;
855
}
7313 avox 856
 
16876 jghali 857
static int checkCJKBreakAfter(QChar ch) {
858
        unsigned int code[] = {0x201C, 0xFF08, 0xFF3B, 0xFF5B, 0xFF5F, 0xFF62, 0xFF0D, 0};
859
        for (int i = 0; code[i]; ++i)
860
                if (code[i] == ch.unicode())
861
                        return 0;
862
        return 1;
863
}
8095 avox 864
 
16876 jghali 865
static int checkCJKBreakBefore(QChar ch) {
866
        unsigned int code[] =
867
         {0x201D, 0x3002, 0xFF01, 0xFF09, 0xFF0C, 0xFF0E, 0xFF1A,
868
          0xFF1B, 0xFF1F, 0xFF3D, 0xFF5D, 0xFF60, 0xFF63, 0xFF64, 0};
869
        for (int i = 0; code[i]; ++i)
870
                if (code[i] == ch.unicode())
871
                        return 0;
872
        return 1;
873
}
874
 
875
static int implicitSpace(ScText *f, ScText *s) {
876
        if (checkCJK(f->ch) && checkCJK(s->ch))
877
                return 1;
878
        else
879
                return 0;
880
}
881
 
882
static int implicitBreak(ScText *f, ScText *s) {
883
        if (checkCJK(f->ch) && checkCJK(s->ch)) {
884
                if (!checkCJKBreakAfter(f->ch)
885
                 || !checkCJKBreakBefore(s->ch))
886
                        return 0;
887
                return 1;
888
        } else
889
                return 0;
890
}
891
 
8761 avox 892
/// called when line length is known and line is to be justified
8095 avox 893
static void justifyLine(StoryText& itemText, LineSpec& line)
894
{
8761 avox 895
 
9047 avox 896
        double glyphNatural = 0;
897
        double spaceNatural = 0;
898
        double glyphExtension;
899
        double spaceExtension;
16876 jghali 900
        int spaceInsertion = 0;
901
        double imSpace = -1;
9047 avox 902
 
903
        const ParagraphStyle& style(itemText.paragraphStyle(line.firstItem));
904
 
905
        // measure natural widths for glyphs and spaces
9054 avox 906
        for (int sof = line.firstItem; sof <= line.lastItem; ++sof)
8095 avox 907
        {
9047 avox 908
                if (!SpecialChars::isExpandingSpace(itemText.text(sof)))
909
                {
910
                        glyphNatural += itemText.item(sof)->glyph.wide();
911
                }
912
                else if ( (itemText.item(sof)->effects() & ScStyle_SuppressSpace) == 0)
913
                {
914
                        spaceNatural += itemText.item(sof)->glyph.wide();
16876 jghali 915
                        if (imSpace < 0.0 || imSpace > itemText.item(sof)->glyph.wide())
916
                                imSpace = itemText.item(sof)->glyph.wide();
9047 avox 917
                }
16876 jghali 918
                if (sof != line.firstItem && implicitSpace(itemText.item(sof - 1), itemText.item(sof))) {
919
                        spaceInsertion += 1;
920
                }
8095 avox 921
        }
9047 avox 922
 
16876 jghali 923
        imSpace /= 2;
924
 
925
        // decision: prio 1: stretch glyph;  prio 2: insert spaces;  prio 3: stretch spaces
9047 avox 926
 
927
        if (line.width < spaceNatural + glyphNatural * style.minGlyphExtension() && spaceNatural > 0)
928
        {
929
                glyphExtension = style.minGlyphExtension() - 1;
930
                spaceExtension = (line.width - glyphNatural * (1+glyphExtension) ) / spaceNatural  - 1;
16876 jghali 931
                imSpace = 0;
9047 avox 932
        }
933
        else if (line.width < spaceNatural + glyphNatural * style.maxGlyphExtension() && glyphNatural > 0)
934
        {
935
                spaceExtension = 0;
936
                glyphExtension = (line.width - spaceNatural) / glyphNatural  - 1;
16876 jghali 937
                imSpace = 0;
9047 avox 938
        }
8095 avox 939
        else
9047 avox 940
        {
941
                glyphExtension = style.maxGlyphExtension() - 1;
16876 jghali 942
                if (spaceInsertion) {
943
                        double remaining = line.width - glyphNatural * (1 + glyphExtension) - spaceNatural;
944
                        if (imSpace > 0) {
945
                                if (remaining / spaceInsertion < imSpace) {
946
                                        imSpace = remaining / spaceInsertion;
947
                                        spaceExtension = 0;
948
                                } else {
949
                                        spaceExtension = (remaining + spaceNatural) / (spaceNatural + spaceInsertion * imSpace) - 1;
950
                                        imSpace *= spaceExtension + 1;
951
                                }
952
                        } else {
953
                                imSpace = remaining / spaceInsertion;
954
                                spaceExtension = 0;
955
                        }
956
                } else {
957
                        if (spaceNatural > 0)
958
                                spaceExtension = (line.width - glyphNatural * (1+glyphExtension) ) / spaceNatural  - 1;
959
                        else
960
                                spaceExtension = 0;
961
                }
9047 avox 962
        }
8095 avox 963
 
9047 avox 964
        double glyphScale = 1 + glyphExtension;
965
 
966
/*
13085 jghali 967
        qDebug() << QString("justify: line = %7 natural = %1 + %2 = %3 (%4); spaces + %5%%; min=%8; glyphs + %6%%; min=%9")
9047 avox 968
                   .arg(spaceNatural).arg(glyphNatural).arg(spaceNatural+glyphNatural).arg(line.naturalWidth)
969
                   .arg(spaceExtension).arg(glyphExtension).arg(line.width)
13085 jghali 970
                   .arg(style.minWordTracking()).arg(style.minGlyphExtension());
9047 avox 971
        */
972
 
16868 jghali 973
        int startItem = line.firstItem;
974
        if (itemText.item(startItem)->effects() & ScStyle_DropCap)
975
                startItem++;
9047 avox 976
        // distribute whitespace on spaces and glyphs
16868 jghali 977
        for (int yof = startItem; yof <= line.lastItem; ++yof)
8095 avox 978
        {
9047 avox 979
                double wide = itemText.item(yof)->glyph.wide();
980
                if (!SpecialChars::isExpandingSpace(itemText.text(yof)))
8095 avox 981
                {
9047 avox 982
                        itemText.item(yof)->glyph.last()->xadvance += wide * glyphExtension;
983
                        GlyphLayout* glyph = &(itemText.item(yof)->glyph);
984
                        while (glyph)
985
                        {
986
                                glyph->xoffset *= glyphScale;
987
                                glyph->scaleH *= glyphScale;
988
                                glyph = glyph->more;
989
                        }
8095 avox 990
                }
9047 avox 991
                else if ((itemText.item(yof)->effects() & ScStyle_SuppressSpace) == 0)
992
                {
993
                        itemText.item(yof)->glyph.last()->xadvance += wide * spaceExtension;
994
                }
16876 jghali 995
                if (yof != line.firstItem && implicitSpace(itemText.item(yof - 1), itemText.item(yof))) {
996
                        itemText.item(yof - 1)->glyph.last()->xadvance += imSpace;
997
                }
8095 avox 998
        }
999
}
1000
 
1001
 
9047 avox 1002
/// called when linelength is known and line is not justified
1003
static void indentLine(StoryText& itemText, LineSpec& line, double leftIndent)
8095 avox 1004
{
9047 avox 1005
        if (line.naturalWidth > line.width)
1006
        {
1007
                justifyLine(itemText, line);
1008
        }
1009
        if (leftIndent > 0)
1010
        {
1011
                line.x += leftIndent;
1012
                line.width -= leftIndent;
1013
        }
8095 avox 1014
}
1015
 
16868 jghali 1016
//defined but not used
1017
///// calculate how much the first char should stick out to the left
1018
//static double opticalLeftMargin(const StoryText& itemText, const LineSpec& line)
1019
//{
1020
//      int b = line.firstItem;
1021
//      while (b < line.lastItem && (itemText.item(b)->effects() & ScStyle_SuppressSpace))
1022
//                 ++b;
1023
//      
1024
//      double chs = itemText.charStyle(b).fontSize() * (itemText.charStyle(b).scaleH() / 1000.0);
1025
//      QChar chr = itemText.text(b);
1026
//      double leftCorr = itemText.charStyle(b).font().realCharWidth(chr, chs / 10.0);
1027
//      if (QString("'´`").indexOf(chr) >= 0
1028
//              || chr == QChar(0x2018) // quote 6
1029
//              || chr == QChar(0x2019) // quote 9
1030
//              || chr == QChar(0x201a) // lower quote 9
1031
//              || chr == QChar(0x201b) // upper reversed 9 6
1032
//              || chr == QChar(0x2039) // single guillemet <
1033
//              || chr == QChar(0x203a) // single guillemet >
1034
//              )
1035
//              leftCorr *= -0.7;
1036
//      else if (QString("\"").indexOf(chr) >= 0
1037
//                       || chr == QChar(0x00ab) // guillemet <<
1038
//                       || chr == QChar(0x00bb) // guillemet >>
1039
//                       || chr == QChar(0x201c) // quote 66
1040
//                       || chr == QChar(0x201d) // quote 99
1041
//                       || chr == QChar(0x201e) // lower quote 99
1042
//                       || chr == QChar(0x201f) // upper reversed 99
1043
//                       ) 
1044
//              leftCorr *= -0.5;
1045
//      else {
1046
//              leftCorr = itemText.charStyle(b).font().charWidth(QChar(' '), chs / 10.0, chr);
1047
//              leftCorr -= itemText.charStyle(b).font().charWidth(QChar(' '), chs / 10.0);
1048
////                            double leftCorr2 = itemText.charStyle(a).font().charWidth(QChar('K'), chs / 10.0, chr);
1049
////                            leftCorr2 -= itemText.charStyle(a).font().charWidth(QChar('K'), chs / 10.0);
1050
////                            leftCorr = qMin(leftCorr, leftCorr2);
1051
//      }
1052
//      return leftCorr;
1053
//}
8095 avox 1054
 
8761 avox 1055
/// calculate how much the last char should stick out to the right
8095 avox 1056
static double opticalRightMargin(const StoryText& itemText, const LineSpec& line)
1057
{
1058
        int b = line.lastItem;
1059
        while (b > line.firstItem &&
8177 avox 1060
                   (SpecialChars::isBreakingSpace(itemText.text(b)) || SpecialChars::isBreak(itemText.text(b)))
8095 avox 1061
                   )
1062
                --b;
1063
        if (b >= line.firstItem)
1064
        {
1065
                double chs = itemText.charStyle(b).fontSize() * (itemText.charStyle(b).scaleH() / 1000.0);
15009 jghali 1066
                QChar chr = (itemText.item(b)->effects() & ScStyle_SoftHyphenVisible) ?
8095 avox 1067
                        QChar('-') : itemText.text(b);
1068
                double rightCorr = itemText.charStyle(b).font().realCharWidth(chr, chs / 10.0);
10517 cbradney 1069
                if (QString("-,.`´'~").indexOf(chr) >= 0
8095 avox 1070
                        || chr == QChar(0x2018)
1071
                        || chr == QChar(0x2019)
1072
                        || chr == QChar(0x201a)
1073
                        || chr == QChar(0x201b)
1074
                        || chr == QChar(0x2039)
1075
                        || chr == QChar(0x203a)
8761 avox 1076
                        || chr == QChar(0x2032) // PRIME
8095 avox 1077
                        )
1078
                        rightCorr *= 0.7;
10517 cbradney 1079
                else if (QString(";:\"").indexOf(chr) >= 0
8095 avox 1080
                                 || chr == QChar(0x00ab)
1081
                                 || chr == QChar(0x00bb)
1082
                                 || chr == QChar(0x201c)
1083
                                 || chr == QChar(0x201d)
1084
                                 || chr == QChar(0x201e)
1085
                                 || chr == QChar(0x201f)
8761 avox 1086
                                 || chr == QChar(0x2013) // EN DASH
1087
                                 || chr == QChar(0x2033) // double prime
8095 avox 1088
                                 )
1089
                        rightCorr *= 0.5;
1090
                else {
1091
                        rightCorr = itemText.charStyle(b).font().charWidth(chr, chs / 10.0);
1092
                        rightCorr -= itemText.charStyle(b).font().charWidth(chr, chs / 10.0, QChar('.'));
1093
                }
1094
                return rightCorr;
1095
        }
1096
        return 0.0;
1097
}
1098
 
16989 jghali 1099
static double findRealOverflowEnd(const QRegion& shape, QRect pt, double maxX)
16868 jghali 1100
{
17044 jghali 1101
        while (!regionContainsRect(shape, pt) && pt.right() < maxX)
16868 jghali 1102
                pt.translate(1, 0);
1103
        if (pt.right() >= maxX)
1104
                return maxX;
16989 jghali 1105
        return pt.left() + 0.5;
16868 jghali 1106
}
1107
 
16840 jghali 1108
static double adjustToBaselineGrid (const LineControl &control, PageItem *item, int OwnPage)
1109
{
1110
        double by = item->yPos();
1111
        if (OwnPage != -1)
1112
                by = by - item->doc()->Pages->at(OwnPage)->yOffset();
1113
        int ol1 = qRound((by + control.yPos - item->doc()->guidesPrefs().offsetBaselineGrid) * 10000.0);
1114
        int ol2 = static_cast<int>(ol1 / item->doc()->guidesPrefs().valueBaselineGrid);
1115
//                                              qDebug() << QString("baseline adjust: y=%1->%2").arg(current.yPos).arg(ceil(  ol2 / 10000.0 ) * item->doc()->typographicSettings.valueBaselineGrid + item->doc()->typographicSettings.offsetBaselineGrid - by);
8095 avox 1116
 
16840 jghali 1117
        return ceil(  ol2 / 10000.0 ) * item->doc()->guidesPrefs().valueBaselineGrid + item->doc()->guidesPrefs().offsetBaselineGrid - by;
1118
}
8761 avox 1119
 
16840 jghali 1120
static double nextAutoTab (const LineControl &current, PageItem *item)
1121
{
1122
        double dtw = item->doc()->itemToolPrefs().textTabWidth;
1123
        if (current.xPos == current.colLeft)
1124
                return current.colLeft + dtw;
1125
        double res = current.colLeft + ceil ((current.xPos - current.colLeft) / dtw) * dtw;
1126
        if (res == current.xPos) res += dtw;
1127
        return res;
1128
}
8761 avox 1129
 
1130
 
16840 jghali 1131
static double calculateLineSpacing (const ParagraphStyle &style, PageItem *item)
1132
{
1133
        if (style.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing)
1134
                return style.charStyle().font().height(style.charStyle().fontSize() / 10.0);
1135
        if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
1136
                return item->doc()->guidesPrefs().valueBaselineGrid;
1137
        return style.lineSpacing();
1138
}
1139
 
1140
 
1141
// This assumes that layout() ran on the previous page and set the incomplete* vars
1142
// It also clears the incomplete* vars, and changes the starting position for this frame
1143
// The incomplete* vars are used to ensure that we don't run into an endless loop
1144
// This setup won't cause any problems, as the only way for the starting position to change
1145
// back is if the previous frame's layout() runs again, but if it does, it also sets the
1146
// incomplete* vars for us
1147
bool PageItem_TextFrame::moveLinesFromPreviousFrame ()
1148
{
1149
        PageItem_TextFrame* prev = dynamic_cast<PageItem_TextFrame*>(BackBox);
1150
        if (!prev) return false;
1151
        if (!prev->incompleteLines) return false;   // no incomplete lines - nothing to do
1152
        int pos = itemText.lastInFrame();
1153
        QChar lastChar = itemText.text (pos);
1154
        // qDebug()<<"pos is"<<pos<<", length is"<<itemText.length()<<", incomplete is "<<prev->incompleteLines;
1155
        if ((pos != itemText.length()-1) && (!SpecialChars::isBreak (lastChar, true)))
1156
                return false;  // the paragraph isn't ending yet
17039 fschmid 1157
        int lines = itemText.lines();  // lines added to the current frame
16840 jghali 1158
 
1159
        ParagraphStyle style = itemText.paragraphStyle (pos);
1160
        int need = style.keepLinesEnd () + 1;
1161
        int prevneed = style.keepLinesStart () + 1;
1162
        if (lines >= need) {
1163
                prev->incompleteLines = 0;   // so that further paragraphs don't pull anything
1164
                return false;   // we have enough lines
1165
        }
1166
 
1167
        // too few lines - we need to pull some from the previous page
17039 fschmid 1168
        int pull = need - lines;
16840 jghali 1169
        // if pulling the lines would lead to the original frame having too few, pull the whole paragraph
1170
        if (prev->incompleteLines - pull < prevneed)
1171
                pull = prev->incompleteLines;
1172
        // qDebug()<<"pulling"<<pull<<"lines;
1173
        // Okay, move the starting/ending character
1174
        int startingPos = prev->incompletePositions[prev->incompleteLines - pull];
17039 fschmid 1175
        for (int i = 0; i < pull; ++i)
16840 jghali 1176
                prev->itemText.removeLastLine();
1177
        firstChar = prev->MaxChars = startingPos;
1178
        // keep the remaining incomplete lines flagged as such
1179
        // this ensures that if pulling one line won't be enough, the subsequent call to layout() will pull more
1180
        prev->incompleteLines -= pull;
1181
 
1182
        return true;
1183
}
1184
 
1185
// called at the end of a frame or column
1186
void PageItem_TextFrame::adjustParagraphEndings ()
1187
{
1188
        // More text to go - let's apply paragraph flowing options - orphans/widows, etc
1189
        int pos = itemText.lastInFrame();
1190
        if (pos >= itemText.length() - 1) return;
1191
 
1192
        ParagraphStyle style = itemText.paragraphStyle (pos);
1193
        int paragraphStart = itemText.prevParagraph (pos) + 1;
1194
        QChar lastChar = itemText.text (pos);
1195
        bool keepWithNext = style.keepWithNext() && (lastChar == SpecialChars::PARSEP);
1196
        if (keepWithNext || (!SpecialChars::isBreak (lastChar, true))) {
1197
                // paragraph continues in the next frame, or needs to be kept with the next one
1198
                // check how many lines are in this frame
1199
                int lineStart = itemText.startOfLine (pos);
1200
                incompleteLines = 1;
1201
                incompletePositions.prepend (lineStart);
1202
                while (lineStart > paragraphStart) {
1203
                        lineStart = itemText.startOfLine (lineStart - 1);
1204
                        incompleteLines++;
1205
                        incompletePositions.prepend (lineStart);
1206
                }
1207
                int need = style.keepLinesStart () + 1;
1208
                if (style.keepTogether()) need = incompleteLines;
1209
                int pull = 0;
1210
                if (style.keepTogether() || (incompleteLines < need)) pull = incompleteLines;
1211
                // if we need to keep it with the next one, pull one line. Next frame layouting
1212
                // will pull more from us if it proves necessary.
1213
                if (keepWithNext && (!pull)) pull = 1;
1214
 
1215
                if (pull) {
1216
                        // push this paragraph to the next frame
1217
                        for (int i = 0; i < pull; ++i)
1218
                                itemText.removeLastLine();
1219
                        MaxChars = itemText.lastInFrame() + 1;
1220
                        incompleteLines = 0;
1221
                        incompletePositions.clear();
1222
                }
1223
        }
1224
}
1225
 
5362 avox 1226
void PageItem_TextFrame::layout()
3614 cbradney 1227
{
13045 jghali 1228
//      qDebug()<<"==Layout==" << itemName() ;
13034 pierre 1229
//      printBacktrace(24);
5732 avox 1230
        if (BackBox != NULL && BackBox->invalid) {
1231
//              qDebug("textframe: len=%d, going back", itemText.length());
16639 jghali 1232
                // Why that invalid = false here? Calling prevInChain->layout() does
1233
                // not ensure that this box will be layouted
1234
                // invalid = false;
8060 avox 1235
                PageItem_TextFrame* prevInChain = dynamic_cast<PageItem_TextFrame*>(BackBox);
16543 jghali 1236
                while (prevInChain && prevInChain->invalid)
1237
                {
1238
                        if (!prevInChain->BackBox || !prevInChain->BackBox->invalid)
1239
                                break;
1240
                        prevInChain = dynamic_cast<PageItem_TextFrame*>(prevInChain->BackBox);
1241
                }
1242
                if (prevInChain && prevInChain->invalid)
1243
                        prevInChain->layout();
16106 jghali 1244
                // #9592 : warning, BackBox->layout() may not layout BackBox next box
1245
                if (!invalid)
1246
                        return;
5362 avox 1247
        }
6370 avox 1248
        else if (!invalid && OnMasterPage.isEmpty()) {
13085 jghali 1249
//              qDebug() << QString("textframe: len=%1, invalid=%2 OnMasterPage=%3: no relayout").arg(itemText.length()).arg(invalid).arg(OnMasterPage);
5732 avox 1250
                return;
1251
        }
8334 avox 1252
        if (invalid && BackBox == NULL)
1253
                firstChar = 0;
1254
 
13085 jghali 1255
//      qDebug() << QString("textframe(%1,%2): len=%3, start relayout at %4").arg(Xpos).arg(Ypos).arg(itemText.length()).arg(firstInFrame());
5362 avox 1256
        QPoint pt1, pt2;
16868 jghali 1257
        QRect pt;
7969 avox 1258
        double chs, chsd = 0;
8095 avox 1259
        double oldCurY, EndX, OFs, wide, kernVal;
7313 avox 1260
        QString chstr;
5362 avox 1261
        ScText *hl;
6805 avox 1262
        ParagraphStyle style;
8095 avox 1263
        int /*ParagraphStyle::OpticalMarginType*/ opticalMargins = ParagraphStyle::OM_None;
5362 avox 1264
 
1265
        bool outs = false;
1266
        bool goNoRoom = false;
1267
        bool goNextColumn = false;
7905 avox 1268
 
1269
        TabControl tabs;
1270
        tabs.active    = false;     // RTab
1271
        tabs.status    = TabNONE;   // TabCode
1272
        tabs.charIndex = -1;        // StartRT
1273
        tabs.xPos      = 0;         // RTabX
1274
 
9803 fschmid 1275
        QList<ParagraphStyle::TabRecord> tTabValues;
7905 avox 1276
        tTabValues.clear();
1277
 
5362 avox 1278
        bool DropCmode = false;
16868 jghali 1279
        double desc=0, asce=0, realAsce=0, realDesc = 0;
1280
        double maxDY=0, maxDX=0;
6370 avox 1281
        double DropCapDrop = 0;
11776 jghali 1282
        int    DropLines = 0;
1283
        int    DropLinesCount = 0;
5362 avox 1284
 
6144 avox 1285
        itemText.clearLines();
16840 jghali 1286
        incompleteLines = 0;
1287
        incompletePositions.clear();
8095 avox 1288
 
1289
        double lineCorr = 0;
1290
        if (lineColor() != CommonStrings::None)
1291
                lineCorr = m_lineWidth / 2.0;
1292
 
1293
        // TODO: refactor this into PageItem
1294
        MarginStruct extra;
1295
        extra.Top = TExtra;
1296
        extra.Left = Extra;
1297
        extra.Right = RExtra;
1298
        extra.Bottom = BExtra;
1299
 
1300
        LineControl current;
1301
        current.init(Width, Height, extra, lineCorr);
1302
        current.initColumns(columnWidth(), ColGap);
1303
        current.hyphenCount = 0;
16868 jghali 1304
 
1305
        //hold Y position of last computed line of text (with glyphs descent)
1306
        //for moving next line if glyphs are higher than that
1307
        double lastLineY = 0;
1308
 
5778 avox 1309
        // dump styles
1310
/*     
1311
        for (int i=0; i < itemText.nrOfParagraphs(); ++i) {
1312
                const ParagraphStyle& pstyle(itemText.paragraphStyle(itemText.endOfParagraph(i)));
13085 jghali 1313
                qDebug("par %d:", i);
5778 avox 1314
                dumpIt(pstyle);
1315
        }
13085 jghali 1316
        qDebug() << "default:";
5778 avox 1317
        dumpIt(itemText.defaultStyle());
16989 jghali 1318
*/
5778 avox 1319
 
6144 avox 1320
        setShadow();
5732 avox 1321
        if ((itemText.length() != 0)) // || (NextBox != 0))
8095 avox 1322
        {
5408 avox 1323
                // determine layout area
16989 jghali 1324
                QRegion cl = availableRegion();
14384 jghali 1325
                if (cl.isEmpty())
1326
                {
1327
                        MaxChars = firstInFrame();
1328
                        goto NoRoom;
1329
                }
5362 avox 1330
 
16989 jghali 1331
                if (imageFlippedH() || imageFlippedV())
5362 avox 1332
                {
16989 jghali 1333
                        QTransform matrix;
1334
                        if (imageFlippedH())
1335
                        {
1336
                                matrix.translate(Width, 0);
1337
                                matrix.scale(-1, 1);
1338
                        }
1339
                        if (imageFlippedV())
1340
                        {
1341
                                matrix.translate(0, Height);
1342
                                matrix.scale(1, -1);
1343
                        }
1344
                        cl = matrix.map(cl);
5362 avox 1345
                }
5408 avox 1346
 
16868 jghali 1347
                current.nextColumn();
1348
                lastLineY = extra.Top;
8095 avox 1349
 
5559 avox 1350
                // find start of first line
7887 avox 1351
                if (firstInFrame() < itemText.length())
5362 avox 1352
                {
7887 avox 1353
                        hl = itemText.item(firstInFrame());
1354
                        style = itemText.paragraphStyle(firstInFrame());
16840 jghali 1355
                        style.setLineSpacing (calculateLineSpacing (style, this));
8956 avox 1356
 
13085 jghali 1357
//                      qDebug() << QString("style @0: %1 -- %2, %4/%5 char: %3").arg(style.leftMargin()).arg(style.rightMargin())
1358
//                                 .arg(style.charStyle().asString()).arg(style.name()).arg(style.parent()?style.parent()->name():"");
6805 avox 1359
                        if (style.hasDropCap())
3614 cbradney 1360
                        {
16840 jghali 1361
                                chs = calculateLineSpacing (style, this) * style.dropCapLines() * 10;
3614 cbradney 1362
                        }
6370 avox 1363
                        else
5691 avox 1364
                                chs = hl->fontSize();
16868 jghali 1365
                        desc = -hl->font().descent(chs / 10.0);
8095 avox 1366
                        current.yPos = extra.Top + lineCorr;
13085 jghali 1367
//                      qDebug() << QString("first line at y=%1").arg(current.yPos);
5362 avox 1368
                }
6370 avox 1369
                else // empty itemText:
5362 avox 1370
                {
16868 jghali 1371
                        desc = -itemText.defaultStyle().charStyle().font().descent(itemText.defaultStyle().charStyle().fontSize() / 10.0);
1372
                        current.yPos = itemText.defaultStyle().lineSpacing() + extra.Top + lineCorr - desc;
5362 avox 1373
                }
8095 avox 1374
                current.startLine(firstInFrame());
16868 jghali 1375
 
5362 avox 1376
                outs = false;
1377
                OFs = 0;
1378
                MaxChars = 0;
16868 jghali 1379
                double realEnd = 0;
1380
                current.restartIndex = current.restartRowIndex = firstInFrame();
1381
                current.afterOverflow = false;
1382
                current.addLine = false;
1383
                current.recalculateY = true;
1384
                current.lastInRowLine = false;
1385
                current.addLeftIndent = true;
1386
                current.wasFirstInRow = false;
1387
                current.leftIndent = 0;
1388
                current.rightMargin = 0.0;
1389
                current.mustLineEnd = current.colRight;
1390
                current.restartX = 0;
1391
 
5721 avox 1392
                for (int a = firstInFrame(); a < itemText.length(); ++a)
5362 avox 1393
                {
5721 avox 1394
                        hl = itemText.item(a);
8060 avox 1395
                        if (a > 0 && itemText.text(a-1) == SpecialChars::PARSEP)
1396
                                style = itemText.paragraphStyle(a);
8095 avox 1397
                        if (current.itemsInLine == 0)
8060 avox 1398
                                opticalMargins = style.opticalMargins();
1399
 
16998 fschmid 1400
                        CharStyle charStyle = (hl->ch != SpecialChars::PARSEP? itemText.charStyle(a) : style.charStyle());
16868 jghali 1401
                        chstr = ExpandToken(a);
1402
                        double hlcsize10 = charStyle.fontSize() / 10.0;
1403
                        double scaleV = charStyle.scaleV() / 1000.0;
1404
                        double scaleH = charStyle.scaleH() / 1000.0;
1405
                        double offset = hlcsize10 * (charStyle.baselineOffset() / 1000.0);
5721 avox 1406
 
6144 avox 1407
                        if (chstr.isEmpty())
8861 cbradney 1408
                                chstr = SpecialChars::ZWNBSPACE;
16840 jghali 1409
                        style.setLineSpacing (calculateLineSpacing (style, this));
5408 avox 1410
                        // find out about par gap and dropcap
5721 avox 1411
                        if (a == firstInFrame())
3614 cbradney 1412
                        {
8814 cbradney 1413
                                if (a == 0 || itemText.text(a-1) == SpecialChars::PARSEP)
5362 avox 1414
                                {
7313 avox 1415
                                        if (chstr[0] != SpecialChars::PARSEP)
5362 avox 1416
                                        {
5721 avox 1417
                                                DropCmode = style.hasDropCap();
5362 avox 1418
                                                if (DropCmode)
5721 avox 1419
                                                        DropLines = style.dropCapLines();
5362 avox 1420
                                        }
1421
                                        else
1422
                                                DropCmode = false;
1423
                                }
3614 cbradney 1424
                        }
16998 fschmid 1425
                        if (a == 0 || itemText.text(a-1) == SpecialChars::PARSEP)
1426
                        {
1427
                                if (style.hasDropCap())
1428
                                {
1429
                                        if (style.dcCharStyleName() == tr("No Style") || style.dcCharStyleName().isEmpty())
1430
                                        {
1431
                                                const QString& curParent(style.hasParent() ? style.parent() : style.name());
1432
                                                CharStyle newStyle;
1433
                                                newStyle.setParent(m_Doc->paragraphStyle(curParent).charStyle().name());
1434
                                                charStyle.setStyle(newStyle);
1435
                                        }
1436
                                        else if (charStyle.name() != style.dcCharStyleName())
1437
                                                charStyle.setStyle(m_Doc->charStyle(style.dcCharStyleName()));
1438
                                        itemText.setCharStyle(a, chstr.length(),charStyle);
1439
                                }
1440
                                else if (style.dcCharStyleName() != tr("No Style") && !style.dcCharStyleName().isEmpty())
1441
                                //hasDropCap is cleared but is set dcCharStyleName = clear drop cap char style
1442
                                {
1443
                                        const QString& curParent(style.hasParent() ? style.parent() : style.name());
1444
                                        CharStyle newStyle;
1445
                                        newStyle.setParent(m_Doc->paragraphStyle(curParent).charStyle().name());
1446
                                        charStyle.setStyle(newStyle);
1447
                                        itemText.setCharStyle(a, chstr.length(),charStyle);
1448
                                }
1449
                        }
8814 cbradney 1450
 
6370 avox 1451
                        {  // local block for 'fl'
5691 avox 1452
                                StyleFlag fl = hl->effects();
1453
                                fl &= ~ScStyle_DropCap;
15009 jghali 1454
                                fl &= ~ScStyle_SoftHyphenVisible;
5691 avox 1455
                                hl->setEffects(fl);
1456
                        }
8814 cbradney 1457
 
8150 avox 1458
                        // No space at begin of line, 
1459
                        if (legacy)
3614 cbradney 1460
                        {
8150 avox 1461
                                // unless at begin of par (eeks)
9737 jghali 1462
                                if ( (current.itemsInLine == 0) && (SpecialChars::isBreakingSpace(hl->ch))
8177 avox 1463
                                         && (a > 0 && ! SpecialChars::isBreak(itemText.text(a-1)))
1464
                                         && ! (a > 0 && SpecialChars::isBreakingSpace(itemText.text(a-1))
8150 avox 1465
                                                   && (itemText.charStyle(a-1).effects() & ScStyle_SuppressSpace) != ScStyle_SuppressSpace))
1466
                                {
1467
                                        hl->setEffects(hl->effects() | ScStyle_SuppressSpace);
1468
                                        hl->glyph.xadvance = 0;
1469
                                        continue;
1470
                                }
1471
                                else
1472
                                        hl->setEffects(hl->effects() & ~ScStyle_SuppressSpace);
5362 avox 1473
                        }
8150 avox 1474
                        else // from 134 on use NBSPACE for this effect
1475
                        {
12477 jghali 1476
                                if ( current.itemsInLine == 0 && (SpecialChars::isBreakingSpace(hl->ch) || hl->ch.isSpace()))
8150 avox 1477
                                {
1478
                                        hl->setEffects(hl->effects() | ScStyle_SuppressSpace);
1479
                                        hl->glyph.xadvance = 0;
1480
                                        continue;
1481
                                }
1482
                                else
1483
                                        hl->setEffects(hl->effects() & ~ScStyle_SuppressSpace);
1484
                        }
8095 avox 1485
                        if (current.itemsInLine == 0)
5362 avox 1486
                        {
16868 jghali 1487
                                if (style.rightMargin() == 0)
3614 cbradney 1488
                                {
16868 jghali 1489
                                        //addLine = true;
1490
                                        current.rightMargin = 0.0;
1491
                                }
1492
                                else
1493
                                {
1494
                                        if (current.lastInRowLine)
1495
                                                current.rightMargin = style.rightMargin();
1496
                                        else
1497
                                                current.rightMargin = 0.0;
1498
                                }
1499
                                current.breakIndex = -1;
1500
                                if (current.startOfCol && !current.afterOverflow && current.recalculateY)
17085 jghali 1501
                                        current.yPos = qMax(current.yPos, extra.Top);
16868 jghali 1502
                                // more about par gap and dropcaps
1503
                                if ((a > firstInFrame() && itemText.text(a-1) == SpecialChars::PARSEP) || (a == 0 && BackBox == 0 && current.startOfCol))
1504
                                {
1505
                                        if (!current.afterOverflow && current.recalculateY && !current.startOfCol)
1506
                                                current.yPos += style.gapBefore();
6370 avox 1507
                                        DropCapDrop = 0;
7313 avox 1508
                                        if (chstr[0] != SpecialChars::PARSEP)
5721 avox 1509
                                                DropCmode = style.hasDropCap();
5362 avox 1510
                                        else
1511
                                                DropCmode = false;
16868 jghali 1512
                                        if (DropCmode && !current.afterOverflow)
5362 avox 1513
                                        {
5721 avox 1514
                                                DropLines = style.dropCapLines();
16840 jghali 1515
                                                DropCapDrop = calculateLineSpacing (style, this) * (DropLines - 1);
13085 jghali 1516
//                                              qDebug() << QString("dropcapdrop: y=%1+%2").arg(current.yPos).arg(DropCapDrop);
5362 avox 1517
                                        }
3614 cbradney 1518
                                }
5362 avox 1519
                        }
16868 jghali 1520
                        // find charsize factors
5362 avox 1521
                        if (DropCmode)
1522
                        {
17140 jghali 1523
                                DropCapDrop = calculateLineSpacing (style, this) * (DropLines - 1);
1524
 
11767 jghali 1525
                                // FIXME : we should ensure that fonts are loaded before calls to layout()
1526
                                // ScFace::realCharHeight()/Ascent() ensure font is loaded thanks to an indirect call to char2CMap()
1527
                                // ScFace::ascent() can be called safely afterwards
1528
                                double realCharHeight = charStyle.font().realCharHeight(chstr[0], 1);
1529
                                double realCharAscent = charStyle.font().realCharAscent(chstr[0], 1);
1530
                                double fontAscent     = charStyle.font().ascent(style.charStyle().fontSize() / 10.0);
14929 jghali 1531
                                if (realCharHeight == 0.0)
1532
                                        realCharHeight = charStyle.font().height(style.charStyle().fontSize() / 10.0);
1533
                                if (realCharAscent == 0.0)
1534
                                        realCharAscent = fontAscent;
16840 jghali 1535
                                chsd = (10 * ((DropCapDrop + fontAscent) / realCharHeight));
1536
                                chs  = (10 * ((DropCapDrop + fontAscent) / realCharAscent));
5691 avox 1537
                                hl->setEffects(hl->effects() | ScStyle_DropCap);
6370 avox 1538
                                hl->glyph.yoffset -= DropCapDrop;
5362 avox 1539
                        }
5408 avox 1540
                        else // ! dropCapMode
5362 avox 1541
                        {
9737 jghali 1542
                                if ((hl->ch == SpecialChars::OBJECT) && (hl->embedded.hasItem()))
8177 avox 1543
                                        chs = qRound((hl->embedded.getItem()->gHeight + hl->embedded.getItem()->lineWidth()) * 10);
5362 avox 1544
                                else
6144 avox 1545
                                        chs = charStyle.fontSize();
5362 avox 1546
                        }
16868 jghali 1547
                        // set StartOfLine (and find tracking?)
8098 avox 1548
                        if (current.itemsInLine == 0)
1549
                        {
1550
                                itemText.item(a)->setEffects(itemText.item(a)->effects() | ScStyle_StartOfLine);
1551
                                kernVal = 0;
1552
                        }
1553
                        else
1554
                        {
1555
                                kernVal = 0; // chs * charStyle.tracking() / 10000.0;
1556
                                itemText.item(a)->setEffects(itemText.item(a)->effects() & ~ScStyle_StartOfLine);
1557
                        }
6144 avox 1558
                        hl->glyph.yadvance = 0;
5980 avox 1559
                        oldCurY = layoutGlyphs(*hl, chstr, hl->glyph);
16868 jghali 1560
                        // find out width, ascent and descent of char
9737 jghali 1561
                        if ((hl->ch == SpecialChars::OBJECT) && (hl->embedded.hasItem()))
14417 jghali 1562
                        {
8177 avox 1563
                                wide = hl->embedded.getItem()->gWidth + hl->embedded.getItem()->lineWidth();
14426 jghali 1564
                                hl->glyph.xadvance = wide * hl->glyph.scaleH;
14417 jghali 1565
                        }
5362 avox 1566
                        else
1567
                        {
6354 avox 1568
                                wide = hl->glyph.wide();
8814 cbradney 1569
                                // apply kerning
6354 avox 1570
                                if (a+1 < itemText.length())
1571
                                {
1572
                                        uint glyph2 = charStyle.font().char2CMap(itemText.text(a+1));
7356 avox 1573
                                        double kern= charStyle.font().glyphKerning(hl->glyph.glyph, glyph2, chs / 10.0) * hl->glyph.scaleH;
1574
                                        wide += kern;
1575
                                        hl->glyph.xadvance += kern;
6354 avox 1576
                                }
5362 avox 1577
                        }
1578
                        if (DropCmode)
1579
                        {
5408 avox 1580
                                // drop caps are wider...
9737 jghali 1581
                                if ((hl->ch == SpecialChars::OBJECT) && (hl->embedded.hasItem()))
5362 avox 1582
                                {
14929 jghali 1583
                                        double itemHeight = hl->embedded.getItem()->gHeight + hl->embedded.getItem()->lineWidth();
1584
                                        if (itemHeight == 0)
1585
                                                itemHeight = charStyle.font().height(style.charStyle().fontSize() / 10.0);
8177 avox 1586
                                        wide = hl->embedded.getItem()->gWidth + hl->embedded.getItem()->lineWidth();
16868 jghali 1587
                                        realAsce = calculateLineSpacing (style, this) * DropLines;
6144 avox 1588
                                        hl->glyph.scaleH /= hl->glyph.scaleV;
16868 jghali 1589
                                        hl->glyph.scaleV = (realAsce / itemHeight);
6144 avox 1590
                                        hl->glyph.scaleH *= hl->glyph.scaleV;
5362 avox 1591
                                }
1592
                                else
1593
                                {
14929 jghali 1594
                                        double realCharHeight = charStyle.font().realCharHeight(chstr[0], charStyle.fontSize() / 10.0);
1595
                                        if (realCharHeight == 0)
1596
                                                realCharHeight = charStyle.font().height(style.charStyle().fontSize() / 10.0);
16868 jghali 1597
                                        wide = (charStyle.font().realCharWidth(chstr[0], chsd / 10.0) * scaleH) + (1 - scaleH); //dropcaps are to close to next glyphs if glyphs are hard scaled
1598
                                        realAsce = charStyle.font().realCharHeight(chstr[0], chsd / 10.0) * scaleV + offset;
1599
                                        asce = charStyle.font().ascent(hlcsize10);
1600
                                        // qDebug() QString("dropcaps pre: chsd=%1 realCharHeight = %2 chstr=%3").arg(chsd).arg(asce).arg(chstr2[0]);
6144 avox 1601
                                        hl->glyph.scaleH /= hl->glyph.scaleV;
16868 jghali 1602
                                        hl->glyph.scaleV = (realAsce / realCharHeight);
6144 avox 1603
                                        hl->glyph.scaleH *= hl->glyph.scaleV;
16868 jghali 1604
                                        hl->glyph.xoffset -= 0.5; //drop caps are always to far from column left edge
5362 avox 1605
                                }
6370 avox 1606
                                hl->glyph.xadvance = wide;
16868 jghali 1607
                                desc = realDesc =0;
5362 avox 1608
                        }
8814 cbradney 1609
                        else // !DropCMode
5362 avox 1610
                        {
9737 jghali 1611
                                if (SpecialChars::isExpandingSpace(hl->ch))
8900 cbradney 1612
                                {
8931 cbradney 1613
                                        double wordtracking = charStyle.wordTracking();
8900 cbradney 1614
                                        hl->glyph.xadvance *= wordtracking;
1615
                                        wide *= wordtracking;
1616
                                }
5408 avox 1617
                                // find ascent / descent
9737 jghali 1618
                                if ((hl->ch == SpecialChars::OBJECT) && (hl->embedded.hasItem()))
16868 jghali 1619
                                        desc = realDesc = 0;
5362 avox 1620
                                else
1621
                                {
16868 jghali 1622
                                        desc = -charStyle.font().descent(hlcsize10);
1623
                                        realDesc = charStyle.font().realCharDescent(chstr[0], hlcsize10) * scaleV - offset;
9737 jghali 1624
                                        current.rememberShrinkStretch(hl->ch, wide, style);
5362 avox 1625
                                }
6144 avox 1626
                                asce = charStyle.font().ascent(hlcsize10);
16868 jghali 1627
                                realAsce = charStyle.font().realCharAscent(chstr[0], hlcsize10) * scaleV + offset;
5362 avox 1628
                        }
16868 jghali 1629
 
1630
                        //check for Y position at beginning of line
1631
                        if (current.itemsInLine == 0 && !current.afterOverflow)
5362 avox 1632
                        {
16868 jghali 1633
                                if (current.recalculateY)
5362 avox 1634
                                {
16868 jghali 1635
                                        //if top of column Y position depends on first line offset
1636
                                        if (current.startOfCol)
3614 cbradney 1637
                                        {
16868 jghali 1638
                                                lastLineY = qMax(lastLineY, extra.Top + lineCorr);
1639
                                                if (style.lineSpacingMode() != ParagraphStyle::BaselineGridLineSpacing)
3614 cbradney 1640
                                                {
16868 jghali 1641
                                                        if (firstLineOffset() == FLOPRealGlyphHeight)
1642
                                                        {
1643
                                                                if (DropCmode)
1644
                                                                        current.yPos += asce;
1645
                                                                else
1646
                                                                        current.yPos += realAsce;
1647
                                                        }
1648
                                                        else if (firstLineOffset() == FLOPLineSpacing)
1649
                                                                current.yPos += style.lineSpacing();
1650
                                                        else
1651
                                                                current.yPos += asce;
3614 cbradney 1652
                                                }
16868 jghali 1653
                                                else if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
1654
                                                {
1655
                                                        if (current.yPos <= lastLineY)
1656
                                                                current.yPos = lastLineY +1;
16986 jghali 1657
                                                        double by = Ypos;
16868 jghali 1658
                                                        if (OwnPage != -1)
1659
                                                                by = Ypos - m_Doc->Pages->at(OwnPage)->yOffset();
1660
                                                        int ol1 = qRound((by + current.yPos - m_Doc->guidesPrefs().offsetBaselineGrid) * 10000.0);
1661
                                                        int ol2 = static_cast<int>(ol1 / m_Doc->guidesPrefs().valueBaselineGrid);
1662
                                                        current.yPos = ceil(  ol2 / 10000.0 ) * m_Doc->guidesPrefs().valueBaselineGrid + m_Doc->guidesPrefs().offsetBaselineGrid - by;
1663
                                                }
3614 cbradney 1664
                                        }
16868 jghali 1665
                                        else
1666
                                        {
1667
                                                if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
1668
                                                {
1669
                                                        current.yPos += m_Doc->guidesPrefs().valueBaselineGrid;
16986 jghali 1670
                                                        double by = Ypos;
16868 jghali 1671
                                                        if (OwnPage != -1)
1672
                                                                by = Ypos - m_Doc->Pages->at(OwnPage)->yOffset();
1673
                                                        int ol1 = qRound((by + current.yPos - m_Doc->guidesPrefs().offsetBaselineGrid) * 10000.0);
1674
                                                        int ol2 = static_cast<int>(ol1 / m_Doc->guidesPrefs().valueBaselineGrid);
1675
                                                        current.yPos = ceil(  ol2 / 10000.0 ) * m_Doc->guidesPrefs().valueBaselineGrid + m_Doc->guidesPrefs().offsetBaselineGrid - by;
1676
                                                }
1677
                                                else
1678
                                                        current.yPos += style.lineSpacing();
1679
                                        }
1680
                                        if (DropCmode)
1681
                                                current.yPos += DropCapDrop;
5362 avox 1682
                                }
16868 jghali 1683
                                //set left indentation
1684
                                current.leftIndent = 0.0;
1685
                                if (current.addLeftIndent && (maxDX == 0 || DropCmode))
5362 avox 1686
                                {
16868 jghali 1687
                                        current.leftIndent = style.leftMargin();
1688
                                        if (current.hasDropCap)
1689
                                                current.leftIndent = 0;
1690
                                        if (a==0 || (a > 0 && (itemText.text(a-1) == SpecialChars::PARSEP)))
1691
                                                current.leftIndent += style.firstIndent();
1692
                                        current.addLeftIndent = false;
5362 avox 1693
                                }
1694
                        }
16868 jghali 1695
                        current.recalculateY = true;
1696
                        int maxYAsc = 0, maxYDesc = 0;
1697
                        if (current.startOfCol)
5362 avox 1698
                        {
16868 jghali 1699
                                //qMax is used for glyphs shifted or vertically scalled above ascent or below descent
1700
                                double addAsce = qMax(realAsce, asce);
1701
                                if (style.lineSpacingMode() != ParagraphStyle::BaselineGridLineSpacing)
5362 avox 1702
                                {
16868 jghali 1703
                                        if (firstLineOffset() == FLOPRealGlyphHeight)
1704
                                                addAsce = realAsce;
1705
                                        else if (firstLineOffset() == FLOPLineSpacing)
1706
                                                addAsce = style.lineSpacing();
5362 avox 1707
                                }
16868 jghali 1708
                                maxYAsc = static_cast<int>(floor(current.yPos - addAsce));
1709
                        }
1710
                        else
1711
                                maxYAsc = static_cast<int>(floor(current.yPos - qMax(realAsce, asce)));
16913 jghali 1712
                        //fix for glyphs with negative realAsce value
1713
                        maxYAsc = qMax(maxYAsc, 0);
16868 jghali 1714
                        maxYDesc = static_cast<int>(ceil(current.yPos + qMax(realDesc, desc)));
1715
 
1716
                        if (current.itemsInLine == 0 && !current.afterOverflow)
1717
                        {
1718
                                //start a new line
1719
                                goNoRoom = false;
1720
 
1721
                                // find line`s start
1722
                                pt1 = QPoint(static_cast<int>(floor(current.xPos)), maxYAsc);
1723
                                pt2 = QPoint(static_cast<int>(ceil(current.xPos + (style.minGlyphExtension() * wide))),maxYDesc);
1724
                                pt = QRect(pt1, pt2);
1725
                                realEnd = 0;
1726
                                //check if there is overflow at start of line, if so jump behind it and check again
1727
                                double Xpos, Xend;
1728
                                bool done = false;
1729
                                bool newColumn = false;
1730
                                while (!done)
8814 cbradney 1731
                                {
16868 jghali 1732
                                        Xpos = current.xPos;
1733
                                        Xend = current.xPos + current.leftIndent;
1734
                                        //check if in indent any overflow occurs
1735
                                        while (Xpos <= Xend && Xpos < current.colRight)
3614 cbradney 1736
                                        {
17044 jghali 1737
                                                if (!regionContainsRect(cl, pt))
16868 jghali 1738
                                                {
16989 jghali 1739
                                                        Xpos = current.xPos = realEnd = findRealOverflowEnd(cl, pt, current.colRight);
16868 jghali 1740
                                                        Xend = current.xPos + current.leftIndent;
1741
                                                }
1742
                                                else
1743
                                                        Xpos++;
1744
                                                pt.moveTopLeft(QPoint(static_cast<int>(floor(Xpos)),maxYAsc));
1745
                                        }
1746
                                        current.xPos = Xend;
1747
                                        done = true;
1748
                                        if (current.isEndOfLine((style.minGlyphExtension() * wide) + current.rightMargin + current.leftIndent))
1749
                                        {
5408 avox 1750
                                                // new line
16868 jghali 1751
                                                current.xPos = qMax(current.colLeft, maxDX);
8062 avox 1752
                                                if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
5362 avox 1753
                                                {
17085 jghali 1754
                                                        current.yPos++;
16868 jghali 1755
                                                        double by = Ypos;
1756
                                                        if (OwnPage != -1)
1757
                                                                by = Ypos - m_Doc->Pages->at(OwnPage)->yOffset();
1758
                                                        int ol1 = qRound((by + current.yPos - m_Doc->guidesPrefs().offsetBaselineGrid) * 10000.0);
1759
                                                        int ol2 = static_cast<int>(ol1 / m_Doc->guidesPrefs().valueBaselineGrid);
1760
                                                        current.yPos = ceil(  ol2 / 10000.0 ) * m_Doc->guidesPrefs().valueBaselineGrid + m_Doc->guidesPrefs().offsetBaselineGrid - by;
1761
                                                }
17085 jghali 1762
                                                else if (style.lineSpacingMode() == ParagraphStyle::FixedLineSpacing)
1763
                                                        current.yPos += (current.startOfCol ? 1 : style.lineSpacing());
1764
                                                else
1765
                                                        current.yPos++;
16868 jghali 1766
                                                lastLineY = maxYAsc +1;
1767
                                                if (current.startOfCol)
1768
                                                {
1769
                                                        //qMax is used for glyphs shifted or vertically scalled above ascent or below descent
1770
                                                        double addAsce = qMax(realAsce, asce);
1771
                                                        if (style.lineSpacingMode() != ParagraphStyle::BaselineGridLineSpacing)
3614 cbradney 1772
                                                        {
16868 jghali 1773
                                                                if (firstLineOffset() == FLOPRealGlyphHeight)
1774
                                                                        addAsce = realAsce;
1775
                                                                else if (firstLineOffset() == FLOPLineSpacing)
1776
                                                                        addAsce = style.lineSpacing();
3614 cbradney 1777
                                                        }
16868 jghali 1778
                                                        maxYAsc = static_cast<int>(floor(current.yPos - addAsce));
3614 cbradney 1779
                                                }
16868 jghali 1780
                                                else
1781
                                                        maxYAsc = static_cast<int>(floor(current.yPos - qMax(realAsce, asce)));
1782
                                                maxYDesc = static_cast<int>(ceil(current.yPos + qMax(realDesc, desc)));
1783
 
1784
                                                pt.moveTopLeft(QPoint(static_cast<int>(floor(current.xPos)),maxYAsc));
1785
                                                done = false;
5362 avox 1786
                                        }
16868 jghali 1787
                                        if (current.isEndOfCol(realDesc))
1788
                                        {
1789
                                                current.column++;
1790
                                                if (current.column < Cols)
1791
                                                {
1792
                                                        newColumn = true;
1793
                                                        break;
1794
                                                }
1795
                                                else
1796
                                                {
1797
                                                        MaxChars = a;
1798
                                                        goto NoRoom;
1799
                                                }
1800
                                        }
5362 avox 1801
                                }
16868 jghali 1802
                                if (newColumn)
5362 avox 1803
                                {
16868 jghali 1804
                                        current.nextColumn();
1805
                                        current.mustLineEnd = current.colRight;
1806
                                        current.restartX = current.xPos;
1807
                                        lastLineY = current.yPos;
1808
                                        current.rowDesc = 0;
1809
                                        a--;
1810
                                        current.recalculateY = true;
1811
                                        current.addLeftIndent = true;
1812
                                        continue;
5362 avox 1813
                                }
16868 jghali 1814
                                current.line.x = current.restartX = current.xPos;
1815
                                current.line.y = current.yPos;
1816
                        }
1817
 
1818
                        //check if line must start at new Y position due to current glyph height or previous line descent
1819
                        if (!SpecialChars::isBreak(hl->ch, true)
1820
                                && !SpecialChars::isBreakingSpace(hl->ch)
1821
                                && !SpecialChars::isExpandingSpace(hl->ch)
1822
                                && hl->ch != SpecialChars::TAB)
1823
                        {
1824
                                double diff = 0;
1825
                                if (current.startOfCol || DropCmode)
16982 jghali 1826
                                        diff = realAsce - (current.yPos - lastLineY);
17082 jghali 1827
                                else if (style.lineSpacingMode() != ParagraphStyle::FixedLineSpacing)
16868 jghali 1828
                                        diff = charStyle.font().realCharAscent(QChar('l'), hlcsize10) * scaleV + offset - (current.yPos - lastLineY);
1829
                                if (diff >= 1 || (!DropCmode && diff > 0))
5362 avox 1830
                                {
16868 jghali 1831
                                        if (current.hasDropCap && DropLinesCount == 0)
1832
                                        {
1833
                                                current.hasDropCap = false;
1834
                                                current.yPos = maxDY;
1835
                                                maxDX = 0;
1836
                                        }
1837
                                        int linesDrop = 0;
17082 jghali 1838
                                        if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
16868 jghali 1839
                                        {
17085 jghali 1840
                                                linesDrop = ceil(diff / m_Doc->guidesPrefs().valueBaselineGrid);
17082 jghali 1841
                                                current.yPos += m_Doc->guidesPrefs().valueBaselineGrid * linesDrop;
16868 jghali 1842
                                        }
1843
                                        else /*if (current.startOfCol)*/
1844
                                        {
1845
                                                //FIX ME - that is ugly hack I must made, because simply expression
1846
                                                //current.yPos += diff; stop working, dont know why (compiler bug?)
1847
                                                float YPOS = (float) current.yPos + (float) diff + 0.01;
1848
                                                current.yPos = (double) YPOS;
1849
                                                if (current.hasDropCap && diff > DropCapDrop)
1850
                                                {
1851
                                                        current.hasDropCap = false;
1852
                                                }
1853
                                        }
1854
                                        if (current.hasDropCap && linesDrop > 0 && DropLinesCount > 1)
1855
                                        {
1856
                                                DropLinesCount += linesDrop;
1857
                                                if (DropLinesCount >= DropLines)
1858
                                                {
1859
                                                        current.hasDropCap = false;
1860
                                                        maxDX = 0;
1861
                                                        maxDY = 0;
1862
                                                }
1863
                                        }
1864
                                        a = current.restartRow(false);
1865
                                        continue;
3614 cbradney 1866
                                }
5362 avox 1867
                        }
5408 avox 1868
                        // right tab stuff
7905 avox 1869
                        if (tabs.active)
5362 avox 1870
                        {
9737 jghali 1871
                                if (((hl->ch == '.') && (tabs.status == TabPOINT)) || ((hl->ch == ',') && (tabs.status == TabCOMMA)) || (hl->ch == SpecialChars::TAB))
5362 avox 1872
                                {
7905 avox 1873
                                        tabs.active = false;
1874
                                        tabs.status = TabNONE;
3614 cbradney 1875
                                }
1876
                        }
5408 avox 1877
                        // tab positioning
9737 jghali 1878
                        if (hl->ch == SpecialChars::TAB)
3614 cbradney 1879
                        {
5362 avox 1880
                                wide = 1;
7905 avox 1881
                                if (tabs.active)
1882
                                        tabs.active = false;
1883
                                else // ???
3614 cbradney 1884
                                {
8095 avox 1885
                                        tabs.xPos = current.xPos;
5721 avox 1886
                                        tTabValues = style.tabValues();
5362 avox 1887
                                        if (tTabValues.isEmpty())
3614 cbradney 1888
                                        {
16840 jghali 1889
                                                current.xPos = nextAutoTab (current, this);
7905 avox 1890
                                                tabs.status = TabNONE;
1891
                                                tabs.active = false;
3614 cbradney 1892
                                        }
5362 avox 1893
                                        else
1894
                                        {
16840 jghali 1895
                                                double tCurX = current.xPos;
1896
                                                double oCurX = current.xPos - current.colLeft + wide;
5362 avox 1897
                                                for (int yg = static_cast<int>(tTabValues.count()-1); yg > -1; yg--)
1898
                                                {
13406 jghali 1899
                                                        if (oCurX < tTabValues.at(yg).tabPosition)
5362 avox 1900
                                                        {
13406 jghali 1901
                                                                tabs.status = static_cast<int>(tTabValues.at(yg).tabType);
1902
                                                                tabs.fillChar = tTabValues.at(yg).tabFillChar;
16840 jghali 1903
                                                                current.xPos = current.colLeft + tTabValues.at(yg).tabPosition;
5362 avox 1904
                                                        }
1905
                                                }
7905 avox 1906
                                                tabs.active = (tabs.status != TabLEFT);
16840 jghali 1907
                                                if (current.xPos == tCurX)  // no more tabs found
1908
                                                {
1909
                                                        current.xPos = nextAutoTab (current, this);
1910
                                                        tabs.status = TabNONE;
1911
                                                        tabs.active = false;
1912
                                                        tabs.fillChar = QChar();
1913
                                                }
1914
 
7905 avox 1915
                                                // remember fill char
1916
                                                if (!tabs.fillChar.isNull()) {
13946 jghali 1917
                                                        hl->glyph.growWithTabLayout();
1918
                                                        TabLayout * tglyph = dynamic_cast<TabLayout*>(hl->glyph.more);
1919
                                                        if (tglyph)
1920
                                                        {
1921
                                                                tglyph->fillChar = tabs.fillChar;
1922
                                                                tglyph->glyph    = charStyle.font().char2CMap(tabs.fillChar);
1923
                                                                tglyph->yoffset  = hl->glyph.yoffset;
1924
                                                                tglyph->scaleV   = tglyph->scaleH = chs / charStyle.fontSize();
1925
                                                                tglyph->xadvance = 0;
1926
                                                        }
7905 avox 1927
                                                }
5362 avox 1928
                                        }
12084 jghali 1929
                                        current.xPos -= (legacy ? 1.0 : 0.0);
1930
                                        hl->glyph.xadvance = current.xPos + wide + kernVal - tabs.xPos;
8095 avox 1931
//                                      wide = current.xPos - RTabX;
7905 avox 1932
                                        tabs.charIndex = a;
3614 cbradney 1933
                                }
5362 avox 1934
                        }
1935
 
5408 avox 1936
                        // remember y pos
5362 avox 1937
                        if (DropCmode)
7313 avox 1938
                                hl->glyph.yoffset -= charStyle.font().realCharHeight(chstr[0], chsd / 10.0) - charStyle.font().realCharAscent(chstr[0], chsd / 10.0);
8095 avox 1939
 
8103 avox 1940
                        // remember x pos
1941
                        double breakPos = current.xPos;
8095 avox 1942
 
7905 avox 1943
                        if (!tabs.active) // normal case
5362 avox 1944
                        {
8095 avox 1945
                                current.xPos += wide+kernVal;
5362 avox 1946
                        }
7905 avox 1947
                        else if (tabs.active && tabs.status == TabCENTER)       // center tab
5362 avox 1948
                        {
8095 avox 1949
                                current.xPos += (wide+kernVal) / 2;
8562 jghali 1950
                                current.xPos = qMax(current.xPos, current.colLeft);
5362 avox 1951
                        }
7905 avox 1952
                        else // other tabs.active                       
5362 avox 1953
                        {
8562 jghali 1954
                                current.xPos = qMax(current.xPos, current.colLeft);
12084 jghali 1955
                        }              
16868 jghali 1956
                        // remember possible break
1957
                        if ( (SpecialChars::isBreakingSpace(hl->ch) || hl->ch == SpecialChars::TAB))
5362 avox 1958
                        {
16868 jghali 1959
                                if ( a == firstInFrame() || !SpecialChars::isBreakingSpace(itemText.text(a-1)) )
1960
                                {
1961
                                        current.rememberBreak(a, breakPos, style.rightMargin());
1962
                                }
5362 avox 1963
                        }
16868 jghali 1964
                        //check against space before PARSEP
1965
                        /*if (SpecialChars::isBreakingSpace(hl->ch) && (a + 1 < itemText.length()) && (itemText.item(a+1)->ch == SpecialChars::PARSEP))
1966
                        {
1967
                                a++;
1968
                                hl = itemText.item(a);
1969
                        }*/
16840 jghali 1970
 
5408 avox 1971
                        // test if end of line reached
16868 jghali 1972
                        double overflowWidth = 0.0;
1973
                        double hyphWidth = 0.0;
1974
                        bool inOverflow = false;
1975
                        if (hl->effects() & ScStyle_HyphenationPossible || hl->ch == SpecialChars::SHYPHEN)
1976
                                hyphWidth = charStyle.font().charWidth('-', hlcsize10) * (charStyle.scaleH() / 1000.0);
1977
                        if ((current.isEndOfLine(style.rightMargin() + hyphWidth)) || current.isEndOfCol(realDesc) || SpecialChars::isBreak(hl->ch, Cols > 1) || (current.xPos - current.maxShrink + hyphWidth) >= current.mustLineEnd)
1978
                        {
1979
                                //end of row reached - right column, end of column, break char or line must end
1980
                                if (current.itemsInLine == 0 && !current.afterOverflow && !SpecialChars::isBreak(hl->ch, Cols > 1))
1981
                                {
1982
                                        //no glyphs in line, so start new row
1983
                                        if (SpecialChars::isBreak(hl->ch, Cols > 1))
1984
                                                current.restartRowIndex = a +1;
1985
                                        a = current.restartRow(true);
1986
                                        inOverflow = false;
1987
                                        outs = false;
1988
                                        continue;
1989
                                }
1990
                                //there we have some glyphs
17087 jghali 1991
                                if (current.breakIndex < 0)
1992
                                {
1993
                                        //force break
1994
                                        if (!SpecialChars::isBreak(hl->ch, Cols > 1))
1995
                                        {
1996
                                                //force line end at previouse glyph
1997
                                                a--;
1998
                                                current.mustLineEnd = current.line.x;
1999
                                        }
2000
                                        current.breakLine(itemText, style, firstLineOffset(),a);
2001
                                }
16868 jghali 2002
                                if (!current.addLine && !current.lastInRowLine && current.afterOverflow)
2003
                                {
2004
                                        //we reach right edge of column
2005
                                        //if we are after overflow area we have to go back and try to insert text before it
2006
                                        //if we have some text here - insert text WITHOUT right margin
2007
                                        //if there is no place for text - insert text WITH right margin and end line
2008
                                        current.lastInRowLine = false;
17087 jghali 2009
                                        if (current.line.firstItem == current.restartIndex)
16868 jghali 2010
                                                current.lastInRowLine = true;
2011
                                        if (current.hasDropCap && DropLinesCount == 0 && current.restartIndex == current.restartRowIndex)
2012
                                        {
2013
                                                current.hasDropCap = false;
2014
                                                maxDX = 0;