KiCad PCB EDA Suite
pcb_plugin.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 (C) 1992-2022 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 <advanced_config.h>
26#include <base_units.h>
27#include <board.h>
29#include <confirm.h>
30#include <convert_basic_shapes_to_polygon.h> // for enum RECT_CHAMFER_POSITIONS definition
31#include <core/arraydim.h>
32#include <pcb_dimension.h>
33#include <footprint.h>
34#include <fp_shape.h>
35#include <fp_textbox.h>
36#include <string_utils.h>
37#include <kiface_base.h>
38#include <locale_io.h>
39#include <macros.h>
40#include <pad.h>
41#include <pcb_group.h>
42#include <pcb_shape.h>
43#include <pcb_bitmap.h>
44#include <pcb_target.h>
45#include <pcb_text.h>
46#include <pcb_textbox.h>
47#include <pcbnew_settings.h>
50#include <trace_helpers.h>
51#include <pcb_track.h>
52#include <progress_reporter.h>
54#include <wx/dir.h>
55#include <wx/log.h>
56#include <zone.h>
57#include <zones.h>
58
59// For some reason wxWidgets is built with wxUSE_BASE64 unset so expose the wxWidgets
60// base64 code. Needed for PCB_BITMAP
61#define wxUSE_BASE64 1
62#include <wx/base64.h>
63#include <wx/mstream.h>
64
65
66using namespace PCB_KEYS_T;
67
68
69FP_CACHE_ITEM::FP_CACHE_ITEM( FOOTPRINT* aFootprint, const WX_FILENAME& aFileName ) :
70 m_filename( aFileName ),
71 m_footprint( aFootprint )
72{ }
73
74
75FP_CACHE::FP_CACHE( PCB_PLUGIN* aOwner, const wxString& aLibraryPath )
76{
77 m_owner = aOwner;
78 m_lib_raw_path = aLibraryPath;
79 m_lib_path.SetPath( aLibraryPath );
81 m_cache_dirty = true;
82}
83
84
85void FP_CACHE::Save( FOOTPRINT* aFootprint )
86{
88
89 if( !m_lib_path.DirExists() && !m_lib_path.Mkdir() )
90 {
91 THROW_IO_ERROR( wxString::Format( _( "Cannot create footprint library '%s'." ),
93 }
94
95 if( !m_lib_path.IsDirWritable() )
96 {
97 THROW_IO_ERROR( wxString::Format( _( "Footprint library '%s' is read only." ),
99 }
100
101 for( FP_CACHE_FOOTPRINT_MAP::iterator it = m_footprints.begin(); it != m_footprints.end(); ++it )
102 {
103 if( aFootprint && aFootprint != it->second->GetFootprint() )
104 continue;
105
106 WX_FILENAME fn = it->second->GetFileName();
107
108 wxString tempFileName =
109#ifdef USE_TMP_FILE
110 wxFileName::CreateTempFileName( fn.GetPath() );
111#else
112 fn.GetFullPath();
113#endif
114 // Allow file output stream to go out of scope to close the file stream before
115 // renaming the file.
116 {
117 wxLogTrace( traceKicadPcbPlugin, wxT( "Creating temporary library file '%s'." ),
118 tempFileName );
119
120 FILE_OUTPUTFORMATTER formatter( tempFileName );
121
122 m_owner->SetOutputFormatter( &formatter );
123 m_owner->Format( (BOARD_ITEM*) it->second->GetFootprint() );
124 }
125
126#ifdef USE_TMP_FILE
127 wxRemove( fn.GetFullPath() ); // it is not an error if this does not exist
128
129 // Even on Linux you can see an _intermittent_ error when calling wxRename(),
130 // and it is fully inexplicable. See if this dodges the error.
131 wxMilliSleep( 250L );
132
133 if( !wxRenameFile( tempFileName, fn.GetFullPath() ) )
134 {
135 wxString msg = wxString::Format( _( "Cannot rename temporary file '%s' to '%s'" ),
136 tempFileName,
137 fn.GetFullPath() );
138 THROW_IO_ERROR( msg );
139 }
140#endif
142 }
143
144 m_cache_timestamp += m_lib_path.GetModificationTime().GetValue().GetValue();
145
146 // If we've saved the full cache, we clear the dirty flag.
147 if( !aFootprint )
148 m_cache_dirty = false;
149}
150
151
153{
154 m_cache_dirty = false;
156
157 wxDir dir( m_lib_raw_path );
158
159 if( !dir.IsOpened() )
160 {
161 wxString msg = wxString::Format( _( "Footprint library '%s' not found." ),
163 THROW_IO_ERROR( msg );
164 }
165
166 wxString fullName;
167 wxString fileSpec = wxT( "*." ) + KiCadFootprintFileExtension;
168
169 // wxFileName construction is egregiously slow. Construct it once and just swap out
170 // the filename thereafter.
171 WX_FILENAME fn( m_lib_raw_path, wxT( "dummyName" ) );
172
173 if( dir.GetFirst( &fullName, fileSpec ) )
174 {
175 wxString cacheError;
176
177 do
178 {
179 fn.SetFullName( fullName );
180
181 // Queue I/O errors so only files that fail to parse don't get loaded.
182 try
183 {
184 FILE_LINE_READER reader( fn.GetFullPath() );
185 PCB_PARSER parser( &reader, nullptr, nullptr );
186
187 // use dynamic cast in case somebody renames a .kicad_pcb as .kicad_mod and chucks it into a library folder
188 // the parsing definitely fails then
189 FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( parser.Parse() );
190 wxString fpName = fn.GetName();
191
192 if( !footprint )
193 {
194 THROW_IO_ERROR( wxString::Format( _( "Unable to read file '%s'" ), fn.GetFullPath() ) );
195 }
196
197 footprint->SetFPID( LIB_ID( wxEmptyString, fpName ) );
198 m_footprints.insert( fpName, new FP_CACHE_ITEM( footprint, fn ) );
199 }
200 catch( const IO_ERROR& ioe )
201 {
202 if( !cacheError.IsEmpty() )
203 cacheError += wxT( "\n\n" );
204
205 cacheError += ioe.What();
206 }
207 } while( dir.GetNext( &fullName ) );
208
210
211 if( !cacheError.IsEmpty() )
212 THROW_IO_ERROR( cacheError );
213 }
214}
215
216
217void FP_CACHE::Remove( const wxString& aFootprintName )
218{
219 FP_CACHE_FOOTPRINT_MAP::const_iterator it = m_footprints.find( aFootprintName );
220
221 if( it == m_footprints.end() )
222 {
223 wxString msg = wxString::Format( _( "Library '%s' has no footprint '%s'." ),
225 aFootprintName );
226 THROW_IO_ERROR( msg );
227 }
228
229 // Remove the footprint from the cache and delete the footprint file from the library.
230 wxString fullPath = it->second->GetFileName().GetFullPath();
231 m_footprints.erase( aFootprintName );
232 wxRemoveFile( fullPath );
233}
234
235
236bool FP_CACHE::IsPath( const wxString& aPath ) const
237{
238 return aPath == m_lib_raw_path;
239}
240
241
242void FP_CACHE::SetPath( const wxString& aPath )
243{
244 m_lib_raw_path = aPath;
245 m_lib_path.SetPath( aPath );
246
247
248 for( const auto& footprint : GetFootprints() )
249 {
250 footprint.second->SetFilePath( aPath );
251 }
252}
253
254
256{
258
259 return m_cache_dirty;
260}
261
262
263long long FP_CACHE::GetTimestamp( const wxString& aLibPath )
264{
265 wxString fileSpec = wxT( "*." ) + KiCadFootprintFileExtension;
266
267 return TimestampDir( aLibPath, fileSpec );
268}
269
270
271void PCB_PLUGIN::Save( const wxString& aFileName, BOARD* aBoard, const STRING_UTF8_MAP* aProperties )
272{
273 LOCALE_IO toggle; // toggles on, then off, the C locale.
274
275 wxString sanityResult = aBoard->GroupsSanityCheck();
276
277 if( sanityResult != wxEmptyString && m_queryUserCallback )
278 {
279 if( !(*m_queryUserCallback)(
280 _( "Internal Group Data Error" ), wxICON_ERROR,
281 wxString::Format( _( "Please report this bug. Error validating group "
282 "structure: %s\n\nSave anyway?" ), sanityResult ),
283 _( "Save Anyway" ) ) )
284 {
285 return;
286 }
287 }
288
289 init( aProperties );
290
291 m_board = aBoard; // after init()
292
293 // Prepare net mapping that assures that net codes saved in a file are consecutive integers
294 m_mapping->SetBoard( aBoard );
295
296 FILE_OUTPUTFORMATTER formatter( aFileName );
297
298 m_out = &formatter; // no ownership
299
300 m_out->Print( 0, "(kicad_pcb (version %d) (generator pcbnew)\n", SEXPR_BOARD_FILE_VERSION );
301
302 Format( aBoard, 1 );
303
304 m_out->Print( 0, ")\n" );
305
306 m_out = nullptr;
307}
308
309
310BOARD_ITEM* PCB_PLUGIN::Parse( const wxString& aClipboardSourceInput )
311{
312 std::string input = TO_UTF8( aClipboardSourceInput );
313
314 STRING_LINE_READER reader( input, wxT( "clipboard" ) );
315 PCB_PARSER parser( &reader, nullptr, m_queryUserCallback );
316
317 try
318 {
319 return parser.Parse();
320 }
321 catch( const PARSE_ERROR& parse_error )
322 {
323 if( parser.IsTooRecent() )
324 throw FUTURE_FORMAT_ERROR( parse_error, parser.GetRequiredVersion() );
325 else
326 throw;
327 }
328}
329
330
331void PCB_PLUGIN::Format( const BOARD_ITEM* aItem, int aNestLevel ) const
332{
333 LOCALE_IO toggle; // public API function, perform anything convenient for caller
334
335 switch( aItem->Type() )
336 {
337 case PCB_T:
338 format( static_cast<const BOARD*>( aItem ), aNestLevel );
339 break;
340
342 case PCB_DIM_CENTER_T:
343 case PCB_DIM_RADIAL_T:
345 case PCB_DIM_LEADER_T:
351 format( static_cast<const PCB_DIMENSION_BASE*>( aItem ), aNestLevel );
352 break;
353
354 case PCB_SHAPE_T:
355 format( static_cast<const PCB_SHAPE*>( aItem ), aNestLevel );
356 break;
357
358 case PCB_BITMAP_T:
359 format( static_cast<const PCB_BITMAP*>( aItem ), aNestLevel );
360 break;
361
362 case PCB_FP_SHAPE_T:
363 format( static_cast<const FP_SHAPE*>( aItem ), aNestLevel );
364 break;
365
366 case PCB_TARGET_T:
367 format( static_cast<const PCB_TARGET*>( aItem ), aNestLevel );
368 break;
369
370 case PCB_FOOTPRINT_T:
371 format( static_cast<const FOOTPRINT*>( aItem ), aNestLevel );
372 break;
373
374 case PCB_PAD_T:
375 format( static_cast<const PAD*>( aItem ), aNestLevel );
376 break;
377
378 case PCB_TEXT_T:
379 format( static_cast<const PCB_TEXT*>( aItem ), aNestLevel );
380 break;
381
382 case PCB_TEXTBOX_T:
383 format( static_cast<const PCB_TEXTBOX*>( aItem ), aNestLevel );
384 break;
385
386 case PCB_FP_TEXT_T:
387 format( static_cast<const FP_TEXT*>( aItem ), aNestLevel );
388 break;
389
390 case PCB_FP_TEXTBOX_T:
391 format( static_cast<const FP_TEXTBOX*>( aItem ), aNestLevel );
392 break;
393
394 case PCB_GROUP_T:
395 format( static_cast<const PCB_GROUP*>( aItem ), aNestLevel );
396 break;
397
398 case PCB_TRACE_T:
399 case PCB_ARC_T:
400 case PCB_VIA_T:
401 format( static_cast<const PCB_TRACK*>( aItem ), aNestLevel );
402 break;
403
404 case PCB_FP_ZONE_T:
405 case PCB_ZONE_T:
406 format( static_cast<const ZONE*>( aItem ), aNestLevel );
407 break;
408
409 default:
410 wxFAIL_MSG( wxT( "Cannot format item " ) + aItem->GetClass() );
411 }
412}
413
414
415void PCB_PLUGIN::formatLayer( PCB_LAYER_ID aLayer, bool aIsKnockout ) const
416{
417 m_out->Print( 0, " (layer %s%s)",
418 m_out->Quotew( LSET::Name( aLayer ) ).c_str(),
419 aIsKnockout ? " knockout" : "" );
420}
421
422
423void PCB_PLUGIN::formatPolyPts( const SHAPE_LINE_CHAIN& outline, int aNestLevel,
424 bool aCompact ) const
425{
426 m_out->Print( aNestLevel + 1, "(pts\n" );
427
428 bool needNewline = false;
429 int nestLevel = aNestLevel + 2;
430 int shapesAdded = 0;
431
432 for( int ii = 0; ii < outline.PointCount(); ++ii )
433 {
434 int ind = outline.ArcIndex( ii );
435
436 if( ind < 0 )
437 {
438 m_out->Print( nestLevel, "(xy %s)",
439 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, outline.CPoint( ii ) ).c_str() );
440 needNewline = true;
441 }
442 else
443 {
444 const SHAPE_ARC& arc = outline.Arc( ind );
445 m_out->Print( nestLevel, "(arc (start %s) (mid %s) (end %s))",
449 needNewline = true;
450
451 do
452 {
453 ++ii;
454 } while( ii < outline.PointCount() && outline.ArcIndex( ii ) == ind );
455
456 --ii;
457 }
458
459 ++shapesAdded;
460
461 if( !( shapesAdded % 4 ) || !aCompact )
462 {
463 // newline every 4 shapes if compact save
464 m_out->Print( 0, "\n" );
465 needNewline = false;
466 }
467 }
468
469 if( needNewline )
470 m_out->Print( 0, "\n" );
471
472 m_out->Print( aNestLevel + 1, ")\n" );
473}
474
475
476void PCB_PLUGIN::formatRenderCache( const EDA_TEXT* aText, int aNestLevel ) const
477{
478 const wxString& shownText = aText->GetShownText();
479 std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = aText->GetRenderCache( aText->GetFont(),
480 shownText );
481
482 m_out->Print( aNestLevel, "(render_cache %s %s\n",
483 m_out->Quotew( shownText ).c_str(),
484 EDA_UNIT_UTILS::FormatAngle( aText->GetDrawRotation() ).c_str() );
485
486 for( const std::unique_ptr<KIFONT::GLYPH>& baseGlyph : *cache )
487 {
488 KIFONT::OUTLINE_GLYPH* glyph = static_cast<KIFONT::OUTLINE_GLYPH*>( baseGlyph.get() );
489
490 if( glyph->OutlineCount() > 0 )
491 {
492 for( int ii = 0; ii < glyph->OutlineCount(); ++ii )
493 {
494 m_out->Print( aNestLevel + 1, "(polygon\n" );
495
496 formatPolyPts( glyph->Outline( ii ), aNestLevel + 1, true );
497
498 for( int jj = 0; jj < glyph->HoleCount( ii ); ++jj )
499 formatPolyPts( glyph->Hole( ii, jj ), aNestLevel + 2, true );
500
501 m_out->Print( aNestLevel + 1, ")\n" );
502 }
503 }
504 }
505
506 m_out->Print( aNestLevel, ")\n" );
507}
508
509
510void PCB_PLUGIN::formatSetup( const BOARD* aBoard, int aNestLevel ) const
511{
512 // Setup
513 m_out->Print( aNestLevel, "(setup\n" );
514
515 // Save the board physical stackup structure
516 const BOARD_STACKUP& stackup = aBoard->GetDesignSettings().GetStackupDescriptor();
517
518 if( aBoard->GetDesignSettings().m_HasStackup )
519 stackup.FormatBoardStackup( m_out, aBoard, aNestLevel+1 );
520
521 BOARD_DESIGN_SETTINGS& dsnSettings = aBoard->GetDesignSettings();
522
523 m_out->Print( aNestLevel+1, "(pad_to_mask_clearance %s)\n",
525
526 if( dsnSettings.m_SolderMaskMinWidth )
527 {
528 m_out->Print( aNestLevel+1, "(solder_mask_min_width %s)\n",
530 }
531
532 if( dsnSettings.m_SolderPasteMargin != 0 )
533 {
534 m_out->Print( aNestLevel+1, "(pad_to_paste_clearance %s)\n",
536 }
537
538 if( dsnSettings.m_SolderPasteMarginRatio != 0 )
539 {
540 m_out->Print( aNestLevel+1, "(pad_to_paste_clearance_ratio %s)\n",
541 FormatDouble2Str( dsnSettings.m_SolderPasteMarginRatio ).c_str() );
542 }
543
544 if( dsnSettings.m_AllowSoldermaskBridgesInFPs )
545 {
546 m_out->Print( aNestLevel+1, "(allow_soldermask_bridges_in_footprints yes)\n" );
547 }
548
549 VECTOR2I origin = dsnSettings.GetAuxOrigin();
550
551 if( origin != VECTOR2I( 0, 0 ) )
552 {
553 m_out->Print( aNestLevel+1, "(aux_axis_origin %s %s)\n",
556 }
557
558 origin = dsnSettings.GetGridOrigin();
559
560 if( origin != VECTOR2I( 0, 0 ) )
561 {
562 m_out->Print( aNestLevel+1, "(grid_origin %s %s)\n",
565 }
566
567 aBoard->GetPlotOptions().Format( m_out, aNestLevel+1 );
568
569 m_out->Print( aNestLevel, ")\n\n" );
570}
571
572
573void PCB_PLUGIN::formatGeneral( const BOARD* aBoard, int aNestLevel ) const
574{
575 const BOARD_DESIGN_SETTINGS& dsnSettings = aBoard->GetDesignSettings();
576
577 m_out->Print( 0, "\n" );
578 m_out->Print( aNestLevel, "(general\n" );
579 m_out->Print( aNestLevel+1, "(thickness %s)\n",
581
582 m_out->Print( aNestLevel, ")\n\n" );
583
584 aBoard->GetPageSettings().Format( m_out, aNestLevel, m_ctl );
585 aBoard->GetTitleBlock().Format( m_out, aNestLevel, m_ctl );
586}
587
588
589void PCB_PLUGIN::formatBoardLayers( const BOARD* aBoard, int aNestLevel ) const
590{
591 m_out->Print( aNestLevel, "(layers\n" );
592
593 // Save only the used copper layers from front to back.
594
595 for( LSEQ cu = aBoard->GetEnabledLayers().CuStack(); cu; ++cu )
596 {
597 PCB_LAYER_ID layer = *cu;
598
599 m_out->Print( aNestLevel+1, "(%d %s %s", layer,
600 m_out->Quotew( LSET::Name( layer ) ).c_str(),
601 LAYER::ShowType( aBoard->GetLayerType( layer ) ) );
602
603 if( LSET::Name( layer ) != m_board->GetLayerName( layer ) )
604 m_out->Print( 0, " %s", m_out->Quotew( m_board->GetLayerName( layer ) ).c_str() );
605
606 m_out->Print( 0, ")\n" );
607 }
608
609 // Save used non-copper layers in the order they are defined.
610 // desired sequence for non Cu BOARD layers.
611 static const PCB_LAYER_ID non_cu[] =
612 {
613 B_Adhes, // 32
614 F_Adhes,
615 B_Paste,
616 F_Paste,
617 B_SilkS,
618 F_SilkS,
619 B_Mask,
620 F_Mask,
621 Dwgs_User,
622 Cmts_User,
623 Eco1_User,
624 Eco2_User,
625 Edge_Cuts,
626 Margin,
627 B_CrtYd,
628 F_CrtYd,
629 B_Fab,
630 F_Fab,
631 User_1,
632 User_2,
633 User_3,
634 User_4,
635 User_5,
636 User_6,
637 User_7,
638 User_8,
639 User_9
640 };
641
642 for( LSEQ seq = aBoard->GetEnabledLayers().Seq( non_cu, arrayDim( non_cu ) ); seq; ++seq )
643 {
644 PCB_LAYER_ID layer = *seq;
645
646 m_out->Print( aNestLevel+1, "(%d %s user", layer,
647 m_out->Quotew( LSET::Name( layer ) ).c_str() );
648
649 if( m_board->GetLayerName( layer ) != LSET::Name( layer ) )
650 m_out->Print( 0, " %s", m_out->Quotew( m_board->GetLayerName( layer ) ).c_str() );
651
652 m_out->Print( 0, ")\n" );
653 }
654
655 m_out->Print( aNestLevel, ")\n\n" );
656}
657
658
659void PCB_PLUGIN::formatNetInformation( const BOARD* aBoard, int aNestLevel ) const
660{
661 for( NETINFO_ITEM* net : *m_mapping )
662 {
663 if( net == nullptr ) // Skip not actually existing nets (orphan nets)
664 continue;
665
666 m_out->Print( aNestLevel, "(net %d %s)\n",
667 m_mapping->Translate( net->GetNetCode() ),
668 m_out->Quotew( net->GetNetname() ).c_str() );
669 }
670
671 m_out->Print( 0, "\n" );
672}
673
674
675void PCB_PLUGIN::formatProperties( const BOARD* aBoard, int aNestLevel ) const
676{
677 for( const std::pair<const wxString, wxString>& prop : aBoard->GetProperties() )
678 {
679 m_out->Print( aNestLevel, "(property %s %s)\n",
680 m_out->Quotew( prop.first ).c_str(),
681 m_out->Quotew( prop.second ).c_str() );
682 }
683
684 if( !aBoard->GetProperties().empty() )
685 m_out->Print( 0, "\n" );
686}
687
688
689void PCB_PLUGIN::formatHeader( const BOARD* aBoard, int aNestLevel ) const
690{
691 formatGeneral( aBoard, aNestLevel );
692
693 // Layers list.
694 formatBoardLayers( aBoard, aNestLevel );
695
696 // Setup
697 formatSetup( aBoard, aNestLevel );
698
699 // Properties
700 formatProperties( aBoard, aNestLevel );
701
702 // Save net codes and names
703 formatNetInformation( aBoard, aNestLevel );
704}
705
706
707void PCB_PLUGIN::format( const BOARD* aBoard, int aNestLevel ) const
708{
709 std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_footprints( aBoard->Footprints().begin(),
710 aBoard->Footprints().end() );
711 std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_drawings( aBoard->Drawings().begin(),
712 aBoard->Drawings().end() );
713 std::set<PCB_TRACK*, PCB_TRACK::cmp_tracks> sorted_tracks( aBoard->Tracks().begin(),
714 aBoard->Tracks().end() );
715 std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_zones( aBoard->Zones().begin(),
716 aBoard->Zones().end() );
717 std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_groups( aBoard->Groups().begin(),
718 aBoard->Groups().end() );
719 formatHeader( aBoard, aNestLevel );
720
721 // Save the footprints.
722 for( BOARD_ITEM* footprint : sorted_footprints )
723 {
724 Format( footprint, aNestLevel );
725 m_out->Print( 0, "\n" );
726 }
727
728 // Save the graphical items on the board (not owned by a footprint)
729 for( BOARD_ITEM* item : sorted_drawings )
730 Format( item, aNestLevel );
731
732 if( sorted_drawings.size() )
733 m_out->Print( 0, "\n" );
734
735 // Do not save PCB_MARKERs, they can be regenerated easily.
736
737 // Save the tracks and vias.
738 for( PCB_TRACK* track : sorted_tracks )
739 Format( track, aNestLevel );
740
741 if( sorted_tracks.size() )
742 m_out->Print( 0, "\n" );
743
744 // Save the polygon (which are the newer technology) zones.
745 for( auto zone : sorted_zones )
746 Format( zone, aNestLevel );
747
748 // Save the groups
749 for( BOARD_ITEM* group : sorted_groups )
750 Format( group, aNestLevel );
751}
752
753
754void PCB_PLUGIN::format( const PCB_DIMENSION_BASE* aDimension, int aNestLevel ) const
755{
756 const PCB_DIM_ALIGNED* aligned = dynamic_cast<const PCB_DIM_ALIGNED*>( aDimension );
757 const PCB_DIM_ORTHOGONAL* ortho = dynamic_cast<const PCB_DIM_ORTHOGONAL*>( aDimension );
758 const PCB_DIM_CENTER* center = dynamic_cast<const PCB_DIM_CENTER*>( aDimension );
759 const PCB_DIM_RADIAL* radial = dynamic_cast<const PCB_DIM_RADIAL*>( aDimension );
760 const PCB_DIM_LEADER* leader = dynamic_cast<const PCB_DIM_LEADER*>( aDimension );
761
762 m_out->Print( aNestLevel, "(dimension" );
763
764 if( aDimension->IsLocked() )
765 m_out->Print( 0, " locked" );
766
767 if( ortho ) // must be tested before aligned, because ortho is derived from aligned
768 // and aligned is not null
769 m_out->Print( 0, " (type orthogonal)" );
770 else if( aligned )
771 m_out->Print( 0, " (type aligned)" );
772 else if( leader )
773 m_out->Print( 0, " (type leader)" );
774 else if( center )
775 m_out->Print( 0, " (type center)" );
776 else if( radial )
777 m_out->Print( 0, " (type radial)" );
778 else
779 wxFAIL_MSG( wxT( "Cannot format unknown dimension type!" ) );
780
781 formatLayer( aDimension->GetLayer() );
782
783 m_out->Print( 0, " (tstamp %s)", TO_UTF8( aDimension->m_Uuid.AsString() ) );
784
785 m_out->Print( 0, "\n" );
786
787 m_out->Print( aNestLevel+1, "(pts (xy %s %s) (xy %s %s))\n",
790 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, aDimension->GetEnd().x ).c_str(),
791 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, aDimension->GetEnd().y ).c_str() );
792
793 if( aligned )
794 {
795 m_out->Print( aNestLevel+1, "(height %s)\n",
797 }
798
799 if( radial )
800 {
801 m_out->Print( aNestLevel+1, "(leader_length %s)\n",
803 }
804
805 if( ortho )
806 {
807 m_out->Print( aNestLevel+1, "(orientation %d)\n",
808 static_cast<int>( ortho->GetOrientation() ) );
809 }
810
811 if( !center )
812 {
813 format( static_cast<const PCB_TEXT*>( aDimension ), aNestLevel + 1 );
814
815 m_out->Print( aNestLevel + 1, "(format (prefix %s) (suffix %s) (units %d) (units_format %d) (precision %d)",
816 m_out->Quotew( aDimension->GetPrefix() ).c_str(),
817 m_out->Quotew( aDimension->GetSuffix() ).c_str(),
818 static_cast<int>( aDimension->GetUnitsMode() ),
819 static_cast<int>( aDimension->GetUnitsFormat() ),
820 static_cast<int>( aDimension->GetPrecision() ) );
821
822 if( aDimension->GetOverrideTextEnabled() )
823 {
824 m_out->Print( 0, " (override_value %s)",
825 m_out->Quotew( aDimension->GetOverrideText() ).c_str() );
826 }
827
828 if( aDimension->GetSuppressZeroes() )
829 m_out->Print( 0, " suppress_zeroes" );
830
831 m_out->Print( 0, ")\n" );
832 }
833
834 m_out->Print( aNestLevel+1, "(style (thickness %s) (arrow_length %s) (text_position_mode %d)",
837 static_cast<int>( aDimension->GetTextPositionMode() ) );
838
839 if( aligned )
840 {
841 m_out->Print( 0, " (extension_height %s)",
843 }
844
845 if( leader )
846 m_out->Print( 0, " (text_frame %d)", static_cast<int>( leader->GetTextBorder() ) );
847
848 m_out->Print( 0, " (extension_offset %s)",
850
851 if( aDimension->GetKeepTextAligned() )
852 m_out->Print( 0, " keep_text_aligned" );
853
854 m_out->Print( 0, ")\n" );
855
856 m_out->Print( aNestLevel, ")\n" );
857}
858
859
860void PCB_PLUGIN::format( const PCB_SHAPE* aShape, int aNestLevel ) const
861{
862 std::string locked = aShape->IsLocked() ? " locked" : "";
863
864 switch( aShape->GetShape() )
865 {
866 case SHAPE_T::SEGMENT:
867 m_out->Print( aNestLevel, "(gr_line%s (start %s) (end %s)",
868 locked.c_str(),
871 break;
872
873 case SHAPE_T::RECT:
874 m_out->Print( aNestLevel, "(gr_rect%s (start %s) (end %s)",
875 locked.c_str(),
878 break;
879
880 case SHAPE_T::CIRCLE:
881 m_out->Print( aNestLevel, "(gr_circle%s (center %s) (end %s)",
882 locked.c_str(),
885 break;
886
887 case SHAPE_T::ARC:
888 m_out->Print( aNestLevel, "(gr_arc%s (start %s) (mid %s) (end %s)",
889 locked.c_str(),
893 break;
894
895 case SHAPE_T::POLY:
896 if( aShape->IsPolyShapeValid() )
897 {
898 const SHAPE_POLY_SET& poly = aShape->GetPolyShape();
899 const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 );
900
901 m_out->Print( aNestLevel, "(gr_poly%s\n", locked.c_str() );
902 formatPolyPts( outline, aNestLevel, ADVANCED_CFG::GetCfg().m_CompactSave );
903 }
904 else
905 {
906 wxFAIL_MSG( wxT( "Cannot format invalid polygon." ) );
907 return;
908 }
909
910 break;
911
912 case SHAPE_T::BEZIER:
913 m_out->Print( aNestLevel, "(gr_curve%s (pts (xy %s) (xy %s) (xy %s) (xy %s))",
914 locked.c_str(),
919 break;
920
921 default:
923 return;
924 };
925
926 m_out->Print( 0, "\n" );
927
928 aShape->GetStroke().Format( m_out, pcbIUScale, aNestLevel + 1 );
929
930 // The filled flag represents if a solid fill is present on circles, rectangles and polygons
931 if( ( aShape->GetShape() == SHAPE_T::POLY )
932 || ( aShape->GetShape() == SHAPE_T::RECT )
933 || ( aShape->GetShape() == SHAPE_T::CIRCLE ) )
934 {
935 if( aShape->IsFilled() )
936 m_out->Print( 0, " (fill solid)" );
937 else
938 m_out->Print( 0, " (fill none)" );
939 }
940
941 formatLayer( aShape->GetLayer() );
942
943 m_out->Print( 0, " (tstamp %s)", TO_UTF8( aShape->m_Uuid.AsString() ) );
944
945 m_out->Print( 0, ")\n" );
946}
947
948
949void PCB_PLUGIN::format( const PCB_BITMAP* aBitmap, int aNestLevel ) const
950{
951 wxCHECK_RET( aBitmap != nullptr && m_out != nullptr, "" );
952
953 const wxImage* image = aBitmap->GetImage()->GetImageData();
954
955 wxCHECK_RET( image != nullptr, "wxImage* is NULL" );
956
957 m_out->Print( aNestLevel, "(image (at %s %s)",
960
961 formatLayer( aBitmap->GetLayer() );
962
963 if( aBitmap->GetImage()->GetScale() != 1.0 )
964 m_out->Print( 0, " (scale %g)", aBitmap->GetImage()->GetScale() );
965
966 m_out->Print( 0, "\n" );
967
968 m_out->Print( aNestLevel + 1, "(data" );
969
970 wxMemoryOutputStream stream;
971
972 image->SaveFile( stream, wxBITMAP_TYPE_PNG );
973
974 // Write binary data in hexadecimal form (ASCII)
975 wxStreamBuffer* buffer = stream.GetOutputStreamBuffer();
976 wxString out = wxBase64Encode( buffer->GetBufferStart(), buffer->GetBufferSize() );
977
978 // Apparently the MIME standard character width for base64 encoding is 76 (unconfirmed)
979 // so use it in a vein attempt to be standard like.
980#define MIME_BASE64_LENGTH 76
981
982 size_t first = 0;
983
984 while( first < out.Length() )
985 {
986 m_out->Print( 0, "\n" );
987 m_out->Print( aNestLevel + 2, "%s", TO_UTF8( out( first, MIME_BASE64_LENGTH ) ) );
988 first += MIME_BASE64_LENGTH;
989 }
990
991 m_out->Print( 0, "\n" );
992 m_out->Print( aNestLevel + 1, ")\n" ); // Closes data token.
993 m_out->Print( aNestLevel, ")\n" ); // Closes image token.
994}
995
996
997void PCB_PLUGIN::format( const FP_SHAPE* aFPShape, int aNestLevel ) const
998{
999 std::string locked = aFPShape->IsLocked() ? " locked" : "";
1000
1001 switch( aFPShape->GetShape() )
1002 {
1003 case SHAPE_T::SEGMENT:
1004 m_out->Print( aNestLevel, "(fp_line%s (start %s) (end %s)",
1005 locked.c_str(),
1007 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, aFPShape->GetEnd0() ).c_str() );
1008 break;
1009
1010 case SHAPE_T::RECT:
1011 m_out->Print( aNestLevel, "(fp_rect%s (start %s) (end %s)",
1012 locked.c_str(),
1014 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, aFPShape->GetEnd0() ).c_str() );
1015 break;
1016
1017 case SHAPE_T::CIRCLE:
1018 m_out->Print( aNestLevel, "(fp_circle%s (center %s) (end %s)",
1019 locked.c_str(),
1021 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, aFPShape->GetEnd0() ).c_str() );
1022 break;
1023
1024 case SHAPE_T::ARC:
1025 m_out->Print( aNestLevel, "(fp_arc%s (start %s) (mid %s) (end %s)",
1026 locked.c_str(),
1029 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, aFPShape->GetEnd0() ).c_str() );
1030 break;
1031
1032 case SHAPE_T::POLY:
1033 if( aFPShape->IsPolyShapeValid() )
1034 {
1035 const SHAPE_POLY_SET& poly = aFPShape->GetPolyShape();
1036 const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 );
1037
1038 m_out->Print( aNestLevel, "(fp_poly%s\n", locked.c_str() );
1039 formatPolyPts( outline, aNestLevel, ADVANCED_CFG::GetCfg().m_CompactSave );
1040 }
1041 else
1042 {
1043 wxFAIL_MSG( wxT( "Cannot format invalid polygon." ) );
1044 return;
1045 }
1046 break;
1047
1048 case SHAPE_T::BEZIER:
1049 m_out->Print( aNestLevel, "(fp_curve%s (pts (xy %s) (xy %s) (xy %s) (xy %s))",
1050 locked.c_str(),
1054 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, aFPShape->GetEnd0() ).c_str() );
1055 break;
1056
1057 default:
1058 wxFAIL_MSG( wxT( "PCB_PLUGIN::format not implemented for " ) + aFPShape->SHAPE_T_asString() );
1059 return;
1060 };
1061
1062 m_out->Print( 0, "\n" );
1063
1064 aFPShape->GetStroke().Format( m_out, pcbIUScale, aNestLevel + 1 );
1065
1066 // The filled flag represents if a solid fill is present on circles, rectangles and polygons
1067 if( ( aFPShape->GetShape() == SHAPE_T::POLY )
1068 || ( aFPShape->GetShape() == SHAPE_T::RECT )
1069 || ( aFPShape->GetShape() == SHAPE_T::CIRCLE ) )
1070 {
1071 if( aFPShape->IsFilled() )
1072 m_out->Print( 0, " (fill solid)" );
1073 else
1074 m_out->Print( 0, " (fill none)" );
1075 }
1076
1077 formatLayer( aFPShape->GetLayer() );
1078
1079 m_out->Print( 0, " (tstamp %s)", TO_UTF8( aFPShape->m_Uuid.AsString() ) );
1080
1081 m_out->Print( 0, ")\n" );
1082}
1083
1084
1085void PCB_PLUGIN::format( const PCB_TARGET* aTarget, int aNestLevel ) const
1086{
1087 m_out->Print( aNestLevel, "(target %s (at %s) (size %s)",
1088 ( aTarget->GetShape() ) ? "x" : "plus",
1091
1092 if( aTarget->GetWidth() != 0 )
1093 m_out->Print( 0, " (width %s)", EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, aTarget->GetWidth() ).c_str() );
1094
1095 formatLayer( aTarget->GetLayer() );
1096
1097 m_out->Print( 0, " (tstamp %s)", TO_UTF8( aTarget->m_Uuid.AsString() ) );
1098
1099 m_out->Print( 0, ")\n" );
1100}
1101
1102
1103void PCB_PLUGIN::format( const FOOTPRINT* aFootprint, int aNestLevel ) const
1104{
1105 if( !( m_ctl & CTL_OMIT_INITIAL_COMMENTS ) )
1106 {
1107 const wxArrayString* initial_comments = aFootprint->GetInitialComments();
1108
1109 if( initial_comments )
1110 {
1111 for( unsigned i = 0; i < initial_comments->GetCount(); ++i )
1112 m_out->Print( aNestLevel, "%s\n", TO_UTF8( (*initial_comments)[i] ) );
1113
1114 m_out->Print( 0, "\n" ); // improve readability?
1115 }
1116 }
1117
1118 if( m_ctl & CTL_OMIT_LIBNAME )
1119 {
1120 m_out->Print( aNestLevel, "(footprint %s",
1121 m_out->Quotes( aFootprint->GetFPID().GetLibItemName() ).c_str() );
1122 }
1123 else
1124 {
1125 m_out->Print( aNestLevel, "(footprint %s",
1126 m_out->Quotes( aFootprint->GetFPID().Format() ).c_str() );
1127 }
1128
1130 m_out->Print( 0, " (version %d) (generator pcbnew)\n ", SEXPR_BOARD_FILE_VERSION );
1131
1132 if( aFootprint->IsLocked() )
1133 m_out->Print( 0, " locked" );
1134
1135 if( aFootprint->IsPlaced() )
1136 m_out->Print( 0, " placed" );
1137
1138 formatLayer( aFootprint->GetLayer() );
1139
1140 m_out->Print( 0, "\n" );
1141
1142 if( !( m_ctl & CTL_OMIT_TSTAMPS ) )
1143 m_out->Print( aNestLevel+1, "(tstamp %s)\n", TO_UTF8( aFootprint->m_Uuid.AsString() ) );
1144
1145 if( !( m_ctl & CTL_OMIT_AT ) )
1146 {
1147 m_out->Print( aNestLevel+1, "(at %s",
1149
1150 if( !aFootprint->GetOrientation().IsZero() )
1151 m_out->Print( 0, " %s", EDA_UNIT_UTILS::FormatAngle( aFootprint->GetOrientation() ).c_str() );
1152
1153 m_out->Print( 0, ")\n" );
1154 }
1155
1156 if( !aFootprint->GetDescription().IsEmpty() )
1157 {
1158 m_out->Print( aNestLevel+1, "(descr %s)\n",
1159 m_out->Quotew( aFootprint->GetDescription() ).c_str() );
1160 }
1161
1162 if( !aFootprint->GetKeywords().IsEmpty() )
1163 {
1164 m_out->Print( aNestLevel+1, "(tags %s)\n",
1165 m_out->Quotew( aFootprint->GetKeywords() ).c_str() );
1166 }
1167
1168 const std::map<wxString, wxString>& props = aFootprint->GetProperties();
1169
1170 for( const std::pair<const wxString, wxString>& prop : props )
1171 {
1172 m_out->Print( aNestLevel+1, "(property %s %s)\n",
1173 m_out->Quotew( prop.first ).c_str(),
1174 m_out->Quotew( prop.second ).c_str() );
1175 }
1176
1177 if( !( m_ctl & CTL_OMIT_PATH ) && !aFootprint->GetPath().empty() )
1178 {
1179 m_out->Print( aNestLevel+1, "(path %s)\n",
1180 m_out->Quotew( aFootprint->GetPath().AsString() ).c_str() );
1181 }
1182
1183 if( aFootprint->GetLocalSolderMaskMargin() != 0 )
1184 {
1185 m_out->Print( aNestLevel+1, "(solder_mask_margin %s)\n",
1187 }
1188
1189 if( aFootprint->GetLocalSolderPasteMargin() != 0 )
1190 {
1191 m_out->Print( aNestLevel+1, "(solder_paste_margin %s)\n",
1193 }
1194
1195 if( aFootprint->GetLocalSolderPasteMarginRatio() != 0 )
1196 {
1197 m_out->Print( aNestLevel+1, "(solder_paste_ratio %s)\n",
1198 FormatDouble2Str( aFootprint->GetLocalSolderPasteMarginRatio() ).c_str() );
1199 }
1200
1201 if( aFootprint->GetLocalClearance() != 0 )
1202 {
1203 m_out->Print( aNestLevel+1, "(clearance %s)\n",
1205 }
1206
1207 if( aFootprint->GetZoneConnection() != ZONE_CONNECTION::INHERITED )
1208 {
1209 m_out->Print( aNestLevel+1, "(zone_connect %d)\n",
1210 static_cast<int>( aFootprint->GetZoneConnection() ) );
1211 }
1212
1213 // Attributes
1214 if( aFootprint->GetAttributes() )
1215 {
1216 m_out->Print( aNestLevel+1, "(attr" );
1217
1218 if( aFootprint->GetAttributes() & FP_SMD )
1219 m_out->Print( 0, " smd" );
1220
1221 if( aFootprint->GetAttributes() & FP_THROUGH_HOLE )
1222 m_out->Print( 0, " through_hole" );
1223
1224 if( aFootprint->GetAttributes() & FP_BOARD_ONLY )
1225 m_out->Print( 0, " board_only" );
1226
1227 if( aFootprint->GetAttributes() & FP_EXCLUDE_FROM_POS_FILES )
1228 m_out->Print( 0, " exclude_from_pos_files" );
1229
1230 if( aFootprint->GetAttributes() & FP_EXCLUDE_FROM_BOM )
1231 m_out->Print( 0, " exclude_from_bom" );
1232
1233 if( aFootprint->GetAttributes() & FP_ALLOW_MISSING_COURTYARD )
1234 m_out->Print( 0, " allow_missing_courtyard" );
1235
1236 if( aFootprint->GetAttributes() & FP_ALLOW_SOLDERMASK_BRIDGES )
1237 m_out->Print( 0, " allow_soldermask_bridges" );
1238
1239 m_out->Print( 0, ")\n" );
1240 }
1241
1242 if( aFootprint->GetPrivateLayers().any() )
1243 {
1244 m_out->Print( aNestLevel+1, "(private_layers" );
1245
1246 for( PCB_LAYER_ID layer : aFootprint->GetPrivateLayers().Seq() )
1247 {
1248 wxString canonicalName( LSET::Name( layer ) );
1249 m_out->Print( 0, " \"%s\"", canonicalName.ToStdString().c_str() );
1250 }
1251
1252 m_out->Print( 0, ")\n" );
1253 }
1254
1255 if( aFootprint->IsNetTie() )
1256 {
1257 m_out->Print( aNestLevel+1, "(net_tie_pad_groups" );
1258
1259 for( const wxString& group : aFootprint->GetNetTiePadGroups() )
1260 {
1261 m_out->Print( 0, " \"%s\"",
1262 EscapeString( group, CTX_QUOTED_STR ).ToStdString().c_str() );
1263 }
1264
1265 m_out->Print( 0, ")\n" );
1266 }
1267
1268 Format( (BOARD_ITEM*) &aFootprint->Reference(), aNestLevel + 1 );
1269 Format( (BOARD_ITEM*) &aFootprint->Value(), aNestLevel + 1 );
1270
1271 std::set<PAD*, FOOTPRINT::cmp_pads> sorted_pads( aFootprint->Pads().begin(),
1272 aFootprint->Pads().end() );
1273 std::set<BOARD_ITEM*, FOOTPRINT::cmp_drawings> sorted_drawings(
1274 aFootprint->GraphicalItems().begin(),
1275 aFootprint->GraphicalItems().end() );
1276 std::set<FP_ZONE*, FOOTPRINT::cmp_zones> sorted_zones( aFootprint->Zones().begin(),
1277 aFootprint->Zones().end() );
1278 std::set<BOARD_ITEM*, PCB_GROUP::ptr_cmp> sorted_groups( aFootprint->Groups().begin(),
1279 aFootprint->Groups().end() );
1280
1281 // Save drawing elements.
1282
1283 for( BOARD_ITEM* gr : sorted_drawings )
1284 Format( gr, aNestLevel+1 );
1285
1286 // Save pads.
1287 for( PAD* pad : sorted_pads )
1288 Format( pad, aNestLevel+1 );
1289
1290 // Save zones.
1291 for( BOARD_ITEM* zone : sorted_zones )
1292 Format( zone, aNestLevel + 1 );
1293
1294 // Save groups.
1295 for( BOARD_ITEM* group : sorted_groups )
1296 Format( group, aNestLevel + 1 );
1297
1298 // Save 3D info.
1299 auto bs3D = aFootprint->Models().begin();
1300 auto es3D = aFootprint->Models().end();
1301
1302 while( bs3D != es3D )
1303 {
1304 if( !bs3D->m_Filename.IsEmpty() )
1305 {
1306 m_out->Print( aNestLevel+1, "(model %s%s\n",
1307 m_out->Quotew( bs3D->m_Filename ).c_str(),
1308 bs3D->m_Show ? "" : " hide" );
1309
1310 if( bs3D->m_Opacity != 1.0 )
1311 m_out->Print( aNestLevel+2, "(opacity %0.4f)", bs3D->m_Opacity );
1312
1313 m_out->Print( aNestLevel+2, "(offset (xyz %s %s %s))\n",
1314 FormatDouble2Str( bs3D->m_Offset.x ).c_str(),
1315 FormatDouble2Str( bs3D->m_Offset.y ).c_str(),
1316 FormatDouble2Str( bs3D->m_Offset.z ).c_str() );
1317
1318 m_out->Print( aNestLevel+2, "(scale (xyz %s %s %s))\n",
1319 FormatDouble2Str( bs3D->m_Scale.x ).c_str(),
1320 FormatDouble2Str( bs3D->m_Scale.y ).c_str(),
1321 FormatDouble2Str( bs3D->m_Scale.z ).c_str() );
1322
1323 m_out->Print( aNestLevel+2, "(rotate (xyz %s %s %s))\n",
1324 FormatDouble2Str( bs3D->m_Rotation.x ).c_str(),
1325 FormatDouble2Str( bs3D->m_Rotation.y ).c_str(),
1326 FormatDouble2Str( bs3D->m_Rotation.z ).c_str() );
1327
1328 m_out->Print( aNestLevel+1, ")\n" );
1329 }
1330
1331 ++bs3D;
1332 }
1333
1334 m_out->Print( aNestLevel, ")\n" );
1335}
1336
1337
1338void PCB_PLUGIN::formatLayers( LSET aLayerMask, int aNestLevel ) const
1339{
1340 std::string output;
1341
1342 if( aNestLevel == 0 )
1343 output += ' ';
1344
1345 output += "(layers";
1346
1347 static const LSET cu_all( LSET::AllCuMask() );
1348 static const LSET fr_bk( 2, B_Cu, F_Cu );
1349 static const LSET adhes( 2, B_Adhes, F_Adhes );
1350 static const LSET paste( 2, B_Paste, F_Paste );
1351 static const LSET silks( 2, B_SilkS, F_SilkS );
1352 static const LSET mask( 2, B_Mask, F_Mask );
1353 static const LSET crt_yd( 2, B_CrtYd, F_CrtYd );
1354 static const LSET fab( 2, B_Fab, F_Fab );
1355
1356 LSET cu_mask = cu_all;
1357
1358 // output copper layers first, then non copper
1359
1360 if( ( aLayerMask & cu_mask ) == cu_mask )
1361 {
1362 output += ' ' + m_out->Quotew( "*.Cu" );
1363 aLayerMask &= ~cu_all; // clear bits, so they are not output again below
1364 }
1365 else if( ( aLayerMask & cu_mask ) == fr_bk )
1366 {
1367 output += ' ' + m_out->Quotew( "F&B.Cu" );
1368 aLayerMask &= ~fr_bk;
1369 }
1370
1371 if( ( aLayerMask & adhes ) == adhes )
1372 {
1373 output += ' ' + m_out->Quotew( "*.Adhes" );
1374 aLayerMask &= ~adhes;
1375 }
1376
1377 if( ( aLayerMask & paste ) == paste )
1378 {
1379 output += ' ' + m_out->Quotew( "*.Paste" );
1380 aLayerMask &= ~paste;
1381 }
1382
1383 if( ( aLayerMask & silks ) == silks )
1384 {
1385 output += ' ' + m_out->Quotew( "*.SilkS" );
1386 aLayerMask &= ~silks;
1387 }
1388
1389 if( ( aLayerMask & mask ) == mask )
1390 {
1391 output += ' ' + m_out->Quotew( "*.Mask" );
1392 aLayerMask &= ~mask;
1393 }
1394
1395 if( ( aLayerMask & crt_yd ) == crt_yd )
1396 {
1397 output += ' ' + m_out->Quotew( "*.CrtYd" );
1398 aLayerMask &= ~crt_yd;
1399 }
1400
1401 if( ( aLayerMask & fab ) == fab )
1402 {
1403 output += ' ' + m_out->Quotew( "*.Fab" );
1404 aLayerMask &= ~fab;
1405 }
1406
1407 // output any individual layers not handled in wildcard combos above
1408 wxString layerName;
1409
1410 for( int layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
1411 {
1412 if( aLayerMask[layer] )
1413 {
1414 layerName = LSET::Name( PCB_LAYER_ID( layer ) );
1415 output += ' ';
1416 output += m_out->Quotew( layerName );
1417 }
1418 }
1419
1420 m_out->Print( aNestLevel, "%s)", output.c_str() );
1421}
1422
1423
1424void PCB_PLUGIN::format( const PAD* aPad, int aNestLevel ) const
1425{
1426 const BOARD* board = aPad->GetBoard();
1427 const char* shape;
1428
1429 switch( aPad->GetShape() )
1430 {
1431 case PAD_SHAPE::CIRCLE: shape = "circle"; break;
1432 case PAD_SHAPE::RECT: shape = "rect"; break;
1433 case PAD_SHAPE::OVAL: shape = "oval"; break;
1434 case PAD_SHAPE::TRAPEZOID: shape = "trapezoid"; break;
1436 case PAD_SHAPE::ROUNDRECT: shape = "roundrect"; break;
1437 case PAD_SHAPE::CUSTOM: shape = "custom"; break;
1438
1439 default:
1440 THROW_IO_ERROR( wxString::Format( _( "unknown pad type: %d"), aPad->GetShape() ) );
1441 }
1442
1443 const char* type;
1444
1445 switch( aPad->GetAttribute() )
1446 {
1447 case PAD_ATTRIB::PTH: type = "thru_hole"; break;
1448 case PAD_ATTRIB::SMD: type = "smd"; break;
1449 case PAD_ATTRIB::CONN: type = "connect"; break;
1450 case PAD_ATTRIB::NPTH: type = "np_thru_hole"; break;
1451
1452 default:
1453 THROW_IO_ERROR( wxString::Format( wxT( "unknown pad attribute: %d" ),
1454 aPad->GetAttribute() ) );
1455 }
1456
1457 const char* property = nullptr;
1458
1459 switch( aPad->GetProperty() )
1460 {
1461 case PAD_PROP::NONE: break; // could be "none"
1462 case PAD_PROP::BGA: property = "pad_prop_bga"; break;
1463 case PAD_PROP::FIDUCIAL_GLBL: property = "pad_prop_fiducial_glob"; break;
1464 case PAD_PROP::FIDUCIAL_LOCAL: property = "pad_prop_fiducial_loc"; break;
1465 case PAD_PROP::TESTPOINT: property = "pad_prop_testpoint"; break;
1466 case PAD_PROP::HEATSINK: property = "pad_prop_heatsink"; break;
1467 case PAD_PROP::CASTELLATED: property = "pad_prop_castellated"; break;
1468
1469 default:
1470 THROW_IO_ERROR( wxString::Format( wxT( "unknown pad property: %d" ),
1471 aPad->GetProperty() ) );
1472 }
1473
1474 m_out->Print( aNestLevel, "(pad %s %s %s",
1475 m_out->Quotew( aPad->GetNumber() ).c_str(),
1476 type,
1477 shape );
1478
1479 if( aPad->IsLocked() )
1480 m_out->Print( 0, " locked" );
1481
1482 m_out->Print( 0, " (at %s", EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, aPad->GetPos0() ).c_str() );
1483
1484 if( !aPad->GetOrientation().IsZero() )
1485 m_out->Print( 0, " %s", EDA_UNIT_UTILS::FormatAngle( aPad->GetOrientation() ).c_str() );
1486
1487 m_out->Print( 0, ")" );
1488
1489 m_out->Print( 0, " (size %s)", EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, aPad->GetSize() ).c_str() );
1490
1491 if( (aPad->GetDelta().x) != 0 || (aPad->GetDelta().y != 0 ) )
1492 m_out->Print( 0, " (rect_delta %s)", EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, aPad->GetDelta() ).c_str() );
1493
1494 VECTOR2I sz = aPad->GetDrillSize();
1495 VECTOR2I shapeoffset = aPad->GetOffset();
1496
1497 if( (sz.x > 0) || (sz.y > 0) ||
1498 (shapeoffset.x != 0) || (shapeoffset.y != 0) )
1499 {
1500 m_out->Print( 0, " (drill" );
1501
1502 if( aPad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG )
1503 m_out->Print( 0, " oval" );
1504
1505 if( sz.x > 0 )
1506 m_out->Print( 0, " %s", EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, sz.x ).c_str() );
1507
1508 if( sz.y > 0 && sz.x != sz.y )
1509 m_out->Print( 0, " %s", EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, sz.y ).c_str() );
1510
1511 if( (shapeoffset.x != 0) || (shapeoffset.y != 0) )
1512 m_out->Print( 0, " (offset %s)", EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, aPad->GetOffset() ).c_str() );
1513
1514 m_out->Print( 0, ")" );
1515 }
1516
1517 // Add pad property, if exists.
1518 if( property )
1519 m_out->Print( 0, " (property %s)", property );
1520
1521 formatLayers( aPad->GetLayerSet() );
1522
1523 if( aPad->GetAttribute() == PAD_ATTRIB::PTH )
1524 {
1525 if( aPad->GetRemoveUnconnected() )
1526 {
1527 m_out->Print( 0, " (remove_unused_layers)" );
1528
1529 if( aPad->GetKeepTopBottom() )
1530 m_out->Print( 0, " (keep_end_layers)" );
1531
1532 if( board ) // Will be nullptr in footprint library
1533 {
1534 m_out->Print( 0, " (zone_layer_connections" );
1535
1536 for( LSEQ cu = board->GetEnabledLayers().CuStack(); cu; ++cu )
1537 {
1538 if( aPad->GetZoneLayerOverride( *cu ) == ZLO_FORCE_FLASHED )
1539 m_out->Print( 0, " %s", m_out->Quotew( LSET::Name( *cu ) ).c_str() );
1540 }
1541
1542 m_out->Print( 0, ")" );
1543 }
1544 }
1545 }
1546
1547 // Output the radius ratio for rounded and chamfered rect pads
1549 {
1550 m_out->Print( 0, " (roundrect_rratio %s)",
1551 FormatDouble2Str( aPad->GetRoundRectRadiusRatio() ).c_str() );
1552 }
1553
1554 // Output the chamfer corners for chamfered rect pads
1555 if( aPad->GetShape() == PAD_SHAPE::CHAMFERED_RECT)
1556 {
1557 m_out->Print( 0, "\n" );
1558
1559 m_out->Print( aNestLevel+1, "(chamfer_ratio %s)",
1560 FormatDouble2Str( aPad->GetChamferRectRatio() ).c_str() );
1561
1562 m_out->Print( 0, " (chamfer" );
1563
1564 if( ( aPad->GetChamferPositions() & RECT_CHAMFER_TOP_LEFT ) )
1565 m_out->Print( 0, " top_left" );
1566
1568 m_out->Print( 0, " top_right" );
1569
1571 m_out->Print( 0, " bottom_left" );
1572
1574 m_out->Print( 0, " bottom_right" );
1575
1576 m_out->Print( 0, ")" );
1577 }
1578
1579 std::string output;
1580
1581 // Unconnected pad is default net so don't save it.
1583 {
1584 StrPrintf( &output, " (net %d %s)", m_mapping->Translate( aPad->GetNetCode() ),
1585 m_out->Quotew( aPad->GetNetname() ).c_str() );
1586 }
1587
1588 // Pin functions and types are closely related to nets, so if CTL_OMIT_NETS is set, omit
1589 // them as well (for instance when saved from library editor).
1590 if( !( m_ctl & CTL_OMIT_PAD_NETS ) )
1591 {
1592 if( !aPad->GetPinFunction().IsEmpty() )
1593 {
1594 StrPrintf( &output, " (pinfunction %s)",
1595 m_out->Quotew( aPad->GetPinFunction() ).c_str() );
1596 }
1597
1598 if( !aPad->GetPinType().IsEmpty() )
1599 {
1600 StrPrintf( &output, " (pintype %s)",
1601 m_out->Quotew( aPad->GetPinType() ).c_str() );
1602 }
1603 }
1604
1605 if( aPad->GetPadToDieLength() != 0 )
1606 {
1607 StrPrintf( &output, " (die_length %s)",
1609 }
1610
1611 if( aPad->GetLocalSolderMaskMargin() != 0 )
1612 {
1613 StrPrintf( &output, " (solder_mask_margin %s)",
1615 }
1616
1617 if( aPad->GetLocalSolderPasteMargin() != 0 )
1618 {
1619 StrPrintf( &output, " (solder_paste_margin %s)",
1621 }
1622
1623 if( aPad->GetLocalSolderPasteMarginRatio() != 0 )
1624 {
1625 StrPrintf( &output, " (solder_paste_margin_ratio %s)",
1627 }
1628
1629 if( aPad->GetLocalClearance() != 0 )
1630 {
1631 StrPrintf( &output, " (clearance %s)",
1633 }
1634
1636 {
1637 StrPrintf( &output, " (zone_connect %d)",
1638 static_cast<int>( aPad->GetZoneConnection() ) );
1639 }
1640
1641 if( aPad->GetThermalSpokeWidth() != 0 )
1642 {
1643 StrPrintf( &output, " (thermal_bridge_width %s)",
1645 }
1646
1647 if( ( aPad->GetShape() == PAD_SHAPE::CIRCLE && aPad->GetThermalSpokeAngle() != ANGLE_45 )
1648 || ( aPad->GetShape() != PAD_SHAPE::CIRCLE && aPad->GetThermalSpokeAngle() != ANGLE_90 ) )
1649 {
1650 StrPrintf( &output, " (thermal_bridge_angle %s)",
1652 }
1653
1654 if( aPad->GetThermalGap() != 0 )
1655 {
1656 StrPrintf( &output, " (thermal_gap %s)",
1658 }
1659
1660 if( output.size() )
1661 {
1662 m_out->Print( 0, "\n" );
1663 m_out->Print( aNestLevel+1, "%s", output.c_str()+1 ); // +1 skips 1st space on 1st element
1664 }
1665
1666 if( aPad->GetShape() == PAD_SHAPE::CUSTOM )
1667 {
1668 m_out->Print( 0, "\n");
1669 m_out->Print( aNestLevel+1, "(options" );
1670
1672 m_out->Print( 0, " (clearance convexhull)" );
1673 #if 1 // Set to 1 to output the default option
1674 else
1675 m_out->Print( 0, " (clearance outline)" );
1676 #endif
1677
1678 // Output the anchor pad shape (circle/rect)
1679 if( aPad->GetAnchorPadShape() == PAD_SHAPE::RECT )
1680 shape = "rect";
1681 else
1682 shape = "circle";
1683
1684 m_out->Print( 0, " (anchor %s)", shape );
1685
1686 m_out->Print( 0, ")"); // end of (options ...
1687
1688 // Output graphic primitive of the pad shape
1689 m_out->Print( 0, "\n");
1690 m_out->Print( aNestLevel+1, "(primitives" );
1691
1692 int nested_level = aNestLevel+2;
1693
1694 // Output all basic shapes
1695 for( const std::shared_ptr<PCB_SHAPE>& primitive : aPad->GetPrimitives() )
1696 {
1697 m_out->Print( 0, "\n");
1698
1699 switch( primitive->GetShape() )
1700 {
1701 case SHAPE_T::SEGMENT:
1702 m_out->Print( nested_level, "(gr_line (start %s) (end %s)",
1703 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, primitive->GetStart() ).c_str(),
1704 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, primitive->GetEnd() ).c_str() );
1705 break;
1706
1707 case SHAPE_T::RECT:
1708 if( primitive->IsAnnotationProxy() )
1709 {
1710 m_out->Print( nested_level, "(gr_bbox (start %s) (end %s)",
1711 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, primitive->GetStart() ).c_str(),
1712 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, primitive->GetEnd() ).c_str() );
1713 }
1714 else
1715 {
1716 m_out->Print( nested_level, "(gr_rect (start %s) (end %s)",
1717 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, primitive->GetStart() ).c_str(),
1718 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, primitive->GetEnd() ).c_str() );
1719 }
1720 break;
1721
1722 case SHAPE_T::ARC:
1723 m_out->Print( nested_level, "(gr_arc (start %s) (mid %s) (end %s)",
1724 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, primitive->GetStart() ).c_str(),
1725 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, primitive->GetArcMid() ).c_str(),
1726 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, primitive->GetEnd() ).c_str() );
1727 break;
1728
1729 case SHAPE_T::CIRCLE:
1730 m_out->Print( nested_level, "(gr_circle (center %s) (end %s)",
1731 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, primitive->GetStart() ).c_str(),
1732 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, primitive->GetEnd() ).c_str() );
1733 break;
1734
1735 case SHAPE_T::BEZIER:
1736 m_out->Print( nested_level, "(gr_curve (pts (xy %s) (xy %s) (xy %s) (xy %s))",
1737 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, primitive->GetStart() ).c_str(),
1738 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, primitive->GetBezierC1() ).c_str(),
1739 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, primitive->GetBezierC2() ).c_str(),
1740 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, primitive->GetEnd() ).c_str() );
1741 break;
1742
1743 case SHAPE_T::POLY:
1744 if( primitive->IsPolyShapeValid() )
1745 {
1746 const SHAPE_POLY_SET& poly = primitive->GetPolyShape();
1747 const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 );
1748
1749 m_out->Print( nested_level, "(gr_poly\n" );
1750 formatPolyPts( outline, nested_level, ADVANCED_CFG::GetCfg().m_CompactSave );
1751 m_out->Print( nested_level, " " ); // just to align the next info at the right place
1752 }
1753 break;
1754
1755 default:
1756 break;
1757 }
1758
1759 m_out->Print( 0, " (width %s)",
1760 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, primitive->GetWidth() ).c_str() );
1761
1762 // The filled flag represents if a solid fill is present on circles,
1763 // rectangles and polygons
1764 if( ( primitive->GetShape() == SHAPE_T::POLY )
1765 || ( primitive->GetShape() == SHAPE_T::RECT )
1766 || ( primitive->GetShape() == SHAPE_T::CIRCLE ) )
1767 {
1768 if( primitive->IsFilled() )
1769 m_out->Print( 0, " (fill yes)" );
1770 else
1771 m_out->Print( 0, " (fill none)" );
1772 }
1773
1774 m_out->Print( 0, ")" );
1775 }
1776
1777 m_out->Print( 0, "\n");
1778 m_out->Print( aNestLevel+1, ")" ); // end of (basic_shapes
1779 }
1780
1781 m_out->Print( 0, " (tstamp %s)", TO_UTF8( aPad->m_Uuid.AsString() ) );
1782
1783 m_out->Print( 0, ")\n" );
1784}
1785
1786
1787void PCB_PLUGIN::format( const PCB_TEXT* aText, int aNestLevel ) const
1788{
1789 m_out->Print( aNestLevel, "(gr_text" );
1790
1791 if( aText->IsLocked() )
1792 m_out->Print( 0, " locked" );
1793
1794 m_out->Print( 0, " %s (at %s",
1795 m_out->Quotew( aText->GetText() ).c_str(),
1797
1798
1799 if( !aText->GetTextAngle().IsZero() )
1800 m_out->Print( 0, " %s", EDA_UNIT_UTILS::FormatAngle( aText->GetTextAngle() ).c_str() );
1801
1802 m_out->Print( 0, ")" );
1803
1804 formatLayer( aText->GetLayer(), aText->IsKnockout() );
1805
1806 m_out->Print( 0, " (tstamp %s)", TO_UTF8( aText->m_Uuid.AsString() ) );
1807
1808 m_out->Print( 0, "\n" );
1809
1810 // PCB_TEXTS are never hidden, so always omit "hide" attribute
1811 aText->EDA_TEXT::Format( m_out, aNestLevel, m_ctl | CTL_OMIT_HIDE );
1812
1813 if( aText->GetFont() && aText->GetFont()->IsOutline() )
1814 formatRenderCache( aText, aNestLevel + 1 );
1815
1816 m_out->Print( aNestLevel, ")\n" );
1817}
1818
1819
1820void PCB_PLUGIN::format( const PCB_TEXTBOX* aTextBox, int aNestLevel ) const
1821{
1822 std::string locked = aTextBox->IsLocked() ? " locked" : "";
1823
1824 m_out->Print( aNestLevel, "(gr_text_box%s %s\n",
1825 locked.c_str(),
1826 m_out->Quotew( aTextBox->GetText() ).c_str() );
1827
1828 if( aTextBox->GetShape() == SHAPE_T::RECT )
1829 {
1830 m_out->Print( aNestLevel + 1, "(start %s) (end %s)",
1832 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, aTextBox->GetEnd() ).c_str() );
1833 }
1834 else if( aTextBox->GetShape() == SHAPE_T::POLY )
1835 {
1836 const SHAPE_POLY_SET& poly = aTextBox->GetPolyShape();
1837 const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 );
1838
1839 formatPolyPts( outline, aNestLevel + 1, true );
1840 }
1841 else
1842 {
1843 UNIMPLEMENTED_FOR( aTextBox->SHAPE_T_asString() );
1844 }
1845
1846 if( !aTextBox->GetTextAngle().IsZero() )
1847 m_out->Print( 0, " (angle %s)", EDA_UNIT_UTILS::FormatAngle( aTextBox->GetTextAngle() ).c_str() );
1848
1849 formatLayer( aTextBox->GetLayer() );
1850
1851 m_out->Print( 0, " (tstamp %s)", TO_UTF8( aTextBox->m_Uuid.AsString() ) );
1852
1853 m_out->Print( 0, "\n" );
1854
1855 // PCB_TEXTBOXes are never hidden, so always omit "hide" attribute
1856 aTextBox->EDA_TEXT::Format( m_out, aNestLevel + 1, m_ctl | CTL_OMIT_HIDE );
1857
1858 if( aTextBox->GetStroke().GetWidth() > 0 )
1859 aTextBox->GetStroke().Format( m_out, pcbIUScale, aNestLevel + 1 );
1860
1861 if( aTextBox->GetFont() && aTextBox->GetFont()->IsOutline() )
1862 formatRenderCache( aTextBox, aNestLevel + 1 );
1863
1864 m_out->Print( aNestLevel, ")\n" );
1865}
1866
1867
1868void PCB_PLUGIN::format( const PCB_GROUP* aGroup, int aNestLevel ) const
1869{
1870 // Don't write empty groups
1871 if( aGroup->GetItems().empty() )
1872 return;
1873
1874 m_out->Print( aNestLevel, "(group %s%s (id %s)\n",
1875 m_out->Quotew( aGroup->GetName() ).c_str(),
1876 aGroup->IsLocked() ? " locked" : "",
1877 TO_UTF8( aGroup->m_Uuid.AsString() ) );
1878
1879 m_out->Print( aNestLevel + 1, "(members\n" );
1880
1881 wxArrayString memberIds;
1882
1883 for( BOARD_ITEM* member : aGroup->GetItems() )
1884 memberIds.Add( member->m_Uuid.AsString() );
1885
1886 memberIds.Sort();
1887
1888 for( const wxString& memberId : memberIds )
1889 m_out->Print( aNestLevel + 2, "%s\n", TO_UTF8( memberId ) );
1890
1891 m_out->Print( aNestLevel + 1, ")\n" ); // Close `members` token.
1892 m_out->Print( aNestLevel, ")\n" ); // Close `group` token.
1893}
1894
1895
1896void PCB_PLUGIN::format( const FP_TEXT* aText, int aNestLevel ) const
1897{
1898 std::string type;
1899
1900 switch( aText->GetType() )
1901 {
1902 case FP_TEXT::TEXT_is_REFERENCE: type = "reference"; break;
1903 case FP_TEXT::TEXT_is_VALUE: type = "value"; break;
1904 case FP_TEXT::TEXT_is_DIVERS: type = "user";
1905 }
1906
1907 std::string locked = aText->IsLocked() ? " locked" : "";
1908
1909 m_out->Print( aNestLevel, "(fp_text %s%s %s (at %s",
1910 type.c_str(),
1911 locked.c_str(),
1912 m_out->Quotew( aText->GetText() ).c_str(),
1914
1915 // Due to Pcbnew history, fp_text angle is saved as an absolute on screen angle,
1916 // but internally the angle is held relative to its parent footprint. parent
1917 // may be NULL when saving a footprint outside a BOARD.
1918 EDA_ANGLE orient = aText->GetTextAngle();
1919 FOOTPRINT* parent = static_cast<FOOTPRINT*>( aText->GetParent() );
1920
1921 if( parent )
1922 {
1923 // GetTextAngle() is always in -360..+360 range because of
1924 // FP_TEXT::SetTextAngle(), but summing that angle with an
1925 // additional board angle could kick sum up >= 360 or <= -360, so to have
1926 // consistent results, normalize again for the BOARD save. A footprint
1927 // save does not use this code path since parent is NULL.
1928#if 0
1929 // This one could be considered reasonable if you like positive angles
1930 // in your board text.
1931 orient = NormalizeAnglePos( orient + parent->GetOrientation() );
1932#else
1933 // Choose compatibility for now, even though this is only a 720 degree clamp
1934 // with two possible values for every angle.
1935 orient = ( orient + parent->GetOrientation() ).Normalize720();
1936#endif
1937 }
1938
1939 if( !orient.IsZero() )
1940 m_out->Print( 0, " %s", EDA_UNIT_UTILS::FormatAngle( orient ).c_str() );
1941
1942 if( !aText->IsKeepUpright() )
1943 m_out->Print( 0, " unlocked" );
1944
1945 m_out->Print( 0, ")" );
1946 formatLayer( aText->GetLayer(), aText->IsKnockout() );
1947
1948 if( !aText->IsVisible() )
1949 m_out->Print( 0, " hide" );
1950
1951 m_out->Print( 0, "\n" );
1952
1953 aText->EDA_TEXT::Format( m_out, aNestLevel + 1, m_ctl | CTL_OMIT_HIDE );
1954
1955 m_out->Print( aNestLevel + 1, "(tstamp %s)\n", TO_UTF8( aText->m_Uuid.AsString() ) );
1956
1957 if( aText->GetFont() && aText->GetFont()->IsOutline() )
1958 formatRenderCache( aText, aNestLevel + 1 );
1959
1960 m_out->Print( aNestLevel, ")\n" );
1961}
1962
1963
1964void PCB_PLUGIN::format( const FP_TEXTBOX* aTextBox, int aNestLevel ) const
1965{
1966 std::string locked = aTextBox->IsLocked() ? " locked" : "";
1967
1968 m_out->Print( aNestLevel, "(fp_text_box%s %s\n",
1969 locked.c_str(),
1970 m_out->Quotew( aTextBox->GetText() ).c_str() );
1971
1972 if( aTextBox->GetShape() == SHAPE_T::RECT )
1973 {
1974 m_out->Print( aNestLevel, "(start %s) (end %s)",
1976 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, aTextBox->GetEnd0() ).c_str() );
1977 }
1978 else if( aTextBox->GetShape() == SHAPE_T::POLY )
1979 {
1980 const SHAPE_POLY_SET& poly = aTextBox->GetPolyShape();
1981 const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 );
1982
1983 formatPolyPts( outline, aNestLevel, true );
1984 }
1985 else
1986 {
1987 UNIMPLEMENTED_FOR( aTextBox->SHAPE_T_asString() );
1988 }
1989
1990 if( !aTextBox->GetTextAngle().IsZero() )
1991 m_out->Print( 0, " (angle %s)", EDA_UNIT_UTILS::FormatAngle( aTextBox->GetTextAngle() ).c_str() );
1992
1993 formatLayer( aTextBox->GetLayer() );
1994
1995 m_out->Print( 0, " (tstamp %s)", TO_UTF8( aTextBox->m_Uuid.AsString() ) );
1996
1997 m_out->Print( 0, "\n" );
1998
1999 // FP_TEXTBOXes are never hidden, so always omit "hide" attribute
2000 aTextBox->EDA_TEXT::Format( m_out, aNestLevel + 1, m_ctl | CTL_OMIT_HIDE );
2001
2002 if( aTextBox->GetStroke().GetWidth() > 0 )
2003 aTextBox->GetStroke().Format( m_out, pcbIUScale, aNestLevel + 1 );
2004
2005 if( aTextBox->GetFont() && aTextBox->GetFont()->IsOutline() )
2006 formatRenderCache( aTextBox, aNestLevel + 1 );
2007
2008 m_out->Print( aNestLevel, ")\n" );
2009}
2010
2011
2012void PCB_PLUGIN::format( const PCB_TRACK* aTrack, int aNestLevel ) const
2013{
2014 if( aTrack->Type() == PCB_VIA_T )
2015 {
2016 PCB_LAYER_ID layer1, layer2;
2017
2018 const PCB_VIA* via = static_cast<const PCB_VIA*>( aTrack );
2019 const BOARD* board = via->GetBoard();
2020
2021 wxCHECK_RET( board != nullptr, wxT( "Via has no parent." ) );
2022
2023 m_out->Print( aNestLevel, "(via" );
2024
2025 via->LayerPair( &layer1, &layer2 );
2026
2027 switch( via->GetViaType() )
2028 {
2029 case VIATYPE::THROUGH: // Default shape not saved.
2030 break;
2031
2033 m_out->Print( 0, " blind" );
2034 break;
2035
2036 case VIATYPE::MICROVIA:
2037 m_out->Print( 0, " micro" );
2038 break;
2039
2040 default:
2041 THROW_IO_ERROR( wxString::Format( _( "unknown via type %d" ), via->GetViaType() ) );
2042 }
2043
2044 if( via->IsLocked() )
2045 m_out->Print( 0, " locked" );
2046
2047 m_out->Print( 0, " (at %s) (size %s)",
2050
2051 // Old boards were using UNDEFINED_DRILL_DIAMETER value in file for via drill when
2052 // via drill was the netclass value.
2053 // recent boards always set the via drill to the actual value, but now we need to
2054 // always store the drill value, because netclass value is not stored in the board file.
2055 // Otherwise the drill value of some (old) vias can be unknown
2056 if( via->GetDrill() != UNDEFINED_DRILL_DIAMETER )
2057 {
2058 m_out->Print( 0, " (drill %s)",
2059 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, via->GetDrill() ).c_str() );
2060 }
2061 else // Probably old board!
2062 {
2063 m_out->Print( 0, " (drill %s)",
2064 EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, via->GetDrillValue() ).c_str() );
2065 }
2066
2067 m_out->Print( 0, " (layers %s %s)",
2068 m_out->Quotew( LSET::Name( layer1 ) ).c_str(),
2069 m_out->Quotew( LSET::Name( layer2 ) ).c_str() );
2070
2071 if( via->GetRemoveUnconnected() )
2072 {
2073 m_out->Print( 0, " (remove_unused_layers)" );
2074
2075 if( via->GetKeepStartEnd() )
2076 m_out->Print( 0, " (keep_end_layers)" );
2077 }
2078
2079 if( via->GetIsFree() )
2080 m_out->Print( 0, " (free)" );
2081
2082 if( via->GetRemoveUnconnected() )
2083 {
2084 m_out->Print( 0, " (zone_layer_connections" );
2085
2086 for( LSEQ cu = board->GetEnabledLayers().CuStack(); cu; ++cu )
2087 {
2088 if( via->GetZoneLayerOverride( *cu ) == ZLO_FORCE_FLASHED )
2089 m_out->Print( 0, " %s", m_out->Quotew( LSET::Name( *cu ) ).c_str() );
2090 }
2091
2092 m_out->Print( 0, ")" );
2093 }
2094 }
2095 else if( aTrack->Type() == PCB_ARC_T )
2096 {
2097 const PCB_ARC* arc = static_cast<const PCB_ARC*>( aTrack );
2098 std::string locked = arc->IsLocked() ? " locked" : "";
2099
2100 m_out->Print( aNestLevel, "(arc%s (start %s) (mid %s) (end %s) (width %s)",
2101 locked.c_str(),
2106
2107 m_out->Print( 0, " (layer %s)", m_out->Quotew( LSET::Name( arc->GetLayer() ) ).c_str() );
2108 }
2109 else
2110 {
2111 std::string locked = aTrack->IsLocked() ? " locked" : "";
2112
2113 m_out->Print( aNestLevel, "(segment%s (start %s) (end %s) (width %s)",
2114 locked.c_str(),
2118
2119 m_out->Print( 0, " (layer %s)", m_out->Quotew( LSET::Name( aTrack->GetLayer() ) ).c_str() );
2120 }
2121
2122 m_out->Print( 0, " (net %d)", m_mapping->Translate( aTrack->GetNetCode() ) );
2123
2124 m_out->Print( 0, " (tstamp %s)", TO_UTF8( aTrack->m_Uuid.AsString() ) );
2125
2126 m_out->Print( 0, ")\n" );
2127}
2128
2129
2130void PCB_PLUGIN::format( const ZONE* aZone, int aNestLevel ) const
2131{
2132 std::string locked = aZone->IsLocked() ? " locked" : "";
2133
2134 // Save the NET info.
2135 // For keepout and non copper zones, net code and net name are irrelevant
2136 // so be sure a dummy value is stored, just for ZONE compatibility
2137 // (perhaps netcode and netname should be not stored)
2138 bool has_no_net = aZone->GetIsRuleArea() || !aZone->IsOnCopperLayer();
2139
2140 m_out->Print( aNestLevel, "(zone%s (net %d) (net_name %s)",
2141 locked.c_str(),
2142 has_no_net ? 0 : m_mapping->Translate( aZone->GetNetCode() ),
2143 m_out->Quotew( has_no_net ? wxString( wxT("") ) : aZone->GetNetname() ).c_str() );
2144
2145 // If a zone exists on multiple layers, format accordingly
2146 if( aZone->GetLayerSet().count() > 1 )
2147 {
2148 formatLayers( aZone->GetLayerSet() );
2149 }
2150 else
2151 {
2152 formatLayer( aZone->GetFirstLayer() );
2153 }
2154
2155 m_out->Print( 0, " (tstamp %s)", TO_UTF8( aZone->m_Uuid.AsString() ) );
2156
2157 if( !aZone->GetZoneName().empty() )
2158 m_out->Print( 0, " (name %s)", m_out->Quotew( aZone->GetZoneName() ).c_str() );
2159
2160 // Save the outline aux info
2161 std::string hatch;
2162
2163 switch( aZone->GetHatchStyle() )
2164 {
2165 default:
2166 case ZONE_BORDER_DISPLAY_STYLE::NO_HATCH: hatch = "none"; break;
2167 case ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE: hatch = "edge"; break;
2168 case ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL: hatch = "full"; break;
2169 }
2170
2171 m_out->Print( 0, " (hatch %s %s)\n", hatch.c_str(),
2173
2174 if( aZone->GetAssignedPriority() > 0 )
2175 m_out->Print( aNestLevel+1, "(priority %d)\n", aZone->GetAssignedPriority() );
2176
2177 // Add teardrop keywords in file: (attr (teardrop (type xxx)))where xxx is the teardrop type
2178 if( aZone->IsTeardropArea() )
2179 {
2180 const char* td_type;
2181
2182 switch( aZone->GetTeardropAreaType() )
2183 {
2184 case TEARDROP_TYPE::TD_VIAPAD: // a teardrop on a via or pad
2185 td_type = "padvia";
2186 break;
2187
2188 default:
2189 case TEARDROP_TYPE::TD_TRACKEND: // a teardrop on a track end
2190 td_type = "track_end";
2191 break;
2192 }
2193
2194 m_out->Print( aNestLevel+1, "(attr (teardrop (type %s)))\n", td_type );
2195 }
2196
2197 m_out->Print( aNestLevel+1, "(connect_pads" );
2198
2199 switch( aZone->GetPadConnection() )
2200 {
2201 default:
2202 case ZONE_CONNECTION::THERMAL: // Default option not saved or loaded.
2203 break;
2204
2206 m_out->Print( 0, " thru_hole_only" );
2207 break;
2208
2210 m_out->Print( 0, " yes" );
2211 break;
2212
2214 m_out->Print( 0, " no" );
2215 break;
2216 }
2217
2218 m_out->Print( 0, " (clearance %s))\n",
2220
2221 m_out->Print( aNestLevel+1, "(min_thickness %s)",
2223
2224 // We continue to write this for 3rd-party parsers, but we no longer read it (as of V7).
2225 m_out->Print( 0, " (filled_areas_thickness no)" );
2226
2227 m_out->Print( 0, "\n" );
2228
2229 if( aZone->GetIsRuleArea() )
2230 {
2231 m_out->Print( aNestLevel + 1,
2232 "(keepout (tracks %s) (vias %s) (pads %s) (copperpour %s) "
2233 "(footprints %s))\n",
2234 aZone->GetDoNotAllowTracks() ? "not_allowed" : "allowed",
2235 aZone->GetDoNotAllowVias() ? "not_allowed" : "allowed",
2236 aZone->GetDoNotAllowPads() ? "not_allowed" : "allowed",
2237 aZone->GetDoNotAllowCopperPour() ? "not_allowed" : "allowed",
2238 aZone->GetDoNotAllowFootprints() ? "not_allowed" : "allowed" );
2239 }
2240
2241 m_out->Print( aNestLevel + 1, "(fill" );
2242
2243 // Default is not filled.
2244 if( aZone->IsFilled() )
2245 m_out->Print( 0, " yes" );
2246
2247 // Default is polygon filled.
2249 m_out->Print( 0, " (mode hatch)" );
2250
2251 m_out->Print( 0, " (thermal_gap %s) (thermal_bridge_width %s)",
2254
2256 {
2257 m_out->Print( 0, " (smoothing" );
2258
2259 switch( aZone->GetCornerSmoothingType() )
2260 {
2262 m_out->Print( 0, " chamfer" );
2263 break;
2264
2266 m_out->Print( 0, " fillet" );
2267 break;
2268
2269 default:
2270 THROW_IO_ERROR( wxString::Format( _( "unknown zone corner smoothing type %d" ),
2271 aZone->GetCornerSmoothingType() ) );
2272 }
2273 m_out->Print( 0, ")" );
2274
2275 if( aZone->GetCornerRadius() != 0 )
2276 m_out->Print( 0, " (radius %s)",
2278 }
2279
2281 {
2282 m_out->Print( 0, " (island_removal_mode %d) (island_area_min %s)",
2283 static_cast<int>( aZone->GetIslandRemovalMode() ),
2285 }
2286
2288 {
2289 m_out->Print( 0, "\n" );
2290 m_out->Print( aNestLevel+2, "(hatch_thickness %s) (hatch_gap %s) (hatch_orientation %s)",
2293 FormatDouble2Str( aZone->GetHatchOrientation().AsDegrees() ).c_str() );
2294
2295 if( aZone->GetHatchSmoothingLevel() > 0 )
2296 {
2297 m_out->Print( 0, "\n" );
2298 m_out->Print( aNestLevel+2, "(hatch_smoothing_level %d) (hatch_smoothing_value %s)",
2299 aZone->GetHatchSmoothingLevel(),
2300 FormatDouble2Str( aZone->GetHatchSmoothingValue() ).c_str() );
2301 }
2302
2303 m_out->Print( 0, "\n" );
2304 m_out->Print( aNestLevel+2, "(hatch_border_algorithm %s) (hatch_min_hole_area %s)",
2305 aZone->GetHatchBorderAlgorithm() ? "hatch_thickness" : "min_thickness",
2306 FormatDouble2Str( aZone->GetHatchHoleMinArea() ).c_str() );
2307 }
2308
2309 m_out->Print( 0, ")\n" );
2310
2311 if( aZone->GetNumCorners() )
2312 {
2313 SHAPE_POLY_SET::POLYGON poly = aZone->Outline()->Polygon(0);
2314
2315 for( auto& chain : poly )
2316 {
2317 m_out->Print( aNestLevel + 1, "(polygon\n" );
2318 formatPolyPts( chain, aNestLevel + 1, ADVANCED_CFG::GetCfg().m_CompactSave );
2319 m_out->Print( aNestLevel + 1, ")\n" );
2320 }
2321 }
2322
2323 // Save the PolysList (filled areas)
2324 for( PCB_LAYER_ID layer : aZone->GetLayerSet().Seq() )
2325 {
2326 const std::shared_ptr<SHAPE_POLY_SET>& fv = aZone->GetFilledPolysList( layer );
2327
2328 for( int ii = 0; ii < fv->OutlineCount(); ++ii )
2329 {
2330 m_out->Print( aNestLevel + 1, "(filled_polygon\n" );
2331 m_out->Print( aNestLevel + 2, "(layer %s)\n",
2332 m_out->Quotew( LSET::Name( layer ) ).c_str() );
2333
2334 if( aZone->IsIsland( layer, ii ) )
2335 m_out->Print( aNestLevel + 2, "(island)\n" );
2336
2337 const SHAPE_LINE_CHAIN& chain = fv->COutline( ii );
2338
2339 formatPolyPts( chain, aNestLevel + 1, ADVANCED_CFG::GetCfg().m_CompactSave );
2340 m_out->Print( aNestLevel + 1, ")\n" );
2341 }
2342 }
2343
2344 m_out->Print( aNestLevel, ")\n" );
2345}
2346
2347
2348PCB_PLUGIN::PCB_PLUGIN( int aControlFlags ) :
2349 m_cache( nullptr ),
2350 m_ctl( aControlFlags ),
2351 m_mapping( new NETINFO_MAPPING() ),
2352 m_queryUserCallback( nullptr )
2353{
2354 init( nullptr );
2355 m_out = &m_sf;
2356}
2357
2358
2360{
2361 delete m_cache;
2362 delete m_mapping;
2363}
2364
2365
2366BOARD* PCB_PLUGIN::Load( const wxString& aFileName, BOARD* aAppendToMe,
2367 const STRING_UTF8_MAP* aProperties, PROJECT* aProject,
2368 PROGRESS_REPORTER* aProgressReporter )
2369{
2370 FILE_LINE_READER reader( aFileName );
2371
2372 unsigned lineCount = 0;
2373
2374 if( aProgressReporter )
2375 {
2376 aProgressReporter->Report( wxString::Format( _( "Loading %s..." ), aFileName ) );
2377
2378 if( !aProgressReporter->KeepRefreshing() )
2379 THROW_IO_ERROR( _( "Open cancelled by user." ) );
2380
2381 while( reader.ReadLine() )
2382 lineCount++;
2383
2384 reader.Rewind();
2385 }
2386
2387 BOARD* board = DoLoad( reader, aAppendToMe, aProperties, aProgressReporter, lineCount );
2388
2389 // Give the filename to the board if it's new
2390 if( !aAppendToMe )
2391 board->SetFileName( aFileName );
2392
2393 return board;
2394}
2395
2396
2397BOARD* PCB_PLUGIN::DoLoad( LINE_READER& aReader, BOARD* aAppendToMe, const STRING_UTF8_MAP* aProperties,
2398 PROGRESS_REPORTER* aProgressReporter, unsigned aLineCount)
2399{
2400 init( aProperties );
2401
2402 PCB_PARSER parser( &aReader, aAppendToMe, m_queryUserCallback, aProgressReporter, aLineCount );
2403 BOARD* board;
2404
2405 try
2406 {
2407 board = dynamic_cast<BOARD*>( parser.Parse() );
2408 }
2409 catch( const FUTURE_FORMAT_ERROR& )
2410 {
2411 // Don't wrap a FUTURE_FORMAT_ERROR in another
2412 throw;
2413 }
2414 catch( const PARSE_ERROR& parse_error )
2415 {
2416 if( parser.IsTooRecent() )
2417 throw FUTURE_FORMAT_ERROR( parse_error, parser.GetRequiredVersion() );
2418 else
2419 throw;
2420 }
2421
2422 if( !board )
2423 {
2424 // The parser loaded something that was valid, but wasn't a board.
2425 THROW_PARSE_ERROR( _( "This file does not contain a PCB." ), parser.CurSource(),
2426 parser.CurLine(), parser.CurLineNumber(), parser.CurOffset() );
2427 }
2428
2429 return board;
2430}
2431
2432
2433void PCB_PLUGIN::init( const STRING_UTF8_MAP* aProperties )
2434{
2435 m_board = nullptr;
2436 m_reader = nullptr;
2437 m_props = aProperties;
2438}
2439
2440
2441void PCB_PLUGIN::validateCache( const wxString& aLibraryPath, bool checkModified )
2442{
2443 if( !m_cache || !m_cache->IsPath( aLibraryPath ) || ( checkModified && m_cache->IsModified() ) )
2444 {
2445 // a spectacular episode in memory management:
2446 delete m_cache;
2447 m_cache = new FP_CACHE( this, aLibraryPath );
2448 m_cache->Load();
2449 }
2450}
2451
2452
2453void PCB_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibPath,
2454 bool aBestEfforts, const STRING_UTF8_MAP* aProperties )
2455{
2456 LOCALE_IO toggle; // toggles on, then off, the C locale.
2457 wxDir dir( aLibPath );
2458 wxString errorMsg;
2459
2460 init( aProperties );
2461
2462 try
2463 {
2464 validateCache( aLibPath );
2465 }
2466 catch( const IO_ERROR& ioe )
2467 {
2468 errorMsg = ioe.What();
2469 }
2470
2471 // Some of the files may have been parsed correctly so we want to add the valid files to
2472 // the library.
2473
2474 for( const auto& footprint : m_cache->GetFootprints() )
2475 aFootprintNames.Add( footprint.first );
2476
2477 if( !errorMsg.IsEmpty() && !aBestEfforts )
2478 THROW_IO_ERROR( errorMsg );
2479}
2480
2481
2482const FOOTPRINT* PCB_PLUGIN::getFootprint( const wxString& aLibraryPath,
2483 const wxString& aFootprintName,
2484 const STRING_UTF8_MAP* aProperties,
2485 bool checkModified )
2486{
2487 LOCALE_IO toggle; // toggles on, then off, the C locale.
2488
2489 init( aProperties );
2490
2491 try
2492 {
2493 validateCache( aLibraryPath, checkModified );
2494 }
2495 catch( const IO_ERROR& )
2496 {
2497 // do nothing with the error
2498 }
2499
2501 FP_CACHE_FOOTPRINT_MAP::const_iterator it = footprints.find( aFootprintName );
2502
2503 if( it == footprints.end() )
2504 return nullptr;
2505
2506 return it->second->GetFootprint();
2507}
2508
2509
2510const FOOTPRINT* PCB_PLUGIN::GetEnumeratedFootprint( const wxString& aLibraryPath,
2511 const wxString& aFootprintName,
2512 const STRING_UTF8_MAP* aProperties )
2513{
2514 return getFootprint( aLibraryPath, aFootprintName, aProperties, false );
2515}
2516
2517
2518bool PCB_PLUGIN::FootprintExists( const wxString& aLibraryPath, const wxString& aFootprintName,
2519 const STRING_UTF8_MAP* aProperties )
2520{
2521 // Note: checking the cache sounds like a good idea, but won't catch files which differ
2522 // only in case.
2523 //
2524 // Since this goes out to the native filesystem, we get platform differences (ie: MSW's
2525 // case-insensitive filesystem) handled "for free".
2526 // Warning: footprint names frequently contain a point. So be careful when initializing
2527 // wxFileName, and use a CTOR with extension specified
2528 wxFileName footprintFile( aLibraryPath, aFootprintName, KiCadFootprintFileExtension );
2529
2530 return footprintFile.Exists();
2531}
2532
2533
2534FOOTPRINT* PCB_PLUGIN::FootprintLoad( const wxString& aLibraryPath,
2535 const wxString& aFootprintName,
2536 bool aKeepUUID,
2537 const STRING_UTF8_MAP* aProperties )
2538{
2539 const FOOTPRINT* footprint = getFootprint( aLibraryPath, aFootprintName, aProperties, true );
2540
2541 if( footprint )
2542 {
2543 FOOTPRINT* copy;
2544
2545 if( aKeepUUID )
2546 copy = static_cast<FOOTPRINT*>( footprint->Clone() );
2547 else
2548 copy = static_cast<FOOTPRINT*>( footprint->Duplicate() );
2549
2550 copy->SetParent( nullptr );
2551 return copy;
2552 }
2553
2554 return nullptr;
2555}
2556
2557
2558void PCB_PLUGIN::FootprintSave( const wxString& aLibraryPath, const FOOTPRINT* aFootprint,
2559 const STRING_UTF8_MAP* aProperties )
2560{
2561 LOCALE_IO toggle; // toggles on, then off, the C locale.
2562
2563 init( aProperties );
2564
2565 // In this public PLUGIN API function, we can safely assume it was
2566 // called for saving into a library path.
2568
2569 validateCache( aLibraryPath );
2570
2571 if( !m_cache->IsWritable() )
2572 {
2573 if( !m_cache->Exists() )
2574 {
2575 const wxString msg = wxString::Format( _( "Library '%s' does not exist.\n"
2576 "Would you like to create it?"),
2577 aLibraryPath );
2578
2579 if( !IsGUI() || wxMessageBox( msg, _( "Library Not Found"), wxYES_NO | wxICON_QUESTION ) != wxYES )
2580 return;
2581
2582 // Save throws its own IO_ERROR on failure, so no need to recreate here
2583 m_cache->Save( nullptr );
2584 }
2585 else
2586 {
2587 wxString msg = wxString::Format( _( "Library '%s' is read only." ), aLibraryPath );
2588 THROW_IO_ERROR( msg );
2589 }
2590 }
2591
2592 wxString footprintName = aFootprint->GetFPID().GetLibItemName();
2593
2595
2596 // Quietly overwrite footprint and delete footprint file from path for any by same name.
2597 wxFileName fn( aLibraryPath, aFootprint->GetFPID().GetLibItemName(),
2599
2600 // Write through symlinks, don't replace them
2602
2603 if( !fn.IsOk() )
2604 {
2605 THROW_IO_ERROR( wxString::Format( _( "Footprint file name '%s' is not valid." ),
2606 fn.GetFullPath() ) );
2607 }
2608
2609 if( fn.FileExists() && !fn.IsFileWritable() )
2610 {
2611 THROW_IO_ERROR( wxString::Format( _( "Insufficient permissions to delete '%s'." ),
2612 fn.GetFullPath() ) );
2613 }
2614
2615 wxString fullPath = fn.GetFullPath();
2616 wxString fullName = fn.GetFullName();
2617 FP_CACHE_FOOTPRINT_MAP::const_iterator it = footprints.find( footprintName );
2618
2619 if( it != footprints.end() )
2620 {
2621 wxLogTrace( traceKicadPcbPlugin, wxT( "Removing footprint file '%s'." ), fullPath );
2622 footprints.erase( footprintName );
2623 wxRemoveFile( fullPath );
2624 }
2625
2626 // I need my own copy for the cache
2627 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aFootprint->Clone() );
2628
2629 // It's orientation should be zero and it should be on the front layer.
2630 footprint->SetOrientation( ANGLE_0 );
2631
2632 if( footprint->GetLayer() != F_Cu )
2633 {
2634 PCBNEW_SETTINGS* cfg = dynamic_cast<PCBNEW_SETTINGS*>( Kiface().KifaceSettings() );
2635
2636 if( cfg )
2637 footprint->Flip( footprint->GetPosition(), cfg->m_FlipLeftRight );
2638 else
2639 footprint->Flip( footprint->GetPosition(), false );
2640 }
2641
2642 // Detach it from the board
2643 footprint->SetParent( nullptr );
2644
2645 wxLogTrace( traceKicadPcbPlugin, wxT( "Creating s-expr footprint file '%s'." ), fullPath );
2646 footprints.insert( footprintName,
2647 new FP_CACHE_ITEM( footprint, WX_FILENAME( fn.GetPath(), fullName ) ) );
2648 m_cache->Save( footprint );
2649}
2650
2651
2652void PCB_PLUGIN::FootprintDelete( const wxString& aLibraryPath, const wxString& aFootprintName,
2653 const STRING_UTF8_MAP* aProperties )
2654{
2655 LOCALE_IO toggle; // toggles on, then off, the C locale.
2656
2657 init( aProperties );
2658
2659 validateCache( aLibraryPath );
2660
2661 if( !m_cache->IsWritable() )
2662 {
2663 THROW_IO_ERROR( wxString::Format( _( "Library '%s' is read only." ),
2664 aLibraryPath.GetData() ) );
2665 }
2666
2667 m_cache->Remove( aFootprintName );
2668}
2669
2670
2671
2672long long PCB_PLUGIN::GetLibraryTimestamp( const wxString& aLibraryPath ) const
2673{
2674 return FP_CACHE::GetTimestamp( aLibraryPath );
2675}
2676
2677
2678void PCB_PLUGIN::FootprintLibCreate( const wxString& aLibraryPath, const STRING_UTF8_MAP* aProperties )
2679{
2680 if( wxDir::Exists( aLibraryPath ) )
2681 {
2682 THROW_IO_ERROR( wxString::Format( _( "Cannot overwrite library path '%s'." ),
2683 aLibraryPath.GetData() ) );
2684 }
2685
2686 LOCALE_IO toggle;
2687
2688 init( aProperties );
2689
2690 delete m_cache;
2691 m_cache = new FP_CACHE( this, aLibraryPath );
2692 m_cache->Save();
2693}
2694
2695
2696bool PCB_PLUGIN::FootprintLibDelete( const wxString& aLibraryPath, const STRING_UTF8_MAP* aProperties )
2697{
2698 wxFileName fn;
2699 fn.SetPath( aLibraryPath );
2700
2701 // Return if there is no library path to delete.
2702 if( !fn.DirExists() )
2703 return false;
2704
2705 if( !fn.IsDirWritable() )
2706 {
2707 THROW_IO_ERROR( wxString::Format( _( "Insufficient permissions to delete folder '%s'." ),
2708 aLibraryPath.GetData() ) );
2709 }
2710
2711 wxDir dir( aLibraryPath );
2712
2713 if( dir.HasSubDirs() )
2714 {
2715 THROW_IO_ERROR( wxString::Format( _( "Library folder '%s' has unexpected sub-folders." ),
2716 aLibraryPath.GetData() ) );
2717 }
2718
2719 // All the footprint files must be deleted before the directory can be deleted.
2720 if( dir.HasFiles() )
2721 {
2722 unsigned i;
2723 wxFileName tmp;
2724 wxArrayString files;
2725
2726 wxDir::GetAllFiles( aLibraryPath, &files );
2727
2728 for( i = 0; i < files.GetCount(); i++ )
2729 {
2730 tmp = files[i];
2731
2732 if( tmp.GetExt() != KiCadFootprintFileExtension )
2733 {
2734 THROW_IO_ERROR( wxString::Format( _( "Unexpected file '%s' found in library "
2735 "path '%s'." ),
2736 files[i].GetData(),
2737 aLibraryPath.GetData() ) );
2738 }
2739 }
2740
2741 for( i = 0; i < files.GetCount(); i++ )
2742 wxRemoveFile( files[i] );
2743 }
2744
2745 wxLogTrace( traceKicadPcbPlugin, wxT( "Removing footprint library '%s'." ),
2746 aLibraryPath.GetData() );
2747
2748 // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
2749 // we don't want that. we want bare metal portability with no UI here.
2750 if( !wxRmdir( aLibraryPath ) )
2751 {
2752 THROW_IO_ERROR( wxString::Format( _( "Footprint library '%s' cannot be deleted." ),
2753 aLibraryPath.GetData() ) );
2754 }
2755
2756 // For some reason removing a directory in Windows is not immediately updated. This delay
2757 // prevents an error when attempting to immediately recreate the same directory when over
2758 // writing an existing library.
2759#ifdef __WINDOWS__
2760 wxMilliSleep( 250L );
2761#endif
2762
2763 if( m_cache && !m_cache->IsPath( aLibraryPath ) )
2764 {
2765 delete m_cache;
2766 m_cache = nullptr;
2767 }
2768
2769 return true;
2770}
2771
2772
2773bool PCB_PLUGIN::IsFootprintLibWritable( const wxString& aLibraryPath )
2774{
2775 LOCALE_IO toggle;
2776
2777 init( nullptr );
2778
2779 validateCache( aLibraryPath );
2780
2781 return m_cache->IsWritable();
2782}
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Returns # of elements in an array.
Definition: arraydim.h:31
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
@ ZLO_FORCE_FLASHED
Definition: board_item.h:59
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
double GetScale() const
Definition: bitmap_base.h:78
wxImage * GetImageData()
Definition: bitmap_base.h:71
Container for design settings for a BOARD object.
int GetBoardThickness() const
The full thickness of the board including copper and masks.
const VECTOR2I & GetGridOrigin()
const VECTOR2I & GetAuxOrigin()
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:70
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:192
virtual bool IsKnockout() const
Definition: board_item.h:262
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Definition: board_item.cpp:43
virtual bool IsLocked() const
Definition: board_item.cpp:71
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:175
Manage layers needed to make a physical board.
void FormatBoardStackup(OUTPUTFORMATTER *aFormatter, const BOARD *aBoard, int aNestLevel) const
Write the stackup info on board file.
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:269
LSET GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:587
void SetFileName(const wxString &aFileName)
Definition: board.h:304
const PAGE_INFO & GetPageSettings() const
Definition: board.h:632
ZONES & Zones()
Definition: board.h:317
LAYER_T GetLayerType(PCB_LAYER_ID aLayer) const
Return the type of the copper layer given by aLayer.
Definition: board.cpp:508
TITLE_BLOCK & GetTitleBlock()
Definition: board.h:638
FOOTPRINTS & Footprints()
Definition: board.h:311
const std::map< wxString, wxString > & GetProperties() const
Definition: board.h:338
GROUPS & Groups()
The groups must maintain the following invariants.
Definition: board.h:333
TRACKS & Tracks()
Definition: board.h:308
DRAWINGS & Drawings()
Definition: board.h:314
const PCB_PLOT_PARAMS & GetPlotOptions() const
Definition: board.h:635
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition: board.cpp:474
wxString GroupsSanityCheck(bool repair=false)
Consistency check of internal m_groups structure.
Definition: board.cpp:2115
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:704
double AsDegrees() const
Definition: eda_angle.h:149
bool IsZero() const
Definition: eda_angle.h:169
const KIID m_Uuid
Definition: eda_item.h:492
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:100
virtual wxString GetClass() const =0
Return the class name.
const VECTOR2I & GetBezierC2() const
Definition: eda_shape.h:179
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:247
bool IsFilled() const
Definition: eda_shape.h:90
SHAPE_T GetShape() const
Definition: eda_shape.h:113
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:145
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:120
wxString SHAPE_T_asString() const
Definition: eda_shape.cpp:75
const VECTOR2I & GetBezierC1() const
Definition: eda_shape.h:176
bool IsPolyShapeValid() const
Definition: eda_shape.cpp:1242
VECTOR2I GetArcMid() const
Definition: eda_shape.cpp:488
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition: eda_text.h:72
const VECTOR2I & GetTextPos() const
Definition: eda_text.h:208
const EDA_ANGLE & GetTextAngle() const
Definition: eda_text.h:120
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:87
bool IsKeepUpright() const
Definition: eda_text.h:155
virtual bool IsVisible() const
Definition: eda_text.h:136
KIFONT::FONT * GetFont() const
Definition: eda_text.h:188
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:453
virtual EDA_ANGLE GetDrawRotation() const
Definition: eda_text.h:317
virtual wxString GetShownText(int aDepth=0, bool aAllowExtraText=true) const
Return the string actually shown after processing of the base text.
Definition: eda_text.h:98
A LINE_READER that reads from an open file.
Definition: richio.h:173
void Rewind()
Rewind the file and resets the line number back to zero.
Definition: richio.h:222
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
Definition: richio.cpp:219
Used for text file output.
Definition: richio.h:457
void SetFPID(const LIB_ID &aFPID)
Definition: footprint.h:213
EDA_ANGLE GetOrientation() const
Definition: footprint.h:191
void SetOrientation(const EDA_ANGLE &aNewAngle)
Definition: footprint.cpp:1820
int GetLocalClearance() const
Definition: footprint.h:230
EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
Definition: footprint.cpp:1387
wxString GetDescription() const
Definition: footprint.h:218
double GetLocalSolderPasteMarginRatio() const
Definition: footprint.h:244
FP_GROUPS & Groups()
Definition: footprint.h:179
BOARD_ITEM * Duplicate() const override
Create a copy of this BOARD_ITEM.
Definition: footprint.cpp:1874
int GetAttributes() const
Definition: footprint.h:250
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: footprint.h:200
LSET GetPrivateLayers() const
Definition: footprint.h:120
PADS & Pads()
Definition: footprint.h:170
int GetLocalSolderPasteMargin() const
Definition: footprint.h:241
const std::vector< wxString > & GetNetTiePadGroups() const
Definition: footprint.h:272
const LIB_ID & GetFPID() const
Definition: footprint.h:212
bool IsLocked() const override
Definition: footprint.h:340
FP_TEXT & Value()
read/write accessors:
Definition: footprint.h:567
bool IsNetTie() const
Definition: footprint.h:257
const wxArrayString * GetInitialComments() const
Definition: footprint.h:784
std::vector< FP_3DMODEL > & Models()
Definition: footprint.h:184
void Flip(const VECTOR2I &aCentre, bool aFlipLeftRight) override
Flip this object, i.e.
Definition: footprint.cpp:1600
ZONE_CONNECTION GetZoneConnection() const
Definition: footprint.h:248
const KIID_PATH & GetPath() const
Definition: footprint.h:224
FP_ZONES & Zones()
Definition: footprint.h:176
wxString GetKeywords() const
Definition: footprint.h:221
bool IsPlaced() const
Definition: footprint.h:363
VECTOR2I GetPosition() const override
Definition: footprint.h:188
DRAWINGS & GraphicalItems()
Definition: footprint.h:173
FP_TEXT & Reference()
Definition: footprint.h:568
int GetLocalSolderMaskMargin() const
Definition: footprint.h:227
const std::map< wxString, wxString > & GetProperties() const
Definition: footprint.h:574
Helper class for creating a footprint library cache.
Definition: pcb_plugin.h:174
FP_CACHE_ITEM(FOOTPRINT *aFootprint, const WX_FILENAME &aFileName)
Definition: pcb_plugin.cpp:69
static long long GetTimestamp(const wxString &aLibPath)
Generate a timestamp representing all source files in the cache (including the parent directory).
Definition: pcb_plugin.cpp:263
FP_CACHE_FOOTPRINT_MAP & GetFootprints()
Definition: pcb_plugin.h:209
bool Exists() const
Definition: pcb_plugin.h:207
void Save(FOOTPRINT *aFootprint=nullptr)
Save the footprint cache or a single footprint from it to disk.
Definition: pcb_plugin.cpp:85
bool IsModified()
Return true if the cache is not up-to-date.
Definition: pcb_plugin.cpp:255
long long m_cache_timestamp
Definition: pcb_plugin.h:197
bool m_cache_dirty
Definition: pcb_plugin.h:195
wxString m_lib_raw_path
Definition: pcb_plugin.h:192
void SetPath(const wxString &aPath)
Definition: pcb_plugin.cpp:242
PCB_PLUGIN * m_owner
Definition: pcb_plugin.h:190
FP_CACHE(PCB_PLUGIN *aOwner, const wxString &aLibraryPath)
Definition: pcb_plugin.cpp:75
wxFileName m_lib_path
Definition: pcb_plugin.h:191
bool IsPath(const wxString &aPath) const
Check if aPath is the same as the current cache path.
Definition: pcb_plugin.cpp:236
void Load()
Definition: pcb_plugin.cpp:152
FP_CACHE_FOOTPRINT_MAP m_footprints
Definition: pcb_plugin.h:193
bool IsWritable() const
Definition: pcb_plugin.h:205
void Remove(const wxString &aFootprintName)
Definition: pcb_plugin.cpp:217
VECTOR2I GetArcMid0() const
Definition: fp_shape.cpp:179
const VECTOR2I & GetBezierC1_0() const
Definition: fp_shape.h:98
const VECTOR2I & GetEnd0() const
Definition: fp_shape.h:95
const VECTOR2I & GetBezierC2_0() const
Definition: fp_shape.h:101
const VECTOR2I & GetStart0() const
Definition: fp_shape.h:92
const VECTOR2I & GetPos0() const
Definition: fp_text.h:124
@ TEXT_is_REFERENCE
Definition: fp_text.h:49
@ TEXT_is_DIVERS
Definition: fp_text.h:51
@ TEXT_is_VALUE
Definition: fp_text.h:50
TEXT_TYPE GetType() const
Definition: fp_text.h:120
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:76
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:30
APP_SETTINGS_BASE * KifaceSettings() const
Definition: kiface_base.h:95
virtual bool IsOutline() const
Definition: font.h:113
wxString AsString() const
Definition: kiid.cpp:359
wxString AsString() const
Definition: kiid.cpp:257
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:117
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:81
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:41
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition: layer_ids.h:493
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:532
LSEQ Seq(const PCB_LAYER_ID *aWishListSequence, unsigned aCount) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:411
LSEQ CuStack() const
Return a sequence of copper layers in starting from the front/top and extending to the back/bottom.
Definition: lset.cpp:170
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:773
static const wxChar * Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition: lset.cpp:82
Handle the data for a net.
Definition: netinfo.h:67
static const int UNCONNECTED
Constant that forces initialization of a netinfo item to the NETINFO_ITEM ORPHANED (typically -1) whe...
Definition: netinfo.h:387
void SetBoard(const BOARD *aBoard)
Set a BOARD object that is used to prepare the net code map.
Definition: netinfo.h:215
int Translate(int aNetCode) const
Translate net number according to the map prepared by Update() function.
std::string Quotew(const wxString &aWrapee) const
Definition: richio.cpp:501
int PRINTF_FUNC Print(int nestLevel, const char *fmt,...)
Format and write text to the output stream.
Definition: richio.cpp:433
virtual std::string Quotes(const std::string &aWrapee) const
Check aWrapee input string for a need to be quoted (e.g.
Definition: richio.cpp:462
Definition: pad.h:60
int GetLocalClearance(wxString *aSource) const override
Return any local clearances set in the "classic" (ie: pre-rule) system.
Definition: pad.cpp:799
PAD_PROP GetProperty() const
Definition: pad.h:404
bool GetRemoveUnconnected() const
Definition: pad.h:610
PAD_DRILL_SHAPE_T GetDrillShape() const
Definition: pad.h:384
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: pad.h:398
const ZONE_LAYER_OVERRIDE & GetZoneLayerOverride(PCB_LAYER_ID aLayer) const
Definition: pad.h:748
const wxString & GetPinType() const
Definition: pad.h:153
const VECTOR2I & GetDrillSize() const
Definition: pad.h:268
PAD_ATTRIB GetAttribute() const
Definition: pad.h:401
ZONE_CONNECTION GetZoneConnection() const
Definition: pad.h:523
const wxString & GetPinFunction() const
Definition: pad.h:147
const wxString & GetNumber() const
Definition: pad.h:136
double GetLocalSolderPasteMarginRatio() const
Definition: pad.h:426
bool IsLocked() const override
Definition: pad.cpp:156
const std::vector< std::shared_ptr< PCB_SHAPE > > & GetPrimitives() const
Accessor to the basic shape list for custom-shaped pads.
Definition: pad.h:331
EDA_ANGLE GetThermalSpokeAngle() const
Definition: pad.h:542
const VECTOR2I & GetOffset() const
Definition: pad.h:275
int GetLocalSolderMaskMargin() const
Definition: pad.h:416
bool GetKeepTopBottom() const
Definition: pad.h:616
CUST_PAD_SHAPE_IN_ZONE GetCustomShapeInZoneOpt() const
Definition: pad.h:213
const VECTOR2I & GetDelta() const
Definition: pad.h:265
PAD_SHAPE GetShape() const
Definition: pad.h:195
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition: pad.h:371
int GetThermalSpokeWidth() const
Definition: pad.h:532
int GetLocalSolderPasteMargin() const
Definition: pad.h:423
int GetChamferPositions() const
Definition: pad.h:595
double GetRoundRectRadiusRatio() const
Definition: pad.h:576
int GetThermalGap() const
Definition: pad.h:555
const VECTOR2I & GetPos0() const
Definition: pad.h:252
const VECTOR2I & GetSize() const
Definition: pad.h:258
PAD_SHAPE GetAnchorPadShape() const
Definition: pad.h:208
double GetChamferRectRatio() const
Definition: pad.h:585
int GetPadToDieLength() const
Definition: pad.h:414
void Format(OUTPUTFORMATTER *aFormatter, int aNestLevel, int aControlBits) const
Output the page class to aFormatter in s-expression form.
Definition: page_info.cpp:273
const VECTOR2I & GetMid() const
Definition: pcb_track.h:313
Object to handle a bitmap image that can be inserted in a PCB.
Definition: pcb_bitmap.h:42
VECTOR2I GetPosition() const override
Definition: pcb_bitmap.h:127
const BITMAP_BASE * GetImage() const
Definition: pcb_bitmap.h:53
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.
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
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:51
std::unordered_set< BOARD_ITEM * > & GetItems()
Definition: pcb_group.h:68
wxString GetName() const
Definition: pcb_group.h:65
Read a Pcbnew s-expression formatted LINE_READER object and returns the appropriate BOARD_ITEM object...
Definition: pcb_parser.h:74
bool IsTooRecent()
Return whether a version number, if any was parsed, was too recent.
Definition: pcb_parser.h:105
BOARD_ITEM * Parse()
Definition: pcb_parser.cpp:681
wxString GetRequiredVersion()
Return a string representing the version of KiCad required to open this file.
Definition: pcb_parser.cpp:228
void Format(OUTPUTFORMATTER *aFormatter, int aNestLevel, int aControl=0) const
A PLUGIN derivation for saving and loading Pcbnew s-expression formatted files.
Definition: pcb_plugin.h:261
void formatSetup(const BOARD *aBoard, int aNestLevel=0) const
formats the board setup information
Definition: pcb_plugin.cpp:510
void FootprintSave(const wxString &aLibraryPath, const FOOTPRINT *aFootprint, const STRING_UTF8_MAP *aProperties=nullptr) override
Write aFootprint to an existing library located at aLibraryPath.
BOARD * m_board
which BOARD, no ownership here
Definition: pcb_plugin.h:417
LINE_READER * m_reader
no ownership here.
Definition: pcb_plugin.h:422
void formatPolyPts(const SHAPE_LINE_CHAIN &outline, int aNestLevel, bool aCompact) const
Definition: pcb_plugin.cpp:423
void init(const STRING_UTF8_MAP *aProperties)
friend class FP_CACHE
Definition: pcb_plugin.h:413
bool FootprintLibDelete(const wxString &aLibraryPath, const STRING_UTF8_MAP *aProperties=nullptr) override
Delete an existing footprint library and returns true, or if library does not exist returns false,...
FP_CACHE * m_cache
Footprint library cache.
Definition: pcb_plugin.h:420
void formatLayer(PCB_LAYER_ID aLayer, bool aIsKnockout=false) const
Definition: pcb_plugin.cpp:415
BOARD_ITEM * Parse(const wxString &aClipboardSourceInput)
Definition: pcb_plugin.cpp:310
std::function< bool(wxString aTitle, int aIcon, wxString aMsg, wxString aAction)> * m_queryUserCallback
Definition: pcb_plugin.h:431
void formatHeader(const BOARD *aBoard, int aNestLevel=0) const
writes everything that comes before the board_items, like settings and layers etc
Definition: pcb_plugin.cpp:689
void formatLayers(LSET aLayerMask, int aNestLevel=0) const
void validateCache(const wxString &aLibraryPath, bool checkModified=true)
bool IsFootprintLibWritable(const wxString &aLibraryPath) override
Return true if the library at aLibraryPath is writable.
BOARD * Load(const wxString &aFileName, BOARD *aAppendToMe, const STRING_UTF8_MAP *aProperties=nullptr, PROJECT *aProject=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr) override
Load information from some input file format that this PLUGIN implementation knows about into either ...
void SetOutputFormatter(OUTPUTFORMATTER *aFormatter)
Definition: pcb_plugin.h:346
void formatRenderCache(const EDA_TEXT *aText, int aNestLevel) const
Definition: pcb_plugin.cpp:476
STRING_FORMATTER m_sf
Definition: pcb_plugin.h:425
OUTPUTFORMATTER * m_out
output any Format()s to this, no ownership
Definition: pcb_plugin.h:426
const FOOTPRINT * GetEnumeratedFootprint(const wxString &aLibraryPath, const wxString &aFootprintName, const STRING_UTF8_MAP *aProperties=nullptr) override
A version of FootprintLoad() for use after FootprintEnumerate() for more efficient cache management.
const STRING_UTF8_MAP * m_props
passed via Save() or Load(), no ownership, may be NULL.
Definition: pcb_plugin.h:419
BOARD * DoLoad(LINE_READER &aReader, BOARD *aAppendToMe, const STRING_UTF8_MAP *aProperties, PROGRESS_REPORTER *aProgressReporter, unsigned aLineCount)
NETINFO_MAPPING * m_mapping
mapping for net codes, so only not empty net codes are stored with consecutive integers as net codes
Definition: pcb_plugin.h:428
void format(const BOARD *aBoard, int aNestLevel=0) const
Definition: pcb_plugin.cpp:707
void formatNetInformation(const BOARD *aBoard, int aNestLevel=0) const
formats the Nets and Netclasses
Definition: pcb_plugin.cpp:659
PCB_PLUGIN(int aControlFlags=CTL_FOR_BOARD)
void FootprintDelete(const wxString &aLibraryPath, const wxString &aFootprintName, const STRING_UTF8_MAP *aProperties=nullptr) override
Delete aFootprintName from the library at aLibraryPath.
bool FootprintExists(const wxString &aLibraryPath, const wxString &aFootprintName, const STRING_UTF8_MAP *aProperties=nullptr) override
Check for the existence of a footprint.
void FootprintLibCreate(const wxString &aLibraryPath, const STRING_UTF8_MAP *aProperties=nullptr) override
Create a new empty footprint library at aLibraryPath empty.
void formatProperties(const BOARD *aBoard, int aNestLevel=0) const
formats the Nets and Netclasses
Definition: pcb_plugin.cpp:675
FOOTPRINT * FootprintLoad(const wxString &aLibraryPath, const wxString &aFootprintName, bool aKeepUUID=false, const STRING_UTF8_MAP *aProperties=nullptr) override
Load a footprint having aFootprintName from the aLibraryPath containing a library format that this PL...
const FOOTPRINT * getFootprint(const wxString &aLibraryPath, const wxString &aFootprintName, const STRING_UTF8_MAP *aProperties, bool checkModified)
void formatBoardLayers(const BOARD *aBoard, int aNestLevel=0) const
formats the board layer information
Definition: pcb_plugin.cpp:589
void formatGeneral(const BOARD *aBoard, int aNestLevel=0) const
formats the General section of the file
Definition: pcb_plugin.cpp:573
long long GetLibraryTimestamp(const wxString &aLibraryPath) const override
Generate a timestamp representing all the files in the library (including the library directory).
void Save(const wxString &aFileName, BOARD *aBoard, const STRING_UTF8_MAP *aProperties=nullptr) override
Write aBoard to a storage file in a format that this PLUGIN implementation knows about or it can be u...
Definition: pcb_plugin.cpp:271
void Format(const BOARD_ITEM *aItem, int aNestLevel=0) const
Output aItem to aFormatter in s-expression format.
Definition: pcb_plugin.cpp:331
void FootprintEnumerate(wxArrayString &aFootprintNames, const wxString &aLibraryPath, bool aBestEfforts, const STRING_UTF8_MAP *aProperties=nullptr) override
Return a list of footprint names contained within the library at aLibraryPath.
virtual ~PCB_PLUGIN()
STROKE_PARAMS GetStroke() const override
Definition: pcb_shape.h:71
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
int GetWidth() const
Definition: pcb_track.h:108
const VECTOR2I & GetStart() const
Definition: pcb_track.h:114
const VECTOR2I & GetEnd() const
Definition: pcb_track.h:111
A progress reporter interface for use in multi-threaded environments.
virtual bool KeepRefreshing(bool aWait=false)=0
Update the UI (if any).
virtual void Report(const wxString &aMessage)=0
Display aMessage in the progress bar dialog.
Container for project specific data.
Definition: project.h:64
const VECTOR2I & GetArcMid() const
Definition: shape_arc.h:114
const VECTOR2I & GetP1() const
Definition: shape_arc.h:113
const VECTOR2I & GetP0() const
Definition: shape_arc.h:112
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)
int HoleCount(int aOutline) const
Return the reference to aIndex-th outline in the set.
std::vector< SHAPE_LINE_CHAIN > POLYGON
< represents a single polygon outline with holes.
SHAPE_LINE_CHAIN & Outline(int aIndex)
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Return the aIndex-th subpolygon in the set.
int OutlineCount() const
Return the number of vertices in a given outline/hole.
Is a LINE_READER that reads from a multiline 8 bit wide std::string.
Definition: richio.h:241
A name/value tuple with unique names and optional values.
int GetWidth() const
Definition: stroke_params.h:98
void Format(OUTPUTFORMATTER *out, const EDA_IU_SCALE &aIuScale, int nestLevel) const
virtual void Format(OUTPUTFORMATTER *aFormatter, int aNestLevel, int aControlBits) const
Output the object to aFormatter in s-expression form.
Definition: title_block.cpp:30
A wrapper around a wxFileName which is much more performant with a subset of the API.
Definition: wx_filename.h:49
void SetFullName(const wxString &aFileNameAndExtension)
Definition: wx_filename.cpp:34
static void ResolvePossibleSymlinks(wxFileName &aFilename)
Definition: wx_filename.cpp:92
wxString GetPath() const
Definition: wx_filename.cpp:60
wxString GetName() const
Definition: wx_filename.cpp:47
wxString GetFullPath() const
Definition: wx_filename.cpp:66
long long GetTimestamp()
Definition: wx_filename.cpp:81
Handle a list of polygons defining a copper zone.
Definition: zone.h:57
int GetHatchBorderAlgorithm() const
Definition: zone.h:284
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition: zone.h:703
bool GetDoNotAllowVias() const
Definition: zone.h:705
const std::shared_ptr< SHAPE_POLY_SET > & GetFilledPolysList(PCB_LAYER_ID aLayer) const
Definition: zone.h:608
const ISLAND_REMOVAL_MODE GetIslandRemovalMode() const
Definition: zone.h:717
bool GetDoNotAllowPads() const
Definition: zone.h:707
bool GetDoNotAllowTracks() const
Definition: zone.h:706
bool IsFilled() const
Definition: zone.h:242
SHAPE_POLY_SET * Outline()
Definition: zone.h:318
bool IsIsland(PCB_LAYER_ID aLayer, int aPolyIdx) const
Check if a given filled polygon is an insulated island.
Definition: zone.cpp:1028
long long int GetMinIslandArea() const
Definition: zone.h:720
wxString GetZoneName() const
Definition: zone.h:124
int GetLocalClearance(wxString *aSource) const override
Return any local clearances set in the "classic" (ie: pre-rule) system.
Definition: zone.cpp:476
int GetMinThickness() const
Definition: zone.h:251
ZONE_CONNECTION GetPadConnection() const
Definition: zone.h:248
int GetHatchThickness() const
Definition: zone.h:266
double GetHatchHoleMinArea() const
Definition: zone.h:281
bool IsTeardropArea() const
Definition: zone.h:687
int GetThermalReliefSpokeWidth() const
Definition: zone.h:195
int GetBorderHatchPitch() const
HatchBorder related methods.
Definition: zone.cpp:820
ZONE_BORDER_DISPLAY_STYLE GetHatchStyle() const
Definition: zone.h:586
EDA_ANGLE GetHatchOrientation() const
Definition: zone.h:272
bool GetDoNotAllowFootprints() const
Definition: zone.h:708
ZONE_FILL_MODE GetFillMode() const
Definition: zone.h:174
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: zone.h:122
bool GetDoNotAllowCopperPour() const
Definition: zone.h:704
int GetHatchGap() const
Definition: zone.h:269
TEARDROP_TYPE GetTeardropAreaType() const
Definition: zone.h:698
double GetHatchSmoothingValue() const
Definition: zone.h:278
int GetHatchSmoothingLevel() const
Definition: zone.h:275
unsigned int GetCornerRadius() const
Definition: zone.h:658
int GetCornerSmoothingType() const
Definition: zone.h:654
bool IsOnCopperLayer() const override
Definition: zone.cpp:260
PCB_LAYER_ID GetFirstLayer() const
Definition: zone.cpp:251
int GetThermalReliefGap() const
Definition: zone.h:184
unsigned GetAssignedPriority() const
Definition: zone.h:112
int GetNumCorners(void) const
Access to m_Poly parameters.
Definition: zone.h:496
long long TimestampDir(const wxString &aDirPath, const wxString &aFilespec)
A copy of ConvertFileTimeToWx() because wxWidgets left it as a static function private to src/common/...
Definition: common.cpp:536
bool IsGUI()
Determine if the application is running with a GUI.
Definition: confirm.cpp:40
This file is part of the common library.
#define _(s)
static constexpr EDA_ANGLE & ANGLE_45
Definition: eda_angle.h:430
static constexpr EDA_ANGLE & ANGLE_90
Definition: eda_angle.h:431
static constexpr EDA_ANGLE & ANGLE_0
Definition: eda_angle.h:429
#define CTL_OMIT_HIDE
Definition: eda_text.h:53
@ FP_SMD
Definition: footprint.h:69
@ FP_ALLOW_MISSING_COURTYARD
Definition: footprint.h:75
@ FP_EXCLUDE_FROM_POS_FILES
Definition: footprint.h:70
@ FP_BOARD_ONLY
Definition: footprint.h:72
@ FP_EXCLUDE_FROM_BOM
Definition: footprint.h:71
@ FP_THROUGH_HOLE
Definition: footprint.h:68
@ FP_ALLOW_SOLDERMASK_BRIDGES
Definition: footprint.h:74
const std::string KiCadFootprintFileExtension
const wxChar *const traceKicadPcbPlugin
Flag to enable GEDA PCB plugin debug output.
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
Definition: ki_exception.h:164
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:59
@ User_8
Definition: layer_ids.h:130
@ F_CrtYd
Definition: layer_ids.h:117
@ B_Adhes
Definition: layer_ids.h:97
@ Edge_Cuts
Definition: layer_ids.h:113
@ Dwgs_User
Definition: layer_ids.h:109
@ F_Paste
Definition: layer_ids.h:101
@ Cmts_User
Definition: layer_ids.h:110
@ User_6
Definition: layer_ids.h:128
@ User_7
Definition: layer_ids.h:129
@ F_Adhes
Definition: layer_ids.h:98
@ B_Mask
Definition: layer_ids.h:106
@ B_Cu
Definition: layer_ids.h:95
@ User_5
Definition: layer_ids.h:127
@ Eco1_User
Definition: layer_ids.h:111
@ F_Mask
Definition: layer_ids.h:107
@ B_Paste
Definition: layer_ids.h:100
@ User_9
Definition: layer_ids.h:131
@ F_Fab
Definition: layer_ids.h:120
@ Margin
Definition: layer_ids.h:114
@ F_SilkS
Definition: layer_ids.h:104
@ B_CrtYd
Definition: layer_ids.h:116
@ Eco2_User
Definition: layer_ids.h:112
@ User_3
Definition: layer_ids.h:125
@ User_1
Definition: layer_ids.h:123
@ B_SilkS
Definition: layer_ids.h:103
@ User_4
Definition: layer_ids.h:126
@ PCB_LAYER_ID_COUNT
Definition: layer_ids.h:137
@ User_2
Definition: layer_ids.h:124
@ F_Cu
Definition: layer_ids.h:64
@ B_Fab
Definition: layer_ids.h:119
This file contains miscellaneous commonly used macros and functions.
#define UNIMPLEMENTED_FOR(type)
Definition: macros.h:120
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:96
std::string FormatInternalUnits(const EDA_IU_SCALE &aIuScale, int aValue)
Converts aValue from internal units to a string appropriate for writing to file.
Definition: eda_units.cpp:142
std::string FormatAngle(const EDA_ANGLE &aAngle)
Converts aAngle from board units to a string appropriate for writing to file.
Definition: eda_units.cpp:134
@ CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL
Definition: pad.h:46
@ NPTH
like PAD_PTH, but not plated
@ SMD
Smd pad, appears on the solder paste layer (default)
@ PTH
Plated through hole pad.
@ CONN
Like smd, does not appear on the solder paste layer (default)
@ PAD_DRILL_SHAPE_OBLONG
Definition: pad_shapes.h:71
@ FIDUCIAL_LOCAL
a fiducial (usually a smd) local to the parent footprint
@ FIDUCIAL_GLBL
a fiducial (usually a smd) for the full board
@ HEATSINK
a pad used as heat sink, usually in SMD footprints
@ NONE
no special fabrication property
@ TESTPOINT
a test point pad
@ CASTELLATED
a pad with a castellated through hole
@ BGA
Smd pad, used in BGA footprints.
Pcbnew s-expression file format parser definition.
#define MIME_BASE64_LENGTH
#define CTL_OMIT_FOOTPRINT_VERSION
Omit the version string from the (footprint) sexpr group.
Definition: pcb_plugin.h:149
#define SEXPR_BOARD_FILE_VERSION
Current s-expression file format version. 2 was the last legacy format version.
Definition: pcb_plugin.h:133
#define CTL_OMIT_TSTAMPS
Omit component time stamp (useless in library)
Definition: pcb_plugin.h:141
#define CTL_OMIT_INITIAL_COMMENTS
omit FOOTPRINT initial comments
Definition: pcb_plugin.h:142
#define CTL_OMIT_LIBNAME
Omit lib alias when saving (used for board/not library).
Definition: pcb_plugin.h:147
#define CTL_OMIT_PATH
Omit component sheet time stamp (useless in library)
Definition: pcb_plugin.h:143
#define CTL_OMIT_AT
Omit position and rotation.
Definition: pcb_plugin.h:144
#define CTL_OMIT_PAD_NETS
Omit pads net names (useless in library)
Definition: pcb_plugin.h:140
boost::ptr_map< wxString, FP_CACHE_ITEM > FP_CACHE_FOOTPRINT_MAP
Definition: pcb_plugin.h:186
#define CTL_FOR_LIBRARY
Format output for a footprint library instead of clipboard or BOARD.
Definition: pcb_plugin.h:158
@ BLIND_BURIED
#define UNDEFINED_DRILL_DIAMETER
Definition: pcb_track.h:73
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
int StrPrintf(std::string *result, const char *format,...)
This is like sprintf() but the output is appended to a std::string instead of to a character array.
Definition: richio.cpp:84
std::string FormatDouble2Str(double aValue)
Print a float number without using scientific notation and no trailing 0 This function is intended in...
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
@ CTX_QUOTED_STR
Definition: string_utils.h:57
const double IU_PER_MM
Definition: base_units.h:77
Variant of PARSE_ERROR indicating that a syntax or related error was likely caused by a file generate...
Definition: ki_exception.h:175
static const char * ShowType(LAYER_T aType)
Convert a LAYER_T enum to a string representation of the layer type.
Definition: board.cpp:535
A filename or source description, a problem input line, a line number, a byte offset,...
Definition: ki_exception.h:119
wxLogTrace helper definitions.
T NormalizeAnglePos(T Angle)
Normalize angle to be in the 0.0 .. 360.0 range: angle is in 1/10 degrees.
Definition: trigo.h:205
@ PCB_FP_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition: typeinfo.h:95
@ 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:110
@ PCB_FP_SHAPE_T
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:94
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:107
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:102
@ PCB_FP_TEXTBOX_T
class FP_TEXTBOX, wrapped text in a footprint
Definition: typeinfo.h:93
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition: typeinfo.h:108
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition: typeinfo.h:115
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition: typeinfo.h:91
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition: typeinfo.h:112
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition: typeinfo.h:90
@ PCB_FP_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition: typeinfo.h:97
@ PCB_TARGET_T
class PCB_TARGET, a target (graphic item)
Definition: typeinfo.h:111
@ PCB_FP_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition: typeinfo.h:99
@ PCB_FP_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:96
@ 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:106
@ PCB_FP_ZONE_T
class ZONE, managed by a footprint
Definition: typeinfo.h:100
@ PCB_BITMAP_T
class PCB_BITMAP, bitmap on a layer
Definition: typeinfo.h:89
@ PCB_FP_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition: typeinfo.h:98
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
@ PCB_FP_TEXT_T
class FP_TEXT, text in a footprint
Definition: typeinfo.h:92
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:103
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:101
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition: typeinfo.h:109
VECTOR2< int > VECTOR2I
Definition: vector2d.h:590
Definition of file extensions used in Kicad.
@ THERMAL
Use thermal relief for pads.
@ THT_THERMAL
Thermal relief only for THT pads.
@ NONE
Pads are not covered.
@ FULL
pads are covered by copper