1
/*******************************************************************
3
Part of the Fritzing project - http://fritzing.org
4
Copyright (c) 2007-2012 Fachhochschule Potsdam - http://fh-potsdam.de
6
Fritzing is free software: you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation, either version 3 of the License, or
9
(at your option) any later version.
11
Fritzing is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
GNU General Public License for more details.
16
You should have received a copy of the GNU General Public License
17
along with Fritzing. If not, see <http://www.gnu.org/licenses/>.
19
********************************************************************
22
$Author: cohen@irascible.com $:
23
$Date: 2012-08-04 08:42:55 +0200 (Sat, 04 Aug 2012) $
25
********************************************************************/
27
#include "fsvgrenderer.h"
28
#include "debugdialog.h"
29
#include "svg/svgfilesplitter.h"
30
#include "utils/textutils.h"
31
#include "utils/graphicsutils.h"
32
#include "connectors/svgidlayer.h"
35
#include <QTextStream>
37
#include <QCoreApplication>
38
#include <QGraphicsSvgItem>
41
QString FSvgRenderer::NonConnectorName("nonconn");
43
static ConnectorInfo VanillaConnectorInfo;
45
FSvgRenderer::FSvgRenderer(QObject * parent) : QSvgRenderer(parent)
47
m_defaultSizeF = QSizeF(0,0);
50
FSvgRenderer::~FSvgRenderer()
52
clearConnectorInfoHash(m_connectorInfoHash);
53
clearConnectorInfoHash(m_nonConnectorInfoHash);
56
void FSvgRenderer::initNames() {
57
VanillaConnectorInfo.gotCircle = false;
60
void FSvgRenderer::clearConnectorInfoHash(QHash<QString, ConnectorInfo *> & hash) {
61
foreach (ConnectorInfo * connectorInfo, hash.values()) {
67
void FSvgRenderer::cleanup() {
71
QByteArray FSvgRenderer::loadSvg(const QString & filename) {
74
return loadSvg(filename, strings, strings, strings, string, string, false);
77
QByteArray FSvgRenderer::loadSvg(const QString & filename, const QStringList & connectorIDs, const QStringList & terminalIDs, const QStringList & legIDs, const QString & setColor, const QString & colorElementID, bool findNonConnectors) {
78
if (!QFileInfo(filename).exists() || !QFileInfo(filename).isFile()) {
83
if (!file.open(QFile::ReadOnly | QFile::Text)) {
87
QByteArray contents = file.readAll();
90
if (contents.length() <= 0) return QByteArray();
92
return loadAux(contents, filename, connectorIDs, terminalIDs, legIDs, setColor, colorElementID, findNonConnectors);
96
bool FSvgRenderer::loadSvgString(const QString & svg) {
97
QByteArray byteArray(svg.toUtf8());
98
QByteArray result = loadSvg(byteArray, "", true);
99
return (!result.isEmpty());
102
bool FSvgRenderer::loadSvgString(const QString & svg, QString & newSvg) {
103
QByteArray byteArray(svg.toUtf8());
104
QByteArray result = loadSvg(byteArray, "", true);
105
newSvg = QString(result);
106
return (!result.isEmpty());
109
QByteArray FSvgRenderer::loadSvg(const QByteArray & contents, const QString & filename, bool findNonConnectors) {
112
return loadSvg(contents, filename, strings, strings, strings, string, string, findNonConnectors);
115
QByteArray FSvgRenderer::loadSvg(const QByteArray & contents, const QString & filename, const QStringList & connectorIDs, const QStringList & terminalIDs, const QStringList & legIDs, const QString & setColor, const QString & colorElementID, bool findNonConnectors) {
116
return loadAux(contents, filename, connectorIDs, terminalIDs, legIDs, setColor, colorElementID, findNonConnectors);
119
QByteArray FSvgRenderer::loadAux(const QByteArray & theContents, const QString & filename, const QStringList & connectorIDs, const QStringList & terminalIDs, const QStringList & legIDs, const QString & setColor, const QString & colorElementID, bool findNonConnectors) {
121
QByteArray cleanContents(theContents);
122
bool cleaned = false;
123
if (TextUtils::isIllustratorFile(cleanContents)) {
124
QString string(cleanContents);
126
if (string.contains("sodipodi") || string.contains("inkscape")) {
127
// if svg has both Illustrator and Inkscape crap then converting back and forth between strings and QDomDocument
128
// in FixPixelDimensionsIn() can result in invalid xml
129
TextUtils::cleanSodipodi(string);
131
if (!filename.contains("icon", Qt::CaseInsensitive)) {
132
DebugDialog::debug("Illustrator and inkscape:" + filename);
137
//DebugDialog::debug("Illustrator " + filename);
138
if (TextUtils::fixPixelDimensionsIn(string)) {
140
cleanContents = string.toUtf8();
144
if (cleanContents.contains("sodipodi") || cleanContents.contains("inkscape")) {
145
//DebugDialog::debug("inkscape " + filename);
148
if (cleanContents.contains("<tspan")) {
149
QString string(cleanContents);
150
TextUtils::tspanRemove(string);
151
cleanContents = string.toUtf8();
155
if (cleanContents.contains("<use")) {
156
QString string(cleanContents);
157
TextUtils::noUse(string);
158
cleanContents = string.toUtf8();
162
if (cleanContents.contains("<pattern")) {
163
QString string(cleanContents);
164
TextUtils::noPattern(string);
165
cleanContents = string.toUtf8();
171
if (connectorIDs.count() > 0 || !setColor.isEmpty() || findNonConnectors) {
176
if (!doc.setContent(cleanContents, &errorStr, &errorLine, &errorColumn)) {
177
DebugDialog::debug(QString("renderer loadAux failed %1 %2 %3 %4").arg(filename).arg(errorStr).arg(errorLine).arg(errorColumn));
180
bool resetContents = false;
182
QDomElement root = doc.documentElement();
183
if (!setColor.isEmpty()) {
184
QDomElement element = TextUtils::findElementWithAttribute(root, "id", colorElementID);
185
if (!element.isNull()) {
186
QStringList exceptions;
187
exceptions << "black" << "#000000";
188
SvgFileSplitter::fixColorRecurse(element, setColor, exceptions);
189
resetContents = true;
192
if (connectorIDs.count() > 0) {
193
bool init = initConnectorInfo(doc, connectorIDs, terminalIDs, legIDs, filename);
194
resetContents = resetContents || init;
196
if (findNonConnectors) {
197
initNonConnectorInfo(doc, filename);
201
cleanContents = TextUtils::removeXMLEntities(doc.toString()).toUtf8();
207
cleanContents = doc.toByteArray();
209
//QFile file("all.txt");
210
//if (file.open(QIODevice::Append)) {
211
//QTextStream t(&file);
212
//t << cleanContents;
218
//DebugDialog::debug(cleanContents.data());
220
QXmlStreamReader xml(cleanContents);
221
bool result = determineDefaultSize(xml);
226
result = QSvgRenderer::load(cleanContents);
228
m_filename = filename;
229
return cleanContents;
235
bool FSvgRenderer::fastLoad(const QByteArray & contents) {
236
return QSvgRenderer::load(contents);
239
const QString & FSvgRenderer::filename() {
243
QPixmap * FSvgRenderer::getPixmap(QSvgRenderer * renderer, QSize size)
245
QPixmap *pixmap = new QPixmap(size);
246
pixmap->fill(Qt::transparent);
247
QPainter painter(pixmap);
248
// preserve aspect ratio
249
QSizeF def = renderer->defaultSize();
250
FSvgRenderer * frenderer = qobject_cast<FSvgRenderer *>(renderer);
252
def = frenderer->defaultSizeF();
254
double newW = size.width();
255
double newH = newW * def.height() / def.width();
256
if (newH > size.height()) {
257
newH = size.height();
258
newW = newH * def.width() / def.height();
260
QRectF bounds((size.width() - newW) / 2.0, (size.height() - newH) / 2.0, newW, newH);
261
renderer->render(&painter, bounds);
267
bool FSvgRenderer::determineDefaultSize(QXmlStreamReader & xml)
269
QSizeF size = parseForWidthAndHeight(xml);
271
m_defaultSizeF = QSizeF(size.width() * GraphicsUtils::SVGDPI, size.height() * GraphicsUtils::SVGDPI);
272
return (size.width() != 0 && size.height() != 0);
275
QSizeF FSvgRenderer::parseForWidthAndHeight(QXmlStreamReader & xml)
277
QSizeF size = TextUtils::parseForWidthAndHeight(xml);
278
if (size.width() != 0 && size.height() != 0) return size;
280
QIODevice * device = xml.device();
281
DebugDialog::debug("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
282
DebugDialog::debug("bad width and/or bad height in svg:");
285
QString string(device->readAll());
286
DebugDialog::debug(string);
288
DebugDialog::debug("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
293
QSizeF FSvgRenderer::defaultSizeF() {
294
if (m_defaultSizeF.width() == 0 && m_defaultSizeF.height() == 0) {
295
return defaultSize();
298
return m_defaultSizeF;
302
void FSvgRenderer::initNonConnectorInfo(QDomDocument & domDocument, const QString & filename)
304
clearConnectorInfoHash(m_nonConnectorInfoHash);
305
QDomElement root = domDocument.documentElement();
306
initNonConnectorInfoAux(root, filename);
309
void FSvgRenderer::initNonConnectorInfoAux(QDomElement & element, const QString & filename)
311
QString id = element.attribute("id");
312
if (id.startsWith(NonConnectorName, Qt::CaseInsensitive)) {
313
ConnectorInfo * connectorInfo = initConnectorInfoStruct(element, filename);
314
m_nonConnectorInfoHash.insert(id, connectorInfo);
316
QDomElement child = element.firstChildElement();
317
while (!child.isNull()) {
318
initNonConnectorInfoAux(child, filename);
319
child = child.nextSiblingElement();
323
bool FSvgRenderer::initConnectorInfo(QDomDocument & domDocument, const QStringList & connectorIDs, const QStringList & terminalIDs, const QStringList & legIDs, const QString & filename)
326
clearConnectorInfoHash(m_connectorInfoHash);
327
QDomElement root = domDocument.documentElement();
328
initConnectorInfoAux(root, connectorIDs, filename);
329
if (terminalIDs.count() > 0) {
330
initTerminalInfoAux(root, connectorIDs, terminalIDs);
332
if (legIDs.count() > 0) {
333
initLegInfoAux(root, connectorIDs, legIDs, result);
339
void FSvgRenderer::initLegInfoAux(QDomElement & element, const QStringList & connectorIDs, const QStringList & legIDs, bool & gotOne)
341
QString id = element.attribute("id");
343
int ix = legIDs.indexOf(id);
345
//DebugDialog::debug("init leg info " + id);
346
//foreach (QString lid, legIDs) {
347
// DebugDialog::debug("\tleg id:" + lid);
350
element.setTagName("g"); // don't want this element to actually be drawn
352
ConnectorInfo * connectorInfo = m_connectorInfoHash.value(connectorIDs.at(ix), NULL);
355
//QTextStream stream(&temp);
356
//element.save(stream, 0);
357
//DebugDialog::debug("\t matched " + connectorIDs.at(ix) + " " + temp);
358
connectorInfo->legMatrix = TextUtils::elementToMatrix(element);
359
connectorInfo->legColor = element.attribute("stroke");
360
connectorInfo->legLine = QLineF();
361
connectorInfo->legStrokeWidth = 0;
362
initLegInfoAux(element, connectorInfo);
364
// don't return here, might miss other legs
368
QDomElement child = element.firstChildElement();
369
while (!child.isNull()) {
370
initLegInfoAux(child, connectorIDs, legIDs, gotOne);
371
child = child.nextSiblingElement();
375
bool FSvgRenderer::initLegInfoAux(QDomElement & element, ConnectorInfo * connectorInfo)
378
double sw = element.attribute("stroke-width").toDouble(&ok);
379
if (!ok) return false;
381
double x1 = element.attribute("x1").toDouble(&ok);
382
if (!ok) return false;
384
double y1 = element.attribute("y1").toDouble(&ok);
385
if (!ok) return false;
387
double x2 = element.attribute("x2").toDouble(&ok);
388
if (!ok) return false;
390
double y2 = element.attribute("y2").toDouble(&ok);
391
if (!ok) return false;
393
connectorInfo->legStrokeWidth = sw;
394
connectorInfo->legLine = QLineF(x1, y1, x2, y2);
398
void FSvgRenderer::initTerminalInfoAux(QDomElement & element, const QStringList & connectorIDs, const QStringList & terminalIDs)
400
QString id = element.attribute("id");
402
int ix = terminalIDs.indexOf(id);
404
ConnectorInfo * connectorInfo = m_connectorInfoHash.value(connectorIDs.at(ix), NULL);
406
connectorInfo->terminalMatrix = TextUtils::elementToMatrix(element);
408
// don't return here, might miss other terminal ids
412
QDomElement child = element.firstChildElement();
413
while (!child.isNull()) {
414
initTerminalInfoAux(child, connectorIDs, terminalIDs);
415
child = child.nextSiblingElement();
419
void FSvgRenderer::initConnectorInfoAux(QDomElement & element, const QStringList & connectorIDs, const QString & filename)
421
QString id = element.attribute("id");
423
if (connectorIDs.contains(id)) {
424
ConnectorInfo * connectorInfo = initConnectorInfoStruct(element, filename);
425
m_connectorInfoHash.insert(id, connectorInfo);
427
// don't return here, might miss other connectors
430
QDomElement child = element.firstChildElement();
431
while (!child.isNull()) {
432
initConnectorInfoAux(child, connectorIDs, filename);
433
child = child.nextSiblingElement();
437
ConnectorInfo * FSvgRenderer::initConnectorInfoStruct(QDomElement & connectorElement, const QString & filename) {
438
ConnectorInfo * connectorInfo = new ConnectorInfo();
439
connectorInfo->radius = connectorInfo->strokeWidth = 0;
440
connectorInfo->gotCircle = false;
442
if (connectorElement.isNull()) return connectorInfo;
444
connectorInfo->matrix = TextUtils::elementToMatrix(connectorElement);
445
initConnectorInfoStructAux(connectorElement, connectorInfo, filename);
446
return connectorInfo;
449
bool FSvgRenderer::initConnectorInfoStructAux(QDomElement & element, ConnectorInfo * connectorInfo, const QString & filename)
451
// right now we only handle circles
452
if (element.nodeName().compare("circle") != 0) {
453
QDomElement child = element.firstChildElement();
454
while (!child.isNull()) {
455
if (initConnectorInfoStructAux(child, connectorInfo, filename)) return true;
457
child = child.nextSiblingElement();
463
element.attribute("cx").toDouble(&ok);
464
if (!ok) return false;
466
element.attribute("cy").toDouble(&ok);
467
if (!ok) return false;
469
double r = element.attribute("r").toDouble(&ok);
470
if (!ok) return false;
472
double sw = element.attribute("stroke-width").toDouble(&ok);
474
QDomElement parent = element.parentNode().toElement();
475
while (!parent.isNull()) {
476
sw = element.attribute("stroke-width").toDouble(&ok);
481
parent = parent.parentNode().toElement();
486
QTextStream stream(&text);
487
element.save(stream, 0);
488
//DebugDialog::debug(QString("no circle stroke width set in %1: %2").arg(filename).arg(text));
489
element.setAttribute("stroke-width", 1);
492
//QString strokewidth("stroke-width");
493
//QString s = element.attribute("style");
494
//SvgFileSplitter::fixStyleAttribute(connectorElement, s, strokewidth);
495
//sw = connectorElement.attribute("stroke-width").toDouble(&ok);
502
QMatrix matrix = TextUtils::elementToMatrix(element);
503
if (!matrix.isIdentity()) {
505
QRectF r2 = matrix.mapRect(r1);
506
if (r2.width() != r1.width()) {
508
sw = sw * r2.width() / r1.width();
512
//DebugDialog::debug("got a circle");
513
connectorInfo->gotCircle = true;
514
//connectorInfo->cbounds.setRect(cx - r - (sw / 2.0), cy - r - (sw / 2.0), (r * 2) + sw, (r * 2) + sw);
515
connectorInfo->radius = r;
516
connectorInfo->strokeWidth = sw;
520
ConnectorInfo * FSvgRenderer::getConnectorInfo(const QString & connectorID) {
521
return m_connectorInfoHash.value(connectorID, &VanillaConnectorInfo);
524
bool FSvgRenderer::setUpConnector(SvgIdLayer * svgIdLayer, bool ignoreTerminalPoint) {
526
if (svgIdLayer == NULL) return false;
528
if (svgIdLayer->m_processed) {
529
// hybrids are not visible in some views
530
return svgIdLayer->m_svgVisible || svgIdLayer->m_hybrid;
533
svgIdLayer->m_processed = true;
535
QString connectorID = svgIdLayer->m_svgId;
537
// boundsOnElement seems to include any matrix on the element itself.
538
// I would swear this wasn't true before Qt4.7, but maybe I am crazy
539
QRectF bounds = this->boundsOnElement(connectorID);
541
if (bounds.isNull() && !svgIdLayer->m_hybrid) { // hybrids can have zero size
542
svgIdLayer->m_svgVisible = false;
543
DebugDialog::debug("renderer::setupconnector: null bounds");
547
QSizeF defaultSizeF = this->defaultSizeF();
548
QRectF viewBox = this->viewBoxF();
550
ConnectorInfo * connectorInfo = getConnectorInfo(connectorID);
553
DebugDialog::debug(QString("connectorid:%1 m11:%2 m12:%3 m21:%4 m22:%5 dx:%6 dy:%7")
555
.arg(connectorInfo->matrix.m11())
556
.arg(connectorInfo->matrix.m12())
557
.arg(connectorInfo->matrix.m21())
558
.arg(connectorInfo->matrix.m22())
559
.arg(connectorInfo->matrix.dx())
560
.arg(connectorInfo->matrix.dy()),
564
if (connectorInfo && connectorInfo->gotCircle) {
565
svgIdLayer->m_radius = connectorInfo->radius * defaultSizeF.width() / viewBox.width();
566
svgIdLayer->m_strokeWidth = connectorInfo->strokeWidth * defaultSizeF.width() / viewBox.width();
567
//bounds = connectorInfo->cbounds;
571
/*DebugDialog::debug(QString("identity matrix %11 %1 %2, viewbox: %3 %4 %5 %6, bounds: %7 %8 %9 %10, size: %12 %13").arg(m_modelPart->title()).arg(connectorSharedID())
572
.arg(viewBox.x()).arg(viewBox.y()).arg(viewBox.width()).arg(viewBox.height())
573
.arg(bounds.x()).arg(bounds.y()).arg(bounds.width()).arg(bounds.height())
575
.arg(defaultSizeF.width()).arg(defaultSizeF.height())
579
// some strangeness in the way that svg items and non-svg items map to screen space
580
// might be a qt problem.
581
//QMatrix matrix0 = connectorInfo->matrix * this->matrixForElement(connectorID);
582
//QRectF r1 = matrix0.mapRect(bounds);
583
QRectF r1 = this->matrixForElement(connectorID).mapRect(bounds);
586
svgIdLayer->m_rect.setRect(r1.x() * defaultSize.width() / viewBox.width(),
587
r1.y() * defaultSize.height() / viewBox.height(),
588
r1.width() * defaultSize.width() / viewBox.width(),
589
r1.height() * defaultSize.height() / viewBox.height());
591
svgIdLayer->m_rect.setRect(r1.x() * defaultSizeF.width() / viewBox.width(),
592
r1.y() * defaultSizeF.height() / viewBox.height(),
593
r1.width() * defaultSizeF.width() / viewBox.width(),
594
r1.height() * defaultSizeF.height() / viewBox.height());
596
svgIdLayer->m_svgVisible = !bounds.isNull();
597
//if (!svgIdLayer->m_svgVisible) {
598
//DebugDialog::debug("not vis");
600
svgIdLayer->m_point = calcTerminalPoint(svgIdLayer->m_terminalId, svgIdLayer->m_rect, ignoreTerminalPoint, viewBox, connectorInfo->terminalMatrix);
601
calcLeg(svgIdLayer, viewBox, connectorInfo);
606
void FSvgRenderer::calcLeg(SvgIdLayer * svgIdLayer, const QRectF & viewBox, ConnectorInfo * connectorInfo)
608
if (svgIdLayer->m_legId.isEmpty()) return;
610
svgIdLayer->m_legColor = connectorInfo->legColor;
612
QSizeF defaultSizeF = this->defaultSizeF();
613
svgIdLayer->m_legStrokeWidth = connectorInfo->legStrokeWidth * defaultSizeF.width() / viewBox.width();
616
DebugDialog::debug( QString("calcleg leg %1 %2 %3 %4")
617
.arg(connectorInfo->legLine.p1().x())
618
.arg(connectorInfo->legLine.p1().y())
619
.arg(connectorInfo->legLine.p2().x())
620
.arg(connectorInfo->legLine.p2().y())
624
QMatrix matrix = this->matrixForElement(svgIdLayer->m_legId) * connectorInfo->legMatrix;
625
QPointF p1 = matrix.map(connectorInfo->legLine.p1());
626
QPointF p2 = matrix.map(connectorInfo->legLine.p2());
628
double x1 = p1.x() * defaultSizeF.width() / viewBox.width();
629
double y1 = p1.y() * defaultSizeF.height() / viewBox.height();
630
double x2 = p2.x() * defaultSizeF.width() / viewBox.width();
631
double y2 = p2.y() * defaultSizeF.height() / viewBox.height();
632
QPointF center = viewBox.center();
633
double d1 = ((x1 - center.x()) * (x1 - center.x())) + ((y1 - center.y()) * (y1 - center.y()));
634
double d2 = ((x2 - center.x()) * (x2 - center.x())) + ((y2 - center.y()) * (y1 - center.y()));
636
// find the end which is closer to the center of the viewBox (which shouldn't include the leg)
638
svgIdLayer->m_legLine = QLineF(x1, y1, x2, y2);
641
svgIdLayer->m_legLine = QLineF(x2, y2, x1, y1);
645
QPointF FSvgRenderer::calcTerminalPoint(const QString & terminalId, const QRectF & connectorRect, bool ignoreTerminalPoint, const QRectF & viewBox, QMatrix & terminalMatrix)
647
Q_UNUSED(terminalMatrix);
648
QPointF terminalPoint = connectorRect.center() - connectorRect.topLeft(); // default spot is centered
649
if (ignoreTerminalPoint) {
650
return terminalPoint;
652
if (terminalId.isNull() || terminalId.isEmpty()) {
653
return terminalPoint;
656
QRectF tBounds = this->boundsOnElement(terminalId);
657
if (tBounds.isNull()) {
658
return terminalPoint;
661
QSizeF defaultSizeF = this->defaultSizeF();
662
if (tBounds.width() >= defaultSizeF.width() && tBounds.height() >= defaultSizeF.height()) {
663
return terminalPoint;
666
//DebugDialog::debug( QString("terminal %5 rect %1,%2,%3,%4").arg(tBounds.x()).
668
//arg(tBounds.width()).
669
//arg(tBounds.height()).
673
// matrixForElement only grabs parent matrices, not any transforms in the element itself
674
//QMatrix tMatrix = this->matrixForElement(terminalId) ; //* terminalMatrix;
675
//QRectF terminalRect = tMatrix.mapRect(tBounds);
676
QRectF terminalRect = this->matrixForElement(terminalId).mapRect(tBounds);
677
QPointF c = terminalRect.center();
678
QPointF q(c.x() * defaultSizeF.width() / viewBox.width(), c.y() * defaultSizeF.height() / viewBox.height());
679
terminalPoint = q - connectorRect.topLeft();
680
//DebugDialog::debug( QString("terminalagain %3 rect %1,%2 ").arg(terminalPoint.x()).
681
//arg(terminalPoint.y()).
684
return terminalPoint;
687
QList<SvgIdLayer *> FSvgRenderer::setUpNonConnectors() {
689
QList<SvgIdLayer *> list;
690
if (m_nonConnectorInfoHash.count() == 0) return list;
692
foreach (QString nonConnectorID, m_nonConnectorInfoHash.keys()) {
693
SvgIdLayer * svgIdLayer = new SvgIdLayer();
694
svgIdLayer->m_processed = true;
695
svgIdLayer->m_svgId = nonConnectorID;
696
QRectF bounds = this->boundsOnElement(nonConnectorID);
697
if (bounds.isNull()) {
702
QSizeF defaultSizeF = this->defaultSizeF();
703
QSize defaultSize = this->defaultSize();
704
if ((bounds.width()) == defaultSizeF.width() && (bounds.height()) == defaultSizeF.height()) {
709
QRectF viewBox = this->viewBoxF();
711
ConnectorInfo * connectorInfo = m_nonConnectorInfoHash.value(nonConnectorID, NULL);
712
if (connectorInfo && connectorInfo->gotCircle) {
713
svgIdLayer->m_radius = connectorInfo->radius * defaultSizeF.width() / viewBox.width();
714
svgIdLayer->m_strokeWidth = connectorInfo->strokeWidth * defaultSizeF.width() / viewBox.width();
715
//bounds = connectorInfo->cbounds;
718
// matrixForElement only grabs parent matrices, not any transforms in the element itself
719
//QMatrix matrix0 = connectorInfo->matrix * this->matrixForElement(nonConnectorID);
720
//QRectF r1 = matrix0.mapRect(bounds);
721
QRectF r1 = this->matrixForElement(nonConnectorID).mapRect(bounds);
722
svgIdLayer->m_rect.setRect(r1.x() * defaultSize.width() / viewBox.width(), r1.y() * defaultSize.height() / viewBox.height(), r1.width() * defaultSize.width() / viewBox.width(), r1.height() * defaultSize.height() / viewBox.height());
723
svgIdLayer->m_point = svgIdLayer->m_rect.center() - svgIdLayer->m_rect.topLeft();
724
svgIdLayer->m_svgVisible = true;
726
list.append(svgIdLayer);
1
/*******************************************************************
3
Part of the Fritzing project - http://fritzing.org
4
Copyright (c) 2007-2012 Fachhochschule Potsdam - http://fh-potsdam.de
6
Fritzing is free software: you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation, either version 3 of the License, or
9
(at your option) any later version.
11
Fritzing is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
GNU General Public License for more details.
16
You should have received a copy of the GNU General Public License
17
along with Fritzing. If not, see <http://www.gnu.org/licenses/>.
19
********************************************************************
22
$Author: irascibl@gmail.com $:
23
$Date: 2012-10-12 22:39:11 +0200 (Fri, 12 Oct 2012) $
25
********************************************************************/
27
#include "fsvgrenderer.h"
28
#include "debugdialog.h"
29
#include "svg/svgfilesplitter.h"
30
#include "utils/textutils.h"
31
#include "utils/graphicsutils.h"
32
#include "connectors/svgidlayer.h"
35
#include <QTextStream>
37
#include <QCoreApplication>
38
#include <QGraphicsSvgItem>
41
QString FSvgRenderer::NonConnectorName("nonconn");
43
static ConnectorInfo VanillaConnectorInfo;
45
FSvgRenderer::FSvgRenderer(QObject * parent) : QSvgRenderer(parent)
47
m_defaultSizeF = QSizeF(0,0);
50
FSvgRenderer::~FSvgRenderer()
52
clearConnectorInfoHash(m_connectorInfoHash);
53
clearConnectorInfoHash(m_nonConnectorInfoHash);
56
void FSvgRenderer::initNames() {
57
VanillaConnectorInfo.gotCircle = false;
60
void FSvgRenderer::clearConnectorInfoHash(QHash<QString, ConnectorInfo *> & hash) {
61
foreach (ConnectorInfo * connectorInfo, hash.values()) {
67
void FSvgRenderer::cleanup() {
71
QByteArray FSvgRenderer::loadSvg(const QString & filename) {
74
return loadSvg(filename, strings, strings, strings, string, string, false);
77
QByteArray FSvgRenderer::loadSvg(const QString & filename, const QStringList & connectorIDs, const QStringList & terminalIDs, const QStringList & legIDs, const QString & setColor, const QString & colorElementID, bool findNonConnectors) {
78
if (!QFileInfo(filename).exists() || !QFileInfo(filename).isFile()) {
83
if (!file.open(QFile::ReadOnly | QFile::Text)) {
87
QByteArray contents = file.readAll();
90
if (contents.length() <= 0) return QByteArray();
92
return loadAux(contents, filename, connectorIDs, terminalIDs, legIDs, setColor, colorElementID, findNonConnectors);
96
bool FSvgRenderer::loadSvgString(const QString & svg) {
97
QByteArray byteArray(svg.toUtf8());
98
QByteArray result = loadSvg(byteArray, "", true);
99
return (!result.isEmpty());
102
bool FSvgRenderer::loadSvgString(const QString & svg, QString & newSvg) {
103
QByteArray byteArray(svg.toUtf8());
104
QByteArray result = loadSvg(byteArray, "", true);
105
newSvg = QString(result);
106
return (!result.isEmpty());
109
QByteArray FSvgRenderer::loadSvg(const QByteArray & contents, const QString & filename, bool findNonConnectors) {
112
return loadSvg(contents, filename, strings, strings, strings, string, string, findNonConnectors);
115
QByteArray FSvgRenderer::loadSvg(const QByteArray & contents, const QString & filename, const QStringList & connectorIDs, const QStringList & terminalIDs, const QStringList & legIDs, const QString & setColor, const QString & colorElementID, bool findNonConnectors) {
116
return loadAux(contents, filename, connectorIDs, terminalIDs, legIDs, setColor, colorElementID, findNonConnectors);
119
QByteArray FSvgRenderer::loadAux(const QByteArray & theContents, const QString & filename, const QStringList & connectorIDs, const QStringList & terminalIDs, const QStringList & legIDs, const QString & setColor, const QString & colorElementID, bool findNonConnectors)
121
QByteArray cleanContents(theContents);
122
bool cleaned = false;
124
QString string(cleanContents);
125
if (TextUtils::fixMuch(string, false)) {
128
if (TextUtils::fixPixelDimensionsIn(string)) {
132
cleanContents = string.toUtf8();
135
if (connectorIDs.count() > 0 || !setColor.isEmpty() || findNonConnectors) {
140
if (!doc.setContent(cleanContents, &errorStr, &errorLine, &errorColumn)) {
141
DebugDialog::debug(QString("renderer loadAux failed %1 %2 %3 %4").arg(filename).arg(errorStr).arg(errorLine).arg(errorColumn));
144
bool resetContents = false;
146
QDomElement root = doc.documentElement();
147
if (!setColor.isEmpty()) {
148
QDomElement element = TextUtils::findElementWithAttribute(root, "id", colorElementID);
149
if (!element.isNull()) {
150
QStringList exceptions;
151
exceptions << "black" << "#000000";
152
SvgFileSplitter::fixColorRecurse(element, setColor, exceptions);
153
resetContents = true;
156
if (connectorIDs.count() > 0) {
157
bool init = initConnectorInfo(doc, connectorIDs, terminalIDs, legIDs, filename);
158
resetContents = resetContents || init;
160
if (findNonConnectors) {
161
initNonConnectorInfo(doc, filename);
165
cleanContents = TextUtils::removeXMLEntities(doc.toString()).toUtf8();
171
cleanContents = doc.toByteArray();
173
//QFile file("all.txt");
174
//if (file.open(QIODevice::Append)) {
175
//QTextStream t(&file);
176
//t << cleanContents;
182
//DebugDialog::debug(cleanContents.data());
184
QXmlStreamReader xml(cleanContents);
185
bool result = determineDefaultSize(xml);
190
result = QSvgRenderer::load(cleanContents);
192
m_filename = filename;
193
return cleanContents;
199
bool FSvgRenderer::fastLoad(const QByteArray & contents) {
200
return QSvgRenderer::load(contents);
203
const QString & FSvgRenderer::filename() {
207
QPixmap * FSvgRenderer::getPixmap(QSvgRenderer * renderer, QSize size)
209
QPixmap *pixmap = new QPixmap(size);
210
pixmap->fill(Qt::transparent);
211
QPainter painter(pixmap);
212
// preserve aspect ratio
213
QSizeF def = renderer->defaultSize();
214
FSvgRenderer * frenderer = qobject_cast<FSvgRenderer *>(renderer);
216
def = frenderer->defaultSizeF();
218
double newW = size.width();
219
double newH = newW * def.height() / def.width();
220
if (newH > size.height()) {
221
newH = size.height();
222
newW = newH * def.width() / def.height();
224
QRectF bounds((size.width() - newW) / 2.0, (size.height() - newH) / 2.0, newW, newH);
225
renderer->render(&painter, bounds);
231
bool FSvgRenderer::determineDefaultSize(QXmlStreamReader & xml)
233
QSizeF size = parseForWidthAndHeight(xml);
235
m_defaultSizeF = QSizeF(size.width() * GraphicsUtils::SVGDPI, size.height() * GraphicsUtils::SVGDPI);
236
return (size.width() != 0 && size.height() != 0);
239
QSizeF FSvgRenderer::parseForWidthAndHeight(QXmlStreamReader & xml)
242
QSizeF size = TextUtils::parseForWidthAndHeight(xml, viewBox);
243
if (size.width() != 0 && size.height() != 0) return size;
245
QIODevice * device = xml.device();
246
DebugDialog::debug("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
247
DebugDialog::debug("bad width and/or bad height in svg:");
250
QString string(device->readAll());
251
DebugDialog::debug(string);
253
DebugDialog::debug("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
258
QSizeF FSvgRenderer::defaultSizeF() {
259
if (m_defaultSizeF.width() == 0 && m_defaultSizeF.height() == 0) {
260
return defaultSize();
263
return m_defaultSizeF;
267
void FSvgRenderer::initNonConnectorInfo(QDomDocument & domDocument, const QString & filename)
269
clearConnectorInfoHash(m_nonConnectorInfoHash);
270
QDomElement root = domDocument.documentElement();
271
initNonConnectorInfoAux(root, filename);
274
void FSvgRenderer::initNonConnectorInfoAux(QDomElement & element, const QString & filename)
276
QString id = element.attribute("id");
277
if (id.startsWith(NonConnectorName, Qt::CaseInsensitive)) {
278
ConnectorInfo * connectorInfo = initConnectorInfoStruct(element, filename);
279
m_nonConnectorInfoHash.insert(id, connectorInfo);
281
QDomElement child = element.firstChildElement();
282
while (!child.isNull()) {
283
initNonConnectorInfoAux(child, filename);
284
child = child.nextSiblingElement();
288
bool FSvgRenderer::initConnectorInfo(QDomDocument & domDocument, const QStringList & connectorIDs, const QStringList & terminalIDs, const QStringList & legIDs, const QString & filename)
291
clearConnectorInfoHash(m_connectorInfoHash);
292
QDomElement root = domDocument.documentElement();
293
initConnectorInfoAux(root, connectorIDs, filename);
294
if (terminalIDs.count() > 0) {
295
initTerminalInfoAux(root, connectorIDs, terminalIDs);
297
if (legIDs.count() > 0) {
298
initLegInfoAux(root, connectorIDs, legIDs, result);
304
void FSvgRenderer::initLegInfoAux(QDomElement & element, const QStringList & connectorIDs, const QStringList & legIDs, bool & gotOne)
306
QString id = element.attribute("id");
308
int ix = legIDs.indexOf(id);
310
//DebugDialog::debug("init leg info " + id);
311
//foreach (QString lid, legIDs) {
312
// DebugDialog::debug("\tleg id:" + lid);
315
element.setTagName("g"); // don't want this element to actually be drawn
317
ConnectorInfo * connectorInfo = m_connectorInfoHash.value(connectorIDs.at(ix), NULL);
320
//QTextStream stream(&temp);
321
//element.save(stream, 0);
322
//DebugDialog::debug("\t matched " + connectorIDs.at(ix) + " " + temp);
323
connectorInfo->legMatrix = TextUtils::elementToMatrix(element);
324
connectorInfo->legColor = element.attribute("stroke");
325
connectorInfo->legLine = QLineF();
326
connectorInfo->legStrokeWidth = 0;
327
initLegInfoAux(element, connectorInfo);
329
// don't return here, might miss other legs
333
QDomElement child = element.firstChildElement();
334
while (!child.isNull()) {
335
initLegInfoAux(child, connectorIDs, legIDs, gotOne);
336
child = child.nextSiblingElement();
340
bool FSvgRenderer::initLegInfoAux(QDomElement & element, ConnectorInfo * connectorInfo)
343
double sw = element.attribute("stroke-width").toDouble(&ok);
344
if (!ok) return false;
346
double x1 = element.attribute("x1").toDouble(&ok);
347
if (!ok) return false;
349
double y1 = element.attribute("y1").toDouble(&ok);
350
if (!ok) return false;
352
double x2 = element.attribute("x2").toDouble(&ok);
353
if (!ok) return false;
355
double y2 = element.attribute("y2").toDouble(&ok);
356
if (!ok) return false;
358
connectorInfo->legStrokeWidth = sw;
359
connectorInfo->legLine = QLineF(x1, y1, x2, y2);
363
void FSvgRenderer::initTerminalInfoAux(QDomElement & element, const QStringList & connectorIDs, const QStringList & terminalIDs)
365
QString id = element.attribute("id");
367
int ix = terminalIDs.indexOf(id);
369
ConnectorInfo * connectorInfo = m_connectorInfoHash.value(connectorIDs.at(ix), NULL);
371
connectorInfo->terminalMatrix = TextUtils::elementToMatrix(element);
373
// don't return here, might miss other terminal ids
377
QDomElement child = element.firstChildElement();
378
while (!child.isNull()) {
379
initTerminalInfoAux(child, connectorIDs, terminalIDs);
380
child = child.nextSiblingElement();
384
void FSvgRenderer::initConnectorInfoAux(QDomElement & element, const QStringList & connectorIDs, const QString & filename)
386
QString id = element.attribute("id");
388
if (connectorIDs.contains(id)) {
389
ConnectorInfo * connectorInfo = initConnectorInfoStruct(element, filename);
390
m_connectorInfoHash.insert(id, connectorInfo);
392
// don't return here, might miss other connectors
395
QDomElement child = element.firstChildElement();
396
while (!child.isNull()) {
397
initConnectorInfoAux(child, connectorIDs, filename);
398
child = child.nextSiblingElement();
402
ConnectorInfo * FSvgRenderer::initConnectorInfoStruct(QDomElement & connectorElement, const QString & filename) {
403
ConnectorInfo * connectorInfo = new ConnectorInfo();
404
connectorInfo->radius = connectorInfo->strokeWidth = 0;
405
connectorInfo->gotCircle = false;
407
if (connectorElement.isNull()) return connectorInfo;
409
connectorInfo->matrix = TextUtils::elementToMatrix(connectorElement);
410
initConnectorInfoStructAux(connectorElement, connectorInfo, filename);
411
return connectorInfo;
414
bool FSvgRenderer::initConnectorInfoStructAux(QDomElement & element, ConnectorInfo * connectorInfo, const QString & filename)
416
// right now we only handle circles
417
if (element.nodeName().compare("circle") != 0) {
418
QDomElement child = element.firstChildElement();
419
while (!child.isNull()) {
420
if (initConnectorInfoStructAux(child, connectorInfo, filename)) return true;
422
child = child.nextSiblingElement();
428
element.attribute("cx").toDouble(&ok);
429
if (!ok) return false;
431
element.attribute("cy").toDouble(&ok);
432
if (!ok) return false;
434
double r = element.attribute("r").toDouble(&ok);
435
if (!ok) return false;
437
double sw = element.attribute("stroke-width").toDouble(&ok);
439
QDomElement parent = element.parentNode().toElement();
440
while (!parent.isNull()) {
441
sw = element.attribute("stroke-width").toDouble(&ok);
446
parent = parent.parentNode().toElement();
451
QTextStream stream(&text);
452
element.save(stream, 0);
453
//DebugDialog::debug(QString("no circle stroke width set in %1: %2").arg(filename).arg(text));
454
element.setAttribute("stroke-width", 1);
457
//QString strokewidth("stroke-width");
458
//QString s = element.attribute("style");
459
//SvgFileSplitter::fixStyleAttribute(connectorElement, s, strokewidth);
460
//sw = connectorElement.attribute("stroke-width").toDouble(&ok);
467
QMatrix matrix = TextUtils::elementToMatrix(element);
468
if (!matrix.isIdentity()) {
470
QRectF r2 = matrix.mapRect(r1);
471
if (r2.width() != r1.width()) {
473
sw = sw * r2.width() / r1.width();
477
//DebugDialog::debug("got a circle");
478
connectorInfo->gotCircle = true;
479
//connectorInfo->cbounds.setRect(cx - r - (sw / 2.0), cy - r - (sw / 2.0), (r * 2) + sw, (r * 2) + sw);
480
connectorInfo->radius = r;
481
connectorInfo->strokeWidth = sw;
485
ConnectorInfo * FSvgRenderer::getConnectorInfo(const QString & connectorID) {
486
return m_connectorInfoHash.value(connectorID, &VanillaConnectorInfo);
489
bool FSvgRenderer::setUpConnector(SvgIdLayer * svgIdLayer, bool ignoreTerminalPoint) {
491
if (svgIdLayer == NULL) return false;
493
if (svgIdLayer->m_processed) {
494
// hybrids are not visible in some views
495
return svgIdLayer->m_svgVisible || svgIdLayer->m_hybrid;
498
svgIdLayer->m_processed = true;
500
QString connectorID = svgIdLayer->m_svgId;
502
// boundsOnElement seems to include any matrix on the element itself.
503
// I would swear this wasn't true before Qt4.7, but maybe I am crazy
504
QRectF bounds = this->boundsOnElement(connectorID);
506
if (bounds.isNull() && !svgIdLayer->m_hybrid) { // hybrids can have zero size
507
svgIdLayer->m_svgVisible = false;
508
DebugDialog::debug("renderer::setupconnector: null bounds");
512
QSizeF defaultSizeF = this->defaultSizeF();
513
QRectF viewBox = this->viewBoxF();
515
ConnectorInfo * connectorInfo = getConnectorInfo(connectorID);
518
DebugDialog::debug(QString("connectorid:%1 m11:%2 m12:%3 m21:%4 m22:%5 dx:%6 dy:%7")
520
.arg(connectorInfo->matrix.m11())
521
.arg(connectorInfo->matrix.m12())
522
.arg(connectorInfo->matrix.m21())
523
.arg(connectorInfo->matrix.m22())
524
.arg(connectorInfo->matrix.dx())
525
.arg(connectorInfo->matrix.dy()),
530
/*DebugDialog::debug(QString("identity matrix %11 %1 %2, viewbox: %3 %4 %5 %6, bounds: %7 %8 %9 %10, size: %12 %13").arg(m_modelPart->title()).arg(connectorSharedID())
531
.arg(viewBox.x()).arg(viewBox.y()).arg(viewBox.width()).arg(viewBox.height())
532
.arg(bounds.x()).arg(bounds.y()).arg(bounds.width()).arg(bounds.height())
534
.arg(defaultSizeF.width()).arg(defaultSizeF.height())
538
// some strangeness in the way that svg items and non-svg items map to screen space
539
// might be a qt problem.
540
//QMatrix matrix0 = connectorInfo->matrix * this->matrixForElement(connectorID);
541
//QRectF r1 = matrix0.mapRect(bounds);
543
QMatrix elementMatrix = this->matrixForElement(connectorID);
544
QRectF r1 = elementMatrix.mapRect(bounds);
546
if (connectorInfo && connectorInfo->gotCircle) {
547
QLineF l(0,0,connectorInfo->radius, 0);
548
QLineF lm = elementMatrix.map(l);
549
svgIdLayer->m_radius = lm.length() * defaultSizeF.width() / viewBox.width();
551
QLineF k(0,0,connectorInfo->strokeWidth, 0);
552
QLineF km = elementMatrix.map(l);
553
svgIdLayer->m_strokeWidth = km.length() * defaultSizeF.width() / viewBox.width();
554
//bounds = connectorInfo->cbounds;
559
svgIdLayer->m_rect.setRect(r1.x() * defaultSize.width() / viewBox.width(),
560
r1.y() * defaultSize.height() / viewBox.height(),
561
r1.width() * defaultSize.width() / viewBox.width(),
562
r1.height() * defaultSize.height() / viewBox.height());
564
svgIdLayer->m_rect.setRect(r1.x() * defaultSizeF.width() / viewBox.width(),
565
r1.y() * defaultSizeF.height() / viewBox.height(),
566
r1.width() * defaultSizeF.width() / viewBox.width(),
567
r1.height() * defaultSizeF.height() / viewBox.height());
569
svgIdLayer->m_svgVisible = !bounds.isNull();
570
//if (!svgIdLayer->m_svgVisible) {
571
//DebugDialog::debug("not vis");
573
svgIdLayer->m_point = calcTerminalPoint(svgIdLayer->m_terminalId, svgIdLayer->m_rect, ignoreTerminalPoint, viewBox, connectorInfo->terminalMatrix);
574
calcLeg(svgIdLayer, viewBox, connectorInfo);
579
void FSvgRenderer::calcLeg(SvgIdLayer * svgIdLayer, const QRectF & viewBox, ConnectorInfo * connectorInfo)
581
if (svgIdLayer->m_legId.isEmpty()) return;
583
svgIdLayer->m_legColor = connectorInfo->legColor;
585
QSizeF defaultSizeF = this->defaultSizeF();
586
svgIdLayer->m_legStrokeWidth = connectorInfo->legStrokeWidth * defaultSizeF.width() / viewBox.width();
589
DebugDialog::debug( QString("calcleg leg %1 %2 %3 %4")
590
.arg(connectorInfo->legLine.p1().x())
591
.arg(connectorInfo->legLine.p1().y())
592
.arg(connectorInfo->legLine.p2().x())
593
.arg(connectorInfo->legLine.p2().y())
597
QMatrix matrix = this->matrixForElement(svgIdLayer->m_legId) * connectorInfo->legMatrix;
598
QPointF p1 = matrix.map(connectorInfo->legLine.p1());
599
QPointF p2 = matrix.map(connectorInfo->legLine.p2());
601
double x1 = p1.x() * defaultSizeF.width() / viewBox.width();
602
double y1 = p1.y() * defaultSizeF.height() / viewBox.height();
603
double x2 = p2.x() * defaultSizeF.width() / viewBox.width();
604
double y2 = p2.y() * defaultSizeF.height() / viewBox.height();
605
QPointF center = viewBox.center();
606
double d1 = ((x1 - center.x()) * (x1 - center.x())) + ((y1 - center.y()) * (y1 - center.y()));
607
double d2 = ((x2 - center.x()) * (x2 - center.x())) + ((y2 - center.y()) * (y1 - center.y()));
609
// find the end which is closer to the center of the viewBox (which shouldn't include the leg)
611
svgIdLayer->m_legLine = QLineF(x1, y1, x2, y2);
614
svgIdLayer->m_legLine = QLineF(x2, y2, x1, y1);
618
QPointF FSvgRenderer::calcTerminalPoint(const QString & terminalId, const QRectF & connectorRect, bool ignoreTerminalPoint, const QRectF & viewBox, QMatrix & terminalMatrix)
620
Q_UNUSED(terminalMatrix);
621
QPointF terminalPoint = connectorRect.center() - connectorRect.topLeft(); // default spot is centered
622
if (ignoreTerminalPoint) {
623
return terminalPoint;
625
if (terminalId.isNull() || terminalId.isEmpty()) {
626
return terminalPoint;
629
QRectF tBounds = this->boundsOnElement(terminalId);
630
if (tBounds.isNull()) {
631
return terminalPoint;
634
QSizeF defaultSizeF = this->defaultSizeF();
635
if (tBounds.width() >= defaultSizeF.width() && tBounds.height() >= defaultSizeF.height()) {
636
return terminalPoint;
639
//DebugDialog::debug( QString("terminal %5 rect %1,%2,%3,%4").arg(tBounds.x()).
641
//arg(tBounds.width()).
642
//arg(tBounds.height()).
646
// matrixForElement only grabs parent matrices, not any transforms in the element itself
647
//QMatrix tMatrix = this->matrixForElement(terminalId) ; //* terminalMatrix;
648
//QRectF terminalRect = tMatrix.mapRect(tBounds);
649
QRectF terminalRect = this->matrixForElement(terminalId).mapRect(tBounds);
650
QPointF c = terminalRect.center();
651
QPointF q(c.x() * defaultSizeF.width() / viewBox.width(), c.y() * defaultSizeF.height() / viewBox.height());
652
terminalPoint = q - connectorRect.topLeft();
653
//DebugDialog::debug( QString("terminalagain %3 rect %1,%2 ").arg(terminalPoint.x()).
654
//arg(terminalPoint.y()).
657
return terminalPoint;
660
QList<SvgIdLayer *> FSvgRenderer::setUpNonConnectors() {
662
QList<SvgIdLayer *> list;
663
if (m_nonConnectorInfoHash.count() == 0) return list;
665
foreach (QString nonConnectorID, m_nonConnectorInfoHash.keys()) {
666
SvgIdLayer * svgIdLayer = new SvgIdLayer(ViewLayer::PCBView);
667
svgIdLayer->m_processed = true;
668
svgIdLayer->m_svgId = nonConnectorID;
669
QRectF bounds = this->boundsOnElement(nonConnectorID);
670
if (bounds.isNull()) {
675
QSizeF defaultSizeF = this->defaultSizeF();
676
QSize defaultSize = this->defaultSize();
677
if ((bounds.width()) == defaultSizeF.width() && (bounds.height()) == defaultSizeF.height()) {
682
QRectF viewBox = this->viewBoxF();
684
ConnectorInfo * connectorInfo = m_nonConnectorInfoHash.value(nonConnectorID, NULL);
685
if (connectorInfo && connectorInfo->gotCircle) {
686
svgIdLayer->m_radius = connectorInfo->radius * defaultSizeF.width() / viewBox.width();
687
svgIdLayer->m_strokeWidth = connectorInfo->strokeWidth * defaultSizeF.width() / viewBox.width();
688
//bounds = connectorInfo->cbounds;
691
// matrixForElement only grabs parent matrices, not any transforms in the element itself
692
//QMatrix matrix0 = connectorInfo->matrix * this->matrixForElement(nonConnectorID);
693
//QRectF r1 = matrix0.mapRect(bounds);
694
QRectF r1 = this->matrixForElement(nonConnectorID).mapRect(bounds);
695
svgIdLayer->m_rect.setRect(r1.x() * defaultSize.width() / viewBox.width(), r1.y() * defaultSize.height() / viewBox.height(), r1.width() * defaultSize.width() / viewBox.width(), r1.height() * defaultSize.height() / viewBox.height());
696
svgIdLayer->m_point = svgIdLayer->m_rect.center() - svgIdLayer->m_rect.topLeft();
697
svgIdLayer->m_svgVisible = true;
699
list.append(svgIdLayer);