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 if (!internalShapes().isEmpty()) {
372 QList<KoShape *> internalS = internalShapes();
373 std::sort(internalS.begin(), internalS.end(), KoShape::compareShapeZIndex);
374 Q_FOREACH(KoShape *shape, internalS) {
375 KoShape *clone = shape->cloneShape();
376 clone->setZIndex(shapes.size());
377 if (clone->paintOrder() != rootShape->paintOrder()) {
378 clone->setInheritPaintOrder(false);
379 }
380 shapes.append(clone);
381 }
382 }
383
384 KoShapeFactoryBase *imageFactory = KoShapeRegistry::instance()->value("ImageShape");
385 const QString imageProp = "image";
386 const QString imageViewTransformProp = "viewboxTransform";
387 KoShapeFactoryBase *rectangleFactory = KoShapeRegistry::instance()->value("RectangleShape");
388
389 KoShapeStrokeModelSP stroke = rootShape->stroke();
392
393 bool currentNodeInheritsBg = false;
394 bool currentNodeInheritsStroke = false;
395
396 for (auto it = compositionBegin(textData); it != compositionEnd(textData); it++) {
397 bool hasPaintOrder = it->properties.hasProperty(KoSvgTextProperties::PaintOrder);
399 QMap<KoSvgText::TextDecoration, QPainterPath> textDecorations = it->textDecorations;
400 QColor textDecorationColor = it->properties.propertyOrDefault(KoSvgTextProperties::TextDecorationColorId).value<QColor>();
402 if (textDecorationColor.isValid() && it->properties.hasProperty(KoSvgTextProperties::TextDecorationColorId)) {
403 decorationColor = QSharedPointer<KoColorBackground>(new KoColorBackground(textDecorationColor));
404 }
405 KoInsets insets;
406 if (stroke) {
407 stroke->strokeInsets(rootShape, insets);
408 }
409
410 KoShape::PaintOrder first = paintOrder.at(0);
411 KoShape::PaintOrder second = paintOrder.at(1);
412
413 if (it != compositionBegin(textData)) {
414 currentNodeInheritsBg = currentNodeInheritsBg? !it->properties.hasProperty(KoSvgTextProperties::FillId): false;
415 currentNodeInheritsStroke = currentNodeInheritsStroke? !it->properties.hasProperty(KoSvgTextProperties::StrokeId): false;
416 }
417
418 if (it.state() == KisForestDetail::Enter) {
419
420
421 if (textDecorations.contains(KoSvgText::DecorationUnderline)) {
423 shape->setBackground(transformBackgroundToBounds(decorationColor,
424 rootShape->outlineRect(),
425 shape->outlineRect()));
426 shape->setStroke(stroke);
427 shape->setZIndex(shapes.size());
428 shape->setFillRule(Qt::WindingFill);
429 if (hasPaintOrder)
430 shape->setPaintOrder(first, second);
431 shapes.append(shape);
432 if (currentNodeInheritsBg && !textDecorationColor.isValid()) {
433 shape->setInheritBackground(true);
434 }
435 shape->setInheritStroke(currentNodeInheritsStroke);
436 }
437 if (textDecorations.contains(KoSvgText::DecorationOverline)) {
439 shape->setBackground(transformBackgroundToBounds(decorationColor,
440 rootShape->outlineRect(),
441 shape->outlineRect()));
443 rootShape->outlineRect(),
444 shape->outlineRect()));
445 shape->setZIndex(shapes.size());
446 shape->setFillRule(Qt::WindingFill);
447 if (hasPaintOrder)
448 shape->setPaintOrder(first, second);
449 shapes.append(shape);
450 if (currentNodeInheritsBg && !textDecorationColor.isValid()) {
451 shape->setInheritBackground(true);
452 }
453 shape->setInheritStroke(currentNodeInheritsStroke);
454 }
455
456 if (childCount(siblingCurrent(it)) == 0) {
457 QPainterPath chunk;
458
459 const int j = it->finalResultIndex;
460 if (j < 0 || j > result.size()) continue;
461 for (int i = currentIndex; i < j; i++) {
462 if (result.at(i).addressable && !result.at(i).hidden) {
463 const QTransform tf = result.at(i).finalTransform();
464 if (const auto *colorGlyph = std::get_if<Glyph::ColorLayers>(&result.at(i).glyph)) {
465 for (int c = 0; c < colorGlyph->paths.size(); c++) {
466 QBrush color = colorGlyph->colors.at(c);
467 bool replace = colorGlyph->replaceWithForeGroundColor.at(c);
468 // In theory we can use the pattern or gradient as well
469 // for ColorV0 fonts, but ColorV1 fonts can have
470 // gradients, so I am hesitant.
471 KoColorBackground *b = dynamic_cast<KoColorBackground *>(background.data());
472 if (b && replace) {
473 color = b->brush();
474 }
475 KoPathShape *shape = KoPathShape::createShapeFromPainterPath(tf.map(colorGlyph->paths.at(c)));
477 shape->setZIndex(shapes.size());
478 shape->setFillRule(Qt::WindingFill);
479 shape->setPaintOrder(first, second);
480 shapes.append(shape);
481 if (replace && currentNodeInheritsBg) {
482 shape->setInheritBackground(true);
483 }
484 shape->setInheritStroke(currentNodeInheritsStroke);
485 }
486 } else if (const auto *outlineGlyph = std::get_if<Glyph::Outline>(&result.at(i).glyph)) {
487 chunk.addPath(tf.map(outlineGlyph->path));
488 } else if (const auto *bitmapGlyph = std::get_if<Glyph::Bitmap>(&result.at(i).glyph)) {
489
490 for (int b = 0; b < bitmapGlyph->images.size(); b++) {
491 QImage img = bitmapGlyph->images.at(b);
492 QRectF drawRect = bitmapGlyph->drawRects.at(b);
493 KoProperties params;
494 QTransform imageTf = QTransform::fromTranslate(drawRect.x(), drawRect.y());
495 QTransform viewBox = QTransform::fromScale(drawRect.width()/img.width(),
496 drawRect.height()/img.height());
497 params.setProperty(imageProp, img);
498 params.setProperty(imageViewTransformProp, viewBox);
499 KoShape *shape = imageFactory->createShape(&params);
500 if (img.format() == QImage::Format_Grayscale8 || img.format() == QImage::Format_Mono) {
501 KoShape *rect = rectangleFactory->createDefaultShape();
502 shape->setSize(drawRect.size());
503 rect->setSize(drawRect.size());
504 rect->setStroke(nullptr);
505 KoClipMask *mask = new KoClipMask();
506 mask->setShapes({shape});
507 rect->setClipMask(mask);
508 rect->setZIndex(shapes.size());
510 rootShape->outlineRect(),
511 tf.mapRect(drawRect)));
512 rect->setTransformation(imageTf*tf);
513
514 shapes.append(rect);
515 rect->setInheritBackground(currentNodeInheritsBg);
516 rect->setInheritStroke(currentNodeInheritsStroke);
517 } else {
518 shape->setSize(drawRect.size());
519 shape->setTransformation(imageTf*tf);
520 shape->setZIndex(shapes.size());
521 shapes.append(shape);
522 }
523 }
524 }
525 }
526 }
529 rootShape->outlineRect(),
530 shape->outlineRect()));
531
533 rootShape->outlineRect().adjusted(-insets.left, -insets.top, insets.right, insets.bottom),
534 shape->outlineRect()));
535 shape->setZIndex(shapes.size());
536 shape->setFillRule(Qt::WindingFill);
537 if (hasPaintOrder)
538 shape->setPaintOrder(first, second);
539 shapes.append(shape);
540 shape->setInheritBackground(currentNodeInheritsBg);
541 shape->setInheritStroke(currentNodeInheritsStroke);
542 currentIndex = j;
543
544 }
545 if (it == compositionBegin(textData)) {
546 // We don't want the root to inherit stroke or fill, but after
547 // that it should, so we turn both to inherit at the end of the
548 // 'enter' code block for the root.
549 currentNodeInheritsBg = true;
550 currentNodeInheritsStroke = true;
551 }
552 }
553 if (it.state() ==KisForestDetail::Leave) {
555 if (textDecorations.contains(KoSvgText::DecorationLineThrough)) {
557 shape->setBackground(transformBackgroundToBounds(decorationColor,
558 rootShape->outlineRect(),
559 shape->outlineRect()));
561 rootShape->outlineRect(),
562 shape->outlineRect()));
563 shape->setZIndex(shapes.size());
564 shape->setFillRule(Qt::WindingFill);
565 if (hasPaintOrder)
566 shape->setPaintOrder(first, second);
567 shapes.append(shape);
568 if (currentNodeInheritsBg && !textDecorationColor.isValid()) {
569 shape->setInheritBackground(true);
570 }
571 shape->setInheritStroke(currentNodeInheritsStroke);
572 }
573 }
574 }
575
576 KoShape *parentShape = new KoPathShape();
577 if (shapes.size() == 1) {
578 parentShape = shapes.first();
579 } else if (shapes.size() > 1) {
580 KoShapeGroup *group = new KoShapeGroup();
581 KoShapeGroupCommand cmd(group, shapes, false);
582 cmd.redo();
583
584 group->setBackground(rootShape->background());
585 group->setStroke(rootShape->stroke());
586 group->setPaintOrder(rootShape->paintOrder().first(), rootShape->paintOrder().at(1));
587 group->setInheritPaintOrder(rootShape->inheritPaintOrder());
588
589 parentShape = group;
590 }
591 parentShape->setZIndex(rootShape->zIndex());
592 parentShape->setTransformation(rootShape->absoluteTransformation());
593 parentShape->setName(rootShape->name());
594
595 return parentShape;
596}
597
598// NOLINTNEXTLINE(readability-function-cognitive-complexity)
599void KoSvgTextShape::Private::paintDebug(QPainter &painter,
600 const QVector<CharacterResult> &result,
601 int &currentIndex)
602{
603
604 for (auto it = textData.depthFirstTailBegin(); it != textData.depthFirstTailEnd(); it++) {
605 const int j = it->finalResultIndex;
606 if (currentIndex > result.size()) continue;
607 if (j < 0 || j > result.size()) continue;
608
609 const QRect shapeGlobalClipRect = painter.transform().mapRect(it->associatedOutline.boundingRect()).toAlignedRect();
610
611 painter.save();
612
613 QFont font(QFont(), painter.device());
614 font.setPointSizeF(16.0);
615
616 if (shapeGlobalClipRect.isValid() && childCount(siblingCurrent(it)) == 0) {
617 for (int i = currentIndex; i < j; i++) {
618 if (result.at(i).addressable && !result.at(i).hidden) {
619 const QTransform tf = result.at(i).finalTransform();
620
621#if 1 // Debug: draw character bounding boxes
622 painter.setBrush(Qt::transparent);
623 QPen pen(QColor(0, 0, 0, 50));
624 pen.setCosmetic(true);
625 pen.setWidth(2);
626 painter.setPen(pen);
627 if (const auto *bitmapGlyph = std::get_if<Glyph::Bitmap>(&result.at(i).glyph)) {
628 Q_FOREACH(const QRectF drawRect, bitmapGlyph->drawRects) {
629 painter.drawPolygon(tf.map(drawRect));
630 }
631 } else if (const auto *colorGlyph = std::get_if<Glyph::ColorLayers>(&result.at(i).glyph)) {
632 QRectF boundingRect;
633 Q_FOREACH (const QPainterPath &p, colorGlyph->paths) {
634 boundingRect |= p.boundingRect();
635 }
636 painter.drawPolygon(tf.map(boundingRect));
637 } else if (const auto *outlineGlyph = std::get_if<Glyph::Outline>(&result.at(i).glyph)) {
638 painter.drawPolygon(tf.map(outlineGlyph->path.boundingRect()));
639 }
640 QColor penColor = result.at(i).anchored_chunk ? result.at(i).isHanging ? Qt::red : Qt::magenta
641 : result.at(i).lineEnd == LineEdgeBehaviour::NoChange ? Qt::cyan
642 : Qt::yellow;
643 penColor.setAlpha(192);
644 pen.setColor(penColor);
645 painter.setPen(pen);
646 painter.drawPolygon(tf.map(result.at(i).layoutBox()));
647
648 penColor.setAlpha(96);
649 pen.setColor(penColor);
650 pen.setWidth(1);
651 pen.setStyle(Qt::DotLine);
652 painter.setPen(pen);
653 painter.drawPolygon(tf.map(result.at(i).lineHeightBox()));
654
655 pen.setStyle(Qt::SolidLine);
656 pen.setWidth(2);
657
658 penColor.setAlpha(192);
659 pen.setColor(penColor);
660 painter.setPen(pen);
661 painter.drawLine(tf.map(result.at(i).cursorInfo.caret));
662
663
664 const QPointF center = tf.mapRect(result.at(i).layoutBox()).center();
665 QString text = "#";
666 text += QString::number(i);
667 {
668 // Find the range of this typographic character
669 int end = i + 1;
670 while (end < result.size() && result[end].middle) {
671 end++;
672 }
673 end--;
674 if (end != i) {
675 text += "~";
676 text += QString::number(end);
677 }
678 }
679 text += QString("\n(%1)").arg(result.at(i).plaintTextIndex);
680 painter.setWorldMatrixEnabled(false);
681 painter.setPen(Qt::red);
682 painter.drawText(QRectF(painter.transform().map(center), QSizeF(0, 64)).adjusted(-128, 0, 128, 0),
683 Qt::AlignHCenter | Qt::AlignTop,
684 text);
685 painter.setWorldMatrixEnabled(true);
686
687 pen.setWidth(6);
688 const BreakType breakType = result.at(i).breakType;
689 if (breakType == BreakType::SoftBreak || breakType == BreakType::HardBreak) {
690 if (breakType == BreakType::SoftBreak) {
691 penColor = Qt::blue;
692 } else if (breakType == BreakType::HardBreak) {
693 penColor = Qt::red;
694 }
695 penColor.setAlpha(128);
696 pen.setColor(penColor);
697 painter.setPen(pen);
698 painter.drawPoint(center);
699 }
700 //ligature carets
701 penColor = Qt::darkGreen;
702 penColor.setAlpha(192);
703 pen.setColor(penColor);
704 painter.setPen(pen);
705 QVector<QPointF> offset = result.at(i).cursorInfo.offsets;
706 for (int k=0; k<offset.size(); k++) {
707 painter.drawPoint(tf.map(offset.at(k)));
708 }
709 // Finalpos
710 penColor = Qt::red;
711 penColor.setAlpha(192);
712 pen.setColor(penColor);
713 painter.setPen(pen);
714 painter.drawPoint(result.at(i).finalPosition);
715#endif
716 }
717 }
718 }
719 painter.restore();
720 currentIndex = j;
721 }
722}
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:669
void setName(const QString &name)
Definition KoShape.cpp:960
virtual QRectF outlineRect() const
Definition KoShape.cpp:566
void setInheritBackground(bool value)
setInheritBackground marks a shape as inheriting the background from the parent shape....
Definition KoShape.cpp:773
void setZIndex(qint16 zIndex)
Definition KoShape.cpp:787
virtual QVector< PaintOrder > paintOrder() const
paintOrder
Definition KoShape.cpp:693
virtual KoShapeStrokeModelSP stroke() const
Definition KoShape.cpp:890
static bool compareShapeZIndex(KoShape *s1, KoShape *s2)
Definition KoShape.cpp:393
virtual QRectF boundingRect() const
Get the bounding box of the shape.
Definition KoShape.cpp:299
virtual void setStroke(KoShapeStrokeModelSP stroke)
Definition KoShape.cpp:904
QTransform absoluteTransformation() const
Definition KoShape.cpp:335
void setTransformation(const QTransform &matrix)
Definition KoShape.cpp:374
virtual void setBackground(QSharedPointer< KoShapeBackground > background)
Definition KoShape.cpp:751
virtual KoShape * cloneShape() const
creates a deep copy of the shape or shape's subtree
Definition KoShape.cpp:172
virtual QSharedPointer< KoShapeBackground > background() const
Definition KoShape.cpp:759
bool inheritPaintOrder() const
inheritPaintOrder
Definition KoShape.cpp:715
void setInheritStroke(bool value)
setInheritStroke marks a shape as inheriting the stroke from the parent shape. NOTE: The currently se...
Definition KoShape.cpp:913
@ Stroke
Definition KoShape.h:117
QSharedDataPointer< SharedData > s
Definition KoShape.h:977
qint16 zIndex() const
Definition KoShape.cpp:529
void setInheritPaintOrder(bool value)
setInheritPaintOrder set inherit paint order.
Definition KoShape.cpp:710
QString name() const
Definition KoShape.cpp:955
virtual void setSize(const QSizeF &size)
Resize the shape.
Definition KoShape.cpp:248
@ 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