KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_io_geda.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 3
9 * of the License, or (at your option) any later version.
10 *
11 * This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
18 *
19 * This file contains embedded symbol definitions and file format knowledge
20 * derived from the gEDA and Lepton EDA projects:
21 *
22 * gEDA/gaf - Copyright (C) 1998-2010 Ales Hvezda
23 * Copyright (C) 1998-2016 gEDA Contributors
24 * Lepton EDA - Copyright (C) 2017-2024 Lepton EDA Contributors
25 *
26 * Both projects are licensed under the GNU General Public License v2 or later.
27 * See https://github.com/lepton-eda/lepton-eda and
28 * https://github.com/rlutz/geda-gaf
29 */
30
32
33#include <wx/dir.h>
34#include <wx/file.h>
35#include <wx/filename.h>
36#include <wx/textfile.h>
37#include <wx/tokenzr.h>
38#include <wx/log.h>
39
40#include <eda_shape.h>
41#include <lib_id.h>
42#include <page_info.h>
43#include <lib_symbol.h>
44#include <project.h>
45#include <project_sch.h>
46#include <default_values.h>
47#include <sch_bus_entry.h>
48#include <sch_junction.h>
49#include <sch_label.h>
50#include <sch_no_connect.h>
51#include <sch_line.h>
52#include <sch_pin.h>
53#include <sch_screen.h>
54#include <sch_shape.h>
55#include <sch_sheet.h>
56#include <sch_sheet_path.h>
57#include <sch_sheet_pin.h>
58#include <sch_symbol.h>
59#include <sch_bitmap.h>
60#include <sch_text.h>
61#include <schematic.h>
62#include <string_utils.h>
63#include <stroke_params.h>
65#include <base_units.h>
66#include <pin_type.h>
67#include <reporter.h>
69#include <math/util.h>
70#include <reference_image.h>
71#include <wx/base64.h>
72#include <wx/image.h>
73
74#include <algorithm>
75#include <climits>
76#include <vector>
77
78// Default text size for imported text (50 mils)
79static constexpr int GEDA_DEFAULT_TEXT_SIZE_MILS = 50;
80
81// Default body size for fallback rectangular symbols
82static constexpr int DEFAULT_SYMBOL_SIZE_MILS = 200;
83
90static wxString convertOverbars( const wxString& aInput )
91{
92 wxString result;
93 result.reserve( aInput.length() + 8 );
94
95 size_t i = 0;
96
97 while( i < aInput.length() )
98 {
99 if( i + 1 < aInput.length() && aInput[i] == '\\' && aInput[i + 1] == '_' )
100 {
101 size_t barEnd = aInput.find( wxT( "\\_" ), i + 2 );
102
103 if( barEnd != wxString::npos )
104 {
105 result += wxT( "~{" );
106 result += aInput.Mid( i + 2, barEnd - ( i + 2 ) );
107 result += wxT( "}" );
108 i = barEnd + 2;
109 }
110 else
111 {
112 result += aInput[i];
113 i++;
114 }
115 }
116 else if( i + 1 < aInput.length() && aInput[i] == '\\' && aInput[i + 1] == '\\' )
117 {
118 result += '\\';
119 i += 2;
120 }
121 else
122 {
123 result += aInput[i];
124 i++;
125 }
126 }
127
128 return result;
129}
130
131
132// Standard gEDA symbol definitions embedded from the geda-gaf project (GPL v2+).
133// These provide correct pin positions for common symbols so the importer works
134// without requiring a gEDA system installation.
135const std::map<wxString, wxString>& SCH_IO_GEDA::getBuiltinSymbols()
136{
137 static const std::map<wxString, wxString> symbols = {
138 { wxT( "resistor-1.sym" ),
139 wxT( "v 20031231 1\n"
140 "L 600 200 500 0 3 0 0 0 -1 -1\n"
141 "L 500 0 400 200 3 0 0 0 -1 -1\n"
142 "L 400 200 300 0 3 0 0 0 -1 -1\n"
143 "L 300 0 200 200 3 0 0 0 -1 -1\n"
144 "T 300 400 5 10 0 0 0 0 1\n"
145 "device=RESISTOR\n"
146 "L 600 200 700 0 3 0 0 0 -1 -1\n"
147 "L 700 0 750 100 3 0 0 0 -1 -1\n"
148 "P 900 100 750 100 1 0 0\n"
149 "{\n"
150 "T 800 150 5 8 0 1 0 0 1\n"
151 "pinnumber=2\n"
152 "T 800 150 5 8 0 0 0 0 1\n"
153 "pinseq=2\n"
154 "T 800 150 5 8 0 1 0 0 1\n"
155 "pinlabel=2\n"
156 "T 800 150 5 8 0 1 0 0 1\n"
157 "pintype=pas\n"
158 "}\n"
159 "P 0 100 152 100 1 0 0\n"
160 "{\n"
161 "T 100 150 5 8 0 1 0 0 1\n"
162 "pinnumber=1\n"
163 "T 100 150 5 8 0 0 0 0 1\n"
164 "pinseq=1\n"
165 "T 100 150 5 8 0 1 0 0 1\n"
166 "pinlabel=1\n"
167 "T 100 150 5 8 0 1 0 0 1\n"
168 "pintype=pas\n"
169 "}\n"
170 "L 201 200 150 100 3 0 0 0 -1 -1\n"
171 "T 200 300 8 10 1 1 0 0 1\n"
172 "refdes=R?\n" ) },
173
174 { wxT( "resistor-2.sym" ),
175 wxT( "v 20031231 1\n"
176 "B 200 0 600 200 3 0 0 0 -1 -1 0 -1 -1 -1 -1 -1\n"
177 "T 300 400 5 10 0 0 0 0 1\n"
178 "device=RESISTOR\n"
179 "P 900 100 800 100 1 0 0\n"
180 "{\n"
181 "T 800 150 5 8 0 1 0 0 1\n"
182 "pinnumber=2\n"
183 "T 800 150 5 8 0 0 0 0 1\n"
184 "pinseq=2\n"
185 "T 800 150 5 8 0 1 0 0 1\n"
186 "pinlabel=2\n"
187 "T 800 150 5 8 0 1 0 0 1\n"
188 "pintype=pas\n"
189 "}\n"
190 "P 0 100 200 100 1 0 0\n"
191 "{\n"
192 "T 100 150 5 8 0 1 0 0 1\n"
193 "pinnumber=1\n"
194 "T 100 150 5 8 0 0 0 0 1\n"
195 "pinseq=1\n"
196 "T 100 150 5 8 0 1 0 0 1\n"
197 "pinlabel=1\n"
198 "T 100 150 5 8 0 1 0 0 1\n"
199 "pintype=pas\n"
200 "}\n"
201 "T 200 300 8 10 1 1 0 0 1\n"
202 "refdes=R?\n" ) },
203
204 { wxT( "capacitor-1.sym" ),
205 wxT( "v 20050820 1\n"
206 "P 0 200 200 200 1 0 0\n"
207 "{\n"
208 "T 150 250 5 8 0 1 0 6 1\n"
209 "pinnumber=1\n"
210 "T 150 150 5 8 0 1 0 8 1\n"
211 "pinseq=1\n"
212 "T 200 200 9 8 0 1 0 0 1\n"
213 "pinlabel=1\n"
214 "T 200 200 5 8 0 1 0 2 1\n"
215 "pintype=pas\n"
216 "}\n"
217 "P 900 200 700 200 1 0 0\n"
218 "{\n"
219 "T 750 250 5 8 0 1 0 0 1\n"
220 "pinnumber=2\n"
221 "T 750 150 5 8 0 1 0 2 1\n"
222 "pinseq=2\n"
223 "T 700 200 9 8 0 1 0 6 1\n"
224 "pinlabel=2\n"
225 "T 700 200 5 8 0 1 0 8 1\n"
226 "pintype=pas\n"
227 "}\n"
228 "L 400 400 400 0 3 0 0 0 -1 -1\n"
229 "L 500 400 500 0 3 0 0 0 -1 -1\n"
230 "L 700 200 500 200 3 0 0 0 -1 -1\n"
231 "L 400 200 200 200 3 0 0 0 -1 -1\n"
232 "T 200 700 5 10 0 0 0 0 1\n"
233 "device=CAPACITOR\n"
234 "T 200 500 8 10 1 1 0 0 1\n"
235 "refdes=C?\n" ) },
236
237 { wxT( "capacitor-2.sym" ),
238 wxT( "v 20050820 1\n"
239 "P 0 200 200 200 1 0 0\n"
240 "{\n"
241 "T 150 250 5 8 1 1 0 6 1\n"
242 "pinnumber=1\n"
243 "T 200 150 5 8 0 1 0 8 1\n"
244 "pinseq=1\n"
245 "T 250 200 9 8 0 1 0 0 1\n"
246 "pinlabel=+\n"
247 "T 250 200 5 8 0 1 0 2 1\n"
248 "pintype=pas\n"
249 "}\n"
250 "P 900 200 700 200 1 0 0\n"
251 "{\n"
252 "T 750 250 5 8 1 1 0 0 1\n"
253 "pinnumber=2\n"
254 "T 700 150 5 8 0 1 0 2 1\n"
255 "pinseq=2\n"
256 "T 650 200 9 8 0 1 0 6 1\n"
257 "pinlabel=-\n"
258 "T 650 200 5 8 0 1 0 8 1\n"
259 "pintype=pas\n"
260 "}\n"
261 "L 400 400 400 0 3 0 0 0 -1 -1\n"
262 "A 1200 200 700 165 30 3 0 0 0 -1 -1\n"
263 "L 700 200 500 200 3 0 0 0 -1 -1\n"
264 "L 400 200 200 200 3 0 0 0 -1 -1\n"
265 "L 289 400 289 300 3 0 0 0 -1 -1\n"
266 "L 340 349 240 349 3 0 0 0 -1 -1\n"
267 "T 200 700 5 10 0 0 0 0 1\n"
268 "device=POLARIZED_CAPACITOR\n"
269 "T 200 500 8 10 1 1 0 0 1\n"
270 "refdes=C?\n" ) },
271
272 { wxT( "gnd-1.sym" ),
273 wxT( "v 20031231 1\n"
274 "P 100 100 100 300 1 0 1\n"
275 "{\n"
276 "T 158 161 5 4 0 1 0 0 1\n"
277 "pinnumber=1\n"
278 "T 158 161 5 4 0 0 0 0 1\n"
279 "pinseq=1\n"
280 "T 158 161 5 4 0 1 0 0 1\n"
281 "pinlabel=1\n"
282 "T 158 161 5 4 0 1 0 0 1\n"
283 "pintype=pwr\n"
284 "}\n"
285 "L 0 100 200 100 3 0 0 0 -1 -1\n"
286 "L 55 50 145 50 3 0 0 0 -1 -1\n"
287 "L 80 10 120 10 3 0 0 0 -1 -1\n"
288 "T 300 50 8 10 0 0 0 0 1\n"
289 "net=GND:1\n" ) },
290
291 { wxT( "generic-power.sym" ),
292 wxT( "v 20031231 1\n"
293 "P 200 0 200 200 1 0 0\n"
294 "{\n"
295 "T 250 50 5 6 0 1 0 0 1\n"
296 "pinnumber=1\n"
297 "T 250 50 5 6 0 0 0 0 1\n"
298 "pinseq=1\n"
299 "T 250 50 5 6 0 1 0 0 1\n"
300 "pinlabel=1\n"
301 "T 250 50 5 6 0 1 0 0 1\n"
302 "pintype=pwr\n"
303 "}\n"
304 "L 50 200 350 200 3 0 0 0 -1 -1\n"
305 "T 200 250 8 10 1 1 0 3 1\n"
306 "net=Vcc:1\n" ) },
307
308 { wxT( "input-1.sym" ),
309 wxT( "v 20031231 1\n"
310 "P 600 100 800 100 1 0 1\n"
311 "{\n"
312 "T 450 50 5 6 0 1 0 0 1\n"
313 "pinnumber=1\n"
314 "T 450 50 5 6 0 0 0 0 1\n"
315 "pinseq=1\n"
316 "}\n"
317 "L 0 200 0 0 3 0 0 0 -1 -1\n"
318 "L 0 200 500 200 3 0 0 0 -1 -1\n"
319 "L 500 200 600 100 3 0 0 0 -1 -1\n"
320 "L 600 100 500 0 3 0 0 0 -1 -1\n"
321 "L 500 0 0 0 3 0 0 0 -1 -1\n"
322 "T 0 300 5 10 0 0 0 0 1\n"
323 "device=INPUT\n" ) },
324
325 { wxT( "output-1.sym" ),
326 wxT( "v 20031231 1\n"
327 "P 0 100 200 100 1 0 0\n"
328 "{\n"
329 "T 250 50 5 6 0 1 0 0 1\n"
330 "pinnumber=1\n"
331 "T 250 50 5 6 0 0 0 0 1\n"
332 "pinseq=1\n"
333 "}\n"
334 "L 200 200 200 0 3 0 0 0 -1 -1\n"
335 "L 200 200 700 200 3 0 0 0 -1 -1\n"
336 "L 700 200 800 100 3 0 0 0 -1 -1\n"
337 "L 800 100 700 0 3 0 0 0 -1 -1\n"
338 "L 700 0 200 0 3 0 0 0 -1 -1\n"
339 "T 100 300 5 10 0 0 0 0 1\n"
340 "device=OUTPUT\n" ) },
341
342 { wxT( "nc-right-1.sym" ),
343 wxT( "v 20060123 1\n"
344 "P 0 100 200 100 1 0 0\n"
345 "{\n"
346 "T 600 100 5 10 0 0 180 8 1\n"
347 "pinseq=1\n"
348 "T 600 300 5 10 0 0 180 8 1\n"
349 "pinnumber=1\n"
350 "}\n"
351 "L 200 0 200 200 3 0 0 0 -1 -1\n"
352 "T 100 500 8 10 0 0 0 0 1\n"
353 "value=NoConnection\n"
354 "T 250 100 9 10 1 0 0 1 1\n"
355 "NC\n"
356 "T 0 600 8 10 0 0 0 0 1\n"
357 "device=DRC_Directive\n"
358 "T 100 900 8 10 0 0 0 0 1\n"
359 "graphical=1\n" ) },
360
361 { wxT( "nc-left-1.sym" ),
362 wxT( "v 20060123 1\n"
363 "P 400 100 200 100 1 0 1\n"
364 "{\n"
365 "T 0 100 5 10 0 0 0 0 1\n"
366 "pinseq=1\n"
367 "T 0 300 5 10 0 0 0 0 1\n"
368 "pinnumber=1\n"
369 "}\n"
370 "L 200 0 200 200 3 0 0 0 -1 -1\n"
371 "T 100 500 8 10 0 0 0 0 1\n"
372 "value=NoConnection\n"
373 "T 50 100 9 10 1 0 0 5 1\n"
374 "NC\n"
375 "T 0 600 8 10 0 0 0 0 1\n"
376 "device=DRC_Directive\n"
377 "T 100 900 8 10 0 0 0 0 1\n"
378 "graphical=1\n" ) },
379
380 { wxT( "terminal-1.sym" ),
381 wxT( "v 20041228 1\n"
382 "T 310 750 8 10 0 0 0 0 1\n"
383 "device=terminal\n"
384 "P 560 100 900 100 1 0 1\n"
385 "{\n"
386 "T 660 150 5 10 0 0 0 0 1\n"
387 "pinseq=1\n"
388 "T 510 100 5 10 0 1 0 0 1\n"
389 "pinnumber=1\n"
390 "T 510 300 5 10 0 1 0 0 1\n"
391 "pintype=pas\n"
392 "T 800 100 5 10 0 1 0 0 1\n"
393 "pinlabel=terminal\n"
394 "}\n"
395 "V 460 100 100 3 0 0 0 -1 -1 0 -1 -1 -1 -1 -1\n"
396 "T 250 50 8 10 1 1 0 6 1\n"
397 "refdes=T?\n" ) },
398
399 // Power symbols
400 { wxT( "vcc-1.sym" ),
401 wxT( "v 20031231 1\n"
402 "P 200 0 200 200 1 0 0\n"
403 "{\n"
404 "T 250 50 5 6 0 1 0 0 1\n"
405 "pinnumber=1\n"
406 "T 250 50 5 6 0 0 0 0 1\n"
407 "pinseq=1\n"
408 "T 250 50 5 6 0 1 0 0 1\n"
409 "pinlabel=1\n"
410 "T 250 50 5 6 0 1 0 0 1\n"
411 "pintype=pwr\n"
412 "}\n"
413 "L 50 200 350 200 3 0 0 0 -1 -1\n"
414 "T 75 250 9 8 1 0 0 0 1\n"
415 "Vcc\n"
416 "T 450 200 8 10 0 0 0 0 1\n"
417 "net=Vcc:1\n" ) },
418
419 { wxT( "vdd-1.sym" ),
420 wxT( "v 20031231 1\n"
421 "P 200 0 200 200 1 0 0\n"
422 "{\n"
423 "T 250 50 5 6 0 1 0 0 1\n"
424 "pinnumber=1\n"
425 "T 250 50 5 6 0 0 0 0 1\n"
426 "pinseq=1\n"
427 "T 250 50 5 6 0 1 0 0 1\n"
428 "pinlabel=1\n"
429 "T 250 50 5 6 0 1 0 0 1\n"
430 "pintype=pwr\n"
431 "}\n"
432 "L 50 200 350 200 3 0 0 0 -1 -1\n"
433 "T 75 250 9 8 1 0 0 0 1\n"
434 "Vdd\n"
435 "T 450 200 8 10 0 0 0 0 1\n"
436 "net=Vdd:1\n" ) },
437
438 { wxT( "5V-plus-1.sym" ),
439 wxT( "v 20031231 1\n"
440 "P 200 0 200 200 1 0 0\n"
441 "{\n"
442 "T 250 50 5 6 0 1 0 0 1\n"
443 "pinnumber=1\n"
444 "T 250 50 5 6 0 0 0 0 1\n"
445 "pinseq=1\n"
446 "T 250 50 5 6 0 1 0 0 1\n"
447 "pinlabel=1\n"
448 "T 250 50 5 6 0 1 0 0 1\n"
449 "pintype=pwr\n"
450 "}\n"
451 "L 50 200 350 200 3 0 0 0 -1 -1\n"
452 "T 75 250 9 8 1 0 0 0 1\n"
453 "+5V\n"
454 "T 300 0 8 8 0 0 0 0 1\n"
455 "net=+5V:1\n" ) },
456
457 { wxT( "3.3V-plus-1.sym" ),
458 wxT( "v 20031231 1\n"
459 "P 200 0 200 200 1 0 0\n"
460 "{\n"
461 "T 250 50 5 6 0 1 0 0 1\n"
462 "pinnumber=1\n"
463 "T 250 50 5 6 0 0 0 0 1\n"
464 "pinseq=1\n"
465 "T 250 50 5 6 0 1 0 0 1\n"
466 "pinlabel=1\n"
467 "T 250 50 5 6 0 1 0 0 1\n"
468 "pintype=pwr\n"
469 "}\n"
470 "L 50 200 350 200 3 0 0 0 -1 -1\n"
471 "T 75 250 9 8 1 0 0 0 1\n"
472 "+3.3V\n"
473 "T 300 0 8 8 0 0 0 0 1\n"
474 "net=+3.3V:1\n" ) },
475
476 { wxT( "12V-plus-1.sym" ),
477 wxT( "v 20031231 1\n"
478 "P 200 0 200 200 1 0 0\n"
479 "{\n"
480 "T 250 50 5 6 0 1 0 0 1\n"
481 "pinnumber=1\n"
482 "T 250 50 5 6 0 0 0 0 1\n"
483 "pinseq=1\n"
484 "T 250 50 5 6 0 1 0 0 1\n"
485 "pinlabel=1\n"
486 "T 250 50 5 6 0 1 0 0 1\n"
487 "pintype=pwr\n"
488 "}\n"
489 "L 50 200 350 200 3 0 0 0 -1 -1\n"
490 "T 75 250 9 8 1 0 0 0 1\n"
491 "+12V\n"
492 "T 300 0 9 8 0 0 0 0 1\n"
493 "net=+12V:1\n" ) },
494
495 { wxT( "vcc-2.sym" ),
496 wxT( "v 20031231 1\n"
497 "V 200 350 50 3 0 0 0 -1 -1 0 -1 -1 -1 -1 -1\n"
498 "P 200 300 200 0 1 0 1\n"
499 "{\n"
500 "T 300 50 5 10 0 1 0 0 1\n"
501 "pinnumber=1\n"
502 "T 300 50 5 10 0 0 0 0 1\n"
503 "pinseq=1\n"
504 "T 300 50 5 10 0 1 0 0 1\n"
505 "pinlabel=1\n"
506 "T 300 50 5 10 0 1 0 0 1\n"
507 "pintype=pwr\n"
508 "}\n"
509 "T 0 450 9 10 1 0 0 0 1\n"
510 "Vcc\n"
511 "T 400 300 8 8 0 0 0 0 1\n"
512 "net=Vcc:1\n" ) },
513
514 { wxT( "vss-1.sym" ),
515 wxT( "v 20031231 1\n"
516 "P 200 0 200 200 1 0 0\n"
517 "{\n"
518 "T 250 50 5 6 0 1 0 0 1\n"
519 "pinnumber=1\n"
520 "T 250 50 5 6 0 0 0 0 1\n"
521 "pinseq=1\n"
522 "T 250 50 5 6 0 1 0 0 1\n"
523 "pinlabel=1\n"
524 "T 250 50 5 6 0 1 0 0 1\n"
525 "pintype=pwr\n"
526 "}\n"
527 "L 50 200 350 200 3 0 0 0 -1 -1\n"
528 "T 75 250 9 8 1 0 0 0 1\n"
529 "Vss\n"
530 "T 450 200 8 10 0 0 0 0 1\n"
531 "net=Vss:1\n" ) },
532
533 { wxT( "vee-1.sym" ),
534 wxT( "v 20031231 1\n"
535 "P 200 0 200 200 1 0 0\n"
536 "{\n"
537 "T 250 50 5 6 0 1 0 0 1\n"
538 "pinnumber=1\n"
539 "T 250 50 5 6 0 0 0 0 1\n"
540 "pinseq=1\n"
541 "T 250 50 5 6 0 1 0 0 1\n"
542 "pinlabel=1\n"
543 "T 250 50 5 6 0 1 0 0 1\n"
544 "pintype=pwr\n"
545 "}\n"
546 "L 50 200 350 200 3 0 0 0 -1 -1\n"
547 "T 75 250 9 8 1 0 0 0 1\n"
548 "Vee\n"
549 "T 450 200 8 10 0 0 0 0 1\n"
550 "net=Vee:1\n" ) },
551
552 { wxT( "gnd-2.sym" ),
553 wxT( "v 20031231 1\n"
554 "P 100 100 100 300 1 0 1\n"
555 "{\n"
556 "T 158 161 5 4 0 1 0 0 1\n"
557 "pinnumber=1\n"
558 "T 158 161 5 4 0 0 0 0 1\n"
559 "pinseq=1\n"
560 "T 300 200 3 10 1 1 0 0 1\n"
561 "pinlabel=PGND\n"
562 "T 158 161 5 4 0 1 0 0 1\n"
563 "pintype=pwr\n"
564 "}\n"
565 "L 0 100 200 100 3 0 0 0 -1 -1\n"
566 "L 55 50 145 50 3 0 0 0 -1 -1\n"
567 "L 80 10 120 10 3 0 0 0 -1 -1\n"
568 "T 300 50 8 10 0 0 0 0 1\n"
569 "net=PGND:1\n" ) },
570
571 // Diodes
572 { wxT( "diode-1.sym" ),
573 wxT( "v 20031231 1\n"
574 "L 300 400 300 0 3 0 0 0 -1 -1\n"
575 "L 300 400 600 200 3 0 0 0 -1 -1\n"
576 "T 400 600 5 10 0 0 0 0 1\n"
577 "device=DIODE\n"
578 "L 600 200 300 0 3 0 0 0 -1 -1\n"
579 "L 600 400 600 0 3 0 0 0 -1 -1\n"
580 "P 0 200 200 200 1 0 0\n"
581 "{\n"
582 "T 100 250 5 8 0 1 0 0 1\n"
583 "pinnumber=1\n"
584 "T 100 250 5 8 0 0 0 0 1\n"
585 "pinseq=1\n"
586 "T 100 250 5 8 0 1 0 0 1\n"
587 "pinlabel=1\n"
588 "T 100 250 5 8 0 1 0 0 1\n"
589 "pintype=pas\n"
590 "}\n"
591 "P 900 200 700 200 1 0 0\n"
592 "{\n"
593 "T 700 250 5 8 0 1 0 0 1\n"
594 "pinnumber=2\n"
595 "T 700 250 5 8 0 0 0 0 1\n"
596 "pinseq=2\n"
597 "T 700 250 5 8 0 1 0 0 1\n"
598 "pinlabel=2\n"
599 "T 700 250 5 8 0 1 0 0 1\n"
600 "pintype=pas\n"
601 "}\n"
602 "L 700 200 600 200 3 0 0 0 -1 -1\n"
603 "L 300 200 200 200 3 0 0 0 -1 -1\n"
604 "T 300 500 8 10 1 1 0 0 1\n"
605 "refdes=D?\n" ) },
606
607 { wxT( "zener-1.sym" ),
608 wxT( "v 20031231 1\n"
609 "L 300 400 300 0 3 0 0 0 -1 -1\n"
610 "L 600 200 300 0 3 0 0 0 -1 -1\n"
611 "L 600 200 300 400 3 0 0 0 -1 -1\n"
612 "T 400 600 5 10 0 0 0 0 1\n"
613 "device=ZENER_DIODE\n"
614 "L 600 400 600 0 3 0 0 0 -1 -1\n"
615 "P 900 200 700 200 1 0 0\n"
616 "{\n"
617 "T 700 250 5 8 0 1 0 0 1\n"
618 "pinnumber=2\n"
619 "T 700 250 5 8 0 0 0 0 1\n"
620 "pinseq=2\n"
621 "T 700 250 5 8 0 1 0 0 1\n"
622 "pinlabel=2\n"
623 "T 700 250 5 8 0 1 0 0 1\n"
624 "pintype=pas\n"
625 "}\n"
626 "P 200 200 0 200 1 0 1\n"
627 "{\n"
628 "T 100 250 5 8 0 1 0 0 1\n"
629 "pinnumber=1\n"
630 "T 100 250 5 8 0 0 0 0 1\n"
631 "pinseq=1\n"
632 "T 100 250 5 8 0 1 0 0 1\n"
633 "pinlabel=1\n"
634 "T 100 250 5 8 0 1 0 0 1\n"
635 "pintype=pas\n"
636 "}\n"
637 "L 700 200 600 200 3 0 0 0 -1 -1\n"
638 "L 300 200 200 200 3 0 0 0 -1 -1\n"
639 "L 600 400 500 400 3 0 0 0 -1 -1\n"
640 "L 600 0 700 0 3 0 0 0 -1 -1\n"
641 "T 300 500 8 10 1 1 0 0 1\n"
642 "refdes=Z?\n" ) },
643
644 { wxT( "schottky-1.sym" ),
645 wxT( "v 20041228 1\n"
646 "L 300 400 300 0 3 0 0 0 -1 -1\n"
647 "L 300 400 600 200 3 0 0 0 -1 -1\n"
648 "T 322 672 8 10 0 0 0 0 1\n"
649 "device=DIODE\n"
650 "L 600 200 300 0 3 0 0 0 -1 -1\n"
651 "L 600 400 600 0 3 0 0 0 -1 -1\n"
652 "P 0 200 200 200 1 0 0\n"
653 "{\n"
654 "T 205 256 5 8 0 1 0 0 1\n"
655 "pinnumber=2\n"
656 "T 170 286 5 8 0 0 90 0 1\n"
657 "pinseq=2\n"
658 "T -10 81 5 10 0 1 0 0 1\n"
659 "pintype=pas\n"
660 "T 75 288 5 10 0 1 90 0 1\n"
661 "pinlabel=anode\n"
662 "}\n"
663 "P 900 200 700 200 1 0 0\n"
664 "{\n"
665 "T 700 250 5 8 0 1 0 0 1\n"
666 "pinnumber=1\n"
667 "T 730 50 5 8 0 0 0 0 1\n"
668 "pinseq=1\n"
669 "T 798 265 5 10 0 1 0 0 1\n"
670 "pintype=pas\n"
671 "T 931 138 5 10 0 1 0 0 1\n"
672 "pinlabel=cathode\n"
673 "}\n"
674 "L 700 200 600 200 3 0 0 0 -1 -1\n"
675 "L 300 200 200 200 3 0 0 0 -1 -1\n"
676 "A 650 400 50 0 180 3 0 0 0 -1 -1\n"
677 "A 550 0 50 180 180 3 0 0 0 -1 -1\n"
678 "T 300 500 8 10 1 1 0 0 1\n"
679 "refdes=D?\n"
680 "T 567 515 8 10 0 0 0 0 1\n"
681 "numslots=0\n" ) },
682
683 { wxT( "led-1.sym" ),
684 wxT( "v 20210407 2\n"
685 "P 0 200 200 200 1 0 0\n"
686 "{\n"
687 "T 150 250 5 8 1 1 0 6 1\n"
688 "pinnumber=1\n"
689 "T 150 150 5 8 0 1 0 8 1\n"
690 "pinseq=1\n"
691 "T 250 200 9 8 0 1 0 0 1\n"
692 "pinlabel=A\n"
693 "T 250 200 5 8 0 1 0 2 1\n"
694 "pintype=pas\n"
695 "}\n"
696 "P 900 200 700 200 1 0 0\n"
697 "{\n"
698 "T 750 250 5 8 1 1 0 0 1\n"
699 "pinnumber=2\n"
700 "T 750 150 5 8 0 1 0 2 1\n"
701 "pinseq=2\n"
702 "T 650 200 9 8 0 1 0 6 1\n"
703 "pinlabel=K\n"
704 "T 650 200 5 8 0 1 0 8 1\n"
705 "pintype=pas\n"
706 "}\n"
707 "L 400 300 500 200 3 0 0 0 -1 -1\n"
708 "L 500 200 400 100 3 0 0 0 -1 -1\n"
709 "L 400 300 400 100 3 0 0 0 -1 -1\n"
710 "L 500 300 500 100 3 0 0 0 -1 -1\n"
711 "L 500 200 700 200 3 0 0 0 -1 -1\n"
712 "L 400 200 200 200 3 0 0 0 -1 -1\n"
713 "V 450 200 200 3 0 0 0 -1 -1 0 -1 -1 -1 -1 -1\n"
714 "T 800 600 5 10 0 0 0 0 1\n"
715 "device=LED\n"
716 "T 800 400 8 10 1 1 0 0 1\n"
717 "refdes=LED?\n"
718 "T 800 1000 5 10 0 0 0 0 1\n"
719 "numslots=0\n"
720 "T 800 800 5 10 0 0 0 0 1\n"
721 "symversion=0.2\n" ) },
722
723 // Transistors
724 { wxT( "npn-1.sym" ),
725 wxT( "v 20210407 2\n"
726 "L 200 800 200 200 3 0 0 0 -1 -1\n"
727 "T 600 500 5 10 0 0 0 0 1\n"
728 "device=NPN_TRANSISTOR\n"
729 "L 500 800 200 500 3 0 0 0 -1 -1\n"
730 "L 200 500 500 200 3 0 0 0 -1 -1\n"
731 "H 3 0 0 0 -1 -1 1 -1 -1 -1 -1 -1 5\n"
732 "M 410,240\n"
733 "L 501,200\n"
734 "L 455,295\n"
735 "L 435,265\n"
736 "z\n"
737 "P 0 500 200 500 1 0 0\n"
738 "{\n"
739 "T 100 550 5 6 1 1 0 0 1\n"
740 "pinnumber=B\n"
741 "T 100 550 5 6 0 0 0 0 1\n"
742 "pinseq=2\n"
743 "T 100 550 5 6 0 1 0 0 1\n"
744 "pinlabel=B\n"
745 "T 100 550 5 6 0 1 0 0 1\n"
746 "pintype=pas\n"
747 "}\n"
748 "P 500 1000 500 800 1 0 0\n"
749 "{\n"
750 "T 400 850 5 6 1 1 0 0 1\n"
751 "pinnumber=C\n"
752 "T 400 850 5 6 0 0 0 0 1\n"
753 "pinseq=1\n"
754 "T 400 850 5 6 0 1 0 0 1\n"
755 "pinlabel=C\n"
756 "T 400 850 5 6 0 1 0 0 1\n"
757 "pintype=pas\n"
758 "}\n"
759 "P 500 200 500 0 1 0 1\n"
760 "{\n"
761 "T 400 50 5 6 1 1 0 0 1\n"
762 "pinnumber=E\n"
763 "T 400 50 5 6 0 0 0 0 1\n"
764 "pinseq=3\n"
765 "T 400 50 5 6 0 1 0 0 1\n"
766 "pinlabel=E\n"
767 "T 400 50 5 6 0 1 0 0 1\n"
768 "pintype=pas\n"
769 "}\n"
770 "T 600 500 8 10 1 1 0 0 1\n"
771 "refdes=Q?\n" ) },
772
773 { wxT( "pnp-1.sym" ),
774 wxT( "v 20210407 2\n"
775 "L 200 800 200 200 3 0 0 0 -1 -1\n"
776 "T 600 500 5 10 0 0 0 0 1\n"
777 "device=PNP_TRANSISTOR\n"
778 "L 500 800 200 500 3 0 0 0 -1 -1\n"
779 "L 200 500 500 200 3 0 0 0 -1 -1\n"
780 "P 0 500 200 500 1 0 0\n"
781 "{\n"
782 "T 100 550 5 6 1 1 0 0 1\n"
783 "pinnumber=B\n"
784 "T 100 550 5 6 0 0 0 0 1\n"
785 "pinseq=2\n"
786 "T 100 550 5 6 0 1 0 0 1\n"
787 "pinlabel=B\n"
788 "T 100 550 5 6 0 1 0 0 1\n"
789 "pintype=pas\n"
790 "}\n"
791 "P 500 1000 500 800 1 0 0\n"
792 "{\n"
793 "T 400 850 5 6 1 1 0 0 1\n"
794 "pinnumber=C\n"
795 "T 400 850 5 6 0 0 0 0 1\n"
796 "pinseq=1\n"
797 "T 400 850 5 6 0 1 0 0 1\n"
798 "pinlabel=C\n"
799 "T 400 850 5 6 0 1 0 0 1\n"
800 "pintype=pas\n"
801 "}\n"
802 "P 500 200 500 0 1 0 1\n"
803 "{\n"
804 "T 400 50 5 6 1 1 0 0 1\n"
805 "pinnumber=E\n"
806 "T 400 50 5 6 0 0 0 0 1\n"
807 "pinseq=3\n"
808 "T 400 50 5 6 0 1 0 0 1\n"
809 "pinlabel=E\n"
810 "T 400 50 5 6 0 1 0 0 1\n"
811 "pintype=pas\n"
812 "}\n"
813 "T 600 500 8 10 1 1 0 0 1\n"
814 "refdes=Q?\n"
815 "H 3 0 0 0 -1 -1 1 -1 -1 -1 -1 -1 5\n"
816 "M 340,290\n"
817 "L 300,401\n"
818 "L 395,355\n"
819 "L 365,335\n"
820 "z\n" ) },
821
822 { wxT( "nmos-1.sym" ),
823 wxT( "v 20210407 2\n"
824 "L 300 600 300 200 3 0 0 0 -1 -1\n"
825 "T 700 800 5 10 0 0 0 0 1\n"
826 "device=NMOS_TRANSISTOR\n"
827 "L 200 600 200 200 3 0 0 0 -1 -1\n"
828 "L 300 600 500 600 3 0 0 0 -1 -1\n"
829 "L 300 200 500 200 3 0 0 0 -1 -1\n"
830 "L 300 400 500 400 3 0 0 0 -1 -1\n"
831 "L 300 400 400 500 3 0 0 0 -1 -1\n"
832 "L 300 400 400 300 3 0 0 0 -1 -1\n"
833 "P 0 400 200 400 1 0 0\n"
834 "{\n"
835 "T 150 450 5 8 0 1 0 6 1\n"
836 "pinnumber=G\n"
837 "T 150 350 5 8 0 1 0 8 1\n"
838 "pinseq=2\n"
839 "T 200 400 9 8 0 1 0 0 1\n"
840 "pinlabel=G\n"
841 "T 200 400 5 8 0 1 0 2 1\n"
842 "pintype=in\n"
843 "}\n"
844 "P 500 600 500 800 1 0 1\n"
845 "{\n"
846 "T 550 650 5 8 0 1 0 0 1\n"
847 "pinnumber=D\n"
848 "T 550 650 5 8 0 1 0 2 1\n"
849 "pinseq=1\n"
850 "T 500 600 9 8 0 1 0 5 1\n"
851 "pinlabel=D\n"
852 "T 500 400 5 8 0 1 0 5 1\n"
853 "pintype=pas\n"
854 "}\n"
855 "P 500 200 500 0 1 0 1\n"
856 "{\n"
857 "T 550 50 5 8 0 1 0 0 1\n"
858 "pinnumber=S\n"
859 "T 550 50 5 8 0 1 0 2 1\n"
860 "pinseq=3\n"
861 "T 500 200 9 8 0 1 0 3 1\n"
862 "pinlabel=S\n"
863 "T 500 500 5 8 0 1 0 3 1\n"
864 "pintype=pas\n"
865 "}\n"
866 "T 700 600 8 10 1 1 0 0 1\n"
867 "refdes=Q?\n"
868 "T 700 1000 5 10 0 0 0 0 1\n"
869 "symversion=0.2\n" ) },
870
871 { wxT( "pmos-1.sym" ),
872 wxT( "v 20210407 2\n"
873 "L 300 600 300 200 3 0 0 0 -1 -1\n"
874 "T 600 200 5 10 0 0 0 0 1\n"
875 "device=PMOS_TRANSISTOR\n"
876 "L 200 600 200 200 3 0 0 0 -1 -1\n"
877 "L 300 600 500 600 3 0 0 0 -1 -1\n"
878 "L 300 200 500 200 3 0 0 0 -1 -1\n"
879 "L 300 400 500 400 3 0 0 0 -1 -1\n"
880 "L 500 400 400 300 3 0 0 0 -1 -1\n"
881 "L 500 400 400 500 3 0 0 0 -1 -1\n"
882 "P 500 600 500 800 1 0 1\n"
883 "{\n"
884 "T 300 700 5 10 0 1 0 0 1\n"
885 "pinnumber=D\n"
886 "T 300 700 5 10 0 0 0 0 1\n"
887 "pinseq=1\n"
888 "T 300 700 5 10 0 1 0 0 1\n"
889 "pinlabel=D\n"
890 "T 300 700 5 10 0 1 0 0 1\n"
891 "pintype=pas\n"
892 "}\n"
893 "P 500 200 500 0 1 0 1\n"
894 "{\n"
895 "T 300 0 5 10 0 1 0 0 1\n"
896 "pinnumber=S\n"
897 "T 300 0 5 10 0 0 0 0 1\n"
898 "pinseq=3\n"
899 "T 300 0 5 10 0 1 0 0 1\n"
900 "pinlabel=S\n"
901 "T 300 0 5 10 0 1 0 0 1\n"
902 "pintype=pas\n"
903 "}\n"
904 "P 200 400 0 400 1 0 1\n"
905 "{\n"
906 "T 0 500 5 10 0 1 0 0 1\n"
907 "pinnumber=G\n"
908 "T 0 500 5 10 0 0 0 0 1\n"
909 "pinseq=2\n"
910 "T 0 500 5 10 0 1 0 0 1\n"
911 "pinlabel=G\n"
912 "T 0 500 5 10 0 1 0 0 1\n"
913 "pintype=pas\n"
914 "}\n"
915 "T 700 600 8 10 1 1 0 0 1\n"
916 "refdes=Q?\n"
917 "T 1200 500 8 10 0 0 0 0 1\n"
918 "symversion=0.1\n" ) },
919
920 // Analog
921 { wxT( "opamp-1.sym" ),
922 wxT( "v 20050820 1\n"
923 "L 200 800 200 0 3 0 0 0 -1 -1\n"
924 "L 200 800 800 400 3 0 0 0 -1 -1\n"
925 "T 700 800 5 10 0 0 0 0 1\n"
926 "device=OPAMP\n"
927 "L 800 400 200 0 3 0 0 0 -1 -1\n"
928 "L 300 650 300 550 3 0 0 0 -1 -1\n"
929 "L 250 600 350 600 3 0 0 0 -1 -1\n"
930 "L 250 200 350 200 3 0 0 0 -1 -1\n"
931 "P 0 600 200 600 1 0 0\n"
932 "{\n"
933 "T 150 650 5 8 1 1 0 6 1\n"
934 "pinnumber=1\n"
935 "T 150 550 5 8 0 1 0 8 1\n"
936 "pinseq=1\n"
937 "T 250 600 9 8 0 1 0 0 1\n"
938 "pinlabel=in+\n"
939 "T 250 600 5 8 0 1 0 2 1\n"
940 "pintype=in\n"
941 "}\n"
942 "P 0 200 200 200 1 0 0\n"
943 "{\n"
944 "T 150 250 5 8 1 1 0 6 1\n"
945 "pinnumber=2\n"
946 "T 150 150 5 8 0 1 0 8 1\n"
947 "pinseq=2\n"
948 "T 250 200 9 8 0 1 0 0 1\n"
949 "pinlabel=in-\n"
950 "T 250 200 5 8 0 1 0 2 1\n"
951 "pintype=in\n"
952 "}\n"
953 "P 800 400 1000 400 1 0 1\n"
954 "{\n"
955 "T 800 450 5 8 1 1 0 0 1\n"
956 "pinnumber=5\n"
957 "T 800 350 5 8 0 1 0 2 1\n"
958 "pinseq=5\n"
959 "T 750 400 9 8 0 1 0 6 1\n"
960 "pinlabel=out\n"
961 "T 750 400 5 8 0 1 0 8 1\n"
962 "pintype=out\n"
963 "}\n"
964 "P 500 600 500 800 1 0 1\n"
965 "{\n"
966 "T 550 600 5 8 1 1 0 0 1\n"
967 "pinnumber=3\n"
968 "T 550 600 5 8 0 1 0 2 1\n"
969 "pinseq=3\n"
970 "T 500 600 9 8 0 1 0 5 1\n"
971 "pinlabel=V+\n"
972 "T 500 550 5 8 0 1 0 5 1\n"
973 "pintype=pwr\n"
974 "}\n"
975 "P 500 200 500 0 1 0 1\n"
976 "{\n"
977 "T 550 100 5 8 1 1 0 0 1\n"
978 "pinnumber=4\n"
979 "T 550 100 5 8 0 1 0 2 1\n"
980 "pinseq=4\n"
981 "T 500 200 9 8 0 1 0 3 1\n"
982 "pinlabel=V-\n"
983 "T 500 300 5 8 0 1 0 3 1\n"
984 "pintype=pwr\n"
985 "}\n"
986 "T 700 600 8 10 1 1 0 0 1\n"
987 "refdes=U?\n"
988 "T 700 1000 5 10 0 0 0 0 1\n"
989 "numslots=0\n"
990 "T 700 1400 5 10 0 0 0 0 1\n"
991 "symversion=0.1\n" ) },
992
993 { wxT( "inductor-1.sym" ),
994 wxT( "v 20210407 2\n"
995 "P 900 100 750 100 1 0 0\n"
996 "{\n"
997 "T 800 150 5 8 0 1 0 0 1\n"
998 "pinnumber=2\n"
999 "T 800 50 5 8 0 1 0 2 1\n"
1000 "pinseq=2\n"
1001 "T 700 100 9 8 0 1 0 6 1\n"
1002 "pinlabel=2\n"
1003 "T 700 100 5 8 0 1 0 8 1\n"
1004 "pintype=pas\n"
1005 "}\n"
1006 "P 0 100 150 100 1 0 0\n"
1007 "{\n"
1008 "T 100 150 5 8 0 1 0 6 1\n"
1009 "pinnumber=1\n"
1010 "T 100 50 5 8 0 1 0 8 1\n"
1011 "pinseq=1\n"
1012 "T 200 100 9 8 0 1 0 0 1\n"
1013 "pinlabel=1\n"
1014 "T 200 100 5 8 0 1 0 2 1\n"
1015 "pintype=pas\n"
1016 "}\n"
1017 "A 237 100 75 0 180 3 0 0 0 -1 -1\n"
1018 "A 379 100 75 0 180 3 0 0 0 -1 -1\n"
1019 "A 521 100 75 0 180 3 0 0 0 -1 -1\n"
1020 "A 663 100 75 0 180 3 0 0 0 -1 -1\n"
1021 "T 200 500 5 10 0 0 0 0 1\n"
1022 "device=INDUCTOR\n"
1023 "T 200 300 8 10 1 1 0 0 1\n"
1024 "refdes=L?\n"
1025 "T 200 700 5 10 0 0 0 0 1\n"
1026 "symversion=0.2\n" ) },
1027
1028 // 7400-series logic gates
1029 { wxT( "7400-1.sym" ),
1030 wxT( "v 20031231 1\n"
1031 "L 300 200 300 800 3 0 0 0 -1 -1\n"
1032 "T 300 0 9 8 1 0 0 0 1\n"
1033 "7400\n"
1034 "L 300 800 700 800 3 0 0 0 -1 -1\n"
1035 "T 500 900 5 10 0 0 0 0 1\n"
1036 "device=7400\n"
1037 "T 500 1100 5 10 0 0 0 0 1\n"
1038 "slot=1\n"
1039 "T 500 1300 5 10 0 0 0 0 1\n"
1040 "numslots=4\n"
1041 "T 500 1500 5 10 0 0 0 0 1\n"
1042 "slotdef=1:1,2,3\n"
1043 "T 500 1700 5 10 0 0 0 0 1\n"
1044 "slotdef=2:4,5,6\n"
1045 "T 500 1900 5 10 0 0 0 0 1\n"
1046 "slotdef=3:9,10,8\n"
1047 "T 500 2100 5 10 0 0 0 0 1\n"
1048 "slotdef=4:12,13,11\n"
1049 "L 300 200 700 200 3 0 0 0 -1 -1\n"
1050 "A 700 500 300 270 180 3 0 0 0 -1 -1\n"
1051 "V 1050 500 50 6 0 0 0 -1 -1 0 -1 -1 -1 -1 -1\n"
1052 "P 1100 500 1300 500 1 0 1\n"
1053 "{\n"
1054 "T 1100 550 5 8 1 1 0 0 1\n"
1055 "pinnumber=3\n"
1056 "T 1100 450 5 8 0 1 0 2 1\n"
1057 "pinseq=3\n"
1058 "T 950 500 9 8 0 1 0 6 1\n"
1059 "pinlabel=Y\n"
1060 "T 950 500 5 8 0 1 0 8 1\n"
1061 "pintype=out\n"
1062 "}\n"
1063 "P 300 300 0 300 1 0 1\n"
1064 "{\n"
1065 "T 200 350 5 8 1 1 0 6 1\n"
1066 "pinnumber=2\n"
1067 "T 200 250 5 8 0 1 0 8 1\n"
1068 "pinseq=2\n"
1069 "T 350 300 9 8 0 1 0 0 1\n"
1070 "pinlabel=B\n"
1071 "T 350 300 5 8 0 1 0 2 1\n"
1072 "pintype=in\n"
1073 "}\n"
1074 "P 300 700 0 700 1 0 1\n"
1075 "{\n"
1076 "T 200 750 5 8 1 1 0 6 1\n"
1077 "pinnumber=1\n"
1078 "T 200 650 5 8 0 1 0 8 1\n"
1079 "pinseq=1\n"
1080 "T 350 700 9 8 0 1 0 0 1\n"
1081 "pinlabel=A\n"
1082 "T 350 700 5 8 0 1 0 2 1\n"
1083 "pintype=in\n"
1084 "}\n"
1085 "T 300 900 8 10 1 1 0 0 1\n"
1086 "refdes=U?\n"
1087 "T 500 2850 5 10 0 0 0 0 1\n"
1088 "net=Vcc:14\n"
1089 "T 500 3050 5 10 0 0 0 0 1\n"
1090 "net=GND:7\n" ) },
1091
1092 { wxT( "7404-1.sym" ),
1093 wxT( "v 20031231 1\n"
1094 "L 300 800 800 500 3 0 0 0 -1 -1\n"
1095 "T 600 900 5 10 0 0 0 0 1\n"
1096 "device=7404\n"
1097 "T 600 1100 5 10 0 0 0 0 1\n"
1098 "slot=1\n"
1099 "T 600 1300 5 10 0 0 0 0 1\n"
1100 "numslots=6\n"
1101 "T 600 1500 5 10 0 0 0 0 1\n"
1102 "slotdef=1:1,2\n"
1103 "T 600 1700 5 10 0 0 0 0 1\n"
1104 "slotdef=2:3,4\n"
1105 "T 600 1900 5 10 0 0 0 0 1\n"
1106 "slotdef=3:5,6\n"
1107 "T 600 2100 5 10 0 0 0 0 1\n"
1108 "slotdef=4:9,8\n"
1109 "T 600 2300 5 10 0 0 0 0 1\n"
1110 "slotdef=5:11,10\n"
1111 "T 600 2500 5 10 0 0 0 0 1\n"
1112 "slotdef=6:13,12\n"
1113 "L 800 500 300 200 3 0 0 0 -1 -1\n"
1114 "L 300 800 300 200 3 0 0 0 -1 -1\n"
1115 "V 850 500 50 6 0 0 0 -1 -1 0 -1 -1 -1 -1 -1\n"
1116 "P 300 500 0 500 1 0 1\n"
1117 "{\n"
1118 "T 200 550 5 8 1 1 0 6 1\n"
1119 "pinnumber=1\n"
1120 "T 200 450 5 8 0 1 0 8 1\n"
1121 "pinseq=1\n"
1122 "T 350 500 9 8 0 1 0 0 1\n"
1123 "pinlabel=A\n"
1124 "T 350 500 5 8 0 1 0 2 1\n"
1125 "pintype=in\n"
1126 "}\n"
1127 "P 1100 500 900 500 1 0 0\n"
1128 "{\n"
1129 "T 900 550 5 8 1 1 0 0 1\n"
1130 "pinnumber=2\n"
1131 "T 900 450 5 8 0 1 0 2 1\n"
1132 "pinseq=2\n"
1133 "T 750 500 9 8 0 1 0 6 1\n"
1134 "pinlabel=Y\n"
1135 "T 750 500 5 8 0 1 0 8 1\n"
1136 "pintype=out\n"
1137 "}\n"
1138 "T 300 0 9 8 1 0 0 0 1\n"
1139 "7404\n"
1140 "T 300 900 8 10 1 1 0 0 1\n"
1141 "refdes=U?\n"
1142 "T 600 3100 5 10 0 0 0 0 1\n"
1143 "net=Vcc:14\n"
1144 "T 600 3300 5 10 0 0 0 0 1\n"
1145 "net=GND:7\n" ) },
1146
1147 { wxT( "7408-1.sym" ),
1148 wxT( "v 20031231 1\n"
1149 "L 300 200 300 800 3 0 0 0 -1 -1\n"
1150 "T 300 0 9 8 1 0 0 0 1\n"
1151 "7408\n"
1152 "L 300 800 700 800 3 0 0 0 -1 -1\n"
1153 "T 700 900 5 10 0 0 0 0 1\n"
1154 "device=7408\n"
1155 "T 700 1100 5 10 0 0 0 0 1\n"
1156 "slot=1\n"
1157 "T 700 1300 5 10 0 0 0 0 1\n"
1158 "numslots=4\n"
1159 "T 700 1500 5 10 0 0 0 0 1\n"
1160 "slotdef=1:1,2,3\n"
1161 "T 700 1700 5 10 0 0 0 0 1\n"
1162 "slotdef=2:4,5,6\n"
1163 "T 700 1900 5 10 0 0 0 0 1\n"
1164 "slotdef=3:9,10,8\n"
1165 "T 700 2100 5 10 0 0 0 0 1\n"
1166 "slotdef=4:12,13,11\n"
1167 "L 300 200 700 200 3 0 0 0 -1 -1\n"
1168 "A 700 500 300 270 180 3 0 0 0 -1 -1\n"
1169 "P 1000 500 1300 500 1 0 1\n"
1170 "{\n"
1171 "T 1100 550 5 8 1 1 0 0 1\n"
1172 "pinnumber=3\n"
1173 "T 1100 450 5 8 0 1 0 2 1\n"
1174 "pinseq=3\n"
1175 "T 950 500 9 8 0 1 0 6 1\n"
1176 "pinlabel=Y\n"
1177 "T 950 500 5 8 0 1 0 8 1\n"
1178 "pintype=out\n"
1179 "}\n"
1180 "P 300 700 0 700 1 0 1\n"
1181 "{\n"
1182 "T 200 750 5 8 1 1 0 6 1\n"
1183 "pinnumber=1\n"
1184 "T 200 650 5 8 0 1 0 8 1\n"
1185 "pinseq=1\n"
1186 "T 350 700 9 8 0 1 0 0 1\n"
1187 "pinlabel=A\n"
1188 "T 350 700 5 8 0 1 0 2 1\n"
1189 "pintype=in\n"
1190 "}\n"
1191 "P 300 300 0 300 1 0 1\n"
1192 "{\n"
1193 "T 200 350 5 8 1 1 0 6 1\n"
1194 "pinnumber=2\n"
1195 "T 200 250 5 8 0 1 0 8 1\n"
1196 "pinseq=2\n"
1197 "T 350 300 9 8 0 1 0 0 1\n"
1198 "pinlabel=B\n"
1199 "T 350 300 5 8 0 1 0 2 1\n"
1200 "pintype=in\n"
1201 "}\n"
1202 "T 300 900 8 10 1 1 0 0 1\n"
1203 "refdes=U?\n"
1204 "T 700 2700 5 10 0 0 0 0 1\n"
1205 "net=Vcc:14\n"
1206 "T 700 2900 5 10 0 0 0 0 1\n"
1207 "net=GND:7\n" ) },
1208
1209 { wxT( "7432-1.sym" ),
1210 wxT( "v 20031231 1\n"
1211 "L 260 200 600 200 3 0 0 0 -1 -1\n"
1212 "L 260 800 600 800 3 0 0 0 -1 -1\n"
1213 "T 600 900 5 10 0 0 0 0 1\n"
1214 "device=7432\n"
1215 "T 600 1100 5 10 0 0 0 0 1\n"
1216 "slot=1\n"
1217 "T 600 1300 5 10 0 0 0 0 1\n"
1218 "numslots=4\n"
1219 "T 600 1500 5 10 0 0 0 0 1\n"
1220 "slotdef=1:1,2,3\n"
1221 "T 600 1700 5 10 0 0 0 0 1\n"
1222 "slotdef=2:4,5,6\n"
1223 "T 600 1900 5 10 0 0 0 0 1\n"
1224 "slotdef=3:9,10,8\n"
1225 "T 600 2100 5 10 0 0 0 0 1\n"
1226 "slotdef=4:12,13,11\n"
1227 "T 300 0 9 8 1 0 0 0 1\n"
1228 "7432\n"
1229 "A 0 500 400 312 97 3 0 0 0 -1 -1\n"
1230 "P 300 700 0 700 1 0 1\n"
1231 "{\n"
1232 "T 200 750 5 8 1 1 0 6 1\n"
1233 "pinnumber=1\n"
1234 "T 200 650 5 8 0 1 0 8 1\n"
1235 "pinseq=1\n"
1236 "T 350 700 9 8 0 1 0 0 1\n"
1237 "pinlabel=A\n"
1238 "T 350 700 5 8 0 1 0 2 1\n"
1239 "pintype=in\n"
1240 "}\n"
1241 "P 300 300 0 300 1 0 1\n"
1242 "{\n"
1243 "T 200 350 5 8 1 1 0 6 1\n"
1244 "pinnumber=2\n"
1245 "T 200 250 5 8 0 1 0 8 1\n"
1246 "pinseq=2\n"
1247 "T 350 300 9 8 0 1 0 0 1\n"
1248 "pinlabel=B\n"
1249 "T 350 300 5 8 0 1 0 2 1\n"
1250 "pintype=in\n"
1251 "}\n"
1252 "P 1300 500 988 500 1 0 0\n"
1253 "{\n"
1254 "T 1100 550 5 8 1 1 0 0 1\n"
1255 "pinnumber=3\n"
1256 "T 1100 450 5 8 0 1 0 2 1\n"
1257 "pinseq=3\n"
1258 "T 950 500 9 8 0 1 0 6 1\n"
1259 "pinlabel=Y\n"
1260 "T 950 500 5 8 0 1 0 8 1\n"
1261 "pintype=out\n"
1262 "}\n"
1263 "A 600 600 400 270 76 3 0 0 0 -1 -1\n"
1264 "A 600 400 400 14 76 3 0 0 0 -1 -1\n"
1265 "T 300 900 8 10 1 1 0 0 1\n"
1266 "refdes=U?\n"
1267 "T 600 2700 5 10 0 0 0 0 1\n"
1268 "net=Vcc:14\n"
1269 "T 600 2900 5 10 0 0 0 0 1\n"
1270 "net=GND:7\n" ) },
1271
1272 { wxT( "7486-1.sym" ),
1273 wxT( "v 20031231 1\n"
1274 "L 260 200 600 200 3 0 0 0 -1 -1\n"
1275 "L 260 800 600 800 3 0 0 0 -1 -1\n"
1276 "T 700 900 5 10 0 0 0 0 1\n"
1277 "device=7486\n"
1278 "T 700 1100 5 10 0 0 0 0 1\n"
1279 "slot=1\n"
1280 "T 700 1300 5 10 0 0 0 0 1\n"
1281 "numslots=4\n"
1282 "T 700 1500 5 10 0 0 0 0 1\n"
1283 "slotdef=1:1,2,3\n"
1284 "T 700 1700 5 10 0 0 0 0 1\n"
1285 "slotdef=2:4,5,6\n"
1286 "T 700 1900 5 10 0 0 0 0 1\n"
1287 "slotdef=3:9,10,8\n"
1288 "T 700 2100 5 10 0 0 0 0 1\n"
1289 "slotdef=4:12,13,11\n"
1290 "A 0 500 400 312 97 3 0 0 0 -1 -1\n"
1291 "A -100 500 400 312 97 3 0 0 0 -1 -1\n"
1292 "P 300 700 0 700 1 0 1\n"
1293 "{\n"
1294 "T 150 750 5 8 1 1 0 6 1\n"
1295 "pinnumber=1\n"
1296 "T 150 650 5 8 0 1 0 8 1\n"
1297 "pinseq=1\n"
1298 "T 350 700 9 8 0 1 0 0 1\n"
1299 "pinlabel=A\n"
1300 "T 350 700 5 8 0 1 0 2 1\n"
1301 "pintype=in\n"
1302 "}\n"
1303 "P 300 300 0 300 1 0 1\n"
1304 "{\n"
1305 "T 150 350 5 8 1 1 0 6 1\n"
1306 "pinnumber=2\n"
1307 "T 150 250 5 8 0 1 0 8 1\n"
1308 "pinseq=2\n"
1309 "T 350 300 9 8 0 1 0 0 1\n"
1310 "pinlabel=B\n"
1311 "T 350 300 5 8 0 1 0 2 1\n"
1312 "pintype=in\n"
1313 "}\n"
1314 "P 988 500 1300 500 1 0 1\n"
1315 "{\n"
1316 "T 1100 550 5 8 1 1 0 0 1\n"
1317 "pinnumber=3\n"
1318 "T 1100 450 5 8 0 1 0 2 1\n"
1319 "pinseq=3\n"
1320 "T 950 500 9 8 0 1 0 6 1\n"
1321 "pinlabel=Y\n"
1322 "T 950 500 5 8 0 1 0 8 1\n"
1323 "pintype=out\n"
1324 "}\n"
1325 "A 600 600 400 270 76 3 0 0 0 -1 -1\n"
1326 "A 600 400 400 14 76 3 0 0 0 -1 -1\n"
1327 "T 300 900 8 10 1 1 0 0 1\n"
1328 "refdes=U?\n"
1329 "T 300 0 9 8 1 0 0 0 1\n"
1330 "7486\n"
1331 "T 700 2700 5 10 0 0 0 0 1\n"
1332 "net=Vcc:14\n"
1333 "T 700 2900 5 10 0 0 0 0 1\n"
1334 "net=GND:7\n" ) },
1335
1336 { wxT( "7402-1.sym" ),
1337 wxT( "v 20031231 1\n"
1338 "L 260 200 600 200 3 0 0 0 -1 -1\n"
1339 "L 260 800 600 800 3 0 0 0 -1 -1\n"
1340 "T 600 900 5 10 0 0 0 0 1\n"
1341 "device=7402\n"
1342 "T 600 1100 5 10 0 0 0 0 1\n"
1343 "slot=1\n"
1344 "T 600 1300 5 10 0 0 0 0 1\n"
1345 "numslots=4\n"
1346 "T 600 1500 5 10 0 0 0 0 1\n"
1347 "slotdef=1:1,2,3\n"
1348 "T 600 1700 5 10 0 0 0 0 1\n"
1349 "slotdef=2:4,5,6\n"
1350 "T 600 1900 5 10 0 0 0 0 1\n"
1351 "slotdef=3:10,8,9\n"
1352 "T 600 2100 5 10 0 0 0 0 1\n"
1353 "slotdef=4:13,11,12\n"
1354 "T 300 0 9 8 1 0 0 0 1\n"
1355 "7402\n"
1356 "A 0 500 400 312 97 3 0 0 0 -1 -1\n"
1357 "P 300 700 0 700 1 0 1\n"
1358 "{\n"
1359 "T 200 750 5 8 1 1 0 6 1\n"
1360 "pinnumber=3\n"
1361 "T 200 650 5 8 0 1 0 8 1\n"
1362 "pinseq=3\n"
1363 "T 350 700 9 8 0 1 0 0 1\n"
1364 "pinlabel=B\n"
1365 "T 350 700 5 8 0 1 0 2 1\n"
1366 "pintype=in\n"
1367 "}\n"
1368 "P 300 300 0 300 1 0 1\n"
1369 "{\n"
1370 "T 200 350 5 8 1 1 0 6 1\n"
1371 "pinnumber=2\n"
1372 "T 200 250 5 8 0 1 0 8 1\n"
1373 "pinseq=2\n"
1374 "T 350 300 9 8 0 1 0 0 1\n"
1375 "pinlabel=A\n"
1376 "T 350 300 5 8 0 1 0 2 1\n"
1377 "pintype=in\n"
1378 "}\n"
1379 "V 1038 500 50 6 0 0 0 -1 -1 0 -1 -1 -1 -1 -1\n"
1380 "P 1300 500 1088 500 1 0 0\n"
1381 "{\n"
1382 "T 1100 550 5 8 1 1 0 0 1\n"
1383 "pinnumber=1\n"
1384 "T 1100 450 5 8 0 1 0 2 1\n"
1385 "pinseq=1\n"
1386 "T 950 500 9 8 0 1 0 6 1\n"
1387 "pinlabel=Y\n"
1388 "T 950 500 5 8 0 1 0 8 1\n"
1389 "pintype=out\n"
1390 "}\n"
1391 "A 600 600 400 270 76 3 0 0 0 -1 -1\n"
1392 "A 600 400 400 14 76 3 0 0 0 -1 -1\n"
1393 "T 300 900 8 10 1 1 0 0 1\n"
1394 "refdes=U?\n"
1395 "T 600 2700 5 10 0 0 0 0 1\n"
1396 "net=Vcc:14\n"
1397 "T 600 2900 5 10 0 0 0 0 1\n"
1398 "net=GND:7\n" ) },
1399
1400 // Miscellaneous
1401 { wxT( "busripper-1.sym" ),
1402 wxT( "v 20031231 1\n"
1403 "T 0 400 5 8 0 0 0 0 1\n"
1404 "device=none\n"
1405 "P 0 0 100 100 1 0 0\n"
1406 "{\n"
1407 "T 0 500 5 8 0 0 0 0 1\n"
1408 "pinseq=1\n"
1409 "T 0 600 5 8 0 0 0 0 1\n"
1410 "pinnumber=1\n"
1411 "T 0 700 5 8 0 0 0 0 1\n"
1412 "pintype=pas\n"
1413 "T 0 800 5 8 0 0 0 0 1\n"
1414 "pinlabel=netside\n"
1415 "}\n"
1416 "T 0 300 5 8 0 0 0 0 1\n"
1417 "graphical=1\n"
1418 "L 200 200 100 100 10 30 0 0 -1 -1\n" ) },
1419
1420 { wxT( "busripper-2.sym" ),
1421 wxT( "v 20031231 1\n"
1422 "T 0 400 5 8 0 0 0 0 1\n"
1423 "device=none\n"
1424 "P 0 0 0 100 1 0 0\n"
1425 "{\n"
1426 "T 0 500 5 8 0 0 0 0 1\n"
1427 "pinseq=1\n"
1428 "T 0 600 5 8 0 0 0 0 1\n"
1429 "pinnumber=1\n"
1430 "T 0 700 5 8 0 0 0 0 1\n"
1431 "pintype=pas\n"
1432 "T 0 800 5 8 0 0 0 0 1\n"
1433 "pinlabel=netside\n"
1434 "}\n"
1435 "T 0 300 5 8 0 0 0 0 1\n"
1436 "graphical=1\n"
1437 "L 0 100 100 200 3 0 0 0 -1 -1\n"
1438 "L 0 100 -100 200 3 0 0 0 -1 -1\n" ) },
1439
1440 { wxT( "title-B.sym" ),
1441 wxT( "v 20031231 1\n"
1442 "B 0 0 17000 11000 15 0 0 0 -1 -1 0 -1 -1 -1 -1 -1\n"
1443 "T 14400 1500 5 10 0 0 0 0 1\n"
1444 "graphical=1\n"
1445 "L 12900 600 12900 0 15 0 0 0 -1 -1\n"
1446 "T 9500 400 15 8 1 0 0 0 1\n"
1447 "FILE:\n"
1448 "T 13000 400 15 8 1 0 0 0 1\n"
1449 "REVISION:\n"
1450 "T 13000 100 15 8 1 0 0 0 1\n"
1451 "DRAWN BY:\n"
1452 "T 9500 100 15 8 1 0 0 0 1\n"
1453 "PAGE\n"
1454 "T 11200 100 15 8 1 0 0 0 1\n"
1455 "OF\n"
1456 "T 9500 700 15 8 1 0 0 0 1\n"
1457 "TITLE\n"
1458 "B 9400 0 7600 1400 15 0 0 0 -1 -1 0 -1 -1 -1 -1 -1\n"
1459 "L 9400 600 17000 600 15 0 0 0 -1 -1\n" ) },
1460 };
1461
1462 return symbols;
1463}
1464
1465
1466// ==========================================================================
1467// Construction
1468// ==========================================================================
1469
1471 SCH_IO( wxS( "gEDA/gschem Schematic" ) ),
1472 m_screen( nullptr ),
1473 m_rootSheet( nullptr ),
1474 m_schematic( nullptr ),
1475 m_maxY( 0 ),
1476 m_releaseVersion( 0 ),
1478 m_symLibraryInitialized( false ),
1479 m_powerCounter( 0 ),
1480 m_properties( nullptr )
1481{
1482}
1483
1484
1485SCH_IO_GEDA::~SCH_IO_GEDA() = default;
1486
1487
1488// ==========================================================================
1489// Format detection
1490// ==========================================================================
1491
1492bool SCH_IO_GEDA::CanReadSchematicFile( const wxString& aFileName ) const
1493{
1494 wxFileName fn( aFileName );
1495
1496 if( fn.GetExt().CmpNoCase( wxT( "sch" ) ) != 0 )
1497 return false;
1498
1499 wxTextFile file;
1500
1501 if( !file.Open( aFileName ) )
1502 return false;
1503
1504 if( file.GetLineCount() == 0 )
1505 return false;
1506
1507 wxString firstLine = file.GetFirstLine().Trim( false );
1508
1509 if( !firstLine.StartsWith( wxT( "v " ) ) )
1510 return false;
1511
1512 wxStringTokenizer tok( firstLine );
1513 tok.GetNextToken(); // skip 'v'
1514
1515 if( !tok.HasMoreTokens() )
1516 return false;
1517
1518 wxString dateStr = tok.GetNextToken();
1519
1520 if( dateStr.length() != 8 )
1521 return false;
1522
1523 long dateVal = 0;
1524 return dateStr.ToLong( &dateVal ) && dateVal >= 19700101;
1525}
1526
1527
1528// ==========================================================================
1529// Coordinate transformation
1530// ==========================================================================
1531
1532int SCH_IO_GEDA::toKiCadDist( int aMils ) const
1533{
1534 return static_cast<int>( static_cast<int64_t>( aMils ) * MILS_TO_IU );
1535}
1536
1537
1538VECTOR2I SCH_IO_GEDA::toKiCad( int aGedaX, int aGedaY ) const
1539{
1540 return VECTOR2I( toKiCadDist( aGedaX ), toKiCadDist( m_maxY - aGedaY ) );
1541}
1542
1543
1544// ==========================================================================
1545// Version parsing
1546// ==========================================================================
1547
1548bool SCH_IO_GEDA::parseVersionLine( const wxString& aLine )
1549{
1550 wxStringTokenizer tokenizer( aLine );
1551
1552 if( tokenizer.CountTokens() < 2 )
1553 return false;
1554
1555 wxString v = tokenizer.GetNextToken();
1556
1557 if( v != wxT( "v" ) )
1558 return false;
1559
1560 wxString dateStr = tokenizer.GetNextToken();
1561 dateStr.ToLong( &m_releaseVersion );
1562
1563 if( m_releaseVersion < 19700101 )
1564 return false;
1565
1566 // File format version is optional in very old files
1567 if( tokenizer.HasMoreTokens() )
1568 tokenizer.GetNextToken().ToLong( &m_fileFormatVersion );
1569 else
1571
1572 return true;
1573}
1574
1575
1576// ==========================================================================
1577// Attribute parsing
1578// ==========================================================================
1579
1580std::vector<SCH_IO_GEDA::GEDA_ATTR> SCH_IO_GEDA::parseAttributes( wxTextFile& aFile,
1581 size_t& aLineIdx )
1582{
1583 std::vector<GEDA_ATTR> attrs;
1584
1585 while( aLineIdx < aFile.GetLineCount() )
1586 {
1587 wxString line = aFile.GetLine( aLineIdx );
1588
1589 if( line.Trim() == wxT( "}" ) )
1590 {
1591 aLineIdx++;
1592 break;
1593 }
1594
1595 if( line.StartsWith( wxT( "T " ) ) )
1596 {
1597 // T x y color size visibility show_name_value angle alignment num_lines
1598 wxStringTokenizer tok( line );
1599 tok.GetNextToken(); // skip 'T'
1600
1601 long x = 0, y = 0, color = 0, size = 0, vis = 0, showNV = 0, angle = 0, align = 0;
1602 long numLines = 1;
1603
1604 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x );
1605 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y );
1606 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
1607 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &size );
1608 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &vis );
1609 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &showNV );
1610 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &angle );
1611 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &align );
1612 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &numLines );
1613
1614 aLineIdx++;
1615
1616 wxString textContent;
1617
1618 for( long i = 0; i < numLines && aLineIdx < aFile.GetLineCount(); i++ )
1619 {
1620 if( i > 0 )
1621 textContent += wxT( "\n" );
1622
1623 textContent += aFile.GetLine( aLineIdx );
1624 aLineIdx++;
1625 }
1626
1627 int eqPos = textContent.Find( '=' );
1628
1629 if( eqPos != wxNOT_FOUND )
1630 {
1631 GEDA_ATTR attr;
1632 attr.name = textContent.Left( eqPos );
1633 attr.value = textContent.Mid( eqPos + 1 );
1634
1635 // Strip surrounding double quotes from attribute values
1636 if( attr.value.length() >= 2
1637 && attr.value.StartsWith( wxT( "\"" ) )
1638 && attr.value.EndsWith( wxT( "\"" ) ) )
1639 {
1640 attr.value = attr.value.Mid( 1, attr.value.length() - 2 );
1641 }
1642
1643 attr.x = static_cast<int>( x );
1644 attr.y = static_cast<int>( y );
1645 attr.size = static_cast<int>( size );
1646 attr.angle = static_cast<int>( angle );
1647 attr.align = static_cast<int>( align );
1648 attr.visible = ( vis == 1 );
1649 attr.showNV = static_cast<int>( showNV );
1650 attrs.push_back( attr );
1651 }
1652
1653 continue;
1654 }
1655
1656 aLineIdx++;
1657 }
1658
1659 return attrs;
1660}
1661
1662
1663wxString SCH_IO_GEDA::findAttr( const std::vector<GEDA_ATTR>& aAttrs,
1664 const wxString& aName ) const
1665{
1666 for( const GEDA_ATTR& attr : aAttrs )
1667 {
1668 if( attr.name == aName )
1669 return attr.value;
1670 }
1671
1672 return wxEmptyString;
1673}
1674
1675
1676const SCH_IO_GEDA::GEDA_ATTR* SCH_IO_GEDA::findAttrStruct( const std::vector<GEDA_ATTR>& aAttrs,
1677 const wxString& aName ) const
1678{
1679 for( const GEDA_ATTR& attr : aAttrs )
1680 {
1681 if( attr.name == aName )
1682 return &attr;
1683 }
1684
1685 return nullptr;
1686}
1687
1688
1689std::vector<SCH_IO_GEDA::GEDA_ATTR> SCH_IO_GEDA::maybeParseAttributes( wxTextFile& aFile,
1690 size_t& aLineIdx )
1691{
1692 if( aLineIdx < aFile.GetLineCount() && aFile.GetLine( aLineIdx ).Trim() == wxT( "{" ) )
1693 {
1694 aLineIdx++;
1695 return parseAttributes( aFile, aLineIdx );
1696 }
1697
1698 return {};
1699}
1700
1701
1702// ==========================================================================
1703// Style mapping
1704// ==========================================================================
1705
1707{
1708 switch( aDashStyle )
1709 {
1710 case 1: return LINE_STYLE::DOT;
1711 case 2: return LINE_STYLE::DASH;
1712 case 3: return LINE_STYLE::DASHDOT;
1713 case 4: return LINE_STYLE::DASHDOTDOT;
1714 default: return LINE_STYLE::DEFAULT;
1715 }
1716}
1717
1718
1720{
1721 switch( aFillType )
1722 {
1723 case 1: return FILL_T::FILLED_SHAPE;
1724 case 2: return FILL_T::CROSS_HATCH;
1725 case 3: return FILL_T::HATCH;
1726 default: return FILL_T::NO_FILL;
1727 }
1728}
1729
1730
1731// ==========================================================================
1732// Orientation mapping
1733// ==========================================================================
1734
1735int SCH_IO_GEDA::toKiCadOrientation( int aAngle, int aMirror ) const
1736{
1737 int orientation = SYM_ORIENT_0;
1738
1739 switch( aAngle )
1740 {
1741 case 0: orientation = SYM_ORIENT_0; break;
1742 case 90: orientation = SYM_ORIENT_90; break;
1743 case 180: orientation = SYM_ORIENT_180; break;
1744 case 270: orientation = SYM_ORIENT_270; break;
1745 default: orientation = SYM_ORIENT_0; break;
1746 }
1747
1748 if( aMirror )
1749 orientation |= SYM_MIRROR_Y;
1750
1751 return orientation;
1752}
1753
1754
1756{
1757 wxString libName;
1758
1759 if( m_schematic )
1760 libName = m_schematic->Project().GetProjectName();
1761
1762 if( libName.IsEmpty() )
1763 libName = m_filename.GetName();
1764
1765 if( libName.IsEmpty() )
1766 libName = wxT( "noname" );
1767
1768 libName += wxT( "-geda-import" );
1769 libName = LIB_ID::FixIllegalChars( libName, true ).wx_str();
1770 return libName;
1771}
1772
1773
1774// ==========================================================================
1775// Object parsers - connectivity
1776// ==========================================================================
1777
1778void SCH_IO_GEDA::parseComponent( const wxString& aLine, wxTextFile& aFile, size_t& aLineIdx )
1779{
1780 // Flush any previous pending component before starting a new one
1782
1783 // C x y selectable angle mirror basename.sym
1784 wxStringTokenizer tok( aLine );
1785 tok.GetNextToken(); // skip 'C'
1786
1787 long x = 0, y = 0, selectable = 0, angle = 0, mirror = 0;
1788
1789 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x );
1790 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y );
1791 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &selectable );
1792 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &angle );
1793 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &mirror );
1794
1795 wxString basename;
1796
1797 if( tok.HasMoreTokens() )
1798 basename = tok.GetNextToken();
1799
1800 // gEDA prefixes embedded component basenames with "EMBEDDED"
1801 if( basename.StartsWith( wxT( "EMBEDDED" ) ) )
1802 basename = basename.Mid( 8 );
1803
1804 m_pendingComp = std::make_unique<PENDING_COMPONENT>();
1805 m_pendingComp->basename = basename;
1806 m_pendingComp->x = static_cast<int>( x );
1807 m_pendingComp->y = static_cast<int>( y );
1808 m_pendingComp->selectable = static_cast<int>( selectable );
1809 m_pendingComp->angle = static_cast<int>( angle );
1810 m_pendingComp->mirror = static_cast<int>( mirror );
1811
1812 m_pendingComp->attrs = maybeParseAttributes( aFile, aLineIdx );
1813}
1814
1815
1816void SCH_IO_GEDA::parseNet( const wxString& aLine, wxTextFile& aFile, size_t& aLineIdx )
1817{
1818 // N x1 y1 x2 y2 color
1819 wxStringTokenizer tok( aLine );
1820 tok.GetNextToken(); // skip 'N'
1821
1822 long x1 = 0, y1 = 0, x2 = 0, y2 = 0;
1823
1824 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x1 );
1825 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y1 );
1826 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x2 );
1827 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y2 );
1828
1829 VECTOR2I start = toKiCad( static_cast<int>( x1 ), static_cast<int>( y1 ) );
1830 VECTOR2I end = toKiCad( static_cast<int>( x2 ), static_cast<int>( y2 ) );
1831
1832 auto wire = std::make_unique<SCH_LINE>( start, LAYER_WIRE );
1833 wire->SetEndPoint( end );
1834 m_screen->Append( wire.release() );
1835
1836 trackEndpoint( static_cast<int>( x1 ), static_cast<int>( y1 ) );
1837 trackEndpoint( static_cast<int>( x2 ), static_cast<int>( y2 ) );
1838
1839 std::vector<GEDA_ATTR> attrs = maybeParseAttributes( aFile, aLineIdx );
1840 wxString netname = findAttr( attrs, wxT( "netname" ) );
1841
1842 if( !netname.IsEmpty() )
1843 {
1844 auto label = std::make_unique<SCH_LABEL>( start, netname );
1845 int textSize = toKiCadDist( GEDA_DEFAULT_TEXT_SIZE_MILS / 2 );
1846 label->SetTextSize( VECTOR2I( textSize, textSize ) );
1847 m_screen->Append( label.release() );
1848 }
1849}
1850
1851
1852void SCH_IO_GEDA::parseBus( const wxString& aLine, wxTextFile& aFile, size_t& aLineIdx )
1853{
1854 // U x1 y1 x2 y2 color [ripperdir]
1855 wxStringTokenizer tok( aLine );
1856 tok.GetNextToken(); // skip 'U'
1857
1858 long x1 = 0, y1 = 0, x2 = 0, y2 = 0, color = 0, ripperDir = 0;
1859
1860 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x1 );
1861 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y1 );
1862 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x2 );
1863 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y2 );
1864 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
1865
1866 if( m_releaseVersion > 20020825 )
1867 {
1868 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &ripperDir );
1869 }
1870
1871 VECTOR2I start = toKiCad( static_cast<int>( x1 ), static_cast<int>( y1 ) );
1872 VECTOR2I end = toKiCad( static_cast<int>( x2 ), static_cast<int>( y2 ) );
1873
1874 auto line = std::make_unique<SCH_LINE>( start, LAYER_BUS );
1875 line->SetEndPoint( end );
1876 m_screen->Append( line.release() );
1877
1878 m_busSegments.push_back( { start, end, static_cast<int>( ripperDir ) } );
1879
1880 maybeParseAttributes( aFile, aLineIdx );
1881}
1882
1883
1884void SCH_IO_GEDA::parsePin( const wxString& aLine, wxTextFile& aFile, size_t& aLineIdx )
1885{
1886 // P x1 y1 x2 y2 color [pintype whichend]
1887 wxStringTokenizer tok( aLine );
1888 tok.GetNextToken(); // skip 'P'
1889
1890 long x1 = 0, y1 = 0, x2 = 0, y2 = 0, color = 0, pintype = 0, whichend = 0;
1891
1892 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x1 );
1893 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y1 );
1894 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x2 );
1895 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y2 );
1896 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
1897
1898 if( m_releaseVersion > 20020825 )
1899 {
1900 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &pintype );
1901 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &whichend );
1902 }
1903
1904 VECTOR2I start = toKiCad( static_cast<int>( x1 ), static_cast<int>( y1 ) );
1905 VECTOR2I end = toKiCad( static_cast<int>( x2 ), static_cast<int>( y2 ) );
1906
1907 // In schematic context, pin lines become wire stubs for connectivity
1908 auto wire = std::make_unique<SCH_LINE>( start, LAYER_WIRE );
1909 wire->SetEndPoint( end );
1910 m_screen->Append( wire.release() );
1911
1912 VECTOR2I connPt = ( whichend == 0 ) ? start : end;
1913 int connX = ( whichend == 0 ) ? static_cast<int>( x1 ) : static_cast<int>( x2 );
1914 int connY = ( whichend == 0 ) ? static_cast<int>( y1 ) : static_cast<int>( y2 );
1915 trackEndpoint( connX, connY );
1916
1917 std::vector<GEDA_ATTR> attrs = maybeParseAttributes( aFile, aLineIdx );
1918
1919 wxString pinlabel = findAttr( attrs, wxT( "pinlabel" ) );
1920
1921 if( !pinlabel.IsEmpty() )
1922 {
1923 auto label = std::make_unique<SCH_LABEL>( connPt, pinlabel );
1924 int textSize = toKiCadDist( GEDA_DEFAULT_TEXT_SIZE_MILS / 2 );
1925 label->SetTextSize( VECTOR2I( textSize, textSize ) );
1926 m_screen->Append( label.release() );
1927 }
1928}
1929
1930
1931void SCH_IO_GEDA::parseEmbeddedComponent( wxTextFile& aFile, size_t& aLineIdx )
1932{
1933 if( !m_pendingComp )
1934 return;
1935
1936 auto libSym = std::make_unique<LIB_SYMBOL>( m_pendingComp->basename );
1937
1938 // Parse until ] closing bracket
1939 while( aLineIdx < aFile.GetLineCount() )
1940 {
1941 wxString line = aFile.GetLine( aLineIdx );
1942
1943 if( line.Trim() == wxT( "]" ) )
1944 {
1945 aLineIdx++;
1946 break;
1947 }
1948
1949 if( line.IsEmpty() )
1950 {
1951 aLineIdx++;
1952 continue;
1953 }
1954
1955 wxChar type = line[0];
1956 aLineIdx++;
1957
1958 switch( type )
1959 {
1960 case 'P':
1961 {
1962 wxStringTokenizer tok( line );
1963 tok.GetNextToken(); // skip 'P'
1964 long px1 = 0, py1 = 0, px2 = 0, py2 = 0, pc = 0, pt = 0, pw = 0;
1965
1966 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &px1 );
1967 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &py1 );
1968 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &px2 );
1969 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &py2 );
1970 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &pc );
1971 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &pt );
1972 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &pw );
1973
1974 std::vector<GEDA_ATTR> pinAttrs = maybeParseAttributes( aFile, aLineIdx );
1975 addSymbolPin( *libSym, static_cast<int>( px1 ), static_cast<int>( py1 ),
1976 static_cast<int>( px2 ), static_cast<int>( py2 ),
1977 static_cast<int>( pw ), pinAttrs );
1978 break;
1979 }
1980
1981 case 'L':
1982 case 'B':
1983 case 'V':
1984 case 'A':
1985 case 'H':
1986 addSymbolGraphic( *libSym, line, aFile, aLineIdx, type );
1987 break;
1988
1989 case 'T':
1990 {
1991 // Skip text lines in embedded symbols (they're attribute text)
1992 wxStringTokenizer tok( line );
1993 tok.GetNextToken(); // skip 'T'
1994 long numLines = 1;
1995
1996 // Parse enough to get numLines (9th field)
1997 for( int i = 0; i < 8 && tok.HasMoreTokens(); i++ )
1998 {
1999 if( i == 7 )
2000 tok.GetNextToken().ToLong( &numLines );
2001 else
2002 tok.GetNextToken();
2003 }
2004
2005 for( long i = 0; i < numLines && aLineIdx < aFile.GetLineCount(); i++ )
2006 aLineIdx++;
2007
2008 maybeParseAttributes( aFile, aLineIdx );
2009 break;
2010 }
2011
2012 case '{':
2013 {
2014 // Skip nested attribute block
2015 while( aLineIdx < aFile.GetLineCount() )
2016 {
2017 if( aFile.GetLine( aLineIdx ).Trim() == wxT( "}" ) )
2018 {
2019 aLineIdx++;
2020 break;
2021 }
2022
2023 aLineIdx++;
2024 }
2025
2026 break;
2027 }
2028
2029 case 'v':
2030 default:
2031 break;
2032 }
2033 }
2034
2035 m_pendingComp->embedded = true;
2036 m_pendingComp->embeddedSym = std::move( libSym );
2037}
2038
2039
2040// ==========================================================================
2041// Object parsers - graphics
2042// ==========================================================================
2043
2044void SCH_IO_GEDA::parseText( const wxString& aLine, wxTextFile& aFile, size_t& aLineIdx )
2045{
2046 // T x y color size visibility show_name_value angle alignment num_lines
2047 // Old formats may omit alignment and/or num_lines fields
2048 wxStringTokenizer tok( aLine );
2049 tok.GetNextToken(); // skip 'T'
2050
2051 long x = 0, y = 0, color = 0, size = 0, vis = 0, showNV = 0, angle = 0, align = 0;
2052 long numLines = 1;
2053
2054 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x );
2055 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y );
2056 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
2057 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &size );
2058 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &vis );
2059 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &showNV );
2060 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &angle );
2061
2062 if( m_fileFormatVersion >= 1 )
2063 {
2064 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &align );
2065 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &numLines );
2066 }
2067 else if( m_releaseVersion >= 20000220 )
2068 {
2069 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &align );
2070
2071 numLines = 1;
2072 }
2073 else
2074 {
2075 align = 0;
2076 numLines = 1;
2077 }
2078
2079 wxString textContent;
2080
2081 for( long i = 0; i < numLines && aLineIdx < aFile.GetLineCount(); i++ )
2082 {
2083 if( i > 0 )
2084 textContent += wxT( "\n" );
2085
2086 textContent += aFile.GetLine( aLineIdx );
2087 aLineIdx++;
2088 }
2089
2090 textContent = convertOverbars( textContent );
2091
2092 if( vis == 0 )
2093 {
2094 maybeParseAttributes( aFile, aLineIdx );
2095 return;
2096 }
2097
2098 // Handle attribute text (contains '=') with show_name_value field
2099 int eqPos = textContent.Find( '=' );
2100
2101 if( eqPos != wxNOT_FOUND )
2102 {
2103 wxString name = textContent.Left( eqPos );
2104 wxString value = textContent.Mid( eqPos + 1 );
2105
2106 switch( static_cast<int>( showNV ) )
2107 {
2108 case 1: textContent = value; break;
2109 case 2: textContent = name; break;
2110 default: textContent = name + wxT( "=" ) + value; break;
2111 }
2112 }
2113
2114 VECTOR2I pos = toKiCad( static_cast<int>( x ), static_cast<int>( y ) );
2115
2116 auto text = std::make_unique<SCH_TEXT>( pos, textContent );
2117
2118 int textSize = toKiCadDist( static_cast<int>( size ) * 10 );
2119
2120 if( textSize < toKiCadDist( 10 ) )
2121 textSize = toKiCadDist( 10 );
2122
2123 text->SetTextSize( VECTOR2I( textSize, textSize ) );
2124
2125 // gEDA allows arbitrary angles but KiCad text only supports orthogonal.
2126 // Snap to the nearest 90-degree increment.
2127 int normAngle = ( ( static_cast<int>( angle ) % 360 ) + 360 ) % 360;
2128 int snapped = ( ( normAngle + 45 ) / 90 ) * 90;
2129
2130 if( snapped == 360 )
2131 snapped = 0;
2132
2133 switch( snapped )
2134 {
2135 case 90: text->SetTextAngle( ANGLE_90 ); break;
2136 case 180: text->SetTextAngle( ANGLE_180 ); break;
2137 case 270: text->SetTextAngle( ANGLE_270 ); break;
2138 default: break;
2139 }
2140
2141 // Horizontal alignment: gEDA align / 3 gives column (0=left, 1=center, 2=right)
2142 switch( static_cast<int>( align ) / 3 )
2143 {
2144 case 1: text->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER ); break;
2145 case 2: text->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT ); break;
2146 default: text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT ); break;
2147 }
2148
2149 // Vertical alignment: gEDA align % 3 gives row (0=bottom, 1=middle, 2=top)
2150 switch( static_cast<int>( align ) % 3 )
2151 {
2152 case 1: text->SetVertJustify( GR_TEXT_V_ALIGN_CENTER ); break;
2153 case 2: text->SetVertJustify( GR_TEXT_V_ALIGN_TOP ); break;
2154 default: text->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM ); break;
2155 }
2156
2157 m_screen->Append( text.release() );
2158 maybeParseAttributes( aFile, aLineIdx );
2159}
2160
2161
2162void SCH_IO_GEDA::parseLine( const wxString& aLine, wxTextFile& aFile, size_t& aLineIdx )
2163{
2164 // L x1 y1 x2 y2 color [width capstyle dashstyle dashlength dashspace]
2165 wxStringTokenizer tok( aLine );
2166 tok.GetNextToken(); // skip 'L'
2167
2168 long x1 = 0, y1 = 0, x2 = 0, y2 = 0, color = 0, width = 0;
2169 long capstyle = 0, dashstyle = 0, dashlength = 0, dashspace = 0;
2170
2171 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x1 );
2172 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y1 );
2173 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x2 );
2174 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y2 );
2175 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
2176
2177 if( m_releaseVersion > 20000704 )
2178 {
2179 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &width );
2180 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &capstyle );
2181 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashstyle );
2182 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashlength );
2183 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashspace );
2184 }
2185
2186 VECTOR2I start = toKiCad( static_cast<int>( x1 ), static_cast<int>( y1 ) );
2187 VECTOR2I end = toKiCad( static_cast<int>( x2 ), static_cast<int>( y2 ) );
2188
2189 auto line = std::make_unique<SCH_LINE>( start, LAYER_NOTES );
2190 line->SetEndPoint( end );
2191 line->SetStroke( STROKE_PARAMS( toKiCadDist( static_cast<int>( width ) ),
2192 toLineStyle( static_cast<int>( dashstyle ) ) ) );
2193 m_screen->Append( line.release() );
2194 maybeParseAttributes( aFile, aLineIdx );
2195}
2196
2197
2198void SCH_IO_GEDA::parseBox( const wxString& aLine, wxTextFile& aFile, size_t& aLineIdx )
2199{
2200 // B x y width height color [linewidth capstyle dashstyle dashlength dashspace
2201 // filltype fillwidth a1 p1 a2 p2]
2202 wxStringTokenizer tok( aLine );
2203 tok.GetNextToken(); // skip 'B'
2204
2205 long x = 0, y = 0, w = 0, h = 0, color = 0, linewidth = 0;
2206 long capstyle = 0, dashstyle = 0, dashlength = 0, dashspace = 0;
2207 long filltype = 0, fillwidth = 0, a1 = 0, p1 = 0, a2 = 0, p2 = 0;
2208
2209 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x );
2210 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y );
2211 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &w );
2212 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &h );
2213 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
2214
2215 if( m_releaseVersion > 20000704 )
2216 {
2217 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &linewidth );
2218 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &capstyle );
2219 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashstyle );
2220 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashlength );
2221 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashspace );
2222 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &filltype );
2223 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &fillwidth );
2224 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &a1 );
2225 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &p1 );
2226 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &a2 );
2227 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &p2 );
2228 }
2229
2230 VECTOR2I topLeft = toKiCad( static_cast<int>( x ), static_cast<int>( y + h ) );
2231 VECTOR2I botRight = toKiCad( static_cast<int>( x + w ), static_cast<int>( y ) );
2232
2233 auto rect = std::make_unique<SCH_SHAPE>( SHAPE_T::RECTANGLE, LAYER_NOTES );
2234 rect->SetStart( topLeft );
2235 rect->SetEnd( botRight );
2236 rect->SetStroke( STROKE_PARAMS( toKiCadDist( static_cast<int>( linewidth ) ),
2237 toLineStyle( static_cast<int>( dashstyle ) ) ) );
2238
2239 FILL_T fill = toFillType( static_cast<int>( filltype ) );
2240
2241 if( fill != FILL_T::NO_FILL )
2242 {
2243 rect->SetFilled( true );
2244 rect->SetFillMode( fill );
2245 }
2246
2247 m_screen->Append( rect.release() );
2248 maybeParseAttributes( aFile, aLineIdx );
2249}
2250
2251
2252void SCH_IO_GEDA::parseCircle( const wxString& aLine, wxTextFile& aFile, size_t& aLineIdx )
2253{
2254 // V cx cy radius color [linewidth capstyle dashstyle dashlength dashspace
2255 // filltype fillwidth a1 p1 a2 p2]
2256 wxStringTokenizer tok( aLine );
2257 tok.GetNextToken(); // skip 'V'
2258
2259 long cx = 0, cy = 0, radius = 0, color = 0, linewidth = 0;
2260 long capstyle = 0, dashstyle = 0, dashlength = 0, dashspace = 0;
2261 long filltype = 0, fillwidth = 0, a1 = 0, p1 = 0, a2 = 0, p2 = 0;
2262
2263 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &cx );
2264 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &cy );
2265 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &radius );
2266 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
2267
2268 if( m_releaseVersion > 20000704 )
2269 {
2270 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &linewidth );
2271 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &capstyle );
2272 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashstyle );
2273 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashlength );
2274 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashspace );
2275 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &filltype );
2276 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &fillwidth );
2277 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &a1 );
2278 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &p1 );
2279 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &a2 );
2280 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &p2 );
2281 }
2282
2283 VECTOR2I center = toKiCad( static_cast<int>( cx ), static_cast<int>( cy ) );
2284
2285 auto circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE, LAYER_NOTES );
2286 circle->SetCenter( center );
2287 circle->SetEnd( VECTOR2I( center.x + toKiCadDist( static_cast<int>( radius ) ), center.y ) );
2288 circle->SetStroke( STROKE_PARAMS( toKiCadDist( static_cast<int>( linewidth ) ),
2289 toLineStyle( static_cast<int>( dashstyle ) ) ) );
2290
2291 FILL_T fill = toFillType( static_cast<int>( filltype ) );
2292
2293 if( fill != FILL_T::NO_FILL )
2294 {
2295 circle->SetFilled( true );
2296 circle->SetFillMode( fill );
2297 }
2298
2299 m_screen->Append( circle.release() );
2300 maybeParseAttributes( aFile, aLineIdx );
2301}
2302
2303
2304void SCH_IO_GEDA::parseArc( const wxString& aLine, wxTextFile& aFile, size_t& aLineIdx )
2305{
2306 // A cx cy radius startangle sweepangle color [linewidth capstyle dashstyle dashlength dashspace]
2307 wxStringTokenizer tok( aLine );
2308 tok.GetNextToken(); // skip 'A'
2309
2310 long cx = 0, cy = 0, radius = 0, startAngle = 0, sweepAngle = 0;
2311 long color = 0, linewidth = 0, capstyle = 0, dashstyle = 0;
2312 long dashlength = 0, dashspace = 0;
2313
2314 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &cx );
2315 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &cy );
2316 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &radius );
2317 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &startAngle );
2318 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &sweepAngle );
2319 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
2320
2321 if( m_releaseVersion > 20000704 )
2322 {
2323 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &linewidth );
2324 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &capstyle );
2325 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashstyle );
2326 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashlength );
2327 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashspace );
2328 }
2329
2330 VECTOR2I center = toKiCad( static_cast<int>( cx ), static_cast<int>( cy ) );
2331 int r = toKiCadDist( static_cast<int>( radius ) );
2332
2333 double startRad = DEG2RAD( static_cast<double>( startAngle ) );
2334 double endRad = DEG2RAD( static_cast<double>( startAngle + sweepAngle ) );
2335
2336 // Y-flip negates the Y component of angle calculations
2337 VECTOR2I arcStart( center.x + KiROUND( r * cos( startRad ) ),
2338 center.y - KiROUND( r * sin( startRad ) ) );
2339 VECTOR2I arcEnd( center.x + KiROUND( r * cos( endRad ) ),
2340 center.y - KiROUND( r * sin( endRad ) ) );
2341
2342 auto arc = std::make_unique<SCH_SHAPE>( SHAPE_T::ARC, LAYER_NOTES );
2343 arc->SetCenter( center );
2344
2345 // Y-flip reverses angular sweep direction, so swap start and end
2346 // to preserve the original arc visual appearance.
2347 arc->SetStart( arcEnd );
2348 arc->SetEnd( arcStart );
2349
2350 arc->SetStroke( STROKE_PARAMS( toKiCadDist( static_cast<int>( linewidth ) ),
2351 toLineStyle( static_cast<int>( dashstyle ) ) ) );
2352 m_screen->Append( arc.release() );
2353 maybeParseAttributes( aFile, aLineIdx );
2354}
2355
2356
2357void SCH_IO_GEDA::parsePath( const wxString& aLine, wxTextFile& aFile, size_t& aLineIdx )
2358{
2359 // H color width capstyle dashstyle dashlength dashspace
2360 // filltype fillwidth a1 p1 a2 p2 numlines
2361 // followed by numlines of SVG-style path data
2362 wxStringTokenizer tok( aLine );
2363 tok.GetNextToken(); // skip 'H'
2364
2365 long color = 0, width = 0, capstyle = 0, dashstyle = 0;
2366 long dashlength = 0, dashspace = 0, filltype = 0, fillwidth = 0;
2367 long a1 = 0, p1 = 0, a2 = 0, p2 = 0, numlines = 0;
2368
2369 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
2370 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &width );
2371 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &capstyle );
2372 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashstyle );
2373 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashlength );
2374 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashspace );
2375 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &filltype );
2376 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &fillwidth );
2377 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &a1 );
2378 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &p1 );
2379 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &a2 );
2380 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &p2 );
2381 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &numlines );
2382
2383 // Collect all path data lines
2384 wxString pathData;
2385
2386 for( long i = 0; i < numlines && aLineIdx < aFile.GetLineCount(); i++ )
2387 {
2388 if( i > 0 )
2389 pathData += wxT( " " );
2390
2391 pathData += aFile.GetLine( aLineIdx ).Trim();
2392 aLineIdx++;
2393 }
2394
2395 VECTOR2I startPt;
2396 VECTOR2I currentPt;
2397 STROKE_PARAMS stroke( toKiCadDist( static_cast<int>( width ) ),
2398 toLineStyle( static_cast<int>( dashstyle ) ) );
2399
2400 auto emitSeg = [&]( const VECTOR2I& aFrom, const VECTOR2I& aTo )
2401 {
2402 auto seg = std::make_unique<SCH_LINE>( aFrom, LAYER_NOTES );
2403 seg->SetEndPoint( aTo );
2404 seg->SetStroke( stroke );
2405 m_screen->Append( seg.release() );
2406 };
2407
2408 wxStringTokenizer pathTok( pathData, wxT( " ,\t" ) );
2409
2410 // For relative (lowercase) SVG commands, deltas are in gEDA mils (Y-up).
2411 // Convert to KiCad IU (Y-down) by scaling X and negating+scaling Y.
2412 auto relToAbs = [&]( long dx, long dy ) -> VECTOR2I
2413 {
2414 return currentPt + VECTOR2I( toKiCadDist( static_cast<int>( dx ) ),
2415 -toKiCadDist( static_cast<int>( dy ) ) );
2416 };
2417
2418 while( pathTok.HasMoreTokens() )
2419 {
2420 wxString token = pathTok.GetNextToken();
2421
2422 if( token == wxT( "M" ) )
2423 {
2424 long px = 0, py = 0;
2425
2426 if( pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &px )
2427 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &py ) )
2428 {
2429 currentPt = toKiCad( static_cast<int>( px ), static_cast<int>( py ) );
2430 startPt = currentPt;
2431 }
2432 }
2433 else if( token == wxT( "m" ) )
2434 {
2435 long dx = 0, dy = 0;
2436
2437 if( pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &dx )
2438 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &dy ) )
2439 {
2440 currentPt = relToAbs( dx, dy );
2441 startPt = currentPt;
2442 }
2443 }
2444 else if( token == wxT( "L" ) )
2445 {
2446 long px = 0, py = 0;
2447
2448 if( pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &px )
2449 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &py ) )
2450 {
2451 VECTOR2I pt = toKiCad( static_cast<int>( px ), static_cast<int>( py ) );
2452 emitSeg( currentPt, pt );
2453 currentPt = pt;
2454 }
2455 }
2456 else if( token == wxT( "l" ) )
2457 {
2458 long dx = 0, dy = 0;
2459
2460 if( pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &dx )
2461 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &dy ) )
2462 {
2463 VECTOR2I pt = relToAbs( dx, dy );
2464 emitSeg( currentPt, pt );
2465 currentPt = pt;
2466 }
2467 }
2468 else if( token == wxT( "C" ) )
2469 {
2470 long cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, px = 0, py = 0;
2471
2472 if( pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &cx1 )
2473 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &cy1 )
2474 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &cx2 )
2475 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &cy2 )
2476 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &px )
2477 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &py ) )
2478 {
2479 VECTOR2I cp1 = toKiCad( static_cast<int>( cx1 ), static_cast<int>( cy1 ) );
2480 VECTOR2I cp2 = toKiCad( static_cast<int>( cx2 ), static_cast<int>( cy2 ) );
2481 VECTOR2I endPt = toKiCad( static_cast<int>( px ), static_cast<int>( py ) );
2482
2483 auto bezier = std::make_unique<SCH_SHAPE>( SHAPE_T::BEZIER, LAYER_NOTES );
2484 bezier->SetStart( currentPt );
2485 bezier->SetBezierC1( cp1 );
2486 bezier->SetBezierC2( cp2 );
2487 bezier->SetEnd( endPt );
2488 bezier->SetStroke( stroke );
2489 bezier->RebuildBezierToSegmentsPointsList(
2490 schIUScale.mmToIU( ARC_LOW_DEF_MM ) );
2491 m_screen->Append( bezier.release() );
2492 currentPt = endPt;
2493 }
2494 }
2495 else if( token == wxT( "c" ) )
2496 {
2497 long cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, dx = 0, dy = 0;
2498
2499 if( pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &cx1 )
2500 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &cy1 )
2501 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &cx2 )
2502 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &cy2 )
2503 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &dx )
2504 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &dy ) )
2505 {
2506 VECTOR2I cp1 = relToAbs( cx1, cy1 );
2507 VECTOR2I cp2 = relToAbs( cx2, cy2 );
2508 VECTOR2I endPt = relToAbs( dx, dy );
2509
2510 auto bezier = std::make_unique<SCH_SHAPE>( SHAPE_T::BEZIER, LAYER_NOTES );
2511 bezier->SetStart( currentPt );
2512 bezier->SetBezierC1( cp1 );
2513 bezier->SetBezierC2( cp2 );
2514 bezier->SetEnd( endPt );
2515 bezier->SetStroke( stroke );
2516 bezier->RebuildBezierToSegmentsPointsList(
2517 schIUScale.mmToIU( ARC_LOW_DEF_MM ) );
2518 m_screen->Append( bezier.release() );
2519 currentPt = endPt;
2520 }
2521 }
2522 else if( token == wxT( "Z" ) || token == wxT( "z" ) )
2523 {
2524 if( currentPt != startPt )
2525 {
2526 emitSeg( currentPt, startPt );
2527 currentPt = startPt;
2528 }
2529 }
2530 }
2531
2532 maybeParseAttributes( aFile, aLineIdx );
2533}
2534
2535
2536void SCH_IO_GEDA::parsePicture( const wxString& aLine, wxTextFile& aFile, size_t& aLineIdx )
2537{
2538 // G x y width height angle mirrored embedded
2539 wxStringTokenizer tok( aLine );
2540 tok.GetNextToken(); // skip 'G'
2541
2542 long x = 0, y = 0, w = 0, h = 0, angle = 0, mirror = 0, embedded = 0;
2543
2544 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x );
2545 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y );
2546 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &w );
2547 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &h );
2548 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &angle );
2549 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &mirror );
2550 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &embedded );
2551
2552 wxString filename;
2553
2554 if( aLineIdx < aFile.GetLineCount() )
2555 {
2556 filename = aFile.GetLine( aLineIdx ).Trim().Trim( false );
2557 aLineIdx++;
2558 }
2559
2560 wxString base64Data;
2561
2562 if( embedded )
2563 {
2564 while( aLineIdx < aFile.GetLineCount() )
2565 {
2566 wxString dataLine = aFile.GetLine( aLineIdx );
2567 aLineIdx++;
2568
2569 if( dataLine.Trim() == wxT( "." ) )
2570 break;
2571
2572 base64Data += dataLine.Trim();
2573 }
2574 }
2575
2576 auto bitmap = std::make_unique<SCH_BITMAP>();
2577 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
2578 bool loaded = false;
2579
2580 int savedLoadFlags = wxImage::GetDefaultLoadFlags();
2581 wxImage::SetDefaultLoadFlags( savedLoadFlags & ~wxImage::Load_Verbose );
2582
2583 if( embedded && !base64Data.IsEmpty() )
2584 {
2585 wxMemoryBuffer buf = wxBase64Decode( base64Data );
2586
2587 if( buf.GetDataLen() > 0 )
2588 loaded = refImage.ReadImageFile( buf );
2589 }
2590 else if( !filename.IsEmpty() )
2591 {
2592 wxFileName imgFile( filename );
2593
2594 if( !imgFile.IsAbsolute() && m_filename.IsOk() )
2595 imgFile.MakeAbsolute( m_filename.GetPath() );
2596
2597 if( imgFile.FileExists() )
2598 loaded = refImage.ReadImageFile( imgFile.GetFullPath() );
2599 }
2600
2601 wxImage::SetDefaultLoadFlags( savedLoadFlags );
2602
2603 if( !loaded )
2604 return;
2605
2606 // gEDA specifies x,y as the lower-left corner of the picture bounding box.
2607 // Compute center position from the lower-left corner + size.
2608 int gCenterX = static_cast<int>( x ) + static_cast<int>( w ) / 2;
2609 int gCenterY = static_cast<int>( y ) + static_cast<int>( h ) / 2;
2610 VECTOR2I center = toKiCad( gCenterX, gCenterY );
2611
2612 int targetWidth = toKiCadDist( static_cast<int>( w ) );
2613 VECTOR2I imgSize = refImage.GetSize();
2614
2615 if( imgSize.x > 0 )
2616 {
2617 double scaleFactor = static_cast<double>( targetWidth ) / imgSize.x;
2618 refImage.SetImageScale( scaleFactor );
2619 }
2620
2621 bitmap->SetPosition( center );
2622
2623 if( mirror )
2624 bitmap->MirrorHorizontally( center.x );
2625
2626 for( long a = angle; a > 0; a -= 90 )
2627 bitmap->Rotate( center, false );
2628
2629 m_maxY = std::max( m_maxY, static_cast<int>( y + h ) );
2630 m_screen->Append( bitmap.release() );
2631}
2632
2633
2634// ==========================================================================
2635// Symbol loading
2636// ==========================================================================
2637
2639{
2641 return;
2642
2644
2645 // Check GEDADATA environment variable first (highest priority for system symbols)
2646 wxString gedaData;
2647
2648 if( wxGetEnv( wxT( "GEDADATA" ), &gedaData ) && !gedaData.IsEmpty() )
2649 {
2650 wxString envSymDir = gedaData + wxFileName::GetPathSeparator() + wxT( "sym" );
2651
2652 if( wxDir::Exists( envSymDir ) )
2653 scanSymbolDir( envSymDir );
2654 }
2655
2656 // Scan standard gEDA and Lepton-EDA symbol directories
2657 static const wxString defaultPaths[] = {
2658 wxT( "/usr/share/gEDA/sym" ),
2659 wxT( "/usr/share/geda-symbols" ),
2660 wxT( "/usr/local/share/gEDA/sym" ),
2661 wxT( "/usr/local/share/geda-symbols" ),
2662 wxT( "/usr/share/lepton-eda/sym" ),
2663 wxT( "/usr/local/share/lepton-eda/sym" ),
2664 };
2665
2666 for( const wxString& dir : defaultPaths )
2667 {
2668 if( wxDir::Exists( dir ) )
2669 scanSymbolDir( dir );
2670 }
2671
2672 // Scan XDG data directories for Lepton-EDA symbols.
2673 // $XDG_DATA_DIRS defaults to /usr/local/share:/usr/share per the spec.
2674 wxString xdgDataDirs;
2675
2676 if( wxGetEnv( wxT( "XDG_DATA_DIRS" ), &xdgDataDirs ) && !xdgDataDirs.IsEmpty() )
2677 {
2678 wxStringTokenizer xdgTok( xdgDataDirs, wxT( ":" ) );
2679
2680 while( xdgTok.HasMoreTokens() )
2681 {
2682 wxString dataDir = xdgTok.GetNextToken();
2683
2684 if( dataDir.IsEmpty() )
2685 continue;
2686
2687 wxString leptonSymDir = dataDir + wxFileName::GetPathSeparator()
2688 + wxT( "lepton-eda" ) + wxFileName::GetPathSeparator()
2689 + wxT( "sym" );
2690
2691 if( wxDir::Exists( leptonSymDir ) )
2692 scanSymbolDir( leptonSymDir );
2693
2694 wxString gedaSymDir = dataDir + wxFileName::GetPathSeparator()
2695 + wxT( "gEDA" ) + wxFileName::GetPathSeparator()
2696 + wxT( "sym" );
2697
2698 if( wxDir::Exists( gedaSymDir ) )
2699 scanSymbolDir( gedaSymDir );
2700 }
2701 }
2702
2703 // Scan relative to the schematic file location.
2704 // Properties-based paths are handled at the end regardless of whether
2705 // a valid schematic path exists.
2706 wxString schDir;
2707
2708 if( m_filename.IsOk() )
2709 schDir = m_filename.GetPath();
2710
2711 // Scan user-level RC files for additional library paths
2712 wxString homeDir = wxGetHomeDir();
2713
2714 if( !homeDir.IsEmpty() )
2715 {
2716 wxString userGedaDir = homeDir + wxFileName::GetPathSeparator() + wxT( ".gEDA" );
2717
2718 parseRcFileForLibraries( userGedaDir + wxFileName::GetPathSeparator() + wxT( "gafrc" ),
2719 userGedaDir );
2720 parseRcFileForLibraries( userGedaDir + wxFileName::GetPathSeparator() + wxT( "gschemrc" ),
2721 userGedaDir );
2722
2723 wxString leptonDir;
2724 wxString sep( wxFileName::GetPathSeparator() );
2725
2726 if( wxGetEnv( wxT( "XDG_CONFIG_HOME" ), &leptonDir ) && !leptonDir.IsEmpty() )
2727 leptonDir += sep + wxT( "lepton-eda" );
2728 else
2729 leptonDir = homeDir + sep + wxT( ".config" ) + sep + wxT( "lepton-eda" );
2730
2731 parseRcFileForLibraries( leptonDir + wxFileName::GetPathSeparator() + wxT( "gafrc" ),
2732 leptonDir );
2733 }
2734
2735 // Parse project-level RC files and scan schematic-relative directories
2736 if( !schDir.IsEmpty() )
2737 {
2738 parseRcFileForLibraries( schDir + wxFileName::GetPathSeparator() + wxT( "gafrc" ),
2739 schDir );
2740 parseRcFileForLibraries( schDir + wxFileName::GetPathSeparator() + wxT( "gschemrc" ),
2741 schDir );
2742
2743 // Parse Lepton-EDA INI-style config files (lepton.conf or geda.conf)
2744 // for [libs] section with component-library entries
2745 static const wxString configNames[] = {
2746 wxT( "lepton.conf" ),
2747 wxT( "geda.conf" ),
2748 };
2749
2750 for( const wxString& configName : configNames )
2751 {
2752 wxString confPath = schDir + wxFileName::GetPathSeparator() + configName;
2753
2754 if( !wxFileExists( confPath ) )
2755 continue;
2756
2757 wxTextFile confFile;
2758
2759 if( !confFile.Open( confPath ) )
2760 continue;
2761
2762 bool inLibsSection = false;
2763
2764 for( size_t i = 0; i < confFile.GetLineCount(); ++i )
2765 {
2766 wxString line = confFile.GetLine( i );
2767 line.Trim( true );
2768 line.Trim( false );
2769
2770 if( line.IsEmpty() || line[0] == '#' || line[0] == ';' )
2771 continue;
2772
2773 if( line[0] == '[' )
2774 {
2775 inLibsSection = ( line.CmpNoCase( wxT( "[libs]" ) ) == 0 );
2776 continue;
2777 }
2778
2779 if( !inLibsSection )
2780 continue;
2781
2782 if( line.StartsWith( wxT( "component-library" ) ) )
2783 {
2784 int eqPos = line.Find( '=' );
2785
2786 if( eqPos == wxNOT_FOUND )
2787 continue;
2788
2789 wxString libPath = line.Mid( eqPos + 1 ).Trim( false ).Trim( true );
2790 wxFileName libDir( libPath );
2791
2792 if( !libDir.IsAbsolute() )
2793 {
2794 libDir.MakeAbsolute( schDir );
2795 libPath = libDir.GetFullPath();
2796 }
2797
2798 if( wxDir::Exists( libPath ) )
2799 scanSymbolDir( libPath );
2800 }
2801 }
2802
2803 break;
2804 }
2805
2806 scanSymbolDir( schDir );
2807
2808 wxString symDir = schDir + wxFileName::GetPathSeparator() + wxT( "sym" );
2809
2810 if( wxDir::Exists( symDir ) )
2811 scanSymbolDir( symDir );
2812
2813 wxFileName parentDir( schDir );
2814 parentDir.RemoveLastDir();
2815 wxString parentSymDir =
2816 parentDir.GetPath() + wxFileName::GetPathSeparator() + wxT( "sym" );
2817
2818 if( wxDir::Exists( parentSymDir ) )
2819 scanSymbolDir( parentSymDir );
2820 }
2821
2822 if( m_properties )
2823 {
2824 auto it = m_properties->find( "sym_search_paths" );
2825
2826 if( it != m_properties->end() )
2827 {
2828 wxString paths = it->second;
2829 wxStringTokenizer tok( paths, wxT( "\n;" ) );
2830
2831 while( tok.HasMoreTokens() )
2832 {
2833 wxString dir = tok.GetNextToken().Trim().Trim( false );
2834
2835 if( !dir.IsEmpty() && wxDir::Exists( dir ) )
2836 scanSymbolDir( dir );
2837 }
2838 }
2839 }
2840}
2841
2842
2843void SCH_IO_GEDA::scanSymbolDir( const wxString& aDir, int aDepth )
2844{
2845 if( aDepth > 20 )
2846 return;
2847
2848 wxDir dir( aDir );
2849
2850 if( !dir.IsOpened() )
2851 return;
2852
2853 wxString filename;
2854 bool cont = dir.GetFirst( &filename );
2855
2856 while( cont )
2857 {
2858 wxString fullPath = aDir + wxFileName::GetPathSeparator() + filename;
2859
2860 if( wxDir::Exists( fullPath ) )
2861 {
2862 scanSymbolDir( fullPath, aDepth + 1 );
2863 }
2864 else if( filename.EndsWith( wxT( ".sym" ) ) )
2865 {
2866 SYM_CACHE_ENTRY entry;
2867 entry.path = fullPath;
2868 m_symLibrary[filename] = std::move( entry );
2869 }
2870
2871 cont = dir.GetNext( &filename );
2872 }
2873}
2874
2875
2876void SCH_IO_GEDA::parseRcFileForLibraries( const wxString& aPath, const wxString& aBaseDir )
2877{
2878 if( !wxFileExists( aPath ) )
2879 return;
2880
2881 wxTextFile rcFile;
2882
2883 if( !rcFile.Open( aPath ) )
2884 return;
2885
2886 for( size_t i = 0; i < rcFile.GetLineCount(); ++i )
2887 {
2888 wxString line = rcFile.GetLine( i );
2889 line.Trim( true );
2890 line.Trim( false );
2891
2892 if( !line.StartsWith( wxT( "(component-library-search " ) )
2893 && !line.StartsWith( wxT( "(component-library " ) ) )
2894 {
2895 continue;
2896 }
2897
2898 int firstQuote = line.Find( '"' );
2899
2900 if( firstQuote == wxNOT_FOUND )
2901 continue;
2902
2903 // Find the closing quote of the first argument. The two-argument
2904 // form (component-library "path" "name") has a second quoted
2905 // string that we ignore.
2906 int secondQuote = line.Mid( firstQuote + 1 ).Find( '"' );
2907
2908 if( secondQuote != wxNOT_FOUND )
2909 secondQuote += firstQuote + 1;
2910
2911 if( secondQuote == wxNOT_FOUND || secondQuote <= firstQuote )
2912 continue;
2913
2914 wxString libPath = line.Mid( firstQuote + 1, secondQuote - firstQuote - 1 );
2915 wxFileName libDir( libPath );
2916
2917 if( !libDir.IsAbsolute() )
2918 {
2919 libDir.MakeAbsolute( aBaseDir );
2920 libPath = libDir.GetFullPath();
2921 }
2922
2923 if( wxDir::Exists( libPath ) )
2924 scanSymbolDir( libPath );
2925 }
2926}
2927
2928
2929std::unique_ptr<LIB_SYMBOL> SCH_IO_GEDA::loadSymbolFile( const wxString& aPath,
2930 wxString* aSymversion,
2931 wxString* aNetAttr )
2932{
2933 wxTextFile file;
2934
2935 if( !file.Open( aPath ) )
2936 return nullptr;
2937
2938 if( file.GetLineCount() == 0 )
2939 return nullptr;
2940
2941 // Validate version line
2942 wxString firstLine = file.GetFirstLine().Trim( false );
2943
2944 if( !firstLine.StartsWith( wxT( "v " ) ) )
2945 return nullptr;
2946
2947 wxFileName fn( aPath );
2948 wxString symName = fn.GetName();
2949 auto libSym = std::make_unique<LIB_SYMBOL>( symName );
2950 libSym->SetShowPinNumbers( true );
2951 libSym->SetShowPinNames( true );
2952
2953 size_t lineIdx = 1; // Skip version line
2954
2955 while( lineIdx < file.GetLineCount() )
2956 {
2957 wxString line = file.GetLine( lineIdx );
2958
2959 if( line.IsEmpty() )
2960 {
2961 lineIdx++;
2962 continue;
2963 }
2964
2965 wxChar type = line[0];
2966 lineIdx++;
2967
2968 switch( type )
2969 {
2970 case 'P':
2971 {
2972 wxStringTokenizer tok( line );
2973 tok.GetNextToken(); // skip 'P'
2974 long px1 = 0, py1 = 0, px2 = 0, py2 = 0, pc = 0, pt = 0, pw = 0;
2975
2976 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &px1 );
2977 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &py1 );
2978 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &px2 );
2979 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &py2 );
2980 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &pc );
2981 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &pt );
2982 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &pw );
2983
2984 std::vector<GEDA_ATTR> pinAttrs = maybeParseAttributes( file, lineIdx );
2985 addSymbolPin( *libSym, static_cast<int>( px1 ), static_cast<int>( py1 ),
2986 static_cast<int>( px2 ), static_cast<int>( py2 ),
2987 static_cast<int>( pw ), pinAttrs );
2988 break;
2989 }
2990
2991 case 'L':
2992 case 'B':
2993 case 'V':
2994 case 'A':
2995 case 'H':
2996 addSymbolGraphic( *libSym, line, file, lineIdx, type );
2997 break;
2998
2999 case 'T':
3000 {
3001 // T x y color size vis show_nv angle align num_lines
3002 wxStringTokenizer tok( line );
3003 tok.GetNextToken(); // skip 'T'
3004 long numLines = 1;
3005
3006 for( int i = 0; i < 9 && tok.HasMoreTokens(); i++ )
3007 {
3008 wxString fieldVal = tok.GetNextToken();
3009
3010 if( i == 8 )
3011 fieldVal.ToLong( &numLines );
3012 }
3013
3014 for( long i = 0; i < numLines && lineIdx < file.GetLineCount(); i++ )
3015 {
3016 wxString textLine = file.GetLine( lineIdx );
3017
3018 if( aSymversion && textLine.StartsWith( wxT( "symversion=" ) ) )
3019 *aSymversion = textLine.Mid( 11 );
3020
3021 if( aNetAttr && textLine.StartsWith( wxT( "net=" ) ) )
3022 *aNetAttr = textLine.Mid( 4 );
3023
3024 lineIdx++;
3025 }
3026
3027 maybeParseAttributes( file, lineIdx );
3028 break;
3029 }
3030
3031 case '{':
3032 {
3033 while( lineIdx < file.GetLineCount() )
3034 {
3035 if( file.GetLine( lineIdx ).Trim() == wxT( "}" ) )
3036 {
3037 lineIdx++;
3038 break;
3039 }
3040
3041 lineIdx++;
3042 }
3043
3044 break;
3045 }
3046
3047 case 'v':
3048 default:
3049 break;
3050 }
3051 }
3052
3053 return libSym;
3054}
3055
3056
3057void SCH_IO_GEDA::addSymbolPin( LIB_SYMBOL& aSymbol, int aX1, int aY1, int aX2, int aY2,
3058 int aWhichEnd, const std::vector<GEDA_ATTR>& aAttrs )
3059{
3060 // whichend determines which endpoint is the connection point (active end)
3061 // 0 = (x1,y1) is connection point, 1 = (x2,y2) is connection point
3062 int connX = 0, connY = 0, otherX = 0, otherY = 0;
3063
3064 if( aWhichEnd == 0 )
3065 {
3066 connX = aX1; connY = aY1;
3067 otherX = aX2; otherY = aY2;
3068 }
3069 else
3070 {
3071 connX = aX2; connY = aY2;
3072 otherX = aX1; otherY = aY1;
3073 }
3074
3075 // Symbol coordinates use the same mil system but are relative to the symbol origin.
3076 // The connection point goes at the tip, the other end is toward the symbol body.
3077 VECTOR2I connPt( toKiCadDist( connX ), -toKiCadDist( connY ) );
3078 VECTOR2I bodyPt( toKiCadDist( otherX ), -toKiCadDist( otherY ) );
3079
3080 // PIN_ORIENTATION describes which direction the pin body extends FROM the
3081 // connection point, so the delta must point from connection toward body.
3082 int dx = otherX - connX;
3083 int dy = otherY - connY;
3084 int length = toKiCadDist( KiROUND( std::hypot( dx, dy ) ) );
3085
3087
3088 if( dx > 0 )
3090 else if( dx < 0 )
3092 else if( dy > 0 )
3093 orient = PIN_ORIENTATION::PIN_UP;
3094 else if( dy < 0 )
3096
3097 wxString pinnumber = findAttr( aAttrs, wxT( "pinnumber" ) );
3098 wxString pinlabel = findAttr( aAttrs, wxT( "pinlabel" ) );
3099 wxString pintypeStr = findAttr( aAttrs, wxT( "pintype" ) );
3100
3101 if( pinnumber.IsEmpty() )
3102 pinnumber = wxT( "?" );
3103
3104 if( pinlabel.IsEmpty() )
3105 pinlabel = pinnumber;
3106
3108
3109 if( pintypeStr == wxT( "in" ) )
3111 else if( pintypeStr == wxT( "out" ) )
3113 else if( pintypeStr == wxT( "io" ) )
3114 elecType = ELECTRICAL_PINTYPE::PT_BIDI;
3115 else if( pintypeStr == wxT( "oc" ) )
3117 else if( pintypeStr == wxT( "oe" ) )
3119 else if( pintypeStr == wxT( "pas" ) )
3121 else if( pintypeStr == wxT( "tp" ) )
3123 else if( pintypeStr == wxT( "tri" ) )
3125 else if( pintypeStr == wxT( "clk" ) )
3127 else if( pintypeStr == wxT( "pwr" ) )
3129
3130 SCH_PIN* pin = new SCH_PIN( &aSymbol );
3131 pin->SetPosition( connPt );
3132 pin->SetLength( length );
3133 pin->SetOrientation( orient );
3134 pin->SetNumber( pinnumber );
3135 pin->SetName( pinlabel );
3136 pin->SetType( elecType );
3137
3138 aSymbol.AddDrawItem( pin );
3139}
3140
3141
3142void SCH_IO_GEDA::addSymbolGraphic( LIB_SYMBOL& aSymbol, const wxString& aLine,
3143 wxTextFile& aFile, size_t& aLineIdx, wxChar aType )
3144{
3145 switch( aType )
3146 {
3147 case 'L':
3148 {
3149 wxStringTokenizer tok( aLine );
3150 tok.GetNextToken(); // skip 'L'
3151 long x1 = 0, y1 = 0, x2 = 0, y2 = 0, color = 0, width = 0;
3152 long capstyle = 0, dashstyle = 0, dashlength = 0, dashspace = 0;
3153
3154 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x1 );
3155 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y1 );
3156 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x2 );
3157 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y2 );
3158 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
3159 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &width );
3160 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &capstyle );
3161 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashstyle );
3162 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashlength );
3163 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashspace );
3164
3165 VECTOR2I start( toKiCadDist( static_cast<int>( x1 ) ),
3166 -toKiCadDist( static_cast<int>( y1 ) ) );
3167 VECTOR2I end( toKiCadDist( static_cast<int>( x2 ) ),
3168 -toKiCadDist( static_cast<int>( y2 ) ) );
3169
3171 shape->AddPoint( start );
3172 shape->AddPoint( end );
3173 shape->SetStroke( STROKE_PARAMS( toKiCadDist( static_cast<int>( width ) ),
3174 toLineStyle( static_cast<int>( dashstyle ) ) ) );
3175 aSymbol.AddDrawItem( shape );
3176 break;
3177 }
3178
3179 case 'B':
3180 {
3181 wxStringTokenizer tok( aLine );
3182 tok.GetNextToken(); // skip 'B'
3183 long x = 0, y = 0, w = 0, h = 0, color = 0, linewidth = 0;
3184 long capstyle = 0, dashstyle = 0, dashlength = 0, dashspace = 0;
3185 long filltype = 0;
3186
3187 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x );
3188 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y );
3189 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &w );
3190 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &h );
3191 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
3192 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &linewidth );
3193 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &capstyle );
3194 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashstyle );
3195 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashlength );
3196 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashspace );
3197 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &filltype );
3198
3199 VECTOR2I topLeft( toKiCadDist( static_cast<int>( x ) ),
3200 -toKiCadDist( static_cast<int>( y + h ) ) );
3201 VECTOR2I botRight( toKiCadDist( static_cast<int>( x + w ) ),
3202 -toKiCadDist( static_cast<int>( y ) ) );
3203
3205 rect->SetStart( topLeft );
3206 rect->SetEnd( botRight );
3207 rect->SetStroke( STROKE_PARAMS( toKiCadDist( static_cast<int>( linewidth ) ),
3208 toLineStyle( static_cast<int>( dashstyle ) ) ) );
3209
3210 FILL_T fill = toFillType( static_cast<int>( filltype ) );
3211
3212 if( fill != FILL_T::NO_FILL )
3213 {
3214 rect->SetFilled( true );
3215 rect->SetFillMode( fill );
3216 }
3217
3218 aSymbol.AddDrawItem( rect );
3219 break;
3220 }
3221
3222 case 'V':
3223 {
3224 wxStringTokenizer tok( aLine );
3225 tok.GetNextToken(); // skip 'V'
3226 long cx = 0, cy = 0, r = 0, color = 0, linewidth = 0;
3227 long capstyle = 0, dashstyle = 0, dashlength = 0, dashspace = 0;
3228 long filltype = 0;
3229
3230 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &cx );
3231 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &cy );
3232 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &r );
3233 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
3234 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &linewidth );
3235 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &capstyle );
3236 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashstyle );
3237 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashlength );
3238 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashspace );
3239 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &filltype );
3240
3241 VECTOR2I center( toKiCadDist( static_cast<int>( cx ) ),
3242 -toKiCadDist( static_cast<int>( cy ) ) );
3243
3245 circle->SetCenter( center );
3246 circle->SetEnd( VECTOR2I( center.x + toKiCadDist( static_cast<int>( r ) ), center.y ) );
3247 circle->SetStroke( STROKE_PARAMS( toKiCadDist( static_cast<int>( linewidth ) ),
3248 toLineStyle( static_cast<int>( dashstyle ) ) ) );
3249
3250 FILL_T fill = toFillType( static_cast<int>( filltype ) );
3251
3252 if( fill != FILL_T::NO_FILL )
3253 {
3254 circle->SetFilled( true );
3255 circle->SetFillMode( fill );
3256 }
3257
3258 aSymbol.AddDrawItem( circle );
3259 break;
3260 }
3261
3262 case 'A':
3263 {
3264 wxStringTokenizer tok( aLine );
3265 tok.GetNextToken(); // skip 'A'
3266 long cx = 0, cy = 0, r = 0, sa = 0, da = 0;
3267 long color = 0, linewidth = 0;
3268
3269 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &cx );
3270 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &cy );
3271 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &r );
3272 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &sa );
3273 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &da );
3274 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
3275 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &linewidth );
3276
3277 VECTOR2I center( toKiCadDist( static_cast<int>( cx ) ),
3278 -toKiCadDist( static_cast<int>( cy ) ) );
3279 int radius = toKiCadDist( static_cast<int>( r ) );
3280
3281 double startRad = DEG2RAD( static_cast<double>( sa ) );
3282 double endRad = DEG2RAD( static_cast<double>( sa + da ) );
3283
3284 VECTOR2I arcStart( center.x + KiROUND( radius * cos( startRad ) ),
3285 center.y - KiROUND( radius * sin( startRad ) ) );
3286 VECTOR2I arcEnd( center.x + KiROUND( radius * cos( endRad ) ),
3287 center.y - KiROUND( radius * sin( endRad ) ) );
3288
3290 arc->SetCenter( center );
3291
3292 // Symbol coordinates use negative-Y, which reverses angular sweep
3293 // direction. Swap start/end to preserve the correct arc extent.
3294 arc->SetStart( arcEnd );
3295 arc->SetEnd( arcStart );
3296 arc->SetStroke( STROKE_PARAMS( toKiCadDist( static_cast<int>( linewidth ) ),
3298 aSymbol.AddDrawItem( arc );
3299 break;
3300 }
3301
3302 case 'H':
3303 {
3304 wxStringTokenizer tok( aLine );
3305 tok.GetNextToken(); // skip 'H'
3306
3307 long color = 0, width = 0, capstyle = 0, dashstyle = 0;
3308 long dashlength = 0, dashspace = 0, filltype = 0, fillwidth = 0;
3309 long a1 = 0, p1 = 0, a2 = 0, p2 = 0, numlines = 0;
3310
3311 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
3312 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &width );
3313 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &capstyle );
3314 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashstyle );
3315 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashlength );
3316 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashspace );
3317 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &filltype );
3318 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &fillwidth );
3319 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &a1 );
3320 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &p1 );
3321 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &a2 );
3322 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &p2 );
3323 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &numlines );
3324
3325 wxString pathData;
3326
3327 for( long i = 0; i < numlines && aLineIdx < aFile.GetLineCount(); i++ )
3328 {
3329 if( i > 0 )
3330 pathData += wxT( " " );
3331
3332 pathData += aFile.GetLine( aLineIdx ).Trim();
3333 aLineIdx++;
3334 }
3335
3336 std::vector<VECTOR2I> polyPts;
3337 VECTOR2I startPt;
3338 VECTOR2I currentPt;
3339 STROKE_PARAMS symStroke( toKiCadDist( static_cast<int>( width ) ),
3340 toLineStyle( static_cast<int>( dashstyle ) ) );
3341 FILL_T symFill = toFillType( static_cast<int>( filltype ) );
3342
3343 auto flushPoly = [&]()
3344 {
3345 if( polyPts.size() < 2 )
3346 return;
3347
3349
3350 for( const VECTOR2I& pt : polyPts )
3351 poly->AddPoint( pt );
3352
3353 poly->SetStroke( symStroke );
3354
3355 if( symFill != FILL_T::NO_FILL )
3356 {
3357 poly->SetFilled( true );
3358 poly->SetFillMode( symFill );
3359 }
3360
3361 aSymbol.AddDrawItem( poly );
3362 polyPts.clear();
3363 };
3364
3365 wxStringTokenizer pathTok( pathData, wxT( " ,\t" ) );
3366
3367 while( pathTok.HasMoreTokens() )
3368 {
3369 wxString pathToken = pathTok.GetNextToken();
3370
3371 // Symbol path coordinates are in mils (Y-up). Convert to IU (Y-down).
3372 auto symAbsPt = [this]( long aX, long aY ) -> VECTOR2I
3373 {
3374 return VECTOR2I( toKiCadDist( static_cast<int>( aX ) ),
3375 -toKiCadDist( static_cast<int>( aY ) ) );
3376 };
3377
3378 auto symRelPt = [this, &currentPt]( long aDx, long aDy ) -> VECTOR2I
3379 {
3380 return currentPt + VECTOR2I( toKiCadDist( static_cast<int>( aDx ) ),
3381 -toKiCadDist( static_cast<int>( aDy ) ) );
3382 };
3383
3384 bool isRelative = ( pathToken.IsAscii() && islower( pathToken[0u] ) );
3385
3386 if( pathToken == wxT( "M" ) || pathToken == wxT( "m" ) )
3387 {
3388 long px = 0, py = 0;
3389
3390 if( pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &px )
3391 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &py ) )
3392 {
3393 currentPt = isRelative ? symRelPt( px, py ) : symAbsPt( px, py );
3394 startPt = currentPt;
3395
3396 if( polyPts.empty() )
3397 polyPts.push_back( currentPt );
3398 }
3399 }
3400 else if( pathToken == wxT( "L" ) || pathToken == wxT( "l" ) )
3401 {
3402 long px = 0, py = 0;
3403
3404 if( pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &px )
3405 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &py ) )
3406 {
3407 currentPt = isRelative ? symRelPt( px, py ) : symAbsPt( px, py );
3408 polyPts.push_back( currentPt );
3409 }
3410 }
3411 else if( pathToken == wxT( "C" ) || pathToken == wxT( "c" ) )
3412 {
3413 long cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, px = 0, py = 0;
3414
3415 if( pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &cx1 )
3416 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &cy1 )
3417 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &cx2 )
3418 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &cy2 )
3419 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &px )
3420 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &py ) )
3421 {
3422 flushPoly();
3423
3424 VECTOR2I cp1 = isRelative ? symRelPt( cx1, cy1 ) : symAbsPt( cx1, cy1 );
3425 VECTOR2I cp2 = isRelative ? symRelPt( cx2, cy2 ) : symAbsPt( cx2, cy2 );
3426 VECTOR2I endPt = isRelative ? symRelPt( px, py ) : symAbsPt( px, py );
3427
3429 bezier->SetStart( currentPt );
3430 bezier->SetBezierC1( cp1 );
3431 bezier->SetBezierC2( cp2 );
3432 bezier->SetEnd( endPt );
3433 bezier->SetStroke( symStroke );
3435 schIUScale.mmToIU( ARC_LOW_DEF_MM ) );
3436
3437 if( symFill != FILL_T::NO_FILL )
3438 {
3439 bezier->SetFilled( true );
3440 bezier->SetFillMode( symFill );
3441 }
3442
3443 aSymbol.AddDrawItem( bezier );
3444 currentPt = endPt;
3445 polyPts.push_back( currentPt );
3446 }
3447 }
3448 else if( pathToken == wxT( "Z" ) || pathToken == wxT( "z" ) )
3449 {
3450 if( currentPt != startPt )
3451 {
3452 polyPts.push_back( startPt );
3453 currentPt = startPt;
3454 }
3455 }
3456 }
3457
3458 flushPoly();
3459
3460 break;
3461 }
3462
3463 default:
3464 break;
3465 }
3466
3467 maybeParseAttributes( aFile, aLineIdx );
3468}
3469
3470
3474static int editDistance( const wxString& a, const wxString& b )
3475{
3476 size_t m = a.length();
3477 size_t n = b.length();
3478
3479 std::vector<int> prev( n + 1 );
3480 std::vector<int> curr( n + 1 );
3481
3482 for( size_t j = 0; j <= n; ++j )
3483 prev[j] = static_cast<int>( j );
3484
3485 for( size_t i = 1; i <= m; ++i )
3486 {
3487 curr[0] = static_cast<int>( i );
3488
3489 for( size_t j = 1; j <= n; ++j )
3490 {
3491 int cost = ( a[i - 1] == b[j - 1] ) ? 0 : 1;
3492 curr[j] = std::min( { prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost } );
3493 }
3494
3495 std::swap( prev, curr );
3496 }
3497
3498 return prev[n];
3499}
3500
3501
3502std::unique_ptr<LIB_SYMBOL> SCH_IO_GEDA::loadBuiltinSymbol( const wxString& aBasename )
3503{
3504 const auto& builtins = getBuiltinSymbols();
3505 auto it = builtins.find( aBasename );
3506
3507 if( it == builtins.end() )
3508 return nullptr;
3509
3510 wxString tempPath = wxFileName::CreateTempFileName( wxT( "geda_sym_" ) );
3511
3512 if( tempPath.IsEmpty() )
3513 return nullptr;
3514
3515 {
3516 wxFile temp( tempPath, wxFile::write );
3517
3518 if( !temp.IsOpened() || !temp.Write( it->second ) )
3519 {
3520 wxRemoveFile( tempPath );
3521 return nullptr;
3522 }
3523 }
3524
3525 wxString netAttr;
3526 auto result = loadSymbolFile( tempPath, nullptr, &netAttr );
3527 wxRemoveFile( tempPath );
3528
3529 if( result )
3530 {
3531 wxString symName = aBasename;
3532
3533 if( symName.EndsWith( wxT( ".sym" ) ) )
3534 symName = symName.Left( symName.length() - 4 );
3535
3536 result->SetName( symName );
3537
3538 if( !netAttr.IsEmpty() )
3539 {
3540 SYM_CACHE_ENTRY& entry = m_symLibrary[aBasename];
3541 entry.netAttr = netAttr;
3542 }
3543 }
3544
3545 return result;
3546}
3547
3548
3549LIB_SYMBOL* SCH_IO_GEDA::getOrLoadSymbol( const wxString& aBasename )
3550{
3551 // Check if already loaded
3552 auto it = m_libSymbols.find( aBasename );
3553
3554 if( it != m_libSymbols.end() )
3555 return it->second.get();
3556
3557 // Try the library cache
3559
3560 auto cacheIt = m_symLibrary.find( aBasename );
3561
3562 if( cacheIt != m_symLibrary.end() )
3563 {
3564 if( !cacheIt->second.symbol )
3565 {
3566 cacheIt->second.symbol = loadSymbolFile( cacheIt->second.path,
3567 &cacheIt->second.symversion,
3568 &cacheIt->second.netAttr );
3569
3570 // When a project-local symbol overrides a builtin power symbol but lacks a
3571 // net= attribute, inherit the builtin's net= so power detection still works.
3572 // This handles projects that provide custom graphics for standard power symbols.
3573 if( cacheIt->second.symbol && cacheIt->second.netAttr.IsEmpty() )
3574 {
3575 const auto& builtins = getBuiltinSymbols();
3576 auto builtinIt = builtins.find( aBasename );
3577
3578 if( builtinIt != builtins.end() )
3579 {
3580 // Scan the builtin symbol text for a net= attribute line.
3581 // In gEDA .sym format, net= always starts a line.
3582 const wxString& src = builtinIt->second;
3583 int pos = src.Find( wxT( "\nnet=" ) );
3584
3585 if( pos != wxNOT_FOUND )
3586 {
3587 wxString rest = src.Mid( pos + 5 );
3588 int eol = rest.Find( '\n' );
3589
3590 cacheIt->second.netAttr = ( eol != wxNOT_FOUND )
3591 ? rest.Left( eol ).Trim()
3592 : rest.Trim();
3593 }
3594 }
3595 }
3596 }
3597
3598 if( cacheIt->second.symbol )
3599 {
3600 m_libSymbols[aBasename] = std::make_unique<LIB_SYMBOL>( *cacheIt->second.symbol );
3601 return m_libSymbols[aBasename].get();
3602 }
3603 }
3604
3605 // Try built-in standard symbols before falling back to a placeholder
3606 auto builtin = loadBuiltinSymbol( aBasename );
3607
3608 if( builtin )
3609 {
3610 m_libSymbols[aBasename] = std::move( builtin );
3611 return m_libSymbols[aBasename].get();
3612 }
3613
3614 // Not found anywhere - create a fallback placeholder
3615 m_libSymbols[aBasename] = createFallbackSymbol( aBasename );
3616
3617 if( m_reporter )
3618 {
3619 wxString suggestion;
3620 int bestDist = INT_MAX;
3621
3622 for( const auto& [name, entry] : m_symLibrary )
3623 {
3624 int dist = editDistance( aBasename, name );
3625
3626 if( dist < bestDist && dist <= 3 )
3627 {
3628 bestDist = dist;
3629 suggestion = name;
3630 }
3631 }
3632
3633 // Also check builtins, which may have a closer match than the library cache
3634 for( const auto& [name, content] : getBuiltinSymbols() )
3635 {
3636 int dist = editDistance( aBasename, name );
3637
3638 if( dist < bestDist && dist <= 3 )
3639 {
3640 bestDist = dist;
3641 suggestion = name;
3642 }
3643 }
3644
3645 wxString msg = wxString::Format( _( "Symbol '%s' not found in gEDA libraries." ),
3646 aBasename );
3647
3648 if( !suggestion.IsEmpty() )
3649 msg += wxString::Format( _( " Did you mean '%s'?" ), suggestion );
3650
3651 m_reporter->Report( msg );
3652 }
3653
3654 return m_libSymbols[aBasename].get();
3655}
3656
3657
3658std::unique_ptr<LIB_SYMBOL> SCH_IO_GEDA::createFallbackSymbol( const wxString& aBasename )
3659{
3660 wxString symName = aBasename;
3661
3662 if( symName.EndsWith( wxT( ".sym" ) ) )
3663 symName = symName.Left( symName.length() - 4 );
3664
3665 auto libSymbol = std::make_unique<LIB_SYMBOL>( symName );
3666 libSymbol->SetShowPinNumbers( true );
3667 libSymbol->SetShowPinNames( true );
3668
3669 int halfSize = toKiCadDist( DEFAULT_SYMBOL_SIZE_MILS ) / 2;
3670
3672 rect->SetStart( VECTOR2I( -halfSize, -halfSize ) );
3673 rect->SetEnd( VECTOR2I( halfSize, halfSize ) );
3675 libSymbol->AddDrawItem( rect );
3676
3677 return libSymbol;
3678}
3679
3680
3682{
3683 if( !m_pendingComp )
3684 return;
3685
3686 // Skip title block symbols
3687 if( m_pendingComp->basename.StartsWith( wxT( "title-" ) ) )
3688 {
3689 m_pendingComp.reset();
3690 return;
3691 }
3692
3693 // Detect no-connect symbols (nc-right-1.sym, nc-left-1.sym, etc.) and emit
3694 // SCH_NO_CONNECT at the pin connection point rather than the component origin.
3695 wxString ncValue = findAttr( m_pendingComp->attrs, wxT( "value" ) );
3696 bool isNoConnect = m_pendingComp->basename.StartsWith( wxT( "nc-" ) )
3697 && ncValue == wxT( "NoConnection" );
3698
3699 if( isNoConnect )
3700 {
3701 LIB_SYMBOL* ncSym = getOrLoadSymbol( m_pendingComp->basename );
3702 int pinLocalX = 0;
3703 int pinLocalY = 0;
3704
3705 if( ncSym )
3706 {
3707 const std::vector<SCH_PIN*>& pins = ncSym->GetPins();
3708
3709 if( !pins.empty() )
3710 {
3711 VECTOR2I pinPos = pins[0]->GetPosition();
3712 pinLocalX = pinPos.x / MILS_TO_IU;
3713 pinLocalY = -pinPos.y / MILS_TO_IU;
3714 }
3715 }
3716
3717 // Apply gEDA component rotation/mirror to the pin offset
3718 int rx = pinLocalX;
3719 int ry = pinLocalY;
3720
3721 switch( m_pendingComp->angle )
3722 {
3723 case 90: rx = -pinLocalY; ry = pinLocalX; break;
3724 case 180: rx = -pinLocalX; ry = -pinLocalY; break;
3725 case 270: rx = pinLocalY; ry = -pinLocalX; break;
3726 default: break;
3727 }
3728
3729 if( m_pendingComp->mirror )
3730 rx = -rx;
3731
3732 int gedaX = m_pendingComp->x + rx;
3733 int gedaY = m_pendingComp->y + ry;
3734
3735 m_screen->Append( new SCH_NO_CONNECT( toKiCad( gedaX, gedaY ) ) );
3736 m_pendingComp.reset();
3737 return;
3738 }
3739
3740 // Check for hierarchical sheet reference. In gEDA, a source= attribute
3741 // pointing to a .sch file indicates a hierarchical sub-schematic.
3742 wxString sourceAttr = findAttr( m_pendingComp->attrs, wxT( "source" ) );
3743
3744 if( !sourceAttr.IsEmpty() && sourceAttr.EndsWith( wxT( ".sch" ) ) )
3745 {
3746 importHierarchicalSheet( sourceAttr );
3747 m_pendingComp.reset();
3748 return;
3749 }
3750
3751 LIB_SYMBOL* libSym = nullptr;
3752
3753 if( m_pendingComp->embedded && m_pendingComp->embeddedSym )
3754 {
3755 wxString basename = m_pendingComp->basename;
3756 m_libSymbols[basename] = std::move( m_pendingComp->embeddedSym );
3757 libSym = m_libSymbols[basename].get();
3758 }
3759 else
3760 {
3761 libSym = getOrLoadSymbol( m_pendingComp->basename );
3762 }
3763
3764 if( !libSym )
3765 {
3766 m_pendingComp.reset();
3767 return;
3768 }
3769
3770 // Compare component symversion against the loaded symbol's symversion.
3771 // A mismatch in the major version number indicates the symbol has changed
3772 // in an incompatible way since the schematic was saved.
3773 wxString compSymver = findAttr( m_pendingComp->attrs, wxT( "symversion" ) );
3774
3775 if( !compSymver.IsEmpty() && m_reporter )
3776 {
3777 wxString symSymver;
3778 auto cacheIt = m_symLibrary.find( m_pendingComp->basename );
3779
3780 if( cacheIt != m_symLibrary.end() )
3781 symSymver = cacheIt->second.symversion;
3782
3783 if( !symSymver.IsEmpty() )
3784 {
3785 long compMajor = 0;
3786 long symMajor = 0;
3787 compSymver.BeforeFirst( '.' ).ToLong( &compMajor );
3788 symSymver.BeforeFirst( '.' ).ToLong( &symMajor );
3789
3790 if( compMajor != symMajor )
3791 {
3792 wxString refdes = findAttr( m_pendingComp->attrs, wxT( "refdes" ) );
3793 wxString label = refdes.IsEmpty() ? m_pendingComp->basename : refdes;
3794
3795 m_reporter->Report(
3796 wxString::Format( _( "Symbol version mismatch for '%s' (%s): "
3797 "schematic has symversion %s, "
3798 "library symbol has symversion %s." ),
3799 label, m_pendingComp->basename,
3800 compSymver, symSymver ),
3802 }
3803 }
3804 }
3805
3806 // Detect power symbols via the net= attribute in the .sym definition.
3807 // gEDA power symbols (gnd-1.sym, vcc-1.sym, etc.) carry a net=NETNAME:PIN
3808 // attribute that identifies them as implicit power connections.
3809 wxString symNetAttr;
3810 auto cacheEntry = m_symLibrary.find( m_pendingComp->basename );
3811
3812 if( cacheEntry != m_symLibrary.end() )
3813 symNetAttr = cacheEntry->second.netAttr;
3814
3815 bool isPowerSym = !symNetAttr.IsEmpty();
3816 wxString powerNetName;
3817 std::unique_ptr<LIB_SYMBOL> powerCopy;
3818
3819 if( isPowerSym )
3820 {
3821 // Schematic-level net= overrides the .sym-level default (e.g. generic-power.sym
3822 // has net=Vcc:1 but instances use net=5V:1 or net=9V:1).
3823 wxString schematicNet = findAttr( m_pendingComp->attrs, wxT( "net" ) );
3824 wxString effectiveNet = schematicNet.IsEmpty() ? symNetAttr : schematicNet;
3825
3826 int colonPos = effectiveNet.Find( ':' );
3827
3828 if( colonPos != wxNOT_FOUND )
3829 powerNetName = effectiveNet.Left( colonPos );
3830 else
3831 powerNetName = effectiveNet;
3832
3833 // Work on a copy so the shared cached symbol is not modified.
3834 // Each power instance may have a different net name.
3835 powerCopy = std::make_unique<LIB_SYMBOL>( *libSym );
3836 libSym = powerCopy.get();
3837
3838 libSym->SetGlobalPower();
3839 libSym->SetShowPinNames( false );
3840 libSym->SetShowPinNumbers( false );
3841 libSym->GetReferenceField().SetText( wxT( "#PWR" ) );
3842 libSym->GetReferenceField().SetVisible( false );
3843 libSym->GetValueField().SetText( powerNetName );
3844 libSym->GetValueField().SetVisible( true );
3845
3846 for( SCH_PIN* pin : libSym->GetPins() )
3847 {
3848 pin->SetName( powerNetName );
3850 }
3851 }
3852
3853 LIB_ID libId( getLibName(), libSym->GetName() );
3855
3856 auto symbol = std::make_unique<SCH_SYMBOL>( *libSym, libId, &m_schematic->CurrentSheet(),
3857 1, 0, pos );
3858
3859 int orient = toKiCadOrientation( m_pendingComp->angle, m_pendingComp->mirror );
3860 symbol->SetOrientation( orient );
3861
3862 if( isPowerSym )
3863 {
3864 wxString pwrRef = wxString::Format( wxT( "#PWR%04d" ), ++m_powerCounter );
3865 symbol->SetRef( &m_schematic->CurrentSheet(), pwrRef );
3866 symbol->GetField( FIELD_T::REFERENCE )->SetText( pwrRef );
3867 symbol->GetField( FIELD_T::REFERENCE )->SetVisible( false );
3868 symbol->GetField( FIELD_T::VALUE )->SetText( powerNetName );
3869 symbol->GetField( FIELD_T::VALUE )->SetVisible( true );
3870 }
3871
3872 // Mark graphical-only symbols as excluded from BOM, board, and simulation
3873 wxString graphicalAttr = findAttr( m_pendingComp->attrs, wxT( "graphical" ) );
3874
3875 if( graphicalAttr == wxT( "1" ) )
3876 {
3877 symbol->SetExcludedFromBOM( true );
3878 symbol->SetExcludedFromBoard( true );
3879 symbol->SetExcludedFromSim( true );
3880 }
3881
3882 wxString refdes = findAttr( m_pendingComp->attrs, wxT( "refdes" ) );
3883 wxString value = findAttr( m_pendingComp->attrs, wxT( "value" ) );
3884 wxString footprint = findAttr( m_pendingComp->attrs, wxT( "footprint" ) );
3885
3886 if( !isPowerSym && !refdes.IsEmpty() )
3887 {
3888 symbol->SetRef( &m_schematic->CurrentSheet(), refdes );
3889
3890 SCH_FIELD* refField = symbol->GetField( FIELD_T::REFERENCE );
3891 const GEDA_ATTR* refAttr = findAttrStruct( m_pendingComp->attrs, wxT( "refdes" ) );
3892 refField->SetText( refdes );
3893
3894 if( refAttr )
3895 {
3896 refField->SetPosition( toKiCad( refAttr->x, refAttr->y ) );
3897 int textSize = toKiCadDist( refAttr->size * 10 );
3898 refField->SetTextSize( VECTOR2I( textSize, textSize ) );
3899 refField->SetVisible( refAttr->visible );
3900 }
3901 }
3902
3903 if( !isPowerSym && !value.IsEmpty() )
3904 {
3905 SCH_FIELD* valField = symbol->GetField( FIELD_T::VALUE );
3906 const GEDA_ATTR* valAttr = findAttrStruct( m_pendingComp->attrs, wxT( "value" ) );
3907 valField->SetText( value );
3908
3909 if( valAttr )
3910 {
3911 valField->SetPosition( toKiCad( valAttr->x, valAttr->y ) );
3912 int textSize = toKiCadDist( valAttr->size * 10 );
3913 valField->SetTextSize( VECTOR2I( textSize, textSize ) );
3914 valField->SetVisible( valAttr->visible );
3915 }
3916 }
3917
3918 if( !footprint.IsEmpty() )
3919 symbol->SetFootprintFieldText( footprint );
3920
3921 // Map documentation attribute to KiCad's native DATASHEET field
3922 wxString documentation = findAttr( m_pendingComp->attrs, wxT( "documentation" ) );
3923
3924 if( !documentation.IsEmpty() )
3925 {
3926 SCH_FIELD* dsField = symbol->GetField( FIELD_T::DATASHEET );
3927 dsField->SetText( documentation );
3928 }
3929
3930 // Map description attribute to KiCad's native DESCRIPTION field
3931 const GEDA_ATTR* descAttr = findAttrStruct( m_pendingComp->attrs, wxT( "description" ) );
3932
3933 if( descAttr && !descAttr->value.IsEmpty() )
3934 {
3935 SCH_FIELD* descField = symbol->GetField( FIELD_T::DESCRIPTION );
3936 descField->SetText( descAttr->value );
3937 descField->SetVisible( descAttr->visible );
3938
3939 if( descAttr->visible )
3940 {
3941 descField->SetPosition( toKiCad( descAttr->x, descAttr->y ) );
3942 int textSize = toKiCadDist( descAttr->size * 10 );
3943 descField->SetTextSize( VECTOR2I( textSize, textSize ) );
3944 }
3945 }
3946
3947 // Collect net= attributes for post-processing. Power symbols already establish
3948 // their net connection through the pin name, so skip global label generation.
3949 if( !isPowerSym )
3950 {
3951 for( const GEDA_ATTR& attr : m_pendingComp->attrs )
3952 {
3953 if( attr.name == wxT( "net" ) )
3954 {
3955 int colonPos = attr.value.Find( ':' );
3956
3957 if( colonPos != wxNOT_FOUND )
3958 {
3959 NET_ATTR_RECORD rec;
3960 rec.netname = attr.value.Left( colonPos );
3961 rec.pinnumber = attr.value.Mid( colonPos + 1 );
3962 rec.symbol = symbol.get();
3963 m_netAttrRecords.push_back( rec );
3964 }
3965 }
3966 }
3967 }
3968
3969 // Store additional attributes as custom fields (skip those already handled)
3970 for( const GEDA_ATTR& attr : m_pendingComp->attrs )
3971 {
3972 if( attr.name == wxT( "refdes" ) || attr.name == wxT( "value" )
3973 || attr.name == wxT( "footprint" ) || attr.name == wxT( "net" )
3974 || attr.name == wxT( "device" ) || attr.name == wxT( "symversion" )
3975 || attr.name == wxT( "documentation" ) || attr.name == wxT( "description" )
3976 || attr.name == wxT( "graphical" )
3977 || attr.name == wxT( "slot" ) || attr.name == wxT( "numslots" )
3978 || attr.name == wxT( "slotdef" ) )
3979 {
3980 continue;
3981 }
3982
3983 SCH_FIELD* field = symbol->AddField(
3984 SCH_FIELD( symbol.get(), FIELD_T::USER, attr.name ) );
3985 field->SetText( attr.value );
3986 field->SetVisible( attr.visible );
3987 field->SetPosition( pos );
3988 }
3989
3990 // Track pin connection points for junction detection. Reverse-map the
3991 // KiCad IU positions back to gEDA coordinates to match the wire endpoints.
3992 for( SCH_PIN* pin : libSym->GetPins() )
3993 {
3994 VECTOR2I pinPos = symbol->GetPinPhysicalPosition( pin );
3995 int gedaX = pinPos.x / MILS_TO_IU;
3996 int gedaY = m_maxY - pinPos.y / MILS_TO_IU;
3997 trackEndpoint( gedaX, gedaY );
3998 }
3999
4000 symbol->SetLibSymbol( new LIB_SYMBOL( *libSym ) );
4001
4002 // Apply multi-slot pin remapping on the symbol's PRIVATE copy of the lib symbol.
4003 // gEDA slotdef format is "N:pin1,pin2,pin3" where N is the slot number
4004 // and each pin corresponds to a pinseq-ordered pin in the symbol.
4005 // Operating on the private copy prevents corrupting the shared cached symbol.
4006 wxString slotStr = findAttr( m_pendingComp->attrs, wxT( "slot" ) );
4007 long slotNum = 0;
4008
4009 if( !slotStr.IsEmpty() && slotStr.ToLong( &slotNum ) && slotNum > 0 )
4010 {
4011 wxString targetSlotDef;
4012
4013 for( const GEDA_ATTR& attr : m_pendingComp->attrs )
4014 {
4015 if( attr.name != wxT( "slotdef" ) )
4016 continue;
4017
4018 int colonPos = attr.value.Find( ':' );
4019
4020 if( colonPos == wxNOT_FOUND )
4021 continue;
4022
4023 long defSlot = 0;
4024
4025 if( attr.value.Left( colonPos ).ToLong( &defSlot ) && defSlot == slotNum )
4026 {
4027 targetSlotDef = attr.value.Mid( colonPos + 1 );
4028 break;
4029 }
4030 }
4031
4032 if( !targetSlotDef.IsEmpty() )
4033 {
4034 wxArrayString slotPins;
4035 wxStringTokenizer slotTok( targetSlotDef, wxT( "," ) );
4036
4037 while( slotTok.HasMoreTokens() )
4038 slotPins.Add( slotTok.GetNextToken() );
4039
4040 LIB_SYMBOL* privateSym = symbol->GetLibSymbolRef().get();
4041
4042 // Build pinseq-ordered list from the private copy's pins.
4043 // Pins are added in file order which matches pinseq order.
4044 std::map<long, SCH_PIN*> pinsBySeq;
4045 int autoSeq = 1;
4046
4047 for( SCH_PIN* pin : privateSym->GetPins() )
4048 {
4049 pinsBySeq[autoSeq] = pin;
4050 autoSeq++;
4051 }
4052
4053 for( size_t i = 0; i < slotPins.size() && i < pinsBySeq.size(); i++ )
4054 {
4055 auto seqIt = pinsBySeq.find( static_cast<long>( i + 1 ) );
4056
4057 if( seqIt != pinsBySeq.end() )
4058 seqIt->second->SetNumber( slotPins[i].Trim() );
4059 }
4060 }
4061 }
4062
4063 m_screen->Append( symbol.release() );
4064 m_pendingComp.reset();
4065}
4066
4067
4068// ==========================================================================
4069// Hierarchical sheet import
4070// ==========================================================================
4071
4072void SCH_IO_GEDA::importHierarchicalSheet( const wxString& aSourceFile )
4073{
4074 wxFileName sourceFileName( aSourceFile );
4075
4076 // Resolve relative paths against the directory of the current schematic
4077 if( !sourceFileName.IsAbsolute() )
4078 sourceFileName.SetPath( m_filename.GetPath() );
4079
4080 wxString fullPath = sourceFileName.GetFullPath();
4081
4082 if( !wxFileExists( fullPath ) )
4083 {
4084 if( m_reporter )
4085 {
4086 m_reporter->Report(
4087 wxString::Format( _( "Hierarchical source '%s' not found, "
4088 "creating empty sheet." ),
4089 aSourceFile ),
4091 }
4092 }
4093
4094 if( m_importStack.count( fullPath ) )
4095 {
4096 if( m_reporter )
4097 {
4098 m_reporter->Report(
4099 wxString::Format( _( "Circular hierarchy detected for '%s', skipping." ),
4100 aSourceFile ),
4102 }
4103
4104 return;
4105 }
4106
4108
4109 // Use a default sheet size. We'll resize after loading the sub-schematic content.
4110 VECTOR2I sheetSize( schIUScale.MilsToIU( 2000 ), schIUScale.MilsToIU( 1500 ) );
4111
4112 auto sheet = std::make_unique<SCH_SHEET>( m_rootSheet, pos, sheetSize );
4113
4114 wxString refdes = findAttr( m_pendingComp->attrs, wxT( "refdes" ) );
4115
4116 if( !refdes.IsEmpty() )
4117 {
4118 sheet->GetField( FIELD_T::SHEET_NAME )->SetText( refdes );
4119 }
4120 else
4121 {
4122 sheet->GetField( FIELD_T::SHEET_NAME )->SetText(
4123 sourceFileName.GetName() );
4124 }
4125
4126 sheet->GetField( FIELD_T::SHEET_FILENAME )->SetText( aSourceFile );
4127 sheet->SetFileName( aSourceFile );
4128
4129 SCH_SHEET* sheetPtr = sheet.get();
4130 m_screen->Append( sheet.release() );
4131
4132 if( wxFileExists( fullPath ) )
4133 {
4134 m_deferredSheets.push_back( { sheetPtr, fullPath } );
4135 }
4136}
4137
4138
4139// ==========================================================================
4140// Endpoint tracking
4141// ==========================================================================
4142
4143void SCH_IO_GEDA::trackEndpoint( int aGedaX, int aGedaY )
4144{
4145 m_netEndpoints[std::make_pair( aGedaX, aGedaY )]++;
4146}
4147
4148
4149// ==========================================================================
4150// Post-processing
4151// ==========================================================================
4152
4161
4162
4164{
4165 for( const NET_ATTR_RECORD& rec : m_netAttrRecords )
4166 {
4167 if( !rec.symbol )
4168 continue;
4169
4170 const LIB_SYMBOL* libSym = rec.symbol->GetLibSymbolRef().get();
4171
4172 if( !libSym )
4173 continue;
4174
4175 // Find the pin with matching number
4176 for( SCH_PIN* pin : libSym->GetPins() )
4177 {
4178 if( pin->GetNumber() == rec.pinnumber )
4179 {
4180 VECTOR2I pinPos = rec.symbol->GetPinPhysicalPosition( pin );
4181
4182 auto label = std::make_unique<SCH_GLOBALLABEL>( pinPos, rec.netname );
4183 int textSize = toKiCadDist( GEDA_DEFAULT_TEXT_SIZE_MILS / 2 );
4184 label->SetTextSize( VECTOR2I( textSize, textSize ) );
4185
4186 // Build a unit direction vector for the pin in library space,
4187 // then transform it through the symbol's rotation/mirror matrix
4188 // to get the world-space pin direction.
4189 VECTOR2I pinDir( 0, 0 );
4190
4191 switch( pin->GetOrientation() )
4192 {
4193 case PIN_ORIENTATION::PIN_RIGHT: pinDir = VECTOR2I( 1, 0 ); break;
4194 case PIN_ORIENTATION::PIN_LEFT: pinDir = VECTOR2I( -1, 0 ); break;
4195 case PIN_ORIENTATION::PIN_UP: pinDir = VECTOR2I( 0, -1 ); break;
4196 case PIN_ORIENTATION::PIN_DOWN: pinDir = VECTOR2I( 0, 1 ); break;
4197 default: pinDir = VECTOR2I( 1, 0 ); break;
4198 }
4199
4200 VECTOR2I worldDir = rec.symbol->GetTransform().TransformCoordinate( pinDir );
4201
4202 // Orient the label away from the transformed pin direction
4203 if( std::abs( worldDir.x ) >= std::abs( worldDir.y ) )
4204 {
4205 label->SetSpinStyle( worldDir.x > 0 ? SPIN_STYLE::LEFT
4207 }
4208 else
4209 {
4210 label->SetSpinStyle( worldDir.y > 0 ? SPIN_STYLE::UP
4212 }
4213
4214 m_screen->Append( label.release() );
4215 break;
4216 }
4217 }
4218 }
4219}
4220
4221
4223{
4224 // Collect all wire segments and pin positions from the screen.
4225 std::vector<std::pair<VECTOR2I, VECTOR2I>> wireSegs;
4226 std::set<std::pair<int, int>> junctionPts;
4227
4228 for( SCH_ITEM* item : m_screen->Items() )
4229 {
4230 if( item->Type() != SCH_LINE_T )
4231 continue;
4232
4233 SCH_LINE* wire = static_cast<SCH_LINE*>( item );
4234
4235 if( wire->GetLayer() != LAYER_WIRE )
4236 continue;
4237
4238 wireSegs.emplace_back( wire->GetStartPoint(), wire->GetEndPoint() );
4239 }
4240
4241 // Place junctions where 3+ endpoints coincide at the same point.
4242 for( const auto& [pos, count] : m_netEndpoints )
4243 {
4244 if( count >= 3 )
4245 junctionPts.insert( { pos.first, pos.second } );
4246 }
4247
4248 // Detect T-junctions: a wire endpoint that lands on the interior of
4249 // another wire segment. Work in gEDA coordinates for exact integer match,
4250 // then convert when placing the junction.
4251 for( const auto& [pos, count] : m_netEndpoints )
4252 {
4253 if( junctionPts.count( pos ) )
4254 continue;
4255
4256 VECTOR2I pt = toKiCad( pos.first, pos.second );
4257
4258 for( const auto& [segStart, segEnd] : wireSegs )
4259 {
4260 if( pt == segStart || pt == segEnd )
4261 continue;
4262
4263 bool isHorizontal = ( segStart.y == segEnd.y );
4264 bool isVertical = ( segStart.x == segEnd.x );
4265
4266 if( !isHorizontal && !isVertical )
4267 continue;
4268
4269 bool onSeg = false;
4270
4271 if( isHorizontal && pt.y == segStart.y )
4272 {
4273 int minX = std::min( segStart.x, segEnd.x );
4274 int maxX = std::max( segStart.x, segEnd.x );
4275 onSeg = ( pt.x > minX && pt.x < maxX );
4276 }
4277 else if( isVertical && pt.x == segStart.x )
4278 {
4279 int minY = std::min( segStart.y, segEnd.y );
4280 int maxY = std::max( segStart.y, segEnd.y );
4281 onSeg = ( pt.y > minY && pt.y < maxY );
4282 }
4283
4284 if( onSeg )
4285 {
4286 junctionPts.insert( pos );
4287 break;
4288 }
4289 }
4290 }
4291
4292 for( const auto& [gX, gY] : junctionPts )
4293 {
4294 VECTOR2I kicadPos = toKiCad( gX, gY );
4295 m_screen->Append( std::make_unique<SCH_JUNCTION>( kicadPos ).release() );
4296 }
4297}
4298
4299
4301{
4302 if( m_busSegments.empty() )
4303 return;
4304
4305 // Collect all net (wire) endpoints from the screen
4306 std::vector<std::pair<VECTOR2I, SCH_LINE*>> wireEndpoints;
4307
4308 for( SCH_ITEM* item : m_screen->Items() )
4309 {
4310 if( item->Type() != SCH_LINE_T )
4311 continue;
4312
4313 SCH_LINE* wire = static_cast<SCH_LINE*>( item );
4314
4315 if( wire->GetLayer() != LAYER_WIRE )
4316 continue;
4317
4318 wireEndpoints.emplace_back( wire->GetStartPoint(), wire );
4319 wireEndpoints.emplace_back( wire->GetEndPoint(), wire );
4320 }
4321
4322 int entrySize = schIUScale.MilsToIU( DEFAULT_SCH_ENTRY_SIZE );
4323
4324 for( const BUS_SEGMENT& bus : m_busSegments )
4325 {
4326 bool busIsHorizontal = ( bus.start.y == bus.end.y );
4327 bool busIsVertical = ( bus.start.x == bus.end.x );
4328
4329 if( !busIsHorizontal && !busIsVertical )
4330 continue;
4331
4332 for( const auto& [pt, wire] : wireEndpoints )
4333 {
4334 bool onBus = false;
4335
4336 if( busIsHorizontal )
4337 {
4338 int minX = std::min( bus.start.x, bus.end.x );
4339 int maxX = std::max( bus.start.x, bus.end.x );
4340 onBus = ( pt.y == bus.start.y && pt.x >= minX && pt.x <= maxX );
4341 }
4342 else
4343 {
4344 int minY = std::min( bus.start.y, bus.end.y );
4345 int maxY = std::max( bus.start.y, bus.end.y );
4346 onBus = ( pt.x == bus.start.x && pt.y >= minY && pt.y <= maxY );
4347 }
4348
4349 if( !onBus )
4350 continue;
4351
4352 // Determine which direction the wire goes away from the bus
4353 VECTOR2I otherEnd = ( wire->GetStartPoint() == pt ) ? wire->GetEndPoint()
4354 : wire->GetStartPoint();
4355 int dx = 0;
4356 int dy = 0;
4357
4358 if( busIsHorizontal )
4359 {
4360 dy = ( otherEnd.y < pt.y ) ? -entrySize : entrySize;
4361 int sign = bus.ripperDir;
4362
4363 if( sign == 0 )
4364 {
4365 // Auto-detect from wire position relative to bus center
4366 int busMidX = ( bus.start.x + bus.end.x ) / 2;
4367 sign = ( pt.x <= busMidX ) ? 1 : -1;
4368 }
4369
4370 dx = sign * entrySize;
4371 }
4372 else
4373 {
4374 dx = ( otherEnd.x < pt.x ) ? -entrySize : entrySize;
4375 int sign = bus.ripperDir;
4376
4377 if( sign == 0 )
4378 {
4379 int busMidY = ( bus.start.y + bus.end.y ) / 2;
4380 sign = ( pt.y <= busMidY ) ? 1 : -1;
4381 }
4382
4383 dy = sign * entrySize;
4384 }
4385
4386 // Bus entry position is on the bus; its end (pos + size) is on the wire side
4387 auto entry = std::make_unique<SCH_BUS_WIRE_ENTRY>( pt );
4388 entry->SetSize( VECTOR2I( dx, dy ) );
4389
4390 // Shorten the wire so it starts at the bus entry tip instead of the bus
4391 VECTOR2I entryTip = pt + VECTOR2I( dx, dy );
4392
4393 if( wire->GetStartPoint() == pt )
4394 wire->SetStartPoint( entryTip );
4395 else
4396 wire->SetEndPoint( entryTip );
4397
4398 m_screen->Append( entry.release() );
4399 }
4400 }
4401}
4402
4403
4405{
4406 for( const DEFERRED_SHEET& deferred : m_deferredSheets )
4407 {
4408 SCH_SHEET* sheet = deferred.sheet;
4409
4410 SCH_SCREEN* subScreen = new SCH_SCREEN( m_schematic );
4411 subScreen->SetFileName( deferred.sourceFile );
4412 sheet->SetScreen( subScreen );
4413
4414 // Use a fresh importer instance for each sub-schematic to avoid
4415 // clobbering parse state. Share the import stack for recursion detection
4416 // and the symbol library cache to avoid redundant filesystem scanning.
4417 SCH_IO_GEDA subImporter;
4418 subImporter.SetReporter( m_reporter );
4419 subImporter.m_importStack = m_importStack;
4420 subImporter.m_importStack.insert( m_filename.GetFullPath() );
4421 for( const auto& [name, entry] : m_symLibrary )
4422 {
4423 SYM_CACHE_ENTRY subEntry;
4424 subEntry.path = entry.path;
4425 subImporter.m_symLibrary[name] = std::move( subEntry );
4426 }
4427
4429
4430 try
4431 {
4432 subImporter.LoadSchematicFile( deferred.sourceFile, m_schematic,
4433 sheet, m_properties );
4434
4435 // Merge any newly-discovered symbols back into the parent cache
4436 // so subsequent sub-schematics benefit from them.
4437 for( auto& [name, entry] : subImporter.m_symLibrary )
4438 {
4439 if( m_symLibrary.find( name ) == m_symLibrary.end() )
4440 m_symLibrary[name] = std::move( entry );
4441 }
4442 }
4443 catch( const IO_ERROR& e )
4444 {
4445 if( m_reporter )
4446 {
4447 m_reporter->Report(
4448 wxString::Format( _( "Failed to load sub-schematic '%s': %s" ),
4449 deferred.sourceFile, e.What() ),
4451 }
4452 }
4453 }
4454}
4455
4456
4458{
4459 BOX2I bbox;
4460
4461 for( SCH_ITEM* item : m_screen->Items() )
4462 bbox.Merge( item->GetBoundingBox() );
4463
4464 if( bbox.GetWidth() == 0 || bbox.GetHeight() == 0 )
4465 return;
4466
4467 VECTOR2I targetSize = bbox.GetSize();
4468 targetSize += VECTOR2I( schIUScale.MilsToIU( 1500 ), schIUScale.MilsToIU( 1500 ) );
4469
4470 PAGE_INFO pageInfo = m_screen->GetPageSettings();
4471 VECTOR2I pageSizeIU = pageInfo.GetSizeIU( schIUScale.IU_PER_MILS );
4472
4473 if( pageSizeIU.x < targetSize.x )
4474 pageInfo.SetWidthMils( schIUScale.IUToMils( targetSize.x ) );
4475
4476 if( pageSizeIU.y < targetSize.y )
4477 pageInfo.SetHeightMils( schIUScale.IUToMils( targetSize.y ) );
4478
4479 m_screen->SetPageSettings( pageInfo );
4480
4481 pageSizeIU = m_screen->GetPageSettings().GetSizeIU( schIUScale.IU_PER_MILS );
4482 VECTOR2I sheetCentre( pageSizeIU.x / 2, pageSizeIU.y / 2 );
4483 VECTOR2I itemsCentre = bbox.Centre();
4484
4485 VECTOR2I translation = sheetCentre - itemsCentre;
4486 translation.x = translation.x - translation.x % schIUScale.MilsToIU( 100 );
4487 translation.y = translation.y - translation.y % schIUScale.MilsToIU( 100 );
4488
4489 std::vector<SCH_ITEM*> allItems;
4490 std::copy( m_screen->Items().begin(), m_screen->Items().end(),
4491 std::back_inserter( allItems ) );
4492
4493 for( SCH_ITEM* item : allItems )
4494 {
4495 item->SetPosition( item->GetPosition() + translation );
4496 item->ClearFlags();
4497 m_screen->Update( item );
4498 }
4499}
4500
4501
4502// ==========================================================================
4503// Main entry point
4504// ==========================================================================
4505
4506SCH_SHEET* SCH_IO_GEDA::LoadSchematicFile( const wxString& aFileName, SCHEMATIC* aSchematic,
4507 SCH_SHEET* aAppendToMe,
4508 const std::map<std::string, UTF8>* aProperties )
4509{
4510 wxASSERT( !aFileName.IsEmpty() && aSchematic );
4511
4512 m_properties = aProperties;
4513 m_schematic = aSchematic;
4514 m_filename = wxFileName( aFileName );
4515 m_libSymbols.clear();
4516 m_netEndpoints.clear();
4517 m_netAttrRecords.clear();
4518 m_busSegments.clear();
4519 m_deferredSheets.clear();
4520 m_pendingComp.reset();
4522 m_symLibrary.clear();
4523
4524 m_maxY = 0;
4525 m_releaseVersion = 0;
4527 m_powerCounter = 0;
4528
4529 if( aAppendToMe )
4530 {
4531 wxCHECK_MSG( aSchematic->IsValid(), nullptr,
4532 "Can't append to a schematic with no root!" );
4533 m_rootSheet = aAppendToMe;
4534 }
4535 else
4536 {
4537 m_rootSheet = new SCH_SHEET( aSchematic );
4538 m_rootSheet->SetFileName( aFileName );
4539 aSchematic->SetTopLevelSheets( { m_rootSheet } );
4540 }
4541
4542 if( !m_rootSheet->GetScreen() )
4543 {
4544 SCH_SCREEN* screen = new SCH_SCREEN( aSchematic );
4545 screen->SetFileName( aFileName );
4546 m_rootSheet->SetScreen( screen );
4547 const_cast<KIID&>( m_rootSheet->m_Uuid ) = screen->GetUuid();
4548 }
4549
4550 m_screen = m_rootSheet->GetScreen();
4551
4552 wxTextFile file;
4553
4554 if( !file.Open( aFileName ) )
4555 THROW_IO_ERRORF( _( "Cannot open file '%s'." ), aFileName );
4556
4557 if( file.GetLineCount() == 0 )
4558 THROW_IO_ERRORF( _( "File '%s' is empty." ), aFileName );
4559
4560 // First pass: scan for max Y coordinate to set up the Y-flip transform.
4561 // We need this before creating objects because coordinates are transformed during creation.
4562 for( size_t i = 0; i < file.GetLineCount(); i++ )
4563 {
4564 wxString line = file.GetLine( i );
4565 wxChar type = line.IsEmpty() ? '\0' : line[0];
4566
4567 // Skip embedded component blocks entirely. Their coordinates are
4568 // symbol-local and must not affect the schematic-level Y extent.
4569 if( type == '[' )
4570 {
4571 while( ++i < file.GetLineCount() )
4572 {
4573 if( file.GetLine( i ).Trim() == wxT( "]" ) )
4574 break;
4575 }
4576
4577 continue;
4578 }
4579
4580 if( type == 'C' || type == 'N' || type == 'U' || type == 'T' || type == 'L'
4581 || type == 'B' || type == 'V' || type == 'A' || type == 'P' || type == 'G' )
4582 {
4583 wxStringTokenizer tok( line );
4584 tok.GetNextToken(); // skip type
4585
4586 long val = 0;
4587
4588 if( tok.HasMoreTokens() ) tok.GetNextToken(); // x or x1
4589
4590 if( tok.HasMoreTokens() )
4591 {
4592 tok.GetNextToken().ToLong( &val );
4593
4594 if( val > m_maxY )
4595 m_maxY = static_cast<int>( val );
4596 }
4597
4598 if( type == 'N' || type == 'U' || type == 'L' || type == 'P' )
4599 {
4600 if( tok.HasMoreTokens() ) tok.GetNextToken(); // x2
4601
4602 if( tok.HasMoreTokens() )
4603 {
4604 tok.GetNextToken().ToLong( &val );
4605
4606 if( val > m_maxY )
4607 m_maxY = static_cast<int>( val );
4608 }
4609 }
4610
4611 if( type == 'B' )
4612 {
4613 long bw = 0, bh = 0;
4614
4615 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &bw );
4616 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &bh );
4617
4618 long top = val + bh;
4619
4620 if( top > m_maxY )
4621 m_maxY = static_cast<int>( top );
4622 }
4623
4624 if( type == 'V' || type == 'A' )
4625 {
4626 // V: cx cy radius ... A: cx cy radius ...
4627 // After reading cy into val, the next token is radius
4628 long radius = 0;
4629
4630 if( tok.HasMoreTokens() )
4631 tok.GetNextToken().ToLong( &radius );
4632
4633 long extent = val + radius;
4634
4635 if( extent > m_maxY )
4636 m_maxY = static_cast<int>( extent );
4637 }
4638
4639 // T lines have content lines that follow the header. Skip them
4640 // to avoid misinterpreting text as object lines.
4641 // Format: T x y color size vis show_nv angle align num_lines
4642 // tok has consumed: type, x (skipped), y (read). Skip remaining 6
4643 // fields to reach num_lines.
4644 if( type == 'T' )
4645 {
4646 long numTextLines = 1;
4647
4648 for( int j = 0; j < 6 && tok.HasMoreTokens(); j++ )
4649 tok.GetNextToken();
4650
4651 if( tok.HasMoreTokens() )
4652 tok.GetNextToken().ToLong( &numTextLines );
4653
4654 i += static_cast<size_t>( numTextLines );
4655 }
4656 }
4657 else if( type == 'H' )
4658 {
4659 // Scan H path lines for Y coordinates in subsequent lines
4660 wxStringTokenizer tok( line );
4661 tok.GetNextToken(); // skip 'H'
4662 long numlines = 0;
4663
4664 for( int j = 0; j < 12 && tok.HasMoreTokens(); j++ )
4665 tok.GetNextToken();
4666
4667 if( tok.HasMoreTokens() )
4668 tok.GetNextToken().ToLong( &numlines );
4669
4670 for( long j = 0; j < numlines && ( i + 1 ) < file.GetLineCount(); j++ )
4671 {
4672 i++;
4673 wxString pathLine = file.GetLine( i );
4674 wxStringTokenizer ptok( pathLine, wxT( " ,\t" ) );
4675
4676 // Path data uses SVG-like commands: M x,y L x,y C x1,y1 x2,y2 x3,y3 z
4677 // Coordinate pairs alternate X then Y. Command letters reset to X.
4678 bool nextIsY = false;
4679
4680 while( ptok.HasMoreTokens() )
4681 {
4682 wxString token = ptok.GetNextToken();
4683
4684 if( token.length() == 1 && wxIsalpha( token[0] ) )
4685 {
4686 nextIsY = false;
4687 continue;
4688 }
4689
4690 long pval = 0;
4691
4692 if( token.ToLong( &pval ) )
4693 {
4694 if( nextIsY && pval > m_maxY )
4695 m_maxY = static_cast<int>( pval );
4696
4697 nextIsY = !nextIsY;
4698 }
4699 }
4700 }
4701 }
4702 }
4703
4704 m_maxY += 1000;
4705
4706 // Parse version line
4707 size_t lineIdx = 0;
4708 wxString firstLine = file.GetLine( lineIdx );
4709 lineIdx++;
4710
4711 if( !parseVersionLine( firstLine ) )
4712 THROW_IO_ERRORF( _( "File '%s' is not a valid gEDA schematic." ), aFileName );
4713
4714 // Main parse loop
4715 while( lineIdx < file.GetLineCount() )
4716 {
4717 wxString line = file.GetLine( lineIdx );
4718 lineIdx++;
4719
4720 if( line.IsEmpty() )
4721 continue;
4722
4723 wxChar type = line[0];
4724
4725 switch( type )
4726 {
4727 case 'C':
4728 parseComponent( line, file, lineIdx );
4729 break;
4730
4731 case 'N':
4732 parseNet( line, file, lineIdx );
4733 break;
4734
4735 case 'U':
4736 parseBus( line, file, lineIdx );
4737 break;
4738
4739 case 'T':
4740 parseText( line, file, lineIdx );
4741 break;
4742
4743 case 'L':
4744 parseLine( line, file, lineIdx );
4745 break;
4746
4747 case 'B':
4748 parseBox( line, file, lineIdx );
4749 break;
4750
4751 case 'V':
4752 parseCircle( line, file, lineIdx );
4753 break;
4754
4755 case 'A':
4756 parseArc( line, file, lineIdx );
4757 break;
4758
4759 case 'H':
4760 parsePath( line, file, lineIdx );
4761 break;
4762
4763 case 'G':
4764 parsePicture( line, file, lineIdx );
4765 break;
4766
4767 case 'P':
4768 parsePin( line, file, lineIdx );
4769 break;
4770
4771 case '[':
4772 parseEmbeddedComponent( file, lineIdx );
4773 break;
4774
4775 case '{':
4776 {
4777 while( lineIdx < file.GetLineCount() )
4778 {
4779 if( file.GetLine( lineIdx ).Trim() == wxT( "}" ) )
4780 {
4781 lineIdx++;
4782 break;
4783 }
4784
4785 lineIdx++;
4786 }
4787
4788 break;
4789 }
4790
4791 case '#':
4792 case 'v':
4793 default:
4794 break;
4795 }
4796 }
4797
4798 postProcess();
4799
4800 // Size the page to fit the imported content and center it, following
4801 // the same approach as the Eagle importer.
4803
4804 // Multi-page support: when the project handler passes additional schematic
4805 // files, create sub-sheets for each and load them into the hierarchy.
4806 if( aProperties )
4807 {
4808 auto it = aProperties->find( "additional_schematics" );
4809
4810 if( it != aProperties->end() )
4811 {
4812 wxString additionalFiles = wxString::FromUTF8( it->second.c_str() );
4813 wxStringTokenizer tok( additionalFiles, wxT( ";" ) );
4814 int sheetY = schIUScale.MilsToIU( 500 );
4815 int sheetSpacing = schIUScale.MilsToIU( 2000 );
4816 int pageNum = 2;
4817
4818 while( tok.HasMoreTokens() )
4819 {
4820 wxString filePath = tok.GetNextToken();
4821 wxFileName fn( filePath );
4822
4823 VECTOR2I pos( schIUScale.MilsToIU( 500 ), sheetY );
4824 VECTOR2I size( schIUScale.MilsToIU( 2000 ), schIUScale.MilsToIU( 1500 ) );
4825
4826 auto subSheet = std::make_unique<SCH_SHEET>( m_rootSheet, pos, size );
4827
4828 subSheet->GetField( FIELD_T::SHEET_NAME )->SetText(
4829 wxString::Format( wxT( "Page %d" ), pageNum ) );
4830 subSheet->GetField( FIELD_T::SHEET_FILENAME )->SetText( fn.GetFullName() );
4831 subSheet->SetFileName( fn.GetFullName() );
4832
4833 SCH_SHEET* subSheetPtr = subSheet.get();
4834 m_screen->Append( subSheet.release() );
4835
4836 if( fn.FileExists() )
4837 {
4838 SCH_SCREEN* subScreen = new SCH_SCREEN( m_schematic );
4839 subScreen->SetFileName( fn.GetFullPath() );
4840 subSheetPtr->SetScreen( subScreen );
4841
4842 SCH_IO_GEDA subImporter;
4843 subImporter.SetReporter( m_reporter );
4844
4845 for( const auto& [name, entry] : m_symLibrary )
4846 {
4848 copy.path = entry.path;
4849 copy.symversion = entry.symversion;
4850 copy.netAttr = entry.netAttr;
4851 subImporter.m_symLibrary[name] = std::move( copy );
4852 }
4853
4855
4856 try
4857 {
4858 subImporter.LoadSchematicFile( fn.GetFullPath(), m_schematic,
4859 subSheetPtr, nullptr );
4860 }
4861 catch( const IO_ERROR& e )
4862 {
4863 if( m_reporter )
4864 {
4865 m_reporter->Report(
4866 wxString::Format( _( "Failed to load page '%s': %s" ),
4867 fn.GetFullName(), e.What() ),
4869 }
4870 }
4871 }
4872
4873 sheetY += sheetSpacing;
4874 pageNum++;
4875 }
4876 }
4877 }
4878
4879 return m_rootSheet;
4880}
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:123
constexpr double ARC_LOW_DEF_MM
Definition base_units.h:127
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
constexpr size_type GetWidth() const
Definition box2.h:210
constexpr Vec Centre() const
Definition box2.h:93
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:654
constexpr size_type GetHeight() const
Definition box2.h:211
constexpr const SizeVec & GetSize() const
Definition box2.h:202
virtual void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:244
void SetCenter(const VECTOR2I &aCenter)
virtual void SetBezierC2(const VECTOR2I &aPt)
Definition eda_shape.h:282
virtual void SetBezierC1(const VECTOR2I &aPt)
Definition eda_shape.h:279
void RebuildBezierToSegmentsPointsList(int aMaxError)
Rebuild the m_bezierPoints vertex list that approximate the Bezier curve by a list of segments.
void SetFillMode(FILL_T aFill)
virtual void SetStart(const VECTOR2I &aStart)
Definition eda_shape.h:194
virtual void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:532
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:381
REPORTER * m_reporter
Reporter to log errors/warnings to, may be nullptr.
Definition io_base.h:238
virtual void SetReporter(REPORTER *aReporter)
Set an optional reporter for warnings/errors.
Definition io_base.h:89
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
Definition kiid.h:46
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
static UTF8 FixIllegalChars(const UTF8 &aLibItemName, bool aLib)
Replace illegal LIB_ID item name characters with underscores '_'.
Definition lib_id.cpp:205
Define a library symbol object.
Definition lib_symbol.h:114
void SetGlobalPower()
wxString GetName() const override
Definition lib_symbol.h:176
SCH_FIELD & GetValueField()
Return reference to the value field.
Definition lib_symbol.h:428
std::vector< SCH_PIN * > GetPins() const override
void AddDrawItem(SCH_ITEM *aItem, bool aSort=true)
Add a new draw aItem to the draw object list and sort according to aSort.
SCH_FIELD & GetReferenceField()
Return reference to the reference designator field.
Definition lib_symbol.h:432
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:75
void SetHeightMils(double aHeightInMils)
const VECTOR2D GetSizeIU(double aIUScale) const
Gets the page size in internal units.
Definition page_info.h:173
void SetWidthMils(double aWidthInMils)
A REFERENCE_IMAGE is a wrapper around a BITMAP_IMAGE that is displayed in an editor as a reference fo...
bool ReadImageFile(const wxString &aFullFilename)
Read and store an image file.
VECTOR2I GetSize() const
void SetImageScale(double aScale)
Set the image "zoom" value.
Holds all the data relating to one schematic.
Definition schematic.h:90
bool IsValid() const
A simple test if the schematic is loaded, not a complete one.
Definition schematic.h:174
void SetTopLevelSheets(const std::vector< SCH_SHEET * > &aSheets)
void SetPosition(const VECTOR2I &aPosition) override
void SetText(const wxString &aText) override
std::unique_ptr< PENDING_COMPONENT > m_pendingComp
Pending component awaiting symbol resolution.
void addBusEntries()
Create SCH_BUS_WIRE_ENTRY objects where net endpoints touch bus segments.
void importHierarchicalSheet(const wxString &aSourceFile)
Import a gEDA hierarchical sub-schematic as a KiCad SCH_SHEET.
int toKiCadDist(int aMils) const
Convert a gEDA distance in mils to KiCad IU.
SCHEMATIC * m_schematic
void addSymbolGraphic(LIB_SYMBOL &aSymbol, const wxString &aLine, wxTextFile &aFile, size_t &aLineIdx, wxChar aType)
Add a graphical item (line/box/circle/arc/path) to a LIB_SYMBOL.
void parseBus(const wxString &aLine, wxTextFile &aFile, size_t &aLineIdx)
void fitPageToContent()
Enlarge the page if needed and center all content on the sheet.
std::vector< GEDA_ATTR > maybeParseAttributes(wxTextFile &aFile, size_t &aLineIdx)
Check for and consume a { } attribute block at the current line position.
wxFileName m_filename
SCH_SHEET * LoadSchematicFile(const wxString &aFileName, SCHEMATIC *aSchematic, SCH_SHEET *aAppendToMe=nullptr, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load information from some input file format that this SCH_IO implementation knows about,...
bool m_symLibraryInitialized
const GEDA_ATTR * findAttrStruct(const std::vector< GEDA_ATTR > &aAttrs, const wxString &aName) const
Find a GEDA_ATTR struct by name, returns nullptr if not found.
void parseEmbeddedComponent(wxTextFile &aFile, size_t &aLineIdx)
void addJunctions()
Place junctions where 3+ net/pin endpoints coincide.
void parseNet(const wxString &aLine, wxTextFile &aFile, size_t &aLineIdx)
void postProcess()
Run the full post-processing pipeline after parsing is complete:
long m_fileFormatVersion
std::map< std::pair< int, int >, int > m_netEndpoints
Net endpoint positions in raw gEDA coordinates for junction detection.
int m_powerCounter
Sequential counter for auto-generated #PWR references.
SCH_SHEET * m_rootSheet
static constexpr int MILS_TO_IU
gEDA coordinates are mils with Y-up.
SCH_SCREEN * m_screen
int m_maxY
The maximum Y coordinate seen during parsing (gEDA coords, before flip).
std::vector< DEFERRED_SHEET > m_deferredSheets
std::unique_ptr< LIB_SYMBOL > loadBuiltinSymbol(const wxString &aBasename)
Try loading a symbol from the built-in standard library embedded in the importer.
LIB_SYMBOL * getOrLoadSymbol(const wxString &aBasename)
Get or load a cached symbol by gEDA basename.
void parseText(const wxString &aLine, wxTextFile &aFile, size_t &aLineIdx)
void parseComponent(const wxString &aLine, wxTextFile &aFile, size_t &aLineIdx)
void parsePin(const wxString &aLine, wxTextFile &aFile, size_t &aLineIdx)
int toKiCadOrientation(int aAngle, int aMirror) const
Map gEDA angle (0/90/180/270) + mirror to KiCad symbol orientation.
void parseLine(const wxString &aLine, wxTextFile &aFile, size_t &aLineIdx)
void parseCircle(const wxString &aLine, wxTextFile &aFile, size_t &aLineIdx)
std::map< wxString, std::unique_ptr< LIB_SYMBOL > > m_libSymbols
Loaded symbols for this import session, keyed by basename.
void parsePath(const wxString &aLine, wxTextFile &aFile, size_t &aLineIdx)
void scanSymbolDir(const wxString &aDir, int aDepth=0)
Recursively scan a directory for .sym files and populate m_symLibrary.
std::set< wxString > m_importStack
Set of fully-resolved file paths currently in the import call stack, used to detect and prevent circu...
wxString getLibName() const
Derive the KiCad import library name from the schematic file.
void flushPendingComponent()
Instantiate the pending component: look up or load the symbol, create SCH_SYMBOL, apply transforms an...
std::unique_ptr< LIB_SYMBOL > createFallbackSymbol(const wxString &aBasename)
Create a fallback rectangular symbol when the .sym file is not found.
static const std::map< wxString, wxString > & getBuiltinSymbols()
Return the map of built-in gEDA symbol definitions (symbol name -> .sym content).
VECTOR2I toKiCad(int aGedaX, int aGedaY) const
Apply the Y-flip and scale to transform gEDA coords to KiCad.
static LINE_STYLE toLineStyle(int aDashStyle)
Map gEDA dashstyle (0-4) to KiCad LINE_STYLE.
void parseBox(const wxString &aLine, wxTextFile &aFile, size_t &aLineIdx)
std::vector< NET_ATTR_RECORD > m_netAttrRecords
wxString findAttr(const std::vector< GEDA_ATTR > &aAttrs, const wxString &aName) const
Find an attribute by name, returns empty string if not found.
std::vector< BUS_SEGMENT > m_busSegments
void parseArc(const wxString &aLine, wxTextFile &aFile, size_t &aLineIdx)
bool CanReadSchematicFile(const wxString &aFileName) const override
Checks if this SCH_IO can read the specified schematic file.
std::unique_ptr< LIB_SYMBOL > loadSymbolFile(const wxString &aPath, wxString *aSymversion=nullptr, wxString *aNetAttr=nullptr)
Load a .sym file and return a LIB_SYMBOL.
void processNetAttributes()
For each component with net= attributes, create global labels at pin positions.
void initSymbolLibrary()
Ensure the symbol library hash is built by scanning gEDA library dirs.
std::map< wxString, SYM_CACHE_ENTRY > m_symLibrary
Symbol library cache: gEDA basename -> cache entry.
long m_releaseVersion
gEDA file version fields from the "v YYYYMMDD N" header line.
~SCH_IO_GEDA() override
void parsePicture(const wxString &aLine, wxTextFile &aFile, size_t &aLineIdx)
void parseRcFileForLibraries(const wxString &aPath, const wxString &aBaseDir)
Parse an RC file (gafrc or gschemrc) for component-library directives.
void trackEndpoint(int aGedaX, int aGedaY)
Track a point as a net or pin endpoint for junction detection.
bool parseVersionLine(const wxString &aLine)
Parse the "v YYYYMMDD N" version line and validate.
std::vector< GEDA_ATTR > parseAttributes(wxTextFile &aFile, size_t &aLineIdx)
Read a { } attribute block starting after the '{' line.
const std::map< std::string, UTF8 > * m_properties
Properties passed from the import framework (search paths, etc.)
void addSymbolPin(LIB_SYMBOL &aSymbol, int aX1, int aY1, int aX2, int aY2, int aWhichEnd, const std::vector< GEDA_ATTR > &aAttrs)
Create a pin on a LIB_SYMBOL from gEDA P line data and its attributes.
void loadDeferredSheets()
Load sub-schematics for any SCH_SHEET objects created during parsing.
static FILL_T toFillType(int aFillType)
Map gEDA filltype (0-4) to KiCad FILL_T.
SCH_IO(const wxString &aName)
Definition sch_io.h:384
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:162
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition sch_item.h:338
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:38
VECTOR2I GetEndPoint() const
Definition sch_line.h:144
VECTOR2I GetStartPoint() const
Definition sch_line.h:135
const KIID & GetUuid() const
Definition sch_screen.h:529
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
void SetFilled(bool aFilled) override
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition sch_shape.cpp:98
void AddPoint(const VECTOR2I &aPosition)
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:44
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
Simple container to manage line stroke parameters.
virtual void SetShowPinNumbers(bool aShow)
Set or clear the pin number visibility flag.
Definition symbol.h:170
virtual void SetShowPinNames(bool aShow)
Set or clear the pin name visibility flag.
Definition symbol.h:164
wxString wx_str() const
Definition utf8.cpp:41
#define DEFAULT_SCH_ENTRY_SIZE
The default text size in mils. (can be changed in preference menu)
#define _(s)
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:413
static constexpr EDA_ANGLE ANGLE_270
Definition eda_angle.h:416
static constexpr EDA_ANGLE ANGLE_180
Definition eda_angle.h:415
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:47
FILL_T
Definition eda_shape.h:59
@ NO_FILL
Definition eda_shape.h:60
@ HATCH
Definition eda_shape.h:64
@ FILLED_SHAPE
Fill with object color.
Definition eda_shape.h:61
@ CROSS_HATCH
Definition eda_shape.h:66
#define THROW_IO_ERRORF(msg,...)
@ LAYER_DEVICE
Definition layer_ids.h:472
@ LAYER_WIRE
Definition layer_ids.h:458
@ LAYER_NOTES
Definition layer_ids.h:473
@ LAYER_BUS
Definition layer_ids.h:459
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
ELECTRICAL_PINTYPE
The symbol library pin object electrical types used in ERC tests.
Definition pin_type.h:32
@ PT_INPUT
usual pin input: must be connected
Definition pin_type.h:33
@ PT_OUTPUT
usual output
Definition pin_type.h:34
@ PT_TRISTATE
tri state bus pin
Definition pin_type.h:36
@ PT_BIDI
input or output (like port for a microprocessor)
Definition pin_type.h:35
@ PT_OPENEMITTER
pin type open emitter
Definition pin_type.h:45
@ PT_OPENCOLLECTOR
pin type open collector
Definition pin_type.h:44
@ PT_POWER_IN
power input (GND, VCC for ICs). Must be connected to a power output.
Definition pin_type.h:42
@ PT_PASSIVE
pin for passive symbols: must be connected, and can be connected to any pin.
Definition pin_type.h:39
PIN_ORIENTATION
The symbol library pin object orientations.
Definition pin_type.h:101
@ PIN_UP
The pin extends upwards from the connection point: Probably on the bottom side of the symbol.
Definition pin_type.h:123
@ PIN_RIGHT
The pin extends rightwards from the connection point.
Definition pin_type.h:107
@ PIN_LEFT
The pin extends leftwards from the connection point: Probably on the right side of the symbol.
Definition pin_type.h:114
@ PIN_DOWN
The pin extends downwards from the connection: Probably on the top side of the symbol.
Definition pin_type.h:131
@ RPT_SEVERITY_WARNING
static constexpr int GEDA_DEFAULT_TEXT_SIZE_MILS
static wxString convertOverbars(const wxString &aInput)
Convert gEDA overbar markup to KiCad syntax.
static constexpr int DEFAULT_SYMBOL_SIZE_MILS
static int editDistance(const wxString &a, const wxString &b)
Compute the Levenshtein edit distance between two strings.
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
LINE_STYLE
Dashed line types.
Parsed bus segment in KiCad coordinates with gEDA ripper direction.
Deferred hierarchical sheet loads.
Parsed attribute from a gEDA T line inside a { } block.
int showNV
0=name+value, 1=value, 2=name
Components with net= attributes that need post-processing.
Entry in the symbol library search cache.
wxString netAttr
net= attribute (e.g. "GND:1") identifying power symbols
wxString path
Full path to .sym file.
@ SYM_ORIENT_270
Definition symbol.h:38
@ SYM_MIRROR_Y
Definition symbol.h:40
@ SYM_ORIENT_180
Definition symbol.h:37
@ SYM_ORIENT_90
Definition symbol.h:36
@ SYM_ORIENT_0
Definition symbol.h:35
@ USER
The field ID hasn't been set yet; field is invalid.
@ DESCRIPTION
Field Description of part, i.e. "1/4W 1% Metal Film Resistor".
@ DATASHEET
name of datasheet
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
KIBIS top(path, &reporter)
KIBIS_PIN * pin
VECTOR2I center
int radius
VECTOR2I end
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
wxString result
Test unit parsing edge cases and error handling.
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_TOP
double DEG2RAD(double deg)
Definition trigo.h:162
@ SCH_LINE_T
Definition typeinfo.h:160
constexpr int sign(T val)
Definition util.h:141
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
Definition of file extensions used in Kicad.