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