105 const bool firstLine,
110 QPointF correctionOffset;
114 if (currentLine.
chunks.isEmpty()) {
124 if (chunkIndices.size() > 0) {
131 result[chunkIndices.first()].anchored_chunk =
true;
141 correctionOffset = QPointF(0, -expectedLineTop) + currentLine.
baselineTop;
147 correctionOffset = QPointF(-expectedLineTop, 0) + currentLine.
baselineTop;
155 correctionOffset = QPointF(expectedLineTop, 0) - currentLine.
baselineTop;
159 bool returnDescent = firstLine;
160 QPointF offset = resHandler.
adjust(lineTop + lineBottom);
162 lineTop = offset - resHandler.
adjust(lineBottom);
163 correctionOffset = resHandler.
adjust(correctionOffset);
166 if (!returnDescent) {
167 for (
auto chunk = currentLine.
chunks.begin(); chunk != currentLine.
chunks.end(); chunk++) {
168 Q_FOREACH (
int j, chunk->chunkIndices) {
169 result[j].cssPosition += lineTop;
170 result[j].cssPosition += result[j].totalBaselineOffset();
171 result[j].finalPosition = result.at(j).cssPosition;
173 chunk->length.translate(lineTop);
174 chunk->boundingBox.translate(lineTop);
177 offset = lineBottom - correctionOffset;
178 for (
auto chunk = currentLine.
chunks.begin(); chunk != currentLine.
chunks.end(); chunk++) {
179 Q_FOREACH (
int j, chunk->chunkIndices) {
180 result[j].cssPosition -= correctionOffset;
181 result[j].cssPosition = result[j].cssPosition + result[j].totalBaselineOffset();
182 result[j].finalPosition = result.at(j).cssPosition;
184 chunk->length.translate(-correctionOffset);
185 chunk->boundingBox.translate(-correctionOffset);
188 return resHandler.
adjust(offset);
196 QPointF endPos = chunk.
length.p2();
198 if (!lineIndices.isEmpty()) {
199 QVectorIterator<int> it(lineIndices);
201 while (it.hasPrevious()) {
202 int lastIndex = it.previous();
204 result[lastIndex].hidden =
true;
207 result[lastIndex].advance = QPointF();
209 result[lastIndex].inkBoundingBox.setWidth(0);
211 result[lastIndex].inkBoundingBox.setHeight(0);
215 QPointF hangPos = result[lastIndex].cssPosition + result[lastIndex].advance;
217 if (hangPos.x() > endPos.x()) {
218 result[lastIndex].isHanging =
true;
222 if (hangPos.y() > endPos.y()) {
223 result[lastIndex].isHanging =
true;
228 QPointF hangPos = result[lastIndex].cssPosition;
229 if (hangPos.x() < endPos.x()) {
230 result[lastIndex].isHanging =
true;
236 result[lastIndex].isHanging =
true;
250 const QPointF anchorPoint,
252 const bool isHorizontal,
253 const QPointF textIndent,
257 qreal shift = isHorizontal ? anchorPoint.x() : anchorPoint.y();
263 Q_FOREACH (
int i, lineIndices) {
264 if (!result.at(i).addressable || result.at(i).hidden || (result.at(i).isHanging && result.at(i).anchored_chunk)) {
268 QPointF
p = result.at(i).finalPosition;
269 QPointF d = result.at(i).advance;
270 if (result.at(i).isHanging) {
276 const qreal pos = isHorizontal ?
p.x() :
p.y();
277 const qreal advance = isHorizontal ? d.x() : d.y();
280 a = qMin(pos, pos + advance);
281 b = qMax(pos, pos + advance);
284 a = qMin(a, qMin(pos, pos + advance));
285 b = qMax(b, qMax(pos, pos + advance));
291 const qreal indent = isHorizontal ? textIndent.x() : textIndent.y();
306 shift -= ((a + b) * 0.5);
309 QPointF shiftP = resHandler.
adjust(isHorizontal ? QPointF(shift, 0) : QPointF(0, shift));
310 Q_FOREACH (
int j, lineIndices) {
311 result[j].cssPosition = result[j].cssPosition+shiftP;
312 result[j].finalPosition = result.at(j).cssPosition;
327 const bool inlineSize,
328 const bool textInShape,
333 bool firstLine = textInShape?
true: currentLine.
firstLine;
335 for (
auto currentChunk = currentLine.
chunks.begin(); currentChunk != currentLine.
chunks.end(); currentChunk++) {
336 QMap<int, int> visualToLogical;
337 Q_FOREACH (
int j, currentChunk->chunkIndices) {
338 visualToLogical.insert(result.at(j).visualIndex, j);
340 currentPos = lineOffset;
344 QPointF justifyOffset;
349 double hangingGlyphLength = isHorizontal? currentChunk->conditionalHangEnd.x(): currentChunk->conditionalHangEnd.y();
350 QPointF advanceLength;
352 Q_FOREACH (
int j, visualToLogical.values()) {
353 if (!result.at(j).addressable || result.at(j).hidden) {
356 advanceLength += result.at(j).advance;
357 if (result.at(j).isHanging) {
358 if (result.at(j).anchored_chunk) {
359 hangingGlyphLength += isHorizontal? result.at(j).advance.x(): result.at(j).advance.y();
363 bool last = visualToLogical.values().last() == j;
365 last = result.at(j+1).isHanging;
368 if (result.at(j).justifyBefore && !first) {
371 if (result.at(j).justifyAfter && !last) {
377 int justificationCount = before.size()+after.size();
378 if (justificationCount > 0) {
379 const QPointF indent = currentChunk == currentLine.
chunks.begin()? currentLine.
textIndent: QPointF();
380 const QLineF modified = QLineF(currentChunk->length.p1()+indent, currentChunk->length.p2());
382 double val = modified.length() + hangingGlyphLength - advanceLength.x();
383 val = val / justificationCount;
384 justifyOffset = QPointF(val, 0);
386 double val = modified.length() + hangingGlyphLength - advanceLength.y();
387 val = val / justificationCount;
388 justifyOffset = QPointF(0, val);
393 Q_FOREACH (
const int j, visualToLogical.values()) {
394 if (!result.at(j).addressable) {
397 if ((result.at(j).isHanging && result.at(j).anchored_chunk)) {
399 result[j].cssPosition = currentPos - result.at(j).advance;
400 result[j].finalPosition = result[j].cssPosition;
402 result[j].cssPosition = currentPos;
403 result[j].finalPosition = result[j].cssPosition;
406 if (before.contains(j)) {
407 currentPos += justifyOffset;
409 result[j].cssPosition = currentPos;
410 result[j].finalPosition = currentPos;
411 currentPos = currentPos + result.at(j).advance;
412 if (after.contains(j)) {
413 currentPos += justifyOffset;
419 QPointF anchorPoint = currentChunk->length.p1();
422 anchorPoint = currentChunk->length.center();
424 anchorPoint = currentChunk->length.p2();
434 currentChunk->length.setLength(isHorizontal? currentChunk->boundingBox.width(): currentChunk->boundingBox.height());
438 lineOffset +=
lineHeightOffset(writingMode, result, currentLine, firstLine, resHandler);
439 currentPos = lineOffset;
444 const QMap<int, int> &logicalToVisual,
468 textIndent = resHandler.
adjust(QPointF(textIdentValue, 0));
469 endPos = ltr ? QPointF(startPos.x() + inlineSize.
customValue, 0) : QPointF(startPos.x() - inlineSize.
customValue, 0);
471 textIndent = resHandler.
adjust(QPointF(0, textIdentValue));
472 endPos = ltr ? QPointF(0, startPos.y() + inlineSize.
customValue) : QPointF(0, startPos.y() - inlineSize.
customValue);
475 LineBox currentLine(startPos, endPos, resHandler);
482 QPointF currentPos = startPos;
487 QPointF lineOffset = startPos;
491 QListIterator<int> it(logicalToVisual.keys());
492 while (it.hasNext()) {
493 int index = it.next();
494 result[index].calculateAndApplyTabsize(wordAdvance + currentPos, isHorizontal, resHandler);
499 bool softBreak =
false;
500 bool doNotCountAdvance =
502 && !(currentLine.
isEmpty() && wordIndices.isEmpty()));
503 if (!doNotCountAdvance) {
504 if (wordIndices.isEmpty()) {
505 wordAdvance = charResult.
advance;
507 wordAdvance += charResult.
advance;
510 wordIndices.append(index);
511 currentLine.
lastLine = !it.hasNext();
514 qreal lineLength = isHorizontal ? (currentPos - startPos + wordAdvance).x()
515 : (currentPos - startPos + wordAdvance).y();
520 if (abs(lineLength) - inlineSize.
customValue > 0.01) {
523 addWordToLine(result, currentPos, wordIndices, currentLine, isHorizontal);
526 addWordToLine(result, currentPos, wordIndices, currentLine, isHorizontal);
543 lineBoxes.append(currentLine);
551 qreal wordLength = isHorizontal ? wordAdvance.x() : wordAdvance.y();
555 wordAdvance = QPointF();
559 Q_FOREACH (
const int i, wordIndices) {
560 result[i].calculateAndApplyTabsize(wordAdvance + currentPos, isHorizontal, resHandler);
561 wordAdvance += result.at(i).advance;
562 wordLength = isHorizontal ? wordAdvance.x() : wordAdvance.y();
564 partialWord.append(i);
566 addWordToLine(result, currentPos, partialWord, currentLine, isHorizontal);
578 lineBoxes.append(currentLine);
583 result[i].calculateAndApplyTabsize(wordAdvance + currentPos, isHorizontal, resHandler);
584 wordAdvance = result.at(i).advance;
585 partialWord.append(i);
588 wordIndices = partialWord;
591 addWordToLine(result, currentPos, wordIndices, currentLine, isHorizontal);
605 lineBoxes.append(currentLine);
606 bool indentLine = textIndentInfo.
hanging?
false: textIndentInfo.
eachLine;
607 currentLine.
clearAndAdjust(isHorizontal, lineOffset, indentLine? textIndent: QPointF());
614 if (!wordIndices.isEmpty()) {
615 addWordToLine(result, currentPos, wordIndices, currentLine, isHorizontal);
627 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 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.
CssLengthPercentage length