Krita Source Code Documentation
Loading...
Searching...
No Matches
KoSvgTextShapeLayoutFunc_lines.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2017 Dmitry Kazakov <dimula73@gmail.com>
3 * SPDX-FileCopyrightText: 2022 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
9
10#include "KoSvgTextProperties.h"
11
12#include <FlakeDebug.h>
13
15{
16
29void calculateLineHeight(CharacterResult cr, double &ascent, double &descent, bool isHorizontal, bool compare)
30{
31 QRectF lineHeightBox = cr.lineHeightBox().translated(cr.totalBaselineOffset());
32 double offsetAsc = isHorizontal? lineHeightBox.top(): lineHeightBox.right();
33 double offsetDsc = isHorizontal? lineHeightBox.bottom(): lineHeightBox.left();
34
35 if (!compare) {
36 ascent = offsetAsc;
37 descent = offsetDsc;
38 } else {
39 if (isHorizontal) {
40 ascent = qMin(offsetAsc, ascent);
41 descent = qMax(offsetDsc, descent);
42 } else {
43 ascent = qMax(offsetAsc, ascent);
44 descent = qMin(offsetDsc, descent);
45 }
46 }
47}
48
55 QPointF &currentPos,
56 QVector<int> &wordIndices,
57 LineBox &currentLine,
58 bool ltr,
59 bool isHorizontal)
60{
61 QPointF lineAdvance = currentPos;
62
63 LineChunk currentChunk = currentLine.chunk();
64
65 Q_FOREACH (const int j, wordIndices) {
66 CharacterResult cr = result.at(j);
67 if (currentLine.isEmpty() && j == wordIndices.first()) {
68 if (result.at(j).lineStart == LineEdgeBehaviour::Collapse) {
69 if (isHorizontal) {
70 result[j].scaleCharacterResult(0.0, 1.0);
71 } else {
72 result[j].scaleCharacterResult(1.0, 0.0);
73 }
74 result[j].hidden = true;
75 continue;
76 }
77 cr.anchored_chunk = true;
78 if (result.at(j).lineStart == LineEdgeBehaviour::ForceHang && currentLine.firstLine) {
79 currentPos -= cr.advance;
80 cr.isHanging = true;
81 }
82 }
83 calculateLineHeight(cr, currentLine.actualLineTop, currentLine.actualLineBottom, isHorizontal, !cr.anchored_chunk);
84
85 cr.cssPosition = currentPos;
86 cr.calculateAndApplyTabsize(currentPos, isHorizontal, KoSvgText::ResolutionHandler());
87 currentPos += cr.advance;
88 lineAdvance = currentPos;
89
90 result[j] = cr;
91 currentChunk.boundingBox |= cr.layoutBox().translated(cr.cssPosition + cr.totalBaselineOffset());
92 }
93 currentPos = lineAdvance;
94 currentChunk.chunkIndices += wordIndices;
95 currentLine.setCurrentChunk(currentChunk);
96 wordIndices.clear();
97}
98
103static QPointF lineHeightOffset(KoSvgText::WritingMode writingMode,
105 LineBox &currentLine,
106 const bool firstLine,
107 const KoSvgText::ResolutionHandler resHandler)
108{
109 QPointF lineTop;
110 QPointF lineBottom;
111 QPointF correctionOffset;
114
115 if (currentLine.chunks.isEmpty()) {
116 return QPointF();
117 } else if (currentLine.chunks.size() == 1 && currentLine.actualLineTop == 0 &&
118 currentLine.actualLineBottom == 0){
124 QVector<int> chunkIndices = currentLine.chunks[0].chunkIndices;
125 if (chunkIndices.size() > 0) {
126 CharacterResult cr = result[chunkIndices.first()];
128 currentLine.actualLineTop,
129 currentLine.actualLineBottom,
130 writingMode == KoSvgText::HorizontalTB,
131 false);
132 result[chunkIndices.first()].anchored_chunk = true;
133 }
134 }
135
136 // We do qmin/qmax here so that later, the correction offset will not go below either value.
137 const qreal expectedLineTop = writingMode == KoSvgText::HorizontalTB? qMin(currentLine.expectedLineTop, currentLine.actualLineTop):
138 qMax(currentLine.expectedLineTop, currentLine.actualLineTop);
139 if (writingMode == KoSvgText::HorizontalTB) {
140 currentLine.baselineTop = QPointF(0, currentLine.actualLineTop);
141 currentLine.baselineBottom = QPointF(0, currentLine.actualLineBottom);
142 correctionOffset = QPointF(0, -expectedLineTop) + currentLine.baselineTop;
143 lineTop = -currentLine.baselineTop;
144 lineBottom = currentLine.baselineBottom;
145 } else if (writingMode == KoSvgText::VerticalLR) {
146 currentLine.baselineTop = QPointF(currentLine.actualLineTop, 0);
147 currentLine.baselineBottom = QPointF(currentLine.actualLineBottom, 0);
148 correctionOffset = QPointF(-expectedLineTop, 0) + currentLine.baselineTop;
149 // Note: while Vertical LR goes left-to-right in its lines, its lines themselves are
150 // oriented with the top pointed in the positive x direction.
151 lineBottom = currentLine.baselineTop;
152 lineTop = -currentLine.baselineBottom;
153 } else {
154 currentLine.baselineTop = QPointF(currentLine.actualLineTop, 0);
155 currentLine.baselineBottom = QPointF(currentLine.actualLineBottom, 0);
156 correctionOffset = QPointF(expectedLineTop, 0) - currentLine.baselineTop;
157 lineTop = -currentLine.baselineTop;
158 lineBottom = currentLine.baselineBottom;
159 }
160 bool returnDescent = firstLine;
161 QPointF offset = resHandler.adjust(lineTop + lineBottom);
162 if (resHandler.roundToPixelHorizontal || resHandler.roundToPixelVertical) {
163 lineTop = offset - resHandler.adjust(lineBottom);
164 correctionOffset = resHandler.adjust(correctionOffset);
165 }
166
167 if (!returnDescent) {
168 for (auto chunk = currentLine.chunks.begin(); chunk != currentLine.chunks.end(); chunk++) {
169 Q_FOREACH (int j, chunk->chunkIndices) {
170 result[j].cssPosition += lineTop;
171 result[j].cssPosition += result[j].totalBaselineOffset();
172 result[j].finalPosition = result.at(j).cssPosition;
173 }
174 chunk->length.translate(lineTop);
175 chunk->boundingBox.translate(lineTop);
176 }
177 } else {
178 offset = lineBottom - correctionOffset;
179 for (auto chunk = currentLine.chunks.begin(); chunk != currentLine.chunks.end(); chunk++) {
180 Q_FOREACH (int j, chunk->chunkIndices) {
181 result[j].cssPosition -= correctionOffset;
182 result[j].cssPosition = result[j].cssPosition + result[j].totalBaselineOffset();
183 result[j].finalPosition = result.at(j).cssPosition;
184 }
185 chunk->length.translate(-correctionOffset);
186 chunk->boundingBox.translate(-correctionOffset);
187 }
188 }
189 return resHandler.adjust(offset);
190}
191
192// NOLINTNEXTLINE(readability-function-cognitive-complexity)
193static void
194handleCollapseAndHang(QVector<CharacterResult> &result, LineChunk &chunk, bool ltr, bool isHorizontal)
195{
196 QVector<int> lineIndices = chunk.chunkIndices;
197 QPointF endPos = chunk.length.p2();
198
199 if (!lineIndices.isEmpty()) {
200 QVectorIterator<int> it(lineIndices);
201 it.toBack();
202 while (it.hasPrevious()) {
203 int lastIndex = it.previous();
204 if (result.at(lastIndex).lineEnd == LineEdgeBehaviour::Collapse) {
205 result[lastIndex].hidden = true;
206 // We literally collapse the advance of the last collapsed white-space to ensure it may
207 // still be possible to track it by cursor movement.
208 result[lastIndex].advance = QPointF();
209 if (isHorizontal) {
210 result[lastIndex].inkBoundingBox.setWidth(0);
211 } else {
212 result[lastIndex].inkBoundingBox.setHeight(0);
213 }
214 } else if (result.at(lastIndex).lineEnd == LineEdgeBehaviour::ConditionallyHang) {
215 if (ltr) {
216 QPointF hangPos = result[lastIndex].cssPosition + result[lastIndex].advance;
217 if (isHorizontal) {
218 if (hangPos.x() > endPos.x()) {
219 result[lastIndex].isHanging = true;
220 chunk.conditionalHangEnd = hangPos - endPos;
221 }
222 } else {
223 if (hangPos.y() > endPos.y()) {
224 result[lastIndex].isHanging = true;
225 chunk.conditionalHangEnd = hangPos - endPos;
226 }
227 }
228 } else {
229 QPointF hangPos = result[lastIndex].cssPosition;
230 if (hangPos.x() < endPos.x()) {
231 result[lastIndex].isHanging = true;
232 chunk.conditionalHangEnd = hangPos - endPos;
233 }
234 }
235
236 } else if (result.at(lastIndex).lineEnd == LineEdgeBehaviour::ForceHang) {
237 result[lastIndex].isHanging = true;
238 chunk.conditionalHangEnd = result[lastIndex].advance;
239 }
240 if (result.at(lastIndex).lineEnd != LineEdgeBehaviour::Collapse) {
241 break;
242 }
243 }
244 }
245}
246
247// NOLINTNEXTLINE(readability-function-cognitive-complexity)
249 LineChunk &chunk,
250 const KoSvgText::TextAnchor anchor,
251 const QPointF anchorPoint,
252 const bool ltr,
253 const bool isHorizontal,
254 const QPointF textIndent,
255 const KoSvgText::ResolutionHandler &resHandler)
256{
257 const QVector<int> lineIndices = chunk.chunkIndices;
258 qreal shift = isHorizontal ? anchorPoint.x() : anchorPoint.y();
259
260 qreal a = 0;
261 qreal b = 0;
262
263 bool first = true;
264 Q_FOREACH (int i, lineIndices) {
265 if (!result.at(i).addressable || result.at(i).hidden || (result.at(i).isHanging && result.at(i).anchored_chunk)) {
266 continue;
267 }
268
269 QPointF p = result.at(i).finalPosition;
270 QPointF d = result.at(i).advance;
271 if (result.at(i).isHanging) {
272 d -= chunk.conditionalHangEnd;
273 if (!ltr) {
274 p += chunk.conditionalHangEnd;
275 }
276 }
277 const qreal pos = isHorizontal ? p.x() : p.y();
278 const qreal advance = isHorizontal ? d.x() : d.y();
279
280 if (first) {
281 a = qMin(pos, pos + advance);
282 b = qMax(pos, pos + advance);
283 first = false;
284 } else {
285 a = qMin(a, qMin(pos, pos + advance));
286 b = qMax(b, qMax(pos, pos + advance));
287 }
288 }
289
290
291 if (anchor == KoSvgText::AnchorStart) {
292 const qreal indent = isHorizontal ? textIndent.x() : textIndent.y();
293 if (ltr) {
294 a -= indent;
295 } else {
296 b += indent;
297 }
298 }
299
300 if ((anchor == KoSvgText::AnchorStart && ltr) || (anchor == KoSvgText::AnchorEnd && !ltr)) {
301 shift -= a;
302
303 } else if ((anchor == KoSvgText::AnchorEnd && ltr) || (anchor == KoSvgText::AnchorStart && !ltr)) {
304 shift -= b;
305
306 } else {
307 shift -= ((a + b) * 0.5);
308 }
309
310 QPointF shiftP = resHandler.adjust(isHorizontal ? QPointF(shift, 0) : QPointF(0, shift));
311 Q_FOREACH (int j, lineIndices) {
312 result[j].cssPosition = result[j].cssPosition+shiftP;
313 result[j].finalPosition = result.at(j).cssPosition;
314 }
315 chunk.boundingBox.translate(shiftP);
316}
317
322 QPointF &currentPos,
323 LineBox &currentLine,
324 QPointF &lineOffset,
325 const KoSvgText::TextAnchor anchor,
326 const KoSvgText::WritingMode writingMode,
327 const bool ltr,
328 const bool inlineSize,
329 const bool textInShape,
330 const KoSvgText::ResolutionHandler &resHandler)
331{
332 bool isHorizontal = writingMode == KoSvgText::HorizontalTB;
333
334 bool firstLine = textInShape? true: currentLine.firstLine;
335
336 for (auto currentChunk = currentLine.chunks.begin(); currentChunk != currentLine.chunks.end(); currentChunk++) {
337 QMap<int, int> visualToLogical;
338 Q_FOREACH (int j, currentChunk->chunkIndices) {
339 visualToLogical.insert(result.at(j).visualIndex, j);
340 }
341 currentPos = lineOffset;
342
343 handleCollapseAndHang(result, *currentChunk, ltr, isHorizontal);
344
345 QPointF justifyOffset;
346 QVector<int> before;
347 QVector<int> after;
348
349 if (currentLine.justifyLine) {
350 double hangingGlyphLength = isHorizontal? currentChunk->conditionalHangEnd.x(): currentChunk->conditionalHangEnd.y();
351 QPointF advanceLength;
352 bool first = true;
353 Q_FOREACH (int j, visualToLogical.values()) {
354 if (!result.at(j).addressable || result.at(j).hidden) {
355 continue;
356 }
357 advanceLength += result.at(j).advance;
358 if (result.at(j).isHanging) {
359 if (result.at(j).anchored_chunk) {
360 hangingGlyphLength += isHorizontal? result.at(j).advance.x(): result.at(j).advance.y();
361 }
362 continue;
363 }
364 bool last = visualToLogical.values().last() == j;
365 if (!last) {
366 last = result.at(j+1).isHanging;
367 }
368
369 if (result.at(j).justifyBefore && !first) {
370 before.append(j);
371 }
372 if (result.at(j).justifyAfter && !last) {
373 after.append(j);
374 }
375 first = false;
376 }
377
378 int justificationCount = before.size()+after.size();
379 if (justificationCount > 0) {
380 if (isHorizontal) {
381 double val = currentChunk->length.length() + hangingGlyphLength - advanceLength.x();
382 val = val / justificationCount;
383 justifyOffset = QPointF(val, 0);
384 } else {
385 double val = currentChunk->length.length() + hangingGlyphLength - advanceLength.y();
386 val = val / justificationCount;
387 justifyOffset = QPointF(0, val);
388 }
389 }
390 }
391
392 Q_FOREACH (const int j, visualToLogical.values()) {
393 if (!result.at(j).addressable) {
394 continue;
395 }
396 if ((result.at(j).isHanging && result.at(j).anchored_chunk)) {
397 if (ltr) {
398 result[j].cssPosition = currentPos - result.at(j).advance;
399 result[j].finalPosition = result[j].cssPosition;
400 } else {
401 result[j].cssPosition = currentPos;
402 result[j].finalPosition = result[j].cssPosition;
403 }
404 } else {
405 if (before.contains(j)) {
406 currentPos += justifyOffset;
407 }
408 result[j].cssPosition = currentPos;
409 result[j].finalPosition = currentPos;
410 currentPos = currentPos + result.at(j).advance;
411 if (after.contains(j)) {
412 currentPos += justifyOffset;
413 }
414 }
415 }
416
417 if (inlineSize) {
418 QPointF anchorPoint = currentChunk->length.p1();
419 if (textInShape) {
420 if (anchor == KoSvgText::AnchorMiddle) {
421 anchorPoint = currentChunk->length.center();
422 } else if (anchor == KoSvgText::AnchorEnd) {
423 anchorPoint = currentChunk->length.p2();
424 }
425 }
426 applyInlineSizeAnchoring(result, *currentChunk, anchor, anchorPoint, ltr, isHorizontal, currentLine.textIndent, resHandler);
427 } else {
428 if (!ltr) {
429 // RTL relies on the start being 0.0, so we need to align it to the startAnchor.
430 applyInlineSizeAnchoring(result, *currentChunk, KoSvgText::AnchorStart, currentChunk->length.p1(), ltr, isHorizontal, currentLine.textIndent, resHandler);
431 } else {
432 // this adds a length for preformated text, only useful for debug.
433 currentChunk->length.setLength(isHorizontal? currentChunk->boundingBox.width(): currentChunk->boundingBox.height());
434 }
435 }
436 }
437 lineOffset += lineHeightOffset(writingMode, result, currentLine, firstLine, resHandler);
438 currentPos = lineOffset;
439}
440
441// NOLINTNEXTLINE(readability-function-cognitive-complexity)
443 const QMap<int, int> &logicalToVisual,
445 QPointF startPos,
446 const KoSvgText::ResolutionHandler &resHandler)
447{
452
453 bool ltr = direction == KoSvgText::DirectionLeftToRight;
454 bool isHorizontal = writingMode == KoSvgText::HorizontalTB;
455
456 QVector<LineBox> lineBoxes;
457
458 QPointF endPos;
459
461 QPointF textIndent;
462 if (!inlineSize.isAuto) {
463
464 qreal textIdentValue = textIndentInfo.length.unit == KoSvgText::CssLengthPercentage::Percentage?
465 textIndentInfo.length.value * inlineSize.customValue: textIndentInfo.length.value;
466 if (isHorizontal) {
467 textIndent = resHandler.adjust(QPointF(textIdentValue, 0));
468 endPos = ltr ? QPointF(startPos.x() + inlineSize.customValue, 0) : QPointF(startPos.x() - inlineSize.customValue, 0);
469 } else {
470 textIndent = resHandler.adjust(QPointF(0, textIdentValue));
471 endPos = ltr ? QPointF(0, startPos.y() + inlineSize.customValue) : QPointF(0, startPos.y() - inlineSize.customValue);
472 }
473 }
474 LineBox currentLine(startPos, endPos, resHandler);
475 currentLine.firstLine = true;
476
477 QVector<int> wordIndices;
479 QPointF wordAdvance;
480
481 QPointF currentPos = startPos;
482 if (!textIndentInfo.hanging && !inlineSize.isAuto) {
483 currentLine.textIndent = textIndent;
484 currentPos += currentLine.textIndent;
485 }
486 QPointF lineOffset = startPos;
487
488 QVector<int> lineIndices;
489
490 QListIterator<int> it(logicalToVisual.keys());
491 while (it.hasNext()) {
492 int index = it.next();
493 result[index].calculateAndApplyTabsize(wordAdvance + currentPos, isHorizontal, resHandler);
494 CharacterResult charResult = result.at(index);
495 if (!charResult.addressable) {
496 continue;
497 }
498 bool softBreak = false;
499 bool doNotCountAdvance =
500 ((charResult.lineEnd != LineEdgeBehaviour::NoChange)
501 && !(currentLine.isEmpty() && wordIndices.isEmpty()));
502 if (!doNotCountAdvance) {
503 if (wordIndices.isEmpty()) {
504 wordAdvance = charResult.advance;
505 } else {
506 wordAdvance += charResult.advance;
507 }
508 }
509 wordIndices.append(index);
510 currentLine.lastLine = !it.hasNext();
511
512 if (charResult.breakType != BreakType::NoBreak || currentLine.lastLine) {
513 qreal lineLength = isHorizontal ? (currentPos - startPos + wordAdvance).x()
514 : (currentPos - startPos + wordAdvance).y();
515 if (!inlineSize.isAuto) {
516 // Sometimes glyphs are a fraction larger than you'd expect, but
517 // not enough to really break the line, so the following is a
518 // bit more stable than a simple compare.
519 if (abs(lineLength) - inlineSize.customValue > 0.01) {
520 softBreak = true;
521 } else {
522 addWordToLine(result, currentPos, wordIndices, currentLine, ltr, isHorizontal);
523 }
524 } else {
525 addWordToLine(result, currentPos, wordIndices, currentLine, ltr, isHorizontal);
526 }
527 }
528
529 if (softBreak) {
530 bool firstLine = currentLine.firstLine;
531 if (!currentLine.isEmpty()) {
532 finalizeLine(result,
533 currentPos,
534 currentLine,
535 lineOffset,
536 anchor,
537 writingMode,
538 ltr,
539 !inlineSize.isAuto,
540 false,
541 resHandler);
542 lineBoxes.append(currentLine);
543 currentLine.clearAndAdjust(isHorizontal, lineOffset, textIndentInfo.hanging? textIndent: QPointF());
544 if (!inlineSize.isAuto) {
545 currentPos += currentLine.textIndent;
546 }
547 }
548
549 if (charResult.overflowWrap) {
550 qreal wordLength = isHorizontal ? wordAdvance.x() : wordAdvance.y();
551 if (!inlineSize.isAuto && wordLength > inlineSize.customValue) {
552 // Word is too large, so we try to add it in
553 // max-width-friendly-chunks.
554 wordAdvance = QPointF();
555 wordLength = 0;
556 QVector<int> partialWord;
557 currentLine.firstLine = firstLine;
558 Q_FOREACH (const int i, wordIndices) {
559 result[i].calculateAndApplyTabsize(wordAdvance + currentPos, isHorizontal, resHandler);
560 wordAdvance += result.at(i).advance;
561 wordLength = isHorizontal ? wordAdvance.x() : wordAdvance.y();
562 if (wordLength <= inlineSize.customValue) {
563 partialWord.append(i);
564 } else {
565 addWordToLine(result, currentPos, partialWord, currentLine, ltr, isHorizontal);
566
567 finalizeLine(result,
568 currentPos,
569 currentLine,
570 lineOffset,
571 anchor,
572 writingMode,
573 ltr,
574 !inlineSize.isAuto,
575 false,
576 resHandler);
577 lineBoxes.append(currentLine);
578 currentLine.clearAndAdjust(isHorizontal, lineOffset, textIndentInfo.hanging? textIndent: QPointF());
579 if (!inlineSize.isAuto) {
580 currentPos += currentLine.textIndent;
581 }
582 result[i].calculateAndApplyTabsize(wordAdvance + currentPos, isHorizontal, resHandler);
583 wordAdvance = result.at(i).advance;
584 partialWord.append(i);
585 }
586 }
587 wordIndices = partialWord;
588 }
589 }
590 addWordToLine(result, currentPos, wordIndices, currentLine, ltr, isHorizontal);
591 }
592
593 if (charResult.breakType == BreakType::HardBreak) {
594 finalizeLine(result,
595 currentPos,
596 currentLine,
597 lineOffset,
598 anchor,
599 writingMode,
600 ltr,
601 !inlineSize.isAuto,
602 false,
603 resHandler);
604 lineBoxes.append(currentLine);
605 bool indentLine = textIndentInfo.hanging? false: textIndentInfo.eachLine;
606 currentLine.clearAndAdjust(isHorizontal, lineOffset, indentLine? textIndent: QPointF());
607 if (!inlineSize.isAuto) {
608 currentPos += currentLine.textIndent;
609 }
610 }
611
612 if (currentLine.lastLine) {
613 if (!wordIndices.isEmpty()) {
614 addWordToLine(result, currentPos, wordIndices, currentLine, ltr, isHorizontal);
615 }
616 finalizeLine(result,
617 currentPos,
618 currentLine,
619 lineOffset,
620 anchor,
621 writingMode,
622 ltr,
623 !inlineSize.isAuto,
624 false,
625 resHandler);
626 lineBoxes.append(currentLine);
627 }
628 }
629 debugFlake << "Linebreaking finished";
630 return lineBoxes;
631}
632
633} // namespace KoSvgTextShapeLayoutFunc
#define debugFlake
Definition FlakeDebug.h:15
const Params2D p
@ ConditionallyHang
Only hang if no space otherwise, only measured for justification if not hanging.
@ Collapse
Collapse if first or last in line.
@ ForceHang
Force hanging at the start or end of a line, never measured for justification.
@ NoChange
Do nothing special.
@ TextAnchorId
KoSvgText::TextAnchor.
@ InlineSizeId
KoSvgText::AutoValue.
@ WritingModeId
KoSvgText::WritingMode.
@ DirectionId
KoSvgText::Direction.
@ TextIndentId
KoSvgText::TextIndentInfo Struct.
QVariant propertyOrDefault(PropertyId id) const
static QPointF lineHeightOffset(KoSvgText::WritingMode writingMode, QVector< CharacterResult > &result, LineBox &currentLine, const bool firstLine, const KoSvgText::ResolutionHandler resHandler)
void calculateLineHeight(CharacterResult cr, double &ascent, double &descent, bool isHorizontal, bool compare=false)
calculateLineHeight calculate the total ascent and descent (including baseline-offset) of a charResul...
static void applyInlineSizeAnchoring(QVector< CharacterResult > &result, LineChunk &chunk, const KoSvgText::TextAnchor anchor, const QPointF anchorPoint, const bool ltr, const bool isHorizontal, const QPointF textIndent, const KoSvgText::ResolutionHandler &resHandler)
void finalizeLine(QVector< CharacterResult > &result, QPointF &currentPos, LineBox &currentLine, QPointF &lineOffset, const KoSvgText::TextAnchor anchor, const KoSvgText::WritingMode writingMode, const bool ltr, const bool inlineSize, const bool textInShape, const KoSvgText::ResolutionHandler &resHandler)
static void handleCollapseAndHang(QVector< CharacterResult > &result, LineChunk &chunk, bool ltr, bool isHorizontal)
QVector< LineBox > breakLines(const KoSvgTextProperties &properties, const QMap< int, int > &logicalToVisual, QVector< CharacterResult > &result, QPointF startPos, const KoSvgText::ResolutionHandler &resHandler)
void addWordToLine(QVector< CharacterResult > &result, QPointF &currentPos, QVector< int > &wordIndices, LineBox &currentLine, bool ltr, bool isHorizontal)
addWordToLine Small function used in break lines to quickly add a 'word' to the current line....
TextAnchor
Where the text is anchored for SVG 1.1 text and 'inline-size'.
Definition KoSvgText.h:79
@ AnchorEnd
Anchor right for LTR, left for RTL.
Definition KoSvgText.h:82
@ AnchorStart
Anchor left for LTR, right for RTL.
Definition KoSvgText.h:80
@ AnchorMiddle
Anchor to the middle.
Definition KoSvgText.h:81
Direction
Base direction used by Bidi algorithm.
Definition KoSvgText.h:48
@ DirectionLeftToRight
Definition KoSvgText.h:49
@ HorizontalTB
Definition KoSvgText.h:38
QPointF totalBaselineOffset() const
QRectF lineHeightBox() const
lineHeightBox
bool anchored_chunk
whether this is the start of a new chunk.
LineEdgeBehaviour lineEnd
void calculateAndApplyTabsize(QPointF currentPos, bool isHorizontal, const KoSvgText::ResolutionHandler &resHandler)
QPointF cssPosition
the position in accordance with the CSS specs, as opossed to the SVG spec.
QRectF layoutBox() const
layoutBox
The ResolutionHandler class.
Definition KoSvgText.h:1084
QPointF adjust(const QPointF point) const
Adjusts the point to rounded pixel values, based on whether roundToPixelHorizontal or roundToPixelVer...
bool hanging
Flip the lines to which text-indent is applied.
Definition KoSvgText.h:659
bool eachLine
Apply the text-indent to each line following a hardbreak.
Definition KoSvgText.h:660
CssLengthPercentage length
Definition KoSvgText.h:658
The LineBox struct.
void setCurrentChunk(LineChunk chunk)
qreal actualLineBottom
QPointF baselineTop
Used to identify the top of the line for baseline-alignment.
qreal actualLineTop
LineChunk chunk()
void clearAndAdjust(bool isHorizontal, QPointF current, QPointF indent)
QPointF textIndent
qreal expectedLineTop
Because fonts can affect lineheight mid-line, and this affects wrapping, this estimates the line-heig...
QVector< LineChunk > chunks
QPointF baselineBottom
Used to identify the bottom of the line for baseline-alignment.
QPointF conditionalHangEnd
QVector< int > chunkIndices
charResult indices that belong to this chunk.
QLineF length
Used to measure how long the current line is allowed to be.