43#include <QLinearGradient>
44#include <QRadialGradient>
57 bool fillRule = pathShape && pathShape->
background() && pathShape->
fillRule() == Qt::OddEvenFill?
true:
false;
73 context.
shapeWriter().addAttribute(
"display",
"none");
74 }
else if (transparency > 0.0) {
75 context.
shapeWriter().addAttribute(
"opacity", 1.0 - transparency);
77 if (!paintOrder.isEmpty() && !inheritPaintorder) {
81 if ((!textShape && notDefault) || textShape) {
87 order.append(
"stroke");
89 order.append(
"markers");
92 context.
shapeWriter().addAttribute(
"paint-order", order.join(
" "));
101 context.
shapeWriter().addAttribute(
"fill",
"none");
104 QBrush fill(Qt::NoBrush);
107 context.
shapeWriter().addAttribute(
"fill", cbg->color().name());
108 if (cbg->color().alphaF() < 1.0)
109 context.
shapeWriter().addAttribute(
"fill-opacity", cbg->color().alphaF());
113 QString gradientId =
saveSvgGradient(gbg->gradient(), gbg->transform(), context);
114 context.
shapeWriter().addAttribute(
"fill",
"url(#" + gradientId +
")");
119 context.
shapeWriter().addAttribute(
"fill",
"url(#" + gradientId +
")");
123 const QString patternId =
saveSvgPattern(pbg, size, absoluteTransform, context);
124 context.
shapeWriter().addAttribute(
"fill",
"url(#" + patternId +
")");
129 context.
shapeWriter().addAttribute(
"fill",
"url(#" + patternId +
")");
134 context.
shapeWriter().addAttribute(
"fill-rule",
"evenodd");
144 QString strokeStr(
"none");
145 if (lineBorder->lineBrush().gradient()) {
146 QString gradientId =
saveSvgGradient(lineBorder->lineBrush().gradient(), lineBorder->lineBrush().transform(), context);
147 strokeStr =
"url(#" + gradientId +
")";
149 if (lineBorder->color().isValid()) {
150 strokeStr = lineBorder->color().name();
152 if (lineBorder->color().alphaF() < 1.0) {
153 context.
shapeWriter().addAttribute(
"stroke-opacity", lineBorder->color().alphaF());
156 if (!strokeStr.isEmpty())
157 context.
shapeWriter().addAttribute(
"stroke", strokeStr);
161 if (lineBorder->capStyle() == Qt::FlatCap)
162 context.
shapeWriter().addAttribute(
"stroke-linecap",
"butt");
163 else if (lineBorder->capStyle() == Qt::RoundCap)
164 context.
shapeWriter().addAttribute(
"stroke-linecap",
"round");
165 else if (lineBorder->capStyle() == Qt::SquareCap)
166 context.
shapeWriter().addAttribute(
"stroke-linecap",
"square");
168 if (lineBorder->joinStyle() == Qt::MiterJoin) {
169 context.
shapeWriter().addAttribute(
"stroke-linejoin",
"miter");
170 context.
shapeWriter().addAttribute(
"stroke-miterlimit", lineBorder->miterLimit());
171 }
else if (lineBorder->joinStyle() == Qt::RoundJoin)
172 context.
shapeWriter().addAttribute(
"stroke-linejoin",
"round");
173 else if (lineBorder->joinStyle() == Qt::BevelJoin)
174 context.
shapeWriter().addAttribute(
"stroke-linejoin",
"bevel");
177 if (lineBorder->lineStyle() > Qt::SolidLine) {
178 qreal dashFactor = lineBorder->lineWidth();
180 if (lineBorder->dashOffset() != 0)
181 context.
shapeWriter().addAttribute(
"stroke-dashoffset", dashFactor * lineBorder->dashOffset());
185 int dashCount = dashes.size();
186 for (
int i = 0; i < dashCount; ++i) {
191 context.
shapeWriter().addAttribute(
"stroke-dasharray", dashStr);
198 buffer.open(QIODevice::WriteOnly);
214 const QString uid = context.
createUID(
"path");
216 shapes.append(clonedShape);
224 if (!title.trimmed().isEmpty()) {
230 if (!desc.trimmed().isEmpty()) {
243 const QString uid = context.
createUID(
"clippath");
253 context.
shapeWriter().addAttribute(
"clip-path",
"url(#" + uid +
")");
254 if (clipPath->
clipRule() != Qt::WindingFill)
255 context.
shapeWriter().addAttribute(
"clip-rule",
"evenodd");
264 const QString uid = context.
createUID(
"clipmask");
282 context.
shapeWriter().addAttribute(
"mask",
"url(#" + uid +
")");
286void writeMarkerStyle(
KoXmlWriter &styleWriter,
const KoMarker *marker,
const QString &assignedId) {
297 styleWriter.
addAttribute(
"markerWidth", refSize.width());
298 styleWriter.
addAttribute(
"markerHeight", refSize.height());
314 const QString &markerTag,
321 const QString uid = context.
createUID(
"lineMarker");
322 writeMarkerStyle(context.
styleWriter(), marker, uid);
323 context.
shapeWriter().addAttribute(markerTag.toLatin1().data(),
"url(#" + uid +
")");
332 if (!pathShape || !pathShape->
hasMarkers())
return;
340 context.
shapeWriter().addAttribute(
"krita:marker-fill-method",
"auto");
346 Q_FOREACH (
const QGradientStop &stop, colorStops) {
348 context.
styleWriter().addAttribute(
"stop-color", stop.second.name());
349 context.
styleWriter().addAttribute(
"offset", stop.first);
350 context.
styleWriter().addAttribute(
"stop-opacity", stop.second.alphaF());
359 mode == QGradient::ObjectBoundingMode ?
360 "objectBoundingBox" :
370 const QString spreadMethod[3] = {
376 const QString uid = context.
createUID(
"gradient");
378 if (gradient->type() == QGradient::LinearGradient) {
379 const QLinearGradient * g =
static_cast<const QLinearGradient*
>(gradient);
380 context.
styleWriter().startElement(
"linearGradient");
384 context.
styleWriter().addAttribute(
"x1", g->start().x());
385 context.
styleWriter().addAttribute(
"y1", g->start().y());
386 context.
styleWriter().addAttribute(
"x2", g->finalStop().x());
387 context.
styleWriter().addAttribute(
"y2", g->finalStop().y());
388 context.
styleWriter().addAttribute(
"spreadMethod", spreadMethod[g->spread()]);
392 }
else if (gradient->type() == QGradient::RadialGradient) {
393 const QRadialGradient * g =
static_cast<const QRadialGradient*
>(gradient);
394 context.
styleWriter().startElement(
"radialGradient");
398 context.
styleWriter().addAttribute(
"cx", g->center().x());
399 context.
styleWriter().addAttribute(
"cy", g->center().y());
400 context.
styleWriter().addAttribute(
"fx", g->focalPoint().x());
401 context.
styleWriter().addAttribute(
"fy", g->focalPoint().y());
402 context.
styleWriter().addAttribute(
"r", g->radius());
403 context.
styleWriter().addAttribute(
"spreadMethod", spreadMethod[g->spread()]);
407 }
else if (gradient->type() == QGradient::ConicalGradient) {
437 const QTransform& transform,
440 if (!gradient || !gradient->
isValid())
443 const QString uid = context.
createUID(
"meshgradient");
444 context.
styleWriter().startElement(
"meshgradient");
448 context.
styleWriter().addAttribute(
"gradientUnits",
"objectBoundingBox");
450 context.
styleWriter().addAttribute(
"gradientUnits",
"userSpaceOnUse");
458 context.
styleWriter().addAttribute(
"x", start.x());
459 context.
styleWriter().addAttribute(
"y", start.y());
462 context.
styleWriter().addAttribute(
"type",
"bilinear");
464 context.
styleWriter().addAttribute(
"type",
"bicubic");
467 for (
int row = 0; row < mesharray->
numRows(); ++row) {
469 const QString uid = context.
createUID(
"meshrow");
473 for (
int col = 0; col < mesharray->
numColumns(); ++col) {
475 const QString uid = context.
createUID(
"meshpatch");
481 for (
int s = 0; s < 4; ++s) {
492 std::array<QPointF, 4> segment = patch->
getSegment(type);
495 QTextStream stream(&pathstr);
498 stream.setRealNumberPrecision(10);
501 << segment[1].x() <<
"," << segment[1].y() <<
" "
502 << segment[2].x() <<
"," << segment[2].y() <<
" "
503 << segment[3].x() <<
"," << segment[3].y();
505 context.
styleWriter().addAttribute(
"path", pathstr);
531 const QString uid = context.
createUID(
"pattern");
533 const QSizeF patternSize = pattern->patternDisplaySize();
534 const QSize imageSize = pattern->pattern().size();
537 QPointF offset = pattern->referencePointOffset();
538 offset.rx() = 0.01 * offset.x() * patternSize.width();
539 offset.ry() = 0.01 * offset.y() * patternSize.height();
542 switch (pattern->referencePoint()) {
546 offset += QPointF(0.5 * shapeSize.width(), 0.0);
549 offset += QPointF(shapeSize.width(), 0.0);
552 offset += QPointF(0.0, 0.5 * shapeSize.height());
555 offset += QPointF(0.5 * shapeSize.width(), 0.5 * shapeSize.height());
558 offset += QPointF(shapeSize.width(), 0.5 * shapeSize.height());
561 offset += QPointF(0.0, shapeSize.height());
564 offset += QPointF(0.5 * shapeSize.width(), shapeSize.height());
567 offset += QPointF(shapeSize.width(), shapeSize.height());
571 offset = absoluteTransform.map(offset);
579 context.
styleWriter().addAttribute(
"width",
"100%");
580 context.
styleWriter().addAttribute(
"height",
"100%");
581 context.
styleWriter().addAttribute(
"patternUnits",
"objectBoundingBox");
585 context.
styleWriter().addAttribute(
"patternUnits",
"userSpaceOnUse");
598 buffer.open(QIODevice::WriteOnly);
599 if (pattern->pattern().save(&buffer,
"PNG")) {
601 context.
styleWriter().addAttribute(
"xlink:href",
"data:"+ mimeType +
";base64," + buffer.data().toBase64());
612 const QString uid = context.
createUID(
"pattern");
620 const QRectF
rect = pattern->referenceRect();
636 const QTransform shapeToRelative = relativeToShape.inverted();
638 Q_FOREACH (
KoShape *shape, shapes) {
641 clonedShapes.append(clone);
645 qDeleteAll(clonedShapes);
void embedShapes(const QList< KoShape * > &shapes, KoXmlWriter &outWriter)
QString convertGradientMode(QGradient::CoordinateMode mode)
static QString mimeTypeForSuffix(const QString &suffix)
Find the mimetype for a given extension. The extension may have the form "*.xxx" or "xxx".
Clip path used to clip shapes.
KoFlake::CoordinateSystem coordinates
QList< KoShape * > clipShapes() const
qreal explicitOrientation
MarkerCoordinateSystem coordinateSystem
static QString coordinateSystemToString(MarkerCoordinateSystem value)
QList< KoShape * > shapes
The position of a path point within a path shape.
bool autoFillMarkers() const
KoMarker * marker(KoFlake::MarkerPosition pos) const
Qt::FillRule fillRule() const
Returns the fill rule for the path object.
virtual QSizeF size() const
Get the size of the shape in pt.
void setName(const QString &name)
virtual QRectF outlineRect() const
virtual QVector< PaintOrder > paintOrder() const
paintOrder
static QVector< PaintOrder > defaultPaintOrder()
default paint order as per SVG specification
virtual KoShapeStrokeModelSP stroke() const
void applyAbsoluteTransformation(const QTransform &matrix)
KoClipPath * clipPath() const
Returns the currently set clip path or 0 if there is no clip path set.
bool inheritStroke() const
inheritStroke shows if the shape inherits the stroke from its parent
bool inheritBackground() const
inheritBackground shows if the shape inherits background from its parent
QTransform absoluteTransformation() const
KoClipMask * clipMask() const
Returns the currently set clip mask or 0 if there is no clip mask set.
virtual KoShape * cloneShape() const
creates a deep copy of the shape or shape's subtree
virtual QSharedPointer< KoShapeBackground > background() const
QString additionalAttribute(const QString &name) const
bool inheritPaintOrder() const
inheritPaintOrder
bool isVisible(bool recursive=true) const
qreal transparency(bool recursive=false) const
void addCompleteElement(QIODevice *dev)
void startElement(const char *tagName, bool indentInside=true)
void addAttribute(const char *attrName, const QString &value)
SvgMeshPatch * getPatch(const int row, const int col) const
SvgMeshGradient::Shading type() const
const QScopedPointer< SvgMeshArray > & getMeshArray() const
KoFlake::CoordinateSystem gradientUnits() const
Type
Position of stop in the patch.
SvgMeshStop getStop(Type type) const
returns the starting point of the stop
std::array< QPointF, 4 > getSegment(Type type) const
Get a segment of the path in the meshpatch.
Context for saving svg files.
QString createUID(const QString &base)
Create a unique id from the specified base text.
QScopedPointer< KoXmlWriter > shapeWriter
QScopedPointer< KoXmlWriter > styleWriter
static void saveSvgMarkers(KoShape *shape, SvgSavingContext &context)
Saves markers of the path shape if present.
static void saveSvgFill(QSharedPointer< KoShapeBackground > background, const bool fillRuleEvenOdd, const QRectF outlineRect, const QSizeF size, const QTransform absoluteTransform, SvgSavingContext &context)
Saves fill style of specified shape.
static void saveSvgClipping(KoShape *shape, SvgSavingContext &context)
Saves clipping of specified shape.
static QString embedShape(const KoShape *shape, SvgSavingContext &context)
static void saveSvgColorStops(const QGradientStops &colorStops, SvgSavingContext &context)
Saves gradient color stops.
static void saveSvgBasicStyle(const bool isVisible, const qreal transparency, const QVector< KoShape::PaintOrder > paintOrder, bool inheritPaintorder, SvgSavingContext &context, bool textShape=false)
Saves only stroke, fill and transparency of the shape.
static QString saveSvgGradient(const QGradient *gradient, const QTransform &gradientTransform, SvgSavingContext &context)
Saves gradient.
static void saveSvgStyle(KoShape *shape, SvgSavingContext &context)
Saves the style of the specified shape.
static QString saveSvgMeshGradient(SvgMeshGradient *gradient, const QTransform &transform, SvgSavingContext &context)
static void saveMetadata(const KoShape *shape, SvgSavingContext &context)
static QString saveSvgPattern(QSharedPointer< KoPatternBackground > pattern, const QSizeF shapeSize, const QTransform absoluteTransform, SvgSavingContext &context)
Saves pattern.
static void saveSvgMasking(KoShape *shape, SvgSavingContext &context)
Saves masking of specified shape.
static QString saveSvgVectorPattern(QSharedPointer< KoVectorPatternBackground > pattern, const QRectF outlineRect, SvgSavingContext &context)
static void saveSvgStroke(KoShapeStrokeModelSP, SvgSavingContext &context)
Saves stroke style of specified shape.
static double toUserSpace(double value)
static void writeTransformAttributeLazy(const QString &name, const QTransform &transform, KoXmlWriter &shapeWriter)
Writes a transform as an attribute name iff the transform is not empty.
Implements exporting shapes to SVG.
bool saveDetached(QIODevice &outputDevice)
#define KIS_ASSERT_RECOVER_NOOP(cond)
T kisRadiansToDegrees(T radians)
QTransform mapToRect(const QRectF &rect)
QString toString(const QString &value)
void setUtf8OnStream(QTextStream &stream)
QString coordinateToString(CoordinateSystem value)
QList< KoShape * > shapes
KoFlake::CoordinateSystem coordinates
KoFlake::CoordinateSystem contentCoordinates