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