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 //
2674 // BOARD::IsItemIndexedById() probes the board's inverse pointer index without dereferencing
2675 // the candidate, so it is safe to call on a possibly-dangling member.
2676 bool validateAgainstBoard = m_board && m_board->IsItemIndexedById( aGroup );
2677
2678 memberIds.Alloc( aGroup->GetItems().size() );
2679
2680 for( EDA_ITEM* member : aGroup->GetItems() )
2681 {
2682 if( !validateAgainstBoard
2683 || m_board->IsItemIndexedById( static_cast<const BOARD_ITEM*>( member ) ) )
2684 {
2685 memberIds.Add( member->m_Uuid.AsString() );
2686 }
2687 }
2688
2689 if( memberIds.empty() )
2690 return;
2691
2692 m_out->Print( "(group %s", m_out->Quotew( aGroup->GetName() ).c_str() );
2693
2695
2696 if( aGroup->IsLocked() )
2697 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2698
2699 if( aGroup->HasDesignBlockLink() )
2700 m_out->Print( "(lib_id \"%s\")", aGroup->GetDesignBlockLibId().Format().c_str() );
2701
2702 memberIds.Sort();
2703
2704 m_out->Print( "(members" );
2705
2706 for( const wxString& memberId : memberIds )
2707 m_out->Print( " %s", m_out->Quotew( memberId ).c_str() );
2708
2709 m_out->Print( ")" ); // Close `members` token.
2710 m_out->Print( ")" ); // Close `group` token.
2711}
2712
2713
2714void PCB_IO_KICAD_SEXPR::format( const PCB_GENERATOR* aGenerator ) const
2715{
2716 // Some conditions appear to still be creating ghost tuning patterns. Don't save them.
2717 if( aGenerator->GetGeneratorType() == wxT( "tuning_pattern" )
2718 && aGenerator->GetItems().empty() )
2719 {
2720 return;
2721 }
2722
2723 m_out->Print( "(generated" );
2724
2725 KICAD_FORMAT::FormatUuid( m_out, aGenerator->m_Uuid );
2726
2727 m_out->Print( "(type %s) (name %s) (layer %s)",
2728 TO_UTF8( aGenerator->GetGeneratorType() ),
2729 m_out->Quotew( aGenerator->GetName() ).c_str(),
2730 m_out->Quotew( LSET::Name( aGenerator->GetLayer() ) ).c_str() );
2731
2732 if( aGenerator->IsLocked() )
2733 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2734
2735 for( const auto& [key, value] : aGenerator->GetProperties() )
2736 {
2737 if( value.CheckType<double>() || value.CheckType<int>() || value.CheckType<long>()
2738 || value.CheckType<long long>() )
2739 {
2740 double val;
2741
2742 if( !value.GetAs( &val ) )
2743 continue;
2744
2745 std::string buf = fmt::format( "{:.10g}", val );
2746
2747 // Don't quote numbers
2748 m_out->Print( "(%s %s)", key.c_str(), buf.c_str() );
2749 }
2750 else if( value.CheckType<bool>() )
2751 {
2752 bool val;
2753 value.GetAs( &val );
2754
2755 KICAD_FORMAT::FormatBool( m_out, key, val );
2756 }
2757 else if( value.CheckType<VECTOR2I>() )
2758 {
2759 VECTOR2I val;
2760 value.GetAs( &val );
2761
2762 m_out->Print( "(%s (xy %s))",
2763 key.c_str(),
2764 formatInternalUnits( val ).c_str() );
2765 }
2766 else if( value.CheckType<SHAPE_LINE_CHAIN>() )
2767 {
2768 SHAPE_LINE_CHAIN val;
2769 value.GetAs( &val );
2770
2771 m_out->Print( "(%s ", key.c_str() );
2772 formatPolyPts( val );
2773 m_out->Print( ")" );
2774 }
2775 else
2776 {
2777 wxString val;
2778
2779 if( value.CheckType<wxString>() )
2780 {
2781 value.GetAs( &val );
2782 }
2783 else if( value.CheckType<std::string>() )
2784 {
2785 std::string str;
2786 value.GetAs( &str );
2787
2788 val = wxString::FromUTF8( str );
2789 }
2790
2791 m_out->Print( "(%s %s)", key.c_str(), m_out->Quotew( val ).c_str() );
2792 }
2793 }
2794
2795 wxArrayString memberIds;
2796
2797 for( EDA_ITEM* member : aGenerator->GetItems() )
2798 memberIds.Add( member->m_Uuid.AsString() );
2799
2800 memberIds.Sort();
2801
2802 m_out->Print( "(members" );
2803
2804 for( const wxString& memberId : memberIds )
2805 m_out->Print( " %s", m_out->Quotew( memberId ).c_str() );
2806
2807 m_out->Print( ")" ); // Close `members` token.
2808 m_out->Print( ")" ); // Close `generated` token.
2809}
2810
2811
2812void PCB_IO_KICAD_SEXPR::format( const PCB_TRACK* aTrack ) const
2813{
2814 if( aTrack->Type() == PCB_VIA_T )
2815 {
2816 PCB_LAYER_ID layer1, layer2;
2817
2818 const PCB_VIA* via = static_cast<const PCB_VIA*>( aTrack );
2819 const BOARD* board = via->GetBoard();
2820
2821 wxCHECK_RET( board != nullptr, wxT( "Via has no parent." ) );
2822
2823 m_out->Print( "(via" );
2824
2825 via->LayerPair( &layer1, &layer2 );
2826
2827 switch( via->GetViaType() )
2828 {
2829 case VIATYPE::THROUGH: // Default shape not saved.
2830 break;
2831
2832 case VIATYPE::BLIND:
2833 m_out->Print( " blind " );
2834 break;
2835
2836 case VIATYPE::BURIED:
2837 m_out->Print( " buried " );
2838 break;
2839
2840 case VIATYPE::MICROVIA:
2841 m_out->Print( " micro " );
2842 break;
2843
2844 default:
2845 THROW_IO_ERROR( wxString::Format( _( "unknown via type %d" ), via->GetViaType() ) );
2846 }
2847
2848 m_out->Print( "(at %s) (size %s)",
2849 formatInternalUnits( aTrack->GetStart() ).c_str(),
2850 formatInternalUnits( via->GetWidth( F_Cu ) ).c_str() );
2851
2852 // Old boards were using UNDEFINED_DRILL_DIAMETER value in file for via drill when
2853 // via drill was the netclass value.
2854 // recent boards always set the via drill to the actual value, but now we need to
2855 // always store the drill value, because netclass value is not stored in the board file.
2856 // Otherwise the drill value of some (old) vias can be unknown
2857 if( via->GetDrill() != UNDEFINED_DRILL_DIAMETER )
2858 m_out->Print( "(drill %s)", formatInternalUnits( via->GetDrill() ).c_str() );
2859 else
2860 m_out->Print( "(drill %s)", formatInternalUnits( via->GetDrillValue() ).c_str() );
2861
2862 if( via->Padstack().SecondaryDrill().size.x > 0 )
2863 {
2864 m_out->Print( "(backdrill (size %s) (layers %s %s))",
2865 formatInternalUnits( via->Padstack().SecondaryDrill().size.x ).c_str(),
2866 m_out->Quotew( LSET::Name( via->Padstack().SecondaryDrill().start ) ).c_str(),
2867 m_out->Quotew( LSET::Name( via->Padstack().SecondaryDrill().end ) ).c_str() );
2868 }
2869
2870 if( via->Padstack().TertiaryDrill().size.x > 0 )
2871 {
2872 m_out->Print( "(tertiary_drill (size %s) (layers %s %s))",
2873 formatInternalUnits( via->Padstack().TertiaryDrill().size.x ).c_str(),
2874 m_out->Quotew( LSET::Name( via->Padstack().TertiaryDrill().start ) ).c_str(),
2875 m_out->Quotew( LSET::Name( via->Padstack().TertiaryDrill().end ) ).c_str() );
2876 }
2877
2878 auto formatPostMachining = [&]( const char* aName, const PADSTACK::POST_MACHINING_PROPS& aProps )
2879 {
2880 if( !aProps.mode.has_value() || aProps.mode == PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED )
2881 return;
2882
2883 m_out->Print( "(%s %s", aName,
2884 aProps.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE ? "counterbore" : "countersink" );
2885
2886 if( aProps.size > 0 )
2887 m_out->Print( " (size %s)", formatInternalUnits( aProps.size ).c_str() );
2888
2889 if( aProps.depth > 0 )
2890 m_out->Print( " (depth %s)", formatInternalUnits( aProps.depth ).c_str() );
2891
2892 if( aProps.angle > 0 )
2893 m_out->Print( " (angle %s)", FormatDouble2Str( aProps.angle / 10.0 ).c_str() );
2894
2895 m_out->Print( ")" );
2896 };
2897
2898 formatPostMachining( "front_post_machining", via->Padstack().FrontPostMachining() );
2899 formatPostMachining( "back_post_machining", via->Padstack().BackPostMachining() );
2900
2901 m_out->Print( "(layers %s %s)",
2902 m_out->Quotew( LSET::Name( layer1 ) ).c_str(),
2903 m_out->Quotew( LSET::Name( layer2 ) ).c_str() );
2904
2905 switch( via->Padstack().UnconnectedLayerMode() )
2906 {
2908 KICAD_FORMAT::FormatBool( m_out, "remove_unused_layers", true );
2909 KICAD_FORMAT::FormatBool( m_out, "keep_end_layers", false );
2910 break;
2911
2913 KICAD_FORMAT::FormatBool( m_out, "remove_unused_layers", true );
2914 KICAD_FORMAT::FormatBool( m_out, "keep_end_layers", true );
2915 break;
2916
2918 KICAD_FORMAT::FormatBool( m_out, "start_end_only", true );
2919 break;
2920
2922 break;
2923 }
2924
2925 if( via->IsLocked() )
2926 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2927
2928 if( via->GetIsFree() )
2929 KICAD_FORMAT::FormatBool( m_out, "free", true );
2930
2931 if( via->GetRemoveUnconnected() )
2932 {
2933 m_out->Print( "(zone_layer_connections" );
2934
2935 for( PCB_LAYER_ID layer : board->GetEnabledLayers().CuStack() )
2936 {
2937 if( via->GetZoneLayerOverride( layer ) == ZLO_FORCE_FLASHED )
2938 m_out->Print( " %s", m_out->Quotew( LSET::Name( layer ) ).c_str() );
2939 }
2940
2941 m_out->Print( ")" );
2942 }
2943
2944 const PADSTACK& padstack = via->Padstack();
2945
2946 if( padstack.FrontOuterLayers().has_solder_mask.has_value()
2947 || padstack.BackOuterLayers().has_solder_mask.has_value() )
2948 {
2949 m_out->Print( 0, " (tenting " );
2951 padstack.FrontOuterLayers().has_solder_mask );
2953 padstack.BackOuterLayers().has_solder_mask );
2954 m_out->Print( 0, ")" );
2955 }
2956
2957 if( padstack.Drill().is_capped.has_value() )
2958 KICAD_FORMAT::FormatOptBool( m_out, "capping", padstack.Drill().is_capped );
2959
2960 if( padstack.FrontOuterLayers().has_covering.has_value()
2961 || padstack.BackOuterLayers().has_covering.has_value() )
2962 {
2963 m_out->Print( 0, " (covering " );
2965 padstack.FrontOuterLayers().has_covering );
2967 padstack.BackOuterLayers().has_covering );
2968 m_out->Print( 0, ")" );
2969 }
2970
2971 if( padstack.FrontOuterLayers().has_plugging.has_value()
2972 || padstack.BackOuterLayers().has_plugging.has_value() )
2973 {
2974 m_out->Print( 0, " (plugging " );
2976 padstack.FrontOuterLayers().has_plugging );
2978 padstack.BackOuterLayers().has_plugging );
2979 m_out->Print( 0, ")" );
2980 }
2981
2982 if( padstack.Drill().is_filled.has_value() )
2983 KICAD_FORMAT::FormatOptBool( m_out, "filling", padstack.Drill().is_filled );
2984
2985 if( padstack.Mode() != PADSTACK::MODE::NORMAL )
2986 {
2987 m_out->Print( "(padstack" );
2988
2989 if( padstack.Mode() == PADSTACK::MODE::FRONT_INNER_BACK )
2990 {
2991 m_out->Print( "(mode front_inner_back)" );
2992
2993 m_out->Print( "(layer \"Inner\"" );
2994 m_out->Print( "(size %s)",
2995 formatInternalUnits( padstack.Size( PADSTACK::INNER_LAYERS ).x ).c_str() );
2996 m_out->Print( ")" );
2997 m_out->Print( "(layer \"B.Cu\"" );
2998 m_out->Print( "(size %s)",
2999 formatInternalUnits( padstack.Size( B_Cu ).x ).c_str() );
3000 m_out->Print( ")" );
3001 }
3002 else
3003 {
3004 m_out->Print( "(mode custom)" );
3005
3006 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, board->GetCopperLayerCount() ) )
3007 {
3008 if( layer == F_Cu )
3009 continue;
3010
3011 m_out->Print( "(layer %s", m_out->Quotew( LSET::Name( layer ) ).c_str() );
3012 m_out->Print( "(size %s)",
3013 formatInternalUnits( padstack.Size( layer ).x ).c_str() );
3014 m_out->Print( ")" );
3015 }
3016 }
3017
3018 m_out->Print( ")" );
3019 }
3020
3021 if( !isDefaultTeardropParameters( via->GetTeardropParams() ) )
3022 formatTeardropParameters( via->GetTeardropParams() );
3023 }
3024 else
3025 {
3026 if( aTrack->Type() == PCB_ARC_T )
3027 {
3028 const PCB_ARC* arc = static_cast<const PCB_ARC*>( aTrack );
3029
3030 m_out->Print( "(arc (start %s) (mid %s) (end %s) (width %s)",
3031 formatInternalUnits( arc->GetStart() ).c_str(),
3032 formatInternalUnits( arc->GetMid() ).c_str(),
3033 formatInternalUnits( arc->GetEnd() ).c_str(),
3034 formatInternalUnits( arc->GetWidth() ).c_str() );
3035 }
3036 else
3037 {
3038 m_out->Print( "(segment (start %s) (end %s) (width %s)",
3039 formatInternalUnits( aTrack->GetStart() ).c_str(),
3040 formatInternalUnits( aTrack->GetEnd() ).c_str(),
3041 formatInternalUnits( aTrack->GetWidth() ).c_str() );
3042 }
3043
3044 if( aTrack->IsLocked() )
3045 KICAD_FORMAT::FormatBool( m_out, "locked", true );
3046
3047 if( aTrack->GetLayerSet().count() > 1 )
3048 formatLayers( aTrack->GetLayerSet(), false /* enumerate layers */ );
3049 else
3050 formatLayer( aTrack->GetLayer() );
3051
3052 if( aTrack->HasSolderMask()
3053 && aTrack->GetLocalSolderMaskMargin().has_value()
3054 && IsExternalCopperLayer( aTrack->GetLayer() ) )
3055 {
3056 m_out->Print( "(solder_mask_margin %s)",
3057 formatInternalUnits( aTrack->GetLocalSolderMaskMargin().value() ).c_str() );
3058 }
3059 }
3060
3061 if( !( m_ctl & CTL_OMIT_PAD_NETS ) )
3062 m_out->Print( "(net %s)", m_out->Quotew( aTrack->GetNetname() ).c_str() );
3063
3065 m_out->Print( ")" );
3066}
3067
3068
3069void PCB_IO_KICAD_SEXPR::format( const ZONE* aZone ) const
3070{
3071 m_out->Print( "(zone" );
3072
3073 if( !( m_ctl & CTL_OMIT_PAD_NETS ) && aZone->IsOnCopperLayer() && !aZone->GetIsRuleArea()
3074 && aZone->GetNetCode() > 0 )
3075 {
3076 m_out->Print( "(net %s)", m_out->Quotew( aZone->GetNetname() ).c_str() );
3077 }
3078
3079 if( aZone->IsLocked() )
3080 KICAD_FORMAT::FormatBool( m_out, "locked", true );
3081
3082 // If a zone exists on multiple layers, format accordingly
3083 LSET layers = aZone->GetLayerSet();
3084
3085 if( aZone->GetBoard() )
3086 layers &= aZone->GetBoard()->GetEnabledLayers();
3087
3088 // Always enumerate every layer for a zone on a copper layer
3089 if( layers.count() > 1 )
3090 formatLayers( layers, aZone->IsOnCopperLayer(), true );
3091 else
3092 formatLayer( aZone->GetFirstLayer() );
3093
3094 if( !aZone->IsTeardropArea() )
3096
3097 if( !aZone->GetZoneName().empty() && !aZone->IsTeardropArea() )
3098 m_out->Print( "(name %s)", m_out->Quotew( aZone->GetZoneName() ).c_str() );
3099
3100 // Save the outline aux info
3101 std::string hatch;
3102
3103 switch( aZone->GetHatchStyle() )
3104 {
3105 default:
3106 case ZONE_BORDER_DISPLAY_STYLE::NO_HATCH: hatch = "none"; break;
3107 case ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE: hatch = "edge"; break;
3108 case ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL: hatch = "full"; break;
3109 }
3110
3111 m_out->Print( "(hatch %s %s)", hatch.c_str(),
3112 formatInternalUnits( aZone->GetBorderHatchPitch() ).c_str() );
3113
3114
3115
3116 if( aZone->GetAssignedPriority() > 0 )
3117 m_out->Print( "(priority %d)", aZone->GetAssignedPriority() );
3118
3119 // Add teardrop keywords in file: (attr (teardrop (type xxx))) where xxx is the teardrop type
3120 if( aZone->IsTeardropArea() )
3121 {
3122 m_out->Print( "(attr (teardrop (type %s)))",
3123 aZone->GetTeardropAreaType() == TEARDROP_TYPE::TD_VIAPAD ? "padvia"
3124 : "track_end" );
3125 }
3126
3127 m_out->Print( "(connect_pads" );
3128
3129 switch( aZone->GetPadConnection() )
3130 {
3131 default:
3132 case ZONE_CONNECTION::THERMAL: // Default option not saved or loaded.
3133 break;
3134
3136 m_out->Print( " thru_hole_only" );
3137 break;
3138
3140 m_out->Print( " yes" );
3141 break;
3142
3144 m_out->Print( " no" );
3145 break;
3146 }
3147
3148 m_out->Print( "(clearance %s)",
3149 formatInternalUnits( aZone->GetLocalClearance().value() ).c_str() );
3150
3151 m_out->Print( ")" );
3152
3153 m_out->Print( "(min_thickness %s)",
3154 formatInternalUnits( aZone->GetMinThickness() ).c_str() );
3155
3156 if( aZone->GetIsRuleArea() )
3157 {
3158 // Keepout settings
3159 m_out->Print( "(keepout (tracks %s) (vias %s) (pads %s) (copperpour %s) (footprints %s))",
3160 aZone->GetDoNotAllowTracks() ? "not_allowed" : "allowed",
3161 aZone->GetDoNotAllowVias() ? "not_allowed" : "allowed",
3162 aZone->GetDoNotAllowPads() ? "not_allowed" : "allowed",
3163 aZone->GetDoNotAllowZoneFills() ? "not_allowed" : "allowed",
3164 aZone->GetDoNotAllowFootprints() ? "not_allowed" : "allowed" );
3165
3166 // Multichannel settings
3167 m_out->Print( "(placement" );
3169
3170 switch( aZone->GetPlacementAreaSourceType() )
3171 {
3173 m_out->Print( "(sheetname %s)", m_out->Quotew( aZone->GetPlacementAreaSource() ).c_str() );
3174 break;
3176 m_out->Print( "(component_class %s)", m_out->Quotew( aZone->GetPlacementAreaSource() ).c_str() );
3177 break;
3179 m_out->Print( "(group %s)", m_out->Quotew( aZone->GetPlacementAreaSource() ).c_str() );
3180 break;
3181 // These are transitory and should not be saved
3183 break;
3184 }
3185
3186 m_out->Print( ")" );
3187 }
3188
3189 m_out->Print( "(fill" );
3190
3191 // Default is not filled.
3192 if( aZone->IsFilled() )
3193 m_out->Print( " yes" );
3194
3195 // Default is polygon filled.
3197 m_out->Print( "(mode hatch)" );
3198 else if( aZone->GetFillMode() == ZONE_FILL_MODE::COPPER_THIEVING )
3199 m_out->Print( "(mode thieving)" );
3200
3201 if( !aZone->IsTeardropArea() )
3202 {
3203 m_out->Print( "(thermal_gap %s) (thermal_bridge_width %s)",
3204 formatInternalUnits( aZone->GetThermalReliefGap() ).c_str(),
3205 formatInternalUnits( aZone->GetThermalReliefSpokeWidth() ).c_str() );
3206 }
3207
3209 {
3210 switch( aZone->GetCornerSmoothingType() )
3211 {
3213 m_out->Print( "(smoothing chamfer)" );
3214 break;
3215
3217 m_out->Print( "(smoothing fillet)" );
3218 break;
3219
3220 default:
3221 THROW_IO_ERROR( wxString::Format( _( "unknown zone corner smoothing type %d" ),
3222 aZone->GetCornerSmoothingType() ) );
3223 }
3224
3225 if( aZone->GetCornerRadius() != 0 )
3226 m_out->Print( "(radius %s)", formatInternalUnits( aZone->GetCornerRadius() ).c_str() );
3227 }
3228
3229 m_out->Print( "(island_removal_mode %d)",
3230 static_cast<int>( aZone->GetIslandRemovalMode() ) );
3231
3233 {
3234 m_out->Print( "(island_area_min %s)",
3235 formatInternalUnits( aZone->GetMinIslandArea() / pcbIUScale.IU_PER_MM ).c_str() );
3236 }
3237
3239 {
3240 m_out->Print( "(hatch_thickness %s) (hatch_gap %s) (hatch_orientation %s)",
3241 formatInternalUnits( aZone->GetHatchThickness() ).c_str(),
3242 formatInternalUnits( aZone->GetHatchGap() ).c_str(),
3243 FormatDouble2Str( aZone->GetHatchOrientation().AsDegrees() ).c_str() );
3244
3245 if( aZone->GetHatchSmoothingLevel() > 0 )
3246 {
3247 m_out->Print( "(hatch_smoothing_level %d) (hatch_smoothing_value %s)",
3248 aZone->GetHatchSmoothingLevel(),
3249 FormatDouble2Str( aZone->GetHatchSmoothingValue() ).c_str() );
3250 }
3251
3252 m_out->Print( "(hatch_border_algorithm %s) (hatch_min_hole_area %s)",
3253 aZone->GetHatchBorderAlgorithm() ? "hatch_thickness" : "min_thickness",
3254 FormatDouble2Str( aZone->GetHatchHoleMinArea() ).c_str() );
3255 }
3256 else if( aZone->GetFillMode() == ZONE_FILL_MODE::COPPER_THIEVING )
3257 {
3258 const THIEVING_SETTINGS& thieving = aZone->GetThievingSettings();
3259 const char* patternStr = "dots";
3260
3261 switch( thieving.pattern )
3262 {
3263 case THIEVING_PATTERN::SQUARES: patternStr = "squares"; break;
3264 case THIEVING_PATTERN::HATCH: patternStr = "hatch"; break;
3266 default: patternStr = "dots"; break;
3267 }
3268
3269 m_out->Print( "(thieving (type %s) (size %s) (gap %s) (width %s) "
3270 "(stagger %s) (orientation %s))",
3271 patternStr,
3272 formatInternalUnits( thieving.element_size ).c_str(),
3273 formatInternalUnits( thieving.gap ).c_str(),
3274 formatInternalUnits( thieving.line_width ).c_str(),
3275 thieving.stagger ? "yes" : "no",
3276 FormatDouble2Str( thieving.orientation.AsDegrees() ).c_str() );
3277 }
3278
3279 m_out->Print( ")" );
3280
3281 for( const auto& [layer, properties] : aZone->LayerProperties() )
3282 {
3283 format( properties, 0, layer );
3284 }
3285
3286 if( aZone->GetNumCorners() )
3287 {
3288 SHAPE_POLY_SET::POLYGON poly = aZone->Outline()->Polygon(0);
3289
3290 for( const SHAPE_LINE_CHAIN& chain : poly )
3291 {
3292 if( chain.PointCount() == 0 )
3293 continue;
3294
3295 m_out->Print( "(polygon" );
3297 m_out->Print( ")" );
3298 }
3299 }
3300
3301 // Save the PolysList (filled areas)
3302 for( PCB_LAYER_ID layer : aZone->GetLayerSet().Seq() )
3303 {
3304 const std::shared_ptr<SHAPE_POLY_SET>& fv = aZone->GetFilledPolysList( layer );
3305
3306 for( int ii = 0; ii < fv->OutlineCount(); ++ii )
3307 {
3308 m_out->Print( "(filled_polygon" );
3309 m_out->Print( "(layer %s)", m_out->Quotew( LSET::Name( layer ) ).c_str() );
3310
3311 if( aZone->IsIsland( layer, ii ) )
3312 KICAD_FORMAT::FormatBool( m_out, "island", true );
3313
3314 const SHAPE_LINE_CHAIN& chain = fv->COutline( ii );
3315
3317 m_out->Print( ")" );
3318 }
3319 }
3320
3321 m_out->Print( ")" );
3322}
3323
3324
3325void PCB_IO_KICAD_SEXPR::format( const ZONE_LAYER_PROPERTIES& aZoneLayerProperties, int aNestLevel,
3326 PCB_LAYER_ID aLayer ) const
3327{
3328 // Do not store the layer properties if no value is actually set.
3329 if( !aZoneLayerProperties.hatching_offset.has_value() )
3330 return;
3331
3332 m_out->Print( aNestLevel, "(property\n" );
3333 m_out->Print( aNestLevel, "(layer %s)\n", m_out->Quotew( LSET::Name( aLayer ) ).c_str() );
3334
3335 if( aZoneLayerProperties.hatching_offset.has_value() )
3336 {
3337 m_out->Print( aNestLevel, "(hatch_position (xy %s))",
3338 formatInternalUnits( aZoneLayerProperties.hatching_offset.value() ).c_str() );
3339 }
3340
3341 m_out->Print( aNestLevel, ")\n" );
3342}
3343
3344
3345PCB_IO_KICAD_SEXPR::PCB_IO_KICAD_SEXPR( int aControlFlags ) : PCB_IO( wxS( "KiCad" ) ),
3346 m_cache( nullptr ),
3347 m_ctl( aControlFlags )
3348{
3349 init( nullptr );
3350 m_out = &m_sf;
3351}
3352
3353
3358
3359
3360BOARD* PCB_IO_KICAD_SEXPR::LoadBoard( const wxString& aFileName, BOARD* aAppendToMe,
3361 const std::map<std::string, UTF8>* aProperties,
3362 PROJECT* aProject )
3363{
3364 FILE_LINE_READER reader( aFileName );
3365
3366 unsigned lineCount = 0;
3367
3368 // Collect the font substitution warnings (RAII - automatically reset on scope exit)
3370
3371 if( m_progressReporter )
3372 {
3373 m_progressReporter->Report( wxString::Format( _( "Loading %s..." ), aFileName ) );
3374
3375 if( !m_progressReporter->KeepRefreshing() )
3376 THROW_IO_ERROR( _( "Open canceled by user." ) );
3377
3378 while( reader.ReadLine() )
3379 lineCount++;
3380
3381 reader.Rewind();
3382 }
3383
3384 BOARD* board = DoLoad( reader, aAppendToMe, aProperties, m_progressReporter, lineCount );
3385
3386 // Give the filename to the board if it's new
3387 if( !aAppendToMe )
3388 board->SetFileName( aFileName );
3389
3390 return board;
3391}
3392
3393
3395 const std::map<std::string, UTF8>* aProperties,
3396 PROGRESS_REPORTER* aProgressReporter, unsigned aLineCount)
3397{
3398 init( aProperties );
3399
3400 bool preserveDestinationStackup =
3401 aProperties && aProperties->contains( PCB_IO_LOAD_PROPERTIES::APPEND_PRESERVE_DESTINATION_STACKUP );
3402
3403 PCB_IO_KICAD_SEXPR_PARSER parser( &aReader, aAppendToMe, m_queryUserCallback, aProgressReporter, aLineCount,
3404 preserveDestinationStackup );
3405
3407
3408 BOARD* board;
3409
3410 try
3411 {
3412 board = dynamic_cast<BOARD*>( parser.Parse() );
3413 }
3414 catch( const FUTURE_FORMAT_ERROR& )
3415 {
3416 // Don't wrap a FUTURE_FORMAT_ERROR in another
3417 throw;
3418 }
3419 catch( const PARSE_ERROR& parse_error )
3420 {
3421 if( parser.IsTooRecent() )
3422 throw FUTURE_FORMAT_ERROR( parse_error, parser.GetRequiredVersion() );
3423 else
3424 throw;
3425 }
3426
3427 if( !board )
3428 {
3429 // The parser loaded something that was valid, but wasn't a board.
3430 THROW_PARSE_ERROR( _( "This file does not contain a PCB." ), parser.CurSource(),
3431 parser.CurLine(), parser.CurLineNumber(), parser.CurOffset() );
3432 }
3433
3434 // Report any non-fatal parse warnings to the load info reporter
3435 for( const wxString& warning : parser.GetParseWarnings() )
3437
3438 return board;
3439}
3440
3441
3442void PCB_IO_KICAD_SEXPR::init( const std::map<std::string, UTF8>* aProperties )
3443{
3444 m_board = nullptr;
3445 m_reader = nullptr;
3446 m_props = aProperties;
3447}
3448
3449
3450void PCB_IO_KICAD_SEXPR::validateCache( const wxString& aLibraryPath, bool checkModified )
3451{
3452 // Suppress font substitution warnings (RAII - automatically restored on scope exit)
3453 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
3454
3455 if( !m_cache || !m_cache->IsPath( aLibraryPath ) || ( checkModified && m_cache->IsModified() ) )
3456 {
3457 // a spectacular episode in memory management:
3458 delete m_cache;
3459 m_cache = new FP_CACHE( this, aLibraryPath );
3460 m_cache->Load();
3461 }
3462}
3463
3464
3465void PCB_IO_KICAD_SEXPR::FootprintEnumerate( wxArrayString& aFootprintNames,
3466 const wxString& aLibPath, bool aBestEfforts,
3467 const std::map<std::string, UTF8>* aProperties )
3468{
3469 wxDir dir( aLibPath );
3470 wxString errorMsg;
3471
3472 init( aProperties );
3473
3474 try
3475 {
3476 validateCache( aLibPath );
3477 }
3478 catch( const IO_ERROR& ioe )
3479 {
3480 errorMsg = ioe.What();
3481 }
3482
3483 // Some of the files may have been parsed correctly so we want to add the valid files to
3484 // the library.
3485
3486 for( const auto& footprint : m_cache->GetFootprints() )
3487 aFootprintNames.Add( footprint.first );
3488
3489 if( !errorMsg.IsEmpty() && !aBestEfforts )
3490 THROW_IO_ERROR( errorMsg );
3491}
3492
3493
3494const FOOTPRINT* PCB_IO_KICAD_SEXPR::getFootprint( const wxString& aLibraryPath,
3495 const wxString& aFootprintName,
3496 const std::map<std::string, UTF8>* aProperties,
3497 bool checkModified )
3498{
3499 init( aProperties );
3500
3501 try
3502 {
3503 validateCache( aLibraryPath, checkModified );
3504 }
3505 catch( const IO_ERROR& )
3506 {
3507 // do nothing with the error
3508 }
3509
3510 auto it = m_cache->GetFootprints().find( aFootprintName );
3511
3512 if( it == m_cache->GetFootprints().end() )
3513 return nullptr;
3514
3515 return it->second->GetFootprint().get();
3516}
3517
3518
3519const FOOTPRINT* PCB_IO_KICAD_SEXPR::GetEnumeratedFootprint( const wxString& aLibraryPath,
3520 const wxString& aFootprintName,
3521 const std::map<std::string, UTF8>* aProperties )
3522{
3523 return getFootprint( aLibraryPath, aFootprintName, aProperties, false );
3524}
3525
3526
3527bool PCB_IO_KICAD_SEXPR::FootprintExists( const wxString& aLibraryPath,
3528 const wxString& aFootprintName,
3529 const std::map<std::string, UTF8>* aProperties )
3530{
3531 // Note: checking the cache sounds like a good idea, but won't catch files which differ
3532 // only in case.
3533 //
3534 // Since this goes out to the native filesystem, we get platform differences (ie: MSW's
3535 // case-insensitive filesystem) handled "for free".
3536 // Warning: footprint names frequently contain a point. So be careful when initializing
3537 // wxFileName, and use a CTOR with extension specified
3538 wxFileName footprintFile( aLibraryPath, aFootprintName, FILEEXT::KiCadFootprintFileExtension );
3539
3540 return footprintFile.Exists();
3541}
3542
3543
3544FOOTPRINT* PCB_IO_KICAD_SEXPR::ImportFootprint( const wxString& aFootprintPath,
3545 wxString& aFootprintNameOut,
3546 const std::map<std::string, UTF8>* aProperties )
3547{
3548 wxString fcontents;
3549 wxFFile f( aFootprintPath );
3550
3551 // Suppress font substitution warnings (RAII - automatically restored on scope exit)
3552 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
3553
3554 if( !f.IsOpened() )
3555 return nullptr;
3556
3557 f.ReadAll( &fcontents );
3558
3559 aFootprintNameOut = wxFileName( aFootprintPath ).GetName();
3560
3561 return dynamic_cast<FOOTPRINT*>( Parse( fcontents ) );
3562}
3563
3564
3565FOOTPRINT* PCB_IO_KICAD_SEXPR::FootprintLoad( const wxString& aLibraryPath,
3566 const wxString& aFootprintName,
3567 bool aKeepUUID,
3568 const std::map<std::string, UTF8>* aProperties )
3569{
3570 // Suppress font substitution warnings (RAII - automatically restored on scope exit)
3571 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
3572
3573 const FOOTPRINT* footprint = getFootprint( aLibraryPath, aFootprintName, aProperties, true );
3574
3575 if( footprint )
3576 {
3577 FOOTPRINT* copy;
3578
3579 if( aKeepUUID )
3580 copy = static_cast<FOOTPRINT*>( footprint->Clone() );
3581 else
3582 copy = static_cast<FOOTPRINT*>( footprint->Duplicate( IGNORE_PARENT_GROUP ) );
3583
3584 copy->SetParent( nullptr );
3585 return copy;
3586 }
3587
3588 return nullptr;
3589}
3590
3591
3592void PCB_IO_KICAD_SEXPR::FootprintSave( const wxString& aLibraryPath, const FOOTPRINT* aFootprint,
3593 const std::map<std::string, UTF8>* aProperties )
3594{
3595 init( aProperties );
3596
3597 // In this public PLUGIN API function, we can safely assume it was
3598 // called for saving into a library path.
3600
3601 // Support saving to a single-file path like "/tmp/foo.kicad_mod" by treating the directory
3602 // as the library path and the file base-name as the footprint name.
3603 wxString libPath = aLibraryPath;
3604 wxString singleFileBaseName; // without extension
3605 bool saveSingleFile = false;
3606
3607 {
3608 wxFileName asFile( aLibraryPath );
3609
3610 if( asFile.GetExt() == FILEEXT::KiCadFootprintFileExtension )
3611 {
3612 saveSingleFile = true;
3613 libPath = asFile.GetPath();
3614 singleFileBaseName = asFile.GetName();
3615 }
3616 }
3617
3618 validateCache( libPath, !aProperties || !aProperties->contains( "skip_cache_validation" ) );
3619
3620 if( !m_cache->IsWritable() )
3621 {
3622 if( !m_cache->Exists() )
3623 {
3624 const wxString msg = wxString::Format( _( "Library '%s' does not exist.\n"
3625 "Would you like to create it?"),
3626 libPath );
3627
3628 if( !Pgm().IsGUI() || wxMessageBox( msg, _( "Library Not Found" ), wxYES_NO | wxICON_QUESTION ) != wxYES )
3629 return;
3630
3631 // Save throws its own IO_ERROR on failure, so no need to recreate here
3632 m_cache->Save( nullptr );
3633 }
3634 else
3635 {
3636 wxString msg = wxString::Format( _( "Library '%s' is read only." ), libPath );
3637 THROW_IO_ERROR( msg );
3638 }
3639 }
3640
3641 // The map key used by the cache and the on-disk filename base.
3642 wxString footprintName = saveSingleFile ? singleFileBaseName
3643 : aFootprint->GetFPID().GetUniStringLibItemName();
3644
3645 wxString fpName = saveSingleFile ? singleFileBaseName
3646 : aFootprint->GetFPID().GetUniStringLibItemName();
3647 ReplaceIllegalFileNameChars( fpName, '_' );
3648
3649 // Quietly overwrite footprint and delete footprint file from path for any by same name.
3650 wxFileName fn( libPath, fpName, FILEEXT::KiCadFootprintFileExtension );
3651
3652 // Write through symlinks, don't replace them
3654
3655 if( !fn.IsOk() )
3656 {
3657 THROW_IO_ERROR( wxString::Format( _( "Footprint file name '%s' is not valid." ), fn.GetFullPath() ) );
3658 }
3659
3660 if( fn.FileExists() && !fn.IsFileWritable() )
3661 {
3662 THROW_IO_ERROR( wxString::Format( _( "Insufficient permissions to delete '%s'." ),
3663 fn.GetFullPath() ) );
3664 }
3665
3666 wxString fullPath = fn.GetFullPath();
3667 wxString fullName = fn.GetFullName();
3668 auto it = m_cache->GetFootprints().find( footprintName );
3669
3670 if( it != m_cache->GetFootprints().end() )
3671 {
3672 // Save() below writes atomically via sibling temp + rename, so no pre-delete.
3673 wxLogTrace( traceKicadPcbPlugin, wxT( "Replacing footprint file '%s'." ), fullPath );
3674 m_cache->GetFootprints().erase( footprintName );
3675 }
3676
3677 // I need my own copy for the cache
3678 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aFootprint->Clone() );
3679
3680 // It's orientation should be zero and it should be on the front layer.
3681 footprint->SetOrientation( ANGLE_0 );
3682
3683 if( footprint->GetLayer() != F_Cu )
3684 {
3685 PCBNEW_SETTINGS* cfg = dynamic_cast<PCBNEW_SETTINGS*>( Kiface().KifaceSettings() );
3686
3687 if( cfg )
3688 footprint->Flip( footprint->GetPosition(), cfg->m_FlipDirection );
3689 else
3690 footprint->Flip( footprint->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
3691 }
3692
3693 // Detach it from the board and its group
3694 footprint->SetParent( nullptr );
3695 footprint->SetParentGroup( nullptr );
3696
3697 // Now that the clone is detached from its parent board, any m_netinfo pointers its
3698 // descendants still carry reference NETINFO_ITEMs owned by that board and may dangle.
3699 // Force them all to the board-independent ORPHANED singleton before serialization.
3700 footprint->ClearAllNets();
3701
3702 wxLogTrace( traceKicadPcbPlugin, wxT( "Creating s-expr footprint file '%s'." ), fullPath );
3703 m_cache->GetFootprints().insert( footprintName,
3704 new FP_CACHE_ENTRY( footprint,
3705 WX_FILENAME( fn.GetPath(), fullName ) ) );
3706 m_cache->Save( footprint );
3707}
3708
3709
3710void PCB_IO_KICAD_SEXPR::FootprintDelete( const wxString& aLibraryPath,
3711 const wxString& aFootprintName,
3712 const std::map<std::string, UTF8>* aProperties )
3713{
3714 init( aProperties );
3715
3716 validateCache( aLibraryPath );
3717
3718 if( !m_cache->IsWritable() )
3719 {
3720 THROW_IO_ERROR( wxString::Format( _( "Library '%s' is read only." ),
3721 aLibraryPath.GetData() ) );
3722 }
3723
3724 m_cache->Remove( aFootprintName );
3725}
3726
3727
3728void PCB_IO_KICAD_SEXPR::ClearCachedFootprints( const wxString& aLibraryPath )
3729{
3730 if( m_cache && m_cache->IsPath( aLibraryPath ) )
3731 {
3732 delete m_cache;
3733 m_cache = nullptr;
3734 }
3735}
3736
3737
3738long long PCB_IO_KICAD_SEXPR::GetLibraryTimestamp( const wxString& aLibraryPath ) const
3739{
3740 return FP_CACHE::GetTimestamp( aLibraryPath );
3741}
3742
3743
3744void PCB_IO_KICAD_SEXPR::CreateLibrary( const wxString& aLibraryPath,
3745 const std::map<std::string, UTF8>* aProperties )
3746{
3747 if( wxDir::Exists( aLibraryPath ) )
3748 {
3749 THROW_IO_ERROR( wxString::Format( _( "Cannot overwrite library path '%s'." ),
3750 aLibraryPath.GetData() ) );
3751 }
3752
3753 init( aProperties );
3754
3755 delete m_cache;
3756 m_cache = new FP_CACHE( this, aLibraryPath );
3757 m_cache->Save();
3758}
3759
3760
3761bool PCB_IO_KICAD_SEXPR::DeleteLibrary( const wxString& aLibraryPath,
3762 const std::map<std::string, UTF8>* aProperties )
3763{
3764 wxFileName fn;
3765 fn.SetPath( aLibraryPath );
3766
3767 // Return if there is no library path to delete.
3768 if( !fn.DirExists() )
3769 return false;
3770
3771 if( !fn.IsDirWritable() )
3772 {
3773 THROW_IO_ERROR( wxString::Format( _( "Insufficient permissions to delete folder '%s'." ),
3774 aLibraryPath.GetData() ) );
3775 }
3776
3777 wxDir dir( aLibraryPath );
3778
3779 if( dir.HasSubDirs() )
3780 {
3781 THROW_IO_ERROR( wxString::Format( _( "Library folder '%s' has unexpected sub-folders." ),
3782 aLibraryPath.GetData() ) );
3783 }
3784
3785 // All the footprint files must be deleted before the directory can be deleted.
3786 if( dir.HasFiles() )
3787 {
3788 unsigned i;
3789 wxFileName tmp;
3790 wxArrayString files;
3791
3792 CollectFilesLoopSafe( aLibraryPath, files );
3793
3794 for( i = 0; i < files.GetCount(); i++ )
3795 {
3796 tmp = files[i];
3797
3798 if( tmp.GetExt() != FILEEXT::KiCadFootprintFileExtension )
3799 {
3800 THROW_IO_ERROR( wxString::Format( _( "Unexpected file '%s' found in library "
3801 "path '%s'." ),
3802 files[i].GetData(),
3803 aLibraryPath.GetData() ) );
3804 }
3805 }
3806
3807 for( i = 0; i < files.GetCount(); i++ )
3808 wxRemoveFile( files[i] );
3809 }
3810
3811 wxLogTrace( traceKicadPcbPlugin, wxT( "Removing footprint library '%s'." ),
3812 aLibraryPath.GetData() );
3813
3814 // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
3815 // we don't want that. we want bare metal portability with no UI here.
3816 if( !wxRmdir( aLibraryPath ) )
3817 {
3818 THROW_IO_ERROR( wxString::Format( _( "Footprint library '%s' cannot be deleted." ),
3819 aLibraryPath.GetData() ) );
3820 }
3821
3822 // For some reason removing a directory in Windows is not immediately updated. This delay
3823 // prevents an error when attempting to immediately recreate the same directory when over
3824 // writing an existing library.
3825#ifdef __WINDOWS__
3826 wxMilliSleep( 250L );
3827#endif
3828
3829 if( m_cache && !m_cache->IsPath( aLibraryPath ) )
3830 {
3831 delete m_cache;
3832 m_cache = nullptr;
3833 }
3834
3835 return true;
3836}
3837
3838
3839bool PCB_IO_KICAD_SEXPR::IsLibraryWritable( const wxString& aLibraryPath )
3840{
3841 init( nullptr );
3842
3843 validateCache( aLibraryPath );
3844
3845 return m_cache->IsWritable();
3846}
const char * name
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
@ LT_FRONT
Definition board.h:243
@ LT_BACK
Definition board.h:244
@ ZLO_FORCE_FLASHED
Definition board_item.h: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:373
const NETINFO_LIST & GetNetInfo() const
Definition board.h:1094
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition board.cpp:3464
const std::vector< wxString > & GetVariantNames() const
Definition board.h:472
const GENERATORS & Generators() const
Definition board.h:434
void SetFileName(const wxString &aFileName)
Definition board.h:408
const PCB_POINTS & Points() const
Definition board.h:442
const PAGE_INFO & GetPageSettings() const
Definition board.h:897
const ZONES & Zones() const
Definition board.h:425
const GROUPS & Groups() const
The groups must maintain the following invariants.
Definition board.h:461
LAYER_T GetLayerType(PCB_LAYER_ID aLayer) const
Return the type of the copper layer given by aLayer.
Definition board.cpp:849
TITLE_BLOCK & GetTitleBlock()
Definition board.h:903
int GetCopperLayerCount() const
Definition board.cpp:985
const std::map< wxString, wxString > & GetProperties() const
Definition board.h:465
const FOOTPRINTS & Footprints() const
Definition board.h:421
const TRACKS & Tracks() const
Definition board.h:419
wxString GetVariantDescription(const wxString &aVariantName) const
Definition board.cpp:2999
const PCB_PLOT_PARAMS & GetPlotOptions() const
Definition board.h:900
bool LegacyTeardrops() const
Definition board.h:1503
wxString GroupsSanityCheck(bool repair=false)
Consistency check of internal m_groups structure.
Definition board.cpp:3688
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:423
A lightweight representation of a component class.
const std::vector< COMPONENT_CLASS * > & GetConstituentClasses() const
Fetches a vector of the constituent classes for this (effective) class.
double AsDegrees() const
Definition eda_angle.h:116
bool IsZero() const
Definition eda_angle.h:136
EDA_ANGLE Normalize720()
Definition eda_angle.h:279
const LIB_ID & GetDesignBlockLibId() const
Definition eda_group.h:74
std::unordered_set< EDA_ITEM * > & GetItems()
Definition eda_group.h:50
wxString GetName() const
Definition eda_group.h:47
bool HasDesignBlockLink() const
Definition eda_group.h:71
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:96
const KIID m_Uuid
Definition eda_item.h:531
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
virtual void SetParentGroup(EDA_GROUP *aGroup)
Definition eda_item.h:113
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:89
const VECTOR2I & GetBezierC2() const
Definition eda_shape.h:283
FILL_T GetFillMode() const
Definition eda_shape.h:158
SHAPE_POLY_SET & GetPolyShape()
SHAPE_T GetShape() const
Definition eda_shape.h:185
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:240
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:190
wxString SHAPE_T_asString() const
const VECTOR2I & GetBezierC1() const
Definition eda_shape.h:280
int GetCornerRadius() const
bool IsPolyShapeValid() const
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:89
virtual VECTOR2I GetTextSize() const
Definition eda_text.h:282
virtual void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:532
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:110
bool IsKeepUpright() const
Definition eda_text.h:227
virtual bool IsVisible() const
Definition eda_text.h:208
KIFONT::FONT * GetFont() const
Definition eda_text.h:268
std::vector< std::unique_ptr< KIFONT::GLYPH > > * GetRenderCache(const KIFONT::FONT *aFont, const wxString &forResolvedText, const VECTOR2I &aOffset={ 0, 0 }) const
Definition eda_text.cpp:703
virtual EDA_ANGLE GetDrawRotation() const
Definition eda_text.h:400
bool GetAutoThickness() const
Definition eda_text.h:160
virtual void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition eda_text.cpp:279
virtual wxString GetShownText(bool aAllowExtraText, int aDepth=0) const
Return the string actually shown after processing of the base text.
Definition eda_text.h:121
virtual int GetTextThickness() const
Definition eda_text.h:149
bool IsEmpty() const
void WriteEmbeddedFiles(OUTPUTFORMATTER &aOut, bool aWriteData) const
Output formatter for the embedded files.
void ClearEmbeddedFiles(bool aDeleteFiles=true)
EMBEDDED_FILE * AddFile(const wxFileName &aName, bool aOverwrite)
Load a file from disk and adds it to the collection.
const std::map< wxString, std::shared_ptr< EMBEDDED_FILE > > & EmbeddedFileMap() const
Provide an iterable view of the file collection.
bool GetAreFontsEmbedded() const
A LINE_READER that reads from an open file.
Definition richio.h:154
void Rewind()
Rewind the file and resets the line number back to zero.
Definition richio.h:203
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
Definition richio.cpp: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:132
const wxString GetUniStringLibItemName() const
Get strings for display messages in dialogs.
Definition lib_id.h:108
const UTF8 & GetLibItemName() const
Definition lib_id.h:98
An abstract class from which implementation specific LINE_READERs may be derived to read single lines...
Definition richio.h:62
static LOAD_INFO_REPORTER & GetInstance()
Definition reporter.cpp: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)
Definition padstack.cpp:979
MASK_LAYER_PROPS & FrontOuterLayers()
Definition padstack.h:372
void ForEachUniqueLayer(const std::function< void(PCB_LAYER_ID)> &aMethod) const
Runs the given callable for each active unique copper layer in this padstack, meaning F_Cu for MODE::...
std::optional< int > & ThermalSpokeWidth(PCB_LAYER_ID aLayer=F_Cu)
VECTOR2I & Offset(PCB_LAYER_ID aLayer)
Definition padstack.cpp:884
EDA_ANGLE ThermalSpokeAngle(PCB_LAYER_ID aLayer=F_Cu) const
POST_MACHINING_PROPS & FrontPostMachining()
Definition padstack.h:360
std::optional< int > & ThermalGap(PCB_LAYER_ID aLayer=F_Cu)
DRILL_PROPS & TertiaryDrill()
Definition padstack.h:357
DRILL_PROPS & Drill()
Definition padstack.h:351
const VECTOR2I & Size(PCB_LAYER_ID aLayer) const
Definition padstack.cpp:854
@ NORMAL
Shape is the same on all layers.
Definition padstack.h:171
@ FRONT_INNER_BACK
Up to three shapes can be defined (F_Cu, inner copper layers, B_Cu)
Definition padstack.h:172
DRILL_PROPS & SecondaryDrill()
Definition padstack.h:354
POST_MACHINING_PROPS & BackPostMachining()
Definition padstack.h:363
MODE Mode() const
Definition padstack.h:335
MASK_LAYER_PROPS & BackOuterLayers()
Definition padstack.h:375
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:177
static constexpr PCB_LAYER_ID INNER_LAYERS
! The layer identifier to use for "inner layers" on top/inner/bottom padstacks
Definition padstack.h:180
std::optional< ZONE_CONNECTION > & ZoneConnection(PCB_LAYER_ID aLayer=F_Cu)
Definition pad.h:61
PAD_PROP GetProperty() const
Definition pad.h:558
bool GetRemoveUnconnected() const
Definition pad.h:862
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition pad.h:552
const std::vector< std::shared_ptr< PCB_SHAPE > > & GetPrimitives(PCB_LAYER_ID aLayer) const
Accessor to the basic shape list for custom-shaped pads.
Definition pad.h:370
const ZONE_LAYER_OVERRIDE & GetZoneLayerOverride(PCB_LAYER_ID aLayer) const
Definition pad.cpp:480
std::optional< double > GetLocalSolderPasteMarginRatio() const
Definition pad.h:595
const wxString & GetPinType() const
Definition pad.h:160
PAD_ATTRIB GetAttribute() const
Definition pad.h:555
const wxString & GetPinFunction() const
Definition pad.h:154
const wxString & GetNumber() const
Definition pad.h:143
const VECTOR2I & GetDelta(PCB_LAYER_ID aLayer) const
Definition pad.h:302
EDA_ANGLE GetThermalSpokeAngle() const
Definition pad.h:745
VECTOR2I GetOffset(PCB_LAYER_ID aLayer) const
Definition pad.cpp:796
VECTOR2I GetDrillSize() const
Definition pad.h:315
double GetRoundRectRadiusRatio(PCB_LAYER_ID aLayer) const
Definition pad.h:800
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition pad.h:202
bool GetKeepTopBottom() const
Definition pad.h:877
int GetPadToDieDelay() const
Definition pad.h:576
std::optional< int > GetLocalClearance() const override
Return any local clearances set in the "classic" (ie: pre-rule) system.
Definition pad.h:578
const PADSTACK & Padstack() const
Definition pad.h:326
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition pad.cpp:1723
PAD_DRILL_SHAPE GetDrillShape() const
Definition pad.h:429
int GetChamferPositions(PCB_LAYER_ID aLayer) const
Definition pad.h:840
std::optional< int > GetLocalSolderPasteMargin() const
Definition pad.h:588
PAD_SIM_ELECTRICAL_TYPE GetSimElectricalType() const
Definition pad.h:568
std::optional< int > GetLocalSolderMaskMargin() const
Definition pad.h:581
double GetChamferRectRatio(PCB_LAYER_ID aLayer) const
Definition pad.h:823
std::optional< int > GetLocalThermalSpokeWidthOverride() const
Definition pad.h:729
ZONE_CONNECTION GetLocalZoneConnection() const
Definition pad.h:606
CUSTOM_SHAPE_ZONE_MODE GetCustomShapeInZoneOpt() const
Definition pad.h:224
int GetLocalThermalGapOverride(wxString *aSource) const
Definition pad.cpp:2149
PAD_SHAPE GetAnchorPadShape(PCB_LAYER_ID aLayer) const
Definition pad.h:216
int GetPadToDieLength() const
Definition pad.h:573
void Format(OUTPUTFORMATTER *aFormatter) const
Output the page class to aFormatter in s-expression form.
FLIP_DIRECTION m_FlipDirection
const VECTOR2I & GetMid() const
Definition pcb_track.h:286
const VECTOR2I & GetMargin() const
Get the barcode margin (in internal units).
VECTOR2I GetPosition() const override
Get the position (center) of the barcode in internal units.
wxString GetText() const
int GetTextSize() const
bool IsKnockout() const override
int GetHeight() const
Get the barcode height (in internal units).
BARCODE_ECC_T GetErrorCorrection() const
bool GetShowText() const
EDA_ANGLE GetAngle() const
BARCODE_T GetKind() const
Returns the type of the barcode (QR, CODE_39, etc.).
int GetWidth() const
Get the barcode width (in internal units).
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:813
std::optional< int > GetLocalClearance() const override
Definition zone.cpp:1012
const THIEVING_SETTINGS & GetThievingSettings() const
Definition zone.h:351
bool GetDoNotAllowVias() const
Definition zone.h:824
ZONE_LAYER_PROPERTIES & LayerProperties(PCB_LAYER_ID aLayer)
Definition zone.h:146
wxString GetPlacementAreaSource() const
Definition zone.h:818
std::shared_ptr< SHAPE_POLY_SET > GetFilledPolysList(PCB_LAYER_ID aLayer) const
Definition zone.h:697
bool GetDoNotAllowPads() const
Definition zone.h:826
PLACEMENT_SOURCE_T GetPlacementAreaSourceType() const
Definition zone.h:820
bool GetDoNotAllowTracks() const
Definition zone.h:825
bool IsFilled() const
Definition zone.h:306
ISLAND_REMOVAL_MODE GetIslandRemovalMode() const
Definition zone.h:835
SHAPE_POLY_SET * Outline()
Definition zone.h:418
bool IsIsland(PCB_LAYER_ID aLayer, int aPolyIdx) const
Check if a given filled polygon is an insulated island.
Definition zone.cpp:1631
long long int GetMinIslandArea() const
Definition zone.h:838
const wxString & GetZoneName() const
Definition zone.h:160
int GetMinThickness() const
Definition zone.h:315
ZONE_CONNECTION GetPadConnection() const
Definition zone.h:312
int GetHatchThickness() const
Definition zone.h:325
double GetHatchHoleMinArea() const
Definition zone.h:340
bool GetPlacementAreaEnabled() const
Definition zone.h:815
bool IsTeardropArea() const
Definition zone.h:788
int GetThermalReliefSpokeWidth() const
Definition zone.h:259
int GetBorderHatchPitch() const
HatchBorder related methods.
Definition zone.h:848
ZONE_BORDER_DISPLAY_STYLE GetHatchStyle() const
Definition zone.h:685
EDA_ANGLE GetHatchOrientation() const
Definition zone.h:331
bool GetDoNotAllowFootprints() const
Definition zone.h:827
ZONE_FILL_MODE GetFillMode() const
Definition zone.h:238
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition zone.h:133
int GetHatchGap() const
Definition zone.h:328
TEARDROP_TYPE GetTeardropAreaType() const
Definition zone.h:799
double GetHatchSmoothingValue() const
Definition zone.h:337
bool GetDoNotAllowZoneFills() const
Definition zone.h:823
int GetHatchSmoothingLevel() const
Definition zone.h:334
unsigned int GetCornerRadius() const
Definition zone.h:756
int GetCornerSmoothingType() const
Definition zone.h:752
bool IsOnCopperLayer() const override
Definition zone.cpp:594
PCB_LAYER_ID GetFirstLayer() const
Definition zone.cpp:574
int GetThermalReliefGap() const
Definition zone.h:248
unsigned GetAssignedPriority() const
Definition zone.h:122
int GetNumCorners(void) const
Access to m_Poly parameters.
Definition zone.h:615
This file is part of the common library.
#define CTL_OMIT_HYPERLINK
Omit the hyperlink attribute in .kicad_xxx files.
Definition ctl_flags.h:46
#define CTL_OMIT_UUIDS
Omit component unique ids (useless in library)
Definition ctl_flags.h:30
#define CTL_OMIT_FOOTPRINT_VERSION
Omit the version string from the (footprint) sexpr group.
Definition ctl_flags.h:39
#define CTL_OMIT_INITIAL_COMMENTS
Omit FOOTPRINT initial comments.
Definition ctl_flags.h:43
#define CTL_OMIT_LIBNAME
Omit lib alias when saving (used for board/not library).
Definition ctl_flags.h:37
#define CTL_OMIT_PATH
Omit component sheet time stamp (useless in library).
Definition ctl_flags.h:33
#define CTL_OMIT_AT
Omit position and rotation.
Definition ctl_flags.h:35
#define CTL_OMIT_PAD_NETS
Omit pads net names (useless in library).
Definition ctl_flags.h:29
#define CTL_OMIT_COLOR
Omit the color attribute in .kicad_xxx files.
Definition ctl_flags.h:45
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:413
static constexpr EDA_ANGLE ANGLE_45
Definition eda_angle.h:412
#define IGNORE_PARENT_GROUP
Definition eda_item.h:53
SHAPE_T
Definition eda_shape.h:44
@ ELLIPSE
Definition eda_shape.h:52
@ SEGMENT
Definition eda_shape.h:46
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:47
@ ELLIPSE_ARC
Definition eda_shape.h:53
@ REVERSE_HATCH
Definition eda_shape.h:65
@ HATCH
Definition eda_shape.h:64
@ FILLED_SHAPE
Fill with object color.
Definition eda_shape.h:61
@ CROSS_HATCH
Definition eda_shape.h:66
EDA_DATA_TYPE
The type of unit.
Definition eda_units.h:34
@ FP_SMD
Definition footprint.h:84
@ FP_DNP
Definition footprint.h:89
@ FP_EXCLUDE_FROM_POS_FILES
Definition footprint.h:85
@ FP_BOARD_ONLY
Definition footprint.h:87
@ FP_EXCLUDE_FROM_BOM
Definition footprint.h:86
@ FP_THROUGH_HOLE
Definition footprint.h:83
@ EXPAND_INNER_LAYERS
The 'normal' stackup handling, where there is a single inner layer (In1) and rule areas using it expa...
Definition footprint.h:148
void CollectFilesLoopSafe(const wxString &aRoot, wxArrayString &aFiles, const wxString &aFileSpec, int aFlags)
Recursively collect every file under aRoot, deduplicating subdirectories by their resolved path.
Definition gestfich.cpp:845
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