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