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