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