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