Krita Source Code Documentation
Loading...
Searching...
No Matches
KoSvgTextShape_p_output.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2017 Dmitry Kazakov <dimula73@gmail.com>
3 * SPDX-FileCopyrightText: 2022 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#include "KoSvgTextShape.h"
9#include "KoSvgTextShape_p.h"
10
11#include "KoSvgTextProperties.h"
12
13#include <KoClipMaskPainter.h>
14#include <KoColorBackground.h>
16#include <KoPathShape.h>
17#include <KoShapeStroke.h>
18#include <KoShapeRegistry.h>
19#include <KoShapeFactoryBase.h>
20#include <KoProperties.h>
21#include <KoClipMask.h>
22#include <KoInsets.h>
23#include <KoShapeGroup.h>
24#include <KoShapeGroupCommand.h>
25
26#include <kis_algebra_2d.h>
27
28#include <QPainter>
29#include <QtMath>
30
31#include <variant>
32
36 QVector<KoShape::PaintOrder> &paintOrder) {
37 for (auto parentIt = KisForestDetail::hierarchyBegin(siblingCurrent(it)); parentIt != KisForestDetail::hierarchyEnd(siblingCurrent(it)); parentIt++) {
38 if (parentIt->properties.hasProperty(KoSvgTextProperties::StrokeId)) {
39 stroke = parentIt->properties.stroke();
40 break;
41 }
42 }
43 for (auto parentIt = KisForestDetail::hierarchyBegin(siblingCurrent(it)); parentIt != KisForestDetail::hierarchyEnd(siblingCurrent(it)); parentIt++) {
44 if (parentIt->properties.hasProperty(KoSvgTextProperties::FillId)) {
45 background = parentIt->properties.background();
46 break;
47 }
48 }
49 for (auto parentIt = KisForestDetail::hierarchyBegin(siblingCurrent(it)); parentIt != KisForestDetail::hierarchyEnd(siblingCurrent(it)); parentIt++) {
50 if (parentIt->properties.hasProperty(KoSvgTextProperties::PaintOrder)) {
51 paintOrder = parentIt->properties.propertyOrDefault(KoSvgTextProperties::PaintOrder).value<QVector<KoShape::PaintOrder>>();
52 break;
53 }
54 }
55}
56
57void setRenderHints(QPainter &painter, const KoSvgText::TextRendering textRendering, const bool testAntialiasing) {
58 if (textRendering != KoSvgText::RenderingOptimizeSpeed && testAntialiasing) {
59 // also apply antialiasing only if antialiasing is active on provided target QPainter
60 painter.setRenderHint(QPainter::Antialiasing, true);
61 painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
62 } else {
63 painter.setRenderHint(QPainter::Antialiasing, false);
64 painter.setRenderHint(QPainter::SmoothPixmapTransform, false);
65 }
66}
67
68void KoSvgTextShape::Private::paintTextDecoration(QPainter &painter,
69 const QPainterPath &rootOutline,
70 const KoShape *rootShape,
72 const KoSvgText::TextRendering rendering)
73{
74 KoShapeStrokeModelSP stroke = rootShape->stroke();
77
78 for (auto it = compositionBegin(textData); it != compositionEnd(textData); it++) {
79 if (it.state() == KisForestDetail::Leave) continue;
80
82
83
84 KoInsets insets;
85 if (stroke) {
86 stroke->strokeInsets(rootShape, insets);
87 }
88 QMap<KoSvgText::TextDecoration, QPainterPath> textDecorations = it->textDecorations;
89
90 if (textDecorations.isEmpty() || !textDecorations.contains(type)) continue;
91
92 const QPainterPath decorPath = textDecorations.value(type);
93 const QRect shapeGlobalClipRect = painter.transform().mapRect(decorPath.boundingRect().adjusted(-insets.left, -insets.top, insets.right, insets.bottom)).toAlignedRect();
94
95 if (!shapeGlobalClipRect.isValid()) continue;
96
97 const QRectF clipRect = painter.clipBoundingRect();
98 if (!clipRect.contains(decorPath.boundingRect()) &&
99 !clipRect.intersects(decorPath.boundingRect())) continue;
100 const QColor textDecorationColor = it->properties.propertyOrDefault(KoSvgTextProperties::TextDecorationColorId).value<QColor>();
101 const bool colorValid = textDecorationColor.isValid() && it->properties.hasProperty(KoSvgTextProperties::TextDecorationColorId) && textDecorationColor != Qt::transparent;
102
103 Q_FOREACH(const KoShape::PaintOrder p, paintOrder) {
104 if (p == KoShape::Fill) {
105
106 if (background && !colorValid) {
107 KoClipMaskPainter fillPainter(&painter, shapeGlobalClipRect);
108 setRenderHints(*fillPainter.maskPainter(), rendering, painter.testRenderHint(QPainter::Antialiasing));
109 background->paint(*fillPainter.shapePainter(), rootOutline);
110 fillPainter.maskPainter()->fillPath(rootOutline, Qt::black);
111 fillPainter.maskPainter()->fillPath(decorPath, Qt::white);
112 fillPainter.renderOnGlobalPainter();
113 } else if (colorValid) {
114 painter.fillPath(decorPath, textDecorationColor);
115 }
116 } else if (p == KoShape::Stroke) {
117 if (stroke) {
118 KoShapeStrokeSP strokeSP = qSharedPointerDynamicCast<KoShapeStroke>(stroke);
119
120 if (strokeSP) {
121 if (strokeSP->lineBrush().gradient()) {
122 KoClipMaskPainter strokePainter(&painter, shapeGlobalClipRect);
123 QPainterPath strokeOutline;
124 strokeOutline.addRect(rootOutline.boundingRect().adjusted(-insets.left, -insets.top, insets.right, insets.bottom));
125 strokePainter.shapePainter()->fillRect(strokeOutline.boundingRect(), strokeSP->lineBrush());
126 strokePainter.maskPainter()->fillRect(strokeOutline.boundingRect(), Qt::black);
127
128 KoShapeStrokeSP maskStroke = KoShapeStrokeSP(new KoShapeStroke(*strokeSP.data()));
129 maskStroke->setColor(Qt::white);
130 maskStroke->setLineBrush(Qt::white);
131
132
133 setRenderHints(*strokePainter.maskPainter(), rendering, painter.testRenderHint(QPainter::Antialiasing));
134 {
135 QScopedPointer<KoShape> shape(KoPathShape::createShapeFromPainterPath(decorPath));
136 maskStroke->paint(shape.data(), *strokePainter.maskPainter());
137 }
138 strokePainter.renderOnGlobalPainter();
139 } else {
140 {
141 QScopedPointer<KoShape> shape(KoPathShape::createShapeFromPainterPath(decorPath));
142 stroke->paint(shape.data(), painter);
143 }
144 }
145 }
146 }
147 }
148 }
149
150 }
151}
152
153// NOLINTNEXTLINE(readability-function-cognitive-complexity)
154void KoSvgTextShape::Private::paintPaths(QPainter &painter,
155 const QPainterPath &rootOutline,
156 const KoShape *rootShape,
157 const QVector<CharacterResult> &result, const KoSvgText::TextRendering rendering,
158 QPainterPath &chunk,
159 int &currentIndex)
160{
161
162 KoShapeStrokeModelSP stroke = rootShape->stroke();
165
166 for (auto it = compositionBegin(textData); it != compositionEnd(textData); it++) {
168
169 KoInsets insets;
170 if (stroke) {
171 stroke->strokeInsets(rootShape, insets);
172 }
173
174 if (it.state() == KisForestDetail::Enter) {
175
176 if (childCount(siblingCurrent(it)) == 0) {
177 if (it->finalResultIndex < 0) continue;
178 const int j = it->finalResultIndex;//currentIndex + it->numChars(true);
179
180 const QRect shapeGlobalClipRect = painter.transform().mapRect(it->associatedOutline.boundingRect().adjusted(-insets.left, -insets.top, insets.right, insets.bottom)).toAlignedRect();
181
182 if (shapeGlobalClipRect.isValid()) {
183 KoClipMaskPainter fillPainter(&painter, shapeGlobalClipRect);
184 if (background) {
185 background->paint(*fillPainter.shapePainter(), rootOutline);
186 fillPainter.maskPainter()->fillPath(rootOutline, Qt::black);
187 setRenderHints(*fillPainter.maskPainter(), rendering, painter.testRenderHint(QPainter::Antialiasing));
188 }
189 QPainterPath textDecorationsRest;
190 textDecorationsRest.setFillRule(Qt::WindingFill);
191
192 for (int i = currentIndex; i < j; i++) {
193 if (result.at(i).addressable && !result.at(i).hidden) {
194 const QTransform tf = result.at(i).finalTransform();
195
200 const QRectF boundingRect = tf.mapRect(result.at(i).inkBoundingBox).adjusted(-insets.left, -insets.top, insets.right, insets.bottom);
201 const QRectF clipRect = painter.clipBoundingRect();
202 if (boundingRect.isEmpty() ||
203 (!clipRect.contains(boundingRect) &&
204 !clipRect.intersects(boundingRect))) continue;
205
215 if (const auto *colorGlyph = std::get_if<Glyph::ColorLayers>(&result.at(i).glyph)) {
216 for (int c = 0; c < colorGlyph->paths.size(); c++) {
217 QBrush color = colorGlyph->colors.at(c);
218 bool replace = colorGlyph->replaceWithForeGroundColor.at(c);
219 // In theory we can use the pattern or gradient as well
220 // for ColorV0 fonts, but ColorV1 fonts can have
221 // gradients, so I am hesitant.
222 KoColorBackground *b = dynamic_cast<KoColorBackground *>(background.data());
223 if (b && replace) {
224 color = b->brush();
225 }
226 painter.fillPath(tf.map(colorGlyph->paths.at(c)), color);
227 }
228 } else if (const auto *outlineGlyph = std::get_if<Glyph::Outline>(&result.at(i).glyph)) {
229 chunk.addPath(tf.map(outlineGlyph->path));
230 } else if (const auto *bitmapGlyph = std::get_if<Glyph::Bitmap>(&result.at(i).glyph)) {
231 for (int b = 0; b < bitmapGlyph->images.size(); b++) {
232 QImage img = bitmapGlyph->images.at(b);
233 QRectF rect = bitmapGlyph->drawRects.value(b, QRectF(0, 0, img.width(), img.height()));
234 if (img.format() == QImage::Format_Grayscale8 || img.format() == QImage::Format_Mono) {
235 fillPainter.maskPainter()->save();
236 fillPainter.maskPainter()->translate(result.at(i).finalPosition.x(), result.at(i).finalPosition.y());
237 fillPainter.maskPainter()->rotate(qRadiansToDegrees(result.at(i).rotate));
238 fillPainter.maskPainter()->setCompositionMode(QPainter::CompositionMode_Plus);
239 fillPainter.maskPainter()->drawImage(rect, img);
240 fillPainter.maskPainter()->restore();
241 } else {
242 painter.save();
243 painter.translate(result.at(i).finalPosition.x(), result.at(i).finalPosition.y());
244 painter.rotate(qRadiansToDegrees(result.at(i).rotate));
245 painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
246 painter.drawImage(rect, img);
247 painter.restore();
248 }
249 }
250 }
251 }
252 }
253 Q_FOREACH(const KoShape::PaintOrder p, paintOrder) {
254 if (p == KoShape::Fill) {
255 if (background) {
256 chunk.setFillRule(Qt::WindingFill);
257 fillPainter.maskPainter()->fillPath(chunk, Qt::white);
258 }
259 if (!textDecorationsRest.isEmpty()) {
260 fillPainter.maskPainter()->fillPath(textDecorationsRest.simplified(), Qt::white);
261 }
262 fillPainter.renderOnGlobalPainter();
263 } else if (p == KoShape::Stroke) {
264 KoShapeStrokeSP maskStroke;
265 if (stroke) {
266 KoShapeStrokeSP strokeSP = qSharedPointerDynamicCast<KoShapeStroke>(stroke);
267
268 if (strokeSP) {
269 if (strokeSP->lineBrush().gradient()) {
270 KoClipMaskPainter strokePainter(&painter, shapeGlobalClipRect);
271 QPainterPath strokeOutline;
272 strokeOutline.addRect(rootOutline.boundingRect().adjusted(-insets.left, -insets.top, insets.right, insets.bottom));
273 strokePainter.shapePainter()->fillRect(strokeOutline.boundingRect(), strokeSP->lineBrush());
274 strokePainter.maskPainter()->fillRect(strokeOutline.boundingRect(), Qt::black);
275 maskStroke = KoShapeStrokeSP(new KoShapeStroke(*strokeSP.data()));
276 maskStroke->setColor(Qt::white);
277 maskStroke->setLineBrush(Qt::white);
278 setRenderHints(*strokePainter.maskPainter(), rendering, painter.testRenderHint(QPainter::Antialiasing));
279 {
280 QScopedPointer<KoShape> shape(KoPathShape::createShapeFromPainterPath(chunk));
281 maskStroke->paint(shape.data(), *strokePainter.maskPainter());
282 }
283 if (!textDecorationsRest.isEmpty()) {
284 QScopedPointer<KoShape> shape(KoPathShape::createShapeFromPainterPath(textDecorationsRest));
285 maskStroke->paint(shape.data(), *strokePainter.maskPainter());
286 }
287 strokePainter.renderOnGlobalPainter();
288 } else {
289 {
290 QScopedPointer<KoShape> shape(KoPathShape::createShapeFromPainterPath(chunk));
291 stroke->paint(shape.data(), painter);
292 }
293 if (!textDecorationsRest.isEmpty()) {
294 QScopedPointer<KoShape> shape(KoPathShape::createShapeFromPainterPath(textDecorationsRest));
295 stroke->paint(shape.data(), painter);
296 }
297 }
298 }
299 }
300 }
301 }
302 }
303 chunk = QPainterPath();
304 currentIndex = j;
305 }
306 }
307 }
308}
309
310QGradient *cloneAndTransformGradient(const QGradient *grad, const QTransform &tf) {
311 QGradient *newGrad = KoFlake::cloneGradient(grad);
312
313 if (newGrad->type() == QGradient::LinearGradient) {
314 QLinearGradient *lgradient = static_cast<QLinearGradient*>(newGrad);
315 lgradient->setStart(tf.map(lgradient->start()));
316 lgradient->setFinalStop(tf.map(lgradient->finalStop()));
317 } else if (newGrad->type() == QGradient::RadialGradient) {
318 QRadialGradient *rgradient = static_cast<QRadialGradient*>(newGrad);
319 rgradient->setFocalPoint(tf.map(rgradient->focalPoint()));
320 rgradient->setCenter(tf.map(rgradient->center()));
321 }
322 return newGrad;
323}
324
326 KoGradientBackground *g = dynamic_cast<KoGradientBackground *>(bg.data());
327
328 if (g) {
329 QRectF relative = KisAlgebra2D::absoluteToRelative(oldBounds, newBounds);
330 QTransform newTf = QTransform::fromTranslate(relative.x(), relative.y());
331 newTf.scale(relative.width(), relative.height());
332
334 }
335
336 // assume bg is KoColorBackground.
337 return bg;
338}
339
340KoShapeStrokeModelSP transformStrokeBgToNewBounds(KoShapeStrokeModelSP stroke, const QRectF &oldBounds, const QRectF &newBounds, bool calcInsets = true) {
341 KoShapeStrokeSP s = qSharedPointerDynamicCast<KoShapeStroke>(stroke);
342 if (s) {
343 QBrush b = s->lineBrush();
344 if (b.gradient()) {
345 QRectF nb = newBounds;
346 if (calcInsets) {
347 KoInsets insets;
348 s->strokeInsets(nullptr, insets);
349 nb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
350 }
351
352 QRectF relative = KisAlgebra2D::absoluteToRelative(oldBounds, nb);
353 KoShapeStrokeSP newStroke(new KoShapeStroke(*s.data()));
354 QTransform newTf = QTransform::fromTranslate(relative.x(), relative.y());
355 newTf.scale(relative.width(), relative.height());
356 QBrush newBrush = *cloneAndTransformGradient(b.gradient(), newTf);
357 newStroke->setLineBrush(newBrush);
358 return newStroke;
359 }
360 }
361 return stroke;
362}
363
364// NOLINTNEXTLINE(readability-function-cognitive-complexity)
365KoShape *
366KoSvgTextShape::Private::collectPaths(const KoShape *rootShape, QVector<CharacterResult> &result, int &currentIndex)
367{
368
369 QList<KoShape *> shapes;
370
371 KoShapeFactoryBase *imageFactory = KoShapeRegistry::instance()->value("ImageShape");
372 const QString imageProp = "image";
373 const QString imageViewTransformProp = "viewboxTransform";
374 KoShapeFactoryBase *rectangleFactory = KoShapeRegistry::instance()->value("RectangleShape");
375
376 KoShapeStrokeModelSP stroke = rootShape->stroke();
379
380 bool currentNodeInheritsBg = false;
381 bool currentNodeInheritsStroke = false;
382
383 for (auto it = compositionBegin(textData); it != compositionEnd(textData); it++) {
384 bool hasPaintOrder = it->properties.hasProperty(KoSvgTextProperties::PaintOrder);
386 QMap<KoSvgText::TextDecoration, QPainterPath> textDecorations = it->textDecorations;
387 QColor textDecorationColor = it->properties.propertyOrDefault(KoSvgTextProperties::TextDecorationColorId).value<QColor>();
389 if (textDecorationColor.isValid() && it->properties.hasProperty(KoSvgTextProperties::TextDecorationColorId)) {
390 decorationColor = QSharedPointer<KoColorBackground>(new KoColorBackground(textDecorationColor));
391 }
392 KoInsets insets;
393 if (stroke) {
394 stroke->strokeInsets(rootShape, insets);
395 }
396
397 KoShape::PaintOrder first = paintOrder.at(0);
398 KoShape::PaintOrder second = paintOrder.at(1);
399
400 if (it != compositionBegin(textData)) {
401 currentNodeInheritsBg = currentNodeInheritsBg? !it->properties.hasProperty(KoSvgTextProperties::FillId): false;
402 currentNodeInheritsStroke = currentNodeInheritsStroke? !it->properties.hasProperty(KoSvgTextProperties::StrokeId): false;
403 }
404
405 if (it.state() == KisForestDetail::Enter) {
406
407
408 if (textDecorations.contains(KoSvgText::DecorationUnderline)) {
410 shape->setBackground(transformBackgroundToBounds(decorationColor,
411 rootShape->outlineRect(),
412 shape->outlineRect()));
413 shape->setStroke(stroke);
414 shape->setZIndex(shapes.size());
415 shape->setFillRule(Qt::WindingFill);
416 if (hasPaintOrder)
417 shape->setPaintOrder(first, second);
418 shapes.append(shape);
419 if (currentNodeInheritsBg && !textDecorationColor.isValid()) {
420 shape->setInheritBackground(true);
421 }
422 shape->setInheritStroke(currentNodeInheritsStroke);
423 }
424 if (textDecorations.contains(KoSvgText::DecorationOverline)) {
426 shape->setBackground(transformBackgroundToBounds(decorationColor,
427 rootShape->outlineRect(),
428 shape->outlineRect()));
430 rootShape->outlineRect(),
431 shape->outlineRect()));
432 shape->setZIndex(shapes.size());
433 shape->setFillRule(Qt::WindingFill);
434 if (hasPaintOrder)
435 shape->setPaintOrder(first, second);
436 shapes.append(shape);
437 if (currentNodeInheritsBg && !textDecorationColor.isValid()) {
438 shape->setInheritBackground(true);
439 }
440 shape->setInheritStroke(currentNodeInheritsStroke);
441 }
442
443 if (childCount(siblingCurrent(it)) == 0) {
444 QPainterPath chunk;
445
446 const int j = it->finalResultIndex;
447 if (j < 0 || j > result.size()) continue;
448 for (int i = currentIndex; i < j; i++) {
449 if (result.at(i).addressable && !result.at(i).hidden) {
450 const QTransform tf = result.at(i).finalTransform();
451 if (const auto *colorGlyph = std::get_if<Glyph::ColorLayers>(&result.at(i).glyph)) {
452 for (int c = 0; c < colorGlyph->paths.size(); c++) {
453 QBrush color = colorGlyph->colors.at(c);
454 bool replace = colorGlyph->replaceWithForeGroundColor.at(c);
455 // In theory we can use the pattern or gradient as well
456 // for ColorV0 fonts, but ColorV1 fonts can have
457 // gradients, so I am hesitant.
458 KoColorBackground *b = dynamic_cast<KoColorBackground *>(background.data());
459 if (b && replace) {
460 color = b->brush();
461 }
462 KoPathShape *shape = KoPathShape::createShapeFromPainterPath(tf.map(colorGlyph->paths.at(c)));
464 shape->setZIndex(shapes.size());
465 shape->setFillRule(Qt::WindingFill);
466 shape->setPaintOrder(first, second);
467 shapes.append(shape);
468 if (replace && currentNodeInheritsBg) {
469 shape->setInheritBackground(true);
470 }
471 shape->setInheritStroke(currentNodeInheritsStroke);
472 }
473 } else if (const auto *outlineGlyph = std::get_if<Glyph::Outline>(&result.at(i).glyph)) {
474 chunk.addPath(tf.map(outlineGlyph->path));
475 } else if (const auto *bitmapGlyph = std::get_if<Glyph::Bitmap>(&result.at(i).glyph)) {
476
477 for (int b = 0; b < bitmapGlyph->images.size(); b++) {
478 QImage img = bitmapGlyph->images.at(b);
479 QRectF drawRect = bitmapGlyph->drawRects.at(b);
480 KoProperties params;
481 QTransform imageTf = QTransform::fromTranslate(drawRect.x(), drawRect.y());
482 QTransform viewBox = QTransform::fromScale(drawRect.width()/img.width(),
483 drawRect.height()/img.height());
484 params.setProperty(imageProp, img);
485 params.setProperty(imageViewTransformProp, viewBox);
486 KoShape *shape = imageFactory->createShape(&params);
487 if (img.format() == QImage::Format_Grayscale8 || img.format() == QImage::Format_Mono) {
488 KoShape *rect = rectangleFactory->createDefaultShape();
489 shape->setSize(drawRect.size());
490 rect->setSize(drawRect.size());
491 rect->setStroke(nullptr);
492 KoClipMask *mask = new KoClipMask();
493 mask->setShapes({shape});
494 rect->setClipMask(mask);
495 rect->setZIndex(shapes.size());
497 rootShape->outlineRect(),
498 tf.mapRect(drawRect)));
499 rect->setTransformation(imageTf*tf);
500
501 shapes.append(rect);
502 rect->setInheritBackground(currentNodeInheritsBg);
503 rect->setInheritStroke(currentNodeInheritsStroke);
504 } else {
505 shape->setSize(drawRect.size());
506 shape->setTransformation(imageTf*tf);
507 shape->setZIndex(shapes.size());
508 shapes.append(shape);
509 }
510 }
511 }
512 }
513 }
516 rootShape->outlineRect(),
517 shape->outlineRect()));
518
520 rootShape->outlineRect().adjusted(-insets.left, -insets.top, insets.right, insets.bottom),
521 shape->outlineRect()));
522 shape->setZIndex(shapes.size());
523 shape->setFillRule(Qt::WindingFill);
524 if (hasPaintOrder)
525 shape->setPaintOrder(first, second);
526 shapes.append(shape);
527 shape->setInheritBackground(currentNodeInheritsBg);
528 shape->setInheritStroke(currentNodeInheritsStroke);
529 currentIndex = j;
530
531 }
532 if (it == compositionBegin(textData)) {
533 // We don't want the root to inherit stroke or fill, but after
534 // that it should, so we turn both to inherit at the end of the
535 // 'enter' code block for the root.
536 currentNodeInheritsBg = true;
537 currentNodeInheritsStroke = true;
538 }
539 }
540 if (it.state() ==KisForestDetail::Leave) {
542 if (textDecorations.contains(KoSvgText::DecorationLineThrough)) {
544 shape->setBackground(transformBackgroundToBounds(decorationColor,
545 rootShape->outlineRect(),
546 shape->outlineRect()));
548 rootShape->outlineRect(),
549 shape->outlineRect()));
550 shape->setZIndex(shapes.size());
551 shape->setFillRule(Qt::WindingFill);
552 if (hasPaintOrder)
553 shape->setPaintOrder(first, second);
554 shapes.append(shape);
555 if (currentNodeInheritsBg && !textDecorationColor.isValid()) {
556 shape->setInheritBackground(true);
557 }
558 shape->setInheritStroke(currentNodeInheritsStroke);
559 }
560 }
561 }
562
563 KoShape *parentShape = new KoPathShape();
564 if (shapes.size() == 1) {
565 parentShape = shapes.first();
566 } else if (shapes.size() > 1) {
567 KoShapeGroup *group = new KoShapeGroup();
568 KoShapeGroupCommand cmd(group, shapes, false);
569 cmd.redo();
570
571 group->setBackground(rootShape->background());
572 group->setStroke(rootShape->stroke());
573 group->setPaintOrder(rootShape->paintOrder().first(), rootShape->paintOrder().at(1));
574 group->setInheritPaintOrder(rootShape->inheritPaintOrder());
575
576 parentShape = group;
577 }
578 parentShape->setZIndex(rootShape->zIndex());
579 parentShape->setTransformation(rootShape->absoluteTransformation());
580 parentShape->setName(rootShape->name());
581
582 return parentShape;
583}
584
585// NOLINTNEXTLINE(readability-function-cognitive-complexity)
586void KoSvgTextShape::Private::paintDebug(QPainter &painter,
587 const QVector<CharacterResult> &result,
588 int &currentIndex)
589{
590
591 for (auto it = textData.depthFirstTailBegin(); it != textData.depthFirstTailEnd(); it++) {
592 const int j = it->finalResultIndex;
593 if (currentIndex > result.size()) continue;
594 if (j < 0 || j > result.size()) continue;
595
596 const QRect shapeGlobalClipRect = painter.transform().mapRect(it->associatedOutline.boundingRect()).toAlignedRect();
597
598 painter.save();
599
600 QFont font(QFont(), painter.device());
601 font.setPointSizeF(16.0);
602
603 if (shapeGlobalClipRect.isValid() && childCount(siblingCurrent(it)) == 0) {
604 for (int i = currentIndex; i < j; i++) {
605 if (result.at(i).addressable && !result.at(i).hidden) {
606 const QTransform tf = result.at(i).finalTransform();
607
608#if 1 // Debug: draw character bounding boxes
609 painter.setBrush(Qt::transparent);
610 QPen pen(QColor(0, 0, 0, 50));
611 pen.setCosmetic(true);
612 pen.setWidth(2);
613 painter.setPen(pen);
614 if (const auto *bitmapGlyph = std::get_if<Glyph::Bitmap>(&result.at(i).glyph)) {
615 Q_FOREACH(const QRectF drawRect, bitmapGlyph->drawRects) {
616 painter.drawPolygon(tf.map(drawRect));
617 }
618 } else if (const auto *colorGlyph = std::get_if<Glyph::ColorLayers>(&result.at(i).glyph)) {
619 QRectF boundingRect;
620 Q_FOREACH (const QPainterPath &p, colorGlyph->paths) {
621 boundingRect |= p.boundingRect();
622 }
623 painter.drawPolygon(tf.map(boundingRect));
624 } else if (const auto *outlineGlyph = std::get_if<Glyph::Outline>(&result.at(i).glyph)) {
625 painter.drawPolygon(tf.map(outlineGlyph->path.boundingRect()));
626 }
627 QColor penColor = result.at(i).anchored_chunk ? result.at(i).isHanging ? Qt::red : Qt::magenta
628 : result.at(i).lineEnd == LineEdgeBehaviour::NoChange ? Qt::cyan
629 : Qt::yellow;
630 penColor.setAlpha(192);
631 pen.setColor(penColor);
632 painter.setPen(pen);
633 painter.drawPolygon(tf.map(result.at(i).layoutBox()));
634
635 penColor.setAlpha(96);
636 pen.setColor(penColor);
637 pen.setWidth(1);
638 pen.setStyle(Qt::DotLine);
639 painter.setPen(pen);
640 painter.drawPolygon(tf.map(result.at(i).lineHeightBox()));
641
642 pen.setStyle(Qt::SolidLine);
643 pen.setWidth(2);
644
645 penColor.setAlpha(192);
646 pen.setColor(penColor);
647 painter.setPen(pen);
648 painter.drawLine(tf.map(result.at(i).cursorInfo.caret));
649
650
651 const QPointF center = tf.mapRect(result.at(i).layoutBox()).center();
652 QString text = "#";
653 text += QString::number(i);
654 {
655 // Find the range of this typographic character
656 int end = i + 1;
657 while (end < result.size() && result[end].middle) {
658 end++;
659 }
660 end--;
661 if (end != i) {
662 text += "~";
663 text += QString::number(end);
664 }
665 }
666 text += QString("\n(%1)").arg(result.at(i).plaintTextIndex);
667 painter.setWorldMatrixEnabled(false);
668 painter.setPen(Qt::red);
669 painter.drawText(QRectF(painter.transform().map(center), QSizeF(0, 64)).adjusted(-128, 0, 128, 0),
670 Qt::AlignHCenter | Qt::AlignTop,
671 text);
672 painter.setWorldMatrixEnabled(true);
673
674 pen.setWidth(6);
675 const BreakType breakType = result.at(i).breakType;
676 if (breakType == BreakType::SoftBreak || breakType == BreakType::HardBreak) {
677 if (breakType == BreakType::SoftBreak) {
678 penColor = Qt::blue;
679 } else if (breakType == BreakType::HardBreak) {
680 penColor = Qt::red;
681 }
682 penColor.setAlpha(128);
683 pen.setColor(penColor);
684 painter.setPen(pen);
685 painter.drawPoint(center);
686 }
687 //ligature carets
688 penColor = Qt::darkGreen;
689 penColor.setAlpha(192);
690 pen.setColor(penColor);
691 painter.setPen(pen);
692 QVector<QPointF> offset = result.at(i).cursorInfo.offsets;
693 for (int k=0; k<offset.size(); k++) {
694 painter.drawPoint(tf.map(offset.at(k)));
695 }
696 // Finalpos
697 penColor = Qt::red;
698 penColor.setAlpha(192);
699 pen.setColor(penColor);
700 painter.setPen(pen);
701 painter.drawPoint(result.at(i).finalPosition);
702#endif
703 }
704 }
705 }
706 painter.restore();
707 currentIndex = j;
708 }
709}
const Params2D p
QSharedPointer< KoShapeStroke > KoShapeStrokeSP
BreakType
@ NoChange
Do nothing special.
static void inheritPaintProperties(const KisForest< KoSvgTextContentElement >::composition_iterator it, KoShapeStrokeModelSP &stroke, QSharedPointer< KoShapeBackground > &background, QVector< KoShape::PaintOrder > &paintOrder)
QGradient * cloneAndTransformGradient(const QGradient *grad, const QTransform &tf)
KoShapeStrokeModelSP transformStrokeBgToNewBounds(KoShapeStrokeModelSP stroke, const QRectF &oldBounds, const QRectF &newBounds, bool calcInsets=true)
QSharedPointer< KoShapeBackground > transformBackgroundToBounds(QSharedPointer< KoShapeBackground > bg, const QRectF &oldBounds, const QRectF &newBounds)
void setRenderHints(QPainter &painter, const KoSvgText::TextRendering textRendering, const bool testAntialiasing)
A simple solid color shape background.
const T value(const QString &id) const
A gradient shape background.
const QGradient * gradient() const
Returns the gradient.
QTransform transform() const
Returns the transform matrix.
The position of a path point within a path shape.
Definition KoPathShape.h:63
QRectF outlineRect() const override
reimplemented
void setFillRule(Qt::FillRule fillRule)
Sets the fill rule to be used for painting the background.
static KoPathShape * createShapeFromPainterPath(const QPainterPath &path)
Creates path shape from given QPainterPath.
void setProperty(const QString &name, const QVariant &value)
virtual KoShape * createDefaultShape(KoDocumentResourceManager *documentResources=0) const
virtual KoShape * createShape(const KoProperties *params, KoDocumentResourceManager *documentResources=0) const
The undo / redo command for grouping shapes.
static KoShapeRegistry * instance()
virtual void setPaintOrder(PaintOrder first, PaintOrder second)
setPaintOrder set the paint order. As there's only three entries in any given paintorder,...
Definition KoShape.cpp:749
void setName(const QString &name)
Definition KoShape.cpp:1155
virtual QRectF outlineRect() const
Definition KoShape.cpp:637
void setInheritBackground(bool value)
setInheritBackground marks a shape as inheriting the background from the parent shape....
Definition KoShape.cpp:940
void setZIndex(qint16 zIndex)
Definition KoShape.cpp:954
virtual QVector< PaintOrder > paintOrder() const
paintOrder
Definition KoShape.cpp:773
virtual KoShapeStrokeModelSP stroke() const
Definition KoShape.cpp:1067
virtual QRectF boundingRect() const
Get the bounding box of the shape.
Definition KoShape.cpp:335
virtual void setStroke(KoShapeStrokeModelSP stroke)
Definition KoShape.cpp:1081
QTransform absoluteTransformation() const
Definition KoShape.cpp:382
void setTransformation(const QTransform &matrix)
Definition KoShape.cpp:417
virtual void setBackground(QSharedPointer< KoShapeBackground > background)
Definition KoShape.cpp:918
virtual QSharedPointer< KoShapeBackground > background() const
Definition KoShape.cpp:926
bool inheritPaintOrder() const
inheritPaintOrder
Definition KoShape.cpp:795
void setInheritStroke(bool value)
setInheritStroke marks a shape as inheriting the stroke from the parent shape. NOTE: The currently se...
Definition KoShape.cpp:1090
@ Stroke
Definition KoShape.h:147
QSharedDataPointer< SharedData > s
Definition KoShape.h:1138
qint16 zIndex() const
Definition KoShape.cpp:600
void setInheritPaintOrder(bool value)
setInheritPaintOrder set inherit paint order.
Definition KoShape.cpp:790
QString name() const
Definition KoShape.cpp:1150
virtual void setSize(const QSizeF &size)
Resize the shape.
Definition KoShape.cpp:276
@ PaintOrder
QVector<KoShape::PaintOrder>
@ StrokeId
KoSvgText::StrokeProperty.
@ FillId
KoSvgText::BackgroundProperty.
QAction * replace(const QObject *recvr, const char *slot, QObject *parent)
QPointF absoluteToRelative(const QPointF &pt, const QRectF &rc)
ResultIterator hierarchyBegin(Iterator it)
Definition KisForest.h:419
ResultIterator hierarchyEnd(Iterator it)
Definition KisForest.h:427
ChildIterator< value_type, is_const > siblingCurrent(ChildIterator< value_type, is_const > it)
Definition KisForest.h:240
ResultIterator compositionEnd(Iterator it)
Definition KisForest.h:570
ResultIterator compositionBegin(Iterator it)
Definition KisForest.h:562
KRITAFLAKE_EXPORT QGradient * cloneGradient(const QGradient *gradient)
clones the given gradient
Definition KoFlake.cpp:17
TextDecoration
Flags for text-decoration, for underline, overline and strikethrough.
Definition KoSvgText.h:257
@ DecorationOverline
Definition KoSvgText.h:260
@ DecorationLineThrough
Definition KoSvgText.h:261
@ DecorationUnderline
Definition KoSvgText.h:259
@ RenderingOptimizeSpeed
Definition KoSvgText.h:316
void setShapes(const QList< KoShape * > &value)
qreal bottom
Bottom inset.
Definition KoInsets.h:50
qreal right
Right inset.
Definition KoInsets.h:52
qreal top
Top inset.
Definition KoInsets.h:49
qreal left
Left inset.
Definition KoInsets.h:51