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) {
381 double val = currentChunk->length.length() + hangingGlyphLength - advanceLength.x();
382 val = val / justificationCount;
383 justifyOffset = QPointF(val, 0);
385 double val = currentChunk->length.length() + hangingGlyphLength - advanceLength.y();
386 val = val / justificationCount;
387 justifyOffset = QPointF(0, val);
392 Q_FOREACH (
const int j, visualToLogical.values()) {
393 if (!result.at(j).addressable) {
396 if ((result.at(j).isHanging && result.at(j).anchored_chunk)) {
398 result[j].cssPosition = currentPos - result.at(j).advance;
399 result[j].finalPosition = result[j].cssPosition;
401 result[j].cssPosition = currentPos;
402 result[j].finalPosition = result[j].cssPosition;
405 if (before.contains(j)) {
406 currentPos += justifyOffset;
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;
418 QPointF anchorPoint = currentChunk->length.p1();
421 anchorPoint = currentChunk->length.center();
423 anchorPoint = currentChunk->length.p2();
433 currentChunk->length.setLength(isHorizontal? currentChunk->boundingBox.width(): currentChunk->boundingBox.height());
437 lineOffset +=
lineHeightOffset(writingMode, result, currentLine, firstLine, resHandler);
438 currentPos = lineOffset;
443 const QMap<int, int> &logicalToVisual,
467 textIndent = resHandler.
adjust(QPointF(textIdentValue, 0));
468 endPos = ltr ? QPointF(startPos.x() + inlineSize.
customValue, 0) : QPointF(startPos.x() - inlineSize.
customValue, 0);
470 textIndent = resHandler.
adjust(QPointF(0, textIdentValue));
471 endPos = ltr ? QPointF(0, startPos.y() + inlineSize.
customValue) : QPointF(0, startPos.y() - inlineSize.
customValue);
474 LineBox currentLine(startPos, endPos, resHandler);
481 QPointF currentPos = startPos;
486 QPointF lineOffset = startPos;
490 QListIterator<int> it(logicalToVisual.keys());
491 while (it.hasNext()) {
492 int index = it.next();
493 result[index].calculateAndApplyTabsize(wordAdvance + currentPos, isHorizontal, resHandler);
498 bool softBreak =
false;
499 bool doNotCountAdvance =
501 && !(currentLine.
isEmpty() && wordIndices.isEmpty()));
502 if (!doNotCountAdvance) {
503 if (wordIndices.isEmpty()) {
504 wordAdvance = charResult.
advance;
506 wordAdvance += charResult.
advance;
509 wordIndices.append(index);
510 currentLine.
lastLine = !it.hasNext();
513 qreal lineLength = isHorizontal ? (currentPos - startPos + wordAdvance).x()
514 : (currentPos - startPos + wordAdvance).y();
519 if (abs(lineLength) - inlineSize.
customValue > 0.01) {
522 addWordToLine(result, currentPos, wordIndices, currentLine, ltr, isHorizontal);
525 addWordToLine(result, currentPos, wordIndices, currentLine, ltr, isHorizontal);
542 lineBoxes.append(currentLine);
550 qreal wordLength = isHorizontal ? wordAdvance.x() : wordAdvance.y();
554 wordAdvance = QPointF();
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();
563 partialWord.append(i);
565 addWordToLine(result, currentPos, partialWord, currentLine, ltr, isHorizontal);
577 lineBoxes.append(currentLine);
582 result[i].calculateAndApplyTabsize(wordAdvance + currentPos, isHorizontal, resHandler);
583 wordAdvance = result.at(i).advance;
584 partialWord.append(i);
587 wordIndices = partialWord;
590 addWordToLine(result, currentPos, wordIndices, currentLine, ltr, isHorizontal);
604 lineBoxes.append(currentLine);
605 bool indentLine = textIndentInfo.
hanging?
false: textIndentInfo.
eachLine;
606 currentLine.
clearAndAdjust(isHorizontal, lineOffset, indentLine? textIndent: QPointF());
613 if (!wordIndices.isEmpty()) {
614 addWordToLine(result, currentPos, wordIndices, currentLine, ltr, isHorizontal);
626 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.