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