KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcbnew_jobs_handler.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) 2022 Mark Roszko <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include <wx/crt.h>
22#include <wx/dir.h>
23#include <wx/zipstrm.h>
24#include <wx/filename.h>
25#include <wx/tokenzr.h>
26#include <wx/wfstream.h>
27
28#include <nlohmann/json.hpp>
29
30#include "pcbnew_jobs_handler.h"
31#include <board_loader.h>
32#include <board_commit.h>
34#include <drc/drc_engine.h>
36#include <drc/drc_item.h>
37#include <drc/drc_report.h>
40#include <footprint.h>
42#include <jobs/job_fp_upgrade.h>
58#include <jobs/job_pcb_render.h>
59#include <jobs/job_pcb_drc.h>
60#include <jobs/job_pcb_import.h>
62#include <eda_units.h>
64#include <lset.h>
65#include <cli/exit_codes.h>
71#include <tool/tool_manager.h>
72#include <tools/drc_tool.h>
73#include <filename_resolver.h>
78#include <kiface_base.h>
79#include <macros.h>
80#include <pad.h>
81#include <pcb_marker.h>
85#include <kiface_ids.h>
88#include <pcbnew_settings.h>
89#include <pcbplot.h>
90#include <pcb_plotter.h>
91#include <pcb_edit_frame.h>
92#include <pcb_track.h>
93#include <pgm_base.h>
96#include <project_pcb.h>
98#include <reporter.h>
99#include <progress_reporter.h>
101#include <export_vrml.h>
102#include <kiplatform/io.h>
109#include <dialogs/dialog_plot.h>
114#include <paths.h>
116
117#include <locale_io.h>
118#include <confirm.h>
119
120
121#ifdef _WIN32
122#ifdef TRANSPARENT
123#undef TRANSPARENT
124#endif
125#endif
126
127
129 JOB_DISPATCHER( aKiway ),
130 m_cliBoard( nullptr ),
131 m_toolManager( nullptr )
132{
133 Register( "3d", std::bind( &PCBNEW_JOBS_HANDLER::JobExportStep, this, std::placeholders::_1 ),
134 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
135 {
136 JOB_EXPORT_PCB_3D* svgJob = dynamic_cast<JOB_EXPORT_PCB_3D*>( job );
137
138 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
139 false ) );
140
141 wxCHECK( svgJob && editFrame, false );
142
143 DIALOG_EXPORT_STEP dlg( editFrame, aParent, "", svgJob );
144 return dlg.ShowModal() == wxID_OK;
145 } );
146 Register( "render",
147 std::bind( &PCBNEW_JOBS_HANDLER::JobExportRender, this, std::placeholders::_1 ),
148 []( JOB* job, wxWindow* aParent ) -> bool
149 {
150 JOB_PCB_RENDER* renderJob = dynamic_cast<JOB_PCB_RENDER*>( job );
151
152 wxCHECK( renderJob, false );
153
154 DIALOG_RENDER_JOB dlg( aParent, renderJob );
155 return dlg.ShowModal() == wxID_OK;
156 } );
157 Register( "upgrade", std::bind( &PCBNEW_JOBS_HANDLER::JobUpgrade, this, std::placeholders::_1 ),
158 []( JOB* job, wxWindow* aParent ) -> bool
159 {
160 return true;
161 } );
162 Register( "pcb_import", std::bind( &PCBNEW_JOBS_HANDLER::JobImport, this, std::placeholders::_1 ),
163 []( JOB* job, wxWindow* aParent ) -> bool
164 {
165 return true;
166 } );
167 Register( "svg", std::bind( &PCBNEW_JOBS_HANDLER::JobExportSvg, this, std::placeholders::_1 ),
168 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
169 {
170 JOB_EXPORT_PCB_SVG* svgJob = dynamic_cast<JOB_EXPORT_PCB_SVG*>( job );
171
172 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
173 false ) );
174
175 wxCHECK( svgJob && editFrame, false );
176
177 DIALOG_PLOT dlg( editFrame, aParent, svgJob );
178 return dlg.ShowModal() == wxID_OK;
179 } );
180 Register( "gencad",
181 std::bind( &PCBNEW_JOBS_HANDLER::JobExportGencad, this, std::placeholders::_1 ),
182 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
183 {
184 JOB_EXPORT_PCB_GENCAD* gencadJob = dynamic_cast<JOB_EXPORT_PCB_GENCAD*>( job );
185
186 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
187 false ) );
188
189 wxCHECK( gencadJob && editFrame, false );
190
191 DIALOG_GENCAD_EXPORT_OPTIONS dlg( editFrame, gencadJob->GetSettingsDialogTitle(), gencadJob );
192 return dlg.ShowModal() == wxID_OK;
193 } );
194 Register( "dxf", std::bind( &PCBNEW_JOBS_HANDLER::JobExportDxf, this, std::placeholders::_1 ),
195 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
196 {
197 JOB_EXPORT_PCB_DXF* dxfJob = dynamic_cast<JOB_EXPORT_PCB_DXF*>( job );
198
199 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
200 false ) );
201
202 wxCHECK( dxfJob && editFrame, false );
203
204 DIALOG_PLOT dlg( editFrame, aParent, dxfJob );
205 return dlg.ShowModal() == wxID_OK;
206 } );
207 Register( "pdf", std::bind( &PCBNEW_JOBS_HANDLER::JobExportPdf, this, std::placeholders::_1 ),
208 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
209 {
210 JOB_EXPORT_PCB_PDF* pdfJob = dynamic_cast<JOB_EXPORT_PCB_PDF*>( job );
211
212 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
213 false ) );
214
215 wxCHECK( pdfJob && editFrame, false );
216
217 DIALOG_PLOT dlg( editFrame, aParent, pdfJob );
218 return dlg.ShowModal() == wxID_OK;
219 } );
220 Register( "ps", std::bind( &PCBNEW_JOBS_HANDLER::JobExportPs, this, std::placeholders::_1 ),
221 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
222 {
223 JOB_EXPORT_PCB_PS* psJob = dynamic_cast<JOB_EXPORT_PCB_PS*>( job );
224
225 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
226 false ) );
227
228 wxCHECK( psJob && editFrame, false );
229
230 DIALOG_PLOT dlg( editFrame, aParent, psJob );
231 return dlg.ShowModal() == wxID_OK;
232 } );
233 Register( "stats",
234 std::bind( &PCBNEW_JOBS_HANDLER::JobExportStats, this, std::placeholders::_1 ),
235 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
236 {
237 JOB_EXPORT_PCB_STATS* statsJob = dynamic_cast<JOB_EXPORT_PCB_STATS*>( job );
238
239 PCB_EDIT_FRAME* editFrame =
240 dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR, false ) );
241
242 wxCHECK( statsJob && editFrame, false );
243
244 if( statsJob->m_filename.IsEmpty() && editFrame->GetBoard() )
245 {
246 wxFileName boardName = editFrame->GetBoard()->GetFileName();
247 statsJob->m_filename = boardName.GetFullPath();
248 }
249
250 wxWindow* parent = aParent ? aParent : static_cast<wxWindow*>( editFrame );
251
252 DIALOG_BOARD_STATS_JOB dlg( parent, statsJob );
253
254 return dlg.ShowModal() == wxID_OK;
255 } );
256 Register( "gerber",
257 std::bind( &PCBNEW_JOBS_HANDLER::JobExportGerber, this, std::placeholders::_1 ),
258 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
259 {
260 JOB_EXPORT_PCB_GERBER* gJob = dynamic_cast<JOB_EXPORT_PCB_GERBER*>( job );
261
262 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
263 false ) );
264
265 wxCHECK( gJob && editFrame, false );
266
267 DIALOG_PLOT dlg( editFrame, aParent, gJob );
268 return dlg.ShowModal() == wxID_OK;
269 } );
270 Register( "gerbers",
271 std::bind( &PCBNEW_JOBS_HANDLER::JobExportGerbers, this, std::placeholders::_1 ),
272 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
273 {
274 JOB_EXPORT_PCB_GERBERS* gJob = dynamic_cast<JOB_EXPORT_PCB_GERBERS*>( job );
275
276 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
277 false ) );
278
279 wxCHECK( gJob && editFrame, false );
280
281 DIALOG_PLOT dlg( editFrame, aParent, gJob );
282 return dlg.ShowModal() == wxID_OK;
283 } );
284 Register( "hpgl",
285 [&]( JOB* aJob )
286 {
287 m_reporter->Report( _( "Plotting to HPGL is no longer supported as of KiCad 10.0.\n" ),
290 },
291 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
292 {
293 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
294 false ) );
295
296 wxCHECK( editFrame, false );
297
298 DisplayErrorMessage( editFrame,
299 _( "Plotting to HPGL is no longer supported as of KiCad 10.0." ) );
300 return false;
301 } );
302 Register( "drill",
303 std::bind( &PCBNEW_JOBS_HANDLER::JobExportDrill, this, std::placeholders::_1 ),
304 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
305 {
306 JOB_EXPORT_PCB_DRILL* drillJob = dynamic_cast<JOB_EXPORT_PCB_DRILL*>( job );
307
308 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
309 false ) );
310
311 wxCHECK( drillJob && editFrame, false );
312
313 DIALOG_GENDRILL dlg( editFrame, drillJob, aParent );
314 return dlg.ShowModal() == wxID_OK;
315 } );
316 Register( "pos", std::bind( &PCBNEW_JOBS_HANDLER::JobExportPos, this, std::placeholders::_1 ),
317 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
318 {
319 JOB_EXPORT_PCB_POS* posJob = dynamic_cast<JOB_EXPORT_PCB_POS*>( job );
320
321 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
322 false ) );
323
324 wxCHECK( posJob && editFrame, false );
325
326 DIALOG_GEN_FOOTPRINT_POSITION dlg( posJob, editFrame, aParent );
327 return dlg.ShowModal() == wxID_OK;
328 } );
329 Register( "fpupgrade",
330 std::bind( &PCBNEW_JOBS_HANDLER::JobExportFpUpgrade, this, std::placeholders::_1 ),
331 []( JOB* job, wxWindow* aParent ) -> bool
332 {
333 return true;
334 } );
335 Register( "fpsvg",
336 std::bind( &PCBNEW_JOBS_HANDLER::JobExportFpSvg, this, std::placeholders::_1 ),
337 []( JOB* job, wxWindow* aParent ) -> bool
338 {
339 return true;
340 } );
341 Register( "drc", std::bind( &PCBNEW_JOBS_HANDLER::JobExportDrc, this, std::placeholders::_1 ),
342 []( JOB* job, wxWindow* aParent ) -> bool
343 {
344 JOB_PCB_DRC* drcJob = dynamic_cast<JOB_PCB_DRC*>( job );
345
346 wxCHECK( drcJob, false );
347
348 DIALOG_DRC_JOB_CONFIG dlg( aParent, drcJob );
349 return dlg.ShowModal() == wxID_OK;
350 } );
351 Register( "ipc2581",
352 std::bind( &PCBNEW_JOBS_HANDLER::JobExportIpc2581, this, std::placeholders::_1 ),
353 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
354 {
355 JOB_EXPORT_PCB_IPC2581* ipcJob = dynamic_cast<JOB_EXPORT_PCB_IPC2581*>( job );
356
357 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
358 false ) );
359
360 wxCHECK( ipcJob && editFrame, false );
361
362 DIALOG_EXPORT_2581 dlg( ipcJob, editFrame, aParent );
363 return dlg.ShowModal() == wxID_OK;
364 } );
365 Register( "ipcd356",
366 std::bind( &PCBNEW_JOBS_HANDLER::JobExportIpcD356, this, std::placeholders::_1 ),
367 []( JOB* job, wxWindow* aParent ) -> bool
368 {
369 return true;
370 } );
371 Register( "odb",
372 std::bind( &PCBNEW_JOBS_HANDLER::JobExportOdb, this, std::placeholders::_1 ),
373 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
374 {
375 JOB_EXPORT_PCB_ODB* odbJob = dynamic_cast<JOB_EXPORT_PCB_ODB*>( job );
376
377 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
378 false ) );
379
380 wxCHECK( odbJob && editFrame, false );
381
382 DIALOG_EXPORT_ODBPP dlg( odbJob, editFrame, aParent );
383 return dlg.ShowModal() == wxID_OK;
384 } );
385}
386
387
391
392
394{
395 TOOL_MANAGER* toolManager = nullptr;
396 if( Pgm().IsGUI() )
397 {
398 // we assume the PCB we are working on here is the one in the frame
399 // so use the frame's tool manager
400 PCB_EDIT_FRAME* editFrame = (PCB_EDIT_FRAME*) m_kiway->Player( FRAME_PCB_EDITOR, false );
401 if( editFrame )
402 toolManager = editFrame->GetToolManager();
403 }
404 else
405 {
406 if( m_toolManager == nullptr )
407 {
408 m_toolManager = std::make_unique<TOOL_MANAGER>();
409 }
410
411 toolManager = m_toolManager.get();
412
413 toolManager->SetEnvironment( aBrd, nullptr, nullptr, Kiface().KifaceSettings(), nullptr );
414 }
415 return toolManager;
416}
417
418
419BOARD* PCBNEW_JOBS_HANDLER::getBoard( const wxString& aPath )
420{
421 BOARD* brd = nullptr;
422 SETTINGS_MANAGER& settingsManager = Pgm().GetSettingsManager();
423
424 auto getProjectForBoard =
425 [&]( const wxString& aBoardPath ) -> PROJECT*
426 {
427 wxFileName pro = aBoardPath;
428 pro.SetExt( FILEEXT::ProjectFileExtension );
429 pro.MakeAbsolute();
430
431 PROJECT* project = settingsManager.GetProject( pro.GetFullPath() );
432
433 if( !project )
434 {
435 settingsManager.LoadProject( pro.GetFullPath(), true );
436 project = settingsManager.GetProject( pro.GetFullPath() );
437 }
438
439 return project;
440 };
441
442 auto loadBoardFromPath =
443 [&]( const wxString& aBoardPath ) -> BOARD*
444 {
445 PROJECT* project = getProjectForBoard( aBoardPath );
446
447 PCB_IO_MGR::PCB_FILE_T pluginType =
449
450 if( !project || pluginType == PCB_IO_MGR::FILE_TYPE_NONE )
451 return nullptr;
452
453 try
454 {
455 std::unique_ptr<BOARD> loadedBoard = BOARD_LOADER::Load( aBoardPath, pluginType,
456 project );
457 return loadedBoard.release();
458 }
459 catch ( ... )
460 {
461 return nullptr;
462 }
463 };
464
465 if( !Pgm().IsGUI() && Pgm().GetSettingsManager().IsProjectOpen() )
466 {
467 wxString pcbPath = aPath;
468
469 if( pcbPath.IsEmpty() )
470 {
471 wxFileName path = Pgm().GetSettingsManager().Prj().GetProjectFullName();
473 path.MakeAbsolute();
474 pcbPath = path.GetFullPath();
475 }
476
477 if( !m_cliBoard )
478 m_cliBoard = loadBoardFromPath( pcbPath );
479
480 brd = m_cliBoard;
481 }
482 else if( Pgm().IsGUI() && Pgm().GetSettingsManager().IsProjectOpen() )
483 {
484 PCB_EDIT_FRAME* editFrame = (PCB_EDIT_FRAME*) m_kiway->Player( FRAME_PCB_EDITOR, false );
485
486 if( editFrame )
487 brd = editFrame->GetBoard();
488 }
489 else
490 {
491 brd = loadBoardFromPath( aPath );
492 }
493
494 if( !brd )
495 m_reporter->Report( _( "Failed to load board\n" ), RPT_SEVERITY_ERROR );
496
497 return brd;
498}
499
500
501LSEQ PCBNEW_JOBS_HANDLER::convertLayerArg( wxString& aLayerString, BOARD* aBoard ) const
502{
503 std::map<wxString, LSET> layerUserMasks;
504 std::map<wxString, LSET> layerMasks;
505 std::map<wxString, LSET> layerGuiMasks;
506
507 // Build list of layer names and their layer mask:
508 for( PCB_LAYER_ID layer : LSET::AllLayersMask() )
509 {
510 // Add user layer name
511 if( aBoard )
512 layerUserMasks[ aBoard->GetLayerName( layer ) ] = LSET( { layer } );
513
514 // Add layer name used in pcb files
515 layerMasks[ LSET::Name( layer ) ] = LSET( { layer } );
516 // Add layer name using GUI canonical layer name
517 layerGuiMasks[ LayerName( layer ) ] = LSET( { layer } );
518 }
519
520 // Add list of grouped layer names used in pcb files
521 layerMasks[ wxT( "*" ) ] = LSET::AllLayersMask();
522 layerMasks[ wxT( "*.Cu" ) ] = LSET::AllCuMask();
523 layerMasks[ wxT( "*In.Cu" ) ] = LSET::InternalCuMask();
524 layerMasks[ wxT( "F&B.Cu" ) ] = LSET( { F_Cu, B_Cu } );
525 layerMasks[ wxT( "*.Adhes" ) ] = LSET( { B_Adhes, F_Adhes } );
526 layerMasks[ wxT( "*.Paste" ) ] = LSET( { B_Paste, F_Paste } );
527 layerMasks[ wxT( "*.Mask" ) ] = LSET( { B_Mask, F_Mask } );
528 layerMasks[ wxT( "*.SilkS" ) ] = LSET( { B_SilkS, F_SilkS } );
529 layerMasks[ wxT( "*.Fab" ) ] = LSET( { B_Fab, F_Fab } );
530 layerMasks[ wxT( "*.CrtYd" ) ] = LSET( { B_CrtYd, F_CrtYd } );
531
532 // Add list of grouped layer names using GUI canonical layer names
533 layerGuiMasks[ wxT( "*.Adhesive" ) ] = LSET( { B_Adhes, F_Adhes } );
534 layerGuiMasks[ wxT( "*.Silkscreen" ) ] = LSET( { B_SilkS, F_SilkS } );
535 layerGuiMasks[ wxT( "*.Courtyard" ) ] = LSET( { B_CrtYd, F_CrtYd } );
536
537 LSEQ layerMask;
538
539 auto pushLayers =
540 [&]( const LSET& layerSet )
541 {
542 for( PCB_LAYER_ID layer : layerSet.Seq() )
543 layerMask.push_back( layer );
544 };
545
546 if( !aLayerString.IsEmpty() )
547 {
548 wxStringTokenizer layerTokens( aLayerString, "," );
549
550 while( layerTokens.HasMoreTokens() )
551 {
552 std::string token = TO_UTF8( layerTokens.GetNextToken().Trim( true ).Trim( false ) );
553
554 if( layerUserMasks.contains( token ) )
555 pushLayers( layerUserMasks.at( token ) );
556 else if( layerMasks.count( token ) )
557 pushLayers( layerMasks.at( token ) );
558 else if( layerGuiMasks.count( token ) )
559 pushLayers( layerGuiMasks.at( token ) );
560 else
561 m_reporter->Report( wxString::Format( _( "Invalid layer name '%s'\n" ), token ) );
562 }
563 }
564
565 return layerMask;
566}
567
568
570{
571 JOB_EXPORT_PCB_3D* aStepJob = dynamic_cast<JOB_EXPORT_PCB_3D*>( aJob );
572
573 if( aStepJob == nullptr )
575
576 BOARD* brd = getBoard( aStepJob->m_filename );
577
578 if( !brd )
580
581 if( !aStepJob->m_variant.IsEmpty() )
582 brd->SetCurrentVariant( aStepJob->m_variant );
583
584 if( aStepJob->GetConfiguredOutputPath().IsEmpty() )
585 {
586 wxFileName fn = brd->GetFileName();
587 fn.SetName( fn.GetName() );
588
589 switch( aStepJob->m_format )
590 {
600 default:
601 m_reporter->Report( _( "Unknown export format" ), RPT_SEVERITY_ERROR );
602 return CLI::EXIT_CODES::ERR_UNKNOWN; // shouldnt have gotten here
603 }
604
605 aStepJob->SetWorkingOutputPath( fn.GetFullName() );
606 }
607
608 wxString outPath = resolveJobOutputPath( aJob, brd );
609
610 if( !PATHS::EnsurePathExists( outPath, true ) )
611 {
612 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
614 }
615
617 {
618
619 double scale = 0.0;
620 switch ( aStepJob->m_vrmlUnits )
621 {
622 case JOB_EXPORT_PCB_3D::VRML_UNITS::MM: scale = 1.0; break;
623 case JOB_EXPORT_PCB_3D::VRML_UNITS::METERS: scale = 0.001; break;
624 case JOB_EXPORT_PCB_3D::VRML_UNITS::TENTHS: scale = 10.0 / 25.4; break;
625 case JOB_EXPORT_PCB_3D::VRML_UNITS::INCH: scale = 1.0 / 25.4; break;
626 }
627
628 EXPORTER_VRML vrmlExporter( brd );
629 wxString messages;
630
631 double originX = pcbIUScale.IUTomm( aStepJob->m_3dparams.m_Origin.x );
632 double originY = pcbIUScale.IUTomm( aStepJob->m_3dparams.m_Origin.y );
633
634 if( !aStepJob->m_hasUserOrigin )
635 {
636 BOX2I bbox = brd->ComputeBoundingBox( true, true );
637 originX = pcbIUScale.IUTomm( bbox.GetCenter().x );
638 originY = pcbIUScale.IUTomm( bbox.GetCenter().y );
639 }
640
641 bool success = vrmlExporter.ExportVRML_File( brd->GetProject(),
642 &messages,
643 outPath,
644 scale,
646 aStepJob->m_3dparams.m_IncludeDNP,
647 !aStepJob->m_vrmlModelDir.IsEmpty(),
648 aStepJob->m_vrmlRelativePaths,
649 aStepJob->m_vrmlModelDir,
650 originX,
651 originY );
652
653 if ( success )
654 {
655 m_reporter->Report( wxString::Format( _( "Successfully exported VRML to %s" ),
656 outPath ),
658 }
659 else
660 {
661 m_reporter->Report( _( "Error exporting VRML" ), RPT_SEVERITY_ERROR );
663 }
664 }
665 else
666 {
667 EXPORTER_STEP_PARAMS params = aStepJob->m_3dparams;
668
669 switch( aStepJob->m_format )
670 {
680 default:
681 m_reporter->Report( _( "Unknown export format" ), RPT_SEVERITY_ERROR );
682 return CLI::EXIT_CODES::ERR_UNKNOWN; // shouldnt have gotten here
683 }
684
685 EXPORTER_STEP stepExporter( brd, params, m_reporter );
686 stepExporter.m_outputFile = aStepJob->GetFullOutputPath( brd->GetProject() );
687
688 if( !stepExporter.Export() )
690 }
691
692 return CLI::EXIT_CODES::OK;
693}
694
695
697{
698 JOB_PCB_RENDER* aRenderJob = dynamic_cast<JOB_PCB_RENDER*>( aJob );
699
700 if( aRenderJob == nullptr )
702
703 // Reject width and height being invalid
704 // Final bit of sanity because this can blow things up
705 if( aRenderJob->m_width <= 0 || aRenderJob->m_height <= 0 )
706 {
707 m_reporter->Report( _( "Invalid image dimensions" ), RPT_SEVERITY_ERROR );
709 }
710
711 BOARD* brd = getBoard( aRenderJob->m_filename );
712
713 if( !brd )
715
716 if( aRenderJob->GetConfiguredOutputPath().IsEmpty() )
717 {
718 wxFileName fn = brd->GetFileName();
719
720 switch( aRenderJob->m_format )
721 {
724 default:
725 m_reporter->Report( _( "Unknown export format" ), RPT_SEVERITY_ERROR );
726 return CLI::EXIT_CODES::ERR_UNKNOWN; // shouldnt have gotten here
727 }
728
729 // set the name to board name + "side", its lazy but its hard to generate anything truely unique
730 // incase someone is doing this in a jobset with multiple jobs, they should be setting the output themselves
731 // or we do a hash based on all the options
732 fn.SetName( wxString::Format( "%s-%d", fn.GetName(), static_cast<int>( aRenderJob->m_side ) ) );
733
734 aRenderJob->SetWorkingOutputPath( fn.GetFullName() );
735 }
736
737 wxString outPath = resolveJobOutputPath( aJob, brd );
738
739 if( !PATHS::EnsurePathExists( outPath, true ) )
740 {
741 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
743 }
744
745 BOARD_ADAPTER boardAdapter;
746
747 boardAdapter.SetBoard( brd );
748 boardAdapter.m_IsBoardView = false;
749
751
753 {
754 cfg.m_Render = userCfg->m_Render;
755 cfg.m_Camera = userCfg->m_Camera;
756 cfg.m_LayerPresets = userCfg->m_LayerPresets;
757 }
758
759 if( aRenderJob->m_appearancePreset.empty() )
760 {
761 // Force display 3D models
763 cfg.m_Render.show_footprints_dnp = true;
767 }
768
769 if( aRenderJob->m_quality == JOB_PCB_RENDER::QUALITY::BASIC )
770 {
771 // Silkscreen is pixelated without antialiasing
773
774 cfg.m_Render.raytrace_backfloor = aRenderJob->m_floor;
775 cfg.m_Render.raytrace_post_processing = aRenderJob->m_floor;
776
778 cfg.m_Render.raytrace_reflections = false;
779 cfg.m_Render.raytrace_shadows = aRenderJob->m_floor;
780
781 // Better colors
783
784 // Tracks below soldermask are not visible without refractions
787 }
788 else if( aRenderJob->m_quality == JOB_PCB_RENDER::QUALITY::HIGH )
789 {
791 cfg.m_Render.raytrace_backfloor = true;
795 cfg.m_Render.raytrace_shadows = true;
798 }
799 else if( aRenderJob->m_quality == JOB_PCB_RENDER::QUALITY::JOB_SETTINGS )
800 {
802 cfg.m_Render.raytrace_backfloor = aRenderJob->m_floor;
805 }
806
808 aRenderJob->m_lightTopIntensity.y,
809 aRenderJob->m_lightTopIntensity.z, 1.0 );
810
812 aRenderJob->m_lightBottomIntensity.y,
813 aRenderJob->m_lightBottomIntensity.z, 1.0 );
814
816 aRenderJob->m_lightCameraIntensity.y,
817 aRenderJob->m_lightCameraIntensity.z, 1.0 );
818
819 COLOR4D lightColor( aRenderJob->m_lightSideIntensity.x,
820 aRenderJob->m_lightSideIntensity.y,
821 aRenderJob->m_lightSideIntensity.z, 1.0 );
822
824 lightColor, lightColor, lightColor, lightColor,
825 lightColor, lightColor, lightColor, lightColor,
826 };
827
828 int sideElevation = aRenderJob->m_lightSideElevation;
829
831 sideElevation, sideElevation, sideElevation, sideElevation,
832 -sideElevation, -sideElevation, -sideElevation, -sideElevation,
833 };
834
836 45, 135, 225, 315, 45, 135, 225, 315,
837 };
838
839 cfg.m_CurrentPreset = aRenderJob->m_appearancePreset;
841 boardAdapter.m_Cfg = &cfg;
842
843 // Apply the preset's layer visibility and colors to the render settings
844 if( !aRenderJob->m_appearancePreset.empty() )
845 {
846 wxString presetName = wxString::FromUTF8( aRenderJob->m_appearancePreset );
847
848 if( presetName == FOLLOW_PCB || presetName == FOLLOW_PLOT_SETTINGS )
849 {
850 boardAdapter.SetVisibleLayers( boardAdapter.GetVisibleLayers() );
851 }
852 else if( LAYER_PRESET_3D* preset = cfg.FindPreset( presetName ) )
853 {
854 boardAdapter.SetVisibleLayers( preset->layers );
855 boardAdapter.SetLayerColors( preset->colors );
856
857 if( preset->name.Lower() == _( "legacy colors" ) )
858 cfg.m_UseStackupColors = false;
859 }
860 }
861
864 && aRenderJob->m_format == JOB_PCB_RENDER::FORMAT::PNG ) )
865 {
866 boardAdapter.m_ColorOverrides[LAYER_3D_BACKGROUND_TOP] = COLOR4D( 1.0, 1.0, 1.0, 0.0 );
867 boardAdapter.m_ColorOverrides[LAYER_3D_BACKGROUND_BOTTOM] = COLOR4D( 1.0, 1.0, 1.0, 0.0 );
868 }
869
871
872 static std::map<JOB_PCB_RENDER::SIDE, VIEW3D_TYPE> s_viewCmdMap = {
879 };
880
883
884 wxSize windowSize( aRenderJob->m_width, aRenderJob->m_height );
885 TRACK_BALL camera( 2 * RANGE_SCALE_3D );
886
887 camera.SetProjection( projection );
888 camera.SetCurWindowSize( windowSize );
889
890 RENDER_3D_RAYTRACE_RAM raytrace( boardAdapter, camera );
891 raytrace.SetCurWindowSize( windowSize );
892
893 for( bool first = true; raytrace.Redraw( false, m_reporter, m_reporter ); first = false )
894 {
895 if( first )
896 {
897 const float cmTo3D = boardAdapter.BiuTo3dUnits() * pcbIUScale.mmToIU( 10.0 );
898
899 // First redraw resets lookat point to the board center, so set up the camera here
900 camera.ViewCommand_T1( s_viewCmdMap[aRenderJob->m_side] );
901
902 camera.SetLookAtPos_T1( camera.GetLookAtPos_T1() + SFVEC3F( aRenderJob->m_pivot.x,
903 aRenderJob->m_pivot.y,
904 aRenderJob->m_pivot.z ) * cmTo3D );
905
906 camera.Pan_T1( SFVEC3F( aRenderJob->m_pan.x, aRenderJob->m_pan.y, aRenderJob->m_pan.z ) );
907
908 camera.Zoom_T1( aRenderJob->m_zoom );
909
910 camera.RotateX_T1( DEG2RAD( aRenderJob->m_rotation.x ) );
911 camera.RotateY_T1( DEG2RAD( aRenderJob->m_rotation.y ) );
912 camera.RotateZ_T1( DEG2RAD( aRenderJob->m_rotation.z ) );
913
914 camera.Interpolate( 1.0f );
915 camera.SetT0_and_T1_current_T();
916 camera.ParametersChanged();
917 }
918 }
919
920 uint8_t* rgbaBuffer = raytrace.GetBuffer();
921 wxSize realSize = raytrace.GetRealBufferSize();
922 bool success = !!rgbaBuffer;
923
924 if( rgbaBuffer )
925 {
926 const unsigned int wxh = realSize.x * realSize.y;
927
928 unsigned char* rgbBuffer = (unsigned char*) malloc( wxh * 3 );
929 unsigned char* alphaBuffer = (unsigned char*) malloc( wxh );
930
931 unsigned char* rgbaPtr = rgbaBuffer;
932 unsigned char* rgbPtr = rgbBuffer;
933 unsigned char* alphaPtr = alphaBuffer;
934
935 for( int y = 0; y < realSize.y; y++ )
936 {
937 for( int x = 0; x < realSize.x; x++ )
938 {
939 rgbPtr[0] = rgbaPtr[0];
940 rgbPtr[1] = rgbaPtr[1];
941 rgbPtr[2] = rgbaPtr[2];
942 alphaPtr[0] = rgbaPtr[3];
943
944 rgbaPtr += 4;
945 rgbPtr += 3;
946 alphaPtr += 1;
947 }
948 }
949
950 wxImage image( realSize );
951 image.SetData( rgbBuffer );
952 image.SetAlpha( alphaBuffer );
953 image = image.Mirror( false );
954
955 image.SetOption( wxIMAGE_OPTION_QUALITY, 90 );
956 image.SaveFile( outPath, aRenderJob->m_format == JOB_PCB_RENDER::FORMAT::PNG ? wxBITMAP_TYPE_PNG
957 : wxBITMAP_TYPE_JPEG );
958 }
959
960 if( success )
961 {
962 m_reporter->Report( _( "Successfully created 3D render image" ) + wxS( "\n" ), RPT_SEVERITY_INFO );
963 return CLI::EXIT_CODES::OK;
964 }
965 else
966 {
967 m_reporter->Report( _( "Error creating 3D render image" ) + wxS( "\n" ), RPT_SEVERITY_ERROR );
969 }
970}
971
972
974{
975 JOB_EXPORT_PCB_SVG* aSvgJob = dynamic_cast<JOB_EXPORT_PCB_SVG*>( aJob );
976
977 if( aSvgJob == nullptr )
979
980 BOARD* brd = getBoard( aSvgJob->m_filename );
981 TOOL_MANAGER* toolManager = getToolManager( brd );
982
983 if( !brd )
985
986 if( !aSvgJob->m_variant.IsEmpty() )
987 brd->SetCurrentVariant( aSvgJob->m_variant );
988
990 {
991 if( aSvgJob->GetConfiguredOutputPath().IsEmpty() )
992 {
993 wxFileName fn = brd->GetFileName();
994 fn.SetName( fn.GetName() );
996
997 aSvgJob->SetWorkingOutputPath( fn.GetFullName() );
998 }
999 }
1000
1001 wxString outPath = resolveJobOutputPath( aJob, brd, &aSvgJob->m_drawingSheet );
1002
1004 {
1005 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1007 }
1008
1009 if( aSvgJob->m_checkZonesBeforePlot )
1010 {
1011 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
1012 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
1013
1014 toolManager->GetTool<ZONE_FILLER_TOOL>()->FillAllZones( nullptr, m_progressReporter, true );
1015 }
1016
1017 if( aSvgJob->m_argLayers )
1018 aSvgJob->m_plotLayerSequence = convertLayerArg( aSvgJob->m_argLayers.value(), brd );
1019
1020 if( aSvgJob->m_argCommonLayers )
1021 aSvgJob->m_plotOnAllLayersSequence = convertLayerArg( aSvgJob->m_argCommonLayers.value(), brd );
1022
1023 if( aSvgJob->m_plotLayerSequence.size() < 1 )
1024 {
1025 m_reporter->Report( _( "At least one layer must be specified\n" ), RPT_SEVERITY_ERROR );
1027 }
1028
1029 PCB_PLOT_PARAMS plotOpts;
1030 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, aSvgJob, *m_reporter );
1031
1032 PCB_PLOTTER plotter( brd, m_reporter, plotOpts );
1033
1034 std::optional<wxString> layerName;
1035 std::optional<wxString> sheetName;
1036 std::optional<wxString> sheetPath;
1037 std::vector<wxString> outputPaths;
1038
1040 {
1041 if( aJob->GetVarOverrides().contains( wxT( "LAYER" ) ) )
1042 layerName = aSvgJob->GetVarOverrides().at( wxT( "LAYER" ) );
1043
1044 if( aJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) )
1045 sheetName = aSvgJob->GetVarOverrides().at( wxT( "SHEETNAME" ) );
1046
1047 if( aJob->GetVarOverrides().contains( wxT( "SHEETPATH" ) ) )
1048 sheetPath = aSvgJob->GetVarOverrides().at( wxT( "SHEETPATH" ) );
1049 }
1050
1051 if( !plotter.Plot( outPath, aSvgJob->m_plotLayerSequence, aSvgJob->m_plotOnAllLayersSequence,
1053 layerName, sheetName, sheetPath, &outputPaths ) )
1054 {
1056 }
1057
1058 for( const wxString& outputPath : outputPaths )
1059 aSvgJob->AddOutput( outputPath );
1060
1061 return CLI::EXIT_CODES::OK;
1062}
1063
1064
1066{
1067 JOB_EXPORT_PCB_DXF* aDxfJob = dynamic_cast<JOB_EXPORT_PCB_DXF*>( aJob );
1068
1069 if( aDxfJob == nullptr )
1071
1072 BOARD* brd = getBoard( aDxfJob->m_filename );
1073
1074 if( !brd )
1076
1077 if( !aDxfJob->m_variant.IsEmpty() )
1078 brd->SetCurrentVariant( aDxfJob->m_variant );
1079
1080 TOOL_MANAGER* toolManager = getToolManager( brd );
1081
1082 if( aDxfJob->m_checkZonesBeforePlot )
1083 {
1084 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
1085 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
1086
1087 toolManager->GetTool<ZONE_FILLER_TOOL>()->FillAllZones( nullptr, m_progressReporter, true );
1088 }
1089
1090 if( aDxfJob->m_argLayers )
1091 aDxfJob->m_plotLayerSequence = convertLayerArg( aDxfJob->m_argLayers.value(), brd );
1092
1093 if( aDxfJob->m_argCommonLayers )
1094 aDxfJob->m_plotOnAllLayersSequence = convertLayerArg( aDxfJob->m_argCommonLayers.value(), brd );
1095
1096 if( aDxfJob->m_plotLayerSequence.size() < 1 )
1097 {
1098 m_reporter->Report( _( "At least one layer must be specified\n" ), RPT_SEVERITY_ERROR );
1100 }
1101
1103 {
1104 if( aDxfJob->GetConfiguredOutputPath().IsEmpty() )
1105 {
1106 wxFileName fn = brd->GetFileName();
1107 fn.SetName( fn.GetName() );
1109
1110 aDxfJob->SetWorkingOutputPath( fn.GetFullName() );
1111 }
1112 }
1113
1114 wxString outPath = resolveJobOutputPath( aJob, brd, &aDxfJob->m_drawingSheet );
1115
1117 {
1118 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1120 }
1121
1122 PCB_PLOT_PARAMS plotOpts;
1123 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, aDxfJob, *m_reporter);
1124
1125 PCB_PLOTTER plotter( brd, m_reporter, plotOpts );
1126
1127 std::optional<wxString> layerName;
1128 std::optional<wxString> sheetName;
1129 std::optional<wxString> sheetPath;
1130
1132 {
1133 if( aJob->GetVarOverrides().contains( wxT( "LAYER" ) ) )
1134 layerName = aDxfJob->GetVarOverrides().at( wxT( "LAYER" ) );
1135
1136 if( aJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) )
1137 sheetName = aDxfJob->GetVarOverrides().at( wxT( "SHEETNAME" ) );
1138
1139 if( aJob->GetVarOverrides().contains( wxT( "SHEETPATH" ) ) )
1140 sheetPath = aDxfJob->GetVarOverrides().at( wxT( "SHEETPATH" ) );
1141 }
1142
1143 std::vector<wxString> outputPaths;
1144
1145 if( !plotter.Plot( outPath, aDxfJob->m_plotLayerSequence, aDxfJob->m_plotOnAllLayersSequence,
1147 layerName, sheetName, sheetPath, &outputPaths ) )
1148 {
1150 }
1151
1152 for( const wxString& outputPath : outputPaths )
1153 aJob->AddOutput( outputPath );
1154
1155 return CLI::EXIT_CODES::OK;
1156}
1157
1158
1160{
1161 bool plotAllLayersOneFile = false;
1162 JOB_EXPORT_PCB_PDF* pdfJob = dynamic_cast<JOB_EXPORT_PCB_PDF*>( aJob );
1163
1164 if( pdfJob == nullptr )
1166
1167 BOARD* brd = getBoard( pdfJob->m_filename );
1168
1169 if( !brd )
1171
1172 if( !pdfJob->m_variant.IsEmpty() )
1173 brd->SetCurrentVariant( pdfJob->m_variant );
1174
1175 TOOL_MANAGER* toolManager = getToolManager( brd );
1176
1177 if( pdfJob->m_checkZonesBeforePlot )
1178 {
1179 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
1180 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
1181
1182 toolManager->GetTool<ZONE_FILLER_TOOL>()->FillAllZones( nullptr, m_progressReporter, true );
1183 }
1184
1185 if( pdfJob->m_argLayers )
1186 pdfJob->m_plotLayerSequence = convertLayerArg( pdfJob->m_argLayers.value(), brd );
1187
1188 if( pdfJob->m_argCommonLayers )
1189 pdfJob->m_plotOnAllLayersSequence = convertLayerArg( pdfJob->m_argCommonLayers.value(), brd );
1190
1192 plotAllLayersOneFile = true;
1193
1194 if( pdfJob->m_plotLayerSequence.size() < 1 )
1195 {
1196 m_reporter->Report( _( "At least one layer must be specified\n" ), RPT_SEVERITY_ERROR );
1198 }
1199
1200 const bool outputIsSingle = plotAllLayersOneFile || pdfJob->m_pdfSingle;
1201
1202 if( outputIsSingle && pdfJob->GetConfiguredOutputPath().IsEmpty() )
1203 {
1204 wxFileName fn = brd->GetFileName();
1205 fn.SetName( fn.GetName() );
1207
1208 pdfJob->SetWorkingOutputPath( fn.GetFullName() );
1209 }
1210
1211 wxString outPath = resolveJobOutputPath( pdfJob, brd, &pdfJob->m_drawingSheet );
1212
1213 PCB_PLOT_PARAMS plotOpts;
1214 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, pdfJob, *m_reporter );
1215
1216 PCB_PLOTTER pcbPlotter( brd, m_reporter, plotOpts );
1217
1218 if( !PATHS::EnsurePathExists( outPath, outputIsSingle ) )
1219 {
1220 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1222 }
1223
1224 std::optional<wxString> layerName;
1225 std::optional<wxString> sheetName;
1226 std::optional<wxString> sheetPath;
1227
1228 if( plotAllLayersOneFile )
1229 {
1230 if( pdfJob->GetVarOverrides().contains( wxT( "LAYER" ) ) )
1231 layerName = pdfJob->GetVarOverrides().at( wxT( "LAYER" ) );
1232
1233 if( pdfJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) )
1234 sheetName = pdfJob->GetVarOverrides().at( wxT( "SHEETNAME" ) );
1235
1236 if( pdfJob->GetVarOverrides().contains( wxT( "SHEETPATH" ) ) )
1237 sheetPath = pdfJob->GetVarOverrides().at( wxT( "SHEETPATH" ) );
1238 }
1239
1240 std::vector<wxString> outputPaths;
1241
1242 if( !pcbPlotter.Plot( outPath, pdfJob->m_plotLayerSequence,
1243 pdfJob->m_plotOnAllLayersSequence, false, outputIsSingle,
1244 layerName, sheetName, sheetPath, &outputPaths ) )
1245 {
1247 }
1248
1249 for( const wxString& outputPath : outputPaths )
1250 aJob->AddOutput( outputPath );
1251
1252 return CLI::EXIT_CODES::OK;
1253}
1254
1255
1257{
1258 JOB_EXPORT_PCB_PS* psJob = dynamic_cast<JOB_EXPORT_PCB_PS*>( aJob );
1259
1260 if( psJob == nullptr )
1262
1263 BOARD* brd = getBoard( psJob->m_filename );
1264
1265 if( !brd )
1267
1268 if( !psJob->m_variant.IsEmpty() )
1269 brd->SetCurrentVariant( psJob->m_variant );
1270
1271 TOOL_MANAGER* toolManager = getToolManager( brd );
1272
1273 if( psJob->m_checkZonesBeforePlot )
1274 {
1275 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
1276 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
1277
1278 toolManager->GetTool<ZONE_FILLER_TOOL>()->FillAllZones( nullptr, m_progressReporter, true );
1279 }
1280
1281 if( psJob->m_argLayers )
1282 psJob->m_plotLayerSequence = convertLayerArg( psJob->m_argLayers.value(), brd );
1283
1284 if( psJob->m_argCommonLayers )
1285 psJob->m_plotOnAllLayersSequence = convertLayerArg( psJob->m_argCommonLayers.value(), brd );
1286
1287 if( psJob->m_plotLayerSequence.size() < 1 )
1288 {
1289 m_reporter->Report( _( "At least one layer must be specified\n" ), RPT_SEVERITY_ERROR );
1291 }
1292
1293 bool isSingle = psJob->m_genMode == JOB_EXPORT_PCB_PS::GEN_MODE::SINGLE;
1294
1295 if( isSingle )
1296 {
1297 if( psJob->GetConfiguredOutputPath().IsEmpty() )
1298 {
1299 wxFileName fn = brd->GetFileName();
1300 fn.SetName( fn.GetName() );
1302
1303 psJob->SetWorkingOutputPath( fn.GetFullName() );
1304 }
1305 }
1306
1307 wxString outPath = resolveJobOutputPath( psJob, brd, &psJob->m_drawingSheet );
1308
1309 if( !PATHS::EnsurePathExists( outPath, isSingle ) )
1310 {
1311 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1313 }
1314
1315 PCB_PLOT_PARAMS plotOpts;
1316 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, psJob, *m_reporter );
1317
1318 PCB_PLOTTER pcbPlotter( brd, m_reporter, plotOpts );
1319
1320 std::optional<wxString> layerName;
1321 std::optional<wxString> sheetName;
1322 std::optional<wxString> sheetPath;
1323
1324 if( isSingle )
1325 {
1326 if( aJob->GetVarOverrides().contains( wxT( "LAYER" ) ) )
1327 layerName = psJob->GetVarOverrides().at( wxT( "LAYER" ) );
1328
1329 if( aJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) )
1330 sheetName = psJob->GetVarOverrides().at( wxT( "SHEETNAME" ) );
1331
1332 if( aJob->GetVarOverrides().contains( wxT( "SHEETPATH" ) ) )
1333 sheetPath = psJob->GetVarOverrides().at( wxT( "SHEETPATH" ) );
1334 }
1335
1336 std::vector<wxString> outputPaths;
1337
1338 if( !pcbPlotter.Plot( outPath, psJob->m_plotLayerSequence, psJob->m_plotOnAllLayersSequence, false, isSingle,
1339 layerName, sheetName, sheetPath, &outputPaths ) )
1340 {
1342 }
1343
1344 for( const wxString& outputPath : outputPaths )
1345 aJob->AddOutput( outputPath );
1346
1347 return CLI::EXIT_CODES::OK;
1348}
1349
1350
1352{
1353 int exitCode = CLI::EXIT_CODES::OK;
1354 JOB_EXPORT_PCB_GERBERS* aGerberJob = dynamic_cast<JOB_EXPORT_PCB_GERBERS*>( aJob );
1355
1356 if( aGerberJob == nullptr )
1358
1359 BOARD* brd = getBoard( aGerberJob->m_filename );
1360
1361 if( !brd )
1363
1364 if( !aGerberJob->m_variant.IsEmpty() )
1365 brd->SetCurrentVariant( aGerberJob->m_variant );
1366
1367 wxString outPath = resolveJobOutputPath( aJob, brd, &aGerberJob->m_drawingSheet );
1368
1369 if( !PATHS::EnsurePathExists( outPath, false ) )
1370 {
1371 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1373 }
1374
1375 TOOL_MANAGER* toolManager = getToolManager( brd );
1376
1377 if( aGerberJob->m_checkZonesBeforePlot )
1378 {
1379 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
1380 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
1381
1382 toolManager->GetTool<ZONE_FILLER_TOOL>()->FillAllZones( nullptr, m_progressReporter, true );
1383 }
1384
1385 bool hasLayerListSpecified = false; // will be true if the user layer list is not empty
1386
1387 if( aGerberJob->m_argLayers )
1388 {
1389 if( !aGerberJob->m_argLayers.value().empty() )
1390 {
1391 aGerberJob->m_plotLayerSequence = convertLayerArg( aGerberJob->m_argLayers.value(), brd );
1392 hasLayerListSpecified = true;
1393 }
1394 else
1395 {
1397 }
1398 }
1399
1400 if( aGerberJob->m_argCommonLayers )
1401 aGerberJob->m_plotOnAllLayersSequence = convertLayerArg( aGerberJob->m_argCommonLayers.value(), brd );
1402
1403 PCB_PLOT_PARAMS boardPlotOptions = brd->GetPlotOptions();
1404 GERBER_JOBFILE_WRITER jobfile_writer( brd );
1405
1406 wxString fileExt;
1407
1408 if( aGerberJob->m_useBoardPlotParams )
1409 {
1410 // The board plot options are saved with all copper layers enabled, even those that don't
1411 // exist in the current stackup. This is done so the layers are automatically enabled in the plot
1412 // dialog when the user enables them. We need to filter out these not-enabled layers here so
1413 // we don't plot 32 layers when we only have 4, etc.
1414 LSET plotLayers = ( boardPlotOptions.GetLayerSelection() & LSET::AllNonCuMask() )
1415 | ( brd->GetEnabledLayers() & LSET::AllCuMask() );
1416 aGerberJob->m_plotLayerSequence = plotLayers.SeqStackupForPlotting();
1417 aGerberJob->m_plotOnAllLayersSequence = boardPlotOptions.GetPlotOnAllLayersSequence();
1418 }
1419 else
1420 {
1421 // default to the board enabled layers, but only if the user has not specifed a layer list
1422 // ( m_plotLayerSequence can be empty with a broken user layer list)
1423 if( aGerberJob->m_plotLayerSequence.empty() && !hasLayerListSpecified )
1425 }
1426
1427 // Ensure layers to plot are restricted to enabled layers of the board to plot
1428 LSET layersToPlot = LSET( { aGerberJob->m_plotLayerSequence } ) & brd->GetEnabledLayers();
1429
1430 for( PCB_LAYER_ID layer : layersToPlot.UIOrder() )
1431 {
1432 LSEQ plotSequence;
1433
1434 // Base layer always gets plotted first.
1435 plotSequence.push_back( layer );
1436
1437 // Now all the "include on all" layers
1438 for( PCB_LAYER_ID layer_all : aGerberJob->m_plotOnAllLayersSequence )
1439 {
1440 // Don't plot the same layer more than once;
1441 if( find( plotSequence.begin(), plotSequence.end(), layer_all ) != plotSequence.end() )
1442 continue;
1443
1444 plotSequence.push_back( layer_all );
1445 }
1446
1447 // Pick the basename from the board file
1448 wxFileName fn( brd->GetFileName() );
1449 wxString layerName = brd->GetLayerName( layer );
1450 wxString sheetName;
1451 wxString sheetPath;
1452 PCB_PLOT_PARAMS plotOpts;
1453
1454 if( aGerberJob->m_useBoardPlotParams )
1455 plotOpts = boardPlotOptions;
1456 else
1457 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, aGerberJob, *m_reporter );
1458
1459 if( plotOpts.GetUseGerberProtelExtensions() )
1460 fileExt = GetGerberProtelExtension( layer );
1461 else
1463
1464 PCB_PLOTTER::BuildPlotFileName( &fn, outPath, layerName, fileExt );
1465 wxString fullname = fn.GetFullName();
1466
1467 if( m_progressReporter )
1468 {
1469 m_progressReporter->AdvancePhase( wxString::Format( _( "Exporting %s" ), fullname ) );
1470 m_progressReporter->KeepRefreshing();
1471 }
1472
1473 jobfile_writer.AddGbrFile( layer, fullname );
1474
1475 if( aJob->GetVarOverrides().contains( wxT( "LAYER" ) ) )
1476 layerName = aJob->GetVarOverrides().at( wxT( "LAYER" ) );
1477
1478 if( aJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) )
1479 sheetName = aJob->GetVarOverrides().at( wxT( "SHEETNAME" ) );
1480
1481 if( aJob->GetVarOverrides().contains( wxT( "SHEETPATH" ) ) )
1482 sheetPath = aJob->GetVarOverrides().at( wxT( "SHEETPATH" ) );
1483
1484 // We are feeding it one layer at the start here to silence a logic check
1485 GERBER_PLOTTER* plotter;
1486 plotter = (GERBER_PLOTTER*) StartPlotBoard( brd, &plotOpts, layer, layerName,
1487 fn.GetFullPath(), sheetName, sheetPath );
1488
1489 if( plotter )
1490 {
1491 m_reporter->Report( wxString::Format( _( "Plotted to '%s'.\n" ), fn.GetFullPath() ),
1493
1494 PlotBoardLayers( brd, plotter, plotSequence, plotOpts );
1495 plotter->EndPlot();
1496 aJob->AddOutput( fn.GetFullPath() );
1497 }
1498 else
1499 {
1500 m_reporter->Report( wxString::Format( _( "Failed to plot to '%s'.\n" ), fn.GetFullPath() ),
1503 }
1504
1505 delete plotter;
1506 }
1507
1508 if( aGerberJob->m_createJobsFile )
1509 {
1510 wxFileName fn( brd->GetFileName() );
1511
1512 // Build gerber job file from basename
1514 jobfile_writer.CreateJobFile( fn.GetFullPath() );
1515 aJob->AddOutput( fn.GetFullPath() );
1516 }
1517
1518 return exitCode;
1519}
1520
1521
1523{
1524 JOB_EXPORT_PCB_GENCAD* aGencadJob = dynamic_cast<JOB_EXPORT_PCB_GENCAD*>( aJob );
1525
1526 if( aGencadJob == nullptr )
1528
1529 BOARD* brd = getBoard( aGencadJob->m_filename );
1530
1531 if( !brd )
1533
1534 GENCAD_EXPORTER exporter( brd );
1535
1536 VECTOR2I GencadOffset;
1537 VECTOR2I auxOrigin = brd->GetDesignSettings().GetAuxOrigin();
1538 GencadOffset.x = aGencadJob->m_useDrillOrigin ? auxOrigin.x : 0;
1539 GencadOffset.y = aGencadJob->m_useDrillOrigin ? auxOrigin.y : 0;
1540
1541 exporter.FlipBottomPads( aGencadJob->m_flipBottomPads );
1542 exporter.UsePinNamesUnique( aGencadJob->m_useUniquePins );
1543 exporter.UseIndividualShapes( aGencadJob->m_useIndividualShapes );
1544 exporter.SetPlotOffet( GencadOffset );
1545 exporter.StoreOriginCoordsInFile( aGencadJob->m_storeOriginCoords );
1546
1547 if( aGencadJob->GetConfiguredOutputPath().IsEmpty() )
1548 {
1549 wxFileName fn = brd->GetFileName();
1550 fn.SetName( fn.GetName() );
1551 fn.SetExt( FILEEXT::GencadFileExtension );
1552
1553 aGencadJob->SetWorkingOutputPath( fn.GetFullName() );
1554 }
1555
1556 wxString outPath = resolveJobOutputPath( aJob, brd );
1557
1558 if( !PATHS::EnsurePathExists( outPath, true ) )
1559 {
1560 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1562 }
1563
1564 if( !exporter.WriteFile( outPath ) )
1565 {
1566 m_reporter->Report( wxString::Format( _( "Failed to create file '%s'.\n" ), outPath ),
1568
1570 }
1571
1572 aJob->AddOutput( outPath );
1573 m_reporter->Report( _( "Successfully created genCAD file\n" ), RPT_SEVERITY_INFO );
1574
1575 return CLI::EXIT_CODES::OK;
1576}
1577
1578
1580{
1581 JOB_EXPORT_PCB_STATS* statsJob = dynamic_cast<JOB_EXPORT_PCB_STATS*>( aJob );
1582
1583 if( statsJob == nullptr )
1585
1586 BOARD* brd = getBoard( statsJob->m_filename );
1587
1588 if( !brd )
1590
1593
1598
1599 ComputeBoardStatistics( brd, options, data );
1600
1601 wxString projectName;
1602
1603 if( brd->GetProject() )
1604 projectName = brd->GetProject()->GetProjectName();
1605
1606 wxFileName boardFile = brd->GetFileName();
1607
1608 if( boardFile.GetName().IsEmpty() )
1609 boardFile = wxFileName( statsJob->m_filename );
1610
1612 UNITS_PROVIDER unitsProvider( pcbIUScale, unitsForReport );
1613
1614 wxString report;
1615
1617 report = FormatBoardStatisticsJson( data, brd, unitsProvider, projectName, boardFile.GetName() );
1618 else
1619 report = FormatBoardStatisticsReport( data, brd, unitsProvider, projectName, boardFile.GetName() );
1620
1621 if( statsJob->GetConfiguredOutputPath().IsEmpty() && statsJob->GetWorkingOutputPath().IsEmpty() )
1622 statsJob->SetDefaultOutputPath( boardFile.GetFullPath() );
1623
1624 wxString outPath = resolveJobOutputPath( aJob, brd );
1625
1626 if( !PATHS::EnsurePathExists( outPath, true ) )
1627 {
1628 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1630 }
1631
1632 FILE* outFile = wxFopen( outPath, wxS( "wt" ) );
1633
1634 if( !outFile )
1635 {
1636 m_reporter->Report( wxString::Format( _( "Failed to create file '%s'.\n" ), outPath ), RPT_SEVERITY_ERROR );
1638 }
1639
1640 if( fprintf( outFile, "%s", TO_UTF8( report ) ) < 0 )
1641 {
1642 fclose( outFile );
1643 m_reporter->Report( wxString::Format( _( "Error writing file '%s'.\n" ), outPath ), RPT_SEVERITY_ERROR );
1645 }
1646
1647 fclose( outFile );
1648
1649 m_reporter->Report( wxString::Format( _( "Wrote board statistics to '%s'.\n" ), outPath ), RPT_SEVERITY_ACTION );
1650
1651 statsJob->AddOutput( outPath );
1652
1653 return CLI::EXIT_CODES::OK;
1654}
1655
1656
1658{
1659 int exitCode = CLI::EXIT_CODES::OK;
1660 JOB_EXPORT_PCB_GERBER* aGerberJob = dynamic_cast<JOB_EXPORT_PCB_GERBER*>( aJob );
1661
1662 if( aGerberJob == nullptr )
1664
1665 BOARD* brd = getBoard( aGerberJob->m_filename );
1666
1667 if( !brd )
1669
1670 if( !aGerberJob->m_variant.IsEmpty() )
1671 brd->SetCurrentVariant( aGerberJob->m_variant );
1672
1673 TOOL_MANAGER* toolManager = getToolManager( brd );
1674
1675 if( aGerberJob->m_argLayers )
1676 aGerberJob->m_plotLayerSequence = convertLayerArg( aGerberJob->m_argLayers.value(), brd );
1677
1678 if( aGerberJob->m_argCommonLayers )
1679 aGerberJob->m_plotOnAllLayersSequence = convertLayerArg( aGerberJob->m_argCommonLayers.value(), brd );
1680
1681 if( aGerberJob->m_plotLayerSequence.size() < 1 )
1682 {
1683 m_reporter->Report( _( "At least one layer must be specified\n" ), RPT_SEVERITY_ERROR );
1685 }
1686
1687 if( aGerberJob->GetConfiguredOutputPath().IsEmpty() )
1688 {
1689 wxFileName fn = brd->GetFileName();
1690 fn.SetName( fn.GetName() );
1692
1693 aGerberJob->SetWorkingOutputPath( fn.GetFullName() );
1694 }
1695
1696 wxString outPath = resolveJobOutputPath( aJob, brd );
1697
1698 if( aGerberJob->m_checkZonesBeforePlot )
1699 {
1700 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
1701 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
1702
1703 toolManager->GetTool<ZONE_FILLER_TOOL>()->FillAllZones( nullptr, m_progressReporter, true );
1704 }
1705
1706 PCB_PLOT_PARAMS plotOpts;
1707 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, aGerberJob, *m_reporter );
1708 plotOpts.SetLayerSelection( aGerberJob->m_plotLayerSequence );
1710
1712 wxString layerName;
1713 wxString sheetName;
1714 wxString sheetPath;
1715
1716 // The first layer will be treated as the layer name for the gerber header,
1717 // the other layers will be treated equivalent to the "Plot on All Layers" option
1718 // in the GUI
1719 if( aGerberJob->m_plotLayerSequence.size() >= 1 )
1720 {
1721 layer = aGerberJob->m_plotLayerSequence.front();
1722 layerName = brd->GetLayerName( layer );
1723 }
1724
1725 if( aJob->GetVarOverrides().contains( wxT( "LAYER" ) ) )
1726 layerName = aJob->GetVarOverrides().at( wxT( "LAYER" ) );
1727
1728 if( aJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) )
1729 sheetName = aJob->GetVarOverrides().at( wxT( "SHEETNAME" ) );
1730
1731 if( aJob->GetVarOverrides().contains( wxT( "SHEETPATH" ) ) )
1732 sheetPath = aJob->GetVarOverrides().at( wxT( "SHEETPATH" ) );
1733
1734 // We are feeding it one layer at the start here to silence a logic check
1735 PLOTTER* plotter = StartPlotBoard( brd, &plotOpts, layer, layerName, outPath, sheetName,
1736 sheetPath );
1737
1738 if( plotter )
1739 {
1740 PlotBoardLayers( brd, plotter, aGerberJob->m_plotLayerSequence, plotOpts );
1741 plotter->EndPlot();
1742 }
1743 else
1744 {
1745 m_reporter->Report( wxString::Format( _( "Failed to plot to '%s'.\n" ), outPath ),
1748 }
1749
1750 delete plotter;
1751
1752 return exitCode;
1753}
1754
1757
1758
1760{
1761 JOB_EXPORT_PCB_DRILL* aDrillJob = dynamic_cast<JOB_EXPORT_PCB_DRILL*>( aJob );
1762
1763 if( aDrillJob == nullptr )
1765
1766 BOARD* brd = getBoard( aDrillJob->m_filename );
1767
1768 if( !brd )
1770
1771 wxString outPath = resolveJobOutputPath( aJob, brd );
1772
1773 if( !PATHS::EnsurePathExists( outPath ) )
1774 {
1775 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1777 }
1778
1779 std::unique_ptr<GENDRILL_WRITER_BASE> drillWriter;
1780
1782 drillWriter = std::make_unique<EXCELLON_WRITER>( brd );
1783 else
1784 drillWriter = std::make_unique<GERBER_WRITER>( brd );
1785
1786 VECTOR2I offset;
1787
1789 offset = VECTOR2I( 0, 0 );
1790 else
1791 offset = brd->GetDesignSettings().GetAuxOrigin();
1792
1793 PLOT_FORMAT mapFormat = PLOT_FORMAT::PDF;
1794
1795 switch( aDrillJob->m_mapFormat )
1796 {
1801 default:
1803 }
1804
1805
1806 if( aDrillJob->m_generateReport && aDrillJob->m_reportPath.IsEmpty() )
1807 {
1808 wxFileName fn = outPath;
1809 fn.SetFullName( brd->GetFileName() );
1810 fn.SetName( fn.GetName() + "-drill" );
1811 fn.SetExt( FILEEXT::ReportFileExtension );
1812
1813 aDrillJob->m_reportPath = fn.GetFullPath();
1814 }
1815
1817 {
1819
1820 switch( aDrillJob->m_zeroFormat )
1821 {
1824 break;
1825
1828 break;
1829
1832 break;
1833
1835 default:
1837 break;
1838 }
1839
1840 DRILL_PRECISION precision;
1841
1843 precision = precisionListForInches;
1844 else
1845 precision = precisionListForMetric;
1846
1847 EXCELLON_WRITER* excellonWriter = dynamic_cast<EXCELLON_WRITER*>( drillWriter.get() );
1848
1849 if( excellonWriter == nullptr )
1851
1852 excellonWriter->SetFormat( aDrillJob->m_drillUnits == JOB_EXPORT_PCB_DRILL::DRILL_UNITS::MM,
1853 zeroFmt, precision.m_Lhs, precision.m_Rhs );
1854 excellonWriter->SetOptions( aDrillJob->m_excellonMirrorY,
1855 aDrillJob->m_excellonMinimalHeader,
1856 offset, aDrillJob->m_excellonCombinePTHNPTH );
1857 excellonWriter->SetRouteModeForOvalHoles( aDrillJob->m_excellonOvalDrillRoute );
1858 excellonWriter->SetMapFileFormat( mapFormat );
1859
1860 if( !excellonWriter->CreateDrillandMapFilesSet( outPath, true, aDrillJob->m_generateMap,
1861 m_reporter ) )
1862 {
1864 }
1865
1866 aDrillJob->AddOutput( outPath );
1867
1868 if( aDrillJob->m_generateReport )
1869 {
1870 wxString reportPath = aDrillJob->ResolveOutputPath( aDrillJob->m_reportPath, true, brd->GetProject() );
1871
1872 if( !excellonWriter->GenDrillReportFile( reportPath ) )
1873 {
1875 }
1876
1877 aDrillJob->AddOutput( reportPath );
1878 }
1879 }
1881 {
1882 GERBER_WRITER* gerberWriter = dynamic_cast<GERBER_WRITER*>( drillWriter.get() );
1883
1884 if( gerberWriter == nullptr )
1886
1887 // Set gerber precision: only 5 or 6 digits for mantissa are allowed
1888 // (SetFormat() accept 5 or 6, and any other value set the precision to 5)
1889 // the integer part precision is always 4, and units always mm
1890 gerberWriter->SetFormat( aDrillJob->m_gerberPrecision );
1891 gerberWriter->SetOptions( offset );
1892 gerberWriter->SetMapFileFormat( mapFormat );
1893
1894 if( !gerberWriter->CreateDrillandMapFilesSet( outPath, true, aDrillJob->m_generateMap,
1895 aDrillJob->m_generateTenting, m_reporter ) )
1896 {
1898 }
1899
1900 aDrillJob->AddOutput( outPath );
1901
1902 if( aDrillJob->m_generateReport )
1903 {
1904 wxString reportPath = aDrillJob->ResolveOutputPath( aDrillJob->m_reportPath, true, brd->GetProject() );
1905
1906 if( !gerberWriter->GenDrillReportFile( reportPath ) )
1907 {
1909 }
1910
1911 aDrillJob->AddOutput( reportPath );
1912 }
1913 }
1914
1915 return CLI::EXIT_CODES::OK;
1916}
1917
1918
1920{
1921 JOB_EXPORT_PCB_POS* aPosJob = dynamic_cast<JOB_EXPORT_PCB_POS*>( aJob );
1922
1923 if( aPosJob == nullptr )
1925
1926 BOARD* brd = getBoard( aPosJob->m_filename );
1927
1928 if( !brd )
1930
1931 if( aPosJob->GetConfiguredOutputPath().IsEmpty() )
1932 {
1933 wxFileName fn = brd->GetFileName();
1934 fn.SetName( fn.GetName() );
1935
1938 else if( aPosJob->m_format == JOB_EXPORT_PCB_POS::FORMAT::CSV )
1939 fn.SetExt( FILEEXT::CsvFileExtension );
1940 else if( aPosJob->m_format == JOB_EXPORT_PCB_POS::FORMAT::GERBER )
1941 fn.SetExt( FILEEXT::GerberFileExtension );
1942
1943 aPosJob->SetWorkingOutputPath( fn.GetFullName() );
1944 }
1945
1946 wxString outPath = resolveJobOutputPath( aJob, brd );
1947
1948 if( !PATHS::EnsurePathExists( outPath, true ) )
1949 {
1950 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1952 }
1953
1956 {
1957 wxFileName fn( outPath );
1958 wxString baseName = fn.GetName();
1959
1960 auto exportPlaceFile =
1961 [&]( bool frontSide, bool backSide, const wxString& curr_outPath ) -> bool
1962 {
1963 FILE* file = wxFopen( curr_outPath, wxS( "wt" ) );
1964 wxCHECK( file, false );
1965
1966 PLACE_FILE_EXPORTER exporter( brd,
1968 aPosJob->m_smdOnly,
1970 aPosJob->m_excludeDNP,
1971 aPosJob->m_excludeBOM,
1972 frontSide,
1973 backSide,
1976 aPosJob->m_negateBottomX );
1977
1978 // Set variant for variant-aware DNP/BOM/position file filtering
1979 exporter.SetVariant( aPosJob->m_variant );
1980
1981 std::string data = exporter.GenPositionData();
1982 fputs( data.c_str(), file );
1983 fclose( file );
1984
1985 return true;
1986 };
1987
1988 if( aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::BOTH && !aPosJob->m_singleFile )
1989 {
1990 fn.SetName( PLACE_FILE_EXPORTER::DecorateFilename( baseName, true, false ) );
1991
1992 if( aPosJob->m_format == JOB_EXPORT_PCB_POS::FORMAT::CSV && !aPosJob->m_nakedFilename )
1993 fn.SetName( fn.GetName() + wxT( "-" ) + FILEEXT::FootprintPlaceFileExtension );
1994
1995 if( exportPlaceFile( true, false, fn.GetFullPath() ) )
1996 {
1997 m_reporter->Report( wxString::Format( _( "Wrote front position data to '%s'.\n" ),
1998 fn.GetFullPath() ),
2000
2001 aPosJob->AddOutput( fn.GetFullPath() );
2002 }
2003 else
2004 {
2006 }
2007
2008 fn.SetName( PLACE_FILE_EXPORTER::DecorateFilename( baseName, false, true ) );
2009
2010 if( aPosJob->m_format == JOB_EXPORT_PCB_POS::FORMAT::CSV && !aPosJob->m_nakedFilename )
2011 fn.SetName( fn.GetName() + wxT( "-" ) + FILEEXT::FootprintPlaceFileExtension );
2012
2013 if( exportPlaceFile( false, true, fn.GetFullPath() ) )
2014 {
2015 m_reporter->Report( wxString::Format( _( "Wrote back position data to '%s'.\n" ),
2016 fn.GetFullPath() ),
2018
2019 aPosJob->AddOutput( fn.GetFullPath() );
2020 }
2021 else
2022 {
2024 }
2025 }
2026 else
2027 {
2028 bool front = aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::FRONT
2030
2031 bool back = aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::BACK
2033
2034 if( !aPosJob->m_nakedFilename )
2035 {
2036 fn.SetName( PLACE_FILE_EXPORTER::DecorateFilename( fn.GetName(), front, back ) );
2037
2039 fn.SetName( fn.GetName() + wxT( "-" ) + FILEEXT::FootprintPlaceFileExtension );
2040 }
2041
2042 if( exportPlaceFile( front, back, fn.GetFullPath() ) )
2043 {
2044 m_reporter->Report( wxString::Format( _( "Wrote position data to '%s'.\n" ),
2045 fn.GetFullPath() ),
2047
2048 aPosJob->AddOutput( fn.GetFullPath() );
2049 }
2050 else
2051 {
2053 }
2054 }
2055 }
2056 else if( aPosJob->m_format == JOB_EXPORT_PCB_POS::FORMAT::GERBER )
2057 {
2058 PLACEFILE_GERBER_WRITER exporter( brd );
2059
2060 // Set variant for variant-aware DNP/BOM/position file filtering
2061 exporter.SetVariant( aPosJob->m_variant );
2062
2063 PCB_LAYER_ID gbrLayer = F_Cu;
2064 wxString outPath_base = outPath;
2065
2067 || aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::BOTH )
2068 {
2069 if( aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::BOTH || !aPosJob->m_nakedFilename )
2070 outPath = exporter.GetPlaceFileName( outPath, gbrLayer );
2071
2072 if( exporter.CreatePlaceFile( outPath, gbrLayer, aPosJob->m_gerberBoardEdge,
2073 aPosJob->m_excludeDNP, aPosJob->m_excludeBOM ) >= 0 )
2074 {
2075 m_reporter->Report( wxString::Format( _( "Wrote front position data to '%s'.\n" ), outPath ),
2077
2078 aPosJob->AddOutput( outPath );
2079 }
2080 else
2081 {
2083 }
2084 }
2085
2087 || aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::BOTH )
2088 {
2089 gbrLayer = B_Cu;
2090
2091 outPath = outPath_base;
2092
2093 if( aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::BOTH || !aPosJob->m_nakedFilename )
2094 outPath = exporter.GetPlaceFileName( outPath, gbrLayer );
2095
2096 if( exporter.CreatePlaceFile( outPath, gbrLayer, aPosJob->m_gerberBoardEdge,
2097 aPosJob->m_excludeDNP, aPosJob->m_excludeBOM ) >= 0 )
2098 {
2099 m_reporter->Report( wxString::Format( _( "Wrote back position data to '%s'.\n" ), outPath ),
2101
2102 aPosJob->AddOutput( outPath );
2103 }
2104 else
2105 {
2107 }
2108 }
2109 }
2110
2111 return CLI::EXIT_CODES::OK;
2112}
2113
2114
2116{
2117 JOB_FP_UPGRADE* upgradeJob = dynamic_cast<JOB_FP_UPGRADE*>( aJob );
2118
2119 if( upgradeJob == nullptr )
2121
2123
2124 if( !upgradeJob->m_outputLibraryPath.IsEmpty() )
2125 {
2126 if( wxFile::Exists( upgradeJob->m_outputLibraryPath )
2127 || wxDir::Exists( upgradeJob->m_outputLibraryPath) )
2128 {
2129 m_reporter->Report( _( "Output path must not conflict with existing path\n" ),
2132 }
2133 }
2134 else if( fileType != PCB_IO_MGR::KICAD_SEXP )
2135 {
2136 m_reporter->Report( _( "Output path must be specified to convert legacy and non-KiCad libraries\n" ),
2138
2140 }
2141
2143 {
2144 if( !wxDir::Exists( upgradeJob->m_libraryPath ) )
2145 {
2146 m_reporter->Report( _( "Footprint library path does not exist or is not accessible\n" ),
2149 }
2150
2152 FP_CACHE fpLib( &pcb_io, upgradeJob->m_libraryPath );
2153
2154 try
2155 {
2156 fpLib.Load();
2157 }
2158 catch( ... )
2159 {
2160 m_reporter->Report( _( "Unable to load library\n" ), RPT_SEVERITY_ERROR );
2162 }
2163
2164 if( m_progressReporter )
2165 m_progressReporter->KeepRefreshing();
2166
2167 bool shouldSave = upgradeJob->m_force;
2168
2169 for( const auto& footprint : fpLib.GetFootprints() )
2170 {
2171 if( footprint.second->GetFootprint()->GetFileFormatVersionAtLoad() < SEXPR_BOARD_FILE_VERSION )
2172 shouldSave = true;
2173 }
2174
2175 if( shouldSave )
2176 {
2177 try
2178 {
2179 if( !upgradeJob->m_outputLibraryPath.IsEmpty() )
2180 fpLib.SetPath( upgradeJob->m_outputLibraryPath );
2181
2182 fpLib.Save();
2183 }
2184 catch( ... )
2185 {
2186 m_reporter->Report( _( "Unable to save library\n" ), RPT_SEVERITY_ERROR );
2188 }
2189 }
2190 else
2191 {
2192 m_reporter->Report( _( "Footprint library was not updated\n" ), RPT_SEVERITY_ERROR );
2193 }
2194 }
2195 else
2196 {
2197 if( !PCB_IO_MGR::ConvertLibrary( {}, upgradeJob->m_libraryPath,
2198 upgradeJob->m_outputLibraryPath, nullptr /* REPORTER */ ) )
2199 {
2200 m_reporter->Report( ( "Unable to convert library\n" ), RPT_SEVERITY_ERROR );
2202 }
2203 }
2204
2205 return CLI::EXIT_CODES::OK;
2206}
2207
2208
2210{
2211 JOB_FP_EXPORT_SVG* svgJob = dynamic_cast<JOB_FP_EXPORT_SVG*>( aJob );
2212
2213 if( svgJob == nullptr )
2215
2217 FP_CACHE fpLib( &pcb_io, svgJob->m_libraryPath );
2218
2219 if( svgJob->m_argLayers )
2220 {
2221 if( !svgJob->m_argLayers.value().empty() )
2222 svgJob->m_plotLayerSequence = convertLayerArg( svgJob->m_argLayers.value(), nullptr );
2223 else
2225 }
2226
2227 try
2228 {
2229 fpLib.Load();
2230 }
2231 catch( ... )
2232 {
2233 m_reporter->Report( _( "Unable to load library\n" ), RPT_SEVERITY_ERROR );
2235 }
2236
2237 wxString outPath = svgJob->GetFullOutputPath( nullptr );
2238
2239 if( !PATHS::EnsurePathExists( outPath, true ) )
2240 {
2241 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
2243 }
2244
2245 int exitCode = CLI::EXIT_CODES::OK;
2246 bool singleFpPlotted = false;
2247
2248 for( const auto& [fpName, fpCacheEntry] : fpLib.GetFootprints() )
2249 {
2250 if( m_progressReporter )
2251 {
2252 m_progressReporter->AdvancePhase( wxString::Format( _( "Exporting %s" ), fpName ) );
2253 m_progressReporter->KeepRefreshing();
2254 }
2255
2256 if( !svgJob->m_footprint.IsEmpty() )
2257 {
2258 // skip until we find the right footprint
2259 if( fpName != svgJob->m_footprint )
2260 continue;
2261 else
2262 singleFpPlotted = true;
2263 }
2264
2265 exitCode = doFpExportSvg( svgJob, fpCacheEntry->GetFootprint().get() );
2266
2267 if( exitCode != CLI::EXIT_CODES::OK )
2268 break;
2269 }
2270
2271 if( !svgJob->m_footprint.IsEmpty() && !singleFpPlotted )
2272 {
2273 m_reporter->Report( _( "The given footprint could not be found to export." ) + wxS( "\n" ),
2275 }
2276
2277 return CLI::EXIT_CODES::OK;
2278}
2279
2280
2282{
2283 // the hack for now is we create fake boards containing the footprint and plot the board
2284 // until we refactor better plot api later
2285 std::unique_ptr<BOARD> brd = BOARD_LOADER::CreateEmptyBoard( Pgm().GetSettingsManager().GetProject( "" ) );
2286 brd->GetProject()->ApplyTextVars( aSvgJob->GetVarOverrides() );
2287 brd->SynchronizeProperties();
2288
2289 FOOTPRINT* fp = dynamic_cast<FOOTPRINT*>( aFootprint->Clone() );
2290
2291 if( fp == nullptr )
2293
2294 fp->SetLink( niluuid );
2295 fp->SetFlags( IS_NEW );
2296 fp->SetParent( brd.get() );
2297
2298 for( PAD* pad : fp->Pads() )
2299 {
2300 pad->SetLocalRatsnestVisible( false );
2301 pad->SetNetCode( 0 );
2302 }
2303
2304 fp->SetOrientation( ANGLE_0 );
2305 fp->SetPosition( VECTOR2I( 0, 0 ) );
2306
2307 brd->Add( fp, ADD_MODE::INSERT, true );
2308
2309 wxFileName outputFile;
2310 outputFile.SetPath( aSvgJob->GetFullOutputPath(nullptr) );
2311 outputFile.SetName( aFootprint->GetFPID().GetLibItemName().wx_str() );
2312 outputFile.SetExt( FILEEXT::SVGFileExtension );
2313
2314 m_reporter->Report( wxString::Format( _( "Plotting footprint '%s' to '%s'\n" ),
2315 aFootprint->GetFPID().GetLibItemName().wx_str(),
2316 outputFile.GetFullPath() ),
2318
2319 PCB_PLOT_PARAMS plotOpts;
2320 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, aSvgJob, *m_reporter );
2321
2322 // always fixed for the svg plot
2323 plotOpts.SetPlotFrameRef( false );
2324 plotOpts.SetSvgFitPageToBoard( true );
2325 plotOpts.SetMirror( false );
2326 plotOpts.SetSkipPlotNPTH_Pads( false );
2327
2328 if( plotOpts.GetSketchPadsOnFabLayers() )
2329 {
2330 plotOpts.SetPlotPadNumbers( true );
2331 }
2332
2333 PCB_PLOTTER plotter( brd.get(), m_reporter, plotOpts );
2334
2335 if( !plotter.Plot( outputFile.GetFullPath(),
2336 aSvgJob->m_plotLayerSequence,
2338 false,
2339 true,
2340 wxEmptyString, wxEmptyString,
2341 wxEmptyString ) )
2342 {
2343 m_reporter->Report( _( "Error creating svg file" ) + wxS( "\n" ), RPT_SEVERITY_ERROR );
2345 }
2346
2347 aSvgJob->AddOutput( outputFile.GetFullPath() );
2348
2349 return CLI::EXIT_CODES::OK;
2350}
2351
2352
2354{
2355 JOB_PCB_DRC* drcJob = dynamic_cast<JOB_PCB_DRC*>( aJob );
2356
2357 if( drcJob == nullptr )
2359
2360 BOARD* brd = getBoard( drcJob->m_filename );
2361
2362 if( !brd )
2364
2365 // Running DRC requires libraries be loaded, so make sure they have been
2367 adapter->AsyncLoad();
2368 adapter->BlockUntilLoaded();
2369
2370 if( drcJob->GetConfiguredOutputPath().IsEmpty() )
2371 {
2372 wxFileName fn = brd->GetFileName();
2373 fn.SetName( fn.GetName() + wxS( "-drc" ) );
2374
2376 fn.SetExt( FILEEXT::JsonFileExtension );
2377 else
2378 fn.SetExt( FILEEXT::ReportFileExtension );
2379
2380 drcJob->SetWorkingOutputPath( fn.GetFullName() );
2381 }
2382
2383 wxString outPath = resolveJobOutputPath( aJob, brd );
2384
2385 if( !PATHS::EnsurePathExists( outPath, true ) )
2386 {
2387 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
2389 }
2390
2391 EDA_UNITS units;
2392
2393 switch( drcJob->m_units )
2394 {
2395 case JOB_PCB_DRC::UNITS::INCH: units = EDA_UNITS::INCH; break;
2396 case JOB_PCB_DRC::UNITS::MILS: units = EDA_UNITS::MILS; break;
2397 case JOB_PCB_DRC::UNITS::MM: units = EDA_UNITS::MM; break;
2398 default: units = EDA_UNITS::MM; break;
2399 }
2400
2401 std::shared_ptr<DRC_ENGINE> drcEngine = brd->GetDesignSettings().m_DRCEngine;
2402 std::unique_ptr<NETLIST> netlist = std::make_unique<NETLIST>();
2403
2404 drcEngine->SetDrawingSheet( getDrawingSheetProxyView( brd ) );
2405
2406 // BOARD_COMMIT uses TOOL_MANAGER to grab the board internally so we must give it one
2407 TOOL_MANAGER* toolManager = getToolManager( brd );
2408
2409 BOARD_COMMIT commit( toolManager );
2410 bool checkParity = drcJob->m_parity;
2411 std::string netlist_str;
2412
2413 if( checkParity )
2414 {
2415 wxString annotateMsg = _( "Schematic parity tests require a fully annotated schematic." );
2416 netlist_str = annotateMsg;
2417
2418 // The KIFACE_NETLIST_SCHEMATIC function has some broken-ness that the schematic
2419 // frame's version does not, but it is the only one that works in CLI, so we use it
2420 // if we don't have the sch frame open.
2421 // TODO: clean this up, see https://gitlab.com/kicad/code/kicad/-/issues/19929
2422 if( m_kiway->Player( FRAME_SCH, false ) )
2423 {
2424 m_kiway->ExpressMail( FRAME_SCH, MAIL_SCH_GET_NETLIST, netlist_str );
2425 }
2426 else
2427 {
2428 wxFileName schematicPath( drcJob->m_filename );
2429 schematicPath.MakeAbsolute();
2430 schematicPath.SetExt( FILEEXT::KiCadSchematicFileExtension );
2431
2432 if( !schematicPath.Exists() )
2433 schematicPath.SetExt( FILEEXT::LegacySchematicFileExtension );
2434
2435 if( !schematicPath.Exists() )
2436 {
2437 m_reporter->Report( _( "Failed to fetch schematic netlist for parity tests.\n" ),
2439 checkParity = false;
2440 }
2441 else
2442 {
2443 typedef bool ( *NETLIST_FN_PTR )( const wxString&, std::string& );
2444 KIFACE* eeschema = m_kiway->KiFACE( KIWAY::FACE_SCH );
2445 NETLIST_FN_PTR netlister =
2446 (NETLIST_FN_PTR) eeschema->IfaceOrAddress( KIFACE_NETLIST_SCHEMATIC );
2447 ( *netlister )( schematicPath.GetFullPath(), netlist_str );
2448 }
2449 }
2450
2451 if( netlist_str == annotateMsg )
2452 {
2453 m_reporter->Report( wxString( netlist_str ) + wxT( "\n" ), RPT_SEVERITY_ERROR );
2454 checkParity = false;
2455 }
2456 }
2457
2458 if( checkParity )
2459 {
2460 try
2461 {
2462 STRING_LINE_READER* lineReader = new STRING_LINE_READER( netlist_str,
2463 _( "Eeschema netlist" ) );
2464 KICAD_NETLIST_READER netlistReader( lineReader, netlist.get() );
2465
2466 netlistReader.LoadNetlist();
2467 }
2468 catch( const IO_ERROR& )
2469 {
2470 m_reporter->Report( _( "Failed to fetch schematic netlist for parity tests.\n" ),
2472 checkParity = false;
2473 }
2474
2475 drcEngine->SetSchematicNetlist( netlist.get() );
2476 }
2477
2478 if( drcJob->m_refillZones )
2479 {
2480 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
2481 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
2482
2483 toolManager->GetTool<ZONE_FILLER_TOOL>()->FillAllZones( nullptr, m_progressReporter, true );
2484 }
2485
2486 drcEngine->SetProgressReporter( m_progressReporter );
2487 drcEngine->SetViolationHandler(
2488 [&]( const std::shared_ptr<DRC_ITEM>& aItem, const VECTOR2I& aPos, int aLayer,
2489 const std::function<void( PCB_MARKER* )>& aPathGenerator )
2490 {
2491 PCB_MARKER* marker = new PCB_MARKER( aItem, aPos, aLayer );
2492 aPathGenerator( marker );
2493 commit.Add( marker );
2494 } );
2495
2496 brd->RecordDRCExclusions();
2497 brd->DeleteMARKERs( true, true );
2498 drcEngine->RunTests( units, drcJob->m_reportAllTrackErrors, checkParity );
2499 drcEngine->ClearViolationHandler();
2500
2501 commit.Push( _( "DRC" ), SKIP_UNDO | SKIP_SET_DIRTY );
2502
2503 // Update the exclusion status on any excluded markers that still exist.
2504 brd->ResolveDRCExclusions( false );
2505
2506 std::shared_ptr<DRC_ITEMS_PROVIDER> markersProvider = std::make_shared<DRC_ITEMS_PROVIDER>(
2508
2509 std::shared_ptr<DRC_ITEMS_PROVIDER> ratsnestProvider =
2510 std::make_shared<DRC_ITEMS_PROVIDER>( brd, MARKER_BASE::MARKER_RATSNEST );
2511
2512 std::shared_ptr<DRC_ITEMS_PROVIDER> fpWarningsProvider =
2513 std::make_shared<DRC_ITEMS_PROVIDER>( brd, MARKER_BASE::MARKER_PARITY );
2514
2515 markersProvider->SetSeverities( drcJob->m_severity );
2516 ratsnestProvider->SetSeverities( drcJob->m_severity );
2517 fpWarningsProvider->SetSeverities( drcJob->m_severity );
2518
2519 m_reporter->Report( wxString::Format( _( "Found %d violations\n" ),
2520 markersProvider->GetCount() ),
2522 m_reporter->Report( wxString::Format( _( "Found %d unconnected items\n" ),
2523 ratsnestProvider->GetCount() ),
2525
2526 if( checkParity )
2527 {
2528 m_reporter->Report( wxString::Format( _( "Found %d schematic parity issues\n" ),
2529 fpWarningsProvider->GetCount() ),
2531 }
2532
2533 DRC_REPORT reportWriter( brd, units, markersProvider, ratsnestProvider, fpWarningsProvider );
2534
2535 bool wroteReport = false;
2536
2538 wroteReport = reportWriter.WriteJsonReport( outPath );
2539 else
2540 wroteReport = reportWriter.WriteTextReport( outPath );
2541
2542 if( !wroteReport )
2543 {
2544 m_reporter->Report( wxString::Format( _( "Unable to save DRC report to %s\n" ), outPath ),
2547 }
2548
2549 drcJob->AddOutput( outPath );
2550
2551 m_reporter->Report( wxString::Format( _( "Saved DRC Report to %s\n" ), outPath ),
2553
2554 if( drcJob->m_refillZones && drcJob->m_saveBoard )
2555 {
2556 if( BOARD_LOADER::SaveBoard( drcJob->m_filename, brd ) )
2557 {
2558 m_reporter->Report( _( "Saved board\n" ), RPT_SEVERITY_ACTION );
2559 }
2560 else
2561 {
2562 m_reporter->Report( _( "Failed to save board.\n" ), RPT_SEVERITY_ERROR );
2563
2565 }
2566 }
2567
2568 if( drcJob->m_exitCodeViolations )
2569 {
2570 if( markersProvider->GetCount() > 0 || ratsnestProvider->GetCount() > 0
2571 || fpWarningsProvider->GetCount() > 0 )
2572 {
2574 }
2575 }
2576
2578}
2579
2580
2582{
2583 JOB_EXPORT_PCB_IPC2581* job = dynamic_cast<JOB_EXPORT_PCB_IPC2581*>( aJob );
2584
2585 if( job == nullptr )
2587
2588 BOARD* brd = getBoard( job->m_filename );
2589
2590 if( !brd )
2592
2593 if( !job->m_variant.IsEmpty() )
2594 brd->SetCurrentVariant( job->m_variant );
2595
2596 if( job->GetConfiguredOutputPath().IsEmpty() )
2597 {
2598 wxFileName fn = brd->GetFileName();
2599 fn.SetExt( job->m_compress ? std::string( "zip" ) : FILEEXT::Ipc2581FileExtension );
2600
2601 job->SetWorkingOutputPath( fn.GetFullName() );
2602 }
2603
2604 wxString outPath = resolveJobOutputPath( aJob, brd );
2605
2606 if( !PATHS::EnsurePathExists( outPath, true ) )
2607 {
2608 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
2610 }
2611
2614
2616}
2617
2618
2620{
2621 JOB_EXPORT_PCB_IPCD356* job = dynamic_cast<JOB_EXPORT_PCB_IPCD356*>( aJob );
2622
2623 if( job == nullptr )
2625
2626 BOARD* brd = getBoard( job->m_filename );
2627
2628 if( !brd )
2630
2631 if( job->GetConfiguredOutputPath().IsEmpty() )
2632 {
2633 wxFileName fn = brd->GetFileName();
2634 fn.SetName( fn.GetName() );
2635 fn.SetExt( FILEEXT::IpcD356FileExtension );
2636
2637 job->SetWorkingOutputPath( fn.GetFullName() );
2638 }
2639
2640 wxString outPath = resolveJobOutputPath( aJob, brd );
2641
2642 if( !PATHS::EnsurePathExists( outPath, true ) )
2643 {
2644 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
2646 }
2647
2648 IPC356D_WRITER exporter( brd );
2649
2650 bool success = exporter.Write( outPath );
2651
2652 if( success )
2653 {
2654 aJob->AddOutput( outPath );
2655 m_reporter->Report( _( "Successfully created IPC-D-356 file\n" ), RPT_SEVERITY_INFO );
2657 }
2658 else
2659 {
2660 m_reporter->Report( _( "Failed to create IPC-D-356 file\n" ), RPT_SEVERITY_ERROR );
2662 }
2663}
2664
2665
2667{
2668 JOB_EXPORT_PCB_ODB* job = dynamic_cast<JOB_EXPORT_PCB_ODB*>( aJob );
2669
2670 if( job == nullptr )
2672
2673 BOARD* brd = getBoard( job->m_filename );
2674
2675 if( !brd )
2677
2678 if( !job->m_variant.IsEmpty() )
2679 brd->SetCurrentVariant( job->m_variant );
2680
2681 if( job->GetConfiguredOutputPath().IsEmpty() )
2682 {
2684 {
2685 // just basic folder name
2686 job->SetWorkingOutputPath( "odb" );
2687 }
2688 else
2689 {
2690 wxFileName fn( brd->GetFileName() );
2691 fn.SetName( fn.GetName() + wxS( "-odb" ) );
2692
2693 switch( job->m_compressionMode )
2694 {
2696 fn.SetExt( FILEEXT::ArchiveFileExtension );
2697 break;
2698
2700 fn.SetExt( "tgz" );
2701 break;
2702
2703 default:
2704 break;
2705 };
2706
2707 job->SetWorkingOutputPath( fn.GetFullName() );
2708 }
2709 }
2710
2711 wxString outPath = resolveJobOutputPath( job, brd );
2712
2713 // The helper handles output path creation, so hand it a job that already has fully-resolved
2714 // token context (title block and project overrides applied above).
2715 CLI_REPORTER reporter;
2716
2717 if( !m_reporter )
2718 m_reporter = &reporter;
2719
2721 aJob->AddOutput( outPath );
2722
2723 if( m_reporter->HasMessageOfSeverity( RPT_SEVERITY_ERROR ) )
2725
2727}
2728
2730{
2731 JOB_PCB_UPGRADE* job = dynamic_cast<JOB_PCB_UPGRADE*>( aJob );
2732
2733 if( job == nullptr )
2735
2736 bool shouldSave = job->m_force;
2737
2738 try
2739 {
2741 BOARD* brd = getBoard( job->m_filename );
2743 shouldSave = true;
2744
2745 if( shouldSave )
2746 {
2747 pi->SaveBoard( brd->GetFileName(), brd );
2748 m_reporter->Report( _( "Successfully saved board file using the latest format\n" ), RPT_SEVERITY_INFO );
2749 }
2750 else
2751 {
2752 m_reporter->Report( _( "Board file was not updated\n" ), RPT_SEVERITY_ERROR );
2753 }
2754 }
2755 catch( const IO_ERROR& ioe )
2756 {
2757 wxString msg =
2758 wxString::Format( _( "Error saving board file '%s'.\n%s" ), job->m_filename, ioe.What().GetData() );
2759 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2761 }
2762
2764}
2765
2766// Most job handlers need to align the running job with the board before resolving any
2767// output paths with variables in them like ${REVISION}.
2768wxString PCBNEW_JOBS_HANDLER::resolveJobOutputPath( JOB* aJob, BOARD* aBoard, const wxString* aDrawingSheet )
2769{
2770 aJob->SetTitleBlock( aBoard->GetTitleBlock() );
2771
2772 if( aDrawingSheet && !aDrawingSheet->IsEmpty() )
2773 loadOverrideDrawingSheet( aBoard, *aDrawingSheet );
2774
2775 PROJECT* project = aBoard->GetProject();
2776
2777 if( project )
2778 project->ApplyTextVars( aJob->GetVarOverrides() );
2779
2780 aBoard->SynchronizeProperties();
2781
2782 return aJob->GetFullOutputPath( project );
2783}
2784
2785
2787{
2789 &aBrd->GetPageSettings(),
2790 aBrd->GetProject(),
2791 &aBrd->GetTitleBlock(),
2792 &aBrd->GetProperties() );
2793
2794 drawingSheet->SetSheetName( std::string() );
2795 drawingSheet->SetSheetPath( std::string() );
2796 drawingSheet->SetIsFirstPage( true );
2797
2798 drawingSheet->SetFileName( TO_UTF8( aBrd->GetFileName() ) );
2799
2800 wxString currentVariant = aBrd->GetCurrentVariant();
2801 wxString variantDesc = aBrd->GetVariantDescription( currentVariant );
2802 drawingSheet->SetVariantName( TO_UTF8( currentVariant ) );
2803 drawingSheet->SetVariantDesc( TO_UTF8( variantDesc ) );
2804
2805 return drawingSheet;
2806}
2807
2808
2809void PCBNEW_JOBS_HANDLER::loadOverrideDrawingSheet( BOARD* aBrd, const wxString& aSheetPath )
2810{
2811 // dont bother attempting to load a empty path, if there was one
2812 if( aSheetPath.IsEmpty() )
2813 return;
2814
2815 auto loadSheet =
2816 [&]( const wxString& path ) -> bool
2817 {
2820 resolver.SetProject( aBrd->GetProject() );
2821 resolver.SetProgramBase( &Pgm() );
2822
2823 wxString filename = resolver.ResolvePath( BASE_SCREEN::m_DrawingSheetFileName,
2824 aBrd->GetProject()->GetProjectPath(),
2825 { aBrd->GetEmbeddedFiles() } );
2826 wxString msg;
2827
2828 if( !DS_DATA_MODEL::GetTheInstance().LoadDrawingSheet( filename, &msg ) )
2829 {
2830 m_reporter->Report( wxString::Format( _( "Error loading drawing sheet '%s'." ),
2831 path )
2832 + wxS( "\n" ) + msg + wxS( "\n" ),
2834 return false;
2835 }
2836
2837 return true;
2838 };
2839
2840 if( loadSheet( aSheetPath ) )
2841 return;
2842
2843 // failed loading custom path, revert back to default
2844 loadSheet( aBrd->GetProject()->GetProjectFile().m_BoardDrawingSheetFile );
2845}
2846
2847
2849{
2850 JOB_PCB_IMPORT* job = dynamic_cast<JOB_PCB_IMPORT*>( aJob );
2851
2852 if( !job )
2854
2855 // Map job format to PCB_IO file type
2857
2858 switch( job->m_format )
2859 {
2862 break;
2863
2866 break;
2867
2870 break;
2871
2874 break;
2875
2878 break;
2879
2882 break;
2883
2886 break;
2887
2890 break;
2891 }
2892
2894 {
2895 m_reporter->Report( wxString::Format( _( "Unable to determine file format for '%s'\n" ),
2896 job->m_inputFile ),
2899 }
2900
2901 // Check that input file exists
2902 if( !wxFile::Exists( job->m_inputFile ) )
2903 {
2904 m_reporter->Report( wxString::Format( _( "Input file not found: '%s'\n" ),
2905 job->m_inputFile ),
2908 }
2909
2910 // Determine output path
2911 wxString outputPath = job->GetConfiguredOutputPath();
2912
2913 if( outputPath.IsEmpty() )
2914 {
2915 wxFileName fn( job->m_inputFile );
2916 fn.SetExt( FILEEXT::KiCadPcbFileExtension );
2917 outputPath = fn.GetFullPath();
2918 }
2919
2920 BOARD* board = nullptr;
2921 wxString formatName = PCB_IO_MGR::ShowType( fileType );
2922 std::vector<wxString> warnings;
2923
2924 try
2925 {
2927
2928 if( !pi )
2929 {
2930 m_reporter->Report( wxString::Format( _( "No plugin found for file type '%s'\n" ),
2931 formatName ),
2934 }
2935
2936 m_reporter->Report( wxString::Format( _( "Importing '%s' using %s format...\n" ),
2937 job->m_inputFile, formatName ),
2939
2940 board = pi->LoadBoard( job->m_inputFile, nullptr, nullptr, nullptr );
2941
2942 if( !board )
2943 {
2944 m_reporter->Report( _( "Failed to load board\n" ), RPT_SEVERITY_ERROR );
2946 }
2947
2948 // Save as KiCad format
2950 kicadPlugin->SaveBoard( outputPath, board );
2951
2952 m_reporter->Report( wxString::Format( _( "Successfully saved imported board to '%s'\n" ),
2953 outputPath ),
2955
2956 // Generate report if requested
2958 {
2959 wxFileName inputFn( job->m_inputFile );
2960 wxFileName outputFn( outputPath );
2961
2962 // Count board statistics
2963 size_t footprintCount = board->Footprints().size();
2964 size_t trackCount = 0;
2965 size_t viaCount = 0;
2966 size_t zoneCount = board->Zones().size();
2967
2968 for( PCB_TRACK* track : board->Tracks() )
2969 {
2970 if( track->Type() == PCB_VIA_T )
2971 viaCount++;
2972 else
2973 trackCount++;
2974 }
2975
2976 // Build layer mapping info
2977 nlohmann::json layerMappings = nlohmann::json::object();
2978 LSEQ enabledLayers = board->GetEnabledLayers().Seq();
2979
2980 for( PCB_LAYER_ID layer : enabledLayers )
2981 {
2982 wxString layerName = board->GetLayerName( layer );
2983
2984 layerMappings[layerName.ToStdString()] = {
2985 { "kicad_layer", LSET::Name( layer ).ToStdString() },
2986 { "method", "auto" }
2987 };
2988 }
2989
2991 {
2992 nlohmann::json report;
2993
2994 report["source_file"] = inputFn.GetFullName().ToStdString();
2995 report["source_format"] = formatName.ToStdString();
2996 report["output_file"] = outputFn.GetFullName().ToStdString();
2997 report["layer_mapping"] = layerMappings;
2998 report["statistics"] = {
2999 { "footprints", footprintCount },
3000 { "tracks", trackCount },
3001 { "vias", viaCount },
3002 { "zones", zoneCount }
3003 };
3004
3005 nlohmann::json warningsJson = nlohmann::json::array();
3006
3007 for( const wxString& warning : warnings )
3008 warningsJson.push_back( warning.ToStdString() );
3009
3010 report["warnings"] = warningsJson;
3011 report["errors"] = nlohmann::json::array();
3012
3013 wxString reportOutput = wxString::FromUTF8( report.dump( 2 ) );
3014
3015 if( !job->m_reportFile.IsEmpty() )
3016 {
3017 wxFile file( job->m_reportFile, wxFile::write );
3018
3019 if( file.IsOpened() )
3020 {
3021 file.Write( reportOutput );
3022 file.Close();
3023 }
3024 }
3025 else
3026 {
3027 m_reporter->Report( reportOutput + wxS( "\n" ), RPT_SEVERITY_INFO );
3028 }
3029 }
3031 {
3032 wxString text;
3033
3034 text += wxString::Format( wxS( "Import Report\n" ) );
3035 text += wxString::Format( wxS( "=============\n\n" ) );
3036 text += wxString::Format( wxS( "Source file: %s\n" ), inputFn.GetFullName() );
3037 text += wxString::Format( wxS( "Source format: %s\n" ), formatName );
3038 text += wxString::Format( wxS( "Output file: %s\n\n" ), outputFn.GetFullName() );
3039 text += wxS( "Statistics:\n" );
3040 text += wxString::Format( wxS( " Footprints: %zu\n" ), footprintCount );
3041 text += wxString::Format( wxS( " Tracks: %zu\n" ), trackCount );
3042 text += wxString::Format( wxS( " Vias: %zu\n" ), viaCount );
3043 text += wxString::Format( wxS( " Zones: %zu\n" ), zoneCount );
3044
3045 if( !warnings.empty() )
3046 {
3047 text += wxS( "\nWarnings:\n" );
3048
3049 for( const wxString& warning : warnings )
3050 text += wxString::Format( wxS( " - %s\n" ), warning );
3051 }
3052
3053 if( !job->m_reportFile.IsEmpty() )
3054 {
3055 wxFile file( job->m_reportFile, wxFile::write );
3056
3057 if( file.IsOpened() )
3058 {
3059 file.Write( text );
3060 file.Close();
3061 }
3062 }
3063 else
3064 {
3065 m_reporter->Report( text, RPT_SEVERITY_INFO );
3066 }
3067 }
3068 }
3069
3070 delete board;
3071 }
3072 catch( const IO_ERROR& ioe )
3073 {
3074 m_reporter->Report( wxString::Format( _( "Error during import: %s\n" ), ioe.What() ),
3076
3077 delete board;
3079 }
3080
3082}
@ VIEW3D_BOTTOM
Definition 3d_enums.h:81
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:112
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
#define RANGE_SCALE_3D
This defines the range that all coord will have to be rendered.
wxString FormatBoardStatisticsJson(const BOARD_STATISTICS_DATA &aData, BOARD *aBoard, const UNITS_PROVIDER &aUnitsProvider, const wxString &aProjectName, const wxString &aBoardName)
void ComputeBoardStatistics(BOARD *aBoard, const BOARD_STATISTICS_OPTIONS &aOptions, BOARD_STATISTICS_DATA &aData)
wxString FormatBoardStatisticsReport(const BOARD_STATISTICS_DATA &aData, BOARD *aBoard, const UNITS_PROVIDER &aUnitsProvider, const wxString &aProjectName, const wxString &aBoardName)
void InitializeBoardStatisticsData(BOARD_STATISTICS_DATA &aData)
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
PROJECTION_TYPE
Definition camera.h:40
static wxString m_DrawingSheetFileName
the name of the drawing sheet file, or empty to use the default drawing sheet
Definition base_screen.h:85
Helper class to handle information needed to display 3D board.
double BiuTo3dUnits() const noexcept
Board integer units To 3D units.
void SetVisibleLayers(const std::bitset< LAYER_3D_END > &aLayers)
std::bitset< LAYER_3D_END > GetVisibleLayers() const
void SetBoard(BOARD *aBoard) noexcept
Set current board to be rendered.
void SetLayerColors(const std::map< int, COLOR4D > &aColors)
EDA_3D_VIEWER_SETTINGS * m_Cfg
std::map< int, COLOR4D > m_ColorOverrides
allows to override color scheme colors
void Set3dCacheManager(S3D_CACHE *aCacheMgr) noexcept
Update the cache manager pointer.
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
std::shared_ptr< DRC_ENGINE > m_DRCEngine
const VECTOR2I & GetAuxOrigin() const
static std::unique_ptr< BOARD > CreateEmptyBoard(PROJECT *aProject)
static std::unique_ptr< BOARD > Load(const wxString &aFileName, PCB_IO_MGR::PCB_FILE_T aFormat, PROJECT *aProject, const OPTIONS &aOptions)
static bool SaveBoard(wxString &aFileName, BOARD *aBoard, PCB_IO_MGR::PCB_FILE_T aFormat)
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:323
void SetCurrentVariant(const wxString &aVariant)
Definition board.cpp:2721
const PAGE_INFO & GetPageSettings() const
Definition board.h:807
const ZONES & Zones() const
Definition board.h:368
void RecordDRCExclusions()
Scan existing markers and record data from any that are Excluded.
Definition board.cpp:338
TITLE_BLOCK & GetTitleBlock()
Definition board.h:813
const std::map< wxString, wxString > & GetProperties() const
Definition board.h:394
const FOOTPRINTS & Footprints() const
Definition board.h:364
const TRACKS & Tracks() const
Definition board.h:362
const wxString & GetFileName() const
Definition board.h:360
std::vector< PCB_MARKER * > ResolveDRCExclusions(bool aCreateMarkers)
Rebuild DRC markers from the serialized data in BOARD_DESIGN_SETTINGS.
Definition board.cpp:395
wxString GetVariantDescription(const wxString &aVariantName) const
Definition board.cpp:2831
int GetFileFormatVersionAtLoad() const
Definition board.h:450
const PCB_PLOT_PARAMS & GetPlotOptions() const
Definition board.h:810
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition board.cpp:737
wxString GetCurrentVariant() const
Definition board.h:398
PROJECT * GetProject() const
Definition board.h:587
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1091
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:976
void SynchronizeProperties()
Copy the current project's text variables into the boards property cache.
Definition board.cpp:2701
BOX2I ComputeBoundingBox(bool aBoardEdgesOnly=false, bool aPhysicalLayersOnly=false) const
Calculate the bounding box containing all board items (or board edge segments).
Definition board.cpp:2323
void DeleteMARKERs()
Delete all MARKERS from the board.
Definition board.cpp:1718
constexpr const Vec GetCenter() const
Definition box2.h:230
void SetProjection(PROJECTION_TYPE aProjection)
Definition camera.h:206
void RotateY_T1(float aAngleInRadians)
Definition camera.cpp:686
bool Zoom_T1(float aFactor)
Definition camera.cpp:629
bool SetCurWindowSize(const wxSize &aSize)
Update the windows size of the camera.
Definition camera.cpp:571
bool ViewCommand_T1(VIEW3D_TYPE aRequestedView)
Definition camera.cpp:110
void RotateX_T1(float aAngleInRadians)
Definition camera.cpp:680
void SetLookAtPos_T1(const SFVEC3F &aLookAtPos)
Definition camera.h:162
const SFVEC3F & GetLookAtPos_T1() const
Definition camera.h:167
void RotateZ_T1(float aAngleInRadians)
Definition camera.cpp:692
bool ParametersChanged()
Definition camera.cpp:730
Reporter forwarding messages to stdout or stderr as appropriate.
Definition reporter.h:236
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:78
static bool GenerateFile(JOB_EXPORT_PCB_IPC2581 &aJob, BOARD *aBoard, PROGRESS_REPORTER *aProgressReporter, REPORTER *aReporter)
static void GenerateODBPPFiles(const JOB_EXPORT_PCB_ODB &aJob, BOARD *aBoard, PCB_EDIT_FRAME *aParentFrame=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr, REPORTER *aErrorReporter=nullptr)
The dialog to create footprint position files and choose options (one or 2 files, units and force all...
A dialog to set the plot options and create plot files in various formats.
Definition dialog_plot.h:41
int ShowModal() override
bool WriteJsonReport(const wxString &aFullFileName)
bool WriteTextReport(const wxString &aFullFileName)
Helper to handle drill precision format in excellon files.
static DS_DATA_MODEL & GetTheInstance()
Return the instance of DS_DATA_MODEL used in the application.
void SetSheetPath(const std::string &aSheetPath)
Set the sheet path displayed in the title block.
void SetVariantName(const std::string &aVariant)
Set the current variant name and description to be shown on the drawing sheet.
void SetVariantDesc(const std::string &aVariantDesc)
void SetSheetName(const std::string &aSheetName)
Set the sheet name displayed in the title block.
void SetIsFirstPage(bool aIsFirstPage)
Change if this is first page.
void SetFileName(const std::string &aFileName)
Set the file name displayed in the title block.
LAYER_PRESET_3D * FindPreset(const wxString &aName)
std::vector< LAYER_PRESET_3D > m_LayerPresets
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:149
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:93
Create Excellon drill, drill map, and drill report files.
void SetFormat(bool aMetric, ZEROS_FMT aZerosFmt=DECIMAL_FORMAT, int aLeftDigits=0, int aRightDigits=0)
Initialize internal parameters to match the given format.
bool CreateDrillandMapFilesSet(const wxString &aPlotDirectory, bool aGenDrill, bool aGenMap, REPORTER *aReporter=nullptr)
Create the full set of Excellon drill file for the board.
void SetOptions(bool aMirror, bool aMinimalHeader, const VECTOR2I &aOffset, bool aMerge_PTH_NPTH)
Initialize internal parameters to match drill options.
void SetRouteModeForOvalHoles(bool aUseRouteModeForOvalHoles)
wxString m_outputFile
Wrapper to expose an API for writing VRML files, without exposing all the many structures used in the...
Definition export_vrml.h:33
bool ExportVRML_File(PROJECT *aProject, wxString *aMessages, const wxString &aFullFileName, double aMMtoWRMLunit, bool aIncludeUnspecified, bool aIncludeDNP, bool aExport3DFiles, bool aUseRelativePaths, const wxString &a3D_Subdir, double aXRef, double aYRef)
Exports the board and its footprint shapes 3D (vrml files only) as a vrml file.
Provide an extensible class to resolve 3D model paths.
An interface to the global shared library manager that is schematic-specific and linked to one projec...
void SetPosition(const VECTOR2I &aPos) override
void SetLink(const KIID &aLink)
Definition footprint.h:1094
void SetOrientation(const EDA_ANGLE &aNewAngle)
EDA_ITEM * Clone() const override
Invoke a function on all children.
std::deque< PAD * > & Pads()
Definition footprint.h:326
const LIB_ID & GetFPID() const
Definition footprint.h:371
void SetPath(const wxString &aPath)
void Save(FOOTPRINT *aFootprintFilter=nullptr)
Save the footprint cache or a single footprint from it to disk.
boost::ptr_map< wxString, FP_CACHE_ENTRY > & GetFootprints()
Export board to GenCAD file format.
void UseIndividualShapes(bool aUnique)
Make pad shapes unique.
void UsePinNamesUnique(bool aUnique)
Make pin names unique.
void StoreOriginCoordsInFile(bool aStore)
Store origin coordinate in GenCAD file.
void FlipBottomPads(bool aFlip)
Flip pad shapes on the bottom side.
void SetPlotOffet(VECTOR2I aOffset)
Set the coordinates offset when exporting items.
bool WriteFile(const wxString &aFullFileName)
Export a GenCAD file.
void SetMapFileFormat(PLOT_FORMAT aMapFmt)
Initialize the format for the drill map file.
bool GenDrillReportFile(const wxString &aFullFileName, REPORTER *aReporter=nullptr)
Create a plain text report file giving a list of drill values and drill count for through holes,...
GERBER_JOBFILE_WRITER is a class used to create Gerber job file a Gerber job file stores info to make...
bool CreateJobFile(const wxString &aFullFilename)
Creates a Gerber job file.
void AddGbrFile(PCB_LAYER_ID aLayer, wxString &aFilename)
add a gerber file name and type in job file list
virtual bool EndPlot() override
Used to create Gerber drill files.
bool CreateDrillandMapFilesSet(const wxString &aPlotDirectory, bool aGenDrill, bool aGenMap, bool aGenTenting, REPORTER *aReporter=nullptr)
Create the full set of Excellon drill file for the board filenames are computed from the board name,...
void SetOptions(const VECTOR2I &aOffset)
Initialize internal parameters to match drill options.
void SetFormat(int aRightDigits=6)
Initialize internal parameters to match the given format.
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
Wrapper to expose an API for writing IPC-D356 files.
Definition export_d356.h:54
bool Write(const wxString &aFilename)
Generates and writes the netlist to a given path.
void Register(const std::string &aJobTypeName, std::function< int(JOB *job)> aHandler, std::function< bool(JOB *job, wxWindow *aParent)> aConfigHandler)
JOB_DISPATCHER(KIWAY *aKiway)
PROGRESS_REPORTER * m_progressReporter
REPORTER * m_reporter
JOB_EXPORT_PCB_3D::FORMAT m_format
EXPORTER_STEP_PARAMS m_3dparams
Despite the name; also used for other formats.
wxString GetSettingsDialogTitle() const override
ODB_COMPRESSION m_compressionMode
@ ALL_LAYERS_ONE_FILE
DEPRECATED MODE.
bool m_pdfSingle
This is a hack to deal with cli having the wrong behavior We will deprecate out the wrong behavior,...
GEN_MODE m_pdfGenMode
The background color specified in a hex string.
LSEQ m_plotOnAllLayersSequence
Used by SVG & PDF.
std::optional< wxString > m_argLayers
std::optional< wxString > m_argCommonLayers
LSEQ m_plotLayerSequence
Layers to include on all individual layer prints.
wxString m_variant
Variant name for variant-aware filtering.
void SetDefaultOutputPath(const wxString &aReferenceName)
wxString m_libraryPath
wxString m_outputLibraryPath
bool m_saveBoard
Definition job_pcb_drc.h:36
bool m_reportAllTrackErrors
Definition job_pcb_drc.h:32
bool m_refillZones
Definition job_pcb_drc.h:35
Job to import a non-KiCad PCB file to KiCad format.
REPORT_FORMAT m_reportFormat
wxString m_reportFile
wxString m_inputFile
VECTOR3D m_lightBottomIntensity
VECTOR3D m_lightTopIntensity
VECTOR3D m_lightCameraIntensity
VECTOR3D m_rotation
wxString m_filename
bool m_useBoardStackupColors
VECTOR3D m_lightSideIntensity
std::string m_appearancePreset
bool m_exitCodeViolations
Definition job_rc.h:52
int m_severity
Definition job_rc.h:49
UNITS m_units
Definition job_rc.h:48
OUTPUT_FORMAT m_format
Definition job_rc.h:50
wxString m_filename
Definition job_rc.h:47
An simple container class that lets us dispatch output jobs to kifaces.
Definition job.h:184
wxString ResolveOutputPath(const wxString &aPath, bool aPathIsDirectory, PROJECT *aProject) const
Definition job.cpp:100
void AddOutput(wxString aOutputPath)
Definition job.h:216
wxString GetFullOutputPath(PROJECT *aProject) const
Returns the full output path for the job, taking into account the configured output path,...
Definition job.cpp:150
wxString GetWorkingOutputPath() const
Returns the working output path for the job, if one has been set.
Definition job.h:244
wxString GetConfiguredOutputPath() const
Returns the configured output path for the job.
Definition job.h:233
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Definition job.h:204
void SetWorkingOutputPath(const wxString &aPath)
Sets a transient output path for the job, it takes priority over the configured output path when GetF...
Definition job.h:239
const std::map< wxString, wxString > & GetVarOverrides() const
Definition job.h:197
Read the new s-expression based KiCad netlist format.
virtual void LoadNetlist() override
Load the contents of the netlist file into aNetlist.
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:105
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition kiway.h:315
virtual KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=nullptr)
Return the KIWAY_PLAYER* given a FRAME_T.
Definition kiway.cpp:402
@ FACE_SCH
eeschema DSO
Definition kiway.h:322
void AsyncLoad()
Loads all available libraries for this adapter type in the background.
const UTF8 & GetLibItemName() const
Definition lib_id.h:102
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition lseq.h:47
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:608
LSEQ UIOrder() const
Return the copper, technical and user layers in the order shown in layer widget.
Definition lset.cpp:743
LSEQ SeqStackupForPlotting() const
Return the sequence that is typical for a bottom-to-top stack-up.
Definition lset.cpp:404
static LSET AllNonCuMask()
Return a mask holding all layer minus CU layers.
Definition lset.cpp:627
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition lset.cpp:313
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:599
static const LSET & AllLayersMask()
Definition lset.cpp:641
static const LSET & InternalCuMask()
Return a complete set of internal copper layers which is all Cu layers except F_Cu and B_Cu.
Definition lset.cpp:577
static wxString Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition lset.cpp:188
@ MARKER_DRAWING_SHEET
Definition marker_base.h:56
Definition pad.h:55
static bool EnsurePathExists(const wxString &aPath, bool aPathToFile=false)
Attempts to create a given path if it does not exist.
Definition paths.cpp:518
DS_PROXY_VIEW_ITEM * getDrawingSheetProxyView(BOARD *aBrd)
wxString resolveJobOutputPath(JOB *aJob, BOARD *aBoard, const wxString *aDrawingSheet=nullptr)
void loadOverrideDrawingSheet(BOARD *brd, const wxString &aSheetPath)
PCBNEW_JOBS_HANDLER(KIWAY *aKiway)
TOOL_MANAGER * getToolManager(BOARD *aBrd)
BOARD * getBoard(const wxString &aPath=wxEmptyString)
std::unique_ptr< TOOL_MANAGER > m_toolManager
LSEQ convertLayerArg(wxString &aLayerString, BOARD *aBoard) const
int doFpExportSvg(JOB_FP_EXPORT_SVG *aSvgJob, const FOOTPRINT *aFootprint)
BOARD * GetBoard() const
The main frame for Pcbnew.
A #PLUGIN derivation for saving and loading Pcbnew s-expression formatted files.
static bool ConvertLibrary(const std::map< std::string, UTF8 > &aOldFileProps, const wxString &aOldFilePath, const wxString &aNewFilePath, REPORTER *aReporter)
Convert a schematic symbol library to the latest KiCad format.
PCB_FILE_T
The set of file types that the PCB_IO_MGR knows about, and for which there has been a plugin written,...
Definition pcb_io_mgr.h:56
@ KICAD_SEXP
S-expression Pcbnew file format.
Definition pcb_io_mgr.h:58
@ ALTIUM_DESIGNER
Definition pcb_io_mgr.h:63
@ CADSTAR_PCB_ARCHIVE
Definition pcb_io_mgr.h:64
@ PCB_FILE_UNKNOWN
0 is not a legal menu id on Mac
Definition pcb_io_mgr.h:57
static PCB_IO * FindPlugin(PCB_FILE_T aFileType)
Return a #PLUGIN which the caller can use to import, export, save, or load design documents.
static PCB_FILE_T FindPluginTypeFromBoardPath(const wxString &aFileName, int aCtl=0)
Return a plugin type given a path for a board file.
static PCB_FILE_T GuessPluginTypeFromLibPath(const wxString &aLibPath, int aCtl=0)
Return a plugin type given a footprint library's libPath.
static const wxString ShowType(PCB_FILE_T aFileType)
Return a brief name for a plugin given aFileType enum.
static void PlotJobToPlotOpts(PCB_PLOT_PARAMS &aOpts, JOB_EXPORT_PCB_PLOT *aJob, REPORTER &aReporter)
Translate a JOB to PCB_PLOT_PARAMS.
bool Plot(const wxString &aOutputPath, const LSEQ &aLayersToPlot, const LSEQ &aCommonLayers, bool aUseGerberFileExtensions, bool aOutputPathIsSingle=false, std::optional< wxString > aLayerName=std::nullopt, std::optional< wxString > aSheetName=std::nullopt, std::optional< wxString > aSheetPath=std::nullopt, std::vector< wxString > *aOutputFiles=nullptr)
static void BuildPlotFileName(wxFileName *aFilename, const wxString &aOutputDir, const wxString &aSuffix, const wxString &aExtension)
Complete a plot filename.
Parameters and options when plotting/printing a board.
LSEQ GetPlotOnAllLayersSequence() const
void SetSkipPlotNPTH_Pads(bool aSkip)
void SetLayerSelection(const LSET &aSelection)
void SetPlotOnAllLayersSequence(LSEQ aSeq)
void SetPlotFrameRef(bool aFlag)
void SetPlotPadNumbers(bool aFlag)
LSET GetLayerSelection() const
void SetMirror(bool aFlag)
bool GetSketchPadsOnFabLayers() const
void SetSvgFitPageToBoard(int aSvgFitPageToBoard)
bool GetUseGerberProtelExtensions() const
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition pgm_base.h:130
Used to create Gerber drill files.
const wxString GetPlaceFileName(const wxString &aFullBaseFilename, PCB_LAYER_ID aLayer) const
void SetVariant(const wxString &aVariant)
Set the variant name for variant-aware filtering.
int CreatePlaceFile(const wxString &aFullFilename, PCB_LAYER_ID aLayer, bool aIncludeBrdEdges, bool aExcludeDNP, bool aExcludeBOM)
Create an pnp gerber file.
The ASCII format of the kicad place file is:
static wxString DecorateFilename(const wxString &aBaseName, bool aFront, bool aBack)
std::string GenPositionData()
build a string filled with the position data
void SetVariant(const wxString &aVariant)
Set the variant name for variant-aware export.
Base plotter engine class.
Definition plotter.h:136
virtual bool EndPlot()=0
wxString m_BoardDrawingSheetFile
PcbNew params.
static S3D_CACHE * Get3DCacheManager(PROJECT *aProject, bool updateProjDir=false)
Return a pointer to an instance of the 3D cache manager.
static FOOTPRINT_LIBRARY_ADAPTER * FootprintLibAdapter(PROJECT *aProject)
Container for project specific data.
Definition project.h:66
virtual const wxString GetProjectFullName() const
Return the full path and name of the project.
Definition project.cpp:171
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition project.cpp:177
virtual const wxString GetProjectName() const
Return the short name of the project.
Definition project.cpp:189
virtual PROJECT_FILE & GetProjectFile() const
Definition project.h:204
bool Redraw(bool aIsMoving, REPORTER *aStatusReporter, REPORTER *aWarningReporter) override
Redraw the view.
void SetCurWindowSize(const wxSize &aSize) override
Before each render, the canvas will tell the render what is the size of its windows,...
bool LoadProject(const wxString &aFullPath, bool aSetActive=true)
Load a project or sets up a new project with a specified path.
PROJECT * GetProject(const wxString &aFullPath) const
Retrieve a loaded project by name.
PROJECT & Prj() const
A helper while we are not MDI-capable – return the one and only project.
Is a LINE_READER that reads from a multiline 8 bit wide std::string.
Definition richio.h:226
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Master controller class:
TOOL_BASE * FindTool(int aId) const
Search for a tool with given ID.
void RegisterTool(TOOL_BASE *aTool)
Add a tool to the manager set and sets it up.
void SetEnvironment(EDA_ITEM *aModel, KIGFX::VIEW *aView, KIGFX::VIEW_CONTROLS *aViewControls, APP_SETTINGS_BASE *aSettings, TOOLS_HOLDER *aFrame)
Set the work environment (model, view, view controls and the parent window).
void Pan_T1(const SFVEC3F &aDeltaOffsetInc) override
void SetT0_and_T1_current_T() override
This will set T0 and T1 with the current values.
void Interpolate(float t) override
It will update the matrix to interpolate between T0 and T1 values.
wxString wx_str() const
Definition utf8.cpp:45
Handle actions specific to filling copper zones.
wxString GetDefaultPlotExtension(PLOT_FORMAT aFormat)
Return the default plot extension for a format.
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:202
This file is part of the common library.
static DRILL_PRECISION precisionListForInches(2, 4)
static DRILL_PRECISION precisionListForMetric(3, 3)
#define _(s)
#define FOLLOW_PLOT_SETTINGS
#define FOLLOW_PCB
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
#define IS_NEW
New item, just created.
EDA_UNITS
Definition eda_units.h:48
static FILENAME_RESOLVER * resolver
@ FRAME_PCB_EDITOR
Definition frame_type.h:42
@ FRAME_SCH
Definition frame_type.h:34
Classes used in drill files, map files and report files generation.
Classes used in drill files, map files and report files generation.
Classes used to generate a Gerber job file in JSON.
Classes used in place file generation.
static const std::string LegacySchematicFileExtension
static const std::string BrepFileExtension
static const std::string JpegFileExtension
static const std::string GerberJobFileExtension
static const std::string GerberFileExtension
static const std::string XaoFileExtension
static const std::string ReportFileExtension
static const std::string GltfBinaryFileExtension
static const std::string ProjectFileExtension
static const std::string PngFileExtension
static const std::string FootprintPlaceFileExtension
static const std::string JsonFileExtension
static const std::string KiCadSchematicFileExtension
static const std::string CsvFileExtension
static const std::string U3DFileExtension
static const std::string PdfFileExtension
static const std::string Ipc2581FileExtension
static const std::string GencadFileExtension
static const std::string StlFileExtension
static const std::string IpcD356FileExtension
static const std::string PlyFileExtension
static const std::string StepFileExtension
static const std::string SVGFileExtension
static const std::string VrmlFileExtension
static const std::string ArchiveFileExtension
static const std::string KiCadPcbFileExtension
std::unique_ptr< T > IO_RELEASER
Helper to hold and release an IO_BASE object when exceptions are thrown.
Definition io_mgr.h:33
@ KIFACE_NETLIST_SCHEMATIC
Definition kiface_ids.h:42
KIID niluuid(0)
#define KICTL_KICAD_ONLY
chosen file is from KiCad according to user
wxString LayerName(int aLayer)
Returns the default display name for a given layer.
Definition layer_id.cpp:31
@ LAYER_3D_BACKGROUND_TOP
Definition layer_ids.h:555
@ LAYER_3D_BACKGROUND_BOTTOM
Definition layer_ids.h:554
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ F_CrtYd
Definition layer_ids.h:116
@ B_Adhes
Definition layer_ids.h:103
@ F_Paste
Definition layer_ids.h:104
@ F_Adhes
Definition layer_ids.h:102
@ B_Mask
Definition layer_ids.h:98
@ B_Cu
Definition layer_ids.h:65
@ F_Mask
Definition layer_ids.h:97
@ B_Paste
Definition layer_ids.h:105
@ F_Fab
Definition layer_ids.h:119
@ F_SilkS
Definition layer_ids.h:100
@ B_CrtYd
Definition layer_ids.h:115
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ B_SilkS
Definition layer_ids.h:101
@ F_Cu
Definition layer_ids.h:64
@ B_Fab
Definition layer_ids.h:118
This file contains miscellaneous commonly used macros and functions.
@ MAIL_SCH_GET_NETLIST
Definition mail_type.h:49
static const int ERR_ARGS
Definition exit_codes.h:31
static const int OK
Definition exit_codes.h:30
static const int ERR_RC_VIOLATIONS
Rules check violation count was greater than 0.
Definition exit_codes.h:37
static const int ERR_INVALID_INPUT_FILE
Definition exit_codes.h:33
static const int SUCCESS
Definition exit_codes.h:29
static const int ERR_INVALID_OUTPUT_CONFLICT
Definition exit_codes.h:34
static const int ERR_UNKNOWN
Definition exit_codes.h:32
#define SEXPR_BOARD_FILE_VERSION
Current s-expression file format version. 2 was the last legacy format version.
#define CTL_FOR_LIBRARY
Format output for a footprint library instead of clipboard or BOARD.
static DRILL_PRECISION precisionListForInches(2, 4)
static DRILL_PRECISION precisionListForMetric(3, 3)
const wxString GetGerberProtelExtension(int aLayer)
Definition pcbplot.cpp:43
PLOTTER * StartPlotBoard(BOARD *aBoard, const PCB_PLOT_PARAMS *aPlotOpts, int aLayer, const wxString &aLayerName, const wxString &aFullFileName, const wxString &aSheetName, const wxString &aSheetPath, const wxString &aPageName=wxT("1"), const wxString &aPageNumber=wxEmptyString, const int aPageCount=1)
Open a new plotfile using the options (and especially the format) specified in the options and prepar...
void PlotBoardLayers(BOARD *aBoard, PLOTTER *aPlotter, const LSEQ &aLayerSequence, const PCB_PLOT_PARAMS &aPlotOptions)
Plot a sequence of board layer IDs.
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
PLOT_FORMAT
The set of supported output plot formats.
Definition plotter.h:64
Plotting engines similar to ps (PostScript, Gerber, svg)
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_INFO
@ RPT_SEVERITY_ACTION
#define SKIP_SET_DIRTY
Definition sch_commit.h:42
#define SKIP_UNDO
Definition sch_commit.h:40
T * GetAppSettings(const char *aFilename)
const int scale
MODEL3D_FORMAT_TYPE fileType(const char *aFileName)
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
std::vector< KIGFX::COLOR4D > raytrace_lightColor
Implement a participant in the KIWAY alchemy.
Definition kiway.h:156
std::string path
Declaration for a track ball camera.
double DEG2RAD(double deg)
Definition trigo.h:166
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:94
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687
Definition of file extensions used in Kicad.
glm::vec3 SFVEC3F
Definition xv3d_types.h:44
#define ZONE_FILLER_TOOL_NAME