Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 137 additions & 43 deletions src/core/svgimporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@

#define RGXS REGEX_SPACES

static qreal parseSvgUnit(const QString &str,
qreal relativeTo)
{
QString trimmed = str.trimmed();
if (trimmed.endsWith("%")) {
trimmed.remove("%");
return (trimmed.toDouble() / 100.0) * relativeTo;
}
return trimmed.toDouble();
}

class TextSvgAttributes {
public:
TextSvgAttributes() {}
Expand Down Expand Up @@ -633,14 +644,31 @@ bool extractScale(const QString& str, QMatrix& target) {
return false;
}

bool extractRotate(const QString& str, QMatrix& target) {
const QRegExp rx5(RGXS "rotate\\(" REGEX_SINGLE_FLOAT "\\)" RGXS,
Qt::CaseInsensitive);
if(rx5.exactMatch(str)) {
rx5.indexIn(str);
const QStringList capturedTxt = rx5.capturedTexts();
target.rotate(capturedTxt.at(1).toDouble());
return true;
bool extractRotate(const QString& str,
QMatrix& target)
{
const QRegExp rxRotate("rotate\\s*\\(\\s*([^\\s,)]+)(?:[\\s,]+([^\\s,)]+)[\\s,]+([^\\s,)]+))?\\s*\\)",
Qt::CaseInsensitive);

int pos = rxRotate.indexIn(str);
if (pos != -1) {
const QStringList captured = rxRotate.capturedTexts();
bool ok;
double angle = captured.at(1).toDouble(&ok);

if (ok) {
if (!captured.at(2).isEmpty() && !captured.at(3).isEmpty()) {
double cx = captured.at(2).toDouble();
double cy = captured.at(3).toDouble();

target.translate(cx, cy);
target.rotate(angle);
target.translate(-cx, -cy);
} else {
target.rotate(angle);
}
return true;
}
}
return false;
}
Expand Down Expand Up @@ -684,6 +712,54 @@ QMatrix getMatrixFromString(const QString &str) {
static QMap<QString, SvgGradient> gGradients;
// to from
static QMap<QString, QStringList> gUnresolvedGradientLinks;

void applyGradientToAttributes(const QDomElement &element,
BoxSvgAttributes &attributes,
const GradientCreator& gradientCreator)
{
QString fillAttr = element.attribute("fill").trimmed();
if (!fillAttr.startsWith("url(#")) { return; }

int start = fillAttr.indexOf('#') + 1;
int end = fillAttr.lastIndexOf(')');
if (fillAttr.at(end-1) == '\'' || fillAttr.at(end-1) == '\"') { end--; }
QString gradId = fillAttr.mid(start, end - start);

if (gGradients.contains(gradId)) {
const SvgGradient &templateGrad = gGradients[gradId];

qreal opacity = 1.0;
if (element.hasAttribute("fill-opacity")) {
opacity = element.attribute("fill-opacity").toDouble();
} else if (element.hasAttribute("opacity")) {
opacity = element.attribute("opacity").toDouble();
}

Gradient* newGradInstance = gradientCreator();

const QGradientStops stops = templateGrad.fGradient->getQGradientStops();
for (const QGradientStop &stop : stops) {
QColor c = stop.second;
c.setAlphaF(c.alphaF() * opacity);
newGradInstance->addColor(c);
}

newGradInstance->updateQGradientStops();

SvgGradient instance = {
newGradInstance,
templateGrad.fX1, templateGrad.fY1,
templateGrad.fX2, templateGrad.fY2,
templateGrad.fTrans,
templateGrad.fType
};

auto& fill = const_cast<FillSvgAttributes&>(attributes.getFillAttributes());
fill.setGradient(instance);
fill.setPaintType(GRADIENTPAINT);
}
}

void loadElement(const QDomElement &element, ContainerBox *parentGroup,
const BoxSvgAttributes &parentGroupAttributes,
const GradientCreator& gradientCreator) {
Expand Down Expand Up @@ -767,52 +843,69 @@ void loadElement(const QDomElement &element, ContainerBox *parentGroup,
}
}

double x1;
double x2;
double y1;
double y2;
switch(type) {
case GradientType::LINEAR:
{
const QString x1s = element.attribute("x1");
const QString y1s = element.attribute("y1");
const QString x2s = element.attribute("x2");
const QString y2s = element.attribute("y2");

x1 = toDouble(x1s);
y1 = toDouble(y1s),
x2 = toDouble(x2s);
y2 = toDouble(y2s);
break;
}
case GradientType::RADIAL:
{
const QString cxs = element.attribute("cx");
const QString cys = element.attribute("cy");
const QString rs = element.attribute("r");

const double cx = toDouble(cxs);
const double cy = toDouble(cys);
const double r = toDouble(rs);

x1 = cx;
y1 = cy;
x2 = cx + r;
y2 = cy + r;
QDomElement svgRoot = element.ownerDocument().documentElement();
QStringList viewBox = svgRoot.attribute("viewBox").split(QRegularExpression("\\s+"),
Qt::SkipEmptyParts);
qreal viewW = 1.0;
qreal viewH = 1.0;

if (viewBox.size() >= 4) {
viewW = viewBox.at(2).toDouble();
viewH = viewBox.at(3).toDouble();
} else {
viewW = svgRoot.attribute("width", "1").toDouble();
viewH = svgRoot.attribute("height", "1").toDouble();
}
if (viewW <= 0) { viewW = 1.0; }
if (viewH <= 0) { viewH = 1.0; }

QPointF p1, p2;
if (type == GradientType::LINEAR) {
p1 = QPointF(parseSvgUnit(element.attribute("x1"), viewW),
parseSvgUnit(element.attribute("y1"), viewH));
p2 = QPointF(parseSvgUnit(element.attribute("x2"), viewW),
parseSvgUnit(element.attribute("y2"), viewH));
} else {
const qreal cx = parseSvgUnit(element.attribute("cx"), viewW);
const qreal cy = parseSvgUnit(element.attribute("cy"), viewH);
const qreal r = parseSvgUnit(element.attribute("r"), (viewW + viewH) * 0.5);
p1 = QPointF(cx, cy);
p2 = QPointF(cx + r, cy + r);
}

const QString gradTrans = element.attribute("gradientTransform");
QMatrix trans = getMatrixFromString(gradTrans);
QString units = element.attribute("gradientUnits");

if (!gradTrans.isEmpty()) {
if (units == "objectBoundingBox" || units.isEmpty()) {
p1 = QPointF(p1.x() / viewW, p1.y() / viewH);
p2 = QPointF(p2.x() / viewW, p2.y() / viewH);

p1 = trans.map(p1);
p2 = trans.map(p2);

p1 = QPointF(p1.x() * viewW, p1.y() * viewH);
p2 = QPointF(p2.x() * viewW, p2.y() * viewH);
} else {
p1 = trans.map(p1);
p2 = trans.map(p2);
}
}

gGradients.insert(id, {gradient, p1.x(), p1.y(), p2.x(), p2.y(), QMatrix(), type});
break;
}
}

const QString gradTrans = element.attribute("gradientTransform");
const QMatrix trans = getMatrixFromString(gradTrans);
gGradients.insert(id, {gradient,
x1, y1,
x2, y2,
trans, type});
} else if(tagName == "path" || tagName == "polyline" || tagName == "polygon" || tagName == "line") {
VectorPathSvgAttributes attributes;
attributes.setParent(parentGroupAttributes);
attributes.loadBoundingBoxAttributes(element);
applyGradientToAttributes(element, attributes, gradientCreator);
if(tagName == "path") {
loadVectorPath(element, parentGroup, attributes);
} else if(tagName == "polyline") {
Expand All @@ -828,6 +921,7 @@ void loadElement(const QDomElement &element, ContainerBox *parentGroup,
BoxSvgAttributes attributes;
attributes.setParent(parentGroupAttributes);
attributes.loadBoundingBoxAttributes(element);
applyGradientToAttributes(element, attributes, gradientCreator);
if(tagName == "g" || tagName == "text") {
const auto group = loadBoxesGroup(element, parentGroup,
attributes, gradientCreator);
Expand Down