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(qBound(0, pos,
d->cursorPos.size())).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->cursorPos.at(qBound(0, pos,
d->cursorPos.size())).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;
1633 if (
d->textData.empty())
return false;
1644 if (cursorListener) {
1657 if (cursorListener) {
1666 d->applyWhiteSpace(
d->textData,
true);
1667 d->insertNewLinesAtAnchors(
d->textData, !
d->shapesInside.isEmpty());
1668 d->cleanUp(
d->textData);
1671 if (makeInlineSize) {
1689 if (
d->result.isEmpty())
return;
1690 d->setTransformsFromLayout(
d->textData,
d->result);
1691 d->cleanUp(
d->textData);
1705 bool success =
false;
1708 Q_FOREACH(
KoShape *shape,
d->internalShapes()) {
1710 visibleShapes.append(shape);
1713 const bool writeGroup = !(visibleShapes.isEmpty() || context.
strippedTextMode());
1723 for (
auto it =
d->textData.compositionBegin(); it !=
d->textData.compositionEnd(); it++) {
1725 bool isTextPath =
false;
1726 QMap<QString, QString> shapeSpecificStyles;
1727 if (!it->textPathId.isEmpty()) {
1730 if (it ==
d->textData.compositionBegin()) {
1731 context.
shapeWriter().startElement(
"text",
false);
1739 context.
shapeWriter().addAttribute(
"krita:textVersion", 3);
1741 if (visibleShapes.isEmpty()) {
1754 context.
shapeWriter().startElement(
"textPath",
false);
1756 context.
shapeWriter().startElement(
"tspan",
false);
1767 KoShape *textPath = KoSvgTextShape::Private::textPathByName(it->textPathId,
d->textPaths);
1768 success = it->saveSvg(context,
1769 it ==
d->textData.compositionBegin(),
1770 d->childCount(siblingCurrent(it)) == 0,
1771 shapeSpecificStyles,
1774 if (it ==
d->textData.compositionBegin()) {
1788 bool success =
true;
1790 for (
auto it =
d->textData.compositionBegin(); it !=
d->textData.compositionEnd(); it++) {
1792 QMap<QString, QString> shapeSpecificStyles;
1794 if (it ==
d->textData.compositionBegin()) {
1800 it ==
d->textData.compositionBegin());
1801 parentProps.append(ownProperties);
1803 if (it ==
d->textData.compositionBegin())
1805 bool addedFill =
false;
1806 if (attributes.size() > 0) {
1807 QString styleString;
1808 for (
auto it = attributes.constBegin(); it != attributes.constEnd(); ++it) {
1809 if (QString(it.key().toLatin1().data()).contains(
"text-anchor")) {
1810 QString val = it.value();
1811 if (it.value()==
"middle") {
1813 }
else if (it.value()==
"end") {
1818 styleString.append(
"text-align")
1822 }
else if (QString(it.key().toLatin1().data()).contains(
"fill")) {
1823 styleString.append(
"color")
1828 }
else if (QString(it.key().toLatin1().data()).contains(
"font-size")) {
1829 QString val = it.value();
1830 styleString.append(it.key().toLatin1().data())
1835 styleString.append(it.key().toLatin1().data())
1844 styleString.append(
"color")
1846 .append(b->color().name())
1852 if (
d->childCount(siblingCurrent(it)) == 0) {
1853 debugFlake <<
"saveHTML" <<
this << it->text;
1859 parentProps.pop_back();
1882 d->logicalToVisualCursorPos,
1885 d->initialTextPosition));
1906 auto root =
d->textData.childBegin();
1907 for (
auto child = childBegin(root); child != childEnd(root); child++) {
1908 if (!child->textPathId.isEmpty()) {
1910 qDebug() <<
"missing path is" << child->textPathId;
1911 child->textPathId = QString();
1921 if (
d->bulkActionState) {
1922 d->bulkActionState->layoutSetFromMemento =
true;
1936 if (shapeOffsetBefore || shapeOffsetAfter) {
1937 if (
d->bulkActionState) {
1938 d->bulkActionState->contourHasChanged =
true;
1940 d->updateTextWrappingAreas();
1943 if (
d->bulkActionState) {
1944 d->bulkActionState->layoutSetFromMemento =
true;
1956 for (
auto it = compositionBegin(
d->textData); it != compositionEnd(
d->textData); it++) {
1959 qDebug() << QString(spaces +
"+") << it->text;
1960 qDebug() << QString(spaces +
"|") << it->properties.convertToSvgTextAttributes();
1967 qDebug() << QString(spaces +
"| TextPath set: ") << it->textPathId;
1968 qDebug() << QString(spaces +
"| Transforms set: ") << it->localTransformations;
1980 d->isLoading = disable;
1985 return d->isLoading;
1990 d->disableFontMatching = disable;
1995 return d->disableFontMatching;
2015 if (
d->bulkActionState->changed()) {
2016 if (
d->bulkActionState->contourHasChanged) {
2017 d->updateTextWrappingAreas();
2018 }
else if (
d->bulkActionState->layoutHasChanged) {
2024 updateRect =
d->bulkActionState->originalBoundingRect |
boundingRect();
2027 d->bulkActionState = std::nullopt;
2035 painter.setTransform(
d->shapeGroup->absoluteTransformation().inverted()*painter.transform());
2036 d->internalShapesPainter->paint(painter);
2043 painter.setRenderHint(QPainter::Antialiasing,
false);
2044 painter.setRenderHint(QPainter::SmoothPixmapTransform,
false);
2046 painter.setRenderHint(QPainter::Antialiasing,
true);
2047 painter.setRenderHint(QPainter::SmoothPixmapTransform,
true);
2051 int currentIndex = 0;
2052 if (!
d->result.isEmpty()) {
2053 QPainterPath rootBounds;
2054 rootBounds.addRect(Private::boundingBoxFromTree(
d->textData,
this,
false));
2057 d->paintPaths(painter, rootBounds,
this,
d->result, textRendering, chunk, currentIndex);
2072 if (!
d->internalShapes().isEmpty()) {
2073 Q_FOREACH(
KoShape *shape,
d->internalShapes()) {
2078 if ((
d->shapesInside.isEmpty() &&
d->shapesSubtract.isEmpty())) {
2079 for (
auto it =
d->textData.depthFirstTailBegin(); it !=
d->textData.depthFirstTailEnd(); it++) {
2080 result.addPath(it->associatedOutline);
2081 for (
int i = 0; i < it->textDecorations.values().size(); ++i) {
2082 result.addPath(it->textDecorations.values().at(i));
2091 return outline().boundingRect();
2097 if (
d->internalShapesPainter->contentRect().isValid()) {
2098 shapesRect =
d->internalShapesPainter->contentRect();
2099 if (!(
d->shapesInside.isEmpty() &&
d->shapesSubtract.isEmpty())) {
2103 QRectF
result = Private::boundingBoxFromTree(
d->textData,
this,
true);
2117 const QSizeF oldSize = oRect.size();
2119 if (
size == oldSize)
return;
2122 if (oldSize.isEmpty())
return;
2124 const qreal scaleX =
size.width() / oldSize.width();
2125 const qreal scaleY =
size.height() / oldSize.height();
2127 if (
d->internalShapes().isEmpty()) {
2133 const qreal scaleX =
size.width() / oldSize.width();
2134 const qreal scaleY =
size.height() / oldSize.height();
2136 this->
scale(scaleX, scaleY);
2141 if (!
d->textPaths.isEmpty())
return;
2143 const bool allInternalShapeAreTranslatedOnly = [
this] () {
2144 Q_FOREACH(
KoShape *shape,
d->internalShapes()) {
2152 if (allInternalShapeAreTranslatedOnly) {
2157 Q_FOREACH (
KoShape *internalShape,
d->internalShapes()) {
2165 const bool useGlobalMode =
false;
2166 const bool usePostScaling =
false;
2222 const QTransform
scale = QTransform::fromScale(scaleX, scaleY);
2224 Q_FOREACH (
KoShape *shape,
d->internalShapes()) {
2237 int currentIndex = 0;
2238 if (!
d->result.isEmpty()) {
2239 QPainterPath rootBounds;
2241 d->paintDebug(painter,
d->result, currentIndex);
2245 Q_FOREACH (
KoShape *shapeInside,
d->shapesInside) {
2246 QPainterPath
p = shapeInside->
outline();
2248 painter.strokePath(
p, QPen(Qt::green));
2250 Q_FOREACH (
KoShape *shapeInside,
d->shapesSubtract) {
2251 QPainterPath
p = shapeInside->
outline();
2253 painter.strokePath(
p, QPen(Qt::red));
2258 Q_FOREACH (
LineBox lineBox,
d->lineBoxes) {
2261 pen.setCosmetic(
true);
2263 painter.setBrush(QBrush(Qt::transparent));
2264 pen.setColor(QColor(0, 128, 255, 128));
2265 painter.setPen(pen);
2266 painter.drawLine(chunk.
length);
2267 pen.setColor(QColor(255, 128, 0, 128));
2268 painter.setPen(pen);
2271 pen.setColor(QColor(255, 0, 0, 128));
2272 pen.setStyle(Qt::DashDotDotLine);
2273 painter.setPen(pen);
2275 pen.setColor(QColor(0, 128, 0, 128));
2276 pen.setStyle(Qt::DashDotLine);
2277 painter.setPen(pen);
2287 int currentIndex = 0;
2288 if (!
d->result.empty()) {
2289 shape =
d->collectPaths(
this,
d->result, currentIndex);
2298 if (!
d->shapesInside.isEmpty()) {
2300 }
else if (!inlineSize.
isAuto) {
2303 bool textSpaceCollapse =
false;
2304 for (
auto it =
d->textData.depthFirstTailBegin(); it !=
d->textData.depthFirstTailEnd(); it++) {
2307 textSpaceCollapse =
true;
2310 if (!it->localTransformations.isEmpty()) {
2311 textSpaceCollapse =
true;
2328 Q_FOREACH(
KoShape *shape, shapes) {
2329 if (
d->textPaths.contains(shape)) {
2330 d->removeTextPathId(
d->textData.childBegin(), shape->
name());
2331 d->cleanUp(
d->textData);
2332 d->textPaths.removeAll(shape);
2336 if (
d->shapesSubtract.contains(shape)) {
2337 d->shapesSubtract.removeAll(shape);
2339 d->shapesInside.append(shape);
2341 if (
d->shapesInside.contains(shape)) {
2342 d->shapesInside.removeAll(shape);
2344 d->shapesSubtract.append(shape);
2346 if (!
d->shapeGroup->shapes().contains(shape)) {
2347 d->shapeGroup->addShape(shape);
2354 if (!
d->isLoading) {
2355 d->updateTextWrappingAreas();
2358 d->updateInternalShapesList();
2365 return (shape->
parent() ==
d->shapeGroup.data());
2370 Q_FOREACH(
KoShape *shape, shapes) {
2372 d->removeTextPathId(
d->textData.childBegin(), shape->
name());
2374 d->shapesInside.removeAll(shape);
2375 d->shapesSubtract.removeAll(shape);
2376 d->textPaths.removeAll(shape);
2379 d->shapeGroup->removeShape(shape);
2382 d->cleanUp(
d->textData);
2386 if (!
d->isLoading) {
2387 d->updateTextWrappingAreas();
2389 d->updateInternalShapesList();
2397 const int oldIndex =
d->shapesInside.indexOf(shapeInside);
2398 if (oldIndex < 0)
return;
2401 d->shapesInside.move(oldIndex, index);
2402 if (!
d->isLoading) {
2403 d->updateTextWrappingAreas();
2405 d->updateInternalShapesList();
2412 const int finalPos =
d->cursorPos.size() - 1;
2413 const int startIndex = (startPos == endPos && startPos < 0)? 0:
d->cursorPos.at(qBound(0, startPos, finalPos)).index;
2414 const int endIndex = (startPos == endPos && startPos < 0)? finalPos:
d->cursorPos.at(qBound(0, endPos, finalPos)).index;
2416 Private::splitTree(
d->textData, startIndex,
false);
2417 Private::splitTree(
d->textData, endIndex,
true);
2418 int currentIndex = 0;
2420 Private::makeTextPathNameUnique(
d->textPaths, textPath);
2423 d->shapeGroup->addShape(textPath);
2424 d->textPaths.append(textPath);
2429 textPathElement.
text =
d->textData.childBegin()->text;
2436 d->textData.childBegin()->properties.removeProperty(
p);
2440 d->textData.childBegin()->text = QString();
2441 d->textData.insert(childEnd(
d->textData.childBegin()), textPathElement);
2444 auto startElement = Private::findTextContentElementForIndex(
d->textData, currentIndex, startIndex,
true);
2446 auto endElement = Private::findTextContentElementForIndex(
d->textData, currentIndex, endIndex,
true);
2449 : childEnd(
d->textData.childBegin());
2451 : childEnd(
d->textData.childBegin());
2456 auto textPathIt = textPathTree.
insert(
2460 for (
auto child = first;
2461 (child != last && child != childEnd(
d->textData.childBegin()));
2463 if (!child->textPathId.isEmpty()) {
2464 Q_FOREACH(
KoShape *shape,
d->textPaths) {
2465 if (shape->
name() == child->textPathId) {
2470 child->textPathId = QString();
2472 movableChildren.append(child);
2474 while (!movableChildren.isEmpty()) {
2475 auto child = movableChildren.takeLast();
2478 d->textData.move(textPathIt, last);
2480 Private::cleanUp(
d->textData);
2482 d->updateInternalShapesList();
2491 const int finalPos =
d->cursorPos.size() - 1;
2492 const int startIndex = (startPos == endPos && startPos < 0)? 0:
d->cursorPos.at(qBound(0, startPos, finalPos)).index;
2493 const int endIndex = (startPos == endPos && startPos < 0)? finalPos:
d->cursorPos.at(qBound(0, endPos, finalPos)).index;
2495 int currentIndex = 0;
2496 auto startElement = Private::findTextContentElementForIndex(
d->textData, currentIndex, startIndex,
true);
2498 auto endElement = Private::findTextContentElementForIndex(
d->textData, currentIndex, endIndex,
true);
2501 : childEnd(
d->textData.childBegin());
2503 : childEnd(
d->textData.childBegin());
2504 if (last != childEnd(
d->textData.childBegin())) {
2508 if (
KoShape *path = Private::textPathByName(child->textPathId,
d->textPaths)) {
2517 auto root =
d->textData.childBegin();
2518 if (root ==
d->textData.childEnd())
return;
2519 if (
d->textPaths.contains(textPath))
return;
2521 Private::makeTextPathNameUnique(
d->textPaths, textPath);
2524 d->shapeGroup->addShape(textPath);
2525 d->textPaths.append(textPath);
2528 d->textData.insert(childEnd(root), textPathElement);
2533 return d->shapesInside;
2544 return d->currentTextWrappingAreas;
2549 return d->shapesSubtract;
2554 return d->internalShapesPainter->internalShapeManager();
2560 if (!
d->shapesInside.isEmpty()) {
2562 Q_FOREACH(
KoShape* shape,
d->shapesInside) {
2564 shapesInsideList.append(QString(
"url(#%1)").arg(
id));
2566 map.insert(
"shape-inside", shapesInsideList.join(
" "));
2569 map.insert(
"overflow",
"clip");
2571 if (!
d->shapesSubtract.isEmpty()) {
2573 Q_FOREACH(
KoShape* shape,
d->shapesSubtract) {
2575 shapesInsideList.append(QString(
"url(#%1)").arg(
id));
2577 map.insert(
"shape-subtract", shapesInsideList.join(
" "));
2597 t.
name = i18n(
"SVG Text");
2599 t.
toolTip = i18n(
"SVG Text Shape");
2605 Q_UNUSED(documentResources);
2606 debugFlake <<
"Create default svg text shape";
2610 shape->
insertText(0, i18nc(
"Default text for the text shape",
"Placeholder Text"));
2620 QString svgText = params->
stringProperty(
"svgText", i18nc(
"Default text for the text shape",
"<text>Placeholder Text</text>"));
2622 QRectF shapeRect = QRectF(0, 0, 200, 60);
2624 QVariant origin = params->
property(
"origin");
2626#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2627 if (
rect.type()==QVariant::RectF) {
2629 if (
rect.typeId() == QMetaType::QRectF) {
2631 shapeRect =
rect.toRectF();
2639#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2640 if (origin.type() == QVariant::PointF) {
2642 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)
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.