13#include <klocalizedstring.h>
41#include <QPainterPath>
121 if (!
d) {
return nullptr;}
127 qDebug() <<
"d not initialized...";
130 return &
d->textElement->textPathInfo;
134 if (!
d) { qDebug() <<
"d not initialized...";
return nullptr;}
155 Q_FOREACH(
KoShape *shape,
d->shapeGroup->shapes()) {
169 static const QString s_placeholderText = i18nc(
"Default text for the text shape",
"Placeholder Text");
170 return s_placeholderText;
188 if ((
d->shapesInside.contains(shape) ||
d->shapesSubtract.contains(shape) ||
d->textPaths.contains(shape))
189 && (transformationTypes.contains(type)
195 if (
d->shapesInside.contains(shape)) {
196 d->shapesInside.removeAll(shape);
198 if (
d->shapesSubtract.contains(shape)) {
199 d->shapesSubtract.removeAll(shape);
201 if (
d->textPaths.contains(shape)) {
202 d->textPaths.removeAll(shape);
205 if (
d->shapeGroup &&
d->shapeGroup->shapes().contains(shape)) {
206 d->shapeGroup->removeShape(shape);
208 d->updateInternalShapesList();
213 if (
d->bulkActionState) {
214 d->bulkActionState->contourHasChanged =
true;
216 d->updateTextWrappingAreas();
224 if ((!shape || shape ==
this)) {
226 if (
d->bulkActionState) {
227 d->bulkActionState->layoutHasChanged =
true;
231 }
else if (transformationTypes.contains(type) || type ==
ParentChanged) {
234 if (
d->bulkActionState) {
235 d->bulkActionState->contourHasChanged =
true;
237 d->updateTextWrappingAreas();
249 int roundedX = qRound(
xRes);
250 int roundedY = qRound(
yRes);
251 if (roundedX !=
d->xRes || roundedY !=
d->yRes) {
325 if (pos < 0 || d->
cursorPos.isEmpty() ||
d->result.isEmpty()) {
329 if (
d->result.at(
p.cluster).anchored_chunk &&
p.offset == 0) {
333 for (
int i = 0; i < pos; i++) {
335 if (
d->result.at(
p2.cluster).anchored_chunk &&
p2.offset == 0) {
344 if (pos < 0 || d->
cursorPos.isEmpty() ||
d->result.isEmpty()) {
347 if (pos >
d->cursorPos.size() - 1) {
348 return d->cursorPos.size() - 1;
351 int posCluster =
d->getCursorPos(pos).cluster;
352 for (
int i = pos; i <
d->cursorPos.size(); i++) {
354 if (
d->result.at(
p.cluster).anchored_chunk && i > pos && posCluster !=
p.cluster) {
366 if (pos < 0 || pos >
d->cursorPos.size()-1 ||
d->result.isEmpty() ||
d->cursorPos.isEmpty()) {
379 if (pos < 0 || pos >
d->cursorPos.size()-1 ||
d->result.isEmpty() ||
d->cursorPos.isEmpty()) {
391 if (
d->cursorPos.isEmpty()) {
394 int currentIndex =
d->getCursorPos(pos).index;
396 for (
int i = pos; i <
d->cursorPos.size(); i++) {
397 if (
d->cursorPos.at(i).index > currentIndex) {
406 if (
d->cursorPos.isEmpty()) {
409 const int currentIndex =
d->getCursorPos(pos).index;
411 for (
int i = pos; i >= 0; i--) {
412 if (
d->getCursorPos(i).index < currentIndex) {
421 if (
d->cursorPos.isEmpty()) {
426 int visualIndex =
d->logicalToVisualCursorPos.value(pos);
427 return d->logicalToVisualCursorPos.key(qMin(visualIndex + 1,
d->cursorPos.size() - 1),
d->cursorPos.size() - 1);
430 return qMin(pos + 1,
d->cursorPos.size() - 1);
435 if (
d->cursorPos.isEmpty()) {
440 int visualIndex =
d->logicalToVisualCursorPos.value(pos);
441 return d->logicalToVisualCursorPos.key(qMax(visualIndex - 1, 0), 0);
444 return qMax(pos - 1, 0);
449 if (pos < 0 || pos >
d->cursorPos.size()-1 ||
d->result.isEmpty() ||
d->cursorPos.isEmpty()) {
453 int nextLineStart =
lineEnd(pos)+1;
454 int nextLineEnd =
lineEnd(nextLineStart);
458 for (
int i = 0; i <
d->lineBoxes.size(); ++i) {
459 for (
int j = 0; j <
d->lineBoxes.at(i).chunks.size(); ++j) {
460 if (
d->lineBoxes.at(i).chunks.at(j).chunkIndices.contains(
cursorPos.cluster)) {
461 nextLineBox =
d->lineBoxes.at(qMin(i + 1,
d->lineBoxes.size()-1));
465 if (nextLineBox.
chunks.size()>0) {
470 if (
d->result.at(i).addressable) {
472 first =
d->result.at(i).cursorInfo.graphemeIndices.first();
474 last =
d->result.at(i).cursorInfo.graphemeIndices.last();
478 if (first > -1 && last > -1) {
486 if (nextLineStart >
d->cursorPos.size()-1) {
487 return d->cursorPos.size()-1;
493 int candidate =
posForPoint(currentPoint, nextLineStart, nextLineEnd+1);
503 if (pos < 0 || pos >
d->cursorPos.size()-1 ||
d->result.isEmpty() ||
d->cursorPos.isEmpty()) {
507 if (currentLineStart - 1 < 0) {
510 int previousLineStart =
lineStart(currentLineStart-1);
515 for (
int i = 0; i <
d->lineBoxes.size(); ++i) {
516 for (
int j = 0; j <
d->lineBoxes.at(i).chunks.size(); ++j) {
517 if (
d->lineBoxes.at(i).chunks.at(j).chunkIndices.contains(
cursorPos.cluster)) {
518 previousLineBox =
d->lineBoxes.at(qMax(i - 1, 0));
522 if (previousLineBox.
chunks.size()>0) {
527 if (
d->result.at(i).addressable) {
529 first =
d->result.at(i).cursorInfo.graphemeIndices.first();
531 last =
d->result.at(i).cursorInfo.graphemeIndices.last();
535 if (first > -1 && last > -1) {
545 int candidate =
posForPoint(currentPoint, previousLineStart, currentLineStart);
555 if (pos < 0 || pos >=
d->cursorPos.size() ||
d->result.isEmpty() ||
d->cursorPos.isEmpty()) {
560 for (
int i = pos; i <
d->cursorPos.size(); i++) {
566 if (pIndex >= 0 && pIndex < d->
plainText.size()) {
567 QChar c =
d->plainText.at(pIndex);
581 if (i < d->
cursorPos.size()-1 && i > pos) {
582 const CursorPos nextCursorPos =
d->cursorPos.at(i+1);
602 if (pos <= 0 || pos >
d->cursorPos.size() ||
d->result.isEmpty() ||
d->cursorPos.isEmpty()) {
607 for (
int i = pos; i >= 0; i--) {
617 if (i > 1 && i < pos) {
618 const CursorPos prevCursorPos =
d->getCursorPos(i-1);
642 p.moveTo(0, fontSize*0.2);
643 p.lineTo(0, -fontSize);
645 p.moveTo(-fontSize * 0.5, 0);
646 p.lineTo(fontSize, 0);
648 p.translate(
d->initialTextPosition);
655 if (
d->result.isEmpty() ||
d->cursorPos.isEmpty() || pos < 0 || pos >=
d->cursorPos.size()) {
669 p.moveTo(tf.map(caret.p1()));
670 p.lineTo(tf.map(caret.p2()));
671 if (
d->isBidi && bidiFlagSize > 0) {
673 double bidiFlagHalf = bidiFlagSize * 0.5;
677 qreal slope = bidiFlagHalf * (caret.dx()/ caret.dy());
678 point3 = QPointF(caret.p2().x() + slope + (
sign * bidiFlagHalf), caret.p2().y() + bidiFlagHalf);
679 point4 = QPointF(point3.x() + slope - (
sign * bidiFlagHalf),point3.y() + bidiFlagHalf);
681 qreal slope = bidiFlagHalf * (caret.dy()/ caret.dx());
682 point3 = QPointF(caret.p2().x() - bidiFlagHalf, caret.p2().y() - slope + (
sign * bidiFlagHalf));
683 point4 = QPointF(point3.x() - bidiFlagHalf, point3.y() - slope - (
sign * bidiFlagHalf));
685 p.lineTo(tf.map(point3));
686 p.lineTo(tf.map(point4));
688 caret = tf.map(caret);
695 int start = qMin(pos,
anchor);
696 int end = qMax(pos,
anchor);
697 end = qMin(
d->cursorPos.size()-1, end);
699 if (start == end || start < 0) {
700 return QPainterPath();
704 p.setFillRule(Qt::WindingFill);
705 for (
int i = start+1; i <= end; i++) {
719 poly << first.p1() << first.p2() << last.p2() << last.p1() << first.p1();
720 p.addPolygon(tf.map(poly));
728 int start = qMin(pos,
anchor);
729 int end = qMax(pos,
anchor);
731 if (start == end || start < 0 || end >=
d->cursorPos.size()) {
732 return QPainterPath();
734 QPainterPathStroker stroker;
737 stroker.setCapStyle(Qt::FlatCap);
739 stroker.setDashPattern(Qt::SolidLine);
741 stroker.setDashPattern(Qt::DashLine);
743 stroker.setDashPattern(Qt::DotLine);
745 stroker.setDashPattern(Qt::SolidLine);
748 QPainterPath underPath;
749 QPainterPath overPath;
750 QPainterPath middlePath;
751 qint32 strokeWidth = 0;
753 for (
int i = start+1; i <= end; i++) {
759 QPointF last = first;
781 underPath.moveTo(tf.map(first));
782 underPath.lineTo(tf.map(last));
786 overPath.moveTo(tf.map(first+diff));
787 overPath.lineTo(tf.map(last+diff));
790 middlePath.moveTo(tf.map(first+(diff*0.5)));
791 middlePath.lineTo(tf.map(last+(diff*0.5)));
795 const qreal freetypePixelsToPt = (1.0 / 64.0) * (72. / qMin(
d->xRes,
d->yRes));
796 const qreal width = strokeWidth > 0 ? qMax(qreal(strokeWidth/qMax(1, end-(start+1)))*freetypePixelsToPt, minimum): minimum;
798 stroker.setWidth(thick? width*2: width);
802 final.addPath(stroker.createStroke(underPath));
805 final.addPath(stroker.createStroke(overPath));
808 final.addPath(stroker.createStroke(middlePath));
817 int b =
d->cursorPos.size();
818 if (start >= 0 && end >= 0) {
822 double closest = std::numeric_limits<double>::max();
824 for (
int i = a; i < b; i++) {
843 bool overlaps =
false;
844 int initialPos =
posForPoint(point, -1, -1, &overlaps);
852 int candidateLineStart = 0;
853 double closest = std::numeric_limits<double>::max();
854 for (
int i = 0; i <
d->cursorPos.size(); i++) {
864 if (caret.p1().y() > point.y() && caret.p2().y() <= point.y() && closest >
distance) {
865 candidateLineStart = i;
869 if (caret.p2().x() > point.x() && caret.p1().x() <= point.x() && closest >
distance) {
870 candidateLineStart = i;
877 if (candidateLineStart > -1) {
878 int end =
lineEnd(candidateLineStart);
879 initialPos =
posForPoint(point, candidateLineStart, qMin(end + 1,
d->cursorPos.size()));
888 if (
d->cursorPos.isEmpty() || index < 0) {
891 for (
int i = 0; i<
d->cursorPos.size(); i++) {
892 if (skipSynthetic &&
d->cursorPos.at(i).synthetic) {
895 if (
d->cursorPos.at(i).index <= index) {
897 if (
d->cursorPos.at(i).index == index && firstIndex) {
900 }
else if (
d->cursorPos.at(i).index > index) {
910 if (
d->cursorPos.isEmpty() || pos < 0) {
914 return d->getCursorPos(pos).index;
919 return d->initialTextPosition;
924 bool success =
false;
925 int currentIndex = 0;
930 int elementIndex = 0;
931 int insertionIndex = 0;
932 if (pos > -1 && !
d->cursorPos.isEmpty()) {
937 elementIndex = qMin(elementIndex,
d->result.size()-1);
939 auto it =
d->findTextContentElementForIndex(
d->textData, currentIndex, elementIndex);
940 if (it !=
d->textData.depthFirstTailEnd()) {
941 const int offset = insertionIndex - currentIndex;
942 it->insertText(offset, text);
944 d->insertTransforms(
d->textData, insertionIndex, text.size(), (elementIndex == insertionIndex));
954 bool success =
false;
958 int currentLength =
length;
960 while (currentLength > 0) {
961 int currentIndex = 0;
963 auto it =
d->findTextContentElementForIndex(
d->textData, currentIndex, index,
true);
964 if (it !=
d->textData.depthFirstTailEnd()) {
965 int offset = index > currentIndex? index - currentIndex: 0;
966 int size = it->numChars(
false);
967 it->removeText(offset, currentLength);
968 int diff =
size - it->numChars(
false);
969 currentLength -= diff;
972 if (index >= currentIndex) {
973 index = currentIndex + offset;
976 d->removeTransforms(
d->textData, index, endLength);
1000 hierarchy.append(parentIt);
1003 while (!hierarchy.isEmpty()) {
1004 auto it = hierarchy.takeLast();
1006 p.inheritFrom(props,
true);
1017 if (
d->isLoading)
return props;
1019 if (((startPos < 0 || startPos >=
d->cursorPos.size()) && startPos == endPos) ||
d->cursorPos.isEmpty()) {
1023 const int startIndex =
d->getCursorPos(startPos).index;
1024 const int endIndex =
d->getCursorPos(endPos).index;
1025 int sought = startIndex;
1026 if (startIndex == endIndex) {
1027 int currentIndex = 0;
1028 auto it =
d->findTextContentElementForIndex(
d->textData, currentIndex, sought);
1029 if (it !=
d->textData.depthFirstTailEnd()) {
1033 props.append(it->properties);
1037 it =
d->findTextContentElementForIndex(
d->textData, currentIndex, sought - 1);
1038 if (it !=
d->textData.depthFirstTailEnd()) {
1042 props.append(it->properties);
1047 while(sought < endIndex) {
1048 int currentIndex = 0;
1049 auto it =
d->findTextContentElementForIndex(
d->textData, currentIndex, sought);
1055 }
else if (it !=
d->textData.depthFirstTailEnd()) {
1059 props.append(it->properties);
1062 sought = currentIndex + it->numChars(
false);
1071 if (pos < 0 || d->
cursorPos.isEmpty()) {
1073 d->textData.childBegin()->properties = properties;
1081 int currentIndex = 0;
1082 auto it =
d->findTextContentElementForIndex(
d->textData, currentIndex, res.
plaintTextIndex);
1083 if (it !=
d->textData.depthFirstTailEnd()) {
1084 it->properties = properties;
1093 const QSet<KoSvgTextProperties::PropertyId> removeProperties)
1095 if ((startPos < 0 && startPos == endPos) ||
d->cursorPos.isEmpty()) {
1098 d->textData.childBegin()->properties.setProperty(
p, properties.
property(
p));
1101 d->textData.childBegin()->properties.removeProperty(
p);
1121 const int startIndex =
d->getCursorPos(startPos).index;
1122 const int endIndex =
d->getCursorPos(endPos).index;
1123 if (startIndex != endIndex) {
1124 KoSvgTextShape::Private::splitContentElement(
d->textData, startIndex);
1125 KoSvgTextShape::Private::splitContentElement(
d->textData, endIndex);
1127 bool changed =
false;
1128 int currentIndex = 0;
1130 bool isWrapping = !
d->shapesInside.isEmpty() || !inlineSize.
isAuto;
1131 for (
auto it =
d->textData.depthFirstTailBegin(); it !=
d->textData.depthFirstTailEnd(); it++) {
1132 if (KoSvgTextShape::Private::childCount(siblingCurrent(it)) > 0) {
1136 if (currentIndex >= startIndex && currentIndex < endIndex) {
1139 d->textData.childBegin()->properties.removeProperty(
p);
1141 it->properties.removeProperty(
p);
1146 d->textData.childBegin()->properties.setProperty(
p, properties.
property(
p));
1148 it->properties.setProperty(
p, properties.
property(
p));
1154 currentIndex += it->numChars(
false);
1158 KoSvgTextShape::Private::cleanUp(
d->textData);
1174 int endRange = index +
length;
1175 int size = KoSvgTextShape::Private::numChars(clone->
d->textData.childBegin(),
false) - endRange;
1178 KoSvgTextShape::Private::cleanUp(clone->
d->textData);
1179 return std::unique_ptr<KoSvgTextShape>(clone);
1184 bool success =
false;
1185 int currentIndex = 0;
1186 int elementIndex = 0;
1187 int insertionIndex = 0;
1189 if (isEnd(richText->
d->textData.childBegin())) {
1194 if (pos > -1 && !
d->cursorPos.isEmpty()) {
1199 elementIndex = qMin(elementIndex,
d->result.size()-1);
1202 KoSvgTextShape::Private::splitContentElement(this->
d->textData, insertionIndex);
1204 auto it =
d->findTextContentElementForIndex(
d->textData, currentIndex, insertionIndex);
1205 auto richTextIt =
d->textData.childEnd();
1206 if (it !=
d->textData.depthFirstTailEnd()) {
1207 richTextIt =
d->textData.move(richText->
d->textData.childBegin(), siblingCurrent(it));
1211 it =
d->findTextContentElementForIndex(
d->textData, currentIndex, elementIndex);
1212 if (it !=
d->textData.depthFirstTailEnd()) {
1213 richTextIt =
d->textData.move(richText->
d->textData.childBegin(), siblingEnd(siblingCurrent(it)));
1218 if (richTextIt !=
d->textData.childEnd()) {
1221 richTextIt->properties.removeProperty(
p);
1226 if (parentIt != parentEnd) {
1228 if (inheritPropertiesIfPossible && parentIt != parentEnd) {
1230 if (richTextIt->properties.inheritsProperty(
p, parentIt->properties)) {
1231 richTextIt->properties.removeProperty(
p);
1247 KoSvgTextShape::Private::cleanUp(
d->textData);
1254 if ((startPos < 0 && startPos == endPos) ||
d->cursorPos.isEmpty()) {
1257 const int startIndex =
d->getCursorPos(qMin(startPos, endPos)).index;
1258 int endIndex =
d->getCursorPos(qMax(startPos, endPos)).index;
1259 if (startIndex == endIndex) {
1261 while(
d->result.at(endIndex).middle) {
1263 if (endIndex >
d->result.size())
break;
1267 bool changed =
false;
1270 Private::removeTransforms(
d->textData, startIndex, endIndex-startIndex);
1271 QPointF totalStartDelta;
1272 QPointF anchorAbsolute;
1273 QPointF anchorCssPos;
1281 if (deltaPosition) {
1282 for (
int i = 0; i< startIndex; i++) {
1285 totalStartDelta.setX(0);
1286 anchorAbsolute.setX(*tf.
xPos);
1289 totalStartDelta.setY(0);
1290 anchorAbsolute.setY(*tf.
yPos);
1300 QPointF positionAtVisualEnd = (rtl?
d->result.first():
d->result.last()).finalPosition;
1302 anchorAbsolute = positionAtVisualEnd;
1304 anchorAbsolute = positionAtVisualEnd/2;
1308 int currentIndex = 0;
1309 QPointF accumulatedOffset;
1310 for (
auto it =
d->textData.depthFirstTailBegin(); it !=
d->textData.depthFirstTailEnd(); it++) {
1311 if (KoSvgTextShape::Private::childCount(siblingCurrent(it)) > 0 || !it->textPathId.isEmpty()) {
1315 int endContentElement = it->finalResultIndex;
1317 if (endContentElement >= startIndex && currentIndex <= endIndex) {
1320 int addressableOffset = 0;
1321 for (
int i = currentIndex; (i < endContentElement); i++) {
1324 addressableOffset += 1;
1328 const int transformIndex = (i - startIndex) - addressableOffset;
1332 auto getDelta = [res, totalStartDelta, accumulatedOffset, anchorAbsolute, anchorCssPos, startAdvance] (QPointF pos) -> QPointF {
1334 delta -= (totalStartDelta + accumulatedOffset + (res.
cssPosition-anchorCssPos) + startAdvance);
1338 auto getAbsolute = [res, tf, anchorAbsolute] (QPointF pos) -> QPointF {
1343 if (i < startIndex) {
1344 if (!deltaPosition) {
1358 if (i >= endIndex) {
1359 if (i == endIndex) {
1363 tf.
dxPos = delta.x();
1364 tf.
dyPos = delta.y();
1366 const QPointF
p = getAbsolute(res.
finalPosition) - anchorAbsolute;
1378 if (rotateDegrees.size()+startIndex > i) {
1381 if (positions.size()+startIndex > i) {
1382 const QPointF pos = positions.value(transformIndex, QPointF());
1384 if (deltaPosition) {
1391 QPointF delta = getDelta(pos);
1392 tf.
dxPos = delta.x();
1393 tf.
dyPos = delta.y();
1396 accumulatedOffset = QPointF();
1402 const QPointF delta = getAbsolute(pos);
1403 tf.
xPos = delta.x();
1404 tf.
yPos = delta.y();
1412 it->localTransformations = transforms;
1415 currentIndex = it->finalResultIndex;
1416 if (currentIndex > endIndex) {
1423 KoSvgTextShape::Private::cleanUp(
d->textData);
1447 if ((startPos < 0 && startPos == endPos) ||
d->cursorPos.isEmpty()) {
1450 const int finalPos =
d->cursorPos.size()-1;
1451 const int startIndex =
d->getCursorPos(qMin(startPos, endPos)).index;
1452 const int endIndex =
d->getCursorPos(qMax(startPos, endPos)).index;
1454 for (
int i = startIndex; i < endIndex; i++) {
1460 if (endIndex == startIndex) {
1461 bool final = qMax(startPos, endPos) == finalPos;
1462 CharacterResult resFinal =
final?
d->result.last():
d->result.value(startIndex);
1473 if ((startPos < 0 && startPos == endPos) ||
d->cursorPos.isEmpty()) {
1476 const int startIndex =
d->getCursorPos(qMin(startPos, endPos)).index;
1477 const int endIndex =
d->getCursorPos(qMax(startPos, endPos)).index;
1479 d->removeTransforms(
d->textData, startIndex, endIndex-startIndex);
1481 KoSvgTextShape::Private::cleanUp(
d->textData);
1488 if (child->properties.hasProperty(propertyId)) {
1492 if (found != child) {
1502 for (
auto it =
d->textData.childBegin(); it !=
d->textData.childEnd(); it++) {
1503 if (it->properties.hasProperty(propertyId)) {
1504 return d->createTextNodeIndex(it);
1508 return d->createTextNodeIndex(found);
1512 return d->createTextNodeIndex(
d->textData.childBegin());
1519 for (
auto child =
d->textData.childBegin(); child !=
d->textData.childEnd(); child++) {
1521 d->startIndexOfIterator(child, node.
d->textElement, startIndex);
1522 endIndex =
d->numChars(node.
d->textElement) + startIndex;
1529 auto candidate =
d->textData.childBegin();
1530 if (
d->isLoading ||
d->cursorPos.isEmpty())
return d->createTextNodeIndex(candidate);
1531 if (childBegin(
d->textData.childBegin()) != childEnd(
d->textData.childBegin())) {
1532 candidate = childBegin(
d->textData.childBegin());
1534 const int index =
d->getCursorPos(pos).index;
1535 int currentIndex = 0;
1537 auto e = Private::findTextContentElementForIndex(
d->textData, currentIndex, index,
true);
1538 if (e ==
d->textData.depthFirstTailEnd()) {
1539 return d->createTextNodeIndex(candidate);
1543 auto parent = Private::findTopLevelParent(
d->textData.childBegin(), element);
1544 if (
parent == childEnd(
d->textData.childBegin()))
return d->createTextNodeIndex(candidate);
1546 return d->createTextNodeIndex(
parent);
1551 auto root =
d->textData.childBegin();
1552 if (
d->isLoading ||
d->cursorPos.isEmpty())
return d->createTextNodeIndex(root);
1554 for (
auto child = childBegin(root); child != childEnd(root); child++) {
1555 if (child->textPathId == textPath->
name()) {
1556 return d->createTextNodeIndex(child);
1559 return d->createTextNodeIndex(root);
1622 if (first !=
Fill) {
1623 if (order.at(1) == first) {
1624 order[1] = order[0];
1626 }
else if (order.at(2) == first) {
1627 order[2] = order[0];
1631 if (second != first && second !=
Stroke) {
1632 if (order.at(2) == second) {
1633 order[2] = order[1];
1638 QVariant::fromValue(order));
1644 return d->plainText;
1659 if (
d->textData.empty())
return false;
1670 if (cursorListener) {
1683 if (cursorListener) {
1692 d->applyWhiteSpace(
d->textData,
true);
1693 d->insertNewLinesAtAnchors(
d->textData, !
d->shapesInside.isEmpty());
1694 d->cleanUp(
d->textData);
1697 if (makeInlineSize) {
1715 if (
d->result.isEmpty())
return;
1716 d->setTransformsFromLayout(
d->textData,
d->result);
1717 d->cleanUp(
d->textData);
1731 bool success =
false;
1734 Q_FOREACH(
KoShape *shape,
d->internalShapes()) {
1736 visibleShapes.append(shape);
1739 const bool writeGroup = !(visibleShapes.isEmpty() || context.
strippedTextMode());
1749 for (
auto it =
d->textData.compositionBegin(); it !=
d->textData.compositionEnd(); it++) {
1751 bool isTextPath =
false;
1752 QMap<QString, QString> shapeSpecificStyles;
1753 if (!it->textPathId.isEmpty()) {
1756 if (it ==
d->textData.compositionBegin()) {
1757 context.
shapeWriter().startElement(
"text",
false);
1765 context.
shapeWriter().addAttribute(
"krita:textVersion", 3);
1767 if (visibleShapes.isEmpty()) {
1780 context.
shapeWriter().startElement(
"textPath",
false);
1782 context.
shapeWriter().startElement(
"tspan",
false);
1793 KoShape *textPath = KoSvgTextShape::Private::textPathByName(it->textPathId,
d->textPaths);
1794 success = it->saveSvg(context,
1795 it ==
d->textData.compositionBegin(),
1796 d->childCount(siblingCurrent(it)) == 0,
1797 shapeSpecificStyles,
1800 if (it ==
d->textData.compositionBegin()) {
1814 bool success =
true;
1816 for (
auto it =
d->textData.compositionBegin(); it !=
d->textData.compositionEnd(); it++) {
1818 QMap<QString, QString> shapeSpecificStyles;
1820 if (it ==
d->textData.compositionBegin()) {
1826 it ==
d->textData.compositionBegin());
1827 parentProps.append(ownProperties);
1829 if (it ==
d->textData.compositionBegin())
1831 bool addedFill =
false;
1832 if (attributes.size() > 0) {
1833 QString styleString;
1834 for (
auto it = attributes.constBegin(); it != attributes.constEnd(); ++it) {
1835 if (QString(it.key().toLatin1().data()).contains(
"text-anchor")) {
1836 QString val = it.value();
1837 if (it.value()==
"middle") {
1839 }
else if (it.value()==
"end") {
1844 styleString.append(
"text-align")
1848 }
else if (QString(it.key().toLatin1().data()).contains(
"fill")) {
1849 styleString.append(
"color")
1854 }
else if (QString(it.key().toLatin1().data()).contains(
"font-size")) {
1855 QString val = it.value();
1856 styleString.append(it.key().toLatin1().data())
1861 styleString.append(it.key().toLatin1().data())
1870 styleString.append(
"color")
1872 .append(b->color().name())
1878 if (
d->childCount(siblingCurrent(it)) == 0) {
1879 debugFlake <<
"saveHTML" <<
this << it->text;
1885 parentProps.pop_back();
1908 d->logicalToVisualCursorPos,
1911 d->initialTextPosition));
1932 auto root =
d->textData.childBegin();
1933 for (
auto child = childBegin(root); child != childEnd(root); child++) {
1934 if (!child->textPathId.isEmpty()) {
1936 qDebug() <<
"missing path is" << child->textPathId;
1937 child->textPathId = QString();
1947 if (
d->bulkActionState) {
1948 d->bulkActionState->layoutSetFromMemento =
true;
1962 if (shapeOffsetBefore || shapeOffsetAfter) {
1963 if (
d->bulkActionState) {
1964 d->bulkActionState->contourHasChanged =
true;
1966 d->updateTextWrappingAreas();
1969 if (
d->bulkActionState) {
1970 d->bulkActionState->layoutSetFromMemento =
true;
1982 for (
auto it = compositionBegin(
d->textData); it != compositionEnd(
d->textData); it++) {
1985 qDebug() << QString(spaces +
"+") << it->text;
1986 qDebug() << QString(spaces +
"|") << it->properties.convertToSvgTextAttributes();
1993 qDebug() << QString(spaces +
"| TextPath set: ") << it->textPathId;
1994 qDebug() << QString(spaces +
"| Transforms set: ") << it->localTransformations;
2006 d->isLoading = disable;
2011 return d->isLoading;
2016 d->disableFontMatching = disable;
2021 return d->disableFontMatching;
2041 if (
d->bulkActionState->changed()) {
2042 if (
d->bulkActionState->contourHasChanged) {
2043 d->updateTextWrappingAreas();
2044 }
else if (
d->bulkActionState->layoutHasChanged) {
2050 updateRect =
d->bulkActionState->originalBoundingRect |
boundingRect();
2053 d->bulkActionState = std::nullopt;
2061 painter.setTransform(
d->shapeGroup->absoluteTransformation().inverted()*painter.transform());
2062 d->internalShapesPainter->paint(painter);
2069 painter.setRenderHint(QPainter::Antialiasing,
false);
2070 painter.setRenderHint(QPainter::SmoothPixmapTransform,
false);
2072 painter.setRenderHint(QPainter::Antialiasing,
true);
2073 painter.setRenderHint(QPainter::SmoothPixmapTransform,
true);
2077 int currentIndex = 0;
2078 if (!
d->result.isEmpty()) {
2079 QPainterPath rootBounds;
2080 rootBounds.addRect(Private::boundingBoxFromTree(
d->textData,
this,
false));
2083 d->paintPaths(painter, rootBounds,
this,
d->result, textRendering, chunk, currentIndex);
2098 if (!
d->internalShapes().isEmpty()) {
2099 Q_FOREACH(
KoShape *shape,
d->internalShapes()) {
2104 if ((
d->shapesInside.isEmpty() &&
d->shapesSubtract.isEmpty())) {
2105 for (
auto it =
d->textData.depthFirstTailBegin(); it !=
d->textData.depthFirstTailEnd(); it++) {
2106 result.addPath(it->associatedOutline);
2107 for (
int i = 0; i < it->textDecorations.values().size(); ++i) {
2108 result.addPath(it->textDecorations.values().at(i));
2117 return outline().boundingRect();
2123 if (
d->internalShapesPainter->contentRect().isValid()) {
2124 shapesRect =
d->internalShapesPainter->contentRect();
2125 if (!(
d->shapesInside.isEmpty() &&
d->shapesSubtract.isEmpty())) {
2129 QRectF
result = Private::boundingBoxFromTree(
d->textData,
this,
true);
2143 const QSizeF oldSize = oRect.size();
2145 if (
size == oldSize)
return;
2148 if (oldSize.isEmpty())
return;
2150 const qreal scaleX =
size.width() / oldSize.width();
2151 const qreal scaleY =
size.height() / oldSize.height();
2153 if (
d->internalShapes().isEmpty()) {
2159 const qreal scaleX =
size.width() / oldSize.width();
2160 const qreal scaleY =
size.height() / oldSize.height();
2162 this->
scale(scaleX, scaleY);
2167 if (!
d->textPaths.isEmpty())
return;
2169 const bool allInternalShapeAreTranslatedOnly = [
this] () {
2170 Q_FOREACH(
KoShape *shape,
d->internalShapes()) {
2178 if (allInternalShapeAreTranslatedOnly) {
2183 Q_FOREACH (
KoShape *internalShape,
d->internalShapes()) {
2191 const bool useGlobalMode =
false;
2192 const bool usePostScaling =
false;
2248 const QTransform
scale = QTransform::fromScale(scaleX, scaleY);
2250 Q_FOREACH (
KoShape *shape,
d->internalShapes()) {
2263 int currentIndex = 0;
2264 if (!
d->result.isEmpty()) {
2265 QPainterPath rootBounds;
2267 d->paintDebug(painter,
d->result, currentIndex);
2271 Q_FOREACH (
KoShape *shapeInside,
d->shapesInside) {
2272 QPainterPath
p = shapeInside->
outline();
2274 painter.strokePath(
p, QPen(Qt::green));
2276 Q_FOREACH (
KoShape *shapeInside,
d->shapesSubtract) {
2277 QPainterPath
p = shapeInside->
outline();
2279 painter.strokePath(
p, QPen(Qt::red));
2284 Q_FOREACH (
LineBox lineBox,
d->lineBoxes) {
2287 pen.setCosmetic(
true);
2289 painter.setBrush(QBrush(Qt::transparent));
2290 pen.setColor(QColor(0, 128, 255, 128));
2291 painter.setPen(pen);
2292 painter.drawLine(chunk.
length);
2293 pen.setColor(QColor(255, 128, 0, 128));
2294 painter.setPen(pen);
2297 pen.setColor(QColor(255, 0, 0, 128));
2298 pen.setStyle(Qt::DashDotDotLine);
2299 painter.setPen(pen);
2301 pen.setColor(QColor(0, 128, 0, 128));
2302 pen.setStyle(Qt::DashDotLine);
2303 painter.setPen(pen);
2313 int currentIndex = 0;
2314 if (!
d->result.empty()) {
2315 shape =
d->collectPaths(
this,
d->result, currentIndex);
2324 if (!
d->shapesInside.isEmpty()) {
2326 }
else if (!inlineSize.
isAuto) {
2329 bool textSpaceCollapse =
false;
2330 for (
auto it =
d->textData.depthFirstTailBegin(); it !=
d->textData.depthFirstTailEnd(); it++) {
2333 textSpaceCollapse =
true;
2336 if (!it->localTransformations.isEmpty()) {
2337 textSpaceCollapse =
true;
2354 Q_FOREACH(
KoShape *shape, shapes) {
2355 if (
d->textPaths.contains(shape)) {
2356 d->removeTextPathId(
d->textData.childBegin(), shape->
name());
2357 d->cleanUp(
d->textData);
2358 d->textPaths.removeAll(shape);
2362 if (
d->shapesSubtract.contains(shape)) {
2363 d->shapesSubtract.removeAll(shape);
2365 d->shapesInside.append(shape);
2367 if (
d->shapesInside.contains(shape)) {
2368 d->shapesInside.removeAll(shape);
2370 d->shapesSubtract.append(shape);
2372 if (!
d->shapeGroup->shapes().contains(shape)) {
2373 d->shapeGroup->addShape(shape);
2380 if (!
d->isLoading) {
2381 d->updateTextWrappingAreas();
2384 d->updateInternalShapesList();
2391 return (shape->
parent() ==
d->shapeGroup.data());
2396 Q_FOREACH(
KoShape *shape, shapes) {
2398 d->removeTextPathId(
d->textData.childBegin(), shape->
name());
2400 d->shapesInside.removeAll(shape);
2401 d->shapesSubtract.removeAll(shape);
2402 d->textPaths.removeAll(shape);
2405 d->shapeGroup->removeShape(shape);
2408 d->cleanUp(
d->textData);
2412 if (!
d->isLoading) {
2413 d->updateTextWrappingAreas();
2415 d->updateInternalShapesList();
2423 const int oldIndex =
d->shapesInside.indexOf(shapeInside);
2424 if (oldIndex < 0)
return;
2427 d->shapesInside.move(oldIndex, index);
2428 if (!
d->isLoading) {
2429 d->updateTextWrappingAreas();
2431 d->updateInternalShapesList();
2438 const int finalPos =
d->cursorPos.size() - 1;
2439 const int startIndex = (startPos == endPos && startPos < 0)? 0:
d->getCursorPos(startPos).index;
2440 const int endIndex = (startPos == endPos && startPos < 0)? finalPos:
d->getCursorPos(endPos).index;
2442 Private::splitTree(
d->textData, startIndex,
false);
2443 Private::splitTree(
d->textData, endIndex,
true);
2444 int currentIndex = 0;
2446 Private::makeTextPathNameUnique(
d->textPaths, textPath);
2449 d->shapeGroup->addShape(textPath);
2450 d->textPaths.append(textPath);
2455 textPathElement.
text =
d->textData.childBegin()->text;
2462 d->textData.childBegin()->properties.removeProperty(
p);
2466 d->textData.childBegin()->text = QString();
2467 d->textData.insert(childEnd(
d->textData.childBegin()), textPathElement);
2470 auto startElement = Private::findTextContentElementForIndex(
d->textData, currentIndex, startIndex,
true);
2472 auto endElement = Private::findTextContentElementForIndex(
d->textData, currentIndex, endIndex,
true);
2475 : childEnd(
d->textData.childBegin());
2477 : childEnd(
d->textData.childBegin());
2482 auto textPathIt = textPathTree.
insert(
2486 for (
auto child = first;
2487 (child != last && child != childEnd(
d->textData.childBegin()));
2489 if (!child->textPathId.isEmpty()) {
2490 Q_FOREACH(
KoShape *shape,
d->textPaths) {
2491 if (shape->
name() == child->textPathId) {
2496 child->textPathId = QString();
2498 movableChildren.append(child);
2500 while (!movableChildren.isEmpty()) {
2501 auto child = movableChildren.takeLast();
2504 d->textData.move(textPathIt, last);
2506 Private::cleanUp(
d->textData);
2508 d->updateInternalShapesList();
2517 const int finalPos =
d->cursorPos.size() - 1;
2518 const int startIndex = (startPos == endPos && startPos < 0)? 0:
d->getCursorPos(startPos).index;
2519 const int endIndex = (startPos == endPos && startPos < 0)? finalPos:
d->getCursorPos(endPos).index;
2521 int currentIndex = 0;
2522 auto startElement = Private::findTextContentElementForIndex(
d->textData, currentIndex, startIndex,
true);
2524 auto endElement = Private::findTextContentElementForIndex(
d->textData, currentIndex, endIndex,
true);
2527 : childEnd(
d->textData.childBegin());
2529 : childEnd(
d->textData.childBegin());
2530 if (last != childEnd(
d->textData.childBegin())) {
2534 if (
KoShape *path = Private::textPathByName(child->textPathId,
d->textPaths)) {
2543 auto root =
d->textData.childBegin();
2544 if (root ==
d->textData.childEnd())
return;
2545 if (
d->textPaths.contains(textPath))
return;
2547 Private::makeTextPathNameUnique(
d->textPaths, textPath);
2550 d->shapeGroup->addShape(textPath);
2551 d->textPaths.append(textPath);
2554 d->textData.insert(childEnd(root), textPathElement);
2559 return d->shapesInside;
2570 return d->currentTextWrappingAreas;
2575 return d->shapesSubtract;
2580 return d->internalShapesPainter->internalShapeManager();
2586 if (!
d->shapesInside.isEmpty()) {
2588 Q_FOREACH(
KoShape* shape,
d->shapesInside) {
2590 shapesInsideList.append(QString(
"url(#%1)").arg(
id));
2592 map.insert(
"shape-inside", shapesInsideList.join(
" "));
2595 map.insert(
"overflow",
"clip");
2597 if (!
d->shapesSubtract.isEmpty()) {
2599 Q_FOREACH(
KoShape* shape,
d->shapesSubtract) {
2601 shapesInsideList.append(QString(
"url(#%1)").arg(
id));
2603 map.insert(
"shape-subtract", shapesInsideList.join(
" "));
2623 t.
name = i18n(
"SVG Text");
2625 t.
toolTip = i18n(
"SVG Text Shape");
2631 Q_UNUSED(documentResources);
2632 debugFlake <<
"Create default svg text shape";
2636 shape->
insertText(0, i18nc(
"Default text for the text shape",
"Placeholder Text"));
2646 QString svgText = params->
stringProperty(
"svgText", i18nc(
"Default text for the text shape",
"<text>Placeholder Text</text>"));
2648 QRectF shapeRect = QRectF(0, 0, 200, 60);
2650 QVariant origin = params->
property(
"origin");
2652#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2653 if (
rect.type()==QVariant::RectF) {
2655 if (
rect.typeId() == QMetaType::QRectF) {
2657 shapeRect =
rect.toRectF();
2665#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2666 if (origin.type() == QVariant::PointF) {
2668 if (origin.typeId() == QMetaType::QPointF) {
qreal length(const QPointF &vec)
float value(const T *src, size_t ch)
QList< QString > QStringList
qreal distance(const QPointF &p1, const QPointF &p2)
KisForest< KoSvgTextContentElement >::child_iterator findNodeIndexForPropertyIdImpl(KisForest< KoSvgTextContentElement >::child_iterator parent, KoSvgTextProperties::PropertyId propertyId)
KoSvgTextProperties inheritProperties(KisForest< KoSvgTextContentElement >::depth_first_tail_iterator it)
KoSvgTextCharacterInfo infoFromCharacterResult(const CharacterResult &res, const int index)
#define KoSvgTextShape_SHAPEID
#define KoSvgTextShape_TEXTCONTOURGROUP
QSharedPointer< KoSvgTextShapeMemento > KoSvgTextShapeMementoSP
The HtmlSavingContext class provides context for saving a flake-based document to html.
KoXmlWriter & shapeWriter()
Provides access to the shape writer.
child_iterator childEnd()
child_iterator insert(child_iterator pos, X &&value)
Inserts element value into position pos. value becomes the child of the same parent as pos and is pla...
child_iterator move(child_iterator subtree, child_iterator newPos)
move a subtree to new position Moves subtree into a new position pointer by newPos....
A simple solid color shape background.
static bool IsCssWordSeparator(QString grapheme)
IsCssWordSeparator CSS has a number of characters it considers word-separators, which are used in jus...
qreal documentResolution() const
QString stringProperty(const QString &name, const QString &defaultValue=QString()) const
bool property(const QString &name, QVariant &value) const
void addTemplate(const KoShapeTemplate ¶ms)
void setToolTip(const QString &tooltip)
void setLoadingPriority(int priority)
void setIconName(const char *iconName)
void setXmlElementNames(const QString &nameSpace, const QStringList &elementNames)
QList< KoShape::ShapeChangeListener * > listeners
KoShapeContainer * parent
QScopedPointer< Private > d
virtual QPainterPath outline() const
KoShapeAnchor * anchor() const
virtual QVector< PaintOrder > paintOrder() const
paintOrder
static QVector< PaintOrder > defaultPaintOrder()
default paint order as per SVG specification
virtual void update() const
virtual void shapeChanged(ChangeType type, KoShape *shape=0)
KoShapeContainer * parent() const
void shapeChangedPriv(KoShape::ChangeType type)
QTransform absoluteTransformation() const
void setTransformation(const QTransform &matrix)
ChangeType
Used by shapeChanged() to select which change was made.
@ RotationChanged
used after a setRotation()
@ StrokeChanged
the shapes stroke has changed
@ PositionChanged
used after a setPosition()
@ Deleted
the shape was deleted
@ ShearChanged
used after a shear()
@ ParentChanged
used after a setParent()
@ ContentChanged
the content of the shape changed e.g. a new image inside a pixmap/text change inside a textshape
@ ParameterChanged
the shapes parameter has changed (KoParameterShape only)
@ BackgroundChanged
the shapes background has changed
@ TextContourMarginChanged
used after text properties changed and modified the contour margin in the text shape
@ ScaleChanged
used after a scale()
@ SizeChanged
used after a setSize()
@ GenericMatrixChange
used after the matrix was changed without knowing which property explicitly changed
void scale(qreal sx, qreal sy)
Scale the shape using the zero-point which is the top-left corner.
void removeDependee(KoShape *shape)
QTransform transformation() const
Returns the shapes local transformation matrix.
bool inheritPaintOrder() const
inheritPaintOrder
virtual void setPosition(const QPointF &position)
Set the position of the shape in pt.
virtual void updateAbsolute(const QRectF &rect) const
bool addDependee(KoShape *shape)
void setShapeId(const QString &id)
void setInheritPaintOrder(bool value)
setInheritPaintOrder set inherit paint order.
bool isVisible(bool recursive=true) const
virtual void setSize(const QSizeF &size)
Resize the shape.
The KoSvgTextNodeIndex class.
KoShape * textPath()
textPath
KoSvgTextProperties * properties()
properties The properties for this node as a pointer.
QScopedPointer< Private > d
KoSvgText::TextOnPathInfo * textPathInfo()
textPathInfo the text path info for this node as a pointer.
@ TextAnchorId
KoSvgText::TextAnchor.
@ InlineSizeId
KoSvgText::AutoValue.
@ PaintOrder
QVector<KoShape::PaintOrder>
@ Opacity
Double, SVG shape opacity.
@ KraTextStyleType
string, used to identify the style preset type (character or paragraph).
@ TextDecorationColorId
QColor.
@ TextCollapseId
KoSvgText::TextSpaceCollapse.
@ StrokeId
KoSvgText::StrokeProperty.
@ TextDecorationStyleId
KoSvgText::TextDecorationStyle.
@ FillId
KoSvgText::BackgroundProperty.
@ WritingModeId
KoSvgText::WritingMode.
@ DirectionId
KoSvgText::Direction.
@ TextWrapId
KoSvgText::TextWrap.
@ Visibility
Bool, CSS visibility.
@ TextDecorationLineId
Flags, KoSvgText::TextDecorations.
void removeProperty(PropertyId id)
QList< PropertyId > properties() const
static bool propertyIsBlockOnly(KoSvgTextProperties::PropertyId id)
QMap< QString, QString > convertToSvgTextAttributes() const
QVariant property(PropertyId id, const QVariant &defaultValue=QVariant()) const
QMap< QString, QString > convertParagraphProperties() const
convertParagraphProperties some properties only apply to the root shape, so we write those separately...
static const KoSvgTextProperties & defaultProperties()
bool hasProperty(PropertyId id) const
void setProperty(PropertyId id, const QVariant &value)
QVariant propertyOrDefault(PropertyId id) const
KoSvgText::CssLengthPercentage fontSize() const
KoSvgTextShapeFactory()
constructor
bool supports(const QDomElement &e, KoShapeLoadingContext &context) const override
Reimplemented.
KoShape * createDefaultShape(KoDocumentResourceManager *documentResources=0) const override
KoShape * createShape(const KoProperties *params, KoDocumentResourceManager *documentResources=0) const override
bool convertFromSvg(const QString &svgText, const QString &stylesText, const QRectF &boundsInPixels, qreal pixelsPerInch)
upload the svg representation of text into the shape
QVector< LineBox > lineBoxes
QPointF initialTextPosition
QMap< int, int > logicalToVisualCursorPos
KisForest< KoSvgTextContentElement > textData
KoSvgTextShapeMementoImpl(const KisForest< KoSvgTextContentElement > &textData, const QVector< CharacterResult > &result, const QVector< LineBox > &lineBoxes, const QVector< CursorPos > &cursorPos, const QMap< int, int > &logicalToVisualCursorPos, const QString &plainText, const bool &isBidi, const QPointF &initialTextPosition)
QVector< CharacterResult > result
QVector< CursorPos > cursorPos
~KoSvgTextShapeMementoImpl()
int posLeft(int pos, bool visual=false)
KoSvgText::Direction direction() const
direction Whether the text is left to right or right to left.
int posForPoint(QPointF point, int start=-1, int end=-1, bool *overlaps=nullptr)
posForPoint Finds the closest cursor position for the given point in shape coordinates.
KoShapeStrokeModelSP stroke() const override
QPair< int, int > findRangeForNodeIndex(const KoSvgTextNodeIndex &node) const
findRangeForNodeIndex Find the start and end cursor position for a given nodeIndex.
void setBackground(QSharedPointer< KoShapeBackground > background) override
int wordLeft(int pos, bool visual=false)
wordLeft return the cursorpos for the word left or the extreme of the line.
void notifyCursorPosChanged(int pos, int anchor)
Notify that the cursor position has changed.
void shapeChanged(ChangeType type, KoShape *shape) override
bool setCharacterTransformsOnRange(const int startPos, const int endPos, const QVector< QPointF > positions, const QVector< qreal > rotateDegrees, const bool deltaPosition=true)
setCharacterTransformsOnRange Set SVG 1.1 style character transforms on the given range....
void addTextPathAtEnd(KoShape *textPath)
addTextPathAtEnd add a textpath node at the end of the text.
bool relayoutIsBlocked() const
relayoutIsBlocked
QRectF endBulkAction() override
void setResolution(qreal xRes, qreal yRes) override
KoSvgTextShapeMementoSP getMemento()
Get a memento holding the current textdata and layout info.
void addShapeContours(QList< KoShape * > shapes, const bool inside=true)
addShapesContours Add shapes to the contours that make up the wrapping area.
bool fontMatchingDisabled() const
fontMatchingDisabled
QVector< CursorPos > cursorPos
QPainterPath selectionBoxes(int pos, int anchor)
selectionBoxes returns all selection boxes for a given range. Range will be normalized internally.
QVector< CharacterResult > result
void paint(QPainter &painter) const override
Paint the shape fill The class extending this one is responsible for painting itself....
QSharedPointer< KoShapeBackground > background() const override
KoSvgTextNodeIndex topLevelNodeForPos(int pos) const
topLevelNodeForPos Get, if possible, an index for the child element of the root at pos....
void removeTransformsFromRange(const int startPos, const int endPos)
bool saveHtml(HtmlSavingContext &context)
void setMemento(const KoSvgTextShapeMementoSP memento)
Set the text data and layout info, reset listening cursors to 0.
QRectF boundingRect() const override
Get the bounding box of the shape.
QList< QPainterPath > textWrappingAreas() const
textWrappingAreas The text wrapping areas are computed from shapesInside() and shapesSubtract(),...
int wordRight(int pos, bool visual=false)
wordRight return the cursorpos for the word right or the extreme of the line.
void setSize(const QSizeF &size) override
Resize the shape.
void notifyMarkupChanged()
Notify that the markup has changed.
KoSvgTextNodeIndex findNodeIndexForPropertyId(KoSvgTextProperties::PropertyId propertyId)
findNodeIndexForPropertyId
int indexForPos(int pos) const
indexForPos get the string index for a given cursor position.
int posUp(int pos, bool visual=false)
return position above.
KoShape * textOutline() const
textOutline This turns the text object into non-text KoShape(s) to the best of its abilities.
@ PreformattedText
Text-on-Path falls under this or PrePositionedText depending on collapse of lines.
@ TextInShape
Uses shape-inside to wrap and preserves spaces.
@ InlineWrap
Uses inline size to wrap and preserves spaces.
std::unique_ptr< KoSvgTextShape > copyRange(int index, int length) const
copyRange Copy the rich text for the given range.
bool setTextPathOnRange(KoShape *textPath, const int startPos=-1, const int endPos=-1)
setTextPathOnRange Set a text path on the specified range. In SVG text paths are always at the first ...
QList< KoShape * > shapesInside
static const QString & defaultPlaceholderText()
int nextLine(int pos)
nextLine get a position on the next line for this position.
QPointF initialTextPosition
void setPaintOrder(PaintOrder first, PaintOrder second) override
setPaintOrder set the paint order. As there's only three entries in any given paintorder,...
int previousLine(int pos)
previousLine get a position on the previous line for this position.
int posForPointLineSensitive(QPointF point)
posForPointLineSensitive When clicking on an empty space in a wrapped text, it is preferable to have ...
void leaveNodeSubtree()
Set the current node to its parent, leaving the subtree.
void setFontMatchingDisabled(const bool disable)
setDisableFontMatching
void cleanUp()
Cleans up the internal text data. Used by undo commands.
int lineStart(int pos)
lineStart return the 'line start' for this pos. This uses anchored chunks, so each absolute x,...
QPainterPath defaultCursorShape()
defaultCursorShape This returns a default cursor shape for when there's no text inside the text shape...
QVector< PaintOrder > paintOrder() const override
paintOrder
KoSvgTextProperties propertiesForPos(const int pos, bool inherited=false) const
Return the properties at a given position.
bool shapeInContours(KoShape *shape)
shapeInContours
int wordEnd(int pos)
wordEnd return the pos of the first wordbreak.
bool singleNode() const
singleNode Sometimes it is useful to know whether there's only a single text node for UX purposes.
int previousIndex(int pos)
previousIndex Return the first pos which has a lower string index.
void moveShapeInsideToIndex(KoShape *shapeInside, const int index)
moveShapeInsideToIndex Because the order of shapes inside shape-inside affects the text layout,...
void setShapesSubtract(QList< KoShape * > shapesSubtract)
setShapesSubtract
QPainterPath cursorForPos(int pos, QLineF &caret, QColor &color, double bidiFlagSize=1.0)
cursorForPos returns the QPainterPath associated with this cursorPosition.
int lineEnd(int pos)
lineEnd return the 'line end' for this pos. This uses anchored chunks, so each absolute x,...
void enterNodeSubtree()
Set the current node to its first child, entering the subtree.
void setPropertiesAtPos(int pos, KoSvgTextProperties properties)
setPropertiesAtPos will set the properties at pos.
int posDown(int pos, bool visual=false)
return position below.
KoShapeManager * internalShapeManager() const
internalShapeManager
void debugParsing()
Outputs debug with the current textData tree.
QList< KoSvgTextProperties > propertiesForRange(const int startPos, const int endPos, bool inherited=false) const
propertiesForRange get the properties for a range.
TextType textType() const
textType This enum gives an indication of what kind of text this shape is. The different text types a...
int posForIndex(int index, bool firstIndex=false, bool skipSynthetic=false) const
posForIndex Get the cursor position for a given index in a string.
KoSvgTextNodeIndex nodeForTextPath(KoShape *textPath) const
nodeForTextPath TextPaths are set on toplevel content elements. This function allows for searching wh...
int nextPos(int pos, bool visual)
nextPos get the next position.
void mergePropertiesIntoRange(const int startPos, const int endPos, const KoSvgTextProperties properties, const QSet< KoSvgTextProperties::PropertyId > removeProperties=QSet< KoSvgTextProperties::PropertyId >())
mergePropertiesIntoRange Merge given properties into the given range. This will first split the nodes...
QList< KoSvgTextCharacterInfo > getPositionsAndRotationsForRange(const int startPos, const int endPos) const
getPositionsAndRotationsForRange
void setCharacterTransformsFromLayout()
setCharacterTransformsFromLayout Converts the text to a prepositioned SVG 1.1 text....
void setShapesInside(QList< KoShape * > shapesInside)
setShapesInside
QScopedPointer< Private > d
int wordStart(int pos)
wordStart return the first pos before a wordbreak in the start direction.
QPainterPath outline() const override
QList< KoShape * > textPathsAtRange(const int startPos=-1, const int endPos=-1)
textPathsAtRange Get a list of textPaths at the given range. This includes textPaths whose node is on...
void setMementoImpl(const KoSvgTextShapeMementoSP memento)
void startBulkAction() override
void paintStroke(QPainter &painter) const override
paintStroke paints the shape's stroked outline
bool saveSvg(SvgSavingContext &context) override
Saves SVG data.
int previousPos(int pos, bool visual)
previousPos get the previous position.
QMap< QString, QString > shapeTypeSpecificStyles(SvgSavingContext &context) const
int posRight(int pos, bool visual=false)
QList< KoShape * > shapesSubtract
void setRelayoutBlocked(const bool disable)
void setStroke(KoShapeStrokeModelSP stroke) override
KoShape * cloneShape() const override
creates a deep copy of the shape or shape's subtree
bool insertRichText(int pos, const KoSvgTextShape *richText, bool inheritPropertiesIfPossible=false)
insertRichText Insert rich text at the given cursor pos. This will first split contents at the given ...
QSizeF size() const override
Get the size of the shape in pt.
QList< KoShape * > textPaths
QPainterPath underlines(int pos, int anchor, KoSvgText::TextDecorations decor, KoSvgText::TextDecorationStyle style, qreal minimum, bool thick)
void paintDebug(QPainter &painter, DebugElements elements) const
bool insertText(int pos, QString text)
insertText Insert a text somewhere in the KoTextShape.
bool removeText(int &index, int &length)
removeText Where insert text explicitly uses a cursorposition, remove text uses a string index....
void convertCharTransformsToPreformatted(bool makeInlineSize=false)
convertCharTransformsToPreformatted Converts the text to a preformatted SVG 2.0 text....
void removeShapesFromContours(QList< KoShape * > shapes, bool callUpdate=true, bool cleanup=true)
removeShapesFromContours Remove list of shapes from any of the internal lists.
KoSvgText::WritingMode writingMode() const
writingMode There's a number of places we need to check the writing mode to provide proper controls.
~KoSvgTextShape() override
static QList< QPainterPath > generateTextAreas(const QList< KoShape * > shapesInside, const QList< KoShape * > shapesSubtract, const KoSvgTextProperties &props)
generateTextAreas Generates text areas with the given shapes and properties. This is used to paint pr...
QRectF outlineRect() const override
int nextIndex(int pos)
nextIndex Return the first cursor position with a higher string index.
KoSvgTextProperties textProperties() const
void startElement(const char *tagName, bool indentInside=true)
void addTextNode(const QString &str)
void addAttribute(const char *attrName, const QString &value)
Context for saving svg files.
QString createUID(const QString &base)
Create a unique id from the specified base text.
QScopedPointer< KoXmlWriter > shapeWriter
QString getID(const KoShape *obj)
Returns the unique id for the given shape.
static void saveSvgFill(QSharedPointer< KoShapeBackground > background, const bool fillRuleEvenOdd, const QRectF outlineRect, const QSizeF size, const QTransform absoluteTransform, SvgSavingContext &context)
Saves fill style of specified shape.
static QString embedShape(const KoShape *shape, SvgSavingContext &context)
static void saveSvgBasicStyle(const bool isVisible, const qreal transparency, const QVector< KoShape::PaintOrder > paintOrder, bool inheritPaintorder, SvgSavingContext &context, bool textShape=false)
Saves only stroke, fill and transparency of the shape.
static void saveSvgStyle(KoShape *shape, SvgSavingContext &context)
Saves the style of the specified shape.
static void saveMetadata(const KoShape *shape, SvgSavingContext &context)
static void saveSvgStroke(KoShapeStrokeModelSP, SvgSavingContext &context)
Saves stroke style of specified shape.
static void writeTransformAttributeLazy(const QString &name, const QTransform &transform, KoXmlWriter &shapeWriter)
Writes a transform as an attribute name iff the transform is not empty.
Implements exporting shapes to SVG.
bool saveDetached(QIODevice &outputDevice)
#define KIS_SAFE_ASSERT_RECOVER(cond)
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
bool isWhiteSpace(char c)
qreal kisDistance(const QPointF &pt1, const QPointF &pt2)
T kisRadiansToDegrees(T radians)
T kisDegreesToRadians(T degrees)
#define koIconNameCStr(name)
ResultIterator hierarchyBegin(Iterator it)
ChildIterator< value_type, is_const > childBegin(const ChildIterator< value_type, is_const > &it)
ResultIterator hierarchyEnd(Iterator it)
ChildIterator< value_type, is_const > siblingCurrent(ChildIterator< value_type, is_const > it)
int size(const Forest< T > &forest)
ChildIterator< value_type, is_const > siblingEnd(const ChildIterator< value_type, is_const > &it)
ChildIterator< value_type, is_const > childEnd(const ChildIterator< value_type, is_const > &it)
int depth(typename Forest< T >::const_child_iterator beginIt, typename Forest< T >::const_child_iterator endIt)
KRITAFLAKE_EXPORT void resizeShapeCommon(KoShape *shape, qreal scaleX, qreal scaleY, const QPointF &absoluteStillPoint, bool useGlobalMode, bool usePostScaling, const QTransform &postScalingCoveringTransform)
TextAnchor
Where the text is anchored for SVG 1.1 text and 'inline-size'.
@ AnchorEnd
Anchor right for LTR, left for RTL.
@ AnchorMiddle
Anchor to the middle.
TextDecorationStyle
Style of the text-decoration.
@ Solid
Draw a solid line.Ex: --—.
@ Dashed
Draw a dashed line. Ex: - - - - -.
@ Dotted
Draw a dotted line. Ex: .....
Direction
Base direction used by Bidi algorithm.
@ Collapse
Collapse white space sequences into a single character.
@ PreserveSpaces
required for 'xml:space="preserve"' emulation.
QPointF finalPosition
the final position, taking into account both CSS and SVG positioning considerations.
KoSvgText::FontMetrics metrics
Fontmetrics for current font, in Freetype scanline coordinates.
bool anchored_chunk
whether this is the start of a new chunk.
QPointF textPathAndAnchoringOffset
Offset caused by textPath and anchoring.
QPointF cssPosition
the position in accordance with the CSS specs, as opossed to the SVG spec.
QRectF layoutBox() const
layoutBox
QPointF textLengthOffset
offset caused by textLength
QTransform finalTransform() const
QLineF caret
Caret for this characterResult.
bool rtl
Whether the current glyph is right-to-left, as opposed to the markup.
QColor color
Which color the current position has.
QVector< QPointF > offsets
The advance offsets for each grapheme index.
int cluster
Which character result this position belongs in.
int offset
Which offset this position belongs with.
QString iconName
Icon name.
QString toolTip
The tooltip text for the template.
QString name
The name to be shown for this template.
The KoSvgTextCharacterInfo class This is a small struct to convey information about character positio...
KoSvgText::FontMetrics metrics
<Whether the character is in the middle of a cluster.
The KoSvgTextContentElement struct.
KoSvgTextProperties properties
The textProperties. This includes.
QString textPathId
The textpath's name, if any.
QString text
Plain text of the current node. Use insertText and removeText to manipulate it.
KisForest< KoSvgTextContentElement >::child_iterator textElement
Private(KisForest< KoSvgTextContentElement >::child_iterator _textElement, KoShape *_textPath)
ShapeChangeListener so we can inform any text cursors that the cursor needs updating.
void notifyShapeChanged(ChangeType type, KoShape *shape) override
virtual void notifyMarkupChanged()=0
virtual void notifyCursorPosChanged(int pos, int anchor)=0
BackgroundProperty is a special wrapper around KoShapeBackground for managing it in KoSvgTextProperti...
bool startsNewChunk() const
QPointF absolutePos() const
boost::optional< qreal > yPos
boost::optional< qreal > dxPos
boost::optional< qreal > dyPos
boost::optional< qreal > rotate
QPointF relativeOffset() const
boost::optional< qreal > xPos
qint32 underlineThickness
underline thickness from font.
StrokeProperty is a special wrapper around KoShapeStrokeModel for managing it in KoSvgTextProperties.
QPointF baselineTop
Used to identify the top of the line for baseline-alignment.
QVector< LineChunk > chunks
QPointF baselineBottom
Used to identify the bottom of the line for baseline-alignment.
QVector< int > chunkIndices
charResult indices that belong to this chunk.
QLineF length
Used to measure how long the current line is allowed to be.