diff --git a/libs/flake/KoGradientBackground.cpp b/libs/flake/KoGradientBackground.cpp index e8aa8f87d5..e6aeb1df62 100644 --- a/libs/flake/KoGradientBackground.cpp +++ b/libs/flake/KoGradientBackground.cpp @@ -1,194 +1,191 @@ /* This file is part of the KDE project * Copyright (C) 2008 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoGradientBackground.h" #include "KoShapeBackground_p.h" #include "KoFlake.h" #include #include #include #include #include #include #include #include #include class KoGradientBackgroundPrivate : public KoShapeBackgroundPrivate { public: KoGradientBackgroundPrivate() : gradient(0) {} QGradient *gradient; QTransform matrix; }; KoGradientBackground::KoGradientBackground(QGradient * gradient, const QTransform &matrix) : KoShapeBackground(*(new KoGradientBackgroundPrivate())) { Q_D(KoGradientBackground); d->gradient = gradient; d->matrix = matrix; Q_ASSERT(d->gradient); - Q_ASSERT(d->gradient->coordinateMode() == QGradient::ObjectBoundingMode); } KoGradientBackground::KoGradientBackground(const QGradient & gradient, const QTransform &matrix) : KoShapeBackground(*(new KoGradientBackgroundPrivate())) { Q_D(KoGradientBackground); d->gradient = KoFlake::cloneGradient(&gradient); d->matrix = matrix; Q_ASSERT(d->gradient); - Q_ASSERT(d->gradient->coordinateMode() == QGradient::ObjectBoundingMode); } KoGradientBackground::~KoGradientBackground() { Q_D(KoGradientBackground); delete d->gradient; } bool KoGradientBackground::compareTo(const KoShapeBackground *other) const { Q_D(const KoGradientBackground); const KoGradientBackground *otherGradient = dynamic_cast(other); return otherGradient && d->matrix == otherGradient->d_func()->matrix && *d->gradient == *otherGradient->d_func()->gradient; } void KoGradientBackground::setTransform(const QTransform &matrix) { Q_D(KoGradientBackground); d->matrix = matrix; } QTransform KoGradientBackground::transform() const { Q_D(const KoGradientBackground); return d->matrix; } void KoGradientBackground::setGradient(const QGradient &gradient) { Q_D(KoGradientBackground); delete d->gradient; d->gradient = KoFlake::cloneGradient(&gradient); Q_ASSERT(d->gradient); - Q_ASSERT(d->gradient->coordinateMode() == QGradient::ObjectBoundingMode); } const QGradient * KoGradientBackground::gradient() const { Q_D(const KoGradientBackground); return d->gradient; } void KoGradientBackground::paint(QPainter &painter, const KoViewConverter &/*converter*/, KoShapePaintingContext &/*context*/, const QPainterPath &fillPath) const { Q_D(const KoGradientBackground); if (!d->gradient) return; if (d->gradient->coordinateMode() == QGradient::ObjectBoundingMode) { /** * NOTE: important hack! * * Qt has different notation of QBrush::setTransform() in comparison * to what SVG defines. SVG defines gradientToUser matrix to be postmultiplied * by QBrush::transform(), but Qt does exectly reverse! * * That most probably has beed caused by the fact that Qt uses transposed * matrices and someone just mistyped the stuff long ago :( * * So here we basically emulate this feature by converting the gradient into * QGradient::LogicalMode and doing transformations manually. */ const QRectF boundingRect = fillPath.boundingRect(); QTransform gradientToUser(boundingRect.width(), 0, 0, boundingRect.height(), boundingRect.x(), boundingRect.y()); // TODO: how about slicing the object? QGradient g = *d->gradient; g.setCoordinateMode(QGradient::LogicalMode); QBrush b(g); b.setTransform(d->matrix * gradientToUser); painter.setBrush(b); } else { QBrush b(*d->gradient); b.setTransform(d->matrix); painter.setBrush(b); } painter.drawPath(fillPath); } void KoGradientBackground::fillStyle(KoGenStyle &style, KoShapeSavingContext &context) { Q_D(KoGradientBackground); if (!d->gradient) return; QBrush brush(*d->gradient); brush.setTransform(d->matrix); KoOdfGraphicStyles::saveOdfFillStyle(style, context.mainStyles(), brush); } bool KoGradientBackground::loadStyle(KoOdfLoadingContext &context, const QSizeF &shapeSize) { Q_D(KoGradientBackground); KoStyleStack &styleStack = context.styleStack(); if (! styleStack.hasProperty(KoXmlNS::draw, "fill")) return false; QString fillStyle = styleStack.property(KoXmlNS::draw, "fill"); if (fillStyle == "gradient") { QBrush brush = KoOdfGraphicStyles::loadOdfGradientStyle(styleStack, context.stylesReader(), shapeSize); const QGradient * gradient = brush.gradient(); if (gradient) { d->gradient = KoFlake::cloneGradient(gradient); d->matrix = brush.transform(); //Gopalakrishna Bhat: If the brush has transparency then we ignore the draw:opacity property and use the brush transparency. // Brush will have transparency if the svg:linearGradient stop point has stop-opacity property otherwise it is opaque if (brush.isOpaque() && styleStack.hasProperty(KoXmlNS::draw, "opacity")) { QString opacityPercent = styleStack.property(KoXmlNS::draw, "opacity"); if (! opacityPercent.isEmpty() && opacityPercent.right(1) == "%") { float opacity = qMin(opacityPercent.left(opacityPercent.length() - 1).toDouble(), 100.0) / 100; QGradientStops stops; Q_FOREACH (QGradientStop stop, d->gradient->stops()) { stop.second.setAlphaF(opacity); stops << stop; } d->gradient->setStops(stops); } } return true; } } return false; } diff --git a/libs/flake/svg/SvgStyleWriter.cpp b/libs/flake/svg/SvgStyleWriter.cpp index 89f764442e..2eb0780d29 100644 --- a/libs/flake/svg/SvgStyleWriter.cpp +++ b/libs/flake/svg/SvgStyleWriter.cpp @@ -1,554 +1,552 @@ /* This file is part of the KDE project Copyright (C) 2002 Lars Siebold Copyright (C) 2002-2003,2005 Rob Buis Copyright (C) 2002,2005-2006 David Faure Copyright (C) 2002 Werner Trobin Copyright (C) 2002 Lennart Kudling Copyright (C) 2004 Nicolas Goutte Copyright (C) 2005 Boudewijn Rempt Copyright (C) 2005 Raphael Langerhorst Copyright (C) 2005 Thomas Zander Copyright (C) 2005,2007-2008 Jan Hambrecht Copyright (C) 2006 Inge Wallin Copyright (C) 2006 Martin Pfeiffer Copyright (C) 2006 Gábor Lehel Copyright (C) 2006 Laurent Montel Copyright (C) 2006 Christian Mueller Copyright (C) 2006 Ariya Hidayat Copyright (C) 2010 Thorsten Zachmann This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "SvgStyleWriter.h" #include "SvgSavingContext.h" #include "SvgUtil.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_dom_utils.h" #include "kis_algebra_2d.h" #include #include void SvgStyleWriter::saveSvgStyle(KoShape *shape, SvgSavingContext &context) { saveSvgBasicStyle(shape, context); saveSvgFill(shape, context); saveSvgStroke(shape, context); saveSvgEffects(shape, context); saveSvgClipping(shape, context); saveSvgMasking(shape, context); saveSvgMarkers(shape, context); } void SvgStyleWriter::saveSvgBasicStyle(KoShape *shape, SvgSavingContext &context) { if (!shape->isVisible(false)) { context.shapeWriter().addAttribute("display", "none"); } else if (shape->transparency() > 0.0) { context.shapeWriter().addAttribute("opacity", 1.0 - shape->transparency()); } } void SvgStyleWriter::saveSvgFill(KoShape *shape, SvgSavingContext &context) { if (! shape->background()) { context.shapeWriter().addAttribute("fill", "none"); } QBrush fill(Qt::NoBrush); QSharedPointer cbg = qSharedPointerDynamicCast(shape->background()); if (cbg) { context.shapeWriter().addAttribute("fill", cbg->color().name()); if (cbg->color().alphaF() < 1.0) context.shapeWriter().addAttribute("fill-opacity", cbg->color().alphaF()); } QSharedPointer gbg = qSharedPointerDynamicCast(shape->background()); if (gbg) { QString gradientId = saveSvgGradient(gbg->gradient(), gbg->transform(), context); context.shapeWriter().addAttribute("fill", "url(#" + gradientId + ")"); } QSharedPointer pbg = qSharedPointerDynamicCast(shape->background()); if (pbg) { const QString patternId = saveSvgPattern(pbg, shape, context); context.shapeWriter().addAttribute("fill", "url(#" + patternId + ")"); } QSharedPointer vpbg = qSharedPointerDynamicCast(shape->background()); if (vpbg) { const QString patternId = saveSvgVectorPattern(vpbg, shape, context); context.shapeWriter().addAttribute("fill", "url(#" + patternId + ")"); } KoPathShape * path = dynamic_cast(shape); if (path && shape->background()) { // non-zero is default, so only write fillrule if evenodd is set if (path->fillRule() == Qt::OddEvenFill) context.shapeWriter().addAttribute("fill-rule", "evenodd"); } } void SvgStyleWriter::saveSvgStroke(KoShape *shape, SvgSavingContext &context) { const QSharedPointer lineBorder = qSharedPointerDynamicCast(shape->stroke()); if (! lineBorder) return; QString strokeStr("none"); if (lineBorder->lineBrush().gradient()) { QString gradientId = saveSvgGradient(lineBorder->lineBrush().gradient(), lineBorder->lineBrush().transform(), context); strokeStr = "url(#" + gradientId + ")"; } else { strokeStr = lineBorder->color().name(); } if (!strokeStr.isEmpty()) context.shapeWriter().addAttribute("stroke", strokeStr); if (lineBorder->color().alphaF() < 1.0) context.shapeWriter().addAttribute("stroke-opacity", lineBorder->color().alphaF()); context.shapeWriter().addAttribute("stroke-width", SvgUtil::toUserSpace(lineBorder->lineWidth())); if (lineBorder->capStyle() == Qt::FlatCap) context.shapeWriter().addAttribute("stroke-linecap", "butt"); else if (lineBorder->capStyle() == Qt::RoundCap) context.shapeWriter().addAttribute("stroke-linecap", "round"); else if (lineBorder->capStyle() == Qt::SquareCap) context.shapeWriter().addAttribute("stroke-linecap", "square"); if (lineBorder->joinStyle() == Qt::MiterJoin) { context.shapeWriter().addAttribute("stroke-linejoin", "miter"); context.shapeWriter().addAttribute("stroke-miterlimit", lineBorder->miterLimit()); } else if (lineBorder->joinStyle() == Qt::RoundJoin) context.shapeWriter().addAttribute("stroke-linejoin", "round"); else if (lineBorder->joinStyle() == Qt::BevelJoin) context.shapeWriter().addAttribute("stroke-linejoin", "bevel"); // dash if (lineBorder->lineStyle() > Qt::SolidLine) { qreal dashFactor = lineBorder->lineWidth(); if (lineBorder->dashOffset() != 0) context.shapeWriter().addAttribute("stroke-dashoffset", dashFactor * lineBorder->dashOffset()); QString dashStr; const QVector dashes = lineBorder->lineDashes(); int dashCount = dashes.size(); for (int i = 0; i < dashCount; ++i) { if (i > 0) dashStr += ","; dashStr += QString("%1").arg(KisDomUtils::toString(dashes[i] * dashFactor)); } context.shapeWriter().addAttribute("stroke-dasharray", dashStr); } } void SvgStyleWriter::saveSvgEffects(KoShape *shape, SvgSavingContext &context) { KoFilterEffectStack * filterStack = shape->filterEffectStack(); if (!filterStack) return; QList filterEffects = filterStack->filterEffects(); if (!filterEffects.count()) return; const QString uid = context.createUID("filter"); filterStack->save(context.styleWriter(), uid); context.shapeWriter().addAttribute("filter", "url(#" + uid + ")"); } void embedShapes(const QList &shapes, KoXmlWriter &outWriter) { QBuffer buffer; buffer.open(QIODevice::WriteOnly); { SvgWriter shapesWriter(shapes); shapesWriter.saveDetached(buffer); } buffer.close(); outWriter.addCompleteElement(&buffer); } void SvgStyleWriter::saveSvgClipping(KoShape *shape, SvgSavingContext &context) { KoClipPath *clipPath = shape->clipPath(); if (!clipPath) return; const QString uid = context.createUID("clippath"); context.styleWriter().startElement("clipPath"); context.styleWriter().addAttribute("id", uid); context.styleWriter().addAttribute("clipPathUnits", KoFlake::coordinateToString(clipPath->coordinates())); embedShapes(clipPath->clipShapes(), context.styleWriter()); context.styleWriter().endElement(); // clipPath context.shapeWriter().addAttribute("clip-path", "url(#" + uid + ")"); if (clipPath->clipRule() != Qt::WindingFill) context.shapeWriter().addAttribute("clip-rule", "evenodd"); } void SvgStyleWriter::saveSvgMasking(KoShape *shape, SvgSavingContext &context) { KoClipMask*clipMask = shape->clipMask(); if (!clipMask) return; const QString uid = context.createUID("clipmask"); context.styleWriter().startElement("mask"); context.styleWriter().addAttribute("id", uid); context.styleWriter().addAttribute("maskUnits", KoFlake::coordinateToString(clipMask->coordinates())); context.styleWriter().addAttribute("maskContentUnits", KoFlake::coordinateToString(clipMask->contentCoordinates())); const QRectF rect = clipMask->maskRect(); // think funny duplication? please note the 'pt' suffix! :) if (clipMask->coordinates() == KoFlake::ObjectBoundingBox) { context.styleWriter().addAttribute("x", rect.x()); context.styleWriter().addAttribute("y", rect.y()); context.styleWriter().addAttribute("width", rect.width()); context.styleWriter().addAttribute("height", rect.height()); } else { context.styleWriter().addAttribute("x", rect.x()); context.styleWriter().addAttribute("y", rect.y()); context.styleWriter().addAttribute("width", rect.width()); context.styleWriter().addAttribute("height", rect.height()); } embedShapes(clipMask->shapes(), context.styleWriter()); context.styleWriter().endElement(); // clipMask context.shapeWriter().addAttribute("mask", "url(#" + uid + ")"); } namespace { void writeMarkerStyle(KoXmlWriter &styleWriter, const KoMarker *marker, const QString &assignedId) { styleWriter.startElement("marker"); styleWriter.addAttribute("id", assignedId); styleWriter.addAttribute("markerUnits", KoMarker::coordinateSystemToString(marker->coordinateSystem())); const QPointF refPoint = marker->referencePoint(); styleWriter.addAttribute("refX", refPoint.x()); styleWriter.addAttribute("refY", refPoint.y()); const QSizeF refSize = marker->referenceSize(); styleWriter.addAttribute("markerWidth", refSize.width()); styleWriter.addAttribute("markerHeight", refSize.height()); if (marker->hasAutoOtientation()) { styleWriter.addAttribute("orient", "auto"); } else { // no suffix means 'degrees' styleWriter.addAttribute("orient", kisRadiansToDegrees(marker->explicitOrientation())); } embedShapes(marker->shapes(), styleWriter); styleWriter.endElement(); // marker } void tryEmbedMarker(const KoPathShape *pathShape, const QString &markerTag, KoFlake::MarkerPosition markerPosition, SvgSavingContext &context) { KoMarker *marker = pathShape->marker(markerPosition); if (marker) { const QString uid = context.createUID("lineMarker"); writeMarkerStyle(context.styleWriter(), marker, uid); context.shapeWriter().addAttribute(markerTag.toLatin1().data(), "url(#" + uid + ")"); } } } void SvgStyleWriter::saveSvgMarkers(KoShape *shape, SvgSavingContext &context) { KoPathShape *pathShape = dynamic_cast(shape); if (!pathShape || !pathShape->hasMarkers()) return; tryEmbedMarker(pathShape, "marker-start", KoFlake::StartMarker, context); tryEmbedMarker(pathShape, "marker-mid", KoFlake::MidMarker, context); tryEmbedMarker(pathShape, "marker-end", KoFlake::EndMarker, context); if (pathShape->autoFillMarkers()) { context.shapeWriter().addAttribute("krita:marker-fill-method", "auto"); } } void SvgStyleWriter::saveSvgColorStops(const QGradientStops &colorStops, SvgSavingContext &context) { Q_FOREACH (const QGradientStop &stop, colorStops) { context.styleWriter().startElement("stop"); context.styleWriter().addAttribute("stop-color", stop.second.name()); context.styleWriter().addAttribute("offset", stop.first); context.styleWriter().addAttribute("stop-opacity", stop.second.alphaF()); context.styleWriter().endElement(); } } inline QString convertGradientMode(QGradient::CoordinateMode mode) { KIS_ASSERT_RECOVER_NOOP(mode != QGradient::StretchToDeviceMode); return mode == QGradient::ObjectBoundingMode ? "objectBoundingBox" : "userSpaceOnUse"; } QString SvgStyleWriter::saveSvgGradient(const QGradient *gradient, const QTransform &gradientTransform, SvgSavingContext &context) { if (! gradient) return QString(); - Q_ASSERT(gradient->coordinateMode() == QGradient::ObjectBoundingMode); - const QString spreadMethod[3] = { QString("pad"), QString("reflect"), QString("repeat") }; const QString uid = context.createUID("gradient"); if (gradient->type() == QGradient::LinearGradient) { const QLinearGradient * g = static_cast(gradient); context.styleWriter().startElement("linearGradient"); context.styleWriter().addAttribute("id", uid); SvgUtil::writeTransformAttributeLazy("gradientTransform", gradientTransform, context.styleWriter()); context.styleWriter().addAttribute("gradientUnits", convertGradientMode(g->coordinateMode())); context.styleWriter().addAttribute("x1", g->start().x()); context.styleWriter().addAttribute("y1", g->start().y()); context.styleWriter().addAttribute("x2", g->finalStop().x()); context.styleWriter().addAttribute("y2", g->finalStop().y()); context.styleWriter().addAttribute("spreadMethod", spreadMethod[g->spread()]); // color stops saveSvgColorStops(gradient->stops(), context); context.styleWriter().endElement(); } else if (gradient->type() == QGradient::RadialGradient) { const QRadialGradient * g = static_cast(gradient); context.styleWriter().startElement("radialGradient"); context.styleWriter().addAttribute("id", uid); SvgUtil::writeTransformAttributeLazy("gradientTransform", gradientTransform, context.styleWriter()); context.styleWriter().addAttribute("gradientUnits", convertGradientMode(g->coordinateMode())); context.styleWriter().addAttribute("cx", g->center().x()); context.styleWriter().addAttribute("cy", g->center().y()); context.styleWriter().addAttribute("fx", g->focalPoint().x()); context.styleWriter().addAttribute("fy", g->focalPoint().y()); context.styleWriter().addAttribute("r", g->radius()); context.styleWriter().addAttribute("spreadMethod", spreadMethod[g->spread()]); // color stops saveSvgColorStops(gradient->stops(), context); context.styleWriter().endElement(); } else if (gradient->type() == QGradient::ConicalGradient) { //const QConicalGradient * g = static_cast( gradient ); // fake conical grad as radial. // fugly but better than data loss. /* printIndentation( m_defs, m_indent2 ); *m_defs << "center().x() << "\" "; *m_defs << "cy=\"" << g->center().y() << "\" "; *m_defs << "fx=\"" << grad.focalPoint().x() << "\" "; *m_defs << "fy=\"" << grad.focalPoint().y() << "\" "; double r = sqrt( pow( grad.vector().x() - grad.origin().x(), 2 ) + pow( grad.vector().y() - grad.origin().y(), 2 ) ); *m_defs << "r=\"" << QString().setNum( r ) << "\" "; *m_defs << spreadMethod[g->spread()]; *m_defs << ">" << endl; // color stops getColorStops( gradient->stops() ); printIndentation( m_defs, m_indent2 ); *m_defs << "" << endl; *m_body << "url(#" << uid << ")"; */ } return uid; } QString SvgStyleWriter::saveSvgPattern(QSharedPointer pattern, KoShape *shape, SvgSavingContext &context) { const QString uid = context.createUID("pattern"); const QSizeF shapeSize = shape->size(); const QSizeF patternSize = pattern->patternDisplaySize(); const QSize imageSize = pattern->pattern().size(); // calculate offset in point QPointF offset = pattern->referencePointOffset(); offset.rx() = 0.01 * offset.x() * patternSize.width(); offset.ry() = 0.01 * offset.y() * patternSize.height(); // now take the reference point into account switch (pattern->referencePoint()) { case KoPatternBackground::TopLeft: break; case KoPatternBackground::Top: offset += QPointF(0.5 * shapeSize.width(), 0.0); break; case KoPatternBackground::TopRight: offset += QPointF(shapeSize.width(), 0.0); break; case KoPatternBackground::Left: offset += QPointF(0.0, 0.5 * shapeSize.height()); break; case KoPatternBackground::Center: offset += QPointF(0.5 * shapeSize.width(), 0.5 * shapeSize.height()); break; case KoPatternBackground::Right: offset += QPointF(shapeSize.width(), 0.5 * shapeSize.height()); break; case KoPatternBackground::BottomLeft: offset += QPointF(0.0, shapeSize.height()); break; case KoPatternBackground::Bottom: offset += QPointF(0.5 * shapeSize.width(), shapeSize.height()); break; case KoPatternBackground::BottomRight: offset += QPointF(shapeSize.width(), shapeSize.height()); break; } offset = shape->absoluteTransformation(0).map(offset); context.styleWriter().startElement("pattern"); context.styleWriter().addAttribute("id", uid); context.styleWriter().addAttribute("x", SvgUtil::toUserSpace(offset.x())); context.styleWriter().addAttribute("y", SvgUtil::toUserSpace(offset.y())); if (pattern->repeat() == KoPatternBackground::Stretched) { context.styleWriter().addAttribute("width", "100%"); context.styleWriter().addAttribute("height", "100%"); context.styleWriter().addAttribute("patternUnits", "objectBoundingBox"); } else { context.styleWriter().addAttribute("width", SvgUtil::toUserSpace(patternSize.width())); context.styleWriter().addAttribute("height", SvgUtil::toUserSpace(patternSize.height())); context.styleWriter().addAttribute("patternUnits", "userSpaceOnUse"); } context.styleWriter().addAttribute("viewBox", QString("0 0 %1 %2").arg(KisDomUtils::toString(imageSize.width())).arg(KisDomUtils::toString(imageSize.height()))); //*m_defs << " patternContentUnits=\"userSpaceOnUse\""; context.styleWriter().startElement("image"); context.styleWriter().addAttribute("x", "0"); context.styleWriter().addAttribute("y", "0"); context.styleWriter().addAttribute("width", QString("%1px").arg(KisDomUtils::toString(imageSize.width()))); context.styleWriter().addAttribute("height", QString("%1px").arg(KisDomUtils::toString(imageSize.height()))); QByteArray ba; QBuffer buffer(&ba); buffer.open(QIODevice::WriteOnly); if (pattern->pattern().save(&buffer, "PNG")) { const QString mimeType = KisMimeDatabase::mimeTypeForSuffix("*.png"); context.styleWriter().addAttribute("xlink:href", "data:"+ mimeType + ";base64," + ba.toBase64()); } context.styleWriter().endElement(); // image context.styleWriter().endElement(); // pattern return uid; } QString SvgStyleWriter::saveSvgVectorPattern(QSharedPointer pattern, KoShape *parentShape, SvgSavingContext &context) { const QString uid = context.createUID("pattern"); context.styleWriter().startElement("pattern"); context.styleWriter().addAttribute("id", uid); context.styleWriter().addAttribute("patternUnits", KoFlake::coordinateToString(pattern->referenceCoordinates())); context.styleWriter().addAttribute("patternContentUnits", KoFlake::coordinateToString(pattern->contentCoordinates())); const QRectF rect = pattern->referenceRect(); if (pattern->referenceCoordinates() == KoFlake::ObjectBoundingBox) { context.styleWriter().addAttribute("x", rect.x()); context.styleWriter().addAttribute("y", rect.y()); context.styleWriter().addAttribute("width", rect.width()); context.styleWriter().addAttribute("height", rect.height()); } else { context.styleWriter().addAttribute("x", rect.x()); context.styleWriter().addAttribute("y", rect.y()); context.styleWriter().addAttribute("width", rect.width()); context.styleWriter().addAttribute("height", rect.height()); } SvgUtil::writeTransformAttributeLazy("patternTransform", pattern->patternTransform(), context.styleWriter()); if (pattern->contentCoordinates() == KoFlake::ObjectBoundingBox) { // TODO: move this normalization into the KoVectorPatternBackground itself QList shapes = pattern->shapes(); QList clonedShapes; const QRectF dstShapeBoundingRect = parentShape->outlineRect(); const QTransform relativeToShape = KisAlgebra2D::mapToRect(dstShapeBoundingRect); const QTransform shapeToRelative = relativeToShape.inverted(); Q_FOREACH (KoShape *shape, shapes) { KoShape *clone = shape->cloneShape(); clone->applyAbsoluteTransformation(shapeToRelative); clonedShapes.append(clone); } embedShapes(clonedShapes, context.styleWriter()); qDeleteAll(clonedShapes); } else { QList shapes = pattern->shapes(); embedShapes(shapes, context.styleWriter()); } context.styleWriter().endElement(); // pattern return uid; } diff --git a/libs/flake/tests/TestSvgParser.h b/libs/flake/tests/TestSvgParser.h index 34351c3422..8fd39dcf34 100644 --- a/libs/flake/tests/TestSvgParser.h +++ b/libs/flake/tests/TestSvgParser.h @@ -1,173 +1,175 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef TESTSVGPARSER_H #define TESTSVGPARSER_H #include class TestSvgParser : public QObject { Q_OBJECT private Q_SLOTS: void testUnitPx(); void testUnitPxResolution(); void testUnitPt(); void testUnitIn(); void testUnitPercentInitial(); void testScalingViewport(); void testScalingViewportKeepMeet1(); void testScalingViewportKeepMeet2(); void testScalingViewportKeepMeetAlign(); void testScalingViewportKeepSlice1(); void testScalingViewportKeepSlice2(); void testScalingViewportResolution(); void testScalingViewportPercentInternal(); void testParsePreserveAspectRatio(); void testParseTransform(); void testScalingViewportTransform(); void testTransformNesting(); void testTransformNestingGroups(); void testTransformRotation1(); void testTransformRotation2(); void testRenderStrokeNone(); void testRenderStrokeColorName(); void testRenderStrokeColorHex3(); void testRenderStrokeColorHex6(); void testRenderStrokeColorRgbValues(); void testRenderStrokeColorRgbPercent(); void testRenderStrokeColorCurrent(); void testRenderStrokeColorNonexistentIri(); void testRenderStrokeWidth(); void testRenderStrokeZeroWidth(); void testRenderStrokeOpacity(); void testRenderStrokeJointRound(); void testRenderStrokeLinecap(); void testRenderStrokeMiterLimit(); void testRenderStrokeDashArrayEven(); void testRenderStrokeDashArrayEvenOffset(); void testRenderStrokeDashArrayOdd(); void testRenderStrokeDashArrayRelative(); void testRenderFillDefault(); void testRenderFillRuleNonZero(); void testRenderFillRuleEvenOdd(); void testRenderFillOpacity(); void testRenderDisplayAttribute(); void testRenderVisibilityAttribute(); void testRenderVisibilityInheritance(); void testRenderDisplayInheritance(); void testRenderStrokeWithInlineStyle(); void testIccColor(); void testRenderFillLinearGradientRelativePercent(); void testRenderFillLinearGradientRelativePortion(); void testRenderFillLinearGradientUserCoord(); void testRenderFillLinearGradientStopPortion(); void testRenderFillLinearGradientTransform(); void testRenderFillLinearGradientTransformUserCoord(); void testRenderFillLinearGradientRotatedShape(); void testRenderFillLinearGradientRotatedShapeUserCoord(); void testRenderFillRadialGradient(); void testRenderFillRadialGradientUserCoord(); void testRenderFillLinearGradientUserCoordPercent(); void testRenderStrokeLinearGradient(); void testManualRenderPattern_ContentUser_RefObb(); void testManualRenderPattern_ContentObb_RefObb(); void testManualRenderPattern_ContentUser_RefUser(); void testManualRenderPattern_ContentObb_RefObb_Transform_Rotate(); void testManualRenderPattern_ContentView_RefObb(); void testManualRenderPattern_ContentView_RefUser(); void testRenderPattern_r_User_c_User(); void testRenderPattern_InfiniteRecursionWhenInherited(); void testRenderPattern_r_User_c_View(); void testRenderPattern_r_User_c_Obb(); void testRenderPattern_r_User_c_View_Rotated(); void testRenderPattern_r_Obb_c_View_Rotated(); void testKoClipPathRendering(); void testKoClipPathRelativeRendering(); void testRenderClipPath_User(); void testRenderClipPath_Obb(); void testRenderClipPath_Obb_Transform(); void testRenderClipMask_Obb(); void testRenderClipMask_User_Clip_Obb(); void testRenderClipMask_User_Clip_User(); void testRenderImage_AspectDefault(); void testRenderImage_AspectNone(); void testRenderImage_AspectMeet(); void testRectShapeRoundUniformX(); void testRectShapeRoundUniformY(); void testRectShapeRoundXY(); void testRectShapeRoundXYOverflow(); void testCircleShape(); void testEllipseShape(); void testLineShape(); void testPolylineShape(); void testPolygonShape(); void testPathShape(); void testDefsHidden(); void testDefsUseInheritance(); void testUseWithoutDefs(); void testMarkersAutoOrientation(); void testMarkersAutoOrientationScaled(); void testMarkersAutoOrientationScaledUserCoordinates(); void testMarkersCustomOrientation(); void testMarkersDifferent(); - void testMarkersFillAsShape(); - void testGradientRecoveringTrasnform(); void testMarkersOnClosedPath(); void testMarkersAngularUnits(); void testSodipodiArcShape(); void testSodipodiArcShapeOpen(); void testKritaChordShape(); void testSodipodiChordShape(); + + void testMarkersFillAsShape(); +private: + }; #endif // TESTSVGPARSER_H