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
3436 if( symFill != FILL_T::NO_FILL )
3437 {
3438 bezier->SetFilled( true );
3439 bezier->SetFillMode( symFill );
3440 }
3441
3442 aSymbol.AddDrawItem( bezier );
3443 currentPt = endPt;
3444 polyPts.push_back( currentPt );
3445 }
3446 }
3447 else if( pathToken == wxT( "Z" ) || pathToken == wxT( "z" ) )
3448 {
3449 if( currentPt != startPt )
3450 {
3451 polyPts.push_back( startPt );
3452 currentPt = startPt;
3453 }
3454 }
3455 }
3456
3457 flushPoly();
3458
3459 break;
3460 }
3461
3462 default:
3463 break;
3464 }
3465
3466 maybeParseAttributes( aFile, aLineIdx );
3467}
3468
3469
3473static int editDistance( const wxString& a, const wxString& b )
3474{
3475 size_t m = a.length();
3476 size_t n = b.length();
3477
3478 std::vector<int> prev( n + 1 );
3479 std::vector<int> curr( n + 1 );
3480
3481 for( size_t j = 0; j <= n; ++j )
3482 prev[j] = static_cast<int>( j );
3483
3484 for( size_t i = 1; i <= m; ++i )
3485 {
3486 curr[0] = static_cast<int>( i );
3487
3488 for( size_t j = 1; j <= n; ++j )
3489 {
3490 int cost = ( a[i - 1] == b[j - 1] ) ? 0 : 1;
3491 curr[j] = std::min( { prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost } );
3492 }
3493
3494 std::swap( prev, curr );
3495 }
3496
3497 return prev[n];
3498}
3499
3500
3501std::unique_ptr<LIB_SYMBOL> SCH_IO_GEDA::loadBuiltinSymbol( const wxString& aBasename )
3502{
3503 const auto& builtins = getBuiltinSymbols();
3504 auto it = builtins.find( aBasename );
3505
3506 if( it == builtins.end() )
3507 return nullptr;
3508
3509 wxString tempPath = wxFileName::CreateTempFileName( wxT( "geda_sym_" ) );
3510
3511 if( tempPath.IsEmpty() )
3512 return nullptr;
3513
3514 {
3515 wxFile temp( tempPath, wxFile::write );
3516
3517 if( !temp.IsOpened() || !temp.Write( it->second ) )
3518 {
3519 wxRemoveFile( tempPath );
3520 return nullptr;
3521 }
3522 }
3523
3524 wxString netAttr;
3525 std::unique_ptr<LIB_SYMBOL> result = loadSymbolFile( tempPath, nullptr, &netAttr );
3526 wxRemoveFile( tempPath );
3527
3528 if( result )
3529 {
3530 wxString symName = aBasename;
3531
3532 if( symName.EndsWith( wxT( ".sym" ) ) )
3533 symName = symName.Left( symName.length() - 4 );
3534
3535 result->SetName( symName );
3536
3537 if( !netAttr.IsEmpty() )
3538 {
3539 SYM_CACHE_ENTRY& entry = m_symLibrary[aBasename];
3540 entry.netAttr = netAttr;
3541 }
3542 }
3543
3544 return result;
3545}
3546
3547
3548LIB_SYMBOL* SCH_IO_GEDA::getOrLoadSymbol( const wxString& aBasename )
3549{
3550 // Check if already loaded
3551 auto it = m_libSymbols.find( aBasename );
3552
3553 if( it != m_libSymbols.end() )
3554 return it->second.get();
3555
3556 // Try the library cache
3558
3559 auto cacheIt = m_symLibrary.find( aBasename );
3560
3561 if( cacheIt != m_symLibrary.end() )
3562 {
3563 if( !cacheIt->second.symbol )
3564 {
3565 cacheIt->second.symbol = loadSymbolFile( cacheIt->second.path, &cacheIt->second.symversion,
3566 &cacheIt->second.netAttr );
3567
3568 // When a project-local symbol overrides a builtin power symbol but lacks a
3569 // net= attribute, inherit the builtin's net= so power detection still works.
3570 // This handles projects that provide custom graphics for standard power symbols.
3571 if( cacheIt->second.symbol && cacheIt->second.netAttr.IsEmpty() )
3572 {
3573 const auto& builtins = getBuiltinSymbols();
3574 auto builtinIt = builtins.find( aBasename );
3575
3576 if( builtinIt != builtins.end() )
3577 {
3578 // Scan the builtin symbol text for a net= attribute line.
3579 // In gEDA .sym format, net= always starts a line.
3580 const wxString& src = builtinIt->second;
3581 int pos = src.Find( wxT( "\nnet=" ) );
3582
3583 if( pos != wxNOT_FOUND )
3584 {
3585 wxString rest = src.Mid( pos + 5 );
3586 int eol = rest.Find( '\n' );
3587
3588 cacheIt->second.netAttr = ( eol != wxNOT_FOUND )
3589 ? rest.Left( eol ).Trim()
3590 : rest.Trim();
3591 }
3592 }
3593 }
3594 }
3595
3596 if( cacheIt->second.symbol )
3597 {
3598 m_libSymbols[aBasename] = std::make_unique<LIB_SYMBOL>( *cacheIt->second.symbol );
3599 return m_libSymbols[aBasename].get();
3600 }
3601 }
3602
3603 // Try built-in standard symbols before falling back to a placeholder
3604 auto builtin = loadBuiltinSymbol( aBasename );
3605
3606 if( builtin )
3607 {
3608 m_libSymbols[aBasename] = std::move( builtin );
3609 return m_libSymbols[aBasename].get();
3610 }
3611
3612 // Not found anywhere - create a fallback placeholder
3613 m_libSymbols[aBasename] = createFallbackSymbol( aBasename );
3614
3615 if( m_reporter )
3616 {
3617 wxString suggestion;
3618 int bestDist = INT_MAX;
3619
3620 for( const auto& [name, entry] : m_symLibrary )
3621 {
3622 int dist = editDistance( aBasename, name );
3623
3624 if( dist < bestDist && dist <= 3 )
3625 {
3626 bestDist = dist;
3627 suggestion = name;
3628 }
3629 }
3630
3631 // Also check builtins, which may have a closer match than the library cache
3632 for( const auto& [name, content] : getBuiltinSymbols() )
3633 {
3634 int dist = editDistance( aBasename, name );
3635
3636 if( dist < bestDist && dist <= 3 )
3637 {
3638 bestDist = dist;
3639 suggestion = name;
3640 }
3641 }
3642
3643 wxString msg = wxString::Format( _( "Symbol '%s' not found in gEDA libraries." ),
3644 aBasename );
3645
3646 if( !suggestion.IsEmpty() )
3647 msg += wxString::Format( _( " Did you mean '%s'?" ), suggestion );
3648
3649 m_reporter->Report( msg, RPT_SEVERITY_WARNING );
3650 }
3651
3652 return m_libSymbols[aBasename].get();
3653}
3654
3655
3656std::unique_ptr<LIB_SYMBOL> SCH_IO_GEDA::createFallbackSymbol( const wxString& aBasename )
3657{
3658 wxString symName = aBasename;
3659
3660 if( symName.EndsWith( wxT( ".sym" ) ) )
3661 symName = symName.Left( symName.length() - 4 );
3662
3663 auto libSymbol = std::make_unique<LIB_SYMBOL>( symName );
3664 libSymbol->SetShowPinNumbers( true );
3665 libSymbol->SetShowPinNames( true );
3666
3667 int halfSize = toKiCadDist( DEFAULT_SYMBOL_SIZE_MILS ) / 2;
3668
3670 rect->SetStart( VECTOR2I( -halfSize, -halfSize ) );
3671 rect->SetEnd( VECTOR2I( halfSize, halfSize ) );
3673 libSymbol->AddDrawItem( rect );
3674
3675 return libSymbol;
3676}
3677
3678
3680{
3681 if( !m_pendingComp )
3682 return;
3683
3684 // Skip title block symbols
3685 if( m_pendingComp->basename.StartsWith( wxT( "title-" ) ) )
3686 {
3687 m_pendingComp.reset();
3688 return;
3689 }
3690
3691 // Detect no-connect symbols (nc-right-1.sym, nc-left-1.sym, etc.) and emit
3692 // SCH_NO_CONNECT at the pin connection point rather than the component origin.
3693 wxString ncValue = findAttr( m_pendingComp->attrs, wxT( "value" ) );
3694 bool isNoConnect = m_pendingComp->basename.StartsWith( wxT( "nc-" ) )
3695 && ncValue == wxT( "NoConnection" );
3696
3697 if( isNoConnect )
3698 {
3699 LIB_SYMBOL* ncSym = getOrLoadSymbol( m_pendingComp->basename );
3700 int pinLocalX = 0;
3701 int pinLocalY = 0;
3702
3703 if( ncSym )
3704 {
3705 const std::vector<SCH_PIN*>& pins = ncSym->GetGraphicalPins( ALL_UNITS, ALL_BODY_STYLES );
3706
3707 if( !pins.empty() )
3708 {
3709 VECTOR2I pinPos = pins[0]->GetPosition();
3710 pinLocalX = pinPos.x / MILS_TO_IU;
3711 pinLocalY = -pinPos.y / MILS_TO_IU;
3712 }
3713 }
3714
3715 // Apply gEDA component rotation/mirror to the pin offset
3716 int rx = pinLocalX;
3717 int ry = pinLocalY;
3718
3719 switch( m_pendingComp->angle )
3720 {
3721 case 90: rx = -pinLocalY; ry = pinLocalX; break;
3722 case 180: rx = -pinLocalX; ry = -pinLocalY; break;
3723 case 270: rx = pinLocalY; ry = -pinLocalX; break;
3724 default: break;
3725 }
3726
3727 if( m_pendingComp->mirror )
3728 rx = -rx;
3729
3730 int gedaX = m_pendingComp->x + rx;
3731 int gedaY = m_pendingComp->y + ry;
3732
3733 m_screen->Append( new SCH_NO_CONNECT( toKiCad( gedaX, gedaY ) ) );
3734 m_pendingComp.reset();
3735 return;
3736 }
3737
3738 // Check for hierarchical sheet reference. In gEDA, a source= attribute
3739 // pointing to a .sch file indicates a hierarchical sub-schematic.
3740 wxString sourceAttr = findAttr( m_pendingComp->attrs, wxT( "source" ) );
3741
3742 if( !sourceAttr.IsEmpty() && sourceAttr.EndsWith( wxT( ".sch" ) ) )
3743 {
3744 importHierarchicalSheet( sourceAttr );
3745 m_pendingComp.reset();
3746 return;
3747 }
3748
3749 LIB_SYMBOL* libSym = nullptr;
3750
3751 if( m_pendingComp->embedded && m_pendingComp->embeddedSym )
3752 {
3753 wxString basename = m_pendingComp->basename;
3754 m_libSymbols[basename] = std::move( m_pendingComp->embeddedSym );
3755 libSym = m_libSymbols[basename].get();
3756 }
3757 else
3758 {
3759 libSym = getOrLoadSymbol( m_pendingComp->basename );
3760 }
3761
3762 if( !libSym )
3763 {
3764 m_pendingComp.reset();
3765 return;
3766 }
3767
3768 // Compare component symversion against the loaded symbol's symversion.
3769 // A mismatch in the major version number indicates the symbol has changed
3770 // in an incompatible way since the schematic was saved.
3771 wxString compSymver = findAttr( m_pendingComp->attrs, wxT( "symversion" ) );
3772
3773 if( !compSymver.IsEmpty() && m_reporter )
3774 {
3775 wxString symSymver;
3776 auto cacheIt = m_symLibrary.find( m_pendingComp->basename );
3777
3778 if( cacheIt != m_symLibrary.end() )
3779 symSymver = cacheIt->second.symversion;
3780
3781 if( !symSymver.IsEmpty() )
3782 {
3783 long compMajor = 0;
3784 long symMajor = 0;
3785 compSymver.BeforeFirst( '.' ).ToLong( &compMajor );
3786 symSymver.BeforeFirst( '.' ).ToLong( &symMajor );
3787
3788 if( compMajor != symMajor )
3789 {
3790 wxString refdes = findAttr( m_pendingComp->attrs, wxT( "refdes" ) );
3791 wxString label = refdes.IsEmpty() ? m_pendingComp->basename : refdes;
3792
3793 m_reporter->Report( wxString::Format( _( "Symbol version mismatch for '%s' (%s): schematic "
3794 "has symversion %s, library symbol has symversion %s." ),
3795 label,
3796 m_pendingComp->basename,
3797 compSymver,
3798 symSymver ),
3800 }
3801 }
3802 }
3803
3804 // Detect power symbols via the net= attribute in the .sym definition.
3805 // gEDA power symbols (gnd-1.sym, vcc-1.sym, etc.) carry a net=NETNAME:PIN
3806 // attribute that identifies them as implicit power connections.
3807 wxString symNetAttr;
3808 auto cacheEntry = m_symLibrary.find( m_pendingComp->basename );
3809
3810 if( cacheEntry != m_symLibrary.end() )
3811 symNetAttr = cacheEntry->second.netAttr;
3812
3813 bool isPowerSym = !symNetAttr.IsEmpty();
3814 wxString powerNetName;
3815 std::unique_ptr<LIB_SYMBOL> powerCopy;
3816
3817 if( isPowerSym )
3818 {
3819 // Schematic-level net= overrides the .sym-level default (e.g. generic-power.sym
3820 // has net=Vcc:1 but instances use net=5V:1 or net=9V:1).
3821 wxString schematicNet = findAttr( m_pendingComp->attrs, wxT( "net" ) );
3822 wxString effectiveNet = schematicNet.IsEmpty() ? symNetAttr : schematicNet;
3823
3824 int colonPos = effectiveNet.Find( ':' );
3825
3826 if( colonPos != wxNOT_FOUND )
3827 powerNetName = effectiveNet.Left( colonPos );
3828 else
3829 powerNetName = effectiveNet;
3830
3831 // Work on a copy so the shared cached symbol is not modified.
3832 // Each power instance may have a different net name.
3833 powerCopy = std::make_unique<LIB_SYMBOL>( *libSym );
3834 libSym = powerCopy.get();
3835
3836 libSym->SetGlobalPower();
3837 libSym->SetShowPinNames( false );
3838 libSym->SetShowPinNumbers( false );
3839 libSym->GetReferenceField().SetText( wxT( "#PWR" ) );
3840 libSym->GetReferenceField().SetVisible( false );
3841 libSym->GetValueField().SetText( powerNetName );
3842 libSym->GetValueField().SetVisible( true );
3843
3845 {
3846 pin->SetName( powerNetName );
3848 }
3849 }
3850
3851 LIB_ID libId( getLibName(), libSym->GetName() );
3853
3854 auto symbol = std::make_unique<SCH_SYMBOL>( *libSym, libId, &m_schematic->CurrentSheet(), 1, 0, pos );
3855
3856 int orient = toKiCadOrientation( m_pendingComp->angle, m_pendingComp->mirror );
3857 symbol->SetOrientation( orient );
3858
3859 if( isPowerSym )
3860 {
3861 wxString pwrRef = wxString::Format( wxT( "#PWR%04d" ), ++m_powerCounter );
3862 symbol->SetRef( &m_schematic->CurrentSheet(), pwrRef );
3863 symbol->GetField( FIELD_T::REFERENCE )->SetText( pwrRef );
3864 symbol->GetField( FIELD_T::REFERENCE )->SetVisible( false );
3865 symbol->GetField( FIELD_T::VALUE )->SetText( powerNetName );
3866 symbol->GetField( FIELD_T::VALUE )->SetVisible( true );
3867 }
3868
3869 // Mark graphical-only symbols as excluded from BOM, board, and simulation
3870 wxString graphicalAttr = findAttr( m_pendingComp->attrs, wxT( "graphical" ) );
3871
3872 if( graphicalAttr == wxT( "1" ) )
3873 {
3874 symbol->SetExcludedFromBOM( true );
3875 symbol->SetExcludedFromBoard( true );
3876 symbol->SetExcludedFromSim( true );
3877 }
3878
3879 wxString refdes = findAttr( m_pendingComp->attrs, wxT( "refdes" ) );
3880 wxString value = findAttr( m_pendingComp->attrs, wxT( "value" ) );
3881 wxString footprint = findAttr( m_pendingComp->attrs, wxT( "footprint" ) );
3882
3883 if( !isPowerSym && !refdes.IsEmpty() )
3884 {
3885 symbol->SetRef( &m_schematic->CurrentSheet(), refdes );
3886
3887 SCH_FIELD* refField = symbol->GetField( FIELD_T::REFERENCE );
3888 const GEDA_ATTR* refAttr = findAttrStruct( m_pendingComp->attrs, wxT( "refdes" ) );
3889 refField->SetText( refdes );
3890
3891 if( refAttr )
3892 {
3893 refField->SetPosition( toKiCad( refAttr->x, refAttr->y ) );
3894 int textSize = toKiCadDist( refAttr->size * 10 );
3895 refField->SetTextSize( VECTOR2I( textSize, textSize ) );
3896 refField->SetVisible( refAttr->visible );
3897 }
3898 }
3899
3900 if( !isPowerSym && !value.IsEmpty() )
3901 {
3902 SCH_FIELD* valField = symbol->GetField( FIELD_T::VALUE );
3903 const GEDA_ATTR* valAttr = findAttrStruct( m_pendingComp->attrs, wxT( "value" ) );
3904 valField->SetText( value );
3905
3906 if( valAttr )
3907 {
3908 valField->SetPosition( toKiCad( valAttr->x, valAttr->y ) );
3909 int textSize = toKiCadDist( valAttr->size * 10 );
3910 valField->SetTextSize( VECTOR2I( textSize, textSize ) );
3911 valField->SetVisible( valAttr->visible );
3912 }
3913 }
3914
3915 if( !footprint.IsEmpty() )
3916 symbol->SetFootprintFieldText( footprint );
3917
3918 // Map documentation attribute to KiCad's native DATASHEET field
3919 wxString documentation = findAttr( m_pendingComp->attrs, wxT( "documentation" ) );
3920
3921 if( !documentation.IsEmpty() )
3922 {
3923 SCH_FIELD* dsField = symbol->GetField( FIELD_T::DATASHEET );
3924 dsField->SetText( documentation );
3925 }
3926
3927 // Map description attribute to KiCad's native DESCRIPTION field
3928 const GEDA_ATTR* descAttr = findAttrStruct( m_pendingComp->attrs, wxT( "description" ) );
3929
3930 if( descAttr && !descAttr->value.IsEmpty() )
3931 {
3932 SCH_FIELD* descField = symbol->GetField( FIELD_T::DESCRIPTION );
3933 descField->SetText( descAttr->value );
3934 descField->SetVisible( descAttr->visible );
3935
3936 if( descAttr->visible )
3937 {
3938 descField->SetPosition( toKiCad( descAttr->x, descAttr->y ) );
3939 int textSize = toKiCadDist( descAttr->size * 10 );
3940 descField->SetTextSize( VECTOR2I( textSize, textSize ) );
3941 }
3942 }
3943
3944 // Collect net= attributes for post-processing. Power symbols already establish
3945 // their net connection through the pin name, so skip global label generation.
3946 if( !isPowerSym )
3947 {
3948 for( const GEDA_ATTR& attr : m_pendingComp->attrs )
3949 {
3950 if( attr.name == wxT( "net" ) )
3951 {
3952 int colonPos = attr.value.Find( ':' );
3953
3954 if( colonPos != wxNOT_FOUND )
3955 {
3956 NET_ATTR_RECORD rec;
3957 rec.netname = attr.value.Left( colonPos );
3958 rec.pinnumber = attr.value.Mid( colonPos + 1 );
3959 rec.symbol = symbol.get();
3960 m_netAttrRecords.push_back( rec );
3961 }
3962 }
3963 }
3964 }
3965
3966 // Store additional attributes as custom fields (skip those already handled)
3967 for( const GEDA_ATTR& attr : m_pendingComp->attrs )
3968 {
3969 if( attr.name == wxT( "refdes" ) || attr.name == wxT( "value" )
3970 || attr.name == wxT( "footprint" ) || attr.name == wxT( "net" )
3971 || attr.name == wxT( "device" ) || attr.name == wxT( "symversion" )
3972 || attr.name == wxT( "documentation" ) || attr.name == wxT( "description" )
3973 || attr.name == wxT( "graphical" )
3974 || attr.name == wxT( "slot" ) || attr.name == wxT( "numslots" )
3975 || attr.name == wxT( "slotdef" ) )
3976 {
3977 continue;
3978 }
3979
3980 SCH_FIELD* field = symbol->AddField(
3981 SCH_FIELD( symbol.get(), FIELD_T::USER, attr.name ) );
3982 field->SetText( attr.value );
3983 field->SetVisible( attr.visible );
3984 field->SetPosition( pos );
3985 }
3986
3987 // Track pin connection points for junction detection. Reverse-map the
3988 // KiCad IU positions back to gEDA coordinates to match the wire endpoints.
3990 {
3991 VECTOR2I pinPos = symbol->GetPinPhysicalPosition( pin );
3992 int gedaX = pinPos.x / MILS_TO_IU;
3993 int gedaY = m_maxY - pinPos.y / MILS_TO_IU;
3994 trackEndpoint( gedaX, gedaY );
3995 }
3996
3997 symbol->SetLibSymbol( new LIB_SYMBOL( *libSym ) );
3998
3999 // Apply multi-slot pin remapping on the symbol's PRIVATE copy of the lib symbol.
4000 // gEDA slotdef format is "N:pin1,pin2,pin3" where N is the slot number
4001 // and each pin corresponds to a pinseq-ordered pin in the symbol.
4002 // Operating on the private copy prevents corrupting the shared cached symbol.
4003 wxString slotStr = findAttr( m_pendingComp->attrs, wxT( "slot" ) );
4004 long slotNum = 0;
4005
4006 if( !slotStr.IsEmpty() && slotStr.ToLong( &slotNum ) && slotNum > 0 )
4007 {
4008 wxString targetSlotDef;
4009
4010 for( const GEDA_ATTR& attr : m_pendingComp->attrs )
4011 {
4012 if( attr.name != wxT( "slotdef" ) )
4013 continue;
4014
4015 int colonPos = attr.value.Find( ':' );
4016
4017 if( colonPos == wxNOT_FOUND )
4018 continue;
4019
4020 long defSlot = 0;
4021
4022 if( attr.value.Left( colonPos ).ToLong( &defSlot ) && defSlot == slotNum )
4023 {
4024 targetSlotDef = attr.value.Mid( colonPos + 1 );
4025 break;
4026 }
4027 }
4028
4029 if( !targetSlotDef.IsEmpty() )
4030 {
4031 wxArrayString slotPins;
4032 wxStringTokenizer slotTok( targetSlotDef, wxT( "," ) );
4033
4034 while( slotTok.HasMoreTokens() )
4035 slotPins.Add( slotTok.GetNextToken() );
4036
4037 LIB_SYMBOL* privateSym = symbol->GetLibSymbolRef().get();
4038
4039 // Build pinseq-ordered list from the private copy's pins.
4040 // Pins are added in file order which matches pinseq order.
4041 std::map<long, SCH_PIN*> pinsBySeq;
4042 int autoSeq = 1;
4043
4044 for( SCH_PIN* pin : privateSym->GetGraphicalPins( ALL_UNITS, ALL_BODY_STYLES ) )
4045 {
4046 pinsBySeq[autoSeq] = pin;
4047 autoSeq++;
4048 }
4049
4050 for( size_t i = 0; i < slotPins.size() && i < pinsBySeq.size(); i++ )
4051 {
4052 auto seqIt = pinsBySeq.find( static_cast<long>( i + 1 ) );
4053
4054 if( seqIt != pinsBySeq.end() )
4055 seqIt->second->SetNumber( slotPins[i].Trim() );
4056 }
4057 }
4058 }
4059
4060 m_screen->Append( symbol.release() );
4061 m_pendingComp.reset();
4062}
4063
4064
4065// ==========================================================================
4066// Hierarchical sheet import
4067// ==========================================================================
4068
4069void SCH_IO_GEDA::importHierarchicalSheet( const wxString& aSourceFile )
4070{
4071 wxFileName sourceFileName( aSourceFile );
4072
4073 // Resolve relative paths against the directory of the current schematic
4074 if( !sourceFileName.IsAbsolute() )
4075 sourceFileName.SetPath( m_filename.GetPath() );
4076
4077 wxString fullPath = sourceFileName.GetFullPath();
4078
4079 if( !wxFileExists( fullPath ) )
4080 {
4081 if( m_reporter )
4082 {
4083 m_reporter->Report( wxString::Format( _( "Hierarchical source '%s' not found, creating empty sheet." ),
4084 aSourceFile ),
4086 }
4087 }
4088
4089 if( m_importStack.count( fullPath ) )
4090 {
4091 if( m_reporter )
4092 {
4093 m_reporter->Report( wxString::Format( _( "Circular hierarchy detected for '%s', skipping." ),
4094 aSourceFile ),
4096 }
4097
4098 return;
4099 }
4100
4102
4103 // Use a default sheet size. We'll resize after loading the sub-schematic content.
4104 VECTOR2I sheetSize( schIUScale.MilsToIU( 2000 ), schIUScale.MilsToIU( 1500 ) );
4105
4106 auto sheet = std::make_unique<SCH_SHEET>( m_rootSheet, pos, sheetSize );
4107
4108 wxString refdes = findAttr( m_pendingComp->attrs, wxT( "refdes" ) );
4109
4110 if( !refdes.IsEmpty() )
4111 sheet->GetField( FIELD_T::SHEET_NAME )->SetText( refdes );
4112 else
4113 sheet->GetField( FIELD_T::SHEET_NAME )->SetText( sourceFileName.GetName() );
4114
4115 sheet->GetField( FIELD_T::SHEET_FILENAME )->SetText( aSourceFile );
4116 sheet->SetFileName( aSourceFile );
4117
4118 SCH_SHEET* sheetPtr = sheet.get();
4119 m_screen->Append( sheet.release() );
4120
4121 if( wxFileExists( fullPath ) )
4122 {
4123 m_deferredSheets.push_back( { sheetPtr, fullPath } );
4124 }
4125}
4126
4127
4128// ==========================================================================
4129// Endpoint tracking
4130// ==========================================================================
4131
4132void SCH_IO_GEDA::trackEndpoint( int aGedaX, int aGedaY )
4133{
4134 m_netEndpoints[std::make_pair( aGedaX, aGedaY )]++;
4135}
4136
4137
4138// ==========================================================================
4139// Post-processing
4140// ==========================================================================
4141
4150
4151
4153{
4154 for( const NET_ATTR_RECORD& rec : m_netAttrRecords )
4155 {
4156 if( !rec.symbol )
4157 continue;
4158
4159 const LIB_SYMBOL* libSym = rec.symbol->GetLibSymbolRef().get();
4160
4161 if( !libSym )
4162 continue;
4163
4164 // Find the pin with matching number
4166 {
4167 if( pin->GetNumber() == rec.pinnumber )
4168 {
4169 VECTOR2I pinPos = rec.symbol->GetPinPhysicalPosition( pin );
4170
4171 auto label = std::make_unique<SCH_GLOBALLABEL>( pinPos, rec.netname );
4172 int textSize = toKiCadDist( GEDA_DEFAULT_TEXT_SIZE_MILS / 2 );
4173 label->SetTextSize( VECTOR2I( textSize, textSize ) );
4174
4175 // Build a unit direction vector for the pin in library space,
4176 // then transform it through the symbol's rotation/mirror matrix
4177 // to get the world-space pin direction.
4178 VECTOR2I pinDir( 0, 0 );
4179
4180 switch( pin->GetOrientation() )
4181 {
4182 case PIN_ORIENTATION::PIN_RIGHT: pinDir = VECTOR2I( 1, 0 ); break;
4183 case PIN_ORIENTATION::PIN_LEFT: pinDir = VECTOR2I( -1, 0 ); break;
4184 case PIN_ORIENTATION::PIN_UP: pinDir = VECTOR2I( 0, -1 ); break;
4185 case PIN_ORIENTATION::PIN_DOWN: pinDir = VECTOR2I( 0, 1 ); break;
4186 default: pinDir = VECTOR2I( 1, 0 ); break;
4187 }
4188
4189 VECTOR2I worldDir = rec.symbol->GetTransform().TransformCoordinate( pinDir );
4190
4191 // Orient the label away from the transformed pin direction
4192 if( std::abs( worldDir.x ) >= std::abs( worldDir.y ) )
4193 {
4194 label->SetSpinStyle( worldDir.x > 0 ? SPIN_STYLE::LEFT
4196 }
4197 else
4198 {
4199 label->SetSpinStyle( worldDir.y > 0 ? SPIN_STYLE::UP
4201 }
4202
4203 m_screen->Append( label.release() );
4204 break;
4205 }
4206 }
4207 }
4208}
4209
4210
4212{
4213 // Collect all wire segments and pin positions from the screen.
4214 std::vector<std::pair<VECTOR2I, VECTOR2I>> wireSegs;
4215 std::set<std::pair<int, int>> junctionPts;
4216
4217 for( SCH_ITEM* item : m_screen->Items() )
4218 {
4219 if( item->Type() != SCH_LINE_T )
4220 continue;
4221
4222 SCH_LINE* wire = static_cast<SCH_LINE*>( item );
4223
4224 if( wire->GetLayer() != LAYER_WIRE )
4225 continue;
4226
4227 wireSegs.emplace_back( wire->GetStartPoint(), wire->GetEndPoint() );
4228 }
4229
4230 // Place junctions where 3+ endpoints coincide at the same point.
4231 for( const auto& [pos, count] : m_netEndpoints )
4232 {
4233 if( count >= 3 )
4234 junctionPts.insert( { pos.first, pos.second } );
4235 }
4236
4237 // Detect T-junctions: a wire endpoint that lands on the interior of
4238 // another wire segment. Work in gEDA coordinates for exact integer match,
4239 // then convert when placing the junction.
4240 for( const auto& [pos, count] : m_netEndpoints )
4241 {
4242 if( junctionPts.count( pos ) )
4243 continue;
4244
4245 VECTOR2I pt = toKiCad( pos.first, pos.second );
4246
4247 for( const auto& [segStart, segEnd] : wireSegs )
4248 {
4249 if( pt == segStart || pt == segEnd )
4250 continue;
4251
4252 bool isHorizontal = ( segStart.y == segEnd.y );
4253 bool isVertical = ( segStart.x == segEnd.x );
4254
4255 if( !isHorizontal && !isVertical )
4256 continue;
4257
4258 bool onSeg = false;
4259
4260 if( isHorizontal && pt.y == segStart.y )
4261 {
4262 int minX = std::min( segStart.x, segEnd.x );
4263 int maxX = std::max( segStart.x, segEnd.x );
4264 onSeg = ( pt.x > minX && pt.x < maxX );
4265 }
4266 else if( isVertical && pt.x == segStart.x )
4267 {
4268 int minY = std::min( segStart.y, segEnd.y );
4269 int maxY = std::max( segStart.y, segEnd.y );
4270 onSeg = ( pt.y > minY && pt.y < maxY );
4271 }
4272
4273 if( onSeg )
4274 {
4275 junctionPts.insert( pos );
4276 break;
4277 }
4278 }
4279 }
4280
4281 for( const auto& [gX, gY] : junctionPts )
4282 {
4283 VECTOR2I kicadPos = toKiCad( gX, gY );
4284 m_screen->Append( std::make_unique<SCH_JUNCTION>( kicadPos ).release() );
4285 }
4286}
4287
4288
4290{
4291 if( m_busSegments.empty() )
4292 return;
4293
4294 // Collect all net (wire) endpoints from the screen
4295 std::vector<std::pair<VECTOR2I, SCH_LINE*>> wireEndpoints;
4296
4297 for( SCH_ITEM* item : m_screen->Items() )
4298 {
4299 if( item->Type() != SCH_LINE_T )
4300 continue;
4301
4302 SCH_LINE* wire = static_cast<SCH_LINE*>( item );
4303
4304 if( wire->GetLayer() != LAYER_WIRE )
4305 continue;
4306
4307 wireEndpoints.emplace_back( wire->GetStartPoint(), wire );
4308 wireEndpoints.emplace_back( wire->GetEndPoint(), wire );
4309 }
4310
4311 int entrySize = schIUScale.MilsToIU( DEFAULT_SCH_ENTRY_SIZE );
4312
4313 for( const BUS_SEGMENT& bus : m_busSegments )
4314 {
4315 bool busIsHorizontal = ( bus.start.y == bus.end.y );
4316 bool busIsVertical = ( bus.start.x == bus.end.x );
4317
4318 if( !busIsHorizontal && !busIsVertical )
4319 continue;
4320
4321 for( const auto& [pt, wire] : wireEndpoints )
4322 {
4323 bool onBus = false;
4324
4325 if( busIsHorizontal )
4326 {
4327 int minX = std::min( bus.start.x, bus.end.x );
4328 int maxX = std::max( bus.start.x, bus.end.x );
4329 onBus = ( pt.y == bus.start.y && pt.x >= minX && pt.x <= maxX );
4330 }
4331 else
4332 {
4333 int minY = std::min( bus.start.y, bus.end.y );
4334 int maxY = std::max( bus.start.y, bus.end.y );
4335 onBus = ( pt.x == bus.start.x && pt.y >= minY && pt.y <= maxY );
4336 }
4337
4338 if( !onBus )
4339 continue;
4340
4341 // Determine which direction the wire goes away from the bus
4342 VECTOR2I otherEnd = ( wire->GetStartPoint() == pt ) ? wire->GetEndPoint()
4343 : wire->GetStartPoint();
4344 int dx = 0;
4345 int dy = 0;
4346
4347 if( busIsHorizontal )
4348 {
4349 dy = ( otherEnd.y < pt.y ) ? -entrySize : entrySize;
4350 int sign = bus.ripperDir;
4351
4352 if( sign == 0 )
4353 {
4354 // Auto-detect from wire position relative to bus center
4355 int busMidX = ( bus.start.x + bus.end.x ) / 2;
4356 sign = ( pt.x <= busMidX ) ? 1 : -1;
4357 }
4358
4359 dx = sign * entrySize;
4360 }
4361 else
4362 {
4363 dx = ( otherEnd.x < pt.x ) ? -entrySize : entrySize;
4364 int sign = bus.ripperDir;
4365
4366 if( sign == 0 )
4367 {
4368 int busMidY = ( bus.start.y + bus.end.y ) / 2;
4369 sign = ( pt.y <= busMidY ) ? 1 : -1;
4370 }
4371
4372 dy = sign * entrySize;
4373 }
4374
4375 // Bus entry position is on the bus; its end (pos + size) is on the wire side
4376 auto entry = std::make_unique<SCH_BUS_WIRE_ENTRY>( pt );
4377 entry->SetSize( VECTOR2I( dx, dy ) );
4378
4379 // Shorten the wire so it starts at the bus entry tip instead of the bus
4380 VECTOR2I entryTip = pt + VECTOR2I( dx, dy );
4381
4382 if( wire->GetStartPoint() == pt )
4383 wire->SetStartPoint( entryTip );
4384 else
4385 wire->SetEndPoint( entryTip );
4386
4387 m_screen->Append( entry.release() );
4388 }
4389 }
4390}
4391
4392
4394{
4395 for( const DEFERRED_SHEET& deferred : m_deferredSheets )
4396 {
4397 SCH_SHEET* sheet = deferred.sheet;
4398
4399 SCH_SCREEN* subScreen = new SCH_SCREEN( m_schematic );
4400 subScreen->SetFileName( deferred.sourceFile );
4401 sheet->SetScreen( subScreen );
4402
4403 // Use a fresh importer instance for each sub-schematic to avoid
4404 // clobbering parse state. Share the import stack for recursion detection
4405 // and the symbol library cache to avoid redundant filesystem scanning.
4406 SCH_IO_GEDA subImporter;
4407 subImporter.SetReporter( m_reporter );
4408 subImporter.m_importStack = m_importStack;
4409 subImporter.m_importStack.insert( m_filename.GetFullPath() );
4410 for( const auto& [name, entry] : m_symLibrary )
4411 {
4412 SYM_CACHE_ENTRY subEntry;
4413 subEntry.path = entry.path;
4414 subImporter.m_symLibrary[name] = std::move( subEntry );
4415 }
4416
4418
4419 try
4420 {
4421 subImporter.LoadSchematicFile( deferred.sourceFile, m_schematic, sheet, m_properties );
4422
4423 // Merge any newly-discovered symbols back into the parent cache
4424 // so subsequent sub-schematics benefit from them.
4425 for( auto& [name, entry] : subImporter.m_symLibrary )
4426 {
4427 if( m_symLibrary.find( name ) == m_symLibrary.end() )
4428 m_symLibrary[name] = std::move( entry );
4429 }
4430 }
4431 catch( const IO_ERROR& e )
4432 {
4433 if( m_reporter )
4434 {
4435 m_reporter->Report( wxString::Format( _( "Failed to load sub-schematic '%s': %s" ),
4436 deferred.sourceFile,
4437 e.What() ),
4439 }
4440 }
4441 }
4442}
4443
4444
4446{
4447 BOX2I bbox;
4448
4449 for( SCH_ITEM* item : m_screen->Items() )
4450 bbox.Merge( item->GetBoundingBox() );
4451
4452 if( bbox.GetWidth() == 0 || bbox.GetHeight() == 0 )
4453 return;
4454
4455 VECTOR2I targetSize = bbox.GetSize();
4456 targetSize += VECTOR2I( schIUScale.MilsToIU( 1500 ), schIUScale.MilsToIU( 1500 ) );
4457
4458 PAGE_INFO pageInfo = m_screen->GetPageSettings();
4459 VECTOR2I pageSizeIU = pageInfo.GetSizeIU( schIUScale.IU_PER_MILS );
4460
4461 if( pageSizeIU.x < targetSize.x )
4462 pageInfo.SetWidthMils( schIUScale.IUToMils( targetSize.x ) );
4463
4464 if( pageSizeIU.y < targetSize.y )
4465 pageInfo.SetHeightMils( schIUScale.IUToMils( targetSize.y ) );
4466
4467 m_screen->SetPageSettings( pageInfo );
4468
4469 pageSizeIU = m_screen->GetPageSettings().GetSizeIU( schIUScale.IU_PER_MILS );
4470 VECTOR2I sheetCentre( pageSizeIU.x / 2, pageSizeIU.y / 2 );
4471 VECTOR2I itemsCentre = bbox.Centre();
4472
4473 VECTOR2I translation = sheetCentre - itemsCentre;
4474 translation.x = translation.x - translation.x % schIUScale.MilsToIU( 100 );
4475 translation.y = translation.y - translation.y % schIUScale.MilsToIU( 100 );
4476
4477 std::vector<SCH_ITEM*> allItems;
4478 std::copy( m_screen->Items().begin(), m_screen->Items().end(),
4479 std::back_inserter( allItems ) );
4480
4481 for( SCH_ITEM* item : allItems )
4482 {
4483 item->SetPosition( item->GetPosition() + translation );
4484 item->ClearFlags();
4485 m_screen->Update( item );
4486 }
4487}
4488
4489
4490// ==========================================================================
4491// Main entry point
4492// ==========================================================================
4493
4494SCH_SHEET* SCH_IO_GEDA::LoadSchematicFile( const wxString& aFileName, SCHEMATIC* aSchematic,
4495 SCH_SHEET* aAppendToMe,
4496 const std::map<std::string, UTF8>* aProperties )
4497{
4498 wxASSERT( !aFileName.IsEmpty() && aSchematic );
4499
4500 m_properties = aProperties;
4501 m_schematic = aSchematic;
4502 m_filename = wxFileName( aFileName );
4503 m_libSymbols.clear();
4504 m_netEndpoints.clear();
4505 m_netAttrRecords.clear();
4506 m_busSegments.clear();
4507 m_deferredSheets.clear();
4508 m_pendingComp.reset();
4510 m_symLibrary.clear();
4511
4512 m_maxY = 0;
4513 m_releaseVersion = 0;
4515 m_powerCounter = 0;
4516
4517 if( aAppendToMe )
4518 {
4519 wxCHECK_MSG( aSchematic->IsValid(), nullptr,
4520 "Can't append to a schematic with no root!" );
4521 m_rootSheet = aAppendToMe;
4522 }
4523 else
4524 {
4525 m_rootSheet = new SCH_SHEET( aSchematic );
4526 m_rootSheet->SetFileName( aFileName );
4527 aSchematic->SetTopLevelSheets( { m_rootSheet } );
4528 }
4529
4530 if( !m_rootSheet->GetScreen() )
4531 {
4532 SCH_SCREEN* screen = new SCH_SCREEN( aSchematic );
4533 screen->SetFileName( aFileName );
4534 m_rootSheet->SetScreen( screen );
4535 m_rootSheet->SyncUuidToScreen();
4536 }
4537
4538 m_screen = m_rootSheet->GetScreen();
4539
4540 wxTextFile file;
4541
4542 if( !file.Open( aFileName ) )
4543 THROW_IO_ERRORF( _( "Cannot open file '%s'." ), aFileName );
4544
4545 if( file.GetLineCount() == 0 )
4546 THROW_IO_ERRORF( _( "File '%s' is empty." ), aFileName );
4547
4548 // First pass: scan for max Y coordinate to set up the Y-flip transform.
4549 // We need this before creating objects because coordinates are transformed during creation.
4550 for( size_t i = 0; i < file.GetLineCount(); i++ )
4551 {
4552 wxString line = file.GetLine( i );
4553 wxChar type = line.IsEmpty() ? '\0' : line[0];
4554
4555 // Skip embedded component blocks entirely. Their coordinates are
4556 // symbol-local and must not affect the schematic-level Y extent.
4557 if( type == '[' )
4558 {
4559 while( ++i < file.GetLineCount() )
4560 {
4561 if( file.GetLine( i ).Trim() == wxT( "]" ) )
4562 break;
4563 }
4564
4565 continue;
4566 }
4567
4568 if( type == 'C' || type == 'N' || type == 'U' || type == 'T' || type == 'L'
4569 || type == 'B' || type == 'V' || type == 'A' || type == 'P' || type == 'G' )
4570 {
4571 wxStringTokenizer tok( line );
4572 tok.GetNextToken(); // skip type
4573
4574 long val = 0;
4575
4576 if( tok.HasMoreTokens() ) tok.GetNextToken(); // x or x1
4577
4578 if( tok.HasMoreTokens() )
4579 {
4580 tok.GetNextToken().ToLong( &val );
4581
4582 if( val > m_maxY )
4583 m_maxY = static_cast<int>( val );
4584 }
4585
4586 if( type == 'N' || type == 'U' || type == 'L' || type == 'P' )
4587 {
4588 if( tok.HasMoreTokens() ) tok.GetNextToken(); // x2
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
4599 if( type == 'B' )
4600 {
4601 long bw = 0, bh = 0;
4602
4603 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &bw );
4604 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &bh );
4605
4606 long top = val + bh;
4607
4608 if( top > m_maxY )
4609 m_maxY = static_cast<int>( top );
4610 }
4611
4612 if( type == 'V' || type == 'A' )
4613 {
4614 // V: cx cy radius ... A: cx cy radius ...
4615 // After reading cy into val, the next token is radius
4616 long radius = 0;
4617
4618 if( tok.HasMoreTokens() )
4619 tok.GetNextToken().ToLong( &radius );
4620
4621 long extent = val + radius;
4622
4623 if( extent > m_maxY )
4624 m_maxY = static_cast<int>( extent );
4625 }
4626
4627 // T lines have content lines that follow the header. Skip them
4628 // to avoid misinterpreting text as object lines.
4629 // Format: T x y color size vis show_nv angle align num_lines
4630 // tok has consumed: type, x (skipped), y (read). Skip remaining 6
4631 // fields to reach num_lines.
4632 if( type == 'T' )
4633 {
4634 long numTextLines = 1;
4635
4636 for( int j = 0; j < 6 && tok.HasMoreTokens(); j++ )
4637 tok.GetNextToken();
4638
4639 if( tok.HasMoreTokens() )
4640 tok.GetNextToken().ToLong( &numTextLines );
4641
4642 i += static_cast<size_t>( numTextLines );
4643 }
4644 }
4645 else if( type == 'H' )
4646 {
4647 // Scan H path lines for Y coordinates in subsequent lines
4648 wxStringTokenizer tok( line );
4649 tok.GetNextToken(); // skip 'H'
4650 long numlines = 0;
4651
4652 for( int j = 0; j < 12 && tok.HasMoreTokens(); j++ )
4653 tok.GetNextToken();
4654
4655 if( tok.HasMoreTokens() )
4656 tok.GetNextToken().ToLong( &numlines );
4657
4658 for( long j = 0; j < numlines && ( i + 1 ) < file.GetLineCount(); j++ )
4659 {
4660 i++;
4661 wxString pathLine = file.GetLine( i );
4662 wxStringTokenizer ptok( pathLine, wxT( " ,\t" ) );
4663
4664 // Path data uses SVG-like commands: M x,y L x,y C x1,y1 x2,y2 x3,y3 z
4665 // Coordinate pairs alternate X then Y. Command letters reset to X.
4666 bool nextIsY = false;
4667
4668 while( ptok.HasMoreTokens() )
4669 {
4670 wxString token = ptok.GetNextToken();
4671
4672 if( token.length() == 1 && wxIsalpha( token[0] ) )
4673 {
4674 nextIsY = false;
4675 continue;
4676 }
4677
4678 long pval = 0;
4679
4680 if( token.ToLong( &pval ) )
4681 {
4682 if( nextIsY && pval > m_maxY )
4683 m_maxY = static_cast<int>( pval );
4684
4685 nextIsY = !nextIsY;
4686 }
4687 }
4688 }
4689 }
4690 }
4691
4692 m_maxY += 1000;
4693
4694 // Parse version line
4695 size_t lineIdx = 0;
4696 wxString firstLine = file.GetLine( lineIdx );
4697 lineIdx++;
4698
4699 if( !parseVersionLine( firstLine ) )
4700 THROW_IO_ERRORF( _( "File '%s' is not a valid gEDA schematic." ), aFileName );
4701
4702 // Main parse loop
4703 while( lineIdx < file.GetLineCount() )
4704 {
4705 wxString line = file.GetLine( lineIdx );
4706 lineIdx++;
4707
4708 if( line.IsEmpty() )
4709 continue;
4710
4711 wxChar type = line[0];
4712
4713 switch( type )
4714 {
4715 case 'C':
4716 parseComponent( line, file, lineIdx );
4717 break;
4718
4719 case 'N':
4720 parseNet( line, file, lineIdx );
4721 break;
4722
4723 case 'U':
4724 parseBus( line, file, lineIdx );
4725 break;
4726
4727 case 'T':
4728 parseText( line, file, lineIdx );
4729 break;
4730
4731 case 'L':
4732 parseLine( line, file, lineIdx );
4733 break;
4734
4735 case 'B':
4736 parseBox( line, file, lineIdx );
4737 break;
4738
4739 case 'V':
4740 parseCircle( line, file, lineIdx );
4741 break;
4742
4743 case 'A':
4744 parseArc( line, file, lineIdx );
4745 break;
4746
4747 case 'H':
4748 parsePath( line, file, lineIdx );
4749 break;
4750
4751 case 'G':
4752 parsePicture( line, file, lineIdx );
4753 break;
4754
4755 case 'P':
4756 parsePin( line, file, lineIdx );
4757 break;
4758
4759 case '[':
4760 parseEmbeddedComponent( file, lineIdx );
4761 break;
4762
4763 case '{':
4764 {
4765 while( lineIdx < file.GetLineCount() )
4766 {
4767 if( file.GetLine( lineIdx ).Trim() == wxT( "}" ) )
4768 {
4769 lineIdx++;
4770 break;
4771 }
4772
4773 lineIdx++;
4774 }
4775
4776 break;
4777 }
4778
4779 case '#':
4780 case 'v':
4781 default:
4782 break;
4783 }
4784 }
4785
4786 postProcess();
4787
4788 // Size the page to fit the imported content and center it, following
4789 // the same approach as the Eagle importer.
4791
4792 // Multi-page support: when the project handler passes additional schematic
4793 // files, create sub-sheets for each and load them into the hierarchy.
4794 if( aProperties )
4795 {
4796 auto it = aProperties->find( "additional_schematics" );
4797
4798 if( it != aProperties->end() )
4799 {
4800 wxString additionalFiles = wxString::FromUTF8( it->second.c_str() );
4801 wxStringTokenizer tok( additionalFiles, wxT( ";" ) );
4802 int sheetY = schIUScale.MilsToIU( 500 );
4803 int sheetSpacing = schIUScale.MilsToIU( 2000 );
4804 int pageNum = 2;
4805
4806 while( tok.HasMoreTokens() )
4807 {
4808 wxString filePath = tok.GetNextToken();
4809 wxFileName fn( filePath );
4810
4811 VECTOR2I pos( schIUScale.MilsToIU( 500 ), sheetY );
4812 VECTOR2I size( schIUScale.MilsToIU( 2000 ), schIUScale.MilsToIU( 1500 ) );
4813
4814 auto subSheet = std::make_unique<SCH_SHEET>( m_rootSheet, pos, size );
4815
4816 subSheet->GetField( FIELD_T::SHEET_NAME )->SetText(
4817 wxString::Format( wxT( "Page %d" ), pageNum ) );
4818 subSheet->GetField( FIELD_T::SHEET_FILENAME )->SetText( fn.GetFullName() );
4819 subSheet->SetFileName( fn.GetFullName() );
4820
4821 SCH_SHEET* subSheetPtr = subSheet.get();
4822 m_screen->Append( subSheet.release() );
4823
4824 if( fn.FileExists() )
4825 {
4826 SCH_SCREEN* subScreen = new SCH_SCREEN( m_schematic );
4827 subScreen->SetFileName( fn.GetFullPath() );
4828 subSheetPtr->SetScreen( subScreen );
4829
4830 SCH_IO_GEDA subImporter;
4831 subImporter.SetReporter( m_reporter );
4832
4833 for( const auto& [name, entry] : m_symLibrary )
4834 {
4836 copy.path = entry.path;
4837 copy.symversion = entry.symversion;
4838 copy.netAttr = entry.netAttr;
4839 subImporter.m_symLibrary[name] = std::move( copy );
4840 }
4841
4843
4844 try
4845 {
4846 subImporter.LoadSchematicFile( fn.GetFullPath(), m_schematic,
4847 subSheetPtr, nullptr );
4848 }
4849 catch( const IO_ERROR& e )
4850 {
4851 if( m_reporter )
4852 {
4853 m_reporter->Report(
4854 wxString::Format( _( "Failed to load page '%s': %s" ),
4855 fn.GetFullName(), e.What() ),
4857 }
4858 }
4859 }
4860
4861 sheetY += sheetSpacing;
4862 pageNum++;
4863 }
4864 }
4865 }
4866
4867 return m_rootSheet;
4868}
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:130
constexpr double ARC_LOW_DEF_MM
Definition base_units.h:134
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
constexpr size_type GetWidth() const
Definition box2.h:211
constexpr Vec Centre() const
Definition box2.h:94
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:594
constexpr size_type GetHeight() const
Definition box2.h:212
constexpr const SizeVec & GetSize() const
Definition box2.h:203
virtual void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:329
void SetCenter(const VECTOR2I &aCenter)
virtual void SetBezierC2(const VECTOR2I &aPt)
Definition eda_shape.h:367
virtual void SetBezierC1(const VECTOR2I &aPt)
Definition eda_shape.h:364
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:279
virtual void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:512
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:347
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()
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:183
SCH_FIELD & GetValueField()
Definition lib_symbol.h:447
std::vector< SCH_PIN * > GetGraphicalPins(int aUnit=0, int aBodyStyle=0) const override
Graphical pins: Return schematic pin objects as drawn (unexpanded), filtered by unit/body.
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()
Definition lib_symbol.h:451
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:172
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:149
bool IsValid() const
A simple test if the schematic is loaded, not a complete one.
Definition schematic.h:289
void SetTopLevelSheets(const std::vector< SCH_SHEET * > &aSheets)
Replace the top level sheets, rebuilding the hierarchy and connectivity around them.
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:406
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:170
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition sch_item.h:353
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:39
VECTOR2I GetEndPoint() const
Definition sch_line.h:145
VECTOR2I GetStartPoint() const
Definition sch_line.h:136
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:48
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:174
virtual void SetShowPinNames(bool aShow)
Set or clear the pin name visibility flag.
Definition symbol.h:168
wxString wx_str() const
Definition utf8.cpp:41
#define DEFAULT_SCH_ENTRY_SIZE
The default bus and wire entry size in mils.
#define _(s)
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:450
static constexpr EDA_ANGLE ANGLE_270
Definition eda_angle.h:453
static constexpr EDA_ANGLE ANGLE_180
Definition eda_angle.h:452
FILL_T
Definition eda_fill.h:29
@ NO_FILL
Definition eda_fill.h:30
@ HATCH
Definition eda_fill.h:34
@ FILLED_SHAPE
Fill with object color.
Definition eda_fill.h:31
@ CROSS_HATCH
Definition eda_fill.h:36
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:57
#define THROW_IO_ERRORF(msg,...)
@ LAYER_DEVICE
Definition layer_ids.h:488
@ LAYER_WIRE
Definition layer_ids.h:474
@ LAYER_NOTES
Definition layer_ids.h:489
@ LAYER_BUS
Definition layer_ids.h:475
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
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.
#define ALL_UNITS
Definition symbol.h:150
#define ALL_BODY_STYLES
Definition symbol.h:151
@ SYM_ORIENT_270
Definition symbol.h:39
@ SYM_MIRROR_Y
Definition symbol.h:41
@ SYM_ORIENT_180
Definition symbol.h:38
@ SYM_ORIENT_90
Definition symbol.h:37
@ SYM_ORIENT_0
Definition symbol.h:36
@ 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:172
@ SCH_LINE_T
Definition typeinfo.h:159
constexpr int sign(T val)
Definition util.h:166
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
Definition of file extensions used in Kicad.