45#include <QLinearGradient>
46#include <QRadialGradient>
59 bool fillRule = pathShape && pathShape->
background() && pathShape->
fillRule() == Qt::OddEvenFill?
true:
false;
76 context.
shapeWriter().addAttribute(
"display",
"none");
77 }
else if (transparency > 0.0) {
78 context.
shapeWriter().addAttribute(
"opacity", 1.0 - transparency);
80 if (!paintOrder.isEmpty() && !inheritPaintorder) {
84 if ((!textShape && notDefault) || textShape) {
90 order.append(
"stroke");
92 order.append(
"markers");
95 context.
shapeWriter().addAttribute(
"paint-order", order.join(
" "));
104 context.
shapeWriter().addAttribute(
"fill",
"none");
107 QBrush fill(Qt::NoBrush);
110 context.
shapeWriter().addAttribute(
"fill", cbg->color().name());
111 if (cbg->color().alphaF() < 1.0)
112 context.
shapeWriter().addAttribute(
"fill-opacity", cbg->color().alphaF());
116 QString gradientId =
saveSvgGradient(gbg->gradient(), gbg->transform(), context);
117 context.
shapeWriter().addAttribute(
"fill",
"url(#" + gradientId +
")");
122 context.
shapeWriter().addAttribute(
"fill",
"url(#" + gradientId +
")");
126 const QString patternId =
saveSvgPattern(pbg, size, absoluteTransform, context);
127 context.
shapeWriter().addAttribute(
"fill",
"url(#" + patternId +
")");
132 context.
shapeWriter().addAttribute(
"fill",
"url(#" + patternId +
")");
137 context.
shapeWriter().addAttribute(
"fill-rule",
"evenodd");
147 QString strokeStr(
"none");
148 if (lineBorder->lineBrush().gradient()) {
149 QString gradientId =
saveSvgGradient(lineBorder->lineBrush().gradient(), lineBorder->lineBrush().transform(), context);
150 strokeStr =
"url(#" + gradientId +
")";
152 if (lineBorder->color().isValid()) {
153 strokeStr = lineBorder->color().name();
155 if (lineBorder->color().alphaF() < 1.0) {
156 context.
shapeWriter().addAttribute(
"stroke-opacity", lineBorder->color().alphaF());
159 if (!strokeStr.isEmpty())
160 context.
shapeWriter().addAttribute(
"stroke", strokeStr);
164 if (lineBorder->capStyle() == Qt::FlatCap)
165 context.
shapeWriter().addAttribute(
"stroke-linecap",
"butt");
166 else if (lineBorder->capStyle() == Qt::RoundCap)
167 context.
shapeWriter().addAttribute(
"stroke-linecap",
"round");
168 else if (lineBorder->capStyle() == Qt::SquareCap)
169 context.
shapeWriter().addAttribute(
"stroke-linecap",
"square");
171 if (lineBorder->joinStyle() == Qt::MiterJoin) {
172 context.
shapeWriter().addAttribute(
"stroke-linejoin",
"miter");
173 context.
shapeWriter().addAttribute(
"stroke-miterlimit", lineBorder->miterLimit());
174 }
else if (lineBorder->joinStyle() == Qt::RoundJoin)
175 context.
shapeWriter().addAttribute(
"stroke-linejoin",
"round");
176 else if (lineBorder->joinStyle() == Qt::BevelJoin)
177 context.
shapeWriter().addAttribute(
"stroke-linejoin",
"bevel");
180 if (lineBorder->lineStyle() > Qt::SolidLine) {
181 qreal dashFactor = lineBorder->lineWidth();
183 if (lineBorder->dashOffset() != 0)
184 context.
shapeWriter().addAttribute(
"stroke-dashoffset", dashFactor * lineBorder->dashOffset());
188 int dashCount = dashes.size();
189 for (
int i = 0; i < dashCount; ++i) {
194 context.
shapeWriter().addAttribute(
"stroke-dasharray", dashStr);
205 if (!filterEffects.count())
208 const QString uid = context.
createUID(
"filter");
212 context.
shapeWriter().addAttribute(
"filter",
"url(#" + uid +
")");
218 buffer.open(QIODevice::WriteOnly);
234 const QString uid = context.
createUID(
"path");
236 shapes.append(clonedShape);
244 if (!title.trimmed().isEmpty()) {
250 if (!desc.trimmed().isEmpty()) {
263 const QString uid = context.
createUID(
"clippath");
273 context.
shapeWriter().addAttribute(
"clip-path",
"url(#" + uid +
")");
274 if (clipPath->
clipRule() != Qt::WindingFill)
275 context.
shapeWriter().addAttribute(
"clip-rule",
"evenodd");
284 const QString uid = context.
createUID(
"clipmask");
302 context.
shapeWriter().addAttribute(
"mask",
"url(#" + uid +
")");
306void writeMarkerStyle(
KoXmlWriter &styleWriter,
const KoMarker *marker,
const QString &assignedId) {
317 styleWriter.
addAttribute(
"markerWidth", refSize.width());
318 styleWriter.
addAttribute(
"markerHeight", refSize.height());
334 const QString &markerTag,
341 const QString uid = context.
createUID(
"lineMarker");
342 writeMarkerStyle(context.
styleWriter(), marker, uid);
343 context.
shapeWriter().addAttribute(markerTag.toLatin1().data(),
"url(#" + uid +
")");
352 if (!pathShape || !pathShape->
hasMarkers())
return;
360 context.
shapeWriter().addAttribute(
"krita:marker-fill-method",
"auto");
366 Q_FOREACH (
const QGradientStop &stop, colorStops) {
368 context.
styleWriter().addAttribute(
"stop-color", stop.second.name());
369 context.
styleWriter().addAttribute(
"offset", stop.first);
370 context.
styleWriter().addAttribute(
"stop-opacity", stop.second.alphaF());
379 mode == QGradient::ObjectBoundingMode ?
380 "objectBoundingBox" :
390 const QString spreadMethod[3] = {
396 const QString uid = context.
createUID(
"gradient");
398 if (gradient->type() == QGradient::LinearGradient) {
399 const QLinearGradient * g =
static_cast<const QLinearGradient*
>(gradient);
400 context.
styleWriter().startElement(
"linearGradient");
404 context.
styleWriter().addAttribute(
"x1", g->start().x());
405 context.
styleWriter().addAttribute(
"y1", g->start().y());
406 context.
styleWriter().addAttribute(
"x2", g->finalStop().x());
407 context.
styleWriter().addAttribute(
"y2", g->finalStop().y());
408 context.
styleWriter().addAttribute(
"spreadMethod", spreadMethod[g->spread()]);
412 }
else if (gradient->type() == QGradient::RadialGradient) {
413 const QRadialGradient * g =
static_cast<const QRadialGradient*
>(gradient);
414 context.
styleWriter().startElement(
"radialGradient");
418 context.
styleWriter().addAttribute(
"cx", g->center().x());
419 context.
styleWriter().addAttribute(
"cy", g->center().y());
420 context.
styleWriter().addAttribute(
"fx", g->focalPoint().x());
421 context.
styleWriter().addAttribute(
"fy", g->focalPoint().y());
422 context.
styleWriter().addAttribute(
"r", g->radius());
423 context.
styleWriter().addAttribute(
"spreadMethod", spreadMethod[g->spread()]);
427 }
else if (gradient->type() == QGradient::ConicalGradient) {
457 const QTransform& transform,
460 if (!gradient || !gradient->
isValid())
463 const QString uid = context.
createUID(
"meshgradient");
464 context.
styleWriter().startElement(
"meshgradient");
468 context.
styleWriter().addAttribute(
"gradientUnits",
"objectBoundingBox");
470 context.
styleWriter().addAttribute(
"gradientUnits",
"userSpaceOnUse");
478 context.
styleWriter().addAttribute(
"x", start.x());
479 context.
styleWriter().addAttribute(
"y", start.y());
482 context.
styleWriter().addAttribute(
"type",
"bilinear");
484 context.
styleWriter().addAttribute(
"type",
"bicubic");
487 for (
int row = 0; row < mesharray->
numRows(); ++row) {
489 const QString uid = context.
createUID(
"meshrow");
493 for (
int col = 0; col < mesharray->
numColumns(); ++col) {
495 const QString uid = context.
createUID(
"meshpatch");
501 for (
int s = 0; s < 4; ++s) {
512 std::array<QPointF, 4> segment = patch->
getSegment(type);
515 QTextStream stream(&pathstr);
518 stream.setRealNumberPrecision(10);
521 << segment[1].x() <<
"," << segment[1].y() <<
" "
522 << segment[2].x() <<
"," << segment[2].y() <<
" "
523 << segment[3].x() <<
"," << segment[3].y();
525 context.
styleWriter().addAttribute(
"path", pathstr);
551 const QString uid = context.
createUID(
"pattern");
553 const QSizeF patternSize = pattern->patternDisplaySize();
554 const QSize imageSize = pattern->pattern().size();
557 QPointF offset = pattern->referencePointOffset();
558 offset.rx() = 0.01 * offset.x() * patternSize.width();
559 offset.ry() = 0.01 * offset.y() * patternSize.height();
562 switch (pattern->referencePoint()) {
566 offset += QPointF(0.5 * shapeSize.width(), 0.0);
569 offset += QPointF(shapeSize.width(), 0.0);
572 offset += QPointF(0.0, 0.5 * shapeSize.height());
575 offset += QPointF(0.5 * shapeSize.width(), 0.5 * shapeSize.height());
578 offset += QPointF(shapeSize.width(), 0.5 * shapeSize.height());
581 offset += QPointF(0.0, shapeSize.height());
584 offset += QPointF(0.5 * shapeSize.width(), shapeSize.height());
587 offset += QPointF(shapeSize.width(), shapeSize.height());
591 offset = absoluteTransform.map(offset);
599 context.
styleWriter().addAttribute(
"width",
"100%");
600 context.
styleWriter().addAttribute(
"height",
"100%");
601 context.
styleWriter().addAttribute(
"patternUnits",
"objectBoundingBox");
605 context.
styleWriter().addAttribute(
"patternUnits",
"userSpaceOnUse");
618 buffer.open(QIODevice::WriteOnly);
619 if (pattern->pattern().save(&buffer,
"PNG")) {
621 context.
styleWriter().addAttribute(
"xlink:href",
"data:"+ mimeType +
";base64," + buffer.data().toBase64());
632 const QString uid = context.
createUID(
"pattern");
640 const QRectF
rect = pattern->referenceRect();
656 const QTransform shapeToRelative = relativeToShape.inverted();
658 Q_FOREACH (
KoShape *shape, shapes) {
661 clonedShapes.append(clone);
665 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
This class manages a stack of filter effects.
void save(KoXmlWriter &writer, const QString &filterId)
QList< KoFilterEffect * > filterEffects
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
KoFilterEffectStack * filterEffectStack() 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 void saveSvgEffects(KoShape *shape, SvgSavingContext &context)
Saves effects 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