~ubuntu-branches/ubuntu/trusty/fritzing/trusty-proposed

« back to all changes in this revision

Viewing changes to src/fsvgrenderer.cpp

  • Committer: Package Import Robot
  • Author(s): Enrique Hernández Bello
  • Date: 2012-11-11 21:38:56 UTC
  • mfrom: (1.1.5)
  • Revision ID: package-import@ubuntu.com-20121111213856-0825ywdrtdcshl91
Tags: 0.7.10b-1
* New upstream version. Closes: #661495, #692998
* Removed useless patches.
* Removed SetupAPI.lib from sourceless files.
* Skip dfsg tarball creation if there are no sourceless files.
* Added libqt4-sql-sqlite to dependencies. Thanks to Tom Hummel <tom@bluespice.org>.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*******************************************************************
2
 
 
3
 
Part of the Fritzing project - http://fritzing.org
4
 
Copyright (c) 2007-2012 Fachhochschule Potsdam - http://fh-potsdam.de
5
 
 
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.
10
 
 
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.
15
 
 
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/>.
18
 
 
19
 
********************************************************************
20
 
 
21
 
$Revision: 6245 $:
22
 
$Author: cohen@irascible.com $:
23
 
$Date: 2012-08-04 08:42:55 +0200 (Sat, 04 Aug 2012) $
24
 
 
25
 
********************************************************************/
26
 
 
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"
33
 
 
34
 
#include <QRegExp>
35
 
#include <QTextStream>
36
 
#include <QPainter>
37
 
#include <QCoreApplication>
38
 
#include <QGraphicsSvgItem>
39
 
#include <qnumeric.h>
40
 
 
41
 
QString FSvgRenderer::NonConnectorName("nonconn");
42
 
 
43
 
static ConnectorInfo VanillaConnectorInfo;
44
 
 
45
 
FSvgRenderer::FSvgRenderer(QObject * parent) : QSvgRenderer(parent)
46
 
{
47
 
        m_defaultSizeF = QSizeF(0,0);
48
 
}
49
 
 
50
 
FSvgRenderer::~FSvgRenderer()
51
 
{
52
 
        clearConnectorInfoHash(m_connectorInfoHash);
53
 
        clearConnectorInfoHash(m_nonConnectorInfoHash);
54
 
}
55
 
 
56
 
void FSvgRenderer::initNames() {
57
 
    VanillaConnectorInfo.gotCircle = false;     
58
 
}
59
 
 
60
 
void FSvgRenderer::clearConnectorInfoHash(QHash<QString, ConnectorInfo *> & hash) {
61
 
        foreach (ConnectorInfo * connectorInfo, hash.values()) {
62
 
                delete connectorInfo;
63
 
        }
64
 
        hash.clear();
65
 
}
66
 
 
67
 
void FSvgRenderer::cleanup() {
68
 
 
69
 
}
70
 
 
71
 
QByteArray FSvgRenderer::loadSvg(const QString & filename) {
72
 
        QStringList strings;
73
 
        QString string;
74
 
        return loadSvg(filename, strings, strings, strings, string, string, false);
75
 
}
76
 
 
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()) {
79
 
                return QByteArray();
80
 
        }
81
 
 
82
 
    QFile file(filename);
83
 
    if (!file.open(QFile::ReadOnly | QFile::Text)) {
84
 
                return QByteArray();
85
 
        }
86
 
 
87
 
        QByteArray contents = file.readAll();
88
 
        file.close();
89
 
 
90
 
        if (contents.length() <= 0) return QByteArray();
91
 
 
92
 
        return loadAux(contents, filename, connectorIDs, terminalIDs, legIDs, setColor, colorElementID, findNonConnectors);
93
 
 
94
 
}
95
 
 
96
 
bool FSvgRenderer::loadSvgString(const QString & svg) {
97
 
        QByteArray byteArray(svg.toUtf8());
98
 
        QByteArray result = loadSvg(byteArray, "", true);
99
 
    return (!result.isEmpty());
100
 
}
101
 
 
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());
107
 
}
108
 
 
109
 
QByteArray FSvgRenderer::loadSvg(const QByteArray & contents, const QString & filename, bool findNonConnectors) {
110
 
        QStringList strings;
111
 
        QString string;
112
 
        return loadSvg(contents, filename, strings, strings, strings, string, string, findNonConnectors);
113
 
}
114
 
 
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);
117
 
}
118
 
 
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) {
120
 
 
121
 
        QByteArray cleanContents(theContents);
122
 
        bool cleaned = false;
123
 
        if (TextUtils::isIllustratorFile(cleanContents)) {
124
 
                QString string(cleanContents);
125
 
 
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);
130
 
#ifndef QT_NO_DEBUG
131
 
                        if (!filename.contains("icon", Qt::CaseInsensitive)) {
132
 
                                DebugDialog::debug("Illustrator and inkscape:" + filename);
133
 
                        }
134
 
#endif
135
 
                }
136
 
 
137
 
                //DebugDialog::debug("Illustrator " + filename);
138
 
                if (TextUtils::fixPixelDimensionsIn(string)) {
139
 
                        cleaned = true;
140
 
                        cleanContents = string.toUtf8();
141
 
                }
142
 
        }
143
 
        
144
 
        if (cleanContents.contains("sodipodi") || cleanContents.contains("inkscape")) {
145
 
                //DebugDialog::debug("inkscape " + filename);
146
 
        }
147
 
 
148
 
        if (cleanContents.contains("<tspan")) {
149
 
                QString string(cleanContents);
150
 
                TextUtils::tspanRemove(string);
151
 
                cleanContents = string.toUtf8();
152
 
                cleaned = true;
153
 
        }
154
 
 
155
 
        if (cleanContents.contains("<use")) {
156
 
                QString string(cleanContents);
157
 
                TextUtils::noUse(string);
158
 
                cleanContents = string.toUtf8();
159
 
                cleaned = true;
160
 
        }
161
 
 
162
 
        if (cleanContents.contains("<pattern")) {
163
 
                QString string(cleanContents);
164
 
                TextUtils::noPattern(string);
165
 
                cleanContents = string.toUtf8();
166
 
                cleaned = true;
167
 
        }
168
 
 
169
 
        // no it isn't
170
 
 
171
 
        if (connectorIDs.count() > 0 || !setColor.isEmpty() || findNonConnectors) {
172
 
                QString errorStr;
173
 
                int errorLine;
174
 
                int errorColumn;
175
 
                QDomDocument doc;
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));
178
 
                }
179
 
 
180
 
                bool resetContents = false;
181
 
 
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;
190
 
                        }
191
 
                }
192
 
                if (connectorIDs.count() > 0) {
193
 
                        bool init =  initConnectorInfo(doc, connectorIDs, terminalIDs, legIDs, filename);
194
 
                        resetContents = resetContents || init;
195
 
                }
196
 
                if (findNonConnectors) {
197
 
                        initNonConnectorInfo(doc, filename);
198
 
                }
199
 
 
200
 
                if (resetContents) {
201
 
                        cleanContents = TextUtils::removeXMLEntities(doc.toString()).toUtf8();
202
 
                }
203
 
        }
204
 
 
205
 
 
206
 
        /*
207
 
        cleanContents = doc.toByteArray();
208
 
 
209
 
        //QFile file("all.txt");
210
 
        //if (file.open(QIODevice::Append)) {
211
 
                //QTextStream t(&file);
212
 
                //t << cleanContents;
213
 
                //file.close();
214
 
        //}
215
 
 
216
 
        */
217
 
 
218
 
        //DebugDialog::debug(cleanContents.data());
219
 
 
220
 
        QXmlStreamReader xml(cleanContents);
221
 
        bool result = determineDefaultSize(xml);
222
 
        if (!result) {
223
 
                return QByteArray();
224
 
        }
225
 
 
226
 
        result = QSvgRenderer::load(cleanContents);
227
 
        if (result) {
228
 
                m_filename = filename;
229
 
                return cleanContents;
230
 
        }
231
 
 
232
 
        return QByteArray();
233
 
}
234
 
 
235
 
bool FSvgRenderer::fastLoad(const QByteArray & contents) {
236
 
        return QSvgRenderer::load(contents);
237
 
}
238
 
 
239
 
const QString & FSvgRenderer::filename() {
240
 
        return m_filename;
241
 
}
242
 
 
243
 
QPixmap * FSvgRenderer::getPixmap(QSvgRenderer * renderer, QSize size) 
244
 
{
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);
251
 
    if (frenderer) {
252
 
        def = frenderer->defaultSizeF();
253
 
    }
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();
259
 
        }
260
 
        QRectF bounds((size.width() - newW) / 2.0, (size.height() - newH) / 2.0, newW, newH);
261
 
        renderer->render(&painter, bounds);
262
 
        painter.end();
263
 
 
264
 
    return pixmap;
265
 
}
266
 
 
267
 
bool FSvgRenderer::determineDefaultSize(QXmlStreamReader & xml)
268
 
{
269
 
        QSizeF size = parseForWidthAndHeight(xml);
270
 
 
271
 
        m_defaultSizeF = QSizeF(size.width() * GraphicsUtils::SVGDPI, size.height() * GraphicsUtils::SVGDPI);
272
 
        return (size.width() != 0 && size.height() != 0);
273
 
}
274
 
 
275
 
QSizeF FSvgRenderer::parseForWidthAndHeight(QXmlStreamReader & xml)
276
 
{
277
 
    QSizeF size = TextUtils::parseForWidthAndHeight(xml);
278
 
    if (size.width() != 0 && size.height() != 0) return size;
279
 
 
280
 
        QIODevice * device = xml.device();
281
 
        DebugDialog::debug("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
282
 
        DebugDialog::debug("bad width and/or bad height in svg:");
283
 
        if (device) {
284
 
                device->reset();
285
 
                QString string(device->readAll());
286
 
                DebugDialog::debug(string);
287
 
        }
288
 
        DebugDialog::debug("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
289
 
 
290
 
        return size;
291
 
}
292
 
 
293
 
QSizeF FSvgRenderer::defaultSizeF() {
294
 
        if (m_defaultSizeF.width() == 0 && m_defaultSizeF.height() == 0) {
295
 
                return defaultSize();
296
 
        }
297
 
 
298
 
        return m_defaultSizeF;
299
 
}
300
 
 
301
 
 
302
 
void FSvgRenderer::initNonConnectorInfo(QDomDocument & domDocument, const QString & filename)
303
 
{
304
 
        clearConnectorInfoHash(m_nonConnectorInfoHash);
305
 
        QDomElement root = domDocument.documentElement();
306
 
        initNonConnectorInfoAux(root, filename);
307
 
}
308
 
 
309
 
void FSvgRenderer::initNonConnectorInfoAux(QDomElement & element, const QString & filename)
310
 
{
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);
315
 
        }
316
 
        QDomElement child = element.firstChildElement();
317
 
        while (!child.isNull()) {
318
 
                initNonConnectorInfoAux(child, filename);
319
 
                child = child.nextSiblingElement();
320
 
        }
321
 
}
322
 
 
323
 
bool FSvgRenderer::initConnectorInfo(QDomDocument & domDocument, const QStringList & connectorIDs, const QStringList & terminalIDs, const QStringList & legIDs, const QString & filename)
324
 
{
325
 
        bool result = false;
326
 
        clearConnectorInfoHash(m_connectorInfoHash);
327
 
        QDomElement root = domDocument.documentElement();
328
 
        initConnectorInfoAux(root, connectorIDs, filename);
329
 
        if (terminalIDs.count() > 0) {
330
 
                initTerminalInfoAux(root, connectorIDs, terminalIDs);
331
 
        }
332
 
        if (legIDs.count() > 0) {
333
 
                initLegInfoAux(root, connectorIDs, legIDs, result);
334
 
        }
335
 
 
336
 
        return result;
337
 
}
338
 
 
339
 
void FSvgRenderer::initLegInfoAux(QDomElement & element, const QStringList & connectorIDs, const QStringList & legIDs, bool & gotOne)
340
 
{
341
 
        QString id = element.attribute("id");
342
 
        if (!id.isEmpty()) {
343
 
                int ix = legIDs.indexOf(id);
344
 
                if (ix >= 0) {
345
 
                        //DebugDialog::debug("init leg info " + id);
346
 
                        //foreach (QString lid, legIDs) {
347
 
                        //      DebugDialog::debug("\tleg id:" + lid);
348
 
                        //}
349
 
 
350
 
                        element.setTagName("g");                        // don't want this element to actually be drawn
351
 
                        gotOne = true;
352
 
                        ConnectorInfo * connectorInfo = m_connectorInfoHash.value(connectorIDs.at(ix), NULL);
353
 
                        if (connectorInfo) {
354
 
                                //QString temp;
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);
363
 
                        }
364
 
                        // don't return here, might miss other legs
365
 
                }
366
 
        }
367
 
 
368
 
        QDomElement child = element.firstChildElement();
369
 
        while (!child.isNull()) {
370
 
                initLegInfoAux(child, connectorIDs, legIDs, gotOne);
371
 
                child = child.nextSiblingElement();
372
 
        }
373
 
}
374
 
 
375
 
bool FSvgRenderer::initLegInfoAux(QDomElement & element, ConnectorInfo * connectorInfo) 
376
 
{
377
 
        bool ok;
378
 
        double sw = element.attribute("stroke-width").toDouble(&ok);
379
 
        if (!ok) return false;
380
 
 
381
 
        double x1 = element.attribute("x1").toDouble(&ok);
382
 
        if (!ok) return false;
383
 
 
384
 
        double y1 = element.attribute("y1").toDouble(&ok);
385
 
        if (!ok) return false;
386
 
 
387
 
        double x2 = element.attribute("x2").toDouble(&ok);
388
 
        if (!ok) return false;
389
 
 
390
 
        double y2 = element.attribute("y2").toDouble(&ok);
391
 
        if (!ok) return false;
392
 
 
393
 
        connectorInfo->legStrokeWidth = sw;
394
 
        connectorInfo->legLine = QLineF(x1, y1, x2, y2);
395
 
        return true;
396
 
}
397
 
 
398
 
void FSvgRenderer::initTerminalInfoAux(QDomElement & element, const QStringList & connectorIDs, const QStringList & terminalIDs)
399
 
{
400
 
        QString id = element.attribute("id");
401
 
        if (!id.isEmpty()) {
402
 
                int ix = terminalIDs.indexOf(id);
403
 
                if (ix >= 0) {
404
 
                        ConnectorInfo * connectorInfo = m_connectorInfoHash.value(connectorIDs.at(ix), NULL);
405
 
                        if (connectorInfo) {
406
 
                                connectorInfo->terminalMatrix = TextUtils::elementToMatrix(element);
407
 
                        }
408
 
                        // don't return here, might miss other terminal ids
409
 
                }
410
 
        }
411
 
 
412
 
        QDomElement child = element.firstChildElement();
413
 
        while (!child.isNull()) {
414
 
                initTerminalInfoAux(child, connectorIDs, terminalIDs);
415
 
                child = child.nextSiblingElement();
416
 
        }
417
 
}
418
 
 
419
 
void FSvgRenderer::initConnectorInfoAux(QDomElement & element, const QStringList & connectorIDs, const QString & filename)
420
 
{
421
 
        QString id = element.attribute("id");
422
 
        if (!id.isEmpty()) {
423
 
                if (connectorIDs.contains(id)) {
424
 
                        ConnectorInfo * connectorInfo = initConnectorInfoStruct(element, filename);
425
 
                        m_connectorInfoHash.insert(id, connectorInfo);
426
 
                }
427
 
                // don't return here, might miss other connectors
428
 
        }
429
 
 
430
 
        QDomElement child = element.firstChildElement();
431
 
        while (!child.isNull()) {
432
 
                initConnectorInfoAux(child, connectorIDs, filename);
433
 
                child = child.nextSiblingElement();
434
 
        }
435
 
}
436
 
 
437
 
ConnectorInfo * FSvgRenderer::initConnectorInfoStruct(QDomElement & connectorElement, const QString & filename) {
438
 
        ConnectorInfo * connectorInfo = new ConnectorInfo();
439
 
        connectorInfo->radius = connectorInfo->strokeWidth = 0;
440
 
        connectorInfo->gotCircle = false;
441
 
 
442
 
        if (connectorElement.isNull()) return connectorInfo;
443
 
 
444
 
        connectorInfo->matrix = TextUtils::elementToMatrix(connectorElement);
445
 
        initConnectorInfoStructAux(connectorElement, connectorInfo, filename);
446
 
        return connectorInfo;
447
 
}
448
 
 
449
 
bool FSvgRenderer::initConnectorInfoStructAux(QDomElement & element, ConnectorInfo * connectorInfo, const QString & filename) 
450
 
{
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;
456
 
 
457
 
                        child = child.nextSiblingElement();
458
 
                }
459
 
                return false;
460
 
        }
461
 
 
462
 
        bool ok;
463
 
        element.attribute("cx").toDouble(&ok);
464
 
        if (!ok) return false;
465
 
 
466
 
        element.attribute("cy").toDouble(&ok);
467
 
        if (!ok) return false;
468
 
 
469
 
        double r = element.attribute("r").toDouble(&ok);
470
 
        if (!ok) return false;
471
 
 
472
 
        double sw = element.attribute("stroke-width").toDouble(&ok);    
473
 
        if (!ok) {
474
 
        QDomElement parent = element.parentNode().toElement();
475
 
        while (!parent.isNull()) {
476
 
            sw = element.attribute("stroke-width").toDouble(&ok);
477
 
            if (ok) {
478
 
                break;
479
 
            }
480
 
 
481
 
            parent = parent.parentNode().toElement();
482
 
        }
483
 
 
484
 
        if (!ok) {
485
 
            QString text;
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);
490
 
            sw = 1;
491
 
 
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);
496
 
                    //if (!ok) {
497
 
                            //return false;
498
 
                    //}
499
 
        }
500
 
        }
501
 
 
502
 
        QMatrix matrix = TextUtils::elementToMatrix(element);
503
 
        if (!matrix.isIdentity()) {
504
 
                QRectF r1(0,0,r,r);
505
 
                QRectF r2 = matrix.mapRect(r1);
506
 
                if (r2.width() != r1.width()) {
507
 
                        r = r2.width();
508
 
                        sw = sw * r2.width() / r1.width();
509
 
                }
510
 
        }
511
 
 
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;
517
 
        return true;
518
 
}
519
 
 
520
 
ConnectorInfo * FSvgRenderer::getConnectorInfo(const QString & connectorID) {
521
 
        return m_connectorInfoHash.value(connectorID, &VanillaConnectorInfo);
522
 
}
523
 
 
524
 
bool FSvgRenderer::setUpConnector(SvgIdLayer * svgIdLayer, bool ignoreTerminalPoint) {
525
 
 
526
 
        if (svgIdLayer == NULL) return false;
527
 
 
528
 
        if (svgIdLayer->m_processed) {
529
 
                // hybrids are not visible in some views
530
 
                return svgIdLayer->m_svgVisible || svgIdLayer->m_hybrid;
531
 
        }
532
 
 
533
 
        svgIdLayer->m_processed = true;
534
 
 
535
 
        QString connectorID = svgIdLayer->m_svgId;
536
 
 
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);     
540
 
 
541
 
        if (bounds.isNull() && !svgIdLayer->m_hybrid) {         // hybrids can have zero size
542
 
                svgIdLayer->m_svgVisible = false;               
543
 
                DebugDialog::debug("renderer::setupconnector: null bounds");
544
 
                return false;
545
 
        }
546
 
 
547
 
        QSizeF defaultSizeF = this->defaultSizeF();
548
 
        QRectF viewBox = this->viewBoxF();
549
 
 
550
 
        ConnectorInfo * connectorInfo = getConnectorInfo(connectorID);  
551
 
 
552
 
        /*
553
 
        DebugDialog::debug(QString("connectorid:%1 m11:%2 m12:%3 m21:%4 m22:%5 dx:%6 dy:%7")
554
 
                                                .arg(connectorID)
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()),
561
 
                                bounds);
562
 
        */
563
 
 
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;
568
 
        }
569
 
 
570
 
        
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())
574
 
                                           .arg(viewIdentifier)
575
 
                                           .arg(defaultSizeF.width()).arg(defaultSizeF.height())
576
 
        );
577
 
        */
578
 
 
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);
584
 
 
585
 
        /*
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());
590
 
        */
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());
595
 
 
596
 
        svgIdLayer->m_svgVisible = !bounds.isNull();
597
 
        //if (!svgIdLayer->m_svgVisible) {
598
 
                //DebugDialog::debug("not vis");
599
 
        //}
600
 
        svgIdLayer->m_point = calcTerminalPoint(svgIdLayer->m_terminalId, svgIdLayer->m_rect, ignoreTerminalPoint, viewBox, connectorInfo->terminalMatrix);
601
 
        calcLeg(svgIdLayer, viewBox, connectorInfo);
602
 
        
603
 
        return true;
604
 
}
605
 
 
606
 
void FSvgRenderer::calcLeg(SvgIdLayer * svgIdLayer, const QRectF & viewBox, ConnectorInfo * connectorInfo)
607
 
{
608
 
        if (svgIdLayer->m_legId.isEmpty()) return;
609
 
 
610
 
        svgIdLayer->m_legColor = connectorInfo->legColor;
611
 
 
612
 
        QSizeF defaultSizeF = this->defaultSizeF();
613
 
        svgIdLayer->m_legStrokeWidth = connectorInfo->legStrokeWidth * defaultSizeF.width() / viewBox.width();
614
 
 
615
 
        /*
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())
621
 
                );
622
 
        */
623
 
 
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());
627
 
 
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()));
635
 
 
636
 
        // find the end which is closer to the center of the viewBox (which shouldn't include the leg)
637
 
        if (d1 <= d2) {
638
 
                svgIdLayer->m_legLine = QLineF(x1, y1, x2, y2);
639
 
        }
640
 
        else {
641
 
                svgIdLayer->m_legLine = QLineF(x2, y2, x1, y1);
642
 
        }
643
 
}
644
 
 
645
 
QPointF FSvgRenderer::calcTerminalPoint(const QString & terminalId, const QRectF & connectorRect, bool ignoreTerminalPoint, const QRectF & viewBox, QMatrix & terminalMatrix)
646
 
{
647
 
        Q_UNUSED(terminalMatrix);
648
 
        QPointF terminalPoint = connectorRect.center() - connectorRect.topLeft();    // default spot is centered
649
 
        if (ignoreTerminalPoint) {
650
 
                return terminalPoint;
651
 
        }
652
 
        if (terminalId.isNull() || terminalId.isEmpty()) {
653
 
                return terminalPoint;
654
 
        }
655
 
 
656
 
        QRectF tBounds = this->boundsOnElement(terminalId);
657
 
        if (tBounds.isNull()) {
658
 
                return terminalPoint;
659
 
        }
660
 
 
661
 
        QSizeF defaultSizeF = this->defaultSizeF();
662
 
        if (tBounds.width() >= defaultSizeF.width() && tBounds.height() >= defaultSizeF.height()) {
663
 
                return terminalPoint;
664
 
        }
665
 
 
666
 
        //DebugDialog::debug(   QString("terminal %5 rect %1,%2,%3,%4").arg(tBounds.x()).
667
 
                                                                                //arg(tBounds.y()).
668
 
                                                                                //arg(tBounds.width()).
669
 
                                                                                //arg(tBounds.height()).
670
 
                                                                                //arg(terminalID) );
671
 
 
672
 
 
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()).
682
 
                                                                                //arg(terminalID) );
683
 
 
684
 
        return terminalPoint;
685
 
}
686
 
 
687
 
QList<SvgIdLayer *> FSvgRenderer::setUpNonConnectors() {
688
 
 
689
 
        QList<SvgIdLayer *> list;
690
 
        if (m_nonConnectorInfoHash.count() == 0) return list;
691
 
 
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()) {
698
 
                        delete svgIdLayer;
699
 
                        continue;
700
 
                }
701
 
 
702
 
                QSizeF defaultSizeF = this->defaultSizeF();
703
 
                QSize defaultSize = this->defaultSize();
704
 
                if ((bounds.width()) == defaultSizeF.width() && (bounds.height()) == defaultSizeF.height()) {
705
 
                        delete svgIdLayer;
706
 
                        continue;
707
 
                }
708
 
 
709
 
                QRectF viewBox = this->viewBoxF();
710
 
 
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;
716
 
                }
717
 
 
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;
725
 
 
726
 
                list.append(svgIdLayer);
727
 
        }
728
 
 
729
 
        return list;
730
 
 
731
 
}
732
 
 
733
 
 
 
1
/*******************************************************************
 
2
 
 
3
Part of the Fritzing project - http://fritzing.org
 
4
Copyright (c) 2007-2012 Fachhochschule Potsdam - http://fh-potsdam.de
 
5
 
 
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.
 
10
 
 
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.
 
15
 
 
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/>.
 
18
 
 
19
********************************************************************
 
20
 
 
21
$Revision: 6561 $:
 
22
$Author: irascibl@gmail.com $:
 
23
$Date: 2012-10-12 22:39:11 +0200 (Fri, 12 Oct 2012) $
 
24
 
 
25
********************************************************************/
 
26
 
 
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"
 
33
 
 
34
#include <QRegExp>
 
35
#include <QTextStream>
 
36
#include <QPainter>
 
37
#include <QCoreApplication>
 
38
#include <QGraphicsSvgItem>
 
39
#include <qnumeric.h>
 
40
 
 
41
QString FSvgRenderer::NonConnectorName("nonconn");
 
42
 
 
43
static ConnectorInfo VanillaConnectorInfo;
 
44
 
 
45
FSvgRenderer::FSvgRenderer(QObject * parent) : QSvgRenderer(parent)
 
46
{
 
47
        m_defaultSizeF = QSizeF(0,0);
 
48
}
 
49
 
 
50
FSvgRenderer::~FSvgRenderer()
 
51
{
 
52
        clearConnectorInfoHash(m_connectorInfoHash);
 
53
        clearConnectorInfoHash(m_nonConnectorInfoHash);
 
54
}
 
55
 
 
56
void FSvgRenderer::initNames() {
 
57
    VanillaConnectorInfo.gotCircle = false;     
 
58
}
 
59
 
 
60
void FSvgRenderer::clearConnectorInfoHash(QHash<QString, ConnectorInfo *> & hash) {
 
61
        foreach (ConnectorInfo * connectorInfo, hash.values()) {
 
62
                delete connectorInfo;
 
63
        }
 
64
        hash.clear();
 
65
}
 
66
 
 
67
void FSvgRenderer::cleanup() {
 
68
 
 
69
}
 
70
 
 
71
QByteArray FSvgRenderer::loadSvg(const QString & filename) {
 
72
        QStringList strings;
 
73
        QString string;
 
74
        return loadSvg(filename, strings, strings, strings, string, string, false);
 
75
}
 
76
 
 
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()) {
 
79
                return QByteArray();
 
80
        }
 
81
 
 
82
    QFile file(filename);
 
83
    if (!file.open(QFile::ReadOnly | QFile::Text)) {
 
84
                return QByteArray();
 
85
        }
 
86
 
 
87
        QByteArray contents = file.readAll();
 
88
        file.close();
 
89
 
 
90
        if (contents.length() <= 0) return QByteArray();
 
91
 
 
92
        return loadAux(contents, filename, connectorIDs, terminalIDs, legIDs, setColor, colorElementID, findNonConnectors);
 
93
 
 
94
}
 
95
 
 
96
bool FSvgRenderer::loadSvgString(const QString & svg) {
 
97
        QByteArray byteArray(svg.toUtf8());
 
98
        QByteArray result = loadSvg(byteArray, "", true);
 
99
    return (!result.isEmpty());
 
100
}
 
101
 
 
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());
 
107
}
 
108
 
 
109
QByteArray FSvgRenderer::loadSvg(const QByteArray & contents, const QString & filename, bool findNonConnectors) {
 
110
        QStringList strings;
 
111
        QString string;
 
112
        return loadSvg(contents, filename, strings, strings, strings, string, string, findNonConnectors);
 
113
}
 
114
 
 
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);
 
117
}
 
118
 
 
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) 
 
120
{
 
121
        QByteArray cleanContents(theContents);
 
122
        bool cleaned = false;
 
123
 
 
124
    QString string(cleanContents);
 
125
    if (TextUtils::fixMuch(string, false)) {
 
126
                cleaned = true;
 
127
        }
 
128
        if (TextUtils::fixPixelDimensionsIn(string)) {
 
129
        cleaned = true;
 
130
    }
 
131
    if (cleaned) {
 
132
        cleanContents = string.toUtf8();
 
133
    }
 
134
 
 
135
        if (connectorIDs.count() > 0 || !setColor.isEmpty() || findNonConnectors) {
 
136
                QString errorStr;
 
137
                int errorLine;
 
138
                int errorColumn;
 
139
                QDomDocument doc;
 
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));
 
142
                }
 
143
 
 
144
                bool resetContents = false;
 
145
 
 
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;
 
154
                        }
 
155
                }
 
156
                if (connectorIDs.count() > 0) {
 
157
                        bool init =  initConnectorInfo(doc, connectorIDs, terminalIDs, legIDs, filename);
 
158
                        resetContents = resetContents || init;
 
159
                }
 
160
                if (findNonConnectors) {
 
161
                        initNonConnectorInfo(doc, filename);
 
162
                }
 
163
 
 
164
                if (resetContents) {
 
165
                        cleanContents = TextUtils::removeXMLEntities(doc.toString()).toUtf8();
 
166
                }
 
167
        }
 
168
 
 
169
 
 
170
        /*
 
171
        cleanContents = doc.toByteArray();
 
172
 
 
173
        //QFile file("all.txt");
 
174
        //if (file.open(QIODevice::Append)) {
 
175
                //QTextStream t(&file);
 
176
                //t << cleanContents;
 
177
                //file.close();
 
178
        //}
 
179
 
 
180
        */
 
181
 
 
182
        //DebugDialog::debug(cleanContents.data());
 
183
 
 
184
        QXmlStreamReader xml(cleanContents);
 
185
        bool result = determineDefaultSize(xml);
 
186
        if (!result) {
 
187
                return QByteArray();
 
188
        }
 
189
 
 
190
        result = QSvgRenderer::load(cleanContents);
 
191
        if (result) {
 
192
                m_filename = filename;
 
193
                return cleanContents;
 
194
        }
 
195
 
 
196
        return QByteArray();
 
197
}
 
198
 
 
199
bool FSvgRenderer::fastLoad(const QByteArray & contents) {
 
200
        return QSvgRenderer::load(contents);
 
201
}
 
202
 
 
203
const QString & FSvgRenderer::filename() {
 
204
        return m_filename;
 
205
}
 
206
 
 
207
QPixmap * FSvgRenderer::getPixmap(QSvgRenderer * renderer, QSize size) 
 
208
{
 
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);
 
215
    if (frenderer) {
 
216
        def = frenderer->defaultSizeF();
 
217
    }
 
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();
 
223
        }
 
224
        QRectF bounds((size.width() - newW) / 2.0, (size.height() - newH) / 2.0, newW, newH);
 
225
        renderer->render(&painter, bounds);
 
226
        painter.end();
 
227
 
 
228
    return pixmap;
 
229
}
 
230
 
 
231
bool FSvgRenderer::determineDefaultSize(QXmlStreamReader & xml)
 
232
{
 
233
        QSizeF size = parseForWidthAndHeight(xml);
 
234
 
 
235
        m_defaultSizeF = QSizeF(size.width() * GraphicsUtils::SVGDPI, size.height() * GraphicsUtils::SVGDPI);
 
236
        return (size.width() != 0 && size.height() != 0);
 
237
}
 
238
 
 
239
QSizeF FSvgRenderer::parseForWidthAndHeight(QXmlStreamReader & xml)
 
240
{
 
241
        QRectF viewBox;
 
242
    QSizeF size = TextUtils::parseForWidthAndHeight(xml, viewBox);
 
243
    if (size.width() != 0 && size.height() != 0) return size;
 
244
 
 
245
        QIODevice * device = xml.device();
 
246
        DebugDialog::debug("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
 
247
        DebugDialog::debug("bad width and/or bad height in svg:");
 
248
        if (device) {
 
249
                device->reset();
 
250
                QString string(device->readAll());
 
251
                DebugDialog::debug(string);
 
252
        }
 
253
        DebugDialog::debug("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
 
254
 
 
255
        return size;
 
256
}
 
257
 
 
258
QSizeF FSvgRenderer::defaultSizeF() {
 
259
        if (m_defaultSizeF.width() == 0 && m_defaultSizeF.height() == 0) {
 
260
                return defaultSize();
 
261
        }
 
262
 
 
263
        return m_defaultSizeF;
 
264
}
 
265
 
 
266
 
 
267
void FSvgRenderer::initNonConnectorInfo(QDomDocument & domDocument, const QString & filename)
 
268
{
 
269
        clearConnectorInfoHash(m_nonConnectorInfoHash);
 
270
        QDomElement root = domDocument.documentElement();
 
271
        initNonConnectorInfoAux(root, filename);
 
272
}
 
273
 
 
274
void FSvgRenderer::initNonConnectorInfoAux(QDomElement & element, const QString & filename)
 
275
{
 
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);
 
280
        }
 
281
        QDomElement child = element.firstChildElement();
 
282
        while (!child.isNull()) {
 
283
                initNonConnectorInfoAux(child, filename);
 
284
                child = child.nextSiblingElement();
 
285
        }
 
286
}
 
287
 
 
288
bool FSvgRenderer::initConnectorInfo(QDomDocument & domDocument, const QStringList & connectorIDs, const QStringList & terminalIDs, const QStringList & legIDs, const QString & filename)
 
289
{
 
290
        bool result = false;
 
291
        clearConnectorInfoHash(m_connectorInfoHash);
 
292
        QDomElement root = domDocument.documentElement();
 
293
        initConnectorInfoAux(root, connectorIDs, filename);
 
294
        if (terminalIDs.count() > 0) {
 
295
                initTerminalInfoAux(root, connectorIDs, terminalIDs);
 
296
        }
 
297
        if (legIDs.count() > 0) {
 
298
                initLegInfoAux(root, connectorIDs, legIDs, result);
 
299
        }
 
300
 
 
301
        return result;
 
302
}
 
303
 
 
304
void FSvgRenderer::initLegInfoAux(QDomElement & element, const QStringList & connectorIDs, const QStringList & legIDs, bool & gotOne)
 
305
{
 
306
        QString id = element.attribute("id");
 
307
        if (!id.isEmpty()) {
 
308
                int ix = legIDs.indexOf(id);
 
309
                if (ix >= 0) {
 
310
                        //DebugDialog::debug("init leg info " + id);
 
311
                        //foreach (QString lid, legIDs) {
 
312
                        //      DebugDialog::debug("\tleg id:" + lid);
 
313
                        //}
 
314
 
 
315
                        element.setTagName("g");                        // don't want this element to actually be drawn
 
316
                        gotOne = true;
 
317
                        ConnectorInfo * connectorInfo = m_connectorInfoHash.value(connectorIDs.at(ix), NULL);
 
318
                        if (connectorInfo) {
 
319
                                //QString temp;
 
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);
 
328
                        }
 
329
                        // don't return here, might miss other legs
 
330
                }
 
331
        }
 
332
 
 
333
        QDomElement child = element.firstChildElement();
 
334
        while (!child.isNull()) {
 
335
                initLegInfoAux(child, connectorIDs, legIDs, gotOne);
 
336
                child = child.nextSiblingElement();
 
337
        }
 
338
}
 
339
 
 
340
bool FSvgRenderer::initLegInfoAux(QDomElement & element, ConnectorInfo * connectorInfo) 
 
341
{
 
342
        bool ok;
 
343
        double sw = element.attribute("stroke-width").toDouble(&ok);
 
344
        if (!ok) return false;
 
345
 
 
346
        double x1 = element.attribute("x1").toDouble(&ok);
 
347
        if (!ok) return false;
 
348
 
 
349
        double y1 = element.attribute("y1").toDouble(&ok);
 
350
        if (!ok) return false;
 
351
 
 
352
        double x2 = element.attribute("x2").toDouble(&ok);
 
353
        if (!ok) return false;
 
354
 
 
355
        double y2 = element.attribute("y2").toDouble(&ok);
 
356
        if (!ok) return false;
 
357
 
 
358
        connectorInfo->legStrokeWidth = sw;
 
359
        connectorInfo->legLine = QLineF(x1, y1, x2, y2);
 
360
        return true;
 
361
}
 
362
 
 
363
void FSvgRenderer::initTerminalInfoAux(QDomElement & element, const QStringList & connectorIDs, const QStringList & terminalIDs)
 
364
{
 
365
        QString id = element.attribute("id");
 
366
        if (!id.isEmpty()) {
 
367
                int ix = terminalIDs.indexOf(id);
 
368
                if (ix >= 0) {
 
369
                        ConnectorInfo * connectorInfo = m_connectorInfoHash.value(connectorIDs.at(ix), NULL);
 
370
                        if (connectorInfo) {
 
371
                                connectorInfo->terminalMatrix = TextUtils::elementToMatrix(element);
 
372
                        }
 
373
                        // don't return here, might miss other terminal ids
 
374
                }
 
375
        }
 
376
 
 
377
        QDomElement child = element.firstChildElement();
 
378
        while (!child.isNull()) {
 
379
                initTerminalInfoAux(child, connectorIDs, terminalIDs);
 
380
                child = child.nextSiblingElement();
 
381
        }
 
382
}
 
383
 
 
384
void FSvgRenderer::initConnectorInfoAux(QDomElement & element, const QStringList & connectorIDs, const QString & filename)
 
385
{
 
386
        QString id = element.attribute("id");
 
387
        if (!id.isEmpty()) {
 
388
                if (connectorIDs.contains(id)) {
 
389
                        ConnectorInfo * connectorInfo = initConnectorInfoStruct(element, filename);
 
390
                        m_connectorInfoHash.insert(id, connectorInfo);
 
391
                }
 
392
                // don't return here, might miss other connectors
 
393
        }
 
394
 
 
395
        QDomElement child = element.firstChildElement();
 
396
        while (!child.isNull()) {
 
397
                initConnectorInfoAux(child, connectorIDs, filename);
 
398
                child = child.nextSiblingElement();
 
399
        }
 
400
}
 
401
 
 
402
ConnectorInfo * FSvgRenderer::initConnectorInfoStruct(QDomElement & connectorElement, const QString & filename) {
 
403
        ConnectorInfo * connectorInfo = new ConnectorInfo();
 
404
        connectorInfo->radius = connectorInfo->strokeWidth = 0;
 
405
        connectorInfo->gotCircle = false;
 
406
 
 
407
        if (connectorElement.isNull()) return connectorInfo;
 
408
 
 
409
        connectorInfo->matrix = TextUtils::elementToMatrix(connectorElement);
 
410
        initConnectorInfoStructAux(connectorElement, connectorInfo, filename);
 
411
        return connectorInfo;
 
412
}
 
413
 
 
414
bool FSvgRenderer::initConnectorInfoStructAux(QDomElement & element, ConnectorInfo * connectorInfo, const QString & filename) 
 
415
{
 
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;
 
421
 
 
422
                        child = child.nextSiblingElement();
 
423
                }
 
424
                return false;
 
425
        }
 
426
 
 
427
        bool ok;
 
428
        element.attribute("cx").toDouble(&ok);
 
429
        if (!ok) return false;
 
430
 
 
431
        element.attribute("cy").toDouble(&ok);
 
432
        if (!ok) return false;
 
433
 
 
434
        double r = element.attribute("r").toDouble(&ok);
 
435
        if (!ok) return false;
 
436
 
 
437
        double sw = element.attribute("stroke-width").toDouble(&ok);    
 
438
        if (!ok) {
 
439
        QDomElement parent = element.parentNode().toElement();
 
440
        while (!parent.isNull()) {
 
441
            sw = element.attribute("stroke-width").toDouble(&ok);
 
442
            if (ok) {
 
443
                break;
 
444
            }
 
445
 
 
446
            parent = parent.parentNode().toElement();
 
447
        }
 
448
 
 
449
        if (!ok) {
 
450
            QString text;
 
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);
 
455
            sw = 1;
 
456
 
 
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);
 
461
                    //if (!ok) {
 
462
                            //return false;
 
463
                    //}
 
464
        }
 
465
        }
 
466
 
 
467
        QMatrix matrix = TextUtils::elementToMatrix(element);
 
468
        if (!matrix.isIdentity()) {
 
469
                QRectF r1(0,0,r,r);
 
470
                QRectF r2 = matrix.mapRect(r1);
 
471
                if (r2.width() != r1.width()) {
 
472
                        r = r2.width();
 
473
                        sw = sw * r2.width() / r1.width();
 
474
                }
 
475
        }
 
476
 
 
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;
 
482
        return true;
 
483
}
 
484
 
 
485
ConnectorInfo * FSvgRenderer::getConnectorInfo(const QString & connectorID) {
 
486
        return m_connectorInfoHash.value(connectorID, &VanillaConnectorInfo);
 
487
}
 
488
 
 
489
bool FSvgRenderer::setUpConnector(SvgIdLayer * svgIdLayer, bool ignoreTerminalPoint) {
 
490
 
 
491
        if (svgIdLayer == NULL) return false;
 
492
 
 
493
        if (svgIdLayer->m_processed) {
 
494
                // hybrids are not visible in some views
 
495
                return svgIdLayer->m_svgVisible || svgIdLayer->m_hybrid;
 
496
        }
 
497
 
 
498
        svgIdLayer->m_processed = true;
 
499
 
 
500
        QString connectorID = svgIdLayer->m_svgId;
 
501
 
 
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);     
 
505
 
 
506
        if (bounds.isNull() && !svgIdLayer->m_hybrid) {         // hybrids can have zero size
 
507
                svgIdLayer->m_svgVisible = false;               
 
508
                DebugDialog::debug("renderer::setupconnector: null bounds");
 
509
                return false;
 
510
        }
 
511
 
 
512
        QSizeF defaultSizeF = this->defaultSizeF();
 
513
        QRectF viewBox = this->viewBoxF();
 
514
 
 
515
        ConnectorInfo * connectorInfo = getConnectorInfo(connectorID);  
 
516
 
 
517
        /*
 
518
        DebugDialog::debug(QString("connectorid:%1 m11:%2 m12:%3 m21:%4 m22:%5 dx:%6 dy:%7")
 
519
                                                .arg(connectorID)
 
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()),
 
526
                                bounds);
 
527
        */
 
528
 
 
529
        
 
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())
 
533
                                           .arg(viewIdentifier)
 
534
                                           .arg(defaultSizeF.width()).arg(defaultSizeF.height())
 
535
        );
 
536
        */
 
537
 
 
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);
 
542
 
 
543
    QMatrix elementMatrix = this->matrixForElement(connectorID);
 
544
        QRectF r1 = elementMatrix.mapRect(bounds);
 
545
 
 
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();
 
550
 
 
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;
 
555
        }
 
556
 
 
557
 
 
558
        /*
 
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());
 
563
        */
 
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());
 
568
 
 
569
        svgIdLayer->m_svgVisible = !bounds.isNull();
 
570
        //if (!svgIdLayer->m_svgVisible) {
 
571
                //DebugDialog::debug("not vis");
 
572
        //}
 
573
        svgIdLayer->m_point = calcTerminalPoint(svgIdLayer->m_terminalId, svgIdLayer->m_rect, ignoreTerminalPoint, viewBox, connectorInfo->terminalMatrix);
 
574
        calcLeg(svgIdLayer, viewBox, connectorInfo);
 
575
        
 
576
        return true;
 
577
}
 
578
 
 
579
void FSvgRenderer::calcLeg(SvgIdLayer * svgIdLayer, const QRectF & viewBox, ConnectorInfo * connectorInfo)
 
580
{
 
581
        if (svgIdLayer->m_legId.isEmpty()) return;
 
582
 
 
583
        svgIdLayer->m_legColor = connectorInfo->legColor;
 
584
 
 
585
        QSizeF defaultSizeF = this->defaultSizeF();
 
586
        svgIdLayer->m_legStrokeWidth = connectorInfo->legStrokeWidth * defaultSizeF.width() / viewBox.width();
 
587
 
 
588
        /*
 
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())
 
594
                );
 
595
        */
 
596
 
 
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());
 
600
 
 
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()));
 
608
 
 
609
        // find the end which is closer to the center of the viewBox (which shouldn't include the leg)
 
610
        if (d1 <= d2) {
 
611
                svgIdLayer->m_legLine = QLineF(x1, y1, x2, y2);
 
612
        }
 
613
        else {
 
614
                svgIdLayer->m_legLine = QLineF(x2, y2, x1, y1);
 
615
        }
 
616
}
 
617
 
 
618
QPointF FSvgRenderer::calcTerminalPoint(const QString & terminalId, const QRectF & connectorRect, bool ignoreTerminalPoint, const QRectF & viewBox, QMatrix & terminalMatrix)
 
619
{
 
620
        Q_UNUSED(terminalMatrix);
 
621
        QPointF terminalPoint = connectorRect.center() - connectorRect.topLeft();    // default spot is centered
 
622
        if (ignoreTerminalPoint) {
 
623
                return terminalPoint;
 
624
        }
 
625
        if (terminalId.isNull() || terminalId.isEmpty()) {
 
626
                return terminalPoint;
 
627
        }
 
628
 
 
629
        QRectF tBounds = this->boundsOnElement(terminalId);
 
630
        if (tBounds.isNull()) {
 
631
                return terminalPoint;
 
632
        }
 
633
 
 
634
        QSizeF defaultSizeF = this->defaultSizeF();
 
635
        if (tBounds.width() >= defaultSizeF.width() && tBounds.height() >= defaultSizeF.height()) {
 
636
                return terminalPoint;
 
637
        }
 
638
 
 
639
        //DebugDialog::debug(   QString("terminal %5 rect %1,%2,%3,%4").arg(tBounds.x()).
 
640
                                                                                //arg(tBounds.y()).
 
641
                                                                                //arg(tBounds.width()).
 
642
                                                                                //arg(tBounds.height()).
 
643
                                                                                //arg(terminalID) );
 
644
 
 
645
 
 
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()).
 
655
                                                                                //arg(terminalID) );
 
656
 
 
657
        return terminalPoint;
 
658
}
 
659
 
 
660
QList<SvgIdLayer *> FSvgRenderer::setUpNonConnectors() {
 
661
 
 
662
        QList<SvgIdLayer *> list;
 
663
        if (m_nonConnectorInfoHash.count() == 0) return list;
 
664
 
 
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()) {
 
671
                        delete svgIdLayer;
 
672
                        continue;
 
673
                }
 
674
 
 
675
                QSizeF defaultSizeF = this->defaultSizeF();
 
676
                QSize defaultSize = this->defaultSize();
 
677
                if ((bounds.width()) == defaultSizeF.width() && (bounds.height()) == defaultSizeF.height()) {
 
678
                        delete svgIdLayer;
 
679
                        continue;
 
680
                }
 
681
 
 
682
                QRectF viewBox = this->viewBoxF();
 
683
 
 
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;
 
689
                }
 
690
 
 
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;
 
698
 
 
699
                list.append(svgIdLayer);
 
700
        }
 
701
 
 
702
        return list;
 
703
 
 
704
}
 
705
 
 
706