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