Rev 24827 | Rev 24908 | 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 | /*************************************************************************** |
18524 | avox | 8 | pageitem.cpp - description |
9 | ------------------- |
||
10 | begin : Sat Apr 7 2001 |
||
11 | copyright : (C) 2001 by Franz Schmid |
||
12 | email : Franz.Schmid@altmuehlnet.de |
||
3614 | cbradney | 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 | |||
19067 | craig | 24 | #include "pageitem_textframe.h" |
25 | |||
12269 | cbradney | 26 | #include <QDebug> |
9803 | fschmid | 27 | #include <QList> |
13951 | fschmid | 28 | #include <QTransform> |
12269 | cbradney | 29 | #include <QPalette> |
10220 | cbradney | 30 | #include <QPoint> |
12269 | cbradney | 31 | #include <QPolygon> |
10220 | cbradney | 32 | #include <QRegion> |
19067 | craig | 33 | #include <cairo.h> |
12269 | cbradney | 34 | #include <cassert> |
3614 | cbradney | 35 | |
19495 | craig | 36 | #include "actionmanager.h" |
19067 | craig | 37 | #include "appmodes.h" |
10532 | avox | 38 | #include "canvas.h" |
10220 | cbradney | 39 | #include "commonstrings.h" |
3829 | cbradney | 40 | #include "hyphenator.h" |
17826 | craig | 41 | #include "marks.h" |
42 | #include "notesstyles.h" |
||
19067 | craig | 43 | #include "numeration.h" |
3614 | cbradney | 44 | #include "pageitem.h" |
16943 | fschmid | 45 | #include "pageitem_group.h" |
17863 | jghali | 46 | #include "pageitem_noteframe.h" |
3614 | cbradney | 47 | #include "prefsmanager.h" |
19067 | craig | 48 | #include "scconfig.h" |
16736 | jghali | 49 | #include "scpage.h" |
10601 | mrdocs | 50 | #include "scpainter.h" |
3614 | cbradney | 51 | #include "scpaths.h" |
10601 | mrdocs | 52 | #include "scraction.h" |
3614 | cbradney | 53 | #include "scribus.h" |
10220 | cbradney | 54 | #include "scribusdoc.h" |
19093 | craig | 55 | #include "scribusview.h" |
3614 | cbradney | 56 | #include "scribusstructs.h" |
4145 | cbradney | 57 | #include "selection.h" |
21107 | craig | 58 | #include "text/boxes.h" |
59 | #include "text/screenpainter.h" |
||
21155 | jghali | 60 | #include "text/textshaper.h" |
21563 | jghali | 61 | #include "text/shapedtext.h" |
62 | #include "text/shapedtextfeed.h" |
||
24684 | jghali | 63 | #include "textnote.h" |
19067 | craig | 64 | #include "ui/guidemanager.h" |
20488 | jghali | 65 | #include "ui/marksmanager.h" |
3614 | cbradney | 66 | #include "undomanager.h" |
67 | #include "undostate.h" |
||
20608 | jghali | 68 | #include "units.h" |
10601 | mrdocs | 69 | #include "util.h" |
10203 | cbradney | 70 | #include "util_math.h" |
16736 | jghali | 71 | |
21563 | jghali | 72 | |
73 | |||
3614 | cbradney | 74 | using namespace std; |
75 | |||
22598 | craig | 76 | PageItem_TextFrame::PageItem_TextFrame(ScribusDoc *pa, double x, double y, double w, double h, double w2, const QString& fill, const QString& outline) |
3614 | cbradney | 77 | : PageItem(pa, PageItem::TextFrame, x, y, w, h, w2, fill, outline) |
78 | { |
||
22281 | craig | 79 | init(); |
9816 | avox | 80 | firstChar = 0; |
3614 | cbradney | 81 | } |
82 | |||
12839 | fschmid | 83 | PageItem_TextFrame::PageItem_TextFrame(const PageItem & p) : PageItem(p) |
84 | { |
||
22281 | craig | 85 | init(); |
86 | m_notesFramesMap.clear(); |
||
87 | } |
||
88 | |||
89 | void PageItem_TextFrame::init() |
||
90 | { |
||
12839 | fschmid | 91 | invalid = true; |
92 | cursorBiasBackward = false; |
||
93 | unicodeTextEditMode = false; |
||
94 | unicodeInputCount = 0; |
||
17988 | fschmid | 95 | m_origAnnotPos = QRectF(xPos(), yPos(), width(), height()); |
18889 | fschmid | 96 | verticalAlign = 0; |
22281 | craig | 97 | incompleteLines = 0; |
98 | maxY = 0.0; |
||
22598 | craig | 99 | connect(&itemText,SIGNAL(changed(int,int)), this, SLOT(slotInvalidateLayout(int,int))); |
12839 | fschmid | 100 | } |
5184 | avox | 101 | |
17578 | craig | 102 | QRegion PageItem_TextFrame::calcAvailableRegion() |
5184 | avox | 103 | { |
16989 | jghali | 104 | QRegion result(this->Clip); |
23047 | jghali | 105 | if (isEmbedded && !isGroupChild()) |
106 | return result; |
||
107 | |||
108 | bool invertible(false); |
||
109 | QTransform canvasToLocalMat; |
||
110 | if (isGroupChild()) |
||
111 | canvasToLocalMat.translate(gXpos, gYpos); |
||
112 | else |
||
113 | canvasToLocalMat.translate(m_xPos, m_yPos); |
||
114 | canvasToLocalMat.rotate(m_rotation); |
||
115 | canvasToLocalMat = canvasToLocalMat.inverted(&invertible); |
||
116 | |||
117 | if (!invertible) |
||
118 | return QRegion(); |
||
119 | |||
23464 | jghali | 120 | int layerLev = m_Doc->layerLevelFromID(m_layerID); |
23047 | jghali | 121 | int docItemsCount = m_Doc->Items->count(); |
122 | PageItem* docItem = nullptr; |
||
23464 | jghali | 123 | int layerLevItem; |
23047 | jghali | 124 | QList<PageItem*> thisList; |
125 | if (!OnMasterPage.isEmpty()) |
||
5184 | avox | 126 | { |
23047 | jghali | 127 | if ((savedOwnPage == -1) || (savedOwnPage >= signed(m_Doc->Pages->count()))) |
128 | return result; |
||
23464 | jghali | 129 | ScPage* Mp = m_Doc->MasterPages.at(m_Doc->MasterNames[OnMasterPage]); |
130 | ScPage* Dp = m_Doc->Pages->at(savedOwnPage); |
||
18794 | jghali | 131 | if (isGroupChild()) |
23047 | jghali | 132 | thisList = Parent->asGroupFrame()->groupItemList; |
17238 | fschmid | 133 | else |
23047 | jghali | 134 | thisList = m_Doc->MasterItems; |
135 | int thisid = thisList.indexOf(this); |
||
23464 | jghali | 136 | for (int i = 0; i < m_Doc->MasterItems.count(); ++i) |
5184 | avox | 137 | { |
23464 | jghali | 138 | docItem = m_Doc->MasterItems.at(i); |
23047 | jghali | 139 | // #10642 : masterpage items interact only with items placed on same masterpage |
140 | if (docItem->OnMasterPage != OnMasterPage) |
||
141 | continue; |
||
23464 | jghali | 142 | layerLevItem = m_Doc->layerLevelFromID(docItem->m_layerID); |
143 | if (((i > thisid) && (docItem->m_layerID == m_layerID)) || (layerLevItem > layerLev && m_Doc->layerFlow(docItem->m_layerID))) |
||
5184 | avox | 144 | { |
23047 | jghali | 145 | if (docItem->textFlowAroundObject()) |
5184 | avox | 146 | { |
23047 | jghali | 147 | QRegion itemRgn = docItem->textInteractionRegion(Mp->xOffset() - Dp->xOffset(), Mp->yOffset() - Dp->yOffset()); |
148 | result = result.subtracted( canvasToLocalMat.map(itemRgn) ); |
||
5387 | avox | 149 | } |
23047 | jghali | 150 | } |
151 | } // for all masterItems |
||
152 | // (JG) #6009 : disable possible interaction between master text frames and normal frames |
||
153 | // which have the text flow option set |
||
154 | /*if (!m_Doc->masterPageMode()) |
||
155 | { |
||
23464 | jghali | 156 | for (uint i = 0; i < docItemsCount; ++i) |
5184 | avox | 157 | { |
23464 | jghali | 158 | docItem = m_Doc->Items->at(i); |
23047 | jghali | 159 | Mp = m_Doc->MasterPages.at(m_Doc->MasterNames[OnMasterPage]); |
160 | Dp = m_Doc->Pages->at(OwnPage); |
||
161 | if ((docItem->textFlowAroundObject()) && (docItem->OwnPage == OwnPage)) |
||
5184 | avox | 162 | { |
23047 | jghali | 163 | result = result.subtract(itemShape(docItem, m_Doc->view(), Mp->xOffset() - Dp->xOffset(), Mp->yOffset() - Dp->yOffset())); |
164 | } |
||
165 | } // for all docItems |
||
166 | } // if (! masterPageMode) */ |
||
167 | } // if (!OnMasterPage.isEmpty()) |
||
168 | else |
||
169 | { |
||
170 | int thisid = 0; |
||
171 | if (isGroupChild()) |
||
172 | { |
||
173 | thisid = Parent->asGroupFrame()->groupItemList.indexOf(this); |
||
174 | docItemsCount = Parent->asGroupFrame()->groupItemList.count(); |
||
23464 | jghali | 175 | for (int i = thisid + 1; i < docItemsCount; ++i) |
23047 | jghali | 176 | { |
23464 | jghali | 177 | docItem = Parent->asGroupFrame()->groupItemList.at(i); |
23047 | jghali | 178 | if (docItem->textFlowAroundObject()) |
179 | { |
||
180 | QRegion itemRgn = docItem->textInteractionRegion(0, 0); |
||
181 | result = result.subtracted( canvasToLocalMat.map(itemRgn) ); |
||
182 | } |
||
183 | } |
||
184 | } |
||
5184 | avox | 185 | else |
186 | { |
||
23047 | jghali | 187 | thisid = m_Doc->Items->indexOf(this); |
23464 | jghali | 188 | for (int i = 0; i < docItemsCount; ++i) |
17238 | fschmid | 189 | { |
23464 | jghali | 190 | docItem = m_Doc->Items->at(i); |
191 | layerLevItem = m_Doc->layerLevelFromID(docItem->m_layerID); |
||
192 | if (((i > thisid) && (docItem->m_layerID == m_layerID)) || (layerLevItem > layerLev && m_Doc->layerFlow(docItem->m_layerID))) |
||
17238 | fschmid | 193 | { |
21663 | jghali | 194 | if (docItem->textFlowAroundObject()) |
17238 | fschmid | 195 | { |
23042 | jghali | 196 | QRegion itemRgn = docItem->textInteractionRegion(0, 0); |
21663 | jghali | 197 | result = result.subtracted( canvasToLocalMat.map(itemRgn) ); |
17238 | fschmid | 198 | } |
199 | } |
||
200 | } |
||
23047 | jghali | 201 | } // for all docItems |
202 | } // if(OnMasterPage.isEmpty() |
||
203 | |||
5184 | avox | 204 | return result; |
205 | } |
||
206 | |||
6144 | avox | 207 | void PageItem_TextFrame::setShadow() |
208 | { |
||
209 | if (OnMasterPage.isEmpty()) |
||
210 | return; |
||
18524 | avox | 211 | |
6144 | avox | 212 | QString newShadow = m_Doc->masterPageMode() ? OnMasterPage : QString::number(OwnPage); |
23048 | jghali | 213 | if (newShadow == m_currentShadow) |
214 | return; |
||
215 | |||
216 | if (m_currentShadow == OnMasterPage) |
||
217 | { |
||
218 | // masterpage was edited, clear all shadows |
||
219 | m_shadows.clear(); |
||
220 | } |
||
221 | if (!m_shadows.contains(newShadow)) |
||
222 | { |
||
223 | if (!m_shadows.contains(OnMasterPage)) |
||
224 | { |
||
225 | m_shadows[OnMasterPage] = itemText; |
||
226 | // const ParagraphStyle& pstyle(shadows[OnMasterPage].paragraphStyle(0)); |
||
227 | // qDebug() << QString("Pageitem_Textframe: style of master: %1 align=%2").arg(pstyle.parent()).arg(pstyle.alignment()); |
||
228 | // qDebug() << QString("Pageitem_Textframe: shadow itemText->%1").arg(OnMasterPage); |
||
6366 | avox | 229 | } |
23048 | jghali | 230 | if (newShadow != OnMasterPage) |
231 | { |
||
232 | m_shadows[newShadow] = m_shadows[OnMasterPage].copy(); |
||
233 | // const ParagraphStyle& pstyle(shadows[newShadow].paragraphStyle(0)); |
||
234 | // qDebug() << QString("Pageitem_Textframe: style of shadow copy: %1 align=%2").arg(pstyle.parent()).arg(pstyle.alignment()); |
||
6144 | avox | 235 | } |
23048 | jghali | 236 | // qDebug() << QString("Pageitem_Textframe: shadow %1<-%2").arg(newShadow).arg(OnMasterPage); |
6144 | avox | 237 | } |
23048 | jghali | 238 | itemText = m_shadows[newShadow]; |
239 | // const ParagraphStyle& pstyle(itemText.paragraphStyle(0)); |
||
240 | // qDebug() << QString("Pageitem_Textframe: style of shadow: %1 align=%2").arg(pstyle.parent()).arg(pstyle.alignment()); |
||
241 | invalid = true; |
||
242 | m_currentShadow = newShadow; |
||
6144 | avox | 243 | } |
17790 | fschmid | 244 | /* |
17740 | jghali | 245 | static void debugLineLayout(const StoryText& itemText, const LineSpec& line) |
246 | { |
||
247 | QFile debugFile(QDir::homePath() + "/Desktop/debug_line.csv"); |
||
248 | debugFile.open(QIODevice::WriteOnly); |
||
249 | |||
250 | QTextStream stream(&debugFile); |
||
251 | stream.setRealNumberNotation(QTextStream::FixedNotation); |
||
252 | stream.setRealNumberPrecision(7); |
||
253 | |||
254 | stream << "xoffset" << "\t"; |
||
255 | stream << "yoffset" << "\t"; |
||
256 | stream << "xadvance" << "\t"; |
||
257 | stream << "yadvance" << "\t"; |
||
258 | stream << "scaleH" << "\t"; |
||
259 | stream << "scaleV" << "\t"; |
||
260 | stream << "\n"; |
||
261 | |||
21107 | craig | 262 | for (int zc = line.firstChar; zc < line.lastChar; ++zc) |
17740 | jghali | 263 | { |
264 | const ScText* item = itemText.item(zc); |
||
265 | |||
266 | stream << item->glyph.xoffset << "\t"; |
||
267 | stream << item->glyph.yoffset << "\t"; |
||
268 | stream << item->glyph.xadvance << "\t"; |
||
269 | stream << item->glyph.yadvance << "\t"; |
||
270 | stream << item->glyph.scaleH << "\t"; |
||
271 | stream << item->glyph.scaleV << "\t"; |
||
272 | stream << "\n"; |
||
273 | } |
||
274 | |||
275 | debugFile.close(); |
||
276 | } |
||
17826 | craig | 277 | |
5778 | avox | 278 | static void dumpIt(const ParagraphStyle& pstyle, QString indent = QString("->")) |
279 | { |
||
10553 | fschmid | 280 | QString db = QString("%6%1/%2 @ %3: %4--%5 linespa%6: %7 align%8") |
5778 | avox | 281 | .arg(pstyle.name()) |
6733 | avox | 282 | .arg(pstyle.parent()) |
5850 | tsoots | 283 | .arg( (unsigned long int) &pstyle) |
5778 | avox | 284 | .arg(pstyle.leftMargin()) |
285 | .arg(pstyle.rightMargin()) |
||
6805 | avox | 286 | .arg(indent) |
287 | .arg(pstyle.lineSpacingMode()) |
||
288 | .arg(pstyle.lineSpacing()) |
||
10553 | fschmid | 289 | .arg(pstyle.alignment()); |
13085 | jghali | 290 | qDebug() << db; |
5778 | avox | 291 | static QString more(" "); |
6733 | avox | 292 | if (pstyle.hasParent()) |
293 | dumpIt(*dynamic_cast<const ParagraphStyle*>(pstyle.parentStyle()), more + indent); |
||
5778 | avox | 294 | } |
17826 | craig | 295 | */ |
8103 | avox | 296 | static const bool legacy = true; |
297 | |||
10553 | fschmid | 298 | /* |
18524 | avox | 299 | static void layoutDropCap(GlyphLayout layout, double curX, double curY, double offsetX, double offsetY, double dropCapDrop) |
300 | { |
||
6370 | avox | 301 | } |
10553 | fschmid | 302 | */ |
6370 | avox | 303 | |
21563 | jghali | 304 | |
7905 | avox | 305 | enum TabStatus { |
306 | TabNONE = 0, |
||
307 | TabLEFT = TabNONE, |
||
308 | TabRIGHT = 1, |
||
309 | TabPOINT = 2, |
||
310 | TabCOMMA = 3, |
||
311 | TabCENTER = 4 |
||
7313 | avox | 312 | }; |
6370 | avox | 313 | |
8761 | avox | 314 | |
315 | /** |
||
316 | fields which describe what type of tab is currently active |
||
317 | */ |
||
7313 | avox | 318 | struct TabControl { |
21107 | craig | 319 | bool active; |
320 | int status; |
||
321 | double xPos; |
||
322 | QChar fillChar; |
||
323 | GlyphLayout* tabGlyph; |
||
7313 | avox | 324 | }; |
325 | |||
21715 | craig | 326 | class LineSpec |
21107 | craig | 327 | { |
21715 | craig | 328 | public: |
329 | LineSpec() |
||
330 | { |
||
331 | x = 0.0; |
||
332 | y = 0.0; |
||
21972 | craig | 333 | width = 0.0; |
334 | height = 0.0; |
||
335 | ascent = 0.0; |
||
336 | descent = 0.0; |
||
337 | colLeft = 0.0; |
||
21715 | craig | 338 | firstCluster = 0; |
339 | lastCluster = 0; |
||
340 | naturalWidth = 0.0; |
||
21972 | craig | 341 | isFirstLine = false; |
21715 | craig | 342 | } |
343 | |||
21107 | craig | 344 | qreal x; |
345 | qreal y; |
||
346 | qreal width; |
||
21972 | craig | 347 | qreal height; |
21107 | craig | 348 | qreal ascent; |
349 | qreal descent; |
||
350 | qreal colLeft; |
||
351 | |||
21563 | jghali | 352 | int firstCluster; |
353 | int lastCluster; |
||
21107 | craig | 354 | qreal naturalWidth; |
355 | bool isFirstLine; |
||
356 | }; |
||
357 | |||
8761 | avox | 358 | /** |
359 | fields which describe how the current line is placed into the frame |
||
21107 | craig | 360 | */ |
7313 | avox | 361 | struct LineControl { |
21715 | craig | 362 | LineSpec lineData; |
21563 | jghali | 363 | QList<GlyphCluster> glyphs; |
24739 | jghali | 364 | bool isEmpty { true }; |
365 | int hyphenCount { 0 }; |
||
7313 | avox | 366 | double colWidth; |
8095 | avox | 367 | double colGap; |
7313 | avox | 368 | double colLeft; |
369 | double colRight; |
||
24739 | jghali | 370 | int column { 0 }; |
371 | bool startOfCol { true }; |
||
15029 | jghali | 372 | bool hasDropCap; |
24739 | jghali | 373 | bool afterOverflow { false }; |
374 | bool addLine { false }; |
||
375 | bool recalculateY { false }; |
||
376 | bool lastInRowLine { false }; |
||
377 | bool addLeftIndent { false }; |
||
378 | bool wasFirstInRow { false }; |
||
379 | double leftIndent { 0.0 }; |
||
380 | double rightIndent { 0.0 }; |
||
381 | double rightMargin { 0.0 }; |
||
382 | double mustLineEnd { false }; |
||
383 | int restartIndex { 0 }; //index of glyph run where line computing should be restarted |
||
384 | int restartRowIndex { 0 }; //index of glyph run where row of text is started |
||
385 | double restartX { 0.0 }; //starting X position of line if must be restarted |
||
386 | double rowDesc { 0.0 }; |
||
16868 | jghali | 387 | |
24739 | jghali | 388 | double xPos { 0.0 }; |
389 | double yPos { 0.0 }; |
||
390 | int breakIndex { -1 }; |
||
391 | double breakXPos { 0.0 }; |
||
9047 | avox | 392 | |
24739 | jghali | 393 | double maxShrink { 0.0 }; |
394 | double maxStretch { 0.0 }; |
||
395 | ScribusDoc* doc { nullptr }; |
||
396 | ITextContext* context { nullptr }; |
||
18524 | avox | 397 | |
8761 | avox | 398 | /// remember frame dimensions and offsets |
21563 | jghali | 399 | LineControl(double w, double h, const MarginStruct& extra, double lCorr, ScribusDoc* d, ITextContext* ctx, double colwidth, double colgap) |
400 | : hasDropCap(false) |
||
401 | , doc(d), context(ctx) |
||
8095 | avox | 402 | { |
403 | insets = extra; |
||
404 | lineCorr = lCorr; |
||
405 | frameWidth = w; |
||
406 | frameHeight = h; |
||
21107 | craig | 407 | colWidth = colwidth; |
408 | colGap = colgap; |
||
21119 | craig | 409 | colLeft = insets.left() + lineCorr; |
410 | colRight = colLeft + colWidth; |
||
8095 | avox | 411 | } |
18524 | avox | 412 | |
8761 | avox | 413 | /// move position to next column |
21107 | craig | 414 | void nextColumn(TextLayout &textLayout) |
8095 | avox | 415 | { |
416 | startOfCol = true; |
||
21563 | jghali | 417 | if (textLayout.story()->defaultStyle().direction() == ParagraphStyle::RTL) |
418 | colLeft = textLayout.frame()->width() - insets.right() - ((colWidth * (column + 1)) + (colGap * column)); |
||
419 | else |
||
420 | colLeft = (colWidth + colGap) * column + insets.left() + lineCorr; |
||
21107 | craig | 421 | textLayout.addColumn(colLeft, colWidth); |
16868 | jghali | 422 | //now colRight is REAL column right edge |
8095 | avox | 423 | colRight = colLeft + colWidth; |
8103 | avox | 424 | if (legacy) |
16868 | jghali | 425 | colRight += lineCorr; |
8095 | avox | 426 | xPos = colLeft; |
21107 | craig | 427 | yPos = insets.top() + lineCorr; |
21715 | craig | 428 | lineData.colLeft = colLeft; |
8095 | avox | 429 | } |
18524 | avox | 430 | |
8095 | avox | 431 | bool isEndOfCol(double morespace = 0) |
432 | { |
||
19831 | craig | 433 | return yPos + morespace + insets.bottom() + lineCorr > frameHeight; |
8095 | avox | 434 | } |
16868 | jghali | 435 | |
8761 | avox | 436 | /** |
437 | init fields for a new line at current position |
||
438 | */ |
||
21563 | jghali | 439 | void startLine(int firstCluster) |
15029 | jghali | 440 | { |
21563 | jghali | 441 | glyphs.clear(); |
21107 | craig | 442 | isEmpty = true; |
21715 | craig | 443 | lineData.x = xPos; |
444 | lineData.y = yPos; |
||
445 | lineData.firstCluster = firstCluster; |
||
446 | lineData.lastCluster = 0; |
||
447 | lineData.ascent = 0.0; |
||
448 | lineData.descent = 0.0; |
||
449 | lineData.width = 0.0; |
||
450 | lineData.height = 0.0; |
||
451 | lineData.naturalWidth = 0.0; |
||
452 | lineData.colLeft = colLeft; |
||
8095 | avox | 453 | breakIndex = -1; |
16868 | jghali | 454 | breakXPos = 0.0; |
455 | maxShrink = 0.0; |
||
456 | maxStretch = 0.0; |
||
457 | leftIndent = 0.0; |
||
23238 | jghali | 458 | rightIndent = 0.0; |
16868 | jghali | 459 | rightMargin = 0.0; |
460 | rowDesc = 0.0; |
||
8095 | avox | 461 | } |
9047 | avox | 462 | |
18524 | avox | 463 | |
9047 | avox | 464 | /// called when glyphs are placed on the line |
465 | void rememberShrinkStretch(QChar ch, double wide, const ParagraphStyle& style) |
||
466 | { |
||
467 | if (SpecialChars::isExpandingSpace(ch)) |
||
468 | maxShrink += (1 - style.minWordTracking()) * wide; |
||
469 | else |
||
470 | { |
||
471 | maxShrink += (1 - style.minGlyphExtension()) * wide; |
||
472 | } |
||
473 | maxStretch += (style.maxGlyphExtension() - 1) * wide; |
||
12883 | jghali | 474 | } |
9047 | avox | 475 | |
8761 | avox | 476 | /// called when a possible break is passed |
16868 | jghali | 477 | void rememberBreak(int index, double pos, double morespace = 0) |
8095 | avox | 478 | { |
16868 | jghali | 479 | if (pos > colRight - morespace) |
9047 | avox | 480 | { |
481 | // only look for the first break behind the right edge |
||
16868 | jghali | 482 | //maxShrink = 0; |
18524 | avox | 483 | |
9047 | avox | 484 | // check if we already have a better break |
485 | if (breakIndex >= 0) |
||
486 | { |
||
487 | double oldLooseness = qAbs(colRight - breakXPos); |
||
488 | |||
489 | double newLooseness = pos - colRight; |
||
490 | if (newLooseness >= oldLooseness) |
||
491 | return; |
||
492 | } |
||
493 | } |
||
16868 | jghali | 494 | breakXPos = pos; |
8095 | avox | 495 | breakIndex = index; |
496 | } |
||
21107 | craig | 497 | |
8761 | avox | 498 | /// called when a mandatory break is found |
21563 | jghali | 499 | void breakLine(int last) |
8095 | avox | 500 | { |
501 | breakIndex = last; |
||
21715 | craig | 502 | breakXPos = lineData.x; |
21563 | jghali | 503 | |
21715 | craig | 504 | for (int i = 0; i <= breakIndex - lineData.firstCluster; i++) |
21563 | jghali | 505 | breakXPos += glyphs.at(i).width(); |
14554 | jghali | 506 | // #8194, #8717 : update line ascent and descent with sensible values |
507 | // so that endOfLine() returns correct result |
||
21107 | craig | 508 | updateHeightMetrics(); |
15029 | jghali | 509 | // #9060 : update line offset too |
17303 | fschmid | 510 | // updateLineOffset(itemText, style, offsetPolicy); |
8095 | avox | 511 | } |
16868 | jghali | 512 | |
8761 | avox | 513 | /// use the last remembered break to set line width and itemrange |
8095 | avox | 514 | void finishLine(double endX) |
515 | { |
||
21715 | craig | 516 | lineData.lastCluster = breakIndex; |
517 | lineData.naturalWidth = breakXPos - lineData.x; |
||
518 | lineData.width = endX - lineData.x; |
||
9047 | avox | 519 | maxShrink = maxStretch = 0; |
8095 | avox | 520 | } |
16868 | jghali | 521 | |
522 | int restartRow(bool recalcY) |
||
523 | { |
||
524 | if (recalcY) |
||
525 | yPos++; |
||
526 | recalculateY = recalcY; |
||
527 | xPos = restartX = colLeft; |
||
528 | startLine(restartRowIndex); |
||
529 | addLeftIndent = true; |
||
530 | afterOverflow = false; |
||
21107 | craig | 531 | return restartRowIndex - 1; |
16868 | jghali | 532 | } |
533 | |||
534 | int restartLine(bool recalcY, bool add) |
||
535 | { |
||
536 | recalculateY = recalcY; |
||
537 | addLine = add; |
||
538 | xPos = restartX; |
||
539 | startLine(restartIndex); |
||
540 | afterOverflow = false; |
||
21107 | craig | 541 | return restartIndex - 1; |
16868 | jghali | 542 | } |
18524 | avox | 543 | |
8095 | avox | 544 | bool isEndOfLine(double moreSpace = 0) |
545 | { |
||
16868 | jghali | 546 | bool res; |
8103 | avox | 547 | if (legacy) |
16868 | jghali | 548 | res = ceil(xPos + lineCorr - maxShrink) + ceil(moreSpace) >= floor(colRight); |
8103 | avox | 549 | else |
16868 | jghali | 550 | res = ceil(xPos - maxShrink) + ceil(moreSpace) >= floor(colRight); |
551 | return res; |
||
8095 | avox | 552 | } |
18524 | avox | 553 | |
16923 | jghali | 554 | /// Keep old endOfLine code for reference |
555 | /*double endOfLine_old(const QRegion& shape, const QTransform& pf2, double morespace, int Yasc, int Ydesc) |
||
8095 | avox | 556 | { |
13402 | jghali | 557 | // if we aren't restricted further, we'll end at this maxX: |
558 | double maxX = colRight - morespace; |
||
16868 | jghali | 559 | if (legacy) maxX -= lineCorr; |
13402 | jghali | 560 | |
23922 | jghali | 561 | double StartX = floor(qMax(line.x, qMin(maxX, breakXPos - maxShrink - 1)) - 1); |
16868 | jghali | 562 | int xPos = static_cast<int>(ceil(maxX)); |
13402 | jghali | 563 | |
16921 | jghali | 564 | QPoint pt12 (xPos, Yasc); |
565 | QPoint pt22 (xPos, Ydesc); |
||
566 | QRect pt(pt12,pt22); |
||
18524 | avox | 567 | QRegion region; |
13402 | jghali | 568 | |
569 | double EndX2 = StartX; |
||
16868 | jghali | 570 | double Interval = 0.25; |
13402 | jghali | 571 | do { |
16868 | jghali | 572 | int xP = static_cast<int>(ceil(EndX2 + morespace)); |
573 | pt.moveTopLeft(QPoint(xP, Yasc)); |
||
16921 | jghali | 574 | region = QRegion(pf2.mapToPolygon(pt)).subtracted(shape); |
575 | if (!region.isEmpty()) |
||
16868 | jghali | 576 | break; |
13402 | jghali | 577 | EndX2 += Interval; |
16921 | jghali | 578 | } while ((EndX2 < maxX) && region.isEmpty()); |
13402 | jghali | 579 | |
16868 | jghali | 580 | return qMin(EndX2, maxX); |
16923 | jghali | 581 | }*/ |
582 | |||
583 | /// find x position where this line must end |
||
16989 | jghali | 584 | double endOfLine(const QRegion& shape, double morespace, int yAsc, int yDesc) |
16923 | jghali | 585 | { |
586 | // if we aren't restricted further, we'll end at this maxX: |
||
587 | double maxX = colRight - morespace; |
||
588 | if (legacy) maxX -= lineCorr; |
||
589 | |||
23922 | jghali | 590 | double StartX = floor(qMax(lineData.x, qMin(maxX, breakXPos - maxShrink - 1)) - 1); |
20144 | jghali | 591 | StartX = qMax(0.0, StartX); |
592 | |||
16923 | jghali | 593 | int xPos = static_cast<int>(ceil(maxX)); |
594 | QPoint pt12 (xPos, yAsc); |
||
595 | QPoint pt22 (xPos, yDesc); |
||
596 | |||
597 | QPolygon p; |
||
16989 | jghali | 598 | p.append (QPoint (StartX, yAsc)); |
599 | p.append (QPoint (StartX, yDesc)); |
||
600 | p.append (pt12); |
||
601 | p.append (pt22); |
||
16923 | jghali | 602 | // check if something gets in the way |
603 | QRegion lineI = shape.intersected (p.boundingRect()); |
||
604 | // if the intersection only has 1 rectangle, then nothing gets in the way |
||
18194 | fschmid | 605 | if (lineI.rectCount() == 1) |
16923 | jghali | 606 | { |
607 | int cPos = static_cast<int>(ceil(StartX + morespace)); |
||
608 | QRect cRect (QPoint(cPos, yAsc), QPoint(cPos, yDesc)); |
||
18524 | avox | 609 | QRegion qr2 = QRegion(cRect).subtracted(shape); |
610 | if (qr2.isEmpty()) // qr2 == 0 <=> cRect subset of shape |
||
16923 | jghali | 611 | { |
24031 | jghali | 612 | QRegion::const_iterator rect = lineI.cbegin(); |
613 | double mx = qMax(rect->left(), rect->right()) /*- pf2.dx()*/; |
||
16989 | jghali | 614 | int steps = static_cast<int>((mx - StartX - morespace - 2) / 0.25); |
16923 | jghali | 615 | if (steps > 0) |
616 | { |
||
617 | StartX += steps * 0.25; |
||
618 | } |
||
619 | } |
||
620 | } |
||
621 | |||
622 | QRect pt(pt12, pt22); |
||
623 | |||
624 | double EndX2 = StartX; |
||
625 | double Interval = 0.25; |
||
626 | do { |
||
627 | int xP = static_cast<int>(ceil(EndX2 + morespace)); |
||
628 | pt.moveTopLeft(QPoint(xP, yAsc)); |
||
17044 | jghali | 629 | if (!regionContainsRect(shape, pt)) |
16923 | jghali | 630 | break; |
631 | EndX2 += Interval; |
||
17044 | jghali | 632 | } while ((EndX2 < maxX) && regionContainsRect(shape, pt)); |
16923 | jghali | 633 | |
634 | /*double oldEndX2 = endOfLine_old(shape, pf2, morespace, yAsc, yDesc); |
||
635 | if (oldEndX2 != qMin(EndX2, maxX)) |
||
636 | { |
||
637 | qDebug() << "Different EndX : " << oldEndX2 << " (old) " << EndX2 << " (new) "; |
||
638 | }*/ |
||
639 | |||
640 | return qMin(EndX2, maxX); |
||
8095 | avox | 641 | } |
18524 | avox | 642 | |
21107 | craig | 643 | void updateHeightMetrics() |
8956 | avox | 644 | { |
21715 | craig | 645 | lineData.ascent = lineData.descent = 0; |
21563 | jghali | 646 | if (glyphs.isEmpty()) |
21107 | craig | 647 | return; |
21563 | jghali | 648 | const CharStyle& cStyle(glyphs.at(0).style()); |
649 | double scaleV = cStyle.scaleV() / 1000.0; |
||
650 | double offset = (cStyle.fontSize() / 10) * (cStyle.baselineOffset() / 1000.0); |
||
21715 | craig | 651 | lineData.ascent = cStyle.font().ascent(cStyle.fontSize()/10.00) * scaleV + offset; |
652 | lineData.descent = cStyle.font().descent(cStyle.fontSize()/10.00) * scaleV - offset; |
||
8956 | avox | 653 | } |
14554 | jghali | 654 | |
16868 | jghali | 655 | // yPos should not be changed when all line is already calculated - at new y position there can be overflow!!! |
17303 | fschmid | 656 | // edit: can't happen as it should only move upwards, and this is covered by the calculations done. |
657 | //void updateLineOffset(const StoryText& itemText, const ParagraphStyle& style, FirstLineOffsetPolicy offsetPolicy) |
||
658 | //{ |
||
21107 | craig | 659 | // if (charsInLine <= 0) |
17303 | fschmid | 660 | // return; |
661 | // if ((!hasDropCap) && (startOfCol) && (style.lineSpacingMode() != ParagraphStyle::BaselineGridLineSpacing)) |
||
16868 | jghali | 662 | // { |
17303 | fschmid | 663 | // //FIXME: use glyphs, not chars |
21107 | craig | 664 | // double firstasce = itemText.charStyle(line.firstChar).font().ascent(itemText.charStyle(line.firstChar).fontSize() / 10.0); |
17303 | fschmid | 665 | // double adj (0.0); |
666 | // double currasce (this->getLineAscent(itemText)); |
||
19927 | jghali | 667 | // if (offsetPolicy == FLOPRealGlyphHeight) |
16868 | jghali | 668 | // { |
17303 | fschmid | 669 | // adj = firstasce - currasce; |
16868 | jghali | 670 | // } |
19927 | jghali | 671 | // else if (offsetPolicy == FLOPFontAscent) |
16868 | jghali | 672 | // { |
17303 | fschmid | 673 | // adj = 0.0; |
16868 | jghali | 674 | // } |
19927 | jghali | 675 | // else if (offsetPolicy == FLOPLineSpacing) |
17303 | fschmid | 676 | // { |
677 | // adj = firstasce - style.lineSpacing(); |
||
678 | // } |
||
679 | // line.ascent = currasce; |
||
680 | // line.y -= adj; |
||
681 | // yPos -= adj; |
||
16868 | jghali | 682 | // } |
17303 | fschmid | 683 | // else if ((!startOfCol) && (style.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing)) |
684 | // { |
||
21107 | craig | 685 | // QChar ch = itemText.text(line.firstChar); |
17303 | fschmid | 686 | // double firstasce = style.lineSpacing(); |
687 | // double currasce = getLineHeight(itemText); |
||
688 | // double adj = firstasce - currasce; |
||
21107 | craig | 689 | // qDebug() << QString("move2 line %1.. down by %2").arg(current.line.firstChar).arg(-adj); |
17303 | fschmid | 690 | // line.ascent = currasce; |
691 | // line.y -= adj; |
||
692 | // yPos -= adj; |
||
693 | // } |
||
694 | //} |
||
15029 | jghali | 695 | |
21563 | jghali | 696 | /// called when line length is known and line is to be justified |
697 | void justifyLine(const ParagraphStyle& style) |
||
21107 | craig | 698 | { |
699 | |||
21563 | jghali | 700 | double glyphNatural = 0; |
701 | double spaceNatural = 0; |
||
702 | double glyphExtension; |
||
703 | double spaceExtension; |
||
704 | int spaceInsertion = 0; |
||
705 | double imSpace = -1; |
||
706 | int trackingInsertion = 0; |
||
707 | double trackingAmount = 0; |
||
708 | |||
21715 | craig | 709 | int glyphsCount = lineData.lastCluster - lineData.firstCluster + 1; |
21563 | jghali | 710 | |
711 | for (int i = 0; i < glyphsCount; ++i) |
||
21107 | craig | 712 | { |
21661 | jghali | 713 | const GlyphCluster& glyphCluster = glyphs.at(i); |
21563 | jghali | 714 | if (!glyphCluster.hasFlag(ScLayout_ExpandingSpace)) |
715 | { |
||
716 | glyphNatural += glyphCluster.width(); |
||
717 | } |
||
718 | else if (!glyphCluster.hasFlag(ScLayout_SuppressSpace)) |
||
719 | { |
||
720 | spaceNatural += glyphCluster.width(); |
||
721 | if (imSpace < 0.0 || imSpace > glyphCluster.width()) |
||
722 | imSpace = glyphCluster.width(); |
||
723 | } |
||
724 | if (i != 0 && glyphCluster.hasFlag(ScLayout_ImplicitSpace)) |
||
725 | { |
||
726 | spaceInsertion += 1; |
||
727 | } |
||
728 | if (i != glyphsCount && glyphCluster.hasFlag(ScLayout_JustificationTracking)) |
||
729 | { |
||
730 | trackingInsertion += 1; |
||
731 | } |
||
21107 | craig | 732 | } |
733 | |||
21563 | jghali | 734 | imSpace /= 2; |
21107 | craig | 735 | |
21563 | jghali | 736 | // decision: prio 1: stretch glyph; prio 2: insert spaces; prio 3: stretch spaces |
21715 | craig | 737 | if (lineData.width < spaceNatural + glyphNatural * style.minGlyphExtension() && spaceNatural > 0) |
21107 | craig | 738 | { |
21563 | jghali | 739 | glyphExtension = style.minGlyphExtension() - 1; |
23674 | jghali | 740 | spaceExtension = (lineData.width - glyphNatural * (1 + glyphExtension) ) / spaceNatural - 1; |
21563 | jghali | 741 | imSpace = 0; |
21107 | craig | 742 | } |
21715 | craig | 743 | else if (lineData.width < spaceNatural + glyphNatural * style.maxGlyphExtension() && glyphNatural > 0) |
21563 | jghali | 744 | { |
745 | spaceExtension = 0; |
||
23674 | jghali | 746 | glyphExtension = (lineData.width - spaceNatural) / glyphNatural - 1; |
21563 | jghali | 747 | imSpace = 0; |
748 | } |
||
21107 | craig | 749 | else |
750 | { |
||
21563 | jghali | 751 | glyphExtension = style.maxGlyphExtension() - 1; |
23674 | jghali | 752 | if (spaceInsertion) |
753 | { |
||
21715 | craig | 754 | double remaining = lineData.width - glyphNatural * (1 + glyphExtension) - spaceNatural; |
23674 | jghali | 755 | if (imSpace > 0) |
756 | { |
||
757 | if (remaining / spaceInsertion < imSpace) |
||
758 | { |
||
21563 | jghali | 759 | imSpace = remaining / spaceInsertion; |
760 | spaceExtension = 0; |
||
23674 | jghali | 761 | } |
762 | else |
||
763 | { |
||
21563 | jghali | 764 | spaceExtension = (remaining + spaceNatural) / (spaceNatural + spaceInsertion * imSpace) - 1; |
765 | imSpace *= spaceExtension + 1; |
||
766 | } |
||
23674 | jghali | 767 | } |
768 | else |
||
769 | { |
||
21563 | jghali | 770 | imSpace = remaining / spaceInsertion; |
771 | spaceExtension = 0; |
||
772 | } |
||
23674 | jghali | 773 | } |
774 | else |
||
775 | { |
||
21563 | jghali | 776 | if (spaceNatural > 0) |
21715 | craig | 777 | spaceExtension = (lineData.width - glyphNatural * (1 + glyphExtension)) / spaceNatural - 1; |
21563 | jghali | 778 | else |
779 | spaceExtension = 0; |
||
780 | |||
781 | if (trackingInsertion && (spaceExtension == 0.0 || spaceExtension >= 20.0)) |
||
782 | { |
||
783 | if (spaceExtension == 0.0) |
||
784 | { |
||
21715 | craig | 785 | double remaining = lineData.width - glyphNatural * (1 + glyphExtension) - spaceNatural; |
21563 | jghali | 786 | trackingAmount = remaining / trackingInsertion; |
787 | } |
||
788 | else |
||
789 | { |
||
790 | spaceExtension = qMin(20.0, spaceExtension); |
||
21715 | craig | 791 | double remaining = lineData.width - glyphNatural * (1 + glyphExtension) - spaceNatural; |
21563 | jghali | 792 | remaining -= spaceExtension * spaceNatural; |
793 | trackingAmount = remaining / trackingInsertion; |
||
794 | if (trackingAmount > imSpace) |
||
795 | { |
||
796 | trackingAmount = imSpace; |
||
21715 | craig | 797 | spaceExtension = (lineData.width - glyphNatural * (1 + glyphExtension) - trackingInsertion * trackingAmount) / spaceNatural - 1; |
21563 | jghali | 798 | } |
799 | } |
||
800 | } |
||
801 | } |
||
21107 | craig | 802 | } |
803 | |||
21563 | jghali | 804 | double glyphScale = 1 + glyphExtension; |
21811 | jghali | 805 | double naturalWidth = 0; |
7313 | avox | 806 | |
21563 | jghali | 807 | /* |
808 | qDebug() << QString("justify: line = %7 natural = %1 + %2 = %3 (%4); spaces + %5%%; min=%8; glyphs + %6%%; min=%9") |
||
809 | .arg(spaceNatural).arg(glyphNatural).arg(spaceNatural+glyphNatural).arg(line.naturalWidth) |
||
810 | .arg(spaceExtension).arg(glyphExtension).arg(line.width) |
||
811 | .arg(style.minWordTracking()).arg(style.minGlyphExtension()); |
||
812 | */ |
||
813 | |||
814 | int startItem = 0; |
||
815 | if (glyphs[startItem].hasFlag(ScLayout_DropCap)) |
||
21811 | jghali | 816 | { |
21563 | jghali | 817 | startItem++; |
21811 | jghali | 818 | naturalWidth += glyphs[startItem].width(); |
819 | } |
||
21563 | jghali | 820 | // distribute whitespace on spaces and glyphs |
821 | for (int i = startItem; i < glyphsCount; ++i) |
||
21107 | craig | 822 | { |
21563 | jghali | 823 | GlyphCluster& glyphCluster = glyphs[i]; |
824 | double wide = glyphCluster.width(); |
||
825 | if (!glyphCluster.hasFlag(ScLayout_ExpandingSpace)) |
||
21107 | craig | 826 | { |
21563 | jghali | 827 | glyphCluster.setScaleH(glyphCluster.scaleH() * glyphScale); |
828 | glyphCluster.xoffset *= glyphScale; |
||
21107 | craig | 829 | } |
21563 | jghali | 830 | else if (!glyphCluster.hasFlag(ScLayout_SuppressSpace)) |
831 | { |
||
832 | glyphCluster.extraWidth += (wide * spaceExtension); |
||
833 | } |
||
834 | if (i != 0 && glyphCluster.hasFlag(ScLayout_ImplicitSpace)) |
||
835 | { |
||
836 | GlyphCluster& lastRun = glyphs[i - 1]; |
||
837 | lastRun.extraWidth += imSpace; |
||
838 | } |
||
839 | if (i != glyphsCount && trackingAmount != 0 && glyphCluster.hasFlag(ScLayout_JustificationTracking)) |
||
840 | { |
||
841 | glyphCluster.extraWidth += trackingAmount; |
||
842 | } |
||
21811 | jghali | 843 | naturalWidth += glyphCluster.width(); |
21107 | craig | 844 | } |
21811 | jghali | 845 | lineData.naturalWidth = naturalWidth; |
21661 | jghali | 846 | |
847 | if ((style.alignment() == ParagraphStyle::Extended) && |
||
21811 | jghali | 848 | (style.direction() == ParagraphStyle::RTL)) |
21661 | jghali | 849 | { |
21811 | jghali | 850 | double offset = lineData.width - lineData.naturalWidth; |
851 | if (offset > 1e-6) |
||
21661 | jghali | 852 | indentLine(style, offset); |
853 | } |
||
21107 | craig | 854 | } |
855 | |||
21563 | jghali | 856 | /// called when linelength is known and line is not justified |
857 | void indentLine(const ParagraphStyle& style, double leftIndent) |
||
8095 | avox | 858 | { |
21715 | craig | 859 | if (lineData.naturalWidth > lineData.width) |
9047 | avox | 860 | { |
21563 | jghali | 861 | justifyLine(style); |
9047 | avox | 862 | } |
21563 | jghali | 863 | if (leftIndent > 0) |
9047 | avox | 864 | { |
21715 | craig | 865 | lineData.x += leftIndent; |
866 | lineData.width -= leftIndent; |
||
9047 | avox | 867 | } |
8095 | avox | 868 | } |
9047 | avox | 869 | |
21563 | jghali | 870 | /** |
24665 | jghali | 871 | Clones the tab fill char as often as necessary after all distances are known |
21563 | jghali | 872 | */ |
873 | void fillInTabLeaders() |
||
9047 | avox | 874 | { |
21563 | jghali | 875 | // fill in tab leaders |
876 | for (int i = 0; i < glyphs.count(); ++i) |
||
877 | { |
||
878 | GlyphCluster& glyphCluster = glyphs[i]; |
||
879 | if (glyphCluster.hasFlag(ScLayout_TabLeaders)) |
||
880 | { |
||
21684 | jghali | 881 | const CharStyle& charStyle(glyphCluster.style()); |
21563 | jghali | 882 | GlyphLayout tglyph = glyphCluster.glyphs().last(); |
883 | double width = glyphCluster.width(); |
||
884 | double wt = charStyle.font().glyphWidth(tglyph.glyph, charStyle.fontSize() * tglyph.scaleV / 10.0); |
||
885 | int count = static_cast<int>(width / wt); |
||
21684 | jghali | 886 | if (count > 0) |
887 | glyphCluster.glyphs().clear(); |
||
21563 | jghali | 888 | for(int cx = 0; cx < count; ++cx) |
889 | { |
||
890 | GlyphLayout more = tglyph; |
||
891 | more.xadvance = 0; |
||
892 | if (cx != 0) |
||
893 | more.xoffset = (width / count) * cx; |
||
894 | glyphCluster.append(more); |
||
16876 | jghali | 895 | } |
21684 | jghali | 896 | glyphCluster.glyphs().last().xadvance = width / glyphCluster.scaleH(); |
16876 | jghali | 897 | } |
898 | } |
||
9047 | avox | 899 | } |
18524 | avox | 900 | |
901 | |||
21563 | jghali | 902 | //defined but not used |
903 | ///// calculate how much the first char should stick out to the left |
||
904 | //double opticalLeftMargin(const StoryText& itemText) |
||
905 | //{ |
||
906 | // int b = line.firstChar; |
||
907 | // while (b < line.lastChar && (itemText.flags(b) & ScLayout_SuppressSpace)) |
||
908 | // ++b; |
||
909 | // |
||
910 | // double chs = itemText.charStyle(b).fontSize() * (itemText.charStyle(b).scaleH() / 1000.0); |
||
911 | // QChar chr = itemText.text(b); |
||
912 | // double leftCorr = itemText.charStyle(b).font().realCharWidth(chr, chs / 10.0); |
||
913 | // if (QString("'´`").indexOf(chr) >= 0 |
||
914 | // || chr == QChar(0x2018) // quote 6 |
||
915 | // || chr == QChar(0x2019) // quote 9 |
||
916 | // || chr == QChar(0x201a) // lower quote 9 |
||
917 | // || chr == QChar(0x201b) // upper reversed 9 6 |
||
918 | // || chr == QChar(0x2039) // single guillemet < |
||
919 | // || chr == QChar(0x203a) // single guillemet > |
||
920 | // ) |
||
921 | // leftCorr *= -0.7; |
||
922 | // else if (QString("\"").indexOf(chr) >= 0 |
||
923 | // || chr == QChar(0x00ab) // guillemet << |
||
924 | // || chr == QChar(0x00bb) // guillemet >> |
||
925 | // || chr == QChar(0x201c) // quote 66 |
||
926 | // || chr == QChar(0x201d) // quote 99 |
||
927 | // || chr == QChar(0x201e) // lower quote 99 |
||
928 | // || chr == QChar(0x201f) // upper reversed 99 |
||
929 | // ) |
||
930 | // leftCorr *= -0.5; |
||
931 | // else { |
||
932 | // leftCorr = itemText.charStyle(b).font().charWidth(QChar(' '), chs / 10.0, chr); |
||
933 | // leftCorr -= itemText.charStyle(b).font().charWidth(QChar(' '), chs / 10.0); |
||
934 | //// double leftCorr2 = itemText.charStyle(a).font().charWidth(QChar('K'), chs / 10.0, chr); |
||
935 | //// leftCorr2 -= itemText.charStyle(a).font().charWidth(QChar('K'), chs / 10.0); |
||
936 | //// leftCorr = qMin(leftCorr, leftCorr2); |
||
937 | // } |
||
938 | // return leftCorr; |
||
939 | //} |
||
18524 | avox | 940 | |
21563 | jghali | 941 | /// calculate how much the last char should stick out to the right |
942 | double opticalRightMargin(const StoryText& itemText) |
||
8095 | avox | 943 | { |
21715 | craig | 944 | int b = lineData.lastCluster - lineData.firstCluster; |
21563 | jghali | 945 | while (b > 0 && |
946 | (SpecialChars::isBreakingSpace(itemText.text(glyphs[b].lastChar())) || |
||
947 | SpecialChars::isBreak(itemText.text(glyphs[b].lastChar()))) |
||
948 | ) |
||
949 | --b; |
||
950 | if (b >= 0) |
||
21107 | craig | 951 | { |
21563 | jghali | 952 | const CharStyle& style = glyphs[b].style(); |
953 | const ScFace& font = style.font(); |
||
954 | double chs = style.fontSize() * (style.scaleH() / 1000.0); |
||
955 | QChar chr = itemText.text(glyphs[b].lastChar()); |
||
956 | double rightCorr; |
||
957 | if (glyphs[b].hasFlag(ScLayout_SoftHyphenVisible)) |
||
958 | rightCorr = font.hyphenWidth(style, chs / 10.0); |
||
959 | else |
||
960 | rightCorr = font.glyphBBox(font.char2CMap(chr.unicode()), chs / 10.0).width; |
||
961 | if (glyphs[b].hasFlag(ScLayout_SoftHyphenVisible) |
||
962 | || QString("-,.`´'~").indexOf(chr) >= 0 |
||
963 | || chr == QChar(0x2010) |
||
964 | || chr == QChar(0x2018) |
||
965 | || chr == QChar(0x2019) |
||
966 | || chr == QChar(0x201a) |
||
967 | || chr == QChar(0x201b) |
||
968 | || chr == QChar(0x2039) |
||
969 | || chr == QChar(0x203a) |
||
970 | || chr == QChar(0x2032) // PRIME |
||
971 | ) |
||
972 | rightCorr *= 0.7; |
||
973 | else if (QString(";:\"").indexOf(chr) >= 0 |
||
974 | || chr == QChar(0x00ab) |
||
975 | || chr == QChar(0x00bb) |
||
976 | || chr == QChar(0x201c) |
||
977 | || chr == QChar(0x201d) |
||
978 | || chr == QChar(0x201e) |
||
979 | || chr == QChar(0x201f) |
||
980 | || chr == QChar(0x2013) // EN DASH |
||
981 | || chr == QChar(0x2033) // double prime |
||
982 | ) |
||
983 | rightCorr *= 0.5; |
||
984 | else { |
||
985 | #if 0 |
||
24665 | jghali | 986 | // FIXME HOST: is the kerning with "." a really reliable way to check this? |
21563 | jghali | 987 | rightCorr = chStyle.font().realCharWidth(chr, chs / 10.0); |
988 | rightCorr -= chStyle.font().charWidth(chr, chs / 10.0, QChar('.')); |
||
989 | #else |
||
990 | rightCorr = 0; |
||
991 | #endif |
||
9047 | avox | 992 | } |
21563 | jghali | 993 | return rightCorr; |
8095 | avox | 994 | } |
21563 | jghali | 995 | return 0.0; |
996 | } |
||
997 | |||
998 | LineBox* createLineBox() |
||
999 | { |
||
1000 | LineBox* result = new LineBox(); |
||
21715 | craig | 1001 | result->moveTo(lineData.x - colLeft, lineData.y - lineData.ascent); |
1002 | result->setWidth(lineData.width); |
||
1003 | result->setAscent(lineData.ascent); |
||
1004 | result->setDescent(lineData.descent); |
||
22506 | jghali | 1005 | for (const GlyphCluster& run : ShapedTextFeed::putInVisualOrder(glyphs, 0, lineData.lastCluster - lineData.firstCluster + 1)) |
9047 | avox | 1006 | { |
21563 | jghali | 1007 | addBox(result, run); |
1008 | // qDebug() << "cluster" << run.firstChar() << ".." << run.lastChar() << "@" << run.visualIndex(); |
||
9047 | avox | 1009 | } |
21563 | jghali | 1010 | return result; |
1011 | } |
||
1012 | |||
1013 | void addBox(LineBox *lineBox, const GlyphCluster& run) |
||
1014 | { |
||
1015 | Box* result; |
||
1016 | if (run.object().getPageItem(doc)) |
||
21135 | jghali | 1017 | { |
21563 | jghali | 1018 | result = new ObjectBox(run, context); |
1019 | QRectF bBox = context->getVisualBoundingBox(run.object()); |
||
1020 | if (run.hasFlag(ScLayout_DropCap)) |
||
1021 | result->setAscent(bBox.height() * run.scaleV() - run.yoffset); |
||
1022 | else |
||
1023 | result->setAscent(bBox.height()); |
||
1024 | result->setDescent(0); |
||
21135 | jghali | 1025 | } |
21563 | jghali | 1026 | else |
1027 | { |
||
1028 | result = new GlyphBox(run); |
||
1029 | result->setAscent(lineBox->ascent()); |
||
1030 | result->setDescent(lineBox->descent()); |
||
1031 | } |
||
1032 | lineBox->addBox(result); |
||
8095 | avox | 1033 | } |
1034 | |||
21563 | jghali | 1035 | private: |
1036 | double frameWidth; |
||
1037 | double frameHeight; |
||
1038 | MarginStruct insets; |
||
1039 | double lineCorr; |
||
1040 | }; |
||
8095 | avox | 1041 | |
16989 | jghali | 1042 | static double findRealOverflowEnd(const QRegion& shape, QRect pt, double maxX) |
16868 | jghali | 1043 | { |
17044 | jghali | 1044 | while (!regionContainsRect(shape, pt) && pt.right() < maxX) |
16868 | jghali | 1045 | pt.translate(1, 0); |
1046 | if (pt.right() >= maxX) |
||
1047 | return maxX; |
||
16989 | jghali | 1048 | return pt.left() + 0.5; |
16868 | jghali | 1049 | } |
1050 | |||
16840 | jghali | 1051 | static double adjustToBaselineGrid (const LineControl &control, PageItem *item, int OwnPage) |
1052 | { |
||
1053 | double by = item->yPos(); |
||
1054 | if (OwnPage != -1) |
||
1055 | by = by - item->doc()->Pages->at(OwnPage)->yOffset(); |
||
1056 | int ol1 = qRound((by + control.yPos - item->doc()->guidesPrefs().offsetBaselineGrid) * 10000.0); |
||
1057 | int ol2 = static_cast<int>(ol1 / item->doc()->guidesPrefs().valueBaselineGrid); |
||
1058 | // 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 | 1059 | |
16840 | jghali | 1060 | return ceil( ol2 / 10000.0 ) * item->doc()->guidesPrefs().valueBaselineGrid + item->doc()->guidesPrefs().offsetBaselineGrid - by; |
1061 | } |
||
8761 | avox | 1062 | |
16840 | jghali | 1063 | static double nextAutoTab (const LineControl ¤t, PageItem *item) |
1064 | { |
||
1065 | double dtw = item->doc()->itemToolPrefs().textTabWidth; |
||
1066 | if (current.xPos == current.colLeft) |
||
1067 | return current.colLeft + dtw; |
||
1068 | double res = current.colLeft + ceil ((current.xPos - current.colLeft) / dtw) * dtw; |
||
1069 | if (res == current.xPos) res += dtw; |
||
1070 | return res; |
||
1071 | } |
||
8761 | avox | 1072 | |
17826 | craig | 1073 | //cezaryece: I remove static statement as this function is used also by PageItem_NoteFrame |
1074 | double calculateLineSpacing (const ParagraphStyle &style, PageItem *item) |
||
16840 | jghali | 1075 | { |
1076 | if (style.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing) |
||
17223 | jghali | 1077 | { |
1078 | double autoLS = static_cast<double>(item->doc()->typographicPrefs().autoLineSpacing) / 100.0; |
||
1079 | return (style.charStyle().font().height(style.charStyle().fontSize() / 10.0) * autoLS); |
||
1080 | } |
||
16840 | jghali | 1081 | if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing) |
1082 | return item->doc()->guidesPrefs().valueBaselineGrid; |
||
1083 | return style.lineSpacing(); |
||
1084 | } |
||
1085 | |||
1086 | |||
1087 | // This assumes that layout() ran on the previous page and set the incomplete* vars |
||
1088 | // It also clears the incomplete* vars, and changes the starting position for this frame |
||
1089 | // The incomplete* vars are used to ensure that we don't run into an endless loop |
||
1090 | // This setup won't cause any problems, as the only way for the starting position to change |
||
1091 | // back is if the previous frame's layout() runs again, but if it does, it also sets the |
||
1092 | // incomplete* vars for us |
||
1093 | bool PageItem_TextFrame::moveLinesFromPreviousFrame () |
||
1094 | { |
||
23912 | craig | 1095 | PageItem_TextFrame* prev = dynamic_cast<PageItem_TextFrame*>(m_backBox); |
22291 | jghali | 1096 | if (!prev) |
1097 | return false; |
||
1098 | if (!prev->incompleteLines) |
||
1099 | return false; // no incomplete lines - nothing to do |
||
1100 | int pos = textLayout.endOfFrame() - 1; |
||
16840 | jghali | 1101 | QChar lastChar = itemText.text (pos); |
1102 | // qDebug()<<"pos is"<<pos<<", length is"<<itemText.length()<<", incomplete is "<<prev->incompleteLines; |
||
23922 | jghali | 1103 | if ((pos != itemText.length() - 1) && (!SpecialChars::isBreak (lastChar, true))) |
16840 | jghali | 1104 | return false; // the paragraph isn't ending yet |
18987 | avox | 1105 | int lines = textLayout.lines(); // lines added to the current frame |
16840 | jghali | 1106 | |
1107 | ParagraphStyle style = itemText.paragraphStyle (pos); |
||
1108 | int need = style.keepLinesEnd () + 1; |
||
1109 | int prevneed = style.keepLinesStart () + 1; |
||
22291 | jghali | 1110 | if (lines >= need) |
1111 | { |
||
16840 | jghali | 1112 | prev->incompleteLines = 0; // so that further paragraphs don't pull anything |
1113 | return false; // we have enough lines |
||
1114 | } |
||
1115 | |||
1116 | // too few lines - we need to pull some from the previous page |
||
17039 | fschmid | 1117 | int pull = need - lines; |
16840 | jghali | 1118 | // if pulling the lines would lead to the original frame having too few, pull the whole paragraph |
1119 | if (prev->incompleteLines - pull < prevneed) |
||
1120 | pull = prev->incompleteLines; |
||
1121 | // qDebug()<<"pulling"<<pull<<"lines; |
||
1122 | // Okay, move the starting/ending character |
||
1123 | int startingPos = prev->incompletePositions[prev->incompleteLines - pull]; |
||
17039 | fschmid | 1124 | for (int i = 0; i < pull; ++i) |
18987 | avox | 1125 | prev->textLayout.removeLastLine(); |
22832 | craig | 1126 | firstChar = prev->m_maxChars = startingPos; |
16840 | jghali | 1127 | // keep the remaining incomplete lines flagged as such |
1128 | // this ensures that if pulling one line won't be enough, the subsequent call to layout() will pull more |
||
1129 | prev->incompleteLines -= pull; |
||
1130 | |||
1131 | return true; |
||
1132 | } |
||
1133 | |||
1134 | // called at the end of a frame or column |
||
1135 | void PageItem_TextFrame::adjustParagraphEndings () |
||
1136 | { |
||
1137 | // More text to go - let's apply paragraph flowing options - orphans/widows, etc |
||
24422 | jghali | 1138 | int start = textLayout.startOfFrame(); |
1139 | int end = textLayout.endOfFrame() - 1; |
||
1140 | if ((start > end) || (end >= itemText.length() - 1)) |
||
22288 | jghali | 1141 | return; |
16840 | jghali | 1142 | |
24422 | jghali | 1143 | ParagraphStyle style = itemText.paragraphStyle (end); |
1144 | int paragraphStart = itemText.prevParagraph (end) + 1; |
||
1145 | QChar lastChar = itemText.text (end); |
||
16840 | jghali | 1146 | bool keepWithNext = style.keepWithNext() && (lastChar == SpecialChars::PARSEP); |
22288 | jghali | 1147 | if (keepWithNext || (!SpecialChars::isBreak (lastChar, true))) |
1148 | { |
||
16840 | jghali | 1149 | // paragraph continues in the next frame, or needs to be kept with the next one |
1150 | // check how many lines are in this frame |
||
24422 | jghali | 1151 | int lineStart = textLayout.startOfLine (end); |
16840 | jghali | 1152 | incompleteLines = 1; |
1153 | incompletePositions.prepend (lineStart); |
||
22288 | jghali | 1154 | while (lineStart > paragraphStart) |
1155 | { |
||
18987 | avox | 1156 | lineStart = textLayout.startOfLine (lineStart - 1); |
16840 | jghali | 1157 | incompleteLines++; |
1158 | incompletePositions.prepend (lineStart); |
||
1159 | } |
||
1160 | int need = style.keepLinesStart () + 1; |
||
22288 | jghali | 1161 | if (style.keepTogether()) |
1162 | need = incompleteLines; |
||
16840 | jghali | 1163 | int pull = 0; |
22291 | jghali | 1164 | if (style.keepTogether() || (incompleteLines < need)) |
1165 | pull = incompleteLines; |
||
16840 | jghali | 1166 | // if we need to keep it with the next one, pull one line. Next frame layouting |
1167 | // will pull more from us if it proves necessary. |
||
22288 | jghali | 1168 | if (keepWithNext && (!pull)) |
1169 | pull = 1; |
||
16840 | jghali | 1170 | |
22288 | jghali | 1171 | if (pull) |
1172 | { |
||
22108 | jghali | 1173 | qDebug() << "pulling" << pull << "lines"; |
16840 | jghali | 1174 | // push this paragraph to the next frame |
1175 | for (int i = 0; i < pull; ++i) |
||
18987 | avox | 1176 | textLayout.removeLastLine(); |
22832 | craig | 1177 | m_maxChars = incompletePositions[incompleteLines-pull]; |
16840 | jghali | 1178 | incompleteLines = 0; |
1179 | incompletePositions.clear(); |
||
1180 | } |
||
1181 | } |
||
1182 | } |
||
22112 | jghali | 1183 | |
18524 | avox | 1184 | void PageItem_TextFrame::layout() |
3614 | cbradney | 1185 | { |
21563 | jghali | 1186 | // qDebug()<<"==Layout==" << itemName() ; |
13034 | pierre | 1187 | // printBacktrace(24); |
23912 | craig | 1188 | if (m_backBox != nullptr) { |
5732 | avox | 1189 | // qDebug("textframe: len=%d, going back", itemText.length()); |
22516 | craig | 1190 | PageItem_TextFrame* firstInvalid = nullptr; |
23912 | craig | 1191 | PageItem_TextFrame* prevInChain = dynamic_cast<PageItem_TextFrame*>(m_backBox); |
21440 | jghali | 1192 | while (prevInChain) |
16543 | jghali | 1193 | { |
21440 | jghali | 1194 | if (prevInChain->invalid) |
1195 | firstInvalid = prevInChain; |
||
23912 | craig | 1196 | prevInChain = dynamic_cast<PageItem_TextFrame*>(prevInChain->m_backBox); |
16543 | jghali | 1197 | } |
21440 | jghali | 1198 | PageItem_TextFrame* nextInChain = firstInvalid; |
1199 | while (nextInChain && (nextInChain != this)) |
||
1200 | { |
||
1201 | nextInChain->layout(); |
||
23912 | craig | 1202 | nextInChain = dynamic_cast<PageItem_TextFrame*>(nextInChain->m_nextBox); |
21440 | jghali | 1203 | } |
16106 | jghali | 1204 | // #9592 : warning, BackBox->layout() may not layout BackBox next box |
1205 | if (!invalid) |
||
1206 | return; |
||
5362 | avox | 1207 | } |
6370 | avox | 1208 | else if (!invalid && OnMasterPage.isEmpty()) { |
13085 | jghali | 1209 | // qDebug() << QString("textframe: len=%1, invalid=%2 OnMasterPage=%3: no relayout").arg(itemText.length()).arg(invalid).arg(OnMasterPage); |
5732 | avox | 1210 | return; |
1211 | } |
||
23912 | craig | 1212 | if (invalid && m_backBox == nullptr) |
8334 | avox | 1213 | firstChar = 0; |
18524 | avox | 1214 | |
22108 | jghali | 1215 | // qDebug() << QString("textframe(%1,%2): len=%3, start relayout at %4").arg(m_xPos).arg(m_yPos).arg(itemText.length()).arg(firstInFrame()); |
5362 | avox | 1216 | QPoint pt1, pt2; |
16868 | jghali | 1217 | QRect pt; |
7969 | avox | 1218 | double chs, chsd = 0; |
21107 | craig | 1219 | double EndX, OFs; |
6805 | avox | 1220 | ParagraphStyle style; |
17742 | jghali | 1221 | int opticalMargins = ParagraphStyle::OM_None; |
18524 | avox | 1222 | |
5362 | avox | 1223 | bool outs = false; |
1224 | bool goNoRoom = false; |
||
1225 | bool goNextColumn = false; |
||
18524 | avox | 1226 | |
7905 | avox | 1227 | TabControl tabs; |
1228 | tabs.active = false; // RTab |
||
1229 | tabs.status = TabNONE; // TabCode |
||
22600 | craig | 1230 | tabs.tabGlyph = nullptr; // was int charIndex ~ StartRT |
7905 | avox | 1231 | tabs.xPos = 0; // RTabX |
1232 | |||
9803 | fschmid | 1233 | QList<ParagraphStyle::TabRecord> tTabValues; |
7905 | avox | 1234 | tTabValues.clear(); |
18524 | avox | 1235 | |
18047 | craig | 1236 | bool BulNumMode = false; //when bullet or counter should be inserted |
17760 | jghali | 1237 | bool DropCmode = false, FlopBaseline = false; |
17599 | craig | 1238 | double desc=0, asce=0, realAsce=0, realDesc = 0, offset = 0; |
16868 | jghali | 1239 | double maxDY=0, maxDX=0; |
6370 | avox | 1240 | double DropCapDrop = 0; |
11776 | jghali | 1241 | int DropLines = 0; |
1242 | int DropLinesCount = 0; |
||
18524 | avox | 1243 | |
18987 | avox | 1244 | textLayout.clear(); |
16840 | jghali | 1245 | incompleteLines = 0; |
1246 | incompletePositions.clear(); |
||
8095 | avox | 1247 | |
1248 | double lineCorr = 0; |
||
1249 | if (lineColor() != CommonStrings::None) |
||
1250 | lineCorr = m_lineWidth / 2.0; |
||
18524 | avox | 1251 | |
16868 | jghali | 1252 | //hold Y position of last computed line of text (with glyphs descent) |
1253 | //for moving next line if glyphs are higher than that |
||
1254 | double lastLineY = 0; |
||
18524 | avox | 1255 | |
24665 | jghali | 1256 | QMap<int, Mark*> noteMarksPosMap; //mapping notes marks and its position in text |
17826 | craig | 1257 | |
5778 | avox | 1258 | // dump styles |
18524 | avox | 1259 | /* |
5778 | avox | 1260 | for (int i=0; i < itemText.nrOfParagraphs(); ++i) { |
1261 | const ParagraphStyle& pstyle(itemText.paragraphStyle(itemText.endOfParagraph(i))); |
||
13085 | jghali | 1262 | qDebug("par %d:", i); |
5778 | avox | 1263 | dumpIt(pstyle); |
1264 | } |
||
13085 | jghali | 1265 | qDebug() << "default:"; |
5778 | avox | 1266 | dumpIt(itemText.defaultStyle()); |
16989 | jghali | 1267 | */ |
18524 | avox | 1268 | |
6144 | avox | 1269 | setShadow(); |
17826 | craig | 1270 | int itLen = itemText.length(); |
1271 | //fast validate empty frames |
||
1272 | if (itLen == 0 || firstInFrame() == itLen) |
||
8095 | avox | 1273 | { |
21495 | jghali | 1274 | PageItem_TextFrame * next = this; |
22516 | craig | 1275 | while (next != nullptr) |
17826 | craig | 1276 | { |
1277 | next->invalid = false; |
||
21495 | jghali | 1278 | next->firstChar = itLen; |
22832 | craig | 1279 | next->m_maxChars = itLen; |
21495 | jghali | 1280 | next->textLayout.clear(); |
1281 | next = dynamic_cast<PageItem_TextFrame*>(next->nextInChain()); |
||
17826 | craig | 1282 | } |
21195 | fschmid | 1283 | // TODO layout() shouldn't delete any frame here, as it breaks any loop |
1284 | // over the doc->Items or doc->MasterItems lists when done with indexes |
||
1285 | // see Bug #12685 for an example |
||
17826 | craig | 1286 | if (!isNoteFrame() && m_Doc->notesChanged() && !m_notesFramesMap.isEmpty()) |
1287 | { //if notes are used |
||
1288 | UndoManager::instance()->setUndoEnabled(false); |
||
1289 | QList<PageItem_NoteFrame*> delList; |
||
22506 | jghali | 1290 | for (PageItem_NoteFrame* nF : m_notesFramesMap.keys()) |
17826 | craig | 1291 | { |
1292 | if (nF->notesList().isEmpty() && !nF->isAutoNoteFrame()) |
||
1293 | delList.append(nF); |
||
1294 | } |
||
1295 | while (!delList.isEmpty()) |
||
1296 | m_Doc->delNoteFrame(delList.takeFirst(), false); |
||
1297 | UndoManager::instance()->setUndoEnabled(true); |
||
1298 | } |
||
1299 | return; |
||
1300 | } |
||
1301 | |||
22600 | craig | 1302 | if ((itLen != 0)) // || (NextBox != nullptr)) |
17826 | craig | 1303 | { |
5408 | avox | 1304 | // determine layout area |
17578 | craig | 1305 | m_availableRegion = calcAvailableRegion(); |
1306 | if (m_availableRegion.isEmpty()) |
||
14384 | jghali | 1307 | { |
22832 | craig | 1308 | m_maxChars = firstInFrame(); |
14384 | jghali | 1309 | goto NoRoom; |
1310 | } |
||
18524 | avox | 1311 | |
16989 | jghali | 1312 | if (imageFlippedH() || imageFlippedV()) |
5362 | avox | 1313 | { |
16989 | jghali | 1314 | QTransform matrix; |
1315 | if (imageFlippedH()) |
||
1316 | { |
||
18095 | craig | 1317 | matrix.translate(m_width, 0); |
16989 | jghali | 1318 | matrix.scale(-1, 1); |
1319 | } |
||
1320 | if (imageFlippedV()) |
||
1321 | { |
||
18095 | craig | 1322 | matrix.translate(0, m_height); |
16989 | jghali | 1323 | matrix.scale(1, -1); |
1324 | } |
||
17578 | craig | 1325 | m_availableRegion = matrix.map(m_availableRegion); |
5362 | avox | 1326 | } |
23456 | jghali | 1327 | |
1328 | // update Bullet & number list if any. |
||
1329 | if (itemText.hasTextMarks() || itemText.hasBulletOrNum() || itemText.marksCountChanged()) |
||
1330 | { |
||
1331 | updateBulletsNum(); |
||
1332 | itemText.resetMarksCountChanged(); |
||
1333 | } |
||
18524 | avox | 1334 | |
21563 | jghali | 1335 | ITextContext* context = this; |
1336 | //TextShaper textShaper(this, itemText, firstInFrame()); |
||
1337 | ShapedTextFeed shapedText(&itemText, firstInFrame(), context); |
||
23238 | jghali | 1338 | |
21563 | jghali | 1339 | QList<GlyphCluster> glyphClusters; // = textShaper.shape(); |
1340 | // std::sort(glyphClusters.begin(), glyphClusters.end(), logicalGlyphRunComp); |
||
21107 | craig | 1341 | |
23456 | jghali | 1342 | LineControl current(m_width, m_height, m_textDistanceMargins, lineCorr, m_Doc, context, columnWidth(), m_columnGap); |
21107 | craig | 1343 | current.nextColumn(textLayout); |
1344 | |||
19831 | craig | 1345 | lastLineY = m_textDistanceMargins.top(); |
8095 | avox | 1346 | |
17223 | jghali | 1347 | //automatic line spacing factor (calculated once) |
21563 | jghali | 1348 | double autoLS = static_cast<double>(context->typographicPrefs().autoLineSpacing) / 100.0; |
17223 | jghali | 1349 | |
5559 | avox | 1350 | // find start of first line |
17826 | craig | 1351 | if (firstInFrame() < itLen) |
5362 | avox | 1352 | { |
21563 | jghali | 1353 | const CharStyle& cstyle = itemText.charStyle(firstInFrame()); |
7887 | avox | 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 | } |
18524 | avox | 1363 | else |
1364 | chs = cstyle.fontSize(); |
||
1365 | desc = -cstyle.font().descent(chs / 10.0); |
||
19831 | craig | 1366 | current.yPos = m_textDistanceMargins.top() + lineCorr; |
13085 | jghali | 1367 | // qDebug() << QString("first line at y=%1").arg(current.yPos); |
21563 | jghali | 1368 | |
5362 | avox | 1369 | } |
6370 | avox | 1370 | else // empty itemText: |
5362 | avox | 1371 | { |
16868 | jghali | 1372 | desc = -itemText.defaultStyle().charStyle().font().descent(itemText.defaultStyle().charStyle().fontSize() / 10.0); |
19831 | craig | 1373 | current.yPos = itemText.defaultStyle().lineSpacing() + m_textDistanceMargins.top() + lineCorr - desc; |
5362 | avox | 1374 | } |
21563 | jghali | 1375 | |
21107 | craig | 1376 | current.startLine(0); |
16868 | jghali | 1377 | |
5362 | avox | 1378 | outs = false; |
1379 | OFs = 0; |
||
22832 | craig | 1380 | m_maxChars = 0; |
16868 | jghali | 1381 | double realEnd = 0; |
21107 | craig | 1382 | current.restartIndex = current.restartRowIndex = 0; |
16868 | jghali | 1383 | current.afterOverflow = false; |
1384 | current.addLine = false; |
||
1385 | current.recalculateY = true; |
||
1386 | current.lastInRowLine = false; |
||
1387 | current.addLeftIndent = true; |
||
1388 | current.wasFirstInRow = false; |
||
23238 | jghali | 1389 | current.leftIndent = 0.0; |
1390 | current.rightIndent = 0.0; |
||
16868 | jghali | 1391 | current.rightMargin = 0.0; |
1392 | current.mustLineEnd = current.colRight; |
||
1393 | current.restartX = 0; |
||
17826 | craig | 1394 | |
24665 | jghali | 1395 | //why emit invalidating signals each time text is changed by applying styles? |
17826 | craig | 1396 | //this speed up layouting in case of using notes marks and drop caps |
1397 | itemText.blockSignals(true); |
||
17595 | craig | 1398 | setMaxY(-1); |
17599 | craig | 1399 | double maxYAsc = 0.0, maxYDesc = 0.0; |
18482 | jghali | 1400 | int regionMinY = 0, regionMaxY= 0; |
21563 | jghali | 1401 | |
21155 | jghali | 1402 | double autoLeftIndent = 0.0; |
21563 | jghali | 1403 | for (int i = 0; shapedText.haveMoreText(i, glyphClusters); ++i) |
21107 | craig | 1404 | { |
21715 | craig | 1405 | int currentIndex = i - current.lineData.firstCluster; |
21563 | jghali | 1406 | GlyphCluster newRun = glyphClusters[i]; |
1407 | if (currentIndex >= current.glyphs.count()) |
||
1408 | current.glyphs.append(newRun); |
||
21155 | jghali | 1409 | else |
21563 | jghali | 1410 | current.glyphs[currentIndex] = newRun; |
21155 | jghali | 1411 | |
21563 | jghali | 1412 | if (current.glyphs[currentIndex].glyphs().isEmpty()) |
21107 | craig | 1413 | continue; |
16868 | jghali | 1414 | |
21563 | jghali | 1415 | int a = current.glyphs[currentIndex].firstChar(); |
18524 | avox | 1416 | bool HasObject = itemText.hasObject(a); |
21563 | jghali | 1417 | PageItem* currentObject = itemText.object(a).getPageItem(m_Doc); |
21301 | fschmid | 1418 | QRectF currentObjectBox = QRectF(); |
1419 | if (HasObject) |
||
1420 | currentObjectBox = currentObject->getVisualBoundingRect(); |
||
21563 | jghali | 1421 | |
18524 | avox | 1422 | bool HasMark = itemText.hasMark(a); |
1423 | |||
17826 | craig | 1424 | if (HasMark) |
1425 | { |
||
21107 | craig | 1426 | Mark* mark = itemText.mark(a); |
17826 | craig | 1427 | //store mark pointer and position in text |
18524 | avox | 1428 | if (mark->isType(MARKNoteMasterType)) |
1429 | noteMarksPosMap.insert(a, mark); |
||
21107 | craig | 1430 | } |
18524 | avox | 1431 | |
18047 | craig | 1432 | BulNumMode = false; |
21563 | jghali | 1433 | if (itemText.isBlockStart(a)) |
18047 | craig | 1434 | { |
1435 | autoLeftIndent = 0.0; |
||
1436 | style = itemText.paragraphStyle(a); |
||
1437 | if (style.hasBullet() || style.hasNum()) |
||
1438 | { |
||
1439 | BulNumMode = true; |
||
1440 | } |
||
1441 | } |
||
21107 | craig | 1442 | |
1443 | if (current.isEmpty) |
||
18047 | craig | 1444 | opticalMargins = style.opticalMargins(); |
20550 | craig | 1445 | //-->#13490 |
21107 | craig | 1446 | if (isNoteFrame() && !HasMark) |
20550 | craig | 1447 | { |
1448 | StyleFlag s(itemText.charStyle(a).effects()); |
||
1449 | s &= ~ScStyle_Superscript; |
||
1450 | CharStyle haveSuperscript; |
||
1451 | haveSuperscript.setFeatures(s.featureList()); |
||
1452 | itemText.applyCharStyle(a, 1, haveSuperscript); |
||
1453 | } |
||
1454 | //--<#13490 |
||
18524 | avox | 1455 | CharStyle charStyle = ((itemText.text(a) != SpecialChars::PARSEP) ? itemText.charStyle(a) : style.charStyle()); |
17826 | craig | 1456 | |
16868 | jghali | 1457 | double hlcsize10 = charStyle.fontSize() / 10.0; |
1458 | double scaleV = charStyle.scaleV() / 1000.0; |
||
1459 | double scaleH = charStyle.scaleH() / 1000.0; |
||
17826 | craig | 1460 | double offset = hlcsize10 * (charStyle.baselineOffset() / 1000.0); |
16840 | jghali | 1461 | style.setLineSpacing (calculateLineSpacing (style, this)); |
17760 | jghali | 1462 | FlopBaseline = (current.startOfCol && firstLineOffset() == FLOPBaselineGrid); |
1463 | |||
5408 | avox | 1464 | // find out about par gap and dropcap |
5721 | avox | 1465 | if (a == firstInFrame()) |
3614 | cbradney | 1466 | { |
21563 | jghali | 1467 | if (itemText.isBlockStart(a)) |
5362 | avox | 1468 | { |
23922 | jghali | 1469 | if (!itemText.isBlockStart(a + 1)) |
5362 | avox | 1470 | { |
5721 | avox | 1471 | DropCmode = style.hasDropCap(); |
5362 | avox | 1472 | if (DropCmode) |
5721 | avox | 1473 | DropLines = style.dropCapLines(); |
5362 | avox | 1474 | } |
1475 | else |
||
1476 | DropCmode = false; |
||
21715 | craig | 1477 | current.lineData.isFirstLine = true; |
5362 | avox | 1478 | } |
3614 | cbradney | 1479 | } |
8814 | cbradney | 1480 | |
21563 | jghali | 1481 | const ScFace& font = charStyle.font(); |
17757 | jghali | 1482 | |
21563 | jghali | 1483 | current.glyphs[currentIndex].clearFlag(ScLayout_DropCap); |
1484 | current.glyphs[currentIndex].clearFlag(ScLayout_SoftHyphenVisible); |
||
8814 | cbradney | 1485 | |
18524 | avox | 1486 | // No space at begin of line, |
8150 | avox | 1487 | if (legacy) |
3614 | cbradney | 1488 | { |
8150 | avox | 1489 | // unless at begin of par (eeks) |
21107 | craig | 1490 | if ( (current.isEmpty) && (SpecialChars::isBreakingSpace(itemText.text(a))) |
23922 | jghali | 1491 | && (a > 0 && ! SpecialChars::isBreak(itemText.text(a - 1))) |
1492 | && ! (a > 0 && SpecialChars::isBreakingSpace(itemText.text(a - 1)) |
||
21563 | jghali | 1493 | && (!glyphClusters[i - 1].hasFlag(ScLayout_SuppressSpace)))) |
8150 | avox | 1494 | { |
21563 | jghali | 1495 | current.glyphs[currentIndex].setFlag(ScLayout_SuppressSpace); |
8150 | avox | 1496 | continue; |
1497 | } |
||
22638 | craig | 1498 | current.glyphs[currentIndex].clearFlag(ScLayout_SuppressSpace); |
5362 | avox | 1499 | } |
8150 | avox | 1500 | else // from 134 on use NBSPACE for this effect |
1501 | { |
||
23711 | jghali | 1502 | if (current.isEmpty && (SpecialChars::isBreakingSpace(itemText.text(a)) || itemText.text(a).isSpace())) |
8150 | avox | 1503 | { |
21563 | jghali | 1504 | current.glyphs[currentIndex].setFlag(ScLayout_SuppressSpace); |
8150 | avox | 1505 | continue; |
1506 | } |
||
22638 | craig | 1507 | current.glyphs[currentIndex].clearFlag(ScLayout_SuppressSpace); |
8150 | avox | 1508 | } |
21107 | craig | 1509 | if (current.isEmpty) |
5362 | avox | 1510 | { |
18524 | avox | 1511 | if (style.rightMargin() == 0) |
3614 | cbradney | 1512 | { |
16868 | jghali | 1513 | //addLine = true; |
1514 | current.rightMargin = 0.0; |
||
1515 | } |
||
1516 | else |
||
1517 | { |
||
1518 | if (current.lastInRowLine) |
||
1519 | current.rightMargin = style.rightMargin(); |
||
1520 | else |
||
1521 | current.rightMargin = 0.0; |
||
1522 | } |
||
1523 | current.breakIndex = -1; |
||
1524 | if (current.startOfCol && !current.afterOverflow && current.recalculateY) |
||
19831 | craig | 1525 | current.yPos = qMax(current.yPos, m_textDistanceMargins.top()); |
16868 | jghali | 1526 | // more about par gap and dropcaps |
23912 | craig | 1527 | if ((a > firstInFrame() && itemText.isBlockStart(a)) || (a == 0 && m_backBox == nullptr && current.startOfCol)) |
16868 | jghali | 1528 | { |
1529 | if (!current.afterOverflow && current.recalculateY && !current.startOfCol) |
||
1530 | current.yPos += style.gapBefore(); |
||
6370 | avox | 1531 | DropCapDrop = 0; |
23922 | jghali | 1532 | if (!itemText.isBlockStart(a + 1)) |
5721 | avox | 1533 | DropCmode = style.hasDropCap(); |
5362 | avox | 1534 | else |
1535 | DropCmode = false; |
||
16868 | jghali | 1536 | if (DropCmode && !current.afterOverflow) |
5362 | avox | 1537 | { |
5721 | avox | 1538 | DropLines = style.dropCapLines(); |
21347 | jghali | 1539 | // DropCapDrop = calculateLineSpacing (style, this) * (DropLines - 1); |
13085 | jghali | 1540 | // qDebug() << QString("dropcapdrop: y=%1+%2").arg(current.yPos).arg(DropCapDrop); |
5362 | avox | 1541 | } |
21715 | craig | 1542 | current.lineData.isFirstLine = true; |
3614 | cbradney | 1543 | } |
5362 | avox | 1544 | } |
16868 | jghali | 1545 | // find charsize factors |
5362 | avox | 1546 | if (DropCmode) |
1547 | { |
||
21347 | jghali | 1548 | DropCapDrop = calculateLineSpacing (style, this) * (DropLines - 1); |
17140 | jghali | 1549 | |
18524 | avox | 1550 | //text height, width, ascent and descent should be calculated for whole text provided by ScText in current position |
17826 | craig | 1551 | //and that may be more than one char (variable text for example) |
1552 | double realCharHeight = 0.0, realCharAscent = 0.0; |
||
22506 | jghali | 1553 | const QList<GlyphLayout>& glyphs = current.glyphs[currentIndex].glyphs(); |
1554 | for (const GlyphLayout& gl : glyphs) { |
||
22682 | jghali | 1555 | GlyphMetrics gm = font.glyphBBox(gl.glyph, style.charStyle().fontSize() / 10.0); |
21107 | craig | 1556 | realCharHeight = qMax(realCharHeight, gm.ascent + gm.descent); |
1557 | realCharAscent = qMax(realCharAscent, gm.ascent); |
||
17826 | craig | 1558 | } |
14929 | jghali | 1559 | if (realCharHeight == 0.0) |
17757 | jghali | 1560 | realCharHeight = font.height(style.charStyle().fontSize() / 10.0); |
14929 | jghali | 1561 | if (realCharAscent == 0.0) |
22682 | jghali | 1562 | realCharAscent = font.ascent(style.charStyle().fontSize() / 10.0); |
22799 | jghali | 1563 | if (current.startOfCol && (m_firstLineOffset == FLOPFontAscent)) |
22682 | jghali | 1564 | realCharAscent = font.ascent(hlcsize10); |
1565 | chsd = ((DropCapDrop + realCharAscent) / realCharHeight) * style.charStyle().fontSize(); |
||
1566 | chs = ((DropCapDrop + realCharAscent) / realCharAscent) * style.charStyle().fontSize(); |
||
21563 | jghali | 1567 | current.glyphs[currentIndex].setFlag(ScLayout_DropCap); |
17826 | craig | 1568 | if (HasObject) |
17199 | fschmid | 1569 | { |
21301 | fschmid | 1570 | // chs = qRound((currentObject->height() + currentObject->lineWidth()) * 10); |
1571 | // chsd = qRound((currentObject->height() + currentObject->lineWidth()) * 10); |
||
1572 | chs = currentObjectBox.height() * 10; |
||
1573 | chsd = currentObjectBox.height() * 10; |
||
17199 | fschmid | 1574 | } |
5362 | avox | 1575 | } |
5408 | avox | 1576 | else // ! dropCapMode |
5362 | avox | 1577 | { |
17826 | craig | 1578 | if (HasObject) |
21301 | fschmid | 1579 | chs = currentObjectBox.height() * 10; |
1580 | // chs = qRound((currentObject->height() + currentObject->lineWidth()) * 10); |
||
5362 | avox | 1581 | else |
6144 | avox | 1582 | chs = charStyle.fontSize(); |
5362 | avox | 1583 | } |
21563 | jghali | 1584 | // set StartOfLine |
1585 | if (current.isEmpty) |
||
1586 | current.glyphs[currentIndex].setFlag(ScLayout_StartOfLine); |
||
1587 | else |
||
1588 | current.glyphs[currentIndex].clearFlag(ScLayout_StartOfLine); |
||
1589 | |||
1590 | if (!current.glyphs[currentIndex].hasFlag(ScLayout_StartOfLine)) |
||
1591 | { |
||
1592 | double tracking = charStyle.fontSize() * charStyle.tracking() / 10000.0; |
||
1593 | current.glyphs[currentIndex].xoffset += tracking; |
||
1594 | if (current.glyphs[currentIndex].width() != 0) |
||
1595 | current.glyphs[currentIndex].extraWidth += tracking; |
||
1596 | } |
||
1597 | |||
21107 | craig | 1598 | // glyphs->yadvance = 0; |
18524 | avox | 1599 | |
23709 | jghali | 1600 | // adjust space between CJK and Latin letter |
1601 | if (!current.glyphs[currentIndex].hasFlag(ScLayout_StartOfLine) && current.glyphs[currentIndex].hasFlag(ScLayout_CJKLatinSpace)) |
||
1602 | { |
||
1603 | double quaterEM = charStyle.fontSize() / 10 / 4; |
||
1604 | current.glyphs[currentIndex].extraWidth += quaterEM; |
||
1605 | current.glyphs[currentIndex].xoffset += quaterEM; |
||
1606 | } |
||
1607 | |||
21715 | craig | 1608 | if (i == current.lineData.firstCluster && current.glyphs[currentIndex].hasFlag(ScLayout_CJKFence)) |
14417 | jghali | 1609 | { |
21563 | jghali | 1610 | current.glyphs[currentIndex].extraWidth -= (charStyle.fontSize() / 10 / 2); |
1611 | current.glyphs[currentIndex].xoffset -= (charStyle.fontSize() / 10 / 2); |
||
5362 | avox | 1612 | } |
21107 | craig | 1613 | // find out width, ascent and descent of char |
21563 | jghali | 1614 | double wide = current.glyphs[currentIndex].width(); |
21107 | craig | 1615 | |
5362 | avox | 1616 | if (DropCmode) |
1617 | { |
||
5408 | avox | 1618 | // drop caps are wider... |
21638 | jghali | 1619 | GlyphCluster& glyphCluster = current.glyphs[currentIndex]; |
17826 | craig | 1620 | if (HasObject) |
5362 | avox | 1621 | { |
21301 | fschmid | 1622 | // double itemHeight = currentObject->height() + currentObject->lineWidth(); |
1623 | // if (itemHeight == 0) |
||
1624 | // itemHeight = font.height(style.charStyle().fontSize() / 10.0); |
||
1625 | // asce = currentObject->height() + currentObject->lineWidth(); |
||
1626 | // wide = currentObject->width() + currentObject->lineWidth(); |
||
1627 | double itemHeight = currentObjectBox.height(); |
||
14929 | jghali | 1628 | if (itemHeight == 0) |
17757 | jghali | 1629 | itemHeight = font.height(style.charStyle().fontSize() / 10.0); |
21301 | fschmid | 1630 | asce = currentObjectBox.height(); |
1631 | wide = currentObjectBox.width(); |
||
16868 | jghali | 1632 | realAsce = calculateLineSpacing (style, this) * DropLines; |
21638 | jghali | 1633 | glyphCluster.setScaleH(glyphCluster.scaleH() / glyphCluster.scaleV()); |
1634 | glyphCluster.setScaleV(realAsce / itemHeight); |
||
1635 | glyphCluster.setScaleH(glyphCluster.scaleH() * glyphCluster.scaleV()); |
||
1636 | wide *= glyphCluster.scaleH(); |
||
5362 | avox | 1637 | } |
1638 | else |
||
1639 | { |
||
22682 | jghali | 1640 | double realCharHeight = 0.0, realCharAscent = 0.0; |
17826 | craig | 1641 | wide = 0.0; realAsce = 0.0; |
22506 | jghali | 1642 | const QList<GlyphLayout>& glyphs = glyphCluster.glyphs(); |
1643 | for (const GlyphLayout& gl : glyphs) { |
||
22600 | craig | 1644 | GlyphMetrics gm = font.glyphBBox(gl.glyph, charStyle.fontSize() / 10.0); |
21107 | craig | 1645 | realCharHeight = qMax(realCharHeight, gm.ascent + gm.descent); |
22682 | jghali | 1646 | realCharAscent = qMax(realCharAscent, gm.ascent); |
21107 | craig | 1647 | gm = font.glyphBBox(gl.glyph, chsd / 10.0); |
1648 | realAsce = qMax(realAsce, gm.ascent + gm.descent); |
||
21356 | jghali | 1649 | wide += gm.width; |
17826 | craig | 1650 | } |
23707 | jghali | 1651 | wide = (wide * scaleH) + (1 - scaleH); |
17826 | craig | 1652 | realAsce = realAsce * scaleV + offset; |
14929 | jghali | 1653 | if (realCharHeight == 0) |
17757 | jghali | 1654 | realCharHeight = font.height(style.charStyle().fontSize() / 10.0); |
22682 | jghali | 1655 | if (realCharAscent == 0) |
1656 | realCharAscent = font.ascent(style.charStyle().fontSize() / 10.0); |
||
1657 | asce = realCharAscent; |
||
22799 | jghali | 1658 | if (current.startOfCol && (m_firstLineOffset == FLOPFontAscent)) |
22682 | jghali | 1659 | asce = font.ascent(hlcsize10); |
21638 | jghali | 1660 | glyphCluster.setScaleH(glyphCluster.scaleH() / glyphCluster.scaleV()); |
1661 | glyphCluster.setScaleV(realAsce / realCharHeight); |
||
1662 | glyphCluster.setScaleH(glyphCluster.scaleH() * glyphCluster.scaleV()); |
||
1663 | glyphCluster.xoffset -= 0.5; //drop caps are always to far from column left edge |
||
5362 | avox | 1664 | } |
21194 | jghali | 1665 | // This is to mimic pre-boxes branches in case first character of paragraph is a space |
1666 | // If we don't do this, paragraph offset will not apply correctly to first line |
||
21638 | jghali | 1667 | if ((glyphCluster.scaleH() == 0.0) || (glyphCluster.scaleV() == 0.0)) |
21194 | jghali | 1668 | { |
21638 | jghali | 1669 | glyphCluster.setScaleH(1.0); |
1670 | glyphCluster.setScaleV(1.0); |
||
21194 | jghali | 1671 | wide = 0.0; |
1672 | } |
||
19338 | jghali | 1673 | desc = realDesc = 0; |
5362 | avox | 1674 | } |
8814 | cbradney | 1675 | else // !DropCMode |
5362 | avox | 1676 | { |
5408 | avox | 1677 | // find ascent / descent |
17826 | craig | 1678 | if (HasObject) |
16868 | jghali | 1679 | desc = realDesc = 0; |
5362 | avox | 1680 | else |
1681 | { |
||
21107 | craig | 1682 | if (itemText.text(a) != SpecialChars::OBJECT) |
17826 | craig | 1683 | { |
22506 | jghali | 1684 | const QList<GlyphLayout>& glyphs = current.glyphs[currentIndex].glyphs(); |
1685 | for (const GlyphLayout& gl : glyphs) |
||
21301 | fschmid | 1686 | { |
21107 | craig | 1687 | GlyphMetrics gm = font.glyphBBox(gl.glyph, hlcsize10); |
21174 | fschmid | 1688 | realDesc = qMax(realDesc, gm.descent * scaleV - offset); |
21107 | craig | 1689 | realAsce = gm.ascent; |
1690 | } |
||
17826 | craig | 1691 | } |
17757 | jghali | 1692 | desc = -font.descent(hlcsize10); |
21107 | craig | 1693 | current.rememberShrinkStretch(itemText.text(a), wide, style); |
5362 | avox | 1694 | } |
17826 | craig | 1695 | if (HasObject) |
17199 | fschmid | 1696 | { |
21301 | fschmid | 1697 | // asce = currentObject->height() + currentObject->lineWidth(); |
1698 | asce = currentObjectBox.height(); |
||
17199 | fschmid | 1699 | realAsce = asce * scaleV + offset; |
21301 | fschmid | 1700 | wide = currentObjectBox.width() * scaleH; |
17199 | fschmid | 1701 | } |
1702 | else |
||
1703 | { |
||
17757 | jghali | 1704 | asce = font.ascent(hlcsize10); |
18047 | craig | 1705 | if (HasMark && !BulNumMode) |
17826 | craig | 1706 | realAsce = asce * scaleV + offset; |
1707 | else |
||
1708 | { |
||
22506 | jghali | 1709 | const QList<GlyphLayout>& glyphs = current.glyphs[currentIndex].glyphs(); |
1710 | for (const GlyphLayout& gl : glyphs) |
||
21107 | craig | 1711 | realAsce = qMax(realAsce, font.glyphBBox(gl.glyph, hlcsize10).ascent * scaleV + offset); |
17826 | craig | 1712 | } |
17199 | fschmid | 1713 | } |
5362 | avox | 1714 | } |
18047 | craig | 1715 | // if (BulNumMode) |
1716 | // hl->glyph.last()->xadvance += style.parEffectOffset(); |
||
16868 | jghali | 1717 | //check for Y position at beginning of line |
21107 | craig | 1718 | if (current.isEmpty && !current.afterOverflow) |
5362 | avox | 1719 | { |
16868 | jghali | 1720 | if (current.recalculateY) |
5362 | avox | 1721 | { |
16868 | jghali | 1722 | //if top of column Y position depends on first line offset |
1723 | if (current.startOfCol) |
||
3614 | cbradney | 1724 | { |
19831 | craig | 1725 | lastLineY = qMax(lastLineY, m_textDistanceMargins.top() + lineCorr); |
17760 | jghali | 1726 | if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing || FlopBaseline) |
3614 | cbradney | 1727 | { |
17760 | jghali | 1728 | if (current.yPos <= lastLineY) |
23922 | jghali | 1729 | current.yPos = lastLineY + 1; |
18032 | craig | 1730 | double by = m_yPos; |
17760 | jghali | 1731 | if (OwnPage != -1) |
18032 | craig | 1732 | by = m_yPos - m_Doc->Pages->at(OwnPage)->yOffset(); |
17760 | jghali | 1733 | int ol1 = qRound((by + current.yPos - m_Doc->guidesPrefs().offsetBaselineGrid) * 10000.0); |
1734 | int ol2 = static_cast<int>(ol1 / m_Doc->guidesPrefs().valueBaselineGrid); |
||
1735 | current.yPos = ceil( ol2 / 10000.0 ) * m_Doc->guidesPrefs().valueBaselineGrid + m_Doc->guidesPrefs().offsetBaselineGrid - by; |
||
1736 | } |
||
1737 | else if (style.lineSpacingMode() != ParagraphStyle::BaselineGridLineSpacing) |
||
1738 | { |
||
16868 | jghali | 1739 | if (firstLineOffset() == FLOPRealGlyphHeight) |
1740 | { |
||
21422 | jghali | 1741 | if (DropCmode || (itemText.text(a) == SpecialChars::PARSEP)) |
16868 | jghali | 1742 | current.yPos += asce; |
1743 | else |
||
1744 | current.yPos += realAsce; |
||
1745 | } |
||
1746 | else if (firstLineOffset() == FLOPLineSpacing) |
||
1747 | current.yPos += style.lineSpacing(); |
||
1748 | else |
||
1749 | current.yPos += asce; |
||
3614 | cbradney | 1750 | } |
1751 | } |
||
16868 | jghali | 1752 | else |
1753 | { |
||
1754 | if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing) |
||
1755 | { |
||
1756 | current.yPos += m_Doc->guidesPrefs().valueBaselineGrid; |
||
18032 | craig | 1757 | double by = m_yPos; |
16868 | jghali | 1758 | if (OwnPage != -1) |
18032 | craig | 1759 | by = m_yPos - m_Doc->Pages->at(OwnPage)->yOffset(); |
16868 | jghali | 1760 | int ol1 = qRound((by + current.yPos - m_Doc->guidesPrefs().offsetBaselineGrid) * 10000.0); |
1761 | int ol2 = static_cast<int>(ol1 / m_Doc->guidesPrefs().valueBaselineGrid); |
||
1762 | current.yPos = ceil( ol2 / 10000.0 ) * m_Doc->guidesPrefs().valueBaselineGrid + m_Doc->guidesPrefs().offsetBaselineGrid - by; |
||
1763 | } |
||
1764 | else |
||
1765 | current.yPos += style.lineSpacing(); |
||
1766 | } |
||
1767 | if (DropCmode) |
||
1768 | current.yPos += DropCapDrop; |
||
5362 | avox | 1769 | } |
16868 | jghali | 1770 | //set left indentation |
1771 | current.leftIndent = 0.0; |
||
22135 | jghali | 1772 | if (current.addLeftIndent && ((maxDX == 0) || DropCmode || BulNumMode)) |
5362 | avox | 1773 | { |
18047 | craig | 1774 | current.leftIndent = style.leftMargin() + autoLeftIndent; |
21563 | jghali | 1775 | if (itemText.isBlockStart(a)) |
18047 | craig | 1776 | { |
23238 | jghali | 1777 | if (style.direction() == ParagraphStyle::RTL) |
1778 | { |
||
23659 | jghali | 1779 | // use rightIndent to not mess with old behavior |
23238 | jghali | 1780 | current.rightIndent = style.firstIndent(); |
1781 | // line width should consider RTL indent when it breaks the line. |
||
23674 | jghali | 1782 | current.mustLineEnd = current.colRight - current.rightIndent; |
23238 | jghali | 1783 | } |
1784 | else |
||
1785 | current.leftIndent += style.firstIndent(); |
||
18047 | craig | 1786 | if (BulNumMode || DropCmode) |
1787 | { |
||
19927 | jghali | 1788 | if (style.parEffectIndent()) |
18047 | craig | 1789 | { |
1790 | current.leftIndent -= style.parEffectOffset() + wide; |
||
1791 | if (current.leftIndent < 0.0) |
||
1792 | { |
||
1793 | autoLeftIndent = abs(current.leftIndent); |
||
1794 | current.leftIndent = 0.0; |
||
1795 | } |
||
1796 | } |
||
1797 | } |
||
1798 | } |
||
16868 | jghali | 1799 | current.addLeftIndent = false; |
5362 | avox | 1800 | } |
1801 | } |
||
16868 | jghali | 1802 | current.recalculateY = true; |
22598 | craig | 1803 | maxYAsc = maxYDesc = 0.0; |
18047 | craig | 1804 | double addAsce = 0.0; |
16868 | jghali | 1805 | if (current.startOfCol) |
5362 | avox | 1806 | { |
17599 | craig | 1807 | if (DropCmode) |
1808 | addAsce = qMax(realAsce, asce + offset); |
||
1809 | else |
||
1810 | addAsce = asce + offset; |
||
16868 | jghali | 1811 | if (style.lineSpacingMode() != ParagraphStyle::BaselineGridLineSpacing) |
5362 | avox | 1812 | { |
16868 | jghali | 1813 | if (firstLineOffset() == FLOPRealGlyphHeight) |
1814 | addAsce = realAsce; |
||
1815 | else if (firstLineOffset() == FLOPLineSpacing) |
||
24104 | jghali | 1816 | { |
1817 | if (DropCmode) |
||
1818 | addAsce = DropCapDrop + style.lineSpacing(); |
||
1819 | else |
||
1820 | addAsce = style.lineSpacing(); |
||
1821 | } |
||
5362 | avox | 1822 | } |
17595 | craig | 1823 | maxYAsc = current.yPos - addAsce; |
16868 | jghali | 1824 | } |
1825 | else |
||
17599 | craig | 1826 | maxYAsc = current.yPos - realAsce; |
16913 | jghali | 1827 | //fix for glyphs with negative realAsce value |
17595 | craig | 1828 | maxYAsc = qMax(maxYAsc, 0.0); |
17599 | craig | 1829 | maxYDesc = current.yPos + realDesc; |
24104 | jghali | 1830 | if (style.lineSpacingMode() != ParagraphStyle::AutomaticLineSpacing) |
18524 | avox | 1831 | { |
24271 | jghali | 1832 | // #11727, #11628, etc. |
1833 | maxYAsc = qMax(0.0, qMin(maxYAsc, current.yPos - asce)); |
||
24104 | jghali | 1834 | maxYDesc = current.yPos + desc; |
18524 | avox | 1835 | } |
18482 | jghali | 1836 | |
24104 | jghali | 1837 | regionMinY = static_cast<int>(floor(maxYAsc)); |
1838 | regionMaxY = static_cast<int>(floor(maxYDesc)); |
||
1839 | |||
21107 | craig | 1840 | if (current.isEmpty && !current.afterOverflow) |
16868 | jghali | 1841 | { |
1842 | //start a new line |
||
1843 | goNoRoom = false; |
||
1844 | |||
1845 | // find line`s start |
||
18482 | jghali | 1846 | pt1 = QPoint(static_cast<int>(floor(current.xPos)), regionMinY); |
23922 | jghali | 1847 | pt2 = QPoint(static_cast<int>(floor(current.xPos + (style.minGlyphExtension() * wide))), regionMaxY - 1); |
16868 | jghali | 1848 | pt = QRect(pt1, pt2); |
1849 | realEnd = 0; |
||
1850 | //check if there is overflow at start of line, if so jump behind it and check again |
||
1851 | double Xpos, Xend; |
||
1852 | bool done = false; |
||
1853 | bool newColumn = false; |
||
18524 | avox | 1854 | |
17215 | jghali | 1855 | //FIX ME - that should be paragraph style`s properties |
1856 | //if set then indent is add to possible line start point (after overflow) |
||
1857 | //if not then indent is calculated from column left edge |
||
21017 | craig | 1858 | //if you don't agree that adding indent to overflow should be default behaviour |
17215 | jghali | 1859 | //then change it to false |
1860 | bool addIndent2overflow = false; // should be addIndent2Overflow = style.addIndent2Overlow(); |
||
1861 | bool addFirstIndent2overflow = true; // should be addFirstIndent2Overflow = style.addFirstIndent2Overlow(); |
||
1862 | //if first line indent is negative and left indent should not be added to overflow |
||
21017 | craig | 1863 | //then don't add first line ident either |
17215 | jghali | 1864 | if ((style.firstIndent() < 0) && !addIndent2overflow) |
1865 | addFirstIndent2overflow = false; |
||
1866 | |||
16868 | jghali | 1867 | while (!done) |
8814 | cbradney | 1868 | { |
17215 | jghali | 1869 | Xpos = current.xPos + (addIndent2overflow ? 0 : current.leftIndent); |
16868 | jghali | 1870 | Xend = current.xPos + current.leftIndent; |
1871 | //check if in indent any overflow occurs |
||
1872 | while (Xpos <= Xend && Xpos < current.colRight) |
||
3614 | cbradney | 1873 | { |
18482 | jghali | 1874 | pt.moveTopLeft(QPoint(static_cast<int>(floor(Xpos)), regionMinY)); |
17578 | craig | 1875 | if (!regionContainsRect(m_availableRegion, pt)) |
16868 | jghali | 1876 | { |
17578 | craig | 1877 | Xpos = current.xPos = realEnd = findRealOverflowEnd(m_availableRegion, pt, current.colRight); |
17215 | jghali | 1878 | Xend = current.xPos + (addIndent2overflow ? current.leftIndent : 0); |
1879 | //for first paragraph`s line - if first line offset should be added |
||
21563 | jghali | 1880 | if ( addFirstIndent2overflow && itemText.isBlockStart(a)) |
17215 | jghali | 1881 | Xend += style.firstIndent(); |
16868 | jghali | 1882 | } |
1883 | else |
||
1884 | Xpos++; |
||
1885 | } |
||
1886 | current.xPos = Xend; |
||
1887 | done = true; |
||
17215 | jghali | 1888 | if (current.isEndOfLine((style.minGlyphExtension() * wide) + current.rightMargin)) |
16868 | jghali | 1889 | { |
5408 | avox | 1890 | // new line |
16868 | jghali | 1891 | current.xPos = qMax(current.colLeft, maxDX); |
17760 | jghali | 1892 | if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing || FlopBaseline) |
5362 | avox | 1893 | { |
17085 | jghali | 1894 | current.yPos++; |
18032 | craig | 1895 | double by = m_yPos; |
16868 | jghali | 1896 | if (OwnPage != -1) |
18032 | craig | 1897 | by = m_yPos - m_Doc->Pages->at(OwnPage)->yOffset(); |
16868 | jghali | 1898 | int ol1 = qRound((by + current.yPos - m_Doc->guidesPrefs().offsetBaselineGrid) * 10000.0); |
1899 | int ol2 = static_cast<int>(ol1 / m_Doc->guidesPrefs().valueBaselineGrid); |
||
1900 | current.yPos = ceil( ol2 / 10000.0 ) * m_Doc->guidesPrefs().valueBaselineGrid + m_Doc->guidesPrefs().offsetBaselineGrid - by; |
||
1901 | } |
||
17085 | jghali | 1902 | else if (style.lineSpacingMode() == ParagraphStyle::FixedLineSpacing) |
1903 | current.yPos += (current.startOfCol ? 1 : style.lineSpacing()); |
||
1904 | else |
||
21188 | fschmid | 1905 | current.yPos += (current.startOfCol ? 1 : style.lineSpacing()); |
17595 | craig | 1906 | lastLineY = maxYAsc; |
24104 | jghali | 1907 | |
24022 | jghali | 1908 | if (current.startOfCol) |
17595 | craig | 1909 | maxYAsc = current.yPos - addAsce; |
16868 | jghali | 1910 | else |
24022 | jghali | 1911 | maxYAsc = current.yPos - realAsce; |
24104 | jghali | 1912 | maxYAsc = qMax(maxYAsc, 0.0); |
17599 | craig | 1913 | maxYDesc = current.yPos + realDesc; |
24104 | jghali | 1914 | if (style.lineSpacingMode() != ParagraphStyle::AutomaticLineSpacing) |
18524 | avox | 1915 | { |
24271 | jghali | 1916 | // #11727, #11628, etc. |
1917 | maxYAsc = qMax(0.0, qMin(maxYAsc, current.yPos - asce)); |
||
24104 | jghali | 1918 | maxYDesc = current.yPos + desc; |
18524 | avox | 1919 | } |
18482 | jghali | 1920 | |
24104 | jghali | 1921 | regionMinY = static_cast<int>(floor(maxYAsc)); |
1922 | regionMaxY = static_cast<int>(floor(maxYDesc)); |
||
1923 | |||
18482 | jghali | 1924 | pt.moveTopLeft(QPoint(static_cast<int>(floor(current.xPos)), regionMinY)); |
16868 | jghali | 1925 | done = false; |
5362 | avox | 1926 | } |
16868 | jghali | 1927 | if (current.isEndOfCol(realDesc)) |
1928 | { |
||
1929 | current.column++; |
||
22832 | craig | 1930 | if (current.column < m_columns) |
16868 | jghali | 1931 | { |
1932 | newColumn = true; |
||
1933 | break; |
||
1934 | } |
||
22832 | craig | 1935 | m_maxChars = a; |
22638 | craig | 1936 | goto NoRoom; |
16868 | jghali | 1937 | } |
5362 | avox | 1938 | } |
16868 | jghali | 1939 | if (newColumn) |
5362 | avox | 1940 | { |
21107 | craig | 1941 | current.nextColumn(textLayout); |
16868 | jghali | 1942 | current.mustLineEnd = current.colRight; |
1943 | current.restartX = current.xPos; |
||
1944 | lastLineY = current.yPos; |
||
1945 | current.rowDesc = 0; |
||
21107 | craig | 1946 | i--; |
16868 | jghali | 1947 | current.recalculateY = true; |
1948 | current.addLeftIndent = true; |
||
1949 | continue; |
||
5362 | avox | 1950 | } |
21715 | craig | 1951 | current.lineData.x = current.restartX = current.xPos; |
1952 | current.lineData.y = current.yPos; |
||
21563 | jghali | 1953 | // if (glyphClusters[current.line.firstCluster].hasFlag(ScLayout_DropCap)) |
21142 | fschmid | 1954 | // current.line.y -= DropCapDrop; |
16868 | jghali | 1955 | } |
1956 | |||
1957 | //check if line must start at new Y position due to current glyph height or previous line descent |
||
21107 | craig | 1958 | if (!SpecialChars::isBreak(itemText.text(a), true) |
1959 | && !SpecialChars::isBreakingSpace(itemText.text(a)) |
||
1960 | && !SpecialChars::isExpandingSpace(itemText.text(a)) |
||
1961 | && itemText.text(a) != SpecialChars::TAB) |
||
16868 | jghali | 1962 | { |
1963 | double diff = 0; |
||
1964 | if (current.startOfCol || DropCmode) |
||
16982 | jghali | 1965 | diff = realAsce - (current.yPos - lastLineY); |
21242 | jghali | 1966 | else if (style.lineSpacingMode() != ParagraphStyle::FixedLineSpacing) |
17303 | fschmid | 1967 | { |
17826 | craig | 1968 | if (HasObject) |
21301 | fschmid | 1969 | // diff = (currentObject->height() + currentObject->lineWidth()) * scaleV + offset - (current.yPos - lastLineY); |
1970 | diff = (currentObjectBox.height() * scaleV + offset) - (current.yPos - lastLineY); |
||
17303 | fschmid | 1971 | else |
21313 | jghali | 1972 | { |
1973 | // Pre-boxes code equivalent below: |
||
1974 | // int glyphID = font.char2CMap(QChar('l')); |
||
1975 | // diff = font.glyphBBox(glyphID, hlcsize10).ascent * scaleV + offset - (current.yPos - lastLineY); |
||
1976 | diff = realAsce - (current.yPos - lastLineY); |
||
1977 | } |
||
17303 | fschmid | 1978 | } |
1979 | else |
||
1980 | { |
||
17826 | craig | 1981 | if (HasObject) |
21301 | fschmid | 1982 | // diff = (currentObject->height() + currentObject->lineWidth()) * scaleV + offset - (current.yPos - lastLineY); |
1983 | diff = currentObjectBox.height() * scaleV + offset - (current.yPos - lastLineY); |
||
17303 | fschmid | 1984 | } |
16868 | jghali | 1985 | if (diff >= 1 || (!DropCmode && diff > 0)) |
5362 | avox | 1986 | { |
16868 | jghali | 1987 | if (current.hasDropCap && DropLinesCount == 0) |
1988 | { |
||
1989 | current.hasDropCap = false; |
||
1990 | current.yPos = maxDY; |
||
1991 | maxDX = 0; |
||