1 #include "s2s.h"
2
3 #include "stdio.h"
4
5 #include <QStringList>
6 #include <QTextStream>
7 #include <QFile>
8 #include <QDir>
9 #include <QFileInfo>
10 #include <QtDebug>
11 #include <QRegExp>
12 #include <qmath.h>
13 #include <QDomNodeList>
14 #include <QDomDocument>
15 #include <QDomElement>
16 #include <QSvgRenderer>
17 #include <QFontDatabase>
18 #include <QFont>
19 #include <QFontInfo>
20 #include <QResource>
21 #include <QImage>
22 #include <QPainter>
23 #include <QBitArray>
24
25 #include "../../src/utils/textutils.h"
26 #include "../../src/utils/schematicrectconstants.h"
27
28 #include <limits>
29
30 /////////////////////////////////
31
xLessThan(ConnectorLocation * cl1,ConnectorLocation * cl2)32 bool xLessThan(ConnectorLocation * cl1, ConnectorLocation * cl2)
33 {
34 return cl1->terminalPoint.x() < cl2->terminalPoint.x();
35 }
36
yLessThan(ConnectorLocation * cl1,ConnectorLocation * cl2)37 bool yLessThan(ConnectorLocation * cl1, ConnectorLocation * cl2)
38 {
39 return cl1->terminalPoint.y() < cl2->terminalPoint.y();
40 }
41
getY(const QPointF & p)42 double getY(const QPointF & p) {
43 return p.y();
44 }
45
getX(const QPointF & p)46 double getX(const QPointF & p) {
47 return p.x();
48 }
49
setHiddenAux(QList<ConnectorLocation * > & allConnectorLocations,QList<ConnectorLocation * > & sideConnectorLocations,double (* get)(const QPointF &),double fudge)50 void setHiddenAux(QList<ConnectorLocation *> & allConnectorLocations, QList<ConnectorLocation *> & sideConnectorLocations, double (*get)(const QPointF &), double fudge)
51 {
52 int i = 0;
53 while (i < sideConnectorLocations.count() - 1) {
54 QList<ConnectorLocation *> same;
55 ConnectorLocation * basis = sideConnectorLocations.at(i);
56 same << basis;
57 for (int j = i + 1; j < sideConnectorLocations.count(); j++) {
58 i = j;
59 ConnectorLocation * next = sideConnectorLocations.at(j);
60 if (qAbs(get(basis->terminalPoint )- get(next->terminalPoint)) < fudge) {
61 same << next;
62 }
63 else {
64 break;
65 }
66 }
67 if (same.count() > 1) {
68 foreach (ConnectorLocation * connectorLocation, same) {
69 connectorLocation->hidden = true;
70 }
71 for (int ix = allConnectorLocations.count() - 1; ix >= 0; ix--) {
72 ConnectorLocation * that = allConnectorLocations.at(ix);
73 if (same.contains(that)) {
74 // assume the topmost is the one at the end of allConnectorLocations
75 // the rest are hidden
76 that->hidden = false;
77 sideConnectorLocations.removeOne(that);
78 int maxIndex = -1;
79 foreach (ConnectorLocation * s, same) {
80 int ix = sideConnectorLocations.indexOf(s);
81 if (ix > maxIndex) maxIndex = ix;
82 }
83 // visible one should be the topmost of the set
84 sideConnectorLocations.insert(maxIndex + 1, that);
85 break;
86 }
87 }
88 }
89 }
90
91 }
92
93 /////////////////////////////////
94
95 static QRegExp IntegerFinder("\\d+");
96 static const double ImageFactor = 5;
97 static const double FudgeDivisor = 300;
98 static const QRegExp VersionRegexp("[ -_][vV][\\d]+");
99
100 ///////////////////////////////////////////////////////
101
makePinNumber(const ConnectorLocation * connectorLocation,double x1,double y1,double x2,double y2)102 QString makePinNumber(const ConnectorLocation * connectorLocation, double x1, double y1, double x2, double y2) {
103
104 if (connectorLocation->id < 0) return "";
105 if (connectorLocation->hidden) return "";
106 if (!connectorLocation->displayPinNumber) return "";
107
108 QString text;
109 double tx = 0;
110 double ty = 0;
111 if (x1 == x2) {
112 text += QString("<g transform='translate(%1,%2)'><g transform='rotate(%3)'>\n")
113 .arg(x2 - SchematicRectConstants::PinWidth + SchematicRectConstants::PinSmallTextVert)
114 .arg((y2 + y1) / 2)
115 .arg(270)
116 ;
117 }
118 else {
119 tx = (x2 + x1) / 2;
120 ty = y2 - SchematicRectConstants::PinWidth + SchematicRectConstants::PinSmallTextVert;
121 }
122
123 text += QString("<text class='text' font-family=\"%8\" stroke='none' stroke-width='%6' fill='%7' font-size='%1' x='%2' y='%3' text-anchor='%4'>%5</text>\n")
124 .arg(SchematicRectConstants::PinSmallTextHeight)
125 .arg(tx)
126 .arg(ty)
127 .arg("middle")
128 .arg(connectorLocation->id)
129 .arg(0) // SW(width)
130 .arg(SchematicRectConstants::PinTextColor)
131 .arg(SchematicRectConstants::FontFamily)
132 ;
133
134 if (x1 == x2) {
135 text += "</g></g>\n";
136 }
137
138
139 return text;
140 }
141
makePinText(const ConnectorLocation * connectorLocation,double x1,double y1,double x2,double y2,bool anchorAtStart)142 QString makePinText(const ConnectorLocation * connectorLocation, double x1, double y1, double x2, double y2, bool anchorAtStart)
143 {
144 if (connectorLocation->hidden) return "";
145
146 QString text;
147
148 bool rotate = false;
149 double xOffset = 0, yOffset = 0;
150 if (x1 == x2) {
151 rotate = true;
152 yOffset = (anchorAtStart ? -SchematicRectConstants::PinTextIndent : SchematicRectConstants::PinTextIndent);
153 xOffset = SchematicRectConstants::PinTextVert;
154 }
155 else if (y1 == y2) {
156 // horizontal pin
157 xOffset = (anchorAtStart ? SchematicRectConstants::PinTextIndent : -SchematicRectConstants::PinTextIndent);
158 yOffset = SchematicRectConstants::PinTextVert;
159 }
160 else {
161 return "";
162 }
163
164 if (rotate) {
165 text += QString("<g transform='translate(%1,%2)'><g transform='rotate(%3)'>\n")
166 .arg(x2 + xOffset)
167 .arg(y2 + yOffset)
168 .arg(270);
169 x2 = 0;
170 y2 = 0;
171 xOffset = yOffset = 0;
172 }
173
174 text += QString("<text class='text' font-family=\"%8\" stroke='none' stroke-width='%6' fill='%7' font-size='%1' x='%2' y='%3' text-anchor='%4'>%5</text>\n")
175 .arg(SchematicRectConstants::PinBigTextHeight)
176 .arg(x2 + xOffset)
177 .arg(y2 + yOffset)
178 .arg(anchorAtStart ? "start" : "end")
179 .arg(TextUtils::escapeAnd(connectorLocation->name))
180 .arg(0) // SW(width)
181 .arg(SchematicRectConstants::PinTextColor)
182 .arg(SchematicRectConstants::FontFamily)
183 ;
184
185 if (rotate) {
186 text += "</g></g>\n";
187 }
188
189 return text;
190 }
191
makePin(const ConnectorLocation * connectorLocation,double x1,double y1,double x2,double y2)192 QString makePin(const ConnectorLocation * connectorLocation, double x1, double y1, double x2, double y2) {
193 return QString("<line class='pin' x1='%1' y1='%2' x2='%3' y2='%4' fill='none' stroke='%5' stroke-width='%6' stroke-linecap='round' id='%7' />\n")
194 .arg(x1)
195 .arg(y1)
196 .arg(x2)
197 .arg(y2)
198 .arg(connectorLocation->hidden ? "none" : SchematicRectConstants::PinColor)
199 .arg(connectorLocation->hidden ? 0 : SchematicRectConstants::PinWidth)
200 .arg(connectorLocation->svgID)
201 ;
202 }
203
makeTerminal(const ConnectorLocation * connectorLocation,double x,double y)204 QString makeTerminal(const ConnectorLocation * connectorLocation, double x, double y) {
205 return QString("<rect class='terminal' x='%1' y='%2' width='%3' height='%4' fill='none' stroke='none' stroke-width='0' id='%5' />\n")
206 .arg(x - (SchematicRectConstants::PinWidth / 2))
207 .arg(y - (SchematicRectConstants::PinWidth / 2))
208 .arg(SchematicRectConstants::PinWidth)
209 .arg(SchematicRectConstants::PinWidth)
210 .arg(connectorLocation->terminalID)
211 ;
212 }
213
214 ///////////////////////////////////////////////////////
215
216
S2S(bool fzpzStyle)217 S2S::S2S(bool fzpzStyle) : QObject()
218 {
219 m_image = new QImage(50 * ImageFactor, 5 * ImageFactor, QImage::Format_Mono);
220 m_fzpzStyle = fzpzStyle;
221 }
222
message(const QString & msg)223 void S2S::message(const QString & msg) {
224 // QTextStream cout(stdout);
225 // cout << msg;
226 // cout.flush();
227
228 qDebug() << msg;
229 emit messageSignal(msg);
230 }
231
saveFile(const QString & content,const QString & path)232 void S2S::saveFile(const QString & content, const QString & path)
233 {
234 QFile file(path);
235 if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
236 QTextStream out(&file);
237 out.setCodec("UTF-8");
238 out << content;
239 file.close();
240 }
241 }
242
onefzp(QString & fzpFilePath,QString & schematicFilePath)243 bool S2S::onefzp(QString & fzpFilePath, QString & schematicFilePath) {
244 if (!fzpFilePath.endsWith(".fzp")) {
245 return false;
246 }
247
248 bool createSchematicFile = schematicFilePath.isEmpty();
249 QString newSchematicFilePath = schematicFilePath;
250
251 m_lefts.clear();
252 m_rights.clear();
253 m_tops.clear();
254 m_bottoms.clear();
255
256 QFile file(fzpFilePath);
257
258 QString errorStr;
259 int errorLine;
260 int errorColumn;
261
262 QDomDocument dom;
263 if (!dom.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) {
264 message(tr("Failed loading '%1', %2 line:%3 col:%4").arg(fzpFilePath).arg(errorStr).arg(errorLine).arg(errorColumn));
265 return false;
266 }
267
268 QDomElement root = dom.documentElement();
269 QDomElement titleElement = root.firstChildElement("title");
270 QString title;
271 TextUtils::findText(titleElement, title);
272 //int ix = VersionRegexp.lastIndexIn(title);
273 //if (ix > 0 && ix + VersionRegexp.cap(0).count() == title.count()) {
274 // title.chop(VersionRegexp.cap(0).count());
275 //}
276 QStringList titles;
277 titles << title;
278 TextUtils::resplit(titles, " ");
279 TextUtils::resplit(titles, "_");
280 TextUtils::resplit(titles, "-");
281
282
283 if (createSchematicFile) {
284 QString schematicFileName;
285 QDomNodeList nodeList = root.elementsByTagName("schematicView");
286 for (int i = 0; i < nodeList.count(); i++) {
287 QDomElement schematicView = nodeList.at(i).toElement();
288 QDomElement layers = schematicView.firstChildElement("layers");
289 schematicFileName = layers.attribute("image");
290 if (!schematicFileName.isEmpty()) break;
291 }
292
293 if (schematicFileName.isEmpty()) {
294 message(tr("Schematic not found for '%1'").arg(fzpFilePath));
295 return false;
296 }
297
298 if (m_fzpzStyle) {
299 schematicFileName.replace("/", ".");
300 schematicFileName = "svg." + schematicFileName;
301 }
302
303 schematicFilePath = m_oldSvgDir.absoluteFilePath(schematicFileName);
304 newSchematicFilePath = m_newSvgDir.absoluteFilePath(schematicFileName);
305 }
306
307 if (!ensureTerminalPoints(fzpFilePath, schematicFilePath, root)) {
308 return false;
309 }
310
311 qDebug() << schematicFilePath;
312
313 QSvgRenderer renderer;
314 bool loaded = renderer.load(schematicFilePath);
315 if (!loaded) {
316 message(tr("Unable to load schematic '%1' for '%2'").arg(schematicFilePath).arg(fzpFilePath));
317 return false;
318 }
319
320 QList<ConnectorLocation *> connectorLocations = initConnectors(root, renderer, fzpFilePath, schematicFilePath);
321
322 QRectF viewBox = renderer.viewBoxF();
323 if (viewBox.isEmpty()) {
324 qDebug() << "\tempty viewbox";
325 }
326 double oldUnit = lrtb(connectorLocations, viewBox);
327
328 if (qAbs(oldUnit - SchematicRectConstants::NewUnit) < (SchematicRectConstants::NewUnit / 25)) {
329 message(tr("Schematic '%1' is already using the 0.1inch standard.").arg(schematicFilePath));
330 return false;
331 }
332
333 setHidden(connectorLocations);
334
335 double minPinV = 0;
336 double maxPinV = 0;
337 if (m_lefts.count() && m_rights.count()) {
338 minPinV = qMin(m_lefts.first()->terminalPoint.y(), m_rights.first()->terminalPoint.y());
339 maxPinV = qMax(m_lefts.last()->terminalPoint.y(), m_rights.last()->terminalPoint.y());
340 }
341 else if (m_rights.count()) {
342 minPinV = m_rights.first()->terminalPoint.y();
343 maxPinV = m_rights.last()->terminalPoint.y();
344 }
345 else if (m_lefts.count()) {
346 minPinV = m_lefts.first()->terminalPoint.y();
347 maxPinV = m_lefts.last()->terminalPoint.y();
348 }
349 int vPinUnits = qRound((maxPinV - minPinV) / oldUnit) + 2;
350 int vUnits = vPinUnits;
351
352 double minPinH = 0;
353 double maxPinH = 0;
354 if (m_bottoms.count() && m_tops.count()) {
355 minPinH = qMin(m_bottoms.first()->terminalPoint.x(), m_tops.first()->terminalPoint.x());
356 maxPinH = qMax(m_bottoms.last()->terminalPoint.x(), m_tops.last()->terminalPoint.x());
357 }
358 else if (m_bottoms.count()) {
359 minPinH = m_bottoms.first()->terminalPoint.x();
360 maxPinH = m_bottoms.last()->terminalPoint.x();
361 }
362 else if (m_tops.count()) {
363 minPinH = m_tops.first()->terminalPoint.x();
364 maxPinH = m_tops.last()->terminalPoint.x();
365 }
366 int hPinUnits = qRound((maxPinH - minPinH) / oldUnit) + 2;
367 int hUnits = hPinUnits;
368
369 double hPinTextMax = 0;
370 for (int i = 0; i < m_lefts.count(); i++) {
371 double w = stringWidthMM(SchematicRectConstants::PinBigTextHeight, m_lefts.at(i)->name);
372 if (w > hPinTextMax) hPinTextMax = w;
373 }
374 for (int i = 0; i < m_rights.count(); i++) {
375 double w = stringWidthMM(SchematicRectConstants::PinBigTextHeight, m_rights.at(i)->name);
376 if (w > hPinTextMax) hPinTextMax = w;
377 }
378 int leftTextUnits = qCeil(hPinTextMax / SchematicRectConstants::NewUnit);
379 int rightTextUnits = leftTextUnits;
380
381
382 double vTopPinTextMax = 0;
383 for (int i = 0; i < m_tops.count(); i++) {
384 double w = stringWidthMM(SchematicRectConstants::PinBigTextHeight, m_tops.at(i)->name);
385 if (w > vTopPinTextMax) vTopPinTextMax = w;
386 }
387 int topTextUnits = qCeil(vTopPinTextMax / SchematicRectConstants::NewUnit);
388 double vBottomPinTextMax = 0;
389 for (int i = 0; i < m_bottoms.count(); i++) {
390 double w = stringWidthMM(SchematicRectConstants::PinBigTextHeight, m_bottoms.at(i)->name);
391 if (w > vBottomPinTextMax) vBottomPinTextMax = w;
392 }
393 int bottomTextUnits = qCeil(vBottomPinTextMax / SchematicRectConstants::NewUnit);
394
395 double labelTextMax = spaceTitle(titles, hUnits - leftTextUnits - rightTextUnits - 2);
396
397 double textWidth = hPinTextMax + hPinTextMax + labelTextMax + (6 * SchematicRectConstants::PinTextIndent); // PTI text PTI PTI title PTI PTI text PTI
398 double hTextUnits = qCeil(textWidth / SchematicRectConstants::NewUnit);
399 if (hTextUnits > hUnits) hUnits = hTextUnits;
400
401 textWidth = titles.count() * (SchematicRectConstants::LabelTextHeight + SchematicRectConstants::LabelTextSpace);
402 double vTextUnits = qCeil((textWidth / SchematicRectConstants::NewUnit) + (2 * SchematicRectConstants::PinTextIndent));
403 if (vTextUnits > vUnits) vUnits = vTextUnits;
404
405 vUnits += topTextUnits;
406 vUnits += bottomTextUnits;
407
408 double fullWidth = hUnits;
409 if (m_lefts.count()) fullWidth += 2;
410 if (m_rights.count()) fullWidth += 2;
411 double fullHeight = vUnits;
412 if (m_tops.count()) fullHeight += 2;
413 if (m_bottoms.count()) fullHeight += 2;
414
415
416
417 // construct svg
418
419 QString svg = TextUtils::makeSVGHeader(25.4, 25.4, fullWidth * SchematicRectConstants::NewUnit, fullHeight * SchematicRectConstants::NewUnit);
420 double rectL = SchematicRectConstants::RectStrokeWidth / 2;
421 double rectT = rectL;
422 if (m_lefts.count()) rectL += 2 * SchematicRectConstants::NewUnit;
423 if (m_tops.count()) rectT += 2 * SchematicRectConstants::NewUnit;
424 svg += "<g id='schematic'>\n";
425 svg += QString("<rect class='interior rect' x='%1' y='%2' width='%3' height='%4' fill='%5' stroke='%6' stroke-width='%7' stroke-linecap='round' />\n")
426 .arg(rectL)
427 .arg(rectT)
428 .arg((hUnits * SchematicRectConstants::NewUnit) - SchematicRectConstants::RectStrokeWidth)
429 .arg((vUnits * SchematicRectConstants::NewUnit) - SchematicRectConstants::RectStrokeWidth)
430 .arg(SchematicRectConstants::RectFillColor)
431 .arg(SchematicRectConstants::RectStrokeColor)
432 .arg(SchematicRectConstants::RectStrokeWidth)
433 ;
434
435 double vPinOffset = 0;
436 if (m_tops.count()) vPinOffset = 2 * SchematicRectConstants::NewUnit;
437 vPinOffset += topTextUnits * SchematicRectConstants::NewUnit;
438 int space = vUnits - vPinUnits - topTextUnits - bottomTextUnits;
439 if (space > 1) {
440 vPinOffset += (space / 2) * SchematicRectConstants::NewUnit;
441 }
442
443 foreach (ConnectorLocation * connectorLocation, m_rights) {
444 int units = qRound((connectorLocation->terminalPoint.y() - minPinV) / oldUnit) + 1;
445 double y = units * SchematicRectConstants::NewUnit + vPinOffset;
446 double x1 = ((fullWidth - 2) * SchematicRectConstants::NewUnit) - (SchematicRectConstants::PinWidth / 2);
447 double x2 = (fullWidth * SchematicRectConstants::NewUnit) - (SchematicRectConstants::PinWidth / 2);
448 svg += makePin(connectorLocation, x1, y, x2, y);
449 svg += makePinNumber(connectorLocation, x1, y, x2, y);
450 svg += makePinText(connectorLocation, x2, y, x1, y, false);
451 svg += makeTerminal(connectorLocation, fullWidth * SchematicRectConstants::NewUnit, y);
452 }
453
454 foreach (ConnectorLocation * connectorLocation, m_lefts) {
455 int units = qRound((connectorLocation->terminalPoint.y() - minPinV) / oldUnit) + 1;
456 double y = units * SchematicRectConstants::NewUnit + vPinOffset;
457 double x1 = SchematicRectConstants::PinWidth / 2;
458 double x2 = (2 * SchematicRectConstants::NewUnit) + (SchematicRectConstants::PinWidth / 2);
459 svg += makePin(connectorLocation, x1, y, x2, y);
460 svg += makePinNumber(connectorLocation, x1, y, x2, y);
461 svg += makePinText(connectorLocation, x1, y, x2, y, true);
462 svg += makeTerminal(connectorLocation, 0, y);
463 }
464
465 double hPinOffset = 0;
466 if (m_lefts.count()) hPinOffset = 2 * SchematicRectConstants::NewUnit;
467 space = hUnits - hPinUnits;
468 if (space > 1) {
469 hPinOffset += (space / 2) * SchematicRectConstants::NewUnit;
470 }
471
472 foreach (ConnectorLocation * connectorLocation, m_bottoms) {
473 int units = qRound((connectorLocation->terminalPoint.x() - minPinH) / oldUnit) + 1;
474 double x = units * SchematicRectConstants::NewUnit + hPinOffset;
475 double y1 = ((fullHeight - 2) * SchematicRectConstants::NewUnit) - (SchematicRectConstants::PinWidth / 2);
476 double y2 = (fullHeight * SchematicRectConstants::NewUnit) - (SchematicRectConstants::PinWidth / 2);
477 svg += makePin(connectorLocation, x, y1, x, y2);
478 svg += makePinNumber(connectorLocation, x, y1, x, y2);
479 svg += makePinText(connectorLocation, x, y2, x, y1, true);
480 svg += makeTerminal(connectorLocation, x, fullHeight * SchematicRectConstants::NewUnit);
481 }
482
483 foreach (ConnectorLocation * connectorLocation, m_tops) {
484 int units = qRound((connectorLocation->terminalPoint.x() - minPinH) / oldUnit) + 1;
485 double x = units * SchematicRectConstants::NewUnit + hPinOffset;
486 double y1 = SchematicRectConstants::PinWidth / 2;
487 double y2 = (2 * SchematicRectConstants::NewUnit) + (SchematicRectConstants::PinWidth / 2);
488 svg += makePin(connectorLocation, x, y1, x, y2);
489 svg += makePinNumber(connectorLocation, x, y1, x, y2);
490 svg += makePinText(connectorLocation, x, y1, x, y2, false);
491 svg += makeTerminal(connectorLocation, x, 0);
492 }
493
494 double y = vUnits * SchematicRectConstants::NewUnit / 2;
495 y -= titles.count() * (SchematicRectConstants::LabelTextHeight + SchematicRectConstants::LabelTextSpace) / 2;
496 y += rectT;
497 y = qMax(y, rectT + (topTextUnits * SchematicRectConstants::NewUnit) + SchematicRectConstants::LabelTextHeight + SchematicRectConstants::LabelTextSpace + SchematicRectConstants::PinTextIndent); // y seems to be the location of the baseline so add a line
498 foreach (QString subTitle, titles) {
499 svg += QString("<text class='text' id='label' font-family=\"%7\" stroke='none' stroke-width='%4' fill='%5' font-size='%1' x='%2' y='%3' text-anchor='middle'>%6</text>\n")
500 .arg(SchematicRectConstants::LabelTextHeight)
501 .arg(((hUnits * SchematicRectConstants::NewUnit) / 2) + rectL)
502 .arg(y)
503 .arg(0) // SW(width)
504 .arg(SchematicRectConstants::TitleColor)
505 .arg(TextUtils::escapeAnd(subTitle))
506 .arg(SchematicRectConstants::FontFamily)
507 ;
508 y += (SchematicRectConstants::LabelTextHeight + SchematicRectConstants::LabelTextSpace);
509 }
510
511
512 svg +="</g>\n";
513 svg +="</svg>\n";
514
515 TextUtils::writeUtf8(newSchematicFilePath, svg);
516 qDebug() << newSchematicFilePath;
517 qDebug() << "";
518 return true;
519 }
520
521
stringWidthMM(double fontSize,const QString & string)522 double S2S::stringWidthMM(double fontSize, const QString & string) {
523
524 /*
525 // tried using FontMetrics but the result is always too short
526 QFont font("Droid Sans");
527 font.setPointSizeF(fontSize * 72 / 25.4);
528 QFontMetricsF fontMetrics(font);
529 double pixels = fontMetrics.width(string);
530 double mm = 25.4 * pixels / 90;
531 */
532
533 QString svg = TextUtils::makeSVGHeader(25.4, 25.4, 50, 5);
534 svg += QString("<text font=\"%1\" font-size='%2' stroke='none' stroke-width='0' fill='black' x='0' y='%4' text-anchor='start' >%3</text>")
535 .arg(SchematicRectConstants::FontFamily).arg(fontSize).arg(TextUtils::escapeAnd(string)).arg(2.5);
536 svg += "</svg>";
537
538 QSvgRenderer renderer(svg.toUtf8());
539 m_image->fill(0xffffffff);
540 QPainter painter;
541 painter.begin(m_image);
542 painter.setRenderHint(QPainter::Antialiasing, false);
543 painter.setRenderHint(QPainter::SmoothPixmapTransform, false);
544 renderer.render(&painter);
545 painter.end();
546 //QDir dir(QCoreApplication::applicationDirPath());
547 //dir.cdUp();
548 //image.save(dir.absoluteFilePath("bloody_font.png"));
549 int bestX = 0;
550 for (int y = 0; y < m_image->height(); y++) {
551 for (int x = bestX; x < m_image->width(); x++) {
552 if (m_image->pixel(x, y) == 0xff000000) {
553 bestX = x;
554 }
555 }
556 }
557
558 //if (mm < bestX / ImageFactor) {
559 // qDebug() << "string width" << mm << (bestX / ImageFactor) << string;
560 //}
561
562 return bestX / ImageFactor;
563 }
564
initConnectors(const QDomElement & root,const QSvgRenderer & renderer,const QString & fzpFilename,const QString & svgFilename)565 QList<ConnectorLocation *> S2S::initConnectors(const QDomElement & root, const QSvgRenderer & renderer, const QString & fzpFilename, const QString & svgFilename)
566 {
567 QList<ConnectorLocation *> connectorLocations;
568 m_minLeft = std::numeric_limits<int>::max();
569 m_minTop = std::numeric_limits<int>::max();
570 m_maxRight = std::numeric_limits<int>::min();
571 m_maxBottom = std::numeric_limits<int>::min();
572
573 QDomElement connectors = root.firstChildElement("connectors");
574 QDomElement connector = connectors.firstChildElement("connector");
575 QBitArray idlist;
576 int noIDCount = 0;
577 while (!connector.isNull()) {
578 QDomElement schematicView = connector.firstChildElement("views").firstChildElement("schematicView");
579 QString svgID = schematicView.firstChildElement("p").attribute("svgId");
580 if (svgID.isEmpty()) {
581 message(tr("Missing connector %1 in '%2' schematic of '%3'").arg(connector.attribute("id")).arg(svgFilename).arg(fzpFilename));
582 }
583 else {
584 ConnectorLocation * connectorLocation = new ConnectorLocation;
585 connectorLocation->id = -1;
586 connectorLocation->hidden = false;
587 connectorLocation->displayPinNumber = false;
588 QString id = connector.attribute("id");
589 int ix = IntegerFinder.indexIn(id);
590 if (ix > 0) {
591 connectorLocation->id = IntegerFinder.cap(0).toInt();
592 if (idlist.size() < connectorLocation->id + 1) {
593 int oldsize = idlist.size();
594 idlist.resize(connectorLocation->id + 1);
595 for (int i = oldsize; i < idlist.size(); i++) idlist.setBit(i, false);
596 }
597 idlist.setBit(connectorLocation->id, true);
598 }
599 else {
600 noIDCount++;
601 }
602 connectorLocation->name = connector.attribute("name");
603 connectorLocation->terminalID = schematicView.firstChildElement("p").attribute("terminalId");
604 connectorLocation->svgID = svgID;
605 QRectF bounds = renderer.boundsOnElement(svgID);
606 QMatrix m = renderer.matrixForElement(svgID);
607 connectorLocation->bounds = m.mapRect(bounds);
608 connectorLocation->terminalPoint = connectorLocation->bounds.center();
609
610 if (!connectorLocation->terminalID.isEmpty()) {
611 bounds = renderer.boundsOnElement(connectorLocation->terminalID);
612 m = renderer.matrixForElement(connectorLocation->terminalID);
613 connectorLocation->terminalPoint = m.mapRect(bounds).center();
614 }
615 connectorLocations.append(connectorLocation);
616 if (connectorLocation->terminalPoint.x() < m_minLeft) {
617 m_minLeft = connectorLocation->terminalPoint.x();
618 }
619 if (connectorLocation->terminalPoint.x() > m_maxRight) {
620 m_maxRight = connectorLocation->terminalPoint.x();
621 }
622 if (connectorLocation->terminalPoint.y() < m_minTop) {
623 m_minTop = connectorLocation->terminalPoint.y();
624 }
625 if (connectorLocation->terminalPoint.y() > m_maxBottom) {
626 m_maxBottom = connectorLocation->terminalPoint.y();
627 }
628 }
629
630 connector = connector.nextSiblingElement("connector");
631 }
632
633 if (noIDCount > 0) {
634 qDebug() << "no id count" << noIDCount;
635 }
636
637 bool display = true;
638 if (!idlist.at(0) && !idlist.at(1)) {
639 display = false;
640 }
641 else {
642 int present = 0;
643 for (int i = 0; i < idlist.size(); i++) {
644 if (idlist.at(i)) present++;
645 }
646 display = (present >= .66 * idlist.size());
647 }
648
649 foreach (ConnectorLocation * connectorLocation, connectorLocations) {
650 connectorLocation->displayPinNumber = display;
651 }
652
653 if (display && idlist.at(0)) {
654 foreach (ConnectorLocation * connectorLocation, connectorLocations) {
655 connectorLocation->id++;
656 }
657 }
658
659 return connectorLocations;
660 }
661
lrtb(QList<ConnectorLocation * > & connectorLocations,const QRectF & viewBox)662 double S2S::lrtb(QList<ConnectorLocation *> & connectorLocations, const QRectF & viewBox)
663 {
664 m_fudge = qMax(m_maxRight - m_minLeft, m_maxBottom - m_minTop) / FudgeDivisor;
665
666 foreach (ConnectorLocation * connectorLocation, connectorLocations) {
667 double d[4];
668 d[0] = connectorLocation->terminalPoint.x() - viewBox.left();
669 d[1] = connectorLocation->terminalPoint.y() - viewBox.top();
670 d[2] = viewBox.right() - connectorLocation->terminalPoint.x();
671 d[3] = viewBox.bottom() - connectorLocation->terminalPoint.y();
672 int ix = 0;
673 int dx = d[0];
674 for (int i = 1; i < 4; i++) {
675 if (d[i] < dx) {
676 dx = d[i];
677 ix = i;
678 }
679 }
680 switch (ix) {
681 case 0:
682 m_lefts << connectorLocation;
683 break;
684 case 1:
685 m_tops << connectorLocation;
686 break;
687 case 2:
688 m_rights << connectorLocation;
689 break;
690 case 3:
691 m_bottoms << connectorLocation;
692 break;
693 default:
694 qDebug() << "shouldn't happen" << ix;
695 }
696
697 }
698
699 qSort(m_lefts.begin(), m_lefts.end(), yLessThan);
700 qSort(m_rights.begin(), m_rights.end(), yLessThan);
701 qSort(m_tops.begin(), m_tops.end(), xLessThan);
702 qSort(m_bottoms.begin(), m_bottoms.end(), xLessThan);
703
704 QList<double> distances;
705 for (int i = 1; i < m_lefts.count(); i++) {
706 ConnectorLocation * l1 = m_lefts.at(i - 1);
707 ConnectorLocation * l2 = m_lefts.at(i);
708 distances << l2->terminalPoint.y() - l1->terminalPoint.y();
709 }
710 for (int i = 1; i < m_rights.count(); i++) {
711 ConnectorLocation * l1 = m_rights.at(i - 1);
712 ConnectorLocation * l2 = m_rights.at(i);
713 distances << l2->terminalPoint.y() - l1->terminalPoint.y();
714 }
715 for (int i = 1; i < m_tops.count(); i++) {
716 ConnectorLocation * l1 = m_tops.at(i - 1);
717 ConnectorLocation * l2 = m_tops.at(i);
718 distances << l2->terminalPoint.x() - l1->terminalPoint.x();
719 }
720 for (int i = 1; i < m_bottoms.count(); i++) {
721 ConnectorLocation * l1 = m_bottoms.at(i - 1);
722 ConnectorLocation * l2 = m_bottoms.at(i);
723 distances << l2->terminalPoint.x() - l1->terminalPoint.x();
724 }
725
726 qSort(distances.begin(), distances.end());
727
728 int totalPins = m_lefts.count() + m_rights.count() + m_bottoms.count() + m_tops.count();
729
730 int most = 0;
731 foreach (double distance, distances) {
732 int d = qRound(distance / m_fudge);
733 if (d > most) most = d;
734 }
735
736 QVector<int> dbins(most + 2, 0);
737 foreach (double distance, distances) {
738 int d = qRound(distance / m_fudge);
739 dbins[d] += 1;
740 }
741
742 int biggest = dbins[1];
743 int biggestIndex = 1;
744 for (int i = 2; i < dbins.count(); i++) {
745 if (dbins[i] > biggest) {
746 biggest = dbins[i];
747 biggestIndex = i;
748 }
749 }
750
751 if (biggest == 0) {
752 qDebug() << "\tbiggest 0" << totalPins;
753 }
754
755 double oldUnit = biggestIndex * m_fudge;
756
757 qDebug() << QString("\tunit is roughly %1mm, bin:%2 pins:%3 fudge:%4").arg(oldUnit).arg(biggest).arg(totalPins).arg(m_fudge);
758 qDebug() << QString("\tl:%1 t:%2 r:%3 b:%4").arg(m_lefts.count()).arg(m_tops.count()).arg(m_rights.count()).arg(m_bottoms.count());
759
760 return oldUnit;
761 }
762
763
setHidden(QList<ConnectorLocation * > & connectorLocations)764 void S2S::setHidden(QList<ConnectorLocation *> & connectorLocations)
765 {
766 setHiddenAux(connectorLocations, m_lefts, getY, m_fudge);
767 setHiddenAux(connectorLocations, m_rights, getY, m_fudge);
768 setHiddenAux(connectorLocations, m_tops, getX, m_fudge);
769 setHiddenAux(connectorLocations, m_bottoms, getX, m_fudge);
770 }
771
ensureTerminalPoints(const QString & fzpFilePath,const QString & svgFilePath,QDomElement & fzpRoot)772 bool S2S::ensureTerminalPoints(const QString & fzpFilePath, const QString & svgFilePath, QDomElement & fzpRoot) {
773 QList<QDomElement> missing;
774 QDomElement connectors = fzpRoot.firstChildElement("connectors");
775 QDomElement connector = connectors.firstChildElement("connector");
776 while (!connector.isNull()) {
777 QDomElement views = connector.firstChildElement("views");
778 QDomElement schematicView = views.firstChildElement("schematicView");
779 QDomElement p = schematicView.firstChildElement("p");
780 QString terminalID = p.attribute("terminalId");
781 if (terminalID.isEmpty()) {
782 missing << p;
783 }
784
785 connector = connector.nextSiblingElement("connector");
786 }
787
788 if (missing.count() == 0) return true;
789
790 QSvgRenderer renderer;
791 bool loaded = renderer.load(svgFilePath);
792 if (!loaded) {
793 message(tr("Uunable to load schematic '%1' for '%2'").arg(svgFilePath).arg(fzpFilePath));
794 return false;
795 }
796
797 QString errorStr;
798 int errorLine;
799 int errorColumn;
800
801 QFile file(svgFilePath);
802 QDomDocument dom;
803 if (!dom.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) {
804 message(tr("Failed loading schematic '%1', %2 line:%3 col:%4").arg(svgFilePath).arg(errorStr).arg(errorLine).arg(errorColumn));
805 return false;
806 }
807
808 QDomElement svgRoot = dom.documentElement();
809
810 bool fzpChanged = false;
811 bool svgChanged = false;
812
813 foreach (QDomElement p, missing) {
814 QDomElement connector = p.parentNode().parentNode().parentNode().toElement();
815 QString name = connector.attribute("id");
816 if (name.isEmpty()) {
817 qDebug() << "empty name in connector";
818 }
819 else {
820 // assumes the file is well behaved, and the terminalID isn't already in use
821 QString terminalName = name + "terminal";
822 p.setAttribute("terminalId", terminalName);
823 fzpChanged = true;
824
825 QDomElement terminalElement = TextUtils::findElementWithAttribute(svgRoot, "id", terminalName);
826 if (terminalElement.isNull()) {
827 QString svgID = p.attribute("svgId");
828 QDomElement connectorElement = TextUtils::findElementWithAttribute(svgRoot, "id", svgID);
829 QRectF bounds = renderer.boundsOnElement(svgID);
830 QDomElement rect = svgRoot.ownerDocument().createElement("rect");
831 connectorElement.parentNode().insertAfter(rect, connectorElement);
832 rect.setAttribute("x", QString::number(bounds.left()));
833 rect.setAttribute("y", QString::number(bounds.top()));
834 rect.setAttribute("width", QString::number(bounds.width()));
835 rect.setAttribute("height", QString::number(bounds.height()));
836 rect.setAttribute("fill", "none");
837 rect.setAttribute("stroke", "none");
838 rect.setAttribute("stroke-width", 0);
839 rect.setAttribute("id", terminalName);
840 svgChanged = true;
841 }
842 }
843 }
844
845 if (svgChanged) {
846 TextUtils::writeUtf8(svgFilePath, TextUtils::removeXMLEntities(dom.toString(4)));
847 }
848 if (fzpChanged) {
849 TextUtils::writeUtf8(fzpFilePath, TextUtils::removeXMLEntities(fzpRoot.ownerDocument().toString(4)));
850 }
851
852 return true;
853 }
854
spaceTitle(QStringList & titles,int openUnits)855 double S2S::spaceTitle(QStringList & titles, int openUnits)
856 {
857 double labelTextMax = 0;
858 foreach (QString title, titles) {
859 double w = stringWidthMM(SchematicRectConstants::LabelTextHeight, title);
860 if (w > labelTextMax) labelTextMax = w;
861 }
862
863 double useMax = qMax(openUnits * SchematicRectConstants::NewUnit, labelTextMax);
864
865 bool changed = false;
866 int ix = 0;
867 while (ix < titles.count() - 1) {
868 QString combined = titles.at(ix);
869 if (!(combined.endsWith("_") || combined.endsWith("-"))) {
870 combined.append(" ");
871 }
872 combined.append(titles.at(ix + 1));
873 double w = stringWidthMM(SchematicRectConstants::LabelTextHeight, combined);
874 if (w <= useMax) {
875 titles[ix] = combined;
876 titles.removeAt(ix + 1);
877 changed = true;
878 continue;
879 }
880
881 ix++;
882 }
883
884 if (!changed) return labelTextMax;
885
886 labelTextMax = 0;
887 foreach (QString title, titles) {
888 double w = stringWidthMM(SchematicRectConstants::LabelTextHeight, title);
889 if (w > labelTextMax) labelTextMax = w;
890 }
891 return labelTextMax;
892 }
893
setSvgDirs(QDir & oldDir,QDir & newDir)894 void S2S::setSvgDirs(QDir & oldDir, QDir & newDir) {
895 m_oldSvgDir = oldDir;
896 m_newSvgDir = newDir;
897 }
898
899