KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_io_kicad_sexpr.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) 2012 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21#include <wx/dir.h>
22#include <wx/ffile.h>
23#include <wx/log.h>
24#include <wx/msgdlg.h>
25#include <wx/mstream.h>
26
27#include <board.h>
29#include <callback_gal.h>
31#include <confirm.h>
32#include <convert_basic_shapes_to_polygon.h> // for enum RECT_CHAMFER_POSITIONS definition
33#include <fmt/core.h>
34#include <font/fontconfig.h>
35#include <footprint.h>
36#include <gestfich.h>
38#include <kiface_base.h>
39#include <kiplatform/io.h>
40#include <layer_range.h>
41#include <macros.h>
42#include <pad.h>
43#include <pcb_dimension.h>
44#include <pcb_generator.h>
45#include <pcb_group.h>
49#include <pcb_point.h>
50#include <pcb_reference_image.h>
51#include <pcb_barcode.h>
52#include <pcb_shape.h>
53#include <pcb_table.h>
54#include <pcb_tablecell.h>
55#include <pcb_target.h>
56#include <pcb_text.h>
57#include <pcb_textbox.h>
58#include <pcb_track.h>
59#include <pcbnew_settings.h>
60#include <pgm_base.h>
61#include <progress_reporter.h>
62#include <reporter.h>
63#include <string_utils.h>
64#include <trace_helpers.h>
66#include <zone.h>
67
68#include <build_version.h>
69#include <filter_reader.h>
70#include <ctl_flags.h>
71
72
73using namespace PCB_KEYS_T;
74
75
76FP_CACHE_ENTRY::FP_CACHE_ENTRY( FOOTPRINT* aFootprint, const WX_FILENAME& aFileName ) :
77 m_filename( aFileName ),
78 m_footprint( aFootprint )
79{ }
80
81
82FP_CACHE::FP_CACHE( PCB_IO_KICAD_SEXPR* aOwner, const wxString& aLibraryPath )
83{
84 m_owner = aOwner;
85 m_lib_raw_path = aLibraryPath;
86 m_lib_path.SetPath( aLibraryPath );
88 m_cache_dirty = true;
89}
90
91
92void FP_CACHE::Save( FOOTPRINT* aFootprintFilter )
93{
95
96 if( !m_lib_path.DirExists() && !m_lib_path.Mkdir() )
97 THROW_IO_ERRORF( _( "Cannot create footprint library '%s'." ), m_lib_raw_path );
98
99 if( !m_lib_path.IsDirWritable() )
100 THROW_IO_ERRORF( _( "Footprint library '%s' is read only." ), m_lib_raw_path );
101
102 for( auto it = m_footprints.begin(); it != m_footprints.end(); ++it )
103 {
104 FP_CACHE_ENTRY* fpCacheEntry = it->second;
105 std::unique_ptr<FOOTPRINT>& footprint = fpCacheEntry->GetFootprint();
106
107 if( aFootprintFilter && footprint.get() != aFootprintFilter )
108 continue;
109
110 // If we've requested to embed the fonts in the footprint, do so. Otherwise, clear the
111 // embedded fonts from the footprint. Embedded fonts will be used if available.
112 if( footprint->GetAreFontsEmbedded() )
113 footprint->EmbedFonts();
114 else
115 footprint->GetEmbeddedFiles()->ClearEmbeddedFonts();
116
117 WX_FILENAME fn = fpCacheEntry->GetFileName();
118 wxString fileName = fn.GetFullPath();
119
120 // Allow file output stream to go out of scope to close the file stream before
121 // renaming the file.
122 {
123 wxLogTrace( traceKicadPcbPlugin, wxT( "Writing library file '%s'." ),
124 fileName );
125
126 PRETTIFIED_FILE_OUTPUTFORMATTER formatter( fileName );
127
128 m_owner->SetOutputFormatter( &formatter );
129 m_owner->Format( footprint.get() );
130 formatter.Finish();
131 }
132
134 }
135
136 if( m_lib_path.IsFileReadable() && m_lib_path.GetModificationTime().IsValid() )
137 m_cache_timestamp += m_lib_path.GetModificationTime().GetValue().GetValue();
138
139 // If we've saved the full cache, we clear the dirty flag.
140 if( !aFootprintFilter )
141 m_cache_dirty = false;
142}
143
144
146{
147 m_cache_dirty = false;
149
150 wxDir dir( m_lib_raw_path );
151
152 if( !dir.IsOpened() )
153 THROW_IO_ERRORF( _( "Footprint library '%s' not found." ), m_lib_raw_path );
154
155 wxString fullName;
156 wxString fileSpec = wxT( "*." ) + wxString( FILEEXT::KiCadFootprintFileExtension );
157
158 // wxFileName construction is egregiously slow. Construct it once and just swap out
159 // the filename thereafter.
160 WX_FILENAME fn( m_lib_raw_path, wxT( "dummyName" ) );
161
162 if( dir.GetFirst( &fullName, fileSpec ) )
163 {
164 wxString cacheError;
165
166 do
167 {
168 fn.SetFullName( fullName );
169
170 // Queue I/O errors so only files that fail to parse don't get loaded.
171 try
172 {
173 FILE_LINE_READER reader( fn.GetFullPath() );
174 PCB_IO_KICAD_SEXPR_PARSER parser( &reader, nullptr, nullptr );
175
176 FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( parser.Parse() );
177 wxString fpName = fn.GetName();
178
179 if( !footprint )
180 THROW_IO_ERROR( wxEmptyString ); // caught locally, just below...
181
182 footprint->SetFPID( LIB_ID( wxEmptyString, fpName ) );
183 m_footprints.insert( fpName, new FP_CACHE_ENTRY( footprint, fn ) );
184
185 // Collect any non-fatal parse warnings
186 for( const wxString& warning : parser.GetParseWarnings() )
187 {
188 if( !cacheError.IsEmpty() )
189 cacheError += wxT( "\n\n" );
190
191 cacheError += wxString::Format( _( "Warning in file '%s'" ) + '\n',
192 fn.GetFullPath() );
193 cacheError += warning;
194 }
195 }
196 catch( const IO_ERROR& ioe )
197 {
198 if( !cacheError.IsEmpty() )
199 cacheError += wxT( "\n\n" );
200
201 cacheError += wxString::Format( _( "Unable to read file '%s'" ) + '\n',
202 fn.GetFullPath() );
203 cacheError += ioe.What();
204 }
205 } while( dir.GetNext( &fullName ) );
206
208
209 if( !cacheError.IsEmpty() )
210 THROW_IO_ERROR( cacheError );
211 }
212}
213
214
215void FP_CACHE::Remove( const wxString& aFootprintName )
216{
217 auto it = m_footprints.find( aFootprintName );
218
219 if( it == m_footprints.end() )
220 {
221 THROW_IO_ERRORF( _( "Library '%s' has no footprint '%s'." ),
223 aFootprintName );
224 }
225
226 // Remove the footprint from the cache and delete the footprint file from the library.
227 wxString fullPath = it->second->GetFileName().GetFullPath();
228 m_footprints.erase( aFootprintName );
229 wxRemoveFile( fullPath );
230}
231
232
233bool FP_CACHE::IsPath( const wxString& aPath ) const
234{
235 return aPath == m_lib_raw_path;
236}
237
238
239void FP_CACHE::SetPath( const wxString& aPath )
240{
241 m_lib_raw_path = aPath;
242 m_lib_path.SetPath( aPath );
243
244
245 for( const auto& footprint : GetFootprints() )
246 footprint.second->SetFilePath( aPath );
247}
248
249
251{
253
254 return m_cache_dirty;
255}
256
257
258long long FP_CACHE::GetTimestamp( const wxString& aLibPath )
259{
260 wxString fileSpec = wxT( "*." ) + wxString( FILEEXT::KiCadFootprintFileExtension );
261
262 return KIPLATFORM::IO::TimestampDir( aLibPath, fileSpec );
263}
264
265
266bool PCB_IO_KICAD_SEXPR::CanReadBoard( const wxString& aFileName ) const
267{
268 if( !PCB_IO::CanReadBoard( aFileName ) )
269 return false;
270
271 try
272 {
273 FILE_LINE_READER reader( aFileName );
274 PCB_IO_KICAD_SEXPR_PARSER parser( &reader, nullptr, m_queryUserCallback );
275
276 return parser.IsValidBoardHeader();
277 }
278 catch( const IO_ERROR& )
279 {
280 }
281
282 return false;
283}
284
285
286void PCB_IO_KICAD_SEXPR::SaveBoard( const wxString& aFileName, BOARD* aBoard,
287 const std::map<std::string, UTF8>* aProperties )
288{
289 wxString sanityResult = aBoard->GroupsSanityCheck();
290
291 if( sanityResult != wxEmptyString && m_queryUserCallback )
292 {
294 _( "Internal Group Data Error" ), wxICON_ERROR,
295 wxString::Format( _( "Please report this bug. Error validating group "
296 "structure: %s\n\nSave anyway?" ), sanityResult ),
297 _( "Save Anyway" ) ) )
298 {
299 return;
300 }
301 }
302
303 PRETTIFIED_FILE_OUTPUTFORMATTER formatter( aFileName );
304 FormatBoardToFormatter( &formatter, aBoard, aProperties );
305 formatter.Finish();
306}
307
308
310 const std::map<std::string, UTF8>* aProperties )
311{
312 init( aProperties );
313
314 m_board = aBoard; // after init()
315
316 // If the user wants fonts embedded, make sure that they are added to the board. Otherwise,
317 // remove any fonts that were previously embedded.
318 if( m_board->GetAreFontsEmbedded() )
319 m_board->EmbedFonts();
320 else
321 m_board->GetEmbeddedFiles()->ClearEmbeddedFonts();
322
323 m_out = aOut;
324
325 m_out->Print( "(kicad_pcb (version %d) (generator \"pcbnew\") (generator_version %s)",
327 m_out->Quotew( GetMajorMinorVersion() ).c_str() );
328
329 Format( aBoard );
330
331 m_out->Print( ")" );
332
333 m_out = nullptr;
334}
335
336
337BOARD_ITEM* PCB_IO_KICAD_SEXPR::Parse( const wxString& aClipboardSourceInput )
338{
339 std::string input = TO_UTF8( aClipboardSourceInput );
340
341 STRING_LINE_READER reader( input, wxT( "clipboard" ) );
342 PCB_IO_KICAD_SEXPR_PARSER parser( &reader, nullptr, m_queryUserCallback );
343
344 try
345 {
346 return parser.Parse();
347 }
348 catch( const PARSE_ERROR& parse_error )
349 {
350 if( parser.IsTooRecent() )
351 throw FUTURE_FORMAT_ERROR( parse_error, parser.GetRequiredVersion() );
352 else
353 throw;
354 }
355}
356
357
358void PCB_IO_KICAD_SEXPR::Format( const BOARD_ITEM* aItem ) const
359{
360 switch( aItem->Type() )
361 {
362 case PCB_T:
363 format( static_cast<const BOARD*>( aItem ) );
364 break;
365
367 case PCB_DIM_CENTER_T:
368 case PCB_DIM_RADIAL_T:
370 case PCB_DIM_LEADER_T:
371 format( static_cast<const PCB_DIMENSION_BASE*>( aItem ) );
372 break;
373
374 case PCB_SHAPE_T:
375 format( static_cast<const PCB_SHAPE*>( aItem ) );
376 break;
377
379 format( static_cast<const PCB_REFERENCE_IMAGE*>( aItem ) );
380 break;
381
382 case PCB_POINT_T:
383 format( static_cast<const PCB_POINT*>( aItem ) );
384 break;
385
386 case PCB_TARGET_T:
387 format( static_cast<const PCB_TARGET*>( aItem ) );
388 break;
389
390 case PCB_FOOTPRINT_T:
391 format( static_cast<const FOOTPRINT*>( aItem ) );
392 break;
393
394 case PCB_PAD_T:
395 format( static_cast<const PAD*>( aItem ) );
396 break;
397
398 case PCB_FIELD_T:
399 // Handled in the footprint formatter when properties are formatted
400 break;
401
402 case PCB_TEXT_T:
403 format( static_cast<const PCB_TEXT*>( aItem ) );
404 break;
405
406 case PCB_TEXTBOX_T:
407 format( static_cast<const PCB_TEXTBOX*>( aItem ) );
408 break;
409
410 case PCB_BARCODE_T:
411 format( static_cast<const PCB_BARCODE*>( aItem ) );
412 break;
413
414 case PCB_TABLE_T:
415 format( static_cast<const PCB_TABLE*>( aItem ) );
416 break;
417
418 case PCB_GROUP_T:
419 format( static_cast<const PCB_GROUP*>( aItem ) );
420 break;
421
422 case PCB_GENERATOR_T:
423 format( static_cast<const PCB_GENERATOR*>( aItem ) );
424 break;
425
426 case PCB_CONSTRAINT_T:
427 format( static_cast<const PCB_CONSTRAINT*>( aItem ) );
428 break;
429
430 case PCB_TRACE_T:
431 case PCB_ARC_T:
432 case PCB_VIA_T:
433 format( static_cast<const PCB_TRACK*>( aItem ) );
434 break;
435
436 case PCB_ZONE_T:
437 format( static_cast<const ZONE*>( aItem ) );
438 break;
439
440 default:
441 wxFAIL_MSG( wxT( "Cannot format item " ) + aItem->GetClass() );
442 }
443}
444
445
446std::string formatInternalUnits( const int aValue, const EDA_DATA_TYPE aDataType = EDA_DATA_TYPE::DISTANCE )
447{
448 return EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, aValue, aDataType );
449}
450
451
452std::string formatInternalUnits( const VECTOR2I& aCoord )
453{
455}
456
457
458std::string formatInternalUnits( const VECTOR2I& aCoord, const FOOTPRINT* aParentFP )
459{
460 if( aParentFP )
461 {
462 return formatInternalUnits( aParentFP->GetTransform().InverseApply( aCoord ) );
463 }
464
465 return formatInternalUnits( aCoord );
466}
467
468
469static VECTOR2I unbakeSize( const VECTOR2I& aSize, const FOOTPRINT* aParentFP )
470{
471 if( !aParentFP )
472 return aSize;
473
474 const TRANSFORM_TRS& xform = aParentFP->GetTransform();
475 return { KiROUND( aSize.x / xform.GetScaleX() ), KiROUND( aSize.y / xform.GetScaleY() ) };
476}
477
478
479static VECTOR2I unbakeSizeUniform( const VECTOR2I& aSize, const FOOTPRINT* aParentFP )
480{
481 if( !aParentFP )
482 return aSize;
483
484 const TRANSFORM_TRS& xform = aParentFP->GetTransform();
485 double avg = ( xform.GetScaleX() + xform.GetScaleY() ) * 0.5;
486 return { KiROUND( aSize.x / avg ), KiROUND( aSize.y / avg ) };
487}
488
489
490static int unbakeLinear( int aValue, const FOOTPRINT* aParentFP )
491{
492 if( !aParentFP )
493 return aValue;
494
495 const TRANSFORM_TRS& xform = aParentFP->GetTransform();
496 double avg = ( xform.GetScaleX() + xform.GetScaleY() ) * 0.5;
497 return KiROUND( aValue / avg );
498}
499
500
501void PCB_IO_KICAD_SEXPR::formatLayer( PCB_LAYER_ID aLayer, bool aIsKnockout ) const
502{
503 m_out->Print( "(layer %s %s)",
504 m_out->Quotew( LSET::Name( aLayer ) ).c_str(),
505 aIsKnockout ? "knockout" : "" );
506}
507
508
510 const FOOTPRINT* aParentFP ) const
511{
512 m_out->Print( "(pts" );
513
514 for( int ii = 0; ii < outline.PointCount(); ++ii )
515 {
516 int ind = outline.ArcIndex( ii );
517
518 if( ind < 0 )
519 {
520 m_out->Print( "(xy %s)",
521 formatInternalUnits( outline.CPoint( ii ), aParentFP ).c_str() );
522 }
523 else
524 {
525 const SHAPE_ARC& arc = outline.Arc( ind );
526 m_out->Print( "(arc (start %s) (mid %s) (end %s))",
527 formatInternalUnits( arc.GetP0(), aParentFP ).c_str(),
528 formatInternalUnits( arc.GetArcMid(), aParentFP ).c_str(),
529 formatInternalUnits( arc.GetP1(), aParentFP ).c_str() );
530
531 do
532 {
533 ++ii;
534 } while( ii < outline.PointCount() && outline.ArcIndex( ii ) == ind );
535
536 --ii;
537 }
538 }
539
540 m_out->Print( ")" );
541}
542
543
545{
546 wxString resolvedText( aText->GetShownText( true ) );
547 std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = aText->GetRenderCache( aText->GetFont(),
548 resolvedText );
549
550 m_out->Print( "(render_cache %s %s",
551 m_out->Quotew( resolvedText ).c_str(),
552 EDA_UNIT_UTILS::FormatAngle( aText->GetDrawRotation() ).c_str() );
553
555
556 CALLBACK_GAL callback_gal( empty_opts,
557 // Polygon callback
558 [&]( const SHAPE_LINE_CHAIN& aPoly )
559 {
560 m_out->Print( "(polygon" );
561 formatPolyPts( aPoly );
562 m_out->Print( ")" );
563 } );
564
565 callback_gal.SetLineWidth( aText->GetTextThickness() );
566 callback_gal.DrawGlyphs( *cache );
567
568 m_out->Print( ")" );
569}
570
571
572void PCB_IO_KICAD_SEXPR::formatSetup( const BOARD* aBoard ) const
573{
574 // Setup
575 m_out->Print( "(setup" );
576
577 // Save the board physical stackup structure
578 const BOARD_STACKUP& stackup = aBoard->GetDesignSettings().GetStackupDescriptor();
579
580 if( aBoard->GetDesignSettings().m_HasStackup )
581 stackup.FormatBoardStackup( m_out, aBoard );
582
583 BOARD_DESIGN_SETTINGS& dsnSettings = aBoard->GetDesignSettings();
584
585 m_out->Print( "(pad_to_mask_clearance %s)",
586 formatInternalUnits( dsnSettings.m_SolderMaskExpansion ).c_str() );
587
588 if( dsnSettings.m_SolderMaskMinWidth )
589 {
590 m_out->Print( "(solder_mask_min_width %s)",
591 formatInternalUnits( dsnSettings.m_SolderMaskMinWidth ).c_str() );
592 }
593
594 if( dsnSettings.m_SolderPasteMargin != 0 )
595 {
596 m_out->Print( "(pad_to_paste_clearance %s)",
597 formatInternalUnits( dsnSettings.m_SolderPasteMargin ).c_str() );
598 }
599
600 if( dsnSettings.m_SolderPasteMarginRatio != 0 )
601 {
602 m_out->Print( "(pad_to_paste_clearance_ratio %s)",
603 FormatDouble2Str( dsnSettings.m_SolderPasteMarginRatio ).c_str() );
604 }
605
606 KICAD_FORMAT::FormatBool( m_out, "allow_soldermask_bridges_in_footprints",
607 dsnSettings.m_AllowSoldermaskBridgesInFPs );
608
609 m_out->Print( 0, " (tenting " );
610 KICAD_FORMAT::FormatBool( m_out, "front", dsnSettings.m_TentViasFront );
611 KICAD_FORMAT::FormatBool( m_out, "back", dsnSettings.m_TentViasBack );
612 m_out->Print( 0, ")" );
613
614 m_out->Print( 0, " (covering " );
615 KICAD_FORMAT::FormatBool( m_out, "front", dsnSettings.m_CoverViasFront );
616 KICAD_FORMAT::FormatBool( m_out, "back", dsnSettings.m_CoverViasBack );
617 m_out->Print( 0, ")" );
618
619 m_out->Print( 0, " (plugging " );
620 KICAD_FORMAT::FormatBool( m_out, "front", dsnSettings.m_PlugViasFront );
621 KICAD_FORMAT::FormatBool( m_out, "back", dsnSettings.m_PlugViasBack );
622 m_out->Print( 0, ")" );
623
624 KICAD_FORMAT::FormatBool( m_out, "capping", dsnSettings.m_CapVias );
625
626 KICAD_FORMAT::FormatBool( m_out, "filling", dsnSettings.m_FillVias );
627
628 if( !dsnSettings.m_ZoneLayerProperties.empty() )
629 {
630 m_out->Print( 0, " (zone_defaults" );
631
632 for( const auto& [layer, properties] : dsnSettings.m_ZoneLayerProperties )
633 format( properties, 0, layer );
634
635 m_out->Print( 0, ")\n" );
636 }
637
638 VECTOR2I origin = dsnSettings.GetAuxOrigin();
639
640 if( origin != VECTOR2I( 0, 0 ) )
641 {
642 m_out->Print( "(aux_axis_origin %s %s)",
643 formatInternalUnits( origin.x ).c_str(),
644 formatInternalUnits( origin.y ).c_str() );
645 }
646
647 origin = dsnSettings.GetGridOrigin();
648
649 if( origin != VECTOR2I( 0, 0 ) )
650 {
651 m_out->Print( "(grid_origin %s %s)",
652 formatInternalUnits( origin.x ).c_str(),
653 formatInternalUnits( origin.y ).c_str() );
654 }
655
656 aBoard->GetPlotOptions().Format( m_out );
657
658 m_out->Print( ")" );
659}
660
661
662void PCB_IO_KICAD_SEXPR::formatGeneral( const BOARD* aBoard ) const
663{
664 const BOARD_DESIGN_SETTINGS& dsnSettings = aBoard->GetDesignSettings();
665
666 m_out->Print( "(general" );
667
668 m_out->Print( "(thickness %s)",
669 formatInternalUnits( dsnSettings.GetBoardThickness() ).c_str() );
670
671 KICAD_FORMAT::FormatBool( m_out, "legacy_teardrops", aBoard->LegacyTeardrops() );
672
673 m_out->Print( ")" );
674
675 aBoard->GetPageSettings().Format( m_out );
676 aBoard->GetTitleBlock().Format( m_out );
677}
678
679
681{
682 m_out->Print( "(layers" );
683
684 // Save only the used copper layers from front to back.
685
686 for( PCB_LAYER_ID layer : aBoard->GetEnabledLayers().CuStack() )
687 {
688 m_out->Print( "(%d %s %s %s)",
689 layer,
690 m_out->Quotew( LSET::Name( layer ) ).c_str(),
691 LAYER::ShowType( aBoard->GetLayerType( layer ) ),
692 LSET::Name( layer ) == m_board->GetLayerName( layer )
693 ? ""
694 : m_out->Quotew( m_board->GetLayerName( layer ) ).c_str() );
695
696 }
697
698 // Save used non-copper layers in the order they are defined.
699 LSEQ seq = aBoard->GetEnabledLayers().TechAndUserUIOrder();
700
701 for( PCB_LAYER_ID layer : seq )
702 {
703 bool print_type = false;
704
705 // User layers (layer id >= User_1) have a qualifier
706 // default is "user", but other qualifiers exist
707 if( layer >= User_1 )
708 {
709 if( IsCopperLayer( layer ) )
710 print_type = true;
711
712 if( aBoard->GetLayerType( layer ) == LT_FRONT
713 || aBoard->GetLayerType( layer ) == LT_BACK )
714 print_type = true;
715 }
716
717 m_out->Print( "(%d %s %s %s)",
718 layer,
719 m_out->Quotew( LSET::Name( layer ) ).c_str(),
720 print_type
721 ? LAYER::ShowType( aBoard->GetLayerType( layer ) )
722 : "user",
723 m_board->GetLayerName( layer ) == LSET::Name( layer )
724 ? ""
725 : m_out->Quotew( m_board->GetLayerName( layer ) ).c_str() );
726 }
727
728 m_out->Print( ")" );
729}
730
731
733{
734 for( const std::pair<const wxString, wxString>& prop : aBoard->GetProperties() )
735 {
736 m_out->Print( "(property %s %s)",
737 m_out->Quotew( prop.first ).c_str(),
738 m_out->Quotew( prop.second ).c_str() );
739 }
740}
741
742
743void PCB_IO_KICAD_SEXPR::formatVariants( const BOARD* aBoard ) const
744{
745 const std::vector<wxString>& variantNames = aBoard->GetVariantNames();
746
747 if( variantNames.empty() )
748 return;
749
750 m_out->Print( "(variants" );
751
752 for( const wxString& variantName : variantNames )
753 {
754 m_out->Print( "(variant (name %s)", m_out->Quotew( variantName ).c_str() );
755
756 wxString description = aBoard->GetVariantDescription( variantName );
757
758 if( !description.IsEmpty() )
759 m_out->Print( "(description %s)", m_out->Quotew( description ).c_str() );
760
761 m_out->Print( ")" );
762 }
763
764 m_out->Print( ")" );
765}
766
767
768void PCB_IO_KICAD_SEXPR::formatHeader( const BOARD* aBoard ) const
769{
770 formatGeneral( aBoard );
771
772 // Layers list.
773 formatBoardLayers( aBoard );
774
775 // Setup
776 formatSetup( aBoard );
777
778 // Properties
779 formatProperties( aBoard );
780
781 // Variants
782 formatVariants( aBoard );
783}
784
785
787{
788 static const TEARDROP_PARAMETERS defaults;
789
790 return tdParams.m_Enabled == defaults.m_Enabled
791 && tdParams.m_BestLengthRatio == defaults.m_BestLengthRatio
792 && tdParams.m_TdMaxLen == defaults.m_TdMaxLen
793 && tdParams.m_BestWidthRatio == defaults.m_BestWidthRatio
794 && tdParams.m_TdMaxWidth == defaults.m_TdMaxWidth
795 && tdParams.m_CurvedEdges == defaults.m_CurvedEdges
797 && tdParams.m_AllowUseTwoTracks == defaults.m_AllowUseTwoTracks
798 && tdParams.m_TdOnPadsInZones == defaults.m_TdOnPadsInZones;
799}
800
801
803{
804 m_out->Print( "(teardrops (best_length_ratio %s) (max_length %s) (best_width_ratio %s) "
805 "(max_width %s)",
806 FormatDouble2Str( tdParams.m_BestLengthRatio ).c_str(),
807 formatInternalUnits( tdParams.m_TdMaxLen ).c_str(),
808 FormatDouble2Str( tdParams.m_BestWidthRatio ).c_str(),
809 formatInternalUnits( tdParams.m_TdMaxWidth ).c_str() );
810
811 KICAD_FORMAT::FormatBool( m_out, "curved_edges", tdParams.m_CurvedEdges );
812
813 m_out->Print( "(filter_ratio %s)",
814 FormatDouble2Str( tdParams.m_WidthtoSizeFilterRatio ).c_str() );
815
816 KICAD_FORMAT::FormatBool( m_out, "enabled", tdParams.m_Enabled );
817 KICAD_FORMAT::FormatBool( m_out, "allow_two_segments", tdParams.m_AllowUseTwoTracks );
818 KICAD_FORMAT::FormatBool( m_out, "prefer_zone_connections", !tdParams.m_TdOnPadsInZones );
819 m_out->Print( ")" );
820}
821
822
823void PCB_IO_KICAD_SEXPR::format( const BOARD* aBoard ) const
824{
825 std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_footprints( aBoard->Footprints().begin(),
826 aBoard->Footprints().end() );
827 std::set<BOARD_ITEM*, BOARD::cmp_drawings> sorted_drawings( aBoard->Drawings().begin(),
828 aBoard->Drawings().end() );
829 std::set<PCB_TRACK*, PCB_TRACK::cmp_tracks> sorted_tracks( aBoard->Tracks().begin(),
830 aBoard->Tracks().end() );
831 std::set<PCB_POINT*, PCB_POINT::cmp_points> sorted_points( aBoard->Points().begin(),
832 aBoard->Points().end() );
833 std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_zones( aBoard->Zones().begin(),
834 aBoard->Zones().end() );
835 std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_groups( aBoard->Groups().begin(),
836 aBoard->Groups().end() );
837 std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_generators( aBoard->Generators().begin(),
838 aBoard->Generators().end() );
839 std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_constraints( aBoard->Constraints().begin(),
840 aBoard->Constraints().end() );
841 formatHeader( aBoard );
842
843 // Save the footprints.
844 for( BOARD_ITEM* footprint : sorted_footprints )
845 Format( footprint );
846
847 // Save the graphical items on the board (not owned by a footprint)
848 for( BOARD_ITEM* item : sorted_drawings )
849 Format( item );
850
851 // Save the points
852 for( PCB_POINT* point : sorted_points )
853 Format( point );
854
855 // Do not save PCB_MARKERs, they can be regenerated easily.
856
857 // Save the tracks and vias.
858 for( PCB_TRACK* track : sorted_tracks )
859 Format( track );
860
861 // Save the polygon (which are the newer technology) zones.
862 for( auto zone : sorted_zones )
863 Format( zone );
864
865 // Save the groups
866 for( BOARD_ITEM* group : sorted_groups )
867 Format( group );
868
869 // Save the generators
870 for( BOARD_ITEM* gen : sorted_generators )
871 Format( gen );
872
873 // Save the geometric constraints last, after every item they may reference.
874 for( BOARD_ITEM* constraint : sorted_constraints )
875 Format( constraint );
876
877 // After writing all items, write the aggregated net chains section (if any)
878 struct CHAIN_INFO
879 {
880 std::vector<NETINFO_ITEM*> nets;
881 PAD* pads[2] = { nullptr, nullptr };
882 };
883
884 // Simple lexicographic ordering using ValueStringCompare comparator logic
885 auto cmp = []( const wxString& a, const wxString& b )
886 {
887 return ValueStringCompare( a, b ) < 0;
888 };
889
890 std::map<wxString, CHAIN_INFO, decltype( cmp )> chains( cmp );
891
892 for( NETINFO_ITEM* net : aBoard->GetNetInfo() )
893 {
894 if( !net )
895 continue;
896
897 if( net->GetNetChain().IsEmpty() && !net->GetTerminalPad( 0 ) && !net->GetTerminalPad( 1 ) )
898 continue; // nothing to aggregate
899
900 wxString chainName = net->GetNetChain();
901
902 if( chainName.IsEmpty() && ( net->GetTerminalPad( 0 ) || net->GetTerminalPad( 1 ) ) )
903 chainName = net->GetNetname(); // synthetic name for unnamed terminal association
904
905 CHAIN_INFO& info = chains[chainName];
906 info.nets.push_back( net );
907 for( int i = 0; i < 2; ++i )
908 {
909 if( net->GetTerminalPad( i ) && !info.pads[i] )
910 info.pads[i] = net->GetTerminalPad( i );
911 }
912 }
913
914 size_t count = 0;
915 for( const auto& kv : chains )
916 {
917 const CHAIN_INFO& si = kv.second;
918 const wxString& chainName = kv.first;
919 // Persist if: multi-net OR terminal pads OR explicit (non-empty) chain name
920 if( si.nets.size() > 1 || si.pads[0] || si.pads[1] || !chainName.IsEmpty() )
921 ++count;
922 }
923
924 if( count )
925 {
926 m_out->Print( "(net_chains" );
927 for( const auto& kv : chains )
928 {
929 const wxString& name = kv.first;
930 const CHAIN_INFO& si = kv.second;
931
932 if( si.nets.size() == 1 && !si.pads[0] && !si.pads[1] && name.IsEmpty() )
933 continue;
934
935 m_out->Print( " (net_chain (name %s)", m_out->Quotew( name ).c_str() );
936 m_out->Print( " (members" );
937 for( NETINFO_ITEM* n : si.nets )
938 {
939 m_out->Print( " (net %s)", m_out->Quotew( n->GetNetname() ).c_str() );
940 }
941 m_out->Print( ")" );
942
943 for( int i = 0; i < 2; ++i )
944 {
945 if( si.pads[i] )
946 m_out->Print( " (terminal_pad %s)",
947 m_out->Quotew( si.pads[i]->m_Uuid.AsString() ).c_str() );
948 }
949
950 m_out->Print( ")" );
951 }
952 m_out->Print( ")" );
953 }
954
955 // Save any embedded files
956 // Consolidate the embedded models in footprints into a single map
957 // to avoid duplicating the same model in the board file.
958 EMBEDDED_FILES files_to_write;
959
960 for( auto& file : aBoard->GetEmbeddedFiles()->EmbeddedFileMap() )
961 files_to_write.AddFile( file.second );
962
963 for( BOARD_ITEM* item : sorted_footprints )
964 {
965 FOOTPRINT* fp = static_cast<FOOTPRINT*>( item );
966
967 for( auto& file : fp->GetEmbeddedFiles()->EmbeddedFileMap() )
968 files_to_write.AddFile( file.second );
969 }
970
971 m_out->Print( "(embedded_fonts %s)",
972 aBoard->GetEmbeddedFiles()->GetAreFontsEmbedded() ? "yes" : "no" );
973
974 if( !files_to_write.IsEmpty() )
975 files_to_write.WriteEmbeddedFiles( *m_out, ( m_ctl & CTL_FOR_BOARD ) );
976
977 // Remove the files so that they are not freed in the DTOR
978 files_to_write.ClearEmbeddedFiles( false );
979}
980
981
982void PCB_IO_KICAD_SEXPR::format( const PCB_DIMENSION_BASE* aDimension ) const
983{
984 const PCB_DIM_ALIGNED* aligned = dynamic_cast<const PCB_DIM_ALIGNED*>( aDimension );
985 const PCB_DIM_ORTHOGONAL* ortho = dynamic_cast<const PCB_DIM_ORTHOGONAL*>( aDimension );
986 const PCB_DIM_CENTER* center = dynamic_cast<const PCB_DIM_CENTER*>( aDimension );
987 const PCB_DIM_RADIAL* radial = dynamic_cast<const PCB_DIM_RADIAL*>( aDimension );
988 const PCB_DIM_LEADER* leader = dynamic_cast<const PCB_DIM_LEADER*>( aDimension );
989
990 m_out->Print( "(dimension" );
991
992 if( ortho ) // must be tested before aligned, because ortho is derived from aligned
993 // and aligned is not null
994 m_out->Print( "(type orthogonal)" );
995 else if( aligned )
996 m_out->Print( "(type aligned)" );
997 else if( leader )
998 m_out->Print( "(type leader)" );
999 else if( center )
1000 m_out->Print( "(type center)" );
1001 else if( radial )
1002 m_out->Print( "(type radial)" );
1003 else
1004 wxFAIL_MSG( wxT( "Cannot format unknown dimension type!" ) );
1005
1006 if( aDimension->IsLocked() )
1007 KICAD_FORMAT::FormatBool( m_out, "locked", aDimension->IsLocked() );
1008
1009 formatLayer( aDimension->GetLayer() );
1010
1011 KICAD_FORMAT::FormatUuid( m_out, aDimension->m_Uuid );
1012
1013 m_out->Print( "(pts (xy %s %s) (xy %s %s))",
1014 formatInternalUnits( aDimension->GetStart().x ).c_str(),
1015 formatInternalUnits( aDimension->GetStart().y ).c_str(),
1016 formatInternalUnits( aDimension->GetEnd().x ).c_str(),
1017 formatInternalUnits( aDimension->GetEnd().y ).c_str() );
1018
1019 if( aligned )
1020 m_out->Print( "(height %s)", formatInternalUnits( aligned->GetHeight() ).c_str() );
1021
1022 if( radial )
1023 {
1024 m_out->Print( "(leader_length %s)",
1025 formatInternalUnits( radial->GetLeaderLength() ).c_str() );
1026 }
1027
1028 if( ortho )
1029 m_out->Print( "(orientation %d)", static_cast<int>( ortho->GetOrientation() ) );
1030
1031 if( !center )
1032 {
1033 m_out->Print( "(format (prefix %s) (suffix %s) (units %d) (units_format %d) (precision %d)",
1034 m_out->Quotew( aDimension->GetPrefix() ).c_str(),
1035 m_out->Quotew( aDimension->GetSuffix() ).c_str(),
1036 static_cast<int>( aDimension->GetUnitsMode() ),
1037 static_cast<int>( aDimension->GetUnitsFormat() ),
1038 static_cast<int>( aDimension->GetPrecision() ) );
1039
1040 if( aDimension->GetOverrideTextEnabled() )
1041 {
1042 m_out->Print( "(override_value %s)",
1043 m_out->Quotew( aDimension->GetOverrideText() ).c_str() );
1044 }
1045
1046 if( aDimension->GetSuppressZeroes() )
1047 KICAD_FORMAT::FormatBool( m_out, "suppress_zeroes", true );
1048
1049 m_out->Print( ")" );
1050 }
1051
1052 m_out->Print( "(style (thickness %s) (arrow_length %s) (text_position_mode %d)",
1053 formatInternalUnits( aDimension->GetLineThickness() ).c_str(),
1054 formatInternalUnits( aDimension->GetArrowLength() ).c_str(),
1055 static_cast<int>( aDimension->GetTextPositionMode() ) );
1056
1057 if( ortho || aligned )
1058 {
1059 switch( aDimension->GetArrowDirection() )
1060 {
1062 m_out->Print( "(arrow_direction outward)" );
1063 break;
1065 m_out->Print( "(arrow_direction inward)" );
1066 break;
1067 // No default, handle all cases
1068 }
1069 }
1070
1071 if( aligned )
1072 {
1073 m_out->Print( "(extension_height %s)",
1074 formatInternalUnits( aligned->GetExtensionHeight() ).c_str() );
1075 }
1076
1077 if( leader )
1078 m_out->Print( "(text_frame %d)", static_cast<int>( leader->GetTextBorder() ) );
1079
1080 m_out->Print( "(extension_offset %s)",
1081 formatInternalUnits( aDimension->GetExtensionOffset() ).c_str() );
1082
1083 if( aDimension->GetKeepTextAligned() )
1084 KICAD_FORMAT::FormatBool( m_out, "keep_text_aligned", true );
1085
1086 m_out->Print( ")" );
1087
1088 // Write dimension text after all other options to be sure the
1089 // text options are known when reading the file
1090 if( !center )
1091 format( static_cast<const PCB_TEXT*>( aDimension ) );
1092
1093 m_out->Print( ")" );
1094}
1095
1096
1097void PCB_IO_KICAD_SEXPR::format( const PCB_SHAPE* aShape ) const
1098{
1099 FOOTPRINT* parentFP = aShape->GetParentFootprint();
1100 std::string prefix = parentFP ? "fp" : "gr";
1101
1102 switch( aShape->GetLibraryShape() )
1103 {
1104 case SHAPE_T::SEGMENT:
1105 m_out->Print( "(%s_line (start %s) (end %s)",
1106 prefix.c_str(),
1107 formatInternalUnits( aShape->GetStart(), parentFP ).c_str(),
1108 formatInternalUnits( aShape->GetEnd(), parentFP ).c_str() );
1109 break;
1110
1111 case SHAPE_T::RECTANGLE:
1112 m_out->Print( "(%s_rect (start %s) (end %s)", prefix.c_str(),
1113 formatInternalUnits( aShape->GetLibraryStart() ).c_str(),
1114 formatInternalUnits( aShape->GetLibraryEnd() ).c_str() );
1115
1116 if( aShape->GetCornerRadius() > 0 )
1117 m_out->Print( " (radius %s)", formatInternalUnits( aShape->GetCornerRadius() ).c_str() );
1118 break;
1119
1120 case SHAPE_T::CIRCLE:
1121 m_out->Print( "(%s_circle (center %s) (end %s)", prefix.c_str(),
1122 formatInternalUnits( aShape->GetLibraryStart() ).c_str(),
1123 formatInternalUnits( aShape->GetLibraryEnd() ).c_str() );
1124 break;
1125
1126 case SHAPE_T::ARC:
1127 m_out->Print( "(%s_arc (start %s) (mid %s) (end %s)", prefix.c_str(),
1128 formatInternalUnits( aShape->GetLibraryStart() ).c_str(),
1129 formatInternalUnits( aShape->GetLibraryArcMid() ).c_str(),
1130 formatInternalUnits( aShape->GetLibraryEnd() ).c_str() );
1131 break;
1132
1133 case SHAPE_T::POLY:
1134 if( aShape->IsPolyShapeValid() )
1135 {
1136 const SHAPE_POLY_SET& poly = aShape->GetPolyShape();
1137 const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 );
1138
1139 m_out->Print( "(%s_poly", prefix.c_str() );
1140 formatPolyPts( outline, parentFP );
1141 }
1142 else
1143 {
1144 return;
1145 }
1146
1147 break;
1148
1149 case SHAPE_T::BEZIER:
1150 m_out->Print( "(%s_curve (pts (xy %s) (xy %s) (xy %s) (xy %s))",
1151 prefix.c_str(),
1152 formatInternalUnits( aShape->GetStart(), parentFP ).c_str(),
1153 formatInternalUnits( aShape->GetBezierC1(), parentFP ).c_str(),
1154 formatInternalUnits( aShape->GetBezierC2(), parentFP ).c_str(),
1155 formatInternalUnits( aShape->GetEnd(), parentFP ).c_str() );
1156 break;
1157
1158 case SHAPE_T::ELLIPSE:
1159 m_out->Print( "(%s_ellipse (center %s) (major_radius %s) (minor_radius %s) "
1160 "(rotation_angle %s)",
1161 prefix.c_str(),
1162 formatInternalUnits( aShape->GetLibraryEllipseCenter() ).c_str(),
1166 break;
1167
1169 m_out->Print( "(%s_ellipse_arc (center %s) (major_radius %s) (minor_radius %s) "
1170 "(rotation_angle %s) (start_angle %s) (end_angle %s)",
1171 prefix.c_str(),
1172 formatInternalUnits( aShape->GetLibraryEllipseCenter() ).c_str(),
1178 break;
1179
1180 default:
1182 return;
1183 };
1184
1185 {
1186 STROKE_PARAMS stroke = aShape->GetStroke();
1187 stroke.SetWidth( unbakeLinear( stroke.GetWidth(), parentFP ) );
1188 stroke.Format( m_out, pcbIUScale );
1189 }
1190
1191 // The filled flag represents if a solid fill is present on circles, rectangles and polygons
1192 if( ( aShape->GetShape() == SHAPE_T::POLY ) || ( aShape->GetShape() == SHAPE_T::RECTANGLE )
1193 || ( aShape->GetShape() == SHAPE_T::CIRCLE ) || ( aShape->GetShape() == SHAPE_T::ELLIPSE ) )
1194 {
1195 switch( aShape->GetFillMode() )
1196 {
1197 case FILL_T::HATCH:
1198 m_out->Print( "(fill hatch)" );
1199 break;
1200
1202 m_out->Print( "(fill reverse_hatch)" );
1203 break;
1204
1206 m_out->Print( "(fill cross_hatch)" );
1207 break;
1208
1210 KICAD_FORMAT::FormatBool( m_out, "fill", true );
1211 break;
1212
1213 default:
1214 KICAD_FORMAT::FormatBool( m_out, "fill", false );
1215 break;
1216 }
1217 }
1218
1219 if( aShape->IsLocked() )
1220 KICAD_FORMAT::FormatBool( m_out, "locked", true );
1221
1222 if( aShape->GetLayerSet().count() > 1 )
1223 formatLayers( aShape->GetLayerSet(), false /* enumerate layers */ );
1224 else
1225 formatLayer( aShape->GetLayer() );
1226
1227 if( aShape->HasSolderMask()
1228 && aShape->GetLocalSolderMaskMargin().has_value()
1229 && IsExternalCopperLayer( aShape->GetLayer() ) )
1230 {
1231 m_out->Print( "(solder_mask_margin %s)",
1232 formatInternalUnits( aShape->GetLocalSolderMaskMargin().value() ).c_str() );
1233 }
1234
1235 if( !( m_ctl & CTL_OMIT_PAD_NETS ) && aShape->GetNetCode() > 0 )
1236 m_out->Print( "(net %s)", m_out->Quotew( aShape->GetNetname() ).c_str() );
1237
1239 m_out->Print( ")" );
1240}
1241
1242
1244{
1245 wxCHECK_RET( aBitmap != nullptr && m_out != nullptr, "" );
1246
1247 const REFERENCE_IMAGE& refImage = aBitmap->GetReferenceImage();
1248
1249 const wxImage* image = refImage.GetImage().GetImageData();
1250
1251 wxCHECK_RET( image != nullptr, "wxImage* is NULL" );
1252
1253 m_out->Print( "(image (at %s %s)",
1254 formatInternalUnits( aBitmap->GetPosition().x ).c_str(),
1255 formatInternalUnits( aBitmap->GetPosition().y ).c_str() );
1256
1257 formatLayer( aBitmap->GetLayer() );
1258
1259 if( refImage.GetImageScale() != 1.0 )
1260 m_out->Print( "%s", fmt::format("(scale {:g})", refImage.GetImageScale()).c_str() );
1261
1262 if( aBitmap->IsLocked() )
1263 KICAD_FORMAT::FormatBool( m_out, "locked", true );
1264
1265 wxMemoryOutputStream ostream;
1266 refImage.GetImage().SaveImageData( ostream );
1267
1268 KICAD_FORMAT::FormatStreamData( *m_out, *ostream.GetOutputStreamBuffer() );
1269
1271 m_out->Print( ")" ); // Closes image token.
1272}
1273
1274
1275void PCB_IO_KICAD_SEXPR::format( const PCB_POINT* aPoint ) const
1276{
1277 m_out->Print( "(point (at %s) (size %s)", formatInternalUnits( aPoint->GetLibraryPosition() ).c_str(),
1278 formatInternalUnits( aPoint->GetSize() ).c_str() );
1279
1280 formatLayer( aPoint->GetLayer() );
1281
1283 m_out->Print( ")" );
1284}
1285
1286
1287void PCB_IO_KICAD_SEXPR::format( const PCB_TARGET* aTarget ) const
1288{
1289 m_out->Print( "(target %s (at %s) (size %s)",
1290 ( aTarget->GetShape() ) ? "x" : "plus",
1291 formatInternalUnits( aTarget->GetPosition() ).c_str(),
1292 formatInternalUnits( aTarget->GetSize() ).c_str() );
1293
1294 if( aTarget->GetWidth() != 0 )
1295 m_out->Print( "(width %s)", formatInternalUnits( aTarget->GetWidth() ).c_str() );
1296
1297 formatLayer( aTarget->GetLayer() );
1299 m_out->Print( ")" );
1300}
1301
1302
1303void PCB_IO_KICAD_SEXPR::format( const FOOTPRINT* aFootprint ) const
1304{
1305 if( !( m_ctl & CTL_OMIT_INITIAL_COMMENTS ) )
1306 {
1307 const wxArrayString* initial_comments = aFootprint->GetInitialComments();
1308
1309 if( initial_comments )
1310 {
1311 for( unsigned i = 0; i < initial_comments->GetCount(); ++i )
1312 m_out->Print( "%s\n", TO_UTF8( (*initial_comments)[i] ) );
1313 }
1314 }
1315
1316 if( m_ctl & CTL_OMIT_LIBNAME )
1317 {
1318 m_out->Print( "(footprint %s",
1319 m_out->Quotes( aFootprint->GetFPID().GetLibItemName() ).c_str() );
1320 }
1321 else
1322 {
1323 m_out->Print( "(footprint %s",
1324 m_out->Quotes( aFootprint->GetFPID().Format() ).c_str() );
1325 }
1326
1328 {
1329 m_out->Print( "(version %d) (generator \"pcbnew\") (generator_version %s)",
1331 m_out->Quotew( GetMajorMinorVersion() ).c_str() );
1332 }
1333
1334 if( aFootprint->IsLocked() )
1335 KICAD_FORMAT::FormatBool( m_out, "locked", true );
1336
1337 if( aFootprint->IsPlaced() )
1338 KICAD_FORMAT::FormatBool( m_out, "placed", true );
1339
1340 formatLayer( aFootprint->GetLayer() );
1341
1342 if( !( m_ctl & CTL_OMIT_UUIDS ) )
1343 KICAD_FORMAT::FormatUuid( m_out, aFootprint->m_Uuid );
1344
1345 if( !( m_ctl & CTL_OMIT_AT ) )
1346 {
1347 m_out->Print( "(transform (translate %s) (rotate %s) (scale %s %s))",
1348 formatInternalUnits( aFootprint->GetPosition() ).c_str(),
1349 EDA_UNIT_UTILS::FormatAngle( aFootprint->GetOrientation() ).c_str(),
1350 FormatDouble2Str( aFootprint->GetTransform().GetScaleX() ).c_str(),
1351 FormatDouble2Str( aFootprint->GetTransform().GetScaleY() ).c_str() );
1352 }
1353
1354 if( !aFootprint->GetLibDescription().IsEmpty() )
1355 m_out->Print( "(descr %s)", m_out->Quotew( aFootprint->GetLibDescription() ).c_str() );
1356
1357 if( !aFootprint->GetKeywords().IsEmpty() )
1358 m_out->Print( "(tags %s)", m_out->Quotew( aFootprint->GetKeywords() ).c_str() );
1359
1360 for( const PCB_FIELD* field : aFootprint->GetFields() )
1361 {
1362 if( !field )
1363 continue;
1364
1365 m_out->Print( "(property %s %s",
1366 m_out->Quotew( field->GetCanonicalName() ).c_str(),
1367 m_out->Quotew( field->GetText() ).c_str() );
1368
1369 format( field );
1370
1371 m_out->Print( ")" );
1372 }
1373
1374 if( const COMPONENT_CLASS* compClass = aFootprint->GetStaticComponentClass() )
1375 {
1376 if( !compClass->IsEmpty() )
1377 {
1378 m_out->Print( "(component_classes" );
1379
1380 for( const COMPONENT_CLASS* constituent : compClass->GetConstituentClasses() )
1381 m_out->Print( "(class %s)", m_out->Quotew( constituent->GetName() ).c_str() );
1382
1383 m_out->Print( ")" );
1384 }
1385 }
1386
1387 if( !aFootprint->GetFilters().empty() )
1388 {
1389 m_out->Print( "(property ki_fp_filters %s)",
1390 m_out->Quotew( aFootprint->GetFilters() ).c_str() );
1391 }
1392
1393 if( !( m_ctl & CTL_OMIT_PATH ) && !aFootprint->GetPath().empty() )
1394 m_out->Print( "(path %s)", m_out->Quotew( aFootprint->GetPath().AsString() ).c_str() );
1395
1396 if( !aFootprint->GetSheetname().empty() )
1397 m_out->Print( "(sheetname %s)", m_out->Quotew( aFootprint->GetSheetname() ).c_str() );
1398
1399 if( !aFootprint->GetSheetfile().empty() )
1400 m_out->Print( "(sheetfile %s)", m_out->Quotew( aFootprint->GetSheetfile() ).c_str() );
1401
1402 // Emit unit info for gate swapping metadata (flat pin list form)
1403 if( !aFootprint->GetUnitInfo().empty() )
1404 {
1405 m_out->Print( "(units" );
1406
1407 for( const FOOTPRINT::FP_UNIT_INFO& u : aFootprint->GetUnitInfo() )
1408 {
1409 m_out->Print( "(unit (name %s)", m_out->Quotew( u.m_unitName ).c_str() );
1410 m_out->Print( "(pins" );
1411
1412 for( const wxString& n : u.m_pins )
1413 m_out->Print( " %s", m_out->Quotew( n ).c_str() );
1414
1415 m_out->Print( ")" ); // </pins>
1416 m_out->Print( ")" ); // </unit>
1417 }
1418
1419 m_out->Print( ")" ); // </units>
1420 }
1421
1422 if( aFootprint->GetLocalSolderMaskMargin().has_value() )
1423 {
1424 m_out->Print( "(solder_mask_margin %s)",
1425 formatInternalUnits( aFootprint->GetLocalSolderMaskMargin().value() ).c_str() );
1426 }
1427
1428 if( aFootprint->GetLocalSolderPasteMargin().has_value() )
1429 {
1430 m_out->Print( "(solder_paste_margin %s)",
1431 formatInternalUnits( aFootprint->GetLocalSolderPasteMargin().value() ).c_str() );
1432 }
1433
1434 if( aFootprint->GetLocalSolderPasteMarginRatio().has_value() )
1435 {
1436 m_out->Print( "(solder_paste_margin_ratio %s)",
1437 FormatDouble2Str( aFootprint->GetLocalSolderPasteMarginRatio().value() ).c_str() );
1438 }
1439
1440 if( aFootprint->GetLocalClearance().has_value() )
1441 {
1442 m_out->Print( "(clearance %s)",
1443 formatInternalUnits( aFootprint->GetLocalClearance().value() ).c_str() );
1444 }
1445
1447 {
1448 m_out->Print( "(zone_connect %d)",
1449 static_cast<int>( aFootprint->GetLocalZoneConnection() ) );
1450 }
1451
1452 // Attributes
1453 if( aFootprint->GetAttributes()
1454 || aFootprint->AllowMissingCourtyard()
1455 || aFootprint->AllowSolderMaskBridges() )
1456 {
1457 m_out->Print( "(attr" );
1458
1459 if( aFootprint->GetAttributes() & FP_SMD )
1460 m_out->Print( " smd" );
1461
1462 if( aFootprint->GetAttributes() & FP_THROUGH_HOLE )
1463 m_out->Print( " through_hole" );
1464
1465 if( aFootprint->GetAttributes() & FP_BOARD_ONLY )
1466 m_out->Print( " board_only" );
1467
1468 if( aFootprint->GetAttributes() & FP_EXCLUDE_FROM_POS_FILES )
1469 m_out->Print( " exclude_from_pos_files" );
1470
1471 if( aFootprint->GetAttributes() & FP_EXCLUDE_FROM_BOM )
1472 m_out->Print( " exclude_from_bom" );
1473
1474 if( aFootprint->AllowMissingCourtyard() )
1475 m_out->Print( " allow_missing_courtyard" );
1476
1477 if( aFootprint->GetAttributes() & FP_DNP )
1478 m_out->Print( " dnp" );
1479
1480 if( aFootprint->AllowSolderMaskBridges() )
1481 m_out->Print( " allow_soldermask_bridges" );
1482
1483 m_out->Print( ")" );
1484 }
1485
1486 // Expand inner layers is the default stackup mode
1488 {
1489 m_out->Print( "(stackup" );
1490
1491 const LSET& fpLset = aFootprint->GetStackupLayers();
1492 for( PCB_LAYER_ID layer : fpLset.Seq() )
1493 {
1494 wxString canonicalName( LSET::Name( layer ) );
1495 m_out->Print( "(layer %s)", m_out->Quotew( canonicalName ).c_str() );
1496 }
1497
1498 m_out->Print( ")" );
1499 }
1500
1501 if( aFootprint->GetPrivateLayers().any() )
1502 {
1503 m_out->Print( "(private_layers" );
1504
1505 for( PCB_LAYER_ID layer : aFootprint->GetPrivateLayers().Seq() )
1506 {
1507 wxString canonicalName( LSET::Name( layer ) );
1508 m_out->Print( " %s", m_out->Quotew( canonicalName ).c_str() );
1509 }
1510
1511 m_out->Print( ")" );
1512 }
1513
1514 if( aFootprint->IsNetTie() )
1515 {
1516 m_out->Print( "(net_tie_pad_groups" );
1517
1518 for( const wxString& group : aFootprint->GetNetTiePadGroups() )
1519 m_out->Print( " %s", m_out->Quotew( group ).c_str() );
1520
1521 m_out->Print( ")" );
1522 }
1523
1524 KICAD_FORMAT::FormatBool( m_out, "duplicate_pad_numbers_are_jumpers",
1525 aFootprint->GetDuplicatePadNumbersAreJumpers() );
1526
1527 const std::vector<std::set<wxString>>& jumperGroups = aFootprint->JumperPadGroups();
1528
1529 if( !jumperGroups.empty() )
1530 {
1531 m_out->Print( "(jumper_pad_groups" );
1532
1533 for( const std::set<wxString>& group : jumperGroups )
1534 {
1535 m_out->Print( "(" );
1536
1537 for( const wxString& padName : group )
1538 m_out->Print( "%s ", m_out->Quotew( padName ).c_str() );
1539
1540 m_out->Print( ")" );
1541 }
1542
1543 m_out->Print( ")" );
1544 }
1545
1546 Format( &aFootprint->Reference() );
1547 Format( &aFootprint->Value() );
1548
1549 std::set<PAD*, FOOTPRINT::cmp_pads> sorted_pads( aFootprint->Pads().begin(),
1550 aFootprint->Pads().end() );
1551 std::set<BOARD_ITEM*, FOOTPRINT::cmp_drawings> sorted_drawings(
1552 aFootprint->GraphicalItems().begin(),
1553 aFootprint->GraphicalItems().end() );
1554 std::set<PCB_POINT*, PCB_POINT::cmp_points> sorted_points(
1555 aFootprint->Points().begin(),
1556 aFootprint->Points().end() );
1557 std::set<ZONE*, FOOTPRINT::cmp_zones> sorted_zones( aFootprint->Zones().begin(),
1558 aFootprint->Zones().end() );
1559 std::set<BOARD_ITEM*, PCB_GROUP::ptr_cmp> sorted_groups( aFootprint->Groups().begin(),
1560 aFootprint->Groups().end() );
1561 std::set<BOARD_ITEM*, PCB_GROUP::ptr_cmp> sorted_constraints( aFootprint->Constraints().begin(),
1562 aFootprint->Constraints().end() );
1563
1564 // Save drawing elements.
1565
1566 for( BOARD_ITEM* gr : sorted_drawings )
1567 Format( gr );
1568
1569 for( PCB_POINT* point : sorted_points )
1570 Format( point );
1571
1572 // Save pads.
1573 for( PAD* pad : sorted_pads )
1574 Format( pad );
1575
1576 // Save zones.
1577 for( BOARD_ITEM* zone : sorted_zones )
1578 Format( zone );
1579
1580 // Save groups.
1581 for( BOARD_ITEM* group : sorted_groups )
1582 Format( group );
1583
1584 // Save geometric constraints, after the items they reference.
1585 for( BOARD_ITEM* constraint : sorted_constraints )
1586 Format( constraint );
1587
1588 // Save variants.
1589 const bool baseDnp = aFootprint->IsDNP();
1590 const bool baseExcludedFromBOM = aFootprint->IsExcludedFromBOM();
1591 const bool baseExcludedFromPosFiles = aFootprint->IsExcludedFromPosFiles();
1592
1593 for( const auto& [variantName, variant] : aFootprint->GetVariants() )
1594 {
1595 m_out->Print( "(variant (name %s)", m_out->Quotew( variantName ).c_str() );
1596
1597 if( variant.GetDNP() != baseDnp )
1598 KICAD_FORMAT::FormatBool( m_out, "dnp", variant.GetDNP() );
1599
1600 if( variant.GetExcludedFromBOM() != baseExcludedFromBOM )
1601 KICAD_FORMAT::FormatBool( m_out, "exclude_from_bom", variant.GetExcludedFromBOM() );
1602
1603 if( variant.GetExcludedFromPosFiles() != baseExcludedFromPosFiles )
1604 {
1605 KICAD_FORMAT::FormatBool( m_out, "exclude_from_pos_files",
1606 variant.GetExcludedFromPosFiles() );
1607 }
1608
1609 for( const auto& [fieldName, fieldValue] : variant.GetFields() )
1610 {
1611 const PCB_FIELD* baseField = aFootprint->GetField( fieldName );
1612 const wxString baseValue = baseField ? baseField->GetText() : wxString();
1613
1614 if( fieldValue == baseValue )
1615 continue;
1616
1617 m_out->Print( "(field (name %s) (value %s))",
1618 m_out->Quotew( fieldName ).c_str(),
1619 m_out->Quotew( fieldValue ).c_str() );
1620 }
1621
1622 m_out->Print( ")" );
1623 }
1624
1625 KICAD_FORMAT::FormatBool( m_out, "embedded_fonts",
1626 aFootprint->GetEmbeddedFiles()->GetAreFontsEmbedded() );
1627
1628 if( !aFootprint->GetEmbeddedFiles()->IsEmpty() )
1629 aFootprint->WriteEmbeddedFiles( *m_out, !( m_ctl & CTL_FOR_BOARD ) );
1630
1631 // Save extruded 3D body info.
1632 if( const EXTRUDED_3D_BODY* body = aFootprint->GetExtrudedBody(); body && body->m_height > 0 )
1633 {
1634 m_out->Print( "(model" );
1635 m_out->Print( "(type extruded)" );
1636 KICAD_FORMAT::FormatBool( m_out, "hide", !body->m_show );
1637 m_out->Print( "(overall_height %s)", formatInternalUnits( body->m_height ).c_str() );
1638 m_out->Print( "(body_pcb_gap %s)", formatInternalUnits( body->m_standoff ).c_str() );
1639
1640 if( body->m_layer == UNSELECTED_LAYER )
1641 m_out->Print( "(layer pad_bbox)" );
1642 else if( body->m_layer != UNDEFINED_LAYER )
1643 m_out->Print( "(layer %s)", m_out->Quotew( LSET::Name( body->m_layer ) ).c_str() );
1644 else
1645 m_out->Print( "(layer auto)" );
1646
1647 {
1648 static const char* matNames[] = { "plastic", "matte", "metal", "copper" };
1649 m_out->Print( "(material %s)", matNames[static_cast<int>( body->m_material )] );
1650 }
1651
1652 if( body->m_color != KIGFX::COLOR4D::UNSPECIFIED )
1653 {
1654 m_out->Print( "(color %s %s %s %s)", FormatDouble2Str( body->m_color.r ).c_str(),
1655 FormatDouble2Str( body->m_color.g ).c_str(), FormatDouble2Str( body->m_color.b ).c_str(),
1656 FormatDouble2Str( body->m_color.a ).c_str() );
1657 }
1658 else
1659 {
1660 m_out->Print( "(color unspecified)" );
1661 }
1662
1663 m_out->Print( "(offset (xyz %s %s %s))", FormatDouble2Str( body->m_offset.x ).c_str(),
1664 FormatDouble2Str( body->m_offset.y ).c_str(), FormatDouble2Str( body->m_offset.z ).c_str() );
1665
1666 m_out->Print( "(scale (xyz %s %s %s))", FormatDouble2Str( body->m_scale.x ).c_str(),
1667 FormatDouble2Str( body->m_scale.y ).c_str(), FormatDouble2Str( body->m_scale.z ).c_str() );
1668
1669 m_out->Print( "(rotate (xyz %s %s %s))", FormatDouble2Str( body->m_rotation.x ).c_str(),
1670 FormatDouble2Str( body->m_rotation.y ).c_str(), FormatDouble2Str( body->m_rotation.z ).c_str() );
1671
1672 m_out->Print( ")" );
1673 }
1674
1675 // Save 3D info.
1676 auto bs3D = aFootprint->Models().begin();
1677 auto es3D = aFootprint->Models().end();
1678
1679 while( bs3D != es3D )
1680 {
1681 if( !bs3D->m_Filename.IsEmpty() )
1682 {
1683 m_out->Print( "(model %s", m_out->Quotew( bs3D->m_Filename ).c_str() );
1684
1685 if( !bs3D->m_Show )
1686 KICAD_FORMAT::FormatBool( m_out, "hide", !bs3D->m_Show );
1687
1688 if( bs3D->m_Opacity != 1.0 )
1689 m_out->Print( "%s", fmt::format("(opacity {:.4f})", bs3D->m_Opacity).c_str() );
1690
1691 m_out->Print( "(offset (xyz %s %s %s))",
1692 FormatDouble2Str( bs3D->m_Offset.x ).c_str(),
1693 FormatDouble2Str( bs3D->m_Offset.y ).c_str(),
1694 FormatDouble2Str( bs3D->m_Offset.z ).c_str() );
1695
1696 m_out->Print( "(scale (xyz %s %s %s))",
1697 FormatDouble2Str( bs3D->m_Scale.x ).c_str(),
1698 FormatDouble2Str( bs3D->m_Scale.y ).c_str(),
1699 FormatDouble2Str( bs3D->m_Scale.z ).c_str() );
1700
1701 m_out->Print( "(rotate (xyz %s %s %s))",
1702 FormatDouble2Str( bs3D->m_Rotation.x ).c_str(),
1703 FormatDouble2Str( bs3D->m_Rotation.y ).c_str(),
1704 FormatDouble2Str( bs3D->m_Rotation.z ).c_str() );
1705
1706 m_out->Print( ")" );
1707 }
1708
1709 ++bs3D;
1710 }
1711
1712 m_out->Print( ")" );
1713}
1714
1715
1716void PCB_IO_KICAD_SEXPR::formatLayers( LSET aLayerMask, bool aEnumerateLayers, bool aIsZone ) const
1717{
1718 static const LSET cu_all( LSET::AllCuMask() );
1719 static const LSET fr_bk( { B_Cu, F_Cu } );
1720 static const LSET adhes( { B_Adhes, F_Adhes } );
1721 static const LSET paste( { B_Paste, F_Paste } );
1722 static const LSET silks( { B_SilkS, F_SilkS } );
1723 static const LSET mask( { B_Mask, F_Mask } );
1724 static const LSET crt_yd( { B_CrtYd, F_CrtYd } );
1725 static const LSET fab( { B_Fab, F_Fab } );
1726
1727 LSET cu_board_mask = LSET::AllCuMask( m_board ? m_board->GetCopperLayerCount() : MAX_CU_LAYERS );
1728
1729 std::string output;
1730
1731 if( !aEnumerateLayers )
1732 {
1733 // If all copper layers present on the board are enabled, then output the wildcard
1734 if( ( aLayerMask & cu_board_mask ) == cu_board_mask )
1735 {
1736 output += ' ' + m_out->Quotew( "*.Cu" );
1737
1738 // Clear all copper bits because pads might have internal layers that aren't part of the
1739 // board enabled, and we don't want to output those in the layers listing if we already
1740 // output the wildcard.
1741 aLayerMask &= ~cu_all;
1742 }
1743 else if( ( aLayerMask & cu_board_mask ) == fr_bk )
1744 {
1745 if( aIsZone )
1746 output += ' ' + m_out->Quotew( "F&B.Cu" );
1747 else
1748 output += ' ' + m_out->Quotew( "*.Cu" );
1749
1750 aLayerMask &= ~fr_bk;
1751 }
1752
1753 if( ( aLayerMask & adhes ) == adhes )
1754 {
1755 output += ' ' + m_out->Quotew( "*.Adhes" );
1756 aLayerMask &= ~adhes;
1757 }
1758
1759 if( ( aLayerMask & paste ) == paste )
1760 {
1761 output += ' ' + m_out->Quotew( "*.Paste" );
1762 aLayerMask &= ~paste;
1763 }
1764
1765 if( ( aLayerMask & silks ) == silks )
1766 {
1767 output += ' ' + m_out->Quotew( "*.SilkS" );
1768 aLayerMask &= ~silks;
1769 }
1770
1771 if( ( aLayerMask & mask ) == mask )
1772 {
1773 output += ' ' + m_out->Quotew( "*.Mask" );
1774 aLayerMask &= ~mask;
1775 }
1776
1777 if( ( aLayerMask & crt_yd ) == crt_yd )
1778 {
1779 output += ' ' + m_out->Quotew( "*.CrtYd" );
1780 aLayerMask &= ~crt_yd;
1781 }
1782
1783 if( ( aLayerMask & fab ) == fab )
1784 {
1785 output += ' ' + m_out->Quotew( "*.Fab" );
1786 aLayerMask &= ~fab;
1787 }
1788 }
1789
1790 // output any individual layers not handled in wildcard combos above
1791 for( int layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
1792 {
1793 if( aLayerMask[layer] )
1794 output += ' ' + m_out->Quotew( LSET::Name( PCB_LAYER_ID( layer ) ) );
1795 }
1796
1797 m_out->Print( "(layers %s)", output.c_str() );
1798}
1799
1800
1801void PCB_IO_KICAD_SEXPR::format( const PAD* aPad ) const
1802{
1803 const BOARD* board = aPad->GetBoard();
1804 const FOOTPRINT* parentFP = aPad->GetParentFootprint();
1805
1806 auto shapeName =
1807 [&]( PCB_LAYER_ID aLayer )
1808 {
1809 switch( aPad->GetShape( aLayer ) )
1810 {
1811 case PAD_SHAPE::CIRCLE: return "circle";
1812 case PAD_SHAPE::RECTANGLE: return "rect";
1813 case PAD_SHAPE::OVAL: return "oval";
1814 case PAD_SHAPE::TRAPEZOID: return "trapezoid";
1816 case PAD_SHAPE::ROUNDRECT: return "roundrect";
1817 case PAD_SHAPE::CUSTOM: return "custom";
1818
1819 default:
1820 THROW_IO_ERRORF( _( "unknown pad type: %d" ), aPad->GetShape( aLayer ) );
1821 }
1822 };
1823
1824 const char* type;
1825
1826 switch( aPad->GetAttribute() )
1827 {
1828 case PAD_ATTRIB::PTH: type = "thru_hole"; break;
1829 case PAD_ATTRIB::SMD: type = "smd"; break;
1830 case PAD_ATTRIB::CONN: type = "connect"; break;
1831 case PAD_ATTRIB::NPTH: type = "np_thru_hole"; break;
1832
1833 default:
1834 THROW_IO_ERRORF( _( "unknown pad attribute: %d" ), aPad->GetAttribute() );
1835 }
1836
1837 const char* property = nullptr;
1838
1839 switch( aPad->GetProperty() )
1840 {
1841 case PAD_PROP::NONE: break; // could be "none"
1842 case PAD_PROP::BGA: property = "pad_prop_bga"; break;
1843 case PAD_PROP::FIDUCIAL_GLBL: property = "pad_prop_fiducial_glob"; break;
1844 case PAD_PROP::FIDUCIAL_LOCAL: property = "pad_prop_fiducial_loc"; break;
1845 case PAD_PROP::TESTPOINT: property = "pad_prop_testpoint"; break;
1846 case PAD_PROP::HEATSINK: property = "pad_prop_heatsink"; break;
1847 case PAD_PROP::CASTELLATED: property = "pad_prop_castellated"; break;
1848 case PAD_PROP::MECHANICAL: property = "pad_prop_mechanical"; break;
1849 case PAD_PROP::PRESSFIT: property = "pad_prop_pressfit"; break;
1850
1851 default:
1852 THROW_IO_ERRORF( _( "unknown pad property: %d" ), aPad->GetProperty() );
1853 }
1854
1855 const char* simElectricalType = nullptr;
1856
1857 switch( aPad->GetSimElectricalType() )
1858 {
1859 case PAD_SIM_ELECTRICAL_TYPE::SOURCE: simElectricalType = "source"; break;
1860 case PAD_SIM_ELECTRICAL_TYPE::SINK: simElectricalType = "sink"; break;
1861 default: simElectricalType = nullptr; break;
1862 }
1863
1864 m_out->Print( "(pad %s %s %s",
1865 m_out->Quotew( aPad->GetNumber() ).c_str(),
1866 type,
1867 shapeName( PADSTACK::ALL_LAYERS ) );
1868
1869 m_out->Print( "(at %s %s)",
1870 formatInternalUnits( aPad->GetFPRelativePosition() ).c_str(),
1871 aPad->GetOrientation().IsZero()
1872 ? ""
1873 : EDA_UNIT_UTILS::FormatAngle( aPad->GetOrientation() ).c_str() );
1874
1875 // Write the stored library size directly: it is the footprint-frame value the parser
1876 // reads back, and avoids a bake/unbake that is not the inverse of GetSize() for a
1877 // pad rotated within the footprint.
1878 m_out->Print( "(size %s)", formatInternalUnits( aPad->Padstack().Size( PADSTACK::ALL_LAYERS ) ).c_str() );
1879
1880 if( aPad->GetDelta( PADSTACK::ALL_LAYERS ).x != 0
1881 || aPad->GetDelta( PADSTACK::ALL_LAYERS ).y != 0 )
1882 {
1883 m_out->Print( "(rect_delta %s)", formatInternalUnits( aPad->GetDelta( PADSTACK::ALL_LAYERS ) ).c_str() );
1884 }
1885
1886 const VECTOR2I drill = aPad->GetDrillShape() == PAD_DRILL_SHAPE::CIRCLE
1887 ? unbakeSizeUniform( aPad->GetDrillSize(), parentFP )
1888 : unbakeSize( aPad->GetDrillSize(), parentFP );
1889 VECTOR2I shapeoffset = aPad->GetOffset( PADSTACK::ALL_LAYERS );
1890 bool forceShapeOffsetOutput = false;
1891
1893 [&]( PCB_LAYER_ID layer )
1894 {
1895 if( aPad->GetOffset( layer ) != shapeoffset )
1896 forceShapeOffsetOutput = true;
1897 } );
1898
1899 if( drill.x > 0 || drill.y > 0 || shapeoffset.x != 0 || shapeoffset.y != 0 || forceShapeOffsetOutput )
1900 {
1901 m_out->Print( "(drill" );
1902
1903 if( aPad->GetDrillShape() == PAD_DRILL_SHAPE::OBLONG )
1904 m_out->Print( " oval" );
1905
1906 if( drill.x > 0 )
1907 m_out->Print( " %s", formatInternalUnits( drill.x ).c_str() );
1908
1909 if( drill.y > 0 && drill.x != drill.y )
1910 m_out->Print( " %s", formatInternalUnits( drill.y ).c_str() );
1911
1912 // NOTE: Shape offest is a property of the copper shape, not of the drill, but this was put
1913 // in the file format under the drill section. So, it is left here to minimize file format
1914 // changes, but note that the other padstack layers (if present) will have an offset stored
1915 // separately.
1916 if( shapeoffset.x != 0 || shapeoffset.y != 0 || forceShapeOffsetOutput )
1917 m_out->Print( "(offset %s)",
1919
1920 m_out->Print( ")" );
1921 }
1922
1923 if( aPad->Padstack().SecondaryDrill().size.x > 0 )
1924 {
1925 m_out->Print( "(backdrill (size %s) (layers %s %s))",
1926 formatInternalUnits( aPad->Padstack().SecondaryDrill().size.x ).c_str(),
1927 m_out->Quotew( LSET::Name( aPad->Padstack().SecondaryDrill().start ) ).c_str(),
1928 m_out->Quotew( LSET::Name( aPad->Padstack().SecondaryDrill().end ) ).c_str() );
1929 }
1930
1931 if( aPad->Padstack().TertiaryDrill().size.x > 0 )
1932 {
1933 m_out->Print( "(tertiary_drill (size %s) (layers %s %s))",
1934 formatInternalUnits( aPad->Padstack().TertiaryDrill().size.x ).c_str(),
1935 m_out->Quotew( LSET::Name( aPad->Padstack().TertiaryDrill().start ) ).c_str(),
1936 m_out->Quotew( LSET::Name( aPad->Padstack().TertiaryDrill().end ) ).c_str() );
1937 }
1938
1939 auto formatPostMachining =
1940 [&]( const char* aName, const PADSTACK::POST_MACHINING_PROPS& aProps )
1941 {
1942 if( !aProps.mode.has_value() || aProps.mode == PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED )
1943 return;
1944
1945 m_out->Print( "(%s %s",
1946 aName,
1947 aProps.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE ? "counterbore"
1948 : "countersink" );
1949
1950 if( aProps.size > 0 )
1951 m_out->Print( " (size %s)", formatInternalUnits( aProps.size ).c_str() );
1952
1953 if( aProps.depth > 0 )
1954 m_out->Print( " (depth %s)", formatInternalUnits( aProps.depth ).c_str() );
1955
1956 if( aProps.angle > 0 )
1957 m_out->Print( " (angle %s)", FormatDouble2Str( aProps.angle / 10.0 ).c_str() );
1958
1959 m_out->Print( ")" );
1960 };
1961
1962 formatPostMachining( "front_post_machining", aPad->Padstack().FrontPostMachining() );
1963 formatPostMachining( "back_post_machining", aPad->Padstack().BackPostMachining() );
1964
1965 // Add pad property, if exists.
1966 if( property )
1967 m_out->Print( "(property %s)", property );
1968
1969 if( simElectricalType )
1970 m_out->Print( "(sim_electrical_type %s)", simElectricalType );
1971
1972 formatLayers( aPad->GetLayerSet(), false /* enumerate layers */ );
1973
1974 if( aPad->GetAttribute() == PAD_ATTRIB::PTH )
1975 {
1976 KICAD_FORMAT::FormatBool( m_out, "remove_unused_layers", aPad->GetRemoveUnconnected() );
1977
1978 if( aPad->GetRemoveUnconnected() )
1979 {
1980 KICAD_FORMAT::FormatBool( m_out, "keep_end_layers", aPad->GetKeepTopBottom() );
1981
1982 if( board ) // Will be nullptr in footprint library
1983 {
1984 m_out->Print( "(zone_layer_connections" );
1985
1986 for( PCB_LAYER_ID layer : board->GetEnabledLayers().CuStack() )
1987 {
1988 if( aPad->GetZoneLayerOverride( layer ) == ZLO_FORCE_FLASHED )
1989 m_out->Print( " %s", m_out->Quotew( LSET::Name( layer ) ).c_str() );
1990 }
1991
1992 m_out->Print( ")" );
1993 }
1994 }
1995 }
1996
1997 auto formatCornerProperties =
1998 [&]( PCB_LAYER_ID aLayer )
1999 {
2000 // Output the radius ratio for rounded and chamfered rect pads
2001 if( aPad->GetShape( aLayer ) == PAD_SHAPE::ROUNDRECT
2002 || aPad->GetShape( aLayer ) == PAD_SHAPE::CHAMFERED_RECT)
2003 {
2004 m_out->Print( "(roundrect_rratio %s)",
2005 FormatDouble2Str( aPad->GetRoundRectRadiusRatio( aLayer ) ).c_str() );
2006 }
2007
2008 // Output the chamfer corners for chamfered rect pads
2009 if( aPad->GetShape( aLayer ) == PAD_SHAPE::CHAMFERED_RECT)
2010 {
2011 m_out->Print( "(chamfer_ratio %s)",
2012 FormatDouble2Str( aPad->GetChamferRectRatio( aLayer ) ).c_str() );
2013
2014 m_out->Print( "(chamfer" );
2015
2016 if( ( aPad->GetChamferPositions( aLayer ) & RECT_CHAMFER_TOP_LEFT ) )
2017 m_out->Print( " top_left" );
2018
2019 if( ( aPad->GetChamferPositions( aLayer ) & RECT_CHAMFER_TOP_RIGHT ) )
2020 m_out->Print( " top_right" );
2021
2022 if( ( aPad->GetChamferPositions( aLayer ) & RECT_CHAMFER_BOTTOM_LEFT ) )
2023 m_out->Print( " bottom_left" );
2024
2025 if( ( aPad->GetChamferPositions( aLayer ) & RECT_CHAMFER_BOTTOM_RIGHT ) )
2026 m_out->Print( " bottom_right" );
2027
2028 m_out->Print( ")" );
2029 }
2030
2031 };
2032
2033 // For normal padstacks, this is the one and only set of properties. For complex ones, this
2034 // will represent the front layer properties, and other layers will be formatted below
2035 formatCornerProperties( PADSTACK::ALL_LAYERS );
2036
2037 // Unconnected pad is default net so don't save it.
2038 if( !( m_ctl & CTL_OMIT_PAD_NETS ) && aPad->GetNetCode() > 0 )
2039 m_out->Print( "(net %s)", m_out->Quotew( aPad->GetNetname() ).c_str() );
2040
2041 // Pin functions and types are closely related to nets, so if CTL_OMIT_NETS is set, omit
2042 // them as well (for instance when saved from library editor).
2043 if( !( m_ctl & CTL_OMIT_PAD_NETS ) )
2044 {
2045 if( !aPad->GetPinFunction().IsEmpty() )
2046 m_out->Print( "(pinfunction %s)", m_out->Quotew( aPad->GetPinFunction() ).c_str() );
2047
2048 if( !aPad->GetPinType().IsEmpty() )
2049 m_out->Print( "(pintype %s)", m_out->Quotew( aPad->GetPinType() ).c_str() );
2050 }
2051
2052 if( aPad->GetPadToDieLength() != 0 )
2053 {
2054 m_out->Print( "(die_length %s)",
2055 formatInternalUnits( aPad->GetPadToDieLength() ).c_str() );
2056 }
2057
2058 if( aPad->GetPadToDieDelay() != 0 )
2059 {
2060 m_out->Print( "(die_delay %s)",
2062 }
2063
2064 if( aPad->GetLocalSolderMaskMargin().has_value() )
2065 {
2066 m_out->Print( "(solder_mask_margin %s)",
2067 formatInternalUnits( aPad->GetLocalSolderMaskMargin().value() ).c_str() );
2068 }
2069
2070 if( aPad->GetLocalSolderPasteMargin().has_value() )
2071 {
2072 m_out->Print( "(solder_paste_margin %s)",
2073 formatInternalUnits( aPad->GetLocalSolderPasteMargin().value() ).c_str() );
2074 }
2075
2076 if( aPad->GetLocalSolderPasteMarginRatio().has_value() )
2077 {
2078 m_out->Print( "(solder_paste_margin_ratio %s)",
2079 FormatDouble2Str( aPad->GetLocalSolderPasteMarginRatio().value() ).c_str() );
2080 }
2081
2082 if( aPad->GetLocalClearance().has_value() )
2083 {
2084 m_out->Print( "(clearance %s)",
2085 formatInternalUnits( aPad->GetLocalClearance().value() ).c_str() );
2086 }
2087
2089 {
2090 m_out->Print( "(zone_connect %d)",
2091 static_cast<int>( aPad->GetLocalZoneConnection() ) );
2092 }
2093
2094 if( aPad->GetLocalThermalSpokeWidthOverride().has_value() )
2095 {
2096 m_out->Print( "(thermal_bridge_width %s)",
2097 formatInternalUnits( aPad->GetLocalThermalSpokeWidthOverride().value() ).c_str() );
2098 }
2099
2100 EDA_ANGLE defaultThermalSpokeAngle = ANGLE_90;
2101
2105 {
2106 defaultThermalSpokeAngle = ANGLE_45;
2107 }
2108
2109 if( aPad->GetThermalSpokeAngle() != defaultThermalSpokeAngle )
2110 {
2111 m_out->Print( "(thermal_bridge_angle %s)",
2113 }
2114
2115 if( aPad->GetLocalThermalGapOverride().has_value() )
2116 {
2117 m_out->Print( "(thermal_gap %s)",
2118 formatInternalUnits( aPad->GetLocalThermalGapOverride().value() ).c_str() );
2119 }
2120
2121 auto anchorShape =
2122 [&]( PCB_LAYER_ID aLayer )
2123 {
2124 switch( aPad->GetAnchorPadShape( aLayer ) )
2125 {
2126 case PAD_SHAPE::RECTANGLE: return "rect";
2127 default:
2128 case PAD_SHAPE::CIRCLE: return "circle";
2129 }
2130 };
2131
2132 auto formatPrimitives =
2133 [&]( PCB_LAYER_ID aLayer )
2134 {
2135 m_out->Print( "(primitives" );
2136
2137 // Output all basic shapes
2138 for( const std::shared_ptr<PCB_SHAPE>& primitive : aPad->GetPrimitives( aLayer ) )
2139 {
2140 const SHAPE_T libShape = primitive->GetLibraryShape();
2141
2142 switch( libShape )
2143 {
2144 case SHAPE_T::SEGMENT:
2145 if( primitive->IsProxyItem() )
2146 {
2147 m_out->Print( "(gr_vector (start %s) (end %s)",
2148 formatInternalUnits( primitive->GetStart() ).c_str(),
2149 formatInternalUnits( primitive->GetEnd() ).c_str() );
2150 }
2151 else
2152 {
2153 m_out->Print( "(gr_line (start %s) (end %s)",
2154 formatInternalUnits( primitive->GetStart() ).c_str(),
2155 formatInternalUnits( primitive->GetEnd() ).c_str() );
2156 }
2157 break;
2158
2159 case SHAPE_T::RECTANGLE:
2160 if( primitive->IsProxyItem() )
2161 {
2162 m_out->Print( "(gr_bbox (start %s) (end %s)",
2163 formatInternalUnits( primitive->GetLibraryStart() ).c_str(),
2164 formatInternalUnits( primitive->GetLibraryEnd() ).c_str() );
2165 }
2166 else
2167 {
2168 m_out->Print( "(gr_rect (start %s) (end %s)",
2169 formatInternalUnits( primitive->GetLibraryStart() ).c_str(),
2170 formatInternalUnits( primitive->GetLibraryEnd() ).c_str() );
2171
2172 if( primitive->GetCornerRadius() > 0 )
2173 {
2174 m_out->Print( " (radius %s)",
2175 formatInternalUnits( primitive->GetCornerRadius() ).c_str() );
2176 }
2177 }
2178 break;
2179
2180 case SHAPE_T::ARC:
2181 m_out->Print( "(gr_arc (start %s) (mid %s) (end %s)",
2182 formatInternalUnits( primitive->GetLibraryStart() ).c_str(),
2183 formatInternalUnits( primitive->GetLibraryArcMid() ).c_str(),
2184 formatInternalUnits( primitive->GetLibraryEnd() ).c_str() );
2185 break;
2186
2187 case SHAPE_T::CIRCLE:
2188 m_out->Print( "(gr_circle (center %s) (end %s)",
2189 formatInternalUnits( primitive->GetLibraryStart() ).c_str(),
2190 formatInternalUnits( primitive->GetLibraryEnd() ).c_str() );
2191 break;
2192
2193 case SHAPE_T::BEZIER:
2194 // Pad primitives are stored in raw library coordinates and read back
2195 // raw, so emit library coordinates like the other primitive types above.
2196 m_out->Print( "(gr_curve (pts (xy %s) (xy %s) (xy %s) (xy %s))",
2197 formatInternalUnits( primitive->GetLibraryStart() ).c_str(),
2198 formatInternalUnits( primitive->GetLibraryBezierC1() ).c_str(),
2199 formatInternalUnits( primitive->GetLibraryBezierC2() ).c_str(),
2200 formatInternalUnits( primitive->GetLibraryEnd() ).c_str() );
2201 break;
2202
2203 case SHAPE_T::POLY:
2204 if( primitive->IsPolyShapeValid() )
2205 {
2206 const SHAPE_POLY_SET poly = primitive->GetLibraryPolyShape();
2207 const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 );
2208
2209 m_out->Print( "(gr_poly" );
2210 formatPolyPts( outline );
2211 }
2212 break;
2213
2214 default:
2215 break;
2216 }
2217
2218 if( !primitive->IsProxyItem() )
2219 m_out->Print( "(width %s)", formatInternalUnits( primitive->GetWidth() ).c_str() );
2220
2221 // The filled flag represents if a solid fill is present on circles,
2222 // rectangles and polygons
2223 if( libShape == SHAPE_T::POLY || libShape == SHAPE_T::RECTANGLE || libShape == SHAPE_T::CIRCLE )
2224 {
2225 KICAD_FORMAT::FormatBool( m_out, "fill", primitive->IsSolidFill() );
2226 }
2227
2228 m_out->Print( ")" );
2229 }
2230
2231 m_out->Print( ")" ); // end of (primitives
2232 };
2233
2235 {
2236 m_out->Print( "(options" );
2237
2239 m_out->Print( "(clearance convexhull)" );
2240 else
2241 m_out->Print( "(clearance outline)" );
2242
2243 // Output the anchor pad shape (circle/rect)
2244 m_out->Print( "(anchor %s)", anchorShape( PADSTACK::ALL_LAYERS ) );
2245
2246 m_out->Print( ")"); // end of (options ...
2247
2248 // Output graphic primitive of the pad shape
2249 formatPrimitives( PADSTACK::ALL_LAYERS );
2250 }
2251
2254
2255 if( aPad->Padstack().FrontOuterLayers().has_solder_mask.has_value()
2256 || aPad->Padstack().BackOuterLayers().has_solder_mask.has_value() )
2257 {
2258 m_out->Print( 0, " (tenting " );
2263 m_out->Print( 0, ")" );
2264 }
2265
2267
2268 // TODO: Refactor so that we call formatPadLayer( ALL_LAYERS ) above instead of redundant code
2269 auto formatPadLayer =
2270 [&]( PCB_LAYER_ID aLayer )
2271 {
2272 const PADSTACK& padstack = aPad->Padstack();
2273
2274 m_out->Print( "(shape %s)", shapeName( aLayer ) );
2275
2276 m_out->Print( "(size %s)", formatInternalUnits( padstack.Size( aLayer ) ).c_str() );
2277
2278 const VECTOR2I delta = aPad->GetDelta( aLayer );
2279
2280 if( delta.x != 0 || delta.y != 0 )
2281 m_out->Print( "(rect_delta %s)", formatInternalUnits( delta ).c_str() );
2282
2283 shapeoffset = aPad->GetOffset( aLayer );
2284
2285 if( shapeoffset.x != 0 || shapeoffset.y != 0 )
2286 m_out->Print( "(offset %s)", formatInternalUnits( padstack.Offset( aLayer ) ).c_str() );
2287
2288 formatCornerProperties( aLayer );
2289
2290 if( aPad->GetShape( aLayer ) == PAD_SHAPE::CUSTOM )
2291 {
2292 m_out->Print( "(options" );
2293
2294 // Output the anchor pad shape (circle/rect)
2295 m_out->Print( "(anchor %s)", anchorShape( aLayer ) );
2296
2297 m_out->Print( ")" ); // end of (options ...
2298
2299 // Output graphic primitive of the pad shape
2300 formatPrimitives( aLayer );
2301 }
2302
2303 EDA_ANGLE defaultLayerAngle = ANGLE_90;
2304
2305 if( aPad->GetShape( aLayer ) == PAD_SHAPE::CIRCLE ||
2306 ( aPad->GetShape( aLayer ) == PAD_SHAPE::CUSTOM
2307 && aPad->GetAnchorPadShape( aLayer ) == PAD_SHAPE::CIRCLE ) )
2308 {
2309 defaultLayerAngle = ANGLE_45;
2310 }
2311
2312 EDA_ANGLE layerSpokeAngle = padstack.ThermalSpokeAngle( aLayer );
2313
2314 if( layerSpokeAngle != defaultLayerAngle )
2315 {
2316 m_out->Print( "(thermal_bridge_angle %s)",
2317 EDA_UNIT_UTILS::FormatAngle( layerSpokeAngle ).c_str() );
2318 }
2319
2320 if( padstack.ThermalGap( aLayer ).has_value() )
2321 {
2322 m_out->Print( "(thermal_gap %s)",
2323 formatInternalUnits( *padstack.ThermalGap( aLayer ) ).c_str() );
2324 }
2325
2326 if( padstack.ThermalSpokeWidth( aLayer ).has_value() )
2327 {
2328 m_out->Print( "(thermal_bridge_width %s)",
2329 formatInternalUnits( *padstack.ThermalSpokeWidth( aLayer ) ).c_str() );
2330 }
2331
2332 if( padstack.Clearance( aLayer ).has_value() )
2333 {
2334 m_out->Print( "(clearance %s)",
2335 formatInternalUnits( *padstack.Clearance( aLayer ) ).c_str() );
2336 }
2337
2338 if( padstack.ZoneConnection( aLayer ).has_value() )
2339 {
2340 m_out->Print( "(zone_connect %d)",
2341 static_cast<int>( *padstack.ZoneConnection( aLayer ) ) );
2342 }
2343 };
2344
2345
2346 if( aPad->Padstack().Mode() != PADSTACK::MODE::NORMAL )
2347 {
2349 {
2350 m_out->Print( "(padstack (mode front_inner_back)" );
2351
2352 m_out->Print( "(layer \"Inner\"" );
2353 formatPadLayer( PADSTACK::INNER_LAYERS );
2354 m_out->Print( ")" );
2355 m_out->Print( "(layer \"B.Cu\"" );
2356 formatPadLayer( B_Cu );
2357 m_out->Print( ")" );
2358 }
2359 else
2360 {
2361 m_out->Print( "(padstack (mode custom)" );
2362
2363 int layerCount = board ? board->GetCopperLayerCount() : MAX_CU_LAYERS;
2364
2365 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, layerCount ) )
2366 {
2367 if( layer == F_Cu )
2368 continue;
2369
2370 m_out->Print( "(layer %s", m_out->Quotew( LSET::Name( layer ) ).c_str() );
2371 formatPadLayer( layer );
2372 m_out->Print( ")" );
2373 }
2374 }
2375
2376 m_out->Print( ")" );
2377 }
2378
2379 m_out->Print( ")" );
2380}
2381
2382
2383void PCB_IO_KICAD_SEXPR::format( const PCB_BARCODE* aBarcode ) const
2384{
2385 wxCHECK_RET( aBarcode != nullptr && m_out != nullptr, "" );
2386
2387 m_out->Print( "(barcode" );
2388
2389 if( aBarcode->IsLocked() )
2390 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2391
2392 m_out->Print( "(at %s %s)",
2393 formatInternalUnits( aBarcode->GetPosition() ).c_str(),
2394 EDA_UNIT_UTILS::FormatAngle( aBarcode->GetAngle() ).c_str() );
2395
2396 formatLayer( aBarcode->GetLayer() );
2397
2398 m_out->Print( "(size %s %s)",
2399 formatInternalUnits( aBarcode->GetWidth() ).c_str(),
2400 formatInternalUnits( aBarcode->GetHeight() ).c_str() );
2401
2402 m_out->Print( "(text %s)", m_out->Quotew( aBarcode->GetText() ).c_str() );
2403
2404 m_out->Print( "(text_height %s)", formatInternalUnits( aBarcode->GetTextSize() ).c_str() );
2405
2406 const char* typeStr = "code39";
2407
2408 switch( aBarcode->GetKind() )
2409 {
2410 case BARCODE_T::CODE_39: typeStr = "code39"; break;
2411 case BARCODE_T::CODE_128: typeStr = "code128"; break;
2412 case BARCODE_T::DATA_MATRIX: typeStr = "datamatrix"; break;
2413 case BARCODE_T::QR_CODE: typeStr = "qr"; break;
2414 case BARCODE_T::MICRO_QR_CODE: typeStr = "microqr"; break;
2415 }
2416
2417 m_out->Print( "(type %s)", typeStr );
2418
2419 if( aBarcode->GetKind() == BARCODE_T::QR_CODE
2420 || aBarcode->GetKind() == BARCODE_T::MICRO_QR_CODE )
2421 {
2422 const char* eccStr = "L";
2423 switch( aBarcode->GetErrorCorrection() )
2424 {
2425 case BARCODE_ECC_T::L: eccStr = "L"; break;
2426 case BARCODE_ECC_T::M: eccStr = "M"; break;
2427 case BARCODE_ECC_T::Q: eccStr = "Q"; break;
2428 case BARCODE_ECC_T::H: eccStr = "H"; break;
2429 }
2430
2431 m_out->Print( "(ecc_level %s)", eccStr );
2432 }
2433
2434 KICAD_FORMAT::FormatBool( m_out, "hide", !aBarcode->GetShowText() );
2435 KICAD_FORMAT::FormatBool( m_out, "knockout", aBarcode->IsKnockout() );
2436
2437 if( aBarcode->GetMargin().x != 0 || aBarcode->GetMargin().y != 0 )
2438 {
2439 m_out->Print( "(margins %s %s)", formatInternalUnits( aBarcode->GetMargin().x ).c_str(),
2440 formatInternalUnits( aBarcode->GetMargin().y ).c_str() );
2441 }
2442
2444
2445 m_out->Print( ")" );
2446}
2447
2448
2449void PCB_IO_KICAD_SEXPR::format( const PCB_TEXT* aText ) const
2450{
2451 FOOTPRINT* parentFP = aText->GetParentFootprint();
2452 std::string prefix;
2453 std::string type;
2454 VECTOR2I pos = aText->GetTextPos();
2455 const PCB_FIELD* field = dynamic_cast<const PCB_FIELD*>( aText );
2456
2457 // Always format dimension text as gr_text
2458 if( dynamic_cast<const PCB_DIMENSION_BASE*>( aText ) )
2459 parentFP = nullptr;
2460
2461 if( parentFP )
2462 {
2463 prefix = "fp";
2464 type = "user";
2465
2466 pos = parentFP->GetTransform().InverseApply( pos );
2467 }
2468 else
2469 {
2470 prefix = "gr";
2471 }
2472
2473 if( !field )
2474 {
2475 m_out->Print( "(%s_text %s %s",
2476 prefix.c_str(),
2477 type.c_str(),
2478 m_out->Quotew( aText->GetText() ).c_str() );
2479
2480 if( aText->IsLocked() )
2481 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2482 }
2483
2484 m_out->Print( "(at %s %s)",
2485 formatInternalUnits( pos ).c_str(),
2486 EDA_UNIT_UTILS::FormatAngle( aText->GetTextAngle() ).c_str() );
2487
2488 if( parentFP && !aText->IsKeepUpright() )
2489 KICAD_FORMAT::FormatBool( m_out, "unlocked", true );
2490
2491 formatLayer( aText->GetLayer(), aText->IsKnockout() );
2492
2493 if( field && !field->IsVisible() )
2494 KICAD_FORMAT::FormatBool( m_out, "hide", true );
2495
2497
2498 // Currently, texts have no specific color and no hyperlink.
2499 // so ensure they are never written in kicad_pcb file
2500 int ctl_flags = CTL_OMIT_COLOR | CTL_OMIT_HYPERLINK;
2501
2502 if( parentFP )
2503 {
2504 EDA_TEXT* mut = const_cast<EDA_TEXT*>( static_cast<const EDA_TEXT*>( aText ) );
2505 const VECTOR2I savedSize = mut->GetTextSize();
2506 const int savedThickness = mut->GetTextThickness();
2507 const bool mutateThickness = !mut->GetAutoThickness();
2508
2509 mut->SetTextSize( unbakeSize( savedSize, parentFP ) );
2510
2511 if( mutateThickness )
2512 mut->SetTextThickness( unbakeLinear( savedThickness, parentFP ) );
2513
2514 aText->EDA_TEXT::Format( m_out, ctl_flags );
2515
2516 mut->SetTextSize( savedSize );
2517
2518 if( mutateThickness )
2519 mut->SetTextThickness( savedThickness );
2520 }
2521 else
2522 {
2523 aText->EDA_TEXT::Format( m_out, ctl_flags );
2524 }
2525
2526 if( aText->GetFont() && aText->GetFont()->IsOutline() )
2527 formatRenderCache( aText );
2528
2529 if( !field )
2530 m_out->Print( ")" );
2531}
2532
2533
2534void PCB_IO_KICAD_SEXPR::format( const PCB_TEXTBOX* aTextBox ) const
2535{
2536 FOOTPRINT* parentFP = aTextBox->GetParentFootprint();
2537
2538 m_out->Print( "(%s %s",
2539 aTextBox->Type() == PCB_TABLECELL_T ? "table_cell"
2540 : parentFP ? "fp_text_box"
2541 : "gr_text_box",
2542 m_out->Quotew( aTextBox->GetText() ).c_str() );
2543
2544 if( aTextBox->IsLocked() )
2545 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2546
2547 // Use the lib-frame shape. The board-frame shape can flip to POLY for
2548 // rendering when the FP rotation is non-cardinal.
2549 SHAPE_T libShape = aTextBox->GetLibraryShape();
2550
2551 if( libShape == SHAPE_T::RECTANGLE )
2552 {
2553 m_out->Print( "(start %s) (end %s)", formatInternalUnits( aTextBox->GetLibraryStart() ).c_str(),
2554 formatInternalUnits( aTextBox->GetLibraryEnd() ).c_str() );
2555 }
2556 else if( libShape == SHAPE_T::POLY )
2557 {
2558 // Fall back to the runtime polygon if the lib copy was never seeded.
2559 const SHAPE_POLY_SET& libPoly = aTextBox->GetLibPoly();
2560 const bool haveLibPoly = libPoly.OutlineCount() > 0;
2561 const SHAPE_POLY_SET& poly = haveLibPoly ? libPoly : aTextBox->GetPolyShape();
2562
2563 if( poly.OutlineCount() > 0 )
2564 formatPolyPts( poly.Outline( 0 ), haveLibPoly ? nullptr : parentFP );
2565 }
2566 else
2567 {
2568 UNIMPLEMENTED_FOR( aTextBox->SHAPE_T_asString() );
2569 }
2570
2571 m_out->Print( "(margins %s %s %s %s)",
2572 formatInternalUnits( aTextBox->GetMarginLeft() ).c_str(),
2573 formatInternalUnits( aTextBox->GetMarginTop() ).c_str(),
2574 formatInternalUnits( aTextBox->GetMarginRight() ).c_str(),
2575 formatInternalUnits( aTextBox->GetMarginBottom() ).c_str() );
2576
2577 if( const PCB_TABLECELL* cell = dynamic_cast<const PCB_TABLECELL*>( aTextBox ) )
2578 m_out->Print( "(span %d %d)", cell->GetColSpan(), cell->GetRowSpan() );
2579
2580 EDA_ANGLE angle = aTextBox->GetTextAngle();
2581
2582 if( parentFP )
2583 {
2584 angle -= parentFP->GetOrientation();
2585 angle.Normalize720();
2586 }
2587
2588 if( !angle.IsZero() )
2589 m_out->Print( "(angle %s)", EDA_UNIT_UTILS::FormatAngle( angle ).c_str() );
2590
2591 formatLayer( aTextBox->GetLayer() );
2592
2594
2595 aTextBox->EDA_TEXT::Format( m_out, 0 );
2596
2597 if( aTextBox->Type() != PCB_TABLECELL_T )
2598 {
2599 KICAD_FORMAT::FormatBool( m_out, "border", aTextBox->IsBorderEnabled() );
2600 aTextBox->GetStroke().Format( m_out, pcbIUScale );
2601 }
2602
2603 KICAD_FORMAT::FormatBool( m_out, "knockout", aTextBox->IsKnockout() );
2604
2605 if( aTextBox->GetFont() && aTextBox->GetFont()->IsOutline() )
2606 formatRenderCache( aTextBox );
2607
2608 m_out->Print( ")" );
2609}
2610
2611
2612void PCB_IO_KICAD_SEXPR::format( const PCB_TABLE* aTable ) const
2613{
2614 wxCHECK_RET( aTable != nullptr && m_out != nullptr, "" );
2615
2616 m_out->Print( "(table (column_count %d)", aTable->GetColCount() );
2617
2619
2620 if( aTable->IsLocked() )
2621 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2622
2623 formatLayer( aTable->GetLayer() );
2624
2625 m_out->Print( "(border" );
2626 KICAD_FORMAT::FormatBool( m_out, "external", aTable->StrokeExternal() );
2628
2629 if( aTable->StrokeExternal() || aTable->StrokeHeaderSeparator() )
2630 aTable->GetBorderStroke().Format( m_out, pcbIUScale );
2631
2632 m_out->Print( ")" ); // Close `border` token.
2633
2634 m_out->Print( "(separators" );
2635 KICAD_FORMAT::FormatBool( m_out, "rows", aTable->StrokeRows() );
2636 KICAD_FORMAT::FormatBool( m_out, "cols", aTable->StrokeColumns() );
2637
2638 if( aTable->StrokeRows() || aTable->StrokeColumns() )
2640
2641 m_out->Print( ")" ); // Close `separators` token.
2642
2643 m_out->Print( "(column_widths" );
2644
2645 for( int col = 0; col < aTable->GetColCount(); ++col )
2646 m_out->Print( " %s", formatInternalUnits( aTable->GetColWidth( col ) ).c_str() );
2647
2648 m_out->Print( ")" );
2649
2650 m_out->Print( "(row_heights" );
2651
2652 for( int row = 0; row < aTable->GetRowCount(); ++row )
2653 m_out->Print( " %s", formatInternalUnits( aTable->GetRowHeight( row ) ).c_str() );
2654
2655 m_out->Print( ")" );
2656
2657 m_out->Print( "(cells" );
2658
2659 for( PCB_TABLECELL* cell : aTable->GetCells() )
2660 format( static_cast<PCB_TEXTBOX*>( cell ) );
2661
2662 m_out->Print( ")" ); // Close `cells` token.
2663 m_out->Print( ")" ); // Close `table` token.
2664}
2665
2666
2667void PCB_IO_KICAD_SEXPR::format( const PCB_GROUP* aGroup ) const
2668{
2669 wxArrayString memberIds;
2670
2671 // Validate member pointers against the board cache to avoid use-after-free on dangling
2672 // pointers (e.g. when a group held a reference to a deleted item). This validation only
2673 // applies when the group itself is part of m_board; for groups created off-board (e.g. a
2674 // DeepClone() used by the clipboard) the cache contains the originals, not our clones, so
2675 // skip the validation in that case and trust the member pointers.
2676 //
2677 // BOARD::IsItemIndexedById() probes the board's inverse pointer index without dereferencing
2678 // the candidate, so it is safe to call on a possibly-dangling member.
2679 bool validateAgainstBoard = m_board && m_board->IsItemIndexedById( aGroup );
2680
2681 memberIds.Alloc( aGroup->GetItems().size() );
2682
2683 for( EDA_ITEM* member : aGroup->GetItems() )
2684 {
2685 if( !validateAgainstBoard
2686 || m_board->IsItemIndexedById( static_cast<const BOARD_ITEM*>( member ) ) )
2687 {
2688 memberIds.Add( member->m_Uuid.AsString() );
2689 }
2690 }
2691
2692 if( memberIds.empty() )
2693 return;
2694
2695 m_out->Print( "(group %s", m_out->Quotew( aGroup->GetName() ).c_str() );
2696
2698
2699 if( aGroup->IsLocked() )
2700 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2701
2702 if( aGroup->HasDesignBlockLink() )
2703 m_out->Print( "(lib_id \"%s\")", aGroup->GetDesignBlockLibId().Format().c_str() );
2704
2705 memberIds.Sort();
2706
2707 m_out->Print( "(members" );
2708
2709 for( const wxString& memberId : memberIds )
2710 m_out->Print( " %s", m_out->Quotew( memberId ).c_str() );
2711
2712 m_out->Print( ")" ); // Close `members` token.
2713 m_out->Print( ")" ); // Close `group` token.
2714}
2715
2716
2717void PCB_IO_KICAD_SEXPR::format( const PCB_CONSTRAINT* aConstraint ) const
2718{
2719 const std::vector<CONSTRAINT_MEMBER>& members = aConstraint->GetMembers();
2720
2721 if( members.empty() )
2722 return;
2723
2724 // Members are KIID references (not pointers), so unlike format(PCB_GROUP*) there is no
2725 // use-after-free risk: every member is written verbatim, including one whose item was deleted,
2726 // so the constraint round-trips in its error state rather than silently losing the reference.
2727 m_out->Print( "(constraint (type %s)", ConstraintTypeToken( aConstraint->GetConstraintType() ) );
2728
2729 KICAD_FORMAT::FormatUuid( m_out, aConstraint->m_Uuid );
2730
2731 m_out->Print( "(members" );
2732
2733 for( const CONSTRAINT_MEMBER& member : members )
2734 {
2735 // Only VERTEX carries an ordinal others stay two-token
2736 if( member.m_anchor == CONSTRAINT_ANCHOR::VERTEX )
2737 {
2738 m_out->Print( "(member %s %s %d)", m_out->Quotew( member.m_item.AsString() ).c_str(),
2739 ConstraintAnchorToken( member.m_anchor ), member.m_index );
2740 }
2741 else
2742 {
2743 m_out->Print( "(member %s %s)", m_out->Quotew( member.m_item.AsString() ).c_str(),
2744 ConstraintAnchorToken( member.m_anchor ) );
2745 }
2746 }
2747
2748 m_out->Print( ")" ); // Close `members` token.
2749
2750 if( aConstraint->HasValue() )
2751 {
2752 // Length/radius values are stored in IU but written in mm like every other dimension;
2753 // angle values are written verbatim in degrees.
2754 double value = *aConstraint->GetValue();
2755
2756 if( ConstraintValueIsLength( aConstraint->GetConstraintType() ) )
2757 value /= pcbIUScale.IU_PER_MM;
2758
2759 m_out->Print( "(value %s)", FormatDouble2Str( value ).c_str() );
2760 }
2761
2762 if( !aConstraint->IsDriving() )
2763 KICAD_FORMAT::FormatBool( m_out, "driving", false );
2764
2765 m_out->Print( ")" ); // Close `constraint` token.
2766}
2767
2768
2769void PCB_IO_KICAD_SEXPR::format( const PCB_GENERATOR* aGenerator ) const
2770{
2771 // Some conditions appear to still be creating ghost tuning patterns. Don't save them.
2772 if( aGenerator->GetGeneratorType() == wxT( "tuning_pattern" )
2773 && aGenerator->GetItems().empty() )
2774 {
2775 return;
2776 }
2777
2778 m_out->Print( "(generated" );
2779
2780 KICAD_FORMAT::FormatUuid( m_out, aGenerator->m_Uuid );
2781
2782 m_out->Print( "(type %s) (name %s) (layer %s)",
2783 TO_UTF8( aGenerator->GetGeneratorType() ),
2784 m_out->Quotew( aGenerator->GetName() ).c_str(),
2785 m_out->Quotew( LSET::Name( aGenerator->GetLayer() ) ).c_str() );
2786
2787 if( aGenerator->IsLocked() )
2788 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2789
2790 for( const auto& [key, value] : aGenerator->GetProperties() )
2791 {
2792 if( value.CheckType<double>() || value.CheckType<int>() || value.CheckType<long>()
2793 || value.CheckType<long long>() )
2794 {
2795 double val;
2796
2797 if( !value.GetAs( &val ) )
2798 continue;
2799
2800 std::string buf = fmt::format( "{:.10g}", val );
2801
2802 // Don't quote numbers
2803 m_out->Print( "(%s %s)", key.c_str(), buf.c_str() );
2804 }
2805 else if( value.CheckType<bool>() )
2806 {
2807 bool val;
2808 value.GetAs( &val );
2809
2810 KICAD_FORMAT::FormatBool( m_out, key, val );
2811 }
2812 else if( value.CheckType<VECTOR2I>() )
2813 {
2814 VECTOR2I val;
2815 value.GetAs( &val );
2816
2817 m_out->Print( "(%s (xy %s))",
2818 key.c_str(),
2819 formatInternalUnits( val ).c_str() );
2820 }
2821 else if( value.CheckType<SHAPE_LINE_CHAIN>() )
2822 {
2823 SHAPE_LINE_CHAIN val;
2824 value.GetAs( &val );
2825
2826 m_out->Print( "(%s ", key.c_str() );
2827 formatPolyPts( val );
2828 m_out->Print( ")" );
2829 }
2830 else
2831 {
2832 wxString val;
2833
2834 if( value.CheckType<wxString>() )
2835 {
2836 value.GetAs( &val );
2837 }
2838 else if( value.CheckType<std::string>() )
2839 {
2840 std::string str;
2841 value.GetAs( &str );
2842
2843 val = wxString::FromUTF8( str );
2844 }
2845
2846 m_out->Print( "(%s %s)", key.c_str(), m_out->Quotew( val ).c_str() );
2847 }
2848 }
2849
2850 wxArrayString memberIds;
2851
2852 for( EDA_ITEM* member : aGenerator->GetItems() )
2853 memberIds.Add( member->m_Uuid.AsString() );
2854
2855 memberIds.Sort();
2856
2857 m_out->Print( "(members" );
2858
2859 for( const wxString& memberId : memberIds )
2860 m_out->Print( " %s", m_out->Quotew( memberId ).c_str() );
2861
2862 m_out->Print( ")" ); // Close `members` token.
2863 m_out->Print( ")" ); // Close `generated` token.
2864}
2865
2866
2867void PCB_IO_KICAD_SEXPR::format( const PCB_TRACK* aTrack ) const
2868{
2869 if( aTrack->Type() == PCB_VIA_T )
2870 {
2871 PCB_LAYER_ID layer1, layer2;
2872
2873 const PCB_VIA* via = static_cast<const PCB_VIA*>( aTrack );
2874 const BOARD* board = via->GetBoard();
2875
2876 wxCHECK_RET( board != nullptr, wxT( "Via has no parent." ) );
2877
2878 m_out->Print( "(via" );
2879
2880 via->LayerPair( &layer1, &layer2 );
2881
2882 switch( via->GetViaType() )
2883 {
2884 case VIATYPE::THROUGH: // Default shape not saved.
2885 break;
2886
2887 case VIATYPE::BLIND:
2888 m_out->Print( " blind " );
2889 break;
2890
2891 case VIATYPE::BURIED:
2892 m_out->Print( " buried " );
2893 break;
2894
2895 case VIATYPE::MICROVIA:
2896 m_out->Print( " micro " );
2897 break;
2898
2899 default:
2900 THROW_IO_ERRORF( _( "unknown via type %d" ), via->GetViaType() );
2901 }
2902
2903 m_out->Print( "(at %s) (size %s)",
2904 formatInternalUnits( aTrack->GetStart() ).c_str(),
2905 formatInternalUnits( via->GetWidth( F_Cu ) ).c_str() );
2906
2907 // Old boards were using UNDEFINED_DRILL_DIAMETER value in file for via drill when
2908 // via drill was the netclass value.
2909 // recent boards always set the via drill to the actual value, but now we need to
2910 // always store the drill value, because netclass value is not stored in the board file.
2911 // Otherwise the drill value of some (old) vias can be unknown
2912 if( via->GetDrill() != UNDEFINED_DRILL_DIAMETER )
2913 m_out->Print( "(drill %s)", formatInternalUnits( via->GetDrill() ).c_str() );
2914 else
2915 m_out->Print( "(drill %s)", formatInternalUnits( via->GetDrillValue() ).c_str() );
2916
2917 if( via->Padstack().SecondaryDrill().size.x > 0 )
2918 {
2919 m_out->Print( "(backdrill (size %s) (layers %s %s))",
2920 formatInternalUnits( via->Padstack().SecondaryDrill().size.x ).c_str(),
2921 m_out->Quotew( LSET::Name( via->Padstack().SecondaryDrill().start ) ).c_str(),
2922 m_out->Quotew( LSET::Name( via->Padstack().SecondaryDrill().end ) ).c_str() );
2923 }
2924
2925 if( via->Padstack().TertiaryDrill().size.x > 0 )
2926 {
2927 m_out->Print( "(tertiary_drill (size %s) (layers %s %s))",
2928 formatInternalUnits( via->Padstack().TertiaryDrill().size.x ).c_str(),
2929 m_out->Quotew( LSET::Name( via->Padstack().TertiaryDrill().start ) ).c_str(),
2930 m_out->Quotew( LSET::Name( via->Padstack().TertiaryDrill().end ) ).c_str() );
2931 }
2932
2933 auto formatPostMachining = [&]( const char* aName, const PADSTACK::POST_MACHINING_PROPS& aProps )
2934 {
2935 if( !aProps.mode.has_value() || aProps.mode == PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED )
2936 return;
2937
2938 m_out->Print( "(%s %s", aName,
2939 aProps.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE ? "counterbore" : "countersink" );
2940
2941 if( aProps.size > 0 )
2942 m_out->Print( " (size %s)", formatInternalUnits( aProps.size ).c_str() );
2943
2944 if( aProps.depth > 0 )
2945 m_out->Print( " (depth %s)", formatInternalUnits( aProps.depth ).c_str() );
2946
2947 if( aProps.angle > 0 )
2948 m_out->Print( " (angle %s)", FormatDouble2Str( aProps.angle / 10.0 ).c_str() );
2949
2950 m_out->Print( ")" );
2951 };
2952
2953 formatPostMachining( "front_post_machining", via->Padstack().FrontPostMachining() );
2954 formatPostMachining( "back_post_machining", via->Padstack().BackPostMachining() );
2955
2956 m_out->Print( "(layers %s %s)",
2957 m_out->Quotew( LSET::Name( layer1 ) ).c_str(),
2958 m_out->Quotew( LSET::Name( layer2 ) ).c_str() );
2959
2960 switch( via->Padstack().UnconnectedLayerMode() )
2961 {
2963 KICAD_FORMAT::FormatBool( m_out, "remove_unused_layers", true );
2964 KICAD_FORMAT::FormatBool( m_out, "keep_end_layers", false );
2965 break;
2966
2968 KICAD_FORMAT::FormatBool( m_out, "remove_unused_layers", true );
2969 KICAD_FORMAT::FormatBool( m_out, "keep_end_layers", true );
2970 break;
2971
2973 KICAD_FORMAT::FormatBool( m_out, "start_end_only", true );
2974 break;
2975
2977 break;
2978 }
2979
2980 if( via->IsLocked() )
2981 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2982
2983 if( via->GetIsFree() )
2984 KICAD_FORMAT::FormatBool( m_out, "free", true );
2985
2986 if( via->GetRemoveUnconnected() )
2987 {
2988 m_out->Print( "(zone_layer_connections" );
2989
2990 for( PCB_LAYER_ID layer : board->GetEnabledLayers().CuStack() )
2991 {
2992 if( via->GetZoneLayerOverride( layer ) == ZLO_FORCE_FLASHED )
2993 m_out->Print( " %s", m_out->Quotew( LSET::Name( layer ) ).c_str() );
2994 }
2995
2996 m_out->Print( ")" );
2997 }
2998
2999 const PADSTACK& padstack = via->Padstack();
3000
3001 if( padstack.FrontOuterLayers().has_solder_mask.has_value()
3002 || padstack.BackOuterLayers().has_solder_mask.has_value() )
3003 {
3004 m_out->Print( 0, " (tenting " );
3006 padstack.FrontOuterLayers().has_solder_mask );
3008 padstack.BackOuterLayers().has_solder_mask );
3009 m_out->Print( 0, ")" );
3010 }
3011
3012 if( padstack.Drill().is_capped.has_value() )
3013 KICAD_FORMAT::FormatOptBool( m_out, "capping", padstack.Drill().is_capped );
3014
3015 if( padstack.FrontOuterLayers().has_covering.has_value()
3016 || padstack.BackOuterLayers().has_covering.has_value() )
3017 {
3018 m_out->Print( 0, " (covering " );
3020 padstack.FrontOuterLayers().has_covering );
3022 padstack.BackOuterLayers().has_covering );
3023 m_out->Print( 0, ")" );
3024 }
3025
3026 if( padstack.FrontOuterLayers().has_plugging.has_value()
3027 || padstack.BackOuterLayers().has_plugging.has_value() )
3028 {
3029 m_out->Print( 0, " (plugging " );
3031 padstack.FrontOuterLayers().has_plugging );
3033 padstack.BackOuterLayers().has_plugging );
3034 m_out->Print( 0, ")" );
3035 }
3036
3037 if( padstack.Drill().is_filled.has_value() )
3038 KICAD_FORMAT::FormatOptBool( m_out, "filling", padstack.Drill().is_filled );
3039
3040 if( padstack.Mode() != PADSTACK::MODE::NORMAL )
3041 {
3042 m_out->Print( "(padstack" );
3043
3044 if( padstack.Mode() == PADSTACK::MODE::FRONT_INNER_BACK )
3045 {
3046 m_out->Print( "(mode front_inner_back)" );
3047
3048 m_out->Print( "(layer \"Inner\"" );
3049 m_out->Print( "(size %s)",
3050 formatInternalUnits( padstack.Size( PADSTACK::INNER_LAYERS ).x ).c_str() );
3051 m_out->Print( ")" );
3052 m_out->Print( "(layer \"B.Cu\"" );
3053 m_out->Print( "(size %s)",
3054 formatInternalUnits( padstack.Size( B_Cu ).x ).c_str() );
3055 m_out->Print( ")" );
3056 }
3057 else
3058 {
3059 m_out->Print( "(mode custom)" );
3060
3061 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, board->GetCopperLayerCount() ) )
3062 {
3063 if( layer == F_Cu )
3064 continue;
3065
3066 m_out->Print( "(layer %s", m_out->Quotew( LSET::Name( layer ) ).c_str() );
3067 m_out->Print( "(size %s)",
3068 formatInternalUnits( padstack.Size( layer ).x ).c_str() );
3069 m_out->Print( ")" );
3070 }
3071 }
3072
3073 m_out->Print( ")" );
3074 }
3075
3076 if( !isDefaultTeardropParameters( via->GetTeardropParams() ) )
3077 formatTeardropParameters( via->GetTeardropParams() );
3078 }
3079 else
3080 {
3081 if( aTrack->Type() == PCB_ARC_T )
3082 {
3083 const PCB_ARC* arc = static_cast<const PCB_ARC*>( aTrack );
3084
3085 m_out->Print( "(arc (start %s) (mid %s) (end %s) (width %s)",
3086 formatInternalUnits( arc->GetStart() ).c_str(),
3087 formatInternalUnits( arc->GetMid() ).c_str(),
3088 formatInternalUnits( arc->GetEnd() ).c_str(),
3089 formatInternalUnits( arc->GetWidth() ).c_str() );
3090 }
3091 else
3092 {
3093 m_out->Print( "(segment (start %s) (end %s) (width %s)",
3094 formatInternalUnits( aTrack->GetStart() ).c_str(),
3095 formatInternalUnits( aTrack->GetEnd() ).c_str(),
3096 formatInternalUnits( aTrack->GetWidth() ).c_str() );
3097 }
3098
3099 if( aTrack->IsLocked() )
3100 KICAD_FORMAT::FormatBool( m_out, "locked", true );
3101
3102 if( aTrack->GetLayerSet().count() > 1 )
3103 formatLayers( aTrack->GetLayerSet(), false /* enumerate layers */ );
3104 else
3105 formatLayer( aTrack->GetLayer() );
3106
3107 if( aTrack->HasSolderMask()
3108 && aTrack->GetLocalSolderMaskMargin().has_value()
3109 && IsExternalCopperLayer( aTrack->GetLayer() ) )
3110 {
3111 m_out->Print( "(solder_mask_margin %s)",
3112 formatInternalUnits( aTrack->GetLocalSolderMaskMargin().value() ).c_str() );
3113 }
3114 }
3115
3116 if( !( m_ctl & CTL_OMIT_PAD_NETS ) )
3117 m_out->Print( "(net %s)", m_out->Quotew( aTrack->GetNetname() ).c_str() );
3118
3120 m_out->Print( ")" );
3121}
3122
3123
3124void PCB_IO_KICAD_SEXPR::format( const ZONE* aZone ) const
3125{
3126 m_out->Print( "(zone" );
3127
3128 if( !( m_ctl & CTL_OMIT_PAD_NETS ) && aZone->IsOnCopperLayer() && !aZone->GetIsRuleArea()
3129 && aZone->GetNetCode() > 0 )
3130 {
3131 m_out->Print( "(net %s)", m_out->Quotew( aZone->GetNetname() ).c_str() );
3132 }
3133
3134 if( aZone->IsLocked() )
3135 KICAD_FORMAT::FormatBool( m_out, "locked", true );
3136
3137 // If a zone exists on multiple layers, format accordingly
3138 LSET layers = aZone->GetLayerSet();
3139
3140 if( aZone->GetBoard() )
3141 layers &= aZone->GetBoard()->GetEnabledLayers();
3142
3143 // Always enumerate every layer for a zone on a copper layer
3144 if( layers.count() > 1 )
3145 formatLayers( layers, aZone->IsOnCopperLayer(), true );
3146 else
3147 formatLayer( aZone->GetFirstLayer() );
3148
3149 if( !aZone->IsTeardropArea() )
3151
3152 if( !aZone->GetZoneName().empty() && !aZone->IsTeardropArea() )
3153 m_out->Print( "(name %s)", m_out->Quotew( aZone->GetZoneName() ).c_str() );
3154
3155 // Save the outline aux info
3156 std::string hatch;
3157
3158 switch( aZone->GetHatchStyle() )
3159 {
3160 default:
3161 case ZONE_BORDER_DISPLAY_STYLE::NO_HATCH: hatch = "none"; break;
3162 case ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE: hatch = "edge"; break;
3163 case ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL: hatch = "full"; break;
3164 }
3165
3166 m_out->Print( "(hatch %s %s)", hatch.c_str(),
3167 formatInternalUnits( aZone->GetBorderHatchPitch() ).c_str() );
3168
3169
3170
3171 if( aZone->GetAssignedPriority() > 0 )
3172 m_out->Print( "(priority %d)", aZone->GetAssignedPriority() );
3173
3174 // Add teardrop keywords in file: (attr (teardrop (type xxx))) where xxx is the teardrop type
3175 if( aZone->IsTeardropArea() )
3176 {
3177 m_out->Print( "(attr (teardrop (type %s)))",
3178 aZone->GetTeardropAreaType() == TEARDROP_TYPE::TD_VIAPAD ? "padvia"
3179 : "track_end" );
3180 }
3181
3182 m_out->Print( "(connect_pads" );
3183
3184 switch( aZone->GetPadConnection() )
3185 {
3186 default:
3187 case ZONE_CONNECTION::THERMAL: // Default option not saved or loaded.
3188 break;
3189
3191 m_out->Print( " thru_hole_only" );
3192 break;
3193
3195 m_out->Print( " yes" );
3196 break;
3197
3199 m_out->Print( " no" );
3200 break;
3201 }
3202
3203 m_out->Print( "(clearance %s)",
3204 formatInternalUnits( aZone->GetLocalClearance().value() ).c_str() );
3205
3206 m_out->Print( ")" );
3207
3208 m_out->Print( "(min_thickness %s)",
3209 formatInternalUnits( aZone->GetMinThickness() ).c_str() );
3210
3211 if( aZone->GetIsRuleArea() )
3212 {
3213 // Keepout settings
3214 m_out->Print( "(keepout (tracks %s) (vias %s) (pads %s) (copperpour %s) (footprints %s))",
3215 aZone->GetDoNotAllowTracks() ? "not_allowed" : "allowed",
3216 aZone->GetDoNotAllowVias() ? "not_allowed" : "allowed",
3217 aZone->GetDoNotAllowPads() ? "not_allowed" : "allowed",
3218 aZone->GetDoNotAllowZoneFills() ? "not_allowed" : "allowed",
3219 aZone->GetDoNotAllowFootprints() ? "not_allowed" : "allowed" );
3220
3221 // Multichannel settings
3222 m_out->Print( "(placement" );
3224
3225 switch( aZone->GetPlacementAreaSourceType() )
3226 {
3228 m_out->Print( "(sheetname %s)", m_out->Quotew( aZone->GetPlacementAreaSource() ).c_str() );
3229 break;
3231 m_out->Print( "(component_class %s)", m_out->Quotew( aZone->GetPlacementAreaSource() ).c_str() );
3232 break;
3234 m_out->Print( "(group %s)", m_out->Quotew( aZone->GetPlacementAreaSource() ).c_str() );
3235 break;
3236 // These are transitory and should not be saved
3238 break;
3239 }
3240
3241 m_out->Print( ")" );
3242 }
3243
3244 m_out->Print( "(fill" );
3245
3246 // Default is not filled.
3247 if( aZone->IsFilled() )
3248 m_out->Print( " yes" );
3249
3250 // Default is polygon filled.
3252 m_out->Print( "(mode hatch)" );
3253 else if( aZone->GetFillMode() == ZONE_FILL_MODE::COPPER_THIEVING )
3254 m_out->Print( "(mode thieving)" );
3255
3256 if( !aZone->IsTeardropArea() )
3257 {
3258 m_out->Print( "(thermal_gap %s) (thermal_bridge_width %s)",
3259 formatInternalUnits( aZone->GetThermalReliefGap() ).c_str(),
3260 formatInternalUnits( aZone->GetThermalReliefSpokeWidth() ).c_str() );
3261 }
3262
3264 {
3265 switch( aZone->GetCornerSmoothingType() )
3266 {
3268 m_out->Print( "(smoothing chamfer)" );
3269 break;
3270
3272 m_out->Print( "(smoothing fillet)" );
3273 break;
3274
3275 default:
3276 THROW_IO_ERRORF( _( "unknown zone corner smoothing type %d" ), aZone->GetCornerSmoothingType() );
3277 }
3278
3279 if( aZone->GetCornerRadius() != 0 )
3280 m_out->Print( "(radius %s)", formatInternalUnits( aZone->GetCornerRadius() ).c_str() );
3281 }
3282
3283 m_out->Print( "(island_removal_mode %d)",
3284 static_cast<int>( aZone->GetIslandRemovalMode() ) );
3285
3287 {
3288 m_out->Print( "(island_area_min %s)",
3289 formatInternalUnits( aZone->GetMinIslandArea() / pcbIUScale.IU_PER_MM ).c_str() );
3290 }
3291
3293 {
3294 m_out->Print( "(hatch_thickness %s) (hatch_gap %s) (hatch_orientation %s)",
3295 formatInternalUnits( aZone->GetHatchThickness() ).c_str(),
3296 formatInternalUnits( aZone->GetHatchGap() ).c_str(),
3297 FormatDouble2Str( aZone->GetHatchOrientation().AsDegrees() ).c_str() );
3298
3299 if( aZone->GetHatchSmoothingLevel() > 0 )
3300 {
3301 m_out->Print( "(hatch_smoothing_level %d) (hatch_smoothing_value %s)",
3302 aZone->GetHatchSmoothingLevel(),
3303 FormatDouble2Str( aZone->GetHatchSmoothingValue() ).c_str() );
3304 }
3305
3306 m_out->Print( "(hatch_border_algorithm %s) (hatch_min_hole_area %s)",
3307 aZone->GetHatchBorderAlgorithm() ? "hatch_thickness" : "min_thickness",
3308 FormatDouble2Str( aZone->GetHatchHoleMinArea() ).c_str() );
3309 }
3310 else if( aZone->GetFillMode() == ZONE_FILL_MODE::COPPER_THIEVING )
3311 {
3312 const THIEVING_SETTINGS& thieving = aZone->GetThievingSettings();
3313 const char* patternStr = "dots";
3314
3315 switch( thieving.pattern )
3316 {
3317 case THIEVING_PATTERN::SQUARES: patternStr = "squares"; break;
3318 case THIEVING_PATTERN::HATCH: patternStr = "hatch"; break;
3320 default: patternStr = "dots"; break;
3321 }
3322
3323 m_out->Print( "(thieving (type %s) (size %s) (gap %s) (width %s) "
3324 "(stagger %s) (orientation %s))",
3325 patternStr,
3326 formatInternalUnits( thieving.element_size ).c_str(),
3327 formatInternalUnits( thieving.gap ).c_str(),
3328 formatInternalUnits( thieving.line_width ).c_str(),
3329 thieving.stagger ? "yes" : "no",
3330 FormatDouble2Str( thieving.orientation.AsDegrees() ).c_str() );
3331 }
3332
3333 m_out->Print( ")" );
3334
3335 for( const auto& [layer, properties] : aZone->LayerProperties() )
3336 {
3337 format( properties, 0, layer );
3338 }
3339
3340 if( aZone->GetNumCorners() )
3341 {
3342 SHAPE_POLY_SET::POLYGON poly = aZone->Outline()->Polygon(0);
3343
3344 for( const SHAPE_LINE_CHAIN& chain : poly )
3345 {
3346 if( chain.PointCount() == 0 )
3347 continue;
3348
3349 m_out->Print( "(polygon" );
3351 m_out->Print( ")" );
3352 }
3353 }
3354
3355 // Save the PolysList (filled areas)
3356 for( PCB_LAYER_ID layer : aZone->GetLayerSet().Seq() )
3357 {
3358 const std::shared_ptr<SHAPE_POLY_SET>& fv = aZone->GetFilledPolysList( layer );
3359
3360 for( int ii = 0; ii < fv->OutlineCount(); ++ii )
3361 {
3362 m_out->Print( "(filled_polygon" );
3363 m_out->Print( "(layer %s)", m_out->Quotew( LSET::Name( layer ) ).c_str() );
3364
3365 if( aZone->IsIsland( layer, ii ) )
3366 KICAD_FORMAT::FormatBool( m_out, "island", true );
3367
3368 const SHAPE_LINE_CHAIN& chain = fv->COutline( ii );
3369
3371 m_out->Print( ")" );
3372 }
3373 }
3374
3375 m_out->Print( ")" );
3376}
3377
3378
3379void PCB_IO_KICAD_SEXPR::format( const ZONE_LAYER_PROPERTIES& aZoneLayerProperties, int aNestLevel,
3380 PCB_LAYER_ID aLayer ) const
3381{
3382 // Do not store the layer properties if no value is actually set.
3383 if( !aZoneLayerProperties.hatching_offset.has_value() )
3384 return;
3385
3386 m_out->Print( aNestLevel, "(property\n" );
3387 m_out->Print( aNestLevel, "(layer %s)\n", m_out->Quotew( LSET::Name( aLayer ) ).c_str() );
3388
3389 if( aZoneLayerProperties.hatching_offset.has_value() )
3390 {
3391 m_out->Print( aNestLevel, "(hatch_position (xy %s))",
3392 formatInternalUnits( aZoneLayerProperties.hatching_offset.value() ).c_str() );
3393 }
3394
3395 m_out->Print( aNestLevel, ")\n" );
3396}
3397
3398
3399PCB_IO_KICAD_SEXPR::PCB_IO_KICAD_SEXPR( int aControlFlags ) : PCB_IO( wxS( "KiCad" ) ),
3400 m_cache( nullptr ),
3401 m_ctl( aControlFlags )
3402{
3403 init( nullptr );
3404 m_out = &m_sf;
3405}
3406
3407
3412
3413
3414BOARD* PCB_IO_KICAD_SEXPR::LoadBoard( const wxString& aFileName, BOARD* aAppendToMe,
3415 const std::map<std::string, UTF8>* aProperties,
3416 PROJECT* aProject )
3417{
3418 FILE_LINE_READER reader( aFileName );
3419
3420 unsigned lineCount = 0;
3421
3422 // Collect the font substitution warnings (RAII - automatically reset on scope exit)
3424
3425 if( m_progressReporter )
3426 {
3427 m_progressReporter->Report( wxString::Format( _( "Loading %s..." ), aFileName ) );
3428
3429 if( !m_progressReporter->KeepRefreshing() )
3430 THROW_IO_ERROR( _( "Open canceled by user." ) );
3431
3432 while( reader.ReadLine() )
3433 lineCount++;
3434
3435 reader.Rewind();
3436 }
3437
3438 BOARD* board = DoLoad( reader, aAppendToMe, aProperties, m_progressReporter, lineCount );
3439
3440 // Give the filename to the board if it's new
3441 if( !aAppendToMe )
3442 board->SetFileName( aFileName );
3443
3444 return board;
3445}
3446
3447
3449 const std::map<std::string, UTF8>* aProperties,
3450 PROGRESS_REPORTER* aProgressReporter, unsigned aLineCount)
3451{
3452 init( aProperties );
3453
3454 bool preserveDestinationStackup =
3455 aProperties && aProperties->contains( PCB_IO_LOAD_PROPERTIES::APPEND_PRESERVE_DESTINATION_STACKUP );
3456
3457 PCB_IO_KICAD_SEXPR_PARSER parser( &aReader, aAppendToMe, m_queryUserCallback, aProgressReporter, aLineCount,
3458 preserveDestinationStackup );
3459
3461
3462 std::set<BOARD_ITEM*> itemsBefore;
3463 std::set<NETINFO_ITEM*> netsBefore;
3464
3465 if( aAppendToMe )
3466 {
3467 for( BOARD_ITEM* item : aAppendToMe->GetItemSet() )
3468 itemsBefore.insert( item );
3469
3470 for( NETINFO_ITEM* net : aAppendToMe->GetNetInfo() )
3471 netsBefore.insert( net );
3472 }
3473
3474 auto revertPartialAppend = [&]()
3475 {
3476 if( !aAppendToMe )
3477 return;
3478
3479 std::vector<BOARD_ITEM*> addedItems;
3480
3481 for( BOARD_ITEM* item : aAppendToMe->GetItemSet() )
3482 {
3483 if( !itemsBefore.contains( item ) )
3484 addedItems.push_back( item );
3485 }
3486
3487 // Remove everything before deleting anything, group member back pointers
3488 // must be unlinked while their groups are still alive
3489 for( BOARD_ITEM* item : addedItems )
3490 aAppendToMe->Remove( item );
3491
3492 for( BOARD_ITEM* item : addedItems )
3493 delete item;
3494
3495 std::vector<NETINFO_ITEM*> addedNets;
3496
3497 for( NETINFO_ITEM* net : aAppendToMe->GetNetInfo() )
3498 {
3499 if( !netsBefore.contains( net ) )
3500 addedNets.push_back( net );
3501 }
3502
3503 for( NETINFO_ITEM* net : addedNets )
3504 {
3505 aAppendToMe->Remove( net );
3506 delete net;
3507 }
3508 };
3509
3510 BOARD* board;
3511
3512 try
3513 {
3514 board = dynamic_cast<BOARD*>( parser.Parse() );
3515 }
3516 catch( const FUTURE_FORMAT_ERROR& )
3517 {
3518 revertPartialAppend();
3519
3520 // Don't wrap a FUTURE_FORMAT_ERROR in another
3521 throw;
3522 }
3523 catch( const PARSE_ERROR& parse_error )
3524 {
3525 revertPartialAppend();
3526
3527 if( parser.IsTooRecent() )
3528 throw FUTURE_FORMAT_ERROR( parse_error, parser.GetRequiredVersion() );
3529 else
3530 throw;
3531 }
3532 catch( ... )
3533 {
3534 revertPartialAppend();
3535 throw;
3536 }
3537
3538 if( !board )
3539 {
3540 // The parser loaded something that was valid, but wasn't a board.
3541 THROW_PARSE_ERROR( _( "This file does not contain a PCB." ), parser.CurSource(),
3542 parser.CurLine(), parser.CurLineNumber(), parser.CurOffset() );
3543 }
3544
3545 // Report any non-fatal parse warnings to the load info reporter
3546 for( const wxString& warning : parser.GetParseWarnings() )
3548
3549 return board;
3550}
3551
3552
3553void PCB_IO_KICAD_SEXPR::init( const std::map<std::string, UTF8>* aProperties )
3554{
3555 m_board = nullptr;
3556 m_reader = nullptr;
3557 m_props = aProperties;
3558}
3559
3560
3561void PCB_IO_KICAD_SEXPR::validateCache( const wxString& aLibraryPath, bool checkModified )
3562{
3563 // Suppress font substitution warnings (RAII - automatically restored on scope exit)
3564 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
3565
3566 if( !m_cache || !m_cache->IsPath( aLibraryPath ) || ( checkModified && m_cache->IsModified() ) )
3567 {
3568 // a spectacular episode in memory management:
3569 delete m_cache;
3570 m_cache = new FP_CACHE( this, aLibraryPath );
3571 m_cache->Load();
3572 }
3573}
3574
3575
3576void PCB_IO_KICAD_SEXPR::FootprintEnumerate( wxArrayString& aFootprintNames,
3577 const wxString& aLibPath, bool aBestEfforts,
3578 const std::map<std::string, UTF8>* aProperties )
3579{
3580 wxDir dir( aLibPath );
3581 wxString errorMsg;
3582
3583 init( aProperties );
3584
3585 try
3586 {
3587 validateCache( aLibPath );
3588 }
3589 catch( const IO_ERROR& ioe )
3590 {
3591 errorMsg = ioe.What();
3592 }
3593
3594 // Some of the files may have been parsed correctly so we want to add the valid files to
3595 // the library.
3596
3597 for( const auto& footprint : m_cache->GetFootprints() )
3598 aFootprintNames.Add( footprint.first );
3599
3600 if( !errorMsg.IsEmpty() && !aBestEfforts )
3601 THROW_IO_ERROR( errorMsg );
3602}
3603
3604
3605const FOOTPRINT* PCB_IO_KICAD_SEXPR::getFootprint( const wxString& aLibraryPath,
3606 const wxString& aFootprintName,
3607 const std::map<std::string, UTF8>* aProperties,
3608 bool checkModified )
3609{
3610 init( aProperties );
3611
3612 try
3613 {
3614 validateCache( aLibraryPath, checkModified );
3615 }
3616 catch( const IO_ERROR& )
3617 {
3618 // do nothing with the error
3619 }
3620
3621 auto it = m_cache->GetFootprints().find( aFootprintName );
3622
3623 if( it == m_cache->GetFootprints().end() )
3624 return nullptr;
3625
3626 return it->second->GetFootprint().get();
3627}
3628
3629
3630const FOOTPRINT* PCB_IO_KICAD_SEXPR::GetEnumeratedFootprint( const wxString& aLibraryPath,
3631 const wxString& aFootprintName,
3632 const std::map<std::string, UTF8>* aProperties )
3633{
3634 return getFootprint( aLibraryPath, aFootprintName, aProperties, false );
3635}
3636
3637
3638bool PCB_IO_KICAD_SEXPR::FootprintExists( const wxString& aLibraryPath,
3639 const wxString& aFootprintName,
3640 const std::map<std::string, UTF8>* aProperties )
3641{
3642 // Note: checking the cache sounds like a good idea, but won't catch files which differ
3643 // only in case.
3644 //
3645 // Since this goes out to the native filesystem, we get platform differences (ie: MSW's
3646 // case-insensitive filesystem) handled "for free".
3647 // Warning: footprint names frequently contain a point. So be careful when initializing
3648 // wxFileName, and use a CTOR with extension specified
3649 wxFileName footprintFile( aLibraryPath, aFootprintName, FILEEXT::KiCadFootprintFileExtension );
3650
3651 return footprintFile.Exists();
3652}
3653
3654
3655FOOTPRINT* PCB_IO_KICAD_SEXPR::ImportFootprint( const wxString& aFootprintPath,
3656 wxString& aFootprintNameOut,
3657 const std::map<std::string, UTF8>* aProperties )
3658{
3659 wxString fcontents;
3660 wxFFile f( aFootprintPath );
3661
3662 // Suppress font substitution warnings (RAII - automatically restored on scope exit)
3663 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
3664
3665 if( !f.IsOpened() )
3666 return nullptr;
3667
3668 f.ReadAll( &fcontents );
3669
3670 aFootprintNameOut = wxFileName( aFootprintPath ).GetName();
3671
3672 return dynamic_cast<FOOTPRINT*>( Parse( fcontents ) );
3673}
3674
3675
3676FOOTPRINT* PCB_IO_KICAD_SEXPR::FootprintLoad( const wxString& aLibraryPath,
3677 const wxString& aFootprintName,
3678 bool aKeepUUID,
3679 const std::map<std::string, UTF8>* aProperties )
3680{
3681 // Suppress font substitution warnings (RAII - automatically restored on scope exit)
3682 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
3683
3684 const FOOTPRINT* footprint = getFootprint( aLibraryPath, aFootprintName, aProperties, true );
3685
3686 if( footprint )
3687 {
3688 FOOTPRINT* copy;
3689
3690 if( aKeepUUID )
3691 copy = static_cast<FOOTPRINT*>( footprint->Clone() );
3692 else
3693 copy = static_cast<FOOTPRINT*>( footprint->Duplicate( IGNORE_PARENT_GROUP ) );
3694
3695 copy->SetParent( nullptr );
3696 return copy;
3697 }
3698
3699 return nullptr;
3700}
3701
3702
3703void PCB_IO_KICAD_SEXPR::FootprintSave( const wxString& aLibraryPath, const FOOTPRINT* aFootprint,
3704 const std::map<std::string, UTF8>* aProperties )
3705{
3706 init( aProperties );
3707
3708 // In this public PLUGIN API function, we can safely assume it was
3709 // called for saving into a library path.
3711
3712 // Support saving to a single-file path like "/tmp/foo.kicad_mod" by treating the directory
3713 // as the library path and the file base-name as the footprint name.
3714 wxString libPath = aLibraryPath;
3715 wxString singleFileBaseName; // without extension
3716 bool saveSingleFile = false;
3717
3718 {
3719 wxFileName asFile( aLibraryPath );
3720
3721 if( asFile.GetExt() == FILEEXT::KiCadFootprintFileExtension )
3722 {
3723 saveSingleFile = true;
3724 libPath = asFile.GetPath();
3725 singleFileBaseName = asFile.GetName();
3726 }
3727 }
3728
3729 validateCache( libPath, !aProperties || !aProperties->contains( "skip_cache_validation" ) );
3730
3731 if( !m_cache->IsWritable() )
3732 {
3733 if( !m_cache->Exists() )
3734 {
3735 const wxString msg = wxString::Format( _( "Library '%s' does not exist.\n"
3736 "Would you like to create it?"),
3737 libPath );
3738
3739 if( !Pgm().IsGUI() || wxMessageBox( msg, _( "Library Not Found" ), wxYES_NO | wxICON_QUESTION ) != wxYES )
3740 return;
3741
3742 // Save throws its own IO_ERROR on failure, so no need to recreate here
3743 m_cache->Save( nullptr );
3744 }
3745 else
3746 {
3747 THROW_IO_ERRORF( _( "Library '%s' is read only." ), libPath );
3748 }
3749 }
3750
3751 // The map key used by the cache and the on-disk filename base.
3752 wxString footprintName = saveSingleFile ? singleFileBaseName
3753 : aFootprint->GetFPID().GetUniStringLibItemName();
3754
3755 wxString fpName = saveSingleFile ? singleFileBaseName
3756 : aFootprint->GetFPID().GetUniStringLibItemName();
3757 ReplaceIllegalFileNameChars( fpName, '_' );
3758
3759 // Quietly overwrite footprint and delete footprint file from path for any by same name.
3760 wxFileName fn( libPath, fpName, FILEEXT::KiCadFootprintFileExtension );
3761
3762 // Write through symlinks, don't replace them
3764
3765 if( !fn.IsOk() )
3766 THROW_IO_ERRORF( _( "Footprint file name '%s' is not valid." ), fn.GetFullPath() );
3767
3768 if( fn.FileExists() && !fn.IsFileWritable() )
3769 THROW_IO_ERRORF( _( "Insufficient permissions to delete '%s'." ), fn.GetFullPath() );
3770
3771 wxString fullPath = fn.GetFullPath();
3772 wxString fullName = fn.GetFullName();
3773 auto it = m_cache->GetFootprints().find( footprintName );
3774
3775 if( it != m_cache->GetFootprints().end() )
3776 {
3777 // Save() below writes atomically via sibling temp + rename, so no pre-delete.
3778 wxLogTrace( traceKicadPcbPlugin, wxT( "Replacing footprint file '%s'." ), fullPath );
3779 m_cache->GetFootprints().erase( footprintName );
3780 }
3781
3782 // I need my own copy for the cache
3783 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aFootprint->Clone() );
3784
3785 // It's orientation should be zero and it should be on the front layer.
3786 footprint->SetOrientation( ANGLE_0 );
3787
3788 if( footprint->GetLayer() != F_Cu )
3789 {
3790 PCBNEW_SETTINGS* cfg = dynamic_cast<PCBNEW_SETTINGS*>( Kiface().KifaceSettings() );
3791
3792 if( cfg )
3793 footprint->Flip( footprint->GetPosition(), cfg->m_FlipDirection );
3794 else
3795 footprint->Flip( footprint->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
3796 }
3797
3798 // Detach it from the board and its group
3799 footprint->SetParent( nullptr );
3800 footprint->SetParentGroup( nullptr );
3801
3802 // Now that the clone is detached from its parent board, any m_netinfo pointers its
3803 // descendants still carry reference NETINFO_ITEMs owned by that board and may dangle.
3804 // Force them all to the board-independent ORPHANED singleton before serialization.
3805 footprint->ClearAllNets();
3806
3807 wxLogTrace( traceKicadPcbPlugin, wxT( "Creating s-expr footprint file '%s'." ), fullPath );
3808 m_cache->GetFootprints().insert( footprintName,
3809 new FP_CACHE_ENTRY( footprint,
3810 WX_FILENAME( fn.GetPath(), fullName ) ) );
3811 m_cache->Save( footprint );
3812}
3813
3814
3815void PCB_IO_KICAD_SEXPR::FootprintDelete( const wxString& aLibraryPath,
3816 const wxString& aFootprintName,
3817 const std::map<std::string, UTF8>* aProperties )
3818{
3819 init( aProperties );
3820
3821 validateCache( aLibraryPath );
3822
3823 if( !m_cache->IsWritable() )
3824 THROW_IO_ERRORF( _( "Library '%s' is read only." ), aLibraryPath.GetData() );
3825
3826 m_cache->Remove( aFootprintName );
3827}
3828
3829
3830void PCB_IO_KICAD_SEXPR::ClearCachedFootprints( const wxString& aLibraryPath )
3831{
3832 if( m_cache && m_cache->IsPath( aLibraryPath ) )
3833 {
3834 delete m_cache;
3835 m_cache = nullptr;
3836 }
3837}
3838
3839
3840long long PCB_IO_KICAD_SEXPR::GetLibraryTimestamp( const wxString& aLibraryPath ) const
3841{
3842 return FP_CACHE::GetTimestamp( aLibraryPath );
3843}
3844
3845
3846void PCB_IO_KICAD_SEXPR::CreateLibrary( const wxString& aLibraryPath,
3847 const std::map<std::string, UTF8>* aProperties )
3848{
3849 if( wxDir::Exists( aLibraryPath ) )
3850 THROW_IO_ERRORF( _( "Cannot overwrite library path '%s'." ), aLibraryPath.GetData() );
3851
3852 init( aProperties );
3853
3854 delete m_cache;
3855 m_cache = new FP_CACHE( this, aLibraryPath );
3856 m_cache->Save();
3857}
3858
3859
3860bool PCB_IO_KICAD_SEXPR::DeleteLibrary( const wxString& aLibraryPath,
3861 const std::map<std::string, UTF8>* aProperties )
3862{
3863 wxFileName fn;
3864 fn.SetPath( aLibraryPath );
3865
3866 // Return if there is no library path to delete.
3867 if( !fn.DirExists() )
3868 return false;
3869
3870 if( !fn.IsDirWritable() )
3871 THROW_IO_ERRORF( _( "Insufficient permissions to delete folder '%s'." ), aLibraryPath.GetData() );
3872
3873 wxDir dir( aLibraryPath );
3874
3875 if( dir.HasSubDirs() )
3876 THROW_IO_ERRORF( _( "Library folder '%s' has unexpected sub-folders." ), aLibraryPath.GetData() );
3877
3878 // All the footprint files must be deleted before the directory can be deleted.
3879 if( dir.HasFiles() )
3880 {
3881 unsigned i;
3882 wxFileName tmp;
3883 wxArrayString files;
3884
3885 CollectFilesLoopSafe( aLibraryPath, files );
3886
3887 for( i = 0; i < files.GetCount(); i++ )
3888 {
3889 tmp = files[i];
3890
3891 if( tmp.GetExt() != FILEEXT::KiCadFootprintFileExtension )
3892 {
3893 THROW_IO_ERRORF( _( "Unexpected file '%s' found in library path '%s'." ),
3894 files[i].GetData(),
3895 aLibraryPath.GetData() );
3896 }
3897 }
3898
3899 for( i = 0; i < files.GetCount(); i++ )
3900 wxRemoveFile( files[i] );
3901 }
3902
3903 wxLogTrace( traceKicadPcbPlugin, wxT( "Removing footprint library '%s'." ),
3904 aLibraryPath.GetData() );
3905
3906 // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
3907 // we don't want that. we want bare metal portability with no UI here.
3908 if( !wxRmdir( aLibraryPath ) )
3909 THROW_IO_ERRORF( _( "Footprint library '%s' cannot be deleted." ), aLibraryPath.GetData() );
3910
3911 // For some reason removing a directory in Windows is not immediately updated. This delay
3912 // prevents an error when attempting to immediately recreate the same directory when over
3913 // writing an existing library.
3914#ifdef __WINDOWS__
3915 wxMilliSleep( 250L );
3916#endif
3917
3918 if( m_cache && !m_cache->IsPath( aLibraryPath ) )
3919 {
3920 delete m_cache;
3921 m_cache = nullptr;
3922 }
3923
3924 return true;
3925}
3926
3927
3928bool PCB_IO_KICAD_SEXPR::IsLibraryWritable( const wxString& aLibraryPath )
3929{
3930 init( nullptr );
3931
3932 validateCache( aLibraryPath );
3933
3934 return m_cache->IsWritable();
3935}
const char * name
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
@ LT_FRONT
Definition board.h:243
@ LT_BACK
Definition board.h:244
@ ZLO_FORCE_FLASHED
Definition board_item.h:73
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
wxString GetMajorMinorVersion()
Get only the major and minor version in a string major.minor.
bool SaveImageData(wxOutputStream &aOutStream) const
Write the bitmap data to aOutStream.
wxImage * GetImageData()
Definition bitmap_base.h:64
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
TEARDROP_PARAMETERS & GetTeardropParams()
Container for design settings for a BOARD object.
std::map< PCB_LAYER_ID, ZONE_LAYER_PROPERTIES > m_ZoneLayerProperties
const VECTOR2I & GetGridOrigin() const
int GetBoardThickness() const
The full thickness of the board including copper and masks.
const VECTOR2I & GetAuxOrigin() const
BOARD_STACKUP & GetStackupDescriptor()
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:83
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition board_item.h:295
virtual bool IsKnockout() const
Definition board_item.h:382
bool IsLocked() const override
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
FOOTPRINT * GetParentFootprint() const
VECTOR2I GetFPRelativePosition() const
Manage layers needed to make a physical board.
void FormatBoardStackup(OUTPUTFORMATTER *aFormatter, const BOARD *aBoard) const
Write the stackup info on board file.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:373
const NETINFO_LIST & GetNetInfo() const
Definition board.h:1098
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition board.cpp:3575
const std::vector< wxString > & GetVariantNames() const
Definition board.h:476
const GENERATORS & Generators() const
Definition board.h:434
void SetFileName(const wxString &aFileName)
Definition board.h:408
const PCB_POINTS & Points() const
Definition board.h:442
const PAGE_INFO & GetPageSettings() const
Definition board.h:901
const ZONES & Zones() const
Definition board.h:425
const GROUPS & Groups() const
The groups must maintain the following invariants.
Definition board.h:461
LAYER_T GetLayerType(PCB_LAYER_ID aLayer) const
Return the type of the copper layer given by aLayer.
Definition board.cpp:858
TITLE_BLOCK & GetTitleBlock()
Definition board.h:907
int GetCopperLayerCount() const
Definition board.cpp:994
const std::map< wxString, wxString > & GetProperties() const
Definition board.h:469
const FOOTPRINTS & Footprints() const
Definition board.h:421
const BOARD_ITEM_SET GetItemSet()
Collect every owned item (tracks, zones, generators, footprints, drawings, markers,...
Definition board.cpp:4056
const TRACKS & Tracks() const
Definition board.h:419
const CONSTRAINTS & Constraints() const
Geometric constraints (#2329) owned by this board.
Definition board.h:465
wxString GetVariantDescription(const wxString &aVariantName) const
Definition board.cpp:3068
const PCB_PLOT_PARAMS & GetPlotOptions() const
Definition board.h:904
bool LegacyTeardrops() const
Definition board.h:1517
wxString GroupsSanityCheck(bool repair=false)
Consistency check of internal m_groups structure.
Definition board.cpp:3799
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1158
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:1043
void Remove(BOARD_ITEM *aBoardItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition board.cpp:1503
const DRAWINGS & Drawings() const
Definition board.h:423
A lightweight representation of a component class.
const std::vector< COMPONENT_CLASS * > & GetConstituentClasses() const
Fetches a vector of the constituent classes for this (effective) class.
double AsDegrees() const
Definition eda_angle.h:116
bool IsZero() const
Definition eda_angle.h:136
EDA_ANGLE Normalize720()
Definition eda_angle.h:279
const LIB_ID & GetDesignBlockLibId() const
Definition eda_group.h:74
std::unordered_set< EDA_ITEM * > & GetItems()
Definition eda_group.h:50
wxString GetName() const
Definition eda_group.h:47
bool HasDesignBlockLink() const
Definition eda_group.h:71
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:96
const KIID m_Uuid
Definition eda_item.h:531
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
virtual void SetParentGroup(EDA_GROUP *aGroup)
Definition eda_item.h:113
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:89
const VECTOR2I & GetBezierC2() const
Definition eda_shape.h:283
FILL_T GetFillMode() const
Definition eda_shape.h:158
SHAPE_POLY_SET & GetPolyShape()
SHAPE_T GetShape() const
Definition eda_shape.h:185
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:240
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:190
wxString SHAPE_T_asString() const
const VECTOR2I & GetBezierC1() const
Definition eda_shape.h:280
int GetCornerRadius() const
bool IsPolyShapeValid() const
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:89
virtual VECTOR2I GetTextSize() const
Definition eda_text.h:282
virtual void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:532
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:110
bool IsKeepUpright() const
Definition eda_text.h:227
virtual bool IsVisible() const
Definition eda_text.h:208
KIFONT::FONT * GetFont() const
Definition eda_text.h:268
std::vector< std::unique_ptr< KIFONT::GLYPH > > * GetRenderCache(const KIFONT::FONT *aFont, const wxString &forResolvedText, const VECTOR2I &aOffset={ 0, 0 }) const
Definition eda_text.cpp:703
virtual EDA_ANGLE GetDrawRotation() const
Definition eda_text.h:400
bool GetAutoThickness() const
Definition eda_text.h:160
virtual void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition eda_text.cpp:279
virtual wxString GetShownText(bool aAllowExtraText, int aDepth=0) const
Return the string actually shown after processing of the base text.
Definition eda_text.h:121
virtual int GetTextThickness() const
Definition eda_text.h:149
bool IsEmpty() const
void WriteEmbeddedFiles(OUTPUTFORMATTER &aOut, bool aWriteData) const
Output formatter for the embedded files.
void ClearEmbeddedFiles(bool aDeleteFiles=true)
EMBEDDED_FILE * AddFile(const wxFileName &aName, bool aOverwrite)
Load a file from disk and adds it to the collection.
const std::map< wxString, std::shared_ptr< EMBEDDED_FILE > > & EmbeddedFileMap() const
Provide an iterable view of the file collection.
bool GetAreFontsEmbedded() const
A LINE_READER that reads from an open file.
Definition richio.h:154
void Rewind()
Rewind the file and resets the line number back to zero.
Definition richio.h:203
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
Definition richio.cpp:204
RAII class to set and restore the fontconfig reporter.
Definition reporter.h:368
bool GetDuplicatePadNumbersAreJumpers() const
Definition footprint.h:1165
const CASE_INSENSITIVE_MAP< FOOTPRINT_VARIANT > & GetVariants() const
Get all variants.
Definition footprint.h:1049
bool AllowSolderMaskBridges() const
Definition footprint.h:516
void SetFPID(const LIB_ID &aFPID)
Definition footprint.h:445
wxString GetLibDescription() const
Definition footprint.h:461
ZONE_CONNECTION GetLocalZoneConnection() const
Definition footprint.h:492
bool IsDNP() const
Definition footprint.h:985
EDA_ANGLE GetOrientation() const
Definition footprint.h:409
ZONES & Zones()
Definition footprint.h:381
PCB_POINTS & Points()
Definition footprint.h:390
bool IsExcludedFromBOM() const
Definition footprint.h:976
void SetOrientation(const EDA_ANGLE &aNewAngle)
const TRANSFORM_TRS & GetTransform() const
Definition footprint.h:422
wxString GetSheetname() const
Definition footprint.h:470
const std::vector< FP_UNIT_INFO > & GetUnitInfo() const
Definition footprint.h:956
const EXTRUDED_3D_BODY * GetExtrudedBody() const
Definition footprint.h:399
std::optional< int > GetLocalSolderPasteMargin() const
Definition footprint.h:485
EDA_ITEM * Clone() const override
Invoke a function on all children.
PCB_FIELD & Value()
read/write accessors:
Definition footprint.h:893
std::optional< int > GetLocalClearance() const
Definition footprint.h:479
std::vector< std::set< wxString > > & JumperPadGroups()
Each jumper pad group is a set of pad numbers that should be treated as internally connected.
Definition footprint.h:1172
CONSTRAINTS & Constraints()
Definition footprint.h:387
PCB_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this footprint.
std::deque< PAD * > & Pads()
Definition footprint.h:375
int GetAttributes() const
Definition footprint.h:510
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition footprint.h:420
LSET GetPrivateLayers() const
Definition footprint.h:315
bool AllowMissingCourtyard() const
Definition footprint.h:513
wxString GetSheetfile() const
Definition footprint.h:473
const std::vector< wxString > & GetNetTiePadGroups() const
Definition footprint.h:565
const LIB_ID & GetFPID() const
Definition footprint.h:444
bool IsLocked() const override
Definition footprint.h:637
bool IsExcludedFromPosFiles() const
Definition footprint.h:967
const LSET & GetStackupLayers() const
Definition footprint.h:508
PCB_FIELD & Reference()
Definition footprint.h:894
void ClearAllNets()
Clear (i.e.
bool IsNetTie() const
Definition footprint.h:523
std::optional< double > GetLocalSolderPasteMarginRatio() const
Definition footprint.h:488
void Flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection) override
Flip this object, i.e.
GROUPS & Groups()
Definition footprint.h:384
wxString GetFilters() const
Definition footprint.h:476
const wxArrayString * GetInitialComments() const
Return the initial comments block or NULL if none, without transfer of ownership.
Definition footprint.h:1293
void GetFields(std::vector< PCB_FIELD * > &aVector, bool aVisibleOnly) const
Populate a std::vector with PCB_TEXTs.
std::vector< FP_3DMODEL > & Models()
Definition footprint.h:395
BOARD_ITEM * Duplicate(bool addToParentGroup, BOARD_COMMIT *aCommit=nullptr) const override
Create a copy of this BOARD_ITEM.
const COMPONENT_CLASS * GetStaticComponentClass() const
Returns the component class for this footprint.
const KIID_PATH & GetPath() const
Definition footprint.h:467
std::optional< int > GetLocalSolderMaskMargin() const
Definition footprint.h:482
wxString GetKeywords() const
Definition footprint.h:464
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition footprint.h:1321
FOOTPRINT_STACKUP GetStackupMode() const
Definition footprint.h:501
bool IsPlaced() const
Definition footprint.h:673
VECTOR2I GetPosition() const override
Definition footprint.h:406
DRAWINGS & GraphicalItems()
Definition footprint.h:378
Helper class for creating a footprint library cache.
std::unique_ptr< FOOTPRINT > m_footprint
WX_FILENAME m_filename
FP_CACHE_ENTRY(FOOTPRINT *aFootprint, const WX_FILENAME &aFileName)
const WX_FILENAME & GetFileName() const
std::unique_ptr< FOOTPRINT > & GetFootprint()
static long long GetTimestamp(const wxString &aLibPath)
Generate a timestamp representing all source files in the cache (including the parent directory).
boost::ptr_map< wxString, FP_CACHE_ENTRY > m_footprints
PCB_IO_KICAD_SEXPR * m_owner
bool IsModified()
Return true if the cache is not up-to-date.
long long m_cache_timestamp
wxString m_lib_raw_path
void SetPath(const wxString &aPath)
wxFileName m_lib_path
bool IsPath(const wxString &aPath) const
Check if aPath is the same as the current cache path.
void Save(FOOTPRINT *aFootprintFilter=nullptr)
Save the footprint cache or a single footprint from it to disk.
FP_CACHE(PCB_IO_KICAD_SEXPR *aOwner, const wxString &aLibraryPath)
boost::ptr_map< wxString, FP_CACHE_ENTRY > & GetFootprints()
void Remove(const wxString &aFootprintName)
PROGRESS_REPORTER * m_progressReporter
Progress reporter to track the progress of the operation, may be nullptr.
Definition io_base.h:241
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()
APP_SETTINGS_BASE * KifaceSettings() const
Definition kiface_base.h:91
virtual bool IsOutline() const
Definition font.h:102
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:398
virtual void SetLineWidth(float aLineWidth)
Set the line width.
virtual wxString GetClass() const =0
Return the class name.
wxString AsString() const
Definition kiid.cpp:423
LAYER_MAPPING_HANDLER m_layer_mapping_handler
Callback to get layer mapping.
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
UTF8 Format() const
Definition lib_id.cpp:132
const wxString GetUniStringLibItemName() const
Get strings for display messages in dialogs.
Definition lib_id.h:108
const UTF8 & GetLibItemName() const
Definition lib_id.h:98
An abstract class from which implementation specific LINE_READERs may be derived to read single lines...
Definition richio.h:62
static LOAD_INFO_REPORTER & GetInstance()
Definition reporter.cpp:306
REPORTER & Report(const wxString &aMsg, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
Definition reporter.cpp:291
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition lseq.h:47
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:604
LSEQ CuStack() const
Return a sequence of copper layers in starting from the front/top and extending to the back/bottom.
Definition lset.cpp:259
LSEQ TechAndUserUIOrder() const
Return the technical and user layers in the order shown in layer widget.
Definition lset.cpp:272
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition lset.cpp:309
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:595
static wxString Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition lset.cpp:184
Handle the data for a net.
Definition netinfo.h:46
const wxString & GetNetname() const
Definition netinfo.h:100
An interface used to output 8 bit text in a convenient way.
Definition richio.h:291
A PADSTACK defines the characteristics of a single or multi-layer pad, in the IPC sense of the word.
Definition padstack.h:157
std::optional< int > & Clearance(PCB_LAYER_ID aLayer=F_Cu)
Definition padstack.cpp:979
MASK_LAYER_PROPS & FrontOuterLayers()
Definition padstack.h:372
void ForEachUniqueLayer(const std::function< void(PCB_LAYER_ID)> &aMethod) const
Runs the given callable for each active unique copper layer in this padstack, meaning F_Cu for MODE::...
std::optional< int > & ThermalSpokeWidth(PCB_LAYER_ID aLayer=F_Cu)
VECTOR2I & Offset(PCB_LAYER_ID aLayer)
Definition padstack.cpp:884
EDA_ANGLE ThermalSpokeAngle(PCB_LAYER_ID aLayer=F_Cu) const
POST_MACHINING_PROPS & FrontPostMachining()
Definition padstack.h:360
std::optional< int > & ThermalGap(PCB_LAYER_ID aLayer=F_Cu)
DRILL_PROPS & TertiaryDrill()
Definition padstack.h:357
DRILL_PROPS & Drill()
Definition padstack.h:351
const VECTOR2I & Size(PCB_LAYER_ID aLayer) const
Definition padstack.cpp:854
@ NORMAL
Shape is the same on all layers.
Definition padstack.h:171
@ FRONT_INNER_BACK
Up to three shapes can be defined (F_Cu, inner copper layers, B_Cu)
Definition padstack.h:172
DRILL_PROPS & SecondaryDrill()
Definition padstack.h:354
POST_MACHINING_PROPS & BackPostMachining()
Definition padstack.h:363
MODE Mode() const
Definition padstack.h:335
MASK_LAYER_PROPS & BackOuterLayers()
Definition padstack.h:375
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:177
static constexpr PCB_LAYER_ID INNER_LAYERS
! The layer identifier to use for "inner layers" on top/inner/bottom padstacks
Definition padstack.h:180
std::optional< ZONE_CONNECTION > & ZoneConnection(PCB_LAYER_ID aLayer=F_Cu)
Definition pad.h:61
PAD_PROP GetProperty() const
Definition pad.h:558
bool GetRemoveUnconnected() const
Definition pad.h:862
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition pad.h:552
const std::vector< std::shared_ptr< PCB_SHAPE > > & GetPrimitives(PCB_LAYER_ID aLayer) const
Accessor to the basic shape list for custom-shaped pads.
Definition pad.h:370
const ZONE_LAYER_OVERRIDE & GetZoneLayerOverride(PCB_LAYER_ID aLayer) const
Definition pad.cpp:480
std::optional< double > GetLocalSolderPasteMarginRatio() const
Definition pad.h:595
const wxString & GetPinType() const
Definition pad.h:160
PAD_ATTRIB GetAttribute() const
Definition pad.h:555
const wxString & GetPinFunction() const
Definition pad.h:154
const wxString & GetNumber() const
Definition pad.h:143
const VECTOR2I & GetDelta(PCB_LAYER_ID aLayer) const
Definition pad.h:302
EDA_ANGLE GetThermalSpokeAngle() const
Definition pad.h:745
VECTOR2I GetOffset(PCB_LAYER_ID aLayer) const
Definition pad.cpp:796
VECTOR2I GetDrillSize() const
Definition pad.h:315
double GetRoundRectRadiusRatio(PCB_LAYER_ID aLayer) const
Definition pad.h:800
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition pad.h:202
bool GetKeepTopBottom() const
Definition pad.h:877
int GetPadToDieDelay() const
Definition pad.h:576
std::optional< int > GetLocalClearance() const override
Return any local clearances set in the "classic" (ie: pre-rule) system.
Definition pad.h:578
const PADSTACK & Padstack() const
Definition pad.h:326
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition pad.cpp:1723
PAD_DRILL_SHAPE GetDrillShape() const
Definition pad.h:429
int GetChamferPositions(PCB_LAYER_ID aLayer) const
Definition pad.h:840
std::optional< int > GetLocalSolderPasteMargin() const
Definition pad.h:588
PAD_SIM_ELECTRICAL_TYPE GetSimElectricalType() const
Definition pad.h:568
std::optional< int > GetLocalSolderMaskMargin() const
Definition pad.h:581
double GetChamferRectRatio(PCB_LAYER_ID aLayer) const
Definition pad.h:823
std::optional< int > GetLocalThermalSpokeWidthOverride() const
Definition pad.h:729
ZONE_CONNECTION GetLocalZoneConnection() const
Definition pad.h:606
CUSTOM_SHAPE_ZONE_MODE GetCustomShapeInZoneOpt() const
Definition pad.h:224
int GetLocalThermalGapOverride(wxString *aSource) const
Definition pad.cpp:2149
PAD_SHAPE GetAnchorPadShape(PCB_LAYER_ID aLayer) const
Definition pad.h:216
int GetPadToDieLength() const
Definition pad.h:573
void Format(OUTPUTFORMATTER *aFormatter) const
Output the page class to aFormatter in s-expression form.
FLIP_DIRECTION m_FlipDirection
const VECTOR2I & GetMid() const
Definition pcb_track.h:286
const VECTOR2I & GetMargin() const
Get the barcode margin (in internal units).
VECTOR2I GetPosition() const override
Get the position (center) of the barcode in internal units.
wxString GetText() const
int GetTextSize() const
bool IsKnockout() const override
int GetHeight() const
Get the barcode height (in internal units).
BARCODE_ECC_T GetErrorCorrection() const
bool GetShowText() const
EDA_ANGLE GetAngle() const
BARCODE_T GetKind() const
Returns the type of the barcode (QR, CODE_39, etc.).
int GetWidth() const
Get the barcode width (in internal units).
A geometric constraint between board items (issue #2329).
const std::vector< CONSTRAINT_MEMBER > & GetMembers() const
std::optional< double > GetValue() const
bool IsDriving() const
A driving constraint forces its value; a reference (non-driving) one only measures it.
PCB_CONSTRAINT_TYPE GetConstraintType() const
bool HasValue() const
Abstract dimension API.
wxString GetOverrideText() const
wxString GetSuffix() const
int GetLineThickness() const
DIM_TEXT_POSITION GetTextPositionMode() const
bool GetKeepTextAligned() const
DIM_PRECISION GetPrecision() const
wxString GetPrefix() const
DIM_UNITS_MODE GetUnitsMode() const
DIM_UNITS_FORMAT GetUnitsFormat() const
DIM_ARROW_DIRECTION GetArrowDirection() const
virtual VECTOR2I GetEnd() const
bool GetSuppressZeroes() const
int GetExtensionOffset() const
virtual VECTOR2I GetStart() const
The dimension's origin is the first feature point for the dimension.
int GetArrowLength() const
bool GetOverrideTextEnabled() const
For better understanding of the points that make a dimension:
int GetHeight() const
int GetExtensionHeight() const
Mark the center of a circle or arc with a cross shape.
A leader is a dimension-like object pointing to a specific point.
DIM_TEXT_BORDER GetTextBorder() const
An orthogonal dimension is like an aligned dimension, but the extension lines are locked to the X or ...
A radial dimension indicates either the radius or diameter of an arc or circle.
int GetLeaderLength() const
virtual const STRING_ANY_MAP GetProperties() const
virtual wxString GetGeneratorType() const
A set of BOARD_ITEMs (i.e., without duplicates).
Definition pcb_group.h:51
Read a Pcbnew s-expression formatted LINE_READER object and returns the appropriate BOARD_ITEM object...
const std::vector< wxString > & GetParseWarnings() const
Return any non-fatal parse warnings that occurred during parsing.
void SetLayerMappingHandler(LAYER_MAPPING_HANDLER aHandler)
Handler to remap an appended board's layers onto the destination board, used on mismatch.
bool IsTooRecent()
Return whether a version number, if any was parsed, was too recent.
bool IsValidBoardHeader()
Partially parse the input and check if it matches expected header.
wxString GetRequiredVersion()
Return a string representing the version of KiCad required to open this file.
A #PLUGIN derivation for saving and loading Pcbnew s-expression formatted files.
BOARD * DoLoad(LINE_READER &aReader, BOARD *aAppendToMe, const std::map< std::string, UTF8 > *aProperties, PROGRESS_REPORTER *aProgressReporter, unsigned aLineCount)
bool CanReadBoard(const wxString &aFileName) const override
Checks if this PCB_IO can read the specified board file.
void formatProperties(const BOARD *aBoard) const
formats the Nets and Netclasses
FOOTPRINT * ImportFootprint(const wxString &aFootprintPath, wxString &aFootprintNameOut, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load a single footprint from aFootprintPath and put its name in aFootprintNameOut.
void FootprintDelete(const wxString &aLibraryPath, const wxString &aFootprintName, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Delete aFootprintName from the library at aLibraryPath.
long long GetLibraryTimestamp(const wxString &aLibraryPath) const override
Generate a timestamp representing all the files in the library (including the library directory).
bool IsLibraryWritable(const wxString &aLibraryPath) override
Return true if the library at aLibraryPath is writable.
void formatTeardropParameters(const TEARDROP_PARAMETERS &tdParams) const
bool DeleteLibrary(const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Delete an existing library and returns true, or if library does not exist returns false,...
const FOOTPRINT * GetEnumeratedFootprint(const wxString &aLibraryPath, const wxString &aFootprintName, const std::map< std::string, UTF8 > *aProperties=nullptr) override
A version of FootprintLoad() for use after FootprintEnumerate() for more efficient cache management.
void CreateLibrary(const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Create a new empty library at aLibraryPath empty.
void FootprintEnumerate(wxArrayString &aFootprintNames, const wxString &aLibraryPath, bool aBestEfforts, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Return a list of footprint names contained within the library at aLibraryPath.
void formatPolyPts(const SHAPE_LINE_CHAIN &outline, const FOOTPRINT *aParentFP=nullptr) const
FP_CACHE * m_cache
Footprint library cache.
void formatBoardLayers(const BOARD *aBoard) const
formats the board layer information
FOOTPRINT * FootprintLoad(const wxString &aLibraryPath, const wxString &aFootprintName, bool aKeepUUID=false, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load a footprint having aFootprintName from the aLibraryPath containing a library format that this PC...
BOARD * LoadBoard(const wxString &aFileName, BOARD *aAppendToMe, const std::map< std::string, UTF8 > *aProperties=nullptr, PROJECT *aProject=nullptr) override
Load information from some input file format that this PCB_IO implementation knows about into either ...
bool FootprintExists(const wxString &aLibraryPath, const wxString &aFootprintName, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Check for the existence of a footprint.
void FootprintSave(const wxString &aLibraryPath, const FOOTPRINT *aFootprint, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Write aFootprint to an existing library located at aLibraryPath.
void format(const BOARD *aBoard) const
void SaveBoard(const wxString &aFileName, BOARD *aBoard, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Write aBoard to a storage file in a format that this PCB_IO implementation knows about or it can be u...
void formatLayers(LSET aLayerMask, bool aEnumerateLayers, bool aIsZone=false) const
void formatGeneral(const BOARD *aBoard) const
formats the General section of the file
void formatVariants(const BOARD *aBoard) const
formats the board variant registry
void ClearCachedFootprints(const wxString &aLibraryPath) override
Clear any cached footprint data for the given library path.
void formatSetup(const BOARD *aBoard) const
formats the board setup information
void FormatBoardToFormatter(OUTPUTFORMATTER *aOut, BOARD *aBoard, const std::map< std::string, UTF8 > *aProperties=nullptr)
Serialize a BOARD to an OUTPUTFORMATTER without file I/O or Prettify.
std::function< bool(wxString aTitle, int aIcon, wxString aMsg, wxString aAction)> m_queryUserCallback
BOARD_ITEM * Parse(const wxString &aClipboardSourceInput)
void init(const std::map< std::string, UTF8 > *aProperties)
STRING_FORMATTER m_sf
void Format(const BOARD_ITEM *aItem) const
Output aItem to aFormatter in s-expression format.
void formatLayer(PCB_LAYER_ID aLayer, bool aIsKnockout=false) const
void formatHeader(const BOARD *aBoard) const
writes everything that comes before the board_items, like settings and layers etc
const FOOTPRINT * getFootprint(const wxString &aLibraryPath, const wxString &aFootprintName, const std::map< std::string, UTF8 > *aProperties, bool checkModified)
PCB_IO_KICAD_SEXPR(int aControlFlags=CTL_FOR_BOARD)
OUTPUTFORMATTER * m_out
output any Format()s to this, no ownership
void validateCache(const wxString &aLibraryPath, bool checkModified=true)
void formatRenderCache(const EDA_TEXT *aText) const
LINE_READER * m_reader
no ownership
BOARD * m_board
The board BOARD being worked on, no ownership here.
Definition pcb_io.h:349
virtual bool CanReadBoard(const wxString &aFileName) const
Checks if this PCB_IO can read the specified board file.
Definition pcb_io.cpp:38
PCB_IO(const wxString &aName)
Definition pcb_io.h:342
const std::map< std::string, UTF8 > * m_props
Properties passed via Save() or Load(), no ownership, may be NULL.
Definition pcb_io.h:352
void Format(OUTPUTFORMATTER *aFormatter) const
A PCB_POINT is a 0-dimensional point that is used to mark a position on a PCB, or more usually a foot...
Definition pcb_point.h:39
int GetSize() const
Definition pcb_point.h:69
VECTOR2I GetLibraryPosition() const
Definition pcb_point.h:61
Object to handle a bitmap image that can be inserted in a PCB.
VECTOR2I GetPosition() const override
Get the position of the image (this is the center of the image).
REFERENCE_IMAGE & GetReferenceImage()
EDA_ANGLE GetLibraryEllipseEndAngle() const
Definition pcb_shape.h:229
std::optional< int > GetLocalSolderMaskMargin() const
Definition pcb_shape.h:332
bool HasSolderMask() const
Definition pcb_shape.h:329
int GetLibraryEllipseMinorRadius() const
Definition pcb_shape.h:226
EDA_ANGLE GetLibraryEllipseStartAngle() const
Definition pcb_shape.h:228
int GetLibraryEllipseMajorRadius() const
Definition pcb_shape.h:225
EDA_ANGLE GetLibraryEllipseRotation() const
Definition pcb_shape.h:227
SHAPE_T GetLibraryShape() const
Definition pcb_shape.h:222
VECTOR2I GetLibraryEllipseCenter() const
Definition pcb_shape.h:224
VECTOR2I GetLibraryEnd() const
Definition pcb_shape.h:221
const SHAPE_POLY_SET & GetLibPoly() const
Definition pcb_shape.h:275
VECTOR2I GetLibraryStart() const
Definition pcb_shape.h:220
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
STROKE_PARAMS GetStroke() const override
VECTOR2I GetLibraryArcMid() const
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition pcb_shape.h:68
bool StrokeRows() const
Definition pcb_table.h:103
int GetRowCount() const
Definition pcb_table.h:121
bool StrokeHeaderSeparator() const
Definition pcb_table.h:61
bool StrokeColumns() const
Definition pcb_table.h:100
bool StrokeExternal() const
Definition pcb_table.h:58
std::vector< PCB_TABLECELL * > GetCells() const
Definition pcb_table.h:156
int GetColCount() const
Definition pcb_table.h:119
const STROKE_PARAMS & GetSeparatorsStroke() const
Definition pcb_table.h:82
const STROKE_PARAMS & GetBorderStroke() const
Definition pcb_table.h:64
int GetColWidth(int aCol) const
Definition pcb_table.h:128
int GetRowHeight(int aRow) const
Definition pcb_table.h:138
int GetShape() const
Definition pcb_target.h:54
int GetWidth() const
Definition pcb_target.h:60
int GetSize() const
Definition pcb_target.h:57
VECTOR2I GetPosition() const override
Definition pcb_target.h:51
bool IsBorderEnabled() const
Disables the border, this is done by changing the stroke internally.
int GetMarginBottom() const
EDA_ANGLE GetTextAngle() const override
int GetMarginLeft() const
Definition pcb_textbox.h:99
int GetMarginRight() const
int GetMarginTop() const
EDA_ANGLE GetTextAngle() const override
Definition pcb_text.cpp:544
VECTOR2I GetTextPos() const override
Definition pcb_text.cpp:445
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
bool HasSolderMask() const
Definition pcb_track.h:117
std::optional< int > GetLocalSolderMaskMargin() const
Definition pcb_track.h:120
const VECTOR2I & GetStart() const
Definition pcb_track.h:93
const VECTOR2I & GetEnd() const
Definition pcb_track.h:90
virtual int GetWidth() const
Definition pcb_track.h:87
bool Finish() override
Runs prettification over the buffered bytes, writes them to the sibling temp file,...
Definition richio.cpp:690
A progress reporter interface for use in multi-threaded environments.
Container for project specific data.
Definition project.h:63
A REFERENCE_IMAGE is a wrapper around a BITMAP_IMAGE that is displayed in an editor as a reference fo...
const BITMAP_BASE & GetImage() const
Get the underlying image.
double GetImageScale() const
const VECTOR2I & GetArcMid() const
Definition shape_arc.h:116
const VECTOR2I & GetP1() const
Definition shape_arc.h:115
const VECTOR2I & GetP0() const
Definition shape_arc.h:114
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const SHAPE_ARC & Arc(size_t aArc) const
int PointCount() const
Return the number of points (vertices) in this line chain.
ssize_t ArcIndex(size_t aSegment) const
Return the arc index for the given segment index.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
Represent a set of closed polygons.
POLYGON & Polygon(int aIndex)
Return the aIndex-th subpolygon in the set.
std::vector< SHAPE_LINE_CHAIN > POLYGON
represents a single polygon outline with holes.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int OutlineCount() const
Return the number of outlines in the set.
Is a LINE_READER that reads from a multiline 8 bit wide std::string.
Definition richio.h:222
Simple container to manage line stroke parameters.
int GetWidth() const
void SetWidth(int aWidth)
void Format(OUTPUTFORMATTER *out, const EDA_IU_SCALE &aIuScale) const
TEARDROP_PARAMETARS is a helper class to handle parameters needed to build teardrops for a board thes...
double m_BestWidthRatio
The height of a teardrop as ratio between height and size of pad/via.
int m_TdMaxLen
max allowed length for teardrops in IU. <= 0 to disable
bool m_AllowUseTwoTracks
True to create teardrops using 2 track segments if the first in too small.
int m_TdMaxWidth
max allowed height for teardrops in IU. <= 0 to disable
double m_BestLengthRatio
The length of a teardrop as ratio between length and size of pad/via.
double m_WidthtoSizeFilterRatio
The ratio (H/D) between the via/pad size and the track width max value to create a teardrop 1....
bool m_TdOnPadsInZones
A filter to exclude pads inside zone fills.
bool m_Enabled
Flag to enable teardrops.
bool m_CurvedEdges
True if the teardrop should be curved.
virtual void Format(OUTPUTFORMATTER *aFormatter) const
Output the object to aFormatter in s-expression form.
VECTOR2I InverseApply(const VECTOR2I &aPoint) const
double GetScaleX() const
double GetScaleY() const
const char * c_str() const
Definition utf8.h:104
A wrapper around a wxFileName which is much more performant with a subset of the API.
Definition wx_filename.h:46
void SetFullName(const wxString &aFileNameAndExtension)
static void ResolvePossibleSymlinks(wxFileName &aFilename)
wxString GetName() const
wxString GetFullPath() const
long long GetTimestamp()
Handle a list of polygons defining a copper zone.
Definition zone.h:70
int GetHatchBorderAlgorithm() const
Definition zone.h:343
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition zone.h:813
std::optional< int > GetLocalClearance() const override
Definition zone.cpp:1012
const THIEVING_SETTINGS & GetThievingSettings() const
Definition zone.h:351
bool GetDoNotAllowVias() const
Definition zone.h:824
ZONE_LAYER_PROPERTIES & LayerProperties(PCB_LAYER_ID aLayer)
Definition zone.h:146
wxString GetPlacementAreaSource() const
Definition zone.h:818
std::shared_ptr< SHAPE_POLY_SET > GetFilledPolysList(PCB_LAYER_ID aLayer) const
Definition zone.h:697
bool GetDoNotAllowPads() const
Definition zone.h:826
PLACEMENT_SOURCE_T GetPlacementAreaSourceType() const
Definition zone.h:820
bool GetDoNotAllowTracks() const
Definition zone.h:825
bool IsFilled() const
Definition zone.h:306
ISLAND_REMOVAL_MODE GetIslandRemovalMode() const
Definition zone.h:835
SHAPE_POLY_SET * Outline()
Definition zone.h:418
bool IsIsland(PCB_LAYER_ID aLayer, int aPolyIdx) const
Check if a given filled polygon is an insulated island.
Definition zone.cpp:1631
long long int GetMinIslandArea() const
Definition zone.h:838
const wxString & GetZoneName() const
Definition zone.h:160
int GetMinThickness() const
Definition zone.h:315
ZONE_CONNECTION GetPadConnection() const
Definition zone.h:312
int GetHatchThickness() const
Definition zone.h:325
double GetHatchHoleMinArea() const
Definition zone.h:340
bool GetPlacementAreaEnabled() const
Definition zone.h:815
bool IsTeardropArea() const
Definition zone.h:788
int GetThermalReliefSpokeWidth() const
Definition zone.h:259
int GetBorderHatchPitch() const
HatchBorder related methods.
Definition zone.h:848
ZONE_BORDER_DISPLAY_STYLE GetHatchStyle() const
Definition zone.h:685
EDA_ANGLE GetHatchOrientation() const
Definition zone.h:331
bool GetDoNotAllowFootprints() const
Definition zone.h:827
ZONE_FILL_MODE GetFillMode() const
Definition zone.h:238
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition zone.h:133
int GetHatchGap() const
Definition zone.h:328
TEARDROP_TYPE GetTeardropAreaType() const
Definition zone.h:799
double GetHatchSmoothingValue() const
Definition zone.h:337
bool GetDoNotAllowZoneFills() const
Definition zone.h:823
int GetHatchSmoothingLevel() const
Definition zone.h:334
unsigned int GetCornerRadius() const
Definition zone.h:756
int GetCornerSmoothingType() const
Definition zone.h:752
bool IsOnCopperLayer() const override
Definition zone.cpp:594
PCB_LAYER_ID GetFirstLayer() const
Definition zone.cpp:574
int GetThermalReliefGap() const
Definition zone.h:248
unsigned GetAssignedPriority() const
Definition zone.h:122
int GetNumCorners(void) const
Access to m_Poly parameters.
Definition zone.h:615
This file is part of the common library.
#define CTL_OMIT_HYPERLINK
Omit the hyperlink attribute in .kicad_xxx files.
Definition ctl_flags.h:46
#define CTL_OMIT_UUIDS
Omit component unique ids (useless in library)
Definition ctl_flags.h:30
#define CTL_OMIT_FOOTPRINT_VERSION
Omit the version string from the (footprint) sexpr group.
Definition ctl_flags.h:39
#define CTL_OMIT_INITIAL_COMMENTS
Omit FOOTPRINT initial comments.
Definition ctl_flags.h:43
#define CTL_OMIT_LIBNAME
Omit lib alias when saving (used for board/not library).
Definition ctl_flags.h:37
#define CTL_OMIT_PATH
Omit component sheet time stamp (useless in library).
Definition ctl_flags.h:33
#define CTL_OMIT_AT
Omit position and rotation.
Definition ctl_flags.h:35
#define CTL_OMIT_PAD_NETS
Omit pads net names (useless in library).
Definition ctl_flags.h:29
#define CTL_OMIT_COLOR
Omit the color attribute in .kicad_xxx files.
Definition ctl_flags.h:45
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:413
static constexpr EDA_ANGLE ANGLE_45
Definition eda_angle.h:412
#define IGNORE_PARENT_GROUP
Definition eda_item.h:53
SHAPE_T
Definition eda_shape.h:44
@ ELLIPSE
Definition eda_shape.h:52
@ SEGMENT
Definition eda_shape.h:46
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:47
@ ELLIPSE_ARC
Definition eda_shape.h:53
@ REVERSE_HATCH
Definition eda_shape.h:65
@ HATCH
Definition eda_shape.h:64
@ FILLED_SHAPE
Fill with object color.
Definition eda_shape.h:61
@ CROSS_HATCH
Definition eda_shape.h:66
EDA_DATA_TYPE
The type of unit.
Definition eda_units.h:34
@ FP_SMD
Definition footprint.h:84
@ FP_DNP
Definition footprint.h:89
@ FP_EXCLUDE_FROM_POS_FILES
Definition footprint.h:85
@ FP_BOARD_ONLY
Definition footprint.h:87
@ FP_EXCLUDE_FROM_BOM
Definition footprint.h:86
@ FP_THROUGH_HOLE
Definition footprint.h:83
@ EXPAND_INNER_LAYERS
The 'normal' stackup handling, where there is a single inner layer (In1) and rule areas using it expa...
Definition footprint.h:148
void CollectFilesLoopSafe(const wxString &aRoot, wxArrayString &aFiles, const wxString &aFileSpec, int aFlags)
Recursively collect every file under aRoot, deduplicating subdirectories by their resolved path.
Definition gestfich.cpp:873
static const std::string KiCadFootprintFileExtension
const wxChar *const traceKicadPcbPlugin
Flag to enable KiCad PCB plugin debug output.
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
#define THROW_IO_ERRORF(msg,...)
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
#define MAX_CU_LAYERS
Definition layer_ids.h:172
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:683
bool IsExternalCopperLayer(int aLayerId)
Test whether a layer is an external (F_Cu or B_Cu) copper layer.
Definition layer_ids.h:694
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ F_CrtYd
Definition layer_ids.h:112
@ B_Adhes
Definition layer_ids.h:99
@ F_Paste
Definition layer_ids.h:100
@ F_Adhes
Definition layer_ids.h:98
@ B_Mask
Definition layer_ids.h:94
@ B_Cu
Definition layer_ids.h:61
@ F_Mask
Definition layer_ids.h:93
@ B_Paste
Definition layer_ids.h:101
@ UNSELECTED_LAYER
Definition layer_ids.h:58
@ F_Fab
Definition layer_ids.h:115
@ F_SilkS
Definition layer_ids.h:96
@ B_CrtYd
Definition layer_ids.h:111
@ UNDEFINED_LAYER
Definition layer_ids.h:57
@ User_1
Definition layer_ids.h:120
@ B_SilkS
Definition layer_ids.h:97
@ PCB_LAYER_ID_COUNT
Definition layer_ids.h:167
@ F_Cu
Definition layer_ids.h:60
@ B_Fab
Definition layer_ids.h:114
This file contains miscellaneous commonly used macros and functions.
#define UNIMPLEMENTED_FOR(type)
Definition macros.h:92
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
Definition mirror.h:25
KICOMMON_API std::string FormatAngle(const EDA_ANGLE &aAngle)
Convert aAngle from board units to a string appropriate for writing to file.
KICOMMON_API std::string FormatInternalUnits(const EDA_IU_SCALE &aIuScale, int aValue, EDA_DATA_TYPE aDataType=EDA_DATA_TYPE::DISTANCE)
Converts aValue from internal units to a string appropriate for writing to file.
void FormatOptBool(OUTPUTFORMATTER *aOut, const wxString &aKey, std::optional< bool > aValue)
Writes an optional boolean to the formatter.
void FormatUuid(OUTPUTFORMATTER *aOut, const KIID &aUuid)
void FormatStreamData(OUTPUTFORMATTER &aOut, const wxStreamBuffer &aStream)
Write binary data to the formatter as base 64 encoded string.
void FormatBool(OUTPUTFORMATTER *aOut, const wxString &aKey, bool aValue)
Writes a boolean to the formatter, in the style (aKey [yes|no])
long long TimestampDir(const wxString &aDirPath, const wxString &aFilespec)
Computes a hash of modification times and sizes for files matching a pattern.
Definition unix/io.cpp:123
constexpr char APPEND_PRESERVE_DESTINATION_STACKUP[]
Definition pcb_io.h:43
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:103
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:99
@ PTH
Plated through hole pad.
Definition padstack.h:98
@ CONN
Like smd, does not appear on the solder paste layer (default) Note: also has a special attribute in G...
Definition padstack.h:100
@ CHAMFERED_RECT
Definition padstack.h:60
@ ROUNDRECT
Definition padstack.h:57
@ TRAPEZOID
Definition padstack.h:56
@ RECTANGLE
Definition padstack.h:54
@ FIDUCIAL_LOCAL
a fiducial (usually a smd) local to the parent footprint
Definition padstack.h:118
@ FIDUCIAL_GLBL
a fiducial (usually a smd) for the full board
Definition padstack.h:117
@ MECHANICAL
a pad used for mechanical support
Definition padstack.h:122
@ PRESSFIT
a PTH with a hole diameter with tight tolerances for press fit pin
Definition padstack.h:123
@ HEATSINK
a pad used as heat sink, usually in SMD footprints
Definition padstack.h:120
@ NONE
no special fabrication property
Definition padstack.h:115
@ TESTPOINT
a test point pad
Definition padstack.h:119
@ CASTELLATED
a pad with a castellated through hole
Definition padstack.h:121
@ BGA
Smd pad, used in BGA footprints.
Definition padstack.h:116
BARCODE class definition.
bool ConstraintValueIsLength(PCB_CONSTRAINT_TYPE aType)
True if this type's value is a length in IU (serialized in mm); false for an angle in degrees.
const char * ConstraintTypeToken(PCB_CONSTRAINT_TYPE aType)
Stable file-format token for a constraint type (e.g. "parallel"). Used by serialization.
const char * ConstraintAnchorToken(CONSTRAINT_ANCHOR aAnchor)
Stable file-format token for a member anchor (e.g. "start").
@ VERTEX
An indexed rectangle corner or polygon outline vertex; pairs with CONSTRAINT_MEMBER::m_index.
Class to handle a set of BOARD_ITEMs.
bool isDefaultTeardropParameters(const TEARDROP_PARAMETERS &tdParams)
static VECTOR2I unbakeSize(const VECTOR2I &aSize, const FOOTPRINT *aParentFP)
std::string formatInternalUnits(const int aValue, const EDA_DATA_TYPE aDataType=EDA_DATA_TYPE::DISTANCE)
static int unbakeLinear(int aValue, const FOOTPRINT *aParentFP)
static VECTOR2I unbakeSizeUniform(const VECTOR2I &aSize, const FOOTPRINT *aParentFP)
#define SEXPR_BOARD_FILE_VERSION
Current s-expression file format version. 2 was the last legacy format version.
#define CTL_FOR_BOARD
The zero arg constructor when PCB_PLUGIN is used for PLUGIN::Load() and PLUGIN::Save()ing a BOARD fil...
#define CTL_FOR_LIBRARY
Format output for a footprint library instead of clipboard or BOARD.
Pcbnew s-expression file format parser definition.
#define UNDEFINED_DRILL_DIAMETER
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
@ RPT_SEVERITY_WARNING
std::string FormatDouble2Str(double aValue)
Print a float number without using scientific notation and no trailing 0 This function is intended in...
bool ReplaceIllegalFileNameChars(std::string &aName, int aReplaceChar)
Checks aName for illegal file name characters.
int ValueStringCompare(const wxString &strFWord, const wxString &strSWord)
Compare strings like the strcmp function but handle numbers and modifiers within the string text corr...
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
One participant in a constraint: a referenced board item plus the feature of that item that participa...
Variant of PARSE_ERROR indicating that a syntax or related error was likely caused by a file generate...
PCB_LAYER_ID start
Definition padstack.h:269
PCB_LAYER_ID end
Definition padstack.h:270
VECTOR2I size
Drill diameter (x == y) or slot dimensions (x != y)
Definition padstack.h:267
std::optional< bool > is_capped
True if the drill hole should be capped.
Definition padstack.h:273
std::optional< bool > is_filled
True if the drill hole should be filled completely.
Definition padstack.h:272
std::optional< bool > has_covering
True if the pad on this side should have covering.
Definition padstack.h:257
std::optional< bool > has_solder_mask
True if this outer layer has mask (is not tented)
Definition padstack.h:255
std::optional< bool > has_plugging
True if the drill hole should be plugged on this side.
Definition padstack.h:258
A filename or source description, a problem input line, a line number, a byte offset,...
Parameters that drive copper-thieving fill generation.
EDA_ANGLE orientation
THIEVING_PATTERN pattern
std::optional< VECTOR2I > hatching_offset
VECTOR2I center
const SHAPE_LINE_CHAIN chain
int delta
wxLogTrace helper definitions.
#define kv
@ PCB_T
Definition typeinfo.h:75
@ PCB_CONSTRAINT_T
class PCB_CONSTRAINT, a geometric constraint between board items
Definition typeinfo.h:238
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:81
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:99
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:96
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
Definition typeinfo.h:84
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:90
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:97
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:104
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:86
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:101
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:85
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:82
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:83
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:94
@ PCB_TARGET_T
class PCB_TARGET, a target (graphic item)
Definition typeinfo.h:100
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition typeinfo.h:88
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:79
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:95
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:80
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:91
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition typeinfo.h:87
@ PCB_POINT_T
class PCB_POINT, a 0-dimensional point
Definition typeinfo.h:106
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:89
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:98
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
Definition of file extensions used in Kicad.
@ THERMAL
Use thermal relief for pads.
Definition zones.h:46
@ THT_THERMAL
Thermal relief only for THT pads.
Definition zones.h:48
@ NONE
Pads are not covered.
Definition zones.h:45
@ FULL
pads are covered by copper
Definition zones.h:47