106 const bool firstLine,
111 QPointF correctionOffset;
115 if (currentLine.
chunks.isEmpty()) {
125 if (chunkIndices.size() > 0) {
132 result[chunkIndices.first()].anchored_chunk =
true;
142 correctionOffset = QPointF(0, -expectedLineTop) + currentLine.
baselineTop;
148 correctionOffset = QPointF(-expectedLineTop, 0) + currentLine.
baselineTop;
156 correctionOffset = QPointF(expectedLineTop, 0) - currentLine.
baselineTop;
160 bool returnDescent = firstLine;
161 QPointF offset = resHandler.
adjust(lineTop + lineBottom);
163 lineTop = offset - resHandler.
adjust(lineBottom);
164 correctionOffset = resHandler.
adjust(correctionOffset);
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;
174 chunk->length.translate(lineTop);
175 chunk->boundingBox.translate(lineTop);
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;
185 chunk->length.translate(-correctionOffset);
186 chunk->boundingBox.translate(-correctionOffset);
189 return resHandler.
adjust(offset);
197 QPointF endPos = chunk.
length.p2();
199 if (!lineIndices.isEmpty()) {
200 QVectorIterator<int> it(lineIndices);
202 while (it.hasPrevious()) {
203 int lastIndex = it.previous();
205 result[lastIndex].hidden =
true;
208 result[lastIndex].advance = QPointF();
210 result[lastIndex].inkBoundingBox.setWidth(0);
212 result[lastIndex].inkBoundingBox.setHeight(0);
216 QPointF hangPos = result[lastIndex].cssPosition + result[lastIndex].advance;
218 if (hangPos.x() > endPos.x()) {
219 result[lastIndex].isHanging =
true;
223 if (hangPos.y() > endPos.y()) {
224 result[lastIndex].isHanging =
true;
229 QPointF hangPos = result[lastIndex].cssPosition;
230 if (hangPos.x() < endPos.x()) {
231 result[lastIndex].isHanging =
true;
237 result[lastIndex].isHanging =
true;
251 const QPointF anchorPoint,
253 const bool isHorizontal,
254 const QPointF textIndent,
258 qreal shift = isHorizontal ? anchorPoint.x() : anchorPoint.y();
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)) {
269 QPointF
p = result.at(i).finalPosition;
270 QPointF d = result.at(i).advance;
271 if (result.at(i).isHanging) {
277 const qreal pos = isHorizontal ?
p.x() :
p.y();
278 const qreal advance = isHorizontal ? d.x() : d.y();
281 a = qMin(pos, pos + advance);
282 b = qMax(pos, pos + advance);
285 a = qMin(a, qMin(pos, pos + advance));
286 b = qMax(b, qMax(pos, pos + advance));
292 const qreal indent = isHorizontal ? textIndent.x() : textIndent.y();
307 shift -= ((a + b) * 0.5);
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;
328 const bool inlineSize,
329 const bool textInShape,
334 bool firstLine = textInShape?
true: currentLine.
firstLine;
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);
341 currentPos = lineOffset;
345 QPointF justifyOffset;
350 double hangingGlyphLength = isHorizontal? currentChunk->conditionalHangEnd.x(): currentChunk->conditionalHangEnd.y();
351 QPointF advanceLength;
353 Q_FOREACH (
int j, visualToLogical.values()) {
354 if (!result.at(j).addressable || result.at(j).hidden) {
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();
364 bool last = visualToLogical.values().last() == j;
366 last = result.at(j+1).isHanging;
369 if (result.at(j).justifyBefore && !first) {
372 if (result.at(j).justifyAfter && !last) {
378 int justificationCount = before.size()+after.size();
379 if (justificationCount > 0) {
380 const QPointF indent = currentChunk == currentLine.
chunks.begin()? currentLine.
textIndent: QPointF();
381 const QLineF modified = QLineF(currentChunk->length.p1()+indent, currentChunk->length.p2());
383 double val = modified.length() + hangingGlyphLength - advanceLength.x();
384 val = val / justificationCount;
385 justifyOffset = QPointF(val, 0);
387 double val = modified.length() + hangingGlyphLength - advanceLength.y();
388 val = val / justificationCount;
389 justifyOffset = QPointF(0, val);
394 Q_FOREACH (
const int j, visualToLogical.values()) {
395 if (!result.at(j).addressable) {
398 if ((result.at(j).isHanging && result.at(j).anchored_chunk)) {
400 result[j].cssPosition = currentPos - result.at(j).advance;
401 result[j].finalPosition = result[j].cssPosition;
403 result[j].cssPosition = currentPos;
404 result[j].finalPosition = result[j].cssPosition;
407 if (before.contains(j)) {
408 currentPos += justifyOffset;
410 result[j].cssPosition = currentPos;
411 result[j].finalPosition = currentPos;
412 currentPos = currentPos + result.at(j).advance;
413 if (after.contains(j)) {
414 currentPos += justifyOffset;
420 QPointF anchorPoint = currentChunk->length.p1();
423 anchorPoint = currentChunk->length.center();
425 anchorPoint = currentChunk->length.p2();
435 currentChunk->length.setLength(isHorizontal? currentChunk->boundingBox.width(): currentChunk->boundingBox.height());
439 lineOffset +=
lineHeightOffset(writingMode, result, currentLine, firstLine, resHandler);
440 currentPos = lineOffset;
445 const QMap<int, int> &logicalToVisual,
469 textIndent = resHandler.
adjust(QPointF(textIdentValue, 0));
470 endPos = ltr ? QPointF(startPos.x() + inlineSize.
customValue, 0) : QPointF(startPos.x() - inlineSize.
customValue, 0);
472 textIndent = resHandler.
adjust(QPointF(0, textIdentValue));
473 endPos = ltr ? QPointF(0, startPos.y() + inlineSize.
customValue) : QPointF(0, startPos.y() - inlineSize.
customValue);
476 LineBox currentLine(startPos, endPos, resHandler);
483 QPointF currentPos = startPos;
488 QPointF lineOffset = startPos;
492 QListIterator<int> it(logicalToVisual.keys());
493 while (it.hasNext()) {
494 int index = it.next();
495 result[index].calculateAndApplyTabsize(wordAdvance + currentPos, isHorizontal, resHandler);
500 bool softBreak =
false;
501 bool doNotCountAdvance =
503 && !(currentLine.
isEmpty() && wordIndices.isEmpty()));
504 if (!doNotCountAdvance) {
505 if (wordIndices.isEmpty()) {
506 wordAdvance = charResult.
advance;
508 wordAdvance += charResult.
advance;
511 wordIndices.append(index);
512 currentLine.
lastLine = !it.hasNext();
515 qreal lineLength = isHorizontal ? (currentPos - startPos + wordAdvance).x()
516 : (currentPos - startPos + wordAdvance).y();
521 if (abs(lineLength) - inlineSize.
customValue > 0.01) {
524 addWordToLine(result, currentPos, wordIndices, currentLine, ltr, isHorizontal);
527 addWordToLine(result, currentPos, wordIndices, currentLine, ltr, isHorizontal);
544 lineBoxes.append(currentLine);
552 qreal wordLength = isHorizontal ? wordAdvance.x() : wordAdvance.y();
556 wordAdvance = QPointF();
560 Q_FOREACH (
const int i, wordIndices) {
561 result[i].calculateAndApplyTabsize(wordAdvance + currentPos, isHorizontal, resHandler);
562 wordAdvance += result.at(i).advance;
563 wordLength = isHorizontal ? wordAdvance.x() : wordAdvance.y();
565 partialWord.append(i);
567 addWordToLine(result, currentPos, partialWord, currentLine, ltr, isHorizontal);
579 lineBoxes.append(currentLine);
584 result[i].calculateAndApplyTabsize(wordAdvance + currentPos, isHorizontal, resHandler);
585 wordAdvance = result.at(i).advance;
586 partialWord.append(i);
589 wordIndices = partialWord;
592 addWordToLine(result, currentPos, wordIndices, currentLine, ltr, isHorizontal);
606 lineBoxes.append(currentLine);
607 bool indentLine = textIndentInfo.
hanging?
false: textIndentInfo.
eachLine;
608 currentLine.
clearAndAdjust(isHorizontal, lineOffset, indentLine? textIndent: QPointF());
615 if (!wordIndices.isEmpty()) {
616 addWordToLine(result, currentPos, wordIndices, currentLine, ltr, isHorizontal);
628 lineBoxes.append(currentLine);
@ 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.
@ TextIndentId
KoSvgText::TextIndentInfo Struct.
QVariant propertyOrDefault(PropertyId id) const
static QPointF lineHeightOffset(KoSvgText::WritingMode writingMode, QVector< CharacterResult > &result, LineBox ¤tLine, 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 ¤tPos, LineBox ¤tLine, 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 ¤tPos, QVector< int > &wordIndices, LineBox ¤tLine, 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'.
@ AnchorEnd
Anchor right for LTR, left for RTL.
@ AnchorStart
Anchor left for LTR, right for RTL.
Direction
Base direction used by Bidi algorithm.
The ResolutionHandler class.
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.
bool eachLine
Apply the text-indent to each line following a hardbreak.