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->cursorPos.at(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->cursorPos.at(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 int currentIndex =
d->cursorPos.at(pos).index;
411 for (
int i = pos; i >= 0; i--) {
412 if (
d->cursorPos.at(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);
587 if (pos <= 0 || pos >
d->cursorPos.size() ||
d->result.isEmpty() ||
d->cursorPos.isEmpty()) {
592 for (
int i = pos; i >= 0; i--) {
612 p.moveTo(0, fontSize*0.2);
613 p.lineTo(0, -fontSize);
615 p.moveTo(-fontSize * 0.5, 0);
616 p.lineTo(fontSize, 0);
618 p.translate(
d->initialTextPosition);
625 if (
d->result.isEmpty() ||
d->cursorPos.isEmpty() || pos < 0 || pos >=
d->cursorPos.size()) {
639 p.moveTo(tf.map(caret.p1()));
640 p.lineTo(tf.map(caret.p2()));
641 if (
d->isBidi && bidiFlagSize > 0) {
643 double bidiFlagHalf = bidiFlagSize * 0.5;
647 qreal slope = bidiFlagHalf * (caret.dx()/ caret.dy());
648 point3 = QPointF(caret.p2().x() + slope + (
sign * bidiFlagHalf), caret.p2().y() + bidiFlagHalf);
649 point4 = QPointF(point3.x() + slope - (
sign * bidiFlagHalf),point3.y() + bidiFlagHalf);
651 qreal slope = bidiFlagHalf * (caret.dy()/ caret.dx());
652 point3 = QPointF(caret.p2().x() - bidiFlagHalf, caret.p2().y() - slope + (
sign * bidiFlagHalf));
653 point4 = QPointF(point3.x() - bidiFlagHalf, point3.y() - slope - (
sign * bidiFlagHalf));
655 p.lineTo(tf.map(point3));
656 p.lineTo(tf.map(point4));
658 caret = tf.map(caret);
665 int start = qMin(pos,
anchor);
666 int end = qMax(pos,
anchor);
667 end = qMin(
d->cursorPos.size()-1, end);
669 if (start == end || start < 0) {
670 return QPainterPath();
674 p.setFillRule(Qt::WindingFill);
675 for (
int i = start+1; i <= end; i++) {
689 poly << first.p1() << first.p2() << last.p2() << last.p1() << first.p1();
690 p.addPolygon(tf.map(poly));
698 int start = qMin(pos,
anchor);
699 int end = qMax(pos,
anchor);
701 if (start == end || start < 0 || end >=
d->cursorPos.size()) {
702 return QPainterPath();
704 QPainterPathStroker stroker;
707 stroker.setCapStyle(Qt::FlatCap);
709 stroker.setDashPattern(Qt::SolidLine);
711 stroker.setDashPattern(Qt::DashLine);
713 stroker.setDashPattern(Qt::DotLine);
715 stroker.setDashPattern(Qt::SolidLine);
718 QPainterPath underPath;
719 QPainterPath overPath;
720 QPainterPath middlePath;
721 qint32 strokeWidth = 0;
723 for (
int i = start+1; i <= end; i++) {
729 QPointF last = first;
751 underPath.moveTo(tf.map(first));
752 underPath.lineTo(tf.map(last));
756 overPath.moveTo(tf.map(first+diff));
757 overPath.lineTo(tf.map(last+diff));
760 middlePath.moveTo(tf.map(first+(diff*0.5)));
761 middlePath.lineTo(tf.map(last+(diff*0.5)));
765 const qreal freetypePixelsToPt = (1.0 / 64.0) * (72. / qMin(
d->xRes,
d->yRes));
766 const qreal width = strokeWidth > 0 ? qMax(qreal(strokeWidth/qMax(1, end-(start+1)))*freetypePixelsToPt, minimum): minimum;
768 stroker.setWidth(thick? width*2: width);
772 final.addPath(stroker.createStroke(underPath));
775 final.addPath(stroker.createStroke(overPath));
778 final.addPath(stroker.createStroke(middlePath));
787 int b =
d->cursorPos.size();
788 if (start >= 0 && end >= 0) {
792 double closest = std::numeric_limits<double>::max();
794 for (
int i = a; i < b; i++) {
813 bool overlaps =
false;
814 int initialPos =
posForPoint(point, -1, -1, &overlaps);
822 int candidateLineStart = 0;
823 double closest = std::numeric_limits<double>::max();
824 for (
int i = 0; i <
d->cursorPos.size(); i++) {
834 if (caret.p1().y() > point.y() && caret.p2().y() <= point.y() && closest >
distance) {
835 candidateLineStart = i;
839 if (caret.p2().x() > point.x() && caret.p1().x() <= point.x() && closest >
distance) {
840 candidateLineStart = i;
847 if (candidateLineStart > -1) {
848 int end =
lineEnd(candidateLineStart);
849 initialPos =
posForPoint(point, candidateLineStart, qMin(end + 1,
d->cursorPos.size()));
858 if (
d->cursorPos.isEmpty() || index < 0) {
861 for (
int i = 0; i<
d->cursorPos.size(); i++) {
862 if (skipSynthetic &&
d->cursorPos.at(i).synthetic) {
865 if (
d->cursorPos.at(i).index <= index) {
867 if (
d->cursorPos.at(i).index == index && firstIndex) {
870 }
else if (
d->cursorPos.at(i).index > index) {
880 if (
d->cursorPos.isEmpty() || pos < 0) {
884 return d->cursorPos.at(qMin(
d->cursorPos.size()-1, pos)).index;
889 return d->initialTextPosition;
894 bool success =
false;
895 int currentIndex = 0;
900 int elementIndex = 0;
901 int insertionIndex = 0;
902 if (pos > -1 && !
d->cursorPos.isEmpty()) {
907 elementIndex = qMin(elementIndex,
d->result.size()-1);
909 auto it =
d->findTextContentElementForIndex(
d->textData, currentIndex, elementIndex);
910 if (it !=
d->textData.depthFirstTailEnd()) {
911 const int offset = insertionIndex - currentIndex;
912 it->insertText(offset, text);
914 d->insertTransforms(
d->textData, insertionIndex, text.size(), (elementIndex == insertionIndex));
924 bool success =
false;
928 int currentLength =
length;
930 while (currentLength > 0) {
931 int currentIndex = 0;
933 auto it =
d->findTextContentElementForIndex(
d->textData, currentIndex, index,
true);
934 if (it !=
d->textData.depthFirstTailEnd()) {
935 int offset = index > currentIndex? index - currentIndex: 0;
936 int size = it->numChars(
false);
937 it->removeText(offset, currentLength);
938 int diff =
size - it->numChars(
false);
939 currentLength -= diff;
942 if (index >= currentIndex) {
943 index = currentIndex + offset;
946 d->removeTransforms(
d->textData, index, endLength);
970 hierarchy.append(parentIt);
973 while (!hierarchy.isEmpty()) {
974 auto it = hierarchy.takeLast();
976 p.inheritFrom(props,
true);
987 if (
d->isLoading)
return props;
989 if (((startPos < 0 || startPos >=
d->cursorPos.size()) && startPos == endPos) ||
d->cursorPos.isEmpty()) {
993 const int finalPos =
d->cursorPos.size() - 1;
994 const int startIndex =
d->cursorPos.at(qBound(0, startPos, finalPos)).index;
995 const int endIndex =
d->cursorPos.at(qBound(0, endPos, finalPos)).index;
996 int sought = startIndex;
997 if (startIndex == endIndex) {
998 int currentIndex = 0;
999 auto it =
d->findTextContentElementForIndex(
d->textData, currentIndex, sought);
1000 if (it !=
d->textData.depthFirstTailEnd()) {
1004 props.append(it->properties);
1008 it =
d->findTextContentElementForIndex(
d->textData, currentIndex, sought - 1);
1009 if (it !=
d->textData.depthFirstTailEnd()) {
1013 props.append(it->properties);
1018 while(sought < endIndex) {
1019 int currentIndex = 0;
1020 auto it =
d->findTextContentElementForIndex(
d->textData, currentIndex, sought);
1026 }
else if (it !=
d->textData.depthFirstTailEnd()) {
1030 props.append(it->properties);
1033 sought = currentIndex + it->numChars(
false);
1042 if (pos < 0 || d->
cursorPos.isEmpty()) {
1044 d->textData.childBegin()->properties = properties;
1052 int currentIndex = 0;
1053 auto it =
d->findTextContentElementForIndex(
d->textData, currentIndex, res.
plaintTextIndex);
1054 if (it !=
d->textData.depthFirstTailEnd()) {
1055 it->properties = properties;
1064 const QSet<KoSvgTextProperties::PropertyId> removeProperties)
1066 if ((startPos < 0 && startPos == endPos) ||
d->cursorPos.isEmpty()) {
1069 d->textData.childBegin()->properties.setProperty(
p, properties.
property(
p));
1072 d->textData.childBegin()->properties.removeProperty(
p);
1092 const int startIndex =
d->cursorPos.at(startPos).index;
1093 const int endIndex =
d->cursorPos.at(endPos).index;
1094 if (startIndex != endIndex) {
1095 KoSvgTextShape::Private::splitContentElement(
d->textData, startIndex);
1096 KoSvgTextShape::Private::splitContentElement(
d->textData, endIndex);
1098 bool changed =
false;
1099 int currentIndex = 0;
1101 bool isWrapping = !
d->shapesInside.isEmpty() || !inlineSize.
isAuto;
1102 for (
auto it =
d->textData.depthFirstTailBegin(); it !=
d->textData.depthFirstTailEnd(); it++) {
1103 if (KoSvgTextShape::Private::childCount(siblingCurrent(it)) > 0) {
1107 if (currentIndex >= startIndex && currentIndex < endIndex) {
1110 d->textData.childBegin()->properties.removeProperty(
p);
1112 it->properties.removeProperty(
p);
1117 d->textData.childBegin()->properties.setProperty(
p, properties.
property(
p));
1119 it->properties.setProperty(
p, properties.
property(
p));
1125 currentIndex += it->numChars(
false);
1129 KoSvgTextShape::Private::cleanUp(
d->textData);
1145 int endRange = index +
length;
1146 int size = KoSvgTextShape::Private::numChars(clone->
d->textData.childBegin(),
false) - endRange;
1149 KoSvgTextShape::Private::cleanUp(clone->
d->textData);
1150 return std::unique_ptr<KoSvgTextShape>(clone);
1155 bool success =
false;
1156 int currentIndex = 0;
1157 int elementIndex = 0;
1158 int insertionIndex = 0;
1160 if (isEnd(richText->
d->textData.childBegin())) {
1165 if (pos > -1 && !
d->cursorPos.isEmpty()) {
1170 elementIndex = qMin(elementIndex,
d->result.size()-1);
1173 KoSvgTextShape::Private::splitContentElement(this->
d->textData, insertionIndex);
1175 auto it =
d->findTextContentElementForIndex(
d->textData, currentIndex, insertionIndex);
1176 auto richTextIt =
d->textData.childEnd();
1177 if (it !=
d->textData.depthFirstTailEnd()) {
1178 richTextIt =
d->textData.move(richText->
d->textData.childBegin(), siblingCurrent(it));
1182 it =
d->findTextContentElementForIndex(
d->textData, currentIndex, elementIndex);
1183 if (it !=
d->textData.depthFirstTailEnd()) {
1184 richTextIt =
d->textData.move(richText->
d->textData.childBegin(), siblingEnd(siblingCurrent(it)));
1189 if (richTextIt !=
d->textData.childEnd()) {
1192 richTextIt->properties.removeProperty(
p);
1197 if (parentIt != parentEnd) {
1199 if (inheritPropertiesIfPossible && parentIt != parentEnd) {
1201 if (richTextIt->properties.inheritsProperty(
p, parentIt->properties)) {
1202 richTextIt->properties.removeProperty(
p);
1218 KoSvgTextShape::Private::cleanUp(
d->textData);
1225 if ((startPos < 0 && startPos == endPos) ||
d->cursorPos.isEmpty()) {
1228 const int finalPos =
d->cursorPos.size()-1;
1229 const int startIndex =
d->cursorPos.at(qBound(0, qMin(startPos, endPos), finalPos)).index;
1230 int endIndex =
d->cursorPos.at(qBound(0, qMax(startPos, endPos), finalPos)).index;
1231 if (startIndex == endIndex) {
1233 while(
d->result.at(endIndex).middle) {
1235 if (endIndex >
d->result.size())
break;
1239 bool changed =
false;
1242 Private::removeTransforms(
d->textData, startIndex, endIndex-startIndex);
1243 QPointF totalStartDelta;
1244 QPointF anchorAbsolute;
1245 QPointF anchorCssPos;
1253 if (deltaPosition) {
1254 for (
int i = 0; i< startIndex; i++) {
1257 totalStartDelta.setX(0);
1258 anchorAbsolute.setX(*tf.
xPos);
1261 totalStartDelta.setY(0);
1262 anchorAbsolute.setY(*tf.
yPos);
1272 QPointF positionAtVisualEnd = (rtl?
d->result.first():
d->result.last()).finalPosition;
1274 anchorAbsolute = positionAtVisualEnd;
1276 anchorAbsolute = positionAtVisualEnd/2;
1280 int currentIndex = 0;
1281 QPointF accumulatedOffset;
1282 for (
auto it =
d->textData.depthFirstTailBegin(); it !=
d->textData.depthFirstTailEnd(); it++) {
1283 if (KoSvgTextShape::Private::childCount(siblingCurrent(it)) > 0 || !it->textPathId.isEmpty()) {
1287 int endContentElement = it->finalResultIndex;
1289 if (endContentElement >= startIndex && currentIndex <= endIndex) {
1292 int addressableOffset = 0;
1293 for (
int i = currentIndex; (i < endContentElement); i++) {
1296 addressableOffset += 1;
1300 const int transformIndex = (i - startIndex) - addressableOffset;
1304 auto getDelta = [res, totalStartDelta, accumulatedOffset, anchorAbsolute, anchorCssPos, startAdvance] (QPointF pos) -> QPointF {
1306 delta -= (totalStartDelta + accumulatedOffset + (res.
cssPosition-anchorCssPos) + startAdvance);
1310 auto getAbsolute = [res, tf, anchorAbsolute] (QPointF pos) -> QPointF {
1315 if (i < startIndex) {
1316 if (!deltaPosition) {
1330 if (i >= endIndex) {
1331 if (i == endIndex) {
1335 tf.
dxPos = delta.x();
1336 tf.
dyPos = delta.y();
1338 const QPointF
p = getAbsolute(res.
finalPosition) - anchorAbsolute;
1350 if (rotateDegrees.size()+startIndex > i) {
1353 if (positions.size()+startIndex > i) {
1354 const QPointF pos = positions.value(transformIndex, QPointF());
1356 if (deltaPosition) {
1363 QPointF delta = getDelta(pos);
1364 tf.
dxPos = delta.x();
1365 tf.
dyPos = delta.y();
1368 accumulatedOffset = QPointF();
1374 const QPointF delta = getAbsolute(pos);
1375 tf.
xPos = delta.x();
1376 tf.
yPos = delta.y();
1384 it->localTransformations = transforms;
1387 currentIndex = it->finalResultIndex;
1388 if (currentIndex > endIndex) {
1395 KoSvgTextShape::Private::cleanUp(
d->textData);
1419 if ((startPos < 0 && startPos == endPos) ||
d->cursorPos.isEmpty()) {
1422 const int finalPos =
d->cursorPos.size()-1;
1423 const int startIndex =
d->cursorPos.at(qBound(0, qMin(startPos, endPos), finalPos)).index;
1424 const int endIndex =
d->cursorPos.at(qBound(0, qMax(startPos, endPos), finalPos)).index;
1426 for (
int i = startIndex; i < endIndex; i++) {
1432 if (endIndex == startIndex) {
1433 bool final = qMax(startPos, endPos) == finalPos;
1434 CharacterResult resFinal =
final?
d->result.last():
d->result.value(startIndex);
1445 if ((startPos < 0 && startPos == endPos) ||
d->cursorPos.isEmpty()) {
1448 const int finalPos =
d->cursorPos.size()-1;
1449 const int startIndex =
d->cursorPos.at(qBound(0, qMin(startPos, endPos), finalPos)).index;
1450 const int endIndex =
d->cursorPos.at(qBound(0, qMax(startPos, endPos), finalPos)).index;
1452 d->removeTransforms(
d->textData, startIndex, endIndex-startIndex);
1454 KoSvgTextShape::Private::cleanUp(
d->textData);
1461 if (child->properties.hasProperty(propertyId)) {
1465 if (found != child) {
1475 for (
auto it =
d->textData.childBegin(); it !=
d->textData.childEnd(); it++) {
1476 if (it->properties.hasProperty(propertyId)) {
1477 return d->createTextNodeIndex(it);
1481 return d->createTextNodeIndex(found);
1485 return d->createTextNodeIndex(
d->textData.childBegin());
1492 for (
auto child =
d->textData.childBegin(); child !=
d->textData.childEnd(); child++) {
1494 d->startIndexOfIterator(child, node.
d->textElement, startIndex);
1495 endIndex =
d->numChars(node.
d->textElement) + startIndex;
1502 auto candidate =
d->textData.childBegin();
1503 if (
d->isLoading ||
d->cursorPos.isEmpty())
return d->createTextNodeIndex(candidate);
1504 if (childBegin(
d->textData.childBegin()) != childEnd(
d->textData.childBegin())) {
1505 candidate = childBegin(
d->textData.childBegin());
1507 const int finalPos =
d->cursorPos.size() - 1;
1508 const int index =
d->cursorPos.at(qBound(0, pos, finalPos)).index;
1509 int currentIndex = 0;
1511 auto e = Private::findTextContentElementForIndex(
d->textData, currentIndex, index,
true);
1512 if (e ==
d->textData.depthFirstTailEnd()) {
1513 return d->createTextNodeIndex(candidate);
1517 auto parent = Private::findTopLevelParent(
d->textData.childBegin(), element);
1518 if (
parent == childEnd(
d->textData.childBegin()))
return d->createTextNodeIndex(candidate);
1520 return d->createTextNodeIndex(
parent);
1525 auto root =
d->textData.childBegin();
1526 if (
d->isLoading ||
d->cursorPos.isEmpty())
return d->createTextNodeIndex(root);
1528 for (
auto child = childBegin(root); child != childEnd(root); child++) {
1529 if (child->textPathId == textPath->
name()) {
1530 return d->createTextNodeIndex(child);
1533 return d->createTextNodeIndex(root);
1596 if (first !=
Fill) {
1597 if (order.at(1) == first) {
1598 order[1] = order[0];
1600 }
else if (order.at(2) == first) {
1601 order[2] = order[0];
1605 if (second != first && second !=
Stroke) {
1606 if (order.at(2) == second) {
1607 order[2] = order[1];
1612 QVariant::fromValue(order));
1618 return d->plainText;
1628 if (
d->textData.empty())
return false;
1639 if (cursorListener) {
1652 if (cursorListener) {
1661 d->applyWhiteSpace(
d->textData,
true);
1662 d->insertNewLinesAtAnchors(
d->textData, !
d->shapesInside.isEmpty());
1663 d->cleanUp(
d->textData);
1666 if (makeInlineSize) {
1684 if (
d->result.isEmpty())
return;
1685 d->setTransformsFromLayout(
d->textData,
d->result);
1686 d->cleanUp(
d->textData);
1700 bool success =
false;
1703 Q_FOREACH(
KoShape *shape,
d->internalShapes()) {
1705 visibleShapes.append(shape);
1708 const bool writeGroup = !(visibleShapes.isEmpty() || context.
strippedTextMode());
1718 for (
auto it =
d->textData.compositionBegin(); it !=
d->textData.compositionEnd(); it++) {
1720 bool isTextPath =
false;
1721 QMap<QString, QString> shapeSpecificStyles;
1722 if (!it->textPathId.isEmpty()) {
1725 if (it ==
d->textData.compositionBegin()) {
1726 context.
shapeWriter().startElement(
"text",
false);
1734 context.
shapeWriter().addAttribute(
"krita:textVersion", 3);
1736 if (visibleShapes.isEmpty()) {
1749 context.
shapeWriter().startElement(
"textPath",
false);
1751 context.
shapeWriter().startElement(
"tspan",
false);
1762 KoShape *textPath = KoSvgTextShape::Private::textPathByName(it->textPathId,
d->textPaths);
1763 success = it->saveSvg(context,
1764 it ==
d->textData.compositionBegin(),
1765 d->childCount(siblingCurrent(it)) == 0,
1766 shapeSpecificStyles,
1769 if (it ==
d->textData.compositionBegin()) {
1783 bool success =
true;
1785 for (
auto it =
d->textData.compositionBegin(); it !=
d->textData.compositionEnd(); it++) {
1787 QMap<QString, QString> shapeSpecificStyles;
1789 if (it ==
d->textData.compositionBegin()) {
1795 it ==
d->textData.compositionBegin());
1796 parentProps.append(ownProperties);
1798 if (it ==
d->textData.compositionBegin())
1800 bool addedFill =
false;
1801 if (attributes.size() > 0) {
1802 QString styleString;
1803 for (
auto it = attributes.constBegin(); it != attributes.constEnd(); ++it) {
1804 if (QString(it.key().toLatin1().data()).contains(
"text-anchor")) {
1805 QString val = it.value();
1806 if (it.value()==
"middle") {
1808 }
else if (it.value()==
"end") {
1813 styleString.append(
"text-align")
1817 }
else if (QString(it.key().toLatin1().data()).contains(
"fill")) {
1818 styleString.append(
"color")
1823 }
else if (QString(it.key().toLatin1().data()).contains(
"font-size")) {
1824 QString val = it.value();
1825 styleString.append(it.key().toLatin1().data())
1830 styleString.append(it.key().toLatin1().data())
1839 styleString.append(
"color")
1841 .append(b->color().name())
1847 if (
d->childCount(siblingCurrent(it)) == 0) {
1848 debugFlake <<
"saveHTML" <<
this << it->text;
1854 parentProps.pop_back();
1877 d->logicalToVisualCursorPos,
1880 d->initialTextPosition));
1901 auto root =
d->textData.childBegin();
1902 for (
auto child = childBegin(root); child != childEnd(root); child++) {
1903 if (!child->textPathId.isEmpty()) {
1905 qDebug() <<
"missing path is" << child->textPathId;
1906 child->textPathId = QString();
1927 if (shapeOffsetBefore || shapeOffsetAfter) {
1928 d->updateTextWrappingAreas();
1938 for (
auto it = compositionBegin(
d->textData); it != compositionEnd(
d->textData); it++) {
1941 qDebug() << QString(spaces +
"+") << it->text;
1942 qDebug() << QString(spaces +
"|") << it->properties.convertToSvgTextAttributes();
1949 qDebug() << QString(spaces +
"| TextPath set: ") << it->textPathId;
1950 qDebug() << QString(spaces +
"| Transforms set: ") << it->localTransformations;
1962 d->isLoading = disable;
1967 return d->isLoading;
1972 d->disableFontMatching = disable;
1977 return d->disableFontMatching;
1997 if (
d->bulkActionState->changed()) {
1998 if (
d->bulkActionState->contourHasChanged) {
1999 d->updateTextWrappingAreas();
2000 }
else if (
d->bulkActionState->layoutHasChanged) {
2005 updateRect =
d->bulkActionState->originalBoundingRect |
boundingRect();
2008 d->bulkActionState = std::nullopt;
2016 painter.setTransform(
d->shapeGroup->absoluteTransformation().inverted()*painter.transform());
2017 d->internalShapesPainter->paint(painter);
2024 painter.setRenderHint(QPainter::Antialiasing,
false);
2025 painter.setRenderHint(QPainter::SmoothPixmapTransform,
false);
2027 painter.setRenderHint(QPainter::Antialiasing,
true);
2028 painter.setRenderHint(QPainter::SmoothPixmapTransform,
true);
2032 int currentIndex = 0;
2033 if (!
d->result.isEmpty()) {
2034 QPainterPath rootBounds;
2038 d->paintPaths(painter, rootBounds,
this,
d->result, textRendering, chunk, currentIndex);
2053 if (!
d->internalShapes().isEmpty()) {
2054 Q_FOREACH(
KoShape *shape,
d->internalShapes()) {
2059 if ((
d->shapesInside.isEmpty() &&
d->shapesSubtract.isEmpty())) {
2060 for (
auto it =
d->textData.depthFirstTailBegin(); it !=
d->textData.depthFirstTailEnd(); it++) {
2061 result.addPath(it->associatedOutline);
2062 for (
int i = 0; i < it->textDecorations.values().size(); ++i) {
2063 result.addPath(it->textDecorations.values().at(i));
2072 return outline().boundingRect();
2078 if (
d->internalShapesPainter->contentRect().isValid()) {
2079 shapesRect =
d->internalShapesPainter->contentRect();
2080 if (!(
d->shapesInside.isEmpty() &&
d->shapesSubtract.isEmpty())) {
2087 for (
auto it =
d->textData.compositionBegin(); it !=
d->textData.compositionEnd(); it++) {
2094 QRectF bb = it->associatedOutline.boundingRect();
2095 QMap<KoSvgText::TextDecoration, QPainterPath> decorations = it->textDecorations;
2096 for (
int i = 0; i < decorations.values().size(); ++i) {
2097 bb |= decorations.values().at(i).boundingRect();
2099 if (!bb.isEmpty()) {
2102 stroke->strokeInsets(
this, insets);
2110 parentStrokes.pop_back();
2127 const QSizeF oldSize = oRect.size();
2129 if (
size == oldSize)
return;
2132 if (oldSize.isEmpty())
return;
2134 const qreal scaleX =
size.width() / oldSize.width();
2135 const qreal scaleY =
size.height() / oldSize.height();
2137 if (
d->internalShapes().isEmpty()) {
2143 const qreal scaleX =
size.width() / oldSize.width();
2144 const qreal scaleY =
size.height() / oldSize.height();
2146 this->
scale(scaleX, scaleY);
2151 if (!
d->textPaths.isEmpty())
return;
2153 const bool allInternalShapeAreTranslatedOnly = [
this] () {
2154 Q_FOREACH(
KoShape *shape,
d->internalShapes()) {
2162 if (allInternalShapeAreTranslatedOnly) {
2167 Q_FOREACH (
KoShape *internalShape,
d->internalShapes()) {
2175 const bool useGlobalMode =
false;
2176 const bool usePostScaling =
false;
2232 const QTransform
scale = QTransform::fromScale(scaleX, scaleY);
2234 Q_FOREACH (
KoShape *shape,
d->internalShapes()) {
2247 int currentIndex = 0;
2248 if (!
d->result.isEmpty()) {
2249 QPainterPath rootBounds;
2251 d->paintDebug(painter,
d->result, currentIndex);
2255 Q_FOREACH (
KoShape *shapeInside,
d->shapesInside) {
2256 QPainterPath
p = shapeInside->
outline();
2258 painter.strokePath(
p, QPen(Qt::green));
2260 Q_FOREACH (
KoShape *shapeInside,
d->shapesSubtract) {
2261 QPainterPath
p = shapeInside->
outline();
2263 painter.strokePath(
p, QPen(Qt::red));
2268 Q_FOREACH (
LineBox lineBox,
d->lineBoxes) {
2271 pen.setCosmetic(
true);
2273 painter.setBrush(QBrush(Qt::transparent));
2274 pen.setColor(QColor(0, 128, 255, 128));
2275 painter.setPen(pen);
2276 painter.drawLine(chunk.
length);
2277 pen.setColor(QColor(255, 128, 0, 128));
2278 painter.setPen(pen);
2281 pen.setColor(QColor(255, 0, 0, 128));
2282 pen.setStyle(Qt::DashDotDotLine);
2283 painter.setPen(pen);
2285 pen.setColor(QColor(0, 128, 0, 128));
2286 pen.setStyle(Qt::DashDotLine);
2287 painter.setPen(pen);
2297 int currentIndex = 0;
2298 if (!
d->result.empty()) {
2299 shape =
d->collectPaths(
this,
d->result, currentIndex);
2308 if (!
d->shapesInside.isEmpty()) {
2310 }
else if (!inlineSize.
isAuto) {
2313 bool textSpaceCollapse =
false;
2314 for (
auto it =
d->textData.depthFirstTailBegin(); it !=
d->textData.depthFirstTailEnd(); it++) {
2317 textSpaceCollapse =
true;
2320 if (!it->localTransformations.isEmpty()) {
2321 textSpaceCollapse =
true;
2338 Q_FOREACH(
KoShape *shape, shapes) {
2339 if (
d->textPaths.contains(shape)) {
2340 d->removeTextPathId(
d->textData.childBegin(), shape->
name());
2341 d->cleanUp(
d->textData);
2342 d->textPaths.removeAll(shape);
2346 if (
d->shapesSubtract.contains(shape)) {
2347 d->shapesSubtract.removeAll(shape);
2349 d->shapesInside.append(shape);
2351 if (
d->shapesInside.contains(shape)) {
2352 d->shapesInside.removeAll(shape);
2354 d->shapesSubtract.append(shape);
2356 if (!
d->shapeGroup->shapes().contains(shape)) {
2357 d->shapeGroup->addShape(shape);
2364 if (!
d->isLoading) {
2365 d->updateTextWrappingAreas();
2368 d->updateInternalShapesList();
2375 return (shape->
parent() ==
d->shapeGroup.data());
2380 Q_FOREACH(
KoShape *shape, shapes) {
2382 d->removeTextPathId(
d->textData.childBegin(), shape->
name());
2384 d->shapesInside.removeAll(shape);
2385 d->shapesSubtract.removeAll(shape);
2386 d->textPaths.removeAll(shape);
2389 d->shapeGroup->removeShape(shape);
2392 d->cleanUp(
d->textData);
2396 if (!
d->isLoading) {
2397 d->updateTextWrappingAreas();
2399 d->updateInternalShapesList();
2407 const int oldIndex =
d->shapesInside.indexOf(shapeInside);
2408 if (oldIndex < 0)
return;
2411 d->shapesInside.move(oldIndex, index);
2412 if (!
d->isLoading) {
2413 d->updateTextWrappingAreas();
2415 d->updateInternalShapesList();
2422 const int finalPos =
d->cursorPos.size() - 1;
2423 const int startIndex = (startPos == endPos && startPos < 0)? 0:
d->cursorPos.at(qBound(0, startPos, finalPos)).index;
2424 const int endIndex = (startPos == endPos && startPos < 0)? finalPos:
d->cursorPos.at(qBound(0, endPos, finalPos)).index;
2426 Private::splitTree(
d->textData, startIndex,
false);
2427 Private::splitTree(
d->textData, endIndex,
true);
2428 int currentIndex = 0;
2430 Private::makeTextPathNameUnique(
d->textPaths, textPath);
2433 d->shapeGroup->addShape(textPath);
2434 d->textPaths.append(textPath);
2439 textPathElement.
text =
d->textData.childBegin()->text;
2446 d->textData.childBegin()->properties.removeProperty(
p);
2450 d->textData.childBegin()->text = QString();
2451 d->textData.insert(childEnd(
d->textData.childBegin()), textPathElement);
2454 auto startElement = Private::findTextContentElementForIndex(
d->textData, currentIndex, startIndex,
true);
2456 auto endElement = Private::findTextContentElementForIndex(
d->textData, currentIndex, endIndex,
true);
2459 : childEnd(
d->textData.childBegin());
2461 : childEnd(
d->textData.childBegin());
2466 auto textPathIt = textPathTree.
insert(
2470 for (
auto child = first;
2471 (child != last && child != childEnd(
d->textData.childBegin()));
2473 if (!child->textPathId.isEmpty()) {
2474 Q_FOREACH(
KoShape *shape,
d->textPaths) {
2475 if (shape->
name() == child->textPathId) {
2480 child->textPathId = QString();
2482 movableChildren.append(child);
2484 while (!movableChildren.isEmpty()) {
2485 auto child = movableChildren.takeLast();
2488 d->textData.move(textPathIt, last);
2490 Private::cleanUp(
d->textData);
2492 d->updateInternalShapesList();
2501 const int finalPos =
d->cursorPos.size() - 1;
2502 const int startIndex = (startPos == endPos && startPos < 0)? 0:
d->cursorPos.at(qBound(0, startPos, finalPos)).index;
2503 const int endIndex = (startPos == endPos && startPos < 0)? finalPos:
d->cursorPos.at(qBound(0, endPos, finalPos)).index;
2505 int currentIndex = 0;
2506 auto startElement = Private::findTextContentElementForIndex(
d->textData, currentIndex, startIndex,
true);
2508 auto endElement = Private::findTextContentElementForIndex(
d->textData, currentIndex, endIndex,
true);
2511 : childEnd(
d->textData.childBegin());
2513 : childEnd(
d->textData.childBegin());
2514 if (last != childEnd(
d->textData.childBegin())) {
2518 if (
KoShape *path = Private::textPathByName(child->textPathId,
d->textPaths)) {
2527 auto root =
d->textData.childBegin();
2528 if (root ==
d->textData.childEnd())
return;
2529 if (
d->textPaths.contains(textPath))
return;
2531 Private::makeTextPathNameUnique(
d->textPaths, textPath);
2534 d->shapeGroup->addShape(textPath);
2535 d->textPaths.append(textPath);
2538 d->textData.insert(childEnd(root), textPathElement);
2543 return d->shapesInside;
2554 return d->currentTextWrappingAreas;
2559 return d->shapesSubtract;
2564 return d->internalShapesPainter->internalShapeManager();
2570 if (!
d->shapesInside.isEmpty()) {
2572 Q_FOREACH(
KoShape* shape,
d->shapesInside) {
2574 shapesInsideList.append(QString(
"url(#%1)").arg(
id));
2576 map.insert(
"shape-inside", shapesInsideList.join(
" "));
2579 map.insert(
"overflow",
"clip");
2581 if (!
d->shapesSubtract.isEmpty()) {
2583 Q_FOREACH(
KoShape* shape,
d->shapesSubtract) {
2585 shapesInsideList.append(QString(
"url(#%1)").arg(
id));
2587 map.insert(
"shape-subtract", shapesInsideList.join(
" "));
2607 t.
name = i18n(
"SVG Text");
2609 t.
toolTip = i18n(
"SVG Text Shape");
2615 Q_UNUSED(documentResources);
2616 debugFlake <<
"Create default svg text shape";
2620 shape->
insertText(0, i18nc(
"Default text for the text shape",
"Placeholder Text"));
2630 QString svgText = params->
stringProperty(
"svgText", i18nc(
"Default text for the text shape",
"<text>Placeholder Text</text>"));
2632 QRectF shapeRect = QRectF(0, 0, 200, 60);
2634 QVariant origin = params->
property(
"origin");
2636 if (
rect.type()==QVariant::RectF) {
2637 shapeRect =
rect.toRectF();
2645 if (origin.type() == QVariant::PointF) {
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)
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)
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.
qreal bottom
Bottom inset.
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.