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