KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_io_pads.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 (C) 2025 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 along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
24
25#include <lib_symbol.h>
26#include <page_info.h>
27#include <sch_junction.h>
28#include <sch_label.h>
29#include <sch_line.h>
30#include <sch_pin.h>
31#include <sch_screen.h>
32#include <sch_shape.h>
33#include <sch_sheet.h>
34#include <sch_sheet_path.h>
35#include <sch_symbol.h>
36#include <sch_text.h>
37#include <schematic.h>
38#include <schematic_settings.h>
40
41#include <math/util.h>
42#include <stroke_params.h>
43
44#include <advanced_config.h>
45#include <io/pads/pads_common.h>
46#include <locale_io.h>
47#include <progress_reporter.h>
48#include <reporter.h>
49
50#include <fstream>
51#include <map>
52#include <set>
53#include <wx/log.h>
54
55
61static std::string extractConnectorPinNumber( const std::string& aRef )
62{
63 size_t sepPos = aRef.rfind( '-' );
64
65 if( sepPos == std::string::npos )
66 sepPos = aRef.rfind( '.' );
67
68 if( sepPos != std::string::npos && sepPos + 1 < aRef.size()
69 && std::isdigit( static_cast<unsigned char>( aRef[sepPos + 1] ) ) )
70 {
71 return aRef.substr( sepPos + 1 );
72 }
73
74 return "";
75}
76
77
83static std::string extractConnectorBaseRef( const std::string& aRef )
84{
85 size_t sepPos = aRef.rfind( '-' );
86
87 if( sepPos == std::string::npos )
88 sepPos = aRef.rfind( '.' );
89
90 if( sepPos != std::string::npos && sepPos + 1 < aRef.size()
91 && std::isdigit( static_cast<unsigned char>( aRef[sepPos + 1] ) ) )
92 {
93 return aRef.substr( 0, sepPos );
94 }
95
96 return aRef;
97}
98
99
104static std::string stripGateSuffix( const std::string& aRef )
105{
106 size_t sepPos = aRef.rfind( '-' );
107
108 if( sepPos == std::string::npos )
109 sepPos = aRef.rfind( '.' );
110
111 if( sepPos != std::string::npos && sepPos + 1 < aRef.size()
112 && std::isalpha( static_cast<unsigned char>( aRef[sepPos + 1] ) ) )
113 {
114 return aRef.substr( 0, sepPos );
115 }
116
117 return aRef;
118}
119
120
121static SCH_TEXT* createSchText( const PADS_SCH::TEXT_ITEM& aText, const VECTOR2I& aPos )
122{
123 SCH_TEXT* schText = new SCH_TEXT( aPos, wxString::FromUTF8( aText.content ) );
124
125 if( aText.height > 0 )
126 {
127 int scaledSize = schIUScale.MilsToIU( aText.height );
128 int charHeight = static_cast<int>( scaledSize
130 int charWidth = static_cast<int>( scaledSize
132 schText->SetTextSize( VECTOR2I( charWidth, charHeight ) );
133 }
134
135 if( aText.width_factor > 0 )
136 schText->SetTextThickness( schIUScale.MilsToIU( aText.width_factor ) );
137
138 // PADS justification: value = vertical_offset + horizontal_code
139 // Vertical offsets: bottom=0, top=2, middle=8
140 // Horizontal codes: left=0, right=1, center=4
141 int justVal = aText.justification;
142 int hCode = 0;
143 int vGroup = 0;
144
145 if( justVal >= 8 )
146 {
147 vGroup = 2; // middle
148 hCode = justVal - 8;
149 }
150 else if( justVal >= 2 )
151 {
152 vGroup = 1; // top
153 hCode = justVal - 2;
154 }
155 else
156 {
157 vGroup = 0; // bottom
158 hCode = justVal;
159 }
160
161 switch( hCode )
162 {
163 default:
164 case 0: schText->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT ); break;
165 case 1: schText->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT ); break;
166 case 4: schText->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER ); break;
167 }
168
169 switch( vGroup )
170 {
171 default:
172 case 0: schText->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM ); break;
173 case 1: schText->SetVertJustify( GR_TEXT_V_ALIGN_TOP ); break;
174 case 2: schText->SetVertJustify( GR_TEXT_V_ALIGN_CENTER ); break;
175 }
176
177 if( aText.rotation != 0 )
178 schText->SetTextAngleDegrees( aText.rotation * 90.0 );
179
180 return schText;
181}
182
183
191static int computePowerOrientation( const std::string& aOpcId,
192 const std::vector<PADS_SCH::SCH_SIGNAL>& aSignals,
193 const VECTOR2I& aOpcPos, bool aPinUp, int aPageHeightIU )
194{
195 // Find the wire endpoint matching this OPC and get the adjacent vertex
196 std::string opcRef = "@@@O" + aOpcId;
197 VECTOR2I adjPos = aOpcPos;
198 bool found = false;
199
200 for( const auto& signal : aSignals )
201 {
202 for( const auto& wire : signal.wires )
203 {
204 if( wire.vertices.size() < 2 )
205 continue;
206
207 if( wire.endpoint_a == opcRef )
208 {
209 adjPos = VECTOR2I(
210 schIUScale.MilsToIU( KiROUND( wire.vertices[1].x ) ),
211 aPageHeightIU
212 - schIUScale.MilsToIU( KiROUND( wire.vertices[1].y ) ) );
213 found = true;
214 break;
215 }
216
217 if( wire.endpoint_b == opcRef )
218 {
219 size_t last = wire.vertices.size() - 1;
220 adjPos = VECTOR2I(
221 schIUScale.MilsToIU( KiROUND( wire.vertices[last - 1].x ) ),
222 aPageHeightIU
223 - schIUScale.MilsToIU(
224 KiROUND( wire.vertices[last - 1].y ) ) );
225 found = true;
226 break;
227 }
228 }
229
230 if( found )
231 break;
232 }
233
234 if( !found )
236
237 // Wire goes from aOpcPos toward adjPos
238 int dx = adjPos.x - aOpcPos.x;
239 int dy = adjPos.y - aOpcPos.y;
240
241 // Determine which direction the wire approaches from (relative to OPC position).
242 // The symbol body should face AWAY from the wire.
243 // In KiCad Y-down coordinates: dy > 0 means wire goes down from OPC.
244
245 if( std::abs( dx ) >= std::abs( dy ) )
246 {
247 // Horizontal wire
248 if( dx > 0 )
249 {
250 // Wire goes right → body should face left
253 }
254 else
255 {
256 // Wire goes left → body should face right
259 }
260 }
261 else
262 {
263 // Vertical wire
264 if( dy > 0 )
265 {
266 // Wire goes down → body should face up
269 }
270 else
271 {
272 // Wire goes up → body should face down
275 }
276 }
277}
278
279
280SCH_IO_PADS::SCH_IO_PADS() : SCH_IO( wxS( "PADS Logic" ) )
281{
282}
283
284
288
289
290bool SCH_IO_PADS::CanReadSchematicFile( const wxString& aFileName ) const
291{
292 if( !SCH_IO::CanReadSchematicFile( aFileName ) )
293 return false;
294
295 return checkFileHeader( aFileName );
296}
297
298
299bool SCH_IO_PADS::CanReadLibrary( const wxString& aFileName ) const
300{
301 if( !SCH_IO::CanReadLibrary( aFileName ) )
302 return false;
303
304 return checkFileHeader( aFileName );
305}
306
307
308SCH_SHEET* SCH_IO_PADS::LoadSchematicFile( const wxString& aFileName,
309 SCHEMATIC* aSchematic,
310 SCH_SHEET* aAppendToMe,
311 const std::map<std::string, UTF8>* aProperties )
312{
313 wxCHECK( !aFileName.IsEmpty() && aSchematic, nullptr );
314
315 LOCALE_IO setlocale;
316
317 SCH_SHEET* rootSheet = nullptr;
318
319 if( aAppendToMe )
320 {
321 wxCHECK_MSG( aSchematic->IsValid(), nullptr, "Can't append to a schematic with no root!" );
322 rootSheet = aAppendToMe;
323 }
324 else
325 {
326 rootSheet = new SCH_SHEET( aSchematic );
327 rootSheet->SetFileName( aFileName );
328 aSchematic->SetTopLevelSheets( { rootSheet } );
329 }
330
331 if( !rootSheet->GetScreen() )
332 {
333 SCH_SCREEN* screen = new SCH_SCREEN( aSchematic );
334 screen->SetFileName( aFileName );
335 rootSheet->SetScreen( screen );
336
337 const_cast<KIID&>( rootSheet->m_Uuid ) = screen->GetUuid();
338 }
339
340 SCH_SHEET_PATH rootPath;
341 rootPath.push_back( rootSheet );
342
343 SCH_SCREEN* rootScreen = rootSheet->GetScreen();
344 wxCHECK( rootScreen, nullptr );
345
346 SCH_SHEET_INSTANCE sheetInstance;
347 sheetInstance.m_Path = rootPath.Path();
348 sheetInstance.m_PageNumber = wxT( "#" );
349 rootScreen->m_sheetInstances.emplace_back( sheetInstance );
350
352 m_progressReporter->SetNumPhases( 3 );
353
355 std::string filename( aFileName.ToUTF8() );
356
357 if( !parser.Parse( filename ) )
358 {
359 THROW_IO_ERROR( wxString::Format( wxT( "Failed to parse PADS file: %s" ), aFileName ) );
360 }
361
363 m_progressReporter->BeginPhase( 1 );
364
365 const PADS_SCH::PARAMETERS& params = parser.GetParameters();
366 PADS_SCH::PADS_SCH_SYMBOL_BUILDER symbolBuilder( params );
367 PADS_SCH::PADS_SCH_SCHEMATIC_BUILDER schBuilder( params, aSchematic );
368
369 // Detect gate suffix separator from multi-gate part references (e.g. U17-A → '-')
370 for( const auto& part : parser.GetPartPlacements() )
371 {
372 const std::string& ref = part.reference;
373 size_t dashPos = ref.rfind( '-' );
374 size_t dotPos = ref.rfind( '.' );
375 size_t sepPos = std::string::npos;
376
377 if( dashPos != std::string::npos )
378 sepPos = dashPos;
379 else if( dotPos != std::string::npos )
380 sepPos = dotPos;
381
382 if( sepPos != std::string::npos && sepPos + 1 < ref.size()
383 && std::isalpha( static_cast<unsigned char>( ref[sepPos + 1] ) ) )
384 {
385 aSchematic->Settings().m_SubpartIdSeparator = static_cast<int>( ref[sepPos] );
386 aSchematic->Settings().m_SubpartFirstId = 'A';
387 break;
388 }
389 }
390
391 // Set KiCad page size to match the PADS drawing sheet
392 PAGE_INFO pageInfo;
393
394 if( !params.sheet_size.name.empty() )
395 pageInfo.SetType( wxString::FromUTF8( params.sheet_size.name ) );
396 else
397 pageInfo.SetType( PAGE_SIZE_TYPE::A );
398
399 // PADS Y-up to KiCad Y-down: Y_kicad = pageHeight - Y_pads
400 const int pageHeightIU = pageInfo.GetHeightIU( schIUScale.IU_PER_MILS );
401
402 // Build LIB_SYMBOL objects from all CAEDECAL definitions
403 for( const PADS_SCH::SYMBOL_DEF& symDef : parser.GetSymbolDefs() )
404 symbolBuilder.GetOrCreateSymbol( symDef );
405
406 std::set<int> sheetNumbers = parser.GetSheetNumbers();
407
408 if( sheetNumbers.empty() )
409 sheetNumbers.insert( 1 );
410
411 bool isSingleSheet = ( sheetNumbers.size() == 1 );
412
413 // Map sheet number -> (SCH_SHEET*, SCH_SCREEN*, SCH_SHEET_PATH)
414 struct SheetContext
415 {
416 SCH_SHEET* sheet = nullptr;
417 SCH_SCREEN* screen = nullptr;
419 };
420
421 std::map<int, SheetContext> sheetContexts;
422
423 if( isSingleSheet )
424 {
425 int sheetNum = *sheetNumbers.begin();
426 SheetContext ctx;
427 ctx.sheet = rootSheet;
428 ctx.screen = rootScreen;
429 ctx.path = rootPath;
430 ctx.screen->SetPageSettings( pageInfo );
431 sheetContexts[sheetNum] = ctx;
432 }
433 else
434 {
435 // Multi-sheet: root is a container with sub-sheets
436 int totalSheets = static_cast<int>( sheetNumbers.size() );
437
438 for( int sheetNum : sheetNumbers )
439 {
440 SCH_SHEET* subSheet = schBuilder.CreateHierarchicalSheet(
441 sheetNum, totalSheets, rootSheet, aFileName );
442
443 if( !subSheet )
444 continue;
445
446 // Find the sheet name from parser headers
447 for( const PADS_SCH::SHEET_HEADER& hdr : parser.GetSheetHeaders() )
448 {
449 if( hdr.sheet_num == sheetNum && !hdr.sheet_name.empty() )
450 {
452 wxString::FromUTF8( hdr.sheet_name ) );
453
454 break;
455 }
456 }
457
458 SCH_SHEET_PATH subPath;
459 subPath.push_back( rootSheet );
460 subPath.push_back( subSheet );
461
462 wxString pageNo = wxString::Format( wxT( "%d" ), sheetNum );
463 subPath.SetPageNumber( pageNo );
464
465 SCH_SHEET_INSTANCE subInstance;
466 subInstance.m_Path = subPath.Path();
467 subInstance.m_PageNumber = pageNo;
468 subSheet->GetScreen()->m_sheetInstances.emplace_back( subInstance );
469
470 SheetContext ctx;
471 ctx.sheet = subSheet;
472 ctx.screen = subSheet->GetScreen();
473 ctx.path = subPath;
474 ctx.screen->SetPageSettings( pageInfo );
475 sheetContexts[sheetNum] = ctx;
476 }
477 }
478
480 m_progressReporter->BeginPhase( 2 );
481
482 // Track connector base references for wire-endpoint label creation
483 std::set<std::string> connectorBaseRefs;
484
485 // Pre-scan connector placements to group pins by base reference.
486 // Each group becomes one multi-unit connector symbol in KiCad.
487 struct ConnectorGroup
488 {
489 std::vector<std::string> pinNumbers;
490 std::map<std::string, int> pinToUnit;
491 std::string partType;
492 };
493
494 std::map<std::string, ConnectorGroup> connectorGroups;
495
496 for( const auto& [sheetNum, ctx] : sheetContexts )
497 {
498 std::vector<PADS_SCH::PART_PLACEMENT> sheetParts = parser.GetPartsOnSheet( sheetNum );
499
500 for( const PADS_SCH::PART_PLACEMENT& part : sheetParts )
501 {
502 auto ptIt = parser.GetPartTypes().find( part.part_type );
503
504 if( ptIt == parser.GetPartTypes().end() || !ptIt->second.is_connector )
505 continue;
506
507 std::string pinNum = extractConnectorPinNumber( part.reference );
508
509 if( pinNum.empty() )
510 continue;
511
512 std::string baseRef = extractConnectorBaseRef( part.reference );
513 ConnectorGroup& group = connectorGroups[baseRef];
514 group.partType = part.part_type;
515 group.pinNumbers.push_back( pinNum );
516 }
517 }
518
519 for( auto& [baseRef, group] : connectorGroups )
520 {
521 std::sort( group.pinNumbers.begin(), group.pinNumbers.end(),
522 []( const std::string& a, const std::string& b )
523 {
524 return std::stoi( a ) < std::stoi( b );
525 } );
526
527 for( size_t i = 0; i < group.pinNumbers.size(); i++ )
528 group.pinToUnit[group.pinNumbers[i]] = static_cast<int>( i + 1 );
529 }
530
531 // Place symbols on each sheet
532 for( auto& [sheetNum, ctx] : sheetContexts )
533 {
534 std::vector<PADS_SCH::PART_PLACEMENT> parts = parser.GetPartsOnSheet( sheetNum );
535
536 for( const PADS_SCH::PART_PLACEMENT& part : parts )
537 {
538 auto ptIt = parser.GetPartTypes().find( part.part_type );
539
540 LIB_SYMBOL* libSymbol = nullptr;
541 bool isMultiGate = false;
542 bool isConnector = false;
543 bool isPower = false;
544 std::string libItemName;
545 std::string connectorPinNumber;
546
547 if( ptIt != parser.GetPartTypes().end() )
548 {
549 const PADS_SCH::PARTTYPE_DEF& ptDef = ptIt->second;
550
551 if( ptDef.gates.size() > 1 )
552 {
553 // Multi-gate PARTTYPE: composite multi-unit symbol
554 libSymbol = symbolBuilder.GetOrCreateMultiUnitSymbol(
555 ptDef, parser.GetSymbolDefs() );
556 libItemName = ptDef.name;
557 isMultiGate = true;
558 }
559 else if( !ptDef.gates.empty() )
560 {
561 const PADS_SCH::GATE_DEF& gate = ptDef.gates[0];
562 int idx = std::max( 0, part.gate_index );
563 std::string decalName;
564
565 if( idx < static_cast<int>( gate.decal_names.size() ) )
566 decalName = gate.decal_names[idx];
567 else if( !gate.decal_names.empty() )
568 decalName = gate.decal_names[0];
569
570 const PADS_SCH::SYMBOL_DEF* symDef = parser.GetSymbolDef( decalName );
571
572 connectorPinNumber = ptDef.is_connector
573 ? extractConnectorPinNumber( part.reference )
574 : std::string();
575
576 if( symDef && !connectorPinNumber.empty() )
577 {
578 // Multi-unit connector placement (e.g. J12-15 → unit of J12).
579 // All pins of the same connector share one multi-unit symbol.
580 std::string baseRef = extractConnectorBaseRef( part.reference );
581 auto groupIt = connectorGroups.find( baseRef );
582
583 if( groupIt != connectorGroups.end() )
584 {
585 std::string cacheKey = ptDef.name + ":conn:" + baseRef;
586
587 libSymbol = symbolBuilder.GetOrCreateMultiUnitConnectorSymbol(
588 ptDef, *symDef, groupIt->second.pinNumbers, cacheKey );
589 libItemName = ptDef.name + "_" + baseRef;
590 isConnector = true;
591 isMultiGate = true;
592
593 connectorBaseRefs.insert( baseRef );
594 }
595 }
596 else if( symDef )
597 {
598 libSymbol = symbolBuilder.GetOrCreatePartTypeSymbol( ptDef, *symDef );
599 libItemName = decalName;
600 }
601 }
602 else if( !ptDef.special_variants.empty() )
603 {
604 // Power/ground symbols
605 int idx = std::max( 0, part.gate_index );
606 idx = std::min( idx, static_cast<int>( ptDef.special_variants.size() ) - 1 );
607 std::string decalName = ptDef.special_variants[idx].decal_name;
608
609 const PADS_SCH::SYMBOL_DEF* symDef = parser.GetSymbolDef( decalName );
610
611 if( symDef )
612 {
613 libSymbol = symbolBuilder.GetOrCreateSymbol( *symDef );
614 libItemName = decalName;
615 }
616 }
617
618 if( !ptDef.special_keyword.empty() && ptDef.special_keyword != "OFF" )
619 isPower = true;
620 }
621
622 // Fallback: resolve directly by CAEDECAL name
623 if( !libSymbol )
624 {
625 const PADS_SCH::SYMBOL_DEF* symDef = parser.GetSymbolDef( part.symbol_name );
626
627 if( !symDef )
628 {
629 m_errorMessages.emplace(
630 wxString::Format( wxT( "PADS Import: symbol '%s' not found,"
631 " part '%s' skipped" ),
632 wxString::FromUTF8( part.symbol_name ),
633 wxString::FromUTF8( part.reference ) ),
635 continue;
636 }
637
638 libSymbol = symbolBuilder.GetOrCreateSymbol( *symDef );
639 libItemName = symDef->name;
640 }
641
642 if( !libSymbol )
643 continue;
644
645 if( ptIt != parser.GetPartTypes().end() && !ptIt->second.sigpins.empty() )
646 symbolBuilder.AddHiddenPowerPins( libSymbol, ptIt->second.sigpins );
647
648 if( !isPower )
649 isPower = PADS_SCH::PADS_SCH_SYMBOL_BUILDER::IsPowerSymbol( part.part_type );
650
651 // Resolve power symbol style. Prefer the PARTTYPE variant decal style
652 // (e.g. +BUBBLE → +VDC) which preserves the original PADS symbol shape,
653 // falling back to net-name matching (e.g. GND → GND, +5V → +5V).
654 std::string powerStyle;
655
656 if( isPower && ptIt != parser.GetPartTypes().end()
657 && !ptIt->second.special_variants.empty() )
658 {
659 int varIdx = std::max( 0, part.gate_index );
660 varIdx = std::min(
661 varIdx,
662 static_cast<int>( ptIt->second.special_variants.size() ) - 1 );
663 const auto& variant = ptIt->second.special_variants[varIdx];
664
666 variant.decal_name, variant.pin_type );
667 }
668
669 if( isPower && powerStyle.empty() )
670 {
671 std::string rawNetName = part.power_net_name.empty()
672 ? part.symbol_name
673 : part.power_net_name;
674
675 auto powerLibId =
677
678 if( powerLibId )
679 powerStyle = std::string( powerLibId->GetLibItemName().c_str() );
680 }
681
682 auto symbolPtr = std::make_unique<SCH_SYMBOL>();
683 SCH_SYMBOL* symbol = symbolPtr.get();
684 LIB_SYMBOL* instanceSymbol = nullptr;
685
686 if( isPower && !powerStyle.empty() )
687 {
688 instanceSymbol = symbolBuilder.BuildKiCadPowerSymbol( powerStyle );
689
690 LIB_ID libId;
691 libId.SetLibNickname( wxT( "power" ) );
692 libId.SetLibItemName( wxString::FromUTF8( powerStyle ) );
693 symbol->SetLibId( libId );
694 }
695 else
696 {
697 LIB_ID libId;
698 libId.SetLibNickname( wxT( "pads_import" ) );
699 libId.SetLibItemName( wxString::FromUTF8( libItemName ) );
700 symbol->SetLibId( libId );
701
702 instanceSymbol = new LIB_SYMBOL( *libSymbol );
703
704 if( isPower )
705 instanceSymbol->SetGlobalPower();
706 }
707
708 symbol->SetLibSymbol( instanceSymbol );
709 symbol->SetPosition( VECTOR2I(
710 schIUScale.MilsToIU( KiROUND( part.position.x ) ),
711 pageHeightIU - schIUScale.MilsToIU( KiROUND( part.position.y ) ) ) );
712
713 int orientation = SYMBOL_ORIENTATION_T::SYM_ORIENT_0;
714
715 if( part.rotation == 90.0 )
717 else if( part.rotation == 180.0 )
719 else if( part.rotation == 270.0 )
721
722 if( part.mirror_flags & 1 )
724
725 if( part.mirror_flags & 2 )
727
728 symbol->SetOrientation( orientation );
729
730 if( isConnector && !connectorPinNumber.empty() )
731 {
732 std::string baseRef = extractConnectorBaseRef( part.reference );
733 auto groupIt = connectorGroups.find( baseRef );
734
735 if( groupIt != connectorGroups.end() )
736 {
737 auto unitIt = groupIt->second.pinToUnit.find( connectorPinNumber );
738
739 if( unitIt != groupIt->second.pinToUnit.end() )
740 symbol->SetUnit( unitIt->second );
741 else
742 symbol->SetUnit( 1 );
743 }
744 else
745 {
746 symbol->SetUnit( 1 );
747 }
748 }
749 else if( isMultiGate )
750 {
751 symbol->SetUnit( part.gate_index + 1 );
752 }
753 else
754 {
755 symbol->SetUnit( 1 );
756 }
757
758 // Assign deterministic UUID so PCB cross-probe can match footprints
759 // to symbols. Only the primary gate (index 0) or the first connector
760 // pin gets the deterministic UUID since one footprint maps to one
761 // symbol instance.
762 bool isPrimaryUnit = isConnector
763 ? ( symbol->GetUnit() == 1 )
764 : ( !isMultiGate || part.gate_index == 0 );
765
766 if( !isPower && isPrimaryUnit )
767 {
768 std::string baseRef = isConnector
769 ? extractConnectorBaseRef( part.reference )
770 : stripGateSuffix( part.reference );
771
772 const_cast<KIID&>( symbol->m_Uuid ) =
774 }
775
776 symbol->SetRef( &ctx.path, wxString::FromUTF8( part.reference ) );
777
778 schBuilder.ApplyPartAttributes( symbol, part );
779 schBuilder.CreateCustomFields( symbol, part );
780
781 // For connectors, override reference to the base (e.g. "J12" not "J12-1").
782 // Must happen after ApplyPartAttributes which only strips alpha suffixes.
783 if( isConnector )
784 {
785 std::string baseRef = extractConnectorBaseRef( part.reference );
786 symbol->SetRef( &ctx.path, wxString::FromUTF8( baseRef ) );
787 }
788
789 // For multi-gate parts, strip the alpha gate suffix (e.g. "U1-A" → "U1")
790 // so KiCad recognizes all units as belonging to the same part.
791 if( isMultiGate && !isConnector )
792 {
793 std::string baseRef = stripGateSuffix( part.reference );
794 symbol->SetRef( &ctx.path, wxString::FromUTF8( baseRef ) );
795 }
796
797 // For passive components, override Value with VALUE1 parametric value
798 // so that e.g. C10 shows "0.1uF" instead of the generic "CAPMF0805".
799 // Also apply the VALUE1 attribute position.
800 if( ptIt != parser.GetPartTypes().end() )
801 {
802 const std::string& cat = ptIt->second.category;
803
804 if( cat == "CAP" || cat == "RES" || cat == "IND" )
805 {
806 auto valIt = part.attr_overrides.find( "VALUE" );
807
808 if( valIt == part.attr_overrides.end() )
809 valIt = part.attr_overrides.find( "VALUE1" );
810
811 if( valIt != part.attr_overrides.end() && !valIt->second.empty() )
812 {
813 symbol->SetValueFieldText( wxString::FromUTF8( valIt->second ) );
814
815 for( const auto& attr : part.attributes )
816 {
817 if( attr.name == "VALUE" || attr.name == "VALUE1"
818 || attr.name == "Value1" )
819 {
820 SCH_FIELD* valField = symbol->GetField( FIELD_T::VALUE );
821 int fx = schIUScale.MilsToIU( KiROUND( attr.position.x ) );
822
823 if( part.mirror_flags & 1 )
824 fx = -fx;
825
826 VECTOR2I fieldPos( fx,
827 -schIUScale.MilsToIU(
828 KiROUND( attr.position.y ) ) );
829 valField->SetPosition( symbol->GetPosition() + fieldPos );
830
831 int fieldTextSize = schIUScale.MilsToIU( 50 );
832 valField->SetTextSize(
833 VECTOR2I( fieldTextSize, fieldTextSize ) );
836 break;
837 }
838 }
839 }
840 }
841 }
842
843 if( isPower )
844 {
845 symbol->GetField( FIELD_T::REFERENCE )->SetVisible( false );
846
847 wxString netName = part.power_net_name.empty()
848 ? wxString::FromUTF8( part.symbol_name )
849 : wxString::FromUTF8( part.power_net_name );
850
851 if( netName.StartsWith( wxT( "/" ) ) )
852 netName = wxT( "~{" ) + netName.Mid( 1 ) + wxT( "}" );
853
854 symbol->GetField( FIELD_T::VALUE )->SetText( netName );
855 symbol->GetField( FIELD_T::VALUE )->SetVisible( true );
856 }
857
858 {
859 std::string hierRef;
860
861 if( isConnector )
862 hierRef = extractConnectorBaseRef( part.reference );
863 else if( isMultiGate )
864 hierRef = stripGateSuffix( part.reference );
865 else
866 hierRef = part.reference;
867
868 symbol->AddHierarchicalReference( ctx.path.Path(),
869 wxString::FromUTF8( hierRef ),
870 symbol->GetUnit() );
871 }
872
873 symbol->ClearFlags();
874
875 // For connector pins, create a local label at the pin position
876 // before transferring ownership to the screen.
877 // The matching label at the wire endpoint creates the electrical connection.
878 if( isConnector && !connectorPinNumber.empty() )
879 {
880 std::string baseRef = extractConnectorBaseRef( part.reference );
881 wxString labelText = wxString::Format( wxT( "%s.%s" ),
882 wxString::FromUTF8( baseRef ),
883 wxString::FromUTF8( connectorPinNumber ) );
884
885 VECTOR2I pinPos = symbol->GetPosition();
886 std::vector<SCH_PIN*> pins = symbol->GetPins();
887
888 if( !pins.empty() )
889 pinPos = pins[0]->GetPosition();
890
891 SCH_LABEL* label = new SCH_LABEL( pinPos, labelText );
892 int labelSize = schIUScale.MilsToIU( 50 );
893 label->SetTextSize( VECTOR2I( labelSize, labelSize ) );
895 label->SetFlags( IS_NEW );
896 ctx.screen->Append( label );
897 }
898
899 ctx.screen->Append( symbolPtr.release() );
900 }
901 }
902
903 // Build set of power signal names so we can suppress duplicate global labels
904 // where a power symbol is placed instead. Non-power signal labels are handled
905 // by CreateNetLabels which places them at dangling wire endpoints.
906 std::set<std::string> powerSignalNames;
907
908 for( const PADS_SCH::OFF_PAGE_CONNECTOR& opc : parser.GetOffPageConnectors() )
909 {
910 if( opc.signal_name.empty() )
911 continue;
912
913 auto ptIt = parser.GetPartTypes().find( opc.symbol_lib );
914
915 if( ptIt != parser.GetPartTypes().end()
916 && !ptIt->second.special_keyword.empty() && ptIt->second.special_keyword != "OFF"
917 && !ptIt->second.special_variants.empty() )
918 {
919 int idx = std::max( 0, opc.flags2 );
920 idx = std::min( idx,
921 static_cast<int>( ptIt->second.special_variants.size() ) - 1 );
922 const auto& variant = ptIt->second.special_variants[idx];
923
925 variant.decal_name, variant.pin_type ).empty() )
926 {
927 powerSignalNames.insert( opc.signal_name );
928 }
929 }
930 }
931
932 // Build set of OPC reference IDs for non-power signal OPCs. Each entry
933 // corresponds to a wire endpoint reference like "@@@O48" that should receive
934 // its own global label with orientation derived from the wire direction.
935 std::set<std::string> signalOpcIds;
936
937 for( const PADS_SCH::OFF_PAGE_CONNECTOR& opc : parser.GetOffPageConnectors() )
938 {
939 if( opc.signal_name.empty() || powerSignalNames.count( opc.signal_name ) )
940 continue;
941
942 signalOpcIds.insert( "@@@O" + std::to_string( opc.id ) );
943 }
944
945 // Create wires and connectivity on each sheet
946 for( auto& [sheetNum, ctx] : sheetContexts )
947 {
948 std::vector<PADS_SCH::SCH_SIGNAL> sheetSignals = parser.GetSignalsOnSheet( sheetNum );
949
950 // Create wire segments from vertex data
951 for( const PADS_SCH::SCH_SIGNAL& signal : sheetSignals )
952 {
953 for( const PADS_SCH::WIRE_SEGMENT& wire : signal.wires )
954 {
955 if( wire.vertices.size() < 2 )
956 continue;
957
958 // Each consecutive pair of vertices becomes a wire segment
959 for( size_t v = 0; v + 1 < wire.vertices.size(); v++ )
960 {
961 VECTOR2I start(
962 schIUScale.MilsToIU( KiROUND( wire.vertices[v].x ) ),
963 pageHeightIU
964 - schIUScale.MilsToIU( KiROUND( wire.vertices[v].y ) ) );
966 schIUScale.MilsToIU( KiROUND( wire.vertices[v + 1].x ) ),
967 pageHeightIU
968 - schIUScale.MilsToIU( KiROUND( wire.vertices[v + 1].y ) ) );
969
970 if( start == end )
971 continue;
972
973 SCH_LINE* line = new SCH_LINE( start, SCH_LAYER_ID::LAYER_WIRE );
974 line->SetEndPoint( end );
975 line->SetConnectivityDirty();
976 ctx.screen->Append( line );
977 }
978 }
979 }
980
981 // Create local labels at wire endpoints that reference connector pins
982 for( const PADS_SCH::SCH_SIGNAL& signal : sheetSignals )
983 {
984 for( const PADS_SCH::WIRE_SEGMENT& wire : signal.wires )
985 {
986 if( wire.vertices.size() < 2 )
987 continue;
988
989 // Check endpoint_a for connector pin reference
990 if( wire.endpoint_a.find( '.' ) != std::string::npos
991 && wire.endpoint_a.find( "@@@" ) == std::string::npos )
992 {
993 size_t dotPos = wire.endpoint_a.find( '.' );
994 std::string ref = wire.endpoint_a.substr( 0, dotPos );
995
996 if( connectorBaseRefs.count( ref ) )
997 {
998 const auto& vtx = wire.vertices.front();
999 VECTOR2I pos(
1000 schIUScale.MilsToIU( KiROUND( vtx.x ) ),
1001 pageHeightIU - schIUScale.MilsToIU( KiROUND( vtx.y ) ) );
1002
1003 // Compute label orientation from adjacent vertex
1004 const auto& adj = wire.vertices[1];
1005 VECTOR2I adjPos(
1006 schIUScale.MilsToIU( KiROUND( adj.x ) ),
1007 pageHeightIU - schIUScale.MilsToIU( KiROUND( adj.y ) ) );
1008
1009 int dx = adjPos.x - pos.x;
1010 int dy = adjPos.y - pos.y;
1012
1013 if( std::abs( dx ) >= std::abs( dy ) )
1014 orient = ( dx > 0 ) ? SPIN_STYLE::LEFT : SPIN_STYLE::RIGHT;
1015 else
1016 orient = ( dy > 0 ) ? SPIN_STYLE::UP : SPIN_STYLE::BOTTOM;
1017
1018 wxString labelText = wxString::FromUTF8( wire.endpoint_a );
1019 SCH_LABEL* label = new SCH_LABEL( pos, labelText );
1020 int labelSize = schIUScale.MilsToIU( 50 );
1021 label->SetTextSize( VECTOR2I( labelSize, labelSize ) );
1022 label->SetSpinStyle( orient );
1023 label->SetFlags( IS_NEW );
1024 ctx.screen->Append( label );
1025 }
1026 }
1027
1028 // Check endpoint_b for connector pin reference
1029 if( wire.endpoint_b.find( '.' ) != std::string::npos
1030 && wire.endpoint_b.find( "@@@" ) == std::string::npos )
1031 {
1032 size_t dotPos = wire.endpoint_b.find( '.' );
1033 std::string ref = wire.endpoint_b.substr( 0, dotPos );
1034
1035 if( connectorBaseRefs.count( ref ) )
1036 {
1037 const auto& vtx = wire.vertices.back();
1038 VECTOR2I pos(
1039 schIUScale.MilsToIU( KiROUND( vtx.x ) ),
1040 pageHeightIU - schIUScale.MilsToIU( KiROUND( vtx.y ) ) );
1041
1042 // Compute label orientation from adjacent vertex
1043 size_t lastIdx = wire.vertices.size() - 1;
1044 const auto& adj = wire.vertices[lastIdx - 1];
1045 VECTOR2I adjPos(
1046 schIUScale.MilsToIU( KiROUND( adj.x ) ),
1047 pageHeightIU - schIUScale.MilsToIU( KiROUND( adj.y ) ) );
1048
1049 int dx = adjPos.x - pos.x;
1050 int dy = adjPos.y - pos.y;
1052
1053 if( std::abs( dx ) >= std::abs( dy ) )
1054 orient = ( dx > 0 ) ? SPIN_STYLE::LEFT : SPIN_STYLE::RIGHT;
1055 else
1056 orient = ( dy > 0 ) ? SPIN_STYLE::UP : SPIN_STYLE::BOTTOM;
1057
1058 wxString labelText = wxString::FromUTF8( wire.endpoint_b );
1059 SCH_LABEL* label = new SCH_LABEL( pos, labelText );
1060 int labelSize = schIUScale.MilsToIU( 50 );
1061 label->SetTextSize( VECTOR2I( labelSize, labelSize ) );
1062 label->SetSpinStyle( orient );
1063 label->SetFlags( IS_NEW );
1064 ctx.screen->Append( label );
1065 }
1066 }
1067 }
1068 }
1069
1070 // Create junctions from TIEDOTS for this sheet
1071 for( const PADS_SCH::TIED_DOT& dot : parser.GetTiedDots() )
1072 {
1073 if( dot.sheet_number != sheetNum )
1074 continue;
1075
1076 VECTOR2I pos( schIUScale.MilsToIU( KiROUND( dot.position.x ) ),
1077 pageHeightIU - schIUScale.MilsToIU( KiROUND( dot.position.y ) ) );
1078
1079 SCH_JUNCTION* junction = new SCH_JUNCTION( pos );
1080 ctx.screen->Append( junction );
1081 }
1082
1083 // Create net labels, skipping power nets that get dedicated symbols
1084 schBuilder.CreateNetLabels( sheetSignals, ctx.screen, signalOpcIds,
1085 powerSignalNames );
1086
1087 // Place off-page connectors: power/ground types become SCH_SYMBOL with
1088 // KiCad standard power graphics; signal types become SCH_GLOBALLABEL.
1089 int pwrIndex = 1;
1090
1091 for( const PADS_SCH::OFF_PAGE_CONNECTOR& opc : parser.GetOffPageConnectors() )
1092 {
1093 if( opc.source_sheet != sheetNum )
1094 continue;
1095
1096 if( opc.signal_name.empty() )
1097 continue;
1098
1099 VECTOR2I pos( schIUScale.MilsToIU( KiROUND( opc.position.x ) ),
1100 pageHeightIU
1101 - schIUScale.MilsToIU( KiROUND( opc.position.y ) ) );
1102
1103 // Resolve power style from the PARTTYPE variant definition
1104 std::string powerStyle;
1105 auto opcPtIt = parser.GetPartTypes().find( opc.symbol_lib );
1106
1107 if( opcPtIt != parser.GetPartTypes().end()
1108 && !opcPtIt->second.special_keyword.empty() && opcPtIt->second.special_keyword != "OFF"
1109 && !opcPtIt->second.special_variants.empty() )
1110 {
1111 int idx = std::max( 0, opc.flags2 );
1112 idx = std::min( idx,
1113 static_cast<int>(
1114 opcPtIt->second.special_variants.size() ) - 1 );
1115 const auto& variant = opcPtIt->second.special_variants[idx];
1116
1118 variant.decal_name, variant.pin_type );
1119 }
1120
1121 if( !powerStyle.empty() )
1122 {
1123 LIB_SYMBOL* pwrSym = symbolBuilder.BuildKiCadPowerSymbol( powerStyle );
1124
1125 if( pwrSym )
1126 {
1127 auto symbolPtr = std::make_unique<SCH_SYMBOL>();
1128 SCH_SYMBOL* symbol = symbolPtr.get();
1129
1130 LIB_ID libId;
1131 libId.SetLibNickname( wxT( "power" ) );
1132 libId.SetLibItemName( wxString::FromUTF8( powerStyle ) );
1133 symbol->SetLibId( libId );
1134 symbol->SetLibSymbol( pwrSym );
1135 symbol->SetPosition( pos );
1136 symbol->SetUnit( 1 );
1137
1138 // VCC and PWR_TRIANGLE have pin pointing up (body above pin).
1139 // All others (GND, GNDD, PWR_BAR, VEE, Earth) have pin pointing down.
1140 bool pinUp = ( powerStyle == "VCC" || powerStyle == "PWR_TRIANGLE" );
1141 int orient = computePowerOrientation(
1142 std::to_string( opc.id ), sheetSignals, pos, pinUp,
1143 pageHeightIU );
1144
1145 symbol->SetOrientation( orient );
1146
1147 wxString netName = wxString::FromUTF8( opc.signal_name );
1148
1149 if( netName.StartsWith( wxT( "/" ) ) )
1150 netName = wxT( "~{" ) + netName.Mid( 1 ) + wxT( "}" );
1151
1152 symbol->GetField( FIELD_T::VALUE )->SetText( netName );
1153 symbol->GetField( FIELD_T::VALUE )->SetVisible( true );
1154
1155 wxString pwrRef = wxString::Format( wxT( "#PWR%03d" ), pwrIndex++ );
1156 symbol->SetRef( &ctx.path, pwrRef );
1157 symbol->GetField( FIELD_T::REFERENCE )->SetVisible( false );
1158
1159 symbol->ClearFlags();
1160 ctx.screen->Append( symbolPtr.release() );
1161 continue;
1162 }
1163 }
1164
1165 // Non-power signal OPCs don't create labels here. CreateNetLabels
1166 // handles all signal net labels, placing them at dangling wire endpoints
1167 // rather than at OPC positions (which may not land on a wire).
1168 }
1169 }
1170
1171 // Place free text items from *TEXT* section on the first (or only) sheet
1172 if( !sheetContexts.empty() )
1173 {
1174 SCH_SCREEN* textScreen = sheetContexts.begin()->second.screen;
1175
1176 for( const PADS_SCH::TEXT_ITEM& textItem : parser.GetTextItems() )
1177 {
1178 if( textItem.content.empty() )
1179 continue;
1180
1181 VECTOR2I pos( schIUScale.MilsToIU( KiROUND( textItem.position.x ) ),
1182 pageHeightIU
1183 - schIUScale.MilsToIU( KiROUND( textItem.position.y ) ) );
1184
1185 textScreen->Append( createSchText( textItem, pos ) );
1186 }
1187 }
1188
1189 // Place graphic lines from *LINES* section (skip the border template)
1190 if( !sheetContexts.empty() )
1191 {
1192 SCH_SCREEN* linesScreen = sheetContexts.begin()->second.screen;
1193
1194 for( const PADS_SCH::LINES_ITEM& linesItem : parser.GetLinesItems() )
1195 {
1196 if( linesItem.name == params.border_template )
1197 continue;
1198
1199 double ox = linesItem.origin.x;
1200 double oy = linesItem.origin.y;
1201
1202 for( const PADS_SCH::SYMBOL_GRAPHIC& prim : linesItem.primitives )
1203 {
1204 int strokeWidth = prim.line_width > 0.0
1205 ? schIUScale.MilsToIU( KiROUND( prim.line_width ) )
1206 : 0;
1207
1209
1211 {
1213 schIUScale.MilsToIU( KiROUND( ox + prim.center.x ) ),
1214 pageHeightIU
1215 - schIUScale.MilsToIU( KiROUND( oy + prim.center.y ) ) );
1216 int radius = schIUScale.MilsToIU( KiROUND( prim.radius ) );
1217
1219 circle->SetStart( center );
1220 circle->SetEnd( VECTOR2I( center.x + radius, center.y ) );
1221 circle->SetStroke( STROKE_PARAMS( strokeWidth, lineStyle ) );
1222
1223 if( prim.filled )
1224 circle->SetFillMode( FILL_T::FILLED_SHAPE );
1225
1226 linesScreen->Append( circle );
1227 }
1229 && prim.points.size() == 2 )
1230 {
1231 VECTOR2I pos(
1232 schIUScale.MilsToIU( KiROUND( ox + prim.points[0].coord.x ) ),
1233 pageHeightIU
1234 - schIUScale.MilsToIU(
1235 KiROUND( oy + prim.points[0].coord.y ) ) );
1236 VECTOR2I end(
1237 schIUScale.MilsToIU( KiROUND( ox + prim.points[1].coord.x ) ),
1238 pageHeightIU
1239 - schIUScale.MilsToIU(
1240 KiROUND( oy + prim.points[1].coord.y ) ) );
1241
1242 SCH_SHAPE* rect = new SCH_SHAPE( SHAPE_T::RECTANGLE );
1243 rect->SetPosition( pos );
1244 rect->SetEnd( end );
1245 rect->SetStroke( STROKE_PARAMS( strokeWidth, lineStyle ) );
1246
1247 if( prim.filled )
1249
1250 linesScreen->Append( rect );
1251 }
1252 else if( prim.points.size() >= 2 )
1253 {
1254 for( size_t p = 0; p + 1 < prim.points.size(); p++ )
1255 {
1256 VECTOR2I start(
1257 schIUScale.MilsToIU(
1258 KiROUND( ox + prim.points[p].coord.x ) ),
1259 pageHeightIU
1260 - schIUScale.MilsToIU(
1261 KiROUND( oy + prim.points[p].coord.y ) ) );
1262 VECTOR2I end(
1263 schIUScale.MilsToIU(
1264 KiROUND( ox + prim.points[p + 1].coord.x ) ),
1265 pageHeightIU
1266 - schIUScale.MilsToIU(
1267 KiROUND( oy + prim.points[p + 1].coord.y ) ) );
1268
1269 if( start == end )
1270 continue;
1271
1272 if( prim.points[p].arc.has_value() )
1273 {
1274 const PADS_SCH::ARC_DATA& ad = *prim.points[p].arc;
1275 double cx = ( ad.bbox_x1 + ad.bbox_x2 ) / 2.0;
1276 double cy = ( ad.bbox_y1 + ad.bbox_y2 ) / 2.0;
1278 schIUScale.MilsToIU( KiROUND( ox + cx ) ),
1279 pageHeightIU
1280 - schIUScale.MilsToIU( KiROUND( oy + cy ) ) );
1281
1282 double sx = start.x - center.x;
1283 double sy = start.y - center.y;
1284 double ex = end.x - center.x;
1285 double ey = end.y - center.y;
1286 double radius = std::sqrt( sx * sx + sy * sy );
1287
1288 double mx = sx + ex;
1289 double my = sy + ey;
1290 double mlen = std::sqrt( mx * mx + my * my );
1291
1292 VECTOR2I midPt;
1293
1294 if( mlen > 0.001 )
1295 {
1296 midPt.x = center.x
1297 + static_cast<int>( radius * mx / mlen );
1298 midPt.y = center.y
1299 + static_cast<int>( radius * my / mlen );
1300 }
1301 else
1302 {
1303 midPt.x = center.x
1304 + static_cast<int>( -sy * radius
1305 / std::max( radius, 1.0 ) );
1306 midPt.y = center.y
1307 + static_cast<int>( sx * radius
1308 / std::max( radius, 1.0 ) );
1309 }
1310
1311 if( ad.angle < 0 )
1312 {
1313 midPt.x = 2 * center.x - midPt.x;
1314 midPt.y = 2 * center.y - midPt.y;
1315 }
1316
1317 SCH_SHAPE* arc = new SCH_SHAPE( SHAPE_T::ARC );
1318 arc->SetArcGeometry( start, midPt, end );
1319 arc->SetStroke( STROKE_PARAMS( strokeWidth, lineStyle ) );
1320
1321 if( prim.filled )
1323
1324 linesScreen->Append( arc );
1325 }
1326 else
1327 {
1328 SCH_LINE* line = new SCH_LINE(
1330 line->SetEndPoint( end );
1331 line->SetStroke( STROKE_PARAMS( strokeWidth, lineStyle ) );
1332 linesScreen->Append( line );
1333 }
1334 }
1335 }
1336 }
1337
1338 // Render text items within this LINES group
1339 for( const PADS_SCH::TEXT_ITEM& textItem : linesItem.texts )
1340 {
1341 if( textItem.content.empty() )
1342 continue;
1343
1344 VECTOR2I pos(
1345 schIUScale.MilsToIU( KiROUND( ox + textItem.position.x ) ),
1346 pageHeightIU
1347 - schIUScale.MilsToIU( KiROUND( oy + textItem.position.y ) ) );
1348
1349 linesScreen->Append( createSchText( textItem, pos ) );
1350 }
1351 }
1352 }
1353
1354 // Set title block from parsed parameters
1355 schBuilder.CreateTitleBlock( rootScreen );
1356
1357 // Finalize all sheets
1358 SCH_SCREENS allSheets( rootSheet );
1359 allSheets.UpdateSymbolLinks();
1360 allSheets.ClearEditFlags();
1361
1362 if( m_reporter )
1363 {
1364 for( const auto& [msg, severity] : m_errorMessages )
1365 m_reporter->Report( msg, severity );
1366 }
1367
1368 m_errorMessages.clear();
1369
1370 return rootSheet;
1371}
1372
1373
1374bool SCH_IO_PADS::checkFileHeader( const wxString& aFileName ) const
1375{
1376 try
1377 {
1378 std::ifstream file( aFileName.fn_str() );
1379
1380 if( !file.is_open() )
1381 return false;
1382
1383 std::string line;
1384
1385 if( std::getline( file, line ) )
1386 {
1387 if( line.find( "*PADS-POWERLOGIC" ) != std::string::npos )
1388 return true;
1389
1390 if( line.find( "*PADS-LOGIC" ) != std::string::npos )
1391 return true;
1392 }
1393 }
1394 catch( ... )
1395 {
1396 }
1397
1398 return false;
1399}
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:114
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:148
const KIID m_Uuid
Definition eda_item.h:527
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:150
void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:220
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
void SetFillMode(FILL_T aFill)
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:546
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:431
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:400
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition eda_text.cpp:298
void SetTextAngleDegrees(double aOrientation)
Definition eda_text.h:150
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:423
REPORTER * m_reporter
Reporter to log errors/warnings to, may be nullptr.
Definition io_base.h:237
PROGRESS_REPORTER * m_progressReporter
Progress reporter to track the progress of the operation, may be nullptr.
Definition io_base.h:240
virtual bool CanReadLibrary(const wxString &aFileName) const
Checks if this IO object can read the specified library file/directory.
Definition io_base.cpp:71
Definition kiid.h:49
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:49
int SetLibItemName(const UTF8 &aLibItemName)
Override the library item name portion of the LIB_ID to aLibItemName.
Definition lib_id.cpp:111
int SetLibNickname(const UTF8 &aLibNickname)
Override the logical library name portion of the LIB_ID to aLibNickname.
Definition lib_id.cpp:100
Define a library symbol object.
Definition lib_symbol.h:83
void SetGlobalPower()
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition locale_io.h:41
Parser for PADS Logic schematic design export files.
const std::vector< TEXT_ITEM > & GetTextItems() const
const std::vector< TIED_DOT > & GetTiedDots() const
const std::vector< OFF_PAGE_CONNECTOR > & GetOffPageConnectors() const
bool Parse(const std::string &aFileName)
const std::vector< SHEET_HEADER > & GetSheetHeaders() const
const std::vector< LINES_ITEM > & GetLinesItems() const
std::vector< SCH_SIGNAL > GetSignalsOnSheet(int aSheetNumber) const
std::set< int > GetSheetNumbers() const
const std::vector< SYMBOL_DEF > & GetSymbolDefs() const
const std::vector< PART_PLACEMENT > & GetPartPlacements() const
const std::map< std::string, PARTTYPE_DEF > & GetPartTypes() const
std::vector< PART_PLACEMENT > GetPartsOnSheet(int aSheetNumber) const
const SYMBOL_DEF * GetSymbolDef(const std::string &aName) const
const PARAMETERS & GetParameters() const
Builder class to create KiCad schematic elements from parsed PADS data.
int CreateNetLabels(const std::vector< SCH_SIGNAL > &aSignals, SCH_SCREEN *aScreen, const std::set< std::string > &aSignalOpcIds, const std::set< std::string > &aSkipSignals={})
Create net labels for named signals.
void ApplyPartAttributes(SCH_SYMBOL *aSymbol, const PART_PLACEMENT &aPlacement)
Apply part attributes to a symbol instance.
void CreateTitleBlock(SCH_SCREEN *aScreen)
Create title block from parsed PADS parameters.
int CreateCustomFields(SCH_SYMBOL *aSymbol, const PART_PLACEMENT &aPlacement)
Create custom fields from non-standard PADS attributes.
SCH_SHEET * CreateHierarchicalSheet(int aSheetNumber, int aTotalSheets, SCH_SHEET *aParentSheet, const wxString &aBaseFilename)
Create hierarchical sheet for a sub-schematic page.
Builder class to convert PADS symbol definitions to KiCad LIB_SYMBOL objects.
void AddHiddenPowerPins(LIB_SYMBOL *aSymbol, const std::vector< PARTTYPE_DEF::SIGPIN > &aSigpins)
Add hidden power pins from PARTTYPE SIGPIN entries to an existing symbol.
LIB_SYMBOL * GetOrCreateMultiUnitSymbol(const PARTTYPE_DEF &aPartType, const std::vector< SYMBOL_DEF > &aSymbolDefs)
Get or create a multi-unit symbol for the given PARTTYPE.
static std::optional< LIB_ID > GetKiCadPowerSymbolId(const std::string &aPadsName)
Get KiCad power library symbol ID for a PADS power symbol.
LIB_SYMBOL * BuildKiCadPowerSymbol(const std::string &aKiCadName)
Build a power symbol using hard-coded KiCad-standard graphics.
static bool IsPowerSymbol(const std::string &aName)
Check if a symbol name indicates a power symbol.
static std::string GetPowerStyleFromVariant(const std::string &aDecalName, const std::string &aPinType)
Map a PADS special_variant to a power symbol style name.
LIB_SYMBOL * GetOrCreateSymbol(const SYMBOL_DEF &aSymbolDef)
Get or create a symbol for the given definition.
LIB_SYMBOL * GetOrCreateMultiUnitConnectorSymbol(const PARTTYPE_DEF &aPartType, const SYMBOL_DEF &aSymbolDef, const std::vector< std::string > &aPinNumbers, const std::string &aCacheKey)
Get or create a multi-unit connector symbol, cached by base reference.
LIB_SYMBOL * GetOrCreatePartTypeSymbol(const PARTTYPE_DEF &aPartType, const SYMBOL_DEF &aSymbolDef)
Get or create a single-gate symbol with PARTTYPE-specific pin remapping.
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:79
int GetHeightIU(double aIUScale) const
Gets the page height in IU.
Definition page_info.h:168
bool SetType(PAGE_SIZE_TYPE aPageSize, bool aIsPortrait=false)
Set the name of the page type and also the sizes and margins commonly associated with that type name.
Holds all the data relating to one schematic.
Definition schematic.h:88
SCHEMATIC_SETTINGS & Settings() const
bool IsValid() const
A simple test if the schematic is loaded, not a complete one.
Definition schematic.h:172
void SetTopLevelSheets(const std::vector< SCH_SHEET * > &aSheets)
void SetPosition(const VECTOR2I &aPosition) override
void SetText(const wxString &aText) override
bool CanReadLibrary(const wxString &aFileName) const override
Checks if this IO object can read the specified library file/directory.
bool checkFileHeader(const wxString &aFileName) const
Check if the file header indicates a PADS Logic schematic file.
bool CanReadSchematicFile(const wxString &aFileName) const override
Checks if this SCH_IO can read the specified schematic file.
std::unordered_map< wxString, SEVERITY > m_errorMessages
Definition sch_io_pads.h:73
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,...
virtual bool CanReadSchematicFile(const wxString &aFileName) const
Checks if this SCH_IO can read the specified schematic file.
Definition sch_io.cpp:45
SCH_IO(const wxString &aName)
Definition sch_io.h:375
int GetUnit() const
Definition sch_item.h:239
void SetConnectivityDirty(bool aDirty=true)
Definition sch_item.h:590
virtual void SetUnit(int aUnit)
Definition sch_item.h:238
virtual void SetSpinStyle(SPIN_STYLE aSpinStyle)
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:42
virtual void SetStroke(const STROKE_PARAMS &aStroke) override
Definition sch_line.h:202
void SetEndPoint(const VECTOR2I &aPosition)
Definition sch_line.h:149
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition sch_screen.h:749
void UpdateSymbolLinks(REPORTER *aReporter=nullptr)
Initialize the LIB_SYMBOL reference for each SCH_SYMBOL found in the full schematic.
void ClearEditFlags()
std::vector< SCH_SHEET_INSTANCE > m_sheetInstances
Definition sch_screen.h:727
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
const KIID & GetUuid() const
Definition sch_screen.h:532
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
void SetPosition(const VECTOR2I &aPos) override
Definition sch_shape.h:86
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition sch_shape.cpp:63
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
KIID_PATH Path() const
Get the sheet path as an KIID_PATH.
void SetPageNumber(const wxString &aPageNumber)
Set the sheet instance user definable page number.
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
void SetFileName(const wxString &aFilename)
Definition sch_sheet.h:377
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this sheet.
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:140
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
Schematic symbol object.
Definition sch_symbol.h:76
void SetLibId(const LIB_ID &aName)
void SetPosition(const VECTOR2I &aPosition) override
Definition sch_symbol.h:868
std::vector< const SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet) const
Retrieve a list of the SCH_PINs for the given sheet path.
void SetRef(const SCH_SHEET_PATH *aSheet, const wxString &aReference)
Set the reference for the given sheet path for this symbol.
void SetOrientation(int aOrientation)
Compute the new transform matrix based on aOrientation for the symbol which is applied to the current...
void AddHierarchicalReference(const KIID_PATH &aPath, const wxString &aRef, int aUnit)
Add a full hierarchical reference to this symbol.
VECTOR2I GetPosition() const override
Definition sch_symbol.h:867
void SetValueFieldText(const wxString &aValue, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString)
void SetLibSymbol(LIB_SYMBOL *aLibSymbol)
Set this schematic symbol library symbol reference to aLibSymbol.
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this symbol.
Simple container to manage line stroke parameters.
#define IS_NEW
New item, just created.
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:46
@ FILLED_SHAPE
Fill with object color.
Definition eda_shape.h:58
double m_PadsSchTextWidthScale
PADS text width scale factor for schematic imports.
double m_PadsSchTextHeightScale
PADS text height scale factor for schematic imports.
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
@ LAYER_WIRE
Definition layer_ids.h:452
@ LAYER_NOTES
Definition layer_ids.h:467
LINE_STYLE PadsLineStyleToKiCad(int aPadsStyle)
Convert a PADS line style integer to a KiCad LINE_STYLE enum value.
KIID GenerateDeterministicUuid(const std::string &aIdentifier)
Generate a deterministic KIID from a PADS component identifier.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
Common utilities and types for parsing PADS file formats.
@ RPT_SEVERITY_WARNING
static int computePowerOrientation(const std::string &aOpcId, const std::vector< PADS_SCH::SCH_SIGNAL > &aSignals, const VECTOR2I &aOpcPos, bool aPinUp, int aPageHeightIU)
Determine the orientation for a power symbol at an OPC position based on the wire direction at that p...
static std::string extractConnectorBaseRef(const std::string &aRef)
Extract the base reference from a connector reference designator.
static SCH_TEXT * createSchText(const PADS_SCH::TEXT_ITEM &aText, const VECTOR2I &aPos)
static std::string extractConnectorPinNumber(const std::string &aRef)
Extract the numeric connector pin suffix from a reference designator.
static std::string stripGateSuffix(const std::string &aRef)
Strip any alphabetic gate suffix (e.g.
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
LINE_STYLE
Dashed line types.
Gate definition within a PARTTYPE.
std::vector< std::string > decal_names
Graphical line/shape item from LINES section.
Off-page reference from OFFPAGE REFS section.
General schematic parameters from SCH and FIELDS sections.
std::string border_template
Part type definition from PARTTYPE section.
std::vector< GATE_DEF > gates
std::vector< SPECIAL_VARIANT > special_variants
Part instance from PART section.
Signal (net) definition from CONNECTION and SIGNAL sections.
Sheet header from SHT section.
Symbol definition from CAEDECAL section.
Graphic primitive from CAEDECAL or LINES sections (OPEN, CLOSED, CIRCLE, COPCLS).
std::vector< GRAPHIC_POINT > points
Free text item from TEXT section.
Junction dot from TIEDOTS section.
Wire segment connecting two endpoints through coordinate vertices.
std::vector< POINT > vertices
A simple container for sheet instance information.
@ SYM_ORIENT_270
Definition symbol.h:42
@ SYM_MIRROR_Y
Definition symbol.h:44
@ SYM_ORIENT_180
Definition symbol.h:41
@ SYM_MIRROR_X
Definition symbol.h:43
@ SYM_ORIENT_90
Definition symbol.h:40
@ SYM_ORIENT_0
Definition symbol.h:39
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
std::string path
VECTOR2I center
int radius
VECTOR2I end
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
@ 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
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
Definition of file extensions used in Kicad.