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>
48#include <pcb_point.h>
49#include <pcb_reference_image.h>
50#include <pcb_barcode.h>
51#include <pcb_shape.h>
52#include <pcb_table.h>
53#include <pcb_tablecell.h>
54#include <pcb_target.h>
55#include <pcb_text.h>
56#include <pcb_textbox.h>
57#include <pcb_track.h>
58#include <pcbnew_settings.h>
59#include <pgm_base.h>
60#include <progress_reporter.h>
61#include <reporter.h>
62#include <string_utils.h>
63#include <trace_helpers.h>
65#include <zone.h>
66
67#include <build_version.h>
68#include <filter_reader.h>
69#include <ctl_flags.h>
70
71
72using namespace PCB_KEYS_T;
73
74
75FP_CACHE_ENTRY::FP_CACHE_ENTRY( FOOTPRINT* aFootprint, const WX_FILENAME& aFileName ) :
76 m_filename( aFileName ),
77 m_footprint( aFootprint )
78{ }
79
80
81FP_CACHE::FP_CACHE( PCB_IO_KICAD_SEXPR* aOwner, const wxString& aLibraryPath )
82{
83 m_owner = aOwner;
84 m_lib_raw_path = aLibraryPath;
85 m_lib_path.SetPath( aLibraryPath );
87 m_cache_dirty = true;
88}
89
90
91void FP_CACHE::Save( FOOTPRINT* aFootprintFilter )
92{
94
95 if( !m_lib_path.DirExists() && !m_lib_path.Mkdir() )
96 {
97 THROW_IO_ERROR( wxString::Format( _( "Cannot create footprint library '%s'." ),
99 }
100
101 if( !m_lib_path.IsDirWritable() )
102 {
103 THROW_IO_ERROR( wxString::Format( _( "Footprint library '%s' is read only." ),
104 m_lib_raw_path ) );
105 }
106
107 for( auto it = m_footprints.begin(); it != m_footprints.end(); ++it )
108 {
109 FP_CACHE_ENTRY* fpCacheEntry = it->second;
110 std::unique_ptr<FOOTPRINT>& footprint = fpCacheEntry->GetFootprint();
111
112 if( aFootprintFilter && footprint.get() != aFootprintFilter )
113 continue;
114
115 // If we've requested to embed the fonts in the footprint, do so. Otherwise, clear the
116 // embedded fonts from the footprint. Embedded fonts will be used if available.
117 if( footprint->GetAreFontsEmbedded() )
118 footprint->EmbedFonts();
119 else
120 footprint->GetEmbeddedFiles()->ClearEmbeddedFonts();
121
122 WX_FILENAME fn = fpCacheEntry->GetFileName();
123 wxString fileName = fn.GetFullPath();
124
125 // Allow file output stream to go out of scope to close the file stream before
126 // renaming the file.
127 {
128 wxLogTrace( traceKicadPcbPlugin, wxT( "Writing library file '%s'." ),
129 fileName );
130
131 PRETTIFIED_FILE_OUTPUTFORMATTER formatter( fileName );
132
133 m_owner->SetOutputFormatter( &formatter );
134 m_owner->Format( footprint.get() );
135 formatter.Finish();
136 }
137
139 }
140
141 if( m_lib_path.IsFileReadable() && m_lib_path.GetModificationTime().IsValid() )
142 m_cache_timestamp += m_lib_path.GetModificationTime().GetValue().GetValue();
143
144 // If we've saved the full cache, we clear the dirty flag.
145 if( !aFootprintFilter )
146 m_cache_dirty = false;
147}
148
149
151{
152 m_cache_dirty = false;
154
155 wxDir dir( m_lib_raw_path );
156
157 if( !dir.IsOpened() )
158 {
159 wxString msg = wxString::Format( _( "Footprint library '%s' not found." ),
161 THROW_IO_ERROR( msg );
162 }
163
164 wxString fullName;
165 wxString fileSpec = wxT( "*." ) + wxString( FILEEXT::KiCadFootprintFileExtension );
166
167 // wxFileName construction is egregiously slow. Construct it once and just swap out
168 // the filename thereafter.
169 WX_FILENAME fn( m_lib_raw_path, wxT( "dummyName" ) );
170
171 if( dir.GetFirst( &fullName, fileSpec ) )
172 {
173 wxString cacheError;
174
175 do
176 {
177 fn.SetFullName( fullName );
178
179 // Queue I/O errors so only files that fail to parse don't get loaded.
180 try
181 {
182 FILE_LINE_READER reader( fn.GetFullPath() );
183 PCB_IO_KICAD_SEXPR_PARSER parser( &reader, nullptr, nullptr );
184
185 FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( parser.Parse() );
186 wxString fpName = fn.GetName();
187
188 if( !footprint )
189 THROW_IO_ERROR( wxEmptyString ); // caught locally, just below...
190
191 footprint->SetFPID( LIB_ID( wxEmptyString, fpName ) );
192 m_footprints.insert( fpName, new FP_CACHE_ENTRY( footprint, fn ) );
193
194 // Collect any non-fatal parse warnings
195 for( const wxString& warning : parser.GetParseWarnings() )
196 {
197 if( !cacheError.IsEmpty() )
198 cacheError += wxT( "\n\n" );
199
200 cacheError += wxString::Format( _( "Warning in file '%s'" ) + '\n',
201 fn.GetFullPath() );
202 cacheError += warning;
203 }
204 }
205 catch( const IO_ERROR& ioe )
206 {
207 if( !cacheError.IsEmpty() )
208 cacheError += wxT( "\n\n" );
209
210 cacheError += wxString::Format( _( "Unable to read file '%s'" ) + '\n',
211 fn.GetFullPath() );
212 cacheError += ioe.What();
213 }
214 } while( dir.GetNext( &fullName ) );
215
217
218 if( !cacheError.IsEmpty() )
219 THROW_IO_ERROR( cacheError );
220 }
221}
222
223
224void FP_CACHE::Remove( const wxString& aFootprintName )
225{
226 auto it = m_footprints.find( aFootprintName );
227
228 if( it == m_footprints.end() )
229 {
230 wxString msg = wxString::Format( _( "Library '%s' has no footprint '%s'." ),
232 aFootprintName );
233 THROW_IO_ERROR( msg );
234 }
235
236 // Remove the footprint from the cache and delete the footprint file from the library.
237 wxString fullPath = it->second->GetFileName().GetFullPath();
238 m_footprints.erase( aFootprintName );
239 wxRemoveFile( fullPath );
240}
241
242
243bool FP_CACHE::IsPath( const wxString& aPath ) const
244{
245 return aPath == m_lib_raw_path;
246}
247
248
249void FP_CACHE::SetPath( const wxString& aPath )
250{
251 m_lib_raw_path = aPath;
252 m_lib_path.SetPath( aPath );
253
254
255 for( const auto& footprint : GetFootprints() )
256 footprint.second->SetFilePath( aPath );
257}
258
259
261{
263
264 return m_cache_dirty;
265}
266
267
268long long FP_CACHE::GetTimestamp( const wxString& aLibPath )
269{
270 wxString fileSpec = wxT( "*." ) + wxString( FILEEXT::KiCadFootprintFileExtension );
271
272 return KIPLATFORM::IO::TimestampDir( aLibPath, fileSpec );
273}
274
275
276bool PCB_IO_KICAD_SEXPR::CanReadBoard( const wxString& aFileName ) const
277{
278 if( !PCB_IO::CanReadBoard( aFileName ) )
279 return false;
280
281 try
282 {
283 FILE_LINE_READER reader( aFileName );
284 PCB_IO_KICAD_SEXPR_PARSER parser( &reader, nullptr, m_queryUserCallback );
285
286 return parser.IsValidBoardHeader();
287 }
288 catch( const IO_ERROR& )
289 {
290 }
291
292 return false;
293}
294
295
296void PCB_IO_KICAD_SEXPR::SaveBoard( const wxString& aFileName, BOARD* aBoard,
297 const std::map<std::string, UTF8>* aProperties )
298{
299 wxString sanityResult = aBoard->GroupsSanityCheck();
300
301 if( sanityResult != wxEmptyString && m_queryUserCallback )
302 {
304 _( "Internal Group Data Error" ), wxICON_ERROR,
305 wxString::Format( _( "Please report this bug. Error validating group "
306 "structure: %s\n\nSave anyway?" ), sanityResult ),
307 _( "Save Anyway" ) ) )
308 {
309 return;
310 }
311 }
312
313 PRETTIFIED_FILE_OUTPUTFORMATTER formatter( aFileName );
314 FormatBoardToFormatter( &formatter, aBoard, aProperties );
315 formatter.Finish();
316}
317
318
320 const std::map<std::string, UTF8>* aProperties )
321{
322 init( aProperties );
323
324 m_board = aBoard; // after init()
325
326 // If the user wants fonts embedded, make sure that they are added to the board. Otherwise,
327 // remove any fonts that were previously embedded.
328 if( m_board->GetAreFontsEmbedded() )
329 m_board->EmbedFonts();
330 else
331 m_board->GetEmbeddedFiles()->ClearEmbeddedFonts();
332
333 m_out = aOut;
334
335 m_out->Print( "(kicad_pcb (version %d) (generator \"pcbnew\") (generator_version %s)",
337 m_out->Quotew( GetMajorMinorVersion() ).c_str() );
338
339 Format( aBoard );
340
341 m_out->Print( ")" );
342
343 m_out = nullptr;
344}
345
346
347BOARD_ITEM* PCB_IO_KICAD_SEXPR::Parse( const wxString& aClipboardSourceInput )
348{
349 std::string input = TO_UTF8( aClipboardSourceInput );
350
351 STRING_LINE_READER reader( input, wxT( "clipboard" ) );
352 PCB_IO_KICAD_SEXPR_PARSER parser( &reader, nullptr, m_queryUserCallback );
353
354 try
355 {
356 return parser.Parse();
357 }
358 catch( const PARSE_ERROR& parse_error )
359 {
360 if( parser.IsTooRecent() )
361 throw FUTURE_FORMAT_ERROR( parse_error, parser.GetRequiredVersion() );
362 else
363 throw;
364 }
365}
366
367
368void PCB_IO_KICAD_SEXPR::Format( const BOARD_ITEM* aItem ) const
369{
370 switch( aItem->Type() )
371 {
372 case PCB_T:
373 format( static_cast<const BOARD*>( aItem ) );
374 break;
375
377 case PCB_DIM_CENTER_T:
378 case PCB_DIM_RADIAL_T:
380 case PCB_DIM_LEADER_T:
381 format( static_cast<const PCB_DIMENSION_BASE*>( aItem ) );
382 break;
383
384 case PCB_SHAPE_T:
385 format( static_cast<const PCB_SHAPE*>( aItem ) );
386 break;
387
389 format( static_cast<const PCB_REFERENCE_IMAGE*>( aItem ) );
390 break;
391
392 case PCB_POINT_T:
393 format( static_cast<const PCB_POINT*>( aItem ) );
394 break;
395
396 case PCB_TARGET_T:
397 format( static_cast<const PCB_TARGET*>( aItem ) );
398 break;
399
400 case PCB_FOOTPRINT_T:
401 format( static_cast<const FOOTPRINT*>( aItem ) );
402 break;
403
404 case PCB_PAD_T:
405 format( static_cast<const PAD*>( aItem ) );
406 break;
407
408 case PCB_FIELD_T:
409 // Handled in the footprint formatter when properties are formatted
410 break;
411
412 case PCB_TEXT_T:
413 format( static_cast<const PCB_TEXT*>( aItem ) );
414 break;
415
416 case PCB_TEXTBOX_T:
417 format( static_cast<const PCB_TEXTBOX*>( aItem ) );
418 break;
419
420 case PCB_BARCODE_T:
421 format( static_cast<const PCB_BARCODE*>( aItem ) );
422 break;
423
424 case PCB_TABLE_T:
425 format( static_cast<const PCB_TABLE*>( aItem ) );
426 break;
427
428 case PCB_GROUP_T:
429 format( static_cast<const PCB_GROUP*>( aItem ) );
430 break;
431
432 case PCB_GENERATOR_T:
433 format( static_cast<const PCB_GENERATOR*>( aItem ) );
434 break;
435
436 case PCB_TRACE_T:
437 case PCB_ARC_T:
438 case PCB_VIA_T:
439 format( static_cast<const PCB_TRACK*>( aItem ) );
440 break;
441
442 case PCB_ZONE_T:
443 format( static_cast<const ZONE*>( aItem ) );
444 break;
445
446 default:
447 wxFAIL_MSG( wxT( "Cannot format item " ) + aItem->GetClass() );
448 }
449}
450
451
452std::string formatInternalUnits( const int aValue, const EDA_DATA_TYPE aDataType = EDA_DATA_TYPE::DISTANCE )
453{
454 return EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, aValue, aDataType );
455}
456
457
458std::string formatInternalUnits( const VECTOR2I& aCoord )
459{
461}
462
463
464std::string formatInternalUnits( const VECTOR2I& aCoord, const FOOTPRINT* aParentFP )
465{
466 if( aParentFP )
467 {
468 return formatInternalUnits( aParentFP->GetTransform().InverseApply( aCoord ) );
469 }
470
471 return formatInternalUnits( aCoord );
472}
473
474
475static VECTOR2I unbakeSize( const VECTOR2I& aSize, const FOOTPRINT* aParentFP )
476{
477 if( !aParentFP )
478 return aSize;
479
480 const TRANSFORM_TRS& xform = aParentFP->GetTransform();
481 return { KiROUND( aSize.x / xform.GetScaleX() ), KiROUND( aSize.y / xform.GetScaleY() ) };
482}
483
484
485static VECTOR2I unbakeSizeUniform( const VECTOR2I& aSize, const FOOTPRINT* aParentFP )
486{
487 if( !aParentFP )
488 return aSize;
489
490 const TRANSFORM_TRS& xform = aParentFP->GetTransform();
491 double avg = ( xform.GetScaleX() + xform.GetScaleY() ) * 0.5;
492 return { KiROUND( aSize.x / avg ), KiROUND( aSize.y / avg ) };
493}
494
495
496static int unbakeLinear( int aValue, const FOOTPRINT* aParentFP )
497{
498 if( !aParentFP )
499 return aValue;
500
501 const TRANSFORM_TRS& xform = aParentFP->GetTransform();
502 double avg = ( xform.GetScaleX() + xform.GetScaleY() ) * 0.5;
503 return KiROUND( aValue / avg );
504}
505
506
507void PCB_IO_KICAD_SEXPR::formatLayer( PCB_LAYER_ID aLayer, bool aIsKnockout ) const
508{
509 m_out->Print( "(layer %s %s)",
510 m_out->Quotew( LSET::Name( aLayer ) ).c_str(),
511 aIsKnockout ? "knockout" : "" );
512}
513
514
516 const FOOTPRINT* aParentFP ) const
517{
518 m_out->Print( "(pts" );
519
520 for( int ii = 0; ii < outline.PointCount(); ++ii )
521 {
522 int ind = outline.ArcIndex( ii );
523
524 if( ind < 0 )
525 {
526 m_out->Print( "(xy %s)",
527 formatInternalUnits( outline.CPoint( ii ), aParentFP ).c_str() );
528 }
529 else
530 {
531 const SHAPE_ARC& arc = outline.Arc( ind );
532 m_out->Print( "(arc (start %s) (mid %s) (end %s))",
533 formatInternalUnits( arc.GetP0(), aParentFP ).c_str(),
534 formatInternalUnits( arc.GetArcMid(), aParentFP ).c_str(),
535 formatInternalUnits( arc.GetP1(), aParentFP ).c_str() );
536
537 do
538 {
539 ++ii;
540 } while( ii < outline.PointCount() && outline.ArcIndex( ii ) == ind );
541
542 --ii;
543 }
544 }
545
546 m_out->Print( ")" );
547}
548
549
551{
552 wxString resolvedText( aText->GetShownText( true ) );
553 std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = aText->GetRenderCache( aText->GetFont(),
554 resolvedText );
555
556 m_out->Print( "(render_cache %s %s",
557 m_out->Quotew( resolvedText ).c_str(),
558 EDA_UNIT_UTILS::FormatAngle( aText->GetDrawRotation() ).c_str() );
559
561
562 CALLBACK_GAL callback_gal( empty_opts,
563 // Polygon callback
564 [&]( const SHAPE_LINE_CHAIN& aPoly )
565 {
566 m_out->Print( "(polygon" );
567 formatPolyPts( aPoly );
568 m_out->Print( ")" );
569 } );
570
571 callback_gal.SetLineWidth( aText->GetTextThickness() );
572 callback_gal.DrawGlyphs( *cache );
573
574 m_out->Print( ")" );
575}
576
577
578void PCB_IO_KICAD_SEXPR::formatSetup( const BOARD* aBoard ) const
579{
580 // Setup
581 m_out->Print( "(setup" );
582
583 // Save the board physical stackup structure
584 const BOARD_STACKUP& stackup = aBoard->GetDesignSettings().GetStackupDescriptor();
585
586 if( aBoard->GetDesignSettings().m_HasStackup )
587 stackup.FormatBoardStackup( m_out, aBoard );
588
589 BOARD_DESIGN_SETTINGS& dsnSettings = aBoard->GetDesignSettings();
590
591 m_out->Print( "(pad_to_mask_clearance %s)",
592 formatInternalUnits( dsnSettings.m_SolderMaskExpansion ).c_str() );
593
594 if( dsnSettings.m_SolderMaskMinWidth )
595 {
596 m_out->Print( "(solder_mask_min_width %s)",
597 formatInternalUnits( dsnSettings.m_SolderMaskMinWidth ).c_str() );
598 }
599
600 if( dsnSettings.m_SolderPasteMargin != 0 )
601 {
602 m_out->Print( "(pad_to_paste_clearance %s)",
603 formatInternalUnits( dsnSettings.m_SolderPasteMargin ).c_str() );
604 }
605
606 if( dsnSettings.m_SolderPasteMarginRatio != 0 )
607 {
608 m_out->Print( "(pad_to_paste_clearance_ratio %s)",
609 FormatDouble2Str( dsnSettings.m_SolderPasteMarginRatio ).c_str() );
610 }
611
612 KICAD_FORMAT::FormatBool( m_out, "allow_soldermask_bridges_in_footprints",
613 dsnSettings.m_AllowSoldermaskBridgesInFPs );
614
615 m_out->Print( 0, " (tenting " );
616 KICAD_FORMAT::FormatBool( m_out, "front", dsnSettings.m_TentViasFront );
617 KICAD_FORMAT::FormatBool( m_out, "back", dsnSettings.m_TentViasBack );
618 m_out->Print( 0, ")" );
619
620 m_out->Print( 0, " (covering " );
621 KICAD_FORMAT::FormatBool( m_out, "front", dsnSettings.m_CoverViasFront );
622 KICAD_FORMAT::FormatBool( m_out, "back", dsnSettings.m_CoverViasBack );
623 m_out->Print( 0, ")" );
624
625 m_out->Print( 0, " (plugging " );
626 KICAD_FORMAT::FormatBool( m_out, "front", dsnSettings.m_PlugViasFront );
627 KICAD_FORMAT::FormatBool( m_out, "back", dsnSettings.m_PlugViasBack );
628 m_out->Print( 0, ")" );
629
630 KICAD_FORMAT::FormatBool( m_out, "capping", dsnSettings.m_CapVias );
631
632 KICAD_FORMAT::FormatBool( m_out, "filling", dsnSettings.m_FillVias );
633
634 if( !dsnSettings.m_ZoneLayerProperties.empty() )
635 {
636 m_out->Print( 0, " (zone_defaults" );
637
638 for( const auto& [layer, properties] : dsnSettings.m_ZoneLayerProperties )
639 format( properties, 0, layer );
640
641 m_out->Print( 0, ")\n" );
642 }
643
644 VECTOR2I origin = dsnSettings.GetAuxOrigin();
645
646 if( origin != VECTOR2I( 0, 0 ) )
647 {
648 m_out->Print( "(aux_axis_origin %s %s)",
649 formatInternalUnits( origin.x ).c_str(),
650 formatInternalUnits( origin.y ).c_str() );
651 }
652
653 origin = dsnSettings.GetGridOrigin();
654
655 if( origin != VECTOR2I( 0, 0 ) )
656 {
657 m_out->Print( "(grid_origin %s %s)",
658 formatInternalUnits( origin.x ).c_str(),
659 formatInternalUnits( origin.y ).c_str() );
660 }
661
662 aBoard->GetPlotOptions().Format( m_out );
663
664 m_out->Print( ")" );
665}
666
667
668void PCB_IO_KICAD_SEXPR::formatGeneral( const BOARD* aBoard ) const
669{
670 const BOARD_DESIGN_SETTINGS& dsnSettings = aBoard->GetDesignSettings();
671
672 m_out->Print( "(general" );
673
674 m_out->Print( "(thickness %s)",
675 formatInternalUnits( dsnSettings.GetBoardThickness() ).c_str() );
676
677 KICAD_FORMAT::FormatBool( m_out, "legacy_teardrops", aBoard->LegacyTeardrops() );
678
679 m_out->Print( ")" );
680
681 aBoard->GetPageSettings().Format( m_out );
682 aBoard->GetTitleBlock().Format( m_out );
683}
684
685
687{
688 m_out->Print( "(layers" );
689
690 // Save only the used copper layers from front to back.
691
692 for( PCB_LAYER_ID layer : aBoard->GetEnabledLayers().CuStack() )
693 {
694 m_out->Print( "(%d %s %s %s)",
695 layer,
696 m_out->Quotew( LSET::Name( layer ) ).c_str(),
697 LAYER::ShowType( aBoard->GetLayerType( layer ) ),
698 LSET::Name( layer ) == m_board->GetLayerName( layer )
699 ? ""
700 : m_out->Quotew( m_board->GetLayerName( layer ) ).c_str() );
701
702 }
703
704 // Save used non-copper layers in the order they are defined.
705 LSEQ seq = aBoard->GetEnabledLayers().TechAndUserUIOrder();
706
707 for( PCB_LAYER_ID layer : seq )
708 {
709 bool print_type = false;
710
711 // User layers (layer id >= User_1) have a qualifier
712 // default is "user", but other qualifiers exist
713 if( layer >= User_1 )
714 {
715 if( IsCopperLayer( layer ) )
716 print_type = true;
717
718 if( aBoard->GetLayerType( layer ) == LT_FRONT
719 || aBoard->GetLayerType( layer ) == LT_BACK )
720 print_type = true;
721 }
722
723 m_out->Print( "(%d %s %s %s)",
724 layer,
725 m_out->Quotew( LSET::Name( layer ) ).c_str(),
726 print_type
727 ? LAYER::ShowType( aBoard->GetLayerType( layer ) )
728 : "user",
729 m_board->GetLayerName( layer ) == LSET::Name( layer )
730 ? ""
731 : m_out->Quotew( m_board->GetLayerName( layer ) ).c_str() );
732 }
733
734 m_out->Print( ")" );
735}
736
737
739{
740 for( const std::pair<const wxString, wxString>& prop : aBoard->GetProperties() )
741 {
742 m_out->Print( "(property %s %s)",
743 m_out->Quotew( prop.first ).c_str(),
744 m_out->Quotew( prop.second ).c_str() );
745 }
746}
747
748
749void PCB_IO_KICAD_SEXPR::formatVariants( const BOARD* aBoard ) const
750{
751 const std::vector<wxString>& variantNames = aBoard->GetVariantNames();
752
753 if( variantNames.empty() )
754 return;
755
756 m_out->Print( "(variants" );
757
758 for( const wxString& variantName : variantNames )
759 {
760 m_out->Print( "(variant (name %s)", m_out->Quotew( variantName ).c_str() );
761
762 wxString description = aBoard->GetVariantDescription( variantName );
763
764 if( !description.IsEmpty() )
765 m_out->Print( "(description %s)", m_out->Quotew( description ).c_str() );
766
767 m_out->Print( ")" );
768 }
769
770 m_out->Print( ")" );
771}
772
773
774void PCB_IO_KICAD_SEXPR::formatHeader( const BOARD* aBoard ) const
775{
776 formatGeneral( aBoard );
777
778 // Layers list.
779 formatBoardLayers( aBoard );
780
781 // Setup
782 formatSetup( aBoard );
783
784 // Properties
785 formatProperties( aBoard );
786
787 // Variants
788 formatVariants( aBoard );
789}
790
791
793{
794 static const TEARDROP_PARAMETERS defaults;
795
796 return tdParams.m_Enabled == defaults.m_Enabled
797 && tdParams.m_BestLengthRatio == defaults.m_BestLengthRatio
798 && tdParams.m_TdMaxLen == defaults.m_TdMaxLen
799 && tdParams.m_BestWidthRatio == defaults.m_BestWidthRatio
800 && tdParams.m_TdMaxWidth == defaults.m_TdMaxWidth
801 && tdParams.m_CurvedEdges == defaults.m_CurvedEdges
803 && tdParams.m_AllowUseTwoTracks == defaults.m_AllowUseTwoTracks
804 && tdParams.m_TdOnPadsInZones == defaults.m_TdOnPadsInZones;
805}
806
807
809{
810 m_out->Print( "(teardrops (best_length_ratio %s) (max_length %s) (best_width_ratio %s) "
811 "(max_width %s)",
812 FormatDouble2Str( tdParams.m_BestLengthRatio ).c_str(),
813 formatInternalUnits( tdParams.m_TdMaxLen ).c_str(),
814 FormatDouble2Str( tdParams.m_BestWidthRatio ).c_str(),
815 formatInternalUnits( tdParams.m_TdMaxWidth ).c_str() );
816
817 KICAD_FORMAT::FormatBool( m_out, "curved_edges", tdParams.m_CurvedEdges );
818
819 m_out->Print( "(filter_ratio %s)",
820 FormatDouble2Str( tdParams.m_WidthtoSizeFilterRatio ).c_str() );
821
822 KICAD_FORMAT::FormatBool( m_out, "enabled", tdParams.m_Enabled );
823 KICAD_FORMAT::FormatBool( m_out, "allow_two_segments", tdParams.m_AllowUseTwoTracks );
824 KICAD_FORMAT::FormatBool( m_out, "prefer_zone_connections", !tdParams.m_TdOnPadsInZones );
825 m_out->Print( ")" );
826}
827
828
829void PCB_IO_KICAD_SEXPR::format( const BOARD* aBoard ) const
830{
831 std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_footprints( aBoard->Footprints().begin(),
832 aBoard->Footprints().end() );
833 std::set<BOARD_ITEM*, BOARD::cmp_drawings> sorted_drawings( aBoard->Drawings().begin(),
834 aBoard->Drawings().end() );
835 std::set<PCB_TRACK*, PCB_TRACK::cmp_tracks> sorted_tracks( aBoard->Tracks().begin(),
836 aBoard->Tracks().end() );
837 std::set<PCB_POINT*, PCB_POINT::cmp_points> sorted_points( aBoard->Points().begin(),
838 aBoard->Points().end() );
839 std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_zones( aBoard->Zones().begin(),
840 aBoard->Zones().end() );
841 std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_groups( aBoard->Groups().begin(),
842 aBoard->Groups().end() );
843 std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_generators( aBoard->Generators().begin(),
844 aBoard->Generators().end() );
845 formatHeader( aBoard );
846
847 // Save the footprints.
848 for( BOARD_ITEM* footprint : sorted_footprints )
849 Format( footprint );
850
851 // Save the graphical items on the board (not owned by a footprint)
852 for( BOARD_ITEM* item : sorted_drawings )
853 Format( item );
854
855 // Save the points
856 for( PCB_POINT* point : sorted_points )
857 Format( point );
858
859 // Do not save PCB_MARKERs, they can be regenerated easily.
860
861 // Save the tracks and vias.
862 for( PCB_TRACK* track : sorted_tracks )
863 Format( track );
864
865 // Save the polygon (which are the newer technology) zones.
866 for( auto zone : sorted_zones )
867 Format( zone );
868
869 // Save the groups
870 for( BOARD_ITEM* group : sorted_groups )
871 Format( group );
872
873 // Save the generators
874 for( BOARD_ITEM* gen : sorted_generators )
875 Format( gen );
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
1562 // Save drawing elements.
1563
1564 for( BOARD_ITEM* gr : sorted_drawings )
1565 Format( gr );
1566
1567 for( PCB_POINT* point : sorted_points )
1568 Format( point );
1569
1570 // Save pads.
1571 for( PAD* pad : sorted_pads )
1572 Format( pad );
1573
1574 // Save zones.
1575 for( BOARD_ITEM* zone : sorted_zones )
1576 Format( zone );
1577
1578 // Save groups.
1579 for( BOARD_ITEM* group : sorted_groups )
1580 Format( group );
1581
1582 // Save variants.
1583 const bool baseDnp = aFootprint->IsDNP();
1584 const bool baseExcludedFromBOM = aFootprint->IsExcludedFromBOM();
1585 const bool baseExcludedFromPosFiles = aFootprint->IsExcludedFromPosFiles();
1586
1587 for( const auto& [variantName, variant] : aFootprint->GetVariants() )
1588 {
1589 m_out->Print( "(variant (name %s)", m_out->Quotew( variantName ).c_str() );
1590
1591 if( variant.GetDNP() != baseDnp )
1592 KICAD_FORMAT::FormatBool( m_out, "dnp", variant.GetDNP() );
1593
1594 if( variant.GetExcludedFromBOM() != baseExcludedFromBOM )
1595 KICAD_FORMAT::FormatBool( m_out, "exclude_from_bom", variant.GetExcludedFromBOM() );
1596
1597 if( variant.GetExcludedFromPosFiles() != baseExcludedFromPosFiles )
1598 {
1599 KICAD_FORMAT::FormatBool( m_out, "exclude_from_pos_files",
1600 variant.GetExcludedFromPosFiles() );
1601 }
1602
1603 for( const auto& [fieldName, fieldValue] : variant.GetFields() )
1604 {
1605 const PCB_FIELD* baseField = aFootprint->GetField( fieldName );
1606 const wxString baseValue = baseField ? baseField->GetText() : wxString();
1607
1608 if( fieldValue == baseValue )
1609 continue;
1610
1611 m_out->Print( "(field (name %s) (value %s))",
1612 m_out->Quotew( fieldName ).c_str(),
1613 m_out->Quotew( fieldValue ).c_str() );
1614 }
1615
1616 m_out->Print( ")" );
1617 }
1618
1619 KICAD_FORMAT::FormatBool( m_out, "embedded_fonts",
1620 aFootprint->GetEmbeddedFiles()->GetAreFontsEmbedded() );
1621
1622 if( !aFootprint->GetEmbeddedFiles()->IsEmpty() )
1623 aFootprint->WriteEmbeddedFiles( *m_out, !( m_ctl & CTL_FOR_BOARD ) );
1624
1625 // Save extruded 3D body info.
1626 if( const EXTRUDED_3D_BODY* body = aFootprint->GetExtrudedBody(); body && body->m_height > 0 )
1627 {
1628 m_out->Print( "(model" );
1629 m_out->Print( "(type extruded)" );
1630 KICAD_FORMAT::FormatBool( m_out, "hide", !body->m_show );
1631 m_out->Print( "(overall_height %s)", formatInternalUnits( body->m_height ).c_str() );
1632 m_out->Print( "(body_pcb_gap %s)", formatInternalUnits( body->m_standoff ).c_str() );
1633
1634 if( body->m_layer == UNSELECTED_LAYER )
1635 m_out->Print( "(layer pad_bbox)" );
1636 else if( body->m_layer != UNDEFINED_LAYER )
1637 m_out->Print( "(layer %s)", m_out->Quotew( LSET::Name( body->m_layer ) ).c_str() );
1638 else
1639 m_out->Print( "(layer auto)" );
1640
1641 {
1642 static const char* matNames[] = { "plastic", "matte", "metal", "copper" };
1643 m_out->Print( "(material %s)", matNames[static_cast<int>( body->m_material )] );
1644 }
1645
1646 if( body->m_color != KIGFX::COLOR4D::UNSPECIFIED )
1647 {
1648 m_out->Print( "(color %s %s %s %s)", FormatDouble2Str( body->m_color.r ).c_str(),
1649 FormatDouble2Str( body->m_color.g ).c_str(), FormatDouble2Str( body->m_color.b ).c_str(),
1650 FormatDouble2Str( body->m_color.a ).c_str() );
1651 }
1652 else
1653 {
1654 m_out->Print( "(color unspecified)" );
1655 }
1656
1657 m_out->Print( "(offset (xyz %s %s %s))", FormatDouble2Str( body->m_offset.x ).c_str(),
1658 FormatDouble2Str( body->m_offset.y ).c_str(), FormatDouble2Str( body->m_offset.z ).c_str() );
1659
1660 m_out->Print( "(scale (xyz %s %s %s))", FormatDouble2Str( body->m_scale.x ).c_str(),
1661 FormatDouble2Str( body->m_scale.y ).c_str(), FormatDouble2Str( body->m_scale.z ).c_str() );
1662
1663 m_out->Print( "(rotate (xyz %s %s %s))", FormatDouble2Str( body->m_rotation.x ).c_str(),
1664 FormatDouble2Str( body->m_rotation.y ).c_str(), FormatDouble2Str( body->m_rotation.z ).c_str() );
1665
1666 m_out->Print( ")" );
1667 }
1668
1669 // Save 3D info.
1670 auto bs3D = aFootprint->Models().begin();
1671 auto es3D = aFootprint->Models().end();
1672
1673 while( bs3D != es3D )
1674 {
1675 if( !bs3D->m_Filename.IsEmpty() )
1676 {
1677 m_out->Print( "(model %s", m_out->Quotew( bs3D->m_Filename ).c_str() );
1678
1679 if( !bs3D->m_Show )
1680 KICAD_FORMAT::FormatBool( m_out, "hide", !bs3D->m_Show );
1681
1682 if( bs3D->m_Opacity != 1.0 )
1683 m_out->Print( "%s", fmt::format("(opacity {:.4f})", bs3D->m_Opacity).c_str() );
1684
1685 m_out->Print( "(offset (xyz %s %s %s))",
1686 FormatDouble2Str( bs3D->m_Offset.x ).c_str(),
1687 FormatDouble2Str( bs3D->m_Offset.y ).c_str(),
1688 FormatDouble2Str( bs3D->m_Offset.z ).c_str() );
1689
1690 m_out->Print( "(scale (xyz %s %s %s))",
1691 FormatDouble2Str( bs3D->m_Scale.x ).c_str(),
1692 FormatDouble2Str( bs3D->m_Scale.y ).c_str(),
1693 FormatDouble2Str( bs3D->m_Scale.z ).c_str() );
1694
1695 m_out->Print( "(rotate (xyz %s %s %s))",
1696 FormatDouble2Str( bs3D->m_Rotation.x ).c_str(),
1697 FormatDouble2Str( bs3D->m_Rotation.y ).c_str(),
1698 FormatDouble2Str( bs3D->m_Rotation.z ).c_str() );
1699
1700 m_out->Print( ")" );
1701 }
1702
1703 ++bs3D;
1704 }
1705
1706 m_out->Print( ")" );
1707}
1708
1709
1710void PCB_IO_KICAD_SEXPR::formatLayers( LSET aLayerMask, bool aEnumerateLayers, bool aIsZone ) const
1711{
1712 static const LSET cu_all( LSET::AllCuMask() );
1713 static const LSET fr_bk( { B_Cu, F_Cu } );
1714 static const LSET adhes( { B_Adhes, F_Adhes } );
1715 static const LSET paste( { B_Paste, F_Paste } );
1716 static const LSET silks( { B_SilkS, F_SilkS } );
1717 static const LSET mask( { B_Mask, F_Mask } );
1718 static const LSET crt_yd( { B_CrtYd, F_CrtYd } );
1719 static const LSET fab( { B_Fab, F_Fab } );
1720
1721 LSET cu_board_mask = LSET::AllCuMask( m_board ? m_board->GetCopperLayerCount() : MAX_CU_LAYERS );
1722
1723 std::string output;
1724
1725 if( !aEnumerateLayers )
1726 {
1727 // If all copper layers present on the board are enabled, then output the wildcard
1728 if( ( aLayerMask & cu_board_mask ) == cu_board_mask )
1729 {
1730 output += ' ' + m_out->Quotew( "*.Cu" );
1731
1732 // Clear all copper bits because pads might have internal layers that aren't part of the
1733 // board enabled, and we don't want to output those in the layers listing if we already
1734 // output the wildcard.
1735 aLayerMask &= ~cu_all;
1736 }
1737 else if( ( aLayerMask & cu_board_mask ) == fr_bk )
1738 {
1739 if( aIsZone )
1740 output += ' ' + m_out->Quotew( "F&B.Cu" );
1741 else
1742 output += ' ' + m_out->Quotew( "*.Cu" );
1743
1744 aLayerMask &= ~fr_bk;
1745 }
1746
1747 if( ( aLayerMask & adhes ) == adhes )
1748 {
1749 output += ' ' + m_out->Quotew( "*.Adhes" );
1750 aLayerMask &= ~adhes;
1751 }
1752
1753 if( ( aLayerMask & paste ) == paste )
1754 {
1755 output += ' ' + m_out->Quotew( "*.Paste" );
1756 aLayerMask &= ~paste;
1757 }
1758
1759 if( ( aLayerMask & silks ) == silks )
1760 {
1761 output += ' ' + m_out->Quotew( "*.SilkS" );
1762 aLayerMask &= ~silks;
1763 }
1764
1765 if( ( aLayerMask & mask ) == mask )
1766 {
1767 output += ' ' + m_out->Quotew( "*.Mask" );
1768 aLayerMask &= ~mask;
1769 }
1770
1771 if( ( aLayerMask & crt_yd ) == crt_yd )
1772 {
1773 output += ' ' + m_out->Quotew( "*.CrtYd" );
1774 aLayerMask &= ~crt_yd;
1775 }
1776
1777 if( ( aLayerMask & fab ) == fab )
1778 {
1779 output += ' ' + m_out->Quotew( "*.Fab" );
1780 aLayerMask &= ~fab;
1781 }
1782 }
1783
1784 // output any individual layers not handled in wildcard combos above
1785 for( int layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
1786 {
1787 if( aLayerMask[layer] )
1788 output += ' ' + m_out->Quotew( LSET::Name( PCB_LAYER_ID( layer ) ) );
1789 }
1790
1791 m_out->Print( "(layers %s)", output.c_str() );
1792}
1793
1794
1795void PCB_IO_KICAD_SEXPR::format( const PAD* aPad ) const
1796{
1797 const BOARD* board = aPad->GetBoard();
1798 const FOOTPRINT* parentFP = aPad->GetParentFootprint();
1799
1800 auto shapeName =
1801 [&]( PCB_LAYER_ID aLayer )
1802 {
1803 switch( aPad->GetShape( aLayer ) )
1804 {
1805 case PAD_SHAPE::CIRCLE: return "circle";
1806 case PAD_SHAPE::RECTANGLE: return "rect";
1807 case PAD_SHAPE::OVAL: return "oval";
1808 case PAD_SHAPE::TRAPEZOID: return "trapezoid";
1810 case PAD_SHAPE::ROUNDRECT: return "roundrect";
1811 case PAD_SHAPE::CUSTOM: return "custom";
1812
1813 default:
1814 THROW_IO_ERROR( wxString::Format( _( "unknown pad type: %d"),
1815 aPad->GetShape( aLayer ) ) );
1816 }
1817 };
1818
1819 const char* type;
1820
1821 switch( aPad->GetAttribute() )
1822 {
1823 case PAD_ATTRIB::PTH: type = "thru_hole"; break;
1824 case PAD_ATTRIB::SMD: type = "smd"; break;
1825 case PAD_ATTRIB::CONN: type = "connect"; break;
1826 case PAD_ATTRIB::NPTH: type = "np_thru_hole"; break;
1827
1828 default:
1829 THROW_IO_ERROR( wxString::Format( wxT( "unknown pad attribute: %d" ),
1830 aPad->GetAttribute() ) );
1831 }
1832
1833 const char* property = nullptr;
1834
1835 switch( aPad->GetProperty() )
1836 {
1837 case PAD_PROP::NONE: break; // could be "none"
1838 case PAD_PROP::BGA: property = "pad_prop_bga"; break;
1839 case PAD_PROP::FIDUCIAL_GLBL: property = "pad_prop_fiducial_glob"; break;
1840 case PAD_PROP::FIDUCIAL_LOCAL: property = "pad_prop_fiducial_loc"; break;
1841 case PAD_PROP::TESTPOINT: property = "pad_prop_testpoint"; break;
1842 case PAD_PROP::HEATSINK: property = "pad_prop_heatsink"; break;
1843 case PAD_PROP::CASTELLATED: property = "pad_prop_castellated"; break;
1844 case PAD_PROP::MECHANICAL: property = "pad_prop_mechanical"; break;
1845 case PAD_PROP::PRESSFIT: property = "pad_prop_pressfit"; break;
1846
1847 default:
1848 THROW_IO_ERROR( wxString::Format( wxT( "unknown pad property: %d" ),
1849 aPad->GetProperty() ) );
1850 }
1851
1852 const char* simElectricalType = nullptr;
1853
1854 switch( aPad->GetSimElectricalType() )
1855 {
1856 case PAD_SIM_ELECTRICAL_TYPE::SOURCE: simElectricalType = "source"; break;
1857 case PAD_SIM_ELECTRICAL_TYPE::SINK: simElectricalType = "sink"; break;
1858 default: simElectricalType = nullptr; break;
1859 }
1860
1861 m_out->Print( "(pad %s %s %s",
1862 m_out->Quotew( aPad->GetNumber() ).c_str(),
1863 type,
1864 shapeName( PADSTACK::ALL_LAYERS ) );
1865
1866 m_out->Print( "(at %s %s)",
1867 formatInternalUnits( aPad->GetFPRelativePosition() ).c_str(),
1868 aPad->GetOrientation().IsZero()
1869 ? ""
1870 : EDA_UNIT_UTILS::FormatAngle( aPad->GetOrientation() ).c_str() );
1871
1872 // Write the stored library size directly: it is the footprint-frame value the parser
1873 // reads back, and avoids a bake/unbake that is not the inverse of GetSize() for a
1874 // pad rotated within the footprint.
1875 m_out->Print( "(size %s)", formatInternalUnits( aPad->Padstack().Size( PADSTACK::ALL_LAYERS ) ).c_str() );
1876
1877 if( aPad->GetDelta( PADSTACK::ALL_LAYERS ).x != 0
1878 || aPad->GetDelta( PADSTACK::ALL_LAYERS ).y != 0 )
1879 {
1880 m_out->Print( "(rect_delta %s)", formatInternalUnits( aPad->GetDelta( PADSTACK::ALL_LAYERS ) ).c_str() );
1881 }
1882
1883 const VECTOR2I drill = aPad->GetDrillShape() == PAD_DRILL_SHAPE::CIRCLE
1884 ? unbakeSizeUniform( aPad->GetDrillSize(), parentFP )
1885 : unbakeSize( aPad->GetDrillSize(), parentFP );
1886 VECTOR2I shapeoffset = aPad->GetOffset( PADSTACK::ALL_LAYERS );
1887 bool forceShapeOffsetOutput = false;
1888
1890 [&]( PCB_LAYER_ID layer )
1891 {
1892 if( aPad->GetOffset( layer ) != shapeoffset )
1893 forceShapeOffsetOutput = true;
1894 } );
1895
1896 if( drill.x > 0 || drill.y > 0 || shapeoffset.x != 0 || shapeoffset.y != 0 || forceShapeOffsetOutput )
1897 {
1898 m_out->Print( "(drill" );
1899
1900 if( aPad->GetDrillShape() == PAD_DRILL_SHAPE::OBLONG )
1901 m_out->Print( " oval" );
1902
1903 if( drill.x > 0 )
1904 m_out->Print( " %s", formatInternalUnits( drill.x ).c_str() );
1905
1906 if( drill.y > 0 && drill.x != drill.y )
1907 m_out->Print( " %s", formatInternalUnits( drill.y ).c_str() );
1908
1909 // NOTE: Shape offest is a property of the copper shape, not of the drill, but this was put
1910 // in the file format under the drill section. So, it is left here to minimize file format
1911 // changes, but note that the other padstack layers (if present) will have an offset stored
1912 // separately.
1913 if( shapeoffset.x != 0 || shapeoffset.y != 0 || forceShapeOffsetOutput )
1914 m_out->Print( "(offset %s)",
1916
1917 m_out->Print( ")" );
1918 }
1919
1920 if( aPad->Padstack().SecondaryDrill().size.x > 0 )
1921 {
1922 m_out->Print( "(backdrill (size %s) (layers %s %s))",
1923 formatInternalUnits( aPad->Padstack().SecondaryDrill().size.x ).c_str(),
1924 m_out->Quotew( LSET::Name( aPad->Padstack().SecondaryDrill().start ) ).c_str(),
1925 m_out->Quotew( LSET::Name( aPad->Padstack().SecondaryDrill().end ) ).c_str() );
1926 }
1927
1928 if( aPad->Padstack().TertiaryDrill().size.x > 0 )
1929 {
1930 m_out->Print( "(tertiary_drill (size %s) (layers %s %s))",
1931 formatInternalUnits( aPad->Padstack().TertiaryDrill().size.x ).c_str(),
1932 m_out->Quotew( LSET::Name( aPad->Padstack().TertiaryDrill().start ) ).c_str(),
1933 m_out->Quotew( LSET::Name( aPad->Padstack().TertiaryDrill().end ) ).c_str() );
1934 }
1935
1936 auto formatPostMachining =
1937 [&]( const char* aName, const PADSTACK::POST_MACHINING_PROPS& aProps )
1938 {
1939 if( !aProps.mode.has_value() || aProps.mode == PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED )
1940 return;
1941
1942 m_out->Print( "(%s %s",
1943 aName,
1944 aProps.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE ? "counterbore"
1945 : "countersink" );
1946
1947 if( aProps.size > 0 )
1948 m_out->Print( " (size %s)", formatInternalUnits( aProps.size ).c_str() );
1949
1950 if( aProps.depth > 0 )
1951 m_out->Print( " (depth %s)", formatInternalUnits( aProps.depth ).c_str() );
1952
1953 if( aProps.angle > 0 )
1954 m_out->Print( " (angle %s)", FormatDouble2Str( aProps.angle / 10.0 ).c_str() );
1955
1956 m_out->Print( ")" );
1957 };
1958
1959 formatPostMachining( "front_post_machining", aPad->Padstack().FrontPostMachining() );
1960 formatPostMachining( "back_post_machining", aPad->Padstack().BackPostMachining() );
1961
1962 // Add pad property, if exists.
1963 if( property )
1964 m_out->Print( "(property %s)", property );
1965
1966 if( simElectricalType )
1967 m_out->Print( "(sim_electrical_type %s)", simElectricalType );
1968
1969 formatLayers( aPad->GetLayerSet(), false /* enumerate layers */ );
1970
1971 if( aPad->GetAttribute() == PAD_ATTRIB::PTH )
1972 {
1973 KICAD_FORMAT::FormatBool( m_out, "remove_unused_layers", aPad->GetRemoveUnconnected() );
1974
1975 if( aPad->GetRemoveUnconnected() )
1976 {
1977 KICAD_FORMAT::FormatBool( m_out, "keep_end_layers", aPad->GetKeepTopBottom() );
1978
1979 if( board ) // Will be nullptr in footprint library
1980 {
1981 m_out->Print( "(zone_layer_connections" );
1982
1983 for( PCB_LAYER_ID layer : board->GetEnabledLayers().CuStack() )
1984 {
1985 if( aPad->GetZoneLayerOverride( layer ) == ZLO_FORCE_FLASHED )
1986 m_out->Print( " %s", m_out->Quotew( LSET::Name( layer ) ).c_str() );
1987 }
1988
1989 m_out->Print( ")" );
1990 }
1991 }
1992 }
1993
1994 auto formatCornerProperties =
1995 [&]( PCB_LAYER_ID aLayer )
1996 {
1997 // Output the radius ratio for rounded and chamfered rect pads
1998 if( aPad->GetShape( aLayer ) == PAD_SHAPE::ROUNDRECT
1999 || aPad->GetShape( aLayer ) == PAD_SHAPE::CHAMFERED_RECT)
2000 {
2001 m_out->Print( "(roundrect_rratio %s)",
2002 FormatDouble2Str( aPad->GetRoundRectRadiusRatio( aLayer ) ).c_str() );
2003 }
2004
2005 // Output the chamfer corners for chamfered rect pads
2006 if( aPad->GetShape( aLayer ) == PAD_SHAPE::CHAMFERED_RECT)
2007 {
2008 m_out->Print( "(chamfer_ratio %s)",
2009 FormatDouble2Str( aPad->GetChamferRectRatio( aLayer ) ).c_str() );
2010
2011 m_out->Print( "(chamfer" );
2012
2013 if( ( aPad->GetChamferPositions( aLayer ) & RECT_CHAMFER_TOP_LEFT ) )
2014 m_out->Print( " top_left" );
2015
2016 if( ( aPad->GetChamferPositions( aLayer ) & RECT_CHAMFER_TOP_RIGHT ) )
2017 m_out->Print( " top_right" );
2018
2019 if( ( aPad->GetChamferPositions( aLayer ) & RECT_CHAMFER_BOTTOM_LEFT ) )
2020 m_out->Print( " bottom_left" );
2021
2022 if( ( aPad->GetChamferPositions( aLayer ) & RECT_CHAMFER_BOTTOM_RIGHT ) )
2023 m_out->Print( " bottom_right" );
2024
2025 m_out->Print( ")" );
2026 }
2027
2028 };
2029
2030 // For normal padstacks, this is the one and only set of properties. For complex ones, this
2031 // will represent the front layer properties, and other layers will be formatted below
2032 formatCornerProperties( PADSTACK::ALL_LAYERS );
2033
2034 // Unconnected pad is default net so don't save it.
2035 if( !( m_ctl & CTL_OMIT_PAD_NETS ) && aPad->GetNetCode() > 0 )
2036 m_out->Print( "(net %s)", m_out->Quotew( aPad->GetNetname() ).c_str() );
2037
2038 // Pin functions and types are closely related to nets, so if CTL_OMIT_NETS is set, omit
2039 // them as well (for instance when saved from library editor).
2040 if( !( m_ctl & CTL_OMIT_PAD_NETS ) )
2041 {
2042 if( !aPad->GetPinFunction().IsEmpty() )
2043 m_out->Print( "(pinfunction %s)", m_out->Quotew( aPad->GetPinFunction() ).c_str() );
2044
2045 if( !aPad->GetPinType().IsEmpty() )
2046 m_out->Print( "(pintype %s)", m_out->Quotew( aPad->GetPinType() ).c_str() );
2047 }
2048
2049 if( aPad->GetPadToDieLength() != 0 )
2050 {
2051 m_out->Print( "(die_length %s)",
2052 formatInternalUnits( aPad->GetPadToDieLength() ).c_str() );
2053 }
2054
2055 if( aPad->GetPadToDieDelay() != 0 )
2056 {
2057 m_out->Print( "(die_delay %s)",
2059 }
2060
2061 if( aPad->GetLocalSolderMaskMargin().has_value() )
2062 {
2063 m_out->Print( "(solder_mask_margin %s)",
2064 formatInternalUnits( aPad->GetLocalSolderMaskMargin().value() ).c_str() );
2065 }
2066
2067 if( aPad->GetLocalSolderPasteMargin().has_value() )
2068 {
2069 m_out->Print( "(solder_paste_margin %s)",
2070 formatInternalUnits( aPad->GetLocalSolderPasteMargin().value() ).c_str() );
2071 }
2072
2073 if( aPad->GetLocalSolderPasteMarginRatio().has_value() )
2074 {
2075 m_out->Print( "(solder_paste_margin_ratio %s)",
2076 FormatDouble2Str( aPad->GetLocalSolderPasteMarginRatio().value() ).c_str() );
2077 }
2078
2079 if( aPad->GetLocalClearance().has_value() )
2080 {
2081 m_out->Print( "(clearance %s)",
2082 formatInternalUnits( aPad->GetLocalClearance().value() ).c_str() );
2083 }
2084
2086 {
2087 m_out->Print( "(zone_connect %d)",
2088 static_cast<int>( aPad->GetLocalZoneConnection() ) );
2089 }
2090
2091 if( aPad->GetLocalThermalSpokeWidthOverride().has_value() )
2092 {
2093 m_out->Print( "(thermal_bridge_width %s)",
2094 formatInternalUnits( aPad->GetLocalThermalSpokeWidthOverride().value() ).c_str() );
2095 }
2096
2097 EDA_ANGLE defaultThermalSpokeAngle = ANGLE_90;
2098
2102 {
2103 defaultThermalSpokeAngle = ANGLE_45;
2104 }
2105
2106 if( aPad->GetThermalSpokeAngle() != defaultThermalSpokeAngle )
2107 {
2108 m_out->Print( "(thermal_bridge_angle %s)",
2110 }
2111
2112 if( aPad->GetLocalThermalGapOverride().has_value() )
2113 {
2114 m_out->Print( "(thermal_gap %s)",
2115 formatInternalUnits( aPad->GetLocalThermalGapOverride().value() ).c_str() );
2116 }
2117
2118 auto anchorShape =
2119 [&]( PCB_LAYER_ID aLayer )
2120 {
2121 switch( aPad->GetAnchorPadShape( aLayer ) )
2122 {
2123 case PAD_SHAPE::RECTANGLE: return "rect";
2124 default:
2125 case PAD_SHAPE::CIRCLE: return "circle";
2126 }
2127 };
2128
2129 auto formatPrimitives =
2130 [&]( PCB_LAYER_ID aLayer )
2131 {
2132 m_out->Print( "(primitives" );
2133
2134 // Output all basic shapes
2135 for( const std::shared_ptr<PCB_SHAPE>& primitive : aPad->GetPrimitives( aLayer ) )
2136 {
2137 const SHAPE_T libShape = primitive->GetLibraryShape();
2138
2139 switch( libShape )
2140 {
2141 case SHAPE_T::SEGMENT:
2142 if( primitive->IsProxyItem() )
2143 {
2144 m_out->Print( "(gr_vector (start %s) (end %s)",
2145 formatInternalUnits( primitive->GetStart() ).c_str(),
2146 formatInternalUnits( primitive->GetEnd() ).c_str() );
2147 }
2148 else
2149 {
2150 m_out->Print( "(gr_line (start %s) (end %s)",
2151 formatInternalUnits( primitive->GetStart() ).c_str(),
2152 formatInternalUnits( primitive->GetEnd() ).c_str() );
2153 }
2154 break;
2155
2156 case SHAPE_T::RECTANGLE:
2157 if( primitive->IsProxyItem() )
2158 {
2159 m_out->Print( "(gr_bbox (start %s) (end %s)",
2160 formatInternalUnits( primitive->GetLibraryStart() ).c_str(),
2161 formatInternalUnits( primitive->GetLibraryEnd() ).c_str() );
2162 }
2163 else
2164 {
2165 m_out->Print( "(gr_rect (start %s) (end %s)",
2166 formatInternalUnits( primitive->GetLibraryStart() ).c_str(),
2167 formatInternalUnits( primitive->GetLibraryEnd() ).c_str() );
2168
2169 if( primitive->GetCornerRadius() > 0 )
2170 {
2171 m_out->Print( " (radius %s)",
2172 formatInternalUnits( primitive->GetCornerRadius() ).c_str() );
2173 }
2174 }
2175 break;
2176
2177 case SHAPE_T::ARC:
2178 m_out->Print( "(gr_arc (start %s) (mid %s) (end %s)",
2179 formatInternalUnits( primitive->GetLibraryStart() ).c_str(),
2180 formatInternalUnits( primitive->GetLibraryArcMid() ).c_str(),
2181 formatInternalUnits( primitive->GetLibraryEnd() ).c_str() );
2182 break;
2183
2184 case SHAPE_T::CIRCLE:
2185 m_out->Print( "(gr_circle (center %s) (end %s)",
2186 formatInternalUnits( primitive->GetLibraryStart() ).c_str(),
2187 formatInternalUnits( primitive->GetLibraryEnd() ).c_str() );
2188 break;
2189
2190 case SHAPE_T::BEZIER:
2191 // Pad primitives are stored in raw library coordinates and read back
2192 // raw, so emit library coordinates like the other primitive types above.
2193 m_out->Print( "(gr_curve (pts (xy %s) (xy %s) (xy %s) (xy %s))",
2194 formatInternalUnits( primitive->GetLibraryStart() ).c_str(),
2195 formatInternalUnits( primitive->GetLibraryBezierC1() ).c_str(),
2196 formatInternalUnits( primitive->GetLibraryBezierC2() ).c_str(),
2197 formatInternalUnits( primitive->GetLibraryEnd() ).c_str() );
2198 break;
2199
2200 case SHAPE_T::POLY:
2201 if( primitive->IsPolyShapeValid() )
2202 {
2203 const SHAPE_POLY_SET poly = primitive->GetLibraryPolyShape();
2204 const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 );
2205
2206 m_out->Print( "(gr_poly" );
2207 formatPolyPts( outline );
2208 }
2209 break;
2210
2211 default:
2212 break;
2213 }
2214
2215 if( !primitive->IsProxyItem() )
2216 m_out->Print( "(width %s)", formatInternalUnits( primitive->GetWidth() ).c_str() );
2217
2218 // The filled flag represents if a solid fill is present on circles,
2219 // rectangles and polygons
2220 if( libShape == SHAPE_T::POLY || libShape == SHAPE_T::RECTANGLE || libShape == SHAPE_T::CIRCLE )
2221 {
2222 KICAD_FORMAT::FormatBool( m_out, "fill", primitive->IsSolidFill() );
2223 }
2224
2225 m_out->Print( ")" );
2226 }
2227
2228 m_out->Print( ")" ); // end of (primitives
2229 };
2230
2232 {
2233 m_out->Print( "(options" );
2234
2236 m_out->Print( "(clearance convexhull)" );
2237 else
2238 m_out->Print( "(clearance outline)" );
2239
2240 // Output the anchor pad shape (circle/rect)
2241 m_out->Print( "(anchor %s)", anchorShape( PADSTACK::ALL_LAYERS ) );
2242
2243 m_out->Print( ")"); // end of (options ...
2244
2245 // Output graphic primitive of the pad shape
2246 formatPrimitives( PADSTACK::ALL_LAYERS );
2247 }
2248
2251
2252 if( aPad->Padstack().FrontOuterLayers().has_solder_mask.has_value()
2253 || aPad->Padstack().BackOuterLayers().has_solder_mask.has_value() )
2254 {
2255 m_out->Print( 0, " (tenting " );
2260 m_out->Print( 0, ")" );
2261 }
2262
2264
2265 // TODO: Refactor so that we call formatPadLayer( ALL_LAYERS ) above instead of redundant code
2266 auto formatPadLayer =
2267 [&]( PCB_LAYER_ID aLayer )
2268 {
2269 const PADSTACK& padstack = aPad->Padstack();
2270
2271 m_out->Print( "(shape %s)", shapeName( aLayer ) );
2272
2273 m_out->Print( "(size %s)", formatInternalUnits( padstack.Size( aLayer ) ).c_str() );
2274
2275 const VECTOR2I delta = aPad->GetDelta( aLayer );
2276
2277 if( delta.x != 0 || delta.y != 0 )
2278 m_out->Print( "(rect_delta %s)", formatInternalUnits( delta ).c_str() );
2279
2280 shapeoffset = aPad->GetOffset( aLayer );
2281
2282 if( shapeoffset.x != 0 || shapeoffset.y != 0 )
2283 m_out->Print( "(offset %s)", formatInternalUnits( padstack.Offset( aLayer ) ).c_str() );
2284
2285 formatCornerProperties( aLayer );
2286
2287 if( aPad->GetShape( aLayer ) == PAD_SHAPE::CUSTOM )
2288 {
2289 m_out->Print( "(options" );
2290
2291 // Output the anchor pad shape (circle/rect)
2292 m_out->Print( "(anchor %s)", anchorShape( aLayer ) );
2293
2294 m_out->Print( ")" ); // end of (options ...
2295
2296 // Output graphic primitive of the pad shape
2297 formatPrimitives( aLayer );
2298 }
2299
2300 EDA_ANGLE defaultLayerAngle = ANGLE_90;
2301
2302 if( aPad->GetShape( aLayer ) == PAD_SHAPE::CIRCLE ||
2303 ( aPad->GetShape( aLayer ) == PAD_SHAPE::CUSTOM
2304 && aPad->GetAnchorPadShape( aLayer ) == PAD_SHAPE::CIRCLE ) )
2305 {
2306 defaultLayerAngle = ANGLE_45;
2307 }
2308
2309 EDA_ANGLE layerSpokeAngle = padstack.ThermalSpokeAngle( aLayer );
2310
2311 if( layerSpokeAngle != defaultLayerAngle )
2312 {
2313 m_out->Print( "(thermal_bridge_angle %s)",
2314 EDA_UNIT_UTILS::FormatAngle( layerSpokeAngle ).c_str() );
2315 }
2316
2317 if( padstack.ThermalGap( aLayer ).has_value() )
2318 {
2319 m_out->Print( "(thermal_gap %s)",
2320 formatInternalUnits( *padstack.ThermalGap( aLayer ) ).c_str() );
2321 }
2322
2323 if( padstack.ThermalSpokeWidth( aLayer ).has_value() )
2324 {
2325 m_out->Print( "(thermal_bridge_width %s)",
2326 formatInternalUnits( *padstack.ThermalSpokeWidth( aLayer ) ).c_str() );
2327 }
2328
2329 if( padstack.Clearance( aLayer ).has_value() )
2330 {
2331 m_out->Print( "(clearance %s)",
2332 formatInternalUnits( *padstack.Clearance( aLayer ) ).c_str() );
2333 }
2334
2335 if( padstack.ZoneConnection( aLayer ).has_value() )
2336 {
2337 m_out->Print( "(zone_connect %d)",
2338 static_cast<int>( *padstack.ZoneConnection( aLayer ) ) );
2339 }
2340 };
2341
2342
2343 if( aPad->Padstack().Mode() != PADSTACK::MODE::NORMAL )
2344 {
2346 {
2347 m_out->Print( "(padstack (mode front_inner_back)" );
2348
2349 m_out->Print( "(layer \"Inner\"" );
2350 formatPadLayer( PADSTACK::INNER_LAYERS );
2351 m_out->Print( ")" );
2352 m_out->Print( "(layer \"B.Cu\"" );
2353 formatPadLayer( B_Cu );
2354 m_out->Print( ")" );
2355 }
2356 else
2357 {
2358 m_out->Print( "(padstack (mode custom)" );
2359
2360 int layerCount = board ? board->GetCopperLayerCount() : MAX_CU_LAYERS;
2361
2362 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, layerCount ) )
2363 {
2364 if( layer == F_Cu )
2365 continue;
2366
2367 m_out->Print( "(layer %s", m_out->Quotew( LSET::Name( layer ) ).c_str() );
2368 formatPadLayer( layer );
2369 m_out->Print( ")" );
2370 }
2371 }
2372
2373 m_out->Print( ")" );
2374 }
2375
2376 m_out->Print( ")" );
2377}
2378
2379
2380void PCB_IO_KICAD_SEXPR::format( const PCB_BARCODE* aBarcode ) const
2381{
2382 wxCHECK_RET( aBarcode != nullptr && m_out != nullptr, "" );
2383
2384 m_out->Print( "(barcode" );
2385
2386 if( aBarcode->IsLocked() )
2387 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2388
2389 m_out->Print( "(at %s %s)",
2390 formatInternalUnits( aBarcode->GetPosition() ).c_str(),
2391 EDA_UNIT_UTILS::FormatAngle( aBarcode->GetAngle() ).c_str() );
2392
2393 formatLayer( aBarcode->GetLayer() );
2394
2395 m_out->Print( "(size %s %s)",
2396 formatInternalUnits( aBarcode->GetWidth() ).c_str(),
2397 formatInternalUnits( aBarcode->GetHeight() ).c_str() );
2398
2399 m_out->Print( "(text %s)", m_out->Quotew( aBarcode->GetText() ).c_str() );
2400
2401 m_out->Print( "(text_height %s)", formatInternalUnits( aBarcode->GetTextSize() ).c_str() );
2402
2403 const char* typeStr = "code39";
2404
2405 switch( aBarcode->GetKind() )
2406 {
2407 case BARCODE_T::CODE_39: typeStr = "code39"; break;
2408 case BARCODE_T::CODE_128: typeStr = "code128"; break;
2409 case BARCODE_T::DATA_MATRIX: typeStr = "datamatrix"; break;
2410 case BARCODE_T::QR_CODE: typeStr = "qr"; break;
2411 case BARCODE_T::MICRO_QR_CODE: typeStr = "microqr"; break;
2412 }
2413
2414 m_out->Print( "(type %s)", typeStr );
2415
2416 if( aBarcode->GetKind() == BARCODE_T::QR_CODE
2417 || aBarcode->GetKind() == BARCODE_T::MICRO_QR_CODE )
2418 {
2419 const char* eccStr = "L";
2420 switch( aBarcode->GetErrorCorrection() )
2421 {
2422 case BARCODE_ECC_T::L: eccStr = "L"; break;
2423 case BARCODE_ECC_T::M: eccStr = "M"; break;
2424 case BARCODE_ECC_T::Q: eccStr = "Q"; break;
2425 case BARCODE_ECC_T::H: eccStr = "H"; break;
2426 }
2427
2428 m_out->Print( "(ecc_level %s)", eccStr );
2429 }
2430
2431 KICAD_FORMAT::FormatBool( m_out, "hide", !aBarcode->GetShowText() );
2432 KICAD_FORMAT::FormatBool( m_out, "knockout", aBarcode->IsKnockout() );
2433
2434 if( aBarcode->GetMargin().x != 0 || aBarcode->GetMargin().y != 0 )
2435 {
2436 m_out->Print( "(margins %s %s)", formatInternalUnits( aBarcode->GetMargin().x ).c_str(),
2437 formatInternalUnits( aBarcode->GetMargin().y ).c_str() );
2438 }
2439
2441
2442 m_out->Print( ")" );
2443}
2444
2445
2446void PCB_IO_KICAD_SEXPR::format( const PCB_TEXT* aText ) const
2447{
2448 FOOTPRINT* parentFP = aText->GetParentFootprint();
2449 std::string prefix;
2450 std::string type;
2451 VECTOR2I pos = aText->GetTextPos();
2452 const PCB_FIELD* field = dynamic_cast<const PCB_FIELD*>( aText );
2453
2454 // Always format dimension text as gr_text
2455 if( dynamic_cast<const PCB_DIMENSION_BASE*>( aText ) )
2456 parentFP = nullptr;
2457
2458 if( parentFP )
2459 {
2460 prefix = "fp";
2461 type = "user";
2462
2463 pos = parentFP->GetTransform().InverseApply( pos );
2464 }
2465 else
2466 {
2467 prefix = "gr";
2468 }
2469
2470 if( !field )
2471 {
2472 m_out->Print( "(%s_text %s %s",
2473 prefix.c_str(),
2474 type.c_str(),
2475 m_out->Quotew( aText->GetText() ).c_str() );
2476
2477 if( aText->IsLocked() )
2478 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2479 }
2480
2481 m_out->Print( "(at %s %s)",
2482 formatInternalUnits( pos ).c_str(),
2483 EDA_UNIT_UTILS::FormatAngle( aText->GetTextAngle() ).c_str() );
2484
2485 if( parentFP && !aText->IsKeepUpright() )
2486 KICAD_FORMAT::FormatBool( m_out, "unlocked", true );
2487
2488 formatLayer( aText->GetLayer(), aText->IsKnockout() );
2489
2490 if( field && !field->IsVisible() )
2491 KICAD_FORMAT::FormatBool( m_out, "hide", true );
2492
2494
2495 // Currently, texts have no specific color and no hyperlink.
2496 // so ensure they are never written in kicad_pcb file
2497 int ctl_flags = CTL_OMIT_COLOR | CTL_OMIT_HYPERLINK;
2498
2499 if( parentFP )
2500 {
2501 EDA_TEXT* mut = const_cast<EDA_TEXT*>( static_cast<const EDA_TEXT*>( aText ) );
2502 const VECTOR2I savedSize = mut->GetTextSize();
2503 const int savedThickness = mut->GetTextThickness();
2504 const bool mutateThickness = !mut->GetAutoThickness();
2505
2506 mut->SetTextSize( unbakeSize( savedSize, parentFP ) );
2507
2508 if( mutateThickness )
2509 mut->SetTextThickness( unbakeLinear( savedThickness, parentFP ) );
2510
2511 aText->EDA_TEXT::Format( m_out, ctl_flags );
2512
2513 mut->SetTextSize( savedSize );
2514
2515 if( mutateThickness )
2516 mut->SetTextThickness( savedThickness );
2517 }
2518 else
2519 {
2520 aText->EDA_TEXT::Format( m_out, ctl_flags );
2521 }
2522
2523 if( aText->GetFont() && aText->GetFont()->IsOutline() )
2524 formatRenderCache( aText );
2525
2526 if( !field )
2527 m_out->Print( ")" );
2528}
2529
2530
2531void PCB_IO_KICAD_SEXPR::format( const PCB_TEXTBOX* aTextBox ) const
2532{
2533 FOOTPRINT* parentFP = aTextBox->GetParentFootprint();
2534
2535 m_out->Print( "(%s %s",
2536 aTextBox->Type() == PCB_TABLECELL_T ? "table_cell"
2537 : parentFP ? "fp_text_box"
2538 : "gr_text_box",
2539 m_out->Quotew( aTextBox->GetText() ).c_str() );
2540
2541 if( aTextBox->IsLocked() )
2542 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2543
2544 // Use the lib-frame shape. The board-frame shape can flip to POLY for
2545 // rendering when the FP rotation is non-cardinal.
2546 SHAPE_T libShape = aTextBox->GetLibraryShape();
2547
2548 if( libShape == SHAPE_T::RECTANGLE )
2549 {
2550 m_out->Print( "(start %s) (end %s)", formatInternalUnits( aTextBox->GetLibraryStart() ).c_str(),
2551 formatInternalUnits( aTextBox->GetLibraryEnd() ).c_str() );
2552 }
2553 else if( libShape == SHAPE_T::POLY )
2554 {
2555 // Fall back to the runtime polygon if the lib copy was never seeded.
2556 const SHAPE_POLY_SET& libPoly = aTextBox->GetLibPoly();
2557 const bool haveLibPoly = libPoly.OutlineCount() > 0;
2558 const SHAPE_POLY_SET& poly = haveLibPoly ? libPoly : aTextBox->GetPolyShape();
2559
2560 if( poly.OutlineCount() > 0 )
2561 formatPolyPts( poly.Outline( 0 ), haveLibPoly ? nullptr : parentFP );
2562 }
2563 else
2564 {
2565 UNIMPLEMENTED_FOR( aTextBox->SHAPE_T_asString() );
2566 }
2567
2568 m_out->Print( "(margins %s %s %s %s)",
2569 formatInternalUnits( aTextBox->GetMarginLeft() ).c_str(),
2570 formatInternalUnits( aTextBox->GetMarginTop() ).c_str(),
2571 formatInternalUnits( aTextBox->GetMarginRight() ).c_str(),
2572 formatInternalUnits( aTextBox->GetMarginBottom() ).c_str() );
2573
2574 if( const PCB_TABLECELL* cell = dynamic_cast<const PCB_TABLECELL*>( aTextBox ) )
2575 m_out->Print( "(span %d %d)", cell->GetColSpan(), cell->GetRowSpan() );
2576
2577 EDA_ANGLE angle = aTextBox->GetTextAngle();
2578
2579 if( parentFP )
2580 {
2581 angle -= parentFP->GetOrientation();
2582 angle.Normalize720();
2583 }
2584
2585 if( !angle.IsZero() )
2586 m_out->Print( "(angle %s)", EDA_UNIT_UTILS::FormatAngle( angle ).c_str() );
2587
2588 formatLayer( aTextBox->GetLayer() );
2589
2591
2592 aTextBox->EDA_TEXT::Format( m_out, 0 );
2593
2594 if( aTextBox->Type() != PCB_TABLECELL_T )
2595 {
2596 KICAD_FORMAT::FormatBool( m_out, "border", aTextBox->IsBorderEnabled() );
2597 aTextBox->GetStroke().Format( m_out, pcbIUScale );
2598 }
2599
2600 KICAD_FORMAT::FormatBool( m_out, "knockout", aTextBox->IsKnockout() );
2601
2602 if( aTextBox->GetFont() && aTextBox->GetFont()->IsOutline() )
2603 formatRenderCache( aTextBox );
2604
2605 m_out->Print( ")" );
2606}
2607
2608
2609void PCB_IO_KICAD_SEXPR::format( const PCB_TABLE* aTable ) const
2610{
2611 wxCHECK_RET( aTable != nullptr && m_out != nullptr, "" );
2612
2613 m_out->Print( "(table (column_count %d)", aTable->GetColCount() );
2614
2616
2617 if( aTable->IsLocked() )
2618 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2619
2620 formatLayer( aTable->GetLayer() );
2621
2622 m_out->Print( "(border" );
2623 KICAD_FORMAT::FormatBool( m_out, "external", aTable->StrokeExternal() );
2625
2626 if( aTable->StrokeExternal() || aTable->StrokeHeaderSeparator() )
2627 aTable->GetBorderStroke().Format( m_out, pcbIUScale );
2628
2629 m_out->Print( ")" ); // Close `border` token.
2630
2631 m_out->Print( "(separators" );
2632 KICAD_FORMAT::FormatBool( m_out, "rows", aTable->StrokeRows() );
2633 KICAD_FORMAT::FormatBool( m_out, "cols", aTable->StrokeColumns() );
2634
2635 if( aTable->StrokeRows() || aTable->StrokeColumns() )
2637
2638 m_out->Print( ")" ); // Close `separators` token.
2639
2640 m_out->Print( "(column_widths" );
2641
2642 for( int col = 0; col < aTable->GetColCount(); ++col )
2643 m_out->Print( " %s", formatInternalUnits( aTable->GetColWidth( col ) ).c_str() );
2644
2645 m_out->Print( ")" );
2646
2647 m_out->Print( "(row_heights" );
2648
2649 for( int row = 0; row < aTable->GetRowCount(); ++row )
2650 m_out->Print( " %s", formatInternalUnits( aTable->GetRowHeight( row ) ).c_str() );
2651
2652 m_out->Print( ")" );
2653
2654 m_out->Print( "(cells" );
2655
2656 for( PCB_TABLECELL* cell : aTable->GetCells() )
2657 format( static_cast<PCB_TEXTBOX*>( cell ) );
2658
2659 m_out->Print( ")" ); // Close `cells` token.
2660 m_out->Print( ")" ); // Close `table` token.
2661}
2662
2663
2664void PCB_IO_KICAD_SEXPR::format( const PCB_GROUP* aGroup ) const
2665{
2666 wxArrayString memberIds;
2667
2668 // Validate member pointers against the board cache to avoid use-after-free on dangling
2669 // pointers (e.g. when a group held a reference to a deleted item). This validation only
2670 // applies when the group itself is part of m_board; for groups created off-board (e.g. a
2671 // DeepClone() used by the clipboard) the cache contains the originals, not our clones, so
2672 // skip the validation in that case and trust the member pointers.
2673 bool validateAgainstBoard = false;
2674 std::unordered_set<const EDA_ITEM*> validPtrs;
2675
2676 if( m_board )
2677 {
2678 const auto& cache = m_board->GetItemByIdCache();
2679
2680 for( const auto& [uuid, item] : cache )
2681 validPtrs.insert( item );
2682
2683 validateAgainstBoard = validPtrs.count( aGroup ) > 0;
2684 }
2685
2686 if( validateAgainstBoard )
2687 {
2688 for( EDA_ITEM* member : aGroup->GetItems() )
2689 {
2690 if( validPtrs.count( member ) )
2691 memberIds.Add( member->m_Uuid.AsString() );
2692 }
2693 }
2694 else
2695 {
2696 for( EDA_ITEM* member : aGroup->GetItems() )
2697 memberIds.Add( member->m_Uuid.AsString() );
2698 }
2699
2700 if( memberIds.empty() )
2701 return;
2702
2703 m_out->Print( "(group %s", m_out->Quotew( aGroup->GetName() ).c_str() );
2704
2706
2707 if( aGroup->IsLocked() )
2708 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2709
2710 if( aGroup->HasDesignBlockLink() )
2711 m_out->Print( "(lib_id \"%s\")", aGroup->GetDesignBlockLibId().Format().c_str() );
2712
2713 memberIds.Sort();
2714
2715 m_out->Print( "(members" );
2716
2717 for( const wxString& memberId : memberIds )
2718 m_out->Print( " %s", m_out->Quotew( memberId ).c_str() );
2719
2720 m_out->Print( ")" ); // Close `members` token.
2721 m_out->Print( ")" ); // Close `group` token.
2722}
2723
2724
2725void PCB_IO_KICAD_SEXPR::format( const PCB_GENERATOR* aGenerator ) const
2726{
2727 // Some conditions appear to still be creating ghost tuning patterns. Don't save them.
2728 if( aGenerator->GetGeneratorType() == wxT( "tuning_pattern" )
2729 && aGenerator->GetItems().empty() )
2730 {
2731 return;
2732 }
2733
2734 m_out->Print( "(generated" );
2735
2736 KICAD_FORMAT::FormatUuid( m_out, aGenerator->m_Uuid );
2737
2738 m_out->Print( "(type %s) (name %s) (layer %s)",
2739 TO_UTF8( aGenerator->GetGeneratorType() ),
2740 m_out->Quotew( aGenerator->GetName() ).c_str(),
2741 m_out->Quotew( LSET::Name( aGenerator->GetLayer() ) ).c_str() );
2742
2743 if( aGenerator->IsLocked() )
2744 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2745
2746 for( const auto& [key, value] : aGenerator->GetProperties() )
2747 {
2748 if( value.CheckType<double>() || value.CheckType<int>() || value.CheckType<long>()
2749 || value.CheckType<long long>() )
2750 {
2751 double val;
2752
2753 if( !value.GetAs( &val ) )
2754 continue;
2755
2756 std::string buf = fmt::format( "{:.10g}", val );
2757
2758 // Don't quote numbers
2759 m_out->Print( "(%s %s)", key.c_str(), buf.c_str() );
2760 }
2761 else if( value.CheckType<bool>() )
2762 {
2763 bool val;
2764 value.GetAs( &val );
2765
2766 KICAD_FORMAT::FormatBool( m_out, key, val );
2767 }
2768 else if( value.CheckType<VECTOR2I>() )
2769 {
2770 VECTOR2I val;
2771 value.GetAs( &val );
2772
2773 m_out->Print( "(%s (xy %s))",
2774 key.c_str(),
2775 formatInternalUnits( val ).c_str() );
2776 }
2777 else if( value.CheckType<SHAPE_LINE_CHAIN>() )
2778 {
2779 SHAPE_LINE_CHAIN val;
2780 value.GetAs( &val );
2781
2782 m_out->Print( "(%s ", key.c_str() );
2783 formatPolyPts( val );
2784 m_out->Print( ")" );
2785 }
2786 else
2787 {
2788 wxString val;
2789
2790 if( value.CheckType<wxString>() )
2791 {
2792 value.GetAs( &val );
2793 }
2794 else if( value.CheckType<std::string>() )
2795 {
2796 std::string str;
2797 value.GetAs( &str );
2798
2799 val = wxString::FromUTF8( str );
2800 }
2801
2802 m_out->Print( "(%s %s)", key.c_str(), m_out->Quotew( val ).c_str() );
2803 }
2804 }
2805
2806 wxArrayString memberIds;
2807
2808 for( EDA_ITEM* member : aGenerator->GetItems() )
2809 memberIds.Add( member->m_Uuid.AsString() );
2810
2811 memberIds.Sort();
2812
2813 m_out->Print( "(members" );
2814
2815 for( const wxString& memberId : memberIds )
2816 m_out->Print( " %s", m_out->Quotew( memberId ).c_str() );
2817
2818 m_out->Print( ")" ); // Close `members` token.
2819 m_out->Print( ")" ); // Close `generated` token.
2820}
2821
2822
2823void PCB_IO_KICAD_SEXPR::format( const PCB_TRACK* aTrack ) const
2824{
2825 if( aTrack->Type() == PCB_VIA_T )
2826 {
2827 PCB_LAYER_ID layer1, layer2;
2828
2829 const PCB_VIA* via = static_cast<const PCB_VIA*>( aTrack );
2830 const BOARD* board = via->GetBoard();
2831
2832 wxCHECK_RET( board != nullptr, wxT( "Via has no parent." ) );
2833
2834 m_out->Print( "(via" );
2835
2836 via->LayerPair( &layer1, &layer2 );
2837
2838 switch( via->GetViaType() )
2839 {
2840 case VIATYPE::THROUGH: // Default shape not saved.
2841 break;
2842
2843 case VIATYPE::BLIND:
2844 m_out->Print( " blind " );
2845 break;
2846
2847 case VIATYPE::BURIED:
2848 m_out->Print( " buried " );
2849 break;
2850
2851 case VIATYPE::MICROVIA:
2852 m_out->Print( " micro " );
2853 break;
2854
2855 default:
2856 THROW_IO_ERROR( wxString::Format( _( "unknown via type %d" ), via->GetViaType() ) );
2857 }
2858
2859 m_out->Print( "(at %s) (size %s)",
2860 formatInternalUnits( aTrack->GetStart() ).c_str(),
2861 formatInternalUnits( via->GetWidth( F_Cu ) ).c_str() );
2862
2863 // Old boards were using UNDEFINED_DRILL_DIAMETER value in file for via drill when
2864 // via drill was the netclass value.
2865 // recent boards always set the via drill to the actual value, but now we need to
2866 // always store the drill value, because netclass value is not stored in the board file.
2867 // Otherwise the drill value of some (old) vias can be unknown
2868 if( via->GetDrill() != UNDEFINED_DRILL_DIAMETER )
2869 m_out->Print( "(drill %s)", formatInternalUnits( via->GetDrill() ).c_str() );
2870 else
2871 m_out->Print( "(drill %s)", formatInternalUnits( via->GetDrillValue() ).c_str() );
2872
2873 if( via->Padstack().SecondaryDrill().size.x > 0 )
2874 {
2875 m_out->Print( "(backdrill (size %s) (layers %s %s))",
2876 formatInternalUnits( via->Padstack().SecondaryDrill().size.x ).c_str(),
2877 m_out->Quotew( LSET::Name( via->Padstack().SecondaryDrill().start ) ).c_str(),
2878 m_out->Quotew( LSET::Name( via->Padstack().SecondaryDrill().end ) ).c_str() );
2879 }
2880
2881 if( via->Padstack().TertiaryDrill().size.x > 0 )
2882 {
2883 m_out->Print( "(tertiary_drill (size %s) (layers %s %s))",
2884 formatInternalUnits( via->Padstack().TertiaryDrill().size.x ).c_str(),
2885 m_out->Quotew( LSET::Name( via->Padstack().TertiaryDrill().start ) ).c_str(),
2886 m_out->Quotew( LSET::Name( via->Padstack().TertiaryDrill().end ) ).c_str() );
2887 }
2888
2889 auto formatPostMachining = [&]( const char* aName, const PADSTACK::POST_MACHINING_PROPS& aProps )
2890 {
2891 if( !aProps.mode.has_value() || aProps.mode == PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED )
2892 return;
2893
2894 m_out->Print( "(%s %s", aName,
2895 aProps.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE ? "counterbore" : "countersink" );
2896
2897 if( aProps.size > 0 )
2898 m_out->Print( " (size %s)", formatInternalUnits( aProps.size ).c_str() );
2899
2900 if( aProps.depth > 0 )
2901 m_out->Print( " (depth %s)", formatInternalUnits( aProps.depth ).c_str() );
2902
2903 if( aProps.angle > 0 )
2904 m_out->Print( " (angle %s)", FormatDouble2Str( aProps.angle / 10.0 ).c_str() );
2905
2906 m_out->Print( ")" );
2907 };
2908
2909 formatPostMachining( "front_post_machining", via->Padstack().FrontPostMachining() );
2910 formatPostMachining( "back_post_machining", via->Padstack().BackPostMachining() );
2911
2912 m_out->Print( "(layers %s %s)",
2913 m_out->Quotew( LSET::Name( layer1 ) ).c_str(),
2914 m_out->Quotew( LSET::Name( layer2 ) ).c_str() );
2915
2916 switch( via->Padstack().UnconnectedLayerMode() )
2917 {
2919 KICAD_FORMAT::FormatBool( m_out, "remove_unused_layers", true );
2920 KICAD_FORMAT::FormatBool( m_out, "keep_end_layers", false );
2921 break;
2922
2924 KICAD_FORMAT::FormatBool( m_out, "remove_unused_layers", true );
2925 KICAD_FORMAT::FormatBool( m_out, "keep_end_layers", true );
2926 break;
2927
2929 KICAD_FORMAT::FormatBool( m_out, "start_end_only", true );
2930 break;
2931
2933 break;
2934 }
2935
2936 if( via->IsLocked() )
2937 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2938
2939 if( via->GetIsFree() )
2940 KICAD_FORMAT::FormatBool( m_out, "free", true );
2941
2942 if( via->GetRemoveUnconnected() )
2943 {
2944 m_out->Print( "(zone_layer_connections" );
2945
2946 for( PCB_LAYER_ID layer : board->GetEnabledLayers().CuStack() )
2947 {
2948 if( via->GetZoneLayerOverride( layer ) == ZLO_FORCE_FLASHED )
2949 m_out->Print( " %s", m_out->Quotew( LSET::Name( layer ) ).c_str() );
2950 }
2951
2952 m_out->Print( ")" );
2953 }
2954
2955 const PADSTACK& padstack = via->Padstack();
2956
2957 if( padstack.FrontOuterLayers().has_solder_mask.has_value()
2958 || padstack.BackOuterLayers().has_solder_mask.has_value() )
2959 {
2960 m_out->Print( 0, " (tenting " );
2962 padstack.FrontOuterLayers().has_solder_mask );
2964 padstack.BackOuterLayers().has_solder_mask );
2965 m_out->Print( 0, ")" );
2966 }
2967
2968 if( padstack.Drill().is_capped.has_value() )
2969 KICAD_FORMAT::FormatOptBool( m_out, "capping", padstack.Drill().is_capped );
2970
2971 if( padstack.FrontOuterLayers().has_covering.has_value()
2972 || padstack.BackOuterLayers().has_covering.has_value() )
2973 {
2974 m_out->Print( 0, " (covering " );
2976 padstack.FrontOuterLayers().has_covering );
2978 padstack.BackOuterLayers().has_covering );
2979 m_out->Print( 0, ")" );
2980 }
2981
2982 if( padstack.FrontOuterLayers().has_plugging.has_value()
2983 || padstack.BackOuterLayers().has_plugging.has_value() )
2984 {
2985 m_out->Print( 0, " (plugging " );
2987 padstack.FrontOuterLayers().has_plugging );
2989 padstack.BackOuterLayers().has_plugging );
2990 m_out->Print( 0, ")" );
2991 }
2992
2993 if( padstack.Drill().is_filled.has_value() )
2994 KICAD_FORMAT::FormatOptBool( m_out, "filling", padstack.Drill().is_filled );
2995
2996 if( padstack.Mode() != PADSTACK::MODE::NORMAL )
2997 {
2998 m_out->Print( "(padstack" );
2999
3000 if( padstack.Mode() == PADSTACK::MODE::FRONT_INNER_BACK )
3001 {
3002 m_out->Print( "(mode front_inner_back)" );
3003
3004 m_out->Print( "(layer \"Inner\"" );
3005 m_out->Print( "(size %s)",
3006 formatInternalUnits( padstack.Size( PADSTACK::INNER_LAYERS ).x ).c_str() );
3007 m_out->Print( ")" );
3008 m_out->Print( "(layer \"B.Cu\"" );
3009 m_out->Print( "(size %s)",
3010 formatInternalUnits( padstack.Size( B_Cu ).x ).c_str() );
3011 m_out->Print( ")" );
3012 }
3013 else
3014 {
3015 m_out->Print( "(mode custom)" );
3016
3017 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, board->GetCopperLayerCount() ) )
3018 {
3019 if( layer == F_Cu )
3020 continue;
3021
3022 m_out->Print( "(layer %s", m_out->Quotew( LSET::Name( layer ) ).c_str() );
3023 m_out->Print( "(size %s)",
3024 formatInternalUnits( padstack.Size( layer ).x ).c_str() );
3025 m_out->Print( ")" );
3026 }
3027 }
3028
3029 m_out->Print( ")" );
3030 }
3031
3032 if( !isDefaultTeardropParameters( via->GetTeardropParams() ) )
3033 formatTeardropParameters( via->GetTeardropParams() );
3034 }
3035 else
3036 {
3037 if( aTrack->Type() == PCB_ARC_T )
3038 {
3039 const PCB_ARC* arc = static_cast<const PCB_ARC*>( aTrack );
3040
3041 m_out->Print( "(arc (start %s) (mid %s) (end %s) (width %s)",
3042 formatInternalUnits( arc->GetStart() ).c_str(),
3043 formatInternalUnits( arc->GetMid() ).c_str(),
3044 formatInternalUnits( arc->GetEnd() ).c_str(),
3045 formatInternalUnits( arc->GetWidth() ).c_str() );
3046 }
3047 else
3048 {
3049 m_out->Print( "(segment (start %s) (end %s) (width %s)",
3050 formatInternalUnits( aTrack->GetStart() ).c_str(),
3051 formatInternalUnits( aTrack->GetEnd() ).c_str(),
3052 formatInternalUnits( aTrack->GetWidth() ).c_str() );
3053 }
3054
3055 if( aTrack->IsLocked() )
3056 KICAD_FORMAT::FormatBool( m_out, "locked", true );
3057
3058 if( aTrack->GetLayerSet().count() > 1 )
3059 formatLayers( aTrack->GetLayerSet(), false /* enumerate layers */ );
3060 else
3061 formatLayer( aTrack->GetLayer() );
3062
3063 if( aTrack->HasSolderMask()
3064 && aTrack->GetLocalSolderMaskMargin().has_value()
3065 && IsExternalCopperLayer( aTrack->GetLayer() ) )
3066 {
3067 m_out->Print( "(solder_mask_margin %s)",
3068 formatInternalUnits( aTrack->GetLocalSolderMaskMargin().value() ).c_str() );
3069 }
3070 }
3071
3072 if( !( m_ctl & CTL_OMIT_PAD_NETS ) )
3073 m_out->Print( "(net %s)", m_out->Quotew( aTrack->GetNetname() ).c_str() );
3074
3076 m_out->Print( ")" );
3077}
3078
3079
3080void PCB_IO_KICAD_SEXPR::format( const ZONE* aZone ) const
3081{
3082 m_out->Print( "(zone" );
3083
3084 if( !( m_ctl & CTL_OMIT_PAD_NETS ) && aZone->IsOnCopperLayer() && !aZone->GetIsRuleArea()
3085 && aZone->GetNetCode() > 0 )
3086 {
3087 m_out->Print( "(net %s)", m_out->Quotew( aZone->GetNetname() ).c_str() );
3088 }
3089
3090 if( aZone->IsLocked() )
3091 KICAD_FORMAT::FormatBool( m_out, "locked", true );
3092
3093 // If a zone exists on multiple layers, format accordingly
3094 LSET layers = aZone->GetLayerSet();
3095
3096 if( aZone->GetBoard() )
3097 layers &= aZone->GetBoard()->GetEnabledLayers();
3098
3099 // Always enumerate every layer for a zone on a copper layer
3100 if( layers.count() > 1 )
3101 formatLayers( layers, aZone->IsOnCopperLayer(), true );
3102 else
3103 formatLayer( aZone->GetFirstLayer() );
3104
3105 if( !aZone->IsTeardropArea() )
3107
3108 if( !aZone->GetZoneName().empty() && !aZone->IsTeardropArea() )
3109 m_out->Print( "(name %s)", m_out->Quotew( aZone->GetZoneName() ).c_str() );
3110
3111 // Save the outline aux info
3112 std::string hatch;
3113
3114 switch( aZone->GetHatchStyle() )
3115 {
3116 default:
3117 case ZONE_BORDER_DISPLAY_STYLE::NO_HATCH: hatch = "none"; break;
3118 case ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE: hatch = "edge"; break;
3119 case ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL: hatch = "full"; break;
3120 }
3121
3122 m_out->Print( "(hatch %s %s)", hatch.c_str(),
3123 formatInternalUnits( aZone->GetBorderHatchPitch() ).c_str() );
3124
3125
3126
3127 if( aZone->GetAssignedPriority() > 0 )
3128 m_out->Print( "(priority %d)", aZone->GetAssignedPriority() );
3129
3130 // Add teardrop keywords in file: (attr (teardrop (type xxx))) where xxx is the teardrop type
3131 if( aZone->IsTeardropArea() )
3132 {
3133 m_out->Print( "(attr (teardrop (type %s)))",
3134 aZone->GetTeardropAreaType() == TEARDROP_TYPE::TD_VIAPAD ? "padvia"
3135 : "track_end" );
3136 }
3137
3138 m_out->Print( "(connect_pads" );
3139
3140 switch( aZone->GetPadConnection() )
3141 {
3142 default:
3143 case ZONE_CONNECTION::THERMAL: // Default option not saved or loaded.
3144 break;
3145
3147 m_out->Print( " thru_hole_only" );
3148 break;
3149
3151 m_out->Print( " yes" );
3152 break;
3153
3155 m_out->Print( " no" );
3156 break;
3157 }
3158
3159 m_out->Print( "(clearance %s)",
3160 formatInternalUnits( aZone->GetLocalClearance().value() ).c_str() );
3161
3162 m_out->Print( ")" );
3163
3164 m_out->Print( "(min_thickness %s)",
3165 formatInternalUnits( aZone->GetMinThickness() ).c_str() );
3166
3167 if( aZone->GetIsRuleArea() )
3168 {
3169 // Keepout settings
3170 m_out->Print( "(keepout (tracks %s) (vias %s) (pads %s) (copperpour %s) (footprints %s))",
3171 aZone->GetDoNotAllowTracks() ? "not_allowed" : "allowed",
3172 aZone->GetDoNotAllowVias() ? "not_allowed" : "allowed",
3173 aZone->GetDoNotAllowPads() ? "not_allowed" : "allowed",
3174 aZone->GetDoNotAllowZoneFills() ? "not_allowed" : "allowed",
3175 aZone->GetDoNotAllowFootprints() ? "not_allowed" : "allowed" );
3176
3177 // Multichannel settings
3178 m_out->Print( "(placement" );
3180
3181 switch( aZone->GetPlacementAreaSourceType() )
3182 {
3184 m_out->Print( "(sheetname %s)", m_out->Quotew( aZone->GetPlacementAreaSource() ).c_str() );
3185 break;
3187 m_out->Print( "(component_class %s)", m_out->Quotew( aZone->GetPlacementAreaSource() ).c_str() );
3188 break;
3190 m_out->Print( "(group %s)", m_out->Quotew( aZone->GetPlacementAreaSource() ).c_str() );
3191 break;
3192 // These are transitory and should not be saved
3194 break;
3195 }
3196
3197 m_out->Print( ")" );
3198 }
3199
3200 m_out->Print( "(fill" );
3201
3202 // Default is not filled.
3203 if( aZone->IsFilled() )
3204 m_out->Print( " yes" );
3205
3206 // Default is polygon filled.
3208 m_out->Print( "(mode hatch)" );
3209 else if( aZone->GetFillMode() == ZONE_FILL_MODE::COPPER_THIEVING )
3210 m_out->Print( "(mode thieving)" );
3211
3212 if( !aZone->IsTeardropArea() )
3213 {
3214 m_out->Print( "(thermal_gap %s) (thermal_bridge_width %s)",
3215 formatInternalUnits( aZone->GetThermalReliefGap() ).c_str(),
3216 formatInternalUnits( aZone->GetThermalReliefSpokeWidth() ).c_str() );
3217 }
3218
3220 {
3221 switch( aZone->GetCornerSmoothingType() )
3222 {
3224 m_out->Print( "(smoothing chamfer)" );
3225 break;
3226
3228 m_out->Print( "(smoothing fillet)" );
3229 break;
3230
3231 default:
3232 THROW_IO_ERROR( wxString::Format( _( "unknown zone corner smoothing type %d" ),
3233 aZone->GetCornerSmoothingType() ) );
3234 }
3235
3236 if( aZone->GetCornerRadius() != 0 )
3237 m_out->Print( "(radius %s)", formatInternalUnits( aZone->GetCornerRadius() ).c_str() );
3238 }
3239
3240 m_out->Print( "(island_removal_mode %d)",
3241 static_cast<int>( aZone->GetIslandRemovalMode() ) );
3242
3244 {
3245 m_out->Print( "(island_area_min %s)",
3246 formatInternalUnits( aZone->GetMinIslandArea() / pcbIUScale.IU_PER_MM ).c_str() );
3247 }
3248
3250 {
3251 m_out->Print( "(hatch_thickness %s) (hatch_gap %s) (hatch_orientation %s)",
3252 formatInternalUnits( aZone->GetHatchThickness() ).c_str(),
3253 formatInternalUnits( aZone->GetHatchGap() ).c_str(),
3254 FormatDouble2Str( aZone->GetHatchOrientation().AsDegrees() ).c_str() );
3255
3256 if( aZone->GetHatchSmoothingLevel() > 0 )
3257 {
3258 m_out->Print( "(hatch_smoothing_level %d) (hatch_smoothing_value %s)",
3259 aZone->GetHatchSmoothingLevel(),
3260 FormatDouble2Str( aZone->GetHatchSmoothingValue() ).c_str() );
3261 }
3262
3263 m_out->Print( "(hatch_border_algorithm %s) (hatch_min_hole_area %s)",
3264 aZone->GetHatchBorderAlgorithm() ? "hatch_thickness" : "min_thickness",
3265 FormatDouble2Str( aZone->GetHatchHoleMinArea() ).c_str() );
3266 }
3267 else if( aZone->GetFillMode() == ZONE_FILL_MODE::COPPER_THIEVING )
3268 {
3269 const THIEVING_SETTINGS& thieving = aZone->GetThievingSettings();
3270 const char* patternStr = "dots";
3271
3272 switch( thieving.pattern )
3273 {
3274 case THIEVING_PATTERN::SQUARES: patternStr = "squares"; break;
3275 case THIEVING_PATTERN::HATCH: patternStr = "hatch"; break;
3277 default: patternStr = "dots"; break;
3278 }
3279
3280 m_out->Print( "(thieving (type %s) (size %s) (gap %s) (width %s) "
3281 "(stagger %s) (orientation %s))",
3282 patternStr,
3283 formatInternalUnits( thieving.element_size ).c_str(),
3284 formatInternalUnits( thieving.gap ).c_str(),
3285 formatInternalUnits( thieving.line_width ).c_str(),
3286 thieving.stagger ? "yes" : "no",
3287 FormatDouble2Str( thieving.orientation.AsDegrees() ).c_str() );
3288 }
3289
3290 m_out->Print( ")" );
3291
3292 for( const auto& [layer, properties] : aZone->LayerProperties() )
3293 {
3294 format( properties, 0, layer );
3295 }
3296
3297 if( aZone->GetNumCorners() )
3298 {
3299 SHAPE_POLY_SET::POLYGON poly = aZone->Outline()->Polygon(0);
3300
3301 for( const SHAPE_LINE_CHAIN& chain : poly )
3302 {
3303 if( chain.PointCount() == 0 )
3304 continue;
3305
3306 m_out->Print( "(polygon" );
3308 m_out->Print( ")" );
3309 }
3310 }
3311
3312 // Save the PolysList (filled areas)
3313 for( PCB_LAYER_ID layer : aZone->GetLayerSet().Seq() )
3314 {
3315 const std::shared_ptr<SHAPE_POLY_SET>& fv = aZone->GetFilledPolysList( layer );
3316
3317 for( int ii = 0; ii < fv->OutlineCount(); ++ii )
3318 {
3319 m_out->Print( "(filled_polygon" );
3320 m_out->Print( "(layer %s)", m_out->Quotew( LSET::Name( layer ) ).c_str() );
3321
3322 if( aZone->IsIsland( layer, ii ) )
3323 KICAD_FORMAT::FormatBool( m_out, "island", true );
3324
3325 const SHAPE_LINE_CHAIN& chain = fv->COutline( ii );
3326
3328 m_out->Print( ")" );
3329 }
3330 }
3331
3332 m_out->Print( ")" );
3333}
3334
3335
3336void PCB_IO_KICAD_SEXPR::format( const ZONE_LAYER_PROPERTIES& aZoneLayerProperties, int aNestLevel,
3337 PCB_LAYER_ID aLayer ) const
3338{
3339 // Do not store the layer properties if no value is actually set.
3340 if( !aZoneLayerProperties.hatching_offset.has_value() )
3341 return;
3342
3343 m_out->Print( aNestLevel, "(property\n" );
3344 m_out->Print( aNestLevel, "(layer %s)\n", m_out->Quotew( LSET::Name( aLayer ) ).c_str() );
3345
3346 if( aZoneLayerProperties.hatching_offset.has_value() )
3347 {
3348 m_out->Print( aNestLevel, "(hatch_position (xy %s))",
3349 formatInternalUnits( aZoneLayerProperties.hatching_offset.value() ).c_str() );
3350 }
3351
3352 m_out->Print( aNestLevel, ")\n" );
3353}
3354
3355
3356PCB_IO_KICAD_SEXPR::PCB_IO_KICAD_SEXPR( int aControlFlags ) : PCB_IO( wxS( "KiCad" ) ),
3357 m_cache( nullptr ),
3358 m_ctl( aControlFlags )
3359{
3360 init( nullptr );
3361 m_out = &m_sf;
3362}
3363
3364
3369
3370
3371BOARD* PCB_IO_KICAD_SEXPR::LoadBoard( const wxString& aFileName, BOARD* aAppendToMe,
3372 const std::map<std::string, UTF8>* aProperties,
3373 PROJECT* aProject )
3374{
3375 FILE_LINE_READER reader( aFileName );
3376
3377 unsigned lineCount = 0;
3378
3379 // Collect the font substitution warnings (RAII - automatically reset on scope exit)
3381
3382 if( m_progressReporter )
3383 {
3384 m_progressReporter->Report( wxString::Format( _( "Loading %s..." ), aFileName ) );
3385
3386 if( !m_progressReporter->KeepRefreshing() )
3387 THROW_IO_ERROR( _( "Open canceled by user." ) );
3388
3389 while( reader.ReadLine() )
3390 lineCount++;
3391
3392 reader.Rewind();
3393 }
3394
3395 BOARD* board = DoLoad( reader, aAppendToMe, aProperties, m_progressReporter, lineCount );
3396
3397 // Give the filename to the board if it's new
3398 if( !aAppendToMe )
3399 board->SetFileName( aFileName );
3400
3401 return board;
3402}
3403
3404
3406 const std::map<std::string, UTF8>* aProperties,
3407 PROGRESS_REPORTER* aProgressReporter, unsigned aLineCount)
3408{
3409 init( aProperties );
3410
3411 bool preserveDestinationStackup =
3412 aProperties && aProperties->contains( PCB_IO_LOAD_PROPERTIES::APPEND_PRESERVE_DESTINATION_STACKUP );
3413
3414 PCB_IO_KICAD_SEXPR_PARSER parser( &aReader, aAppendToMe, m_queryUserCallback, aProgressReporter, aLineCount,
3415 preserveDestinationStackup );
3416
3418
3419 BOARD* board;
3420
3421 try
3422 {
3423 board = dynamic_cast<BOARD*>( parser.Parse() );
3424 }
3425 catch( const FUTURE_FORMAT_ERROR& )
3426 {
3427 // Don't wrap a FUTURE_FORMAT_ERROR in another
3428 throw;
3429 }
3430 catch( const PARSE_ERROR& parse_error )
3431 {
3432 if( parser.IsTooRecent() )
3433 throw FUTURE_FORMAT_ERROR( parse_error, parser.GetRequiredVersion() );
3434 else
3435 throw;
3436 }
3437
3438 if( !board )
3439 {
3440 // The parser loaded something that was valid, but wasn't a board.
3441 THROW_PARSE_ERROR( _( "This file does not contain a PCB." ), parser.CurSource(),
3442 parser.CurLine(), parser.CurLineNumber(), parser.CurOffset() );
3443 }
3444
3445 // Report any non-fatal parse warnings to the load info reporter
3446 for( const wxString& warning : parser.GetParseWarnings() )
3448
3449 return board;
3450}
3451
3452
3453void PCB_IO_KICAD_SEXPR::init( const std::map<std::string, UTF8>* aProperties )
3454{
3455 m_board = nullptr;
3456 m_reader = nullptr;
3457 m_props = aProperties;
3458}
3459
3460
3461void PCB_IO_KICAD_SEXPR::validateCache( const wxString& aLibraryPath, bool checkModified )
3462{
3463 // Suppress font substitution warnings (RAII - automatically restored on scope exit)
3464 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
3465
3466 if( !m_cache || !m_cache->IsPath( aLibraryPath ) || ( checkModified && m_cache->IsModified() ) )
3467 {
3468 // a spectacular episode in memory management:
3469 delete m_cache;
3470 m_cache = new FP_CACHE( this, aLibraryPath );
3471 m_cache->Load();
3472 }
3473}
3474
3475
3476void PCB_IO_KICAD_SEXPR::FootprintEnumerate( wxArrayString& aFootprintNames,
3477 const wxString& aLibPath, bool aBestEfforts,
3478 const std::map<std::string, UTF8>* aProperties )
3479{
3480 wxDir dir( aLibPath );
3481 wxString errorMsg;
3482
3483 init( aProperties );
3484
3485 try
3486 {
3487 validateCache( aLibPath );
3488 }
3489 catch( const IO_ERROR& ioe )
3490 {
3491 errorMsg = ioe.What();
3492 }
3493
3494 // Some of the files may have been parsed correctly so we want to add the valid files to
3495 // the library.
3496
3497 for( const auto& footprint : m_cache->GetFootprints() )
3498 aFootprintNames.Add( footprint.first );
3499
3500 if( !errorMsg.IsEmpty() && !aBestEfforts )
3501 THROW_IO_ERROR( errorMsg );
3502}
3503
3504
3505const FOOTPRINT* PCB_IO_KICAD_SEXPR::getFootprint( const wxString& aLibraryPath,
3506 const wxString& aFootprintName,
3507 const std::map<std::string, UTF8>* aProperties,
3508 bool checkModified )
3509{
3510 init( aProperties );
3511
3512 try
3513 {
3514 validateCache( aLibraryPath, checkModified );
3515 }
3516 catch( const IO_ERROR& )
3517 {
3518 // do nothing with the error
3519 }
3520
3521 auto it = m_cache->GetFootprints().find( aFootprintName );
3522
3523 if( it == m_cache->GetFootprints().end() )
3524 return nullptr;
3525
3526 return it->second->GetFootprint().get();
3527}
3528
3529
3530const FOOTPRINT* PCB_IO_KICAD_SEXPR::GetEnumeratedFootprint( const wxString& aLibraryPath,
3531 const wxString& aFootprintName,
3532 const std::map<std::string, UTF8>* aProperties )
3533{
3534 return getFootprint( aLibraryPath, aFootprintName, aProperties, false );
3535}
3536
3537
3538bool PCB_IO_KICAD_SEXPR::FootprintExists( const wxString& aLibraryPath,
3539 const wxString& aFootprintName,
3540 const std::map<std::string, UTF8>* aProperties )
3541{
3542 // Note: checking the cache sounds like a good idea, but won't catch files which differ
3543 // only in case.
3544 //
3545 // Since this goes out to the native filesystem, we get platform differences (ie: MSW's
3546 // case-insensitive filesystem) handled "for free".
3547 // Warning: footprint names frequently contain a point. So be careful when initializing
3548 // wxFileName, and use a CTOR with extension specified
3549 wxFileName footprintFile( aLibraryPath, aFootprintName, FILEEXT::KiCadFootprintFileExtension );
3550
3551 return footprintFile.Exists();
3552}
3553
3554
3555FOOTPRINT* PCB_IO_KICAD_SEXPR::ImportFootprint( const wxString& aFootprintPath,
3556 wxString& aFootprintNameOut,
3557 const std::map<std::string, UTF8>* aProperties )
3558{
3559 wxString fcontents;
3560 wxFFile f( aFootprintPath );
3561
3562 // Suppress font substitution warnings (RAII - automatically restored on scope exit)
3563 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
3564
3565 if( !f.IsOpened() )
3566 return nullptr;
3567
3568 f.ReadAll( &fcontents );
3569
3570 aFootprintNameOut = wxFileName( aFootprintPath ).GetName();
3571
3572 return dynamic_cast<FOOTPRINT*>( Parse( fcontents ) );
3573}
3574
3575
3576FOOTPRINT* PCB_IO_KICAD_SEXPR::FootprintLoad( const wxString& aLibraryPath,
3577 const wxString& aFootprintName,
3578 bool aKeepUUID,
3579 const std::map<std::string, UTF8>* aProperties )
3580{
3581 // Suppress font substitution warnings (RAII - automatically restored on scope exit)
3582 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
3583
3584 const FOOTPRINT* footprint = getFootprint( aLibraryPath, aFootprintName, aProperties, true );
3585
3586 if( footprint )
3587 {
3588 FOOTPRINT* copy;
3589
3590 if( aKeepUUID )
3591 copy = static_cast<FOOTPRINT*>( footprint->Clone() );
3592 else
3593 copy = static_cast<FOOTPRINT*>( footprint->Duplicate( IGNORE_PARENT_GROUP ) );
3594
3595 copy->SetParent( nullptr );
3596 return copy;
3597 }
3598
3599 return nullptr;
3600}
3601
3602
3603void PCB_IO_KICAD_SEXPR::FootprintSave( const wxString& aLibraryPath, const FOOTPRINT* aFootprint,
3604 const std::map<std::string, UTF8>* aProperties )
3605{
3606 init( aProperties );
3607
3608 // In this public PLUGIN API function, we can safely assume it was
3609 // called for saving into a library path.
3611
3612 // Support saving to a single-file path like "/tmp/foo.kicad_mod" by treating the directory
3613 // as the library path and the file base-name as the footprint name.
3614 wxString libPath = aLibraryPath;
3615 wxString singleFileBaseName; // without extension
3616 bool saveSingleFile = false;
3617
3618 {
3619 wxFileName asFile( aLibraryPath );
3620
3621 if( asFile.GetExt() == FILEEXT::KiCadFootprintFileExtension )
3622 {
3623 saveSingleFile = true;
3624 libPath = asFile.GetPath();
3625 singleFileBaseName = asFile.GetName();
3626 }
3627 }
3628
3629 validateCache( libPath, !aProperties || !aProperties->contains( "skip_cache_validation" ) );
3630
3631 if( !m_cache->IsWritable() )
3632 {
3633 if( !m_cache->Exists() )
3634 {
3635 const wxString msg = wxString::Format( _( "Library '%s' does not exist.\n"
3636 "Would you like to create it?"),
3637 libPath );
3638
3639 if( !Pgm().IsGUI() || wxMessageBox( msg, _( "Library Not Found" ), wxYES_NO | wxICON_QUESTION ) != wxYES )
3640 return;
3641
3642 // Save throws its own IO_ERROR on failure, so no need to recreate here
3643 m_cache->Save( nullptr );
3644 }
3645 else
3646 {
3647 wxString msg = wxString::Format( _( "Library '%s' is read only." ), libPath );
3648 THROW_IO_ERROR( msg );
3649 }
3650 }
3651
3652 // The map key used by the cache and the on-disk filename base.
3653 wxString footprintName = saveSingleFile ? singleFileBaseName
3654 : aFootprint->GetFPID().GetUniStringLibItemName();
3655
3656 wxString fpName = saveSingleFile ? singleFileBaseName
3657 : aFootprint->GetFPID().GetUniStringLibItemName();
3658 ReplaceIllegalFileNameChars( fpName, '_' );
3659
3660 // Quietly overwrite footprint and delete footprint file from path for any by same name.
3661 wxFileName fn( libPath, fpName, FILEEXT::KiCadFootprintFileExtension );
3662
3663 // Write through symlinks, don't replace them
3665
3666 if( !fn.IsOk() )
3667 {
3668 THROW_IO_ERROR( wxString::Format( _( "Footprint file name '%s' is not valid." ), fn.GetFullPath() ) );
3669 }
3670
3671 if( fn.FileExists() && !fn.IsFileWritable() )
3672 {
3673 THROW_IO_ERROR( wxString::Format( _( "Insufficient permissions to delete '%s'." ),
3674 fn.GetFullPath() ) );
3675 }
3676
3677 wxString fullPath = fn.GetFullPath();
3678 wxString fullName = fn.GetFullName();
3679 auto it = m_cache->GetFootprints().find( footprintName );
3680
3681 if( it != m_cache->GetFootprints().end() )
3682 {
3683 // Save() below writes atomically via sibling temp + rename, so no pre-delete.
3684 wxLogTrace( traceKicadPcbPlugin, wxT( "Replacing footprint file '%s'." ), fullPath );
3685 m_cache->GetFootprints().erase( footprintName );
3686 }
3687
3688 // I need my own copy for the cache
3689 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aFootprint->Clone() );
3690
3691 // It's orientation should be zero and it should be on the front layer.
3692 footprint->SetOrientation( ANGLE_0 );
3693
3694 if( footprint->GetLayer() != F_Cu )
3695 {
3696 PCBNEW_SETTINGS* cfg = dynamic_cast<PCBNEW_SETTINGS*>( Kiface().KifaceSettings() );
3697
3698 if( cfg )
3699 footprint->Flip( footprint->GetPosition(), cfg->m_FlipDirection );
3700 else
3701 footprint->Flip( footprint->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
3702 }
3703
3704 // Detach it from the board and its group
3705 footprint->SetParent( nullptr );
3706 footprint->SetParentGroup( nullptr );
3707
3708 // Now that the clone is detached from its parent board, any m_netinfo pointers its
3709 // descendants still carry reference NETINFO_ITEMs owned by that board and may dangle.
3710 // Force them all to the board-independent ORPHANED singleton before serialization.
3711 footprint->ClearAllNets();
3712
3713 wxLogTrace( traceKicadPcbPlugin, wxT( "Creating s-expr footprint file '%s'." ), fullPath );
3714 m_cache->GetFootprints().insert( footprintName,
3715 new FP_CACHE_ENTRY( footprint,
3716 WX_FILENAME( fn.GetPath(), fullName ) ) );
3717 m_cache->Save( footprint );
3718}
3719
3720
3721void PCB_IO_KICAD_SEXPR::FootprintDelete( const wxString& aLibraryPath,
3722 const wxString& aFootprintName,
3723 const std::map<std::string, UTF8>* aProperties )
3724{
3725 init( aProperties );
3726
3727 validateCache( aLibraryPath );
3728
3729 if( !m_cache->IsWritable() )
3730 {
3731 THROW_IO_ERROR( wxString::Format( _( "Library '%s' is read only." ),
3732 aLibraryPath.GetData() ) );
3733 }
3734
3735 m_cache->Remove( aFootprintName );
3736}
3737
3738
3739void PCB_IO_KICAD_SEXPR::ClearCachedFootprints( const wxString& aLibraryPath )
3740{
3741 if( m_cache && m_cache->IsPath( aLibraryPath ) )
3742 {
3743 delete m_cache;
3744 m_cache = nullptr;
3745 }
3746}
3747
3748
3749long long PCB_IO_KICAD_SEXPR::GetLibraryTimestamp( const wxString& aLibraryPath ) const
3750{
3751 return FP_CACHE::GetTimestamp( aLibraryPath );
3752}
3753
3754
3755void PCB_IO_KICAD_SEXPR::CreateLibrary( const wxString& aLibraryPath,
3756 const std::map<std::string, UTF8>* aProperties )
3757{
3758 if( wxDir::Exists( aLibraryPath ) )
3759 {
3760 THROW_IO_ERROR( wxString::Format( _( "Cannot overwrite library path '%s'." ),
3761 aLibraryPath.GetData() ) );
3762 }
3763
3764 init( aProperties );
3765
3766 delete m_cache;
3767 m_cache = new FP_CACHE( this, aLibraryPath );
3768 m_cache->Save();
3769}
3770
3771
3772bool PCB_IO_KICAD_SEXPR::DeleteLibrary( const wxString& aLibraryPath,
3773 const std::map<std::string, UTF8>* aProperties )
3774{
3775 wxFileName fn;
3776 fn.SetPath( aLibraryPath );
3777
3778 // Return if there is no library path to delete.
3779 if( !fn.DirExists() )
3780 return false;
3781
3782 if( !fn.IsDirWritable() )
3783 {
3784 THROW_IO_ERROR( wxString::Format( _( "Insufficient permissions to delete folder '%s'." ),
3785 aLibraryPath.GetData() ) );
3786 }
3787
3788 wxDir dir( aLibraryPath );
3789
3790 if( dir.HasSubDirs() )
3791 {
3792 THROW_IO_ERROR( wxString::Format( _( "Library folder '%s' has unexpected sub-folders." ),
3793 aLibraryPath.GetData() ) );
3794 }
3795
3796 // All the footprint files must be deleted before the directory can be deleted.
3797 if( dir.HasFiles() )
3798 {
3799 unsigned i;
3800 wxFileName tmp;
3801 wxArrayString files;
3802
3803 CollectFilesLoopSafe( aLibraryPath, files );
3804
3805 for( i = 0; i < files.GetCount(); i++ )
3806 {
3807 tmp = files[i];
3808
3809 if( tmp.GetExt() != FILEEXT::KiCadFootprintFileExtension )
3810 {
3811 THROW_IO_ERROR( wxString::Format( _( "Unexpected file '%s' found in library "
3812 "path '%s'." ),
3813 files[i].GetData(),
3814 aLibraryPath.GetData() ) );
3815 }
3816 }
3817
3818 for( i = 0; i < files.GetCount(); i++ )
3819 wxRemoveFile( files[i] );
3820 }
3821
3822 wxLogTrace( traceKicadPcbPlugin, wxT( "Removing footprint library '%s'." ),
3823 aLibraryPath.GetData() );
3824
3825 // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
3826 // we don't want that. we want bare metal portability with no UI here.
3827 if( !wxRmdir( aLibraryPath ) )
3828 {
3829 THROW_IO_ERROR( wxString::Format( _( "Footprint library '%s' cannot be deleted." ),
3830 aLibraryPath.GetData() ) );
3831 }
3832
3833 // For some reason removing a directory in Windows is not immediately updated. This delay
3834 // prevents an error when attempting to immediately recreate the same directory when over
3835 // writing an existing library.
3836#ifdef __WINDOWS__
3837 wxMilliSleep( 250L );
3838#endif
3839
3840 if( m_cache && !m_cache->IsPath( aLibraryPath ) )
3841 {
3842 delete m_cache;
3843 m_cache = nullptr;
3844 }
3845
3846 return true;
3847}
3848
3849
3850bool PCB_IO_KICAD_SEXPR::IsLibraryWritable( const wxString& aLibraryPath )
3851{
3852 init( nullptr );
3853
3854 validateCache( aLibraryPath );
3855
3856 return m_cache->IsWritable();
3857}
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:242
@ LT_BACK
Definition board.h:243
@ ZLO_FORCE_FLASHED
Definition board_item.h:71
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:81
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition board_item.h:265
virtual bool IsKnockout() const
Definition board_item.h:352
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:372
const NETINFO_LIST & GetNetInfo() const
Definition board.h:1086
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition board.cpp:3413
const std::vector< wxString > & GetVariantNames() const
Definition board.h:464
const GENERATORS & Generators() const
Definition board.h:426
void SetFileName(const wxString &aFileName)
Definition board.h:407
const PCB_POINTS & Points() const
Definition board.h:434
const PAGE_INFO & GetPageSettings() const
Definition board.h:889
const ZONES & Zones() const
Definition board.h:424
const GROUPS & Groups() const
The groups must maintain the following invariants.
Definition board.h:453
LAYER_T GetLayerType(PCB_LAYER_ID aLayer) const
Return the type of the copper layer given by aLayer.
Definition board.cpp:849
TITLE_BLOCK & GetTitleBlock()
Definition board.h:895
int GetCopperLayerCount() const
Definition board.cpp:985
const std::map< wxString, wxString > & GetProperties() const
Definition board.h:457
const FOOTPRINTS & Footprints() const
Definition board.h:420
const TRACKS & Tracks() const
Definition board.h:418
wxString GetVariantDescription(const wxString &aVariantName) const
Definition board.cpp:2948
const PCB_PLOT_PARAMS & GetPlotOptions() const
Definition board.h:892
bool LegacyTeardrops() const
Definition board.h:1495
wxString GroupsSanityCheck(bool repair=false)
Consistency check of internal m_groups structure.
Definition board.cpp:3637
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1149
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:1034
const DRAWINGS & Drawings() const
Definition board.h:422
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, EMBEDDED_FILE * > & EmbeddedFileMap() const
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:208
RAII class to set and restore the fontconfig reporter.
Definition reporter.h:332
bool GetDuplicatePadNumbersAreJumpers() const
Definition footprint.h:1149
const CASE_INSENSITIVE_MAP< FOOTPRINT_VARIANT > & GetVariants() const
Get all variants.
Definition footprint.h:1033
bool AllowSolderMaskBridges() const
Definition footprint.h:513
void SetFPID(const LIB_ID &aFPID)
Definition footprint.h:442
wxString GetLibDescription() const
Definition footprint.h:458
ZONE_CONNECTION GetLocalZoneConnection() const
Definition footprint.h:489
bool IsDNP() const
Definition footprint.h:969
EDA_ANGLE GetOrientation() const
Definition footprint.h:406
ZONES & Zones()
Definition footprint.h:381
PCB_POINTS & Points()
Definition footprint.h:387
bool IsExcludedFromBOM() const
Definition footprint.h:960
void SetOrientation(const EDA_ANGLE &aNewAngle)
const TRANSFORM_TRS & GetTransform() const
Definition footprint.h:419
wxString GetSheetname() const
Definition footprint.h:467
const std::vector< FP_UNIT_INFO > & GetUnitInfo() const
Definition footprint.h:940
const EXTRUDED_3D_BODY * GetExtrudedBody() const
Definition footprint.h:396
std::optional< int > GetLocalSolderPasteMargin() const
Definition footprint.h:482
EDA_ITEM * Clone() const override
Invoke a function on all children.
PCB_FIELD & Value()
read/write accessors:
Definition footprint.h:877
std::optional< int > GetLocalClearance() const
Definition footprint.h:476
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:1156
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:507
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition footprint.h:417
LSET GetPrivateLayers() const
Definition footprint.h:315
bool AllowMissingCourtyard() const
Definition footprint.h:510
wxString GetSheetfile() const
Definition footprint.h:470
const std::vector< wxString > & GetNetTiePadGroups() const
Definition footprint.h:562
const LIB_ID & GetFPID() const
Definition footprint.h:441
bool IsLocked() const override
Definition footprint.h:634
bool IsExcludedFromPosFiles() const
Definition footprint.h:951
const LSET & GetStackupLayers() const
Definition footprint.h:505
PCB_FIELD & Reference()
Definition footprint.h:878
void ClearAllNets()
Clear (i.e.
bool IsNetTie() const
Definition footprint.h:520
std::optional< double > GetLocalSolderPasteMarginRatio() const
Definition footprint.h:485
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:473
const wxArrayString * GetInitialComments() const
Return the initial comments block or NULL if none, without transfer of ownership.
Definition footprint.h:1277
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:392
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:464
std::optional< int > GetLocalSolderMaskMargin() const
Definition footprint.h:479
wxString GetKeywords() const
Definition footprint.h:461
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition footprint.h:1305
FOOTPRINT_STACKUP GetStackupMode() const
Definition footprint.h:498
bool IsPlaced() const
Definition footprint.h:657
VECTOR2I GetPosition() const override
Definition footprint.h:403
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:240
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:393
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:115
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:247
REPORTER & Report(const wxString &aMsg, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
Definition reporter.cpp:232
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)
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:908
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:878
@ 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).
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:49
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:543
VECTOR2I GetTextPos() const override
Definition pcb_text.cpp:444
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:696
A progress reporter interface for use in multi-threaded environments.
Container for project specific data.
Definition project.h:62
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:811
std::optional< int > GetLocalClearance() const override
Definition zone.cpp:973
const THIEVING_SETTINGS & GetThievingSettings() const
Definition zone.h:351
bool GetDoNotAllowVias() const
Definition zone.h:822
ZONE_LAYER_PROPERTIES & LayerProperties(PCB_LAYER_ID aLayer)
Definition zone.h:146
wxString GetPlacementAreaSource() const
Definition zone.h:816
std::shared_ptr< SHAPE_POLY_SET > GetFilledPolysList(PCB_LAYER_ID aLayer) const
Definition zone.h:697
bool GetDoNotAllowPads() const
Definition zone.h:824
PLACEMENT_SOURCE_T GetPlacementAreaSourceType() const
Definition zone.h:818
bool GetDoNotAllowTracks() const
Definition zone.h:823
bool IsFilled() const
Definition zone.h:306
ISLAND_REMOVAL_MODE GetIslandRemovalMode() const
Definition zone.h:833
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:1588
long long int GetMinIslandArea() const
Definition zone.h:836
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:813
bool IsTeardropArea() const
Definition zone.h:786
int GetThermalReliefSpokeWidth() const
Definition zone.h:259
int GetBorderHatchPitch() const
HatchBorder related methods.
Definition zone.h:846
ZONE_BORDER_DISPLAY_STYLE GetHatchStyle() const
Definition zone.h:685
EDA_ANGLE GetHatchOrientation() const
Definition zone.h:331
bool GetDoNotAllowFootprints() const
Definition zone.h:825
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:797
double GetHatchSmoothingValue() const
Definition zone.h:337
bool GetDoNotAllowZoneFills() const
Definition zone.h:821
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:574
PCB_LAYER_ID GetFirstLayer() const
Definition zone.cpp:554
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:817
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_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:675
bool IsExternalCopperLayer(int aLayerId)
Test whether a layer is an external (F_Cu or B_Cu) copper layer.
Definition layer_ids.h:686
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.
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.
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
nlohmann::json output
VECTOR2I center
const SHAPE_LINE_CHAIN chain
int delta
wxLogTrace helper definitions.
#define kv
@ PCB_T
Definition typeinfo.h:75
@ 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